Import backports 20150424

PREFIX=http://www.kernel.org/pub/linux/kernel/projects/backports
rm -rf *
curl -L $PREFIX/2015/04/24/backports-20150424.tar.xz |
tar Jxv --strip-components=1

Change-Id: Id8e3e87a20ce3ba970eab54fd4da06c5602de845
diff --git a/.local-symbols b/.local-symbols
index 054c1ee..8ac5ffc 100644
--- a/.local-symbols
+++ b/.local-symbols
@@ -20,6 +20,7 @@
 BPAUTO_BUILD_CRYPTO_CCM=
 BPAUTO_WANT_DEV_COREDUMP=
 BPAUTO_BUILD_WANT_DEV_COREDUMP=
+BPAUTO_RHASHTABLE=
 CFG80211=
 NL80211_TESTMODE=
 CFG80211_DEVELOPER_WARNINGS=
@@ -31,6 +32,7 @@
 CFG80211_DEBUGFS=
 CFG80211_INTERNAL_REGDB=
 CFG80211_WEXT=
+CFG80211_WEXT_EXPORT=
 LIB80211=
 LIB80211_CRYPT_WEP=
 LIB80211_CRYPT_CCMP=
@@ -68,6 +70,10 @@
 BT_BREDR=
 BT_LE=
 BT_6LOWPAN=
+BT_SELFTEST=
+BT_SELFTEST_ECDH=
+BT_SELFTEST_SMP=
+BT_DEBUGFS=
 BT_RFCOMM=
 BT_RFCOMM_TTY=
 BT_BNEP=
@@ -75,7 +81,10 @@
 BT_BNEP_PROTO_FILTER=
 BT_CMTP=
 BT_HIDP=
+BT_INTEL=
+BT_BCM=
 BT_HCIBTUSB=
+BT_HCIBTUSB_BCM=
 BT_HCIBTSDIO=
 BT_HCIUART=
 BT_HCIUART_H4=
@@ -83,6 +92,8 @@
 BT_HCIUART_ATH3K=
 BT_HCIUART_LL=
 BT_HCIUART_3WIRE=
+BT_HCIUART_INTEL=
+BT_HCIUART_BCM=
 BT_HCIBCM203X=
 BT_HCIBPA10X=
 BT_HCIBFUSB=
@@ -161,7 +172,6 @@
 WIL6210=
 WIL6210_ISR_COR=
 WIL6210_TRACING=
-WIL6210_PLATFORM_MSM=
 ATH10K=
 ATH10K_PCI=
 ATH10K_DEBUG=
@@ -187,6 +197,7 @@
 B43_PHY_LP=
 B43_PHY_HT=
 B43_PHY_LCN=
+B43_PHY_AC=
 B43_LEDS=
 B43_HWRNG=
 B43_DEBUG=
@@ -344,7 +355,6 @@
 FEALNX=
 NET_NETX=
 ETHOC=
-S6GMAC=
 NET_VENDOR_ATHEROS=
 ATL2=
 ATL1=
@@ -376,6 +386,7 @@
 IGBVF=
 IXGB=
 IXGBE=
+IXGBE_VXLAN=
 IXGBE_HWMON=
 IXGBE_DCA=
 IXGBE_DCB=
@@ -383,6 +394,7 @@
 I40E=
 I40E_VXLAN=
 I40E_DCB=
+I40E_FCOE=
 I40EVF=
 FM10K=
 FM10K_VXLAN=
@@ -457,6 +469,7 @@
 BCMA_HOST_PCI=
 BCMA_DRIVER_PCI_HOSTMODE=
 BCMA_HOST_SOC=
+BCMA_DRIVER_PCI=
 BCMA_DRIVER_MIPS=
 BCMA_SFLASH=
 BCMA_NFLASH=
@@ -487,6 +500,8 @@
 NFC_ST21NFCA_I2C=
 NFC_ST21NFCB=
 NFC_ST21NFCB_I2C=
+NFC_NXP_NCI=
+NFC_NXP_NCI_I2C=
 MEDIA_SUPPORT=
 MEDIA_CAMERA_SUPPORT=
 MEDIA_ANALOG_TV_SUPPORT=
@@ -495,6 +510,7 @@
 MEDIA_SDR_SUPPORT=
 MEDIA_RC_SUPPORT=
 MEDIA_CONTROLLER=
+MEDIA_CONTROLLER_DVB=
 VIDEO_DEV=
 VIDEO_V4L2_SUBDEV_API=
 DVB_CORE=
@@ -564,6 +580,8 @@
 IR_IMG_SONY=
 IR_IMG_SHARP=
 IR_IMG_SANYO=
+IR_IMG_RC5=
+IR_IMG_RC6=
 MEDIA_USB_SUPPORT=
 USB_VIDEO_CLASS=
 USB_VIDEO_CLASS_INPUT_EVDEV=
@@ -608,6 +626,7 @@
 USB_GSPCA_SUNPLUS=
 USB_GSPCA_T613=
 USB_GSPCA_TOPRO=
+USB_GSPCA_TOUPTEK=
 USB_GSPCA_TV8532=
 USB_GSPCA_VC032X=
 USB_GSPCA_VICAM=
@@ -823,6 +842,10 @@
 VIDEO_SAMSUNG_S5P_SDO=
 VIDEO_SAMSUNG_S5P_MIXER=
 VIDEO_SAMSUNG_S5P_MIXER_DEBUG=
+VIDEO_AM437X_VPFE=
+VIDEO_XILINX=
+VIDEO_XILINX_TPG=
+VIDEO_XILINX_VTC=
 VIDEO_VIVID=
 SMS_SDIO_DRV=
 RADIO_ADAPTERS=
@@ -877,7 +900,6 @@
 DVB_FIREDTV_INPUT=
 MEDIA_COMMON_OPTIONS=
 VIDEO_CX2341X=
-VIDEO_BTCX=
 VIDEO_TVEEPROM=
 CYPRESS_FIRMWARE=
 DVB_B2C2_FLEXCOP=
@@ -934,6 +956,7 @@
 VIDEO_THS8200=
 VIDEO_APTINA_PLL=
 VIDEO_SMIAPP_PLL=
+VIDEO_OV2659=
 VIDEO_OV7640=
 VIDEO_OV7670=
 VIDEO_OV9650=
@@ -1005,7 +1028,6 @@
 MEDIA_TUNER_TDA18212=
 MEDIA_TUNER_E4000=
 MEDIA_TUNER_FC2580=
-MEDIA_TUNER_M88TS2022=
 MEDIA_TUNER_M88RS6000T=
 MEDIA_TUNER_TUA9001=
 MEDIA_TUNER_SI2157=
@@ -1084,6 +1106,7 @@
 DVB_BCM3510=
 DVB_LGDT330X=
 DVB_LGDT3305=
+DVB_LGDT3306A=
 DVB_LG2160=
 DVB_S5H1409=
 DVB_AU8522=
@@ -1114,7 +1137,16 @@
 DVB_DUMMY_FE=
 DVB_DRX39XYJ=
 6LOWPAN=
+6LOWPAN_NHC=
+6LOWPAN_NHC_DEST=
+6LOWPAN_NHC_FRAGMENT=
+6LOWPAN_NHC_HOP=
+6LOWPAN_NHC_IPV6=
+6LOWPAN_NHC_MOBILITY=
+6LOWPAN_NHC_ROUTING=
+6LOWPAN_NHC_UDP=
 IEEE802154=
+IEEE802154_SOCKET=
 IEEE802154_6LOWPAN=
 MAC802154=
 IEEE802154_DRIVERS=
diff --git a/MAINTAINERS b/MAINTAINERS
index ddb9ac8..68e96ee 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -34,7 +34,7 @@
 	generalized kernel feature ready for next time.
 
 	PLEASE check your patch with the automated style checker
-	(scripts/checkpatch.pl) to catch trival style violations.
+	(scripts/checkpatch.pl) to catch trivial style violations.
 	See Documentation/CodingStyle for guidance here.
 
 	PLEASE CC: the maintainers and mailing lists that are generated
@@ -270,12 +270,12 @@
 F:	drivers/pnp/pnpacpi/
 F:	include/linux/acpi.h
 F:	include/acpi/
-F:	Documentation/acpi
+F:	Documentation/acpi/
 F:	Documentation/ABI/testing/sysfs-bus-acpi
 F:	drivers/pci/*acpi*
 F:	drivers/pci/*/*acpi*
 F:	drivers/pci/*/*/*acpi*
-F:	tools/power/acpi
+F:	tools/power/acpi/
 
 ACPI COMPONENT ARCHITECTURE (ACPICA)
 M:	Robert Moore <robert.moore@intel.com>
@@ -563,6 +563,18 @@
 L:	linux-alpha@vger.kernel.org
 F:	arch/alpha/
 
+ALTERA MAILBOX DRIVER
+M:	Ley Foon Tan <lftan@altera.com>
+L:	nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
+S:	Maintained
+F:	drivers/mailbox/mailbox-altera.c
+
+ALTERA PIO DRIVER
+M:	Tien Hock Loh <thloh@altera.com>
+L:	linux-gpio@vger.kernel.org
+S:	Maintained
+F:	drivers/gpio/gpio-altera.c
+
 ALTERA TRIPLE SPEED ETHERNET DRIVER
 M:	Vince Bridgers <vbridger@opensource.altera.com>
 L:	netdev@vger.kernel.org
@@ -619,18 +631,19 @@
 F:	include/linux/amd-iommu.h
 
 AMD KFD
-M:      Oded Gabbay <oded.gabbay@amd.com>
-L:      dri-devel@lists.freedesktop.org
-T:      git git://people.freedesktop.org/~gabbayo/linux.git
-S:      Supported
-F:      drivers/gpu/drm/amd/amdkfd/
-F:      drivers/gpu/drm/radeon/radeon_kfd.c
-F:      drivers/gpu/drm/radeon/radeon_kfd.h
-F:      include/uapi/linux/kfd_ioctl.h
+M:	Oded Gabbay <oded.gabbay@amd.com>
+L:	dri-devel@lists.freedesktop.org
+T:	git git://people.freedesktop.org/~gabbayo/linux.git
+S:	Supported
+F:	drivers/gpu/drm/amd/amdkfd/
+F:	drivers/gpu/drm/amd/include/cik_structs.h
+F:	drivers/gpu/drm/amd/include/kgd_kfd_interface.h
+F:	drivers/gpu/drm/radeon/radeon_kfd.c
+F:	drivers/gpu/drm/radeon/radeon_kfd.h
+F:	include/uapi/linux/kfd_ioctl.h
 
 AMD MICROCODE UPDATE SUPPORT
-M:	Andreas Herrmann <herrmann.der.user@googlemail.com>
-L:	amd64-microcode@amd64.org
+M:	Borislav Petkov <bp@alien8.de>
 S:	Maintained
 F:	arch/x86/kernel/cpu/microcode/amd*
 
@@ -659,6 +672,13 @@
 S:	Maintained
 F:	drivers/media/i2c/ad9389b*
 
+ANALOG DEVICES INC ADV7180 DRIVER
+M:	Lars-Peter Clausen <lars@metafoo.de>
+L:	linux-media@vger.kernel.org
+W:	http://ez.analog.com/community/linux-device-drivers
+S:	Supported
+F:	drivers/media/i2c/adv7180.c
+
 ANALOG DEVICES INC ADV7511 DRIVER
 M:	Hans Verkuil <hans.verkuil@cisco.com>
 L:	linux-media@vger.kernel.org
@@ -696,7 +716,7 @@
 W:	http://blackfin.uclinux.org/
 S:	Supported
 F:	sound/soc/blackfin/*
- 
+
 ANALOG DEVICES INC IIO DRIVERS
 M:	Lars-Peter Clausen <lars@metafoo.de>
 M:	Michael Hennerich <Michael.Hennerich@analog.com>
@@ -708,6 +728,16 @@
 F:	drivers/staging/iio/*/ad*
 F:	staging/iio/trigger/iio-trig-bfin-timer.c
 
+ANDROID DRIVERS
+M:	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+M:	Arve Hjønnevåg <arve@android.com>
+M:	Riley Andrews <riandrews@android.com>
+T:	git git://git.kernel.org/pub/scm/linux/kernel/gregkh/staging.git
+L:	devel@driverdev.osuosl.org
+S:	Supported
+F:	drivers/android/
+F:	drivers/staging/android/
+
 AOA (Apple Onboard Audio) ALSA DRIVER
 M:	Johannes Berg <johannes@sipsolutions.net>
 L:	linuxppc-dev@lists.ozlabs.org
@@ -724,15 +754,15 @@
 F:	drivers/char/apm-emulation.c
 
 APPLE BCM5974 MULTITOUCH DRIVER
-M:	Henrik Rydberg <rydberg@euromail.se>
+M:	Henrik Rydberg <rydberg@bitmath.org>
 L:	linux-input@vger.kernel.org
-S:	Maintained
+S:	Odd fixes
 F:	drivers/input/mouse/bcm5974.c
 
 APPLE SMC DRIVER
-M:	Henrik Rydberg <rydberg@euromail.se>
+M:	Henrik Rydberg <rydberg@bitmath.org>
 L:	lm-sensors@lm-sensors.org
-S:	Maintained
+S:	Odd fixes
 F:	drivers/hwmon/applesmc.c
 
 APPLETALK NETWORK LAYER
@@ -754,13 +784,6 @@
 S:	Maintained
 F:	drivers/media/i2c/aptina-pll.*
 
-ARASAN COMPACT FLASH PATA CONTROLLER
-M:	Viresh Kumar <viresh.linux@gmail.com>
-L:	linux-ide@vger.kernel.org
-S:	Maintained
-F:	include/linux/pata_arasan_cf_data.h
-F:	drivers/ata/pata_arasan_cf.c
-
 ARC FRAMEBUFFER DRIVER
 M:	Jaya Kumar <jayalk@intworks.biz>
 S:	Maintained
@@ -863,6 +886,11 @@
 F:	drivers/media/rc/meson-ir.c
 N:	meson[x68]
 
+ARM/Annapurna Labs ALPINE ARCHITECTURE
+M:	Tsahee Zidenberg <tsahee@annapurnalabs.com>
+S:	Maintained
+F:	arch/arm/mach-alpine/
+
 ARM/ATMEL AT91RM9200 AND AT91SAM ARM ARCHITECTURES
 M:	Andrew Victor <linux@maxim.org.za>
 M:	Nicolas Ferre <nicolas.ferre@atmel.com>
@@ -877,6 +905,7 @@
 F:	arch/arm/boot/dts/at91*.dtsi
 F:	arch/arm/boot/dts/sama*.dts
 F:	arch/arm/boot/dts/sama*.dtsi
+F:	arch/arm/include/debug/at91.S
 
 ARM/ATMEL AT91 Clock Support
 M:	Boris Brezillon <boris.brezillon@free-electrons.com>
@@ -934,7 +963,7 @@
 M:	Mathieu Poirier <mathieu.poirier@linaro.org>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
-F:	drivers/coresight/*
+F:	drivers/hwtracing/coresight/*
 F:	Documentation/trace/coresight.txt
 F:	Documentation/devicetree/bindings/arm/coresight.txt
 F:	Documentation/ABI/testing/sysfs-bus-coresight-devices-*
@@ -958,7 +987,7 @@
 F:	arch/arm/mach-prima2/
 F:	drivers/clk/sirf/
 F:	drivers/clocksource/timer-prima2.c
-F:	drivers/clocksource/timer-marco.c
+F:	drivers/clocksource/timer-atlas7.c
 N:	[^a-z]sirf
 
 ARM/EBSA110 MACHINE SUPPORT
@@ -1011,6 +1040,16 @@
 F:	arch/arm/boot/dts/imx*
 F:	arch/arm/configs/imx*_defconfig
 
+ARM/FREESCALE VYBRID ARM ARCHITECTURE
+M:	Shawn Guo <shawn.guo@linaro.org>
+M:	Sascha Hauer <kernel@pengutronix.de>
+R:	Stefan Agner <stefan@agner.ch>
+L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S:	Maintained
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux.git
+F:	arch/arm/mach-imx/*vf610*
+F:	arch/arm/boot/dts/vf*
+
 ARM/GLOMATION GESBC9312SX MACHINE SUPPORT
 M:	Lennert Buytenhek <kernel@wantstofly.org>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -1157,6 +1196,7 @@
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
 F:	arch/arm/mach-mvebu/
+F:	drivers/rtc/rtc-armada38x.c
 
 ARM/Marvell Berlin SoC support
 M:	Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
@@ -1168,6 +1208,7 @@
 M:	Jason Cooper <jason@lakedaemon.net>
 M:	Andrew Lunn <andrew@lunn.ch>
 M:	Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+M:	Gregory Clement <gregory.clement@free-electrons.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
 F:	arch/arm/mach-dove/
@@ -1185,6 +1226,7 @@
 ARM/Mediatek SoC support
 M:	Matthias Brugger <matthias.bgg@gmail.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L:	linux-mediatek@lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
 F:	arch/arm/boot/dts/mt6*
 F:	arch/arm/boot/dts/mt8*
@@ -1224,22 +1266,6 @@
 W:	http://wiki.openmoko.org/wiki/Neo_FreeRunner
 S:	Supported
 
-ARM/QUALCOMM MSM MACHINE SUPPORT
-M:	David Brown <davidb@codeaurora.org>
-M:	Daniel Walker <dwalker@fifo99.com>
-M:	Bryan Huntsman <bryanh@codeaurora.org>
-L:	linux-arm-msm@vger.kernel.org
-F:	arch/arm/mach-msm/
-F:	drivers/video/fbdev/msm/
-F:	drivers/mmc/host/msm_sdcc.c
-F:	drivers/mmc/host/msm_sdcc.h
-F:	drivers/tty/serial/msm_serial.h
-F:	drivers/tty/serial/msm_serial.c
-F:	drivers/*/pm8???-*
-F:	drivers/mfd/ssbi.c
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/davidb/linux-msm.git
-S:	Maintained
-
 ARM/TOSA MACHINE SUPPORT
 M:	Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
 M:	Dirk Opfer <dirk@opfer-online.de>
@@ -1290,10 +1316,18 @@
 
 ARM/QUALCOMM SUPPORT
 M:	Kumar Gala <galak@codeaurora.org>
+M:	Andy Gross <agross@codeaurora.org>
 M:	David Brown <davidb@codeaurora.org>
 L:	linux-arm-msm@vger.kernel.org
+L:	linux-soc@vger.kernel.org
 S:	Maintained
 F:	arch/arm/mach-qcom/
+F:	drivers/soc/qcom/
+F:	drivers/tty/serial/msm_serial.h
+F:	drivers/tty/serial/msm_serial.c
+F:	drivers/*/pm8???-*
+F:	drivers/mfd/ssbi.c
+F:	drivers/firmware/qcom_scm.c
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom.git
 
 ARM/RADISYS ENP2611 MACHINE SUPPORT
@@ -1328,6 +1362,7 @@
 F:	drivers/*/*rockchip*
 F:	drivers/*/*/*rockchip*
 F:	sound/soc/rockchip/
+N:	rockchip
 
 ARM/SAMSUNG EXYNOS ARM ARCHITECTURES
 M:	Kukjin Kim <kgene@kernel.org>
@@ -1391,12 +1426,9 @@
 F:	arch/arm/boot/dts/r7s*
 F:	arch/arm/boot/dts/r8a*
 F:	arch/arm/boot/dts/sh*
-F:	arch/arm/configs/ape6evm_defconfig
 F:	arch/arm/configs/armadillo800eva_defconfig
 F:	arch/arm/configs/bockw_defconfig
 F:	arch/arm/configs/kzm9g_defconfig
-F:	arch/arm/configs/lager_defconfig
-F:	arch/arm/configs/mackerel_defconfig
 F:	arch/arm/configs/marzen_defconfig
 F:	arch/arm/configs/shmobile_defconfig
 F:	arch/arm/include/debug/renesas-scif.S
@@ -1435,6 +1467,8 @@
 F:	drivers/i2c/busses/i2c-st.c
 F:	drivers/media/rc/st_rc.c
 F:	drivers/mmc/host/sdhci-st.c
+F:	drivers/phy/phy-miphy28lp.c
+F:	drivers/phy/phy-miphy365x.c
 F:	drivers/phy/phy-stih407-usb.c
 F:	drivers/phy/phy-stih41x-usb.c
 F:	drivers/pinctrl/pinctrl-st.c
@@ -1583,12 +1617,14 @@
 F:	drivers/clocksource/cadence_ttc_timer.c
 F:	drivers/i2c/busses/i2c-cadence.c
 F:	drivers/mmc/host/sdhci-of-arasan.c
+F:	drivers/edac/synopsys_edac.c
 
 ARM SMMU DRIVER
 M:	Will Deacon <will.deacon@arm.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
 F:	drivers/iommu/arm-smmu.c
+F:	drivers/iommu/io-pgtable-arm.c
 
 ARM64 PORT (AARCH64 ARCHITECTURE)
 M:	Catalin Marinas <catalin.marinas@arm.com>
@@ -1640,8 +1676,8 @@
 F:	include/linux/platform_data/at24.h
 
 ATA OVER ETHERNET (AOE) DRIVER
-M:	"Ed L. Cashin" <ecashin@coraid.com>
-W:	http://support.coraid.com/support/linux
+M:	"Ed L. Cashin" <ed.cashin@acm.org>
+W:	http://www.openaoe.org/
 S:	Supported
 F:	Documentation/aoe/
 F:	drivers/block/aoe/
@@ -1657,7 +1693,6 @@
 M:	Nick Kossifidis <mickflemm@gmail.com>
 M:	"Luis R. Rodriguez" <mcgrof@do-not-panic.com>
 L:	linux-wireless@vger.kernel.org
-L:	ath5k-devel@lists.ath5k.org
 W:	http://wireless.kernel.org/en/users/Drivers/ath5k
 S:	Maintained
 F:	drivers/net/wireless/ath/ath5k/
@@ -1707,7 +1742,7 @@
 F:	drivers/net/ethernet/atheros/
 
 ATM
-M:	Chas Williams <chas@cmf.nrl.navy.mil>
+M:	Chas Williams <3chas3@gmail.com>
 L:	linux-atm-general@lists.sourceforge.net (moderated for non-subscribers)
 L:	netdev@vger.kernel.org
 W:	http://linux-atm.sourceforge.net
@@ -1728,7 +1763,7 @@
 F:	drivers/tty/serial/atmel_serial.c
 
 ATMEL Audio ALSA driver
-M:	Bo Shen <voice.shen@atmel.com>
+M:	Nicolas Ferre <nicolas.ferre@atmel.com>
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
 S:	Supported
 F:	sound/soc/atmel
@@ -1785,7 +1820,7 @@
 F:	drivers/spi/spi-atmel.*
 
 ATMEL SSC DRIVER
-M:	Bo Shen <voice.shen@atmel.com>
+M:	Nicolas Ferre <nicolas.ferre@atmel.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:	Supported
 F:	drivers/misc/atmel-ssc.c
@@ -1879,16 +1914,14 @@
 F:	drivers/media/radio/radio-aztech*
 
 B43 WIRELESS DRIVER
-M:	Stefano Brivio <stefano.brivio@polimi.it>
 L:	linux-wireless@vger.kernel.org
 L:	b43-dev@lists.infradead.org
 W:	http://wireless.kernel.org/en/users/Drivers/b43
-S:	Maintained
+S:	Odd Fixes
 F:	drivers/net/wireless/b43/
 
 B43LEGACY WIRELESS DRIVER
 M:	Larry Finger <Larry.Finger@lwfinger.net>
-M:	Stefano Brivio <stefano.brivio@polimi.it>
 L:	linux-wireless@vger.kernel.org
 L:	b43-dev@lists.infradead.org
 W:	http://wireless.kernel.org/en/users/Drivers/b43
@@ -1931,10 +1964,10 @@
 F:	fs/befs/
 
 BECKHOFF CX5020 ETHERCAT MASTER DRIVER
-M: Dariusz Marcinkiewicz <reksio@newterm.pl>
-L: netdev@vger.kernel.org
-S: Maintained
-F: drivers/net/ethernet/ec_bhf.c
+M:	Dariusz Marcinkiewicz <reksio@newterm.pl>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/net/ethernet/ec_bhf.c
 
 BFS FILE SYSTEM
 M:	"Tigran A. Aivazian" <tigran@aivazian.fsnet.co.uk>
@@ -2042,7 +2075,7 @@
 BONDING DRIVER
 M:	Jay Vosburgh <j.vosburgh@gmail.com>
 M:	Veaceslav Falico <vfalico@gmail.com>
-M:	Andy Gospodarek <andy@greyhouse.net>
+M:	Andy Gospodarek <gospo@cumulusnetworks.com>
 L:	netdev@vger.kernel.org
 W:	http://sourceforge.net/projects/bonding/
 S:	Supported
@@ -2084,7 +2117,6 @@
 
 BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITECTURE
 M:	Christian Daudt <bcm@fixthebug.org>
-M:	Matt Porter <mporter@linaro.org>
 M:	Florian Fainelli <f.fainelli@gmail.com>
 L:	bcm-kernel-feedback-list@broadcom.com
 T:	git git://github.com/broadcom/mach-bcm
@@ -2124,7 +2156,7 @@
 BROADCOM BCM63XX ARM ARCHITECTURE
 M:	Florian Fainelli <f.fainelli@gmail.com>
 L:	linux-arm-kernel@lists.infradead.org
-T:	git git://git.github.com/brcm/linux.git
+T:	git git://github.com/broadcom/arm-bcm63xx.git
 S:	Maintained
 F:	arch/arm/mach-bcm/bcm63xx.c
 F:	arch/arm/include/debug/bcm63xx.S
@@ -2141,6 +2173,7 @@
 M:	Gregory Fong <gregory.0xf0@gmail.com>
 M:	Florian Fainelli <f.fainelli@gmail.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T:	git git://github.com/broadcom/stblinux.git
 S:	Maintained
 F:	arch/arm/mach-bcm/*brcmstb*
 F:	arch/arm/boot/dts/bcm7*.dts*
@@ -2150,6 +2183,7 @@
 M:	Kevin Cernekee <cernekee@gmail.com>
 M:	Florian Fainelli <f.fainelli@gmail.com>
 L:	linux-mips@linux-mips.org
+T:	git git://github.com/broadcom/stblinux.git
 S:	Maintained
 F:	arch/mips/bmips/*
 F:	arch/mips/include/asm/mach-bmips/*
@@ -2192,7 +2226,7 @@
 M:	Scott Branden <sbranden@broadcom.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 L:	bcm-kernel-feedback-list@broadcom.com
-T:	git git://git.github.com/brcm/linux.git
+T:	git git://github.com/broadcom/cygnus-linux.git
 S:	Maintained
 N:	iproc
 N:	cygnus
@@ -2259,6 +2293,7 @@
 BTRFS FILE SYSTEM
 M:	Chris Mason <clm@fb.com>
 M:	Josef Bacik <jbacik@fb.com>
+M:	David Sterba <dsterba@suse.cz>
 L:	linux-btrfs@vger.kernel.org
 W:	http://btrfs.wiki.kernel.org/
 Q:	http://patchwork.kernel.org/project/linux-btrfs/list/
@@ -2343,9 +2378,11 @@
 
 CAN NETWORK LAYER
 M:	Oliver Hartkopp <socketcan@hartkopp.net>
+M:	Marc Kleine-Budde <mkl@pengutronix.de>
 L:	linux-can@vger.kernel.org
-W:	http://gitorious.org/linux-can
-T:	git git://gitorious.org/linux-can/linux-can-next.git
+W:	https://github.com/linux-can
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can.git
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
 S:	Maintained
 F:	Documentation/networking/can.txt
 F:	net/can/
@@ -2359,8 +2396,9 @@
 M:	Wolfgang Grandegger <wg@grandegger.com>
 M:	Marc Kleine-Budde <mkl@pengutronix.de>
 L:	linux-can@vger.kernel.org
-W:	http://gitorious.org/linux-can
-T:	git git://gitorious.org/linux-can/linux-can-next.git
+W:	https://github.com/linux-can
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can.git
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
 S:	Maintained
 F:	drivers/net/can/
 F:	include/linux/can/dev.h
@@ -2378,6 +2416,12 @@
 F:	security/commoncap.c
 F:	kernel/capability.c
 
+CAPELLA MICROSYSTEMS LIGHT SENSOR DRIVER
+M:	Kevin Tsai <ktsai@capellamicro.com>
+S:	Maintained
+F:	drivers/iio/light/cm*
+F:	Documentation/devicetree/bindings/i2c/trivial-devices.txt
+
 CC2520 IEEE-802.15.4 RADIO DRIVER
 M:	Varka Bhadram <varkabhadram@gmail.com>
 L:	linux-wpan@vger.kernel.org
@@ -2399,7 +2443,8 @@
 F:	arch/powerpc/platforms/cell/
 
 CEPH DISTRIBUTED FILE SYSTEM CLIENT
-M:	Sage Weil <sage@inktank.com>
+M:	Yan, Zheng <zyan@redhat.com>
+M:	Sage Weil <sage@redhat.com>
 L:	ceph-devel@vger.kernel.org
 W:	http://ceph.com/
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git
@@ -2472,11 +2517,18 @@
 
 CHIPIDEA USB HIGH SPEED DUAL ROLE CONTROLLER
 M:	Peter Chen <Peter.Chen@freescale.com>
-T:	git git://github.com/hzpeterchen/linux-usb.git
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
 L:	linux-usb@vger.kernel.org
 S:	Maintained
 F:	drivers/usb/chipidea/
 
+CHIPONE ICN8318 I2C TOUCHSCREEN DRIVER
+M:	Hans de Goede <hdegoede@redhat.com>
+L:	linux-input@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt
+F:	drivers/input/touchscreen/chipone_icn8318.c
+
 CHROME HARDWARE PLATFORM SUPPORT
 M:	Olof Johansson <olof@lixom.net>
 S:	Maintained
@@ -2517,6 +2569,7 @@
 
 CLK API
 M:	Russell King <linux@arm.linux.org.uk>
+L:	linux-clk@vger.kernel.org
 S:	Maintained
 F:	include/linux/clk.h
 
@@ -2577,7 +2630,7 @@
 COMMON CLK FRAMEWORK
 M:	Mike Turquette <mturquette@linaro.org>
 M:	Stephen Boyd <sboyd@codeaurora.org>
-L:	linux-kernel@vger.kernel.org
+L:	linux-clk@vger.kernel.org
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git
 S:	Maintained
 F:	drivers/clk/
@@ -2770,6 +2823,7 @@
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6.git
 S:	Maintained
 F:	Documentation/crypto/
+F:	Documentation/DocBook/crypto-API.tmpl
 F:	arch/*/crypto/
 F:	crypto/
 F:	drivers/crypto/
@@ -2840,11 +2894,11 @@
 F:	drivers/net/ethernet/chelsio/cxgb3/
 
 CXGB3 ISCSI DRIVER (CXGB3I)
-M:      Karen Xie <kxie@chelsio.com>
-L:      linux-scsi@vger.kernel.org
-W:      http://www.chelsio.com
-S:      Supported
-F:      drivers/scsi/cxgbi/cxgb3i
+M:	Karen Xie <kxie@chelsio.com>
+L:	linux-scsi@vger.kernel.org
+W:	http://www.chelsio.com
+S:	Supported
+F:	drivers/scsi/cxgbi/cxgb3i
 
 CXGB3 IWARP RNIC DRIVER (IW_CXGB3)
 M:	Steve Wise <swise@chelsio.com>
@@ -2861,11 +2915,11 @@
 F:	drivers/net/ethernet/chelsio/cxgb4/
 
 CXGB4 ISCSI DRIVER (CXGB4I)
-M:      Karen Xie <kxie@chelsio.com>
-L:      linux-scsi@vger.kernel.org
-W:      http://www.chelsio.com
-S:      Supported
-F:      drivers/scsi/cxgbi/cxgb4i
+M:	Karen Xie <kxie@chelsio.com>
+L:	linux-scsi@vger.kernel.org
+W:	http://www.chelsio.com
+S:	Supported
+F:	drivers/scsi/cxgbi/cxgb4i
 
 CXGB4 IWARP RNIC DRIVER (IW_CXGB4)
 M:	Steve Wise <swise@chelsio.com>
@@ -2936,6 +2990,12 @@
 F:	drivers/input/touchscreen/cyttsp*
 F:	include/linux/input/cyttsp.h
 
+DALLAS/MAXIM DS1685-FAMILY REAL TIME CLOCK
+M:	Joshua Kinard <kumba@gentoo.org>
+S:	Maintained
+F:	drivers/rtc/rtc-ds1685.c
+F:	include/linux/rtc/ds1685.h
+
 DAMA SLAVE for AX.25
 M:	Joerg Reuter <jreuter@yaina.de>
 W:	http://yaina.de/jreuter/
@@ -3006,12 +3066,19 @@
 
 DELL LAPTOP DRIVER
 M:	Matthew Garrett <mjg59@srcf.ucam.org>
+M:	Pali Rohár <pali.rohar@gmail.com>
 L:	platform-driver-x86@vger.kernel.org
 S:	Maintained
 F:	drivers/platform/x86/dell-laptop.c
 
+DELL LAPTOP FREEFALL DRIVER
+M:	Pali Rohár <pali.rohar@gmail.com>
+S:	Maintained
+F:	drivers/platform/x86/dell-smo8800.c
+
 DELL LAPTOP SMM DRIVER
 M:	Guenter Roeck <linux@roeck-us.net>
+S:	Maintained
 F:	drivers/char/i8k.c
 F:	include/uapi/linux/i8k.h
 
@@ -3023,11 +3090,12 @@
 
 DELL WMI EXTRAS DRIVER
 M:	Matthew Garrett <mjg59@srcf.ucam.org>
+M:	Pali Rohár <pali.rohar@gmail.com>
 S:	Maintained
 F:	drivers/platform/x86/dell-wmi.c
 
 DESIGNWARE USB2 DRD IP DRIVER
-M:	Paul Zimmerman <paulz@synopsys.com>
+M:	John Youn <johnyoun@synopsys.com>
 L:	linux-usb@vger.kernel.org
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
 S:	Maintained
@@ -3084,12 +3152,15 @@
 F:	Documentation/hwmon/da90??
 F:	drivers/gpio/gpio-da90??.c
 F:	drivers/hwmon/da90??-hwmon.c
+F:	drivers/iio/adc/da91??-*.c
 F:	drivers/input/misc/da90??_onkey.c
 F:	drivers/input/touchscreen/da9052_tsi.c
 F:	drivers/leds/leds-da90??.c
 F:	drivers/mfd/da903x.c
 F:	drivers/mfd/da90??-*.c
+F:	drivers/mfd/da91??-*.c
 F:	drivers/power/da9052-battery.c
+F:	drivers/power/da91??-*.c
 F:	drivers/regulator/da903x.c
 F:	drivers/regulator/da9???-regulator.[ch]
 F:	drivers/rtc/rtc-da90??.c
@@ -3099,6 +3170,7 @@
 F:	include/linux/mfd/da9052/
 F:	include/linux/mfd/da9055/
 F:	include/linux/mfd/da9063/
+F:	include/linux/mfd/da9150/
 F:	include/sound/da[79]*.h
 F:	sound/soc/codecs/da[79]*.[ch]
 
@@ -3123,6 +3195,12 @@
 S:	Maintained
 F:	drivers/i2c/busses/i2c-diolan-u2c.c
 
+DIRECT ACCESS (DAX)
+M:	Matthew Wilcox <willy@linux.intel.com>
+L:	linux-fsdevel@vger.kernel.org
+S:	Supported
+F:	fs/dax.c
+
 DIRECTORY NOTIFICATION (DNOTIFY)
 M:	Eric Paris <eparis@parisplace.org>
 S:	Maintained
@@ -3182,7 +3260,7 @@
 Q:	https://patchwork.kernel.org/project/linux-dmaengine/list/
 S:	Maintained
 F:	drivers/dma/
-F:	include/linux/dma*
+F:	include/linux/dmaengine.h
 F:	Documentation/dmaengine/
 T:	git git://git.infradead.org/users/vkoul/slave-dma.git
 
@@ -3193,11 +3271,12 @@
 F:	Documentation/hwmon/dme1737
 F:	drivers/hwmon/dme1737.c
 
-DOCKING STATION DRIVER
-M:	Shaohua Li <shaohua.li@intel.com>
-L:	linux-acpi@vger.kernel.org
-S:	Supported
-F:	drivers/acpi/dock.c
+DMI/SMBIOS SUPPORT
+M:	Jean Delvare <jdelvare@suse.de>
+S:	Maintained
+F:	drivers/firmware/dmi-id.c
+F:	drivers/firmware/dmi_scan.c
+F:	include/linux/dmi.h
 
 DOCUMENTATION
 M:	Jonathan Corbet <corbet@lwn.net>
@@ -3206,7 +3285,10 @@
 F:	Documentation/
 X:	Documentation/ABI/
 X:	Documentation/devicetree/
-X:	Documentation/[a-z][a-z]_[A-Z][A-Z]/
+X:	Documentation/acpi
+X:	Documentation/power
+X:	Documentation/spi
+T:	git git://git.lwn.net/linux-2.6.git docs-next
 
 DOUBLETALK DRIVER
 M:	"James R. Van Zandt" <jrv@vanzandt.mv.com>
@@ -3329,7 +3411,6 @@
 S:	Supported
 F:	drivers/gpu/drm/rcar-du/
 F:	drivers/gpu/drm/shmobile/
-F:	include/linux/platform_data/rcar-du.h
 F:	include/linux/platform_data/shmob_drm.h
 
 DSBR100 USB FM RADIO DRIVER
@@ -3466,6 +3547,14 @@
 S:	Maintained
 F:	drivers/tty/serial/dz.*
 
+E3X0 POWER BUTTON DRIVER
+M:	Moritz Fischer <moritz.fischer@ettus.com>
+L:	usrp-users@lists.ettus.com
+W:	http://www.ettus.com
+S:	Supported
+F:	drivers/input/misc/e3x0-button.c
+F:	Documentation/devicetree/bindings/input/e3x0-button.txt
+
 E4000 MEDIA DRIVER
 M:	Antti Palosaari <crope@iki.fi>
 L:	linux-media@vger.kernel.org
@@ -3507,6 +3596,8 @@
 M:	Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:	linux-edac@vger.kernel.org
 W:	bluesmoke.sourceforge.net
+T:	git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git#for-next
+T:	git://git.kernel.org/pub/linux/kernel/git/mchehab/linux-edac.git#linux_next
 S:	Supported
 F:	Documentation/edac.txt
 F:	drivers/edac/
@@ -3871,8 +3962,14 @@
 F:	Documentation/fault-injection/
 F:	lib/fault-inject.c
 
+FBTFT Framebuffer drivers
+M:	Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+M:	Noralf Trønnes <noralf@tronnes.org>
+S:	Maintained
+F:	drivers/staging/fbtft/
+
 FCOE SUBSYSTEM (libfc, libfcoe, fcoe)
-M:	Robert Love <robert.w.love@intel.com>
+M:	Vasu Dev <vasu.dev@intel.com>
 L:	fcoe-devel@open-fcoe.org
 W:	www.Open-FCoE.org
 S:	Supported
@@ -4028,6 +4125,12 @@
 F:	include/linux/platform_data/video-imxfb.h
 F:	drivers/video/fbdev/imxfb.c
 
+FREESCALE QUAD SPI DRIVER
+M:	Han Xu <han.xu@freescale.com>
+L:	linux-mtd@lists.infradead.org
+S:	Maintained
+F:	drivers/mtd/spi-nor/fsl-quadspi.c
+
 FREESCALE SOC FS_ENET DRIVER
 M:	Pantelis Antoniou <pantelis.antoniou@gmail.com>
 M:	Vitaly Bordug <vbordug@ru.mvista.com>
@@ -4074,6 +4177,12 @@
 F:	sound/soc/fsl/imx*
 F:	sound/soc/fsl/mpc8610_hpcd.c
 
+FREESCALE QORIQ MANAGEMENT COMPLEX DRIVER
+M:	J. German Rivera <German.Rivera@freescale.com>
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+F:	drivers/staging/fsl-mc/
+
 FREEVXFS FILESYSTEM
 M:	Christoph Hellwig <hch@infradead.org>
 W:	ftp://ftp.openlinux.org/pub/people/hch/vxfs
@@ -4168,6 +4277,11 @@
 S:	Supported
 F:	drivers/scsi/gdt*
 
+GDB KERNEL DEBUGGING HELPER SCRIPTS
+M:	Jan Kiszka <jan.kiszka@siemens.com>
+S:	Supported
+F:	scripts/gdb/
+
 GEMTEK FM RADIO RECEIVER DRIVER
 M:	Hans Verkuil <hverkuil@xs4all.nl>
 L:	linux-media@vger.kernel.org
@@ -4218,6 +4332,15 @@
 F:	drivers/phy/
 F:	include/linux/phy/
 
+GENERIC PM DOMAINS
+M:	"Rafael J. Wysocki" <rjw@rjwysocki.net>
+M:	Kevin Hilman <khilman@kernel.org>
+M:	Ulf Hansson <ulf.hansson@linaro.org>
+L:	linux-pm@vger.kernel.org
+S:	Supported
+F:	drivers/base/power/domain*.c
+F:	include/linux/pm_domain.h
+
 GENERIC UIO DRIVER FOR PCI DEVICES
 M:	"Michael S. Tsirkin" <mst@redhat.com>
 L:	kvm@vger.kernel.org
@@ -4231,10 +4354,10 @@
 
 GFS2 FILE SYSTEM
 M:	Steven Whitehouse <swhiteho@redhat.com>
+M:	Bob Peterson <rpeterso@redhat.com>
 L:	cluster-devel@redhat.com
 W:	http://sources.redhat.com/cluster/
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/steve/gfs2-3.0-fixes.git
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/steve/gfs2-3.0-nmw.git
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2.git
 S:	Supported
 F:	Documentation/filesystems/gfs2*.txt
 F:	fs/gfs2/
@@ -4344,7 +4467,7 @@
 F:	block/partitions/efi.*
 
 STK1160 USB VIDEO CAPTURE DRIVER
-M:	Ezequiel Garcia <elezegarcia@gmail.com>
+M:	Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
 L:	linux-media@vger.kernel.org
 T:	git git://linuxtv.org/media_tree.git
 S:	Maintained
@@ -4402,6 +4525,7 @@
 HARDWARE RANDOM NUMBER GENERATOR CORE
 M:	Matt Mackall <mpm@selenic.com>
 M:	Herbert Xu <herbert@gondor.apana.org.au>
+L:	linux-crypto@vger.kernel.org
 S:	Odd fixes
 F:	Documentation/hw_random.txt
 F:	drivers/char/hw_random/
@@ -4748,20 +4872,20 @@
 F:	drivers/scsi/ipr.*
 
 IBM Power Virtual Ethernet Device Driver
-M:	Santiago Leon <santil@linux.vnet.ibm.com>
+M:	Thomas Falcon <tlfalcon@linux.vnet.ibm.com>
 L:	netdev@vger.kernel.org
 S:	Supported
 F:	drivers/net/ethernet/ibm/ibmveth.*
 
 IBM Power Virtual SCSI Device Drivers
-M:	Nathan Fontenot <nfont@linux.vnet.ibm.com>
+M:	Tyrel Datwyler <tyreld@linux.vnet.ibm.com>
 L:	linux-scsi@vger.kernel.org
 S:	Supported
 F:	drivers/scsi/ibmvscsi/ibmvscsi*
 F:	drivers/scsi/ibmvscsi/viosrp.h
 
 IBM Power Virtual FC Device Drivers
-M:	Brian King <brking@linux.vnet.ibm.com>
+M:	Tyrel Datwyler <tyreld@linux.vnet.ibm.com>
 L:	linux-scsi@vger.kernel.org
 S:	Supported
 F:	drivers/scsi/ibmvscsi/ibmvfc*
@@ -4853,6 +4977,7 @@
 F:	drivers/iio/
 F:	drivers/staging/iio/
 F:	include/linux/iio/
+F:	tools/iio/
 
 IKANOS/ADI EAGLE ADSL USB DRIVER
 M:	Matthieu Castet <castet.matthieu@free.fr>
@@ -4885,9 +5010,14 @@
 S:	Maintained
 F:	drivers/ipack/
 
+INGENIC JZ4780 DMA Driver
+M:	Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com>
+S:	Maintained
+F:	drivers/dma/dma-jz4780.c
+
 INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
 M:	Mimi Zohar <zohar@linux.vnet.ibm.com>
-M:	Dmitry Kasatkin <d.kasatkin@samsung.com>
+M:	Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
 L:	linux-ima-devel@lists.sourceforge.net
 L:	linux-ima-user@lists.sourceforge.net
 L:	linux-security-module@vger.kernel.org
@@ -4929,7 +5059,6 @@
 
 INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN) DRIVERS
 M:	Dmitry Torokhov <dmitry.torokhov@gmail.com>
-M:	Dmitry Torokhov <dtor@mail.ru>
 L:	linux-input@vger.kernel.org
 Q:	http://patchwork.kernel.org/project/linux-input/list/
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git
@@ -4940,18 +5069,27 @@
 F:	include/linux/input/
 
 INPUT MULTITOUCH (MT) PROTOCOL
-M:	Henrik Rydberg <rydberg@euromail.se>
+M:	Henrik Rydberg <rydberg@bitmath.org>
 L:	linux-input@vger.kernel.org
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/rydberg/input-mt.git
-S:	Maintained
+S:	Odd fixes
 F:	Documentation/input/multi-touch-protocol.txt
 F:	drivers/input/input-mt.c
 K:	\b(ABS|SYN)_MT_
 
+INTEL ASoC BDW/HSW DRIVERS
+M:	Jie Yang <yang.jie@linux.intel.com>
+L:	alsa-devel@alsa-project.org
+S:	Supported
+F:	sound/soc/intel/sst-haswell*
+F:	sound/soc/intel/sst-dsp*
+F:	sound/soc/intel/sst-firmware.c
+F:	sound/soc/intel/broadwell.c
+F:	sound/soc/intel/haswell.c
+
 INTEL C600 SERIES SAS CONTROLLER DRIVER
 M:	Intel SCU Linux support <intel-linux-scu@intel.com>
 M:	Artur Paszkiewicz <artur.paszkiewicz@intel.com>
-M:	Dave Jiang <dave.jiang@intel.com>
 L:	linux-scsi@vger.kernel.org
 T:	git git://git.code.sf.net/p/intel-sas/isci
 S:	Supported
@@ -4991,7 +5129,7 @@
 F:	drivers/platform/x86/intel_menlow.c
 
 INTEL IA32 MICROCODE UPDATE SUPPORT
-M:	Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
+M:	Borislav Petkov <bp@alien8.de>
 S:	Maintained
 F:	arch/x86/kernel/cpu/microcode/core*
 F:	arch/x86/kernel/cpu/microcode/intel*
@@ -5032,22 +5170,21 @@
 S:	Maintained
 F:	drivers/char/hw_random/ixp4xx-rng.c
 
-INTEL ETHERNET DRIVERS (e100/e1000/e1000e/fm10k/igb/igbvf/ixgb/ixgbe/ixgbevf/i40e/i40evf)
+INTEL ETHERNET DRIVERS
 M:	Jeff Kirsher <jeffrey.t.kirsher@intel.com>
-M:	Jesse Brandeburg <jesse.brandeburg@intel.com>
-M:	Bruce Allan <bruce.w.allan@intel.com>
-M:	Carolyn Wyborny <carolyn.wyborny@intel.com>
-M:	Don Skidmore <donald.c.skidmore@intel.com>
-M:	Greg Rose <gregory.v.rose@intel.com>
-M:	Matthew Vick <matthew.vick@intel.com>
-M:	John Ronciak <john.ronciak@intel.com>
-M:	Mitch Williams <mitch.a.williams@intel.com>
-M:	Linux NICS <linux.nics@intel.com>
-L:	e1000-devel@lists.sourceforge.net
+R:	Jesse Brandeburg <jesse.brandeburg@intel.com>
+R:	Shannon Nelson <shannon.nelson@intel.com>
+R:	Carolyn Wyborny <carolyn.wyborny@intel.com>
+R:	Don Skidmore <donald.c.skidmore@intel.com>
+R:	Matthew Vick <matthew.vick@intel.com>
+R:	John Ronciak <john.ronciak@intel.com>
+R:	Mitch Williams <mitch.a.williams@intel.com>
+L:	intel-wired-lan@lists.osuosl.org
 W:	http://www.intel.com/support/feedback.htm
 W:	http://e1000.sourceforge.net/
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net.git
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-next.git
+Q:	http://patchwork.ozlabs.org/project/intel-wired-lan/list/
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-queue.git
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue.git
 S:	Supported
 F:	Documentation/networking/e100.txt
 F:	Documentation/networking/e1000.txt
@@ -5091,7 +5228,7 @@
 INTEL WIRELESS WIMAX CONNECTION 2400
 M:	Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
 M:	linux-wimax@intel.com
-L:     wimax@linuxwimax.org (subscribers-only)
+L:	wimax@linuxwimax.org (subscribers-only)
 S:	Supported
 W:	http://linuxwimax.org
 F:	Documentation/wimax/README.i2400m
@@ -5169,6 +5306,13 @@
 F:	include/linux/ipmi*
 F:	include/uapi/linux/ipmi*
 
+QCOM AUDIO (ASoC) DRIVERS
+M:	Patrick Lai <plai@codeaurora.org>
+M:	Banajit Goswami <bgoswami@codeaurora.org>
+L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
+S:	Supported
+F:	sound/soc/qcom/
+
 IPS SCSI RAID DRIVER
 M:	Adaptec OEM Raid Solutions <aacraid@adaptec.com>
 L:	linux-scsi@vger.kernel.org
@@ -5279,6 +5423,15 @@
 Q:	http://patchwork.kernel.org/project/linux-rdma/list/
 F:	drivers/infiniband/ulp/iser/
 
+ISCSI EXTENSIONS FOR RDMA (ISER) TARGET
+M:	Sagi Grimberg <sagig@mellanox.com>
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending.git master
+L:	linux-rdma@vger.kernel.org
+L:	target-devel@vger.kernel.org
+S:	Supported
+W:	http://www.linux-iscsi.org
+F:	drivers/infiniband/ulp/isert
+
 ISDN SUBSYSTEM
 M:	Karsten Keil <isdn@linux-pingi.de>
 L:	isdn4linux@listserv.isdn4linux.de (subscribers-only)
@@ -5409,6 +5562,19 @@
 F:	Documentation/kbuild/kconfig-language.txt
 F:	scripts/kconfig/
 
+KDBUS
+M:	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+M:	Daniel Mack <daniel@zonque.org>
+M:	David Herrmann <dh.herrmann@googlemail.com>
+M:	Djalal Harouni <tixxdz@opendz.org>
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+F:	ipc/kdbus/*
+F:	samples/kdbus/*
+F:	Documentation/kdbus/*
+F:	include/uapi/linux/kdbus.h
+F:	tools/testing/selftests/kdbus/
+
 KDUMP
 M:	Vivek Goyal <vgoyal@redhat.com>
 M:	Haren Myneni <hbabu@us.ibm.com>
@@ -5480,6 +5646,8 @@
 F:	Documentation/*/kvm*.txt
 F:	Documentation/virtual/kvm/
 F:	arch/*/kvm/
+F:	arch/x86/kernel/kvm.c
+F:	arch/x86/kernel/kvmclock.c
 F:	arch/*/include/asm/kvm*
 F:	include/linux/kvm*
 F:	include/uapi/linux/kvm*
@@ -5693,6 +5861,49 @@
 F:	include/linux/lguest*.h
 F:	tools/lguest/
 
+LIBATA SUBSYSTEM (Serial and Parallel ATA drivers)
+M:	Tejun Heo <tj@kernel.org>
+L:	linux-ide@vger.kernel.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
+S:	Maintained
+F:	drivers/ata/
+F:	include/linux/ata.h
+F:	include/linux/libata.h
+
+LIBATA PATA ARASAN COMPACT FLASH CONTROLLER
+M:	Viresh Kumar <viresh.linux@gmail.com>
+L:	linux-ide@vger.kernel.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
+S:	Maintained
+F:	include/linux/pata_arasan_cf_data.h
+F:	drivers/ata/pata_arasan_cf.c
+
+LIBATA PATA DRIVERS
+M:	Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+M:	Tejun Heo <tj@kernel.org>
+L:	linux-ide@vger.kernel.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
+S:	Maintained
+F:	drivers/ata/pata_*.c
+F:	drivers/ata/ata_generic.c
+
+LIBATA SATA AHCI PLATFORM devices support
+M:	Hans de Goede <hdegoede@redhat.com>
+M:	Tejun Heo <tj@kernel.org>
+L:	linux-ide@vger.kernel.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
+S:	Maintained
+F:	drivers/ata/ahci_platform.c
+F:	drivers/ata/libahci_platform.c
+F:	include/linux/ahci_platform.h
+
+LIBATA SATA PROMISE TX2/TX4 CONTROLLER DRIVER
+M:	Mikael Pettersson <mikpelinux@gmail.com>
+L:	linux-ide@vger.kernel.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
+S:	Maintained
+F:	drivers/ata/sata_promise.*
+
 LIBLOCKDEP
 M:	Sasha Levin <sasha.levin@oracle.com>
 S:	Maintained
@@ -5733,7 +5944,7 @@
 F:	arch/powerpc/platforms/52xx/
 
 LINUX FOR POWERPC EMBEDDED PPC4XX
-M:  Alistair Popple <alistair@popple.id.au>
+M:	Alistair Popple <alistair@popple.id.au>
 M:	Matt Porter <mporter@kernel.crashing.org>
 W:	http://www.penguinppc.org/
 L:	linuxppc-dev@lists.ozlabs.org
@@ -5784,6 +5995,21 @@
 F:	drivers/misc/lis3lv02d/
 F:	drivers/platform/x86/hp_accel.c
 
+LIVE PATCHING
+M:	Josh Poimboeuf <jpoimboe@redhat.com>
+M:	Seth Jennings <sjenning@redhat.com>
+M:	Jiri Kosina <jkosina@suse.cz>
+M:	Vojtech Pavlik <vojtech@suse.cz>
+S:	Maintained
+F:	kernel/livepatch/
+F:	include/linux/livepatch.h
+F:	arch/x86/include/asm/livepatch.h
+F:	arch/x86/kernel/livepatch.c
+F:	Documentation/ABI/testing/sysfs-kernel-livepatch
+F:	samples/livepatch/
+L:	live-patching@vger.kernel.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching.git
+
 LLC (802.2)
 M:	Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
 S:	Maintained
@@ -5951,16 +6177,6 @@
 S:	Maintained
 F:	drivers/media/dvb-frontends/m88rs2000*
 
-M88TS2022 MEDIA DRIVER
-M:	Antti Palosaari <crope@iki.fi>
-L:	linux-media@vger.kernel.org
-W:	http://linuxtv.org/
-W:	http://palosaari.fi/linux/
-Q:	http://patchwork.linuxtv.org/project/linux-media/list/
-T:	git git://linuxtv.org/anttip/media_tree.git
-S:	Maintained
-F:	drivers/media/tuners/m88ts2022*
-
 MA901 MASTERKIT USB FM RADIO DRIVER
 M:	Alexey Klimov <klimov.linux@gmail.com>
 L:	linux-media@vger.kernel.org
@@ -6082,6 +6298,33 @@
 F:	drivers/hwmon/max6697.c
 F:	include/linux/platform_data/max6697.h
 
+MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS
+M:	Krzysztof Kozlowski <k.kozlowski@samsung.com>
+L:	linux-pm@vger.kernel.org
+S:	Supported
+F:	drivers/power/max14577_charger.c
+F:	drivers/power/max77693_charger.c
+
+MAXIM PMIC AND MUIC DRIVERS FOR EXYNOS BASED BOARDS
+M:	Chanwoo Choi <cw00.choi@samsung.com>
+M:	Krzysztof Kozlowski <k.kozlowski@samsung.com>
+L:	linux-kernel@vger.kernel.org
+S:	Supported
+F:	drivers/*/max14577.c
+F:	drivers/*/max77686.c
+F:	drivers/*/max77693.c
+F:	drivers/extcon/extcon-max14577.c
+F:	drivers/extcon/extcon-max77693.c
+F:	drivers/rtc/rtc-max77686.c
+F:	drivers/clk/clk-max77686.c
+F:	Documentation/devicetree/bindings/mfd/max14577.txt
+F:	Documentation/devicetree/bindings/mfd/max77686.txt
+F:	Documentation/devicetree/bindings/mfd/max77693.txt
+F:	Documentation/devicetree/bindings/clock/maxim,max77686.txt
+F:	include/linux/mfd/max14577*.h
+F:	include/linux/mfd/max77686*.h
+F:	include/linux/mfd/max77693*.h
+
 MAXIRADIO FM RADIO RECEIVER DRIVER
 M:	Hans Verkuil <hverkuil@xs4all.nl>
 L:	linux-media@vger.kernel.org
@@ -6112,14 +6355,6 @@
 F:	include/uapi/linux/ivtv*
 F:	include/uapi/linux/uvcvideo.h
 
-MEDIAVISION PRO MOVIE STUDIO DRIVER
-M:	Hans Verkuil <hverkuil@xs4all.nl>
-L:	linux-media@vger.kernel.org
-T:	git git://linuxtv.org/media_tree.git
-W:	http://linuxtv.org
-S:	Odd Fixes
-F:	drivers/media/parport/pms*
-
 MEGARAID SCSI/SAS DRIVERS
 M:	Kashyap Desai <kashyap.desai@avagotech.com>
 M:	Sumit Saxena <sumit.saxena@avagotech.com>
@@ -6134,6 +6369,7 @@
 
 MELLANOX ETHERNET DRIVER (mlx4_en)
 M:	Amir Vadai <amirv@mellanox.com>
+M:	Ido Shamay <idos@mellanox.com>
 L:	netdev@vger.kernel.org
 S:	Supported
 W:	http://www.mellanox.com
@@ -6171,7 +6407,7 @@
 F:	drivers/watchdog/mena21_wdt.c
 
 MEN CHAMELEON BUS (mcb)
-M:  	Johannes Thumshirn <johannes.thumshirn@men.de>
+M:	Johannes Thumshirn <johannes.thumshirn@men.de>
 S:	Supported
 F:	drivers/mcb/
 F:	include/linux/mcb.h
@@ -6364,6 +6600,7 @@
 L:	linux-media@vger.kernel.org
 T:	git git://linuxtv.org/media_tree.git
 S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/mt9v032.txt
 F:	drivers/media/i2c/mt9v032.c
 F:	include/media/mt9v032.h
 
@@ -6376,10 +6613,8 @@
 F:	include/linux/mfd/
 
 MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
-M:	Chris Ball <chris@printf.net>
 M:	Ulf Hansson <ulf.hansson@linaro.org>
 L:	linux-mmc@vger.kernel.org
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
 T:	git git://git.linaro.org/people/ulf.hansson/mmc.git
 S:	Maintained
 F:	drivers/mmc/
@@ -6537,9 +6772,10 @@
 F:	net/netrom/
 
 NETWORK BLOCK DEVICE (NBD)
-M:	Paul Clements <Paul.Clements@steeleye.com>
+M:	Markus Pargmann <mpa@pengutronix.de>
 S:	Maintained
 L:	nbd-general@lists.sourceforge.net
+T:	git git://git.pengutronix.de/git/mpa/linux-nbd.git
 F:	Documentation/blockdev/nbd.txt
 F:	drivers/block/nbd.c
 F:	include/linux/nbd.h
@@ -6568,6 +6804,7 @@
 F:	include/uapi/linux/in.h
 F:	include/uapi/linux/net.h
 F:	include/uapi/linux/netdevice.h
+F:	include/uapi/linux/net_namespace.h
 F:	tools/net/
 F:	tools/testing/selftests/net/
 F:	lib/random32.c
@@ -6672,6 +6909,7 @@
 
 NFS, SUNRPC, AND LOCKD CLIENTS
 M:	Trond Myklebust <trond.myklebust@primarydata.com>
+M:	Anna Schumaker <anna.schumaker@netapp.com>
 L:	linux-nfs@vger.kernel.org
 W:	http://client.linux-nfs.org
 T:	git git://git.linux-nfs.org/projects/trondmy/linux-nfs.git
@@ -6714,7 +6952,7 @@
 NIOS2 ARCHITECTURE
 M:	Ley Foon Tan <lftan@altera.com>
 L:	nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
-T:	git git://git.rocketboards.org/linux-socfpga.git
+T:	git git://git.rocketboards.org/linux-socfpga-next.git
 S:	Maintained
 F:	arch/nios2/
 
@@ -6752,6 +6990,13 @@
 F:	drivers/block/nvme*
 F:	include/linux/nvme.h
 
+NXP-NCI NFC DRIVER
+M:	Clément Perrochaud <clement.perrochaud@effinnov.com>
+R:	Charles Gorand <charles.gorand@effinnov.com>
+L:	linux-nfc@lists.01.org (moderated for non-subscribers)
+S:	Supported
+F:	drivers/nfc/nxp-nci
+
 NXP TDA998X DRM DRIVER
 M:	Russell King <rmk+kernel@arm.linux.org.uk>
 S:	Supported
@@ -6773,6 +7018,8 @@
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap.git
 S:	Maintained
 F:	arch/arm/*omap*/
+F:	arch/arm/configs/omap1_defconfig
+F:	arch/arm/configs/omap2plus_defconfig
 F:	drivers/i2c/busses/i2c-omap.c
 F:	drivers/irqchip/irq-omap-intc.c
 F:	drivers/mfd/*omap*.c
@@ -6887,6 +7134,12 @@
 S:	Maintained
 F:	arch/arm/mach-omap2/omap_hwmod.*
 
+OMAP HWMOD DATA
+M:	Paul Walmsley <paul@pwsan.com>
+L:	linux-omap@vger.kernel.org
+S:	Maintained
+F:	arch/arm/mach-omap2/omap_hwmod*data*
+
 OMAP HWMOD DATA FOR OMAP4-BASED DEVICES
 M:	Benoît Cousson <bcousson@baylibre.com>
 L:	linux-omap@vger.kernel.org
@@ -6897,6 +7150,7 @@
 M:	Laurent Pinchart <laurent.pinchart@ideasonboard.com>
 L:	linux-media@vger.kernel.org
 S:	Maintained
+F:	Documentation/devicetree/bindings/media/ti,omap3isp.txt
 F:	drivers/media/platform/omap3isp/
 F:	drivers/staging/media/omap4iss/
 
@@ -6977,14 +7231,12 @@
 M:	Grant Likely <grant.likely@linaro.org>
 M:	Rob Herring <robh+dt@kernel.org>
 L:	devicetree@vger.kernel.org
-W:	http://fdt.secretlab.ca
-T:	git git://git.secretlab.ca/git/linux-2.6.git
+W:	http://www.devicetree.org/
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/glikely/linux.git
 S:	Maintained
 F:	drivers/of/
 F:	include/linux/of*.h
 F:	scripts/dtc/
-K:	of_get_property
-K:	of_match_table
 
 OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
 M:	Rob Herring <robh+dt@kernel.org>
@@ -6998,6 +7250,15 @@
 F:	arch/*/boot/dts/
 F:	include/dt-bindings/
 
+OPEN FIRMWARE AND DEVICE TREE OVERLAYS
+M:	Pantelis Antoniou <pantelis.antoniou@konsulko.com>
+L:	devicetree@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/dynamic-resolution-notes.txt
+F:	Documentation/devicetree/overlay-notes.txt
+F:	drivers/of/overlay.c
+F:	drivers/of/resolver.c
+
 OPENRISC ARCHITECTURE
 M:	Jonas Bonn <jonas@southpole.se>
 W:	http://openrisc.net
@@ -7008,11 +7269,12 @@
 
 OPENVSWITCH
 M:	Pravin Shelar <pshelar@nicira.com>
+L:	netdev@vger.kernel.org
 L:	dev@openvswitch.org
 W:	http://openvswitch.org
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/pshelar/openvswitch.git
 S:	Maintained
 F:	net/openvswitch/
+F:	include/uapi/linux/openvswitch.h
 
 OPL4 DRIVER
 M:	Clemens Ladisch <clemens@ladisch.de>
@@ -7034,8 +7296,7 @@
 M:	Mark Fasheh <mfasheh@suse.com>
 M:	Joel Becker <jlbec@evilplan.org>
 L:	ocfs2-devel@oss.oracle.com (moderated for non-subscribers)
-W:	http://oss.oracle.com/projects/ocfs2/
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jlbec/ocfs2.git
+W:	http://ocfs2.wiki.kernel.org
 S:	Supported
 F:	Documentation/filesystems/ocfs2.txt
 F:	Documentation/filesystems/dlmfs.txt
@@ -7124,7 +7385,7 @@
 M:	Rusty Russell <rusty@rustcorp.com.au>
 L:	virtualization@lists.linux-foundation.org
 S:	Supported
-F:	Documentation/ia64/paravirt_ops.txt
+F:	Documentation/virtual/paravirt_ops.txt
 F:	arch/*/kernel/paravirt*
 F:	arch/*/include/asm/paravirt.h
 
@@ -7210,6 +7471,14 @@
 F:	arch/x86/pci/
 F:	arch/x86/kernel/quirks.c
 
+PCI DRIVER FOR ARM VERSATILE PLATFORM
+M:	Rob Herring <robh@kernel.org>
+L:	linux-pci@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/versatile.txt
+F:	drivers/pci/host/pci-versatile.c
+
 PCI DRIVER FOR APPLIEDMICRO XGENE
 M:	Tanmay Inamdar <tinamdar@apm.com>
 L:	linux-pci@vger.kernel.org
@@ -7229,7 +7498,7 @@
 F:	drivers/pci/host/*layerscape*
 
 PCI DRIVER FOR IMX6
-M:	Richard Zhu <r65037@freescale.com>
+M:	Richard Zhu <Richard.Zhu@freescale.com>
 M:	Lucas Stach <l.stach@pengutronix.de>
 L:	linux-pci@vger.kernel.org
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -7283,7 +7552,6 @@
 F:	drivers/pci/host/pci-exynos.c
 
 PCI DRIVER FOR SYNOPSIS DESIGNWARE
-M:	Mohit Kumar <mohit.kumar@st.com>
 M:	Jingoo Han <jg1.han@samsung.com>
 L:	linux-pci@vger.kernel.org
 S:	Maintained
@@ -7298,9 +7566,8 @@
 F:	drivers/pci/host/pci-host-generic.c
 
 PCIE DRIVER FOR ST SPEAR13XX
-M:	Mohit Kumar <mohit.kumar@st.com>
 L:	linux-pci@vger.kernel.org
-S:	Maintained
+S:	Orphan
 F:	drivers/pci/host/*spear*
 
 PCMCIA SUBSYSTEM
@@ -7399,6 +7666,7 @@
 PIN CONTROL SUBSYSTEM
 M:	Linus Walleij <linus.walleij@linaro.org>
 L:	linux-gpio@vger.kernel.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git
 S:	Maintained
 F:	drivers/pinctrl/
 F:	include/linux/pinctrl/
@@ -7566,12 +7834,6 @@
 S:	Obsolete
 F:	drivers/net/wireless/prism54/
 
-PROMISE SATA TX2/TX4 CONTROLLER LIBATA DRIVER
-M:	Mikael Pettersson <mikpelinux@gmail.com>
-L:	linux-ide@vger.kernel.org
-S:	Maintained
-F:	drivers/ata/sata_promise.*
-
 PS3 NETWORK SUPPORT
 M:	Geoff Levand <geoff@infradead.org>
 L:	netdev@vger.kernel.org
@@ -7712,10 +7974,10 @@
 S:	Maintained
 
 QAT DRIVER
-M:      Tadeusz Struk <tadeusz.struk@intel.com>
-L:      qat-linux@intel.com
-S:      Supported
-F:      drivers/crypto/qat/
+M:	Tadeusz Struk <tadeusz.struk@intel.com>
+L:	qat-linux@intel.com
+S:	Supported
+F:	drivers/crypto/qat/
 
 QIB DRIVER
 M:	Mike Marciniszyn <infinipath@intel.com>
@@ -7737,8 +7999,7 @@
 F:	drivers/scsi/qla2xxx/
 
 QLOGIC QLA4XXX iSCSI DRIVER
-M:	Vikas Chaudhary <vikas.chaudhary@qlogic.com>
-M:	iscsi-driver@qlogic.com
+M:	QLogic-Storage-Upstream@qlogic.com
 L:	linux-scsi@vger.kernel.org
 S:	Supported
 F:	Documentation/scsi/LICENSE.qla4xxx
@@ -7817,17 +8078,9 @@
 S:	Supported
 F:	drivers/net/wireless/ath/wcn36xx/
 
-QUICKCAM PARALLEL PORT WEBCAMS
-M:	Hans Verkuil <hverkuil@xs4all.nl>
-L:	linux-media@vger.kernel.org
-T:	git git://linuxtv.org/media_tree.git
-W:	http://linuxtv.org
-S:	Odd Fixes
-F:	drivers/media/parport/*-qcam*
-
 RADOS BLOCK DEVICE (RBD)
-M:	Yehuda Sadeh <yehuda@inktank.com>
-M:	Sage Weil <sage@inktank.com>
+M:	Ilya Dryomov <idryomov@gmail.com>
+M:	Sage Weil <sage@redhat.com>
 M:	Alex Elder <elder@kernel.org>
 M:	ceph-devel@vger.kernel.org
 W:	http://ceph.com/
@@ -7875,11 +8128,17 @@
 F:	drivers/net/wireless/rt2x00/
 
 RAMDISK RAM BLOCK DEVICE DRIVER
-M:	Nick Piggin <npiggin@kernel.dk>
+M:	Jens Axboe <axboe@kernel.dk>
 S:	Maintained
 F:	Documentation/blockdev/ramdisk.txt
 F:	drivers/block/brd.c
 
+PERSISTENT MEMORY DRIVER
+M:	Ross Zwisler <ross.zwisler@linux.intel.com>
+L:	linux-nvdimm@lists.01.org
+S:	Supported
+F:	drivers/block/pmem.c
+
 RANDOM NUMBER DRIVER
 M:	"Theodore Ts'o" <tytso@mit.edu>
 S:	Maintained
@@ -7951,6 +8210,7 @@
 
 REAL TIME CLOCK (RTC) SUBSYSTEM
 M:	Alessandro Zummo <a.zummo@towertech.it>
+M:	Alexandre Belloni <alexandre.belloni@free-electrons.com>
 L:	rtc-linux@googlegroups.com
 Q:	http://patchwork.ozlabs.org/project/rtc-linux/list/
 S:	Maintained
@@ -8013,6 +8273,13 @@
 F:	Documentation/rfkill.txt
 F:	net/rfkill/
 
+RHASHTABLE
+M:	Thomas Graf <tgraf@suug.ch>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	lib/rhashtable.c
+F:	include/linux/rhashtable.h
+
 RICOH SMARTMEDIA/XD DRIVER
 M:	Maxim Levitsky <maximlevitsky@gmail.com>
 S:	Maintained
@@ -8155,7 +8422,6 @@
 
 S390 NETWORK DRIVERS
 M:	Ursula Braun <ursula.braun@de.ibm.com>
-M:	Frank Blaschka <blaschka@linux.vnet.ibm.com>
 M:	linux390@de.ibm.com
 L:	linux-s390@vger.kernel.org
 W:	http://www.ibm.com/developerworks/linux/linux390/
@@ -8301,6 +8567,14 @@
 L:	netdev@vger.kernel.org
 F:	drivers/net/ethernet/samsung/sxgbe/
 
+SAMSUNG THERMAL DRIVER
+M:	Lukasz Majewski <l.majewski@samsung.com>
+L:	linux-pm@vger.kernel.org
+L:	linux-samsung-soc@vger.kernel.org
+S:	Supported
+T:	https://github.com/lmajewski/linux-samsung-thermal.git
+F:	drivers/thermal/samsung/
+
 SAMSUNG USB2 PHY DRIVER
 M:	Kamil Debski <k.debski@samsung.com>
 L:	linux-kernel@vger.kernel.org
@@ -8324,6 +8598,7 @@
 M:	Viresh Kumar <viresh.linux@gmail.com>
 M:	Andy Shevchenko <andriy.shevchenko@linux.intel.com>
 S:	Maintained
+F:	include/linux/dma/dw.h
 F:	include/linux/platform_data/dma-dw.h
 F:	drivers/dma/dw/
 
@@ -8354,12 +8629,7 @@
 F:	kernel/time/clocksource.c
 F:	kernel/time/time*.c
 F:	kernel/time/ntp.c
-
-TLG2300 VIDEO4LINUX-2 DRIVER
-M:	Huang Shijie <shijie8@gmail.com>
-M:	Hans Verkuil <hverkuil@xs4all.nl>
-S:	Odd Fixes
-F:	drivers/media/usb/tlg2300/
+F:	tools/testing/selftests/timers/
 
 SC1200 WDT DRIVER
 M:	Zwane Mwaikambo <zwanem@gmail.com>
@@ -8392,7 +8662,7 @@
 F:	drivers/scsi/sr*
 
 SCSI RDMA PROTOCOL (SRP) INITIATOR
-M:	Bart Van Assche <bvanassche@acm.org>
+M:	Bart Van Assche <bart.vanassche@sandisk.com>
 L:	linux-rdma@vger.kernel.org
 S:	Supported
 W:	http://www.openfabrics.org
@@ -8411,11 +8681,9 @@
 F:	include/scsi/sg.h
 
 SCSI SUBSYSTEM
-M:	"James E.J. Bottomley" <JBottomley@parallels.com>
+M:	"James E.J. Bottomley" <JBottomley@odin.com>
 L:	linux-scsi@vger.kernel.org
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-rc-fixes-2.6.git
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-pending-2.6.git
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi.git
 S:	Maintained
 F:	drivers/scsi/
 F:	include/scsi/
@@ -8468,10 +8736,8 @@
 F:	drivers/mmc/host/sdricoh_cs.c
 
 SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
-M:	Chris Ball <chris@printf.net>
 L:	linux-mmc@vger.kernel.org
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
-S:	Maintained
+S:	Orphan
 F:	drivers/mmc/host/sdhci.*
 F:	drivers/mmc/host/sdhci-pltfm.[ch]
 
@@ -8487,18 +8753,12 @@
 K:	\bsecure_computing
 K:	\bTIF_SECCOMP\b
 
-SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF)
-M:	Anton Vorontsov <anton@enomsg.org>
-L:	linuxppc-dev@lists.ozlabs.org
-L:	linux-mmc@vger.kernel.org
-S:	Maintained
-F:	drivers/mmc/host/sdhci-pltfm.[ch]
-
 SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER
 M:	Ben Dooks <ben-linux@fluff.org>
+M:	Jaehoon Chung <jh80.chung@samsung.com>
 L:	linux-mmc@vger.kernel.org
 S:	Maintained
-F:	drivers/mmc/host/sdhci-s3c.c
+F:	drivers/mmc/host/sdhci-s3c*
 
 SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) ST SPEAR DRIVER
 M:	Viresh Kumar <viresh.linux@gmail.com>
@@ -8546,25 +8806,6 @@
 F:	drivers/misc/phantom.c
 F:	include/uapi/linux/phantom.h
 
-SERIAL ATA (SATA) SUBSYSTEM
-M:	Tejun Heo <tj@kernel.org>
-L:	linux-ide@vger.kernel.org
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
-S:	Supported
-F:	drivers/ata/
-F:	include/linux/ata.h
-F:	include/linux/libata.h
-
-SERIAL ATA AHCI PLATFORM devices support
-M:	Hans de Goede <hdegoede@redhat.com>
-M:	Tejun Heo <tj@kernel.org>
-L:	linux-ide@vger.kernel.org
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
-S:	Supported
-F:	drivers/ata/ahci_platform.c
-F:	drivers/ata/libahci_platform.c
-F:	include/linux/ahci_platform.h
-
 SERVER ENGINES 10Gbps iSCSI - BladeEngine 2 DRIVER
 M:	Jayamohan Kallickal <jayamohan.kallickal@emulex.com>
 L:	linux-scsi@vger.kernel.org
@@ -8581,6 +8822,15 @@
 S:	Supported
 F:	drivers/net/ethernet/emulex/benet/
 
+EMULEX ONECONNECT ROCE DRIVER
+M:	Selvin Xavier <selvin.xavier@emulex.com>
+M:	Devesh Sharma <devesh.sharma@emulex.com>
+M:	Mitesh Ahuja <mitesh.ahuja@emulex.com>
+L:	linux-rdma@vger.kernel.org
+W:	http://www.emulex.com
+S:	Supported
+F:	drivers/infiniband/hw/ocrdma/
+
 SFC NETWORK DRIVER
 M:	Solarflare linux maintainers <linux-net-drivers@solarflare.com>
 M:	Shradha Shah <sshah@solarflare.com>
@@ -8745,6 +8995,25 @@
 F:	drivers/media/platform/davinci/
 F:	include/media/davinci/
 
+TI AM437X VPFE DRIVER
+M:	Lad, Prabhakar <prabhakar.csengg@gmail.com>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org/
+Q:	http://patchwork.linuxtv.org/project/linux-media/list/
+T:	git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
+S:	Maintained
+F:	drivers/media/platform/am437x/
+
+OV2659 OMNIVISION SENSOR DRIVER
+M:	Lad, Prabhakar <prabhakar.csengg@gmail.com>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org/
+Q:	http://patchwork.linuxtv.org/project/linux-media/list/
+T:	git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
+S:	Maintained
+F:	drivers/media/i2c/ov2659.c
+F:	include/media/ov2659.h
+
 SIS 190 ETHERNET DRIVER
 M:	Francois Romieu <romieu@fr.zoreil.com>
 L:	netdev@vger.kernel.org
@@ -8826,6 +9095,8 @@
 F:	include/media/smiapp.h
 F:	drivers/media/i2c/smiapp-pll.c
 F:	drivers/media/i2c/smiapp-pll.h
+F:	include/uapi/linux/smiapp.h
+F:	Documentation/devicetree/bindings/media/i2c/nokia,smia.txt
 
 SMM665 HARDWARE MONITOR DRIVER
 M:	Guenter Roeck <linux@roeck-us.net>
@@ -8892,6 +9163,7 @@
 M:	Bluecherry Maintainers <maintainers@bluecherrydvr.com>
 M:	Andrey Utkin <andrey.utkin@corp.bluecherry.net>
 M:	Andrey Utkin <andrey.krieger.utkin@gmail.com>
+M:	Ismael Luceno <ismael@iodev.co.uk>
 L:	linux-media@vger.kernel.org
 S:	Supported
 F:	drivers/media/pci/solo6x10/
@@ -9164,6 +9436,22 @@
 S:	Maintained
 F:	drivers/staging/rtl8723au/
 
+STAGING - SILICON MOTION SM7XX FRAME BUFFER DRIVER
+M:	Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+M:	Teddy Wang <teddy.wang@siliconmotion.com>
+M:	Sudip Mukherjee <sudip@vectorindia.org>
+L:	linux-fbdev@vger.kernel.org
+S:	Maintained
+F:	drivers/staging/sm7xxfb/
+
+STAGING - SILICON MOTION SM750 FRAME BUFFER DRIVER
+M:	Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+M:	Teddy Wang <teddy.wang@siliconmotion.com>
+M:	Sudip Mukherjee <sudip@vectorindia.org>
+L:	linux-fbdev@vger.kernel.org
+S:	Maintained
+F:	drivers/staging/sm750fb/
+
 STAGING - SLICOSS
 M:	Lior Dotan <liodot@gmail.com>
 M:	Christopher Harrer <charrer@alacritech.com>
@@ -9204,6 +9492,13 @@
 F:	arch/m68k/include/asm/sun3*
 F:	drivers/net/ethernet/i825xx/sun3*
 
+SUN4I LOW RES ADC ATTACHED TABLET KEYS DRIVER
+M:	Hans de Goede <hdegoede@redhat.com>
+L:	linux-input@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
+F:	drivers/input/keyboard/sun4i-lradc-keys.c
+
 SUNDANCE NETWORK DRIVER
 M:	Denis Kirjanov <kda@linux-powerpc.org>
 L:	netdev@vger.kernel.org
@@ -9212,7 +9507,6 @@
 
 SUPERH
 L:	linux-sh@vger.kernel.org
-W:	http://www.linux-sh.org
 Q:	http://patchwork.kernel.org/project/linux-sh/list/
 S:	Orphan
 F:	Documentation/sh/
@@ -9533,9 +9827,15 @@
 TI BANDGAP AND THERMAL DRIVER
 M:	Eduardo Valentin <edubezval@gmail.com>
 L:	linux-pm@vger.kernel.org
-S:	Supported
+L:	linux-omap@vger.kernel.org
+S:	Maintained
 F:	drivers/thermal/ti-soc-thermal/
 
+TI CDCE706 CLOCK DRIVER
+M:	Max Filippov <jcmvbkbc@gmail.com>
+S:	Maintained
+F:	drivers/clk/clk-cdce706.c
+
 TI CLOCK DRIVER
 M:	Tero Kristo <t-kristo@ti.com>
 L:	linux-omap@vger.kernel.org
@@ -9590,6 +9890,13 @@
 F:	drivers/regulator/lp8788-*.c
 F:	include/linux/mfd/lp8788*.h
 
+TI NETCP ETHERNET DRIVER
+M:	Wingman Kwok <w-kwok2@ti.com>
+M:	Murali Karicheri <m-karicheri2@ti.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/net/ethernet/ti/netcp*
+
 TI TWL4030 SERIES SOC CODEC DRIVER
 M:	Peter Ujfalusi <peter.ujfalusi@ti.com>
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
@@ -9607,7 +9914,7 @@
 
 TIPC NETWORK LAYER
 M:	Jon Maloy <jon.maloy@ericsson.com>
-M:	Allan Stephens <allan.stephens@windriver.com>
+M:	Ying Xue <ying.xue@windriver.com>
 L:	netdev@vger.kernel.org (core kernel code)
 L:	tipc-discussion@lists.sourceforge.net (user apps, general discussion)
 W:	http://tipc.sourceforge.net/
@@ -9617,7 +9924,7 @@
 
 TILE ARCHITECTURE
 M:	Chris Metcalf <cmetcalf@ezchip.com>
-W:	http://www.tilera.com/scm/
+W:	http://www.ezchip.com/scm/
 S:	Supported
 F:	arch/tile/
 F:	drivers/char/tile-srom.c
@@ -9656,10 +9963,23 @@
 F:	drivers/platform/x86/topstar-laptop.c
 
 TOSHIBA ACPI EXTRAS DRIVER
+M:	Azael Avalos <coproscefalo@gmail.com>
 L:	platform-driver-x86@vger.kernel.org
-S:	Orphan
+S:	Maintained
 F:	drivers/platform/x86/toshiba_acpi.c
 
+TOSHIBA BLUETOOTH DRIVER
+M:	Azael Avalos <coproscefalo@gmail.com>
+L:	platform-driver-x86@vger.kernel.org
+S:	Maintained
+F:	drivers/platform/x86/toshiba_bluetooth.c
+
+TOSHIBA HDD ACTIVE PROTECTION SENSOR DRIVER
+M:	Azael Avalos <coproscefalo@gmail.com>
+L:	platform-driver-x86@vger.kernel.org
+S:	Maintained
+F:	drivers/platform/x86/toshiba_haps.c
+
 TOSHIBA SMM DRIVER
 M:	Jonathan Buzzard <jonathan@buzzard.org.uk>
 L:	tlinux-users@tce.toshiba-dme.co.jp
@@ -9710,12 +10030,21 @@
 
 TPM DEVICE DRIVER
 M:	Peter Huewe <peterhuewe@gmx.de>
-M:	Ashley Lai <ashley@ashleylai.com>
 M:	Marcel Selhorst <tpmdd@selhorst.net>
+R:	Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
+W:	http://tpmdd.sourceforge.net
+L:	tpmdd-devel@lists.sourceforge.net (moderated for non-subscribers)
+Q:	git git://github.com/PeterHuewe/linux-tpmdd.git
+T:	https://github.com/PeterHuewe/linux-tpmdd
+S:	Maintained
+F:	drivers/char/tpm/
+
+TPM IBM_VTPM DEVICE DRIVER
+M:	Ashley Lai <ashleydlai@gmail.com>
 W:	http://tpmdd.sourceforge.net
 L:	tpmdd-devel@lists.sourceforge.net (moderated for non-subscribers)
 S:	Maintained
-F:	drivers/char/tpm/
+F:	drivers/char/tpm/tpm_ibmvtpm*
 
 TRACING
 M:	Steven Rostedt <rostedt@goodmis.org>
@@ -9856,11 +10185,11 @@
 F:	include/uapi/linux/cdrom.h
 
 UNISYS S-PAR DRIVERS
-M:     Benjamin Romer <benjamin.romer@unisys.com>
-M:     David Kershner <david.kershner@unisys.com>
-L:     sparmaintainer@unisys.com (Unisys internal)
-S:     Supported
-F:     drivers/staging/unisys/
+M:	Benjamin Romer <benjamin.romer@unisys.com>
+M:	David Kershner <david.kershner@unisys.com>
+L:	sparmaintainer@unisys.com (Unisys internal)
+S:	Supported
+F:	drivers/staging/unisys/
 
 UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER
 M:	Vinayak Holikatti <vinholikatti@gmail.com>
@@ -9871,20 +10200,15 @@
 
 UNSORTED BLOCK IMAGES (UBI)
 M:	Artem Bityutskiy <dedekind1@gmail.com>
+M:	Richard Weinberger <richard@nod.at>
 W:	http://www.linux-mtd.infradead.org/
 L:	linux-mtd@lists.infradead.org
 T:	git git://git.infradead.org/ubifs-2.6.git
-S:	Maintained
+S:	Supported
 F:	drivers/mtd/ubi/
 F:	include/linux/mtd/ubi.h
 F:	include/uapi/mtd/ubi-user.h
 
-UNSORTED BLOCK IMAGES (UBI) Fastmap
-M:	Richard Weinberger <richard@nod.at>
-L:	linux-mtd@lists.infradead.org
-S:	Maintained
-F:	drivers/mtd/ubi/fastmap.c
-
 USB ACM DRIVER
 M:	Oliver Neukum <oliver@neukum.org>
 L:	linux-usb@vger.kernel.org
@@ -9913,6 +10237,12 @@
 F:	drivers/net/usb/cdc_*.c
 F:	include/uapi/linux/usb/cdc.h
 
+USB CHAOSKEY DRIVER
+M:	Keith Packard <keithp@keithp.com>
+L:	linux-usb@vger.kernel.org
+S:	Maintained
+F:	drivers/usb/misc/chaoskey.c
+
 USB CYPRESS C67X00 DRIVER
 M:	Peter Korsgaard <jacmet@sunsite.dk>
 L:	linux-usb@vger.kernel.org
@@ -9991,6 +10321,13 @@
 F:	Documentation/usb/ohci.txt
 F:	drivers/usb/host/ohci*
 
+USB OTG FSM (Finite State Machine)
+M:	Peter Chen <Peter.Chen@freescale.com>
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
+L:	linux-usb@vger.kernel.org
+S:	Maintained
+F:	drivers/usb/common/usb-otg-fsm.c
+
 USB OVER IP DRIVER
 M:	Valentina Manea <valentina.manea.m@gmail.com>
 M:	Shuah Khan <shuah.kh@samsung.com>
@@ -10146,6 +10483,7 @@
 M:	"Hans J. Koch" <hjk@hansjkoch.de>
 M:	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 S:	Maintained
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
 F:	Documentation/DocBook/uio-howto.tmpl
 F:	drivers/uio/
 F:	include/linux/uio*.h
@@ -10218,6 +10556,12 @@
 F:	drivers/vhost/
 F:	include/uapi/linux/vhost.h
 
+VIRTIO INPUT DRIVER
+M:	Gerd Hoffmann <kraxel@redhat.com>
+S:	Maintained
+F:	drivers/virtio/virtio_input.c
+F:	include/uapi/linux/virtio_input.h
+
 VIA RHINE NETWORK DRIVER
 M:	Roger Luethi <rl@hellgate.ch>
 S:	Maintained
@@ -10293,6 +10637,14 @@
 S:	Maintained
 F:	drivers/misc/vmw_balloon.c
 
+VMWARE VMMOUSE SUBDRIVER
+M:	"VMware Graphics" <linux-graphics-maintainer@vmware.com>
+M:	"VMware, Inc." <pv-drivers@vmware.com>
+L:	linux-input@vger.kernel.org
+S:	Maintained
+F:	drivers/input/mouse/vmmouse.c
+F:	drivers/input/mouse/vmmouse.h
+
 VMWARE VMXNET3 ETHERNET DRIVER
 M:	Shreyas Bhatewara <sbhatewara@vmware.com>
 M:	"VMware, Inc." <pv-drivers@vmware.com>
@@ -10408,7 +10760,7 @@
 WIMAX STACK
 M:	Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
 M:	linux-wimax@intel.com
-L:     wimax@linuxwimax.org (subscribers-only)
+L:	wimax@linuxwimax.org (subscribers-only)
 S:	Supported
 W:	http://linuxwimax.org
 F:	Documentation/wimax/README.wimax
@@ -10573,6 +10925,7 @@
 
 XEN BLOCK SUBSYSTEM
 M:	Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+M:	Roger Pau Monné <roger.pau@citrix.com>
 L:	xen-devel@lists.xenproject.org (moderated for non-subscribers)
 S:	Supported
 F:	drivers/block/xen-blkback/*
@@ -10617,6 +10970,16 @@
 S:	Maintained
 F:	drivers/tty/serial/uartlite.c
 
+XILINX VIDEO IP CORES
+M:	Hyun Kwon <hyun.kwon@xilinx.com>
+M:	Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Supported
+F:	Documentation/devicetree/bindings/media/xilinx/
+F:	drivers/media/platform/xilinx/
+F:	include/uapi/linux/xilinx-v4l2-controls.h
+
 XILLYBUS DRIVER
 M:	Eli Billauer <eli.billauer@gmail.com>
 L:	linux-kernel@vger.kernel.org
@@ -10628,6 +10991,7 @@
 L:	linux-xtensa@linux-xtensa.org
 S:	Maintained
 F:	drivers/spi/spi-xtensa-xtfpga.c
+F:	sound/soc/xtensa/xtfpga-i2s.c
 
 YAM DRIVER FOR AX.25
 M:	Jean-Paul Roubelat <jpr@f6fbb.org>
@@ -10680,6 +11044,7 @@
 ZRAM COMPRESSED RAM BLOCK DEVICE DRVIER
 M:	Minchan Kim <minchan@kernel.org>
 M:	Nitin Gupta <ngupta@vflare.org>
+R:	Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com>
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
 F:	drivers/block/zram/
@@ -10697,6 +11062,7 @@
 S:	Maintained
 F:	mm/zsmalloc.c
 F:	include/linux/zsmalloc.h
+F:	Documentation/vm/zsmalloc.txt
 
 ZSWAP COMPRESSED SWAP CACHING
 M:	Seth Jennings <sjennings@variantweb.net>
diff --git a/Makefile b/Makefile
index c21b813..11678f6 100644
--- a/Makefile
+++ b/Makefile
@@ -85,11 +85,12 @@
 			done								\
 		) > Kconfig.kernel							;\
 		kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion |	\
-			sed 's/^\(\(3\|2\.6\)\.[0-9]\+\).*/\1/;t;d')			;\
+			sed 's/^\(\([3-4]\|2\.6\)\.[0-9]\+\).*/\1/;t;d')		;\
 		test "$$kver" != "" || echo "Kernel version parse failed!"		;\
 		test "$$kver" != ""							;\
 		kvers="$$(seq 14 39 | sed 's/^/2.6./')"					;\
-		kvers="$$kvers $$(seq 0 99 | sed 's/^/3./')"				;\
+		kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')"				;\
+		kvers="$$kvers $$(seq 0 99 | sed 's/^/4./')"				;\
 		print=0									;\
 		for v in $$kvers ; do							\
 			if [ "$$print" = "1" ] ; then					\
diff --git a/backport-include/linux/clk.h b/backport-include/linux/clk.h
index a619b66..c5c4570 100644
--- a/backport-include/linux/clk.h
+++ b/backport-include/linux/clk.h
@@ -86,4 +86,31 @@
 
 #endif /* #if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) */
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) && \
+    LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
+#define clk_prepare_enable LINUX_BACKPORT(clk_prepare_enable)
+/* clk_prepare_enable helps cases using clk_enable in non-atomic context. */
+static inline int clk_prepare_enable(struct clk *clk)
+{
+	int ret;
+
+	ret = clk_prepare(clk);
+	if (ret)
+		return ret;
+	ret = clk_enable(clk);
+	if (ret)
+		clk_unprepare(clk);
+
+	return ret;
+}
+
+#define clk_disable_unprepare LINUX_BACKPORT(clk_disable_unprepare)
+/* clk_disable_unprepare helps cases using clk_disable in non-atomic context. */
+static inline void clk_disable_unprepare(struct clk *clk)
+{
+	clk_disable(clk);
+	clk_unprepare(clk);
+}
+#endif /* < 3,3,0 && >= 3,2,0 */
+
 #endif /* __LINUX_CLK_H */
diff --git a/backport-include/linux/completion.h b/backport-include/linux/completion.h
index 399430d..477530d 100644
--- a/backport-include/linux/completion.h
+++ b/backport-include/linux/completion.h
@@ -11,6 +11,7 @@
  * This inline function should be used to reinitialize a completion structure so it can
  * be reused. This is especially important after complete_all() is used.
  */
+#define reinit_completion LINUX_BACKPORT(reinit_completion)
 static inline void reinit_completion(struct completion *x)
 {
 	x->done = 0;
diff --git a/backport-include/linux/device.h b/backport-include/linux/device.h
index 29dc077..03ca1f1 100644
--- a/backport-include/linux/device.h
+++ b/backport-include/linux/device.h
@@ -5,6 +5,13 @@
 
 #include <linux/version.h>
 
+/*
+ * string.h is usually included from the asm/ folder in most configuration,
+ * but on some older kernels it doesn't. As we're using memcpy() in the code
+ * below, we need to be safe and make sure string.h is indeed there.
+ */
+#include <linux/string.h>
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
 /* backport
  * commit 9f3b795a626ee79574595e06d1437fe0c7d51d29
diff --git a/backport-include/linux/dma-buf.h b/backport-include/linux/dma-buf.h
index 48c2ebf..07ac0b4 100644
--- a/backport-include/linux/dma-buf.h
+++ b/backport-include/linux/dma-buf.h
@@ -8,13 +8,47 @@
 #include <linux/dma-attrs.h>
 #include <linux/dma-mapping.h>
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
-#define dma_buf_export(priv, ops, size, flags, resv)	\
-	dma_buf_export(priv, ops, size, flags)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0)
+#if !defined(DEFINE_DMA_BUF_EXPORT_INFO) && LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)
+/**
+ * helper macro for exporters; zeros and fills in most common values
+ */
+#define DEFINE_DMA_BUF_EXPORT_INFO(a)	\
+	struct dma_buf_export_info a = { .exp_name = KBUILD_MODNAME }
+
+struct dma_buf_export_info {
+	const char *exp_name;
+	const struct dma_buf_ops *ops;
+	size_t size;
+	int flags;
+	struct reservation_object *resv;
+	void *priv;
+};
+
+#ifdef dma_buf_export
 #undef dma_buf_export
-#define dma_buf_export(priv, ops, size, flags, resv)	\
-	dma_buf_export_named(priv, ops, size, flags, KBUILD_MODNAME)
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) */
+#endif
+
+static inline
+struct dma_buf *backport_dma_buf_export(const struct dma_buf_export_info *exp_info)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)
+	return dma_buf_export(exp_info->priv,
+			      (struct dma_buf_ops *)exp_info->ops,
+			      exp_info->size, exp_info->flags);
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
+	return dma_buf_export(exp_info->priv, exp_info->ops,
+			      exp_info->size, exp_info->flags);
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0)
+	return dma_buf_export_named(exp_info->priv, exp_info->ops,
+				    exp_info->size, exp_info->flags,
+				    exp_info->exp_name);
+#else
+	return dma_buf_export_named(exp_info->priv, exp_info->ops,
+				    exp_info->size, exp_info->flags,
+				    exp_info->exp_name, exp_info->resv);
+#endif
+}
+#define dma_buf_export LINUX_BACKPORT(dma_buf_export)
+#endif /* !defined(DEFINE_DMA_BUF_EXPORT_INFO) */
 
 #endif /* _BACKPORT_DMA_BUF_H__ */
diff --git a/backport-include/linux/hashtable.h b/backport-include/linux/hashtable.h
new file mode 100644
index 0000000..dc53e74
--- /dev/null
+++ b/backport-include/linux/hashtable.h
@@ -0,0 +1,42 @@
+#ifndef __BACKPORT_HASHTABLE_H
+#define __BACKPORT_HASHTABLE_H
+#include_next <linux/hashtable.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
+/**
+ * backport:
+ *
+ * commit 0bbacca7c3911451cea923b0ad6389d58e3d9ce9
+ * Author: Sasha Levin <sasha.levin@oracle.com>
+ * Date:   Thu Feb 7 12:32:18 2013 +1100
+ *
+ *     hlist: drop the node parameter from iterators
+ */
+#include <linux/list.h>
+#include <backport/magic.h>
+
+#undef hash_for_each
+#define hash_for_each(name, bkt, obj, member)				\
+	for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
+			(bkt)++)\
+		hlist_for_each_entry(obj, &name[bkt], member)
+
+#undef hash_for_each_safe
+#define hash_for_each_safe(name, bkt, tmp, obj, member)			\
+	for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
+			(bkt)++)\
+		hlist_for_each_entry_safe(obj, tmp, &name[bkt], member)
+
+#undef hash_for_each_possible
+#define hash_for_each_possible(name, obj, member, key)			\
+	hlist_for_each_entry(obj, &name[hash_min(key, HASH_BITS(name))], member)
+
+#undef hash_for_each_possible_safe
+#define hash_for_each_possible_safe(name, obj, tmp, member, key)	\
+	hlist_for_each_entry_safe(obj, tmp,\
+		&name[hash_min(key, HASH_BITS(name))], member)
+
+#endif
+
+#endif /* __BACKPORT_HASHTABLE_H */
diff --git a/backport-include/linux/hwmon.h b/backport-include/linux/hwmon.h
new file mode 100644
index 0000000..c0185f1
--- /dev/null
+++ b/backport-include/linux/hwmon.h
@@ -0,0 +1,34 @@
+#ifndef __BACKPORT_LINUX_HWMON_H
+#define __BACKPORT_LINUX_HWMON_H
+#include_next <linux/hwmon.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
+/*
+ * Backports
+ *
+ * commit bab2243ce1897865e31ea6d59b0478391f51812b
+ * Author: Guenter Roeck <linux@roeck-us.net>
+ * Date:   Sat Jul 6 13:57:23 2013 -0700
+ *
+ *     hwmon: Introduce hwmon_device_register_with_groups
+ *
+ *     hwmon_device_register_with_groups() lets callers register a hwmon device
+ *     together with all sysfs attributes in a single call.
+ *
+ *     When using hwmon_device_register_with_groups(), hwmon attributes are attached
+ *     to the hwmon device directly and no longer with its parent device.
+ *
+ * Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+ */
+struct device *
+hwmon_device_register_with_groups(struct device *dev, const char *name,
+				  void *drvdata,
+				  const struct attribute_group **groups);
+struct device *
+devm_hwmon_device_register_with_groups(struct device *dev, const char *name,
+				       void *drvdata,
+				       const struct attribute_group **groups);
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0) */
+
+#endif /* __BACKPORT_LINUX_HWMON_H */
diff --git a/backport-include/linux/if_vlan.h b/backport-include/linux/if_vlan.h
index 7d39d52..be2e259 100644
--- a/backport-include/linux/if_vlan.h
+++ b/backport-include/linux/if_vlan.h
@@ -24,4 +24,16 @@
 #define __vlan_find_dev_deep_rcu(real_dev, vlan_proto, vlan_id) __vlan_find_dev_deep(real_dev, vlan_proto, vlan_id)
 #endif
 
+#ifndef skb_vlan_tag_present
+#define skb_vlan_tag_present(__skb)	((__skb)->vlan_tci & VLAN_TAG_PRESENT)
+#endif
+
+#ifndef skb_vlan_tag_get
+#define skb_vlan_tag_get(__skb)		((__skb)->vlan_tci & ~VLAN_TAG_PRESENT)
+#endif
+
+#ifndef skb_vlan_tag_get_id
+#define skb_vlan_tag_get_id(__skb)	((__skb)->vlan_tci & VLAN_VID_MASK)
+#endif
+
 #endif /* __BACKPORT_LINUX_IF_VLAN_H_ */
diff --git a/backport-include/linux/kconfig.h b/backport-include/linux/kconfig.h
index ee938e3..4fe2a10 100644
--- a/backport-include/linux/kconfig.h
+++ b/backport-include/linux/kconfig.h
@@ -24,4 +24,15 @@
 #undef IS_BUILTIN
 #define IS_BUILTIN(option) config_enabled(option)
 
+#ifndef IS_REACHABLE
+/*
+ * IS_REACHABLE(CONFIG_FOO) evaluates to 1 if the currently compiled
+ * code can call a function defined in code compiled based on CONFIG_FOO.
+ * This is similar to IS_ENABLED(), but returns false when invoked from
+ * built-in code when CONFIG_FOO is set to 'm'.
+ */
+#define IS_REACHABLE(option) (config_enabled(option) || \
+		 (config_enabled(option##_MODULE) && config_enabled(MODULE)))
+#endif
+
 #endif
diff --git a/backport-include/linux/kernel.h b/backport-include/linux/kernel.h
index 9d31d0f..63ffe98 100644
--- a/backport-include/linux/kernel.h
+++ b/backport-include/linux/kernel.h
@@ -72,10 +72,27 @@
 #endif
 
 #ifndef DIV_ROUND_CLOSEST
-#define DIV_ROUND_CLOSEST(x, divisor) ({		\
-	typeof(divisor) __divisor = divisor;		\
-	(((x) + ((__divisor) / 2)) / (__divisor));	\
-})
+#define DIV_ROUND_CLOSEST(x, divisor)(			\
+{							\
+	typeof(x) __x = x;				\
+	typeof(divisor) __d = divisor;			\
+	(((typeof(x))-1) > 0 ||				\
+	 ((typeof(divisor))-1) > 0 || (__x) > 0) ?	\
+		(((__x) + ((__d) / 2)) / (__d)) :	\
+		(((__x) - ((__d) / 2)) / (__d));	\
+}							\
+)
+#endif
+
+#ifndef DIV_ROUND_CLOSEST_ULL
+#define DIV_ROUND_CLOSEST_ULL(x, divisor)(		\
+{							\
+	typeof(divisor) __d = divisor;			\
+	unsigned long long _tmp = (x) + (__d) / 2;	\
+	do_div(_tmp, __d);				\
+	_tmp;						\
+}							\
+)
 #endif
 
 #ifndef swap
diff --git a/backport-include/linux/ktime.h b/backport-include/linux/ktime.h
index adefc27..6fbc6c6 100644
--- a/backport-include/linux/ktime.h
+++ b/backport-include/linux/ktime.h
@@ -9,4 +9,9 @@
 
 #endif /* < 3.17 */
 
+#ifndef ktime_to_timespec64
+/* Map the ktime_t to timespec conversion to ns_to_timespec function */
+#define ktime_to_timespec64(kt)		ns_to_timespec64((kt).tv64)
+#endif
+
 #endif /* __BACKPORT_LINUX_KTIME_H */
diff --git a/backport-include/linux/list_nulls.h b/backport-include/linux/list_nulls.h
new file mode 100644
index 0000000..ba28834
--- /dev/null
+++ b/backport-include/linux/list_nulls.h
@@ -0,0 +1,9 @@
+#ifndef __BACKPORT_LIST_NULLS
+#define __BACKPORT_LIST_NULLS
+#include_next <linux/list_nulls.h>
+
+#ifndef NULLS_MARKER
+#define NULLS_MARKER(value) (1UL | (((long)value) << 1))
+#endif
+
+#endif /* __BACKPORT_LIST_NULLS */
diff --git a/backport-include/linux/lockdep.h b/backport-include/linux/lockdep.h
deleted file mode 100644
index 1d4aa65..0000000
--- a/backport-include/linux/lockdep.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef __BACKPORT_LINUX_LOCKDEP_H
-#define __BACKPORT_LINUX_LOCKDEP_H
-#include_next <linux/lockdep.h>
-#include <linux/version.h>
-
-#ifndef lockdep_assert_held
-#define lockdep_assert_held(l)			do { } while (0)
-#endif
-
-#endif /* __BACKPORT_LINUX_LOCKDEP_H */
diff --git a/backport-include/linux/mm.h b/backport-include/linux/mm.h
index 8671d29..d6cbffe 100644
--- a/backport-include/linux/mm.h
+++ b/backport-include/linux/mm.h
@@ -25,4 +25,26 @@
 int vm_iomap_memory(struct vm_area_struct *vma, phys_addr_t start, unsigned long len);
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)
+#define kvfree LINUX_BACKPORT(kvfree)
+void kvfree(const void *addr);
+#endif /* < 3.15 */
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,20,0))
+#define get_user_pages_unlocked LINUX_BACKPORT(get_user_pages_unlocked)
+static inline long
+get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
+			unsigned long start, unsigned long nr_pages,
+			int write, int force, struct page **pages)
+{
+	long err;
+
+	down_read(&mm->mmap_sem);
+	err = get_user_pages(tsk, mm, start, nr_pages, write, force, pages,
+			     NULL);
+	up_read(&mm->mmap_sem);
+
+	return err;
+}
+#endif
 #endif /* __BACKPORT_MM_H */
diff --git a/backport-include/linux/netdevice.h b/backport-include/linux/netdevice.h
index 961450a..ae67771 100644
--- a/backport-include/linux/netdevice.h
+++ b/backport-include/linux/netdevice.h
@@ -163,6 +163,22 @@
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0)
+#ifndef NET_NAME_UNKNOWN
+#define NET_NAME_UNKNOWN	0
+#endif
+#ifndef NET_NAME_ENUM
+#define NET_NAME_ENUM		1
+#endif
+#ifndef NET_NAME_PREDICTABLE
+#define NET_NAME_PREDICTABLE	2
+#endif
+#ifndef NET_NAME_USER
+#define NET_NAME_USER		3
+#endif
+#ifndef NET_NAME_RENAMED
+#define NET_NAME_RENAMED	4
+#endif
+
 #define alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, txqs, rxqs) \
 	alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
 
@@ -234,4 +250,14 @@
 }
 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) */
 
+#ifndef IFF_TX_SKB_SHARING
+#define IFF_TX_SKB_SHARING 0
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0)
+netdev_features_t passthru_features_check(struct sk_buff *skb,
+					  struct net_device *dev,
+					  netdev_features_t features);
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0) */
+
 #endif /* __BACKPORT_NETDEVICE_H */
diff --git a/backport-include/linux/of.h b/backport-include/linux/of.h
index b289bcb..ef9c6aa 100644
--- a/backport-include/linux/of.h
+++ b/backport-include/linux/of.h
@@ -64,6 +64,16 @@
 {
 	return of_property_read_u32_array(np, propname, out_value, 1);
 }
+#ifndef CONFIG_OF
+#define of_get_property LINUX_BACKPORT(of_get_property)
+static inline const void *of_get_property(const struct device_node *node,
+				const char *name,
+				int *lenp)
+{
+	return NULL;
+}
+
+#endif
 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0) */
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
diff --git a/backport-include/linux/pnp.h b/backport-include/linux/pnp.h
new file mode 100644
index 0000000..0a88455
--- /dev/null
+++ b/backport-include/linux/pnp.h
@@ -0,0 +1,19 @@
+#ifndef __BACKPORT_LINUX_PNP_H
+#define __BACKPORT_LINUX_PNP_H
+#include_next <linux/pnp.h>
+
+#ifndef module_pnp_driver
+/**
+ * module_pnp_driver() - Helper macro for registering a PnP driver
+ * @__pnp_driver: pnp_driver struct
+ *
+ * Helper macro for PnP drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_pnp_driver(__pnp_driver) \
+	module_driver(__pnp_driver, pnp_register_driver, \
+				    pnp_unregister_driver)
+#endif
+
+#endif /* __BACKPORT_LINUX_PNP_H */
diff --git a/backport-include/linux/skbuff.h b/backport-include/linux/skbuff.h
index 687297c..a21e6cc 100644
--- a/backport-include/linux/skbuff.h
+++ b/backport-include/linux/skbuff.h
@@ -15,14 +15,15 @@
 #define __pskb_copy LINUX_BACKPORT(__pskb_copy)
 extern struct sk_buff *__pskb_copy(struct sk_buff *skb,
 				   int headroom, gfp_t gfp_mask);
-#endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
 #define skb_complete_wifi_ack LINUX_BACKPORT(skb_complete_wifi_ack)
 static inline void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)
 {
 	WARN_ON(1);
 }
+
+/* define to 0 so checks for it are always false */
+#define SKBTX_WIFI_STATUS 0
 #elif LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
 #define skb_complete_wifi_ack LINUX_BACKPORT(skb_complete_wifi_ack)
 void skb_complete_wifi_ack(struct sk_buff *skb, bool acked);
diff --git a/backport-include/linux/time.h b/backport-include/linux/time.h
new file mode 100644
index 0000000..3191047
--- /dev/null
+++ b/backport-include/linux/time.h
@@ -0,0 +1,7 @@
+#ifndef __BACKPORT_LINUX_TIME_H
+#define __BACKPORT_LINUX_TIME_H
+#include_next <linux/time.h>
+
+#include <linux/time64.h>
+
+#endif /* __BACKPORT_LINUX_TIME_H */
diff --git a/backport-include/linux/time64.h b/backport-include/linux/time64.h
new file mode 100644
index 0000000..31a7f1b
--- /dev/null
+++ b/backport-include/linux/time64.h
@@ -0,0 +1,24 @@
+#ifndef __BACKPORT_LINUX_TIME64_H
+#define __BACKPORT_LINUX_TIME64_H
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+#include_next <linux/time64.h>
+#else
+#include <linux/time.h>
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0)
+#define timespec64_equal		timespec_equal
+#define timespec64_compare		timespec_compare
+#define set_normalized_timespec64	set_normalized_timespec
+#define timespec64_add_safe		timespec_add_safe
+#define timespec64_add			timespec_add
+#define timespec64_sub			timespec_sub
+#define timespec64_valid		timespec_valid
+#define timespec64_valid_strict		timespec_valid_strict
+#define timespec64_to_ns		timespec_to_ns
+#define ns_to_timespec64		ns_to_timespec
+#define timespec64_add_ns		timespec_add_ns
+#define timespec64			timespec
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0) */
+
+#endif /* __BACKPORT_LINUX_TIME64_H */
diff --git a/backport-include/linux/timecounter.h b/backport-include/linux/timecounter.h
new file mode 100644
index 0000000..92fafb9
--- /dev/null
+++ b/backport-include/linux/timecounter.h
@@ -0,0 +1,25 @@
+#ifndef __BACKPORT_LINUX_TIMECOUNTER_H
+#define __BACKPORT_LINUX_TIMECOUNTER_H
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,20,0)
+#include_next <linux/timecounter.h>
+#else
+#include <linux/clocksource.h>
+
+/**
+ * timecounter_adjtime - Shifts the time of the clock.
+ * @delta:	Desired change in nanoseconds.
+ */
+#define timecounter_adjtime LINUX_BACKPORT(timecounter_adjtime)
+static inline void timecounter_adjtime(struct timecounter *tc, s64 delta)
+{
+	tc->nsec += delta;
+}
+#endif
+
+#ifndef CYCLECOUNTER_MASK
+/* simplify initialization of mask field */
+#define CYCLECOUNTER_MASK(bits) (cycle_t)((bits) < 64 ? ((1ULL<<(bits))-1) : -1)
+#endif
+
+#endif /* __BACKPORT_LINUX_TIMECOUNTER_H */
diff --git a/backport-include/linux/tracepoint.h b/backport-include/linux/tracepoint.h
new file mode 100644
index 0000000..a695c6f
--- /dev/null
+++ b/backport-include/linux/tracepoint.h
@@ -0,0 +1,10 @@
+#include_next <linux/tracepoint.h>
+
+#ifndef __BACKPORT_LINUX_TRACEPOINT_H
+#define __BACKPORT_LINUX_TRACEPOINT_H
+
+#ifndef TRACE_DEFINE_ENUM
+#define TRACE_DEFINE_ENUM(a)
+#endif
+
+#endif /* __BACKPORT_LINUX_TRACEPOINT_H */
diff --git a/backport-include/linux/wait.h b/backport-include/linux/wait.h
index 4057ff1..86ad2b0 100644
--- a/backport-include/linux/wait.h
+++ b/backport-include/linux/wait.h
@@ -32,4 +32,48 @@
 int woken_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key);
 #endif
 
+/**
+ * For wait_on_bit_timeout() an extra member in struct wait_bit_key is needed.
+ * This was introuced in kernel 3.17 and we are only able to backport this
+ * function on these kernel versions.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
+#define out_of_line_wait_on_bit_timeout LINUX_BACKPORT(out_of_line_wait_on_bit_timeout)
+int out_of_line_wait_on_bit_timeout(void *, int, wait_bit_action_f *, unsigned, unsigned long);
+
+#define bit_wait_timeout LINUX_BACKPORT(bit_wait_timeout)
+extern int bit_wait_timeout(struct wait_bit_key *);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,20,0)
+#define wait_on_bit_timeout LINUX_BACKPORT(wait_on_bit_timeout)
+/**
+ * wait_on_bit_timeout - wait for a bit to be cleared or a timeout elapses
+ * @word: the word being waited on, a kernel virtual address
+ * @bit: the bit of the word being waited on
+ * @mode: the task state to sleep in
+ * @timeout: timeout, in jiffies
+ *
+ * Use the standard hashed waitqueue table to wait for a bit
+ * to be cleared. This is similar to wait_on_bit(), except also takes a
+ * timeout parameter.
+ *
+ * Returned value will be zero if the bit was cleared before the
+ * @timeout elapsed, or non-zero if the @timeout elapsed or process
+ * received a signal and the mode permitted wakeup on that signal.
+ */
+static inline int
+wait_on_bit_timeout(void *word, int bit, unsigned mode, unsigned long timeout)
+{
+	might_sleep();
+	if (!test_bit(bit, word))
+		return 0;
+	return out_of_line_wait_on_bit_timeout(word, bit,
+					       bit_wait_timeout,
+					       mode, timeout);
+}
+#endif
+#endif
+
 #endif /* __BACKPORT_LINUX_WAIT_H */
diff --git a/backport-include/net/genetlink.h b/backport-include/net/genetlink.h
index 0535b0f..5761268 100644
--- a/backport-include/net/genetlink.h
+++ b/backport-include/net/genetlink.h
@@ -18,7 +18,7 @@
 #define genl_dump_check_consistent(cb, user_hdr, family)
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)
 static inline int __real_genl_register_family(struct genl_family *family)
 {
 	return genl_register_family(family);
diff --git a/backport-include/net/iw_handler.h b/backport-include/net/iw_handler.h
new file mode 100644
index 0000000..11135c8
--- /dev/null
+++ b/backport-include/net/iw_handler.h
@@ -0,0 +1,28 @@
+#ifndef __BACKPORT_IW_HANDLER_H
+#define __BACKPORT_IW_HANDLER_H
+#include_next <net/iw_handler.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0)
+static inline char *
+iwe_stream_add_event_check(struct iw_request_info *info, char *stream,
+			   char *ends, struct iw_event *iwe, int event_len)
+{
+	char *res = iwe_stream_add_event(info, stream, ends, iwe, event_len);
+
+	if (res == stream)
+		return ERR_PTR(-E2BIG);
+	return res;
+}
+
+static inline char *
+iwe_stream_add_point_check(struct iw_request_info *info, char *stream,
+			   char *ends, struct iw_event *iwe, char *extra)
+{
+	char *res = iwe_stream_add_point(info, stream, ends, iwe, extra);
+
+	if (res == stream)
+		return ERR_PTR(-E2BIG);
+	return res;
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0) */
+#endif /* __BACKPORT_IW_HANDLER_H */
diff --git a/backport-include/net/net_namespace.h b/backport-include/net/net_namespace.h
index e83b103..90c044f 100644
--- a/backport-include/net/net_namespace.h
+++ b/backport-include/net/net_namespace.h
@@ -24,4 +24,39 @@
 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0) */
 #endif /* CPTCFG_IEEE802154_6LOWPAN */
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,20,0)
+/*
+ * In older kernels we simply fail this function.
+ */
+#define get_net_ns_by_fd	LINUX_BACKPORT(get_net_ns_by_fd)
+static inline struct net *get_net_ns_by_fd(int fd)
+{
+	return ERR_PTR(-EINVAL);
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0)
+typedef struct {
+#ifdef CONFIG_NET_NS
+	struct net *net;
+#endif
+} possible_net_t;
+
+static inline void possible_write_pnet(possible_net_t *pnet, struct net *net)
+{
+#ifdef CONFIG_NET_NS
+	pnet->net = net;
+#endif
+}
+
+static inline struct net *possible_read_pnet(const possible_net_t *pnet)
+{
+#ifdef CONFIG_NET_NS
+	return pnet->net;
+#else
+	return &init_net;
+#endif
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0) */
+
 #endif	/* _COMPAT_NET_NET_NAMESPACE_H */
diff --git a/backport-include/net/netlink.h b/backport-include/net/netlink.h
index 9324d02..35d9260 100644
--- a/backport-include/net/netlink.h
+++ b/backport-include/net/netlink.h
@@ -2,6 +2,7 @@
 #define __BACKPORT_NET_NETLINK_H
 #include_next <net/netlink.h>
 #include <linux/version.h>
+#include <linux/in6.h>
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
 /**
@@ -137,4 +138,33 @@
 #define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0)
+#define nla_put_in_addr LINUX_BACKPORT(nla_put_in_addr)
+static inline int nla_put_in_addr(struct sk_buff *skb, int attrtype,
+				  __be32 addr)
+{
+	return nla_put_be32(skb, attrtype, addr);
+}
+
+#define nla_put_in6_addr LINUX_BACKPORT(nla_put_in6_addr)
+static inline int nla_put_in6_addr(struct sk_buff *skb, int attrtype,
+				   const struct in6_addr *addr)
+{
+	return nla_put(skb, attrtype, sizeof(*addr), addr);
+}
+
+static inline __be32 nla_get_in_addr(const struct nlattr *nla)
+{
+	return *(__be32 *) nla_data(nla);
+}
+
+static inline struct in6_addr nla_get_in6_addr(const struct nlattr *nla)
+{
+	struct in6_addr tmp;
+
+	nla_memcpy(&tmp, nla, sizeof(tmp));
+	return tmp;
+}
+#endif /* < 4.1 */
+
 #endif /* __BACKPORT_NET_NETLINK_H */
diff --git a/backport-include/net/sock.h b/backport-include/net/sock.h
index ec67b9e..1a0a415 100644
--- a/backport-include/net/sock.h
+++ b/backport-include/net/sock.h
@@ -38,4 +38,9 @@
 #define SOCK_SELECT_ERR_QUEUE (SOCK_QUEUE_SHRUNK + 14)
 #endif
 
+#ifndef sock_skb_cb_check_size
+#define sock_skb_cb_check_size(size) \
+	BUILD_BUG_ON((size) > FIELD_SIZEOF(struct sk_buff, cb))
+#endif
+
 #endif /* __BACKPORT_NET_SOCK_H */
diff --git a/compat/Kconfig b/compat/Kconfig
index a5ae300..fc236cb 100644
--- a/compat/Kconfig
+++ b/compat/Kconfig
@@ -136,3 +136,12 @@
 	default y if BPAUTO_WANT_DEV_COREDUMP
 	#h-file linux/devcoredump.h
 	#c-file drivers/base/devcoredump.c
+
+config BPAUTO_RHASHTABLE
+	bool
+	# current API of rhashtable was introduced in version 4.1
+	depends on KERNEL_4_1
+	# not very nice - but better than always having it
+	default y if MAC80211
+	#h-file linux/rhashtable.h
+	#c-file lib/rhashtable.c
diff --git a/compat/Makefile b/compat/Makefile
index 36c4377..0ae40d2 100644
--- a/compat/Makefile
+++ b/compat/Makefile
@@ -25,9 +25,11 @@
 compat-$(CPTCFG_KERNEL_3_17) += backport-3.17.o
 compat-$(CPTCFG_KERNEL_3_18) += backport-3.18.o
 compat-$(CPTCFG_KERNEL_3_19) += backport-3.19.o
+compat-$(CPTCFG_KERNEL_4_1) += backport-4.1.o
 
 compat-$(CPTCFG_BPAUTO_BUILD_CRYPTO_CCM) += crypto-ccm.o
 compat-$(CPTCFG_BPAUTO_BUILD_DMA_SHARED_HELPERS) += dma-shared-helpers.o
+compat-$(CPTCFG_BPAUTO_RHASHTABLE) += lib-rhashtable.o
 compat-$(CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP) += drivers-base-devcoredump.o
 compat-$(CPTCFG_BPAUTO_BUILD_AVERAGE) += lib-average.o
 cordic-objs += lib-cordic.o
diff --git a/compat/backport-3.13.c b/compat/backport-3.13.c
index 553ed8f..6ce72b2 100644
--- a/compat/backport-3.13.c
+++ b/compat/backport-3.13.c
@@ -14,6 +14,8 @@
 #include <net/genetlink.h>
 #include <linux/delay.h>
 #include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
 
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0))
 #ifdef CONFIG_REGULATOR
@@ -91,6 +93,7 @@
 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) */
 
 /************* generic netlink backport *****************/
+#if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)
 
 #undef genl_register_family
 #undef genl_unregister_family
@@ -152,6 +155,8 @@
 }
 EXPORT_SYMBOL_GPL(backport_genl_unregister_family);
 
+#endif /* RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0) */
+
 #ifdef __BACKPORT_NET_GET_RANDOM_ONCE
 struct __net_random_once_work {
 	struct work_struct work;
@@ -252,3 +257,53 @@
 }
 EXPORT_SYMBOL_GPL(pci_device_is_present);
 #endif /* CONFIG_PCI */
+
+#ifdef CONFIG_HWMON
+struct device*
+hwmon_device_register_with_groups(struct device *dev, const char *name,
+				  void *drvdata,
+				  const struct attribute_group **groups)
+{
+	struct device *hwdev;
+
+	hwdev = hwmon_device_register(dev);
+	hwdev->groups = groups;
+	dev_set_drvdata(hwdev, drvdata);
+	return hwdev;
+}
+
+static void devm_hwmon_release(struct device *dev, void *res)
+{
+	struct device *hwdev = *(struct device **)res;
+
+	hwmon_device_unregister(hwdev);
+}
+
+struct device *
+devm_hwmon_device_register_with_groups(struct device *dev, const char *name,
+				       void *drvdata,
+				       const struct attribute_group **groups)
+{
+	struct device **ptr, *hwdev;
+
+	if (!dev)
+		return ERR_PTR(-EINVAL);
+
+	ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups);
+	if (IS_ERR(hwdev))
+		goto error;
+
+	*ptr = hwdev;
+	devres_add(dev, ptr);
+	return hwdev;
+
+error:
+	devres_free(ptr);
+	return hwdev;
+}
+EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups);
+#endif
diff --git a/compat/backport-3.15.c b/compat/backport-3.15.c
index 75216eb..c6f9d43 100644
--- a/compat/backport-3.15.c
+++ b/compat/backport-3.15.c
@@ -13,6 +13,8 @@
 #include <linux/device.h>
 #include <linux/of.h>
 #include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
 #include <net/net_namespace.h>
 
 #if IS_ENABLED(CPTCFG_IEEE802154_6LOWPAN)
@@ -88,3 +90,12 @@
 }
 EXPORT_SYMBOL_GPL(of_property_count_elems_of_size);
 #endif
+
+void kvfree(const void *addr)
+{
+	if (is_vmalloc_addr(addr))
+		vfree(addr);
+	else
+		kfree(addr);
+}
+EXPORT_SYMBOL_GPL(kvfree);
diff --git a/compat/backport-3.18.c b/compat/backport-3.18.c
index b3c3f21..9e1a026 100644
--- a/compat/backport-3.18.c
+++ b/compat/backport-3.18.c
@@ -14,6 +14,7 @@
 #include <scsi/fc/fc_fcoe.h>
 #include <linux/skbuff.h>
 #include <linux/errqueue.h>
+#include <linux/wait.h>
 
 /**
  * eth_get_headlen - determine the the length of header for an ethernet frame
@@ -194,3 +195,29 @@
 }
 EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
 #endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+int __sched out_of_line_wait_on_bit_timeout(
+	void *word, int bit, wait_bit_action_f *action,
+	unsigned mode, unsigned long timeout)
+{
+	wait_queue_head_t *wq = bit_waitqueue(word, bit);
+	DEFINE_WAIT_BIT(wait, word, bit);
+
+	wait.key.private = jiffies + timeout;
+	return __wait_on_bit(wq, &wait, action, mode);
+}
+EXPORT_SYMBOL_GPL(out_of_line_wait_on_bit_timeout);
+
+__sched int bit_wait_timeout(struct wait_bit_key *word)
+{
+	unsigned long now = ACCESS_ONCE(jiffies);
+	if (signal_pending_state(current->state, current))
+		return 1;
+	if (time_after_eq(now, word->private))
+		return -EAGAIN;
+	schedule_timeout(word->private - now);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(bit_wait_timeout);
+#endif
diff --git a/compat/backport-3.19.c b/compat/backport-3.19.c
index c633f38..0b59bef 100644
--- a/compat/backport-3.19.c
+++ b/compat/backport-3.19.c
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2014  Hauke Mehrtens <hauke@hauke-m.de>
  *
- * Backport functionality introduced in Linux 3.18.
+ * Backport functionality introduced in Linux 3.19.
  *
  * 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
diff --git a/compat/backport-4.1.c b/compat/backport-4.1.c
new file mode 100644
index 0000000..d5027bd
--- /dev/null
+++ b/compat/backport-4.1.c
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015  Stefan Assmann <sassmann@kpanic.de>
+ *
+ * Backport functionality introduced in Linux 4.1.
+ *
+ * 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/netdevice.h>
+
+netdev_features_t passthru_features_check(struct sk_buff *skb,
+					  struct net_device *dev,
+					  netdev_features_t features)
+{
+	return features;
+}
+EXPORT_SYMBOL_GPL(passthru_features_check);
diff --git a/compat/lib-rhashtable.c b/compat/lib-rhashtable.c
new file mode 100644
index 0000000..1b70b49
--- /dev/null
+++ b/compat/lib-rhashtable.c
@@ -0,0 +1,838 @@
+/*
+ * Resizable, Scalable, Concurrent Hash Table
+ *
+ * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
+ * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
+ *
+ * Code partially derived from nft_hash
+ * Rewritten with rehash code from br_multicast plus single list
+ * pointer as suggested by Josh Triplett
+ *
+ * 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/log2.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/jhash.h>
+#include <linux/random.h>
+#include <linux/rhashtable.h>
+#include <linux/err.h>
+#include <linux/export.h>
+
+#define HASH_DEFAULT_SIZE	64UL
+#define HASH_MIN_SIZE		4U
+#define BUCKET_LOCKS_PER_CPU   128UL
+
+static u32 head_hashfn(struct rhashtable *ht,
+		       const struct bucket_table *tbl,
+		       const struct rhash_head *he)
+{
+	return rht_head_hashfn(ht, tbl, he, ht->p);
+}
+
+#ifdef CONFIG_PROVE_LOCKING
+#define ASSERT_RHT_MUTEX(HT) BUG_ON(!lockdep_rht_mutex_is_held(HT))
+
+int lockdep_rht_mutex_is_held(struct rhashtable *ht)
+{
+	return (debug_locks) ? lockdep_is_held(&ht->mutex) : 1;
+}
+EXPORT_SYMBOL_GPL(lockdep_rht_mutex_is_held);
+
+int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash)
+{
+	spinlock_t *lock = rht_bucket_lock(tbl, hash);
+
+	return (debug_locks) ? lockdep_is_held(lock) : 1;
+}
+EXPORT_SYMBOL_GPL(lockdep_rht_bucket_is_held);
+#else
+#define ASSERT_RHT_MUTEX(HT)
+#endif
+
+
+static int alloc_bucket_locks(struct rhashtable *ht, struct bucket_table *tbl,
+			      gfp_t gfp)
+{
+	unsigned int i, size;
+#if defined(CONFIG_PROVE_LOCKING)
+	unsigned int nr_pcpus = 2;
+#else
+	unsigned int nr_pcpus = num_possible_cpus();
+#endif
+
+	nr_pcpus = min_t(unsigned int, nr_pcpus, 32UL);
+	size = roundup_pow_of_two(nr_pcpus * ht->p.locks_mul);
+
+	/* Never allocate more than 0.5 locks per bucket */
+	size = min_t(unsigned int, size, tbl->size >> 1);
+
+	if (sizeof(spinlock_t) != 0) {
+#ifdef CONFIG_NUMA
+		if (size * sizeof(spinlock_t) > PAGE_SIZE &&
+		    gfp == GFP_KERNEL)
+			tbl->locks = vmalloc(size * sizeof(spinlock_t));
+		else
+#endif
+		tbl->locks = kmalloc_array(size, sizeof(spinlock_t),
+					   gfp);
+		if (!tbl->locks)
+			return -ENOMEM;
+		for (i = 0; i < size; i++)
+			spin_lock_init(&tbl->locks[i]);
+	}
+	tbl->locks_mask = size - 1;
+
+	return 0;
+}
+
+static void bucket_table_free(const struct bucket_table *tbl)
+{
+	if (tbl)
+		kvfree(tbl->locks);
+
+	kvfree(tbl);
+}
+
+static void bucket_table_free_rcu(struct rcu_head *head)
+{
+	bucket_table_free(container_of(head, struct bucket_table, rcu));
+}
+
+static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
+					       size_t nbuckets,
+					       gfp_t gfp)
+{
+	struct bucket_table *tbl = NULL;
+	size_t size;
+	int i;
+
+	size = sizeof(*tbl) + nbuckets * sizeof(tbl->buckets[0]);
+	if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER) ||
+	    gfp != GFP_KERNEL)
+		tbl = kzalloc(size, gfp | __GFP_NOWARN | __GFP_NORETRY);
+	if (tbl == NULL && gfp == GFP_KERNEL)
+		tbl = vzalloc(size);
+	if (tbl == NULL)
+		return NULL;
+
+	tbl->size = nbuckets;
+
+	if (alloc_bucket_locks(ht, tbl, gfp) < 0) {
+		bucket_table_free(tbl);
+		return NULL;
+	}
+
+	INIT_LIST_HEAD(&tbl->walkers);
+
+	get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd));
+
+	for (i = 0; i < nbuckets; i++)
+		INIT_RHT_NULLS_HEAD(tbl->buckets[i], ht, i);
+
+	return tbl;
+}
+
+static struct bucket_table *rhashtable_last_table(struct rhashtable *ht,
+						  struct bucket_table *tbl)
+{
+	struct bucket_table *new_tbl;
+
+	do {
+		new_tbl = tbl;
+		tbl = rht_dereference_rcu(tbl->future_tbl, ht);
+	} while (tbl);
+
+	return new_tbl;
+}
+
+static int rhashtable_rehash_one(struct rhashtable *ht, unsigned int old_hash)
+{
+	struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
+	struct bucket_table *new_tbl = rhashtable_last_table(ht,
+		rht_dereference_rcu(old_tbl->future_tbl, ht));
+	struct rhash_head __rcu **pprev = &old_tbl->buckets[old_hash];
+	int err = -ENOENT;
+	struct rhash_head *head, *next, *entry;
+	spinlock_t *new_bucket_lock;
+	unsigned int new_hash;
+
+	rht_for_each(entry, old_tbl, old_hash) {
+		err = 0;
+		next = rht_dereference_bucket(entry->next, old_tbl, old_hash);
+
+		if (rht_is_a_nulls(next))
+			break;
+
+		pprev = &entry->next;
+	}
+
+	if (err)
+		goto out;
+
+	new_hash = head_hashfn(ht, new_tbl, entry);
+
+	new_bucket_lock = rht_bucket_lock(new_tbl, new_hash);
+
+	spin_lock_nested(new_bucket_lock, SINGLE_DEPTH_NESTING);
+	head = rht_dereference_bucket(new_tbl->buckets[new_hash],
+				      new_tbl, new_hash);
+
+	if (rht_is_a_nulls(head))
+		INIT_RHT_NULLS_HEAD(entry->next, ht, new_hash);
+	else
+		RCU_INIT_POINTER(entry->next, head);
+
+	rcu_assign_pointer(new_tbl->buckets[new_hash], entry);
+	spin_unlock(new_bucket_lock);
+
+	rcu_assign_pointer(*pprev, next);
+
+out:
+	return err;
+}
+
+static void rhashtable_rehash_chain(struct rhashtable *ht,
+				    unsigned int old_hash)
+{
+	struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
+	spinlock_t *old_bucket_lock;
+
+	old_bucket_lock = rht_bucket_lock(old_tbl, old_hash);
+
+	spin_lock_bh(old_bucket_lock);
+	while (!rhashtable_rehash_one(ht, old_hash))
+		;
+	old_tbl->rehash++;
+	spin_unlock_bh(old_bucket_lock);
+}
+
+static int rhashtable_rehash_attach(struct rhashtable *ht,
+				    struct bucket_table *old_tbl,
+				    struct bucket_table *new_tbl)
+{
+	/* Protect future_tbl using the first bucket lock. */
+	spin_lock_bh(old_tbl->locks);
+
+	/* Did somebody beat us to it? */
+	if (rcu_access_pointer(old_tbl->future_tbl)) {
+		spin_unlock_bh(old_tbl->locks);
+		return -EEXIST;
+	}
+
+	/* Make insertions go into the new, empty table right away. Deletions
+	 * and lookups will be attempted in both tables until we synchronize.
+	 */
+	rcu_assign_pointer(old_tbl->future_tbl, new_tbl);
+
+	/* Ensure the new table is visible to readers. */
+	smp_wmb();
+
+	spin_unlock_bh(old_tbl->locks);
+
+	return 0;
+}
+
+static int rhashtable_rehash_table(struct rhashtable *ht)
+{
+	struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
+	struct bucket_table *new_tbl;
+	struct rhashtable_walker *walker;
+	unsigned int old_hash;
+
+	new_tbl = rht_dereference(old_tbl->future_tbl, ht);
+	if (!new_tbl)
+		return 0;
+
+	for (old_hash = 0; old_hash < old_tbl->size; old_hash++)
+		rhashtable_rehash_chain(ht, old_hash);
+
+	/* Publish the new table pointer. */
+	rcu_assign_pointer(ht->tbl, new_tbl);
+
+	spin_lock(&ht->lock);
+	list_for_each_entry(walker, &old_tbl->walkers, list)
+		walker->tbl = NULL;
+	spin_unlock(&ht->lock);
+
+	/* Wait for readers. All new readers will see the new
+	 * table, and thus no references to the old table will
+	 * remain.
+	 */
+	call_rcu(&old_tbl->rcu, bucket_table_free_rcu);
+
+	return rht_dereference(new_tbl->future_tbl, ht) ? -EAGAIN : 0;
+}
+
+/**
+ * rhashtable_expand - Expand hash table while allowing concurrent lookups
+ * @ht:		the hash table to expand
+ *
+ * A secondary bucket array is allocated and the hash entries are migrated.
+ *
+ * This function may only be called in a context where it is safe to call
+ * synchronize_rcu(), e.g. not within a rcu_read_lock() section.
+ *
+ * The caller must ensure that no concurrent resizing occurs by holding
+ * ht->mutex.
+ *
+ * It is valid to have concurrent insertions and deletions protected by per
+ * bucket locks or concurrent RCU protected lookups and traversals.
+ */
+static int rhashtable_expand(struct rhashtable *ht)
+{
+	struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht);
+	int err;
+
+	ASSERT_RHT_MUTEX(ht);
+
+	old_tbl = rhashtable_last_table(ht, old_tbl);
+
+	new_tbl = bucket_table_alloc(ht, old_tbl->size * 2, GFP_KERNEL);
+	if (new_tbl == NULL)
+		return -ENOMEM;
+
+	err = rhashtable_rehash_attach(ht, old_tbl, new_tbl);
+	if (err)
+		bucket_table_free(new_tbl);
+
+	return err;
+}
+
+/**
+ * rhashtable_shrink - Shrink hash table while allowing concurrent lookups
+ * @ht:		the hash table to shrink
+ *
+ * This function shrinks the hash table to fit, i.e., the smallest
+ * size would not cause it to expand right away automatically.
+ *
+ * The caller must ensure that no concurrent resizing occurs by holding
+ * ht->mutex.
+ *
+ * The caller must ensure that no concurrent table mutations take place.
+ * It is however valid to have concurrent lookups if they are RCU protected.
+ *
+ * It is valid to have concurrent insertions and deletions protected by per
+ * bucket locks or concurrent RCU protected lookups and traversals.
+ */
+static int rhashtable_shrink(struct rhashtable *ht)
+{
+	struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht);
+	unsigned int size;
+	int err;
+
+	ASSERT_RHT_MUTEX(ht);
+
+	size = roundup_pow_of_two(atomic_read(&ht->nelems) * 3 / 2);
+	if (size < ht->p.min_size)
+		size = ht->p.min_size;
+
+	if (old_tbl->size <= size)
+		return 0;
+
+	if (rht_dereference(old_tbl->future_tbl, ht))
+		return -EEXIST;
+
+	new_tbl = bucket_table_alloc(ht, size, GFP_KERNEL);
+	if (new_tbl == NULL)
+		return -ENOMEM;
+
+	err = rhashtable_rehash_attach(ht, old_tbl, new_tbl);
+	if (err)
+		bucket_table_free(new_tbl);
+
+	return err;
+}
+
+static void rht_deferred_worker(struct work_struct *work)
+{
+	struct rhashtable *ht;
+	struct bucket_table *tbl;
+	int err = 0;
+
+	ht = container_of(work, struct rhashtable, run_work);
+	mutex_lock(&ht->mutex);
+
+	tbl = rht_dereference(ht->tbl, ht);
+	tbl = rhashtable_last_table(ht, tbl);
+
+	if (rht_grow_above_75(ht, tbl))
+		rhashtable_expand(ht);
+	else if (ht->p.automatic_shrinking && rht_shrink_below_30(ht, tbl))
+		rhashtable_shrink(ht);
+
+	err = rhashtable_rehash_table(ht);
+
+	mutex_unlock(&ht->mutex);
+
+	if (err)
+		schedule_work(&ht->run_work);
+}
+
+static bool rhashtable_check_elasticity(struct rhashtable *ht,
+					struct bucket_table *tbl,
+					unsigned int hash)
+{
+	unsigned int elasticity = ht->elasticity;
+	struct rhash_head *head;
+
+	rht_for_each(head, tbl, hash)
+		if (!--elasticity)
+			return true;
+
+	return false;
+}
+
+int rhashtable_insert_rehash(struct rhashtable *ht)
+{
+	struct bucket_table *old_tbl;
+	struct bucket_table *new_tbl;
+	struct bucket_table *tbl;
+	unsigned int size;
+	int err;
+
+	old_tbl = rht_dereference_rcu(ht->tbl, ht);
+	tbl = rhashtable_last_table(ht, old_tbl);
+
+	size = tbl->size;
+
+	if (rht_grow_above_75(ht, tbl))
+		size *= 2;
+	/* Do not schedule more than one rehash */
+	else if (old_tbl != tbl)
+		return -EBUSY;
+
+	new_tbl = bucket_table_alloc(ht, size, GFP_ATOMIC);
+	if (new_tbl == NULL) {
+		/* Schedule async resize/rehash to try allocation
+		 * non-atomic context.
+		 */
+		schedule_work(&ht->run_work);
+		return -ENOMEM;
+	}
+
+	err = rhashtable_rehash_attach(ht, tbl, new_tbl);
+	if (err) {
+		bucket_table_free(new_tbl);
+		if (err == -EEXIST)
+			err = 0;
+	} else
+		schedule_work(&ht->run_work);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(rhashtable_insert_rehash);
+
+int rhashtable_insert_slow(struct rhashtable *ht, const void *key,
+			   struct rhash_head *obj,
+			   struct bucket_table *tbl)
+{
+	struct rhash_head *head;
+	unsigned int hash;
+	int err;
+
+	tbl = rhashtable_last_table(ht, tbl);
+	hash = head_hashfn(ht, tbl, obj);
+	spin_lock_nested(rht_bucket_lock(tbl, hash), SINGLE_DEPTH_NESTING);
+
+	err = -EEXIST;
+	if (key && rhashtable_lookup_fast(ht, key, ht->p))
+		goto exit;
+
+	err = -EAGAIN;
+	if (rhashtable_check_elasticity(ht, tbl, hash) ||
+	    rht_grow_above_100(ht, tbl))
+		goto exit;
+
+	err = 0;
+
+	head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash);
+
+	RCU_INIT_POINTER(obj->next, head);
+
+	rcu_assign_pointer(tbl->buckets[hash], obj);
+
+	atomic_inc(&ht->nelems);
+
+exit:
+	spin_unlock(rht_bucket_lock(tbl, hash));
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(rhashtable_insert_slow);
+
+/**
+ * rhashtable_walk_init - Initialise an iterator
+ * @ht:		Table to walk over
+ * @iter:	Hash table Iterator
+ *
+ * This function prepares a hash table walk.
+ *
+ * Note that if you restart a walk after rhashtable_walk_stop you
+ * may see the same object twice.  Also, you may miss objects if
+ * there are removals in between rhashtable_walk_stop and the next
+ * call to rhashtable_walk_start.
+ *
+ * For a completely stable walk you should construct your own data
+ * structure outside the hash table.
+ *
+ * This function may sleep so you must not call it from interrupt
+ * context or with spin locks held.
+ *
+ * You must call rhashtable_walk_exit if this function returns
+ * successfully.
+ */
+int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter)
+{
+	iter->ht = ht;
+	iter->p = NULL;
+	iter->slot = 0;
+	iter->skip = 0;
+
+	iter->walker = kmalloc(sizeof(*iter->walker), GFP_KERNEL);
+	if (!iter->walker)
+		return -ENOMEM;
+
+	mutex_lock(&ht->mutex);
+	iter->walker->tbl = rht_dereference(ht->tbl, ht);
+	list_add(&iter->walker->list, &iter->walker->tbl->walkers);
+	mutex_unlock(&ht->mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_init);
+
+/**
+ * rhashtable_walk_exit - Free an iterator
+ * @iter:	Hash table Iterator
+ *
+ * This function frees resources allocated by rhashtable_walk_init.
+ */
+void rhashtable_walk_exit(struct rhashtable_iter *iter)
+{
+	mutex_lock(&iter->ht->mutex);
+	if (iter->walker->tbl)
+		list_del(&iter->walker->list);
+	mutex_unlock(&iter->ht->mutex);
+	kfree(iter->walker);
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_exit);
+
+/**
+ * rhashtable_walk_start - Start a hash table walk
+ * @iter:	Hash table iterator
+ *
+ * Start a hash table walk.  Note that we take the RCU lock in all
+ * cases including when we return an error.  So you must always call
+ * rhashtable_walk_stop to clean up.
+ *
+ * Returns zero if successful.
+ *
+ * Returns -EAGAIN if resize event occured.  Note that the iterator
+ * will rewind back to the beginning and you may use it immediately
+ * by calling rhashtable_walk_next.
+ */
+int rhashtable_walk_start(struct rhashtable_iter *iter)
+	__acquires(RCU)
+{
+	struct rhashtable *ht = iter->ht;
+
+	mutex_lock(&ht->mutex);
+
+	if (iter->walker->tbl)
+		list_del(&iter->walker->list);
+
+	rcu_read_lock();
+
+	mutex_unlock(&ht->mutex);
+
+	if (!iter->walker->tbl) {
+		iter->walker->tbl = rht_dereference_rcu(ht->tbl, ht);
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_start);
+
+/**
+ * rhashtable_walk_next - Return the next object and advance the iterator
+ * @iter:	Hash table iterator
+ *
+ * Note that you must call rhashtable_walk_stop when you are finished
+ * with the walk.
+ *
+ * Returns the next object or NULL when the end of the table is reached.
+ *
+ * Returns -EAGAIN if resize event occured.  Note that the iterator
+ * will rewind back to the beginning and you may continue to use it.
+ */
+void *rhashtable_walk_next(struct rhashtable_iter *iter)
+{
+	struct bucket_table *tbl = iter->walker->tbl;
+	struct rhashtable *ht = iter->ht;
+	struct rhash_head *p = iter->p;
+	void *obj = NULL;
+
+	if (p) {
+		p = rht_dereference_bucket_rcu(p->next, tbl, iter->slot);
+		goto next;
+	}
+
+	for (; iter->slot < tbl->size; iter->slot++) {
+		int skip = iter->skip;
+
+		rht_for_each_rcu(p, tbl, iter->slot) {
+			if (!skip)
+				break;
+			skip--;
+		}
+
+next:
+		if (!rht_is_a_nulls(p)) {
+			iter->skip++;
+			iter->p = p;
+			obj = rht_obj(ht, p);
+			goto out;
+		}
+
+		iter->skip = 0;
+	}
+
+	/* Ensure we see any new tables. */
+	smp_rmb();
+
+	iter->walker->tbl = rht_dereference_rcu(tbl->future_tbl, ht);
+	if (iter->walker->tbl) {
+		iter->slot = 0;
+		iter->skip = 0;
+		return ERR_PTR(-EAGAIN);
+	}
+
+	iter->p = NULL;
+
+out:
+
+	return obj;
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_next);
+
+/**
+ * rhashtable_walk_stop - Finish a hash table walk
+ * @iter:	Hash table iterator
+ *
+ * Finish a hash table walk.
+ */
+void rhashtable_walk_stop(struct rhashtable_iter *iter)
+	__releases(RCU)
+{
+	struct rhashtable *ht;
+	struct bucket_table *tbl = iter->walker->tbl;
+
+	if (!tbl)
+		goto out;
+
+	ht = iter->ht;
+
+	spin_lock(&ht->lock);
+	if (tbl->rehash < tbl->size)
+		list_add(&iter->walker->list, &tbl->walkers);
+	else
+		iter->walker->tbl = NULL;
+	spin_unlock(&ht->lock);
+
+	iter->p = NULL;
+
+out:
+	rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_stop);
+
+static size_t rounded_hashtable_size(const struct rhashtable_params *params)
+{
+	return max(roundup_pow_of_two(params->nelem_hint * 4 / 3),
+		   (unsigned long)params->min_size);
+}
+
+static u32 rhashtable_jhash2(const void *key, u32 length, u32 seed)
+{
+	return jhash2(key, length, seed);
+}
+
+/**
+ * rhashtable_init - initialize a new hash table
+ * @ht:		hash table to be initialized
+ * @params:	configuration parameters
+ *
+ * Initializes a new hash table based on the provided configuration
+ * parameters. A table can be configured either with a variable or
+ * fixed length key:
+ *
+ * Configuration Example 1: Fixed length keys
+ * struct test_obj {
+ *	int			key;
+ *	void *			my_member;
+ *	struct rhash_head	node;
+ * };
+ *
+ * struct rhashtable_params params = {
+ *	.head_offset = offsetof(struct test_obj, node),
+ *	.key_offset = offsetof(struct test_obj, key),
+ *	.key_len = sizeof(int),
+ *	.hashfn = jhash,
+ *	.nulls_base = (1U << RHT_BASE_SHIFT),
+ * };
+ *
+ * Configuration Example 2: Variable length keys
+ * struct test_obj {
+ *	[...]
+ *	struct rhash_head	node;
+ * };
+ *
+ * u32 my_hash_fn(const void *data, u32 len, u32 seed)
+ * {
+ *	struct test_obj *obj = data;
+ *
+ *	return [... hash ...];
+ * }
+ *
+ * struct rhashtable_params params = {
+ *	.head_offset = offsetof(struct test_obj, node),
+ *	.hashfn = jhash,
+ *	.obj_hashfn = my_hash_fn,
+ * };
+ */
+int rhashtable_init(struct rhashtable *ht,
+		    const struct rhashtable_params *params)
+{
+	struct bucket_table *tbl;
+	size_t size;
+
+	size = HASH_DEFAULT_SIZE;
+
+	if ((!params->key_len && !params->obj_hashfn) ||
+	    (params->obj_hashfn && !params->obj_cmpfn))
+		return -EINVAL;
+
+	if (params->nulls_base && params->nulls_base < (1U << RHT_BASE_SHIFT))
+		return -EINVAL;
+
+	if (params->nelem_hint)
+		size = rounded_hashtable_size(params);
+
+	memset(ht, 0, sizeof(*ht));
+	mutex_init(&ht->mutex);
+	spin_lock_init(&ht->lock);
+	memcpy(&ht->p, params, sizeof(*params));
+
+	if (params->min_size)
+		ht->p.min_size = roundup_pow_of_two(params->min_size);
+
+	if (params->max_size)
+		ht->p.max_size = rounddown_pow_of_two(params->max_size);
+
+	ht->p.min_size = max(ht->p.min_size, HASH_MIN_SIZE);
+
+	/* The maximum (not average) chain length grows with the
+	 * size of the hash table, at a rate of (log N)/(log log N).
+	 * The value of 16 is selected so that even if the hash
+	 * table grew to 2^32 you would not expect the maximum
+	 * chain length to exceed it unless we are under attack
+	 * (or extremely unlucky).
+	 *
+	 * As this limit is only to detect attacks, we don't need
+	 * to set it to a lower value as you'd need the chain
+	 * length to vastly exceed 16 to have any real effect
+	 * on the system.
+	 */
+	if (!params->insecure_elasticity)
+		ht->elasticity = 16;
+
+	if (params->locks_mul)
+		ht->p.locks_mul = roundup_pow_of_two(params->locks_mul);
+	else
+		ht->p.locks_mul = BUCKET_LOCKS_PER_CPU;
+
+	ht->key_len = ht->p.key_len;
+	if (!params->hashfn) {
+		ht->p.hashfn = jhash;
+
+		if (!(ht->key_len & (sizeof(u32) - 1))) {
+			ht->key_len /= sizeof(u32);
+			ht->p.hashfn = rhashtable_jhash2;
+		}
+	}
+
+	tbl = bucket_table_alloc(ht, size, GFP_KERNEL);
+	if (tbl == NULL)
+		return -ENOMEM;
+
+	atomic_set(&ht->nelems, 0);
+
+	RCU_INIT_POINTER(ht->tbl, tbl);
+
+	INIT_WORK(&ht->run_work, rht_deferred_worker);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rhashtable_init);
+
+/**
+ * rhashtable_free_and_destroy - free elements and destroy hash table
+ * @ht:		the hash table to destroy
+ * @free_fn:	callback to release resources of element
+ * @arg:	pointer passed to free_fn
+ *
+ * Stops an eventual async resize. If defined, invokes free_fn for each
+ * element to releasal resources. Please note that RCU protected
+ * readers may still be accessing the elements. Releasing of resources
+ * must occur in a compatible manner. Then frees the bucket array.
+ *
+ * This function will eventually sleep to wait for an async resize
+ * to complete. The caller is responsible that no further write operations
+ * occurs in parallel.
+ */
+void rhashtable_free_and_destroy(struct rhashtable *ht,
+				 void (*free_fn)(void *ptr, void *arg),
+				 void *arg)
+{
+	const struct bucket_table *tbl;
+	unsigned int i;
+
+	cancel_work_sync(&ht->run_work);
+
+	mutex_lock(&ht->mutex);
+	tbl = rht_dereference(ht->tbl, ht);
+	if (free_fn) {
+		for (i = 0; i < tbl->size; i++) {
+			struct rhash_head *pos, *next;
+
+			for (pos = rht_dereference(tbl->buckets[i], ht),
+			     next = !rht_is_a_nulls(pos) ?
+					rht_dereference(pos->next, ht) : NULL;
+			     !rht_is_a_nulls(pos);
+			     pos = next,
+			     next = !rht_is_a_nulls(pos) ?
+					rht_dereference(pos->next, ht) : NULL)
+				free_fn(rht_obj(ht, pos), arg);
+		}
+	}
+
+	bucket_table_free(tbl);
+	mutex_unlock(&ht->mutex);
+}
+EXPORT_SYMBOL_GPL(rhashtable_free_and_destroy);
+
+void rhashtable_destroy(struct rhashtable *ht)
+{
+	return rhashtable_free_and_destroy(ht, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(rhashtable_destroy);
diff --git a/defconfigs/wifi b/defconfigs/wifi
index 96496ed..3d26444 100644
--- a/defconfigs/wifi
+++ b/defconfigs/wifi
@@ -1,5 +1,7 @@
 CPTCFG_ADM8211=m
 CPTCFG_AR5523=m
+CPTCFG_AIRO=m
+CPTCFG_AIRO_CS=m
 CPTCFG_AT76C50X_USB=m
 CPTCFG_ATH10K=m
 CPTCFG_ATH10K_PCI=m
@@ -10,6 +12,7 @@
 CPTCFG_ATH9K_HTC=m
 CPTCFG_ATH9K=m
 CPTCFG_ATH_CARDS=m
+CPTCFG_ATMEL=m
 CPTCFG_B43LEGACY=m
 CPTCFG_B43=m
 CPTCFG_B43_PCMCIA=y
@@ -66,7 +69,9 @@
 CPTCFG_P54_PCI=m
 CPTCFG_P54_SPI=m
 CPTCFG_P54_USB=m
+CPTCFG_PCI_ATMEL=m
 CPTCFG_PCI_HERMES=m
+CPTCFG_PCMCIA_ATMEL=m
 CPTCFG_PCMCIA_HERMES=m
 CPTCFG_PCMCIA_SPECTRUM=m
 CPTCFG_PLX_HERMES=m
@@ -101,6 +106,7 @@
 CPTCFG_SSB_SDIOHOST=y
 CPTCFG_TMD_HERMES=m
 CPTCFG_USB_NET_RNDIS_WLAN=m
+CPTCFG_USB_ZD1201=m
 CPTCFG_WCN36XX=m
 CPTCFG_WIL6210=m
 CPTCFG_WL1251=m
diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
index b9686bb..2032a6b 100644
--- a/drivers/bcma/Kconfig
+++ b/drivers/bcma/Kconfig
@@ -27,6 +27,7 @@
 config BCMA_HOST_PCI
 	bool "Support for BCMA on PCI-host bus"
 	depends on BCMA_HOST_PCI_POSSIBLE
+	select BCMA_DRIVER_PCI
 	default y
 
 config BCMA_DRIVER_PCI_HOSTMODE
@@ -46,6 +47,22 @@
 
 	  If unsure, say N
 
+config BCMA_DRIVER_PCI
+	bool "BCMA Broadcom PCI core driver"
+	depends on BCMA && PCI
+	default y
+	help
+	  BCMA bus may have many versions of PCIe core. This driver
+	  supports:
+	  1) PCIe core working in clientmode
+	  2) PCIe Gen 2 clientmode core
+
+	  In general PCIe (Gen 2) clientmode core is required on PCIe
+	  hosted buses. It's responsible for initialization and basic
+	  hardware management.
+	  This driver is also prerequisite for a hostmode PCIe core
+	  support.
+
 config BCMA_DRIVER_MIPS
 	depends on !KERNEL_3_3
 	bool "BCMA Broadcom MIPS core driver"
diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile
index f6c9aab..a28db43 100644
--- a/drivers/bcma/Makefile
+++ b/drivers/bcma/Makefile
@@ -3,8 +3,8 @@
 bcma-y					+= driver_chipcommon_b.o
 bcma-$(CPTCFG_BCMA_SFLASH)		+= driver_chipcommon_sflash.o
 bcma-$(CPTCFG_BCMA_NFLASH)		+= driver_chipcommon_nflash.o
-bcma-y					+= driver_pci.o
-bcma-y					+= driver_pcie2.o
+bcma-$(CPTCFG_BCMA_DRIVER_PCI)		+= driver_pci.o
+bcma-$(CPTCFG_BCMA_DRIVER_PCI)		+= driver_pcie2.o
 bcma-$(CPTCFG_BCMA_DRIVER_PCI_HOSTMODE)	+= driver_pci_host.o
 bcma-$(CPTCFG_BCMA_DRIVER_MIPS)		+= driver_mips.o
 bcma-$(CPTCFG_BCMA_DRIVER_GMAC_CMN)	+= driver_gmac_cmn.o
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index cb91beb..7b649ac 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -25,27 +25,27 @@
 bool bcma_wait_value(struct bcma_device *core, u16 reg, u32 mask, u32 value,
 		     int timeout);
 void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core);
+void bcma_init_bus(struct bcma_bus *bus);
+void bcma_unregister_cores(struct bcma_bus *bus);
 int bcma_bus_register(struct bcma_bus *bus);
 void bcma_bus_unregister(struct bcma_bus *bus);
-int __init bcma_bus_early_register(struct bcma_bus *bus,
-				   struct bcma_device *core_cc,
-				   struct bcma_device *core_mips);
+int __init bcma_bus_early_register(struct bcma_bus *bus);
 #ifdef CONFIG_PM
 int bcma_bus_suspend(struct bcma_bus *bus);
 int bcma_bus_resume(struct bcma_bus *bus);
 #endif
 
 /* scan.c */
+void bcma_detect_chip(struct bcma_bus *bus);
 int bcma_bus_scan(struct bcma_bus *bus);
-int __init bcma_bus_scan_early(struct bcma_bus *bus,
-			       struct bcma_device_id *match,
-			       struct bcma_device *core);
-void bcma_init_bus(struct bcma_bus *bus);
 
 /* sprom.c */
 int bcma_sprom_get(struct bcma_bus *bus);
 
 /* driver_chipcommon.c */
+void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc);
+void bcma_core_chipcommon_init(struct bcma_drv_cc *cc);
+void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable);
 #ifdef CPTCFG_BCMA_DRIVER_MIPS
 void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
 extern struct platform_device bcma_pflash_dev;
@@ -56,6 +56,8 @@
 void bcma_core_chipcommon_b_free(struct bcma_drv_cc_b *ccb);
 
 /* driver_chipcommon_pmu.c */
+void bcma_pmu_early_init(struct bcma_drv_cc *cc);
+void bcma_pmu_init(struct bcma_drv_cc *cc);
 u32 bcma_pmu_get_alp_clock(struct bcma_drv_cc *cc);
 u32 bcma_pmu_get_cpu_clock(struct bcma_drv_cc *cc);
 
@@ -104,15 +106,84 @@
 #endif /* CPTCFG_BCMA_HOST_SOC && CONFIG_OF */
 
 /* driver_pci.c */
+#ifdef CPTCFG_BCMA_DRIVER_PCI
 u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address);
+void bcma_core_pci_early_init(struct bcma_drv_pci *pc);
+void bcma_core_pci_init(struct bcma_drv_pci *pc);
+void bcma_core_pci_up(struct bcma_drv_pci *pc);
+void bcma_core_pci_down(struct bcma_drv_pci *pc);
+#else
+static inline void bcma_core_pci_early_init(struct bcma_drv_pci *pc)
+{
+	WARN_ON(pc->core->bus->hosttype == BCMA_HOSTTYPE_PCI);
+}
+static inline void bcma_core_pci_init(struct bcma_drv_pci *pc)
+{
+	/* Initialization is required for PCI hosted bus */
+	WARN_ON(pc->core->bus->hosttype == BCMA_HOSTTYPE_PCI);
+}
+#endif
+
+/* driver_pcie2.c */
+#ifdef CPTCFG_BCMA_DRIVER_PCI
+void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2);
+void bcma_core_pcie2_up(struct bcma_drv_pcie2 *pcie2);
+#else
+static inline void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2)
+{
+	/* Initialization is required for PCI hosted bus */
+	WARN_ON(pcie2->core->bus->hosttype == BCMA_HOSTTYPE_PCI);
+}
+#endif
 
 extern int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc);
 
 #ifdef CPTCFG_BCMA_DRIVER_PCI_HOSTMODE
 bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc);
 void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
+#else
+static inline bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
+{
+	return false;
+}
+static inline void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
+{
+}
 #endif /* CPTCFG_BCMA_DRIVER_PCI_HOSTMODE */
 
+/**************************************************
+ * driver_mips.c
+ **************************************************/
+
+#ifdef CPTCFG_BCMA_DRIVER_MIPS
+unsigned int bcma_core_mips_irq(struct bcma_device *dev);
+void bcma_core_mips_early_init(struct bcma_drv_mips *mcore);
+void bcma_core_mips_init(struct bcma_drv_mips *mcore);
+#else
+static inline unsigned int bcma_core_mips_irq(struct bcma_device *dev)
+{
+	return 0;
+}
+static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore)
+{
+}
+static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore)
+{
+}
+#endif
+
+/**************************************************
+ * driver_gmac_cmn.c
+ **************************************************/
+
+#ifdef CPTCFG_BCMA_DRIVER_GMAC_CMN
+void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc);
+#else
+static inline void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc)
+{
+}
+#endif
+
 #ifdef CPTCFG_BCMA_DRIVER_GPIO
 /* driver_gpio.c */
 int bcma_gpio_init(struct bcma_drv_cc *cc);
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index 368c24c..470d541 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -79,7 +79,9 @@
 
 	if (cc->capabilities & BCMA_CC_CAP_PMU) {
 		if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706)
-			/* 4706 CC and PMU watchdogs are clocked at 1/4 of ALP clock */
+			/* 4706 CC and PMU watchdogs are clocked at 1/4 of ALP
+			 * clock
+			 */
 			return bcma_chipco_get_alp_clock(cc) / 4000;
 		else
 			/* based on 32KHz ILP clock */
@@ -97,7 +99,8 @@
 	wdt.driver_data = cc;
 	wdt.timer_set = bcma_chipco_watchdog_timer_set_wdt;
 	wdt.timer_set_ms = bcma_chipco_watchdog_timer_set_ms_wdt;
-	wdt.max_timer_ms = bcma_chipco_watchdog_get_max_timer(cc) / cc->ticks_per_ms;
+	wdt.max_timer_ms =
+		bcma_chipco_watchdog_get_max_timer(cc) / cc->ticks_per_ms;
 
 	pdev = platform_device_register_data(NULL, "bcm47xx-wdt",
 					     cc->core->bus->num, &wdt,
@@ -175,7 +178,6 @@
 u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
 {
 	u32 maxt;
-	enum bcma_clkmode clkmode;
 
 	maxt = bcma_chipco_watchdog_get_max_timer(cc);
 	if (cc->capabilities & BCMA_CC_CAP_PMU) {
@@ -185,8 +187,13 @@
 			ticks = maxt;
 		bcma_cc_write32(cc, BCMA_CC_PMU_WATCHDOG, ticks);
 	} else {
-		clkmode = ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC;
-		bcma_core_set_clockmode(cc->core, clkmode);
+		struct bcma_bus *bus = cc->core->bus;
+
+		if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4707 &&
+		    bus->chipinfo.id != BCMA_CHIP_ID_BCM53018)
+			bcma_core_set_clockmode(cc->core,
+						ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC);
+
 		if (ticks > maxt)
 			ticks = maxt;
 		/* instant NMI */
@@ -335,7 +342,8 @@
 				       | BCMA_CC_CORECTL_UARTCLKEN);
 		}
 	} else {
-		bcma_err(cc->core->bus, "serial not supported on this device ccrev: 0x%x\n", ccrev);
+		bcma_err(cc->core->bus, "serial not supported on this device ccrev: 0x%x\n",
+			 ccrev);
 		return;
 	}
 
diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c
index 598a6cd..74ccb02 100644
--- a/drivers/bcma/driver_gpio.c
+++ b/drivers/bcma/driver_gpio.c
@@ -17,6 +17,8 @@
 
 #include "bcma_private.h"
 
+#define BCMA_GPIO_MAX_PINS	32
+
 static inline struct bcma_drv_cc *bcma_gpio_get_cc(struct gpio_chip *chip)
 {
 	return container_of(chip, struct bcma_drv_cc, gpio);
@@ -76,7 +78,7 @@
 	bcma_chipco_gpio_pullup(cc, 1 << gpio, 0);
 }
 
-#if IS_BUILTIN(CONFIG_BCM47XX)
+#if IS_BUILTIN(CONFIG_BCM47XX) || IS_BUILTIN(CONFIG_ARCH_BCM_5301X)
 static int bcma_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
 {
 	struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
@@ -204,6 +206,7 @@
 
 int bcma_gpio_init(struct bcma_drv_cc *cc)
 {
+	struct bcma_bus *bus = cc->core->bus;
 	struct gpio_chip *chip = &cc->gpio;
 	int err;
 
@@ -215,14 +218,14 @@
 	chip->set		= bcma_gpio_set_value;
 	chip->direction_input	= bcma_gpio_direction_input;
 	chip->direction_output	= bcma_gpio_direction_output;
-#if IS_BUILTIN(CONFIG_BCM47XX)
+#if IS_BUILTIN(CONFIG_BCM47XX) || IS_BUILTIN(CONFIG_ARCH_BCM_5301X)
 	chip->to_irq		= bcma_gpio_to_irq;
 #endif
 #if IS_BUILTIN(CONFIG_OF)
 	if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
 		chip->of_node	= cc->core->dev.of_node;
 #endif
-	switch (cc->core->bus->chipinfo.id) {
+	switch (bus->chipinfo.id) {
 	case BCMA_CHIP_ID_BCM5357:
 	case BCMA_CHIP_ID_BCM53572:
 		chip->ngpio	= 32;
@@ -231,13 +234,17 @@
 		chip->ngpio	= 16;
 	}
 
-	/* There is just one SoC in one device and its GPIO addresses should be
-	 * deterministic to address them more easily. The other buses could get
-	 * a random base number. */
-	if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
-		chip->base		= 0;
-	else
-		chip->base		= -1;
+	/*
+	 * On MIPS we register GPIO devices (LEDs, buttons) using absolute GPIO
+	 * pin numbers. We don't have Device Tree there and we can't really use
+	 * relative (per chip) numbers.
+	 * So let's use predictable base for BCM47XX and "random" for all other.
+	 */
+#if IS_BUILTIN(CONFIG_BCM47XX)
+	chip->base		= bus->num * BCMA_GPIO_MAX_PINS;
+#else
+	chip->base		= -1;
+#endif
 
 	err = bcma_gpio_irq_domain_init(cc);
 	if (err)
diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c
index 04faf6d..24424f3 100644
--- a/drivers/bcma/driver_mips.c
+++ b/drivers/bcma/driver_mips.c
@@ -21,7 +21,7 @@
 #include <linux/serial_reg.h>
 #include <linux/time.h>
 #ifdef CONFIG_BCM47XX
-#include <bcm47xx_nvram.h>
+#include <linux/bcm47xx_nvram.h>
 #endif
 
 enum bcma_boot_dev {
diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c
index ab6acf2..f499a46 100644
--- a/drivers/bcma/driver_pci.c
+++ b/drivers/bcma/driver_pci.c
@@ -145,6 +145,47 @@
 }
 
 /**************************************************
+ * Early init.
+ **************************************************/
+
+static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
+{
+	struct bcma_device *core = pc->core;
+	u16 val16, core_index;
+	uint regoff;
+
+	regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
+	core_index = (u16)core->core_index;
+
+	val16 = pcicore_read16(pc, regoff);
+	if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
+	     != core_index) {
+		val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
+			(val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
+		pcicore_write16(pc, regoff, val16);
+	}
+}
+
+/*
+ * Apply some early fixes required before accessing SPROM.
+ * See also si_pci_fixcfg.
+ */
+void bcma_core_pci_early_init(struct bcma_drv_pci *pc)
+{
+	if (pc->early_setup_done)
+		return;
+
+	pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
+	if (pc->hostmode)
+		goto out;
+
+	bcma_core_pci_fixcfg(pc);
+
+out:
+	pc->early_setup_done = true;
+}
+
+/**************************************************
  * Workarounds.
  **************************************************/
 
@@ -175,24 +216,6 @@
 		                     tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN);
 }
 
-static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
-{
-	struct bcma_device *core = pc->core;
-	u16 val16, core_index;
-	uint regoff;
-
-	regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
-	core_index = (u16)core->core_index;
-
-	val16 = pcicore_read16(pc, regoff);
-	if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
-	     != core_index) {
-		val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
-			(val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
-		pcicore_write16(pc, regoff, val16);
-	}
-}
-
 /* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
 /* Needs to happen when coming out of 'standby'/'hibernate' */
 static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
@@ -216,7 +239,6 @@
 
 static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
 {
-	bcma_core_pci_fixcfg(pc);
 	bcma_pcicore_serdes_workaround(pc);
 	bcma_core_pci_config_fixup(pc);
 }
@@ -226,13 +248,11 @@
 	if (pc->setup_done)
 		return;
 
-#ifdef CPTCFG_BCMA_DRIVER_PCI_HOSTMODE
-	pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
+	bcma_core_pci_early_init(pc);
+
 	if (pc->hostmode)
 		bcma_core_pci_hostmode_init(pc);
-#endif /* CPTCFG_BCMA_DRIVER_PCI_HOSTMODE */
-
-	if (!pc->hostmode)
+	else
 		bcma_core_pci_clientmode_init(pc);
 }
 
@@ -262,39 +282,6 @@
 }
 EXPORT_SYMBOL_GPL(bcma_core_pci_power_save);
 
-int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
-			  bool enable)
-{
-	struct pci_dev *pdev;
-	u32 coremask, tmp;
-	int err = 0;
-
-	if (!pc || core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
-		/* This bcma device is not on a PCI host-bus. So the IRQs are
-		 * not routed through the PCI core.
-		 * So we must not enable routing through the PCI core. */
-		goto out;
-	}
-
-	pdev = pc->core->bus->host_pci;
-
-	err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
-	if (err)
-		goto out;
-
-	coremask = BIT(core->core_index) << 8;
-	if (enable)
-		tmp |= coremask;
-	else
-		tmp &= ~coremask;
-
-	err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
-
-out:
-	return err;
-}
-EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
-
 static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
 {
 	u32 w;
@@ -308,28 +295,12 @@
 	bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
 }
 
-void bcma_core_pci_up(struct bcma_bus *bus)
+void bcma_core_pci_up(struct bcma_drv_pci *pc)
 {
-	struct bcma_drv_pci *pc;
-
-	if (bus->hosttype != BCMA_HOSTTYPE_PCI)
-		return;
-
-	pc = &bus->drv_pci[0];
-
 	bcma_core_pci_extend_L1timer(pc, true);
 }
-EXPORT_SYMBOL_GPL(bcma_core_pci_up);
 
-void bcma_core_pci_down(struct bcma_bus *bus)
+void bcma_core_pci_down(struct bcma_drv_pci *pc)
 {
-	struct bcma_drv_pci *pc;
-
-	if (bus->hosttype != BCMA_HOSTTYPE_PCI)
-		return;
-
-	pc = &bus->drv_pci[0];
-
 	bcma_core_pci_extend_L1timer(pc, false);
 }
-EXPORT_SYMBOL_GPL(bcma_core_pci_down);
diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c
index c8a6b74..c42cec7 100644
--- a/drivers/bcma/driver_pci_host.c
+++ b/drivers/bcma/driver_pci_host.c
@@ -11,6 +11,7 @@
 
 #include "bcma_private.h"
 #include <linux/pci.h>
+#include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/bcma/bcma.h>
 #include <asm/paccess.h>
diff --git a/drivers/bcma/driver_pcie2.c b/drivers/bcma/driver_pcie2.c
index e4be537..b1a6e32 100644
--- a/drivers/bcma/driver_pcie2.c
+++ b/drivers/bcma/driver_pcie2.c
@@ -10,6 +10,7 @@
 
 #include "bcma_private.h"
 #include <linux/bcma/bcma.h>
+#include <linux/pci.h>
 
 /**************************************************
  * R/W ops.
@@ -156,14 +157,23 @@
 
 void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2)
 {
-	struct bcma_chipinfo *ci = &pcie2->core->bus->chipinfo;
+	struct bcma_bus *bus = pcie2->core->bus;
+	struct bcma_chipinfo *ci = &bus->chipinfo;
 	u32 tmp;
 
 	tmp = pcie2_read32(pcie2, BCMA_CORE_PCIE2_SPROM(54));
 	if ((tmp & 0xe) >> 1 == 2)
 		bcma_core_pcie2_cfg_write(pcie2, 0x4e0, 0x17);
 
-	/* TODO: Do we need pcie_reqsize? */
+	switch (bus->chipinfo.id) {
+	case BCMA_CHIP_ID_BCM4360:
+	case BCMA_CHIP_ID_BCM4352:
+		pcie2->reqsize = 1024;
+		break;
+	default:
+		pcie2->reqsize = 128;
+		break;
+	}
 
 	if (ci->id == BCMA_CHIP_ID_BCM4360 && ci->rev > 3)
 		bcma_core_pcie2_war_delay_perst_enab(pcie2, true);
@@ -173,3 +183,18 @@
 	pciedev_crwlpciegen2_180(pcie2);
 	pciedev_crwlpciegen2_182(pcie2);
 }
+
+/**************************************************
+ * Runtime ops.
+ **************************************************/
+
+void bcma_core_pcie2_up(struct bcma_drv_pcie2 *pcie2)
+{
+	struct bcma_bus *bus = pcie2->core->bus;
+	struct pci_dev *dev = bus->host_pci;
+	int err;
+
+	err = pcie_set_readrq(dev, pcie2->reqsize);
+	if (err)
+		bcma_err(bus, "Error setting PCI_EXP_DEVCTL_READRQ: %d\n", err);
+}
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index 34b40bb..9088fe0 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -13,10 +13,12 @@
 
 static void bcma_host_pci_switch_core(struct bcma_device *core)
 {
+	int win2 = core->bus->host_is_pcie2 ?
+		BCMA_PCIE2_BAR0_WIN2 : BCMA_PCI_BAR0_WIN2;
+
 	pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN,
 			       core->addr);
-	pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN2,
-			       core->wrap);
+	pci_write_config_dword(core->bus->host_pci, win2, core->wrap);
 	core->bus->mapped_core = core;
 	bcma_debug(core->bus, "Switched to core: 0x%X\n", core->id.id);
 }
@@ -211,16 +213,26 @@
 	/* Initialize struct, detect chip */
 	bcma_init_bus(bus);
 
+	/* Scan bus to find out generation of PCIe core */
+	err = bcma_bus_scan(bus);
+	if (err)
+		goto err_pci_unmap_mmio;
+
+	if (bcma_find_core(bus, BCMA_CORE_PCIE2))
+		bus->host_is_pcie2 = true;
+
 	/* Register */
 	err = bcma_bus_register(bus);
 	if (err)
-		goto err_pci_unmap_mmio;
+		goto err_unregister_cores;
 
 	pci_set_drvdata(dev, bus);
 
 out:
 	return err;
 
+err_unregister_cores:
+	bcma_unregister_cores(bus);
 err_pci_unmap_mmio:
 	pci_iounmap(dev, bus->mmio);
 err_pci_release_regions:
@@ -281,9 +293,12 @@
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4358) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4360) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4365) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a0) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43b1) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43227) },	/* 0xa8db, BCM43217 (sic!) */
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43228) },	/* 0xa8dc */
@@ -308,3 +323,65 @@
 {
 	pci_unregister_driver(&bcma_pci_bridge_driver);
 }
+
+/**************************************************
+ * Runtime ops for drivers.
+ **************************************************/
+
+/* See also pcicore_up */
+void bcma_host_pci_up(struct bcma_bus *bus)
+{
+	if (bus->hosttype != BCMA_HOSTTYPE_PCI)
+		return;
+
+	if (bus->host_is_pcie2)
+		bcma_core_pcie2_up(&bus->drv_pcie2);
+	else
+		bcma_core_pci_up(&bus->drv_pci[0]);
+}
+EXPORT_SYMBOL_GPL(bcma_host_pci_up);
+
+/* See also pcicore_down */
+void bcma_host_pci_down(struct bcma_bus *bus)
+{
+	if (bus->hosttype != BCMA_HOSTTYPE_PCI)
+		return;
+
+	if (!bus->host_is_pcie2)
+		bcma_core_pci_down(&bus->drv_pci[0]);
+}
+EXPORT_SYMBOL_GPL(bcma_host_pci_down);
+
+/* See also si_pci_setup */
+int bcma_host_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core,
+			  bool enable)
+{
+	struct pci_dev *pdev;
+	u32 coremask, tmp;
+	int err = 0;
+
+	if (bus->hosttype != BCMA_HOSTTYPE_PCI) {
+		/* This bcma device is not on a PCI host-bus. So the IRQs are
+		 * not routed through the PCI core.
+		 * So we must not enable routing through the PCI core. */
+		goto out;
+	}
+
+	pdev = bus->host_pci;
+
+	err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
+	if (err)
+		goto out;
+
+	coremask = BIT(core->core_index) << 8;
+	if (enable)
+		tmp |= coremask;
+	else
+		tmp &= ~coremask;
+
+	err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
+
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(bcma_host_pci_irq_ctl);
diff --git a/drivers/bcma/host_soc.c b/drivers/bcma/host_soc.c
index 1e1e679..0c37a88 100644
--- a/drivers/bcma/host_soc.c
+++ b/drivers/bcma/host_soc.c
@@ -193,7 +193,7 @@
 	int err;
 
 	/* Scan bus and initialize it */
-	err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips);
+	err = bcma_bus_early_register(bus);
 	if (err)
 		iounmap(bus->mmio);
 
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index 17fcf56..7d8bfbc 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -277,6 +277,18 @@
 	}
 }
 
+void bcma_init_bus(struct bcma_bus *bus)
+{
+	mutex_lock(&bcma_buses_mutex);
+	bus->num = bcma_bus_next_num++;
+	mutex_unlock(&bcma_buses_mutex);
+
+	INIT_LIST_HEAD(&bus->cores);
+	bus->nr_cores = 0;
+
+	bcma_detect_chip(bus);
+}
+
 static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
 {
 	int err;
@@ -360,17 +372,24 @@
 	return 0;
 }
 
-static void bcma_unregister_cores(struct bcma_bus *bus)
+void bcma_unregister_cores(struct bcma_bus *bus)
 {
 	struct bcma_device *core, *tmp;
 
 	list_for_each_entry_safe(core, tmp, &bus->cores, list) {
+		if (!core->dev_registered)
+			continue;
 		list_del(&core->list);
-		if (core->dev_registered)
-			device_unregister(&core->dev);
+		device_unregister(&core->dev);
 	}
 	if (bus->hosttype == BCMA_HOSTTYPE_SOC)
 		platform_device_unregister(bus->drv_cc.watchdog);
+
+	/* Now noone uses internally-handled cores, we can free them */
+	list_for_each_entry_safe(core, tmp, &bus->cores, list) {
+		list_del(&core->list);
+		kfree(core);
+	}
 }
 
 int bcma_bus_register(struct bcma_bus *bus)
@@ -378,10 +397,6 @@
 	int err;
 	struct bcma_device *core;
 
-	mutex_lock(&bcma_buses_mutex);
-	bus->num = bcma_bus_next_num++;
-	mutex_unlock(&bcma_buses_mutex);
-
 	/* Scan for devices (cores) */
 	err = bcma_bus_scan(bus);
 	if (err) {
@@ -396,6 +411,13 @@
 		bcma_core_chipcommon_early_init(&bus->drv_cc);
 	}
 
+	/* Early init PCIE core */
+	core = bcma_find_core(bus, BCMA_CORE_PCIE);
+	if (core) {
+		bus->drv_pci[0].core = core;
+		bcma_core_pci_early_init(&bus->drv_pci[0]);
+	}
+
 	/* Cores providing flash access go before SPROM init */
 	list_for_each_entry(core, &bus->cores, list) {
 		if (bcma_is_core_needed_early(core->id.id))
@@ -468,7 +490,6 @@
 
 void bcma_bus_unregister(struct bcma_bus *bus)
 {
-	struct bcma_device *cores[3];
 	int err;
 
 	err = bcma_gpio_unregister(&bus->drv_cc);
@@ -479,46 +500,23 @@
 
 	bcma_core_chipcommon_b_free(&bus->drv_cc_b);
 
-	cores[0] = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
-	cores[1] = bcma_find_core(bus, BCMA_CORE_PCIE);
-	cores[2] = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON);
-
 	bcma_unregister_cores(bus);
-
-	kfree(cores[2]);
-	kfree(cores[1]);
-	kfree(cores[0]);
 }
 
-int __init bcma_bus_early_register(struct bcma_bus *bus,
-				   struct bcma_device *core_cc,
-				   struct bcma_device *core_mips)
+/*
+ * This is a special version of bus registration function designed for SoCs.
+ * It scans bus and performs basic initialization of main cores only.
+ * Please note it requires memory allocation, however it won't try to sleep.
+ */
+int __init bcma_bus_early_register(struct bcma_bus *bus)
 {
 	int err;
 	struct bcma_device *core;
-	struct bcma_device_id match;
 
-	match.manuf = BCMA_MANUF_BCM;
-	match.id = bcma_cc_core_id(bus);
-	match.class = BCMA_CL_SIM;
-	match.rev = BCMA_ANY_REV;
-
-	/* Scan for chip common core */
-	err = bcma_bus_scan_early(bus, &match, core_cc);
+	/* Scan for devices (cores) */
+	err = bcma_bus_scan(bus);
 	if (err) {
-		bcma_err(bus, "Failed to scan for common core: %d\n", err);
-		return -1;
-	}
-
-	match.manuf = BCMA_MANUF_MIPS;
-	match.id = BCMA_CORE_MIPS_74K;
-	match.class = BCMA_CL_SIM;
-	match.rev = BCMA_ANY_REV;
-
-	/* Scan for mips core */
-	err = bcma_bus_scan_early(bus, &match, core_mips);
-	if (err) {
-		bcma_err(bus, "Failed to scan for mips core: %d\n", err);
+		bcma_err(bus, "Failed to scan bus: %d\n", err);
 		return -1;
 	}
 
diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c
index 9175207..df806b9 100644
--- a/drivers/bcma/scan.c
+++ b/drivers/bcma/scan.c
@@ -435,15 +435,12 @@
 	return 0;
 }
 
-void bcma_init_bus(struct bcma_bus *bus)
+void bcma_detect_chip(struct bcma_bus *bus)
 {
 	s32 tmp;
 	struct bcma_chipinfo *chipinfo = &(bus->chipinfo);
 	char chip_id[8];
 
-	INIT_LIST_HEAD(&bus->cores);
-	bus->nr_cores = 0;
-
 	bcma_scan_switch_core(bus, BCMA_ADDR_BASE);
 
 	tmp = bcma_scan_read32(bus, 0, BCMA_CC_ID);
@@ -464,6 +461,10 @@
 
 	int err, core_num = 0;
 
+	/* Skip if bus was already scanned (e.g. during early register) */
+	if (bus->nr_cores)
+		return 0;
+
 	erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
 	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
 		eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
@@ -522,61 +523,3 @@
 
 	return err;
 }
-
-int __init bcma_bus_scan_early(struct bcma_bus *bus,
-			       struct bcma_device_id *match,
-			       struct bcma_device *core)
-{
-	u32 erombase;
-	u32 __iomem *eromptr, *eromend;
-
-	int err = -ENODEV;
-	int core_num = 0;
-
-	erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
-	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
-		eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
-		if (!eromptr)
-			return -ENOMEM;
-	} else {
-		eromptr = bus->mmio;
-	}
-
-	eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
-
-	bcma_scan_switch_core(bus, erombase);
-
-	while (eromptr < eromend) {
-		memset(core, 0, sizeof(*core));
-		INIT_LIST_HEAD(&core->list);
-		core->bus = bus;
-
-		err = bcma_get_next_core(bus, &eromptr, match, core_num, core);
-		if (err == -ENODEV) {
-			core_num++;
-			continue;
-		} else if (err == -ENXIO)
-			continue;
-		else if (err == -ESPIPE)
-			break;
-		else if (err < 0)
-			goto out;
-
-		core->core_index = core_num++;
-		bus->nr_cores++;
-		bcma_info(bus, "Core %d found: %s (manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
-			  core->core_index, bcma_device_name(&core->id),
-			  core->id.manuf, core->id.id, core->id.rev,
-			  core->id.class);
-
-		list_add_tail(&core->list, &bus->cores);
-		err = 0;
-		break;
-	}
-
-out:
-	if (bus->hosttype == BCMA_HOSTTYPE_SOC)
-		iounmap(eromptr);
-
-	return err;
-}
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index efb037f..206edd3 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -579,7 +579,8 @@
 	u16 offset = BCMA_CC_SPROM;
 	u16 *sprom;
 	size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4,
-				 SSB_SPROMSIZE_WORDS_R10, };
+				 SSB_SPROMSIZE_WORDS_R10,
+				 SSB_SPROMSIZE_WORDS_R11, };
 	int i, err = 0;
 
 	if (!bus->drv_cc.core)
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index c30744e..02d972e 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -2,10 +2,20 @@
 menu "Bluetooth device drivers"
 	depends on BT
 
+config BT_INTEL
+	tristate
+	depends on m
+
+config BT_BCM
+	tristate
+	depends on m
+	depends on FW_LOADER
+
 config BT_HCIBTUSB
 	tristate "HCI USB driver"
 	depends on m
 	depends on USB
+	select BT_INTEL
 	help
 	  Bluetooth HCI USB driver.
 	  This driver is required if you want to use Bluetooth devices with
@@ -14,6 +24,17 @@
 	  Say Y here to compile support for Bluetooth USB devices into the
 	  kernel or say M to compile it as module (btusb).
 
+config BT_HCIBTUSB_BCM
+	bool "Broadcom protocol support"
+	depends on BT_HCIBTUSB
+	select BT_BCM
+	default y
+	help
+	  The Broadcom protocol support enables firmware and patchram
+	  download support for Broadcom Bluetooth controllers.
+
+	  Say Y here to compile support for Broadcom protocol.
+
 config BT_HCIBTSDIO
 	tristate "HCI SDIO driver"
 	depends on m
@@ -65,6 +86,7 @@
 config BT_HCIUART_ATH3K
 	bool "Atheros AR300x serial support"
 	depends on BT_HCIUART
+	select BT_HCIUART_H4
 	help
 	  HCIATH3K (HCI Atheros AR300x) is a serial protocol for
 	  communication between host and Atheros AR300x Bluetooth devices.
@@ -97,6 +119,27 @@
 
 	  Say Y here to compile support for Three-wire UART protocol.
 
+config BT_HCIUART_INTEL
+	bool "Intel protocol support"
+	depends on BT_HCIUART
+	select BT_INTEL
+	help
+	  The Intel protocol support enables Bluetooth HCI over serial
+	  port interface for Intel Bluetooth controllers.
+
+	  Say Y here to compile support for Intel protocol.
+
+config BT_HCIUART_BCM
+	bool "Broadcom protocol support"
+	depends on BT_HCIUART
+	select BT_HCIUART_H4
+	select BT_BCM
+	help
+	  The Broadcom protocol support enables Bluetooth HCI over serial
+	  port interface for Broadcom Bluetooth controllers.
+
+	  Say Y here to compile support for Broadcom protocol.
+
 config BT_HCIBCM203X
 	tristate "HCI BCM203x USB driver"
 	depends on m
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 3a1599e..1662a96 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -15,10 +15,12 @@
 obj-$(CPTCFG_BT_HCIBTUSB)	+= btusb.o
 obj-$(CPTCFG_BT_HCIBTSDIO)	+= btsdio.o
 
+obj-$(CPTCFG_BT_INTEL)		+= btintel.o
 obj-$(CPTCFG_BT_ATH3K)		+= ath3k.o
 obj-$(CPTCFG_BT_MRVL)		+= btmrvl.o
 obj-$(CPTCFG_BT_MRVL_SDIO)	+= btmrvl_sdio.o
 obj-$(CPTCFG_BT_WILINK)		+= btwilink.o
+obj-$(CPTCFG_BT_BCM)		+= btbcm.o
 
 btmrvl-y			:= btmrvl_main.o
 btmrvl-$(CONFIG_DEBUG_FS)	+= btmrvl_debugfs.o
@@ -29,6 +31,8 @@
 hci_uart-$(CPTCFG_BT_HCIUART_LL)	+= hci_ll.o
 hci_uart-$(CPTCFG_BT_HCIUART_ATH3K)	+= hci_ath.o
 hci_uart-$(CPTCFG_BT_HCIUART_3WIRE)	+= hci_h5.o
+hci_uart-$(CPTCFG_BT_HCIUART_INTEL)	+= hci_intel.o
+hci_uart-$(CPTCFG_BT_HCIUART_BCM)	+= hci_bcm.o
 hci_uart-objs				:= $(hci_uart-y)
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 544feb3..cd79894 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -65,6 +65,7 @@
 	/* Atheros AR3011 with sflash firmware*/
 	{ USB_DEVICE(0x0489, 0xE027) },
 	{ USB_DEVICE(0x0489, 0xE03D) },
+	{ USB_DEVICE(0x04F2, 0xAFF1) },
 	{ USB_DEVICE(0x0930, 0x0215) },
 	{ USB_DEVICE(0x0CF3, 0x3002) },
 	{ USB_DEVICE(0x0CF3, 0xE019) },
@@ -108,6 +109,7 @@
 	{ USB_DEVICE(0x13d3, 0x3393) },
 	{ USB_DEVICE(0x13d3, 0x3402) },
 	{ USB_DEVICE(0x13d3, 0x3408) },
+	{ USB_DEVICE(0x13d3, 0x3423) },
 	{ USB_DEVICE(0x13d3, 0x3432) },
 
 	/* Atheros AR5BBU12 with sflash firmware */
@@ -162,6 +164,7 @@
 	{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
 
 	/* Atheros AR5BBU22 with sflash firmware */
@@ -174,6 +177,8 @@
 #define USB_REQ_DFU_DNLOAD	1
 #define BULK_SIZE		4096
 #define FW_HDR_SIZE		20
+#define TIMEGAP_USEC_MIN	50
+#define TIMEGAP_USEC_MAX	100
 
 static int ath3k_load_firmware(struct usb_device *udev,
 				const struct firmware *firmware)
@@ -205,6 +210,9 @@
 	pipe = usb_sndbulkpipe(udev, 0x02);
 
 	while (count) {
+		/* workaround the compatibility issue with xHCI controller*/
+		usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX);
+
 		size = min_t(uint, count, BULK_SIZE);
 		memcpy(send_buf, firmware->data + sent, size);
 
@@ -302,6 +310,9 @@
 	pipe = usb_sndbulkpipe(udev, 0x02);
 
 	while (count) {
+		/* workaround the compatibility issue with xHCI controller*/
+		usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX);
+
 		size = min_t(uint, count, BULK_SIZE);
 		memcpy(send_buf, firmware->data + sent, size);
 
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index 7f9504d..57ebf89 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -696,6 +696,8 @@
 	hdev->flush = bfusb_flush;
 	hdev->send  = bfusb_send_frame;
 
+	set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
+
 	if (hci_register_dev(hdev) < 0) {
 		BT_ERR("Can't register HCI device");
 		hci_free_dev(hdev);
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 4f7e8d4..6de97b3 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -227,7 +227,6 @@
 	iobase = info->p_dev->resource[0]->start;
 
 	avail = bt3c_read(iobase, 0x7006);
-	//printk("bt3c_cs: receiving %d bytes\n", avail);
 
 	bt3c_address(iobase, 0x7480);
 	while (size < avail) {
@@ -250,7 +249,6 @@
 
 			bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);
 			inb(iobase + DATA_H);
-			//printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type);
 
 			switch (bt_cb(info->rx_skb)->pkt_type) {
 
@@ -364,7 +362,6 @@
 			if (stat & 0x0001)
 				bt3c_receive(info);
 			if (stat & 0x0002) {
-				//BT_ERR("Ack (stat=0x%04x)", stat);
 				clear_bit(XMIT_SENDING, &(info->tx_state));
 				bt3c_write_wakeup(info);
 			}
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
new file mode 100644
index 0000000..4bba866
--- /dev/null
+++ b/drivers/bluetooth/btbcm.c
@@ -0,0 +1,397 @@
+/*
+ *
+ *  Bluetooth support for Broadcom devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btbcm.h"
+
+#define VERSION "0.1"
+
+#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
+
+int btbcm_check_bdaddr(struct hci_dev *hdev)
+{
+	struct hci_rp_read_bd_addr *bda;
+	struct sk_buff *skb;
+
+	skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+			     HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		int err = PTR_ERR(skb);
+		BT_ERR("%s: BCM: Reading device address failed (%d)",
+		       hdev->name, err);
+		return err;
+	}
+
+	if (skb->len != sizeof(*bda)) {
+		BT_ERR("%s: BCM: Device address length mismatch", hdev->name);
+		kfree_skb(skb);
+		return -EIO;
+	}
+
+	bda = (struct hci_rp_read_bd_addr *)skb->data;
+	if (bda->status) {
+		BT_ERR("%s: BCM: Device address result failed (%02x)",
+		       hdev->name, bda->status);
+		kfree_skb(skb);
+		return -bt_to_errno(bda->status);
+	}
+
+	/* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
+	 * with no configured address.
+	 */
+	if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) {
+		BT_INFO("%s: BCM: Using default device address (%pMR)",
+			hdev->name, &bda->bdaddr);
+		set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+	}
+
+	kfree_skb(skb);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_check_bdaddr);
+
+int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+	struct sk_buff *skb;
+	int err;
+
+	skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		BT_ERR("%s: BCM: Change address command failed (%d)",
+		       hdev->name, err);
+		return err;
+	}
+	kfree_skb(skb);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_set_bdaddr);
+
+int btbcm_patchram(struct hci_dev *hdev, const char *firmware)
+{
+	const struct hci_command_hdr *cmd;
+	const struct firmware *fw;
+	const u8 *fw_ptr;
+	size_t fw_size;
+	struct sk_buff *skb;
+	u16 opcode;
+	int err;
+
+	err = request_firmware(&fw, firmware, &hdev->dev);
+	if (err < 0) {
+		BT_INFO("%s: BCM: Patch %s not found", hdev->name, firmware);
+		return err;
+	}
+
+	/* Start Download */
+	skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		BT_ERR("%s: BCM: Download Minidrv command failed (%d)",
+		       hdev->name, err);
+		goto done;
+	}
+	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)) {
+		const u8 *cmd_param;
+
+		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,
+			       firmware);
+			err = -EINVAL;
+			goto done;
+		}
+
+		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)) {
+			err = PTR_ERR(skb);
+			BT_ERR("%s: BCM: Patch command %04x failed (%d)",
+			       hdev->name, opcode, err);
+			goto done;
+		}
+		kfree_skb(skb);
+	}
+
+	/* 250 msec delay after Launch Ram completes */
+	msleep(250);
+
+done:
+	release_firmware(fw);
+	return err;
+}
+EXPORT_SYMBOL(btbcm_patchram);
+
+static int btbcm_reset(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+
+	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		int err = PTR_ERR(skb);
+		BT_ERR("%s: BCM: Reset failed (%d)", hdev->name, err);
+		return err;
+	}
+	kfree_skb(skb);
+
+	return 0;
+}
+
+static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+
+	skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+			     HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: BCM: Reading local version info failed (%ld)",
+		       hdev->name, PTR_ERR(skb));
+		return skb;
+	}
+
+	if (skb->len != sizeof(struct hci_rp_read_local_version)) {
+		BT_ERR("%s: BCM: Local version length mismatch", hdev->name);
+		kfree_skb(skb);
+		return ERR_PTR(-EIO);
+	}
+
+	return skb;
+}
+
+static struct sk_buff *btbcm_read_verbose_config(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+
+	skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: BCM: Read verbose config info failed (%ld)",
+		       hdev->name, PTR_ERR(skb));
+		return skb;
+	}
+
+	if (skb->len != 7) {
+		BT_ERR("%s: BCM: Verbose config length mismatch", hdev->name);
+		kfree_skb(skb);
+		return ERR_PTR(-EIO);
+	}
+
+	return skb;
+}
+
+static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+
+	skb = __hci_cmd_sync(hdev, 0xfc5a, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: BCM: Read USB product info failed (%ld)",
+		       hdev->name, PTR_ERR(skb));
+		return skb;
+	}
+
+	if (skb->len != 5) {
+		BT_ERR("%s: BCM: USB product length mismatch", hdev->name);
+		kfree_skb(skb);
+		return ERR_PTR(-EIO);
+	}
+
+	return skb;
+}
+
+static const struct {
+	u16 subver;
+	const char *name;
+} bcm_uart_subver_table[] = {
+	{ 0x410e, "BCM43341B0"	},	/* 002.001.014 */
+	{ }
+};
+
+static const struct {
+	u16 subver;
+	const char *name;
+} bcm_usb_subver_table[] = {
+	{ 0x210b, "BCM43142A0"	},	/* 001.001.011 */
+	{ 0x2112, "BCM4314A0"	},	/* 001.001.018 */
+	{ 0x2118, "BCM20702A0"	},	/* 001.001.024 */
+	{ 0x2126, "BCM4335A0"	},	/* 001.001.038 */
+	{ 0x220e, "BCM20702A1"	},	/* 001.002.014 */
+	{ 0x230f, "BCM4354A2"	},	/* 001.003.015 */
+	{ 0x4106, "BCM4335B0"	},	/* 002.001.006 */
+	{ 0x410e, "BCM20702B0"	},	/* 002.001.014 */
+	{ 0x6109, "BCM4335C0"	},	/* 003.001.009 */
+	{ 0x610c, "BCM4354"	},	/* 003.001.012 */
+	{ }
+};
+
+int btbcm_setup_patchram(struct hci_dev *hdev)
+{
+	char fw_name[64];
+	u16 subver, rev, pid, vid;
+	const char *hw_name = NULL;
+	struct sk_buff *skb;
+	struct hci_rp_read_local_version *ver;
+	int i, err;
+
+	/* Reset */
+	err = btbcm_reset(hdev);
+	if (err)
+		return err;
+
+	/* Read Local Version Info */
+	skb = btbcm_read_local_version(hdev);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	ver = (struct hci_rp_read_local_version *)skb->data;
+	rev = le16_to_cpu(ver->hci_rev);
+	subver = le16_to_cpu(ver->lmp_subver);
+	kfree_skb(skb);
+
+	/* Read Verbose Config Version Info */
+	skb = btbcm_read_verbose_config(hdev);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
+	kfree_skb(skb);
+
+	switch ((rev & 0xf000) >> 12) {
+	case 0:
+		for (i = 0; bcm_uart_subver_table[i].name; i++) {
+			if (subver == bcm_uart_subver_table[i].subver) {
+				hw_name = bcm_uart_subver_table[i].name;
+				break;
+			}
+		}
+
+		snprintf(fw_name, sizeof(fw_name), "brcm/%s.hcd",
+			 hw_name ? : "BCM");
+		break;
+	case 1:
+	case 2:
+		/* Read USB Product Info */
+		skb = btbcm_read_usb_product(hdev);
+		if (IS_ERR(skb))
+			return PTR_ERR(skb);
+
+		vid = get_unaligned_le16(skb->data + 1);
+		pid = get_unaligned_le16(skb->data + 3);
+		kfree_skb(skb);
+
+		for (i = 0; bcm_usb_subver_table[i].name; i++) {
+			if (subver == bcm_usb_subver_table[i].subver) {
+				hw_name = bcm_usb_subver_table[i].name;
+				break;
+			}
+		}
+
+		snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd",
+			 hw_name ? : "BCM", vid, pid);
+		break;
+	default:
+		return 0;
+	}
+
+	BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
+		hw_name ? : "BCM", (subver & 0x7000) >> 13,
+		(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
+
+	err = btbcm_patchram(hdev, fw_name);
+	if (err == -ENOENT)
+		return 0;
+
+	/* Reset */
+	err = btbcm_reset(hdev);
+	if (err)
+		return err;
+
+	/* Read Local Version Info */
+	skb = btbcm_read_local_version(hdev);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	ver = (struct hci_rp_read_local_version *)skb->data;
+	rev = le16_to_cpu(ver->hci_rev);
+	subver = le16_to_cpu(ver->lmp_subver);
+	kfree_skb(skb);
+
+	BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
+		hw_name ? : "BCM", (subver & 0x7000) >> 13,
+		(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
+
+	btbcm_check_bdaddr(hdev);
+
+	set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_setup_patchram);
+
+int btbcm_setup_apple(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+
+	/* Read Verbose Config Version Info */
+	skb = btbcm_read_verbose_config(hdev);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1],
+		get_unaligned_le16(skb->data + 5));
+	kfree_skb(skb);
+
+	set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_setup_apple);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h
new file mode 100644
index 0000000..7ad3975
--- /dev/null
+++ b/drivers/bluetooth/btbcm.h
@@ -0,0 +1,60 @@
+/*
+ *
+ *  Bluetooth support for Broadcom devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#if IS_ENABLED(CPTCFG_BT_BCM)
+
+int btbcm_check_bdaddr(struct hci_dev *hdev);
+int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
+int btbcm_patchram(struct hci_dev *hdev, const char *firmware);
+
+int btbcm_setup_patchram(struct hci_dev *hdev);
+int btbcm_setup_apple(struct hci_dev *hdev);
+
+#else
+
+static inline int btbcm_check_bdaddr(struct hci_dev *hdev)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int btbcm_patchram(struct hci_dev *hdev, const char *firmware)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int btbcm_setup_patchram(struct hci_dev *hdev)
+{
+	return 0;
+}
+
+static inline int btbcm_setup_apple(struct hci_dev *hdev)
+{
+	return 0;
+}
+
+#endif
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
new file mode 100644
index 0000000..2d43d42
--- /dev/null
+++ b/drivers/bluetooth/btintel.c
@@ -0,0 +1,101 @@
+/*
+ *
+ *  Bluetooth support for Intel devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btintel.h"
+
+#define VERSION "0.1"
+
+#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
+
+int btintel_check_bdaddr(struct hci_dev *hdev)
+{
+	struct hci_rp_read_bd_addr *bda;
+	struct sk_buff *skb;
+
+	skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+			     HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		int err = PTR_ERR(skb);
+		BT_ERR("%s: Reading Intel device address failed (%d)",
+		       hdev->name, err);
+		return err;
+	}
+
+	if (skb->len != sizeof(*bda)) {
+		BT_ERR("%s: Intel device address length mismatch", hdev->name);
+		kfree_skb(skb);
+		return -EIO;
+	}
+
+	bda = (struct hci_rp_read_bd_addr *)skb->data;
+	if (bda->status) {
+		BT_ERR("%s: Intel device address result failed (%02x)",
+		       hdev->name, bda->status);
+		kfree_skb(skb);
+		return -bt_to_errno(bda->status);
+	}
+
+	/* For some Intel based controllers, the default Bluetooth device
+	 * address 00:03:19:9E:8B:00 can be found. These controllers are
+	 * fully operational, but have the danger of duplicate addresses
+	 * and that in turn can cause problems with Bluetooth operation.
+	 */
+	if (!bacmp(&bda->bdaddr, BDADDR_INTEL)) {
+		BT_ERR("%s: Found Intel default device address (%pMR)",
+		       hdev->name, &bda->bdaddr);
+		set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+	}
+
+	kfree_skb(skb);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_check_bdaddr);
+
+int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+	struct sk_buff *skb;
+	int err;
+
+	skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		BT_ERR("%s: Changing Intel device address failed (%d)",
+		       hdev->name, err);
+		return err;
+	}
+	kfree_skb(skb);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_set_bdaddr);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
new file mode 100644
index 0000000..9f17144
--- /dev/null
+++ b/drivers/bluetooth/btintel.h
@@ -0,0 +1,89 @@
+/*
+ *
+ *  Bluetooth support for Intel devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+struct intel_version {
+	u8 status;
+	u8 hw_platform;
+	u8 hw_variant;
+	u8 hw_revision;
+	u8 fw_variant;
+	u8 fw_revision;
+	u8 fw_build_num;
+	u8 fw_build_ww;
+	u8 fw_build_yy;
+	u8 fw_patch_num;
+} __packed;
+
+struct intel_boot_params {
+	__u8     status;
+	__u8     otp_format;
+	__u8     otp_content;
+	__u8     otp_patch;
+	__le16   dev_revid;
+	__u8     secure_boot;
+	__u8     key_from_hdr;
+	__u8     key_type;
+	__u8     otp_lock;
+	__u8     api_lock;
+	__u8     debug_lock;
+	bdaddr_t otp_bdaddr;
+	__u8     min_fw_build_nn;
+	__u8     min_fw_build_cw;
+	__u8     min_fw_build_yy;
+	__u8     limited_cce;
+	__u8     unlocked_state;
+} __packed;
+
+struct intel_bootup {
+	__u8     zero;
+	__u8     num_cmds;
+	__u8     source;
+	__u8     reset_type;
+	__u8     reset_reason;
+	__u8     ddc_status;
+} __packed;
+
+struct intel_secure_send_result {
+	__u8     result;
+	__le16   opcode;
+	__u8     status;
+} __packed;
+
+#if IS_ENABLED(CPTCFG_BT_INTEL)
+
+int btintel_check_bdaddr(struct hci_dev *hdev);
+int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
+
+#else
+
+static inline int btintel_check_bdaddr(struct hci_dev *hdev)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+	return -EOPNOTSUPP;
+}
+
+#endif
diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h
index 330f8f8..086f0ec 100644
--- a/drivers/bluetooth/btmrvl_drv.h
+++ b/drivers/bluetooth/btmrvl_drv.h
@@ -28,9 +28,9 @@
 #define BTM_UPLD_SIZE			2312
 
 /* Time to wait until Host Sleep state change in millisecond */
-#define WAIT_UNTIL_HS_STATE_CHANGED	5000
+#define WAIT_UNTIL_HS_STATE_CHANGED	msecs_to_jiffies(5000)
 /* Time to wait for command response in millisecond */
-#define WAIT_UNTIL_CMD_RESP		5000
+#define WAIT_UNTIL_CMD_RESP		msecs_to_jiffies(5000)
 
 enum rdwr_status {
 	RDWR_STATUS_SUCCESS = 0,
@@ -104,12 +104,14 @@
 #ifdef CONFIG_DEBUG_FS
 	void *debugfs_data;
 #endif
+	bool surprise_removed;
 };
 
 #define MRVL_VENDOR_PKT			0xFE
 
 /* Vendor specific Bluetooth commands */
 #define BT_CMD_PSCAN_WIN_REPORT_ENABLE	0xFC03
+#define BT_CMD_ROUTE_SCO_TO_HOST	0xFC1D
 #define BT_CMD_SET_BDADDR		0xFC22
 #define BT_CMD_AUTO_SLEEP_MODE		0xFC23
 #define BT_CMD_HOST_SLEEP_CONFIG	0xFC59
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index 30939c9..de05deb 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -178,6 +178,11 @@
 	struct sk_buff *skb;
 	struct hci_command_hdr *hdr;
 
+	if (priv->surprise_removed) {
+		BT_ERR("Card is removed");
+		return -EFAULT;
+	}
+
 	skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC);
 	if (skb == NULL) {
 		BT_ERR("No free skb");
@@ -202,10 +207,14 @@
 	wake_up_interruptible(&priv->main_thread.wait_q);
 
 	if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
-				priv->adapter->cmd_complete,
-				msecs_to_jiffies(WAIT_UNTIL_CMD_RESP)))
+					      priv->adapter->cmd_complete ||
+					      priv->surprise_removed,
+					      WAIT_UNTIL_CMD_RESP))
 		return -ETIMEDOUT;
 
+	if (priv->surprise_removed)
+		return -EFAULT;
+
 	return 0;
 }
 
@@ -221,6 +230,18 @@
 }
 EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
 
+static int btmrvl_enable_sco_routing_to_host(struct btmrvl_private *priv)
+{
+	int ret;
+	u8 subcmd = 0;
+
+	ret = btmrvl_send_sync_cmd(priv, BT_CMD_ROUTE_SCO_TO_HOST, &subcmd, 1);
+	if (ret)
+		BT_ERR("BT_CMD_ROUTE_SCO_TO_HOST command failed: %#x", ret);
+
+	return ret;
+}
+
 int btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd)
 {
 	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
@@ -287,9 +308,10 @@
 	}
 
 	ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q,
-					       adapter->hs_state,
-			msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED));
-	if (ret < 0) {
+					       adapter->hs_state ||
+					       priv->surprise_removed,
+					       WAIT_UNTIL_HS_STATE_CHANGED);
+	if (ret < 0 || priv->surprise_removed) {
 		BT_ERR("event_hs_wait_q terminated (%d): %d,%d,%d",
 		       ret, adapter->hs_state, adapter->ps_state,
 		       adapter->wakeup_tries);
@@ -538,13 +560,18 @@
 static int btmrvl_setup(struct hci_dev *hdev)
 {
 	struct btmrvl_private *priv = hci_get_drvdata(hdev);
+	int ret;
 
-	btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+	ret = btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+	if (ret)
+		return ret;
 
 	priv->btmrvl_dev.gpio_gap = 0xffff;
 
 	btmrvl_check_device_tree(priv);
 
+	btmrvl_enable_sco_routing_to_host(priv);
+
 	btmrvl_pscan_window_reporting(priv, 0x01);
 
 	priv->btmrvl_dev.psmode = 1;
@@ -597,7 +624,7 @@
 		add_wait_queue(&thread->wait_q, &wait);
 
 		set_current_state(TASK_INTERRUPTIBLE);
-		if (kthread_should_stop()) {
+		if (kthread_should_stop() || priv->surprise_removed) {
 			BT_DBG("main_thread: break from main thread");
 			break;
 		}
@@ -616,6 +643,11 @@
 
 		BT_DBG("main_thread woke up");
 
+		if (kthread_should_stop() || priv->surprise_removed) {
+			BT_DBG("main_thread: break from main thread");
+			break;
+		}
+
 		spin_lock_irqsave(&priv->driver_lock, flags);
 		if (adapter->int_count) {
 			adapter->int_count = 0;
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index 0057c0b..01d6da5 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -573,7 +573,7 @@
 		offset += txlen;
 	} while (true);
 
-	BT_DBG("FW download over, size %d bytes", offset);
+	BT_INFO("FW download over, size %d bytes", offset);
 
 	ret = 0;
 
@@ -798,6 +798,9 @@
 
 	priv = card->priv;
 
+	if (priv->surprise_removed)
+		return;
+
 	if (card->reg->int_read_to_clear)
 		ret = btmrvl_sdio_read_to_clear(card, &ireg);
 	else
@@ -1466,6 +1469,7 @@
 				btmrvl_sdio_disable_host_int(card);
 			}
 			BT_DBG("unregester dev");
+			card->priv->surprise_removed = true;
 			btmrvl_sdio_unregister_dev(card);
 			btmrvl_remove_card(card->priv);
 		}
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 61d9c77..9ac591c 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -24,11 +24,15 @@
 #include <linux/module.h>
 #include <linux/usb.h>
 #include <linux/firmware.h>
+#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-#define VERSION "0.6"
+#include "btintel.h"
+#include "btbcm.h"
+
+#define VERSION "0.8"
 
 static bool disable_scofix;
 static bool force_scofix;
@@ -49,13 +53,23 @@
 #define BTUSB_INTEL_BOOT	0x200
 #define BTUSB_BCM_PATCHRAM	0x400
 #define BTUSB_MARVELL		0x800
+#define BTUSB_SWAVE		0x1000
+#define BTUSB_INTEL_NEW		0x2000
+#define BTUSB_AMP		0x4000
+#define BTUSB_QCA_ROME		0x8000
+#define BTUSB_BCM_APPLE		0x10000
+#define BTUSB_REALTEK		0x20000
 
 static const struct usb_device_id btusb_table[] = {
 	/* Generic Bluetooth USB device */
 	{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
 
+	/* Generic Bluetooth AMP device */
+	{ USB_DEVICE_INFO(0xe0, 0x01, 0x04), .driver_info = BTUSB_AMP },
+
 	/* Apple-specific (Broadcom) devices */
-	{ USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01),
+	  .driver_info = BTUSB_BCM_APPLE },
 
 	/* MediaTek MT76x0E */
 	{ USB_DEVICE(0x0e8d, 0x763f) },
@@ -85,7 +99,7 @@
 	{ USB_DEVICE(0x05ac, 0x8281) },
 
 	/* AVM BlueFRITZ! USB v2.0 */
-	{ USB_DEVICE(0x057c, 0x3800) },
+	{ USB_DEVICE(0x057c, 0x3800), .driver_info = BTUSB_SWAVE },
 
 	/* Bluetooth Ultraport Module from IBM */
 	{ USB_DEVICE(0x04bf, 0x030a) },
@@ -101,30 +115,34 @@
 	{ USB_DEVICE(0x0c10, 0x0000) },
 
 	/* Broadcom BCM20702A0 */
-	{ USB_DEVICE(0x0489, 0xe042) },
-	{ USB_DEVICE(0x04ca, 0x2003) },
-	{ USB_DEVICE(0x0b05, 0x17b5) },
-	{ USB_DEVICE(0x0b05, 0x17cb) },
 	{ USB_DEVICE(0x413c, 0x8197) },
-	{ USB_DEVICE(0x13d3, 0x3404),
-	  .driver_info = BTUSB_BCM_PATCHRAM },
+
+	/* Broadcom BCM20702B0 (Dynex/Insignia) */
+	{ USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM },
 
 	/* Foxconn - Hon Hai */
 	{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01),
 	  .driver_info = BTUSB_BCM_PATCHRAM },
 
+	/* Lite-On Technology - Broadcom based */
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x04ca, 0xff, 0x01, 0x01),
+	  .driver_info = BTUSB_BCM_PATCHRAM },
+
 	/* Broadcom devices with vendor specific id */
 	{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01),
 	  .driver_info = BTUSB_BCM_PATCHRAM },
 
 	/* ASUSTek Computer - Broadcom based */
-	{ USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01),
+	  .driver_info = BTUSB_BCM_PATCHRAM },
 
 	/* Belkin F8065bf - Broadcom based */
-	{ USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01),
+	  .driver_info = BTUSB_BCM_PATCHRAM },
 
 	/* IMC Networks - Broadcom based */
-	{ USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01),
+	  .driver_info = BTUSB_BCM_PATCHRAM },
 
 	/* Intel Bluetooth USB Bootloader (RAM module) */
 	{ USB_DEVICE(0x8087, 0x0a5a),
@@ -145,6 +163,7 @@
 	/* Atheros 3011 with sflash firmware */
 	{ USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
 	{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x04f2, 0xaff1), .driver_info = BTUSB_IGNORE },
 	{ USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
 	{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
 	{ USB_DEVICE(0x0cf3, 0xe019), .driver_info = BTUSB_IGNORE },
@@ -188,6 +207,7 @@
 	{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
 
 	/* Atheros AR5BBU12 with sflash firmware */
@@ -197,6 +217,10 @@
 	{ USB_DEVICE(0x0489, 0xe036), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
 
+	/* QCA ROME chipset */
+	{ USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME },
+	{ USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME },
+
 	/* Broadcom BCM2035 */
 	{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
 	{ USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
@@ -237,6 +261,9 @@
 	/* CONWISE Technology based adapters with buggy SCO support */
 	{ USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC },
 
+	/* Roper Class 1 Bluetooth Dongle (Silicon Wave based) */
+	{ USB_DEVICE(0x1300, 0x0001), .driver_info = BTUSB_SWAVE },
+
 	/* Digianswer devices */
 	{ USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER },
 	{ USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE },
@@ -249,14 +276,42 @@
 	{ USB_DEVICE(0x16d3, 0x0002),
 	  .driver_info = BTUSB_SNIFFER | BTUSB_BROKEN_ISOC },
 
-	/* Intel Bluetooth device */
-	{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
-	{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
-
-	/* Marvell device */
+	/* Marvell Bluetooth devices */
 	{ USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL },
 	{ USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL },
 
+	/* Intel Bluetooth devices */
+	{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
+	{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
+	{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
+	{ USB_DEVICE(0x8087, 0x0a2b), .driver_info = BTUSB_INTEL_NEW },
+
+	/* Other Intel Bluetooth devices */
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x8087, 0xe0, 0x01, 0x01),
+	  .driver_info = BTUSB_IGNORE },
+
+	/* Realtek Bluetooth devices */
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
+	  .driver_info = BTUSB_REALTEK },
+
+	/* Additional Realtek 8723AE Bluetooth devices */
+	{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
+	{ USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK },
+
+	/* Additional Realtek 8723BE Bluetooth devices */
+	{ USB_DEVICE(0x0489, 0xe085), .driver_info = BTUSB_REALTEK },
+	{ USB_DEVICE(0x0489, 0xe08b), .driver_info = BTUSB_REALTEK },
+	{ USB_DEVICE(0x13d3, 0x3410), .driver_info = BTUSB_REALTEK },
+	{ USB_DEVICE(0x13d3, 0x3416), .driver_info = BTUSB_REALTEK },
+	{ USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK },
+
+	/* Additional Realtek 8821AE Bluetooth devices */
+	{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
+	{ USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
+	{ USB_DEVICE(0x13d3, 0x3458), .driver_info = BTUSB_REALTEK },
+	{ USB_DEVICE(0x13d3, 0x3461), .driver_info = BTUSB_REALTEK },
+	{ USB_DEVICE(0x13d3, 0x3462), .driver_info = BTUSB_REALTEK },
+
 	{ }	/* Terminating entry */
 };
 
@@ -267,6 +322,11 @@
 #define BTUSB_ISOC_RUNNING	2
 #define BTUSB_SUSPENDING	3
 #define BTUSB_DID_ISO_RESUME	4
+#define BTUSB_BOOTLOADER	5
+#define BTUSB_DOWNLOADING	6
+#define BTUSB_FIRMWARE_LOADED	7
+#define BTUSB_FIRMWARE_FAILED	8
+#define BTUSB_BOOTING		9
 
 struct btusb_data {
 	struct hci_dev       *hdev;
@@ -300,12 +360,16 @@
 	struct usb_endpoint_descriptor *isoc_rx_ep;
 
 	__u8 cmdreq_type;
+	__u8 cmdreq;
 
 	unsigned int sco_num;
 	int isoc_altsetting;
 	int suspend_count;
 
+	int (*recv_event)(struct hci_dev *hdev, struct sk_buff *skb);
 	int (*recv_bulk)(struct btusb_data *data, void *buffer, int count);
+
+	int (*setup_on_usb)(struct hci_dev *hdev);
 };
 
 static inline void btusb_free_frags(struct btusb_data *data)
@@ -370,7 +434,7 @@
 
 		if (bt_cb(skb)->expect == 0) {
 			/* Complete frame */
-			hci_recv_frame(data->hdev, skb);
+			data->recv_event(data->hdev, skb);
 			skb = NULL;
 		}
 	}
@@ -847,6 +911,15 @@
 
 	BT_DBG("%s", hdev->name);
 
+	/* Patching USB firmware files prior to starting any URBs of HCI path
+	 * It is more safe to use USB bulk channel for downloading USB patch
+	 */
+	if (data->setup_on_usb) {
+		err = data->setup_on_usb(hdev);
+		if (err < 0)
+			return err;
+	}
+
 	err = usb_autopm_get_interface(data->intf);
 	if (err < 0)
 		return err;
@@ -952,7 +1025,7 @@
 	}
 
 	dr->bRequestType = data->cmdreq_type;
-	dr->bRequest     = 0;
+	dr->bRequest     = data->cmdreq;
 	dr->wIndex       = 0;
 	dr->wValue       = 0;
 	dr->wLength      = __cpu_to_le16(skb->len);
@@ -1222,6 +1295,28 @@
 	usb_autopm_put_interface(data->intf);
 }
 
+static struct sk_buff *btusb_read_local_version(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+
+	skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+			     HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
+		       hdev->name, PTR_ERR(skb));
+		return skb;
+	}
+
+	if (skb->len != sizeof(struct hci_rp_read_local_version)) {
+		BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
+		       hdev->name);
+		kfree_skb(skb);
+		return ERR_PTR(-EIO);
+	}
+
+	return skb;
+}
+
 static int btusb_setup_bcm92035(struct hci_dev *hdev)
 {
 	struct sk_buff *skb;
@@ -1246,12 +1341,9 @@
 
 	BT_DBG("%s", hdev->name);
 
-	skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
-			     HCI_INIT_TIMEOUT);
-	if (IS_ERR(skb)) {
-		BT_ERR("Reading local version failed (%ld)", -PTR_ERR(skb));
+	skb = btusb_read_local_version(hdev);
+	if (IS_ERR(skb))
 		return -PTR_ERR(skb);
-	}
 
 	rp = (struct hci_rp_read_local_version *)skb->data;
 
@@ -1277,19 +1369,378 @@
 	return ret;
 }
 
-struct intel_version {
-	u8 status;
-	u8 hw_platform;
-	u8 hw_variant;
-	u8 hw_revision;
-	u8 fw_variant;
-	u8 fw_revision;
-	u8 fw_build_num;
-	u8 fw_build_ww;
-	u8 fw_build_yy;
-	u8 fw_patch_num;
+#define RTL_FRAG_LEN 252
+
+struct rtl_download_cmd {
+	__u8 index;
+	__u8 data[RTL_FRAG_LEN];
 } __packed;
 
+struct rtl_download_response {
+	__u8 status;
+	__u8 index;
+} __packed;
+
+struct rtl_rom_version_evt {
+	__u8 status;
+	__u8 version;
+} __packed;
+
+struct rtl_epatch_header {
+	__u8 signature[8];
+	__le32 fw_version;
+	__le16 num_patches;
+} __packed;
+
+#define RTL_EPATCH_SIGNATURE	"Realtech"
+#define RTL_ROM_LMP_3499	0x3499
+#define RTL_ROM_LMP_8723A	0x1200
+#define RTL_ROM_LMP_8723B	0x8723
+#define RTL_ROM_LMP_8821A	0x8821
+#define RTL_ROM_LMP_8761A	0x8761
+
+static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
+{
+	struct rtl_rom_version_evt *rom_version;
+	struct sk_buff *skb;
+	int ret;
+
+	/* Read RTL ROM version command */
+	skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: Read ROM version failed (%ld)",
+		       hdev->name, PTR_ERR(skb));
+		return PTR_ERR(skb);
+	}
+
+	if (skb->len != sizeof(*rom_version)) {
+		BT_ERR("%s: RTL version event length mismatch", hdev->name);
+		kfree_skb(skb);
+		return -EIO;
+	}
+
+	rom_version = (struct rtl_rom_version_evt *)skb->data;
+	BT_INFO("%s: rom_version status=%x version=%x",
+		hdev->name, rom_version->status, rom_version->version);
+
+	ret = rom_version->status;
+	if (ret == 0)
+		*version = rom_version->version;
+
+	kfree_skb(skb);
+	return ret;
+}
+
+static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
+				   const struct firmware *fw,
+				   unsigned char **_buf)
+{
+	const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
+	struct rtl_epatch_header *epatch_info;
+	unsigned char *buf;
+	int i, ret, len;
+	size_t min_size;
+	u8 opcode, length, data, rom_version = 0;
+	int project_id = -1;
+	const unsigned char *fwptr, *chip_id_base;
+	const unsigned char *patch_length_base, *patch_offset_base;
+	u32 patch_offset = 0;
+	u16 patch_length, num_patches;
+	const u16 project_id_to_lmp_subver[] = {
+		RTL_ROM_LMP_8723A,
+		RTL_ROM_LMP_8723B,
+		RTL_ROM_LMP_8821A,
+		RTL_ROM_LMP_8761A
+	};
+
+	ret = rtl_read_rom_version(hdev, &rom_version);
+	if (ret)
+		return -bt_to_errno(ret);
+
+	min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
+	if (fw->size < min_size)
+		return -EINVAL;
+
+	fwptr = fw->data + fw->size - sizeof(extension_sig);
+	if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) {
+		BT_ERR("%s: extension section signature mismatch", hdev->name);
+		return -EINVAL;
+	}
+
+	/* Loop from the end of the firmware parsing instructions, until
+	 * we find an instruction that identifies the "project ID" for the
+	 * hardware supported by this firwmare file.
+	 * Once we have that, we double-check that that project_id is suitable
+	 * for the hardware we are working with.
+	 */
+	while (fwptr >= fw->data + (sizeof(struct rtl_epatch_header) + 3)) {
+		opcode = *--fwptr;
+		length = *--fwptr;
+		data = *--fwptr;
+
+		BT_DBG("check op=%x len=%x data=%x", opcode, length, data);
+
+		if (opcode == 0xff) /* EOF */
+			break;
+
+		if (length == 0) {
+			BT_ERR("%s: found instruction with length 0",
+			       hdev->name);
+			return -EINVAL;
+		}
+
+		if (opcode == 0 && length == 1) {
+			project_id = data;
+			break;
+		}
+
+		fwptr -= length;
+	}
+
+	if (project_id < 0) {
+		BT_ERR("%s: failed to find version instruction", hdev->name);
+		return -EINVAL;
+	}
+
+	if (project_id >= ARRAY_SIZE(project_id_to_lmp_subver)) {
+		BT_ERR("%s: unknown project id %d", hdev->name, project_id);
+		return -EINVAL;
+	}
+
+	if (lmp_subver != project_id_to_lmp_subver[project_id]) {
+		BT_ERR("%s: firmware is for %x but this is a %x", hdev->name,
+		       project_id_to_lmp_subver[project_id], lmp_subver);
+		return -EINVAL;
+	}
+
+	epatch_info = (struct rtl_epatch_header *)fw->data;
+	if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) {
+		BT_ERR("%s: bad EPATCH signature", hdev->name);
+		return -EINVAL;
+	}
+
+	num_patches = le16_to_cpu(epatch_info->num_patches);
+	BT_DBG("fw_version=%x, num_patches=%d",
+	       le32_to_cpu(epatch_info->fw_version), num_patches);
+
+	/* After the rtl_epatch_header there is a funky patch metadata section.
+	 * Assuming 2 patches, the layout is:
+	 * ChipID1 ChipID2 PatchLength1 PatchLength2 PatchOffset1 PatchOffset2
+	 *
+	 * Find the right patch for this chip.
+	 */
+	min_size += 8 * num_patches;
+	if (fw->size < min_size)
+		return -EINVAL;
+
+	chip_id_base = fw->data + sizeof(struct rtl_epatch_header);
+	patch_length_base = chip_id_base + (sizeof(u16) * num_patches);
+	patch_offset_base = patch_length_base + (sizeof(u16) * num_patches);
+	for (i = 0; i < num_patches; i++) {
+		u16 chip_id = get_unaligned_le16(chip_id_base +
+						 (i * sizeof(u16)));
+		if (chip_id == rom_version + 1) {
+			patch_length = get_unaligned_le16(patch_length_base +
+							  (i * sizeof(u16)));
+			patch_offset = get_unaligned_le32(patch_offset_base +
+							  (i * sizeof(u32)));
+			break;
+		}
+	}
+
+	if (!patch_offset) {
+		BT_ERR("%s: didn't find patch for chip id %d",
+		       hdev->name, rom_version);
+		return -EINVAL;
+	}
+
+	BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
+	min_size = patch_offset + patch_length;
+	if (fw->size < min_size)
+		return -EINVAL;
+
+	/* Copy the firmware into a new buffer and write the version at
+	 * the end.
+	 */
+	len = patch_length;
+	buf = kmemdup(fw->data + patch_offset, patch_length, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4);
+
+	*_buf = buf;
+	return len;
+}
+
+static int rtl_download_firmware(struct hci_dev *hdev,
+				 const unsigned char *data, int fw_len)
+{
+	struct rtl_download_cmd *dl_cmd;
+	int frag_num = fw_len / RTL_FRAG_LEN + 1;
+	int frag_len = RTL_FRAG_LEN;
+	int ret = 0;
+	int i;
+
+	dl_cmd = kmalloc(sizeof(struct rtl_download_cmd), GFP_KERNEL);
+	if (!dl_cmd)
+		return -ENOMEM;
+
+	for (i = 0; i < frag_num; i++) {
+		struct rtl_download_response *dl_resp;
+		struct sk_buff *skb;
+
+		BT_DBG("download fw (%d/%d)", i, frag_num);
+
+		dl_cmd->index = i;
+		if (i == (frag_num - 1)) {
+			dl_cmd->index |= 0x80; /* data end */
+			frag_len = fw_len % RTL_FRAG_LEN;
+		}
+		memcpy(dl_cmd->data, data, frag_len);
+
+		/* Send download command */
+		skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd,
+				     HCI_INIT_TIMEOUT);
+		if (IS_ERR(skb)) {
+			BT_ERR("%s: download fw command failed (%ld)",
+			       hdev->name, PTR_ERR(skb));
+			ret = -PTR_ERR(skb);
+			goto out;
+		}
+
+		if (skb->len != sizeof(*dl_resp)) {
+			BT_ERR("%s: download fw event length mismatch",
+			       hdev->name);
+			kfree_skb(skb);
+			ret = -EIO;
+			goto out;
+		}
+
+		dl_resp = (struct rtl_download_response *)skb->data;
+		if (dl_resp->status != 0) {
+			kfree_skb(skb);
+			ret = bt_to_errno(dl_resp->status);
+			goto out;
+		}
+
+		kfree_skb(skb);
+		data += RTL_FRAG_LEN;
+	}
+
+out:
+	kfree(dl_cmd);
+	return ret;
+}
+
+static int btusb_setup_rtl8723a(struct hci_dev *hdev)
+{
+	struct btusb_data *data = dev_get_drvdata(&hdev->dev);
+	struct usb_device *udev = interface_to_usbdev(data->intf);
+	const struct firmware *fw;
+	int ret;
+
+	BT_INFO("%s: rtl: loading rtl_bt/rtl8723a_fw.bin", hdev->name);
+	ret = request_firmware(&fw, "rtl_bt/rtl8723a_fw.bin", &udev->dev);
+	if (ret < 0) {
+		BT_ERR("%s: Failed to load rtl_bt/rtl8723a_fw.bin", hdev->name);
+		return ret;
+	}
+
+	if (fw->size < 8) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Check that the firmware doesn't have the epatch signature
+	 * (which is only for RTL8723B and newer).
+	 */
+	if (!memcmp(fw->data, RTL_EPATCH_SIGNATURE, 8)) {
+		BT_ERR("%s: unexpected EPATCH signature!", hdev->name);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = rtl_download_firmware(hdev, fw->data, fw->size);
+
+out:
+	release_firmware(fw);
+	return ret;
+}
+
+static int btusb_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
+				const char *fw_name)
+{
+	struct btusb_data *data = dev_get_drvdata(&hdev->dev);
+	struct usb_device *udev = interface_to_usbdev(data->intf);
+	unsigned char *fw_data = NULL;
+	const struct firmware *fw;
+	int ret;
+
+	BT_INFO("%s: rtl: loading %s", hdev->name, fw_name);
+	ret = request_firmware(&fw, fw_name, &udev->dev);
+	if (ret < 0) {
+		BT_ERR("%s: Failed to load %s", hdev->name, fw_name);
+		return ret;
+	}
+
+	ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data);
+	if (ret < 0)
+		goto out;
+
+	ret = rtl_download_firmware(hdev, fw_data, ret);
+	kfree(fw_data);
+	if (ret < 0)
+		goto out;
+
+out:
+	release_firmware(fw);
+	return ret;
+}
+
+static int btusb_setup_realtek(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+	struct hci_rp_read_local_version *resp;
+	u16 lmp_subver;
+
+	skb = btusb_read_local_version(hdev);
+	if (IS_ERR(skb))
+		return -PTR_ERR(skb);
+
+	resp = (struct hci_rp_read_local_version *)skb->data;
+	BT_INFO("%s: rtl: examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
+		"lmp_subver=%04x", hdev->name, resp->hci_ver, resp->hci_rev,
+		resp->lmp_ver, resp->lmp_subver);
+
+	lmp_subver = le16_to_cpu(resp->lmp_subver);
+	kfree_skb(skb);
+
+	/* Match a set of subver values that correspond to stock firmware,
+	 * which is not compatible with standard btusb.
+	 * If matched, upload an alternative firmware that does conform to
+	 * standard btusb. Once that firmware is uploaded, the subver changes
+	 * to a different value.
+	 */
+	switch (lmp_subver) {
+	case RTL_ROM_LMP_8723A:
+	case RTL_ROM_LMP_3499:
+		return btusb_setup_rtl8723a(hdev);
+	case RTL_ROM_LMP_8723B:
+		return btusb_setup_rtl8723b(hdev, lmp_subver,
+					    "rtl_bt/rtl8723b_fw.bin");
+	case RTL_ROM_LMP_8821A:
+		return btusb_setup_rtl8723b(hdev, lmp_subver,
+					    "rtl_bt/rtl8821a_fw.bin");
+	case RTL_ROM_LMP_8761A:
+		return btusb_setup_rtl8723b(hdev, lmp_subver,
+					    "rtl_bt/rtl8761a_fw.bin");
+	default:
+		BT_INFO("rtl: assuming no firmware upload needed.");
+		return 0;
+	}
+}
+
 static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
 						       struct intel_version *ver)
 {
@@ -1446,51 +1897,6 @@
 	return 0;
 }
 
-#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
-
-static int btusb_check_bdaddr_intel(struct hci_dev *hdev)
-{
-	struct sk_buff *skb;
-	struct hci_rp_read_bd_addr *rp;
-
-	skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
-			     HCI_INIT_TIMEOUT);
-	if (IS_ERR(skb)) {
-		BT_ERR("%s reading Intel device address failed (%ld)",
-		       hdev->name, PTR_ERR(skb));
-		return PTR_ERR(skb);
-	}
-
-	if (skb->len != sizeof(*rp)) {
-		BT_ERR("%s Intel device address length mismatch", hdev->name);
-		kfree_skb(skb);
-		return -EIO;
-	}
-
-	rp = (struct hci_rp_read_bd_addr *)skb->data;
-	if (rp->status) {
-		BT_ERR("%s Intel device address result failed (%02x)",
-		       hdev->name, rp->status);
-		kfree_skb(skb);
-		return -bt_to_errno(rp->status);
-	}
-
-	/* For some Intel based controllers, the default Bluetooth device
-	 * address 00:03:19:9E:8B:00 can be found. These controllers are
-	 * fully operational, but have the danger of duplicate addresses
-	 * and that in turn can cause problems with Bluetooth operation.
-	 */
-	if (!bacmp(&rp->bdaddr, BDADDR_INTEL)) {
-		BT_ERR("%s found Intel default device address (%pMR)",
-		       hdev->name, &rp->bdaddr);
-		set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
-	}
-
-	kfree_skb(skb);
-
-	return 0;
-}
-
 static int btusb_setup_intel(struct hci_dev *hdev)
 {
 	struct sk_buff *skb;
@@ -1563,7 +1969,7 @@
 		BT_INFO("%s: Intel device is already patched. patch num: %02x",
 			hdev->name, ver->fw_patch_num);
 		kfree_skb(skb);
-		btusb_check_bdaddr_intel(hdev);
+		btintel_check_bdaddr(hdev);
 		return 0;
 	}
 
@@ -1576,7 +1982,7 @@
 	fw = btusb_setup_intel_get_fw(hdev, ver);
 	if (!fw) {
 		kfree_skb(skb);
-		btusb_check_bdaddr_intel(hdev);
+		btintel_check_bdaddr(hdev);
 		return 0;
 	}
 	fw_ptr = fw->data;
@@ -1657,7 +2063,7 @@
 	BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
 		hdev->name);
 
-	btusb_check_bdaddr_intel(hdev);
+	btintel_check_bdaddr(hdev);
 	return 0;
 
 exit_mfg_disable:
@@ -1673,7 +2079,7 @@
 
 	BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
 
-	btusb_check_bdaddr_intel(hdev);
+	btintel_check_bdaddr(hdev);
 	return 0;
 
 exit_mfg_deactivate:
@@ -1694,19 +2100,668 @@
 	BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
 		hdev->name);
 
-	btusb_check_bdaddr_intel(hdev);
+	btintel_check_bdaddr(hdev);
 	return 0;
 }
 
-static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
+{
+	struct sk_buff *skb;
+	struct hci_event_hdr *hdr;
+	struct hci_ev_cmd_complete *evt;
+
+	skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_ATOMIC);
+	if (!skb)
+		return -ENOMEM;
+
+	hdr = (struct hci_event_hdr *)skb_put(skb, sizeof(*hdr));
+	hdr->evt = HCI_EV_CMD_COMPLETE;
+	hdr->plen = sizeof(*evt) + 1;
+
+	evt = (struct hci_ev_cmd_complete *)skb_put(skb, sizeof(*evt));
+	evt->ncmd = 0x01;
+	evt->opcode = cpu_to_le16(opcode);
+
+	*skb_put(skb, 1) = 0x00;
+
+	bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+
+	return hci_recv_frame(hdev, skb);
+}
+
+static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer,
+				 int count)
+{
+	/* When the device is in bootloader mode, then it can send
+	 * events via the bulk endpoint. These events are treated the
+	 * same way as the ones received from the interrupt endpoint.
+	 */
+	if (test_bit(BTUSB_BOOTLOADER, &data->flags))
+		return btusb_recv_intr(data, buffer, count);
+
+	return btusb_recv_bulk(data, buffer, count);
+}
+
+static void btusb_intel_bootup(struct btusb_data *data, const void *ptr,
+			       unsigned int len)
+{
+	const struct intel_bootup *evt = ptr;
+
+	if (len != sizeof(*evt))
+		return;
+
+	if (test_and_clear_bit(BTUSB_BOOTING, &data->flags)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+		smp_mb__after_atomic();
+		wake_up_bit(&data->flags, BTUSB_BOOTING);
+#else
+		wake_up_interruptible(&data->hdev->req_wait_q);
+#endif
+	}
+}
+
+static void btusb_intel_secure_send_result(struct btusb_data *data,
+					   const void *ptr, unsigned int len)
+{
+	const struct intel_secure_send_result *evt = ptr;
+
+	if (len != sizeof(*evt))
+		return;
+
+	if (evt->result)
+		set_bit(BTUSB_FIRMWARE_FAILED, &data->flags);
+
+	if (test_and_clear_bit(BTUSB_DOWNLOADING, &data->flags) &&
+	    test_bit(BTUSB_FIRMWARE_LOADED, &data->flags)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+		smp_mb__after_atomic();
+		wake_up_bit(&data->flags, BTUSB_DOWNLOADING);
+#else
+		wake_up_interruptible(&data->hdev->req_wait_q);
+#endif
+	}
+}
+
+static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct btusb_data *data = hci_get_drvdata(hdev);
+
+	if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
+		struct hci_event_hdr *hdr = (void *)skb->data;
+
+		if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
+		    hdr->plen > 0) {
+			const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
+			unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
+
+			switch (skb->data[2]) {
+			case 0x02:
+				/* When switching to the operational firmware
+				 * the device sends a vendor specific event
+				 * indicating that the bootup completed.
+				 */
+				btusb_intel_bootup(data, ptr, len);
+				break;
+			case 0x06:
+				/* When the firmware loading completes the
+				 * device sends out a vendor specific event
+				 * indicating the result of the firmware
+				 * loading.
+				 */
+				btusb_intel_secure_send_result(data, ptr, len);
+				break;
+			}
+		}
+	}
+
+	return hci_recv_frame(hdev, skb);
+}
+
+static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct urb *urb;
+
+	BT_DBG("%s", hdev->name);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
+			struct hci_command_hdr *cmd = (void *)skb->data;
+			__u16 opcode = le16_to_cpu(cmd->opcode);
+
+			/* When in bootloader mode and the command 0xfc09
+			 * is received, it needs to be send down the
+			 * bulk endpoint. So allocate a bulk URB instead.
+			 */
+			if (opcode == 0xfc09)
+				urb = alloc_bulk_urb(hdev, skb);
+			else
+				urb = alloc_ctrl_urb(hdev, skb);
+
+			/* When the 0xfc01 command is issued to boot into
+			 * the operational firmware, it will actually not
+			 * send a command complete event. To keep the flow
+			 * control working inject that event here.
+			 */
+			if (opcode == 0xfc01)
+				inject_cmd_complete(hdev, opcode);
+		} else {
+			urb = alloc_ctrl_urb(hdev, skb);
+		}
+		if (IS_ERR(urb))
+			return PTR_ERR(urb);
+
+		hdev->stat.cmd_tx++;
+		return submit_or_queue_tx_urb(hdev, urb);
+
+	case HCI_ACLDATA_PKT:
+		urb = alloc_bulk_urb(hdev, skb);
+		if (IS_ERR(urb))
+			return PTR_ERR(urb);
+
+		hdev->stat.acl_tx++;
+		return submit_or_queue_tx_urb(hdev, urb);
+
+	case HCI_SCODATA_PKT:
+		if (hci_conn_num(hdev, SCO_LINK) < 1)
+			return -ENODEV;
+
+		urb = alloc_isoc_urb(hdev, skb);
+		if (IS_ERR(urb))
+			return PTR_ERR(urb);
+
+		hdev->stat.sco_tx++;
+		return submit_tx_urb(hdev, urb);
+	}
+
+	return -EILSEQ;
+}
+
+static int btusb_intel_secure_send(struct hci_dev *hdev, u8 fragment_type,
+				   u32 plen, const void *param)
+{
+	while (plen > 0) {
+		struct sk_buff *skb;
+		u8 cmd_param[253], fragment_len = (plen > 252) ? 252 : plen;
+
+		cmd_param[0] = fragment_type;
+		memcpy(cmd_param + 1, param, fragment_len);
+
+		skb = __hci_cmd_sync(hdev, 0xfc09, fragment_len + 1,
+				     cmd_param, HCI_INIT_TIMEOUT);
+		if (IS_ERR(skb))
+			return PTR_ERR(skb);
+
+		kfree_skb(skb);
+
+		plen -= fragment_len;
+		param += fragment_len;
+	}
+
+	return 0;
+}
+
+static void btusb_intel_version_info(struct hci_dev *hdev,
+				     struct intel_version *ver)
+{
+	const char *variant;
+
+	switch (ver->fw_variant) {
+	case 0x06:
+		variant = "Bootloader";
+		break;
+	case 0x23:
+		variant = "Firmware";
+		break;
+	default:
+		return;
+	}
+
+	BT_INFO("%s: %s revision %u.%u build %u week %u %u", hdev->name,
+		variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f,
+		ver->fw_build_num, ver->fw_build_ww, 2000 + ver->fw_build_yy);
+}
+
+static int btusb_setup_intel_new(struct hci_dev *hdev)
+{
+	static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01,
+					  0x00, 0x08, 0x04, 0x00 };
+	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct sk_buff *skb;
+	struct intel_version *ver;
+	struct intel_boot_params *params;
+	const struct firmware *fw;
+	const u8 *fw_ptr;
+	char fwname[64];
+	ktime_t calltime, delta, rettime;
+	unsigned long long duration;
+	int err;
+
+	BT_DBG("%s", hdev->name);
+
+	calltime = ktime_get();
+
+	/* Read the Intel version information to determine if the device
+	 * is in bootloader mode or if it already has operational firmware
+	 * loaded.
+	 */
+	skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: Reading Intel version information failed (%ld)",
+		       hdev->name, PTR_ERR(skb));
+		return PTR_ERR(skb);
+	}
+
+	if (skb->len != sizeof(*ver)) {
+		BT_ERR("%s: Intel version event size mismatch", hdev->name);
+		kfree_skb(skb);
+		return -EILSEQ;
+	}
+
+	ver = (struct intel_version *)skb->data;
+	if (ver->status) {
+		BT_ERR("%s: Intel version command failure (%02x)",
+		       hdev->name, ver->status);
+		err = -bt_to_errno(ver->status);
+		kfree_skb(skb);
+		return err;
+	}
+
+	/* The hardware platform number has a fixed value of 0x37 and
+	 * for now only accept this single value.
+	 */
+	if (ver->hw_platform != 0x37) {
+		BT_ERR("%s: Unsupported Intel hardware platform (%u)",
+		       hdev->name, ver->hw_platform);
+		kfree_skb(skb);
+		return -EINVAL;
+	}
+
+	/* At the moment only the hardware variant iBT 3.0 (LnP/SfP) is
+	 * supported by this firmware loading method. This check has been
+	 * put in place to ensure correct forward compatibility options
+	 * when newer hardware variants come along.
+	 */
+	if (ver->hw_variant != 0x0b) {
+		BT_ERR("%s: Unsupported Intel hardware variant (%u)",
+		       hdev->name, ver->hw_variant);
+		kfree_skb(skb);
+		return -EINVAL;
+	}
+
+	btusb_intel_version_info(hdev, ver);
+
+	/* The firmware variant determines if the device is in bootloader
+	 * mode or is running operational firmware. The value 0x06 identifies
+	 * the bootloader and the value 0x23 identifies the operational
+	 * firmware.
+	 *
+	 * When the operational firmware is already present, then only
+	 * the check for valid Bluetooth device address is needed. This
+	 * determines if the device will be added as configured or
+	 * unconfigured controller.
+	 *
+	 * It is not possible to use the Secure Boot Parameters in this
+	 * case since that command is only available in bootloader mode.
+	 */
+	if (ver->fw_variant == 0x23) {
+		kfree_skb(skb);
+		clear_bit(BTUSB_BOOTLOADER, &data->flags);
+		btintel_check_bdaddr(hdev);
+		return 0;
+	}
+
+	/* If the device is not in bootloader mode, then the only possible
+	 * choice is to return an error and abort the device initialization.
+	 */
+	if (ver->fw_variant != 0x06) {
+		BT_ERR("%s: Unsupported Intel firmware variant (%u)",
+		       hdev->name, ver->fw_variant);
+		kfree_skb(skb);
+		return -ENODEV;
+	}
+
+	kfree_skb(skb);
+
+	/* Read the secure boot parameters to identify the operating
+	 * details of the bootloader.
+	 */
+	skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: Reading Intel boot parameters failed (%ld)",
+		       hdev->name, PTR_ERR(skb));
+		return PTR_ERR(skb);
+	}
+
+	if (skb->len != sizeof(*params)) {
+		BT_ERR("%s: Intel boot parameters size mismatch", hdev->name);
+		kfree_skb(skb);
+		return -EILSEQ;
+	}
+
+	params = (struct intel_boot_params *)skb->data;
+	if (params->status) {
+		BT_ERR("%s: Intel boot parameters command failure (%02x)",
+		       hdev->name, params->status);
+		err = -bt_to_errno(params->status);
+		kfree_skb(skb);
+		return err;
+	}
+
+	BT_INFO("%s: Device revision is %u", hdev->name,
+		le16_to_cpu(params->dev_revid));
+
+	BT_INFO("%s: Secure boot is %s", hdev->name,
+		params->secure_boot ? "enabled" : "disabled");
+
+	BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name,
+		params->min_fw_build_nn, params->min_fw_build_cw,
+		2000 + params->min_fw_build_yy);
+
+	/* It is required that every single firmware fragment is acknowledged
+	 * with a command complete event. If the boot parameters indicate
+	 * that this bootloader does not send them, then abort the setup.
+	 */
+	if (params->limited_cce != 0x00) {
+		BT_ERR("%s: Unsupported Intel firmware loading method (%u)",
+		       hdev->name, params->limited_cce);
+		kfree_skb(skb);
+		return -EINVAL;
+	}
+
+	/* If the OTP has no valid Bluetooth device address, then there will
+	 * also be no valid address for the operational firmware.
+	 */
+	if (!bacmp(&params->otp_bdaddr, BDADDR_ANY)) {
+		BT_INFO("%s: No device address configured", hdev->name);
+		set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+	}
+
+	/* With this Intel bootloader only the hardware variant and device
+	 * revision information are used to select the right firmware.
+	 *
+	 * Currently this bootloader support is limited to hardware variant
+	 * iBT 3.0 (LnP/SfP) which is identified by the value 11 (0x0b).
+	 */
+	snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.sfi",
+		 le16_to_cpu(params->dev_revid));
+
+	err = request_firmware(&fw, fwname, &hdev->dev);
+	if (err < 0) {
+		BT_ERR("%s: Failed to load Intel firmware file (%d)",
+		       hdev->name, err);
+		kfree_skb(skb);
+		return err;
+	}
+
+	BT_INFO("%s: Found device firmware: %s", hdev->name, fwname);
+
+	kfree_skb(skb);
+
+	if (fw->size < 644) {
+		BT_ERR("%s: Invalid size of firmware file (%zu)",
+		       hdev->name, fw->size);
+		err = -EBADF;
+		goto done;
+	}
+
+	set_bit(BTUSB_DOWNLOADING, &data->flags);
+
+	/* Start the firmware download transaction with the Init fragment
+	 * represented by the 128 bytes of CSS header.
+	 */
+	err = btusb_intel_secure_send(hdev, 0x00, 128, fw->data);
+	if (err < 0) {
+		BT_ERR("%s: Failed to send firmware header (%d)",
+		       hdev->name, err);
+		goto done;
+	}
+
+	/* Send the 256 bytes of public key information from the firmware
+	 * as the PKey fragment.
+	 */
+	err = btusb_intel_secure_send(hdev, 0x03, 256, fw->data + 128);
+	if (err < 0) {
+		BT_ERR("%s: Failed to send firmware public key (%d)",
+		       hdev->name, err);
+		goto done;
+	}
+
+	/* Send the 256 bytes of signature information from the firmware
+	 * as the Sign fragment.
+	 */
+	err = btusb_intel_secure_send(hdev, 0x02, 256, fw->data + 388);
+	if (err < 0) {
+		BT_ERR("%s: Failed to send firmware signature (%d)",
+		       hdev->name, err);
+		goto done;
+	}
+
+	fw_ptr = fw->data + 644;
+
+	while (fw_ptr - fw->data < fw->size) {
+		struct hci_command_hdr *cmd = (void *)fw_ptr;
+		u8 cmd_len;
+
+		cmd_len = sizeof(*cmd) + cmd->plen;
+
+		/* Send each command from the firmware data buffer as
+		 * a single Data fragment.
+		 */
+		err = btusb_intel_secure_send(hdev, 0x01, cmd_len, fw_ptr);
+		if (err < 0) {
+			BT_ERR("%s: Failed to send firmware data (%d)",
+			       hdev->name, err);
+			goto done;
+		}
+
+		fw_ptr += cmd_len;
+	}
+
+	set_bit(BTUSB_FIRMWARE_LOADED, &data->flags);
+
+	BT_INFO("%s: Waiting for firmware download to complete", hdev->name);
+
+	/* Before switching the device into operational mode and with that
+	 * booting the loaded firmware, wait for the bootloader notification
+	 * that all fragments have been successfully received.
+	 *
+	 * When the event processing receives the notification, then the
+	 * BTUSB_DOWNLOADING flag will be cleared.
+	 *
+	 * The firmware loading should not take longer than 5 seconds
+	 * and thus just timeout if that happens and fail the setup
+	 * of this device.
+	 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+	err = wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING,
+				  TASK_INTERRUPTIBLE,
+				  msecs_to_jiffies(5000));
+	if (err == 1) {
+		BT_ERR("%s: Firmware loading interrupted", hdev->name);
+		err = -EINTR;
+		goto done;
+	}
+
+	if (err) {
+		BT_ERR("%s: Firmware loading timeout", hdev->name);
+		err = -ETIMEDOUT;
+		goto done;
+	}
+#else
+	if (test_bit(BTUSB_DOWNLOADING, &data->flags)) {
+		DECLARE_WAITQUEUE(wait, current);
+		signed long timeout;
+
+		add_wait_queue(&hdev->req_wait_q, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		timeout = schedule_timeout(msecs_to_jiffies(5000));
+
+		remove_wait_queue(&hdev->req_wait_q, &wait);
+
+		if (signal_pending(current)) {
+			BT_ERR("%s: Firmware loading interrupted", hdev->name);
+			err = -EINTR;
+			goto done;
+		}
+
+		if (!timeout) {
+			BT_ERR("%s: Firmware loading timeout", hdev->name);
+			err = -ETIMEDOUT;
+			goto done;
+		}
+	}
+#endif
+
+	if (test_bit(BTUSB_FIRMWARE_FAILED, &data->flags)) {
+		BT_ERR("%s: Firmware loading failed", hdev->name);
+		err = -ENOEXEC;
+		goto done;
+	}
+
+	rettime = ktime_get();
+	delta = ktime_sub(rettime, calltime);
+	duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+
+	BT_INFO("%s: Firmware loaded in %llu usecs", hdev->name, duration);
+
+done:
+	release_firmware(fw);
+
+	if (err < 0)
+		return err;
+
+	calltime = ktime_get();
+
+	set_bit(BTUSB_BOOTING, &data->flags);
+
+	skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(reset_param), reset_param,
+			     HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	kfree_skb(skb);
+
+	/* The bootloader will not indicate when the device is ready. This
+	 * is done by the operational firmware sending bootup notification.
+	 *
+	 * Booting into operational firmware should not take longer than
+	 * 1 second. However if that happens, then just fail the setup
+	 * since something went wrong.
+	 */
+	BT_INFO("%s: Waiting for device to boot", hdev->name);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+	err = wait_on_bit_timeout(&data->flags, BTUSB_BOOTING,
+				  TASK_INTERRUPTIBLE,
+				  msecs_to_jiffies(1000));
+
+	if (err == 1) {
+		BT_ERR("%s: Device boot interrupted", hdev->name);
+		return -EINTR;
+	}
+
+	if (err) {
+		BT_ERR("%s: Device boot timeout", hdev->name);
+		return -ETIMEDOUT;
+	}
+#else
+	if (test_bit(BTUSB_BOOTING, &data->flags)) {
+		DECLARE_WAITQUEUE(wait, current);
+		signed long timeout;
+
+		add_wait_queue(&hdev->req_wait_q, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		/* Booting into operational firmware should not take
+		 * longer than 1 second. However if that happens, then
+		 * just fail the setup since something went wrong.
+		 */
+		timeout = schedule_timeout(msecs_to_jiffies(1000));
+
+		remove_wait_queue(&hdev->req_wait_q, &wait);
+
+		if (signal_pending(current)) {
+			BT_ERR("%s: Device boot interrupted", hdev->name);
+			return -EINTR;
+		}
+
+		if (!timeout) {
+			BT_ERR("%s: Device boot timeout", hdev->name);
+			return -ETIMEDOUT;
+		}
+	}
+#endif
+
+	rettime = ktime_get();
+	delta = ktime_sub(rettime, calltime);
+	duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+
+	BT_INFO("%s: Device booted in %llu usecs", hdev->name, duration);
+
+	clear_bit(BTUSB_BOOTLOADER, &data->flags);
+
+	return 0;
+}
+
+static void btusb_hw_error_intel(struct hci_dev *hdev, u8 code)
+{
+	struct sk_buff *skb;
+	u8 type = 0x00;
+
+	BT_ERR("%s: Hardware error 0x%2.2x", hdev->name, code);
+
+	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: Reset after hardware error failed (%ld)",
+		       hdev->name, PTR_ERR(skb));
+		return;
+	}
+	kfree_skb(skb);
+
+	skb = __hci_cmd_sync(hdev, 0xfc22, 1, &type, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: Retrieving Intel exception info failed (%ld)",
+		       hdev->name, PTR_ERR(skb));
+		return;
+	}
+
+	if (skb->len != 13) {
+		BT_ERR("%s: Exception info size mismatch", hdev->name);
+		kfree_skb(skb);
+		return;
+	}
+
+	if (skb->data[0] != 0x00) {
+		BT_ERR("%s: Exception info command failure (%02x)",
+		       hdev->name, skb->data[0]);
+		kfree_skb(skb);
+		return;
+	}
+
+	BT_ERR("%s: Exception info %s", hdev->name, (char *)(skb->data + 1));
+
+	kfree_skb(skb);
+}
+
+static int btusb_shutdown_intel(struct hci_dev *hdev)
 {
 	struct sk_buff *skb;
 	long ret;
 
-	skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
+	/* Some platforms have an issue with BT LED when the interface is
+	 * down or BT radio is turned off, which takes 5 seconds to BT LED
+	 * goes off. This command turns off the BT LED immediately.
+	 */
+	skb = __hci_cmd_sync(hdev, 0xfc3f, 0, NULL, HCI_INIT_TIMEOUT);
 	if (IS_ERR(skb)) {
 		ret = PTR_ERR(skb);
-		BT_ERR("%s: changing Intel device address failed (%ld)",
+		BT_ERR("%s: turning off Intel device LED failed (%ld)",
 		       hdev->name, ret);
 		return ret;
 	}
@@ -1738,203 +2793,23 @@
 	return 0;
 }
 
-#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
-
-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;
-	struct hci_rp_read_bd_addr *bda;
-	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);
-
-	/* Read BD Address */
-	skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
-			     HCI_INIT_TIMEOUT);
-	if (IS_ERR(skb)) {
-		ret = PTR_ERR(skb);
-		BT_ERR("%s: HCI_OP_READ_BD_ADDR failed (%ld)",
-		       hdev->name, ret);
-		goto done;
-	}
-
-	if (skb->len != sizeof(*bda)) {
-		BT_ERR("%s: HCI_OP_READ_BD_ADDR event length mismatch",
-		       hdev->name);
-		kfree_skb(skb);
-		ret = -EIO;
-		goto done;
-	}
-
-	bda = (struct hci_rp_read_bd_addr *)skb->data;
-	if (bda->status) {
-		BT_ERR("%s: HCI_OP_READ_BD_ADDR error status (%02x)",
-		       hdev->name, bda->status);
-		kfree_skb(skb);
-		ret = -bt_to_errno(bda->status);
-		goto done;
-	}
-
-	/* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
-	 * with no configured address.
-	 */
-	if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) {
-		BT_INFO("%s: BCM: using default device address (%pMR)",
-			hdev->name, &bda->bdaddr);
-		set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
-	}
-
-	kfree_skb(skb);
-
-done:
-	release_firmware(fw);
-
-	return ret;
-}
-
-static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev,
+				    const bdaddr_t *bdaddr)
 {
 	struct sk_buff *skb;
+	u8 buf[10];
 	long ret;
 
-	skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT);
+	buf[0] = 0x01;
+	buf[1] = 0x01;
+	buf[2] = 0x00;
+	buf[3] = sizeof(bdaddr_t);
+	memcpy(buf + 4, bdaddr, sizeof(bdaddr_t));
+
+	skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT);
 	if (IS_ERR(skb)) {
 		ret = PTR_ERR(skb);
-		BT_ERR("%s: BCM: Change address command failed (%ld)",
+		BT_ERR("%s: Change address command failed (%ld)",
 		       hdev->name, ret);
 		return ret;
 	}
@@ -1943,6 +2818,258 @@
 	return 0;
 }
 
+#define QCA_DFU_PACKET_LEN	4096
+
+#define QCA_GET_TARGET_VERSION	0x09
+#define QCA_CHECK_STATUS	0x05
+#define QCA_DFU_DOWNLOAD	0x01
+
+#define QCA_SYSCFG_UPDATED	0x40
+#define QCA_PATCH_UPDATED	0x80
+#define QCA_DFU_TIMEOUT		3000
+
+struct qca_version {
+	__le32	rom_version;
+	__le32	patch_version;
+	__le32	ram_version;
+	__le32	ref_clock;
+	__u8	reserved[4];
+} __packed;
+
+struct qca_rampatch_version {
+	__le16	rom_version;
+	__le16	patch_version;
+} __packed;
+
+struct qca_device_info {
+	u32	rom_version;
+	u8	rampatch_hdr;	/* length of header in rampatch */
+	u8	nvm_hdr;	/* length of header in NVM */
+	u8	ver_offset;	/* offset of version structure in rampatch */
+};
+
+static const struct qca_device_info qca_devices_table[] = {
+	{ 0x00000100, 20, 4, 10 }, /* Rome 1.0 */
+	{ 0x00000101, 20, 4, 10 }, /* Rome 1.1 */
+	{ 0x00000201, 28, 4, 18 }, /* Rome 2.1 */
+	{ 0x00000300, 28, 4, 18 }, /* Rome 3.0 */
+	{ 0x00000302, 28, 4, 18 }, /* Rome 3.2 */
+};
+
+static int btusb_qca_send_vendor_req(struct hci_dev *hdev, u8 request,
+				     void *data, u16 size)
+{
+	struct btusb_data *btdata = hci_get_drvdata(hdev);
+	struct usb_device *udev = btdata->udev;
+	int pipe, err;
+	u8 *buf;
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/* Found some of USB hosts have IOT issues with ours so that we should
+	 * not wait until HCI layer is ready.
+	 */
+	pipe = usb_rcvctrlpipe(udev, 0);
+	err = usb_control_msg(udev, pipe, request, USB_TYPE_VENDOR | USB_DIR_IN,
+			      0, 0, buf, size, USB_CTRL_SET_TIMEOUT);
+	if (err < 0) {
+		BT_ERR("%s: Failed to access otp area (%d)", hdev->name, err);
+		goto done;
+	}
+
+	memcpy(data, buf, size);
+
+done:
+	kfree(buf);
+
+	return err;
+}
+
+static int btusb_setup_qca_download_fw(struct hci_dev *hdev,
+				       const struct firmware *firmware,
+				       size_t hdr_size)
+{
+	struct btusb_data *btdata = hci_get_drvdata(hdev);
+	struct usb_device *udev = btdata->udev;
+	size_t count, size, sent = 0;
+	int pipe, len, err;
+	u8 *buf;
+
+	buf = kmalloc(QCA_DFU_PACKET_LEN, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	count = firmware->size;
+
+	size = min_t(size_t, count, hdr_size);
+	memcpy(buf, firmware->data, size);
+
+	/* USB patches should go down to controller through USB path
+	 * because binary format fits to go down through USB channel.
+	 * USB control path is for patching headers and USB bulk is for
+	 * patch body.
+	 */
+	pipe = usb_sndctrlpipe(udev, 0);
+	err = usb_control_msg(udev, pipe, QCA_DFU_DOWNLOAD, USB_TYPE_VENDOR,
+			      0, 0, buf, size, USB_CTRL_SET_TIMEOUT);
+	if (err < 0) {
+		BT_ERR("%s: Failed to send headers (%d)", hdev->name, err);
+		goto done;
+	}
+
+	sent += size;
+	count -= size;
+
+	while (count) {
+		size = min_t(size_t, count, QCA_DFU_PACKET_LEN);
+
+		memcpy(buf, firmware->data + sent, size);
+
+		pipe = usb_sndbulkpipe(udev, 0x02);
+		err = usb_bulk_msg(udev, pipe, buf, size, &len,
+				   QCA_DFU_TIMEOUT);
+		if (err < 0) {
+			BT_ERR("%s: Failed to send body at %zd of %zd (%d)",
+			       hdev->name, sent, firmware->size, err);
+			break;
+		}
+
+		if (size != len) {
+			BT_ERR("%s: Failed to get bulk buffer", hdev->name);
+			err = -EILSEQ;
+			break;
+		}
+
+		sent  += size;
+		count -= size;
+	}
+
+done:
+	kfree(buf);
+	return err;
+}
+
+static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev,
+					 struct qca_version *ver,
+					 const struct qca_device_info *info)
+{
+	struct qca_rampatch_version *rver;
+	const struct firmware *fw;
+	u32 ver_rom, ver_patch;
+	u16 rver_rom, rver_patch;
+	char fwname[64];
+	int err;
+
+	ver_rom = le32_to_cpu(ver->rom_version);
+	ver_patch = le32_to_cpu(ver->patch_version);
+
+	snprintf(fwname, sizeof(fwname), "qca/rampatch_usb_%08x.bin", ver_rom);
+
+	err = request_firmware(&fw, fwname, &hdev->dev);
+	if (err) {
+		BT_ERR("%s: failed to request rampatch file: %s (%d)",
+		       hdev->name, fwname, err);
+		return err;
+	}
+
+	BT_INFO("%s: using rampatch file: %s", hdev->name, fwname);
+
+	rver = (struct qca_rampatch_version *)(fw->data + info->ver_offset);
+	rver_rom = le16_to_cpu(rver->rom_version);
+	rver_patch = le16_to_cpu(rver->patch_version);
+
+	BT_INFO("%s: QCA: patch rome 0x%x build 0x%x, firmware rome 0x%x "
+		"build 0x%x", hdev->name, rver_rom, rver_patch, ver_rom,
+		ver_patch);
+
+	if (rver_rom != ver_rom || rver_patch <= ver_patch) {
+		BT_ERR("%s: rampatch file version did not match with firmware",
+		       hdev->name);
+		err = -EINVAL;
+		goto done;
+	}
+
+	err = btusb_setup_qca_download_fw(hdev, fw, info->rampatch_hdr);
+
+done:
+	release_firmware(fw);
+
+	return err;
+}
+
+static int btusb_setup_qca_load_nvm(struct hci_dev *hdev,
+				    struct qca_version *ver,
+				    const struct qca_device_info *info)
+{
+	const struct firmware *fw;
+	char fwname[64];
+	int err;
+
+	snprintf(fwname, sizeof(fwname), "qca/nvm_usb_%08x.bin",
+		 le32_to_cpu(ver->rom_version));
+
+	err = request_firmware(&fw, fwname, &hdev->dev);
+	if (err) {
+		BT_ERR("%s: failed to request NVM file: %s (%d)",
+		       hdev->name, fwname, err);
+		return err;
+	}
+
+	BT_INFO("%s: using NVM file: %s", hdev->name, fwname);
+
+	err = btusb_setup_qca_download_fw(hdev, fw, info->nvm_hdr);
+
+	release_firmware(fw);
+
+	return err;
+}
+
+static int btusb_setup_qca(struct hci_dev *hdev)
+{
+	const struct qca_device_info *info = NULL;
+	struct qca_version ver;
+	u32 ver_rom;
+	u8 status;
+	int i, err;
+
+	err = btusb_qca_send_vendor_req(hdev, QCA_GET_TARGET_VERSION, &ver,
+					sizeof(ver));
+	if (err < 0)
+		return err;
+
+	ver_rom = le32_to_cpu(ver.rom_version);
+	for (i = 0; i < ARRAY_SIZE(qca_devices_table); i++) {
+		if (ver_rom == qca_devices_table[i].rom_version)
+			info = &qca_devices_table[i];
+	}
+	if (!info) {
+		BT_ERR("%s: don't support firmware rome 0x%x", hdev->name,
+		       ver_rom);
+		return -ENODEV;
+	}
+
+	err = btusb_qca_send_vendor_req(hdev, QCA_CHECK_STATUS, &status,
+					sizeof(status));
+	if (err < 0)
+		return err;
+
+	if (!(status & QCA_PATCH_UPDATED)) {
+		err = btusb_setup_qca_load_rampatch(hdev, &ver, info);
+		if (err < 0)
+			return err;
+	}
+
+	if (!(status & QCA_SYSCFG_UPDATED)) {
+		err = btusb_setup_qca_load_nvm(hdev, &ver, info);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
 static int btusb_probe(struct usb_interface *intf,
 		       const struct usb_device_id *id)
 {
@@ -2003,7 +3130,13 @@
 	if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep)
 		return -ENODEV;
 
-	data->cmdreq_type = USB_TYPE_CLASS;
+	if (id->driver_info & BTUSB_AMP) {
+		data->cmdreq_type = USB_TYPE_CLASS | 0x01;
+		data->cmdreq = 0x2b;
+	} else {
+		data->cmdreq_type = USB_TYPE_CLASS;
+		data->cmdreq = 0x00;
+	}
 
 	data->udev = interface_to_usbdev(intf);
 	data->intf = intf;
@@ -2019,7 +3152,14 @@
 	init_usb_anchor(&data->isoc_anchor);
 	spin_lock_init(&data->rxlock);
 
-	data->recv_bulk = btusb_recv_bulk;
+	if (id->driver_info & BTUSB_INTEL_NEW) {
+		data->recv_event = btusb_recv_event_intel;
+		data->recv_bulk = btusb_recv_bulk_intel;
+		set_bit(BTUSB_BOOTLOADER, &data->flags);
+	} else {
+		data->recv_event = hci_recv_frame;
+		data->recv_bulk = btusb_recv_bulk;
+	}
 
 	hdev = hci_alloc_dev();
 	if (!hdev)
@@ -2028,6 +3168,11 @@
 	hdev->bus = HCI_USB;
 	hci_set_drvdata(hdev, data);
 
+	if (id->driver_info & BTUSB_AMP)
+		hdev->dev_type = HCI_AMP;
+	else
+		hdev->dev_type = HCI_BREDR;
+
 	data->hdev = hdev;
 
 	SET_HCIDEV_DEV(hdev, &intf->dev);
@@ -2041,25 +3186,64 @@
 	if (id->driver_info & BTUSB_BCM92035)
 		hdev->setup = btusb_setup_bcm92035;
 
+#ifdef CPTCFG_BT_HCIBTUSB_BCM
 	if (id->driver_info & BTUSB_BCM_PATCHRAM) {
-		hdev->setup = btusb_setup_bcm_patchram;
-		hdev->set_bdaddr = btusb_set_bdaddr_bcm;
-		set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+		hdev->setup = btbcm_setup_patchram;
+		hdev->set_bdaddr = btbcm_set_bdaddr;
 	}
 
+	if (id->driver_info & BTUSB_BCM_APPLE)
+		hdev->setup = btbcm_setup_apple;
+#endif
+
 	if (id->driver_info & BTUSB_INTEL) {
 		hdev->setup = btusb_setup_intel;
-		hdev->set_bdaddr = btusb_set_bdaddr_intel;
+		hdev->shutdown = btusb_shutdown_intel;
+		hdev->set_bdaddr = btintel_set_bdaddr;
+		set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+		set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+	}
+
+	if (id->driver_info & BTUSB_INTEL_NEW) {
+		hdev->send = btusb_send_frame_intel;
+		hdev->setup = btusb_setup_intel_new;
+		hdev->hw_error = btusb_hw_error_intel;
+		hdev->set_bdaddr = btintel_set_bdaddr;
+		set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
 	}
 
 	if (id->driver_info & BTUSB_MARVELL)
 		hdev->set_bdaddr = btusb_set_bdaddr_marvell;
 
+	if (id->driver_info & BTUSB_SWAVE) {
+		set_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks);
+		set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
+	}
+
 	if (id->driver_info & BTUSB_INTEL_BOOT)
 		set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
 
-	/* Interface numbers are hardcoded in the specification */
-	data->isoc = usb_ifnum_to_if(data->udev, 1);
+	if (id->driver_info & BTUSB_ATH3012) {
+		hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
+		set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+		set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+	}
+
+	if (id->driver_info & BTUSB_QCA_ROME) {
+		data->setup_on_usb = btusb_setup_qca;
+		hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
+	}
+
+	if (id->driver_info & BTUSB_REALTEK)
+		hdev->setup = btusb_setup_realtek;
+
+	if (id->driver_info & BTUSB_AMP) {
+		/* AMP controllers do not support SCO packets */
+		data->isoc = NULL;
+	} else {
+		/* Interface numbers are hardcoded in the specification */
+		data->isoc = usb_ifnum_to_if(data->udev, 1);
+	}
 
 	if (!reset)
 		set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
@@ -2088,6 +3272,8 @@
 		/* Fake CSR devices with broken commands */
 		if (bcdDevice <= 0x100)
 			hdev->setup = btusb_setup_csr;
+
+		set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
 	}
 
 	if (id->driver_info & BTUSB_SNIFFER) {
@@ -2153,7 +3339,6 @@
 	else if (data->isoc)
 		usb_driver_release_interface(&btusb_driver, data->isoc);
 
-	btusb_free_frags(data);
 	hci_free_dev(hdev);
 }
 
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index 326eb27..ec8fa0e 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -45,43 +45,29 @@
 	struct hci_uart *hu;
 	unsigned int cur_sleep;
 
+	struct sk_buff *rx_skb;
 	struct sk_buff_head txq;
 	struct work_struct ctxtsw;
 };
 
 static int ath_wakeup_ar3k(struct tty_struct *tty)
 {
-	struct ktermios ktermios;
 	int status = tty->driver->ops->tiocmget(tty);
 
 	if (status & TIOCM_CTS)
 		return status;
 
-	/* Disable Automatic RTSCTS */
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0))
-	ktermios = tty->termios;
-#else
-	memcpy(&ktermios, tty->termios, sizeof(ktermios));
-#endif
-	ktermios.c_cflag &= ~CRTSCTS;
-	tty_set_termios(tty, &ktermios);
-
 	/* Clear RTS first */
-	status = tty->driver->ops->tiocmget(tty);
+	tty->driver->ops->tiocmget(tty);
 	tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
 	mdelay(20);
 
 	/* Set RTS, wake up board */
-	status = tty->driver->ops->tiocmget(tty);
+	tty->driver->ops->tiocmget(tty);
 	tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
 	mdelay(20);
 
 	status = tty->driver->ops->tiocmget(tty);
-
-	/* Enable Automatic RTSCTS */
-	ktermios.c_cflag |= CRTSCTS;
-	status = tty_set_termios(tty, &ktermios);
-
 	return status;
 }
 
@@ -109,7 +95,6 @@
 	hci_uart_tx_wakeup(hu);
 }
 
-/* Initialize protocol */
 static int ath_open(struct hci_uart *hu)
 {
 	struct ath_struct *ath;
@@ -130,7 +115,24 @@
 	return 0;
 }
 
-/* Flush protocol data */
+static int ath_close(struct hci_uart *hu)
+{
+	struct ath_struct *ath = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&ath->txq);
+
+	kfree_skb(ath->rx_skb);
+
+	cancel_work_sync(&ath->ctxtsw);
+
+	hu->priv = NULL;
+	kfree(ath);
+
+	return 0;
+}
+
 static int ath_flush(struct hci_uart *hu)
 {
 	struct ath_struct *ath = hu->priv;
@@ -142,26 +144,62 @@
 	return 0;
 }
 
-/* Close protocol */
-static int ath_close(struct hci_uart *hu)
+static int ath_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
 {
-	struct ath_struct *ath = hu->priv;
+	struct sk_buff *skb;
+	u8 buf[10];
+	int err;
 
-	BT_DBG("hu %p", hu);
+	buf[0] = 0x01;
+	buf[1] = 0x01;
+	buf[2] = 0x00;
+	buf[3] = sizeof(bdaddr_t);
+	memcpy(buf + 4, bdaddr, sizeof(bdaddr_t));
 
-	skb_queue_purge(&ath->txq);
-
-	cancel_work_sync(&ath->ctxtsw);
-
-	hu->priv = NULL;
-	kfree(ath);
+	skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		BT_ERR("%s: Change address command failed (%d)",
+		       hdev->name, err);
+		return err;
+	}
+	kfree_skb(skb);
 
 	return 0;
 }
 
+static int ath_setup(struct hci_uart *hu)
+{
+	BT_DBG("hu %p", hu);
+
+	hu->hdev->set_bdaddr = ath_set_bdaddr;
+
+	return 0;
+}
+
+static const struct h4_recv_pkt ath_recv_pkts[] = {
+	{ H4_RECV_ACL,   .recv = hci_recv_frame },
+	{ H4_RECV_SCO,   .recv = hci_recv_frame },
+	{ H4_RECV_EVENT, .recv = hci_recv_frame },
+};
+
+static int ath_recv(struct hci_uart *hu, const void *data, int count)
+{
+	struct ath_struct *ath = hu->priv;
+
+	ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count,
+				  ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts));
+	if (IS_ERR(ath->rx_skb)) {
+		int err = PTR_ERR(ath->rx_skb);
+		BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+		return err;
+	}
+
+	return count;
+}
+
 #define HCI_OP_ATH_SLEEP 0xFC04
 
-/* Enqueue frame for transmittion */
 static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
 {
 	struct ath_struct *ath = hu->priv;
@@ -171,8 +209,7 @@
 		return 0;
 	}
 
-	/*
-	 * Update power management enable flag with parameters of
+	/* Update power management enable flag with parameters of
 	 * HCI sleep enable vendor specific HCI command.
 	 */
 	if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
@@ -202,40 +239,21 @@
 	return skb_dequeue(&ath->txq);
 }
 
-/* Recv data */
-static int ath_recv(struct hci_uart *hu, void *data, int count)
-{
-	int ret;
-
-	ret = hci_recv_stream_fragment(hu->hdev, data, count);
-	if (ret < 0) {
-		BT_ERR("Frame Reassembly Failed");
-		return ret;
-	}
-
-	return count;
-}
-
-static struct hci_uart_proto athp = {
-	.id = HCI_UART_ATH3K,
-	.open = ath_open,
-	.close = ath_close,
-	.recv = ath_recv,
-	.enqueue = ath_enqueue,
-	.dequeue = ath_dequeue,
-	.flush = ath_flush,
+static const struct hci_uart_proto athp = {
+	.id		= HCI_UART_ATH3K,
+	.name		= "ATH3K",
+	.open		= ath_open,
+	.close		= ath_close,
+	.flush		= ath_flush,
+	.setup		= ath_setup,
+	.recv		= ath_recv,
+	.enqueue	= ath_enqueue,
+	.dequeue	= ath_dequeue,
 };
 
 int __init ath_init(void)
 {
-	int err = hci_uart_register_proto(&athp);
-
-	if (!err)
-		BT_INFO("HCIATH3K protocol initialized");
-	else
-		BT_ERR("HCIATH3K protocol registration failed");
-
-	return err;
+	return hci_uart_register_proto(&athp);
 }
 
 int __exit ath_deinit(void)
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
new file mode 100644
index 0000000..1ec0b4a
--- /dev/null
+++ b/drivers/bluetooth/hci_bcm.c
@@ -0,0 +1,153 @@
+/*
+ *
+ *  Bluetooth HCI UART driver for Broadcom devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btbcm.h"
+#include "hci_uart.h"
+
+struct bcm_data {
+	struct sk_buff *rx_skb;
+	struct sk_buff_head txq;
+};
+
+static int bcm_open(struct hci_uart *hu)
+{
+	struct bcm_data *bcm;
+
+	BT_DBG("hu %p", hu);
+
+	bcm = kzalloc(sizeof(*bcm), GFP_KERNEL);
+	if (!bcm)
+		return -ENOMEM;
+
+	skb_queue_head_init(&bcm->txq);
+
+	hu->priv = bcm;
+	return 0;
+}
+
+static int bcm_close(struct hci_uart *hu)
+{
+	struct bcm_data *bcm = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&bcm->txq);
+	kfree_skb(bcm->rx_skb);
+	kfree(bcm);
+
+	hu->priv = NULL;
+	return 0;
+}
+
+static int bcm_flush(struct hci_uart *hu)
+{
+	struct bcm_data *bcm = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&bcm->txq);
+
+	return 0;
+}
+
+static int bcm_setup(struct hci_uart *hu)
+{
+	BT_DBG("hu %p", hu);
+
+	hu->hdev->set_bdaddr = btbcm_set_bdaddr;
+
+	return btbcm_setup_patchram(hu->hdev);
+}
+
+static const struct h4_recv_pkt bcm_recv_pkts[] = {
+	{ H4_RECV_ACL,   .recv = hci_recv_frame },
+	{ H4_RECV_SCO,   .recv = hci_recv_frame },
+	{ H4_RECV_EVENT, .recv = hci_recv_frame },
+};
+
+static int bcm_recv(struct hci_uart *hu, const void *data, int count)
+{
+	struct bcm_data *bcm = hu->priv;
+
+	if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
+		return -EUNATCH;
+
+	bcm->rx_skb = h4_recv_buf(hu->hdev, bcm->rx_skb, data, count,
+				  bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts));
+	if (IS_ERR(bcm->rx_skb)) {
+		int err = PTR_ERR(bcm->rx_skb);
+		BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+		return err;
+	}
+
+	return count;
+}
+
+static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct bcm_data *bcm = hu->priv;
+
+	BT_DBG("hu %p skb %p", hu, skb);
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+	skb_queue_tail(&bcm->txq, skb);
+
+	return 0;
+}
+
+static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
+{
+	struct bcm_data *bcm = hu->priv;
+
+	return skb_dequeue(&bcm->txq);
+}
+
+static const struct hci_uart_proto bcm_proto = {
+	.id		= HCI_UART_BCM,
+	.name		= "BCM",
+	.open		= bcm_open,
+	.close		= bcm_close,
+	.flush		= bcm_flush,
+	.setup		= bcm_setup,
+	.recv		= bcm_recv,
+	.enqueue	= bcm_enqueue,
+	.dequeue	= bcm_dequeue,
+};
+
+int __init bcm_init(void)
+{
+	return hci_uart_register_proto(&bcm_proto);
+}
+
+int __exit bcm_deinit(void)
+{
+	return hci_uart_unregister_proto(&bcm_proto);
+}
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index 21cc45b..dc8e3d4 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -47,8 +47,6 @@
 
 #include "hci_uart.h"
 
-#define VERSION "0.3"
-
 static bool txcrc = 1;
 static bool hciextn = 1;
 
@@ -554,10 +552,10 @@
 }
 
 /* Recv data */
-static int bcsp_recv(struct hci_uart *hu, void *data, int count)
+static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
 {
 	struct bcsp_struct *bcsp = hu->priv;
-	unsigned char *ptr;
+	const unsigned char *ptr;
 
 	BT_DBG("hu %p count %d rx_state %d rx_count %ld", 
 		hu, count, bcsp->rx_state, bcsp->rx_count);
@@ -735,8 +733,9 @@
 	return 0;
 }
 
-static struct hci_uart_proto bcsp = {
+static const struct hci_uart_proto bcsp = {
 	.id		= HCI_UART_BCSP,
+	.name		= "BCSP",
 	.open		= bcsp_open,
 	.close		= bcsp_close,
 	.enqueue	= bcsp_enqueue,
@@ -747,14 +746,7 @@
 
 int __init bcsp_init(void)
 {
-	int err = hci_uart_register_proto(&bcsp);
-
-	if (!err)
-		BT_INFO("HCI BCSP protocol initialized");
-	else
-		BT_ERR("HCI BCSP protocol registration failed");
-
-	return err;
+	return hci_uart_register_proto(&bcsp);
 }
 
 int __exit bcsp_deinit(void)
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index 66db9a8..f7190f0 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -40,17 +40,14 @@
 #include <linux/signal.h>
 #include <linux/ioctl.h>
 #include <linux/skbuff.h>
+#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
 #include "hci_uart.h"
 
-#define VERSION "1.2"
-
 struct h4_struct {
-	unsigned long rx_state;
-	unsigned long rx_count;
 	struct sk_buff *rx_skb;
 	struct sk_buff_head txq;
 };
@@ -117,18 +114,26 @@
 	return 0;
 }
 
+static const struct h4_recv_pkt h4_recv_pkts[] = {
+	{ H4_RECV_ACL,   .recv = hci_recv_frame },
+	{ H4_RECV_SCO,   .recv = hci_recv_frame },
+	{ H4_RECV_EVENT, .recv = hci_recv_frame },
+};
+
 /* Recv data */
-static int h4_recv(struct hci_uart *hu, void *data, int count)
+static int h4_recv(struct hci_uart *hu, const void *data, int count)
 {
-	int ret;
+	struct h4_struct *h4 = hu->priv;
 
 	if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
 		return -EUNATCH;
 
-	ret = hci_recv_stream_fragment(hu->hdev, data, count);
-	if (ret < 0) {
-		BT_ERR("Frame Reassembly Failed");
-		return ret;
+	h4->rx_skb = h4_recv_buf(hu->hdev, h4->rx_skb, data, count,
+				 h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts));
+	if (IS_ERR(h4->rx_skb)) {
+		int err = PTR_ERR(h4->rx_skb);
+		BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+		return err;
 	}
 
 	return count;
@@ -140,8 +145,9 @@
 	return skb_dequeue(&h4->txq);
 }
 
-static struct hci_uart_proto h4p = {
+static const struct hci_uart_proto h4p = {
 	.id		= HCI_UART_H4,
+	.name		= "H4",
 	.open		= h4_open,
 	.close		= h4_close,
 	.recv		= h4_recv,
@@ -152,17 +158,105 @@
 
 int __init h4_init(void)
 {
-	int err = hci_uart_register_proto(&h4p);
-
-	if (!err)
-		BT_INFO("HCI H4 protocol initialized");
-	else
-		BT_ERR("HCI H4 protocol registration failed");
-
-	return err;
+	return hci_uart_register_proto(&h4p);
 }
 
 int __exit h4_deinit(void)
 {
 	return hci_uart_unregister_proto(&h4p);
 }
+
+struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+			    const unsigned char *buffer, int count,
+			    const struct h4_recv_pkt *pkts, int pkts_count)
+{
+	while (count) {
+		int i, len;
+
+		if (!skb) {
+			for (i = 0; i < pkts_count; i++) {
+				if (buffer[0] != (&pkts[i])->type)
+					continue;
+
+				skb = bt_skb_alloc((&pkts[i])->maxlen,
+						   GFP_ATOMIC);
+				if (!skb)
+					return ERR_PTR(-ENOMEM);
+
+				bt_cb(skb)->pkt_type = (&pkts[i])->type;
+				bt_cb(skb)->expect = (&pkts[i])->hlen;
+				break;
+			}
+
+			/* Check for invalid packet type */
+			if (!skb)
+				return ERR_PTR(-EILSEQ);
+
+			count -= 1;
+			buffer += 1;
+		}
+
+		len = min_t(uint, bt_cb(skb)->expect - skb->len, count);
+		memcpy(skb_put(skb, len), buffer, len);
+
+		count -= len;
+		buffer += len;
+
+		/* Check for partial packet */
+		if (skb->len < bt_cb(skb)->expect)
+			continue;
+
+		for (i = 0; i < pkts_count; i++) {
+			if (bt_cb(skb)->pkt_type == (&pkts[i])->type)
+				break;
+		}
+
+		if (i >= pkts_count) {
+			kfree_skb(skb);
+			return ERR_PTR(-EILSEQ);
+		}
+
+		if (skb->len == (&pkts[i])->hlen) {
+			u16 dlen;
+
+			switch ((&pkts[i])->lsize) {
+			case 0:
+				/* No variable data length */
+				(&pkts[i])->recv(hdev, skb);
+				skb = NULL;
+				break;
+			case 1:
+				/* Single octet variable length */
+				dlen = skb->data[(&pkts[i])->loff];
+				bt_cb(skb)->expect += dlen;
+
+				if (skb_tailroom(skb) < dlen) {
+					kfree_skb(skb);
+					return ERR_PTR(-EMSGSIZE);
+				}
+				break;
+			case 2:
+				/* Double octet variable length */
+				dlen = get_unaligned_le16(skb->data +
+							  (&pkts[i])->loff);
+				bt_cb(skb)->expect += dlen;
+
+				if (skb_tailroom(skb) < dlen) {
+					kfree_skb(skb);
+					return ERR_PTR(-EMSGSIZE);
+				}
+				break;
+			default:
+				/* Unsupported variable length */
+				kfree_skb(skb);
+				return ERR_PTR(-EILSEQ);
+			}
+		} else {
+			/* Complete frame */
+			(&pkts[i])->recv(hdev, skb);
+			skb = NULL;
+		}
+	}
+
+	return skb;
+}
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index ec0fa77..3455cec 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -511,10 +511,10 @@
 	clear_bit(H5_RX_ESC, &h5->flags);
 }
 
-static int h5_recv(struct hci_uart *hu, void *data, int count)
+static int h5_recv(struct hci_uart *hu, const void *data, int count)
 {
 	struct h5 *h5 = hu->priv;
-	unsigned char *ptr = data;
+	const unsigned char *ptr = data;
 
 	BT_DBG("%s pending %zu count %d", hu->hdev->name, h5->rx_pending,
 	       count);
@@ -743,8 +743,9 @@
 	return 0;
 }
 
-static struct hci_uart_proto h5p = {
+static const struct hci_uart_proto h5p = {
 	.id		= HCI_UART_3WIRE,
+	.name		= "Three-wire (H5)",
 	.open		= h5_open,
 	.close		= h5_close,
 	.recv		= h5_recv,
@@ -755,14 +756,7 @@
 
 int __init h5_init(void)
 {
-	int err = hci_uart_register_proto(&h5p);
-
-	if (!err)
-		BT_INFO("HCI Three-wire UART (H5) protocol initialized");
-	else
-		BT_ERR("HCI Three-wire UART (H5) protocol init failed");
-
-	return err;
+	return hci_uart_register_proto(&h5p);
 }
 
 int __exit h5_deinit(void)
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
new file mode 100644
index 0000000..5dd07bf
--- /dev/null
+++ b/drivers/bluetooth/hci_intel.c
@@ -0,0 +1,31 @@
+/*
+ *
+ *  Bluetooth HCI UART driver for Intel devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 0a31356..8b9779b 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -44,13 +44,15 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
+#include "btintel.h"
+#include "btbcm.h"
 #include "hci_uart.h"
 
-#define VERSION "2.2"
+#define VERSION "2.3"
 
-static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
+static const struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
 
-int hci_uart_register_proto(struct hci_uart_proto *p)
+int hci_uart_register_proto(const struct hci_uart_proto *p)
 {
 	if (p->id >= HCI_UART_MAX_PROTO)
 		return -EINVAL;
@@ -60,10 +62,12 @@
 
 	hup[p->id] = p;
 
+	BT_INFO("HCI UART protocol %s registered", p->name);
+
 	return 0;
 }
 
-int hci_uart_unregister_proto(struct hci_uart_proto *p)
+int hci_uart_unregister_proto(const struct hci_uart_proto *p)
 {
 	if (p->id >= HCI_UART_MAX_PROTO)
 		return -EINVAL;
@@ -76,7 +80,7 @@
 	return 0;
 }
 
-static struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
+static const struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
 {
 	if (id >= HCI_UART_MAX_PROTO)
 		return NULL;
@@ -261,6 +265,54 @@
 	return 0;
 }
 
+static int hci_uart_setup(struct hci_dev *hdev)
+{
+	struct hci_uart *hu = hci_get_drvdata(hdev);
+	struct hci_rp_read_local_version *ver;
+	struct sk_buff *skb;
+
+	if (hu->proto->setup)
+		return hu->proto->setup(hu);
+
+	if (!test_bit(HCI_UART_VND_DETECT, &hu->hdev_flags))
+		return 0;
+
+	skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+			     HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: Reading local version information failed (%ld)",
+		       hdev->name, PTR_ERR(skb));
+		return 0;
+	}
+
+	if (skb->len != sizeof(*ver)) {
+		BT_ERR("%s: Event length mismatch for version information",
+		       hdev->name);
+		goto done;
+	}
+
+	ver = (struct hci_rp_read_local_version *)skb->data;
+
+	switch (le16_to_cpu(ver->manufacturer)) {
+#ifdef CPTCFG_BT_HCIUART_INTEL
+	case 2:
+		hdev->set_bdaddr = btintel_set_bdaddr;
+		btintel_check_bdaddr(hdev);
+		break;
+#endif
+#ifdef CPTCFG_BT_HCIUART_BCM
+	case 15:
+		hdev->set_bdaddr = btbcm_set_bdaddr;
+		btbcm_check_bdaddr(hdev);
+		break;
+#endif
+	}
+
+done:
+	kfree_skb(skb);
+	return 0;
+}
+
 /* ------ LDISC part ------ */
 /* hci_uart_tty_open
  *
@@ -316,7 +368,7 @@
  */
 static void hci_uart_tty_close(struct tty_struct *tty)
 {
-	struct hci_uart *hu = (void *)tty->disc_data;
+	struct hci_uart *hu = tty->disc_data;
 	struct hci_dev *hdev;
 
 	BT_DBG("tty %p", tty);
@@ -355,7 +407,7 @@
  */
 static void hci_uart_tty_wakeup(struct tty_struct *tty)
 {
-	struct hci_uart *hu = (void *)tty->disc_data;
+	struct hci_uart *hu = tty->disc_data;
 
 	BT_DBG("");
 
@@ -383,9 +435,10 @@
  *
  * Return Value:    None
  */
-static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count)
+static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
+				 char *flags, int count)
 {
-	struct hci_uart *hu = (void *)tty->disc_data;
+	struct hci_uart *hu = tty->disc_data;
 
 	if (!hu || tty != hu->tty)
 		return;
@@ -394,7 +447,7 @@
 		return;
 
 	spin_lock(&hu->rx_lock);
-	hu->proto->recv(hu, (void *) data, count);
+	hu->proto->recv(hu, data, count);
 
 	if (hu->hdev)
 		hu->hdev->stat.byte_rx += count;
@@ -426,6 +479,7 @@
 	hdev->close = hci_uart_close;
 	hdev->flush = hci_uart_flush;
 	hdev->send  = hci_uart_send_frame;
+	hdev->setup = hci_uart_setup;
 	SET_HCIDEV_DEV(hdev, hu->tty->dev);
 
 	if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
@@ -458,7 +512,7 @@
 
 static int hci_uart_set_proto(struct hci_uart *hu, int id)
 {
-	struct hci_uart_proto *p;
+	const struct hci_uart_proto *p;
 	int err;
 
 	p = hci_uart_get_proto(id);
@@ -486,9 +540,10 @@
 				    BIT(HCI_UART_RESET_ON_INIT) |
 				    BIT(HCI_UART_CREATE_AMP) |
 				    BIT(HCI_UART_INIT_PENDING) |
-				    BIT(HCI_UART_EXT_CONFIG);
+				    BIT(HCI_UART_EXT_CONFIG) |
+				    BIT(HCI_UART_VND_DETECT);
 
-	if ((flags & ~valid_flags))
+	if (flags & ~valid_flags)
 		return -EINVAL;
 
 	hu->hdev_flags = flags;
@@ -509,10 +564,10 @@
  *
  * Return Value:    Command dependent
  */
-static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
-					unsigned int cmd, unsigned long arg)
+static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
+			      unsigned int cmd, unsigned long arg)
 {
-	struct hci_uart *hu = (void *)tty->disc_data;
+	struct hci_uart *hu = tty->disc_data;
 	int err = 0;
 
 	BT_DBG("");
@@ -566,19 +621,19 @@
  * We don't provide read/write/poll interface for user space.
  */
 static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file,
-					unsigned char __user *buf, size_t nr)
+				 unsigned char __user *buf, size_t nr)
 {
 	return 0;
 }
 
 static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file,
-					const unsigned char *data, size_t count)
+				  const unsigned char *data, size_t count)
 {
 	return 0;
 }
 
 static unsigned int hci_uart_tty_poll(struct tty_struct *tty,
-					struct file *filp, poll_table *wait)
+				      struct file *filp, poll_table *wait)
 {
 	return 0;
 }
@@ -626,6 +681,9 @@
 #ifdef CPTCFG_BT_HCIUART_3WIRE
 	h5_init();
 #endif
+#ifdef CPTCFG_BT_HCIUART_BCM
+	bcm_init();
+#endif
 
 	return 0;
 }
@@ -649,6 +707,9 @@
 #ifdef CPTCFG_BT_HCIUART_3WIRE
 	h5_deinit();
 #endif
+#ifdef CPTCFG_BT_HCIUART_BCM
+	bcm_deinit();
+#endif
 
 	/* Release tty registration of line discipline */
 	err = tty_unregister_ldisc(N_HCI);
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index 69a90b1..9ee24b0 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -370,10 +370,10 @@
 }
 
 /* Recv data */
-static int ll_recv(struct hci_uart *hu, void *data, int count)
+static int ll_recv(struct hci_uart *hu, const void *data, int count)
 {
 	struct ll_struct *ll = hu->priv;
-	char *ptr;
+	const char *ptr;
 	struct hci_event_hdr *eh;
 	struct hci_acl_hdr   *ah;
 	struct hci_sco_hdr   *sh;
@@ -505,8 +505,9 @@
 	return skb_dequeue(&ll->txq);
 }
 
-static struct hci_uart_proto llp = {
+static const struct hci_uart_proto llp = {
 	.id		= HCI_UART_LL,
+	.name		= "LL",
 	.open		= ll_open,
 	.close		= ll_close,
 	.recv		= ll_recv,
@@ -517,14 +518,7 @@
 
 int __init ll_init(void)
 {
-	int err = hci_uart_register_proto(&llp);
-
-	if (!err)
-		BT_INFO("HCILL protocol initialized");
-	else
-		BT_ERR("HCILL protocol registration failed");
-
-	return err;
+	return hci_uart_register_proto(&llp);
 }
 
 int __exit ll_deinit(void)
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 5ee319c..97d64bf 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -35,7 +35,7 @@
 #define HCIUARTGETFLAGS		_IOR('U', 204, int)
 
 /* UART protocols */
-#define HCI_UART_MAX_PROTO	6
+#define HCI_UART_MAX_PROTO	8
 
 #define HCI_UART_H4	0
 #define HCI_UART_BCSP	1
@@ -43,21 +43,26 @@
 #define HCI_UART_H4DS	3
 #define HCI_UART_LL	4
 #define HCI_UART_ATH3K	5
+#define HCI_UART_INTEL	6
+#define HCI_UART_BCM	7
 
 #define HCI_UART_RAW_DEVICE	0
 #define HCI_UART_RESET_ON_INIT	1
 #define HCI_UART_CREATE_AMP	2
 #define HCI_UART_INIT_PENDING	3
 #define HCI_UART_EXT_CONFIG	4
+#define HCI_UART_VND_DETECT	5
 
 struct hci_uart;
 
 struct hci_uart_proto {
 	unsigned int id;
+	const char *name;
 	int (*open)(struct hci_uart *hu);
 	int (*close)(struct hci_uart *hu);
 	int (*flush)(struct hci_uart *hu);
-	int (*recv)(struct hci_uart *hu, void *data, int len);
+	int (*setup)(struct hci_uart *hu);
+	int (*recv)(struct hci_uart *hu, const void *data, int len);
 	int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
 	struct sk_buff *(*dequeue)(struct hci_uart *hu);
 };
@@ -71,7 +76,7 @@
 	struct work_struct	init_ready;
 	struct work_struct	write_work;
 
-	struct hci_uart_proto	*proto;
+	const struct hci_uart_proto *proto;
 	void			*priv;
 
 	struct sk_buff		*tx_skb;
@@ -87,14 +92,48 @@
 #define HCI_UART_SENDING	1
 #define HCI_UART_TX_WAKEUP	2
 
-int hci_uart_register_proto(struct hci_uart_proto *p);
-int hci_uart_unregister_proto(struct hci_uart_proto *p);
+int hci_uart_register_proto(const struct hci_uart_proto *p);
+int hci_uart_unregister_proto(const struct hci_uart_proto *p);
 int hci_uart_tx_wakeup(struct hci_uart *hu);
 int hci_uart_init_ready(struct hci_uart *hu);
 
 #ifdef CPTCFG_BT_HCIUART_H4
 int h4_init(void);
 int h4_deinit(void);
+
+struct h4_recv_pkt {
+	u8  type;	/* Packet type */
+	u8  hlen;	/* Header length */
+	u8  loff;	/* Data length offset in header */
+	u8  lsize;	/* Data length field size */
+	u16 maxlen;	/* Max overall packet length */
+	int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
+};
+
+#define H4_RECV_ACL \
+	.type = HCI_ACLDATA_PKT, \
+	.hlen = HCI_ACL_HDR_SIZE, \
+	.loff = 2, \
+	.lsize = 2, \
+	.maxlen = HCI_MAX_FRAME_SIZE \
+
+#define H4_RECV_SCO \
+	.type = HCI_SCODATA_PKT, \
+	.hlen = HCI_SCO_HDR_SIZE, \
+	.loff = 2, \
+	.lsize = 1, \
+	.maxlen = HCI_MAX_SCO_SIZE
+
+#define H4_RECV_EVENT \
+	.type = HCI_EVENT_PKT, \
+	.hlen = HCI_EVENT_HDR_SIZE, \
+	.loff = 1, \
+	.lsize = 1, \
+	.maxlen = HCI_MAX_EVENT_SIZE
+
+struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+			    const unsigned char *buffer, int count,
+			    const struct h4_recv_pkt *pkts, int pkts_count);
 #endif
 
 #ifdef CPTCFG_BT_HCIUART_BCSP
@@ -116,3 +155,8 @@
 int h5_init(void);
 int h5_deinit(void);
 #endif
+
+#ifdef CPTCFG_BT_HCIUART_BCM
+int bcm_init(void);
+int bcm_deinit(void);
+#endif
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index ac7a704..2d5cd59 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -89,13 +89,21 @@
 
 config MEDIA_CONTROLLER
 	bool "Media Controller API"
-	depends on MEDIA_CAMERA_SUPPORT
+	depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT
 	---help---
 	  Enable the media controller API used to query media devices internal
 	  topology and configure it dynamically.
 
 	  This API is mostly used by camera interfaces in embedded platforms.
 
+config MEDIA_CONTROLLER_DVB
+	bool "Enable Media controller for DVB"
+	depends on MEDIA_CONTROLLER
+	---help---
+	  Enable the media controller API support for DVB.
+
+	  This is currently experimental.
+
 #
 # Video4Linux support
 #	Only enables if one of the V4L2 types (ATV, webcam, radio) is selected
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig
index 7657b43..17e81b6 100644
--- a/drivers/media/common/Kconfig
+++ b/drivers/media/common/Kconfig
@@ -9,11 +9,6 @@
 	tristate
 	depends on m
 
-config VIDEO_BTCX
-	depends on PCI
-	tristate
-	depends on m
-
 config VIDEO_TVEEPROM
 	tristate
 	depends on m
diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile
index d1960f0..f821a87 100644
--- a/drivers/media/common/Makefile
+++ b/drivers/media/common/Makefile
@@ -1,5 +1,4 @@
 obj-y += b2c2/ saa7146/ siano/
 obj-$(CPTCFG_VIDEO_CX2341X) += cx2341x.o
-obj-$(CPTCFG_VIDEO_BTCX)  += btcx-risc.o
 obj-$(CPTCFG_VIDEO_TVEEPROM) += tveeprom.o
 obj-$(CPTCFG_CYPRESS_FIRMWARE) += cypress_firmware.o
diff --git a/drivers/media/common/btcx-risc.h b/drivers/media/common/btcx-risc.h
index f8bc6e8..03583ef 100644
--- a/drivers/media/common/btcx-risc.h
+++ b/drivers/media/common/btcx-risc.h
@@ -26,9 +26,3 @@
 void btcx_calc_skips(int line, int width, int *maxy,
 		     struct btcx_skiplist *skips, unsigned int *nskips,
 		     const struct v4l2_clip *clips, unsigned int nclips);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c
index b7d6393..df1e8c9 100644
--- a/drivers/media/common/saa7146/saa7146_fops.c
+++ b/drivers/media/common/saa7146/saa7146_fops.c
@@ -587,26 +587,20 @@
 }
 EXPORT_SYMBOL_GPL(saa7146_vv_release);
 
-int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
+int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev,
 			    char *name, int type)
 {
-	struct video_device *vfd;
 	int err;
 	int i;
 
 	DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
 
-	// released by vfd->release
-	vfd = video_device_alloc();
-	if (vfd == NULL)
-		return -ENOMEM;
-
 	vfd->fops = &video_fops;
 	if (type == VFL_TYPE_GRABBER)
 		vfd->ioctl_ops = &dev->ext_vv_data->vid_ops;
 	else
 		vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops;
-	vfd->release = video_device_release;
+	vfd->release = video_device_release_empty;
 	vfd->lock = &dev->v4l2_lock;
 	vfd->v4l2_dev = &dev->v4l2_dev;
 	vfd->tvnorms = 0;
@@ -618,25 +612,20 @@
 	err = video_register_device(vfd, type, -1);
 	if (err < 0) {
 		ERR("cannot register v4l2 device. skipping.\n");
-		video_device_release(vfd);
 		return err;
 	}
 
 	pr_info("%s: registered device %s [v4l2]\n",
 		dev->name, video_device_node_name(vfd));
-
-	*vid = vfd;
 	return 0;
 }
 EXPORT_SYMBOL_GPL(saa7146_register_device);
 
-int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
+int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev)
 {
 	DEB_EE("dev:%p\n", dev);
 
-	video_unregister_device(*vid);
-	*vid = NULL;
-
+	video_unregister_device(vfd);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(saa7146_unregister_device);
diff --git a/drivers/media/common/saa7146/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c
index 1e71e37..2da9957 100644
--- a/drivers/media/common/saa7146/saa7146_vbi.c
+++ b/drivers/media/common/saa7146/saa7146_vbi.c
@@ -95,7 +95,7 @@
 
 		/* prepare to wait to be woken up by the irq-handler */
 		add_wait_queue(&vv->vbi_wq, &wait);
-		current->state = TASK_INTERRUPTIBLE;
+		set_current_state(TASK_INTERRUPTIBLE);
 
 		/* start rps1 to enable workaround */
 		saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
@@ -106,7 +106,7 @@
 		DEB_VBI("brs bug workaround %d/1\n", i);
 
 		remove_wait_queue(&vv->vbi_wq, &wait);
-		current->state = TASK_RUNNING;
+		__set_current_state(TASK_RUNNING);
 
 		/* disable rps1 irqs */
 		SAA7146_IER_DISABLE(dev,MASK_28);
diff --git a/drivers/media/common/siano/sms-cards.c b/drivers/media/common/siano/sms-cards.c
index 82c7a12..ca2f80c 100644
--- a/drivers/media/common/siano/sms-cards.c
+++ b/drivers/media/common/siano/sms-cards.c
@@ -21,10 +21,6 @@
 #include "smsir.h"
 #include <linux/module.h>
 
-static int sms_dbg;
-module_param_named(cards_dbg, sms_dbg, int, 0644);
-MODULE_PARM_DESC(cards_dbg, "set debug level (info=1, adv=2 (or-able))");
-
 static struct sms_board sms_boards[] = {
 	[SMS_BOARD_UNKNOWN] = {
 		.name	= "Unknown board",
@@ -232,7 +228,7 @@
 		break; /* BOARD_EVENT_MULTIPLEX_ERRORS */
 
 	default:
-		sms_err("Unknown SMS board event");
+		pr_err("Unknown SMS board event\n");
 		break;
 	}
 	return 0;
@@ -342,7 +338,7 @@
 	int board_id = smscore_get_board_id(coredev);
 	struct sms_board *board = sms_get_board(board_id);
 
-	sms_debug("%s: LNA %s", __func__, onoff ? "enabled" : "disabled");
+	pr_debug("%s: LNA %s\n", __func__, onoff ? "enabled" : "disabled");
 
 	switch (board_id) {
 	case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
diff --git a/drivers/media/common/siano/sms-cards.h b/drivers/media/common/siano/sms-cards.h
index 4c4cadd..bb3d733 100644
--- a/drivers/media/common/siano/sms-cards.h
+++ b/drivers/media/common/siano/sms-cards.h
@@ -20,8 +20,9 @@
 #ifndef __SMS_CARDS_H__
 #define __SMS_CARDS_H__
 
-#include <linux/usb.h>
 #include "smscoreapi.h"
+
+#include <linux/usb.h>
 #include "smsir.h"
 
 #define SMS_BOARD_UNKNOWN 0
diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c
index a367743..8bea0ef 100644
--- a/drivers/media/common/siano/smscoreapi.c
+++ b/drivers/media/common/siano/smscoreapi.c
@@ -21,6 +21,8 @@
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include "smscoreapi.h"
+
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -34,14 +36,9 @@
 #include <linux/wait.h>
 #include <asm/byteorder.h>
 
-#include "smscoreapi.h"
 #include "sms-cards.h"
 #include "smsir.h"
 
-static int sms_dbg;
-module_param_named(debug, sms_dbg, int, 0644);
-MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
-
 struct smscore_device_notifyee_t {
 	struct list_head entry;
 	hotplug_t hotplug;
@@ -460,7 +457,7 @@
 		strcpy(entry->devpath, devpath);
 		list_add(&entry->entry, &g_smscore_registry);
 	} else
-		sms_err("failed to create smscore_registry.");
+		pr_err("failed to create smscore_registry.\n");
 	kmutex_unlock(&g_smscore_registrylock);
 	return entry;
 }
@@ -473,7 +470,7 @@
 	if (entry)
 		return entry->mode;
 	else
-		sms_err("No registry found.");
+		pr_err("No registry found.\n");
 
 	return default_mode;
 }
@@ -487,7 +484,7 @@
 	if (entry)
 		return entry->type;
 	else
-		sms_err("No registry found.");
+		pr_err("No registry found.\n");
 
 	return -EINVAL;
 }
@@ -500,7 +497,7 @@
 	if (entry)
 		entry->mode = mode;
 	else
-		sms_err("No registry found.");
+		pr_err("No registry found.\n");
 }
 
 static void smscore_registry_settype(char *devpath,
@@ -512,7 +509,7 @@
 	if (entry)
 		entry->type = type;
 	else
-		sms_err("No registry found.");
+		pr_err("No registry found.\n");
 }
 
 
@@ -635,10 +632,8 @@
 	struct smscore_buffer_t *cb;
 
 	cb = kzalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL);
-	if (!cb) {
-		sms_info("kzalloc(...) failed");
+	if (!cb)
 		return NULL;
-	}
 
 	cb->p = buffer;
 	cb->offset_in_common = buffer - (u8 *) common_buffer;
@@ -658,16 +653,19 @@
  * @return 0 on success, <0 on error.
  */
 int smscore_register_device(struct smsdevice_params_t *params,
-			    struct smscore_device_t **coredev)
+			    struct smscore_device_t **coredev,
+			    void *mdev)
 {
 	struct smscore_device_t *dev;
 	u8 *buffer;
 
 	dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL);
-	if (!dev) {
-		sms_info("kzalloc(...) failed");
+	if (!dev)
 		return -ENOMEM;
-	}
+
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+	dev->media_dev = mdev;
+#endif
 
 	/* init list entry so it could be safe in smscore_unregister_device */
 	INIT_LIST_HEAD(&dev->entry);
@@ -722,7 +720,7 @@
 		smscore_putbuffer(dev, cb);
 	}
 
-	sms_info("allocated %d buffers", dev->num_buffers);
+	pr_debug("allocated %d buffers\n", dev->num_buffers);
 
 	dev->mode = DEVICE_MODE_NONE;
 	dev->board_id = SMS_BOARD_UNKNOWN;
@@ -746,7 +744,7 @@
 
 	*coredev = dev;
 
-	sms_info("device %p created", dev);
+	pr_debug("device %p created\n", dev);
 
 	return 0;
 }
@@ -763,7 +761,7 @@
 
 	rc = coredev->sendrequest_handler(coredev->context, buffer, size);
 	if (rc < 0) {
-		sms_info("sendrequest returned error %d", rc);
+		pr_info("sendrequest returned error %d\n", rc);
 		return rc;
 	}
 
@@ -786,11 +784,11 @@
 	coredev->ir.dev = NULL;
 	ir_io = sms_get_board(smscore_get_board_id(coredev))->board_cfg.ir;
 	if (ir_io) {/* only if IR port exist we use IR sub-module */
-		sms_info("IR loading");
+		pr_debug("IR loading\n");
 		rc = sms_ir_init(coredev);
 
 		if	(rc != 0)
-			sms_err("Error initialization DTV IR sub-module");
+			pr_err("Error initialization DTV IR sub-module\n");
 		else {
 			buffer = kmalloc(sizeof(struct sms_msg_data2) +
 						SMS_DMA_ALIGNMENT,
@@ -812,11 +810,10 @@
 
 				kfree(buffer);
 			} else
-				sms_err
-				("Sending IR initialization message failed");
+				pr_err("Sending IR initialization message failed\n");
 		}
 	} else
-		sms_info("IR port has not been detected");
+		pr_info("IR port has not been detected\n");
 
 	return 0;
 }
@@ -835,13 +832,13 @@
 
 	board = sms_get_board(coredev->board_id);
 	if (!board) {
-		sms_err("no board configuration exist.");
+		pr_err("no board configuration exist.\n");
 		return -EINVAL;
 	}
 
 	if (board->mtu) {
 		struct sms_msg_data mtu_msg;
-		sms_debug("set max transmit unit %d", board->mtu);
+		pr_debug("set max transmit unit %d\n", board->mtu);
 
 		mtu_msg.x_msg_header.msg_src_id = 0;
 		mtu_msg.x_msg_header.msg_dst_id = HIF_TASK;
@@ -856,7 +853,7 @@
 
 	if (board->crystal) {
 		struct sms_msg_data crys_msg;
-		sms_debug("set crystal value %d", board->crystal);
+		pr_debug("set crystal value %d\n", board->crystal);
 
 		SMS_INIT_MSG(&crys_msg.x_msg_header,
 				MSG_SMS_NEW_CRYSTAL_REQ,
@@ -890,12 +887,12 @@
 
 	rc = smscore_set_device_mode(coredev, mode);
 	if (rc < 0) {
-		sms_info("set device mode faile , rc %d", rc);
+		pr_info("set device mode failed , rc %d\n", rc);
 		return rc;
 	}
 	rc = smscore_configure_board(coredev);
 	if (rc < 0) {
-		sms_info("configure board failed , rc %d", rc);
+		pr_info("configure board failed , rc %d\n", rc);
 		return rc;
 	}
 
@@ -904,7 +901,7 @@
 	rc = smscore_notify_callbacks(coredev, coredev->device, 1);
 	smscore_init_ir(coredev);
 
-	sms_info("device %p started, rc %d", coredev, rc);
+	pr_debug("device %p started, rc %d\n", coredev, rc);
 
 	kmutex_unlock(&g_smscore_deviceslock);
 
@@ -927,7 +924,7 @@
 
 	mem_address = firmware->start_address;
 
-	sms_info("loading FW to addr 0x%x size %d",
+	pr_debug("loading FW to addr 0x%x size %d\n",
 		 mem_address, firmware->length);
 	if (coredev->preload_handler) {
 		rc = coredev->preload_handler(coredev->context);
@@ -941,14 +938,14 @@
 		return -ENOMEM;
 
 	if (coredev->mode != DEVICE_MODE_NONE) {
-		sms_debug("sending reload command.");
+		pr_debug("sending reload command.\n");
 		SMS_INIT_MSG(&msg->x_msg_header, MSG_SW_RELOAD_START_REQ,
 			     sizeof(struct sms_msg_hdr));
 		rc = smscore_sendrequest_and_wait(coredev, msg,
 						  msg->x_msg_header.msg_length,
 						  &coredev->reload_start_done);
 		if (rc < 0) {
-			sms_err("device reload failed, rc %d", rc);
+			pr_err("device reload failed, rc %d\n", rc);
 			goto exit_fw_download;
 		}
 		mem_address = *(u32 *) &payload[20];
@@ -982,7 +979,7 @@
 	if (rc < 0)
 		goto exit_fw_download;
 
-	sms_debug("sending MSG_SMS_DATA_VALIDITY_REQ expecting 0x%x",
+	pr_debug("sending MSG_SMS_DATA_VALIDITY_REQ expecting 0x%x\n",
 		calc_checksum);
 	SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_DATA_VALIDITY_REQ,
 			sizeof(msg->x_msg_header) +
@@ -1001,7 +998,7 @@
 		struct sms_msg_data *trigger_msg =
 			(struct sms_msg_data *) msg;
 
-		sms_debug("sending MSG_SMS_SWDOWNLOAD_TRIGGER_REQ");
+		pr_debug("sending MSG_SMS_SWDOWNLOAD_TRIGGER_REQ\n");
 		SMS_INIT_MSG(&msg->x_msg_header,
 				MSG_SMS_SWDOWNLOAD_TRIGGER_REQ,
 				sizeof(struct sms_msg_hdr) +
@@ -1037,12 +1034,13 @@
 	kfree(msg);
 
 	if (coredev->postload_handler) {
-		sms_debug("rc=%d, postload=0x%p", rc, coredev->postload_handler);
+		pr_debug("rc=%d, postload=0x%p\n",
+			 rc, coredev->postload_handler);
 		if (rc >= 0)
 			return coredev->postload_handler(coredev->context);
 	}
 
-	sms_debug("rc=%d", rc);
+	pr_debug("rc=%d\n", rc);
 	return rc;
 }
 
@@ -1121,11 +1119,11 @@
 	if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX)
 		return NULL;
 
-	sms_debug("trying to get fw name from sms_boards board_id %d mode %d",
+	pr_debug("trying to get fw name from sms_boards board_id %d mode %d\n",
 		  board_id, mode);
 	fw = sms_get_board(board_id)->fw;
 	if (!fw || !fw[mode]) {
-		sms_debug("cannot find fw name in sms_boards, getting from lookup table mode %d type %d",
+		pr_debug("cannot find fw name in sms_boards, getting from lookup table mode %d type %d\n",
 			  mode, type);
 		return smscore_fw_lkup[type][mode];
 	}
@@ -1154,10 +1152,10 @@
 
 	char *fw_filename = smscore_get_fw_filename(coredev, mode);
 	if (!fw_filename) {
-		sms_err("mode %d not supported on this device", mode);
+		pr_err("mode %d not supported on this device\n", mode);
 		return -ENOENT;
 	}
-	sms_debug("Firmware name: %s", fw_filename);
+	pr_debug("Firmware name: %s\n", fw_filename);
 
 	if (loadfirmware_handler == NULL && !(coredev->device_flags
 			& SMS_DEVICE_FAMILY2))
@@ -1165,14 +1163,14 @@
 
 	rc = request_firmware(&fw, fw_filename, coredev->device);
 	if (rc < 0) {
-		sms_err("failed to open firmware file \"%s\"", fw_filename);
+		pr_err("failed to open firmware file '%s'\n", fw_filename);
 		return rc;
 	}
-	sms_info("read fw %s, buffer size=0x%zx", fw_filename, fw->size);
+	pr_debug("read fw %s, buffer size=0x%zx\n", fw_filename, fw->size);
 	fw_buf = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT),
 			 GFP_KERNEL | GFP_DMA);
 	if (!fw_buf) {
-		sms_err("failed to allocate firmware buffer");
+		pr_err("failed to allocate firmware buffer\n");
 		rc = -ENOMEM;
 	} else {
 		memcpy(fw_buf, fw->data, fw->size);
@@ -1226,18 +1224,18 @@
 		if (num_buffers == coredev->num_buffers)
 			break;
 		if (++retry > 10) {
-			sms_info("exiting although not all buffers released.");
+			pr_info("exiting although not all buffers released.\n");
 			break;
 		}
 
-		sms_info("waiting for %d buffer(s)",
+		pr_debug("waiting for %d buffer(s)\n",
 			 coredev->num_buffers - num_buffers);
 		kmutex_unlock(&g_smscore_deviceslock);
 		msleep(100);
 		kmutex_lock(&g_smscore_deviceslock);
 	}
 
-	sms_info("freed %d buffers", num_buffers);
+	pr_debug("freed %d buffers\n", num_buffers);
 
 	if (coredev->common_buffer)
 		dma_free_coherent(NULL, coredev->common_buffer_size,
@@ -1250,7 +1248,7 @@
 
 	kmutex_unlock(&g_smscore_deviceslock);
 
-	sms_info("device %p destroyed", coredev);
+	pr_debug("device %p destroyed\n", coredev);
 }
 EXPORT_SYMBOL_GPL(smscore_unregister_device);
 
@@ -1271,7 +1269,7 @@
 	rc = smscore_sendrequest_and_wait(coredev, msg, msg->msg_length,
 					  &coredev->version_ex_done);
 	if (rc == -ETIME) {
-		sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try");
+		pr_err("MSG_SMS_GET_VERSION_EX_REQ failed first try\n");
 
 		if (wait_for_completion_timeout(&coredev->resume_done,
 						msecs_to_jiffies(5000))) {
@@ -1279,7 +1277,7 @@
 				coredev, msg, msg->msg_length,
 				&coredev->version_ex_done);
 			if (rc < 0)
-				sms_err("MSG_SMS_GET_VERSION_EX_REQ failed second try, rc %d",
+				pr_err("MSG_SMS_GET_VERSION_EX_REQ failed second try, rc %d\n",
 					rc);
 		} else
 			rc = -ETIME;
@@ -1308,7 +1306,7 @@
 	buffer = kmalloc(sizeof(struct sms_msg_data) +
 			SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
 	if (!buffer) {
-		sms_err("Could not allocate buffer for init device message.");
+		pr_err("Could not allocate buffer for init device message.\n");
 		return -ENOMEM;
 	}
 
@@ -1339,10 +1337,10 @@
 {
 	int rc = 0;
 
-	sms_debug("set device mode to %d", mode);
+	pr_debug("set device mode to %d\n", mode);
 	if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
 		if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX) {
-			sms_err("invalid mode specified %d", mode);
+			pr_err("invalid mode specified %d\n", mode);
 			return -EINVAL;
 		}
 
@@ -1351,13 +1349,13 @@
 		if (!(coredev->device_flags & SMS_DEVICE_NOT_READY)) {
 			rc = smscore_detect_mode(coredev);
 			if (rc < 0) {
-				sms_err("mode detect failed %d", rc);
+				pr_err("mode detect failed %d\n", rc);
 				return rc;
 			}
 		}
 
 		if (coredev->mode == mode) {
-			sms_info("device mode %d already set", mode);
+			pr_debug("device mode %d already set\n", mode);
 			return 0;
 		}
 
@@ -1365,19 +1363,19 @@
 			rc = smscore_load_firmware_from_file(coredev,
 							     mode, NULL);
 			if (rc >= 0)
-				sms_info("firmware download success");
+				pr_debug("firmware download success\n");
 		} else {
-			sms_info("mode %d is already supported by running firmware",
+			pr_debug("mode %d is already supported by running firmware\n",
 				 mode);
 		}
 		if (coredev->fw_version >= 0x800) {
 			rc = smscore_init_device(coredev, mode);
 			if (rc < 0)
-				sms_err("device init failed, rc %d.", rc);
+				pr_err("device init failed, rc %d.\n", rc);
 		}
 	} else {
 		if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX) {
-			sms_err("invalid mode specified %d", mode);
+			pr_err("invalid mode specified %d\n", mode);
 			return -EINVAL;
 		}
 
@@ -1414,9 +1412,9 @@
 	}
 
 	if (rc < 0)
-		sms_err("return error code %d.", rc);
+		pr_err("return error code %d.\n", rc);
 	else
-		sms_debug("Success setting device mode.");
+		pr_debug("Success setting device mode.\n");
 
 	return rc;
 }
@@ -1495,7 +1493,7 @@
 		last_sample_time = time_now;
 
 	if (time_now - last_sample_time > 10000) {
-		sms_debug("data rate %d bytes/secs",
+		pr_debug("data rate %d bytes/secs\n",
 			  (int)((data_total * 1000) /
 				(time_now - last_sample_time)));
 
@@ -1539,7 +1537,7 @@
 		{
 			struct sms_version_res *ver =
 				(struct sms_version_res *) phdr;
-			sms_debug("Firmware id %d prots 0x%x ver %d.%d",
+			pr_debug("Firmware id %d prots 0x%x ver %d.%d\n",
 				  ver->firmware_id, ver->supported_protocols,
 				  ver->rom_ver_major, ver->rom_ver_minor);
 
@@ -1562,7 +1560,7 @@
 		{
 			struct sms_msg_data *validity = (struct sms_msg_data *) phdr;
 
-			sms_debug("MSG_SMS_DATA_VALIDITY_RES, checksum = 0x%x",
+			pr_debug("MSG_SMS_DATA_VALIDITY_RES, checksum = 0x%x\n",
 				validity->msg_data[0]);
 			complete(&coredev->data_validity_done);
 			break;
@@ -1588,7 +1586,7 @@
 		{
 			u32 *msgdata = (u32 *) phdr;
 			coredev->gpio_get_res = msgdata[1];
-			sms_debug("gpio level %d",
+			pr_debug("gpio level %d\n",
 					coredev->gpio_get_res);
 			complete(&coredev->gpio_get_level_done);
 			break;
@@ -1615,7 +1613,7 @@
 			break;
 
 		default:
-			sms_debug("message %s(%d) not handled.",
+			pr_debug("message %s(%d) not handled.\n",
 				  smscore_translate_msg(phdr->msg_type),
 				  phdr->msg_type);
 			break;
@@ -1681,7 +1679,7 @@
 	struct smscore_client_t *registered_client;
 
 	if (!client) {
-		sms_err("bad parameter.");
+		pr_err("bad parameter.\n");
 		return -EINVAL;
 	}
 	registered_client = smscore_find_client(coredev, data_type, id);
@@ -1689,12 +1687,12 @@
 		return 0;
 
 	if (registered_client) {
-		sms_err("The msg ID already registered to another client.");
+		pr_err("The msg ID already registered to another client.\n");
 		return -EEXIST;
 	}
 	listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL);
 	if (!listentry) {
-		sms_err("Can't allocate memory for client id.");
+		pr_err("Can't allocate memory for client id.\n");
 		return -ENOMEM;
 	}
 	listentry->id = id;
@@ -1726,13 +1724,13 @@
 	/* check that no other channel with same parameters exists */
 	if (smscore_find_client(coredev, params->data_type,
 				params->initial_id)) {
-		sms_err("Client already exist.");
+		pr_err("Client already exist.\n");
 		return -EEXIST;
 	}
 
 	newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL);
 	if (!newclient) {
-		sms_err("Failed to allocate memory for client.");
+		pr_err("Failed to allocate memory for client.\n");
 		return -ENOMEM;
 	}
 
@@ -1746,7 +1744,7 @@
 	smscore_validate_client(coredev, newclient, params->data_type,
 				params->initial_id);
 	*client = newclient;
-	sms_debug("%p %d %d", params->context, params->data_type,
+	pr_debug("%p %d %d\n", params->context, params->data_type,
 		  params->initial_id);
 
 	return 0;
@@ -1775,7 +1773,7 @@
 		kfree(identry);
 	}
 
-	sms_info("%p", client->context);
+	pr_debug("%p\n", client->context);
 
 	list_del(&client->entry);
 	kfree(client);
@@ -1803,7 +1801,7 @@
 	int rc;
 
 	if (client == NULL) {
-		sms_err("Got NULL client");
+		pr_err("Got NULL client\n");
 		return -EINVAL;
 	}
 
@@ -1811,7 +1809,7 @@
 
 	/* check that no other channel with same id exists */
 	if (coredev == NULL) {
-		sms_err("Got NULL coredev");
+		pr_err("Got NULL coredev\n");
 		return -EINVAL;
 	}
 
@@ -2016,9 +2014,9 @@
 
 	if (rc != 0) {
 		if (rc == -ETIME)
-			sms_err("smscore_gpio_configure timeout");
+			pr_err("smscore_gpio_configure timeout\n");
 		else
-			sms_err("smscore_gpio_configure error");
+			pr_err("smscore_gpio_configure error\n");
 	}
 free:
 	kfree(buffer);
@@ -2065,9 +2063,9 @@
 
 	if (rc != 0) {
 		if (rc == -ETIME)
-			sms_err("smscore_gpio_set_level timeout");
+			pr_err("smscore_gpio_set_level timeout\n");
 		else
-			sms_err("smscore_gpio_set_level error");
+			pr_err("smscore_gpio_set_level error\n");
 	}
 	kfree(buffer);
 
@@ -2113,9 +2111,9 @@
 
 	if (rc != 0) {
 		if (rc == -ETIME)
-			sms_err("smscore_gpio_get_level timeout");
+			pr_err("smscore_gpio_get_level timeout\n");
 		else
-			sms_err("smscore_gpio_get_level error");
+			pr_err("smscore_gpio_get_level error\n");
 	}
 	kfree(buffer);
 
@@ -2163,7 +2161,7 @@
 	}
 	kmutex_unlock(&g_smscore_registrylock);
 
-	sms_debug("");
+	pr_debug("\n");
 }
 
 module_init(smscore_module_init);
diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h
index 9c9063c..80b4b22 100644
--- a/drivers/media/common/siano/smscoreapi.h
+++ b/drivers/media/common/siano/smscoreapi.h
@@ -22,6 +22,8 @@
 #ifndef __SMS_CORE_API_H__
 #define __SMS_CORE_API_H__
 
+#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
+
 #include <linux/device.h>
 #include <linux/list.h>
 #include <linux/mm.h>
@@ -31,6 +33,8 @@
 #include <linux/wait.h>
 #include <linux/timer.h>
 
+#include <media/media-device.h>
+
 #include <asm/page.h>
 
 #include "smsir.h"
@@ -215,6 +219,10 @@
 	bool is_usb_device;
 
 	int led_state;
+
+#if defined(CPTCFG_MEDIA_CONTROLLER_DVB)
+	struct media_device *media_dev;
+#endif
 };
 
 /* GPIO definitions for antenna frequency domain control (SMS8021) */
@@ -1115,7 +1123,8 @@
 extern void smscore_unregister_hotplug(hotplug_t hotplug);
 
 extern int smscore_register_device(struct smsdevice_params_t *params,
-				   struct smscore_device_t **coredev);
+				   struct smscore_device_t **coredev,
+				   void *mdev);
 extern void smscore_unregister_device(struct smscore_device_t *coredev);
 
 extern int smscore_start_device(struct smscore_device_t *coredev);
@@ -1168,25 +1177,4 @@
 
 /* ------------------------------------------------------------------------ */
 
-#define DBG_INFO 1
-#define DBG_ADV  2
-
-#define sms_printk(kern, fmt, arg...) \
-	printk(kern "%s: " fmt "\n", __func__, ##arg)
-
-#define dprintk(kern, lvl, fmt, arg...) do {\
-	if (sms_dbg & lvl) \
-		sms_printk(kern, fmt, ##arg); \
-} while (0)
-
-#define sms_log(fmt, arg...) sms_printk(KERN_INFO, fmt, ##arg)
-#define sms_err(fmt, arg...) \
-	sms_printk(KERN_ERR, "line: %d: " fmt, __LINE__, ##arg)
-#define sms_warn(fmt, arg...)  sms_printk(KERN_WARNING, fmt, ##arg)
-#define sms_info(fmt, arg...) \
-	dprintk(KERN_INFO, DBG_INFO, fmt, ##arg)
-#define sms_debug(fmt, arg...) \
-	dprintk(KERN_DEBUG, DBG_ADV, fmt, ##arg)
-
-
 #endif /* __SMS_CORE_API_H__ */
diff --git a/drivers/media/common/siano/smsdvb-debugfs.c b/drivers/media/common/siano/smsdvb-debugfs.c
index 2408d7e..1a8677a 100644
--- a/drivers/media/common/siano/smsdvb-debugfs.c
+++ b/drivers/media/common/siano/smsdvb-debugfs.c
@@ -17,7 +17,7 @@
  *
  ***********************************************************************/
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include "smscoreapi.h"
 
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -31,8 +31,6 @@
 #include "dvb_demux.h"
 #include "dvb_frontend.h"
 
-#include "smscoreapi.h"
-
 #include "smsdvb.h"
 
 static struct dentry *smsdvb_debugfs_usb_root;
@@ -536,7 +534,7 @@
 	 */
 	d = debugfs_create_dir("smsdvb", usb_debug_root);
 	if (IS_ERR_OR_NULL(d)) {
-		sms_err("Couldn't create sysfs node for smsdvb");
+		pr_err("Couldn't create sysfs node for smsdvb\n");
 		return PTR_ERR(d);
 	} else {
 		smsdvb_debugfs_usb_root = d;
diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c
index 85151ef..3278eb2 100644
--- a/drivers/media/common/siano/smsdvb-main.c
+++ b/drivers/media/common/siano/smsdvb-main.c
@@ -19,6 +19,8 @@
 
 ****************************************************************/
 
+#include "smscoreapi.h"
+
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/init.h>
@@ -29,7 +31,6 @@
 #include "dvb_demux.h"
 #include "dvb_frontend.h"
 
-#include "smscoreapi.h"
 #include "sms-cards.h"
 
 #include "smsdvb.h"
@@ -39,11 +40,6 @@
 static struct list_head g_smsdvb_clients;
 static struct mutex g_smsdvb_clientslock;
 
-static int sms_dbg;
-module_param_named(debug, sms_dbg, int, 0644);
-MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
-
-
 static u32 sms_to_guard_interval_table[] = {
 	[0] = GUARD_INTERVAL_1_32,
 	[1] = GUARD_INTERVAL_1_16,
@@ -82,48 +78,48 @@
 	struct smscore_device_t *coredev = client->coredev;
 	switch (event) {
 	case DVB3_EVENT_INIT:
-		sms_debug("DVB3_EVENT_INIT");
+		pr_debug("DVB3_EVENT_INIT\n");
 		sms_board_event(coredev, BOARD_EVENT_BIND);
 		break;
 	case DVB3_EVENT_SLEEP:
-		sms_debug("DVB3_EVENT_SLEEP");
+		pr_debug("DVB3_EVENT_SLEEP\n");
 		sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND);
 		break;
 	case DVB3_EVENT_HOTPLUG:
-		sms_debug("DVB3_EVENT_HOTPLUG");
+		pr_debug("DVB3_EVENT_HOTPLUG\n");
 		sms_board_event(coredev, BOARD_EVENT_POWER_INIT);
 		break;
 	case DVB3_EVENT_FE_LOCK:
 		if (client->event_fe_state != DVB3_EVENT_FE_LOCK) {
 			client->event_fe_state = DVB3_EVENT_FE_LOCK;
-			sms_debug("DVB3_EVENT_FE_LOCK");
+			pr_debug("DVB3_EVENT_FE_LOCK\n");
 			sms_board_event(coredev, BOARD_EVENT_FE_LOCK);
 		}
 		break;
 	case DVB3_EVENT_FE_UNLOCK:
 		if (client->event_fe_state != DVB3_EVENT_FE_UNLOCK) {
 			client->event_fe_state = DVB3_EVENT_FE_UNLOCK;
-			sms_debug("DVB3_EVENT_FE_UNLOCK");
+			pr_debug("DVB3_EVENT_FE_UNLOCK\n");
 			sms_board_event(coredev, BOARD_EVENT_FE_UNLOCK);
 		}
 		break;
 	case DVB3_EVENT_UNC_OK:
 		if (client->event_unc_state != DVB3_EVENT_UNC_OK) {
 			client->event_unc_state = DVB3_EVENT_UNC_OK;
-			sms_debug("DVB3_EVENT_UNC_OK");
+			pr_debug("DVB3_EVENT_UNC_OK\n");
 			sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_OK);
 		}
 		break;
 	case DVB3_EVENT_UNC_ERR:
 		if (client->event_unc_state != DVB3_EVENT_UNC_ERR) {
 			client->event_unc_state = DVB3_EVENT_UNC_ERR;
-			sms_debug("DVB3_EVENT_UNC_ERR");
+			pr_debug("DVB3_EVENT_UNC_ERR\n");
 			sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS);
 		}
 		break;
 
 	default:
-		sms_err("Unknown dvb3 api event");
+		pr_err("Unknown dvb3 api event\n");
 		break;
 	}
 }
@@ -590,7 +586,7 @@
 		is_status_update = true;
 		break;
 	default:
-		sms_info("message not handled");
+		pr_debug("message not handled\n");
 	}
 	smscore_putbuffer(client->coredev, cb);
 
@@ -613,6 +609,19 @@
 	return 0;
 }
 
+static void smsdvb_media_device_unregister(struct smsdvb_client_t *client)
+{
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+	struct smscore_device_t *coredev = client->coredev;
+
+	if (!coredev->media_dev)
+		return;
+	media_device_unregister(coredev->media_dev);
+	kfree(coredev->media_dev);
+	coredev->media_dev = NULL;
+#endif
+}
+
 static void smsdvb_unregister_client(struct smsdvb_client_t *client)
 {
 	/* must be called under clientslock */
@@ -624,6 +633,7 @@
 	dvb_unregister_frontend(&client->frontend);
 	dvb_dmxdev_release(&client->dmxdev);
 	dvb_dmx_release(&client->demux);
+	smsdvb_media_device_unregister(client);
 	dvb_unregister_adapter(&client->adapter);
 	kfree(client);
 }
@@ -643,7 +653,7 @@
 		container_of(feed->demux, struct smsdvb_client_t, demux);
 	struct sms_msg_data pid_msg;
 
-	sms_debug("add pid %d(%x)",
+	pr_debug("add pid %d(%x)\n",
 		  feed->pid, feed->pid);
 
 	client->feed_users++;
@@ -665,7 +675,7 @@
 		container_of(feed->demux, struct smsdvb_client_t, demux);
 	struct sms_msg_data pid_msg;
 
-	sms_debug("remove pid %d(%x)",
+	pr_debug("remove pid %d(%x)\n",
 		  feed->pid, feed->pid);
 
 	client->feed_users--;
@@ -835,7 +845,7 @@
 static int smsdvb_get_tune_settings(struct dvb_frontend *fe,
 				    struct dvb_frontend_tune_settings *tune)
 {
-	sms_debug("");
+	pr_debug("\n");
 
 	tune->min_delay_ms = 400;
 	tune->step_size = 250000;
@@ -869,7 +879,7 @@
 	msg.Data[0] = c->frequency;
 	msg.Data[2] = 12000000;
 
-	sms_info("%s: freq %d band %d", __func__, c->frequency,
+	pr_debug("%s: freq %d band %d\n", __func__, c->frequency,
 		 c->bandwidth_hz);
 
 	switch (c->bandwidth_hz / 1000000) {
@@ -954,7 +964,7 @@
 
 	c->bandwidth_hz = 6000000;
 
-	sms_info("%s: freq %d segwidth %d segindex %d", __func__,
+	pr_debug("freq %d segwidth %d segindex %d\n",
 		 c->frequency, c->isdbt_sb_segment_count,
 		 c->isdbt_sb_segment_idx);
 
@@ -1082,10 +1092,8 @@
 	if (!arrival)
 		return 0;
 	client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL);
-	if (!client) {
-		sms_err("kmalloc() failed");
+	if (!client)
 		return -ENOMEM;
-	}
 
 	/* register dvb adapter */
 	rc = dvb_register_adapter(&client->adapter,
@@ -1093,9 +1101,10 @@
 					smscore_get_board_id(coredev))->name,
 				  THIS_MODULE, device, adapter_nr);
 	if (rc < 0) {
-		sms_err("dvb_register_adapter() failed %d", rc);
+		pr_err("dvb_register_adapter() failed %d\n", rc);
 		goto adapter_error;
 	}
+	dvb_register_media_controller(&client->adapter, coredev->media_dev);
 
 	/* init dvb demux */
 	client->demux.dmx.capabilities = DMX_TS_FILTERING;
@@ -1106,7 +1115,7 @@
 
 	rc = dvb_dmx_init(&client->demux);
 	if (rc < 0) {
-		sms_err("dvb_dmx_init failed %d", rc);
+		pr_err("dvb_dmx_init failed %d\n", rc);
 		goto dvbdmx_error;
 	}
 
@@ -1117,7 +1126,7 @@
 
 	rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter);
 	if (rc < 0) {
-		sms_err("dvb_dmxdev_init failed %d", rc);
+		pr_err("dvb_dmxdev_init failed %d\n", rc);
 		goto dmxdev_error;
 	}
 
@@ -1138,7 +1147,7 @@
 
 	rc = dvb_register_frontend(&client->adapter, &client->frontend);
 	if (rc < 0) {
-		sms_err("frontend registration failed %d", rc);
+		pr_err("frontend registration failed %d\n", rc);
 		goto frontend_error;
 	}
 
@@ -1150,7 +1159,7 @@
 
 	rc = smscore_register_client(coredev, &params, &client->smsclient);
 	if (rc < 0) {
-		sms_err("smscore_register_client() failed %d", rc);
+		pr_err("smscore_register_client() failed %d\n", rc);
 		goto client_error;
 	}
 
@@ -1169,12 +1178,14 @@
 	client->event_unc_state = -1;
 	sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG);
 
-	sms_info("success");
 	sms_board_setup(coredev);
 
 	if (smsdvb_debugfs_create(client) < 0)
-		sms_info("failed to create debugfs node");
+		pr_info("failed to create debugfs node\n");
 
+	dvb_create_media_graph(&client->adapter);
+
+	pr_info("DVB interface registered.\n");
 	return 0;
 
 client_error:
@@ -1187,6 +1198,7 @@
 	dvb_dmx_release(&client->demux);
 
 dvbdmx_error:
+	smsdvb_media_device_unregister(client);
 	dvb_unregister_adapter(&client->adapter);
 
 adapter_error:
@@ -1205,7 +1217,7 @@
 
 	rc = smscore_register_hotplug(smsdvb_hotplug);
 
-	sms_debug("");
+	pr_debug("\n");
 
 	return rc;
 }
diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
index 35d0e88..1d60d20 100644
--- a/drivers/media/common/siano/smsir.c
+++ b/drivers/media/common/siano/smsir.c
@@ -25,10 +25,11 @@
  ****************************************************************/
 
 
+#include "smscoreapi.h"
+
 #include <linux/types.h>
 #include <linux/input.h>
 
-#include "smscoreapi.h"
 #include "smsir.h"
 #include "sms-cards.h"
 
@@ -56,16 +57,14 @@
 	int board_id = smscore_get_board_id(coredev);
 	struct rc_dev *dev;
 
-	sms_log("Allocating rc device");
+	pr_debug("Allocating rc device\n");
 	dev = rc_allocate_device();
-	if (!dev) {
-		sms_err("Not enough memory");
+	if (!dev)
 		return -ENOMEM;
-	}
 
 	coredev->ir.controller = 0;	/* Todo: vega/nova SPI number */
 	coredev->ir.timeout = IR_DEFAULT_TIMEOUT;
-	sms_log("IR port %d, timeout %d ms",
+	pr_debug("IR port %d, timeout %d ms\n",
 			coredev->ir.controller, coredev->ir.timeout);
 
 	snprintf(coredev->ir.name, sizeof(coredev->ir.name),
@@ -92,11 +91,12 @@
 	dev->map_name = sms_get_board(board_id)->rc_codes;
 	dev->driver_name = MODULE_NAME;
 
-	sms_log("Input device (IR) %s is set for key events", dev->input_name);
+	pr_debug("Input device (IR) %s is set for key events\n",
+		 dev->input_name);
 
 	err = rc_register_device(dev);
 	if (err < 0) {
-		sms_err("Failed to register device");
+		pr_err("Failed to register device\n");
 		rc_free_device(dev);
 		return err;
 	}
@@ -109,5 +109,5 @@
 {
 	rc_unregister_device(coredev->ir.dev);
 
-	sms_log("");
+	pr_debug("\n");
 }
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index abff803..233150c 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -1136,10 +1136,13 @@
 	.llseek = default_llseek,
 };
 
-static struct dvb_device dvbdev_demux = {
+static const struct dvb_device dvbdev_demux = {
 	.priv = NULL,
 	.users = 1,
 	.writers = 1,
+#if defined(CPTCFG_MEDIA_CONTROLLER_DVB)
+	.name = "dvb-demux",
+#endif
 	.fops = &dvb_demux_fops
 };
 
@@ -1209,13 +1212,15 @@
 	.llseek = default_llseek,
 };
 
-static struct dvb_device dvbdev_dvr = {
+static const struct dvb_device dvbdev_dvr = {
 	.priv = NULL,
 	.readers = 1,
 	.users = 1,
+#if defined(CPTCFG_MEDIA_CONTROLLER_DVB)
+	.name = "dvb-dvr",
+#endif
 	.fops = &dvb_dvr_fops
 };
-
 int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
 {
 	int i;
diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
index 80ab8d0..c117fb3 100644
--- a/drivers/media/dvb-core/dvb-usb-ids.h
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
@@ -245,6 +245,7 @@
 #define USB_PID_TECHNOTREND_CONNECT_S2400               0x3006
 #define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM	0x3009
 #define USB_PID_TECHNOTREND_CONNECT_CT3650		0x300d
+#define USB_PID_TECHNOTREND_CONNECT_S2_4600             0x3011
 #define USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI		0x3012
 #define USB_PID_TECHNOTREND_TVSTICK_CT2_4400		0x3014
 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY	0x005a
@@ -318,6 +319,7 @@
 #define USB_PID_GRANDTEC_DVBT_USB2_COLD			0x0bc6
 #define USB_PID_GRANDTEC_DVBT_USB2_WARM			0x0bc7
 #define USB_PID_WINFAST_DTV2000DS			0x6a04
+#define USB_PID_WINFAST_DTV2000DS_PLUS			0x6f12
 #define USB_PID_WINFAST_DTV_DONGLE_COLD			0x6025
 #define USB_PID_WINFAST_DTV_DONGLE_WARM			0x6026
 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P		0x6f00
@@ -385,4 +387,5 @@
 #define USB_PID_PCTV_2002E                              0x025c
 #define USB_PID_PCTV_2002E_SE                           0x025d
 #define USB_PID_SVEON_STV27                             0xd3af
+#define USB_PID_TURBOX_DTT_2000                         0xd3a4
 #endif
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index 0aac309..6983c42 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -1638,15 +1638,17 @@
 	.llseek = noop_llseek,
 };
 
-static struct dvb_device dvbdev_ca = {
+static const struct dvb_device dvbdev_ca = {
 	.priv = NULL,
 	.users = 1,
 	.readers = 1,
 	.writers = 1,
+#if defined(CPTCFG_MEDIA_CONTROLLER_DVB)
+	.name = "dvb-ca-en50221",
+#endif
 	.fops = &dvb_ca_fops,
 };
 
-
 /* ******************************************************************************** */
 /* Initialisation/shutdown functions */
 
@@ -1676,14 +1678,14 @@
 	/* initialise the system data */
 	if ((ca = kzalloc(sizeof(struct dvb_ca_private), GFP_KERNEL)) == NULL) {
 		ret = -ENOMEM;
-		goto error;
+		goto exit;
 	}
 	ca->pub = pubca;
 	ca->flags = flags;
 	ca->slot_count = slot_count;
 	if ((ca->slot_info = kcalloc(slot_count, sizeof(struct dvb_ca_slot), GFP_KERNEL)) == NULL) {
 		ret = -ENOMEM;
-		goto error;
+		goto free_ca;
 	}
 	init_waitqueue_head(&ca->wait_queue);
 	ca->open = 0;
@@ -1694,7 +1696,7 @@
 	/* register the DVB device */
 	ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA);
 	if (ret)
-		goto error;
+		goto free_slot_info;
 
 	/* now initialise each slot */
 	for (i = 0; i < slot_count; i++) {
@@ -1709,7 +1711,7 @@
 
 	if (signal_pending(current)) {
 		ret = -EINTR;
-		goto error;
+		goto unregister_device;
 	}
 	mb();
 
@@ -1720,17 +1722,17 @@
 		ret = PTR_ERR(ca->thread);
 		printk("dvb_ca_init: failed to start kernel_thread (%d)\n",
 			ret);
-		goto error;
+		goto unregister_device;
 	}
 	return 0;
 
-error:
-	if (ca != NULL) {
-		if (ca->dvbdev != NULL)
-			dvb_unregister_device(ca->dvbdev);
-		kfree(ca->slot_info);
-		kfree(ca);
-	}
+unregister_device:
+	dvb_unregister_device(ca->dvbdev);
+free_slot_info:
+	kfree(ca->slot_info);
+free_ca:
+	kfree(ca);
+exit:
 	pubca->private = NULL;
 	return ret;
 }
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
index c29531e..ae00733 100644
--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -131,6 +131,11 @@
 	int quality;
 	unsigned int check_wrapped;
 	enum dvbfe_search algo_status;
+
+#if defined(CPTCFG_MEDIA_CONTROLLER_DVB)
+	struct media_pipeline pipe;
+	struct media_entity *pipe_start_entity;
+#endif
 };
 
 static void dvb_frontend_wakeup(struct dvb_frontend *fe);
@@ -590,12 +595,106 @@
 	wake_up_interruptible(&fepriv->wait_queue);
 }
 
+/**
+ * dvb_enable_media_tuner() - tries to enable the DVB tuner
+ *
+ * @fe:		struct dvb_frontend pointer
+ *
+ * This function ensures that just one media tuner is enabled for a given
+ * frontend. It has two different behaviors:
+ * - For trivial devices with just one tuner:
+ *   it just enables the existing tuner->fe link
+ * - For devices with more than one tuner:
+ *   It is up to the driver to implement the logic that will enable one tuner
+ *   and disable the other ones. However, if more than one tuner is enabled for
+ *   the same frontend, it will print an error message and return -EINVAL.
+ *
+ * At return, it will return the error code returned by media_entity_setup_link,
+ * or 0 if everything is OK, if no tuner is linked to the frontend or if the
+ * mdev is NULL.
+ */
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+static int dvb_enable_media_tuner(struct dvb_frontend *fe)
+{
+	struct dvb_frontend_private *fepriv = fe->frontend_priv;
+	struct dvb_adapter *adapter = fe->dvb;
+	struct media_device *mdev = adapter->mdev;
+	struct media_entity  *entity, *source;
+	struct media_link *link, *found_link = NULL;
+	int i, ret, n_links = 0, active_links = 0;
+
+	fepriv->pipe_start_entity = NULL;
+
+	if (!mdev)
+		return 0;
+
+	entity = fepriv->dvbdev->entity;
+	fepriv->pipe_start_entity = entity;
+
+	for (i = 0; i < entity->num_links; i++) {
+		link = &entity->links[i];
+		if (link->sink->entity == entity) {
+			found_link = link;
+			n_links++;
+			if (link->flags & MEDIA_LNK_FL_ENABLED)
+				active_links++;
+		}
+	}
+
+	if (!n_links || active_links == 1 || !found_link)
+		return 0;
+
+	/*
+	 * If a frontend has more than one tuner linked, it is up to the driver
+	 * to select with one will be the active one, as the frontend core can't
+	 * guess. If the driver doesn't do that, it is a bug.
+	 */
+	if (n_links > 1 && active_links != 1) {
+		dev_err(fe->dvb->device,
+			"WARNING: there are %d active links among %d tuners. This is a driver's bug!\n",
+			active_links, n_links);
+		return -EINVAL;
+	}
+
+	source = found_link->source->entity;
+	fepriv->pipe_start_entity = source;
+	for (i = 0; i < source->num_links; i++) {
+		struct media_entity *sink;
+		int flags = 0;
+
+		link = &source->links[i];
+		sink = link->sink->entity;
+
+		if (sink == entity)
+			flags = MEDIA_LNK_FL_ENABLED;
+
+		ret = media_entity_setup_link(link, flags);
+		if (ret) {
+			dev_err(fe->dvb->device,
+				"Couldn't change link %s->%s to %s. Error %d\n",
+				source->name, sink->name,
+				flags ? "enabled" : "disabled",
+				ret);
+			return ret;
+		} else
+			dev_dbg(fe->dvb->device,
+				"link %s->%s was %s\n",
+				source->name, sink->name,
+				flags ? "ENABLED" : "disabled");
+	}
+	return 0;
+}
+#endif
+
 static int dvb_frontend_thread(void *data)
 {
 	struct dvb_frontend *fe = data;
 	struct dvb_frontend_private *fepriv = fe->frontend_priv;
 	fe_status_t s;
 	enum dvbfe_algo algo;
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+	int ret;
+#endif
 
 	bool re_tune = false;
 	bool semheld = false;
@@ -609,6 +708,20 @@
 	fepriv->wakeup = 0;
 	fepriv->reinitialise = 0;
 
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+	ret = dvb_enable_media_tuner(fe);
+	if (ret) {
+		/* FIXME: return an error if it fails */
+		dev_info(fe->dvb->device,
+			"proceeding with FE task\n");
+	} else if (fepriv->pipe_start_entity) {
+		ret = media_entity_pipeline_start(fepriv->pipe_start_entity,
+						  &fepriv->pipe);
+		if (ret)
+			return ret;
+	}
+#endif
+
 	dvb_frontend_init(fe);
 
 	set_freezable();
@@ -718,6 +831,12 @@
 		}
 	}
 
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+	if (fepriv->pipe_start_entity)
+		media_entity_pipeline_stop(fepriv->pipe_start_entity);
+	fepriv->pipe_start_entity = NULL;
+#endif
+
 	if (dvb_powerdown_on_sleep) {
 		if (fe->ops.set_voltage)
 			fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF);
@@ -2612,11 +2731,14 @@
 			  struct dvb_frontend* fe)
 {
 	struct dvb_frontend_private *fepriv;
-	static const struct dvb_device dvbdev_template = {
+	const struct dvb_device dvbdev_template = {
 		.users = ~0,
 		.writers = 1,
 		.readers = (~0)-1,
 		.fops = &dvb_frontend_fops,
+#if defined(CPTCFG_MEDIA_CONTROLLER_DVB)
+		.name = fe->ops.info.name,
+#endif
 		.kernel_ioctl = dvb_frontend_ioctl
 	};
 
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
index e4041f0..f5a03c2 100644
--- a/drivers/media/dvb-core/dvb_net.c
+++ b/drivers/media/dvb-core/dvb_net.c
@@ -68,13 +68,6 @@
 #include "dvb_demux.h"
 #include "dvb_net.h"
 
-static int dvb_net_debug;
-module_param(dvb_net_debug, int, 0444);
-MODULE_PARM_DESC(dvb_net_debug, "enable debug messages");
-
-#define dprintk(x...) do { if (dvb_net_debug) printk(x); } while (0)
-
-
 static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt )
 {
 	unsigned int j;
@@ -90,36 +83,9 @@
 
 #ifdef ULE_DEBUG
 
-#define MAC_ADDR_PRINTFMT "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"
-#define MAX_ADDR_PRINTFMT_ARGS(macap) (macap)[0],(macap)[1],(macap)[2],(macap)[3],(macap)[4],(macap)[5]
-
-#define isprint(c)	((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
-
-static void hexdump( const unsigned char *buf, unsigned short len )
+static void hexdump(const unsigned char *buf, unsigned short len)
 {
-	char str[80], octet[10];
-	int ofs, i, l;
-
-	for (ofs = 0; ofs < len; ofs += 16) {
-		sprintf( str, "%03d: ", ofs );
-
-		for (i = 0; i < 16; i++) {
-			if ((i + ofs) < len)
-				sprintf( octet, "%02x ", buf[ofs + i] );
-			else
-				strcpy( octet, "   " );
-
-			strcat( str, octet );
-		}
-		strcat( str, "  " );
-		l = strlen( str );
-
-		for (i = 0; (i < 16) && ((i + ofs) < len); i++)
-			str[l++] = isprint( buf[ofs + i] ) ? buf[ofs + i] : '.';
-
-		str[l] = '\0';
-		printk( KERN_WARNING "%s\n", str );
-	}
+	print_hex_dump_debug("", DUMP_PREFIX_OFFSET, 16, 1, buf, len, true);
 }
 
 #endif
@@ -315,9 +281,9 @@
 			return l;	/* Stop extension header processing and discard SNDU. */
 		total_ext_len += l;
 #ifdef ULE_DEBUG
-		dprintk("handle_ule_extensions: ule_next_hdr=%p, ule_sndu_type=%i, "
-			"l=%i, total_ext_len=%i\n", p->ule_next_hdr,
-			(int) p->ule_sndu_type, l, total_ext_len);
+		pr_debug("ule_next_hdr=%p, ule_sndu_type=%i, l=%i, total_ext_len=%i\n",
+			 p->ule_next_hdr, (int)p->ule_sndu_type,
+			 l, total_ext_len);
 #endif
 
 	} while (p->ule_sndu_type < ETH_P_802_3_MIN);
@@ -700,8 +666,8 @@
 
 					if (drop) {
 #ifdef ULE_DEBUG
-						dprintk("Dropping SNDU: MAC destination address does not match: dest addr: "MAC_ADDR_PRINTFMT", dev addr: "MAC_ADDR_PRINTFMT"\n",
-							MAX_ADDR_PRINTFMT_ARGS(priv->ule_skb->data), MAX_ADDR_PRINTFMT_ARGS(dev->dev_addr));
+						netdev_dbg(dev, "Dropping SNDU: MAC destination address does not match: dest addr: %pM, dev addr: %pM\n",
+							   priv->ule_skb->data, dev->dev_addr);
 #endif
 						dev_kfree_skb(priv->ule_skb);
 						goto sndu_done;
@@ -964,8 +930,7 @@
 	(*secfilter)->filter_mask[10] = mac_mask[1];
 	(*secfilter)->filter_mask[11]=mac_mask[0];
 
-	dprintk("%s: filter mac=%pM\n", dev->name, mac);
-	dprintk("%s: filter mask=%pM\n", dev->name, mac_mask);
+	netdev_dbg(dev, "filter mac=%pM mask=%pM\n", mac, mac_mask);
 
 	return 0;
 }
@@ -977,7 +942,7 @@
 	struct dmx_demux *demux = priv->demux;
 	unsigned char *mac = (unsigned char *) dev->dev_addr;
 
-	dprintk("%s: rx_mode %i\n", __func__, priv->rx_mode);
+	netdev_dbg(dev, "rx_mode %i\n", priv->rx_mode);
 	mutex_lock(&priv->mutex);
 	if (priv->tsfeed || priv->secfeed || priv->secfilter || priv->multi_secfilter[0])
 		printk("%s: BUG %d\n", __func__, __LINE__);
@@ -987,7 +952,7 @@
 	priv->tsfeed = NULL;
 
 	if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
-		dprintk("%s: alloc secfeed\n", __func__);
+		netdev_dbg(dev, "alloc secfeed\n");
 		ret=demux->allocate_section_feed(demux, &priv->secfeed,
 					 dvb_net_sec_callback);
 		if (ret<0) {
@@ -1005,38 +970,38 @@
 		}
 
 		if (priv->rx_mode != RX_MODE_PROMISC) {
-			dprintk("%s: set secfilter\n", __func__);
+			netdev_dbg(dev, "set secfilter\n");
 			dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_normal);
 		}
 
 		switch (priv->rx_mode) {
 		case RX_MODE_MULTI:
 			for (i = 0; i < priv->multi_num; i++) {
-				dprintk("%s: set multi_secfilter[%d]\n", __func__, i);
+				netdev_dbg(dev, "set multi_secfilter[%d]\n", i);
 				dvb_net_filter_sec_set(dev, &priv->multi_secfilter[i],
 						       priv->multi_macs[i], mask_normal);
 			}
 			break;
 		case RX_MODE_ALL_MULTI:
 			priv->multi_num=1;
-			dprintk("%s: set multi_secfilter[0]\n", __func__);
+			netdev_dbg(dev, "set multi_secfilter[0]\n");
 			dvb_net_filter_sec_set(dev, &priv->multi_secfilter[0],
 					       mac_allmulti, mask_allmulti);
 			break;
 		case RX_MODE_PROMISC:
 			priv->multi_num=0;
-			dprintk("%s: set secfilter\n", __func__);
+			netdev_dbg(dev, "set secfilter\n");
 			dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_promisc);
 			break;
 		}
 
-		dprintk("%s: start filtering\n", __func__);
+		netdev_dbg(dev, "start filtering\n");
 		priv->secfeed->start_filtering(priv->secfeed);
 	} else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
 		struct timespec timeout = { 0, 10000000 }; // 10 msec
 
 		/* we have payloads encapsulated in TS */
-		dprintk("%s: alloc tsfeed\n", __func__);
+		netdev_dbg(dev, "alloc tsfeed\n");
 		ret = demux->allocate_ts_feed(demux, &priv->tsfeed, dvb_net_ts_callback);
 		if (ret < 0) {
 			printk("%s: could not allocate ts feed\n", dev->name);
@@ -1060,7 +1025,7 @@
 			goto error;
 		}
 
-		dprintk("%s: start filtering\n", __func__);
+		netdev_dbg(dev, "start filtering\n");
 		priv->tsfeed->start_filtering(priv->tsfeed);
 	} else
 		ret = -EINVAL;
@@ -1075,17 +1040,16 @@
 	struct dvb_net_priv *priv = netdev_priv(dev);
 	int i, ret = 0;
 
-	dprintk("%s\n", __func__);
 	mutex_lock(&priv->mutex);
 	if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
 		if (priv->secfeed) {
 			if (priv->secfeed->is_filtering) {
-				dprintk("%s: stop secfeed\n", __func__);
+				netdev_dbg(dev, "stop secfeed\n");
 				priv->secfeed->stop_filtering(priv->secfeed);
 			}
 
 			if (priv->secfilter) {
-				dprintk("%s: release secfilter\n", __func__);
+				netdev_dbg(dev, "release secfilter\n");
 				priv->secfeed->release_filter(priv->secfeed,
 							      priv->secfilter);
 				priv->secfilter=NULL;
@@ -1093,8 +1057,8 @@
 
 			for (i=0; i<priv->multi_num; i++) {
 				if (priv->multi_secfilter[i]) {
-					dprintk("%s: release multi_filter[%d]\n",
-						__func__, i);
+					netdev_dbg(dev, "release multi_filter[%d]\n",
+						   i);
 					priv->secfeed->release_filter(priv->secfeed,
 								      priv->multi_secfilter[i]);
 					priv->multi_secfilter[i] = NULL;
@@ -1108,7 +1072,7 @@
 	} else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
 		if (priv->tsfeed) {
 			if (priv->tsfeed->is_filtering) {
-				dprintk("%s: stop tsfeed\n", __func__);
+				netdev_dbg(dev, "stop tsfeed\n");
 				priv->tsfeed->stop_filtering(priv->tsfeed);
 			}
 			priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
@@ -1148,16 +1112,16 @@
 	netif_addr_lock_bh(dev);
 
 	if (dev->flags & IFF_PROMISC) {
-		dprintk("%s: promiscuous mode\n", dev->name);
+		netdev_dbg(dev, "promiscuous mode\n");
 		priv->rx_mode = RX_MODE_PROMISC;
 	} else if ((dev->flags & IFF_ALLMULTI)) {
-		dprintk("%s: allmulti mode\n", dev->name);
+		netdev_dbg(dev, "allmulti mode\n");
 		priv->rx_mode = RX_MODE_ALL_MULTI;
 	} else if (!netdev_mc_empty(dev)) {
 		struct netdev_hw_addr *ha;
 
-		dprintk("%s: set_mc_list, %d entries\n",
-			dev->name, netdev_mc_count(dev));
+		netdev_dbg(dev, "set_mc_list, %d entries\n",
+			   netdev_mc_count(dev));
 
 		priv->rx_mode = RX_MODE_MULTI;
 		priv->multi_num = 0;
@@ -1226,7 +1190,6 @@
 static const struct header_ops dvb_header_ops = {
 	.create		= eth_header,
 	.parse		= eth_header_parse,
-	.rebuild	= eth_rebuild_header,
 };
 
 
@@ -1498,14 +1461,16 @@
 	.llseek = noop_llseek,
 };
 
-static struct dvb_device dvbdev_net = {
+static const struct dvb_device dvbdev_net = {
 	.priv = NULL,
 	.users = 1,
 	.writers = 1,
+#if defined(CPTCFG_MEDIA_CONTROLLER_DVB)
+	.name = "dvb-net",
+#endif
 	.fops = &dvb_net_fops,
 };
 
-
 void dvb_net_release (struct dvb_net *dvbnet)
 {
 	int i;
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index 5f07cc2..fc65aa7 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -180,6 +180,93 @@
 	return -ENFILE;
 }
 
+static void dvb_register_media_device(struct dvb_device *dvbdev,
+				      int type, int minor)
+{
+#if defined(CPTCFG_MEDIA_CONTROLLER_DVB)
+	int ret = 0, npads;
+
+	if (!dvbdev->adapter->mdev)
+		return;
+
+	dvbdev->entity = kzalloc(sizeof(*dvbdev->entity), GFP_KERNEL);
+	if (!dvbdev->entity)
+		return;
+
+	dvbdev->entity->info.dev.major = DVB_MAJOR;
+	dvbdev->entity->info.dev.minor = minor;
+	dvbdev->entity->name = dvbdev->name;
+
+	switch (type) {
+	case DVB_DEVICE_CA:
+	case DVB_DEVICE_DEMUX:
+	case DVB_DEVICE_FRONTEND:
+		npads = 2;
+		break;
+	case DVB_DEVICE_NET:
+		npads = 0;
+		break;
+	default:
+		npads = 1;
+	}
+
+	if (npads) {
+		dvbdev->pads = kcalloc(npads, sizeof(*dvbdev->pads),
+				       GFP_KERNEL);
+		if (!dvbdev->pads) {
+			kfree(dvbdev->entity);
+			return;
+		}
+	}
+
+	switch (type) {
+	case DVB_DEVICE_FRONTEND:
+		dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_FE;
+		dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
+		dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
+		break;
+	case DVB_DEVICE_DEMUX:
+		dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_DEMUX;
+		dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
+		dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
+		break;
+	case DVB_DEVICE_DVR:
+		dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_DVR;
+		dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
+		break;
+	case DVB_DEVICE_CA:
+		dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_CA;
+		dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
+		dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
+		break;
+	case DVB_DEVICE_NET:
+		dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_NET;
+		break;
+	default:
+		kfree(dvbdev->entity);
+		dvbdev->entity = NULL;
+		return;
+	}
+
+	if (npads)
+		ret = media_entity_init(dvbdev->entity, npads, dvbdev->pads, 0);
+	if (!ret)
+		ret = media_device_register_entity(dvbdev->adapter->mdev,
+						   dvbdev->entity);
+	if (ret < 0) {
+		printk(KERN_ERR
+			"%s: media_device_register_entity failed for %s\n",
+			__func__, dvbdev->entity->name);
+		kfree(dvbdev->pads);
+		kfree(dvbdev->entity);
+		dvbdev->entity = NULL;
+		return;
+	}
+
+	printk(KERN_DEBUG "%s: media device '%s' registered.\n",
+		__func__, dvbdev->entity->name);
+#endif
+}
 
 int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
 			const struct dvb_device *template, void *priv, int type)
@@ -258,10 +345,11 @@
 		       __func__, adap->num, dnames[type], id, PTR_ERR(clsdev));
 		return PTR_ERR(clsdev);
 	}
-
 	dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
 		adap->num, dnames[type], id, minor, minor);
 
+	dvb_register_media_device(dvbdev, type, minor);
+
 	return 0;
 }
 EXPORT_SYMBOL(dvb_register_device);
@@ -278,12 +366,66 @@
 
 	device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
 
+#if defined(CPTCFG_MEDIA_CONTROLLER_DVB)
+	if (dvbdev->entity) {
+		media_device_unregister_entity(dvbdev->entity);
+		kfree(dvbdev->entity);
+		kfree(dvbdev->pads);
+	}
+#endif
+
 	list_del (&dvbdev->list_head);
 	kfree (dvbdev->fops);
 	kfree (dvbdev);
 }
 EXPORT_SYMBOL(dvb_unregister_device);
 
+
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+void dvb_create_media_graph(struct dvb_adapter *adap)
+{
+	struct media_device *mdev = adap->mdev;
+	struct media_entity *entity, *tuner = NULL, *fe = NULL;
+	struct media_entity *demux = NULL, *dvr = NULL, *ca = NULL;
+
+	if (!mdev)
+		return;
+
+	media_device_for_each_entity(entity, mdev) {
+		switch (entity->type) {
+		case MEDIA_ENT_T_V4L2_SUBDEV_TUNER:
+			tuner = entity;
+			break;
+		case MEDIA_ENT_T_DEVNODE_DVB_FE:
+			fe = entity;
+			break;
+		case MEDIA_ENT_T_DEVNODE_DVB_DEMUX:
+			demux = entity;
+			break;
+		case MEDIA_ENT_T_DEVNODE_DVB_DVR:
+			dvr = entity;
+			break;
+		case MEDIA_ENT_T_DEVNODE_DVB_CA:
+			ca = entity;
+			break;
+		}
+	}
+
+	if (tuner && fe)
+		media_entity_create_link(tuner, 0, fe, 0, 0);
+
+	if (fe && demux)
+		media_entity_create_link(fe, 1, demux, 0, MEDIA_LNK_FL_ENABLED);
+
+	if (demux && dvr)
+		media_entity_create_link(demux, 1, dvr, 0, MEDIA_LNK_FL_ENABLED);
+
+	if (demux && ca)
+		media_entity_create_link(demux, 1, ca, 0, MEDIA_LNK_FL_ENABLED);
+}
+EXPORT_SYMBOL_GPL(dvb_create_media_graph);
+#endif
+
 static int dvbdev_check_free_adapter_num(int num)
 {
 	struct list_head *entry;
diff --git a/drivers/media/dvb-core/dvbdev.h b/drivers/media/dvb-core/dvbdev.h
index b7f1556..eb38a50 100644
--- a/drivers/media/dvb-core/dvbdev.h
+++ b/drivers/media/dvb-core/dvbdev.h
@@ -27,6 +27,7 @@
 #include <linux/poll.h>
 #include <linux/fs.h>
 #include <linux/list.h>
+#include <media/media-device.h>
 
 #define DVB_MAJOR 212
 
@@ -71,6 +72,10 @@
 	int mfe_shared;			/* indicates mutually exclusive frontends */
 	struct dvb_device *mfe_dvbdev;	/* frontend device in use */
 	struct mutex mfe_lock;		/* access lock for thread creation */
+
+#if defined(CPTCFG_MEDIA_CONTROLLER_DVB)
+	struct media_device *mdev;
+#endif
 };
 
 
@@ -92,6 +97,15 @@
 	/* don't really need those !? -- FIXME: use video_usercopy  */
 	int (*kernel_ioctl)(struct file *file, unsigned int cmd, void *arg);
 
+	/* Needed for media controller register/unregister */
+#if defined(CPTCFG_MEDIA_CONTROLLER_DVB)
+	const char *name;
+
+	/* Allocated and filled inside dvbdev.c */
+	struct media_entity *entity;
+	struct media_pad *pads;
+#endif
+
 	void *priv;
 };
 
@@ -109,6 +123,19 @@
 
 extern void dvb_unregister_device (struct dvb_device *dvbdev);
 
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+void dvb_create_media_graph(struct dvb_adapter *adap);
+static inline void dvb_register_media_controller(struct dvb_adapter *adap,
+						 struct media_device *mdev)
+{
+	adap->mdev = mdev;
+}
+
+#else
+static inline void dvb_create_media_graph(struct dvb_adapter *adap) {}
+#define dvb_register_media_controller(a, b) {}
+#endif
+
 extern int dvb_generic_open (struct inode *inode, struct file *file);
 extern int dvb_generic_release (struct inode *inode, struct file *file);
 extern long dvb_generic_ioctl (struct file *file,
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index bafccf7..9e65b93 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -498,17 +498,21 @@
 	  Say Y when you want to support this frontend.
 
 config DVB_RTL2830
+	depends on !KERNEL_3_8
 	tristate "Realtek RTL2830 DVB-T"
 	depends on m
-	depends on DVB_CORE && I2C
+	depends on DVB_CORE && I2C && I2C_MUX
+	depends on REGMAP
 	default m if !MEDIA_SUBDRV_AUTOSELECT
 	help
 	  Say Y when you want to support this frontend.
 
 config DVB_RTL2832
+	depends on !KERNEL_3_8
 	tristate "Realtek RTL2832 DVB-T"
 	depends on m
 	depends on DVB_CORE && I2C && I2C_MUX
+	depends on REGMAP
 	default m if !MEDIA_SUBDRV_AUTOSELECT
 	help
 	  Say Y when you want to support this frontend.
@@ -646,6 +650,15 @@
 	  An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
 	  to support this frontend.
 
+config DVB_LGDT3306A
+	tristate "LG Electronics LGDT3306A based"
+	depends on m
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  An ATSC 8VSB and QAM-B 64/256 demodulator module. Say Y when you want
+	  to support this frontend.
+
 config DVB_LG2160
 	tristate "LG Electronics LG216x based"
 	depends on m
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index 1b93c47..2523232 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -54,6 +54,7 @@
 obj-$(CPTCFG_DVB_S5H1420) += s5h1420.o
 obj-$(CPTCFG_DVB_LGDT330X) += lgdt330x.o
 obj-$(CPTCFG_DVB_LGDT3305) += lgdt3305.o
+obj-$(CPTCFG_DVB_LGDT3306A) += lgdt3306a.o
 obj-$(CPTCFG_DVB_LG2160) += lg2160.o
 obj-$(CPTCFG_DVB_CX24123) += cx24123.o
 obj-$(CPTCFG_DVB_LNBP21) += lnbp21.o
diff --git a/drivers/media/dvb-frontends/a8293.h b/drivers/media/dvb-frontends/a8293.h
index 84eff0c..fdde075 100644
--- a/drivers/media/dvb-frontends/a8293.h
+++ b/drivers/media/dvb-frontends/a8293.h
@@ -27,7 +27,7 @@
 	u8 i2c_addr;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_A8293)
+#if IS_REACHABLE(CPTCFG_DVB_A8293)
 extern struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
 	struct i2c_adapter *i2c, const struct a8293_config *cfg);
 #else
diff --git a/drivers/media/dvb-frontends/af9013.h b/drivers/media/dvb-frontends/af9013.h
index e1e9bd5..a13fbc1 100644
--- a/drivers/media/dvb-frontends/af9013.h
+++ b/drivers/media/dvb-frontends/af9013.h
@@ -103,7 +103,7 @@
 	u8 gpio[4];
 };
 
-#if IS_ENABLED(CPTCFG_DVB_AF9013)
+#if IS_REACHABLE(CPTCFG_DVB_AF9013)
 extern struct dvb_frontend *af9013_attach(const struct af9013_config *config,
 	struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/atbm8830.h b/drivers/media/dvb-frontends/atbm8830.h
index 57b7fb5..c563dab 100644
--- a/drivers/media/dvb-frontends/atbm8830.h
+++ b/drivers/media/dvb-frontends/atbm8830.h
@@ -61,7 +61,7 @@
 	u8 agc_hold_loop;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_ATBM8830)
+#if IS_REACHABLE(CPTCFG_DVB_ATBM8830)
 extern struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
 		struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/au8522.h b/drivers/media/dvb-frontends/au8522.h
index 3567f72..77fb851 100644
--- a/drivers/media/dvb-frontends/au8522.h
+++ b/drivers/media/dvb-frontends/au8522.h
@@ -61,7 +61,7 @@
 	enum au8522_if_freq qam_if;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_AU8522_DTV)
+#if IS_REACHABLE(CPTCFG_DVB_AU8522_DTV)
 extern struct dvb_frontend *au8522_attach(const struct au8522_config *config,
 					  struct i2c_adapter *i2c);
 #else
@@ -91,8 +91,3 @@
 };
 
 #endif /* __AU8522_H__ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/dvb-frontends/bcm3510.h b/drivers/media/dvb-frontends/bcm3510.h
index 9396e2f..16f58c5 100644
--- a/drivers/media/dvb-frontends/bcm3510.h
+++ b/drivers/media/dvb-frontends/bcm3510.h
@@ -34,7 +34,7 @@
 	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_BCM3510)
+#if IS_REACHABLE(CPTCFG_DVB_BCM3510)
 extern struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config,
 					   struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/cx22700.h b/drivers/media/dvb-frontends/cx22700.h
index 264c84e..2199465 100644
--- a/drivers/media/dvb-frontends/cx22700.h
+++ b/drivers/media/dvb-frontends/cx22700.h
@@ -31,7 +31,7 @@
 	u8 demod_address;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_CX22700)
+#if IS_REACHABLE(CPTCFG_DVB_CX22700)
 extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
 					   struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/cx22702.h b/drivers/media/dvb-frontends/cx22702.h
index 6957594..6ddcb48 100644
--- a/drivers/media/dvb-frontends/cx22702.h
+++ b/drivers/media/dvb-frontends/cx22702.h
@@ -41,7 +41,7 @@
 	u8 output_mode;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_CX22702)
+#if IS_REACHABLE(CPTCFG_DVB_CX22702)
 extern struct dvb_frontend *cx22702_attach(
 	const struct cx22702_config *config,
 	struct i2c_adapter *i2c);
diff --git a/drivers/media/dvb-frontends/cx24110.h b/drivers/media/dvb-frontends/cx24110.h
index 1aba7ed..931194a 100644
--- a/drivers/media/dvb-frontends/cx24110.h
+++ b/drivers/media/dvb-frontends/cx24110.h
@@ -46,7 +46,7 @@
 	return 0;
 }
 
-#if IS_ENABLED(CPTCFG_DVB_CX24110)
+#if IS_REACHABLE(CPTCFG_DVB_CX24110)
 extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
 					   struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/cx24113.h b/drivers/media/dvb-frontends/cx24113.h
index 183449f..5faaa83 100644
--- a/drivers/media/dvb-frontends/cx24113.h
+++ b/drivers/media/dvb-frontends/cx24113.h
@@ -32,7 +32,7 @@
 	u32 xtal_khz;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TUNER_CX24113)
+#if IS_REACHABLE(CPTCFG_DVB_TUNER_CX24113)
 extern struct dvb_frontend *cx24113_attach(struct dvb_frontend *,
 	const struct cx24113_config *config, struct i2c_adapter *i2c);
 
diff --git a/drivers/media/dvb-frontends/cx24116.h b/drivers/media/dvb-frontends/cx24116.h
index 40891ed..d5ea795 100644
--- a/drivers/media/dvb-frontends/cx24116.h
+++ b/drivers/media/dvb-frontends/cx24116.h
@@ -41,7 +41,7 @@
 	u16 i2c_wr_max;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_CX24116)
+#if IS_REACHABLE(CPTCFG_DVB_CX24116)
 extern struct dvb_frontend *cx24116_attach(
 	const struct cx24116_config *config,
 	struct i2c_adapter *i2c);
diff --git a/drivers/media/dvb-frontends/cx24117.h b/drivers/media/dvb-frontends/cx24117.h
index 32e51c5..90c47fb 100644
--- a/drivers/media/dvb-frontends/cx24117.h
+++ b/drivers/media/dvb-frontends/cx24117.h
@@ -30,7 +30,7 @@
 	u8 demod_address;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_CX24117)
+#if IS_REACHABLE(CPTCFG_DVB_CX24117)
 extern struct dvb_frontend *cx24117_attach(
 	const struct cx24117_config *config,
 	struct i2c_adapter *i2c);
diff --git a/drivers/media/dvb-frontends/cx24123.h b/drivers/media/dvb-frontends/cx24123.h
index 21a2f5e..7971a19 100644
--- a/drivers/media/dvb-frontends/cx24123.h
+++ b/drivers/media/dvb-frontends/cx24123.h
@@ -39,7 +39,7 @@
 	void (*agc_callback) (struct dvb_frontend *);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_CX24123)
+#if IS_REACHABLE(CPTCFG_DVB_CX24123)
 extern struct dvb_frontend *cx24123_attach(const struct cx24123_config *config,
 					   struct i2c_adapter *i2c);
 extern struct i2c_adapter *cx24123_get_tuner_i2c_adapter(struct dvb_frontend *);
diff --git a/drivers/media/dvb-frontends/cxd2820r.h b/drivers/media/dvb-frontends/cxd2820r.h
index 1f5124c..d30a716 100644
--- a/drivers/media/dvb-frontends/cxd2820r.h
+++ b/drivers/media/dvb-frontends/cxd2820r.h
@@ -72,7 +72,7 @@
 };
 
 
-#if IS_ENABLED(CPTCFG_DVB_CXD2820R)
+#if IS_REACHABLE(CPTCFG_DVB_CXD2820R)
 extern struct dvb_frontend *cxd2820r_attach(
 	const struct cxd2820r_config *config,
 	struct i2c_adapter *i2c,
diff --git a/drivers/media/dvb-frontends/cxd2820r_c.c b/drivers/media/dvb-frontends/cxd2820r_c.c
index 149fdca..72b0e2d 100644
--- a/drivers/media/dvb-frontends/cxd2820r_c.c
+++ b/drivers/media/dvb-frontends/cxd2820r_c.c
@@ -79,7 +79,7 @@
 
 	num = if_freq / 1000; /* Hz => kHz */
 	num *= 0x4000;
-	if_ctl = 0x4000 - cxd2820r_div_u64_round_closest(num, 41000);
+	if_ctl = 0x4000 - DIV_ROUND_CLOSEST_ULL(num, 41000);
 	buf[0] = (if_ctl >> 8) & 0x3f;
 	buf[1] = (if_ctl >> 0) & 0xff;
 
diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c
index 422e84b..490e090 100644
--- a/drivers/media/dvb-frontends/cxd2820r_core.c
+++ b/drivers/media/dvb-frontends/cxd2820r_core.c
@@ -244,12 +244,6 @@
 	return ret;
 }
 
-/* 64 bit div with round closest, like DIV_ROUND_CLOSEST but 64 bit */
-u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor)
-{
-	return div_u64(dividend + (divisor / 2), divisor);
-}
-
 static int cxd2820r_set_frontend(struct dvb_frontend *fe)
 {
 	struct cxd2820r_priv *priv = fe->demodulator_priv;
diff --git a/drivers/media/dvb-frontends/cxd2820r_priv.h b/drivers/media/dvb-frontends/cxd2820r_priv.h
index 7ff5f60..4b42895 100644
--- a/drivers/media/dvb-frontends/cxd2820r_priv.h
+++ b/drivers/media/dvb-frontends/cxd2820r_priv.h
@@ -64,8 +64,6 @@
 int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
 	int len);
 
-u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor);
-
 int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
 	int len);
 
diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c
index 51401d0..008cb2a 100644
--- a/drivers/media/dvb-frontends/cxd2820r_t.c
+++ b/drivers/media/dvb-frontends/cxd2820r_t.c
@@ -103,7 +103,7 @@
 
 	num = if_freq / 1000; /* Hz => kHz */
 	num *= 0x1000000;
-	if_ctl = cxd2820r_div_u64_round_closest(num, 41000);
+	if_ctl = DIV_ROUND_CLOSEST_ULL(num, 41000);
 	buf[0] = ((if_ctl >> 16) & 0xff);
 	buf[1] = ((if_ctl >>  8) & 0xff);
 	buf[2] = ((if_ctl >>  0) & 0xff);
diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c
index 9c0c4f4..35fe364 100644
--- a/drivers/media/dvb-frontends/cxd2820r_t2.c
+++ b/drivers/media/dvb-frontends/cxd2820r_t2.c
@@ -120,7 +120,7 @@
 
 	num = if_freq / 1000; /* Hz => kHz */
 	num *= 0x1000000;
-	if_ctl = cxd2820r_div_u64_round_closest(num, 41000);
+	if_ctl = DIV_ROUND_CLOSEST_ULL(num, 41000);
 	buf[0] = ((if_ctl >> 16) & 0xff);
 	buf[1] = ((if_ctl >>  8) & 0xff);
 	buf[2] = ((if_ctl >>  0) & 0xff);
diff --git a/drivers/media/dvb-frontends/dib0070.h b/drivers/media/dvb-frontends/dib0070.h
index 8cdd1c0..fcd5c87 100644
--- a/drivers/media/dvb-frontends/dib0070.h
+++ b/drivers/media/dvb-frontends/dib0070.h
@@ -48,7 +48,7 @@
 	u8 vga_filter;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TUNER_DIB0070)
+#if IS_REACHABLE(CPTCFG_DVB_TUNER_DIB0070)
 extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg);
 extern u16 dib0070_wbd_offset(struct dvb_frontend *);
 extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open);
diff --git a/drivers/media/dvb-frontends/dib0090.h b/drivers/media/dvb-frontends/dib0090.h
index a805cf7..75e0d0d 100644
--- a/drivers/media/dvb-frontends/dib0090.h
+++ b/drivers/media/dvb-frontends/dib0090.h
@@ -75,7 +75,7 @@
 	u8 force_crystal_mode;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TUNER_DIB0090)
+#if IS_REACHABLE(CPTCFG_DVB_TUNER_DIB0090)
 extern struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config);
 extern struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config);
 extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast);
diff --git a/drivers/media/dvb-frontends/dib3000.h b/drivers/media/dvb-frontends/dib3000.h
index 649d00a..7a3033b 100644
--- a/drivers/media/dvb-frontends/dib3000.h
+++ b/drivers/media/dvb-frontends/dib3000.h
@@ -41,7 +41,7 @@
 	int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_DIB3000MB)
+#if IS_REACHABLE(CPTCFG_DVB_DIB3000MB)
 extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
 					     struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops);
 #else
diff --git a/drivers/media/dvb-frontends/dib3000mc.h b/drivers/media/dvb-frontends/dib3000mc.h
index 4d9309c..9240ad0 100644
--- a/drivers/media/dvb-frontends/dib3000mc.h
+++ b/drivers/media/dvb-frontends/dib3000mc.h
@@ -41,7 +41,7 @@
 #define DEFAULT_DIB3000MC_I2C_ADDRESS 16
 #define DEFAULT_DIB3000P_I2C_ADDRESS  24
 
-#if IS_ENABLED(CPTCFG_DVB_DIB3000MC)
+#if IS_REACHABLE(CPTCFG_DVB_DIB3000MC)
 extern struct dvb_frontend *dib3000mc_attach(struct i2c_adapter *i2c_adap,
 					     u8 i2c_addr,
 					     struct dib3000mc_config *cfg);
diff --git a/drivers/media/dvb-frontends/dib7000m.h b/drivers/media/dvb-frontends/dib7000m.h
index 4b1e08a..0282094 100644
--- a/drivers/media/dvb-frontends/dib7000m.h
+++ b/drivers/media/dvb-frontends/dib7000m.h
@@ -40,7 +40,7 @@
 
 #define DEFAULT_DIB7000M_I2C_ADDRESS 18
 
-#if IS_ENABLED(CPTCFG_DVB_DIB7000M)
+#if IS_REACHABLE(CPTCFG_DVB_DIB7000M)
 extern struct dvb_frontend *dib7000m_attach(struct i2c_adapter *i2c_adap,
 					    u8 i2c_addr,
 					    struct dib7000m_config *cfg);
diff --git a/drivers/media/dvb-frontends/dib7000p.h b/drivers/media/dvb-frontends/dib7000p.h
index 26f13ad..833dd7c 100644
--- a/drivers/media/dvb-frontends/dib7000p.h
+++ b/drivers/media/dvb-frontends/dib7000p.h
@@ -66,7 +66,7 @@
 	struct dvb_frontend *(*init)(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_DIB7000P)
+#if IS_REACHABLE(CPTCFG_DVB_DIB7000P)
 void *dib7000p_attach(struct dib7000p_ops *ops);
 #else
 static inline void *dib7000p_attach(struct dib7000p_ops *ops)
diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c
index 61e31f2..8c6663b 100644
--- a/drivers/media/dvb-frontends/dib8000.c
+++ b/drivers/media/dvb-frontends/dib8000.c
@@ -1263,7 +1263,8 @@
 	struct dib8000_state *state = fe->demodulator_priv;
 	enum frontend_tune_state *tune_state = &state->tune_state;
 	int ret = 0;
-	u16 reg, upd_demod_gain_period = 0x8000;
+	u16 reg;
+	u32 upd_demod_gain_period = 0x8000;
 
 	switch (*tune_state) {
 	case CT_AGC_START:
diff --git a/drivers/media/dvb-frontends/dib8000.h b/drivers/media/dvb-frontends/dib8000.h
index e0df6a6..ef1419f 100644
--- a/drivers/media/dvb-frontends/dib8000.h
+++ b/drivers/media/dvb-frontends/dib8000.h
@@ -63,7 +63,7 @@
 	struct dvb_frontend *(*init)(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_DIB8000)
+#if IS_REACHABLE(CPTCFG_DVB_DIB8000)
 void *dib8000_attach(struct dib8000_ops *ops);
 #else
 static inline int dib8000_attach(struct dib8000_ops *ops)
diff --git a/drivers/media/dvb-frontends/dib9000.h b/drivers/media/dvb-frontends/dib9000.h
index 8529c52..d2e1683 100644
--- a/drivers/media/dvb-frontends/dib9000.h
+++ b/drivers/media/dvb-frontends/dib9000.h
@@ -27,7 +27,7 @@
 
 #define DEFAULT_DIB9000_I2C_ADDRESS 18
 
-#if IS_ENABLED(CPTCFG_DVB_DIB9000)
+#if IS_REACHABLE(CPTCFG_DVB_DIB9000)
 extern struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg);
 extern int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr);
 extern struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe);
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h b/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h
index 69f6499..2fdd7c0 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h
+++ b/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h
@@ -34,7 +34,7 @@
 	const struct firmware *fw;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_DRX39XYJ)
+#if IS_REACHABLE(CPTCFG_DVB_DRX39XYJ)
 struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c);
 #else
 static inline struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c) {
diff --git a/drivers/media/dvb-frontends/drxd.h b/drivers/media/dvb-frontends/drxd.h
index a880ae7..81069da 100644
--- a/drivers/media/dvb-frontends/drxd.h
+++ b/drivers/media/dvb-frontends/drxd.h
@@ -52,7 +52,7 @@
 	 s16(*osc_deviation) (void *priv, s16 dev, int flag);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_DRXD)
+#if IS_REACHABLE(CPTCFG_DVB_DRXD)
 extern
 struct dvb_frontend *drxd_attach(const struct drxd_config *config,
 				 void *priv, struct i2c_adapter *i2c,
diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h
index f98ef3a..d0be551 100644
--- a/drivers/media/dvb-frontends/drxk.h
+++ b/drivers/media/dvb-frontends/drxk.h
@@ -51,7 +51,7 @@
 	int		 qam_demod_parameter_count;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_DRXK)
+#if IS_REACHABLE(CPTCFG_DVB_DRXK)
 extern struct dvb_frontend *drxk_attach(const struct drxk_config *config,
 					struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/ds3000.h b/drivers/media/dvb-frontends/ds3000.h
index ff091e5..11b00e8 100644
--- a/drivers/media/dvb-frontends/ds3000.h
+++ b/drivers/media/dvb-frontends/ds3000.h
@@ -35,7 +35,7 @@
 	void (*set_lock_led)(struct dvb_frontend *fe, int offon);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_DS3000)
+#if IS_REACHABLE(CPTCFG_DVB_DS3000)
 extern struct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
 					struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h
index 27d5abf..0800dee 100644
--- a/drivers/media/dvb-frontends/dvb-pll.h
+++ b/drivers/media/dvb-frontends/dvb-pll.h
@@ -38,7 +38,7 @@
  * @param pll_desc_id dvb_pll_desc to use.
  * @return Frontend pointer on success, NULL on failure
  */
-#if IS_ENABLED(CPTCFG_DVB_PLL)
+#if IS_REACHABLE(CPTCFG_DVB_PLL)
 extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
 					   int pll_addr,
 					   struct i2c_adapter *i2c,
diff --git a/drivers/media/dvb-frontends/dvb_dummy_fe.h b/drivers/media/dvb-frontends/dvb_dummy_fe.h
index 7b222d2..b92263a 100644
--- a/drivers/media/dvb-frontends/dvb_dummy_fe.h
+++ b/drivers/media/dvb-frontends/dvb_dummy_fe.h
@@ -26,7 +26,7 @@
 #include <linux/dvb/frontend.h>
 #include "dvb_frontend.h"
 
-#if IS_ENABLED(CPTCFG_DVB_DUMMY_FE)
+#if IS_REACHABLE(CPTCFG_DVB_DUMMY_FE)
 extern struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void);
 extern struct dvb_frontend* dvb_dummy_fe_qpsk_attach(void);
 extern struct dvb_frontend* dvb_dummy_fe_qam_attach(void);
diff --git a/drivers/media/dvb-frontends/ec100.h b/drivers/media/dvb-frontends/ec100.h
index a6b28d2..2e44ae2 100644
--- a/drivers/media/dvb-frontends/ec100.h
+++ b/drivers/media/dvb-frontends/ec100.h
@@ -31,7 +31,7 @@
 };
 
 
-#if IS_ENABLED(CPTCFG_DVB_EC100)
+#if IS_REACHABLE(CPTCFG_DVB_EC100)
 extern struct dvb_frontend *ec100_attach(const struct ec100_config *config,
 	struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/hd29l2.c b/drivers/media/dvb-frontends/hd29l2.c
index d7b9d54..67c8e6d 100644
--- a/drivers/media/dvb-frontends/hd29l2.c
+++ b/drivers/media/dvb-frontends/hd29l2.c
@@ -22,20 +22,24 @@
 
 #include "hd29l2_priv.h"
 
+#define HD29L2_MAX_LEN (3)
+
 /* write multiple registers */
 static int hd29l2_wr_regs(struct hd29l2_priv *priv, u8 reg, u8 *val, int len)
 {
 	int ret;
-	u8 buf[2 + len];
+	u8 buf[2 + HD29L2_MAX_LEN];
 	struct i2c_msg msg[1] = {
 		{
 			.addr = priv->cfg.i2c_addr,
 			.flags = 0,
-			.len = sizeof(buf),
+			.len = 2 + len,
 			.buf = buf,
 		}
 	};
 
+	if (len > HD29L2_MAX_LEN)
+		return -EINVAL;
 	buf[0] = 0x00;
 	buf[1] = reg;
 	memcpy(&buf[2], val, len);
@@ -118,7 +122,7 @@
 }
 
 /* read single register with mask */
-int hd29l2_rd_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 *val, u8 mask)
+static int hd29l2_rd_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 *val, u8 mask)
 {
 	int ret, i;
 	u8 tmp;
diff --git a/drivers/media/dvb-frontends/hd29l2.h b/drivers/media/dvb-frontends/hd29l2.h
index c5b89ef..e683ea1 100644
--- a/drivers/media/dvb-frontends/hd29l2.h
+++ b/drivers/media/dvb-frontends/hd29l2.h
@@ -51,7 +51,7 @@
 };
 
 
-#if IS_ENABLED(CPTCFG_DVB_HD29L2)
+#if IS_REACHABLE(CPTCFG_DVB_HD29L2)
 extern struct dvb_frontend *hd29l2_attach(const struct hd29l2_config *config,
 	struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/isl6405.h b/drivers/media/dvb-frontends/isl6405.h
index 1b3aedd..9de6fec 100644
--- a/drivers/media/dvb-frontends/isl6405.h
+++ b/drivers/media/dvb-frontends/isl6405.h
@@ -55,7 +55,7 @@
 #define ISL6405_ENT2	0x20
 #define ISL6405_ISEL2	0x40
 
-#if IS_ENABLED(CPTCFG_DVB_ISL6405)
+#if IS_REACHABLE(CPTCFG_DVB_ISL6405)
 /* override_set and override_clear control which system register bits (above)
  * to always set & clear
  */
diff --git a/drivers/media/dvb-frontends/isl6421.h b/drivers/media/dvb-frontends/isl6421.h
index b717975..1153d99 100644
--- a/drivers/media/dvb-frontends/isl6421.h
+++ b/drivers/media/dvb-frontends/isl6421.h
@@ -39,7 +39,7 @@
 #define ISL6421_ISEL1	0x20
 #define ISL6421_DCL	0x40
 
-#if IS_ENABLED(CPTCFG_DVB_ISL6421)
+#if IS_REACHABLE(CPTCFG_DVB_ISL6421)
 /* override_set and override_clear control which system register bits (above) to always set & clear */
 extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
 			  u8 override_set, u8 override_clear, bool override_tone);
diff --git a/drivers/media/dvb-frontends/isl6423.h b/drivers/media/dvb-frontends/isl6423.h
index 1166922..04bc99e 100644
--- a/drivers/media/dvb-frontends/isl6423.h
+++ b/drivers/media/dvb-frontends/isl6423.h
@@ -42,7 +42,7 @@
 	u8 mod_extern;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_ISL6423)
+#if IS_REACHABLE(CPTCFG_DVB_ISL6423)
 
 
 extern struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/dvb-frontends/itd1000.h b/drivers/media/dvb-frontends/itd1000.h
index 7d8b394..7f9b1ff 100644
--- a/drivers/media/dvb-frontends/itd1000.h
+++ b/drivers/media/dvb-frontends/itd1000.h
@@ -29,7 +29,7 @@
 	u8 i2c_address;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TUNER_ITD1000)
+#if IS_REACHABLE(CPTCFG_DVB_TUNER_ITD1000)
 extern struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg);
 #else
 static inline struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg)
diff --git a/drivers/media/dvb-frontends/ix2505v.h b/drivers/media/dvb-frontends/ix2505v.h
index b058503..5efa841 100644
--- a/drivers/media/dvb-frontends/ix2505v.h
+++ b/drivers/media/dvb-frontends/ix2505v.h
@@ -49,7 +49,7 @@
 
 };
 
-#if IS_ENABLED(CPTCFG_DVB_IX2505V)
+#if IS_REACHABLE(CPTCFG_DVB_IX2505V)
 extern struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe,
 	const struct ix2505v_config *config, struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/l64781.h b/drivers/media/dvb-frontends/l64781.h
index 5f7a6b7..e224409 100644
--- a/drivers/media/dvb-frontends/l64781.h
+++ b/drivers/media/dvb-frontends/l64781.h
@@ -31,7 +31,7 @@
 	u8 demod_address;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_L64781)
+#if IS_REACHABLE(CPTCFG_DVB_L64781)
 extern struct dvb_frontend* l64781_attach(const struct l64781_config* config,
 					  struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c
index 5fd14f8..99efeba 100644
--- a/drivers/media/dvb-frontends/lg2160.c
+++ b/drivers/media/dvb-frontends/lg2160.c
@@ -1456,9 +1456,3 @@
 MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("0.3");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb-frontends/lg2160.h b/drivers/media/dvb-frontends/lg2160.h
index 1a25535..85161ef 100644
--- a/drivers/media/dvb-frontends/lg2160.h
+++ b/drivers/media/dvb-frontends/lg2160.h
@@ -67,7 +67,7 @@
 	enum lg_chip_type lg_chip;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_LG2160)
+#if IS_REACHABLE(CPTCFG_DVB_LG2160)
 extern
 struct dvb_frontend *lg2160_attach(const struct lg2160_config *config,
 				     struct i2c_adapter *i2c_adap);
diff --git a/drivers/media/dvb-frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c
index 92c891a..d08570a 100644
--- a/drivers/media/dvb-frontends/lgdt3305.c
+++ b/drivers/media/dvb-frontends/lgdt3305.c
@@ -236,12 +236,13 @@
 	return lgdt3305_set_reg_bit(state, LGDT3305_TP_CTRL_1, 5, mode);
 }
 
-static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state,
-				       enum lgdt3305_tp_clock_edge edge,
-				       enum lgdt3305_tp_valid_polarity valid)
+static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state)
 {
 	u8 val;
 	int ret;
+	enum lgdt3305_tp_clock_edge edge = state->cfg->tpclk_edge;
+	enum lgdt3305_tp_clock_mode mode = state->cfg->tpclk_mode;
+	enum lgdt3305_tp_valid_polarity valid = state->cfg->tpvalid_polarity;
 
 	lg_dbg("edge = %d, valid = %d\n", edge, valid);
 
@@ -253,6 +254,8 @@
 
 	if (edge)
 		val |= 0x08;
+	if (mode)
+		val |= 0x40;
 	if (valid)
 		val |= 0x01;
 
@@ -740,9 +743,7 @@
 		goto fail;
 
 	/* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */
-	ret = lgdt3305_mpeg_mode_polarity(state,
-					  state->cfg->tpclk_edge,
-					  state->cfg->tpvalid_polarity);
+	ret = lgdt3305_mpeg_mode_polarity(state);
 fail:
 	return ret;
 }
@@ -806,9 +807,7 @@
 		goto fail;
 
 	/* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */
-	ret = lgdt3305_mpeg_mode_polarity(state,
-					  state->cfg->tpclk_edge,
-					  state->cfg->tpvalid_polarity);
+	ret = lgdt3305_mpeg_mode_polarity(state);
 fail:
 	return ret;
 }
@@ -1215,9 +1214,3 @@
 MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("0.2");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb-frontends/lgdt3305.h b/drivers/media/dvb-frontends/lgdt3305.h
index 6f6b6b8..91b503c 100644
--- a/drivers/media/dvb-frontends/lgdt3305.h
+++ b/drivers/media/dvb-frontends/lgdt3305.h
@@ -37,6 +37,11 @@
 	LGDT3305_TPCLK_FALLING_EDGE = 1,
 };
 
+enum lgdt3305_tp_clock_mode {
+	LGDT3305_TPCLK_GATED = 0,
+	LGDT3305_TPCLK_FIXED = 1,
+};
+
 enum lgdt3305_tp_valid_polarity {
 	LGDT3305_TP_VALID_LOW = 0,
 	LGDT3305_TP_VALID_HIGH = 1,
@@ -70,11 +75,12 @@
 
 	enum lgdt3305_mpeg_mode mpeg_mode;
 	enum lgdt3305_tp_clock_edge tpclk_edge;
+	enum lgdt3305_tp_clock_mode tpclk_mode;
 	enum lgdt3305_tp_valid_polarity tpvalid_polarity;
 	enum lgdt_demod_chip_type demod_chip;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_LGDT3305)
+#if IS_REACHABLE(CPTCFG_DVB_LGDT3305)
 extern
 struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
 				     struct i2c_adapter *i2c_adap);
diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c
new file mode 100644
index 0000000..d9a2b0e
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgdt3306a.c
@@ -0,0 +1,2144 @@
+/*
+ *    Support for LGDT3306A - 8VSB/QAM-B
+ *
+ *    Copyright (C) 2013 Fred Richter <frichter@hauppauge.com>
+ *    - driver structure based on lgdt3305.[ch] by Michael Krufky
+ *    - code based on LG3306_V0.35 API by LG Electronics Inc.
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <asm/div64.h>
+#include <linux/dvb/frontend.h>
+#include "dvb_math.h"
+#include "lgdt3306a.h"
+
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))");
+
+#define DBG_INFO 1
+#define DBG_REG  2
+#define DBG_DUMP 4 /* FGR - comment out to remove dump code */
+
+#define lg_debug(fmt, arg...) \
+	printk(KERN_DEBUG pr_fmt(fmt), ## arg)
+
+#define dbg_info(fmt, arg...)					\
+	do {							\
+		if (debug & DBG_INFO)				\
+			lg_debug(fmt, ## arg);			\
+	} while (0)
+
+#define dbg_reg(fmt, arg...)					\
+	do {							\
+		if (debug & DBG_REG)				\
+			lg_debug(fmt, ## arg);			\
+	} while (0)
+
+#define lg_chkerr(ret)							\
+({									\
+	int __ret;							\
+	__ret = (ret < 0);						\
+	if (__ret)							\
+		pr_err("error %d on line %d\n",	ret, __LINE__);		\
+	__ret;								\
+})
+
+struct lgdt3306a_state {
+	struct i2c_adapter *i2c_adap;
+	const struct lgdt3306a_config *cfg;
+
+	struct dvb_frontend frontend;
+
+	fe_modulation_t current_modulation;
+	u32 current_frequency;
+	u32 snr;
+};
+
+/*
+ * LG3306A Register Usage
+ *  (LG does not really name the registers, so this code does not either)
+ *
+ * 0000 -> 00FF Common control and status
+ * 1000 -> 10FF Synchronizer control and status
+ * 1F00 -> 1FFF Smart Antenna control and status
+ * 2100 -> 21FF VSB Equalizer control and status
+ * 2800 -> 28FF QAM Equalizer control and status
+ * 3000 -> 30FF FEC control and status
+ */
+
+enum lgdt3306a_lock_status {
+	LG3306_UNLOCK       = 0x00,
+	LG3306_LOCK         = 0x01,
+	LG3306_UNKNOWN_LOCK = 0xff
+};
+
+enum lgdt3306a_neverlock_status {
+	LG3306_NL_INIT    = 0x00,
+	LG3306_NL_PROCESS = 0x01,
+	LG3306_NL_LOCK    = 0x02,
+	LG3306_NL_FAIL    = 0x03,
+	LG3306_NL_UNKNOWN = 0xff
+};
+
+enum lgdt3306a_modulation {
+	LG3306_VSB          = 0x00,
+	LG3306_QAM64        = 0x01,
+	LG3306_QAM256       = 0x02,
+	LG3306_UNKNOWN_MODE = 0xff
+};
+
+enum lgdt3306a_lock_check {
+	LG3306_SYNC_LOCK,
+	LG3306_FEC_LOCK,
+	LG3306_TR_LOCK,
+	LG3306_AGC_LOCK,
+};
+
+
+#ifdef DBG_DUMP
+static void lgdt3306a_DumpAllRegs(struct lgdt3306a_state *state);
+static void lgdt3306a_DumpRegs(struct lgdt3306a_state *state);
+#endif
+
+
+static int lgdt3306a_write_reg(struct lgdt3306a_state *state, u16 reg, u8 val)
+{
+	int ret;
+	u8 buf[] = { reg >> 8, reg & 0xff, val };
+	struct i2c_msg msg = {
+		.addr = state->cfg->i2c_addr, .flags = 0,
+		.buf = buf, .len = 3,
+	};
+
+	dbg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val);
+
+	ret = i2c_transfer(state->i2c_adap, &msg, 1);
+
+	if (ret != 1) {
+		pr_err("error (addr %02x %02x <- %02x, err = %i)\n",
+		       msg.buf[0], msg.buf[1], msg.buf[2], ret);
+		if (ret < 0)
+			return ret;
+		else
+			return -EREMOTEIO;
+	}
+	return 0;
+}
+
+static int lgdt3306a_read_reg(struct lgdt3306a_state *state, u16 reg, u8 *val)
+{
+	int ret;
+	u8 reg_buf[] = { reg >> 8, reg & 0xff };
+	struct i2c_msg msg[] = {
+		{ .addr = state->cfg->i2c_addr,
+		  .flags = 0, .buf = reg_buf, .len = 2 },
+		{ .addr = state->cfg->i2c_addr,
+		  .flags = I2C_M_RD, .buf = val, .len = 1 },
+	};
+
+	ret = i2c_transfer(state->i2c_adap, msg, 2);
+
+	if (ret != 2) {
+		pr_err("error (addr %02x reg %04x error (ret == %i)\n",
+		       state->cfg->i2c_addr, reg, ret);
+		if (ret < 0)
+			return ret;
+		else
+			return -EREMOTEIO;
+	}
+	dbg_reg("reg: 0x%04x, val: 0x%02x\n", reg, *val);
+
+	return 0;
+}
+
+#define read_reg(state, reg)						\
+({									\
+	u8 __val;							\
+	int ret = lgdt3306a_read_reg(state, reg, &__val);		\
+	if (lg_chkerr(ret))						\
+		__val = 0;						\
+	__val;								\
+})
+
+static int lgdt3306a_set_reg_bit(struct lgdt3306a_state *state,
+				u16 reg, int bit, int onoff)
+{
+	u8 val;
+	int ret;
+
+	dbg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff);
+
+	ret = lgdt3306a_read_reg(state, reg, &val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	val &= ~(1 << bit);
+	val |= (onoff & 1) << bit;
+
+	ret = lgdt3306a_write_reg(state, reg, val);
+	lg_chkerr(ret);
+fail:
+	return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lgdt3306a_soft_reset(struct lgdt3306a_state *state)
+{
+	int ret;
+
+	dbg_info("\n");
+
+	ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 0);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	msleep(20);
+	ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 1);
+	lg_chkerr(ret);
+
+fail:
+	return ret;
+}
+
+static int lgdt3306a_mpeg_mode(struct lgdt3306a_state *state,
+				     enum lgdt3306a_mpeg_mode mode)
+{
+	u8 val;
+	int ret;
+
+	dbg_info("(%d)\n", mode);
+	/* transport packet format - TPSENB=0x80 */
+	ret = lgdt3306a_set_reg_bit(state, 0x0071, 7,
+				     mode == LGDT3306A_MPEG_PARALLEL ? 1 : 0);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/*
+	 * start of packet signal duration
+	 * TPSSOPBITEN=0x40; 0=byte duration, 1=bit duration
+	 */
+	ret = lgdt3306a_set_reg_bit(state, 0x0071, 6, 0);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	ret = lgdt3306a_read_reg(state, 0x0070, &val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	val |= 0x10; /* TPCLKSUPB=0x10 */
+
+	if (mode == LGDT3306A_MPEG_PARALLEL)
+		val &= ~0x10;
+
+	ret = lgdt3306a_write_reg(state, 0x0070, val);
+	lg_chkerr(ret);
+
+fail:
+	return ret;
+}
+
+static int lgdt3306a_mpeg_mode_polarity(struct lgdt3306a_state *state,
+				       enum lgdt3306a_tp_clock_edge edge,
+				       enum lgdt3306a_tp_valid_polarity valid)
+{
+	u8 val;
+	int ret;
+
+	dbg_info("edge=%d, valid=%d\n", edge, valid);
+
+	ret = lgdt3306a_read_reg(state, 0x0070, &val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	val &= ~0x06; /* TPCLKPOL=0x04, TPVALPOL=0x02 */
+
+	if (edge == LGDT3306A_TPCLK_RISING_EDGE)
+		val |= 0x04;
+	if (valid == LGDT3306A_TP_VALID_HIGH)
+		val |= 0x02;
+
+	ret = lgdt3306a_write_reg(state, 0x0070, val);
+	lg_chkerr(ret);
+
+fail:
+	return ret;
+}
+
+static int lgdt3306a_mpeg_tristate(struct lgdt3306a_state *state,
+				     int mode)
+{
+	u8 val;
+	int ret;
+
+	dbg_info("(%d)\n", mode);
+
+	if (mode) {
+		ret = lgdt3306a_read_reg(state, 0x0070, &val);
+		if (lg_chkerr(ret))
+			goto fail;
+		/*
+		 * Tristate bus; TPOUTEN=0x80, TPCLKOUTEN=0x20,
+		 * TPDATAOUTEN=0x08
+		 */
+		val &= ~0xa8;
+		ret = lgdt3306a_write_reg(state, 0x0070, val);
+		if (lg_chkerr(ret))
+			goto fail;
+
+		/* AGCIFOUTENB=0x40; 1=Disable IFAGC pin */
+		ret = lgdt3306a_set_reg_bit(state, 0x0003, 6, 1);
+		if (lg_chkerr(ret))
+			goto fail;
+
+	} else {
+		/* enable IFAGC pin */
+		ret = lgdt3306a_set_reg_bit(state, 0x0003, 6, 0);
+		if (lg_chkerr(ret))
+			goto fail;
+
+		ret = lgdt3306a_read_reg(state, 0x0070, &val);
+		if (lg_chkerr(ret))
+			goto fail;
+
+		val |= 0xa8; /* enable bus */
+		ret = lgdt3306a_write_reg(state, 0x0070, val);
+		if (lg_chkerr(ret))
+			goto fail;
+	}
+
+fail:
+	return ret;
+}
+
+static int lgdt3306a_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+
+	dbg_info("acquire=%d\n", acquire);
+
+	return lgdt3306a_mpeg_tristate(state, acquire ? 0 : 1);
+
+}
+
+static int lgdt3306a_power(struct lgdt3306a_state *state,
+				     int mode)
+{
+	int ret;
+
+	dbg_info("(%d)\n", mode);
+
+	if (mode == 0) {
+		/* into reset */
+		ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 0);
+		if (lg_chkerr(ret))
+			goto fail;
+
+		/* power down */
+		ret = lgdt3306a_set_reg_bit(state, 0x0000, 0, 0);
+		if (lg_chkerr(ret))
+			goto fail;
+
+	} else {
+		/* out of reset */
+		ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 1);
+		if (lg_chkerr(ret))
+			goto fail;
+
+		/* power up */
+		ret = lgdt3306a_set_reg_bit(state, 0x0000, 0, 1);
+		if (lg_chkerr(ret))
+			goto fail;
+	}
+
+#ifdef DBG_DUMP
+	lgdt3306a_DumpAllRegs(state);
+#endif
+fail:
+	return ret;
+}
+
+
+static int lgdt3306a_set_vsb(struct lgdt3306a_state *state)
+{
+	u8 val;
+	int ret;
+
+	dbg_info("\n");
+
+	/* 0. Spectrum inversion detection manual; spectrum inverted */
+	ret = lgdt3306a_read_reg(state, 0x0002, &val);
+	val &= 0xf7; /* SPECINVAUTO Off */
+	val |= 0x04; /* SPECINV On */
+	ret = lgdt3306a_write_reg(state, 0x0002, val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 1. Selection of standard mode(0x08=QAM, 0x80=VSB) */
+	ret = lgdt3306a_write_reg(state, 0x0008, 0x80);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 2. Bandwidth mode for VSB(6MHz) */
+	ret = lgdt3306a_read_reg(state, 0x0009, &val);
+	val &= 0xe3;
+	val |= 0x0c; /* STDOPDETTMODE[2:0]=3 */
+	ret = lgdt3306a_write_reg(state, 0x0009, val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 3. QAM mode detection mode(None) */
+	ret = lgdt3306a_read_reg(state, 0x0009, &val);
+	val &= 0xfc; /* STDOPDETCMODE[1:0]=0 */
+	ret = lgdt3306a_write_reg(state, 0x0009, val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 4. ADC sampling frequency rate(2x sampling) */
+	ret = lgdt3306a_read_reg(state, 0x000d, &val);
+	val &= 0xbf; /* SAMPLING4XFEN=0 */
+	ret = lgdt3306a_write_reg(state, 0x000d, val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+#if 0
+	/* FGR - disable any AICC filtering, testing only */
+
+	ret = lgdt3306a_write_reg(state, 0x0024, 0x00);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* AICCFIXFREQ0 NT N-1(Video rejection) */
+	ret = lgdt3306a_write_reg(state, 0x002e, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x002f, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x0030, 0x00);
+
+	/* AICCFIXFREQ1 NT N-1(Audio rejection) */
+	ret = lgdt3306a_write_reg(state, 0x002b, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x002c, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x002d, 0x00);
+
+	/* AICCFIXFREQ2 NT Co-Channel(Video rejection) */
+	ret = lgdt3306a_write_reg(state, 0x0028, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x0029, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x002a, 0x00);
+
+	/* AICCFIXFREQ3 NT Co-Channel(Audio rejection) */
+	ret = lgdt3306a_write_reg(state, 0x0025, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x0026, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x0027, 0x00);
+
+#else
+	/* FGR - this works well for HVR-1955,1975 */
+
+	/* 5. AICCOPMODE  NT N-1 Adj. */
+	ret = lgdt3306a_write_reg(state, 0x0024, 0x5A);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* AICCFIXFREQ0 NT N-1(Video rejection) */
+	ret = lgdt3306a_write_reg(state, 0x002e, 0x5A);
+	ret = lgdt3306a_write_reg(state, 0x002f, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x0030, 0x00);
+
+	/* AICCFIXFREQ1 NT N-1(Audio rejection) */
+	ret = lgdt3306a_write_reg(state, 0x002b, 0x36);
+	ret = lgdt3306a_write_reg(state, 0x002c, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x002d, 0x00);
+
+	/* AICCFIXFREQ2 NT Co-Channel(Video rejection) */
+	ret = lgdt3306a_write_reg(state, 0x0028, 0x2A);
+	ret = lgdt3306a_write_reg(state, 0x0029, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x002a, 0x00);
+
+	/* AICCFIXFREQ3 NT Co-Channel(Audio rejection) */
+	ret = lgdt3306a_write_reg(state, 0x0025, 0x06);
+	ret = lgdt3306a_write_reg(state, 0x0026, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x0027, 0x00);
+#endif
+
+	ret = lgdt3306a_read_reg(state, 0x001e, &val);
+	val &= 0x0f;
+	val |= 0xa0;
+	ret = lgdt3306a_write_reg(state, 0x001e, val);
+
+	ret = lgdt3306a_write_reg(state, 0x0022, 0x08);
+
+	ret = lgdt3306a_write_reg(state, 0x0023, 0xFF);
+
+	ret = lgdt3306a_read_reg(state, 0x211f, &val);
+	val &= 0xef;
+	ret = lgdt3306a_write_reg(state, 0x211f, val);
+
+	ret = lgdt3306a_write_reg(state, 0x2173, 0x01);
+
+	ret = lgdt3306a_read_reg(state, 0x1061, &val);
+	val &= 0xf8;
+	val |= 0x04;
+	ret = lgdt3306a_write_reg(state, 0x1061, val);
+
+	ret = lgdt3306a_read_reg(state, 0x103d, &val);
+	val &= 0xcf;
+	ret = lgdt3306a_write_reg(state, 0x103d, val);
+
+	ret = lgdt3306a_write_reg(state, 0x2122, 0x40);
+
+	ret = lgdt3306a_read_reg(state, 0x2141, &val);
+	val &= 0x3f;
+	ret = lgdt3306a_write_reg(state, 0x2141, val);
+
+	ret = lgdt3306a_read_reg(state, 0x2135, &val);
+	val &= 0x0f;
+	val |= 0x70;
+	ret = lgdt3306a_write_reg(state, 0x2135, val);
+
+	ret = lgdt3306a_read_reg(state, 0x0003, &val);
+	val &= 0xf7;
+	ret = lgdt3306a_write_reg(state, 0x0003, val);
+
+	ret = lgdt3306a_read_reg(state, 0x001c, &val);
+	val &= 0x7f;
+	ret = lgdt3306a_write_reg(state, 0x001c, val);
+
+	/* 6. EQ step size */
+	ret = lgdt3306a_read_reg(state, 0x2179, &val);
+	val &= 0xf8;
+	ret = lgdt3306a_write_reg(state, 0x2179, val);
+
+	ret = lgdt3306a_read_reg(state, 0x217a, &val);
+	val &= 0xf8;
+	ret = lgdt3306a_write_reg(state, 0x217a, val);
+
+	/* 7. Reset */
+	ret = lgdt3306a_soft_reset(state);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	dbg_info("complete\n");
+fail:
+	return ret;
+}
+
+static int lgdt3306a_set_qam(struct lgdt3306a_state *state, int modulation)
+{
+	u8 val;
+	int ret;
+
+	dbg_info("modulation=%d\n", modulation);
+
+	/* 1. Selection of standard mode(0x08=QAM, 0x80=VSB) */
+	ret = lgdt3306a_write_reg(state, 0x0008, 0x08);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 1a. Spectrum inversion detection to Auto */
+	ret = lgdt3306a_read_reg(state, 0x0002, &val);
+	val &= 0xfb; /* SPECINV Off */
+	val |= 0x08; /* SPECINVAUTO On */
+	ret = lgdt3306a_write_reg(state, 0x0002, val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 2. Bandwidth mode for QAM */
+	ret = lgdt3306a_read_reg(state, 0x0009, &val);
+	val &= 0xe3; /* STDOPDETTMODE[2:0]=0 VSB Off */
+	ret = lgdt3306a_write_reg(state, 0x0009, val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 3. : 64QAM/256QAM detection(manual, auto) */
+	ret = lgdt3306a_read_reg(state, 0x0009, &val);
+	val &= 0xfc;
+	val |= 0x02; /* STDOPDETCMODE[1:0]=1=Manual 2=Auto */
+	ret = lgdt3306a_write_reg(state, 0x0009, val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 3a. : 64QAM/256QAM selection for manual */
+	ret = lgdt3306a_read_reg(state, 0x101a, &val);
+	val &= 0xf8;
+	if (modulation == QAM_64)
+		val |= 0x02; /* QMDQMODE[2:0]=2=QAM64 */
+	else
+		val |= 0x04; /* QMDQMODE[2:0]=4=QAM256 */
+
+	ret = lgdt3306a_write_reg(state, 0x101a, val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 4. ADC sampling frequency rate(4x sampling) */
+	ret = lgdt3306a_read_reg(state, 0x000d, &val);
+	val &= 0xbf;
+	val |= 0x40; /* SAMPLING4XFEN=1 */
+	ret = lgdt3306a_write_reg(state, 0x000d, val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 5. No AICC operation in QAM mode */
+	ret = lgdt3306a_read_reg(state, 0x0024, &val);
+	val &= 0x00;
+	ret = lgdt3306a_write_reg(state, 0x0024, val);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 6. Reset */
+	ret = lgdt3306a_soft_reset(state);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	dbg_info("complete\n");
+fail:
+	return ret;
+}
+
+static int lgdt3306a_set_modulation(struct lgdt3306a_state *state,
+				   struct dtv_frontend_properties *p)
+{
+	int ret;
+
+	dbg_info("\n");
+
+	switch (p->modulation) {
+	case VSB_8:
+		ret = lgdt3306a_set_vsb(state);
+		break;
+	case QAM_64:
+		ret = lgdt3306a_set_qam(state, QAM_64);
+		break;
+	case QAM_256:
+		ret = lgdt3306a_set_qam(state, QAM_256);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (lg_chkerr(ret))
+		goto fail;
+
+	state->current_modulation = p->modulation;
+
+fail:
+	return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lgdt3306a_agc_setup(struct lgdt3306a_state *state,
+			      struct dtv_frontend_properties *p)
+{
+	/* TODO: anything we want to do here??? */
+	dbg_info("\n");
+
+	switch (p->modulation) {
+	case VSB_8:
+		break;
+	case QAM_64:
+	case QAM_256:
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lgdt3306a_set_inversion(struct lgdt3306a_state *state,
+				       int inversion)
+{
+	int ret;
+
+	dbg_info("(%d)\n", inversion);
+
+	ret = lgdt3306a_set_reg_bit(state, 0x0002, 2, inversion ? 1 : 0);
+	return ret;
+}
+
+static int lgdt3306a_set_inversion_auto(struct lgdt3306a_state *state,
+				       int enabled)
+{
+	int ret;
+
+	dbg_info("(%d)\n", enabled);
+
+	/* 0=Manual 1=Auto(QAM only) - SPECINVAUTO=0x04 */
+	ret = lgdt3306a_set_reg_bit(state, 0x0002, 3, enabled);
+	return ret;
+}
+
+static int lgdt3306a_spectral_inversion(struct lgdt3306a_state *state,
+				       struct dtv_frontend_properties *p,
+				       int inversion)
+{
+	int ret = 0;
+
+	dbg_info("(%d)\n", inversion);
+#if 0
+	/*
+	 * FGR - spectral_inversion defaults already set for VSB and QAM;
+	 * can enable later if desired
+	 */
+
+	ret = lgdt3306a_set_inversion(state, inversion);
+
+	switch (p->modulation) {
+	case VSB_8:
+		/* Manual only for VSB */
+		ret = lgdt3306a_set_inversion_auto(state, 0);
+		break;
+	case QAM_64:
+	case QAM_256:
+		/* Auto ok for QAM */
+		ret = lgdt3306a_set_inversion_auto(state, 1);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+#endif
+	return ret;
+}
+
+static int lgdt3306a_set_if(struct lgdt3306a_state *state,
+			   struct dtv_frontend_properties *p)
+{
+	int ret;
+	u16 if_freq_khz;
+	u8 nco1, nco2;
+
+	switch (p->modulation) {
+	case VSB_8:
+		if_freq_khz = state->cfg->vsb_if_khz;
+		break;
+	case QAM_64:
+	case QAM_256:
+		if_freq_khz = state->cfg->qam_if_khz;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (if_freq_khz) {
+	default:
+		pr_warn("IF=%d KHz is not supportted, 3250 assumed\n",
+			if_freq_khz);
+		/* fallthrough */
+	case 3250: /* 3.25Mhz */
+		nco1 = 0x34;
+		nco2 = 0x00;
+		break;
+	case 3500: /* 3.50Mhz */
+		nco1 = 0x38;
+		nco2 = 0x00;
+		break;
+	case 4000: /* 4.00Mhz */
+		nco1 = 0x40;
+		nco2 = 0x00;
+		break;
+	case 5000: /* 5.00Mhz */
+		nco1 = 0x50;
+		nco2 = 0x00;
+		break;
+	case 5380: /* 5.38Mhz */
+		nco1 = 0x56;
+		nco2 = 0x14;
+		break;
+	}
+	ret = lgdt3306a_write_reg(state, 0x0010, nco1);
+	if (ret)
+		return ret;
+	ret = lgdt3306a_write_reg(state, 0x0011, nco2);
+	if (ret)
+		return ret;
+
+	dbg_info("if_freq=%d KHz->[%04x]\n", if_freq_khz, nco1<<8 | nco2);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lgdt3306a_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+
+	if (state->cfg->deny_i2c_rptr) {
+		dbg_info("deny_i2c_rptr=%d\n", state->cfg->deny_i2c_rptr);
+		return 0;
+	}
+	dbg_info("(%d)\n", enable);
+
+	/* NI2CRPTEN=0x80 */
+	return lgdt3306a_set_reg_bit(state, 0x0002, 7, enable ? 0 : 1);
+}
+
+static int lgdt3306a_sleep(struct lgdt3306a_state *state)
+{
+	int ret;
+
+	dbg_info("\n");
+	state->current_frequency = -1; /* force re-tune, when we wake */
+
+	ret = lgdt3306a_mpeg_tristate(state, 1); /* disable data bus */
+	if (lg_chkerr(ret))
+		goto fail;
+
+	ret = lgdt3306a_power(state, 0); /* power down */
+	lg_chkerr(ret);
+
+fail:
+	return 0;
+}
+
+static int lgdt3306a_fe_sleep(struct dvb_frontend *fe)
+{
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+
+	return lgdt3306a_sleep(state);
+}
+
+static int lgdt3306a_init(struct dvb_frontend *fe)
+{
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+	u8 val;
+	int ret;
+
+	dbg_info("\n");
+
+	/* 1. Normal operation mode */
+	ret = lgdt3306a_set_reg_bit(state, 0x0001, 0, 1); /* SIMFASTENB=0x01 */
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 2. Spectrum inversion auto detection (Not valid for VSB) */
+	ret = lgdt3306a_set_inversion_auto(state, 0);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 3. Spectrum inversion(According to the tuner configuration) */
+	ret = lgdt3306a_set_inversion(state, 1);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 4. Peak-to-peak voltage of ADC input signal */
+
+	/* ADCSEL1V=0x80=1Vpp; 0x00=2Vpp */
+	ret = lgdt3306a_set_reg_bit(state, 0x0004, 7, 1);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 5. ADC output data capture clock phase */
+
+	/* 0=same phase as ADC clock */
+	ret = lgdt3306a_set_reg_bit(state, 0x0004, 2, 0);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 5a. ADC sampling clock source */
+
+	/* ADCCLKPLLSEL=0x08; 0=use ext clock, not PLL */
+	ret = lgdt3306a_set_reg_bit(state, 0x0004, 3, 0);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	/* 6. Automatic PLL set */
+
+	/* PLLSETAUTO=0x40; 0=off */
+	ret = lgdt3306a_set_reg_bit(state, 0x0005, 6, 0);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	if (state->cfg->xtalMHz == 24) {	/* 24MHz */
+		/* 7. Frequency for PLL output(0x2564 for 192MHz for 24MHz) */
+		ret = lgdt3306a_read_reg(state, 0x0005, &val);
+		if (lg_chkerr(ret))
+			goto fail;
+		val &= 0xc0;
+		val |= 0x25;
+		ret = lgdt3306a_write_reg(state, 0x0005, val);
+		if (lg_chkerr(ret))
+			goto fail;
+		ret = lgdt3306a_write_reg(state, 0x0006, 0x64);
+		if (lg_chkerr(ret))
+			goto fail;
+
+		/* 8. ADC sampling frequency(0x180000 for 24MHz sampling) */
+		ret = lgdt3306a_read_reg(state, 0x000d, &val);
+		if (lg_chkerr(ret))
+			goto fail;
+		val &= 0xc0;
+		val |= 0x18;
+		ret = lgdt3306a_write_reg(state, 0x000d, val);
+		if (lg_chkerr(ret))
+			goto fail;
+
+	} else if (state->cfg->xtalMHz == 25) { /* 25MHz */
+		/* 7. Frequency for PLL output */
+		ret = lgdt3306a_read_reg(state, 0x0005, &val);
+		if (lg_chkerr(ret))
+			goto fail;
+		val &= 0xc0;
+		val |= 0x25;
+		ret = lgdt3306a_write_reg(state, 0x0005, val);
+		if (lg_chkerr(ret))
+			goto fail;
+		ret = lgdt3306a_write_reg(state, 0x0006, 0x64);
+		if (lg_chkerr(ret))
+			goto fail;
+
+		/* 8. ADC sampling frequency(0x190000 for 25MHz sampling) */
+		ret = lgdt3306a_read_reg(state, 0x000d, &val);
+		if (lg_chkerr(ret))
+			goto fail;
+		val &= 0xc0;
+		val |= 0x19;
+		ret = lgdt3306a_write_reg(state, 0x000d, val);
+		if (lg_chkerr(ret))
+			goto fail;
+	} else {
+		pr_err("Bad xtalMHz=%d\n", state->cfg->xtalMHz);
+	}
+#if 0
+	ret = lgdt3306a_write_reg(state, 0x000e, 0x00);
+	ret = lgdt3306a_write_reg(state, 0x000f, 0x00);
+#endif
+
+	/* 9. Center frequency of input signal of ADC */
+	ret = lgdt3306a_write_reg(state, 0x0010, 0x34); /* 3.25MHz */
+	ret = lgdt3306a_write_reg(state, 0x0011, 0x00);
+
+	/* 10. Fixed gain error value */
+	ret = lgdt3306a_write_reg(state, 0x0014, 0); /* gain error=0 */
+
+	/* 10a. VSB TR BW gear shift initial step */
+	ret = lgdt3306a_read_reg(state, 0x103c, &val);
+	val &= 0x0f;
+	val |= 0x20; /* SAMGSAUTOSTL_V[3:0] = 2 */
+	ret = lgdt3306a_write_reg(state, 0x103c, val);
+
+	/* 10b. Timing offset calibration in low temperature for VSB */
+	ret = lgdt3306a_read_reg(state, 0x103d, &val);
+	val &= 0xfc;
+	val |= 0x03;
+	ret = lgdt3306a_write_reg(state, 0x103d, val);
+
+	/* 10c. Timing offset calibration in low temperature for QAM */
+	ret = lgdt3306a_read_reg(state, 0x1036, &val);
+	val &= 0xf0;
+	val |= 0x0c;
+	ret = lgdt3306a_write_reg(state, 0x1036, val);
+
+	/* 11. Using the imaginary part of CIR in CIR loading */
+	ret = lgdt3306a_read_reg(state, 0x211f, &val);
+	val &= 0xef; /* do not use imaginary of CIR */
+	ret = lgdt3306a_write_reg(state, 0x211f, val);
+
+	/* 12. Control of no signal detector function */
+	ret = lgdt3306a_read_reg(state, 0x2849, &val);
+	val &= 0xef; /* NOUSENOSIGDET=0, enable no signal detector */
+	ret = lgdt3306a_write_reg(state, 0x2849, val);
+
+	/* FGR - put demod in some known mode */
+	ret = lgdt3306a_set_vsb(state);
+
+	/* 13. TP stream format */
+	ret = lgdt3306a_mpeg_mode(state, state->cfg->mpeg_mode);
+
+	/* 14. disable output buses */
+	ret = lgdt3306a_mpeg_tristate(state, 1);
+
+	/* 15. Sleep (in reset) */
+	ret = lgdt3306a_sleep(state);
+	lg_chkerr(ret);
+
+fail:
+	return ret;
+}
+
+static int lgdt3306a_set_parameters(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+	int ret;
+
+	dbg_info("(%d, %d)\n", p->frequency, p->modulation);
+
+	if (state->current_frequency  == p->frequency &&
+	   state->current_modulation == p->modulation) {
+		dbg_info(" (already set, skipping ...)\n");
+		return 0;
+	}
+	state->current_frequency = -1;
+	state->current_modulation = -1;
+
+	ret = lgdt3306a_power(state, 1); /* power up */
+	if (lg_chkerr(ret))
+		goto fail;
+
+	if (fe->ops.tuner_ops.set_params) {
+		ret = fe->ops.tuner_ops.set_params(fe);
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
+#if 0
+		if (lg_chkerr(ret))
+			goto fail;
+		state->current_frequency = p->frequency;
+#endif
+	}
+
+	ret = lgdt3306a_set_modulation(state, p);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	ret = lgdt3306a_agc_setup(state, p);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	ret = lgdt3306a_set_if(state, p);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	ret = lgdt3306a_spectral_inversion(state, p,
+					state->cfg->spectral_inversion ? 1 : 0);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	ret = lgdt3306a_mpeg_mode(state, state->cfg->mpeg_mode);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	ret = lgdt3306a_mpeg_mode_polarity(state,
+					  state->cfg->tpclk_edge,
+					  state->cfg->tpvalid_polarity);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	ret = lgdt3306a_mpeg_tristate(state, 0); /* enable data bus */
+	if (lg_chkerr(ret))
+		goto fail;
+
+	ret = lgdt3306a_soft_reset(state);
+	if (lg_chkerr(ret))
+		goto fail;
+
+#ifdef DBG_DUMP
+	lgdt3306a_DumpAllRegs(state);
+#endif
+	state->current_frequency = p->frequency;
+fail:
+	return ret;
+}
+
+static int lgdt3306a_get_frontend(struct dvb_frontend *fe)
+{
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+	dbg_info("(%u, %d)\n",
+		 state->current_frequency, state->current_modulation);
+
+	p->modulation = state->current_modulation;
+	p->frequency = state->current_frequency;
+	return 0;
+}
+
+static enum dvbfe_algo lgdt3306a_get_frontend_algo(struct dvb_frontend *fe)
+{
+#if 1
+	return DVBFE_ALGO_CUSTOM;
+#else
+	return DVBFE_ALGO_HW;
+#endif
+}
+
+/* ------------------------------------------------------------------------ */
+static int lgdt3306a_monitor_vsb(struct lgdt3306a_state *state)
+{
+	u8 val;
+	int ret;
+	u8 snrRef, maxPowerMan, nCombDet;
+	u16 fbDlyCir;
+
+	ret = lgdt3306a_read_reg(state, 0x21a1, &val);
+	if (ret)
+		return ret;
+	snrRef = val & 0x3f;
+
+	ret = lgdt3306a_read_reg(state, 0x2185, &maxPowerMan);
+	if (ret)
+		return ret;
+
+	ret = lgdt3306a_read_reg(state, 0x2191, &val);
+	if (ret)
+		return ret;
+	nCombDet = (val & 0x80) >> 7;
+
+	ret = lgdt3306a_read_reg(state, 0x2180, &val);
+	if (ret)
+		return ret;
+	fbDlyCir = (val & 0x03) << 8;
+
+	ret = lgdt3306a_read_reg(state, 0x2181, &val);
+	if (ret)
+		return ret;
+	fbDlyCir |= val;
+
+	dbg_info("snrRef=%d maxPowerMan=0x%x nCombDet=%d fbDlyCir=0x%x\n",
+		snrRef, maxPowerMan, nCombDet, fbDlyCir);
+
+	/* Carrier offset sub loop bandwidth */
+	ret = lgdt3306a_read_reg(state, 0x1061, &val);
+	if (ret)
+		return ret;
+	val &= 0xf8;
+	if ((snrRef > 18) && (maxPowerMan > 0x68)
+	    && (nCombDet == 0x01)
+	    && ((fbDlyCir == 0x03FF) || (fbDlyCir < 0x6C))) {
+		/* SNR is over 18dB and no ghosting */
+		val |= 0x00; /* final bandwidth = 0 */
+	} else {
+		val |= 0x04; /* final bandwidth = 4 */
+	}
+	ret = lgdt3306a_write_reg(state, 0x1061, val);
+	if (ret)
+		return ret;
+
+	/* Adjust Notch Filter */
+	ret = lgdt3306a_read_reg(state, 0x0024, &val);
+	if (ret)
+		return ret;
+	val &= 0x0f;
+	if (nCombDet == 0) { /* Turn on the Notch Filter */
+		val |= 0x50;
+	}
+	ret = lgdt3306a_write_reg(state, 0x0024, val);
+	if (ret)
+		return ret;
+
+	/* VSB Timing Recovery output normalization */
+	ret = lgdt3306a_read_reg(state, 0x103d, &val);
+	if (ret)
+		return ret;
+	val &= 0xcf;
+	val |= 0x20;
+	ret = lgdt3306a_write_reg(state, 0x103d, val);
+
+	return ret;
+}
+
+static enum lgdt3306a_modulation
+lgdt3306a_check_oper_mode(struct lgdt3306a_state *state)
+{
+	u8 val = 0;
+	int ret;
+
+	ret = lgdt3306a_read_reg(state, 0x0081, &val);
+	if (ret)
+		goto err;
+
+	if (val & 0x80)	{
+		dbg_info("VSB\n");
+		return LG3306_VSB;
+	}
+	if (val & 0x08) {
+		ret = lgdt3306a_read_reg(state, 0x00a6, &val);
+		if (ret)
+			goto err;
+		val = val >> 2;
+		if (val & 0x01) {
+			dbg_info("QAM256\n");
+			return LG3306_QAM256;
+		}
+		dbg_info("QAM64\n");
+		return LG3306_QAM64;
+	}
+err:
+	pr_warn("UNKNOWN\n");
+	return LG3306_UNKNOWN_MODE;
+}
+
+static enum lgdt3306a_lock_status
+lgdt3306a_check_lock_status(struct lgdt3306a_state *state,
+			    enum lgdt3306a_lock_check whatLock)
+{
+	u8 val = 0;
+	int ret;
+	enum lgdt3306a_modulation	modeOper;
+	enum lgdt3306a_lock_status lockStatus;
+
+	modeOper = LG3306_UNKNOWN_MODE;
+
+	switch (whatLock) {
+	case LG3306_SYNC_LOCK:
+	{
+		ret = lgdt3306a_read_reg(state, 0x00a6, &val);
+		if (ret)
+			return ret;
+
+		if ((val & 0x80) == 0x80)
+			lockStatus = LG3306_LOCK;
+		else
+			lockStatus = LG3306_UNLOCK;
+
+		dbg_info("SYNC_LOCK=%x\n", lockStatus);
+		break;
+	}
+	case LG3306_AGC_LOCK:
+	{
+		ret = lgdt3306a_read_reg(state, 0x0080, &val);
+		if (ret)
+			return ret;
+
+		if ((val & 0x40) == 0x40)
+			lockStatus = LG3306_LOCK;
+		else
+			lockStatus = LG3306_UNLOCK;
+
+		dbg_info("AGC_LOCK=%x\n", lockStatus);
+		break;
+	}
+	case LG3306_TR_LOCK:
+	{
+		modeOper = lgdt3306a_check_oper_mode(state);
+		if ((modeOper == LG3306_QAM64) || (modeOper == LG3306_QAM256)) {
+			ret = lgdt3306a_read_reg(state, 0x1094, &val);
+			if (ret)
+				return ret;
+
+			if ((val & 0x80) == 0x80)
+				lockStatus = LG3306_LOCK;
+			else
+				lockStatus = LG3306_UNLOCK;
+		} else
+			lockStatus = LG3306_UNKNOWN_LOCK;
+
+		dbg_info("TR_LOCK=%x\n", lockStatus);
+		break;
+	}
+	case LG3306_FEC_LOCK:
+	{
+		modeOper = lgdt3306a_check_oper_mode(state);
+		if ((modeOper == LG3306_QAM64) || (modeOper == LG3306_QAM256)) {
+			ret = lgdt3306a_read_reg(state, 0x0080, &val);
+			if (ret)
+				return ret;
+
+			if ((val & 0x10) == 0x10)
+				lockStatus = LG3306_LOCK;
+			else
+				lockStatus = LG3306_UNLOCK;
+		} else
+			lockStatus = LG3306_UNKNOWN_LOCK;
+
+		dbg_info("FEC_LOCK=%x\n", lockStatus);
+		break;
+	}
+
+	default:
+		lockStatus = LG3306_UNKNOWN_LOCK;
+		pr_warn("UNKNOWN whatLock=%d\n", whatLock);
+		break;
+	}
+
+	return lockStatus;
+}
+
+static enum lgdt3306a_neverlock_status
+lgdt3306a_check_neverlock_status(struct lgdt3306a_state *state)
+{
+	u8 val = 0;
+	int ret;
+	enum lgdt3306a_neverlock_status lockStatus;
+
+	ret = lgdt3306a_read_reg(state, 0x0080, &val);
+	if (ret)
+		return ret;
+	lockStatus = (enum lgdt3306a_neverlock_status)(val & 0x03);
+
+	dbg_info("NeverLock=%d", lockStatus);
+
+	return lockStatus;
+}
+
+static int lgdt3306a_pre_monitoring(struct lgdt3306a_state *state)
+{
+	u8 val = 0;
+	int ret;
+	u8 currChDiffACQ, snrRef, mainStrong, aiccrejStatus;
+
+	/* Channel variation */
+	ret = lgdt3306a_read_reg(state, 0x21bc, &currChDiffACQ);
+	if (ret)
+		return ret;
+
+	/* SNR of Frame sync */
+	ret = lgdt3306a_read_reg(state, 0x21a1, &val);
+	if (ret)
+		return ret;
+	snrRef = val & 0x3f;
+
+	/* Strong Main CIR */
+	ret = lgdt3306a_read_reg(state, 0x2199, &val);
+	if (ret)
+		return ret;
+	mainStrong = (val & 0x40) >> 6;
+
+	ret = lgdt3306a_read_reg(state, 0x0090, &val);
+	if (ret)
+		return ret;
+	aiccrejStatus = (val & 0xf0) >> 4;
+
+	dbg_info("snrRef=%d mainStrong=%d aiccrejStatus=%d currChDiffACQ=0x%x\n",
+		snrRef, mainStrong, aiccrejStatus, currChDiffACQ);
+
+#if 0
+	/* Dynamic ghost exists */
+	if ((mainStrong == 0) && (currChDiffACQ > 0x70))
+#endif
+	if (mainStrong == 0) {
+		ret = lgdt3306a_read_reg(state, 0x2135, &val);
+		if (ret)
+			return ret;
+		val &= 0x0f;
+		val |= 0xa0;
+		ret = lgdt3306a_write_reg(state, 0x2135, val);
+		if (ret)
+			return ret;
+
+		ret = lgdt3306a_read_reg(state, 0x2141, &val);
+		if (ret)
+			return ret;
+		val &= 0x3f;
+		val |= 0x80;
+		ret = lgdt3306a_write_reg(state, 0x2141, val);
+		if (ret)
+			return ret;
+
+		ret = lgdt3306a_write_reg(state, 0x2122, 0x70);
+		if (ret)
+			return ret;
+	} else { /* Weak ghost or static channel */
+		ret = lgdt3306a_read_reg(state, 0x2135, &val);
+		if (ret)
+			return ret;
+		val &= 0x0f;
+		val |= 0x70;
+		ret = lgdt3306a_write_reg(state, 0x2135, val);
+		if (ret)
+			return ret;
+
+		ret = lgdt3306a_read_reg(state, 0x2141, &val);
+		if (ret)
+			return ret;
+		val &= 0x3f;
+		val |= 0x40;
+		ret = lgdt3306a_write_reg(state, 0x2141, val);
+		if (ret)
+			return ret;
+
+		ret = lgdt3306a_write_reg(state, 0x2122, 0x40);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static enum lgdt3306a_lock_status
+lgdt3306a_sync_lock_poll(struct lgdt3306a_state *state)
+{
+	enum lgdt3306a_lock_status syncLockStatus = LG3306_UNLOCK;
+	int	i;
+
+	for (i = 0; i < 2; i++)	{
+		msleep(30);
+
+		syncLockStatus = lgdt3306a_check_lock_status(state,
+							     LG3306_SYNC_LOCK);
+
+		if (syncLockStatus == LG3306_LOCK) {
+			dbg_info("locked(%d)\n", i);
+			return LG3306_LOCK;
+		}
+	}
+	dbg_info("not locked\n");
+	return LG3306_UNLOCK;
+}
+
+static enum lgdt3306a_lock_status
+lgdt3306a_fec_lock_poll(struct lgdt3306a_state *state)
+{
+	enum lgdt3306a_lock_status FECLockStatus = LG3306_UNLOCK;
+	int	i;
+
+	for (i = 0; i < 2; i++)	{
+		msleep(30);
+
+		FECLockStatus = lgdt3306a_check_lock_status(state,
+							    LG3306_FEC_LOCK);
+
+		if (FECLockStatus == LG3306_LOCK) {
+			dbg_info("locked(%d)\n", i);
+			return FECLockStatus;
+		}
+	}
+	dbg_info("not locked\n");
+	return FECLockStatus;
+}
+
+static enum lgdt3306a_neverlock_status
+lgdt3306a_neverlock_poll(struct lgdt3306a_state *state)
+{
+	enum lgdt3306a_neverlock_status NLLockStatus = LG3306_NL_FAIL;
+	int	i;
+
+	for (i = 0; i < 5; i++) {
+		msleep(30);
+
+		NLLockStatus = lgdt3306a_check_neverlock_status(state);
+
+		if (NLLockStatus == LG3306_NL_LOCK) {
+			dbg_info("NL_LOCK(%d)\n", i);
+			return NLLockStatus;
+		}
+	}
+	dbg_info("NLLockStatus=%d\n", NLLockStatus);
+	return NLLockStatus;
+}
+
+static u8 lgdt3306a_get_packet_error(struct lgdt3306a_state *state)
+{
+	u8 val;
+	int ret;
+
+	ret = lgdt3306a_read_reg(state, 0x00fa, &val);
+	if (ret)
+		return ret;
+
+	return val;
+}
+
+static const u32 valx_x10[] = {
+	10,  11,  13,  15,  17,  20,  25,  33,  41,  50,  59,  73,  87,  100
+};
+static const u32 log10x_x1000[] = {
+	0,   41, 114, 176, 230, 301, 398, 518, 613, 699, 771, 863, 939, 1000
+};
+
+static u32 log10_x1000(u32 x)
+{
+	u32 diff_val, step_val, step_log10;
+	u32 log_val = 0;
+	u32 i;
+
+	if (x <= 0)
+		return -1000000; /* signal error */
+
+	if (x == 10)
+		return 0; /* log(1)=0 */
+
+	if (x < 10) {
+		while (x < 10) {
+			x = x * 10;
+			log_val--;
+		}
+	} else {	/* x > 10 */
+		while (x >= 100) {
+			x = x / 10;
+			log_val++;
+		}
+	}
+	log_val *= 1000;
+
+	if (x == 10) /* was our input an exact multiple of 10 */
+		return log_val;	/* don't need to interpolate */
+
+	/* find our place on the log curve */
+	for (i = 1; i < ARRAY_SIZE(valx_x10); i++) {
+		if (valx_x10[i] >= x)
+			break;
+	}
+	if (i == ARRAY_SIZE(valx_x10))
+		return log_val + log10x_x1000[i - 1];
+
+	diff_val   = x - valx_x10[i-1];
+	step_val   = valx_x10[i] - valx_x10[i - 1];
+	step_log10 = log10x_x1000[i] - log10x_x1000[i - 1];
+
+	/* do a linear interpolation to get in-between values */
+	return log_val + log10x_x1000[i - 1] +
+		((diff_val*step_log10) / step_val);
+}
+
+static u32 lgdt3306a_calculate_snr_x100(struct lgdt3306a_state *state)
+{
+	u32 mse; /* Mean-Square Error */
+	u32 pwr; /* Constelation power */
+	u32 snr_x100;
+
+	mse = (read_reg(state, 0x00ec) << 8) |
+	      (read_reg(state, 0x00ed));
+	pwr = (read_reg(state, 0x00e8) << 8) |
+	      (read_reg(state, 0x00e9));
+
+	if (mse == 0) /* no signal */
+		return 0;
+
+	snr_x100 = log10_x1000((pwr * 10000) / mse) - 3000;
+	dbg_info("mse=%u, pwr=%u, snr_x100=%d\n", mse, pwr, snr_x100);
+
+	return snr_x100;
+}
+
+static enum lgdt3306a_lock_status
+lgdt3306a_vsb_lock_poll(struct lgdt3306a_state *state)
+{
+	int ret;
+	u8 cnt = 0;
+	u8 packet_error;
+	u32 snr;
+
+	for (cnt = 0; cnt < 10; cnt++) {
+		if (lgdt3306a_sync_lock_poll(state) == LG3306_UNLOCK) {
+			dbg_info("no sync lock!\n");
+			return LG3306_UNLOCK;
+		}
+
+		msleep(20);
+		ret = lgdt3306a_pre_monitoring(state);
+		if (ret)
+			break;
+
+		packet_error = lgdt3306a_get_packet_error(state);
+		snr = lgdt3306a_calculate_snr_x100(state);
+		dbg_info("cnt=%d errors=%d snr=%d\n", cnt, packet_error, snr);
+
+		if ((snr >= 1500) && (packet_error < 0xff))
+			return LG3306_LOCK;
+	}
+
+	dbg_info("not locked!\n");
+	return LG3306_UNLOCK;
+}
+
+static enum lgdt3306a_lock_status
+lgdt3306a_qam_lock_poll(struct lgdt3306a_state *state)
+{
+	u8 cnt;
+	u8 packet_error;
+	u32	snr;
+
+	for (cnt = 0; cnt < 10; cnt++) {
+		if (lgdt3306a_fec_lock_poll(state) == LG3306_UNLOCK) {
+			dbg_info("no fec lock!\n");
+			return LG3306_UNLOCK;
+		}
+
+		msleep(20);
+
+		packet_error = lgdt3306a_get_packet_error(state);
+		snr = lgdt3306a_calculate_snr_x100(state);
+		dbg_info("cnt=%d errors=%d snr=%d\n", cnt, packet_error, snr);
+
+		if ((snr >= 1500) && (packet_error < 0xff))
+			return LG3306_LOCK;
+	}
+
+	dbg_info("not locked!\n");
+	return LG3306_UNLOCK;
+}
+
+static int lgdt3306a_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+	u16 strength = 0;
+	int ret = 0;
+
+	if (fe->ops.tuner_ops.get_rf_strength) {
+		ret = fe->ops.tuner_ops.get_rf_strength(fe, &strength);
+		if (ret == 0)
+			dbg_info("strength=%d\n", strength);
+		else
+			dbg_info("fe->ops.tuner_ops.get_rf_strength() failed\n");
+	}
+
+	*status = 0;
+	if (lgdt3306a_neverlock_poll(state) == LG3306_NL_LOCK) {
+		*status |= FE_HAS_SIGNAL;
+		*status |= FE_HAS_CARRIER;
+
+		switch (state->current_modulation) {
+		case QAM_256:
+		case QAM_64:
+			if (lgdt3306a_qam_lock_poll(state) == LG3306_LOCK) {
+				*status |= FE_HAS_VITERBI;
+				*status |= FE_HAS_SYNC;
+
+				*status |= FE_HAS_LOCK;
+			}
+			break;
+		case VSB_8:
+			if (lgdt3306a_vsb_lock_poll(state) == LG3306_LOCK) {
+				*status |= FE_HAS_VITERBI;
+				*status |= FE_HAS_SYNC;
+
+				*status |= FE_HAS_LOCK;
+
+				ret = lgdt3306a_monitor_vsb(state);
+			}
+			break;
+		default:
+			ret = -EINVAL;
+		}
+	}
+	return ret;
+}
+
+
+static int lgdt3306a_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+
+	state->snr = lgdt3306a_calculate_snr_x100(state);
+	/* report SNR in dB * 10 */
+	*snr = state->snr/10;
+
+	return 0;
+}
+
+static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe,
+					 u16 *strength)
+{
+	/*
+	 * Calculate some sort of "strength" from SNR
+	 */
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+	u16 snr; /* snr_x10 */
+	int ret;
+	u32 ref_snr; /* snr*100 */
+	u32 str;
+
+	*strength = 0;
+
+	switch (state->current_modulation) {
+	case VSB_8:
+		 ref_snr = 1600; /* 16dB */
+		 break;
+	case QAM_64:
+		 ref_snr = 2200; /* 22dB */
+		 break;
+	case QAM_256:
+		 ref_snr = 2800; /* 28dB */
+		 break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = fe->ops.read_snr(fe, &snr);
+	if (lg_chkerr(ret))
+		goto fail;
+
+	if (state->snr <= (ref_snr - 100))
+		str = 0;
+	else if (state->snr <= ref_snr)
+		str = (0xffff * 65) / 100; /* 65% */
+	else {
+		str = state->snr - ref_snr;
+		str /= 50;
+		str += 78; /* 78%-100% */
+		if (str > 100)
+			str = 100;
+		str = (0xffff * str) / 100;
+	}
+	*strength = (u16)str;
+	dbg_info("strength=%u\n", *strength);
+
+fail:
+	return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lgdt3306a_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+	u32 tmp;
+
+	*ber = 0;
+#if 1
+	/* FGR - FIXME - I don't know what value is expected by dvb_core
+	 * what is the scale of the value?? */
+	tmp =              read_reg(state, 0x00fc); /* NBERVALUE[24-31] */
+	tmp = (tmp << 8) | read_reg(state, 0x00fd); /* NBERVALUE[16-23] */
+	tmp = (tmp << 8) | read_reg(state, 0x00fe); /* NBERVALUE[8-15] */
+	tmp = (tmp << 8) | read_reg(state, 0x00ff); /* NBERVALUE[0-7] */
+	*ber = tmp;
+	dbg_info("ber=%u\n", tmp);
+#endif
+	return 0;
+}
+
+static int lgdt3306a_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+
+	*ucblocks = 0;
+#if 1
+	/* FGR - FIXME - I don't know what value is expected by dvb_core
+	 * what happens when value wraps? */
+	*ucblocks = read_reg(state, 0x00f4); /* TPIFTPERRCNT[0-7] */
+	dbg_info("ucblocks=%u\n", *ucblocks);
+#endif
+
+	return 0;
+}
+
+static int lgdt3306a_tune(struct dvb_frontend *fe, bool re_tune,
+			  unsigned int mode_flags, unsigned int *delay,
+			  fe_status_t *status)
+{
+	int ret = 0;
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+
+	dbg_info("re_tune=%u\n", re_tune);
+
+	if (re_tune) {
+		state->current_frequency = -1; /* force re-tune */
+		ret = lgdt3306a_set_parameters(fe);
+		if (ret != 0)
+			return ret;
+	}
+	*delay = 125;
+	ret = lgdt3306a_read_status(fe, status);
+
+	return ret;
+}
+
+static int lgdt3306a_get_tune_settings(struct dvb_frontend *fe,
+				       struct dvb_frontend_tune_settings
+				       *fe_tune_settings)
+{
+	fe_tune_settings->min_delay_ms = 100;
+	dbg_info("\n");
+	return 0;
+}
+
+static int lgdt3306a_search(struct dvb_frontend *fe)
+{
+	fe_status_t status = 0;
+	int i, ret;
+
+	/* set frontend */
+	ret = lgdt3306a_set_parameters(fe);
+	if (ret)
+		goto error;
+
+	/* wait frontend lock */
+	for (i = 20; i > 0; i--) {
+		dbg_info(": loop=%d\n", i);
+		msleep(50);
+		ret = lgdt3306a_read_status(fe, &status);
+		if (ret)
+			goto error;
+
+		if (status & FE_HAS_LOCK)
+			break;
+	}
+
+	/* check if we have a valid signal */
+	if (status & FE_HAS_LOCK)
+		return DVBFE_ALGO_SEARCH_SUCCESS;
+	else
+		return DVBFE_ALGO_SEARCH_AGAIN;
+
+error:
+	dbg_info("failed (%d)\n", ret);
+	return DVBFE_ALGO_SEARCH_ERROR;
+}
+
+static void lgdt3306a_release(struct dvb_frontend *fe)
+{
+	struct lgdt3306a_state *state = fe->demodulator_priv;
+
+	dbg_info("\n");
+	kfree(state);
+}
+
+static struct dvb_frontend_ops lgdt3306a_ops;
+
+struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config,
+				      struct i2c_adapter *i2c_adap)
+{
+	struct lgdt3306a_state *state = NULL;
+	int ret;
+	u8 val;
+
+	dbg_info("(%d-%04x)\n",
+	       i2c_adap ? i2c_adapter_id(i2c_adap) : 0,
+	       config ? config->i2c_addr : 0);
+
+	state = kzalloc(sizeof(struct lgdt3306a_state), GFP_KERNEL);
+	if (state == NULL)
+		goto fail;
+
+	state->cfg = config;
+	state->i2c_adap = i2c_adap;
+
+	memcpy(&state->frontend.ops, &lgdt3306a_ops,
+	       sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+
+	/* verify that we're talking to a lg3306a */
+	/* FGR - NOTE - there is no obvious ChipId to check; we check
+	 * some "known" bits after reset, but it's still just a guess */
+	ret = lgdt3306a_read_reg(state, 0x0000, &val);
+	if (lg_chkerr(ret))
+		goto fail;
+	if ((val & 0x74) != 0x74) {
+		pr_warn("expected 0x74, got 0x%x\n", (val & 0x74));
+#if 0
+		/* FIXME - re-enable when we know this is right */
+		goto fail;
+#endif
+	}
+	ret = lgdt3306a_read_reg(state, 0x0001, &val);
+	if (lg_chkerr(ret))
+		goto fail;
+	if ((val & 0xf6) != 0xc6) {
+		pr_warn("expected 0xc6, got 0x%x\n", (val & 0xf6));
+#if 0
+		/* FIXME - re-enable when we know this is right */
+		goto fail;
+#endif
+	}
+	ret = lgdt3306a_read_reg(state, 0x0002, &val);
+	if (lg_chkerr(ret))
+		goto fail;
+	if ((val & 0x73) != 0x03) {
+		pr_warn("expected 0x03, got 0x%x\n", (val & 0x73));
+#if 0
+		/* FIXME - re-enable when we know this is right */
+		goto fail;
+#endif
+	}
+
+	state->current_frequency = -1;
+	state->current_modulation = -1;
+
+	lgdt3306a_sleep(state);
+
+	return &state->frontend;
+
+fail:
+	pr_warn("unable to detect LGDT3306A hardware\n");
+	kfree(state);
+	return NULL;
+}
+EXPORT_SYMBOL(lgdt3306a_attach);
+
+#ifdef DBG_DUMP
+
+static const short regtab[] = {
+	0x0000, /* SOFTRSTB 1'b1 1'b1 1'b1 ADCPDB 1'b1 PLLPDB GBBPDB 11111111 */
+	0x0001, /* 1'b1 1'b1 1'b0 1'b0 AUTORPTRS */
+	0x0002, /* NI2CRPTEN 1'b0 1'b0 1'b0 SPECINVAUT */
+	0x0003, /* AGCRFOUT */
+	0x0004, /* ADCSEL1V ADCCNT ADCCNF ADCCNS ADCCLKPLL */
+	0x0005, /* PLLINDIVSE */
+	0x0006, /* PLLCTRL[7:0] 11100001 */
+	0x0007, /* SYSINITWAITTIME[7:0] (msec) 00001000 */
+	0x0008, /* STDOPMODE[7:0] 10000000 */
+	0x0009, /* 1'b0 1'b0 1'b0 STDOPDETTMODE[2:0] STDOPDETCMODE[1:0] 00011110 */
+	0x000a, /* DAFTEN 1'b1 x x SCSYSLOCK */
+	0x000b, /* SCSYSLOCKCHKTIME[7:0] (10msec) 01100100 */
+	0x000d, /* x SAMPLING4 */
+	0x000e, /* SAMFREQ[15:8] 00000000 */
+	0x000f, /* SAMFREQ[7:0] 00000000 */
+	0x0010, /* IFFREQ[15:8] 01100000 */
+	0x0011, /* IFFREQ[7:0] 00000000 */
+	0x0012, /* AGCEN AGCREFMO */
+	0x0013, /* AGCRFFIXB AGCIFFIXB AGCLOCKDETRNGSEL[1:0] 1'b1 1'b0 1'b0 1'b0 11101000 */
+	0x0014, /* AGCFIXVALUE[7:0] 01111111 */
+	0x0015, /* AGCREF[15:8] 00001010 */
+	0x0016, /* AGCREF[7:0] 11100100 */
+	0x0017, /* AGCDELAY[7:0] 00100000 */
+	0x0018, /* AGCRFBW[3:0] AGCIFBW[3:0] 10001000 */
+	0x0019, /* AGCUDOUTMODE[1:0] AGCUDCTRLLEN[1:0] AGCUDCTRL */
+	0x001c, /* 1'b1 PFEN MFEN AICCVSYNC */
+	0x001d, /* 1'b0 1'b1 1'b0 1'b1 AICCVSYNC */
+	0x001e, /* AICCALPHA[3:0] 1'b1 1'b0 1'b1 1'b0 01111010 */
+	0x001f, /* AICCDETTH[19:16] AICCOFFTH[19:16] 00000000 */
+	0x0020, /* AICCDETTH[15:8] 01111100 */
+	0x0021, /* AICCDETTH[7:0] 00000000 */
+	0x0022, /* AICCOFFTH[15:8] 00000101 */
+	0x0023, /* AICCOFFTH[7:0] 11100000 */
+	0x0024, /* AICCOPMODE3[1:0] AICCOPMODE2[1:0] AICCOPMODE1[1:0] AICCOPMODE0[1:0] 00000000 */
+	0x0025, /* AICCFIXFREQ3[23:16] 00000000 */
+	0x0026, /* AICCFIXFREQ3[15:8] 00000000 */
+	0x0027, /* AICCFIXFREQ3[7:0] 00000000 */
+	0x0028, /* AICCFIXFREQ2[23:16] 00000000 */
+	0x0029, /* AICCFIXFREQ2[15:8] 00000000 */
+	0x002a, /* AICCFIXFREQ2[7:0] 00000000 */
+	0x002b, /* AICCFIXFREQ1[23:16] 00000000 */
+	0x002c, /* AICCFIXFREQ1[15:8] 00000000 */
+	0x002d, /* AICCFIXFREQ1[7:0] 00000000 */
+	0x002e, /* AICCFIXFREQ0[23:16] 00000000 */
+	0x002f, /* AICCFIXFREQ0[15:8] 00000000 */
+	0x0030, /* AICCFIXFREQ0[7:0] 00000000 */
+	0x0031, /* 1'b0 1'b1 1'b0 1'b0 x DAGC1STER */
+	0x0032, /* DAGC1STEN DAGC1STER */
+	0x0033, /* DAGC1STREF[15:8] 00001010 */
+	0x0034, /* DAGC1STREF[7:0] 11100100 */
+	0x0035, /* DAGC2NDE */
+	0x0036, /* DAGC2NDREF[15:8] 00001010 */
+	0x0037, /* DAGC2NDREF[7:0] 10000000 */
+	0x0038, /* DAGC2NDLOCKDETRNGSEL[1:0] */
+	0x003d, /* 1'b1 SAMGEARS */
+	0x0040, /* SAMLFGMA */
+	0x0041, /* SAMLFBWM */
+	0x0044, /* 1'b1 CRGEARSHE */
+	0x0045, /* CRLFGMAN */
+	0x0046, /* CFLFBWMA */
+	0x0047, /* CRLFGMAN */
+	0x0048, /* x x x x CRLFGSTEP_VS[3:0] xxxx1001 */
+	0x0049, /* CRLFBWMA */
+	0x004a, /* CRLFBWMA */
+	0x0050, /* 1'b0 1'b1 1'b1 1'b0 MSECALCDA */
+	0x0070, /* TPOUTEN TPIFEN TPCLKOUTE */
+	0x0071, /* TPSENB TPSSOPBITE */
+	0x0073, /* TP47HINS x x CHBERINT PERMODE[1:0] PERINT[1:0] 1xx11100 */
+	0x0075, /* x x x x x IQSWAPCTRL[2:0] xxxxx000 */
+	0x0076, /* NBERCON NBERST NBERPOL NBERWSYN */
+	0x0077, /* x NBERLOSTTH[2:0] NBERACQTH[3:0] x0000000 */
+	0x0078, /* NBERPOLY[31:24] 00000000 */
+	0x0079, /* NBERPOLY[23:16] 00000000 */
+	0x007a, /* NBERPOLY[15:8] 00000000 */
+	0x007b, /* NBERPOLY[7:0] 00000000 */
+	0x007c, /* NBERPED[31:24] 00000000 */
+	0x007d, /* NBERPED[23:16] 00000000 */
+	0x007e, /* NBERPED[15:8] 00000000 */
+	0x007f, /* NBERPED[7:0] 00000000 */
+	0x0080, /* x AGCLOCK DAGCLOCK SYSLOCK x x NEVERLOCK[1:0] */
+	0x0085, /* SPECINVST */
+	0x0088, /* SYSLOCKTIME[15:8] */
+	0x0089, /* SYSLOCKTIME[7:0] */
+	0x008c, /* FECLOCKTIME[15:8] */
+	0x008d, /* FECLOCKTIME[7:0] */
+	0x008e, /* AGCACCOUT[15:8] */
+	0x008f, /* AGCACCOUT[7:0] */
+	0x0090, /* AICCREJSTATUS[3:0] AICCREJBUSY[3:0] */
+	0x0091, /* AICCVSYNC */
+	0x009c, /* CARRFREQOFFSET[15:8] */
+	0x009d, /* CARRFREQOFFSET[7:0] */
+	0x00a1, /* SAMFREQOFFSET[23:16] */
+	0x00a2, /* SAMFREQOFFSET[15:8] */
+	0x00a3, /* SAMFREQOFFSET[7:0] */
+	0x00a6, /* SYNCLOCK SYNCLOCKH */
+#if 0 /* covered elsewhere */
+	0x00e8, /* CONSTPWR[15:8] */
+	0x00e9, /* CONSTPWR[7:0] */
+	0x00ea, /* BMSE[15:8] */
+	0x00eb, /* BMSE[7:0] */
+	0x00ec, /* MSE[15:8] */
+	0x00ed, /* MSE[7:0] */
+	0x00ee, /* CONSTI[7:0] */
+	0x00ef, /* CONSTQ[7:0] */
+#endif
+	0x00f4, /* TPIFTPERRCNT[7:0] */
+	0x00f5, /* TPCORREC */
+	0x00f6, /* VBBER[15:8] */
+	0x00f7, /* VBBER[7:0] */
+	0x00f8, /* VABER[15:8] */
+	0x00f9, /* VABER[7:0] */
+	0x00fa, /* TPERRCNT[7:0] */
+	0x00fb, /* NBERLOCK x x x x x x x */
+	0x00fc, /* NBERVALUE[31:24] */
+	0x00fd, /* NBERVALUE[23:16] */
+	0x00fe, /* NBERVALUE[15:8] */
+	0x00ff, /* NBERVALUE[7:0] */
+	0x1000, /* 1'b0 WODAGCOU */
+	0x1005, /* x x 1'b1 1'b1 x SRD_Q_QM */
+	0x1009, /* SRDWAITTIME[7:0] (10msec) 00100011 */
+	0x100a, /* SRDWAITTIME_CQS[7:0] (msec) 01100100 */
+	0x101a, /* x 1'b1 1'b0 1'b0 x QMDQAMMODE[2:0] x100x010 */
+	0x1036, /* 1'b0 1'b1 1'b0 1'b0 SAMGSEND_CQS[3:0] 01001110 */
+	0x103c, /* SAMGSAUTOSTL_V[3:0] SAMGSAUTOEDL_V[3:0] 01000110 */
+	0x103d, /* 1'b1 1'b1 SAMCNORMBP_V[1:0] 1'b0 1'b0 SAMMODESEL_V[1:0] 11100001 */
+	0x103f, /* SAMZTEDSE */
+	0x105d, /* EQSTATUSE */
+	0x105f, /* x PMAPG2_V[2:0] x DMAPG2_V[2:0] x001x011 */
+	0x1060, /* 1'b1 EQSTATUSE */
+	0x1061, /* CRMAPBWSTL_V[3:0] CRMAPBWEDL_V[3:0] 00000100 */
+	0x1065, /* 1'b0 x CRMODE_V[1:0] 1'b1 x 1'b1 x 0x111x1x */
+	0x1066, /* 1'b0 1'b0 1'b1 1'b0 1'b1 PNBOOSTSE */
+	0x1068, /* CREPHNGAIN2_V[3:0] CREPHNPBW_V[3:0] 10010001 */
+	0x106e, /* x x x x x CREPHNEN_ */
+	0x106f, /* CREPHNTH_V[7:0] 00010101 */
+	0x1072, /* CRSWEEPN */
+	0x1073, /* CRPGAIN_V[3:0] x x 1'b1 1'b1 1001xx11 */
+	0x1074, /* CRPBW_V[3:0] x x 1'b1 1'b1 0001xx11 */
+	0x1080, /* DAFTSTATUS[1:0] x x x x x x */
+	0x1081, /* SRDSTATUS[1:0] x x x x x SRDLOCK */
+	0x10a9, /* EQSTATUS_CQS[1:0] x x x x x x */
+	0x10b7, /* EQSTATUS_V[1:0] x x x x x x */
+#if 0 /* SMART_ANT */
+	0x1f00, /* MODEDETE */
+	0x1f01, /* x x x x x x x SFNRST xxxxxxx0 */
+	0x1f03, /* NUMOFANT[7:0] 10000000 */
+	0x1f04, /* x SELMASK[6:0] x0000000 */
+	0x1f05, /* x SETMASK[6:0] x0000000 */
+	0x1f06, /* x TXDATA[6:0] x0000000 */
+	0x1f07, /* x CHNUMBER[6:0] x0000000 */
+	0x1f09, /* AGCTIME[23:16] 10011000 */
+	0x1f0a, /* AGCTIME[15:8] 10010110 */
+	0x1f0b, /* AGCTIME[7:0] 10000000 */
+	0x1f0c, /* ANTTIME[31:24] 00000000 */
+	0x1f0d, /* ANTTIME[23:16] 00000011 */
+	0x1f0e, /* ANTTIME[15:8] 10010000 */
+	0x1f0f, /* ANTTIME[7:0] 10010000 */
+	0x1f11, /* SYNCTIME[23:16] 10011000 */
+	0x1f12, /* SYNCTIME[15:8] 10010110 */
+	0x1f13, /* SYNCTIME[7:0] 10000000 */
+	0x1f14, /* SNRTIME[31:24] 00000001 */
+	0x1f15, /* SNRTIME[23:16] 01111101 */
+	0x1f16, /* SNRTIME[15:8] 01111000 */
+	0x1f17, /* SNRTIME[7:0] 01000000 */
+	0x1f19, /* FECTIME[23:16] 00000000 */
+	0x1f1a, /* FECTIME[15:8] 01110010 */
+	0x1f1b, /* FECTIME[7:0] 01110000 */
+	0x1f1d, /* FECTHD[7:0] 00000011 */
+	0x1f1f, /* SNRTHD[23:16] 00001000 */
+	0x1f20, /* SNRTHD[15:8] 01111111 */
+	0x1f21, /* SNRTHD[7:0] 10000101 */
+	0x1f80, /* IRQFLG x x SFSDRFLG MODEBFLG SAVEFLG SCANFLG TRACKFLG */
+	0x1f81, /* x SYNCCON SNRCON FECCON x STDBUSY SYNCRST AGCFZCO */
+	0x1f82, /* x x x SCANOPCD[4:0] */
+	0x1f83, /* x x x x MAINOPCD[3:0] */
+	0x1f84, /* x x RXDATA[13:8] */
+	0x1f85, /* RXDATA[7:0] */
+	0x1f86, /* x x SDTDATA[13:8] */
+	0x1f87, /* SDTDATA[7:0] */
+	0x1f89, /* ANTSNR[23:16] */
+	0x1f8a, /* ANTSNR[15:8] */
+	0x1f8b, /* ANTSNR[7:0] */
+	0x1f8c, /* x x x x ANTFEC[13:8] */
+	0x1f8d, /* ANTFEC[7:0] */
+	0x1f8e, /* MAXCNT[7:0] */
+	0x1f8f, /* SCANCNT[7:0] */
+	0x1f91, /* MAXPW[23:16] */
+	0x1f92, /* MAXPW[15:8] */
+	0x1f93, /* MAXPW[7:0] */
+	0x1f95, /* CURPWMSE[23:16] */
+	0x1f96, /* CURPWMSE[15:8] */
+	0x1f97, /* CURPWMSE[7:0] */
+#endif /* SMART_ANT */
+	0x211f, /* 1'b1 1'b1 1'b1 CIRQEN x x 1'b0 1'b0 1111xx00 */
+	0x212a, /* EQAUTOST */
+	0x2122, /* CHFAST[7:0] 01100000 */
+	0x212b, /* FFFSTEP_V[3:0] x FBFSTEP_V[2:0] 0001x001 */
+	0x212c, /* PHDEROTBWSEL[3:0] 1'b1 1'b1 1'b1 1'b0 10001110 */
+	0x212d, /* 1'b1 1'b1 1'b1 1'b1 x x TPIFLOCKS */
+	0x2135, /* DYNTRACKFDEQ[3:0] x 1'b0 1'b0 1'b0 1010x000 */
+	0x2141, /* TRMODE[1:0] 1'b1 1'b1 1'b0 1'b1 1'b1 1'b1 01110111 */
+	0x2162, /* AICCCTRLE */
+	0x2173, /* PHNCNFCNT[7:0] 00000100 */
+	0x2179, /* 1'b0 1'b0 1'b0 1'b1 x BADSINGLEDYNTRACKFBF[2:0] 0001x001 */
+	0x217a, /* 1'b0 1'b0 1'b0 1'b1 x BADSLOWSINGLEDYNTRACKFBF[2:0] 0001x001 */
+	0x217e, /* CNFCNTTPIF[7:0] 00001000 */
+	0x217f, /* TPERRCNTTPIF[7:0] 00000001 */
+	0x2180, /* x x x x x x FBDLYCIR[9:8] */
+	0x2181, /* FBDLYCIR[7:0] */
+	0x2185, /* MAXPWRMAIN[7:0] */
+	0x2191, /* NCOMBDET x x x x x x x */
+	0x2199, /* x MAINSTRON */
+	0x219a, /* FFFEQSTEPOUT_V[3:0] FBFSTEPOUT_V[2:0] */
+	0x21a1, /* x x SNRREF[5:0] */
+	0x2845, /* 1'b0 1'b1 x x FFFSTEP_CQS[1:0] FFFCENTERTAP[1:0] 01xx1110 */
+	0x2846, /* 1'b0 x 1'b0 1'b1 FBFSTEP_CQS[1:0] 1'b1 1'b0 0x011110 */
+	0x2847, /* ENNOSIGDE */
+	0x2849, /* 1'b1 1'b1 NOUSENOSI */
+	0x284a, /* EQINITWAITTIME[7:0] 01100100 */
+	0x3000, /* 1'b1 1'b1 1'b1 x x x 1'b0 RPTRSTM */
+	0x3001, /* RPTRSTWAITTIME[7:0] (100msec) 00110010 */
+	0x3031, /* FRAMELOC */
+	0x3032, /* 1'b1 1'b0 1'b0 1'b0 x x FRAMELOCKMODE_CQS[1:0] 1000xx11 */
+	0x30a9, /* VDLOCK_Q FRAMELOCK */
+	0x30aa, /* MPEGLOCK */
+};
+
+#define numDumpRegs (sizeof(regtab)/sizeof(regtab[0]))
+static u8 regval1[numDumpRegs] = {0, };
+static u8 regval2[numDumpRegs] = {0, };
+
+static void lgdt3306a_DumpAllRegs(struct lgdt3306a_state *state)
+{
+		memset(regval2, 0xff, sizeof(regval2));
+		lgdt3306a_DumpRegs(state);
+}
+
+static void lgdt3306a_DumpRegs(struct lgdt3306a_state *state)
+{
+	int i;
+	int sav_debug = debug;
+
+	if ((debug & DBG_DUMP) == 0)
+		return;
+	debug &= ~DBG_REG; /* suppress DBG_REG during reg dump */
+
+	lg_debug("\n");
+
+	for (i = 0; i < numDumpRegs; i++) {
+		lgdt3306a_read_reg(state, regtab[i], &regval1[i]);
+		if (regval1[i] != regval2[i]) {
+			lg_debug(" %04X = %02X\n", regtab[i], regval1[i]);
+				 regval2[i] = regval1[i];
+		}
+	}
+	debug = sav_debug;
+}
+#endif /* DBG_DUMP */
+
+
+
+static struct dvb_frontend_ops lgdt3306a_ops = {
+	.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
+	.info = {
+		.name = "LG Electronics LGDT3306A VSB/QAM Frontend",
+		.frequency_min      = 54000000,
+		.frequency_max      = 858000000,
+		.frequency_stepsize = 62500,
+		.caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+	},
+	.i2c_gate_ctrl        = lgdt3306a_i2c_gate_ctrl,
+	.init                 = lgdt3306a_init,
+	.sleep                = lgdt3306a_fe_sleep,
+	/* if this is set, it overrides the default swzigzag */
+	.tune                 = lgdt3306a_tune,
+	.set_frontend         = lgdt3306a_set_parameters,
+	.get_frontend         = lgdt3306a_get_frontend,
+	.get_frontend_algo    = lgdt3306a_get_frontend_algo,
+	.get_tune_settings    = lgdt3306a_get_tune_settings,
+	.read_status          = lgdt3306a_read_status,
+	.read_ber             = lgdt3306a_read_ber,
+	.read_signal_strength = lgdt3306a_read_signal_strength,
+	.read_snr             = lgdt3306a_read_snr,
+	.read_ucblocks        = lgdt3306a_read_ucblocks,
+	.release              = lgdt3306a_release,
+	.ts_bus_ctrl          = lgdt3306a_ts_bus_ctrl,
+	.search               = lgdt3306a_search,
+};
+
+MODULE_DESCRIPTION("LG Electronics LGDT3306A ATSC/QAM-B Demodulator Driver");
+MODULE_AUTHOR("Fred Richter <frichter@hauppauge.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.2");
diff --git a/drivers/media/dvb-frontends/lgdt3306a.h b/drivers/media/dvb-frontends/lgdt3306a.h
new file mode 100644
index 0000000..0adfa7b
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgdt3306a.h
@@ -0,0 +1,74 @@
+/*
+ *    Support for LGDT3306A - 8VSB/QAM-B
+ *
+ *    Copyright (C) 2013,2014 Fred Richter <frichter@hauppauge.com>
+ *      based on lgdt3305.[ch] by Michael Krufky
+ *
+ *    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.
+ */
+
+#ifndef _LGDT3306A_H_
+#define _LGDT3306A_H_
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+
+enum lgdt3306a_mpeg_mode {
+	LGDT3306A_MPEG_PARALLEL = 0,
+	LGDT3306A_MPEG_SERIAL = 1,
+};
+
+enum lgdt3306a_tp_clock_edge {
+	LGDT3306A_TPCLK_RISING_EDGE = 0,
+	LGDT3306A_TPCLK_FALLING_EDGE = 1,
+};
+
+enum lgdt3306a_tp_valid_polarity {
+	LGDT3306A_TP_VALID_LOW = 0,
+	LGDT3306A_TP_VALID_HIGH = 1,
+};
+
+struct lgdt3306a_config {
+	u8 i2c_addr;
+
+	/* user defined IF frequency in KHz */
+	u16 qam_if_khz;
+	u16 vsb_if_khz;
+
+	/* disable i2c repeater - 0:repeater enabled 1:repeater disabled */
+	unsigned int deny_i2c_rptr:1;
+
+	/* spectral inversion - 0:disabled 1:enabled */
+	unsigned int spectral_inversion:1;
+
+	enum lgdt3306a_mpeg_mode mpeg_mode;
+	enum lgdt3306a_tp_clock_edge tpclk_edge;
+	enum lgdt3306a_tp_valid_polarity tpvalid_polarity;
+
+	/* demod clock freq in MHz; 24 or 25 supported */
+	int  xtalMHz;
+};
+
+#if IS_REACHABLE(CPTCFG_DVB_LGDT3306A)
+struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config,
+				      struct i2c_adapter *i2c_adap);
+#else
+static inline
+struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config,
+				      struct i2c_adapter *i2c_adap)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CPTCFG_DVB_LGDT3306A */
+
+#endif /* _LGDT3306A_H_ */
diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c
index e046622..2e1a618 100644
--- a/drivers/media/dvb-frontends/lgdt330x.c
+++ b/drivers/media/dvb-frontends/lgdt330x.c
@@ -823,9 +823,3 @@
 MODULE_LICENSE("GPL");
 
 EXPORT_SYMBOL(lgdt330x_attach);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb-frontends/lgdt330x.h b/drivers/media/dvb-frontends/lgdt330x.h
index ac71f93..9d89d56 100644
--- a/drivers/media/dvb-frontends/lgdt330x.h
+++ b/drivers/media/dvb-frontends/lgdt330x.h
@@ -52,7 +52,7 @@
 	int clock_polarity_flip;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_LGDT330X)
+#if IS_REACHABLE(CPTCFG_DVB_LGDT330X)
 extern struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config,
 					    struct i2c_adapter* i2c);
 #else
@@ -65,9 +65,3 @@
 #endif // CPTCFG_DVB_LGDT330X
 
 #endif /* LGDT330X_H */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb-frontends/lgdt330x_priv.h b/drivers/media/dvb-frontends/lgdt330x_priv.h
index 38c7669..1922f09 100644
--- a/drivers/media/dvb-frontends/lgdt330x_priv.h
+++ b/drivers/media/dvb-frontends/lgdt330x_priv.h
@@ -69,9 +69,3 @@
 };
 
 #endif /* _LGDT330X_PRIV_ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb-frontends/lgs8gl5.h b/drivers/media/dvb-frontends/lgs8gl5.h
index 75bbc5c..ceda679 100644
--- a/drivers/media/dvb-frontends/lgs8gl5.h
+++ b/drivers/media/dvb-frontends/lgs8gl5.h
@@ -31,7 +31,7 @@
 	u8 demod_address;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_LGS8GL5)
+#if IS_REACHABLE(CPTCFG_DVB_LGS8GL5)
 extern struct dvb_frontend *lgs8gl5_attach(
 	const struct lgs8gl5_config *config, struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/lgs8gxx.h b/drivers/media/dvb-frontends/lgs8gxx.h
index e474951..bb322b6 100644
--- a/drivers/media/dvb-frontends/lgs8gxx.h
+++ b/drivers/media/dvb-frontends/lgs8gxx.h
@@ -80,7 +80,7 @@
 	u8 tuner_address;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_LGS8GXX)
+#if IS_REACHABLE(CPTCFG_DVB_LGS8GXX)
 extern struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
 					   struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/lnbh24.h b/drivers/media/dvb-frontends/lnbh24.h
index 811952c..056e258 100644
--- a/drivers/media/dvb-frontends/lnbh24.h
+++ b/drivers/media/dvb-frontends/lnbh24.h
@@ -37,7 +37,7 @@
 
 #include <linux/dvb/frontend.h>
 
-#if IS_ENABLED(CPTCFG_DVB_LNBP21)
+#if IS_REACHABLE(CPTCFG_DVB_LNBP21)
 /* override_set and override_clear control which
    system register bits (above) to always set & clear */
 extern struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/dvb-frontends/lnbp21.h b/drivers/media/dvb-frontends/lnbp21.h
index f3fd30e..1b85d26 100644
--- a/drivers/media/dvb-frontends/lnbp21.h
+++ b/drivers/media/dvb-frontends/lnbp21.h
@@ -57,7 +57,7 @@
 
 #include <linux/dvb/frontend.h>
 
-#if IS_ENABLED(CPTCFG_DVB_LNBP21)
+#if IS_REACHABLE(CPTCFG_DVB_LNBP21)
 /* override_set and override_clear control which
  system register bits (above) to always set & clear */
 extern struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/dvb-frontends/lnbp22.h b/drivers/media/dvb-frontends/lnbp22.h
index e045ec4..305859a 100644
--- a/drivers/media/dvb-frontends/lnbp22.h
+++ b/drivers/media/dvb-frontends/lnbp22.h
@@ -39,7 +39,7 @@
 
 #include <linux/dvb/frontend.h>
 
-#if IS_ENABLED(CPTCFG_DVB_LNBP22)
+#if IS_REACHABLE(CPTCFG_DVB_LNBP22)
 /*
  * override_set and override_clear control which system register bits (above)
  * to always set & clear
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index ba4ee0b..d3d928e 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -630,7 +630,7 @@
 	/* request the firmware, this will block and timeout */
 	ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
 	if (ret) {
-		dev_err(&priv->i2c->dev, "%s: firmare file '%s' not found\n",
+		dev_err(&priv->i2c->dev, "%s: firmware file '%s' not found\n",
 				KBUILD_MODNAME, fw_file);
 		goto err;
 	}
diff --git a/drivers/media/dvb-frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h
index 8256fd0..fc2061d 100644
--- a/drivers/media/dvb-frontends/m88rs2000.h
+++ b/drivers/media/dvb-frontends/m88rs2000.h
@@ -41,7 +41,7 @@
 	CALL_IS_READ,
 };
 
-#if IS_ENABLED(CPTCFG_DVB_M88RS2000)
+#if IS_REACHABLE(CPTCFG_DVB_M88RS2000)
 extern struct dvb_frontend *m88rs2000_attach(
 	const struct m88rs2000_config *config, struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/mb86a16.h b/drivers/media/dvb-frontends/mb86a16.h
index 3cd121e..610dbf4 100644
--- a/drivers/media/dvb-frontends/mb86a16.h
+++ b/drivers/media/dvb-frontends/mb86a16.h
@@ -33,7 +33,7 @@
 
 
 
-#if IS_ENABLED(CPTCFG_DVB_MB86A16)
+#if IS_REACHABLE(CPTCFG_DVB_MB86A16)
 
 extern struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config,
 					   struct i2c_adapter *i2c_adap);
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index e6f165a..8f54c39 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -22,10 +22,6 @@
 
 #define NUM_LAYERS 3
 
-static int debug = 1;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
-
 enum mb86a20s_bandwidth {
 	MB86A20S_13SEG = 0,
 	MB86A20S_13SEG_PARTIAL = 1,
diff --git a/drivers/media/dvb-frontends/mb86a20s.h b/drivers/media/dvb-frontends/mb86a20s.h
index 5200d15..c7d74ff 100644
--- a/drivers/media/dvb-frontends/mb86a20s.h
+++ b/drivers/media/dvb-frontends/mb86a20s.h
@@ -34,7 +34,7 @@
 	bool	is_serial;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_MB86A20S)
+#if IS_REACHABLE(CPTCFG_DVB_MB86A20S)
 extern struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config,
 					   struct i2c_adapter *i2c);
 extern struct i2c_adapter *mb86a20s_get_tuner_i2c_adapter(struct dvb_frontend *);
diff --git a/drivers/media/dvb-frontends/mn88472.h b/drivers/media/dvb-frontends/mn88472.h
index da4558b..095294d 100644
--- a/drivers/media/dvb-frontends/mn88472.h
+++ b/drivers/media/dvb-frontends/mn88472.h
@@ -19,6 +19,16 @@
 
 #include <linux/dvb/frontend.h>
 
+enum ts_clock {
+	VARIABLE_TS_CLOCK,
+	FIXED_TS_CLOCK,
+};
+
+enum ts_mode {
+	SERIAL_TS_MODE,
+	PARALLEL_TS_MODE,
+};
+
 struct mn88472_config {
 	/*
 	 * Max num of bytes given I2C adapter could write at once.
@@ -33,6 +43,14 @@
 	 * DVB frontend.
 	 */
 	struct dvb_frontend **fe;
+
+	/*
+	 * Xtal frequency.
+	 * Hz
+	 */
+	u32 xtal;
+	int ts_mode;
+	int ts_clock;
 };
 
 #endif
diff --git a/drivers/media/dvb-frontends/mn88473.h b/drivers/media/dvb-frontends/mn88473.h
index a373ec9..c717ebe 100644
--- a/drivers/media/dvb-frontends/mn88473.h
+++ b/drivers/media/dvb-frontends/mn88473.h
@@ -33,6 +33,12 @@
 	 * DVB frontend.
 	 */
 	struct dvb_frontend **fe;
+
+	/*
+	 * Xtal frequency.
+	 * Hz
+	 */
+	u32 xtal;
 };
 
 #endif
diff --git a/drivers/media/dvb-frontends/mt312.h b/drivers/media/dvb-frontends/mt312.h
index 23a53e8..2b24dca 100644
--- a/drivers/media/dvb-frontends/mt312.h
+++ b/drivers/media/dvb-frontends/mt312.h
@@ -36,7 +36,7 @@
 	unsigned int voltage_inverted:1;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_MT312)
+#if IS_REACHABLE(CPTCFG_DVB_MT312)
 struct dvb_frontend *mt312_attach(const struct mt312_config *config,
 					struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/mt352.h b/drivers/media/dvb-frontends/mt352.h
index 8901a10..c2595aa 100644
--- a/drivers/media/dvb-frontends/mt352.h
+++ b/drivers/media/dvb-frontends/mt352.h
@@ -51,7 +51,7 @@
 	int (*demod_init)(struct dvb_frontend* fe);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_MT352)
+#if IS_REACHABLE(CPTCFG_DVB_MT352)
 extern struct dvb_frontend* mt352_attach(const struct mt352_config* config,
 					 struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/nxt200x.h b/drivers/media/dvb-frontends/nxt200x.h
index cecc93a..2b69e92 100644
--- a/drivers/media/dvb-frontends/nxt200x.h
+++ b/drivers/media/dvb-frontends/nxt200x.h
@@ -42,7 +42,7 @@
 	int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_NXT200X)
+#if IS_REACHABLE(CPTCFG_DVB_NXT200X)
 extern struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
 					   struct i2c_adapter* i2c);
 #else
@@ -55,9 +55,3 @@
 #endif // CPTCFG_DVB_NXT200X
 
 #endif /* NXT200X_H */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb-frontends/nxt6000.h b/drivers/media/dvb-frontends/nxt6000.h
index d573743..3ff027f 100644
--- a/drivers/media/dvb-frontends/nxt6000.h
+++ b/drivers/media/dvb-frontends/nxt6000.h
@@ -33,7 +33,7 @@
 	u8 clock_inversion:1;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_NXT6000)
+#if IS_REACHABLE(CPTCFG_DVB_NXT6000)
 extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
 					   struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c
index 5ef9218..cbbd259 100644
--- a/drivers/media/dvb-frontends/or51132.c
+++ b/drivers/media/dvb-frontends/or51132.c
@@ -623,9 +623,3 @@
 MODULE_LICENSE("GPL");
 
 EXPORT_SYMBOL(or51132_attach);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb-frontends/or51132.h b/drivers/media/dvb-frontends/or51132.h
index 4bd7e5d..08788c6 100644
--- a/drivers/media/dvb-frontends/or51132.h
+++ b/drivers/media/dvb-frontends/or51132.h
@@ -34,7 +34,7 @@
 	int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_OR51132)
+#if IS_REACHABLE(CPTCFG_DVB_OR51132)
 extern struct dvb_frontend* or51132_attach(const struct or51132_config* config,
 					   struct i2c_adapter* i2c);
 #else
@@ -47,9 +47,3 @@
 #endif // CPTCFG_DVB_OR51132
 
 #endif // OR51132_H
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb-frontends/or51211.h b/drivers/media/dvb-frontends/or51211.h
index 4ba07df..e5b5460 100644
--- a/drivers/media/dvb-frontends/or51211.h
+++ b/drivers/media/dvb-frontends/or51211.h
@@ -37,7 +37,7 @@
 	void (*sleep)(struct dvb_frontend * fe);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_OR51211)
+#if IS_REACHABLE(CPTCFG_DVB_OR51211)
 extern struct dvb_frontend* or51211_attach(const struct or51211_config* config,
 					   struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c
index 50e8b63..e1b8df6 100644
--- a/drivers/media/dvb-frontends/rtl2830.c
+++ b/drivers/media/dvb-frontends/rtl2830.c
@@ -13,261 +13,154 @@
  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *    GNU General Public License for more details.
  *
- *    You should have received a copy of the GNU General Public License along
- *    with this program; if not, write to the Free Software Foundation, Inc.,
- *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-
-/*
- * Driver implements own I2C-adapter for tuner I2C access. That's since chip
- * have unusual I2C-gate control which closes gate automatically after each
- * I2C transfer. Using own I2C adapter we can workaround that.
  */
 
 #include "rtl2830_priv.h"
 
-/* Max transfer size done by I2C transfer functions */
-#define MAX_XFER_SIZE  64
-
-/* write multiple hardware registers */
-static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, const u8 *val, int len)
+/* Our regmap is bypassing I2C adapter lock, thus we do it! */
+static int rtl2830_bulk_write(struct i2c_client *client, unsigned int reg,
+			      const void *val, size_t val_count)
 {
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
 	int ret;
-	u8 buf[MAX_XFER_SIZE];
-	struct i2c_msg msg[1] = {
-		{
-			.addr = priv->cfg.i2c_addr,
-			.flags = 0,
-			.len = 1 + len,
-			.buf = buf,
-		}
-	};
 
-	if (1 + len > sizeof(buf)) {
-		dev_warn(&priv->i2c->dev,
-			 "%s: i2c wr reg=%04x: len=%d is too big!\n",
-			 KBUILD_MODNAME, reg, len);
-		return -EINVAL;
-	}
-
-	buf[0] = reg;
-	memcpy(&buf[1], val, len);
-
-	ret = i2c_transfer(priv->i2c, msg, 1);
-	if (ret == 1) {
-		ret = 0;
-	} else {
-		dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
-				"len=%d\n", KBUILD_MODNAME, ret, reg, len);
-		ret = -EREMOTEIO;
-	}
+	i2c_lock_adapter(client->adapter);
+	ret = regmap_bulk_write(dev->regmap, reg, val, val_count);
+	i2c_unlock_adapter(client->adapter);
 	return ret;
 }
 
-/* read multiple hardware registers */
-static int rtl2830_rd(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
+static int rtl2830_update_bits(struct i2c_client *client, unsigned int reg,
+			       unsigned int mask, unsigned int val)
 {
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
 	int ret;
-	struct i2c_msg msg[2] = {
-		{
-			.addr = priv->cfg.i2c_addr,
-			.flags = 0,
-			.len = 1,
-			.buf = &reg,
-		}, {
-			.addr = priv->cfg.i2c_addr,
-			.flags = I2C_M_RD,
-			.len = len,
-			.buf = val,
-		}
-	};
 
-	ret = i2c_transfer(priv->i2c, msg, 2);
-	if (ret == 2) {
-		ret = 0;
-	} else {
-		dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
-				"len=%d\n", KBUILD_MODNAME, ret, reg, len);
-		ret = -EREMOTEIO;
-	}
+	i2c_lock_adapter(client->adapter);
+	ret = regmap_update_bits(dev->regmap, reg, mask, val);
+	i2c_unlock_adapter(client->adapter);
 	return ret;
 }
 
-/* write multiple registers */
-static int rtl2830_wr_regs(struct rtl2830_priv *priv, u16 reg, const u8 *val,
-		int len)
+static int rtl2830_bulk_read(struct i2c_client *client, unsigned int reg,
+			     void *val, size_t val_count)
 {
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
 	int ret;
-	u8 reg2 = (reg >> 0) & 0xff;
-	u8 page = (reg >> 8) & 0xff;
 
-	/* switch bank if needed */
-	if (page != priv->page) {
-		ret = rtl2830_wr(priv, 0x00, &page, 1);
-		if (ret)
-			return ret;
-
-		priv->page = page;
-	}
-
-	return rtl2830_wr(priv, reg2, val, len);
-}
-
-/* read multiple registers */
-static int rtl2830_rd_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
-{
-	int ret;
-	u8 reg2 = (reg >> 0) & 0xff;
-	u8 page = (reg >> 8) & 0xff;
-
-	/* switch bank if needed */
-	if (page != priv->page) {
-		ret = rtl2830_wr(priv, 0x00, &page, 1);
-		if (ret)
-			return ret;
-
-		priv->page = page;
-	}
-
-	return rtl2830_rd(priv, reg2, val, len);
-}
-
-/* read single register */
-static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val)
-{
-	return rtl2830_rd_regs(priv, reg, val, 1);
-}
-
-/* write single register with mask */
-static int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask)
-{
-	int ret;
-	u8 tmp;
-
-	/* no need for read if whole reg is written */
-	if (mask != 0xff) {
-		ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
-		if (ret)
-			return ret;
-
-		val &= mask;
-		tmp &= ~mask;
-		val |= tmp;
-	}
-
-	return rtl2830_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register with mask */
-static int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask)
-{
-	int ret, i;
-	u8 tmp;
-
-	ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
-	if (ret)
-		return ret;
-
-	tmp &= mask;
-
-	/* find position of the first bit */
-	for (i = 0; i < 8; i++) {
-		if ((mask >> i) & 0x01)
-			break;
-	}
-	*val = tmp >> i;
-
-	return 0;
+	i2c_lock_adapter(client->adapter);
+	ret = regmap_bulk_read(dev->regmap, reg, val, val_count);
+	i2c_unlock_adapter(client->adapter);
+	return ret;
 }
 
 static int rtl2830_init(struct dvb_frontend *fe)
 {
-	struct rtl2830_priv *priv = fe->demodulator_priv;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
+	struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
 	int ret, i;
 	struct rtl2830_reg_val_mask tab[] = {
-		{ 0x00d, 0x01, 0x03 },
-		{ 0x00d, 0x10, 0x10 },
-		{ 0x104, 0x00, 0x1e },
-		{ 0x105, 0x80, 0x80 },
-		{ 0x110, 0x02, 0x03 },
-		{ 0x110, 0x08, 0x0c },
-		{ 0x17b, 0x00, 0x40 },
-		{ 0x17d, 0x05, 0x0f },
-		{ 0x17d, 0x50, 0xf0 },
-		{ 0x18c, 0x08, 0x0f },
-		{ 0x18d, 0x00, 0xc0 },
-		{ 0x188, 0x05, 0x0f },
-		{ 0x189, 0x00, 0xfc },
-		{ 0x2d5, 0x02, 0x02 },
-		{ 0x2f1, 0x02, 0x06 },
-		{ 0x2f1, 0x20, 0xf8 },
-		{ 0x16d, 0x00, 0x01 },
-		{ 0x1a6, 0x00, 0x80 },
-		{ 0x106, priv->cfg.vtop, 0x3f },
-		{ 0x107, priv->cfg.krf, 0x3f },
-		{ 0x112, 0x28, 0xff },
-		{ 0x103, priv->cfg.agc_targ_val, 0xff },
-		{ 0x00a, 0x02, 0x07 },
-		{ 0x140, 0x0c, 0x3c },
-		{ 0x140, 0x40, 0xc0 },
-		{ 0x15b, 0x05, 0x07 },
-		{ 0x15b, 0x28, 0x38 },
-		{ 0x15c, 0x05, 0x07 },
-		{ 0x15c, 0x28, 0x38 },
-		{ 0x115, priv->cfg.spec_inv, 0x01 },
-		{ 0x16f, 0x01, 0x07 },
-		{ 0x170, 0x18, 0x38 },
-		{ 0x172, 0x0f, 0x0f },
-		{ 0x173, 0x08, 0x38 },
-		{ 0x175, 0x01, 0x07 },
-		{ 0x176, 0x00, 0xc0 },
+		{0x00d, 0x01, 0x03},
+		{0x00d, 0x10, 0x10},
+		{0x104, 0x00, 0x1e},
+		{0x105, 0x80, 0x80},
+		{0x110, 0x02, 0x03},
+		{0x110, 0x08, 0x0c},
+		{0x17b, 0x00, 0x40},
+		{0x17d, 0x05, 0x0f},
+		{0x17d, 0x50, 0xf0},
+		{0x18c, 0x08, 0x0f},
+		{0x18d, 0x00, 0xc0},
+		{0x188, 0x05, 0x0f},
+		{0x189, 0x00, 0xfc},
+		{0x2d5, 0x02, 0x02},
+		{0x2f1, 0x02, 0x06},
+		{0x2f1, 0x20, 0xf8},
+		{0x16d, 0x00, 0x01},
+		{0x1a6, 0x00, 0x80},
+		{0x106, dev->pdata->vtop, 0x3f},
+		{0x107, dev->pdata->krf, 0x3f},
+		{0x112, 0x28, 0xff},
+		{0x103, dev->pdata->agc_targ_val, 0xff},
+		{0x00a, 0x02, 0x07},
+		{0x140, 0x0c, 0x3c},
+		{0x140, 0x40, 0xc0},
+		{0x15b, 0x05, 0x07},
+		{0x15b, 0x28, 0x38},
+		{0x15c, 0x05, 0x07},
+		{0x15c, 0x28, 0x38},
+		{0x115, dev->pdata->spec_inv, 0x01},
+		{0x16f, 0x01, 0x07},
+		{0x170, 0x18, 0x38},
+		{0x172, 0x0f, 0x0f},
+		{0x173, 0x08, 0x38},
+		{0x175, 0x01, 0x07},
+		{0x176, 0x00, 0xc0},
 	};
 
 	for (i = 0; i < ARRAY_SIZE(tab); i++) {
-		ret = rtl2830_wr_reg_mask(priv, tab[i].reg, tab[i].val,
-			tab[i].mask);
+		ret = rtl2830_update_bits(client, tab[i].reg, tab[i].mask,
+					  tab[i].val);
 		if (ret)
 			goto err;
 	}
 
-	ret = rtl2830_wr_regs(priv, 0x18f, "\x28\x00", 2);
+	ret = rtl2830_bulk_write(client, 0x18f, "\x28\x00", 2);
 	if (ret)
 		goto err;
 
-	ret = rtl2830_wr_regs(priv, 0x195,
-		"\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8);
+	ret = rtl2830_bulk_write(client, 0x195,
+				 "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8);
 	if (ret)
 		goto err;
 
 	/* TODO: spec init */
 
 	/* soft reset */
-	ret = rtl2830_wr_reg_mask(priv, 0x101, 0x04, 0x04);
+	ret = rtl2830_update_bits(client, 0x101, 0x04, 0x04);
 	if (ret)
 		goto err;
 
-	ret = rtl2830_wr_reg_mask(priv, 0x101, 0x00, 0x04);
+	ret = rtl2830_update_bits(client, 0x101, 0x04, 0x00);
 	if (ret)
 		goto err;
 
-	priv->sleeping = false;
+	/* init stats here in order signal app which stats are supported */
+	c->strength.len = 1;
+	c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->cnr.len = 1;
+	c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->post_bit_error.len = 1;
+	c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->post_bit_count.len = 1;
+	c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	/* start statistics polling */
+	schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
+
+	dev->sleeping = false;
 
 	return ret;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int rtl2830_sleep(struct dvb_frontend *fe)
 {
-	struct rtl2830_priv *priv = fe->demodulator_priv;
-	priv->sleeping = true;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
+
+	dev->sleeping = true;
+	/* stop statistics polling */
+	cancel_delayed_work_sync(&dev->stat_work);
+	dev->fe_status = 0;
+
 	return 0;
 }
 
 static int rtl2830_get_tune_settings(struct dvb_frontend *fe,
-	struct dvb_frontend_tune_settings *s)
+				     struct dvb_frontend_tune_settings *s)
 {
 	s->min_delay_ms = 500;
 	s->step_size = fe->ops.info.frequency_stepsize * 2;
@@ -278,11 +171,12 @@
 
 static int rtl2830_set_frontend(struct dvb_frontend *fe)
 {
-	struct rtl2830_priv *priv = fe->demodulator_priv;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret, i;
 	u64 num;
-	u8 buf[3], tmp;
+	u8 buf[3], u8tmp;
 	u32 if_ctl, if_frequency;
 	static const u8 bw_params1[3][34] = {
 		{
@@ -308,9 +202,8 @@
 		{0xae, 0xba, 0xf3, 0x26, 0x66, 0x64}, /* 8 MHz */
 	};
 
-	dev_dbg(&priv->i2c->dev,
-			"%s: frequency=%d bandwidth_hz=%d inversion=%d\n",
-			__func__, c->frequency, c->bandwidth_hz, c->inversion);
+	dev_dbg(&client->dev, "frequency=%u bandwidth_hz=%u inversion=%u\n",
+		c->frequency, c->bandwidth_hz, c->inversion);
 
 	/* program tuner */
 	if (fe->ops.tuner_ops.set_params)
@@ -327,11 +220,12 @@
 		i = 2;
 		break;
 	default:
-		dev_dbg(&priv->i2c->dev, "%s: invalid bandwidth\n", __func__);
+		dev_err(&client->dev, "invalid bandwidth_hz %u\n",
+			c->bandwidth_hz);
 		return -EINVAL;
 	}
 
-	ret = rtl2830_wr_reg_mask(priv, 0x008, i << 1, 0x06);
+	ret = rtl2830_update_bits(client, 0x008, 0x06, i << 1);
 	if (ret)
 		goto err;
 
@@ -340,70 +234,71 @@
 		ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
 	else
 		ret = -EINVAL;
-
-	if (ret < 0)
-		goto err;
-
-	num = if_frequency % priv->cfg.xtal;
-	num *= 0x400000;
-	num = div_u64(num, priv->cfg.xtal);
-	num = -num;
-	if_ctl = num & 0x3fffff;
-	dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d if_ctl=%08x\n",
-			__func__, if_frequency, if_ctl);
-
-	ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */
 	if (ret)
 		goto err;
 
-	buf[0] = tmp << 6;
-	buf[0] |= (if_ctl >> 16) & 0x3f;
+	num = if_frequency % dev->pdata->clk;
+	num *= 0x400000;
+	num = div_u64(num, dev->pdata->clk);
+	num = -num;
+	if_ctl = num & 0x3fffff;
+	dev_dbg(&client->dev, "if_frequency=%d if_ctl=%08x\n",
+		if_frequency, if_ctl);
+
+	buf[0] = (if_ctl >> 16) & 0x3f;
 	buf[1] = (if_ctl >>  8) & 0xff;
 	buf[2] = (if_ctl >>  0) & 0xff;
 
-	ret = rtl2830_wr_regs(priv, 0x119, buf, 3);
+	ret = rtl2830_bulk_read(client, 0x119, &u8tmp, 1);
+	if (ret)
+		goto err;
+
+	buf[0] |= u8tmp & 0xc0;  /* [7:6] */
+
+	ret = rtl2830_bulk_write(client, 0x119, buf, 3);
 	if (ret)
 		goto err;
 
 	/* 1/2 split I2C write */
-	ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17);
+	ret = rtl2830_bulk_write(client, 0x11c, &bw_params1[i][0], 17);
 	if (ret)
 		goto err;
 
 	/* 2/2 split I2C write */
-	ret = rtl2830_wr_regs(priv, 0x12d, &bw_params1[i][17], 17);
+	ret = rtl2830_bulk_write(client, 0x12d, &bw_params1[i][17], 17);
 	if (ret)
 		goto err;
 
-	ret = rtl2830_wr_regs(priv, 0x19d, bw_params2[i], 6);
+	ret = rtl2830_bulk_write(client, 0x19d, bw_params2[i], 6);
 	if (ret)
 		goto err;
 
 	return ret;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int rtl2830_get_frontend(struct dvb_frontend *fe)
 {
-	struct rtl2830_priv *priv = fe->demodulator_priv;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret;
 	u8 buf[3];
 
-	if (priv->sleeping)
+	if (dev->sleeping)
 		return 0;
 
-	ret = rtl2830_rd_regs(priv, 0x33c, buf, 2);
+	ret = rtl2830_bulk_read(client, 0x33c, buf, 2);
 	if (ret)
 		goto err;
 
-	ret = rtl2830_rd_reg(priv, 0x351, &buf[2]);
+	ret = rtl2830_bulk_read(client, 0x351, &buf[2], 1);
 	if (ret)
 		goto err;
 
-	dev_dbg(&priv->i2c->dev, "%s: TPS=%*ph\n", __func__, 3, buf);
+	dev_dbg(&client->dev, "TPS=%*ph\n", 3, buf);
 
 	switch ((buf[0] >> 2) & 3) {
 	case 0:
@@ -493,244 +388,87 @@
 
 	return 0;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status)
 {
-	struct rtl2830_priv *priv = fe->demodulator_priv;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
 	int ret;
-	u8 tmp;
+	u8 u8tmp;
+
 	*status = 0;
 
-	if (priv->sleeping)
+	if (dev->sleeping)
 		return 0;
 
-	ret = rtl2830_rd_reg_mask(priv, 0x351, &tmp, 0x78); /* [6:3] */
+	ret = rtl2830_bulk_read(client, 0x351, &u8tmp, 1);
 	if (ret)
 		goto err;
 
-	if (tmp == 11) {
+	u8tmp = (u8tmp >> 3) & 0x0f; /* [6:3] */
+	if (u8tmp == 11) {
 		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
 			FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
-	} else if (tmp == 10) {
+	} else if (u8tmp == 10) {
 		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
 			FE_HAS_VITERBI;
 	}
 
+	dev->fe_status = *status;
+
 	return ret;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr)
 {
-	struct rtl2830_priv *priv = fe->demodulator_priv;
-	int ret, hierarchy, constellation;
-	u8 buf[2], tmp;
-	u16 tmp16;
-#define CONSTELLATION_NUM 3
-#define HIERARCHY_NUM 4
-	static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
-		{ 70705899, 70705899, 70705899, 70705899 },
-		{ 82433173, 82433173, 87483115, 94445660 },
-		{ 92888734, 92888734, 95487525, 99770748 },
-	};
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 
-	if (priv->sleeping)
-		return 0;
-
-	/* reports SNR in resolution of 0.1 dB */
-
-	ret = rtl2830_rd_reg(priv, 0x33c, &tmp);
-	if (ret)
-		goto err;
-
-	constellation = (tmp >> 2) & 0x03; /* [3:2] */
-	if (constellation > CONSTELLATION_NUM - 1)
-		goto err;
-
-	hierarchy = (tmp >> 4) & 0x07; /* [6:4] */
-	if (hierarchy > HIERARCHY_NUM - 1)
-		goto err;
-
-	ret = rtl2830_rd_regs(priv, 0x40c, buf, 2);
-	if (ret)
-		goto err;
-
-	tmp16 = buf[0] << 8 | buf[1];
-
-	if (tmp16)
-		*snr = (snr_constant[constellation][hierarchy] -
-				intlog10(tmp16)) / ((1 << 24) / 100);
+	if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
+		*snr = div_s64(c->cnr.stat[0].svalue, 100);
 	else
 		*snr = 0;
 
 	return 0;
-err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-	return ret;
 }
 
 static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber)
 {
-	struct rtl2830_priv *priv = fe->demodulator_priv;
-	int ret;
-	u8 buf[2];
+	struct i2c_client *client = fe->demodulator_priv;
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
 
-	if (priv->sleeping)
-		return 0;
-
-	ret = rtl2830_rd_regs(priv, 0x34e, buf, 2);
-	if (ret)
-		goto err;
-
-	*ber = buf[0] << 8 | buf[1];
+	*ber = (dev->post_bit_error - dev->post_bit_error_prev);
+	dev->post_bit_error_prev = dev->post_bit_error;
 
 	return 0;
-err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-	return ret;
 }
 
 static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
 {
 	*ucblocks = 0;
+
 	return 0;
 }
 
 static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
 {
-	struct rtl2830_priv *priv = fe->demodulator_priv;
-	int ret;
-	u8 buf[2];
-	u16 if_agc_raw, if_agc;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 
-	if (priv->sleeping)
-		return 0;
-
-	ret = rtl2830_rd_regs(priv, 0x359, buf, 2);
-	if (ret)
-		goto err;
-
-	if_agc_raw = (buf[0] << 8 | buf[1]) & 0x3fff;
-
-	if (if_agc_raw & (1 << 9))
-		if_agc = -(~(if_agc_raw - 1) & 0x1ff);
+	if (c->strength.stat[0].scale == FE_SCALE_RELATIVE)
+		*strength = c->strength.stat[0].uvalue;
 	else
-		if_agc = if_agc_raw;
-
-	*strength = (u8) (55 - if_agc / 182);
-	*strength |= *strength << 8;
+		*strength = 0;
 
 	return 0;
-err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-	return ret;
 }
 
-static struct dvb_frontend_ops rtl2830_ops;
-
-static u32 rtl2830_tuner_i2c_func(struct i2c_adapter *adapter)
-{
-	return I2C_FUNC_I2C;
-}
-
-static int rtl2830_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
-	struct i2c_msg msg[], int num)
-{
-	struct rtl2830_priv *priv = i2c_get_adapdata(i2c_adap);
-	int ret;
-
-	/* open i2c-gate */
-	ret = rtl2830_wr_reg_mask(priv, 0x101, 0x08, 0x08);
-	if (ret)
-		goto err;
-
-	ret = i2c_transfer(priv->i2c, msg, num);
-	if (ret < 0)
-		dev_warn(&priv->i2c->dev, "%s: tuner i2c failed=%d\n",
-			KBUILD_MODNAME, ret);
-
-	return ret;
-err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-	return ret;
-}
-
-static struct i2c_algorithm rtl2830_tuner_i2c_algo = {
-	.master_xfer   = rtl2830_tuner_i2c_xfer,
-	.functionality = rtl2830_tuner_i2c_func,
-};
-
-struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(struct dvb_frontend *fe)
-{
-	struct rtl2830_priv *priv = fe->demodulator_priv;
-	return &priv->tuner_i2c_adapter;
-}
-EXPORT_SYMBOL(rtl2830_get_tuner_i2c_adapter);
-
-static void rtl2830_release(struct dvb_frontend *fe)
-{
-	struct rtl2830_priv *priv = fe->demodulator_priv;
-
-	i2c_del_adapter(&priv->tuner_i2c_adapter);
-	kfree(priv);
-}
-
-struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg,
-	struct i2c_adapter *i2c)
-{
-	struct rtl2830_priv *priv = NULL;
-	int ret = 0;
-	u8 tmp;
-
-	/* allocate memory for the internal state */
-	priv = kzalloc(sizeof(struct rtl2830_priv), GFP_KERNEL);
-	if (priv == NULL)
-		goto err;
-
-	/* setup the priv */
-	priv->i2c = i2c;
-	memcpy(&priv->cfg, cfg, sizeof(struct rtl2830_config));
-
-	/* check if the demod is there */
-	ret = rtl2830_rd_reg(priv, 0x000, &tmp);
-	if (ret)
-		goto err;
-
-	/* create dvb_frontend */
-	memcpy(&priv->fe.ops, &rtl2830_ops, sizeof(struct dvb_frontend_ops));
-	priv->fe.demodulator_priv = priv;
-
-	/* create tuner i2c adapter */
-	strlcpy(priv->tuner_i2c_adapter.name, "RTL2830 tuner I2C adapter",
-		sizeof(priv->tuner_i2c_adapter.name));
-	priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo;
-	priv->tuner_i2c_adapter.algo_data = NULL;
-	priv->tuner_i2c_adapter.dev.parent = &i2c->dev;
-	i2c_set_adapdata(&priv->tuner_i2c_adapter, priv);
-	if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) {
-		dev_err(&i2c->dev,
-				"%s: tuner i2c bus could not be initialized\n",
-				KBUILD_MODNAME);
-		goto err;
-	}
-
-	priv->sleeping = true;
-
-	return &priv->fe;
-err:
-	dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
-	kfree(priv);
-	return NULL;
-}
-EXPORT_SYMBOL(rtl2830_attach);
-
 static struct dvb_frontend_ops rtl2830_ops = {
-	.delsys = { SYS_DVBT },
+	.delsys = {SYS_DVBT},
 	.info = {
 		.name = "Realtek RTL2830 (DVB-T)",
 		.caps = FE_CAN_FEC_1_2 |
@@ -750,8 +488,6 @@
 			FE_CAN_MUTE_TS
 	},
 
-	.release = rtl2830_release,
-
 	.init = rtl2830_init,
 	.sleep = rtl2830_sleep,
 
@@ -767,6 +503,428 @@
 	.read_signal_strength = rtl2830_read_signal_strength,
 };
 
+static void rtl2830_stat_work(struct work_struct *work)
+{
+	struct rtl2830_dev *dev = container_of(work, struct rtl2830_dev, stat_work.work);
+	struct i2c_client *client = dev->client;
+	struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+	int ret, tmp;
+	u8 u8tmp, buf[2];
+	u16 u16tmp;
+
+	dev_dbg(&client->dev, "\n");
+
+	/* signal strength */
+	if (dev->fe_status & FE_HAS_SIGNAL) {
+		struct {signed int x:14; } s;
+
+		/* read IF AGC */
+		ret = rtl2830_bulk_read(client, 0x359, buf, 2);
+		if (ret)
+			goto err;
+
+		u16tmp = buf[0] << 8 | buf[1] << 0;
+		u16tmp &= 0x3fff; /* [13:0] */
+		tmp = s.x = u16tmp; /* 14-bit bin to 2 complement */
+		u16tmp = clamp_val(-4 * tmp + 32767, 0x0000, 0xffff);
+
+		dev_dbg(&client->dev, "IF AGC=%d\n", tmp);
+
+		c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+		c->strength.stat[0].uvalue = u16tmp;
+	} else {
+		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	}
+
+	/* CNR */
+	if (dev->fe_status & FE_HAS_VITERBI) {
+		unsigned hierarchy, constellation;
+		#define CONSTELLATION_NUM 3
+		#define HIERARCHY_NUM 4
+		static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
+			{70705899, 70705899, 70705899, 70705899},
+			{82433173, 82433173, 87483115, 94445660},
+			{92888734, 92888734, 95487525, 99770748},
+		};
+
+		ret = rtl2830_bulk_read(client, 0x33c, &u8tmp, 1);
+		if (ret)
+			goto err;
+
+		constellation = (u8tmp >> 2) & 0x03; /* [3:2] */
+		if (constellation > CONSTELLATION_NUM - 1)
+			goto err_schedule_delayed_work;
+
+		hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */
+		if (hierarchy > HIERARCHY_NUM - 1)
+			goto err_schedule_delayed_work;
+
+		ret = rtl2830_bulk_read(client, 0x40c, buf, 2);
+		if (ret)
+			goto err;
+
+		u16tmp = buf[0] << 8 | buf[1] << 0;
+		if (u16tmp)
+			tmp = (constant[constellation][hierarchy] -
+			       intlog10(u16tmp)) / ((1 << 24) / 10000);
+		else
+			tmp = 0;
+
+		dev_dbg(&client->dev, "CNR raw=%u\n", u16tmp);
+
+		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+		c->cnr.stat[0].svalue = tmp;
+	} else {
+		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	}
+
+	/* BER */
+	if (dev->fe_status & FE_HAS_LOCK) {
+		ret = rtl2830_bulk_read(client, 0x34e, buf, 2);
+		if (ret)
+			goto err;
+
+		u16tmp = buf[0] << 8 | buf[1] << 0;
+		dev->post_bit_error += u16tmp;
+		dev->post_bit_count += 1000000;
+
+		dev_dbg(&client->dev, "BER errors=%u total=1000000\n", u16tmp);
+
+		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+		c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
+		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+		c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
+	} else {
+		c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	}
+
+err_schedule_delayed_work:
+	schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
+	return;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+}
+
+static int rtl2830_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	int ret;
+	u8 u8tmp;
+
+	dev_dbg(&client->dev, "onoff=%d\n", onoff);
+
+	/* enable / disable PID filter */
+	if (onoff)
+		u8tmp = 0x80;
+	else
+		u8tmp = 0x00;
+
+	ret = rtl2830_update_bits(client, 0x061, 0x80, u8tmp);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int rtl2830_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, int onoff)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
+	int ret;
+	u8 buf[4];
+
+	dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n",
+		index, pid, onoff);
+
+	/* skip invalid PIDs (0x2000) */
+	if (pid > 0x1fff || index > 32)
+		return 0;
+
+	if (onoff)
+		set_bit(index, &dev->filters);
+	else
+		clear_bit(index, &dev->filters);
+
+	/* enable / disable PIDs */
+	buf[0] = (dev->filters >>  0) & 0xff;
+	buf[1] = (dev->filters >>  8) & 0xff;
+	buf[2] = (dev->filters >> 16) & 0xff;
+	buf[3] = (dev->filters >> 24) & 0xff;
+	ret = rtl2830_bulk_write(client, 0x062, buf, 4);
+	if (ret)
+		goto err;
+
+	/* add PID */
+	buf[0] = (pid >> 8) & 0xff;
+	buf[1] = (pid >> 0) & 0xff;
+	ret = rtl2830_bulk_write(client, 0x066 + 2 * index, buf, 2);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+/*
+ * I2C gate/mux/repeater logic
+ * We must use unlocked __i2c_transfer() here (through regmap) because of I2C
+ * adapter lock is already taken by tuner driver.
+ * Gate is closed automatically after single I2C transfer.
+ */
+static int rtl2830_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id)
+{
+	struct i2c_client *client = mux_priv;
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
+	int ret;
+
+	dev_dbg(&client->dev, "\n");
+
+	/* open I2C repeater for 1 transfer, closes automatically */
+	/* XXX: regmap_update_bits() does not lock I2C adapter */
+	ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static struct dvb_frontend *rtl2830_get_dvb_frontend(struct i2c_client *client)
+{
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "\n");
+
+	return &dev->fe;
+}
+
+static struct i2c_adapter *rtl2830_get_i2c_adapter(struct i2c_client *client)
+{
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "\n");
+
+	return dev->adapter;
+}
+
+/*
+ * We implement own I2C access routines for regmap in order to get manual access
+ * to I2C adapter lock, which is needed for I2C mux adapter.
+ */
+static int rtl2830_regmap_read(void *context, const void *reg_buf,
+			       size_t reg_size, void *val_buf, size_t val_size)
+{
+	struct i2c_client *client = context;
+	int ret;
+	struct i2c_msg msg[2] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = reg_size,
+			.buf = (u8 *)reg_buf,
+		}, {
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = val_size,
+			.buf = val_buf,
+		}
+	};
+
+	ret = __i2c_transfer(client->adapter, msg, 2);
+	if (ret != 2) {
+		dev_warn(&client->dev, "i2c reg read failed %d\n", ret);
+		if (ret >= 0)
+			ret = -EREMOTEIO;
+		return ret;
+	}
+	return 0;
+}
+
+static int rtl2830_regmap_write(void *context, const void *data, size_t count)
+{
+	struct i2c_client *client = context;
+	int ret;
+	struct i2c_msg msg[1] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = count,
+			.buf = (u8 *)data,
+		}
+	};
+
+	ret = __i2c_transfer(client->adapter, msg, 1);
+	if (ret != 1) {
+		dev_warn(&client->dev, "i2c reg write failed %d\n", ret);
+		if (ret >= 0)
+			ret = -EREMOTEIO;
+		return ret;
+	}
+	return 0;
+}
+
+static int rtl2830_regmap_gather_write(void *context, const void *reg,
+				       size_t reg_len, const void *val,
+				       size_t val_len)
+{
+	struct i2c_client *client = context;
+	int ret;
+	u8 buf[256];
+	struct i2c_msg msg[1] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = 1 + val_len,
+			.buf = buf,
+		}
+	};
+
+	buf[0] = *(u8 const *)reg;
+	memcpy(&buf[1], val, val_len);
+
+	ret = __i2c_transfer(client->adapter, msg, 1);
+	if (ret != 1) {
+		dev_warn(&client->dev, "i2c reg write failed %d\n", ret);
+		if (ret >= 0)
+			ret = -EREMOTEIO;
+		return ret;
+	}
+	return 0;
+}
+
+static int rtl2830_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct rtl2830_platform_data *pdata = client->dev.platform_data;
+	struct rtl2830_dev *dev;
+	int ret;
+	u8 u8tmp;
+	static const struct regmap_bus regmap_bus = {
+		.read = rtl2830_regmap_read,
+		.write = rtl2830_regmap_write,
+		.gather_write = rtl2830_regmap_gather_write,
+		.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+	};
+	static const struct regmap_range_cfg regmap_range_cfg[] = {
+		{
+			.selector_reg     = 0x00,
+			.selector_mask    = 0xff,
+			.selector_shift   = 0,
+			.window_start     = 0,
+			.window_len       = 0x100,
+			.range_min        = 0 * 0x100,
+			.range_max        = 5 * 0x100,
+		},
+	};
+	static const struct regmap_config regmap_config = {
+		.reg_bits    =  8,
+		.val_bits    =  8,
+		.max_register = 5 * 0x100,
+		.ranges = regmap_range_cfg,
+		.num_ranges = ARRAY_SIZE(regmap_range_cfg),
+	};
+
+	dev_dbg(&client->dev, "\n");
+
+	if (pdata == NULL) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* allocate memory for the internal state */
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	/* setup the state */
+	i2c_set_clientdata(client, dev);
+	dev->client = client;
+	dev->pdata = client->dev.platform_data;
+	dev->sleeping = true;
+	INIT_DELAYED_WORK(&dev->stat_work, rtl2830_stat_work);
+	dev->regmap = regmap_init(&client->dev, &regmap_bus, client,
+				  &regmap_config);
+	if (IS_ERR(dev->regmap)) {
+		ret = PTR_ERR(dev->regmap);
+		goto err_kfree;
+	}
+
+	/* check if the demod is there */
+	ret = rtl2830_bulk_read(client, 0x000, &u8tmp, 1);
+	if (ret)
+		goto err_regmap_exit;
+
+	/* create muxed i2c adapter for tuner */
+	dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
+			client, 0, 0, 0, rtl2830_select, NULL);
+	if (dev->adapter == NULL) {
+		ret = -ENODEV;
+		goto err_regmap_exit;
+	}
+
+	/* create dvb frontend */
+	memcpy(&dev->fe.ops, &rtl2830_ops, sizeof(dev->fe.ops));
+	dev->fe.demodulator_priv = client;
+
+	/* setup callbacks */
+	pdata->get_dvb_frontend = rtl2830_get_dvb_frontend;
+	pdata->get_i2c_adapter = rtl2830_get_i2c_adapter;
+	pdata->pid_filter = rtl2830_pid_filter;
+	pdata->pid_filter_ctrl = rtl2830_pid_filter_ctrl;
+
+	dev_info(&client->dev, "Realtek RTL2830 successfully attached\n");
+
+	return 0;
+err_regmap_exit:
+	regmap_exit(dev->regmap);
+err_kfree:
+	kfree(dev);
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int rtl2830_remove(struct i2c_client *client)
+{
+	struct rtl2830_dev *dev = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "\n");
+
+	i2c_del_mux_adapter(dev->adapter);
+	regmap_exit(dev->regmap);
+	kfree(dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id rtl2830_id_table[] = {
+	{"rtl2830", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rtl2830_id_table);
+
+static struct i2c_driver rtl2830_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "rtl2830",
+	},
+	.probe		= rtl2830_probe,
+	.remove		= rtl2830_remove,
+	.id_table	= rtl2830_id_table,
+};
+
+module_i2c_driver(rtl2830_driver);
+
 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
 MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/rtl2830.h b/drivers/media/dvb-frontends/rtl2830.h
index ec402a6..0cde151 100644
--- a/drivers/media/dvb-frontends/rtl2830.h
+++ b/drivers/media/dvb-frontends/rtl2830.h
@@ -13,78 +13,37 @@
  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *    GNU General Public License for more details.
  *
- *    You should have received a copy of the GNU General Public License along
- *    with this program; if not, write to the Free Software Foundation, Inc.,
- *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #ifndef RTL2830_H
 #define RTL2830_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
-struct rtl2830_config {
-	/*
-	 * Demodulator I2C address.
-	 */
-	u8 i2c_addr;
+/**
+ * struct rtl2830_platform_data - Platform data for the rtl2830 driver
+ * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000).
+ * @spec_inv: Spectrum inversion.
+ * @vtop: AGC take-over point.
+ * @krf: AGC ratio.
+ * @agc_targ_val: AGC.
+ * @get_dvb_frontend: Get DVB frontend.
+ * @get_i2c_adapter: Get I2C adapter.
+ * @pid_filter: Set PID to PID filter.
+ * @pid_filter_ctrl: Control PID filter.
+ */
 
-	/*
-	 * Xtal frequency.
-	 * Hz
-	 * 4000000, 16000000, 25000000, 28800000
-	 */
-	u32 xtal;
-
-	/*
-	 * TS output mode.
-	 */
-	u8 ts_mode;
-
-	/*
-	 * Spectrum inversion.
-	 */
+struct rtl2830_platform_data {
+	u32 clk;
 	bool spec_inv;
-
-	/*
-	 */
 	u8 vtop;
-
-	/*
-	 */
 	u8 krf;
-
-	/*
-	 */
 	u8 agc_targ_val;
+
+	struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+	struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *);
+	int (*pid_filter)(struct dvb_frontend *, u8, u16, int);
+	int (*pid_filter_ctrl)(struct dvb_frontend *, int);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_RTL2830)
-extern struct dvb_frontend *rtl2830_attach(
-	const struct rtl2830_config *config,
-	struct i2c_adapter *i2c
-);
-
-extern struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
-	struct dvb_frontend *fe
-);
-#else
-static inline struct dvb_frontend *rtl2830_attach(
-	const struct rtl2830_config *config,
-	struct i2c_adapter *i2c
-)
-{
-	pr_warn("%s: driver disabled by Kconfig\n", __func__);
-	return NULL;
-}
-
-static inline struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
-	struct dvb_frontend *fe
-)
-{
-	return NULL;
-}
-#endif
-
 #endif /* RTL2830_H */
diff --git a/drivers/media/dvb-frontends/rtl2830_priv.h b/drivers/media/dvb-frontends/rtl2830_priv.h
index fab10ec..d50d537 100644
--- a/drivers/media/dvb-frontends/rtl2830_priv.h
+++ b/drivers/media/dvb-frontends/rtl2830_priv.h
@@ -13,9 +13,6 @@
  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *    GNU General Public License for more details.
  *
- *    You should have received a copy of the GNU General Public License along
- *    with this program; if not, write to the Free Software Foundation, Inc.,
- *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #ifndef RTL2830_PRIV_H
@@ -24,16 +21,23 @@
 #include "dvb_frontend.h"
 #include "dvb_math.h"
 #include "rtl2830.h"
+#include <linux/i2c-mux.h>
+#include <linux/math64.h>
+#include <linux/regmap.h>
 
-struct rtl2830_priv {
-	struct i2c_adapter *i2c;
+struct rtl2830_dev {
+	struct rtl2830_platform_data *pdata;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct i2c_adapter *adapter;
 	struct dvb_frontend fe;
-	struct rtl2830_config cfg;
-	struct i2c_adapter tuner_i2c_adapter;
-
 	bool sleeping;
-
-	u8 page; /* active register page */
+	unsigned long filters;
+	struct delayed_work stat_work;
+	fe_status_t fe_status;
+	u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */
+	u64 post_bit_error;
+	u64 post_bit_count;
 };
 
 struct rtl2830_reg_val_mask {
diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c
index 9026e1a..b400f7b 100644
--- a/drivers/media/dvb-frontends/rtl2832.c
+++ b/drivers/media/dvb-frontends/rtl2832.c
@@ -2,6 +2,7 @@
  * Realtek RTL2832 DVB-T demodulator driver
  *
  * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
+ * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi>
  *
  *	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
@@ -19,280 +20,191 @@
  */
 
 #include "rtl2832_priv.h"
-#include "dvb_math.h"
-#include <linux/bitops.h>
 
-/* Max transfer size done by I2C transfer functions */
-#define MAX_XFER_SIZE  64
 #define REG_MASK(b) (BIT(b + 1) - 1)
 
 static const struct rtl2832_reg_entry registers[] = {
-	[DVBT_SOFT_RST]		= {0x1, 0x1,   2, 2},
-	[DVBT_IIC_REPEAT]	= {0x1, 0x1,   3, 3},
-	[DVBT_TR_WAIT_MIN_8K]	= {0x1, 0x88, 11, 2},
-	[DVBT_RSD_BER_FAIL_VAL]	= {0x1, 0x8f, 15, 0},
-	[DVBT_EN_BK_TRK]	= {0x1, 0xa6,  7, 7},
-	[DVBT_AD_EN_REG]	= {0x0, 0x8,   7, 7},
-	[DVBT_AD_EN_REG1]	= {0x0, 0x8,   6, 6},
-	[DVBT_EN_BBIN]		= {0x1, 0xb1,  0, 0},
-	[DVBT_MGD_THD0]		= {0x1, 0x95,  7, 0},
-	[DVBT_MGD_THD1]		= {0x1, 0x96,  7, 0},
-	[DVBT_MGD_THD2]		= {0x1, 0x97,  7, 0},
-	[DVBT_MGD_THD3]		= {0x1, 0x98,  7, 0},
-	[DVBT_MGD_THD4]		= {0x1, 0x99,  7, 0},
-	[DVBT_MGD_THD5]		= {0x1, 0x9a,  7, 0},
-	[DVBT_MGD_THD6]		= {0x1, 0x9b,  7, 0},
-	[DVBT_MGD_THD7]		= {0x1, 0x9c,  7, 0},
-	[DVBT_EN_CACQ_NOTCH]	= {0x1, 0x61,  4, 4},
-	[DVBT_AD_AV_REF]	= {0x0, 0x9,   6, 0},
-	[DVBT_REG_PI]		= {0x0, 0xa,   2, 0},
-	[DVBT_PIP_ON]		= {0x0, 0x21,  3, 3},
-	[DVBT_SCALE1_B92]	= {0x2, 0x92,  7, 0},
-	[DVBT_SCALE1_B93]	= {0x2, 0x93,  7, 0},
-	[DVBT_SCALE1_BA7]	= {0x2, 0xa7,  7, 0},
-	[DVBT_SCALE1_BA9]	= {0x2, 0xa9,  7, 0},
-	[DVBT_SCALE1_BAA]	= {0x2, 0xaa,  7, 0},
-	[DVBT_SCALE1_BAB]	= {0x2, 0xab,  7, 0},
-	[DVBT_SCALE1_BAC]	= {0x2, 0xac,  7, 0},
-	[DVBT_SCALE1_BB0]	= {0x2, 0xb0,  7, 0},
-	[DVBT_SCALE1_BB1]	= {0x2, 0xb1,  7, 0},
-	[DVBT_KB_P1]		= {0x1, 0x64,  3, 1},
-	[DVBT_KB_P2]		= {0x1, 0x64,  6, 4},
-	[DVBT_KB_P3]		= {0x1, 0x65,  2, 0},
-	[DVBT_OPT_ADC_IQ]	= {0x0, 0x6,   5, 4},
-	[DVBT_AD_AVI]		= {0x0, 0x9,   1, 0},
-	[DVBT_AD_AVQ]		= {0x0, 0x9,   3, 2},
-	[DVBT_K1_CR_STEP12]	= {0x2, 0xad,  9, 4},
-	[DVBT_TRK_KS_P2]	= {0x1, 0x6f,  2, 0},
-	[DVBT_TRK_KS_I2]	= {0x1, 0x70,  5, 3},
-	[DVBT_TR_THD_SET2]	= {0x1, 0x72,  3, 0},
-	[DVBT_TRK_KC_P2]	= {0x1, 0x73,  5, 3},
-	[DVBT_TRK_KC_I2]	= {0x1, 0x75,  2, 0},
-	[DVBT_CR_THD_SET2]	= {0x1, 0x76,  7, 6},
-	[DVBT_PSET_IFFREQ]	= {0x1, 0x19, 21, 0},
-	[DVBT_SPEC_INV]		= {0x1, 0x15,  0, 0},
-	[DVBT_RSAMP_RATIO]	= {0x1, 0x9f, 27, 2},
-	[DVBT_CFREQ_OFF_RATIO]	= {0x1, 0x9d, 23, 4},
-	[DVBT_FSM_STAGE]	= {0x3, 0x51,  6, 3},
-	[DVBT_RX_CONSTEL]	= {0x3, 0x3c,  3, 2},
-	[DVBT_RX_HIER]		= {0x3, 0x3c,  6, 4},
-	[DVBT_RX_C_RATE_LP]	= {0x3, 0x3d,  2, 0},
-	[DVBT_RX_C_RATE_HP]	= {0x3, 0x3d,  5, 3},
-	[DVBT_GI_IDX]		= {0x3, 0x51,  1, 0},
-	[DVBT_FFT_MODE_IDX]	= {0x3, 0x51,  2, 2},
-	[DVBT_RSD_BER_EST]	= {0x3, 0x4e, 15, 0},
-	[DVBT_CE_EST_EVM]	= {0x4, 0xc,  15, 0},
-	[DVBT_RF_AGC_VAL]	= {0x3, 0x5b, 13, 0},
-	[DVBT_IF_AGC_VAL]	= {0x3, 0x59, 13, 0},
-	[DVBT_DAGC_VAL]		= {0x3, 0x5,   7, 0},
-	[DVBT_SFREQ_OFF]	= {0x3, 0x18, 13, 0},
-	[DVBT_CFREQ_OFF]	= {0x3, 0x5f, 17, 0},
-	[DVBT_POLAR_RF_AGC]	= {0x0, 0xe,   1, 1},
-	[DVBT_POLAR_IF_AGC]	= {0x0, 0xe,   0, 0},
-	[DVBT_AAGC_HOLD]	= {0x1, 0x4,   5, 5},
-	[DVBT_EN_RF_AGC]	= {0x1, 0x4,   6, 6},
-	[DVBT_EN_IF_AGC]	= {0x1, 0x4,   7, 7},
-	[DVBT_IF_AGC_MIN]	= {0x1, 0x8,   7, 0},
-	[DVBT_IF_AGC_MAX]	= {0x1, 0x9,   7, 0},
-	[DVBT_RF_AGC_MIN]	= {0x1, 0xa,   7, 0},
-	[DVBT_RF_AGC_MAX]	= {0x1, 0xb,   7, 0},
-	[DVBT_IF_AGC_MAN]	= {0x1, 0xc,   6, 6},
-	[DVBT_IF_AGC_MAN_VAL]	= {0x1, 0xc,  13, 0},
-	[DVBT_RF_AGC_MAN]	= {0x1, 0xe,   6, 6},
-	[DVBT_RF_AGC_MAN_VAL]	= {0x1, 0xe,  13, 0},
-	[DVBT_DAGC_TRG_VAL]	= {0x1, 0x12,  7, 0},
-	[DVBT_AGC_TARG_VAL_0]	= {0x1, 0x2,   0, 0},
-	[DVBT_AGC_TARG_VAL_8_1]	= {0x1, 0x3,   7, 0},
-	[DVBT_AAGC_LOOP_GAIN]	= {0x1, 0xc7,  5, 1},
-	[DVBT_LOOP_GAIN2_3_0]	= {0x1, 0x4,   4, 1},
-	[DVBT_LOOP_GAIN2_4]	= {0x1, 0x5,   7, 7},
-	[DVBT_LOOP_GAIN3]	= {0x1, 0xc8,  4, 0},
-	[DVBT_VTOP1]		= {0x1, 0x6,   5, 0},
-	[DVBT_VTOP2]		= {0x1, 0xc9,  5, 0},
-	[DVBT_VTOP3]		= {0x1, 0xca,  5, 0},
-	[DVBT_KRF1]		= {0x1, 0xcb,  7, 0},
-	[DVBT_KRF2]		= {0x1, 0x7,   7, 0},
-	[DVBT_KRF3]		= {0x1, 0xcd,  7, 0},
-	[DVBT_KRF4]		= {0x1, 0xce,  7, 0},
-	[DVBT_EN_GI_PGA]	= {0x1, 0xe5,  0, 0},
-	[DVBT_THD_LOCK_UP]	= {0x1, 0xd9,  8, 0},
-	[DVBT_THD_LOCK_DW]	= {0x1, 0xdb,  8, 0},
-	[DVBT_THD_UP1]		= {0x1, 0xdd,  7, 0},
-	[DVBT_THD_DW1]		= {0x1, 0xde,  7, 0},
-	[DVBT_INTER_CNT_LEN]	= {0x1, 0xd8,  3, 0},
-	[DVBT_GI_PGA_STATE]	= {0x1, 0xe6,  3, 3},
-	[DVBT_EN_AGC_PGA]	= {0x1, 0xd7,  0, 0},
-	[DVBT_CKOUTPAR]		= {0x1, 0x7b,  5, 5},
-	[DVBT_CKOUT_PWR]	= {0x1, 0x7b,  6, 6},
-	[DVBT_SYNC_DUR]		= {0x1, 0x7b,  7, 7},
-	[DVBT_ERR_DUR]		= {0x1, 0x7c,  0, 0},
-	[DVBT_SYNC_LVL]		= {0x1, 0x7c,  1, 1},
-	[DVBT_ERR_LVL]		= {0x1, 0x7c,  2, 2},
-	[DVBT_VAL_LVL]		= {0x1, 0x7c,  3, 3},
-	[DVBT_SERIAL]		= {0x1, 0x7c,  4, 4},
-	[DVBT_SER_LSB]		= {0x1, 0x7c,  5, 5},
-	[DVBT_CDIV_PH0]		= {0x1, 0x7d,  3, 0},
-	[DVBT_CDIV_PH1]		= {0x1, 0x7d,  7, 4},
-	[DVBT_MPEG_IO_OPT_2_2]	= {0x0, 0x6,   7, 7},
-	[DVBT_MPEG_IO_OPT_1_0]	= {0x0, 0x7,   7, 6},
-	[DVBT_CKOUTPAR_PIP]	= {0x0, 0xb7,  4, 4},
-	[DVBT_CKOUT_PWR_PIP]	= {0x0, 0xb7,  3, 3},
-	[DVBT_SYNC_LVL_PIP]	= {0x0, 0xb7,  2, 2},
-	[DVBT_ERR_LVL_PIP]	= {0x0, 0xb7,  1, 1},
-	[DVBT_VAL_LVL_PIP]	= {0x0, 0xb7,  0, 0},
-	[DVBT_CKOUTPAR_PID]	= {0x0, 0xb9,  4, 4},
-	[DVBT_CKOUT_PWR_PID]	= {0x0, 0xb9,  3, 3},
-	[DVBT_SYNC_LVL_PID]	= {0x0, 0xb9,  2, 2},
-	[DVBT_ERR_LVL_PID]	= {0x0, 0xb9,  1, 1},
-	[DVBT_VAL_LVL_PID]	= {0x0, 0xb9,  0, 0},
-	[DVBT_SM_PASS]		= {0x1, 0x93, 11, 0},
-	[DVBT_AD7_SETTING]	= {0x0, 0x11, 15, 0},
-	[DVBT_RSSI_R]		= {0x3, 0x1,   6, 0},
-	[DVBT_ACI_DET_IND]	= {0x3, 0x12,  0, 0},
-	[DVBT_REG_MON]		= {0x0, 0xd,   1, 0},
-	[DVBT_REG_MONSEL]	= {0x0, 0xd,   2, 2},
-	[DVBT_REG_GPE]		= {0x0, 0xd,   7, 7},
-	[DVBT_REG_GPO]		= {0x0, 0x10,  0, 0},
-	[DVBT_REG_4MSEL]	= {0x0, 0x13,  0, 0},
+	[DVBT_SOFT_RST]		= {0x101,  2, 2},
+	[DVBT_IIC_REPEAT]	= {0x101,  3, 3},
+	[DVBT_TR_WAIT_MIN_8K]	= {0x188, 11, 2},
+	[DVBT_RSD_BER_FAIL_VAL]	= {0x18f, 15, 0},
+	[DVBT_EN_BK_TRK]	= {0x1a6,  7, 7},
+	[DVBT_AD_EN_REG]	= {0x008,  7, 7},
+	[DVBT_AD_EN_REG1]	= {0x008,  6, 6},
+	[DVBT_EN_BBIN]		= {0x1b1,  0, 0},
+	[DVBT_MGD_THD0]		= {0x195,  7, 0},
+	[DVBT_MGD_THD1]		= {0x196,  7, 0},
+	[DVBT_MGD_THD2]		= {0x197,  7, 0},
+	[DVBT_MGD_THD3]		= {0x198,  7, 0},
+	[DVBT_MGD_THD4]		= {0x199,  7, 0},
+	[DVBT_MGD_THD5]		= {0x19a,  7, 0},
+	[DVBT_MGD_THD6]		= {0x19b,  7, 0},
+	[DVBT_MGD_THD7]		= {0x19c,  7, 0},
+	[DVBT_EN_CACQ_NOTCH]	= {0x161,  4, 4},
+	[DVBT_AD_AV_REF]	= {0x009,  6, 0},
+	[DVBT_REG_PI]		= {0x00a,  2, 0},
+	[DVBT_PIP_ON]		= {0x021,  3, 3},
+	[DVBT_SCALE1_B92]	= {0x292,  7, 0},
+	[DVBT_SCALE1_B93]	= {0x293,  7, 0},
+	[DVBT_SCALE1_BA7]	= {0x2a7,  7, 0},
+	[DVBT_SCALE1_BA9]	= {0x2a9,  7, 0},
+	[DVBT_SCALE1_BAA]	= {0x2aa,  7, 0},
+	[DVBT_SCALE1_BAB]	= {0x2ab,  7, 0},
+	[DVBT_SCALE1_BAC]	= {0x2ac,  7, 0},
+	[DVBT_SCALE1_BB0]	= {0x2b0,  7, 0},
+	[DVBT_SCALE1_BB1]	= {0x2b1,  7, 0},
+	[DVBT_KB_P1]		= {0x164,  3, 1},
+	[DVBT_KB_P2]		= {0x164,  6, 4},
+	[DVBT_KB_P3]		= {0x165,  2, 0},
+	[DVBT_OPT_ADC_IQ]	= {0x006,  5, 4},
+	[DVBT_AD_AVI]		= {0x009,  1, 0},
+	[DVBT_AD_AVQ]		= {0x009,  3, 2},
+	[DVBT_K1_CR_STEP12]	= {0x2ad,  9, 4},
+	[DVBT_TRK_KS_P2]	= {0x16f,  2, 0},
+	[DVBT_TRK_KS_I2]	= {0x170,  5, 3},
+	[DVBT_TR_THD_SET2]	= {0x172,  3, 0},
+	[DVBT_TRK_KC_P2]	= {0x173,  5, 3},
+	[DVBT_TRK_KC_I2]	= {0x175,  2, 0},
+	[DVBT_CR_THD_SET2]	= {0x176,  7, 6},
+	[DVBT_PSET_IFFREQ]	= {0x119, 21, 0},
+	[DVBT_SPEC_INV]		= {0x115,  0, 0},
+	[DVBT_RSAMP_RATIO]	= {0x19f, 27, 2},
+	[DVBT_CFREQ_OFF_RATIO]	= {0x19d, 23, 4},
+	[DVBT_FSM_STAGE]	= {0x351,  6, 3},
+	[DVBT_RX_CONSTEL]	= {0x33c,  3, 2},
+	[DVBT_RX_HIER]		= {0x33c,  6, 4},
+	[DVBT_RX_C_RATE_LP]	= {0x33d,  2, 0},
+	[DVBT_RX_C_RATE_HP]	= {0x33d,  5, 3},
+	[DVBT_GI_IDX]		= {0x351,  1, 0},
+	[DVBT_FFT_MODE_IDX]	= {0x351,  2, 2},
+	[DVBT_RSD_BER_EST]	= {0x34e, 15, 0},
+	[DVBT_CE_EST_EVM]	= {0x40c, 15, 0},
+	[DVBT_RF_AGC_VAL]	= {0x35b, 13, 0},
+	[DVBT_IF_AGC_VAL]	= {0x359, 13, 0},
+	[DVBT_DAGC_VAL]		= {0x305,  7, 0},
+	[DVBT_SFREQ_OFF]	= {0x318, 13, 0},
+	[DVBT_CFREQ_OFF]	= {0x35f, 17, 0},
+	[DVBT_POLAR_RF_AGC]	= {0x00e,  1, 1},
+	[DVBT_POLAR_IF_AGC]	= {0x00e,  0, 0},
+	[DVBT_AAGC_HOLD]	= {0x104,  5, 5},
+	[DVBT_EN_RF_AGC]	= {0x104,  6, 6},
+	[DVBT_EN_IF_AGC]	= {0x104,  7, 7},
+	[DVBT_IF_AGC_MIN]	= {0x108,  7, 0},
+	[DVBT_IF_AGC_MAX]	= {0x109,  7, 0},
+	[DVBT_RF_AGC_MIN]	= {0x10a,  7, 0},
+	[DVBT_RF_AGC_MAX]	= {0x10b,  7, 0},
+	[DVBT_IF_AGC_MAN]	= {0x10c,  6, 6},
+	[DVBT_IF_AGC_MAN_VAL]	= {0x10c, 13, 0},
+	[DVBT_RF_AGC_MAN]	= {0x10e,  6, 6},
+	[DVBT_RF_AGC_MAN_VAL]	= {0x10e, 13, 0},
+	[DVBT_DAGC_TRG_VAL]	= {0x112,  7, 0},
+	[DVBT_AGC_TARG_VAL_0]	= {0x102,  0, 0},
+	[DVBT_AGC_TARG_VAL_8_1]	= {0x103,  7, 0},
+	[DVBT_AAGC_LOOP_GAIN]	= {0x1c7,  5, 1},
+	[DVBT_LOOP_GAIN2_3_0]	= {0x104,  4, 1},
+	[DVBT_LOOP_GAIN2_4]	= {0x105,  7, 7},
+	[DVBT_LOOP_GAIN3]	= {0x1c8,  4, 0},
+	[DVBT_VTOP1]		= {0x106,  5, 0},
+	[DVBT_VTOP2]		= {0x1c9,  5, 0},
+	[DVBT_VTOP3]		= {0x1ca,  5, 0},
+	[DVBT_KRF1]		= {0x1cb,  7, 0},
+	[DVBT_KRF2]		= {0x107,  7, 0},
+	[DVBT_KRF3]		= {0x1cd,  7, 0},
+	[DVBT_KRF4]		= {0x1ce,  7, 0},
+	[DVBT_EN_GI_PGA]	= {0x1e5,  0, 0},
+	[DVBT_THD_LOCK_UP]	= {0x1d9,  8, 0},
+	[DVBT_THD_LOCK_DW]	= {0x1db,  8, 0},
+	[DVBT_THD_UP1]		= {0x1dd,  7, 0},
+	[DVBT_THD_DW1]		= {0x1de,  7, 0},
+	[DVBT_INTER_CNT_LEN]	= {0x1d8,  3, 0},
+	[DVBT_GI_PGA_STATE]	= {0x1e6,  3, 3},
+	[DVBT_EN_AGC_PGA]	= {0x1d7,  0, 0},
+	[DVBT_CKOUTPAR]		= {0x17b,  5, 5},
+	[DVBT_CKOUT_PWR]	= {0x17b,  6, 6},
+	[DVBT_SYNC_DUR]		= {0x17b,  7, 7},
+	[DVBT_ERR_DUR]		= {0x17c,  0, 0},
+	[DVBT_SYNC_LVL]		= {0x17c,  1, 1},
+	[DVBT_ERR_LVL]		= {0x17c,  2, 2},
+	[DVBT_VAL_LVL]		= {0x17c,  3, 3},
+	[DVBT_SERIAL]		= {0x17c,  4, 4},
+	[DVBT_SER_LSB]		= {0x17c,  5, 5},
+	[DVBT_CDIV_PH0]		= {0x17d,  3, 0},
+	[DVBT_CDIV_PH1]		= {0x17d,  7, 4},
+	[DVBT_MPEG_IO_OPT_2_2]	= {0x006,  7, 7},
+	[DVBT_MPEG_IO_OPT_1_0]	= {0x007,  7, 6},
+	[DVBT_CKOUTPAR_PIP]	= {0x0b7,  4, 4},
+	[DVBT_CKOUT_PWR_PIP]	= {0x0b7,  3, 3},
+	[DVBT_SYNC_LVL_PIP]	= {0x0b7,  2, 2},
+	[DVBT_ERR_LVL_PIP]	= {0x0b7,  1, 1},
+	[DVBT_VAL_LVL_PIP]	= {0x0b7,  0, 0},
+	[DVBT_CKOUTPAR_PID]	= {0x0b9,  4, 4},
+	[DVBT_CKOUT_PWR_PID]	= {0x0b9,  3, 3},
+	[DVBT_SYNC_LVL_PID]	= {0x0b9,  2, 2},
+	[DVBT_ERR_LVL_PID]	= {0x0b9,  1, 1},
+	[DVBT_VAL_LVL_PID]	= {0x0b9,  0, 0},
+	[DVBT_SM_PASS]		= {0x193, 11, 0},
+	[DVBT_AD7_SETTING]	= {0x011, 15, 0},
+	[DVBT_RSSI_R]		= {0x301,  6, 0},
+	[DVBT_ACI_DET_IND]	= {0x312,  0, 0},
+	[DVBT_REG_MON]		= {0x00d,  1, 0},
+	[DVBT_REG_MONSEL]	= {0x00d,  2, 2},
+	[DVBT_REG_GPE]		= {0x00d,  7, 7},
+	[DVBT_REG_GPO]		= {0x010,  0, 0},
+	[DVBT_REG_4MSEL]	= {0x013,  0, 0},
 };
 
-/* write multiple hardware registers */
-static int rtl2832_wr(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
+/* Our regmap is bypassing I2C adapter lock, thus we do it! */
+static int rtl2832_bulk_write(struct i2c_client *client, unsigned int reg,
+			      const void *val, size_t val_count)
 {
+	struct rtl2832_dev *dev = i2c_get_clientdata(client);
 	int ret;
-	u8 buf[MAX_XFER_SIZE];
-	struct i2c_msg msg[1] = {
-		{
-			.addr = priv->cfg.i2c_addr,
-			.flags = 0,
-			.len = 1 + len,
-			.buf = buf,
-		}
-	};
 
-	if (1 + len > sizeof(buf)) {
-		dev_warn(&priv->i2c->dev,
-			 "%s: i2c wr reg=%04x: len=%d is too big!\n",
-			 KBUILD_MODNAME, reg, len);
-		return -EINVAL;
-	}
-
-	buf[0] = reg;
-	memcpy(&buf[1], val, len);
-
-	ret = i2c_transfer(priv->i2c_adapter, msg, 1);
-	if (ret == 1) {
-		ret = 0;
-	} else {
-		dev_warn(&priv->i2c->dev,
-				"%s: i2c wr failed=%d reg=%02x len=%d\n",
-				KBUILD_MODNAME, ret, reg, len);
-		ret = -EREMOTEIO;
-	}
+	i2c_lock_adapter(client->adapter);
+	ret = regmap_bulk_write(dev->regmap, reg, val, val_count);
+	i2c_unlock_adapter(client->adapter);
 	return ret;
 }
 
-/* read multiple hardware registers */
-static int rtl2832_rd(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
+static int rtl2832_update_bits(struct i2c_client *client, unsigned int reg,
+			       unsigned int mask, unsigned int val)
 {
+	struct rtl2832_dev *dev = i2c_get_clientdata(client);
 	int ret;
-	struct i2c_msg msg[2] = {
-		{
-			.addr = priv->cfg.i2c_addr,
-			.flags = 0,
-			.len = 1,
-			.buf = &reg,
-		}, {
-			.addr = priv->cfg.i2c_addr,
-			.flags = I2C_M_RD,
-			.len = len,
-			.buf = val,
-		}
-	};
 
-	ret = i2c_transfer(priv->i2c_adapter, msg, 2);
-	if (ret == 2) {
-		ret = 0;
-	} else {
-		dev_warn(&priv->i2c->dev,
-				"%s: i2c rd failed=%d reg=%02x len=%d\n",
-				KBUILD_MODNAME, ret, reg, len);
-		ret = -EREMOTEIO;
-	}
+	i2c_lock_adapter(client->adapter);
+	ret = regmap_update_bits(dev->regmap, reg, mask, val);
+	i2c_unlock_adapter(client->adapter);
 	return ret;
 }
 
-/* write multiple registers */
-static int rtl2832_wr_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val,
-	int len)
+static int rtl2832_bulk_read(struct i2c_client *client, unsigned int reg,
+			     void *val, size_t val_count)
 {
+	struct rtl2832_dev *dev = i2c_get_clientdata(client);
 	int ret;
 
-	/* switch bank if needed */
-	if (page != priv->page) {
-		ret = rtl2832_wr(priv, 0x00, &page, 1);
-		if (ret)
-			return ret;
-
-		priv->page = page;
+	i2c_lock_adapter(client->adapter);
+	ret = regmap_bulk_read(dev->regmap, reg, val, val_count);
+	i2c_unlock_adapter(client->adapter);
+	return ret;
 }
 
-return rtl2832_wr(priv, reg, val, len);
-}
-
-/* read multiple registers */
-static int rtl2832_rd_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val,
-	int len)
+static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val)
 {
-	int ret;
-
-	/* switch bank if needed */
-	if (page != priv->page) {
-		ret = rtl2832_wr(priv, 0x00, &page, 1);
-		if (ret)
-			return ret;
-
-		priv->page = page;
-	}
-
-	return rtl2832_rd(priv, reg, val, len);
-}
-
-/* write single register */
-static int rtl2832_wr_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 val)
-{
-	return rtl2832_wr_regs(priv, reg, page, &val, 1);
-}
-
-/* read single register */
-static int rtl2832_rd_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val)
-{
-	return rtl2832_rd_regs(priv, reg, page, val, 1);
-}
-
-static int rtl2832_rd_demod_reg(struct rtl2832_priv *priv, int reg, u32 *val)
-{
-	int ret;
-
-	u8 reg_start_addr;
-	u8 msb, lsb;
-	u8 page;
-	u8 reading[4];
-	u32 reading_tmp;
-	int i;
-
-	u8 len;
-	u32 mask;
+	struct i2c_client *client = dev->client;
+	int ret, i;
+	u16 reg_start_addr;
+	u8 msb, lsb, reading[4], len;
+	u32 reading_tmp, mask;
 
 	reg_start_addr = registers[reg].start_address;
 	msb = registers[reg].msb;
 	lsb = registers[reg].lsb;
-	page = registers[reg].page;
-
 	len = (msb >> 3) + 1;
 	mask = REG_MASK(msb - lsb);
 
-	ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len);
+	ret = rtl2832_bulk_read(client, reg_start_addr, reading, len);
 	if (ret)
 		goto err;
 
@@ -302,40 +214,27 @@
 
 	*val = (reading_tmp >> lsb) & mask;
 
-	return ret;
-
+	return 0;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
-
 }
 
-static int rtl2832_wr_demod_reg(struct rtl2832_priv *priv, int reg, u32 val)
+static int rtl2832_wr_demod_reg(struct rtl2832_dev *dev, int reg, u32 val)
 {
+	struct i2c_client *client = dev->client;
 	int ret, i;
-	u8 len;
-	u8 reg_start_addr;
-	u8 msb, lsb;
-	u8 page;
-	u32 mask;
-
-
-	u8 reading[4];
-	u8 writing[4];
-	u32 reading_tmp;
-	u32 writing_tmp;
-
+	u16 reg_start_addr;
+	u8 msb, lsb, reading[4], writing[4], len;
+	u32 reading_tmp, writing_tmp, mask;
 
 	reg_start_addr = registers[reg].start_address;
 	msb = registers[reg].msb;
 	lsb = registers[reg].lsb;
-	page = registers[reg].page;
-
 	len = (msb >> 3) + 1;
 	mask = REG_MASK(msb - lsb);
 
-
-	ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len);
+	ret = rtl2832_bulk_read(client, reg_start_addr, reading, len);
 	if (ret)
 		goto err;
 
@@ -346,49 +245,23 @@
 	writing_tmp = reading_tmp & ~(mask << lsb);
 	writing_tmp |= ((val & mask) << lsb);
 
-
 	for (i = 0; i < len; i++)
 		writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff;
 
-	ret = rtl2832_wr_regs(priv, reg_start_addr, page, &writing[0], len);
+	ret = rtl2832_bulk_write(client, reg_start_addr, writing, len);
 	if (ret)
 		goto err;
 
-	return ret;
-
+	return 0;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-	return ret;
-
-}
-
-static int rtl2832_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
-	int ret;
-	struct rtl2832_priv *priv = fe->demodulator_priv;
-
-	dev_dbg(&priv->i2c->dev, "%s: enable=%d\n", __func__, enable);
-
-	/* gate already open or close */
-	if (priv->i2c_gate_state == enable)
-		return 0;
-
-	ret = rtl2832_wr_demod_reg(priv, DVBT_IIC_REPEAT, (enable ? 0x1 : 0x0));
-	if (ret)
-		goto err;
-
-	priv->i2c_gate_state = enable;
-
-	return ret;
-err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
-
 static int rtl2832_set_if(struct dvb_frontend *fe, u32 if_freq)
 {
-	struct rtl2832_priv *priv = fe->demodulator_priv;
+	struct rtl2832_dev *dev = fe->demodulator_priv;
+	struct i2c_client *client = dev->client;
 	int ret;
 	u64 pset_iffreq;
 	u8 en_bbin = (if_freq == 0 ? 0x1 : 0x0);
@@ -397,30 +270,35 @@
 	* PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22)
 	*		/ CrystalFreqHz)
 	*/
-
-	pset_iffreq = if_freq % priv->cfg.xtal;
+	pset_iffreq = if_freq % dev->pdata->clk;
 	pset_iffreq *= 0x400000;
-	pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal);
+	pset_iffreq = div_u64(pset_iffreq, dev->pdata->clk);
 	pset_iffreq = -pset_iffreq;
 	pset_iffreq = pset_iffreq & 0x3fffff;
-	dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d pset_iffreq=%08x\n",
-			__func__, if_freq, (unsigned)pset_iffreq);
+	dev_dbg(&client->dev, "if_frequency=%d pset_iffreq=%08x\n",
+		if_freq, (unsigned)pset_iffreq);
 
-	ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin);
+	ret = rtl2832_wr_demod_reg(dev, DVBT_EN_BBIN, en_bbin);
 	if (ret)
-		return ret;
+		goto err;
 
-	ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq);
+	ret = rtl2832_wr_demod_reg(dev, DVBT_PSET_IFFREQ, pset_iffreq);
+	if (ret)
+		goto err;
 
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int rtl2832_init(struct dvb_frontend *fe)
 {
-	struct rtl2832_priv *priv = fe->demodulator_priv;
+	struct rtl2832_dev *dev = fe->demodulator_priv;
+	struct i2c_client *client = dev->client;
+	struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
 	const struct rtl2832_reg_value *init;
 	int i, ret, len;
-
 	/* initialization values for the demodulator registers */
 	struct rtl2832_reg_value rtl2832_initial_regs[] = {
 		{DVBT_AD_EN_REG,		0x1},
@@ -467,19 +345,19 @@
 		{DVBT_CR_THD_SET2,		0x1},
 	};
 
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+	dev_dbg(&client->dev, "\n");
 
 	for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) {
-		ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg,
+		ret = rtl2832_wr_demod_reg(dev, rtl2832_initial_regs[i].reg,
 			rtl2832_initial_regs[i].value);
 		if (ret)
 			goto err;
 	}
 
 	/* load tuner specific settings */
-	dev_dbg(&priv->i2c->dev, "%s: load settings for tuner=%02x\n",
-			__func__, priv->cfg.tuner);
-	switch (priv->cfg.tuner) {
+	dev_dbg(&client->dev, "load settings for tuner=%02x\n",
+		dev->pdata->tuner);
+	switch (dev->pdata->tuner) {
 	case RTL2832_TUNER_FC0012:
 	case RTL2832_TUNER_FC0013:
 		len = ARRAY_SIZE(rtl2832_tuner_init_fc0012);
@@ -504,51 +382,60 @@
 	}
 
 	for (i = 0; i < len; i++) {
-		ret = rtl2832_wr_demod_reg(priv, init[i].reg, init[i].value);
+		ret = rtl2832_wr_demod_reg(dev, init[i].reg, init[i].value);
 		if (ret)
 			goto err;
 	}
 
-	/*
-	 * r820t NIM code does a software reset here at the demod -
-	 * may not be needed, as there's already a software reset at
-	 * set_params()
-	 */
-#if 1
-	/* soft reset */
-	ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1);
-	if (ret)
-		goto err;
+	/* init stats here in order signal app which stats are supported */
+	c->strength.len = 1;
+	c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->cnr.len = 1;
+	c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->post_bit_error.len = 1;
+	c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	c->post_bit_count.len = 1;
+	c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	/* start statistics polling */
+	schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
+	dev->sleeping = false;
 
-	ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0);
-	if (ret)
-		goto err;
-#endif
-
-	priv->sleeping = false;
-
-	return ret;
-
+	return 0;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int rtl2832_sleep(struct dvb_frontend *fe)
 {
-	struct rtl2832_priv *priv = fe->demodulator_priv;
+	struct rtl2832_dev *dev = fe->demodulator_priv;
+	struct i2c_client *client = dev->client;
+	int ret;
 
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
-	priv->sleeping = true;
+	dev_dbg(&client->dev, "\n");
+
+	dev->sleeping = true;
+	/* stop statistics polling */
+	cancel_delayed_work_sync(&dev->stat_work);
+	dev->fe_status = 0;
+
+	ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1);
+	if (ret)
+		goto err;
+
 	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
 }
 
 static int rtl2832_get_tune_settings(struct dvb_frontend *fe,
 	struct dvb_frontend_tune_settings *s)
 {
-	struct rtl2832_priv *priv = fe->demodulator_priv;
+	struct rtl2832_dev *dev = fe->demodulator_priv;
+	struct i2c_client *client = dev->client;
 
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+	dev_dbg(&client->dev, "\n");
 	s->min_delay_ms = 1000;
 	s->step_size = fe->ops.info.frequency_stepsize * 2;
 	s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
@@ -557,7 +444,8 @@
 
 static int rtl2832_set_frontend(struct dvb_frontend *fe)
 {
-	struct rtl2832_priv *priv = fe->demodulator_priv;
+	struct rtl2832_dev *dev = fe->demodulator_priv;
+	struct i2c_client *client = dev->client;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret, i, j;
 	u64 bw_mode, num, num2;
@@ -588,17 +476,15 @@
 		},
 	};
 
-
-	dev_dbg(&priv->i2c->dev,
-			"%s: frequency=%d bandwidth_hz=%d inversion=%d\n",
-			__func__, c->frequency, c->bandwidth_hz, c->inversion);
+	dev_dbg(&client->dev, "frequency=%u bandwidth_hz=%u inversion=%u\n",
+		c->frequency, c->bandwidth_hz, c->inversion);
 
 	/* program tuner */
 	if (fe->ops.tuner_ops.set_params)
 		fe->ops.tuner_ops.set_params(fe);
 
 	/* PIP mode related */
-	ret = rtl2832_wr_regs(priv, 0x92, 1, "\x00\x0f\xff", 3);
+	ret = rtl2832_bulk_write(client, 0x192, "\x00\x0f\xff", 3);
 	if (ret)
 		goto err;
 
@@ -629,12 +515,14 @@
 		bw_mode = 64000000;
 		break;
 	default:
-		dev_dbg(&priv->i2c->dev, "%s: invalid bandwidth\n", __func__);
-		return -EINVAL;
+		dev_err(&client->dev, "invalid bandwidth_hz %u\n",
+			c->bandwidth_hz);
+		ret = -EINVAL;
+		goto err;
 	}
 
 	for (j = 0; j < sizeof(bw_params[0]); j++) {
-		ret = rtl2832_wr_regs(priv, 0x1c+j, 1, &bw_params[i][j], 1);
+		ret = rtl2832_bulk_write(client, 0x11c + j, &bw_params[i][j], 1);
 		if (ret)
 			goto err;
 	}
@@ -643,11 +531,11 @@
 	* RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22)
 	*	/ ConstWithBandwidthMode)
 	*/
-	num = priv->cfg.xtal * 7;
+	num = dev->pdata->clk * 7;
 	num *= 0x400000;
 	num = div_u64(num, bw_mode);
 	resamp_ratio =  num & 0x3ffffff;
-	ret = rtl2832_wr_demod_reg(priv, DVBT_RSAMP_RATIO, resamp_ratio);
+	ret = rtl2832_wr_demod_reg(dev, DVBT_RSAMP_RATIO, resamp_ratio);
 	if (ret)
 		goto err;
 
@@ -656,48 +544,49 @@
 	*	/ (CrystalFreqHz * 7))
 	*/
 	num = bw_mode << 20;
-	num2 = priv->cfg.xtal * 7;
+	num2 = dev->pdata->clk * 7;
 	num = div_u64(num, num2);
 	num = -num;
 	cfreq_off_ratio = num & 0xfffff;
-	ret = rtl2832_wr_demod_reg(priv, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio);
+	ret = rtl2832_wr_demod_reg(dev, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio);
 	if (ret)
 		goto err;
 
 	/* soft reset */
-	ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1);
+	ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0);
+	ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0);
 	if (ret)
 		goto err;
 
-	return ret;
+	return 0;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int rtl2832_get_frontend(struct dvb_frontend *fe)
 {
-	struct rtl2832_priv *priv = fe->demodulator_priv;
+	struct rtl2832_dev *dev = fe->demodulator_priv;
+	struct i2c_client *client = dev->client;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret;
 	u8 buf[3];
 
-	if (priv->sleeping)
+	if (dev->sleeping)
 		return 0;
 
-	ret = rtl2832_rd_regs(priv, 0x3c, 3, buf, 2);
+	ret = rtl2832_bulk_read(client, 0x33c, buf, 2);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_rd_reg(priv, 0x51, 3, &buf[2]);
+	ret = rtl2832_bulk_read(client, 0x351, &buf[2], 1);
 	if (ret)
 		goto err;
 
-	dev_dbg(&priv->i2c->dev, "%s: TPS=%*ph\n", __func__, 3, buf);
+	dev_dbg(&client->dev, "TPS=%*ph\n", 3, buf);
 
 	switch ((buf[0] >> 2) & 3) {
 	case 0:
@@ -787,361 +676,220 @@
 
 	return 0;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int rtl2832_read_status(struct dvb_frontend *fe, fe_status_t *status)
 {
-	struct rtl2832_priv *priv = fe->demodulator_priv;
+	struct rtl2832_dev *dev = fe->demodulator_priv;
+	struct i2c_client *client = dev->client;
 	int ret;
-	u32 tmp;
-	*status = 0;
+	u32 uninitialized_var(tmp);
 
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
-	if (priv->sleeping)
+	dev_dbg(&client->dev, "\n");
+
+	*status = 0;
+	if (dev->sleeping)
 		return 0;
 
-	ret = rtl2832_rd_demod_reg(priv, DVBT_FSM_STAGE, &tmp);
+	ret = rtl2832_rd_demod_reg(dev, DVBT_FSM_STAGE, &tmp);
 	if (ret)
 		goto err;
 
 	if (tmp == 11) {
 		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
 				FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
-	}
-	/* TODO find out if this is also true for rtl2832? */
-	/*else if (tmp == 10) {
+	} else if (tmp == 10) {
 		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
 				FE_HAS_VITERBI;
-	}*/
+	}
 
-	return ret;
+	dev->fe_status = *status;
+	return 0;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr)
 {
-	struct rtl2832_priv *priv = fe->demodulator_priv;
-	int ret, hierarchy, constellation;
-	u8 buf[2], tmp;
-	u16 tmp16;
-#define CONSTELLATION_NUM 3
-#define HIERARCHY_NUM 4
-	static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
-		{ 85387325, 85387325, 85387325, 85387325 },
-		{ 86676178, 86676178, 87167949, 87795660 },
-		{ 87659938, 87659938, 87885178, 88241743 },
-	};
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 
-	/* reports SNR in resolution of 0.1 dB */
-
-	ret = rtl2832_rd_reg(priv, 0x3c, 3, &tmp);
-	if (ret)
-		goto err;
-
-	constellation = (tmp >> 2) & 0x03; /* [3:2] */
-	if (constellation > CONSTELLATION_NUM - 1)
-		goto err;
-
-	hierarchy = (tmp >> 4) & 0x07; /* [6:4] */
-	if (hierarchy > HIERARCHY_NUM - 1)
-		goto err;
-
-	ret = rtl2832_rd_regs(priv, 0x0c, 4, buf, 2);
-	if (ret)
-		goto err;
-
-	tmp16 = buf[0] << 8 | buf[1];
-
-	if (tmp16)
-		*snr = (snr_constant[constellation][hierarchy] -
-				intlog10(tmp16)) / ((1 << 24) / 100);
+	/* report SNR in resolution of 0.1 dB */
+	if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
+		*snr = div_s64(c->cnr.stat[0].svalue, 100);
 	else
 		*snr = 0;
 
 	return 0;
-err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-	return ret;
 }
 
 static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber)
 {
-	struct rtl2832_priv *priv = fe->demodulator_priv;
-	int ret;
-	u8 buf[2];
+	struct rtl2832_dev *dev = fe->demodulator_priv;
 
-	ret = rtl2832_rd_regs(priv, 0x4e, 3, buf, 2);
-	if (ret)
-		goto err;
-
-	*ber = buf[0] << 8 | buf[1];
+	*ber = (dev->post_bit_error - dev->post_bit_error_prev);
+	dev->post_bit_error_prev = dev->post_bit_error;
 
 	return 0;
-err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-	return ret;
 }
 
-static struct dvb_frontend_ops rtl2832_ops;
-
-static void rtl2832_release(struct dvb_frontend *fe)
+static void rtl2832_stat_work(struct work_struct *work)
 {
-	struct rtl2832_priv *priv = fe->demodulator_priv;
+	struct rtl2832_dev *dev = container_of(work, struct rtl2832_dev, stat_work.work);
+	struct i2c_client *client = dev->client;
+	struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+	int ret, tmp;
+	u8 u8tmp, buf[2];
+	u16 u16tmp;
 
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
-	cancel_delayed_work_sync(&priv->i2c_gate_work);
-	i2c_del_mux_adapter(priv->i2c_adapter_tuner);
-	i2c_del_mux_adapter(priv->i2c_adapter);
-	kfree(priv);
+	dev_dbg(&client->dev, "\n");
+
+	/* signal strength */
+	if (dev->fe_status & FE_HAS_SIGNAL) {
+		/* read digital AGC */
+		ret = rtl2832_bulk_read(client, 0x305, &u8tmp, 1);
+		if (ret)
+			goto err;
+
+		dev_dbg(&client->dev, "digital agc=%02x", u8tmp);
+
+		u8tmp = ~u8tmp;
+		u16tmp = u8tmp << 8 | u8tmp << 0;
+
+		c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+		c->strength.stat[0].uvalue = u16tmp;
+	} else {
+		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	}
+
+	/* CNR */
+	if (dev->fe_status & FE_HAS_VITERBI) {
+		unsigned hierarchy, constellation;
+		#define CONSTELLATION_NUM 3
+		#define HIERARCHY_NUM 4
+		static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
+			{85387325, 85387325, 85387325, 85387325},
+			{86676178, 86676178, 87167949, 87795660},
+			{87659938, 87659938, 87885178, 88241743},
+		};
+
+		ret = rtl2832_bulk_read(client, 0x33c, &u8tmp, 1);
+		if (ret)
+			goto err;
+
+		constellation = (u8tmp >> 2) & 0x03; /* [3:2] */
+		if (constellation > CONSTELLATION_NUM - 1)
+			goto err_schedule_delayed_work;
+
+		hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */
+		if (hierarchy > HIERARCHY_NUM - 1)
+			goto err_schedule_delayed_work;
+
+		ret = rtl2832_bulk_read(client, 0x40c, buf, 2);
+		if (ret)
+			goto err;
+
+		u16tmp = buf[0] << 8 | buf[1] << 0;
+		if (u16tmp)
+			tmp = (constant[constellation][hierarchy] -
+			       intlog10(u16tmp)) / ((1 << 24) / 10000);
+		else
+			tmp = 0;
+
+		dev_dbg(&client->dev, "cnr raw=%u\n", u16tmp);
+
+		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+		c->cnr.stat[0].svalue = tmp;
+	} else {
+		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	}
+
+	/* BER */
+	if (dev->fe_status & FE_HAS_LOCK) {
+		ret = rtl2832_bulk_read(client, 0x34e, buf, 2);
+		if (ret)
+			goto err;
+
+		u16tmp = buf[0] << 8 | buf[1] << 0;
+		dev->post_bit_error += u16tmp;
+		dev->post_bit_count += 1000000;
+
+		dev_dbg(&client->dev, "ber errors=%u total=1000000\n", u16tmp);
+
+		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+		c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
+		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+		c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
+	} else {
+		c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	}
+
+err_schedule_delayed_work:
+	schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
+	return;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 }
 
 /*
- * Delay mechanism to avoid unneeded I2C gate open / close. Gate close is
- * delayed here a little bit in order to see if there is sequence of I2C
+ * I2C gate/mux/repeater logic
+ * We must use unlocked __i2c_transfer() here (through regmap) because of I2C
+ * adapter lock is already taken by tuner driver.
+ * There is delay mechanism to avoid unneeded I2C gate open / close. Gate close
+ * is delayed here a little bit in order to see if there is sequence of I2C
  * messages sent to same I2C bus.
- * We must use unlocked version of __i2c_transfer() in order to avoid deadlock
- * as lock is already taken by calling muxed i2c_transfer().
  */
 static void rtl2832_i2c_gate_work(struct work_struct *work)
 {
-	struct rtl2832_priv *priv = container_of(work,
-			struct rtl2832_priv, i2c_gate_work.work);
-	struct i2c_adapter *adap = priv->i2c;
+	struct rtl2832_dev *dev = container_of(work, struct rtl2832_dev, i2c_gate_work.work);
+	struct i2c_client *client = dev->client;
 	int ret;
-	u8 buf[2];
-	struct i2c_msg msg[1] = {
-		{
-			.addr = priv->cfg.i2c_addr,
-			.flags = 0,
-			.len = sizeof(buf),
-			.buf = buf,
-		}
-	};
 
-	/* select reg bank 1 */
-	buf[0] = 0x00;
-	buf[1] = 0x01;
-	ret = __i2c_transfer(adap, msg, 1);
-	if (ret != 1)
+	/* close gate */
+	ret = rtl2832_update_bits(dev->client, 0x101, 0x08, 0x00);
+	if (ret)
 		goto err;
 
-	priv->page = 1;
-
-	/* close I2C repeater gate */
-	buf[0] = 0x01;
-	buf[1] = 0x10;
-	ret = __i2c_transfer(adap, msg, 1);
-	if (ret != 1)
-		goto err;
-
-	priv->i2c_gate_state = false;
-
 	return;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-
-	return;
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 }
 
 static int rtl2832_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id)
 {
-	struct rtl2832_priv *priv = mux_priv;
+	struct rtl2832_dev *dev = mux_priv;
+	struct i2c_client *client = dev->client;
 	int ret;
-	u8 buf[2], val;
-	struct i2c_msg msg[1] = {
-		{
-			.addr = priv->cfg.i2c_addr,
-			.flags = 0,
-			.len = sizeof(buf),
-			.buf = buf,
-		}
-	};
-	struct i2c_msg msg_rd[2] = {
-		{
-			.addr = priv->cfg.i2c_addr,
-			.flags = 0,
-			.len = 1,
-			.buf = "\x01",
-		}, {
-			.addr = priv->cfg.i2c_addr,
-			.flags = I2C_M_RD,
-			.len = 1,
-			.buf = &val,
-		}
-	};
 
 	/* terminate possible gate closing */
-	cancel_delayed_work_sync(&priv->i2c_gate_work);
+	cancel_delayed_work(&dev->i2c_gate_work);
 
-	if (priv->i2c_gate_state == chan_id)
-		return 0;
-
-	/* select reg bank 1 */
-	buf[0] = 0x00;
-	buf[1] = 0x01;
-	ret = __i2c_transfer(adap, msg, 1);
-	if (ret != 1)
+	/*
+	 * I2C adapter lock is already taken and due to that we will use
+	 * regmap_update_bits() which does not lock again I2C adapter.
+	 */
+	ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08);
+	if (ret)
 		goto err;
 
-	priv->page = 1;
-
-	/* we must read that register, otherwise there will be errors */
-	ret = __i2c_transfer(adap, msg_rd, 2);
-	if (ret != 2)
-		goto err;
-
-	/* open or close I2C repeater gate */
-	buf[0] = 0x01;
-	if (chan_id == 1)
-		buf[1] = 0x18; /* open */
-	else
-		buf[1] = 0x10; /* close */
-
-	ret = __i2c_transfer(adap, msg, 1);
-	if (ret != 1)
-		goto err;
-
-	priv->i2c_gate_state = chan_id;
-
 	return 0;
 err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-
-	return -EREMOTEIO;
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
 }
 
 static int rtl2832_deselect(struct i2c_adapter *adap, void *mux_priv,
-		u32 chan_id)
+			    u32 chan_id)
 {
-	struct rtl2832_priv *priv = mux_priv;
-	schedule_delayed_work(&priv->i2c_gate_work, usecs_to_jiffies(100));
+	struct rtl2832_dev *dev = mux_priv;
+
+	schedule_delayed_work(&dev->i2c_gate_work, usecs_to_jiffies(100));
 	return 0;
 }
 
-int rtl2832_enable_external_ts_if(struct dvb_frontend *fe)
-{
-	struct rtl2832_priv *priv = fe->demodulator_priv;
-	int ret;
-
-	dev_dbg(&priv->i2c->dev, "%s: setting PIP mode\n", __func__);
-
-	ret = rtl2832_wr_regs(priv, 0x0c, 1, "\x5f\xff", 2);
-	if (ret)
-		goto err;
-
-	ret = rtl2832_wr_demod_reg(priv, DVBT_PIP_ON, 0x1);
-	if (ret)
-		goto err;
-
-	ret = rtl2832_wr_reg(priv, 0xbc, 0, 0x18);
-	if (ret)
-		goto err;
-
-	ret = rtl2832_wr_reg(priv, 0x22, 0, 0x01);
-	if (ret)
-		goto err;
-
-	ret = rtl2832_wr_reg(priv, 0x26, 0, 0x1f);
-	if (ret)
-		goto err;
-
-	ret = rtl2832_wr_reg(priv, 0x27, 0, 0xff);
-	if (ret)
-		goto err;
-
-	ret = rtl2832_wr_regs(priv, 0x92, 1, "\x7f\xf7\xff", 3);
-	if (ret)
-		goto err;
-
-	/* soft reset */
-	ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1);
-	if (ret)
-		goto err;
-
-	ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0);
-	if (ret)
-		goto err;
-
-	return 0;
-err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-	return ret;
-
-}
-EXPORT_SYMBOL(rtl2832_enable_external_ts_if);
-
-struct i2c_adapter *rtl2832_get_i2c_adapter(struct dvb_frontend *fe)
-{
-	struct rtl2832_priv *priv = fe->demodulator_priv;
-	return priv->i2c_adapter_tuner;
-}
-EXPORT_SYMBOL(rtl2832_get_i2c_adapter);
-
-struct i2c_adapter *rtl2832_get_private_i2c_adapter(struct dvb_frontend *fe)
-{
-	struct rtl2832_priv *priv = fe->demodulator_priv;
-	return priv->i2c_adapter;
-}
-EXPORT_SYMBOL(rtl2832_get_private_i2c_adapter);
-
-struct dvb_frontend *rtl2832_attach(const struct rtl2832_config *cfg,
-	struct i2c_adapter *i2c)
-{
-	struct rtl2832_priv *priv = NULL;
-	int ret = 0;
-	u8 tmp;
-
-	dev_dbg(&i2c->dev, "%s:\n", __func__);
-
-	/* allocate memory for the internal state */
-	priv = kzalloc(sizeof(struct rtl2832_priv), GFP_KERNEL);
-	if (priv == NULL)
-		goto err;
-
-	/* setup the priv */
-	priv->i2c = i2c;
-	priv->tuner = cfg->tuner;
-	memcpy(&priv->cfg, cfg, sizeof(struct rtl2832_config));
-	INIT_DELAYED_WORK(&priv->i2c_gate_work, rtl2832_i2c_gate_work);
-
-	/* create muxed i2c adapter for demod itself */
-	priv->i2c_adapter = i2c_add_mux_adapter(i2c, &i2c->dev, priv, 0, 0, 0,
-			rtl2832_select, NULL);
-	if (priv->i2c_adapter == NULL)
-		goto err;
-
-	/* check if the demod is there */
-	ret = rtl2832_rd_reg(priv, 0x00, 0x0, &tmp);
-	if (ret)
-		goto err;
-
-	/* create muxed i2c adapter for demod tuner bus */
-	priv->i2c_adapter_tuner = i2c_add_mux_adapter(i2c, &i2c->dev, priv,
-			0, 1, 0, rtl2832_select, rtl2832_deselect);
-	if (priv->i2c_adapter_tuner == NULL)
-		goto err;
-
-	/* create dvb_frontend */
-	memcpy(&priv->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops));
-	priv->fe.demodulator_priv = priv;
-
-	/* TODO implement sleep mode */
-	priv->sleeping = true;
-
-	return &priv->fe;
-err:
-	dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
-	if (priv && priv->i2c_adapter)
-		i2c_del_mux_adapter(priv->i2c_adapter);
-	kfree(priv);
-	return NULL;
-}
-EXPORT_SYMBOL(rtl2832_attach);
-
 static struct dvb_frontend_ops rtl2832_ops = {
 	.delsys = { SYS_DVBT },
 	.info = {
@@ -1166,8 +914,6 @@
 			FE_CAN_MUTE_TS
 	 },
 
-	.release = rtl2832_release,
-
 	.init = rtl2832_init,
 	.sleep = rtl2832_sleep,
 
@@ -1179,11 +925,403 @@
 	.read_status = rtl2832_read_status,
 	.read_snr = rtl2832_read_snr,
 	.read_ber = rtl2832_read_ber,
-
-	.i2c_gate_ctrl = rtl2832_i2c_gate_ctrl,
 };
 
+static bool rtl2832_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x305:
+	case 0x33c:
+	case 0x34e:
+	case 0x351:
+	case 0x40c ... 0x40d:
+		return true;
+	default:
+		break;
+	}
+
+	return false;
+}
+
+/*
+ * We implement own I2C access routines for regmap in order to get manual access
+ * to I2C adapter lock, which is needed for I2C mux adapter.
+ */
+static int rtl2832_regmap_read(void *context, const void *reg_buf,
+			       size_t reg_size, void *val_buf, size_t val_size)
+{
+	struct i2c_client *client = context;
+	int ret;
+	struct i2c_msg msg[2] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = reg_size,
+			.buf = (u8 *)reg_buf,
+		}, {
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = val_size,
+			.buf = val_buf,
+		}
+	};
+
+	ret = __i2c_transfer(client->adapter, msg, 2);
+	if (ret != 2) {
+		dev_warn(&client->dev, "i2c reg read failed %d\n", ret);
+		if (ret >= 0)
+			ret = -EREMOTEIO;
+		return ret;
+	}
+	return 0;
+}
+
+static int rtl2832_regmap_write(void *context, const void *data, size_t count)
+{
+	struct i2c_client *client = context;
+	int ret;
+	struct i2c_msg msg[1] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = count,
+			.buf = (u8 *)data,
+		}
+	};
+
+	ret = __i2c_transfer(client->adapter, msg, 1);
+	if (ret != 1) {
+		dev_warn(&client->dev, "i2c reg write failed %d\n", ret);
+		if (ret >= 0)
+			ret = -EREMOTEIO;
+		return ret;
+	}
+	return 0;
+}
+
+static int rtl2832_regmap_gather_write(void *context, const void *reg,
+				       size_t reg_len, const void *val,
+				       size_t val_len)
+{
+	struct i2c_client *client = context;
+	int ret;
+	u8 buf[256];
+	struct i2c_msg msg[1] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = 1 + val_len,
+			.buf = buf,
+		}
+	};
+
+	buf[0] = *(u8 const *)reg;
+	memcpy(&buf[1], val, val_len);
+
+	ret = __i2c_transfer(client->adapter, msg, 1);
+	if (ret != 1) {
+		dev_warn(&client->dev, "i2c reg write failed %d\n", ret);
+		if (ret >= 0)
+			ret = -EREMOTEIO;
+		return ret;
+	}
+	return 0;
+}
+
+/*
+ * FIXME: Hack. Implement own regmap locking in order to silence lockdep
+ * recursive lock warning. That happens when regmap I2C client calls I2C mux
+ * adapter, which leads demod I2C repeater enable via demod regmap. Operation
+ * takes two regmap locks recursively - but those are different regmap instances
+ * in a two different I2C drivers, so it is not deadlock. Proper fix is to make
+ * regmap aware of lockdep.
+ */
+static void rtl2832_regmap_lock(void *__dev)
+{
+	struct rtl2832_dev *dev = __dev;
+	struct i2c_client *client = dev->client;
+
+	dev_dbg(&client->dev, "\n");
+	mutex_lock(&dev->regmap_mutex);
+}
+
+static void rtl2832_regmap_unlock(void *__dev)
+{
+	struct rtl2832_dev *dev = __dev;
+	struct i2c_client *client = dev->client;
+
+	dev_dbg(&client->dev, "\n");
+	mutex_unlock(&dev->regmap_mutex);
+}
+
+static struct dvb_frontend *rtl2832_get_dvb_frontend(struct i2c_client *client)
+{
+	struct rtl2832_dev *dev = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "\n");
+	return &dev->fe;
+}
+
+static struct i2c_adapter *rtl2832_get_i2c_adapter(struct i2c_client *client)
+{
+	struct rtl2832_dev *dev = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "\n");
+	return dev->i2c_adapter_tuner;
+}
+
+static int rtl2832_enable_slave_ts(struct i2c_client *client)
+{
+	struct rtl2832_dev *dev = i2c_get_clientdata(client);
+	int ret;
+
+	dev_dbg(&client->dev, "\n");
+
+	ret = rtl2832_bulk_write(client, 0x10c, "\x5f\xff", 2);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_bulk_write(client, 0x0bc, "\x18", 1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_bulk_write(client, 0x022, "\x01", 1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_bulk_write(client, 0x026, "\x1f", 1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_bulk_write(client, 0x027, "\xff", 1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_bulk_write(client, 0x192, "\x7f\xf7\xff", 3);
+	if (ret)
+		goto err;
+
+	/* soft reset */
+	ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
+{
+	struct rtl2832_dev *dev = fe->demodulator_priv;
+	struct i2c_client *client = dev->client;
+	int ret;
+	u8 u8tmp;
+
+	dev_dbg(&client->dev, "onoff=%d\n", onoff);
+
+	/* enable / disable PID filter */
+	if (onoff)
+		u8tmp = 0x80;
+	else
+		u8tmp = 0x00;
+
+	ret = rtl2832_update_bits(client, 0x061, 0xc0, u8tmp);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid,
+			      int onoff)
+{
+	struct rtl2832_dev *dev = fe->demodulator_priv;
+	struct i2c_client *client = dev->client;
+	int ret;
+	u8 buf[4];
+
+	dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n",
+		index, pid, onoff);
+
+	/* skip invalid PIDs (0x2000) */
+	if (pid > 0x1fff || index > 32)
+		return 0;
+
+	if (onoff)
+		set_bit(index, &dev->filters);
+	else
+		clear_bit(index, &dev->filters);
+
+	/* enable / disable PIDs */
+	buf[0] = (dev->filters >>  0) & 0xff;
+	buf[1] = (dev->filters >>  8) & 0xff;
+	buf[2] = (dev->filters >> 16) & 0xff;
+	buf[3] = (dev->filters >> 24) & 0xff;
+	ret = rtl2832_bulk_write(client, 0x062, buf, 4);
+	if (ret)
+		goto err;
+
+	/* add PID */
+	buf[0] = (pid >> 8) & 0xff;
+	buf[1] = (pid >> 0) & 0xff;
+	ret = rtl2832_bulk_write(client, 0x066 + 2 * index, buf, 2);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int rtl2832_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct rtl2832_platform_data *pdata = client->dev.platform_data;
+	struct i2c_adapter *i2c = client->adapter;
+	struct rtl2832_dev *dev;
+	int ret;
+	u8 tmp;
+	static const struct regmap_bus regmap_bus = {
+		.read = rtl2832_regmap_read,
+		.write = rtl2832_regmap_write,
+		.gather_write = rtl2832_regmap_gather_write,
+		.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+	};
+	static const struct regmap_range_cfg regmap_range_cfg[] = {
+		{
+			.selector_reg     = 0x00,
+			.selector_mask    = 0xff,
+			.selector_shift   = 0,
+			.window_start     = 0,
+			.window_len       = 0x100,
+			.range_min        = 0 * 0x100,
+			.range_max        = 5 * 0x100,
+		},
+	};
+
+	dev_dbg(&client->dev, "\n");
+
+	/* allocate memory for the internal state */
+	dev = kzalloc(sizeof(struct rtl2832_dev), GFP_KERNEL);
+	if (dev == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	/* setup the state */
+	i2c_set_clientdata(client, dev);
+	dev->client = client;
+	dev->pdata = client->dev.platform_data;
+	dev->sleeping = true;
+	INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work);
+	INIT_DELAYED_WORK(&dev->stat_work, rtl2832_stat_work);
+	/* create regmap */
+	mutex_init(&dev->regmap_mutex);
+	dev->regmap_config.reg_bits =  8,
+	dev->regmap_config.val_bits =  8,
+	dev->regmap_config.lock = rtl2832_regmap_lock,
+	dev->regmap_config.unlock = rtl2832_regmap_unlock,
+	dev->regmap_config.lock_arg = dev,
+	dev->regmap_config.volatile_reg = rtl2832_volatile_reg,
+	dev->regmap_config.max_register = 5 * 0x100,
+	dev->regmap_config.ranges = regmap_range_cfg,
+	dev->regmap_config.num_ranges = ARRAY_SIZE(regmap_range_cfg),
+	dev->regmap_config.cache_type = REGCACHE_NONE,
+	dev->regmap = regmap_init(&client->dev, &regmap_bus, client,
+				  &dev->regmap_config);
+	if (IS_ERR(dev->regmap)) {
+		ret = PTR_ERR(dev->regmap);
+		goto err_kfree;
+	}
+
+	/* check if the demod is there */
+	ret = rtl2832_bulk_read(client, 0x000, &tmp, 1);
+	if (ret)
+		goto err_regmap_exit;
+
+	/* create muxed i2c adapter for demod tuner bus */
+	dev->i2c_adapter_tuner = i2c_add_mux_adapter(i2c, &i2c->dev, dev,
+			0, 0, 0, rtl2832_select, rtl2832_deselect);
+	if (dev->i2c_adapter_tuner == NULL) {
+		ret = -ENODEV;
+		goto err_regmap_exit;
+	}
+
+	/* create dvb_frontend */
+	memcpy(&dev->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops));
+	dev->fe.demodulator_priv = dev;
+
+	/* setup callbacks */
+	pdata->get_dvb_frontend = rtl2832_get_dvb_frontend;
+	pdata->get_i2c_adapter = rtl2832_get_i2c_adapter;
+	pdata->enable_slave_ts = rtl2832_enable_slave_ts;
+	pdata->pid_filter = rtl2832_pid_filter;
+	pdata->pid_filter_ctrl = rtl2832_pid_filter_ctrl;
+	pdata->bulk_read = rtl2832_bulk_read;
+	pdata->bulk_write = rtl2832_bulk_write;
+	pdata->update_bits = rtl2832_update_bits;
+
+	dev_info(&client->dev, "Realtek RTL2832 successfully attached\n");
+	return 0;
+err_regmap_exit:
+	regmap_exit(dev->regmap);
+err_kfree:
+	kfree(dev);
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int rtl2832_remove(struct i2c_client *client)
+{
+	struct rtl2832_dev *dev = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "\n");
+
+	cancel_delayed_work_sync(&dev->i2c_gate_work);
+
+	i2c_del_mux_adapter(dev->i2c_adapter_tuner);
+
+	regmap_exit(dev->regmap);
+
+	kfree(dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id rtl2832_id_table[] = {
+	{"rtl2832", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rtl2832_id_table);
+
+static struct i2c_driver rtl2832_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "rtl2832",
+	},
+	.probe		= rtl2832_probe,
+	.remove		= rtl2832_remove,
+	.id_table	= rtl2832_id_table,
+};
+
+module_i2c_driver(rtl2832_driver);
+
 MODULE_AUTHOR("Thomas Mair <mair.thomas86@gmail.com>");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
 MODULE_DESCRIPTION("Realtek RTL2832 DVB-T demodulator driver");
 MODULE_LICENSE("GPL");
-MODULE_VERSION("0.5");
diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h
index 5e72630..a8e912e 100644
--- a/drivers/media/dvb-frontends/rtl2832.h
+++ b/drivers/media/dvb-frontends/rtl2832.h
@@ -2,6 +2,7 @@
  * Realtek RTL2832 DVB-T demodulator driver
  *
  * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
+ * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi>
  *
  *	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
@@ -21,86 +22,42 @@
 #ifndef RTL2832_H
 #define RTL2832_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
+#include <linux/i2c-mux.h>
 
-struct rtl2832_config {
-	/*
-	 * Demodulator I2C address.
-	 */
-	u8 i2c_addr;
+/**
+ * struct rtl2832_platform_data - Platform data for the rtl2832 driver
+ * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000).
+ * @tuner: Used tuner model.
+ * @get_dvb_frontend: Get DVB frontend.
+ * @get_i2c_adapter: Get I2C adapter.
+ * @enable_slave_ts: Enable slave TS IF.
+ * @pid_filter: Set PID to PID filter.
+ * @pid_filter_ctrl: Control PID filter.
+ */
 
+struct rtl2832_platform_data {
+	u32 clk;
 	/*
-	 * Xtal frequency.
-	 * Hz
-	 * 4000000, 16000000, 25000000, 28800000
-	 */
-	u32 xtal;
-
-	/*
-	 * tuner
-	 * XXX: This must be keep sync with dvb_usb_rtl28xxu demod driver.
+	 * XXX: This list must be kept sync with dvb_usb_rtl28xxu USB IF driver.
 	 */
 #define RTL2832_TUNER_TUA9001   0x24
 #define RTL2832_TUNER_FC0012    0x26
 #define RTL2832_TUNER_E4000     0x27
 #define RTL2832_TUNER_FC0013    0x29
-#define RTL2832_TUNER_R820T	0x2a
-#define RTL2832_TUNER_R828D	0x2b
+#define RTL2832_TUNER_R820T     0x2a
+#define RTL2832_TUNER_R828D     0x2b
 	u8 tuner;
+
+	struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+	struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *);
+	int (*enable_slave_ts)(struct i2c_client *);
+	int (*pid_filter)(struct dvb_frontend *, u8, u16, int);
+	int (*pid_filter_ctrl)(struct dvb_frontend *, int);
+/* private: Register access for SDR module use only */
+	int (*bulk_read)(struct i2c_client *, unsigned int, void *, size_t);
+	int (*bulk_write)(struct i2c_client *, unsigned int, const void *, size_t);
+	int (*update_bits)(struct i2c_client *, unsigned int, unsigned int, unsigned int);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_RTL2832)
-struct dvb_frontend *rtl2832_attach(
-	const struct rtl2832_config *cfg,
-	struct i2c_adapter *i2c
-);
-
-extern struct i2c_adapter *rtl2832_get_i2c_adapter(
-	struct dvb_frontend *fe
-);
-
-extern struct i2c_adapter *rtl2832_get_private_i2c_adapter(
-	struct dvb_frontend *fe
-);
-
-extern int rtl2832_enable_external_ts_if(
-	struct dvb_frontend *fe
-);
-
-#else
-
-static inline struct dvb_frontend *rtl2832_attach(
-	const struct rtl2832_config *config,
-	struct i2c_adapter *i2c
-)
-{
-	pr_warn("%s: driver disabled by Kconfig\n", __func__);
-	return NULL;
-}
-
-static inline struct i2c_adapter *rtl2832_get_i2c_adapter(
-	struct dvb_frontend *fe
-)
-{
-	return NULL;
-}
-
-static inline struct i2c_adapter *rtl2832_get_private_i2c_adapter(
-	struct dvb_frontend *fe
-)
-{
-	return NULL;
-}
-
-static inline int rtl2832_enable_external_ts_if(
-	struct dvb_frontend *fe
-)
-{
-	return -ENODEV;
-}
-
-#endif
-
-
 #endif /* RTL2832_H */
diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h
index ae469f0..c3a922c 100644
--- a/drivers/media/dvb-frontends/rtl2832_priv.h
+++ b/drivers/media/dvb-frontends/rtl2832_priv.h
@@ -2,6 +2,7 @@
  * Realtek RTL2832 DVB-T demodulator driver
  *
  * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
+ * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi>
  *
  *	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
@@ -21,28 +22,34 @@
 #ifndef RTL2832_PRIV_H
 #define RTL2832_PRIV_H
 
-#include "dvb_frontend.h"
-#include "rtl2832.h"
-#include <linux/i2c-mux.h>
+#include <linux/regmap.h>
+#include <linux/math64.h>
+#include <linux/bitops.h>
 
-struct rtl2832_priv {
-	struct i2c_adapter *i2c;
-	struct i2c_adapter *i2c_adapter;
+#include "dvb_frontend.h"
+#include "dvb_math.h"
+#include "rtl2832.h"
+
+struct rtl2832_dev {
+	struct rtl2832_platform_data *pdata;
+	struct i2c_client *client;
+	struct mutex regmap_mutex;
+	struct regmap_config regmap_config;
+	struct regmap *regmap;
 	struct i2c_adapter *i2c_adapter_tuner;
 	struct dvb_frontend fe;
-	struct rtl2832_config cfg;
-
-	bool i2c_gate_state;
+	struct delayed_work stat_work;
+	fe_status_t fe_status;
+	u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */
+	u64 post_bit_error;
+	u64 post_bit_count;
 	bool sleeping;
-
-	u8 tuner;
-	u8 page; /* active register page */
 	struct delayed_work i2c_gate_work;
+	unsigned long filters; /* PID filter */
 };
 
 struct rtl2832_reg_entry {
-	u8 page;
-	u8 start_address;
+	u16 start_address;
 	u8 msb;
 	u8 lsb;
 };
@@ -52,7 +59,6 @@
 	u32 value;
 };
 
-
 /* Demod register bit names */
 enum DVBT_REG_BIT_NAME {
 	DVBT_SOFT_RST,
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
index 2896b47..3ff8806 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.c
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
@@ -22,7 +22,6 @@
  *
  */
 
-#include "dvb_frontend.h"
 #include "rtl2832_sdr.h"
 #include "dvb_usb.h"
 
@@ -32,6 +31,7 @@
 #include <media/v4l2-event.h>
 #include <media/videobuf2-vmalloc.h>
 
+#include <linux/platform_device.h>
 #include <linux/jiffies.h>
 #include <linux/math64.h>
 
@@ -107,16 +107,12 @@
 	struct list_head list;
 };
 
-struct rtl2832_sdr_state {
+struct rtl2832_sdr_dev {
 #define POWER_ON           (1 << 1)
 #define URB_BUF            (1 << 2)
 	unsigned long flags;
 
-	const struct rtl2832_config *cfg;
-	struct dvb_frontend *fe;
-	struct dvb_usb_device *d;
-	struct i2c_adapter *i2c;
-	u8 bank;
+	struct platform_device *pdev;
 
 	struct video_device vdev;
 	struct v4l2_device v4l2_dev;
@@ -160,200 +156,77 @@
 	unsigned long jiffies_next;
 };
 
-/* write multiple hardware registers */
-static int rtl2832_sdr_wr(struct rtl2832_sdr_state *s, u8 reg, const u8 *val,
-		int len)
-{
-	int ret;
-#define MAX_WR_LEN 24
-#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1)
-	u8 buf[MAX_WR_XFER_LEN];
-	struct i2c_msg msg[1] = {
-		{
-			.addr = s->cfg->i2c_addr,
-			.flags = 0,
-			.len = 1 + len,
-			.buf = buf,
-		}
-	};
-
-	if (WARN_ON(len > MAX_WR_LEN))
-		return -EINVAL;
-
-	buf[0] = reg;
-	memcpy(&buf[1], val, len);
-
-	ret = i2c_transfer(s->i2c, msg, 1);
-	if (ret == 1) {
-		ret = 0;
-	} else {
-		dev_err(&s->i2c->dev,
-			"%s: I2C wr failed=%d reg=%02x len=%d\n",
-			KBUILD_MODNAME, ret, reg, len);
-		ret = -EREMOTEIO;
-	}
-	return ret;
-}
-
-/* read multiple hardware registers */
-static int rtl2832_sdr_rd(struct rtl2832_sdr_state *s, u8 reg, u8 *val, int len)
-{
-	int ret;
-	struct i2c_msg msg[2] = {
-		{
-			.addr = s->cfg->i2c_addr,
-			.flags = 0,
-			.len = 1,
-			.buf = &reg,
-		}, {
-			.addr = s->cfg->i2c_addr,
-			.flags = I2C_M_RD,
-			.len = len,
-			.buf = val,
-		}
-	};
-
-	ret = i2c_transfer(s->i2c, msg, 2);
-	if (ret == 2) {
-		ret = 0;
-	} else {
-		dev_err(&s->i2c->dev,
-				"%s: I2C rd failed=%d reg=%02x len=%d\n",
-				KBUILD_MODNAME, ret, reg, len);
-		ret = -EREMOTEIO;
-	}
-	return ret;
-}
-
 /* write multiple registers */
-static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_state *s, u16 reg,
+static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_dev *dev, u16 reg,
 		const u8 *val, int len)
 {
-	int ret;
-	u8 reg2 = (reg >> 0) & 0xff;
-	u8 bank = (reg >> 8) & 0xff;
+	struct platform_device *pdev = dev->pdev;
+	struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+	struct i2c_client *client = pdata->i2c_client;
 
-	/* switch bank if needed */
-	if (bank != s->bank) {
-		ret = rtl2832_sdr_wr(s, 0x00, &bank, 1);
-		if (ret)
-			return ret;
-
-		s->bank = bank;
-	}
-
-	return rtl2832_sdr_wr(s, reg2, val, len);
+	return pdata->bulk_write(client, reg, val, len);
 }
 
+#if 0
 /* read multiple registers */
-static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_state *s, u16 reg, u8 *val,
+static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_dev *dev, u16 reg, u8 *val,
 		int len)
 {
-	int ret;
-	u8 reg2 = (reg >> 0) & 0xff;
-	u8 bank = (reg >> 8) & 0xff;
+	struct platform_device *pdev = dev->pdev;
+	struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+	struct i2c_client *client = pdata->i2c_client;
 
-	/* switch bank if needed */
-	if (bank != s->bank) {
-		ret = rtl2832_sdr_wr(s, 0x00, &bank, 1);
-		if (ret)
-			return ret;
-
-		s->bank = bank;
-	}
-
-	return rtl2832_sdr_rd(s, reg2, val, len);
+	return pdata->bulk_read(client, reg, val, len);
 }
+#endif
 
 /* write single register */
-static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_state *s, u16 reg, u8 val)
+static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_dev *dev, u16 reg, u8 val)
 {
-	return rtl2832_sdr_wr_regs(s, reg, &val, 1);
+	return rtl2832_sdr_wr_regs(dev, reg, &val, 1);
 }
 
-#if 0
-/* read single register */
-static int rtl2832_sdr_rd_reg(struct rtl2832_sdr_state *s, u16 reg, u8 *val)
-{
-	return rtl2832_sdr_rd_regs(s, reg, val, 1);
-}
-#endif
-
 /* write single register with mask */
-static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_state *s, u16 reg,
+static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_dev *dev, u16 reg,
 		u8 val, u8 mask)
 {
-	int ret;
-	u8 tmp;
+	struct platform_device *pdev = dev->pdev;
+	struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+	struct i2c_client *client = pdata->i2c_client;
 
-	/* no need for read if whole reg is written */
-	if (mask != 0xff) {
-		ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1);
-		if (ret)
-			return ret;
-
-		val &= mask;
-		tmp &= ~mask;
-		val |= tmp;
-	}
-
-	return rtl2832_sdr_wr_regs(s, reg, &val, 1);
+	return pdata->update_bits(client, reg, mask, val);
 }
 
-#if 0
-/* read single register with mask */
-static int rtl2832_sdr_rd_reg_mask(struct rtl2832_sdr_state *s, u16 reg,
-		u8 *val, u8 mask)
-{
-	int ret, i;
-	u8 tmp;
-
-	ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1);
-	if (ret)
-		return ret;
-
-	tmp &= mask;
-
-	/* find position of the first bit */
-	for (i = 0; i < 8; i++) {
-		if ((mask >> i) & 0x01)
-			break;
-	}
-	*val = tmp >> i;
-
-	return 0;
-}
-#endif
-
 /* Private functions */
 static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf(
-		struct rtl2832_sdr_state *s)
+		struct rtl2832_sdr_dev *dev)
 {
 	unsigned long flags;
 	struct rtl2832_sdr_frame_buf *buf = NULL;
 
-	spin_lock_irqsave(&s->queued_bufs_lock, flags);
-	if (list_empty(&s->queued_bufs))
+	spin_lock_irqsave(&dev->queued_bufs_lock, flags);
+	if (list_empty(&dev->queued_bufs))
 		goto leave;
 
-	buf = list_entry(s->queued_bufs.next,
+	buf = list_entry(dev->queued_bufs.next,
 			struct rtl2832_sdr_frame_buf, list);
 	list_del(&buf->list);
 leave:
-	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
+	spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
 	return buf;
 }
 
-static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s,
+static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_dev *dev,
 		void *dst, const u8 *src, unsigned int src_len)
 {
+	struct platform_device *pdev = dev->pdev;
 	unsigned int dst_len;
 
-	if (s->pixelformat ==  V4L2_SDR_FMT_CU8) {
+	if (dev->pixelformat ==  V4L2_SDR_FMT_CU8) {
 		/* native stream, no need to convert */
 		memcpy(dst, src, src_len);
 		dst_len = src_len;
-	} else if (s->pixelformat == V4L2_SDR_FMT_CU16LE) {
+	} else if (dev->pixelformat == V4L2_SDR_FMT_CU16LE) {
 		/* convert u8 to u16 */
 		unsigned int i;
 		u16 *u16dst = dst;
@@ -366,22 +239,21 @@
 	}
 
 	/* calculate sample rate and output it in 10 seconds intervals */
-	if (unlikely(time_is_before_jiffies(s->jiffies_next))) {
+	if (unlikely(time_is_before_jiffies(dev->jiffies_next))) {
 		#define MSECS 10000UL
 		unsigned int msecs = jiffies_to_msecs(jiffies -
-				s->jiffies_next + msecs_to_jiffies(MSECS));
-		unsigned int samples = s->sample - s->sample_measured;
+				dev->jiffies_next + msecs_to_jiffies(MSECS));
+		unsigned int samples = dev->sample - dev->sample_measured;
 
-		s->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
-		s->sample_measured = s->sample;
-		dev_dbg(&s->udev->dev,
-				"slen=%u samples=%u msecs=%u sample rate=%lu\n",
-				src_len, samples, msecs,
-				samples * 1000UL / msecs);
+		dev->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
+		dev->sample_measured = dev->sample;
+		dev_dbg(&pdev->dev,
+			"slen=%u samples=%u msecs=%u sample rate=%lu\n",
+			src_len, samples, msecs, samples * 1000UL / msecs);
 	}
 
 	/* total number of I+Q pairs */
-	s->sample += src_len / 2;
+	dev->sample += src_len / 2;
 
 	return dst_len;
 }
@@ -392,13 +264,13 @@
  */
 static void rtl2832_sdr_urb_complete(struct urb *urb)
 {
-	struct rtl2832_sdr_state *s = urb->context;
+	struct rtl2832_sdr_dev *dev = urb->context;
+	struct platform_device *pdev = dev->pdev;
 	struct rtl2832_sdr_frame_buf *fbuf;
 
-	dev_dbg_ratelimited(&s->udev->dev,
-			"status=%d length=%d/%d errors=%d\n",
-			urb->status, urb->actual_length,
-			urb->transfer_buffer_length, urb->error_count);
+	dev_dbg_ratelimited(&pdev->dev, "status=%d length=%d/%d errors=%d\n",
+			    urb->status, urb->actual_length,
+			    urb->transfer_buffer_length, urb->error_count);
 
 	switch (urb->status) {
 	case 0:             /* success */
@@ -409,8 +281,7 @@
 	case -ESHUTDOWN:
 		return;
 	default:            /* error */
-		dev_err_ratelimited(&s->udev->dev, "urb failed=%d\n",
-				urb->status);
+		dev_err_ratelimited(&pdev->dev, "urb failed=%d\n", urb->status);
 		break;
 	}
 
@@ -418,204 +289,192 @@
 		void *ptr;
 		unsigned int len;
 		/* get free framebuffer */
-		fbuf = rtl2832_sdr_get_next_fill_buf(s);
+		fbuf = rtl2832_sdr_get_next_fill_buf(dev);
 		if (unlikely(fbuf == NULL)) {
-			s->vb_full++;
-			dev_notice_ratelimited(&s->udev->dev,
-					"videobuf is full, %d packets dropped\n",
-					s->vb_full);
+			dev->vb_full++;
+			dev_notice_ratelimited(&pdev->dev,
+					       "videobuf is full, %d packets dropped\n",
+					       dev->vb_full);
 			goto skip;
 		}
 
 		/* fill framebuffer */
 		ptr = vb2_plane_vaddr(&fbuf->vb, 0);
-		len = rtl2832_sdr_convert_stream(s, ptr, urb->transfer_buffer,
+		len = rtl2832_sdr_convert_stream(dev, ptr, urb->transfer_buffer,
 				urb->actual_length);
 		vb2_set_plane_payload(&fbuf->vb, 0, len);
 		v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp);
-		fbuf->vb.v4l2_buf.sequence = s->sequence++;
+		fbuf->vb.v4l2_buf.sequence = dev->sequence++;
 		vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
 	}
 skip:
 	usb_submit_urb(urb, GFP_ATOMIC);
 }
 
-static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_state *s)
+static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_dev *dev)
 {
+	struct platform_device *pdev = dev->pdev;
 	int i;
 
-	for (i = s->urbs_submitted - 1; i >= 0; i--) {
-		dev_dbg(&s->udev->dev, "kill urb=%d\n", i);
+	for (i = dev->urbs_submitted - 1; i >= 0; i--) {
+		dev_dbg(&pdev->dev, "kill urb=%d\n", i);
 		/* stop the URB */
-		usb_kill_urb(s->urb_list[i]);
+		usb_kill_urb(dev->urb_list[i]);
 	}
-	s->urbs_submitted = 0;
+	dev->urbs_submitted = 0;
 
 	return 0;
 }
 
-static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_state *s)
+static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_dev *dev)
 {
+	struct platform_device *pdev = dev->pdev;
 	int i, ret;
 
-	for (i = 0; i < s->urbs_initialized; i++) {
-		dev_dbg(&s->udev->dev, "submit urb=%d\n", i);
-		ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC);
+	for (i = 0; i < dev->urbs_initialized; i++) {
+		dev_dbg(&pdev->dev, "submit urb=%d\n", i);
+		ret = usb_submit_urb(dev->urb_list[i], GFP_ATOMIC);
 		if (ret) {
-			dev_err(&s->udev->dev,
-					"Could not submit urb no. %d - get them all back\n",
-					i);
-			rtl2832_sdr_kill_urbs(s);
+			dev_err(&pdev->dev,
+				"Could not submit urb no. %d - get them all back\n",
+				i);
+			rtl2832_sdr_kill_urbs(dev);
 			return ret;
 		}
-		s->urbs_submitted++;
+		dev->urbs_submitted++;
 	}
 
 	return 0;
 }
 
-static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_state *s)
+static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_dev *dev)
 {
-	if (s->flags & USB_STATE_URB_BUF) {
-		while (s->buf_num) {
-			s->buf_num--;
-			dev_dbg(&s->udev->dev, "free buf=%d\n", s->buf_num);
-			usb_free_coherent(s->udev, s->buf_size,
-					  s->buf_list[s->buf_num],
-					  s->dma_addr[s->buf_num]);
+	struct platform_device *pdev = dev->pdev;
+
+	if (dev->flags & USB_STATE_URB_BUF) {
+		while (dev->buf_num) {
+			dev->buf_num--;
+			dev_dbg(&pdev->dev, "free buf=%d\n", dev->buf_num);
+			usb_free_coherent(dev->udev, dev->buf_size,
+					  dev->buf_list[dev->buf_num],
+					  dev->dma_addr[dev->buf_num]);
 		}
 	}
-	s->flags &= ~USB_STATE_URB_BUF;
+	dev->flags &= ~USB_STATE_URB_BUF;
 
 	return 0;
 }
 
-static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_state *s)
+static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_dev *dev)
 {
-	s->buf_num = 0;
-	s->buf_size = BULK_BUFFER_SIZE;
+	struct platform_device *pdev = dev->pdev;
 
-	dev_dbg(&s->udev->dev, "all in all I will use %u bytes for streaming\n",
-			MAX_BULK_BUFS * BULK_BUFFER_SIZE);
+	dev->buf_num = 0;
+	dev->buf_size = BULK_BUFFER_SIZE;
 
-	for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) {
-		s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev,
+	dev_dbg(&pdev->dev, "all in all I will use %u bytes for streaming\n",
+		MAX_BULK_BUFS * BULK_BUFFER_SIZE);
+
+	for (dev->buf_num = 0; dev->buf_num < MAX_BULK_BUFS; dev->buf_num++) {
+		dev->buf_list[dev->buf_num] = usb_alloc_coherent(dev->udev,
 				BULK_BUFFER_SIZE, GFP_ATOMIC,
-				&s->dma_addr[s->buf_num]);
-		if (!s->buf_list[s->buf_num]) {
-			dev_dbg(&s->udev->dev, "alloc buf=%d failed\n",
-					s->buf_num);
-			rtl2832_sdr_free_stream_bufs(s);
+				&dev->dma_addr[dev->buf_num]);
+		if (!dev->buf_list[dev->buf_num]) {
+			dev_dbg(&pdev->dev, "alloc buf=%d failed\n",
+				dev->buf_num);
+			rtl2832_sdr_free_stream_bufs(dev);
 			return -ENOMEM;
 		}
 
-		dev_dbg(&s->udev->dev, "alloc buf=%d %p (dma %llu)\n",
-				s->buf_num, s->buf_list[s->buf_num],
-				(long long)s->dma_addr[s->buf_num]);
-		s->flags |= USB_STATE_URB_BUF;
+		dev_dbg(&pdev->dev, "alloc buf=%d %p (dma %llu)\n",
+			dev->buf_num, dev->buf_list[dev->buf_num],
+			(long long)dev->dma_addr[dev->buf_num]);
+		dev->flags |= USB_STATE_URB_BUF;
 	}
 
 	return 0;
 }
 
-static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_state *s)
+static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_dev *dev)
 {
+	struct platform_device *pdev = dev->pdev;
 	int i;
 
-	rtl2832_sdr_kill_urbs(s);
+	rtl2832_sdr_kill_urbs(dev);
 
-	for (i = s->urbs_initialized - 1; i >= 0; i--) {
-		if (s->urb_list[i]) {
-			dev_dbg(&s->udev->dev, "free urb=%d\n", i);
+	for (i = dev->urbs_initialized - 1; i >= 0; i--) {
+		if (dev->urb_list[i]) {
+			dev_dbg(&pdev->dev, "free urb=%d\n", i);
 			/* free the URBs */
-			usb_free_urb(s->urb_list[i]);
+			usb_free_urb(dev->urb_list[i]);
 		}
 	}
-	s->urbs_initialized = 0;
+	dev->urbs_initialized = 0;
 
 	return 0;
 }
 
-static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s)
+static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev)
 {
+	struct platform_device *pdev = dev->pdev;
 	int i, j;
 
 	/* allocate the URBs */
 	for (i = 0; i < MAX_BULK_BUFS; i++) {
-		dev_dbg(&s->udev->dev, "alloc urb=%d\n", i);
-		s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
-		if (!s->urb_list[i]) {
-			dev_dbg(&s->udev->dev, "failed\n");
+		dev_dbg(&pdev->dev, "alloc urb=%d\n", i);
+		dev->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
+		if (!dev->urb_list[i]) {
+			dev_dbg(&pdev->dev, "failed\n");
 			for (j = 0; j < i; j++)
-				usb_free_urb(s->urb_list[j]);
+				usb_free_urb(dev->urb_list[j]);
 			return -ENOMEM;
 		}
-		usb_fill_bulk_urb(s->urb_list[i],
-				s->udev,
-				usb_rcvbulkpipe(s->udev, 0x81),
-				s->buf_list[i],
+		usb_fill_bulk_urb(dev->urb_list[i],
+				dev->udev,
+				usb_rcvbulkpipe(dev->udev, 0x81),
+				dev->buf_list[i],
 				BULK_BUFFER_SIZE,
-				rtl2832_sdr_urb_complete, s);
+				rtl2832_sdr_urb_complete, dev);
 
-		s->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
-		s->urb_list[i]->transfer_dma = s->dma_addr[i];
-		s->urbs_initialized++;
+		dev->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+		dev->urb_list[i]->transfer_dma = dev->dma_addr[i];
+		dev->urbs_initialized++;
 	}
 
 	return 0;
 }
 
 /* Must be called with vb_queue_lock hold */
-static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s)
+static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev)
 {
+	struct platform_device *pdev = dev->pdev;
 	unsigned long flags;
 
-	dev_dbg(&s->udev->dev, "\n");
+	dev_dbg(&pdev->dev, "\n");
 
-	spin_lock_irqsave(&s->queued_bufs_lock, flags);
-	while (!list_empty(&s->queued_bufs)) {
+	spin_lock_irqsave(&dev->queued_bufs_lock, flags);
+	while (!list_empty(&dev->queued_bufs)) {
 		struct rtl2832_sdr_frame_buf *buf;
 
-		buf = list_entry(s->queued_bufs.next,
+		buf = list_entry(dev->queued_bufs.next,
 				struct rtl2832_sdr_frame_buf, list);
 		list_del(&buf->list);
 		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
 	}
-	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
-}
-
-/* The user yanked out the cable... */
-static void rtl2832_sdr_release_sec(struct dvb_frontend *fe)
-{
-	struct rtl2832_sdr_state *s = fe->sec_priv;
-
-	dev_dbg(&s->udev->dev, "\n");
-
-	mutex_lock(&s->vb_queue_lock);
-	mutex_lock(&s->v4l2_lock);
-	/* No need to keep the urbs around after disconnection */
-	s->udev = NULL;
-
-	v4l2_device_disconnect(&s->v4l2_dev);
-	video_unregister_device(&s->vdev);
-	mutex_unlock(&s->v4l2_lock);
-	mutex_unlock(&s->vb_queue_lock);
-
-	v4l2_device_put(&s->v4l2_dev);
-
-	fe->sec_priv = NULL;
+	spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
 }
 
 static int rtl2832_sdr_querycap(struct file *file, void *fh,
 		struct v4l2_capability *cap)
 {
-	struct rtl2832_sdr_state *s = video_drvdata(file);
+	struct rtl2832_sdr_dev *dev = video_drvdata(file);
+	struct platform_device *pdev = dev->pdev;
 
-	dev_dbg(&s->udev->dev, "\n");
+	dev_dbg(&pdev->dev, "\n");
 
 	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
-	strlcpy(cap->card, s->vdev.name, sizeof(cap->card));
-	usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info));
+	strlcpy(cap->card, dev->vdev.name, sizeof(cap->card));
+	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
 	cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING |
 			V4L2_CAP_READWRITE | V4L2_CAP_TUNER;
 	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
@@ -627,26 +486,26 @@
 		const struct v4l2_format *fmt, unsigned int *nbuffers,
 		unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
 {
-	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+	struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq);
+	struct platform_device *pdev = dev->pdev;
 
-	dev_dbg(&s->udev->dev, "nbuffers=%d\n", *nbuffers);
+	dev_dbg(&pdev->dev, "nbuffers=%d\n", *nbuffers);
 
 	/* Need at least 8 buffers */
 	if (vq->num_buffers + *nbuffers < 8)
 		*nbuffers = 8 - vq->num_buffers;
 	*nplanes = 1;
-	sizes[0] = PAGE_ALIGN(s->buffersize);
-	dev_dbg(&s->udev->dev, "nbuffers=%d sizes[0]=%d\n",
-			*nbuffers, sizes[0]);
+	sizes[0] = PAGE_ALIGN(dev->buffersize);
+	dev_dbg(&pdev->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]);
 	return 0;
 }
 
 static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb)
 {
-	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue);
+	struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
 
 	/* Don't allow queing new buffers after device disconnection */
-	if (!s->udev)
+	if (!dev->udev)
 		return -ENODEV;
 
 	return 0;
@@ -654,46 +513,48 @@
 
 static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb)
 {
-	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue);
+	struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
 	struct rtl2832_sdr_frame_buf *buf =
 			container_of(vb, struct rtl2832_sdr_frame_buf, vb);
 	unsigned long flags;
 
 	/* Check the device has not disconnected between prep and queuing */
-	if (!s->udev) {
+	if (!dev->udev) {
 		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
 		return;
 	}
 
-	spin_lock_irqsave(&s->queued_bufs_lock, flags);
-	list_add_tail(&buf->list, &s->queued_bufs);
-	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
+	spin_lock_irqsave(&dev->queued_bufs_lock, flags);
+	list_add_tail(&buf->list, &dev->queued_bufs);
+	spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
 }
 
-static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s)
+static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev)
 {
-	struct dvb_frontend *fe = s->fe;
+	struct platform_device *pdev = dev->pdev;
+	struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+	struct dvb_frontend *fe = pdata->dvb_frontend;
 	int ret;
 	unsigned int f_sr, f_if;
 	u8 buf[4], u8tmp1, u8tmp2;
 	u64 u64tmp;
 	u32 u32tmp;
 
-	dev_dbg(&s->udev->dev, "f_adc=%u\n", s->f_adc);
+	dev_dbg(&pdev->dev, "f_adc=%u\n", dev->f_adc);
 
-	if (!test_bit(POWER_ON, &s->flags))
+	if (!test_bit(POWER_ON, &dev->flags))
 		return 0;
 
-	if (s->f_adc == 0)
+	if (dev->f_adc == 0)
 		return 0;
 
-	f_sr = s->f_adc;
+	f_sr = dev->f_adc;
 
-	ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x00\x00", 2);
+	ret = rtl2832_sdr_wr_regs(dev, 0x13e, "\x00\x00", 2);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_wr_regs(s, 0x115, "\x00\x00\x00\x00", 4);
+	ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x00\x00\x00\x00", 4);
 	if (ret)
 		goto err;
 
@@ -707,19 +568,19 @@
 		goto err;
 
 	/* program IF */
-	u64tmp = f_if % s->cfg->xtal;
+	u64tmp = f_if % pdata->clk;
 	u64tmp *= 0x400000;
-	u64tmp = div_u64(u64tmp, s->cfg->xtal);
+	u64tmp = div_u64(u64tmp, pdata->clk);
 	u64tmp = -u64tmp;
 	u32tmp = u64tmp & 0x3fffff;
 
-	dev_dbg(&s->udev->dev, "f_if=%u if_ctl=%08x\n", f_if, u32tmp);
+	dev_dbg(&pdev->dev, "f_if=%u if_ctl=%08x\n", f_if, u32tmp);
 
 	buf[0] = (u32tmp >> 16) & 0xff;
 	buf[1] = (u32tmp >>  8) & 0xff;
 	buf[2] = (u32tmp >>  0) & 0xff;
 
-	ret = rtl2832_sdr_wr_regs(s, 0x119, buf, 3);
+	ret = rtl2832_sdr_wr_regs(dev, 0x119, buf, 3);
 	if (ret)
 		goto err;
 
@@ -733,208 +594,212 @@
 		u8tmp2 = 0xcd; /* enable ADC I, ADC Q */
 	}
 
-	ret = rtl2832_sdr_wr_reg(s, 0x1b1, u8tmp1);
+	ret = rtl2832_sdr_wr_reg(dev, 0x1b1, u8tmp1);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_wr_reg(s, 0x008, u8tmp2);
+	ret = rtl2832_sdr_wr_reg(dev, 0x008, u8tmp2);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_wr_reg(s, 0x006, 0x80);
+	ret = rtl2832_sdr_wr_reg(dev, 0x006, 0x80);
 	if (ret)
 		goto err;
 
 	/* program sampling rate (resampling down) */
-	u32tmp = div_u64(s->cfg->xtal * 0x400000ULL, f_sr * 4U);
+	u32tmp = div_u64(pdata->clk * 0x400000ULL, f_sr * 4U);
 	u32tmp <<= 2;
 	buf[0] = (u32tmp >> 24) & 0xff;
 	buf[1] = (u32tmp >> 16) & 0xff;
 	buf[2] = (u32tmp >>  8) & 0xff;
 	buf[3] = (u32tmp >>  0) & 0xff;
-	ret = rtl2832_sdr_wr_regs(s, 0x19f, buf, 4);
+	ret = rtl2832_sdr_wr_regs(dev, 0x19f, buf, 4);
 	if (ret)
 		goto err;
 
 	/* low-pass filter */
-	ret = rtl2832_sdr_wr_regs(s, 0x11c,
+	ret = rtl2832_sdr_wr_regs(dev, 0x11c,
 			"\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5",
 			20);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2);
+	ret = rtl2832_sdr_wr_regs(dev, 0x017, "\x11\x10", 2);
 	if (ret)
 		goto err;
 
 	/* mode */
-	ret = rtl2832_sdr_wr_regs(s, 0x019, "\x05", 1);
+	ret = rtl2832_sdr_wr_regs(dev, 0x019, "\x05", 1);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_wr_regs(s, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6);
+	ret = rtl2832_sdr_wr_regs(dev, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6);
 	if (ret)
 		goto err;
 
 	/* FSM */
-	ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\xf0\x0f", 3);
+	ret = rtl2832_sdr_wr_regs(dev, 0x192, "\x00\xf0\x0f", 3);
 	if (ret)
 		goto err;
 
 	/* PID filter */
-	ret = rtl2832_sdr_wr_regs(s, 0x061, "\x60", 1);
+	ret = rtl2832_sdr_wr_regs(dev, 0x061, "\x60", 1);
 	if (ret)
 		goto err;
 
 	/* used RF tuner based settings */
-	switch (s->cfg->tuner) {
-	case RTL2832_TUNER_E4000:
-		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x30", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xd0", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x18", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xd4", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x14", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xec", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x83", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x010, "\x49", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x87", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x85", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x013, "\x02", 1);
+	switch (pdata->tuner) {
+	case RTL2832_SDR_TUNER_E4000:
+		ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x30", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xd0", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x18", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xd4", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1e5, "\xf0", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1d9, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1db, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1dd, "\x14", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1de, "\xec", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1d8, "\x0c", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1e6, "\x02", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1d7, "\x09", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x83", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x010, "\x49", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x87", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x85", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x013, "\x02", 1);
 		break;
-	case RTL2832_TUNER_FC0012:
-	case RTL2832_TUNER_FC0013:
-		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x2c", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x16", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xe9\xbf", 2);
-		ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x11", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xef", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1);
+	case RTL2832_SDR_TUNER_FC0012:
+	case RTL2832_SDR_TUNER_FC0013:
+		ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x2c", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x16", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xe9\xbf", 2);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1e5, "\xf0", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1d9, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1db, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1dd, "\x11", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1de, "\xef", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1d8, "\x0c", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1e6, "\x02", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1d7, "\x09", 1);
 		break;
-	case RTL2832_TUNER_R820T:
-		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x115, "\x01", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x80", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x24", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x14", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
-		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xf4", 1);
+	case RTL2832_SDR_TUNER_R820T:
+	case RTL2832_SDR_TUNER_R828D:
+		ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x01", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x24", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x14", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xf4", 1);
 		break;
 	default:
-		dev_notice(&s->udev->dev, "Unsupported tuner\n");
+		dev_notice(&pdev->dev, "Unsupported tuner\n");
 	}
 
 	/* software reset */
-	ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x04, 0x04);
+	ret = rtl2832_sdr_wr_reg_mask(dev, 0x101, 0x04, 0x04);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x00, 0x04);
+	ret = rtl2832_sdr_wr_reg_mask(dev, 0x101, 0x00, 0x04);
 	if (ret)
 		goto err;
 err:
 	return ret;
 };
 
-static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_state *s)
+static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_dev *dev)
 {
+	struct platform_device *pdev = dev->pdev;
 	int ret;
 
-	dev_dbg(&s->udev->dev, "\n");
+	dev_dbg(&pdev->dev, "\n");
 
 	/* PID filter */
-	ret = rtl2832_sdr_wr_regs(s, 0x061, "\xe0", 1);
+	ret = rtl2832_sdr_wr_regs(dev, 0x061, "\xe0", 1);
 	if (ret)
 		goto err;
 
 	/* mode */
-	ret = rtl2832_sdr_wr_regs(s, 0x019, "\x20", 1);
+	ret = rtl2832_sdr_wr_regs(dev, 0x019, "\x20", 1);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2);
+	ret = rtl2832_sdr_wr_regs(dev, 0x017, "\x11\x10", 2);
 	if (ret)
 		goto err;
 
 	/* FSM */
-	ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\x0f\xff", 3);
+	ret = rtl2832_sdr_wr_regs(dev, 0x192, "\x00\x0f\xff", 3);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x40\x00", 2);
+	ret = rtl2832_sdr_wr_regs(dev, 0x13e, "\x40\x00", 2);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_wr_regs(s, 0x115, "\x06\x3f\xce\xcc", 4);
+	ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x06\x3f\xce\xcc", 4);
 	if (ret)
 		goto err;
 err:
 	return;
 };
 
-static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s)
+static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_dev *dev)
 {
-	struct dvb_frontend *fe = s->fe;
+	struct platform_device *pdev = dev->pdev;
+	struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+	struct dvb_frontend *fe = pdata->dvb_frontend;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	struct v4l2_ctrl *bandwidth_auto;
 	struct v4l2_ctrl *bandwidth;
@@ -942,29 +807,29 @@
 	/*
 	 * tuner RF (Hz)
 	 */
-	if (s->f_tuner == 0)
+	if (dev->f_tuner == 0)
 		return 0;
 
 	/*
 	 * bandwidth (Hz)
 	 */
-	bandwidth_auto = v4l2_ctrl_find(&s->hdl,
+	bandwidth_auto = v4l2_ctrl_find(&dev->hdl,
 					V4L2_CID_RF_TUNER_BANDWIDTH_AUTO);
-	bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH);
+	bandwidth = v4l2_ctrl_find(&dev->hdl, V4L2_CID_RF_TUNER_BANDWIDTH);
 	if (v4l2_ctrl_g_ctrl(bandwidth_auto)) {
-		c->bandwidth_hz = s->f_adc;
-		v4l2_ctrl_s_ctrl(bandwidth, s->f_adc);
+		c->bandwidth_hz = dev->f_adc;
+		v4l2_ctrl_s_ctrl(bandwidth, dev->f_adc);
 	} else {
 		c->bandwidth_hz = v4l2_ctrl_g_ctrl(bandwidth);
 	}
 
-	c->frequency = s->f_tuner;
+	c->frequency = dev->f_tuner;
 	c->delivery_system = SYS_DVBT;
 
-	dev_dbg(&s->udev->dev, "frequency=%u bandwidth=%d\n",
-			c->frequency, c->bandwidth_hz);
+	dev_dbg(&pdev->dev, "frequency=%u bandwidth=%d\n",
+		c->frequency, c->bandwidth_hz);
 
-	if (!test_bit(POWER_ON, &s->flags))
+	if (!test_bit(POWER_ON, &dev->flags))
 		return 0;
 
 	if (fe->ops.tuner_ops.set_params)
@@ -973,11 +838,13 @@
 	return 0;
 };
 
-static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_state *s)
+static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_dev *dev)
 {
-	struct dvb_frontend *fe = s->fe;
+	struct platform_device *pdev = dev->pdev;
+	struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+	struct dvb_frontend *fe = pdata->dvb_frontend;
 
-	dev_dbg(&s->udev->dev, "\n");
+	dev_dbg(&pdev->dev, "\n");
 
 	if (fe->ops.tuner_ops.init)
 		fe->ops.tuner_ops.init(fe);
@@ -985,11 +852,13 @@
 	return 0;
 };
 
-static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_state *s)
+static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_dev *dev)
 {
-	struct dvb_frontend *fe = s->fe;
+	struct platform_device *pdev = dev->pdev;
+	struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+	struct dvb_frontend *fe = pdata->dvb_frontend;
 
-	dev_dbg(&s->udev->dev, "\n");
+	dev_dbg(&pdev->dev, "\n");
 
 	if (fe->ops.tuner_ops.sleep)
 		fe->ops.tuner_ops.sleep(fe);
@@ -999,83 +868,89 @@
 
 static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
-	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+	struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq);
+	struct platform_device *pdev = dev->pdev;
+	struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+	struct dvb_usb_device *d = pdata->dvb_usb_device;
 	int ret;
 
-	dev_dbg(&s->udev->dev, "\n");
+	dev_dbg(&pdev->dev, "\n");
 
-	if (!s->udev)
+	if (!dev->udev)
 		return -ENODEV;
 
-	if (mutex_lock_interruptible(&s->v4l2_lock))
+	if (mutex_lock_interruptible(&dev->v4l2_lock))
 		return -ERESTARTSYS;
 
-	if (s->d->props->power_ctrl)
-		s->d->props->power_ctrl(s->d, 1);
+	if (d->props->power_ctrl)
+		d->props->power_ctrl(d, 1);
 
 	/* enable ADC */
-	if (s->d->props->frontend_ctrl)
-		s->d->props->frontend_ctrl(s->fe, 1);
+	if (d->props->frontend_ctrl)
+		d->props->frontend_ctrl(pdata->dvb_frontend, 1);
 
-	set_bit(POWER_ON, &s->flags);
+	set_bit(POWER_ON, &dev->flags);
 
-	ret = rtl2832_sdr_set_tuner(s);
+	ret = rtl2832_sdr_set_tuner(dev);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_set_tuner_freq(s);
+	ret = rtl2832_sdr_set_tuner_freq(dev);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_set_adc(s);
+	ret = rtl2832_sdr_set_adc(dev);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_alloc_stream_bufs(s);
+	ret = rtl2832_sdr_alloc_stream_bufs(dev);
 	if (ret)
 		goto err;
 
-	ret = rtl2832_sdr_alloc_urbs(s);
+	ret = rtl2832_sdr_alloc_urbs(dev);
 	if (ret)
 		goto err;
 
-	s->sequence = 0;
+	dev->sequence = 0;
 
-	ret = rtl2832_sdr_submit_urbs(s);
+	ret = rtl2832_sdr_submit_urbs(dev);
 	if (ret)
 		goto err;
 
 err:
-	mutex_unlock(&s->v4l2_lock);
+	mutex_unlock(&dev->v4l2_lock);
 
 	return ret;
 }
 
 static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
 {
-	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+	struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq);
+	struct platform_device *pdev = dev->pdev;
+	struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+	struct dvb_usb_device *d = pdata->dvb_usb_device;
 
-	dev_dbg(&s->udev->dev, "\n");
+	dev_dbg(&pdev->dev, "\n");
 
-	mutex_lock(&s->v4l2_lock);
+	mutex_lock(&dev->v4l2_lock);
 
-	rtl2832_sdr_kill_urbs(s);
-	rtl2832_sdr_free_urbs(s);
-	rtl2832_sdr_free_stream_bufs(s);
-	rtl2832_sdr_cleanup_queued_bufs(s);
-	rtl2832_sdr_unset_adc(s);
-	rtl2832_sdr_unset_tuner(s);
+	rtl2832_sdr_kill_urbs(dev);
+	rtl2832_sdr_free_urbs(dev);
+	rtl2832_sdr_free_stream_bufs(dev);
+	rtl2832_sdr_cleanup_queued_bufs(dev);
+	rtl2832_sdr_unset_adc(dev);
+	rtl2832_sdr_unset_tuner(dev);
 
-	clear_bit(POWER_ON, &s->flags);
+	clear_bit(POWER_ON, &dev->flags);
 
 	/* disable ADC */
-	if (s->d->props->frontend_ctrl)
-		s->d->props->frontend_ctrl(s->fe, 0);
+	if (d->props->frontend_ctrl)
+		d->props->frontend_ctrl(pdata->dvb_frontend, 0);
 
-	if (s->d->props->power_ctrl)
-		s->d->props->power_ctrl(s->d, 0);
+	if (d->props->power_ctrl)
+		d->props->power_ctrl(d, 0);
 
-	mutex_unlock(&s->v4l2_lock);
+	mutex_unlock(&dev->v4l2_lock);
 }
 
 static struct vb2_ops rtl2832_sdr_vb2_ops = {
@@ -1091,9 +966,10 @@
 static int rtl2832_sdr_g_tuner(struct file *file, void *priv,
 		struct v4l2_tuner *v)
 {
-	struct rtl2832_sdr_state *s = video_drvdata(file);
+	struct rtl2832_sdr_dev *dev = video_drvdata(file);
+	struct platform_device *pdev = dev->pdev;
 
-	dev_dbg(&s->udev->dev, "index=%d type=%d\n", v->index, v->type);
+	dev_dbg(&pdev->dev, "index=%d type=%d\n", v->index, v->type);
 
 	if (v->index == 0) {
 		strlcpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name));
@@ -1117,9 +993,10 @@
 static int rtl2832_sdr_s_tuner(struct file *file, void *priv,
 		const struct v4l2_tuner *v)
 {
-	struct rtl2832_sdr_state *s = video_drvdata(file);
+	struct rtl2832_sdr_dev *dev = video_drvdata(file);
+	struct platform_device *pdev = dev->pdev;
 
-	dev_dbg(&s->udev->dev, "\n");
+	dev_dbg(&pdev->dev, "\n");
 
 	if (v->index > 1)
 		return -EINVAL;
@@ -1129,10 +1006,11 @@
 static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv,
 		struct v4l2_frequency_band *band)
 {
-	struct rtl2832_sdr_state *s = video_drvdata(file);
+	struct rtl2832_sdr_dev *dev = video_drvdata(file);
+	struct platform_device *pdev = dev->pdev;
 
-	dev_dbg(&s->udev->dev, "tuner=%d type=%d index=%d\n",
-			band->tuner, band->type, band->index);
+	dev_dbg(&pdev->dev, "tuner=%d type=%d index=%d\n",
+		band->tuner, band->type, band->index);
 
 	if (band->tuner == 0) {
 		if (band->index >= ARRAY_SIZE(bands_adc))
@@ -1154,17 +1032,17 @@
 static int rtl2832_sdr_g_frequency(struct file *file, void *priv,
 		struct v4l2_frequency *f)
 {
-	struct rtl2832_sdr_state *s = video_drvdata(file);
+	struct rtl2832_sdr_dev *dev = video_drvdata(file);
+	struct platform_device *pdev = dev->pdev;
 	int ret  = 0;
 
-	dev_dbg(&s->udev->dev, "tuner=%d type=%d\n",
-			f->tuner, f->type);
+	dev_dbg(&pdev->dev, "tuner=%d type=%d\n", f->tuner, f->type);
 
 	if (f->tuner == 0) {
-		f->frequency = s->f_adc;
+		f->frequency = dev->f_adc;
 		f->type = V4L2_TUNER_ADC;
 	} else if (f->tuner == 1) {
-		f->frequency = s->f_tuner;
+		f->frequency = dev->f_tuner;
 		f->type = V4L2_TUNER_RF;
 	} else {
 		return -EINVAL;
@@ -1176,11 +1054,12 @@
 static int rtl2832_sdr_s_frequency(struct file *file, void *priv,
 		const struct v4l2_frequency *f)
 {
-	struct rtl2832_sdr_state *s = video_drvdata(file);
+	struct rtl2832_sdr_dev *dev = video_drvdata(file);
+	struct platform_device *pdev = dev->pdev;
 	int ret, band;
 
-	dev_dbg(&s->udev->dev, "tuner=%d type=%d frequency=%u\n",
-			f->tuner, f->type, f->frequency);
+	dev_dbg(&pdev->dev, "tuner=%d type=%d frequency=%u\n",
+		f->tuner, f->type, f->frequency);
 
 	/* ADC band midpoints */
 	#define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2)
@@ -1194,19 +1073,19 @@
 		else
 			band = 2;
 
-		s->f_adc = clamp_t(unsigned int, f->frequency,
+		dev->f_adc = clamp_t(unsigned int, f->frequency,
 				bands_adc[band].rangelow,
 				bands_adc[band].rangehigh);
 
-		dev_dbg(&s->udev->dev, "ADC frequency=%u Hz\n", s->f_adc);
-		ret = rtl2832_sdr_set_adc(s);
+		dev_dbg(&pdev->dev, "ADC frequency=%u Hz\n", dev->f_adc);
+		ret = rtl2832_sdr_set_adc(dev);
 	} else if (f->tuner == 1) {
-		s->f_tuner = clamp_t(unsigned int, f->frequency,
+		dev->f_tuner = clamp_t(unsigned int, f->frequency,
 				bands_fm[0].rangelow,
 				bands_fm[0].rangehigh);
-		dev_dbg(&s->udev->dev, "RF frequency=%u Hz\n", f->frequency);
+		dev_dbg(&pdev->dev, "RF frequency=%u Hz\n", f->frequency);
 
-		ret = rtl2832_sdr_set_tuner_freq(s);
+		ret = rtl2832_sdr_set_tuner_freq(dev);
 	} else {
 		ret = -EINVAL;
 	}
@@ -1217,11 +1096,12 @@
 static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv,
 		struct v4l2_fmtdesc *f)
 {
-	struct rtl2832_sdr_state *s = video_drvdata(file);
+	struct rtl2832_sdr_dev *dev = video_drvdata(file);
+	struct platform_device *pdev = dev->pdev;
 
-	dev_dbg(&s->udev->dev, "\n");
+	dev_dbg(&pdev->dev, "\n");
 
-	if (f->index >= s->num_formats)
+	if (f->index >= dev->num_formats)
 		return -EINVAL;
 
 	strlcpy(f->description, formats[f->index].name, sizeof(f->description));
@@ -1233,12 +1113,13 @@
 static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv,
 		struct v4l2_format *f)
 {
-	struct rtl2832_sdr_state *s = video_drvdata(file);
+	struct rtl2832_sdr_dev *dev = video_drvdata(file);
+	struct platform_device *pdev = dev->pdev;
 
-	dev_dbg(&s->udev->dev, "\n");
+	dev_dbg(&pdev->dev, "\n");
 
-	f->fmt.sdr.pixelformat = s->pixelformat;
-	f->fmt.sdr.buffersize = s->buffersize;
+	f->fmt.sdr.pixelformat = dev->pixelformat;
+	f->fmt.sdr.buffersize = dev->buffersize;
 
 	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
 
@@ -1248,28 +1129,29 @@
 static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv,
 		struct v4l2_format *f)
 {
-	struct rtl2832_sdr_state *s = video_drvdata(file);
-	struct vb2_queue *q = &s->vb_queue;
+	struct rtl2832_sdr_dev *dev = video_drvdata(file);
+	struct platform_device *pdev = dev->pdev;
+	struct vb2_queue *q = &dev->vb_queue;
 	int i;
 
-	dev_dbg(&s->udev->dev, "pixelformat fourcc %4.4s\n",
-			(char *)&f->fmt.sdr.pixelformat);
+	dev_dbg(&pdev->dev, "pixelformat fourcc %4.4s\n",
+		(char *)&f->fmt.sdr.pixelformat);
 
 	if (vb2_is_busy(q))
 		return -EBUSY;
 
 	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
-	for (i = 0; i < s->num_formats; i++) {
+	for (i = 0; i < dev->num_formats; i++) {
 		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
-			s->pixelformat = formats[i].pixelformat;
-			s->buffersize = formats[i].buffersize;
+			dev->pixelformat = formats[i].pixelformat;
+			dev->buffersize = formats[i].buffersize;
 			f->fmt.sdr.buffersize = formats[i].buffersize;
 			return 0;
 		}
 	}
 
-	s->pixelformat = formats[0].pixelformat;
-	s->buffersize = formats[0].buffersize;
+	dev->pixelformat = formats[0].pixelformat;
+	dev->buffersize = formats[0].buffersize;
 	f->fmt.sdr.pixelformat = formats[0].pixelformat;
 	f->fmt.sdr.buffersize = formats[0].buffersize;
 
@@ -1279,14 +1161,15 @@
 static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv,
 		struct v4l2_format *f)
 {
-	struct rtl2832_sdr_state *s = video_drvdata(file);
+	struct rtl2832_sdr_dev *dev = video_drvdata(file);
+	struct platform_device *pdev = dev->pdev;
 	int i;
 
-	dev_dbg(&s->udev->dev, "pixelformat fourcc %4.4s\n",
-			(char *)&f->fmt.sdr.pixelformat);
+	dev_dbg(&pdev->dev, "pixelformat fourcc %4.4s\n",
+		(char *)&f->fmt.sdr.pixelformat);
 
 	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
-	for (i = 0; i < s->num_formats; i++) {
+	for (i = 0; i < dev->num_formats; i++) {
 		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
 			f->fmt.sdr.buffersize = formats[i].buffersize;
 			return 0;
@@ -1348,37 +1231,38 @@
 
 static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct rtl2832_sdr_state *s =
-			container_of(ctrl->handler, struct rtl2832_sdr_state,
+	struct rtl2832_sdr_dev *dev =
+			container_of(ctrl->handler, struct rtl2832_sdr_dev,
 					hdl);
-	struct dvb_frontend *fe = s->fe;
+	struct platform_device *pdev = dev->pdev;
+	struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+	struct dvb_frontend *fe = pdata->dvb_frontend;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret;
 
-	dev_dbg(&s->udev->dev,
-			"id=%d name=%s val=%d min=%lld max=%lld step=%lld\n",
-			ctrl->id, ctrl->name, ctrl->val,
-			ctrl->minimum, ctrl->maximum, ctrl->step);
+	dev_dbg(&pdev->dev, "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n",
+		ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum,
+		ctrl->step);
 
 	switch (ctrl->id) {
 	case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
 	case V4L2_CID_RF_TUNER_BANDWIDTH:
 		/* TODO: these controls should be moved to tuner drivers */
-		if (s->bandwidth_auto->val) {
+		if (dev->bandwidth_auto->val) {
 			/* Round towards the closest legal value */
-			s32 val = s->f_adc + div_u64(s->bandwidth->step, 2);
+			s32 val = dev->f_adc + div_u64(dev->bandwidth->step, 2);
 			u32 offset;
 
-			val = clamp_t(s32, val, s->bandwidth->minimum,
-				      s->bandwidth->maximum);
-			offset = val - s->bandwidth->minimum;
-			offset = s->bandwidth->step *
-				div_u64(offset, s->bandwidth->step);
-			s->bandwidth->val = s->bandwidth->minimum + offset;
+			val = clamp_t(s32, val, dev->bandwidth->minimum,
+				      dev->bandwidth->maximum);
+			offset = val - dev->bandwidth->minimum;
+			offset = dev->bandwidth->step *
+				div_u64(offset, dev->bandwidth->step);
+			dev->bandwidth->val = dev->bandwidth->minimum + offset;
 		}
-		c->bandwidth_hz = s->bandwidth->val;
+		c->bandwidth_hz = dev->bandwidth->val;
 
-		if (!test_bit(POWER_ON, &s->flags))
+		if (!test_bit(POWER_ON, &dev->flags))
 			return 0;
 
 		if (fe->ops.tuner_ops.set_params)
@@ -1399,154 +1283,195 @@
 
 static void rtl2832_sdr_video_release(struct v4l2_device *v)
 {
-	struct rtl2832_sdr_state *s =
-			container_of(v, struct rtl2832_sdr_state, v4l2_dev);
+	struct rtl2832_sdr_dev *dev =
+			container_of(v, struct rtl2832_sdr_dev, v4l2_dev);
+	struct platform_device *pdev = dev->pdev;
 
-	v4l2_ctrl_handler_free(&s->hdl);
-	v4l2_device_unregister(&s->v4l2_dev);
-	kfree(s);
+	dev_dbg(&pdev->dev, "\n");
+
+	v4l2_ctrl_handler_free(&dev->hdl);
+	v4l2_device_unregister(&dev->v4l2_dev);
+	kfree(dev);
 }
 
-struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
-		struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
-		struct v4l2_subdev *sd)
+/* Platform driver interface */
+static int rtl2832_sdr_probe(struct platform_device *pdev)
 {
-	int ret;
-	struct rtl2832_sdr_state *s;
+	struct rtl2832_sdr_dev *dev;
+	struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
 	const struct v4l2_ctrl_ops *ops = &rtl2832_sdr_ctrl_ops;
-	struct dvb_usb_device *d = i2c_get_adapdata(i2c);
+	struct v4l2_subdev *subdev;
+	int ret;
 
-	s = kzalloc(sizeof(struct rtl2832_sdr_state), GFP_KERNEL);
-	if (s == NULL) {
-		dev_err(&d->udev->dev,
-				"Could not allocate memory for rtl2832_sdr_state\n");
-		return NULL;
+	dev_dbg(&pdev->dev, "\n");
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "Cannot proceed without platform data\n");
+		ret = -EINVAL;
+		goto err;
+	}
+	if (!pdev->dev.parent->driver) {
+		dev_dbg(&pdev->dev, "No parent device\n");
+		ret = -EINVAL;
+		goto err;
+	}
+	/* try to refcount host drv since we are the consumer */
+	if (!try_module_get(pdev->dev.parent->driver->owner)) {
+		dev_err(&pdev->dev, "Refcount fail");
+		ret = -EINVAL;
+		goto err;
+	}
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL) {
+		ret = -ENOMEM;
+		goto err_module_put;
 	}
 
 	/* setup the state */
-	s->fe = fe;
-	s->d = d;
-	s->udev = d->udev;
-	s->i2c = i2c;
-	s->cfg = cfg;
-	s->f_adc = bands_adc[0].rangelow;
-	s->f_tuner = bands_fm[0].rangelow;
-	s->pixelformat = formats[0].pixelformat;
-	s->buffersize = formats[0].buffersize;
-	s->num_formats = NUM_FORMATS;
+	subdev = pdata->v4l2_subdev;
+	dev->pdev = pdev;
+	dev->udev = pdata->dvb_usb_device->udev;
+	dev->f_adc = bands_adc[0].rangelow;
+	dev->f_tuner = bands_fm[0].rangelow;
+	dev->pixelformat = formats[0].pixelformat;
+	dev->buffersize = formats[0].buffersize;
+	dev->num_formats = NUM_FORMATS;
 	if (!rtl2832_sdr_emulated_fmt)
-		s->num_formats -= 1;
+		dev->num_formats -= 1;
 
-	mutex_init(&s->v4l2_lock);
-	mutex_init(&s->vb_queue_lock);
-	spin_lock_init(&s->queued_bufs_lock);
-	INIT_LIST_HEAD(&s->queued_bufs);
+	mutex_init(&dev->v4l2_lock);
+	mutex_init(&dev->vb_queue_lock);
+	spin_lock_init(&dev->queued_bufs_lock);
+	INIT_LIST_HEAD(&dev->queued_bufs);
 
 	/* Init videobuf2 queue structure */
-	s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
-	s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
-	s->vb_queue.drv_priv = s;
-	s->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf);
-	s->vb_queue.ops = &rtl2832_sdr_vb2_ops;
-	s->vb_queue.mem_ops = &vb2_vmalloc_memops;
-	s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-	ret = vb2_queue_init(&s->vb_queue);
+	dev->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
+	dev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+	dev->vb_queue.drv_priv = dev;
+	dev->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf);
+	dev->vb_queue.ops = &rtl2832_sdr_vb2_ops;
+	dev->vb_queue.mem_ops = &vb2_vmalloc_memops;
+	dev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	ret = vb2_queue_init(&dev->vb_queue);
 	if (ret) {
-		dev_err(&s->udev->dev, "Could not initialize vb2 queue\n");
-		goto err_free_mem;
+		dev_err(&pdev->dev, "Could not initialize vb2 queue\n");
+		goto err_kfree;
 	}
 
 	/* Register controls */
-	switch (s->cfg->tuner) {
-	case RTL2832_TUNER_E4000:
-		v4l2_ctrl_handler_init(&s->hdl, 9);
-		if (sd)
-			v4l2_ctrl_add_handler(&s->hdl, sd->ctrl_handler, NULL);
+	switch (pdata->tuner) {
+	case RTL2832_SDR_TUNER_E4000:
+		v4l2_ctrl_handler_init(&dev->hdl, 9);
+		if (subdev)
+			v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler, NULL);
 		break;
-	case RTL2832_TUNER_R820T:
-		v4l2_ctrl_handler_init(&s->hdl, 2);
-		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops,
-						      V4L2_CID_RF_TUNER_BANDWIDTH_AUTO,
-						      0, 1, 1, 1);
-		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops,
-						 V4L2_CID_RF_TUNER_BANDWIDTH,
-						 0, 8000000, 100000, 0);
-		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+	case RTL2832_SDR_TUNER_R820T:
+	case RTL2832_SDR_TUNER_R828D:
+		v4l2_ctrl_handler_init(&dev->hdl, 2);
+		dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, ops,
+							V4L2_CID_RF_TUNER_BANDWIDTH_AUTO,
+							0, 1, 1, 1);
+		dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, ops,
+						   V4L2_CID_RF_TUNER_BANDWIDTH,
+						   0, 8000000, 100000, 0);
+		v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false);
 		break;
-	case RTL2832_TUNER_FC0012:
-	case RTL2832_TUNER_FC0013:
-		v4l2_ctrl_handler_init(&s->hdl, 2);
-		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops,
-						      V4L2_CID_RF_TUNER_BANDWIDTH_AUTO,
-						      0, 1, 1, 1);
-		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops,
-						 V4L2_CID_RF_TUNER_BANDWIDTH,
-						 6000000, 8000000, 1000000,
-						 6000000);
-		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+	case RTL2832_SDR_TUNER_FC0012:
+	case RTL2832_SDR_TUNER_FC0013:
+		v4l2_ctrl_handler_init(&dev->hdl, 2);
+		dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, ops,
+							V4L2_CID_RF_TUNER_BANDWIDTH_AUTO,
+							0, 1, 1, 1);
+		dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, ops,
+						   V4L2_CID_RF_TUNER_BANDWIDTH,
+						   6000000, 8000000, 1000000,
+						   6000000);
+		v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false);
 		break;
 	default:
-		v4l2_ctrl_handler_init(&s->hdl, 0);
-		dev_notice(&s->udev->dev, "%s: Unsupported tuner\n",
-				KBUILD_MODNAME);
-		goto err_free_controls;
+		v4l2_ctrl_handler_init(&dev->hdl, 0);
+		dev_err(&pdev->dev, "Unsupported tuner\n");
+		goto err_v4l2_ctrl_handler_free;
 	}
-
-	if (s->hdl.error) {
-		ret = s->hdl.error;
-		dev_err(&s->udev->dev, "Could not initialize controls\n");
-		goto err_free_controls;
+	if (dev->hdl.error) {
+		ret = dev->hdl.error;
+		dev_err(&pdev->dev, "Could not initialize controls\n");
+		goto err_v4l2_ctrl_handler_free;
 	}
 
 	/* Init video_device structure */
-	s->vdev = rtl2832_sdr_template;
-	s->vdev.queue = &s->vb_queue;
-	s->vdev.queue->lock = &s->vb_queue_lock;
-	video_set_drvdata(&s->vdev, s);
+	dev->vdev = rtl2832_sdr_template;
+	dev->vdev.queue = &dev->vb_queue;
+	dev->vdev.queue->lock = &dev->vb_queue_lock;
+	video_set_drvdata(&dev->vdev, dev);
 
 	/* Register the v4l2_device structure */
-	s->v4l2_dev.release = rtl2832_sdr_video_release;
-	ret = v4l2_device_register(&s->udev->dev, &s->v4l2_dev);
+	dev->v4l2_dev.release = rtl2832_sdr_video_release;
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
 	if (ret) {
-		dev_err(&s->udev->dev,
-				"Failed to register v4l2-device (%d)\n", ret);
-		goto err_free_controls;
+		dev_err(&pdev->dev, "Failed to register v4l2-device %d\n", ret);
+		goto err_v4l2_ctrl_handler_free;
 	}
 
-	s->v4l2_dev.ctrl_handler = &s->hdl;
-	s->vdev.v4l2_dev = &s->v4l2_dev;
-	s->vdev.lock = &s->v4l2_lock;
-	s->vdev.vfl_dir = VFL_DIR_RX;
+	dev->v4l2_dev.ctrl_handler = &dev->hdl;
+	dev->vdev.v4l2_dev = &dev->v4l2_dev;
+	dev->vdev.lock = &dev->v4l2_lock;
+	dev->vdev.vfl_dir = VFL_DIR_RX;
 
-	ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1);
+	ret = video_register_device(&dev->vdev, VFL_TYPE_SDR, -1);
 	if (ret) {
-		dev_err(&s->udev->dev,
-				"Failed to register as video device (%d)\n",
-				ret);
-		goto err_unregister_v4l2_dev;
+		dev_err(&pdev->dev, "Failed to register as video device %d\n",
+			ret);
+		goto err_v4l2_device_unregister;
 	}
-	dev_info(&s->udev->dev, "Registered as %s\n",
-			video_device_node_name(&s->vdev));
-
-	fe->sec_priv = s;
-	fe->ops.release_sec = rtl2832_sdr_release_sec;
-
-	dev_info(&s->i2c->dev, "%s: Realtek RTL2832 SDR attached\n",
-			KBUILD_MODNAME);
-	dev_notice(&s->udev->dev,
-			"%s: SDR API is still slightly experimental and functionality changes may follow\n",
-			KBUILD_MODNAME);
-	return fe;
-
-err_unregister_v4l2_dev:
-	v4l2_device_unregister(&s->v4l2_dev);
-err_free_controls:
-	v4l2_ctrl_handler_free(&s->hdl);
-err_free_mem:
-	kfree(s);
-	return NULL;
+	dev_info(&pdev->dev, "Registered as %s\n",
+		 video_device_node_name(&dev->vdev));
+	dev_info(&pdev->dev, "Realtek RTL2832 SDR attached\n");
+	dev_notice(&pdev->dev,
+		   "SDR API is still slightly experimental and functionality changes may follow\n");
+	platform_set_drvdata(pdev, dev);
+	return 0;
+err_v4l2_device_unregister:
+	v4l2_device_unregister(&dev->v4l2_dev);
+err_v4l2_ctrl_handler_free:
+	v4l2_ctrl_handler_free(&dev->hdl);
+err_kfree:
+	kfree(dev);
+err_module_put:
+	module_put(pdev->dev.parent->driver->owner);
+err:
+	return ret;
 }
-EXPORT_SYMBOL(rtl2832_sdr_attach);
+
+static int rtl2832_sdr_remove(struct platform_device *pdev)
+{
+	struct rtl2832_sdr_dev *dev = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "\n");
+
+	mutex_lock(&dev->vb_queue_lock);
+	mutex_lock(&dev->v4l2_lock);
+	/* No need to keep the urbs around after disconnection */
+	dev->udev = NULL;
+	v4l2_device_disconnect(&dev->v4l2_dev);
+	video_unregister_device(&dev->vdev);
+	mutex_unlock(&dev->v4l2_lock);
+	mutex_unlock(&dev->vb_queue_lock);
+	v4l2_device_put(&dev->v4l2_dev);
+	module_put(pdev->dev.parent->driver->owner);
+
+	return 0;
+}
+
+static struct platform_driver rtl2832_sdr_driver = {
+	.driver = {
+		.name   = "rtl2832_sdr",
+		.owner  = THIS_MODULE,
+	},
+	.probe          = rtl2832_sdr_probe,
+	.remove         = rtl2832_sdr_remove,
+};
+module_platform_driver(rtl2832_sdr_driver);
 
 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
 MODULE_DESCRIPTION("Realtek RTL2832 SDR driver");
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.h b/drivers/media/dvb-frontends/rtl2832_sdr.h
index 3428e95..d259476 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.h
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.h
@@ -20,35 +20,48 @@
  * GNU Radio plugin "gr-kernel" for device usage will be on:
  * http://git.linuxtv.org/anttip/gr-kernel.git
  *
- * TODO:
- * Help is very highly welcome for these + all the others you could imagine:
- * - move controls to V4L2 API
- * - use libv4l2 for stream format conversions
- * - gr-kernel: switch to v4l2_mmap (current read eats a lot of cpu)
- * - SDRSharp support
  */
 
 #ifndef RTL2832_SDR_H
 #define RTL2832_SDR_H
 
-#include <linux/kconfig.h>
+#include <linux/i2c.h>
 #include <media/v4l2-subdev.h>
+#include "dvb_frontend.h"
 
-/* for config struct */
-#include "rtl2832.h"
+/**
+ * struct rtl2832_sdr_platform_data - Platform data for the rtl2832_sdr driver
+ * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000).
+ * @tuner: Used tuner model.
+ * @i2c_client: rtl2832 demod driver I2C client.
+ * @bulk_read: rtl2832 driver private I/O interface.
+ * @bulk_write: rtl2832 driver private I/O interface.
+ * @update_bits: rtl2832 driver private I/O interface.
+ * @dvb_frontend: rtl2832 DVB frontend.
+ * @v4l2_subdev: Tuner v4l2 controls.
+ * @dvb_usb_device: DVB USB interface for USB streaming.
+ */
 
-#if IS_ENABLED(CPTCFG_DVB_RTL2832_SDR)
-extern struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
-	struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
-	struct v4l2_subdev *sd);
-#else
-static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
-	struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
-	struct v4l2_subdev *sd)
-{
-	dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
-	return NULL;
-}
-#endif
+struct rtl2832_sdr_platform_data {
+	u32 clk;
+	/*
+	 * XXX: This list must be kept sync with dvb_usb_rtl28xxu USB IF driver.
+	 */
+#define RTL2832_SDR_TUNER_TUA9001   0x24
+#define RTL2832_SDR_TUNER_FC0012    0x26
+#define RTL2832_SDR_TUNER_E4000     0x27
+#define RTL2832_SDR_TUNER_FC0013    0x29
+#define RTL2832_SDR_TUNER_R820T     0x2a
+#define RTL2832_SDR_TUNER_R828D     0x2b
+	u8 tuner;
+
+	struct i2c_client *i2c_client;
+	int (*bulk_read)(struct i2c_client *, unsigned int, void *, size_t);
+	int (*bulk_write)(struct i2c_client *, unsigned int, const void *, size_t);
+	int (*update_bits)(struct i2c_client *, unsigned int, unsigned int, unsigned int);
+	struct dvb_frontend *dvb_frontend;
+	struct v4l2_subdev *v4l2_subdev;
+	struct dvb_usb_device *dvb_usb_device;
+};
 
 #endif /* RTL2832_SDR_H */
diff --git a/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c
index f71b062..5ff474a 100644
--- a/drivers/media/dvb-frontends/s5h1409.c
+++ b/drivers/media/dvb-frontends/s5h1409.c
@@ -1021,9 +1021,3 @@
 MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver");
 MODULE_AUTHOR("Steven Toth");
 MODULE_LICENSE("GPL");
-
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/dvb-frontends/s5h1409.h b/drivers/media/dvb-frontends/s5h1409.h
index 7dfe52e..0ff9033 100644
--- a/drivers/media/dvb-frontends/s5h1409.h
+++ b/drivers/media/dvb-frontends/s5h1409.h
@@ -67,7 +67,7 @@
 	u8 hvr1600_opt;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_S5H1409)
+#if IS_REACHABLE(CPTCFG_DVB_S5H1409)
 extern struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config,
 					   struct i2c_adapter *i2c);
 #else
@@ -81,8 +81,3 @@
 #endif /* CPTCFG_DVB_S5H1409 */
 
 #endif /* __S5H1409_H__ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c
index 6cc4b7a..64f35fe 100644
--- a/drivers/media/dvb-frontends/s5h1411.c
+++ b/drivers/media/dvb-frontends/s5h1411.c
@@ -944,8 +944,3 @@
 MODULE_DESCRIPTION("Samsung S5H1411 QAM-B/ATSC Demodulator driver");
 MODULE_AUTHOR("Steven Toth");
 MODULE_LICENSE("GPL");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/dvb-frontends/s5h1411.h b/drivers/media/dvb-frontends/s5h1411.h
index d9acc2f..1d35c92 100644
--- a/drivers/media/dvb-frontends/s5h1411.h
+++ b/drivers/media/dvb-frontends/s5h1411.h
@@ -69,7 +69,7 @@
 	u8 status_mode;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_S5H1411)
+#if IS_REACHABLE(CPTCFG_DVB_S5H1411)
 extern struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config,
 					   struct i2c_adapter *i2c);
 #else
@@ -83,8 +83,3 @@
 #endif /* CPTCFG_DVB_S5H1411 */
 
 #endif /* __S5H1411_H__ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/dvb-frontends/s5h1420.h b/drivers/media/dvb-frontends/s5h1420.h
index a183c9f..cc58061 100644
--- a/drivers/media/dvb-frontends/s5h1420.h
+++ b/drivers/media/dvb-frontends/s5h1420.h
@@ -40,7 +40,7 @@
 	u8 serial_mpeg:1;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_S5H1420)
+#if IS_REACHABLE(CPTCFG_DVB_S5H1420)
 extern struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config,
 	     struct i2c_adapter *i2c);
 extern struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe);
diff --git a/drivers/media/dvb-frontends/s5h1432.h b/drivers/media/dvb-frontends/s5h1432.h
index fdf2272..9a45f37 100644
--- a/drivers/media/dvb-frontends/s5h1432.h
+++ b/drivers/media/dvb-frontends/s5h1432.h
@@ -75,7 +75,7 @@
 	u8 status_mode;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_S5H1432)
+#if IS_REACHABLE(CPTCFG_DVB_S5H1432)
 extern struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config,
 					   struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/s921.h b/drivers/media/dvb-frontends/s921.h
index 0105b80..8712dd5 100644
--- a/drivers/media/dvb-frontends/s921.h
+++ b/drivers/media/dvb-frontends/s921.h
@@ -25,7 +25,7 @@
 	u8 demod_address;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_S921)
+#if IS_REACHABLE(CPTCFG_DVB_S921)
 extern struct dvb_frontend *s921_attach(const struct s921_config *config,
 					   struct i2c_adapter *i2c);
 extern struct i2c_adapter *s921_get_tuner_i2c_adapter(struct dvb_frontend *);
diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c
index 98ddb49..4cc5d10 100644
--- a/drivers/media/dvb-frontends/si2165.c
+++ b/drivers/media/dvb-frontends/si2165.c
@@ -505,7 +505,7 @@
 	/* reset crc */
 	ret = si2165_writereg8(state, 0x0379, 0x01);
 	if (ret)
-		return ret;
+		goto error;
 
 	ret = si2165_upload_firmware_block(state, data, len,
 					   &offset, block_count);
diff --git a/drivers/media/dvb-frontends/si2165.h b/drivers/media/dvb-frontends/si2165.h
index fb9d440..ed455ae 100644
--- a/drivers/media/dvb-frontends/si2165.h
+++ b/drivers/media/dvb-frontends/si2165.h
@@ -45,7 +45,7 @@
 	bool inversion;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_SI2165)
+#if IS_REACHABLE(CPTCFG_DVB_SI2165)
 struct dvb_frontend *si2165_attach(
 	const struct si2165_config *config,
 	struct i2c_adapter *i2c);
diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c
index ce9ab44..5db588e 100644
--- a/drivers/media/dvb-frontends/si2168.c
+++ b/drivers/media/dvb-frontends/si2168.c
@@ -19,16 +19,17 @@
 static const struct dvb_frontend_ops si2168_ops;
 
 /* execute firmware command */
-static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd)
+static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd)
 {
+	struct si2168_dev *dev = i2c_get_clientdata(client);
 	int ret;
 	unsigned long timeout;
 
-	mutex_lock(&s->i2c_mutex);
+	mutex_lock(&dev->i2c_mutex);
 
 	if (cmd->wlen) {
 		/* write cmd and args for firmware */
-		ret = i2c_master_send(s->client, cmd->args, cmd->wlen);
+		ret = i2c_master_send(client, cmd->args, cmd->wlen);
 		if (ret < 0) {
 			goto err_mutex_unlock;
 		} else if (ret != cmd->wlen) {
@@ -39,10 +40,10 @@
 
 	if (cmd->rlen) {
 		/* wait cmd execution terminate */
-		#define TIMEOUT 50
+		#define TIMEOUT 70
 		timeout = jiffies + msecs_to_jiffies(TIMEOUT);
 		while (!time_after(jiffies, timeout)) {
-			ret = i2c_master_recv(s->client, cmd->args, cmd->rlen);
+			ret = i2c_master_recv(client, cmd->args, cmd->rlen);
 			if (ret < 0) {
 				goto err_mutex_unlock;
 			} else if (ret != cmd->rlen) {
@@ -55,7 +56,7 @@
 				break;
 		}
 
-		dev_dbg(&s->client->dev, "cmd execution took %d ms\n",
+		dev_dbg(&client->dev, "cmd execution took %d ms\n",
 				jiffies_to_msecs(jiffies) -
 				(jiffies_to_msecs(timeout) - TIMEOUT));
 
@@ -65,29 +66,26 @@
 		}
 	}
 
-	ret = 0;
+	mutex_unlock(&dev->i2c_mutex);
+	return 0;
 
 err_mutex_unlock:
-	mutex_unlock(&s->i2c_mutex);
-	if (ret)
-		goto err;
-
-	return 0;
-err:
-	dev_dbg(&s->client->dev, "failed=%d\n", ret);
+	mutex_unlock(&dev->i2c_mutex);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status)
 {
-	struct si2168 *s = fe->demodulator_priv;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2168_dev *dev = i2c_get_clientdata(client);
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret;
 	struct si2168_cmd cmd;
 
 	*status = 0;
 
-	if (!s->active) {
+	if (!dev->active) {
 		ret = -EAGAIN;
 		goto err;
 	}
@@ -113,21 +111,10 @@
 		goto err;
 	}
 
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
-	/*
-	 * Possible values seen, in order from strong signal to weak:
-	 * 16 0001 0110 full lock
-	 * 1e 0001 1110 partial lock
-	 * 1a 0001 1010 partial lock
-	 * 18 0001 1000 no lock
-	 *
-	 * [b3:b1] lock bits
-	 * [b4] statistics ready? Set in a few secs after lock is gained.
-	 */
-
 	switch ((cmd.args[2] >> 1) & 0x03) {
 	case 0x01:
 		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
@@ -138,7 +125,7 @@
 		break;
 	}
 
-	s->fe_status = *status;
+	dev->fe_status = *status;
 
 	if (*status & FE_HAS_LOCK) {
 		c->cnr.len = 1;
@@ -149,30 +136,31 @@
 		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
 	}
 
-	dev_dbg(&s->client->dev, "status=%02x args=%*ph\n",
+	dev_dbg(&client->dev, "status=%02x args=%*ph\n",
 			*status, cmd.rlen, cmd.args);
 
 	return 0;
 err:
-	dev_dbg(&s->client->dev, "failed=%d\n", ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int si2168_set_frontend(struct dvb_frontend *fe)
 {
-	struct si2168 *s = fe->demodulator_priv;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2168_dev *dev = i2c_get_clientdata(client);
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret;
 	struct si2168_cmd cmd;
 	u8 bandwidth, delivery_system;
 
-	dev_dbg(&s->client->dev,
-			"delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u, stream_id=%d\n",
-			c->delivery_system, c->modulation,
-			c->frequency, c->bandwidth_hz, c->symbol_rate,
-			c->inversion, c->stream_id);
+	dev_dbg(&client->dev,
+			"delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u stream_id=%u\n",
+			c->delivery_system, c->modulation, c->frequency,
+			c->bandwidth_hz, c->symbol_rate, c->inversion,
+			c->stream_id);
 
-	if (!s->active) {
+	if (!dev->active) {
 		ret = -EAGAIN;
 		goto err;
 	}
@@ -192,7 +180,12 @@
 		goto err;
 	}
 
-	if (c->bandwidth_hz <= 5000000)
+	if (c->bandwidth_hz == 0) {
+		ret = -EINVAL;
+		goto err;
+	} else if (c->bandwidth_hz <= 2000000)
+		bandwidth = 0x02;
+	else if (c->bandwidth_hz <= 5000000)
 		bandwidth = 0x05;
 	else if (c->bandwidth_hz <= 6000000)
 		bandwidth = 0x06;
@@ -217,7 +210,7 @@
 	memcpy(cmd.args, "\x88\x02\x02\x02\x02", 5);
 	cmd.wlen = 5;
 	cmd.rlen = 5;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
@@ -230,7 +223,7 @@
 		memcpy(cmd.args, "\x89\x21\x06\x11\x89\x20", 6);
 	cmd.wlen = 6;
 	cmd.rlen = 3;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
@@ -241,7 +234,7 @@
 		cmd.args[2] = c->stream_id == NO_STREAM_ID_FILTER ? 0 : 1;
 		cmd.wlen = 3;
 		cmd.rlen = 1;
-		ret = si2168_cmd_execute(s, &cmd);
+		ret = si2168_cmd_execute(client, &cmd);
 		if (ret)
 			goto err;
 	}
@@ -249,35 +242,35 @@
 	memcpy(cmd.args, "\x51\x03", 2);
 	cmd.wlen = 2;
 	cmd.rlen = 12;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	memcpy(cmd.args, "\x12\x08\x04", 3);
 	cmd.wlen = 3;
 	cmd.rlen = 3;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6);
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	memcpy(cmd.args, "\x14\x00\x06\x10\x24\x00", 6);
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6);
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
@@ -285,18 +278,18 @@
 	cmd.args[4] = delivery_system | bandwidth;
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	/* set DVB-C symbol rate */
 	if (c->delivery_system == SYS_DVBC_ANNEX_A) {
 		memcpy(cmd.args, "\x14\x00\x02\x11", 4);
-		cmd.args[4] = (c->symbol_rate / 1000) & 0xff;
+		cmd.args[4] = ((c->symbol_rate / 1000) >> 0) & 0xff;
 		cmd.args[5] = ((c->symbol_rate / 1000) >> 8) & 0xff;
 		cmd.wlen = 6;
 		cmd.rlen = 4;
-		ret = si2168_cmd_execute(s, &cmd);
+		ret = si2168_cmd_execute(client, &cmd);
 		if (ret)
 			goto err;
 	}
@@ -304,88 +297,88 @@
 	memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6);
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x08", 6);
-	cmd.args[5] |= s->ts_clock_inv ? 0x00 : 0x10;
+	cmd.args[5] |= dev->ts_clock_inv ? 0x00 : 0x10;
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x05", 6);
-	cmd.args[5] |= s->ts_clock_inv ? 0x00 : 0x10;
+	cmd.args[5] |= dev->ts_clock_inv ? 0x00 : 0x10;
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6);
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	memcpy(cmd.args, "\x14\x00\x01\x03\x0c\x00", 6);
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	memcpy(cmd.args, "\x85", 1);
 	cmd.wlen = 1;
 	cmd.rlen = 1;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
-	s->delivery_system = c->delivery_system;
+	dev->delivery_system = c->delivery_system;
 
 	return 0;
 err:
-	dev_dbg(&s->client->dev, "failed=%d\n", ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int si2168_init(struct dvb_frontend *fe)
 {
-	struct si2168 *s = fe->demodulator_priv;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2168_dev *dev = i2c_get_clientdata(client);
 	int ret, len, remaining;
-	const struct firmware *fw = NULL;
-	u8 *fw_file;
-	const unsigned int i2c_wr_max = 8;
+	const struct firmware *fw;
+	const char *fw_name;
 	struct si2168_cmd cmd;
 	unsigned int chip_id;
 
-	dev_dbg(&s->client->dev, "\n");
+	dev_dbg(&client->dev, "\n");
 
 	/* initialize */
 	memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13);
 	cmd.wlen = 13;
 	cmd.rlen = 0;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
-	if (s->fw_loaded) {
+	if (dev->fw_loaded) {
 		/* resume */
 		memcpy(cmd.args, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8);
 		cmd.wlen = 8;
 		cmd.rlen = 1;
-		ret = si2168_cmd_execute(s, &cmd);
+		ret = si2168_cmd_execute(client, &cmd);
 		if (ret)
 			goto err;
 
 		memcpy(cmd.args, "\x85", 1);
 		cmd.wlen = 1;
 		cmd.rlen = 1;
-		ret = si2168_cmd_execute(s, &cmd);
+		ret = si2168_cmd_execute(client, &cmd);
 		if (ret)
 			goto err;
 
@@ -396,7 +389,7 @@
 	memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8);
 	cmd.wlen = 8;
 	cmd.rlen = 1;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
@@ -404,7 +397,7 @@
 	memcpy(cmd.args, "\x02", 1);
 	cmd.wlen = 1;
 	cmd.rlen = 13;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
@@ -417,50 +410,48 @@
 
 	switch (chip_id) {
 	case SI2168_A20:
-		fw_file = SI2168_A20_FIRMWARE;
+		fw_name = SI2168_A20_FIRMWARE;
 		break;
 	case SI2168_A30:
-		fw_file = SI2168_A30_FIRMWARE;
+		fw_name = SI2168_A30_FIRMWARE;
 		break;
 	case SI2168_B40:
-		fw_file = SI2168_B40_FIRMWARE;
+		fw_name = SI2168_B40_FIRMWARE;
 		break;
 	default:
-		dev_err(&s->client->dev,
-				"unknown chip version Si21%d-%c%c%c\n",
+		dev_err(&client->dev, "unknown chip version Si21%d-%c%c%c\n",
 				cmd.args[2], cmd.args[1],
 				cmd.args[3], cmd.args[4]);
 		ret = -EINVAL;
 		goto err;
 	}
 
-	/* cold state - try to download firmware */
-	dev_info(&s->client->dev, "found a '%s' in cold state\n",
-			si2168_ops.info.name);
+	dev_info(&client->dev, "found a 'Silicon Labs Si21%d-%c%c%c'\n",
+			cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]);
 
 	/* request the firmware, this will block and timeout */
-	ret = request_firmware(&fw, fw_file, &s->client->dev);
+	ret = request_firmware(&fw, fw_name, &client->dev);
 	if (ret) {
 		/* fallback mechanism to handle old name for Si2168 B40 fw */
 		if (chip_id == SI2168_B40) {
-			fw_file = SI2168_B40_FIRMWARE_FALLBACK;
-			ret = request_firmware(&fw, fw_file, &s->client->dev);
+			fw_name = SI2168_B40_FIRMWARE_FALLBACK;
+			ret = request_firmware(&fw, fw_name, &client->dev);
 		}
 
 		if (ret == 0) {
-			dev_notice(&s->client->dev,
+			dev_notice(&client->dev,
 					"please install firmware file '%s'\n",
 					SI2168_B40_FIRMWARE);
 		} else {
-			dev_err(&s->client->dev,
+			dev_err(&client->dev,
 					"firmware file '%s' not found\n",
-					fw_file);
-			goto error_fw_release;
+					fw_name);
+			goto err_release_firmware;
 		}
 	}
 
-	dev_info(&s->client->dev, "downloading firmware from file '%s'\n",
-			fw_file);
+	dev_info(&client->dev, "downloading firmware from file '%s'\n",
+			fw_name);
 
 	if ((fw->size % 17 == 0) && (fw->data[0] > 5)) {
 		/* firmware is in the new format */
@@ -469,41 +460,37 @@
 			memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len);
 			cmd.wlen = len;
 			cmd.rlen = 1;
-			ret = si2168_cmd_execute(s, &cmd);
-			if (ret) {
-				dev_err(&s->client->dev,
-						"firmware download failed=%d\n",
-						ret);
-				goto error_fw_release;
-			}
+			ret = si2168_cmd_execute(client, &cmd);
+			if (ret)
+				break;
 		}
-	} else {
+	} else if (fw->size % 8 == 0) {
 		/* firmware is in the old format */
-		for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) {
-			len = remaining;
-			if (len > i2c_wr_max)
-				len = i2c_wr_max;
-
+		for (remaining = fw->size; remaining > 0; remaining -= 8) {
+			len = 8;
 			memcpy(cmd.args, &fw->data[fw->size - remaining], len);
 			cmd.wlen = len;
 			cmd.rlen = 1;
-			ret = si2168_cmd_execute(s, &cmd);
-			if (ret) {
-				dev_err(&s->client->dev,
-						"firmware download failed=%d\n",
-						ret);
-				goto error_fw_release;
-			}
+			ret = si2168_cmd_execute(client, &cmd);
+			if (ret)
+				break;
 		}
+	} else {
+		/* bad or unknown firmware format */
+		ret = -EINVAL;
+	}
+
+	if (ret) {
+		dev_err(&client->dev, "firmware download failed %d\n", ret);
+		goto err_release_firmware;
 	}
 
 	release_firmware(fw);
-	fw = NULL;
 
 	memcpy(cmd.args, "\x01\x01", 2);
 	cmd.wlen = 2;
 	cmd.rlen = 1;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
@@ -511,58 +498,56 @@
 	memcpy(cmd.args, "\x11", 1);
 	cmd.wlen = 1;
 	cmd.rlen = 10;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
-	dev_dbg(&s->client->dev, "firmware version: %c.%c.%d\n",
+	dev_info(&client->dev, "firmware version: %c.%c.%d\n",
 			cmd.args[6], cmd.args[7], cmd.args[8]);
 
 	/* set ts mode */
 	memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6);
-	cmd.args[4] |= s->ts_mode;
+	cmd.args[4] |= dev->ts_mode;
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
-	s->fw_loaded = true;
-
-	dev_info(&s->client->dev, "found a '%s' in warm state\n",
-			si2168_ops.info.name);
+	dev->fw_loaded = true;
 warm:
-	s->active = true;
+	dev->active = true;
 
 	return 0;
 
-error_fw_release:
+err_release_firmware:
 	release_firmware(fw);
 err:
-	dev_dbg(&s->client->dev, "failed=%d\n", ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int si2168_sleep(struct dvb_frontend *fe)
 {
-	struct si2168 *s = fe->demodulator_priv;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2168_dev *dev = i2c_get_clientdata(client);
 	int ret;
 	struct si2168_cmd cmd;
 
-	dev_dbg(&s->client->dev, "\n");
+	dev_dbg(&client->dev, "\n");
 
-	s->active = false;
+	dev->active = false;
 
 	memcpy(cmd.args, "\x13", 1);
 	cmd.wlen = 1;
 	cmd.rlen = 0;
-	ret = si2168_cmd_execute(s, &cmd);
+	ret = si2168_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	return 0;
 err:
-	dev_dbg(&s->client->dev, "failed=%d\n", ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
@@ -581,21 +566,22 @@
  */
 static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
 {
-	struct si2168 *s = mux_priv;
+	struct i2c_client *client = mux_priv;
+	struct si2168_dev *dev = i2c_get_clientdata(client);
 	int ret;
 	struct i2c_msg gate_open_msg = {
-		.addr = s->client->addr,
+		.addr = client->addr,
 		.flags = 0,
 		.len = 3,
 		.buf = "\xc0\x0d\x01",
 	};
 
-	mutex_lock(&s->i2c_mutex);
+	mutex_lock(&dev->i2c_mutex);
 
 	/* open tuner I2C gate */
-	ret = __i2c_transfer(s->client->adapter, &gate_open_msg, 1);
+	ret = __i2c_transfer(client->adapter, &gate_open_msg, 1);
 	if (ret != 1) {
-		dev_warn(&s->client->dev, "i2c write failed=%d\n", ret);
+		dev_warn(&client->dev, "i2c write failed=%d\n", ret);
 		if (ret >= 0)
 			ret = -EREMOTEIO;
 	} else {
@@ -607,26 +593,27 @@
 
 static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan)
 {
-	struct si2168 *s = mux_priv;
+	struct i2c_client *client = mux_priv;
+	struct si2168_dev *dev = i2c_get_clientdata(client);
 	int ret;
 	struct i2c_msg gate_close_msg = {
-		.addr = s->client->addr,
+		.addr = client->addr,
 		.flags = 0,
 		.len = 3,
 		.buf = "\xc0\x0d\x00",
 	};
 
 	/* close tuner I2C gate */
-	ret = __i2c_transfer(s->client->adapter, &gate_close_msg, 1);
+	ret = __i2c_transfer(client->adapter, &gate_close_msg, 1);
 	if (ret != 1) {
-		dev_warn(&s->client->dev, "i2c write failed=%d\n", ret);
+		dev_warn(&client->dev, "i2c write failed=%d\n", ret);
 		if (ret >= 0)
 			ret = -EREMOTEIO;
 	} else {
 		ret = 0;
 	}
 
-	mutex_unlock(&s->i2c_mutex);
+	mutex_unlock(&dev->i2c_mutex);
 
 	return ret;
 }
@@ -635,6 +622,8 @@
 	.delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A},
 	.info = {
 		.name = "Silicon Labs Si2168",
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 7200000,
 		.caps =	FE_CAN_FEC_1_2 |
 			FE_CAN_FEC_2_3 |
 			FE_CAN_FEC_3_4 |
@@ -670,71 +659,69 @@
 		const struct i2c_device_id *id)
 {
 	struct si2168_config *config = client->dev.platform_data;
-	struct si2168 *s;
+	struct si2168_dev *dev;
 	int ret;
 
 	dev_dbg(&client->dev, "\n");
 
-	s = kzalloc(sizeof(struct si2168), GFP_KERNEL);
-	if (!s) {
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
 		ret = -ENOMEM;
 		dev_err(&client->dev, "kzalloc() failed\n");
 		goto err;
 	}
 
-	s->client = client;
-	mutex_init(&s->i2c_mutex);
+	mutex_init(&dev->i2c_mutex);
 
 	/* create mux i2c adapter for tuner */
-	s->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, s,
-			0, 0, 0, si2168_select, si2168_deselect);
-	if (s->adapter == NULL) {
+	dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
+			client, 0, 0, 0, si2168_select, si2168_deselect);
+	if (dev->adapter == NULL) {
 		ret = -ENODEV;
-		goto err;
+		goto err_kfree;
 	}
 
 	/* create dvb_frontend */
-	memcpy(&s->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops));
-	s->fe.demodulator_priv = s;
+	memcpy(&dev->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops));
+	dev->fe.demodulator_priv = client;
+	*config->i2c_adapter = dev->adapter;
+	*config->fe = &dev->fe;
+	dev->ts_mode = config->ts_mode;
+	dev->ts_clock_inv = config->ts_clock_inv;
+	dev->fw_loaded = false;
 
-	*config->i2c_adapter = s->adapter;
-	*config->fe = &s->fe;
-	s->ts_mode = config->ts_mode;
-	s->ts_clock_inv = config->ts_clock_inv;
-	s->fw_loaded = false;
+	i2c_set_clientdata(client, dev);
 
-	i2c_set_clientdata(client, s);
-
-	dev_info(&s->client->dev,
-			"Silicon Labs Si2168 successfully attached\n");
+	dev_info(&client->dev, "Silicon Labs Si2168 successfully attached\n");
 	return 0;
+err_kfree:
+	kfree(dev);
 err:
-	kfree(s);
 	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int si2168_remove(struct i2c_client *client)
 {
-	struct si2168 *s = i2c_get_clientdata(client);
+	struct si2168_dev *dev = i2c_get_clientdata(client);
 
 	dev_dbg(&client->dev, "\n");
 
-	i2c_del_mux_adapter(s->adapter);
+	i2c_del_mux_adapter(dev->adapter);
 
-	s->fe.ops.release = NULL;
-	s->fe.demodulator_priv = NULL;
+	dev->fe.ops.release = NULL;
+	dev->fe.demodulator_priv = NULL;
 
-	kfree(s);
+	kfree(dev);
 
 	return 0;
 }
 
-static const struct i2c_device_id si2168_id[] = {
+static const struct i2c_device_id si2168_id_table[] = {
 	{"si2168", 0},
 	{}
 };
-MODULE_DEVICE_TABLE(i2c, si2168_id);
+MODULE_DEVICE_TABLE(i2c, si2168_id_table);
 
 static struct i2c_driver si2168_driver = {
 	.driver = {
@@ -743,7 +730,7 @@
 	},
 	.probe		= si2168_probe,
 	.remove		= si2168_remove,
-	.id_table	= si2168_id,
+	.id_table	= si2168_id_table,
 };
 
 module_i2c_driver(si2168_driver);
diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h
index 87bc121..70d702a 100644
--- a/drivers/media/dvb-frontends/si2168.h
+++ b/drivers/media/dvb-frontends/si2168.h
@@ -36,14 +36,12 @@
 	struct i2c_adapter **i2c_adapter;
 
 	/* TS mode */
+#define SI2168_TS_PARALLEL	0x06
+#define SI2168_TS_SERIAL	0x03
 	u8 ts_mode;
 
 	/* TS clock inverted */
 	bool ts_clock_inv;
-
 };
 
-#define SI2168_TS_PARALLEL	0x06
-#define SI2168_TS_SERIAL	0x03
-
 #endif
diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h
index 60bc334..d7efce8 100644
--- a/drivers/media/dvb-frontends/si2168_priv.h
+++ b/drivers/media/dvb-frontends/si2168_priv.h
@@ -28,8 +28,7 @@
 #define SI2168_B40_FIRMWARE_FALLBACK "dvb-demod-si2168-02.fw"
 
 /* state struct */
-struct si2168 {
-	struct i2c_client *client;
+struct si2168_dev {
 	struct i2c_adapter *adapter;
 	struct mutex i2c_mutex;
 	struct dvb_frontend fe;
@@ -41,7 +40,7 @@
 	bool ts_clock_inv;
 };
 
-/* firmare command struct */
+/* firmware command struct */
 #define SI2168_ARGLEN      30
 struct si2168_cmd {
 	u8 args[SI2168_ARGLEN];
diff --git a/drivers/media/dvb-frontends/si21xx.h b/drivers/media/dvb-frontends/si21xx.h
index 160383f..3ad0c29 100644
--- a/drivers/media/dvb-frontends/si21xx.h
+++ b/drivers/media/dvb-frontends/si21xx.h
@@ -13,7 +13,7 @@
 	int min_delay_ms;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_SI21XX)
+#if IS_REACHABLE(CPTCFG_DVB_SI21XX)
 extern struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
 						struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c
index cc1ef96..8fd4276 100644
--- a/drivers/media/dvb-frontends/sp2.c
+++ b/drivers/media/dvb-frontends/sp2.c
@@ -413,11 +413,8 @@
 	struct sp2 *s = i2c_get_clientdata(client);
 
 	dev_dbg(&client->dev, "\n");
-
 	sp2_exit(client);
-	if (s != NULL)
-		kfree(s);
-
+	kfree(s);
 	return 0;
 }
 
diff --git a/drivers/media/dvb-frontends/sp8870.h b/drivers/media/dvb-frontends/sp8870.h
index f6bcc93..9851f40 100644
--- a/drivers/media/dvb-frontends/sp8870.h
+++ b/drivers/media/dvb-frontends/sp8870.h
@@ -35,7 +35,7 @@
 	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_SP8870)
+#if IS_REACHABLE(CPTCFG_DVB_SP8870)
 extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
 					  struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/sp887x.h b/drivers/media/dvb-frontends/sp887x.h
index 2025280..41ba1d6 100644
--- a/drivers/media/dvb-frontends/sp887x.h
+++ b/drivers/media/dvb-frontends/sp887x.h
@@ -17,7 +17,7 @@
 	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_SP887X)
+#if IS_REACHABLE(CPTCFG_DVB_SP887X)
 extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
 					  struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/stb0899_algo.c b/drivers/media/dvb-frontends/stb0899_algo.c
index 93596e0..3012f19 100644
--- a/drivers/media/dvb-frontends/stb0899_algo.c
+++ b/drivers/media/dvb-frontends/stb0899_algo.c
@@ -19,6 +19,7 @@
 	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+#include <linux/bitops.h>
 #include "stb0899_drv.h"
 #include "stb0899_priv.h"
 #include "stb0899_reg.h"
@@ -1490,9 +1491,7 @@
 		/* Store signal parameters	*/
 		offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ);
 
-		/* sign extend 30 bit value before using it in calculations */
-		if (offsetfreq & (1 << 29))
-			offsetfreq |= -1 << 30;
+		offsetfreq = sign_extend32(offsetfreq, 29);
 
 		offsetfreq = offsetfreq / ((1 << 30) / 1000);
 		offsetfreq *= (internal->master_clk / 1000000);
diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c
index 19646fb..c73899d 100644
--- a/drivers/media/dvb-frontends/stb0899_drv.c
+++ b/drivers/media/dvb-frontends/stb0899_drv.c
@@ -20,6 +20,7 @@
 */
 
 #include <linux/init.h>
+#include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -691,7 +692,7 @@
 		reg = stb0899_read_reg(state, STB0899_DISSTATUS);
 		if (!STB0899_GETFIELD(FIFOFULL, reg))
 			break;
-		if ((jiffies - start) > timeout) {
+		if (time_after(jiffies, start + timeout)) {
 			dprintk(state->verbose, FE_ERROR, 1, "timed out !!");
 			return -ETIMEDOUT;
 		}
@@ -733,7 +734,7 @@
 
 	while (!STB0899_GETFIELD(RXEND, reg)) {
 		reg = stb0899_read_reg(state, STB0899_DISRX_ST0);
-		if (jiffies - start > timeout) {
+		if (time_after(jiffies, start + timeout)) {
 			dprintk(state->verbose, FE_ERROR, 1, "timed out!!");
 			return -ETIMEDOUT;
 		}
@@ -782,7 +783,7 @@
 
 	while (!STB0899_GETFIELD(TXIDLE, reg)) {
 		reg = stb0899_read_reg(state, STB0899_DISSTATUS);
-		if (jiffies - start > timeout) {
+		if (time_after(jiffies, start + timeout)) {
 			dprintk(state->verbose, FE_ERROR, 1, "timed out!!");
 			return -ETIMEDOUT;
 		}
diff --git a/drivers/media/dvb-frontends/stb0899_drv.h b/drivers/media/dvb-frontends/stb0899_drv.h
index 57fb7be..1c9e68a 100644
--- a/drivers/media/dvb-frontends/stb0899_drv.h
+++ b/drivers/media/dvb-frontends/stb0899_drv.h
@@ -141,7 +141,7 @@
 	int (*tuner_set_rfsiggain)(struct dvb_frontend *fe, u32 rf_gain);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_STB0899)
+#if IS_REACHABLE(CPTCFG_DVB_STB0899)
 
 extern struct dvb_frontend *stb0899_attach(struct stb0899_config *config,
 					   struct i2c_adapter *i2c);
diff --git a/drivers/media/dvb-frontends/stb6000.h b/drivers/media/dvb-frontends/stb6000.h
index 3b9d1f9..f95272f 100644
--- a/drivers/media/dvb-frontends/stb6000.h
+++ b/drivers/media/dvb-frontends/stb6000.h
@@ -35,7 +35,7 @@
  * @param i2c i2c adapter to use.
  * @return FE pointer on success, NULL on failure.
  */
-#if IS_ENABLED(CPTCFG_DVB_STB6000)
+#if IS_REACHABLE(CPTCFG_DVB_STB6000)
 extern struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr,
 					   struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/stb6100.h b/drivers/media/dvb-frontends/stb6100.h
index 79052d6..29f1b15 100644
--- a/drivers/media/dvb-frontends/stb6100.h
+++ b/drivers/media/dvb-frontends/stb6100.h
@@ -94,7 +94,7 @@
 	u32 reference;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_STB6100)
+#if IS_REACHABLE(CPTCFG_DVB_STB6100)
 
 extern struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe,
 					   const struct stb6100_config *config,
diff --git a/drivers/media/dvb-frontends/stv0288.h b/drivers/media/dvb-frontends/stv0288.h
index a5d4eac..d387b22 100644
--- a/drivers/media/dvb-frontends/stv0288.h
+++ b/drivers/media/dvb-frontends/stv0288.h
@@ -43,7 +43,7 @@
 	int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_STV0288)
+#if IS_REACHABLE(CPTCFG_DVB_STV0288)
 extern struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
 					   struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/stv0297.h b/drivers/media/dvb-frontends/stv0297.h
index f8e3f07..58ef73d 100644
--- a/drivers/media/dvb-frontends/stv0297.h
+++ b/drivers/media/dvb-frontends/stv0297.h
@@ -42,7 +42,7 @@
 	u8 stop_during_read:1;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_STV0297)
+#if IS_REACHABLE(CPTCFG_DVB_STV0297)
 extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config,
 					   struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/stv0299.h b/drivers/media/dvb-frontends/stv0299.h
index e1a85b2..b29985c 100644
--- a/drivers/media/dvb-frontends/stv0299.h
+++ b/drivers/media/dvb-frontends/stv0299.h
@@ -95,7 +95,7 @@
 	int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_STV0299)
+#if IS_REACHABLE(CPTCFG_DVB_STV0299)
 extern struct dvb_frontend *stv0299_attach(const struct stv0299_config *config,
 					   struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/stv0367.h b/drivers/media/dvb-frontends/stv0367.h
index 749347a..35365c5 100644
--- a/drivers/media/dvb-frontends/stv0367.h
+++ b/drivers/media/dvb-frontends/stv0367.h
@@ -39,7 +39,7 @@
 	int clk_pol;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_STV0367)
+#if IS_REACHABLE(CPTCFG_DVB_STV0367)
 extern struct
 dvb_frontend *stv0367ter_attach(const struct stv0367_config *config,
 					struct i2c_adapter *i2c);
diff --git a/drivers/media/dvb-frontends/stv0900.h b/drivers/media/dvb-frontends/stv0900.h
index 24b4c4f..abc4800 100644
--- a/drivers/media/dvb-frontends/stv0900.h
+++ b/drivers/media/dvb-frontends/stv0900.h
@@ -58,7 +58,7 @@
 	void (*set_lock_led)(struct dvb_frontend *fe, int offon);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_STV0900)
+#if IS_REACHABLE(CPTCFG_DVB_STV0900)
 extern struct dvb_frontend *stv0900_attach(const struct stv0900_config *config,
 					struct i2c_adapter *i2c, int demod);
 #else
diff --git a/drivers/media/dvb-frontends/stv090x.h b/drivers/media/dvb-frontends/stv090x.h
index 9a3ca6f..efcdc61 100644
--- a/drivers/media/dvb-frontends/stv090x.h
+++ b/drivers/media/dvb-frontends/stv090x.h
@@ -107,7 +107,7 @@
 			u8 xor_value);
 };
 
-#if IS_ENABLED(CPTCFG_DVB_STV090x)
+#if IS_REACHABLE(CPTCFG_DVB_STV090x)
 
 struct dvb_frontend *stv090x_attach(struct stv090x_config *config,
 				    struct i2c_adapter *i2c,
diff --git a/drivers/media/dvb-frontends/stv6110.h b/drivers/media/dvb-frontends/stv6110.h
index 4104e1a..1de2f85 100644
--- a/drivers/media/dvb-frontends/stv6110.h
+++ b/drivers/media/dvb-frontends/stv6110.h
@@ -46,7 +46,7 @@
 	u8 clk_div;	/* divisor value for the output clock */
 };
 
-#if IS_ENABLED(CPTCFG_DVB_STV6110)
+#if IS_REACHABLE(CPTCFG_DVB_STV6110)
 extern struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe,
 					const struct stv6110_config *config,
 					struct i2c_adapter *i2c);
diff --git a/drivers/media/dvb-frontends/stv6110x.h b/drivers/media/dvb-frontends/stv6110x.h
index 974210f..454c0a0 100644
--- a/drivers/media/dvb-frontends/stv6110x.h
+++ b/drivers/media/dvb-frontends/stv6110x.h
@@ -53,7 +53,7 @@
 };
 
 
-#if IS_ENABLED(CPTCFG_DVB_STV6110x)
+#if IS_REACHABLE(CPTCFG_DVB_STV6110x)
 
 extern struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe,
 					       const struct stv6110x_config *config,
diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c
index b35d65c..dce22ce 100644
--- a/drivers/media/dvb-frontends/tc90522.c
+++ b/drivers/media/dvb-frontends/tc90522.c
@@ -214,6 +214,7 @@
 	state = fe->demodulator_priv;
 	c = &fe->dtv_property_cache;
 	c->delivery_system = SYS_ISDBS;
+	c->symbol_rate = 28860000;
 
 	layers = 0;
 	ret = reg_read(state, 0xe6, val, 5);
diff --git a/drivers/media/dvb-frontends/tda1002x.h b/drivers/media/dvb-frontends/tda1002x.h
index 921d22e..e9dfa3d 100644
--- a/drivers/media/dvb-frontends/tda1002x.h
+++ b/drivers/media/dvb-frontends/tda1002x.h
@@ -57,7 +57,7 @@
 	u16 deltaf;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TDA10021)
+#if IS_REACHABLE(CPTCFG_DVB_TDA10021)
 extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
 					    struct i2c_adapter* i2c, u8 pwm);
 #else
@@ -69,7 +69,7 @@
 }
 #endif // CPTCFG_DVB_TDA10021
 
-#if IS_ENABLED(CPTCFG_DVB_TDA10023)
+#if IS_REACHABLE(CPTCFG_DVB_TDA10023)
 extern struct dvb_frontend *tda10023_attach(
 	const struct tda10023_config *config,
 	struct i2c_adapter *i2c, u8 pwm);
diff --git a/drivers/media/dvb-frontends/tda10048.h b/drivers/media/dvb-frontends/tda10048.h
index 6c97a89..bcaaaa0 100644
--- a/drivers/media/dvb-frontends/tda10048.h
+++ b/drivers/media/dvb-frontends/tda10048.h
@@ -73,7 +73,7 @@
 	u8 pll_n;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TDA10048)
+#if IS_REACHABLE(CPTCFG_DVB_TDA10048)
 extern struct dvb_frontend *tda10048_attach(
 	const struct tda10048_config *config,
 	struct i2c_adapter *i2c);
diff --git a/drivers/media/dvb-frontends/tda1004x.h b/drivers/media/dvb-frontends/tda1004x.h
index a4ab60b..b3b429e 100644
--- a/drivers/media/dvb-frontends/tda1004x.h
+++ b/drivers/media/dvb-frontends/tda1004x.h
@@ -117,7 +117,7 @@
 	enum tda1004x_demod demod_type;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TDA1004X)
+#if IS_REACHABLE(CPTCFG_DVB_TDA1004X)
 extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
 					    struct i2c_adapter* i2c);
 
diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h
index 22c01d4..bfd6038 100644
--- a/drivers/media/dvb-frontends/tda10071.h
+++ b/drivers/media/dvb-frontends/tda10071.h
@@ -72,7 +72,7 @@
 };
 
 
-#if IS_ENABLED(CPTCFG_DVB_TDA10071)
+#if IS_REACHABLE(CPTCFG_DVB_TDA10071)
 extern struct dvb_frontend *tda10071_attach(
 	const struct tda10071_config *config, struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/tda10071_priv.h b/drivers/media/dvb-frontends/tda10071_priv.h
index 4204861..03f839c 100644
--- a/drivers/media/dvb-frontends/tda10071_priv.h
+++ b/drivers/media/dvb-frontends/tda10071_priv.h
@@ -99,7 +99,7 @@
 #define CMD_BER_CONTROL         0x3e
 #define CMD_BER_UPDATE_COUNTERS 0x3f
 
-/* firmare command struct */
+/* firmware command struct */
 #define TDA10071_ARGLEN      30
 struct tda10071_cmd {
 	u8 args[TDA10071_ARGLEN];
diff --git a/drivers/media/dvb-frontends/tda10086.h b/drivers/media/dvb-frontends/tda10086.h
index a8256c7..da67233 100644
--- a/drivers/media/dvb-frontends/tda10086.h
+++ b/drivers/media/dvb-frontends/tda10086.h
@@ -46,7 +46,7 @@
 	enum tda10086_xtal xtal_freq;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TDA10086)
+#if IS_REACHABLE(CPTCFG_DVB_TDA10086)
 extern struct dvb_frontend* tda10086_attach(const struct tda10086_config* config,
 					    struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/tda18271c2dd.h b/drivers/media/dvb-frontends/tda18271c2dd.h
index 351dab7..c638e42 100644
--- a/drivers/media/dvb-frontends/tda18271c2dd.h
+++ b/drivers/media/dvb-frontends/tda18271c2dd.h
@@ -3,7 +3,7 @@
 
 #include <linux/kconfig.h>
 
-#if IS_ENABLED(CPTCFG_DVB_TDA18271C2DD)
+#if IS_REACHABLE(CPTCFG_DVB_TDA18271C2DD)
 struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe,
 					 struct i2c_adapter *i2c, u8 adr);
 #else
diff --git a/drivers/media/dvb-frontends/tda665x.h b/drivers/media/dvb-frontends/tda665x.h
index ec50e8a..c45c141 100644
--- a/drivers/media/dvb-frontends/tda665x.h
+++ b/drivers/media/dvb-frontends/tda665x.h
@@ -31,7 +31,7 @@
 	u32	ref_divider;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TDA665x)
+#if IS_REACHABLE(CPTCFG_DVB_TDA665x)
 
 extern struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe,
 					   const struct tda665x_config *config,
diff --git a/drivers/media/dvb-frontends/tda8083.h b/drivers/media/dvb-frontends/tda8083.h
index 6d95867..4f0f229 100644
--- a/drivers/media/dvb-frontends/tda8083.h
+++ b/drivers/media/dvb-frontends/tda8083.h
@@ -35,7 +35,7 @@
 	u8 demod_address;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TDA8083)
+#if IS_REACHABLE(CPTCFG_DVB_TDA8083)
 extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
 					   struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/tda8261.h b/drivers/media/dvb-frontends/tda8261.h
index 981bfde..35908dc 100644
--- a/drivers/media/dvb-frontends/tda8261.h
+++ b/drivers/media/dvb-frontends/tda8261.h
@@ -34,7 +34,7 @@
 	enum tda8261_step	step_size;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TDA8261)
+#if IS_REACHABLE(CPTCFG_DVB_TDA8261)
 
 extern struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe,
 					   const struct tda8261_config *config,
diff --git a/drivers/media/dvb-frontends/tda826x.h b/drivers/media/dvb-frontends/tda826x.h
index 31ce682..e0c6370 100644
--- a/drivers/media/dvb-frontends/tda826x.h
+++ b/drivers/media/dvb-frontends/tda826x.h
@@ -35,7 +35,7 @@
  * @param has_loopthrough Set to 1 if the card has a loopthrough RF connector.
  * @return FE pointer on success, NULL on failure.
  */
-#if IS_ENABLED(CPTCFG_DVB_TDA826X)
+#if IS_REACHABLE(CPTCFG_DVB_TDA826X)
 extern struct dvb_frontend* tda826x_attach(struct dvb_frontend *fe, int addr,
 					   struct i2c_adapter *i2c,
 					   int has_loopthrough);
diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c
index 9aba044..90164a3 100644
--- a/drivers/media/dvb-frontends/ts2020.c
+++ b/drivers/media/dvb-frontends/ts2020.c
@@ -26,12 +26,23 @@
 #define FREQ_OFFSET_LOW_SYM_RATE 3000
 
 struct ts2020_priv {
+	struct dvb_frontend *fe;
 	/* i2c details */
 	int i2c_address;
 	struct i2c_adapter *i2c;
-	u8 clk_out_div;
+	u8 clk_out:2;
+	u8 clk_out_div:5;
 	u32 frequency;
 	u32 frequency_div;
+#define TS2020_M88TS2020 0
+#define TS2020_M88TS2022 1
+	u8 tuner;
+	u8 loop_through:1;
+};
+
+struct ts2020_reg_val {
+	u8 reg;
+	u8 val;
 };
 
 static int ts2020_release(struct dvb_frontend *fe)
@@ -112,40 +123,77 @@
 static int ts2020_sleep(struct dvb_frontend *fe)
 {
 	struct ts2020_priv *priv = fe->tuner_priv;
-	int ret;
-	u8 buf[] = { 10, 0 };
-	struct i2c_msg msg = {
-		.addr = priv->i2c_address,
-		.flags = 0,
-		.buf = buf,
-		.len = 2
-	};
+	u8 u8tmp;
 
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (priv->tuner == TS2020_M88TS2020)
+		u8tmp = 0x0a; /* XXX: probably wrong */
+	else
+		u8tmp = 0x00;
 
-	ret = i2c_transfer(priv->i2c, &msg, 1);
-	if (ret != 1)
-		printk(KERN_ERR "%s: i2c error\n", __func__);
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-
-	return (ret == 1) ? 0 : ret;
+	return ts2020_writereg(fe, u8tmp, 0x00);
 }
 
 static int ts2020_init(struct dvb_frontend *fe)
 {
 	struct ts2020_priv *priv = fe->tuner_priv;
+	int i;
+	u8 u8tmp;
 
-	ts2020_writereg(fe, 0x42, 0x73);
-	ts2020_writereg(fe, 0x05, priv->clk_out_div);
-	ts2020_writereg(fe, 0x20, 0x27);
-	ts2020_writereg(fe, 0x07, 0x02);
-	ts2020_writereg(fe, 0x11, 0xff);
-	ts2020_writereg(fe, 0x60, 0xf9);
-	ts2020_writereg(fe, 0x08, 0x01);
-	ts2020_writereg(fe, 0x00, 0x41);
+	if (priv->tuner == TS2020_M88TS2020) {
+		ts2020_writereg(fe, 0x42, 0x73);
+		ts2020_writereg(fe, 0x05, priv->clk_out_div);
+		ts2020_writereg(fe, 0x20, 0x27);
+		ts2020_writereg(fe, 0x07, 0x02);
+		ts2020_writereg(fe, 0x11, 0xff);
+		ts2020_writereg(fe, 0x60, 0xf9);
+		ts2020_writereg(fe, 0x08, 0x01);
+		ts2020_writereg(fe, 0x00, 0x41);
+	} else {
+		static const struct ts2020_reg_val reg_vals[] = {
+			{0x7d, 0x9d},
+			{0x7c, 0x9a},
+			{0x7a, 0x76},
+			{0x3b, 0x01},
+			{0x63, 0x88},
+			{0x61, 0x85},
+			{0x22, 0x30},
+			{0x30, 0x40},
+			{0x20, 0x23},
+			{0x24, 0x02},
+			{0x12, 0xa0},
+		};
+
+		ts2020_writereg(fe, 0x00, 0x01);
+		ts2020_writereg(fe, 0x00, 0x03);
+
+		switch (priv->clk_out) {
+		case TS2020_CLK_OUT_DISABLED:
+			u8tmp = 0x60;
+			break;
+		case TS2020_CLK_OUT_ENABLED:
+			u8tmp = 0x70;
+			ts2020_writereg(fe, 0x05, priv->clk_out_div);
+			break;
+		case TS2020_CLK_OUT_ENABLED_XTALOUT:
+			u8tmp = 0x6c;
+			break;
+		default:
+			u8tmp = 0x60;
+			break;
+		}
+
+		ts2020_writereg(fe, 0x42, u8tmp);
+
+		if (priv->loop_through)
+			u8tmp = 0xec;
+		else
+			u8tmp = 0x6c;
+
+		ts2020_writereg(fe, 0x62, u8tmp);
+
+		for (i = 0; i < ARRAY_SIZE(reg_vals); i++)
+			ts2020_writereg(fe, reg_vals[i].reg, reg_vals[i].val);
+	}
 
 	return 0;
 }
@@ -203,7 +251,14 @@
 	ndiv = ndiv + ndiv % 2;
 	ndiv = ndiv - 1024;
 
-	ret = ts2020_writereg(fe, 0x10, 0x80 | lo);
+	if (priv->tuner == TS2020_M88TS2020) {
+		lpf_coeff = 2766;
+		ret = ts2020_writereg(fe, 0x10, 0x80 | lo);
+	} else {
+		lpf_coeff = 3200;
+		ret = ts2020_writereg(fe, 0x10, 0x0b);
+		ret |= ts2020_writereg(fe, 0x11, 0x40);
+	}
 
 	/* Set frequency divider */
 	ret |= ts2020_writereg(fe, 0x01, (ndiv >> 8) & 0xf);
@@ -220,7 +275,8 @@
 	ret |= ts2020_tuner_gate_ctrl(fe, 0x08);
 
 	/* Tuner RF */
-	ret |= ts2020_set_tuner_rf(fe);
+	if (priv->tuner == TS2020_M88TS2020)
+		ret |= ts2020_set_tuner_rf(fe);
 
 	gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000;
 	ret |= ts2020_writereg(fe, 0x04, gdiv28 & 0xff);
@@ -228,6 +284,15 @@
 	if (ret < 0)
 		return -ENODEV;
 
+	if (priv->tuner == TS2020_M88TS2022) {
+		ret = ts2020_writereg(fe, 0x25, 0x00);
+		ret |= ts2020_writereg(fe, 0x27, 0x70);
+		ret |= ts2020_writereg(fe, 0x41, 0x09);
+		ret |= ts2020_writereg(fe, 0x08, 0x0b);
+		if (ret < 0)
+			return -ENODEV;
+	}
+
 	value = ts2020_readreg(fe, 0x26);
 
 	f3db = (symbol_rate * 135) / 200 + 2000;
@@ -243,8 +308,6 @@
 	if (mlpf_max > 63)
 		mlpf_max = 63;
 
-	lpf_coeff = 2766;
-
 	nlpf = (f3db * gdiv28 * 2 / lpf_coeff /
 		(TS2020_XTAL_FREQ / 1000)  + 1) / 2;
 	if (nlpf > 23)
@@ -285,6 +348,13 @@
 {
 	struct ts2020_priv *priv = fe->tuner_priv;
 	*frequency = priv->frequency;
+
+	return 0;
+}
+
+static int ts2020_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	*frequency = 0; /* Zero-IF */
 	return 0;
 }
 
@@ -324,6 +394,7 @@
 	.sleep = ts2020_sleep,
 	.set_params = ts2020_set_params,
 	.get_frequency = ts2020_get_frequency,
+	.get_if_frequency = ts2020_get_if_frequency,
 	.get_rf_strength = ts2020_read_signal_strength,
 };
 
@@ -340,8 +411,10 @@
 
 	priv->i2c_address = config->tuner_address;
 	priv->i2c = i2c;
+	priv->clk_out = config->clk_out;
 	priv->clk_out_div = config->clk_out_div;
 	priv->frequency_div = config->frequency_div;
+	priv->fe = fe;
 	fe->tuner_priv = priv;
 
 	if (!priv->frequency_div)
@@ -358,9 +431,13 @@
 
 	/* Check the tuner version */
 	buf = ts2020_readreg(fe, 0x00);
-	if ((buf == 0x01) || (buf == 0x41) || (buf == 0x81))
+	if ((buf == 0x01) || (buf == 0x41) || (buf == 0x81)) {
 		printk(KERN_INFO "%s: Find tuner TS2020!\n", __func__);
-	else {
+		priv->tuner = TS2020_M88TS2020;
+	} else if ((buf == 0x83) || (buf == 0xc3)) {
+		printk(KERN_INFO "%s: Find tuner TS2022!\n", __func__);
+		priv->tuner = TS2020_M88TS2022;
+	} else {
 		printk(KERN_ERR "%s: Read tuner reg[0] = %d\n", __func__, buf);
 		kfree(priv);
 		return NULL;
@@ -373,6 +450,165 @@
 }
 EXPORT_SYMBOL(ts2020_attach);
 
+static int ts2020_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct ts2020_config *pdata = client->dev.platform_data;
+	struct dvb_frontend *fe = pdata->fe;
+	struct ts2020_priv *dev;
+	int ret;
+	u8 u8tmp;
+	unsigned int utmp;
+	char *chip_str;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	dev->i2c = client->adapter;
+	dev->i2c_address = client->addr;
+	dev->clk_out = pdata->clk_out;
+	dev->clk_out_div = pdata->clk_out_div;
+	dev->frequency_div = pdata->frequency_div;
+	dev->fe = fe;
+	fe->tuner_priv = dev;
+
+	/* check if the tuner is there */
+	ret = ts2020_readreg(fe, 0x00);
+	if (ret < 0)
+		goto err;
+	utmp = ret;
+
+	if ((utmp & 0x03) == 0x00) {
+		ret = ts2020_writereg(fe, 0x00, 0x01);
+		if (ret)
+			goto err;
+
+		usleep_range(2000, 50000);
+	}
+
+	ret = ts2020_writereg(fe, 0x00, 0x03);
+	if (ret)
+		goto err;
+
+	usleep_range(2000, 50000);
+
+	ret = ts2020_readreg(fe, 0x00);
+	if (ret < 0)
+		goto err;
+	utmp = ret;
+
+	dev_dbg(&client->dev, "chip_id=%02x\n", utmp);
+
+	switch (utmp) {
+	case 0x01:
+	case 0x41:
+	case 0x81:
+		dev->tuner = TS2020_M88TS2020;
+		chip_str = "TS2020";
+		if (!dev->frequency_div)
+			dev->frequency_div = 1060000;
+		break;
+	case 0xc3:
+	case 0x83:
+		dev->tuner = TS2020_M88TS2022;
+		chip_str = "TS2022";
+		if (!dev->frequency_div)
+			dev->frequency_div = 1103000;
+		break;
+	default:
+		ret = -ENODEV;
+		goto err;
+	}
+
+	if (dev->tuner == TS2020_M88TS2022) {
+		switch (dev->clk_out) {
+		case TS2020_CLK_OUT_DISABLED:
+			u8tmp = 0x60;
+			break;
+		case TS2020_CLK_OUT_ENABLED:
+			u8tmp = 0x70;
+			ret = ts2020_writereg(fe, 0x05, dev->clk_out_div);
+			if (ret)
+				goto err;
+			break;
+		case TS2020_CLK_OUT_ENABLED_XTALOUT:
+			u8tmp = 0x6c;
+			break;
+		default:
+			ret = -EINVAL;
+			goto err;
+		}
+
+		ret = ts2020_writereg(fe, 0x42, u8tmp);
+		if (ret)
+			goto err;
+
+		if (dev->loop_through)
+			u8tmp = 0xec;
+		else
+			u8tmp = 0x6c;
+
+		ret = ts2020_writereg(fe, 0x62, u8tmp);
+		if (ret)
+			goto err;
+	}
+
+	/* sleep */
+	ret = ts2020_writereg(fe, 0x00, 0x00);
+	if (ret)
+		goto err;
+
+	dev_info(&client->dev,
+		 "Montage Technology %s successfully identified\n", chip_str);
+
+	memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops,
+			sizeof(struct dvb_tuner_ops));
+	fe->ops.tuner_ops.release = NULL;
+
+	i2c_set_clientdata(client, dev);
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	kfree(dev);
+	return ret;
+}
+
+static int ts2020_remove(struct i2c_client *client)
+{
+	struct ts2020_priv *dev = i2c_get_clientdata(client);
+	struct dvb_frontend *fe = dev->fe;
+
+	dev_dbg(&client->dev, "\n");
+
+	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
+	fe->tuner_priv = NULL;
+	kfree(dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id ts2020_id_table[] = {
+	{"ts2020", 0},
+	{"ts2022", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, ts2020_id_table);
+
+static struct i2c_driver ts2020_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "ts2020",
+	},
+	.probe		= ts2020_probe,
+	.remove		= ts2020_remove,
+	.id_table	= ts2020_id_table,
+};
+
+module_i2c_driver(ts2020_driver);
+
 MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>");
 MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module");
 MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h
index 884631a..730c541 100644
--- a/drivers/media/dvb-frontends/ts2020.h
+++ b/drivers/media/dvb-frontends/ts2020.h
@@ -27,11 +27,34 @@
 
 struct ts2020_config {
 	u8 tuner_address;
-	u8 clk_out_div;
 	u32 frequency_div;
+
+	/*
+	 * RF loop-through
+	 */
+	u8 loop_through:1;
+
+	/*
+	 * clock output
+	 */
+#define TS2020_CLK_OUT_DISABLED        0
+#define TS2020_CLK_OUT_ENABLED         1
+#define TS2020_CLK_OUT_ENABLED_XTALOUT 2
+	u8 clk_out:2;
+
+	/*
+	 * clock output divider
+	 * 1 - 31
+	 */
+	u8 clk_out_div:5;
+
+	/*
+	 * pointer to DVB frontend
+	 */
+	struct dvb_frontend *fe;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_TS2020)
+#if IS_REACHABLE(CPTCFG_DVB_TS2020)
 
 extern struct dvb_frontend *ts2020_attach(
 	struct dvb_frontend *fe,
diff --git a/drivers/media/dvb-frontends/tua6100.h b/drivers/media/dvb-frontends/tua6100.h
index 2350c67..81e2d7d 100644
--- a/drivers/media/dvb-frontends/tua6100.h
+++ b/drivers/media/dvb-frontends/tua6100.h
@@ -34,7 +34,7 @@
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
-#if IS_ENABLED(CPTCFG_DVB_TUA6100)
+#if IS_REACHABLE(CPTCFG_DVB_TUA6100)
 extern struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c);
 #else
 static inline struct dvb_frontend* tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c)
diff --git a/drivers/media/dvb-frontends/ves1820.h b/drivers/media/dvb-frontends/ves1820.h
index 6b16280..16c5cdf 100644
--- a/drivers/media/dvb-frontends/ves1820.h
+++ b/drivers/media/dvb-frontends/ves1820.h
@@ -41,7 +41,7 @@
 	u8 selagc:1;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_VES1820)
+#if IS_REACHABLE(CPTCFG_DVB_VES1820)
 extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
 					   struct i2c_adapter* i2c, u8 pwm);
 #else
diff --git a/drivers/media/dvb-frontends/ves1x93.h b/drivers/media/dvb-frontends/ves1x93.h
index eb4f231..b453296 100644
--- a/drivers/media/dvb-frontends/ves1x93.h
+++ b/drivers/media/dvb-frontends/ves1x93.h
@@ -40,7 +40,7 @@
 	u8 invert_pwm:1;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_VES1X93)
+#if IS_REACHABLE(CPTCFG_DVB_VES1X93)
 extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
 					   struct i2c_adapter* i2c);
 #else
diff --git a/drivers/media/dvb-frontends/zl10036.h b/drivers/media/dvb-frontends/zl10036.h
index c68d190..4cac3e8 100644
--- a/drivers/media/dvb-frontends/zl10036.h
+++ b/drivers/media/dvb-frontends/zl10036.h
@@ -38,7 +38,7 @@
 	int rf_loop_enable;
 };
 
-#if IS_ENABLED(CPTCFG_DVB_ZL10036)
+#if IS_REACHABLE(CPTCFG_DVB_ZL10036)
 extern struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe,
 	const struct zl10036_config *config, struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/dvb-frontends/zl10039.h b/drivers/media/dvb-frontends/zl10039.h
index 2fbf630..bf29ab2 100644
--- a/drivers/media/dvb-frontends/zl10039.h
+++ b/drivers/media/dvb-frontends/zl10039.h
@@ -24,7 +24,7 @@
 
 #include <linux/kconfig.h>
 
-#if IS_ENABLED(CPTCFG_DVB_ZL10039)
+#if IS_REACHABLE(CPTCFG_DVB_ZL10039)
 struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe,
 					u8 i2c_addr,
 					struct i2c_adapter *i2c);
diff --git a/drivers/media/dvb-frontends/zl10353.h b/drivers/media/dvb-frontends/zl10353.h
index 2008ab3..a03f7a7 100644
--- a/drivers/media/dvb-frontends/zl10353.h
+++ b/drivers/media/dvb-frontends/zl10353.h
@@ -47,7 +47,7 @@
 	u8 pll_0;        /* default: 0x15 */
 };
 
-#if IS_ENABLED(CPTCFG_DVB_ZL10353)
+#if IS_REACHABLE(CPTCFG_DVB_ZL10353)
 extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config,
 					   struct i2c_adapter *i2c);
 #else
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index bd78e2f..86cb0ea 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -194,7 +194,7 @@
 config VIDEO_ADV7180
 	tristate "Analog Devices ADV7180 decoder"
 	depends on m
-	depends on VIDEO_V4L2 && I2C
+	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
 	---help---
 	  Support for the Analog Devices ADV7180 video decoder.
 
@@ -216,7 +216,7 @@
 	depends on !KERNEL_3_13
 	tristate "Analog Devices ADV7604 decoder"
 	depends on m
-	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
+	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
 	---help---
 	  Support for the Analog Devices ADV7604 video decoder.
 
@@ -229,7 +229,8 @@
 config VIDEO_ADV7842
 	tristate "Analog Devices ADV7842 decoder"
 	depends on m
-	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
+	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+	depends on HDMI
 	---help---
 	  Support for the Analog Devices ADV7842 video decoder.
 
@@ -466,7 +467,7 @@
 config VIDEO_ADV7511
 	tristate "Analog Devices ADV7511 encoder"
 	depends on m
-	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
+	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
 	---help---
 	  Support for the Analog Devices ADV7511 video encoder.
 
@@ -514,6 +515,18 @@
 	tristate
 	depends on m
 
+config VIDEO_OV2659
+	tristate "OmniVision OV2659 sensor support"
+	depends on m
+	depends on VIDEO_V4L2 && I2C
+	depends on MEDIA_CAMERA_SUPPORT
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV2659 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov2659.
+
 config VIDEO_OV7640
 	tristate "OmniVision OV7640 sensor support"
 	depends on m
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 61f289c..a16a729 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -77,3 +77,4 @@
 obj-$(CPTCFG_VIDEO_AK881X)		+= ak881x.o
 obj-$(CPTCFG_VIDEO_IR_I2C)  += ir-kbd-i2c.o
 obj-$(CPTCFG_VIDEO_ML86V7667)	+= ml86v7667.o
+obj-$(CPTCFG_VIDEO_OV2659)	+= ov2659.o
diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c
index 03382d3..7ef5f43 100644
--- a/drivers/media/i2c/ad9389b.c
+++ b/drivers/media/i2c/ad9389b.c
@@ -239,8 +239,8 @@
 {
 	struct ad9389b_state *state = get_ad9389b_state(sd);
 
-	if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
-		/* CEA format, not IT  */
+	if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
+		/* CE format, not IT  */
 		ad9389b_wr_and_or(sd, 0xcd, 0xbf, 0x00);
 	} else {
 		/* IT format */
@@ -255,11 +255,11 @@
 	switch (ctrl->val) {
 	case V4L2_DV_RGB_RANGE_AUTO:
 		/* automatic */
-		if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
-			/* cea format, RGB limited range (16-235) */
+		if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
+			/* CE format, RGB limited range (16-235) */
 			ad9389b_csc_rgb_full2limit(sd, true);
 		} else {
-			/* not cea format, RGB full range (0-255) */
+			/* not CE format, RGB full range (0-255) */
 			ad9389b_csc_rgb_full2limit(sd, false);
 		}
 		break;
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index bffe6eb..a493c0b 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -30,56 +30,60 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-ctrls.h>
 #include <linux/mutex.h>
+#include <linux/delay.h>
 
-#define ADV7180_INPUT_CONTROL_REG			0x00
-#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM	0x00
-#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
-#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM	0x20
-#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM	0x30
-#define ADV7180_INPUT_CONTROL_NTSC_J			0x40
-#define ADV7180_INPUT_CONTROL_NTSC_M			0x50
-#define ADV7180_INPUT_CONTROL_PAL60			0x60
-#define ADV7180_INPUT_CONTROL_NTSC_443			0x70
-#define ADV7180_INPUT_CONTROL_PAL_BG			0x80
-#define ADV7180_INPUT_CONTROL_PAL_N			0x90
-#define ADV7180_INPUT_CONTROL_PAL_M			0xa0
-#define ADV7180_INPUT_CONTROL_PAL_M_PED			0xb0
-#define ADV7180_INPUT_CONTROL_PAL_COMB_N		0xc0
-#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED		0xd0
-#define ADV7180_INPUT_CONTROL_PAL_SECAM			0xe0
-#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED		0xf0
+#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM		0x0
+#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED		0x1
+#define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM		0x2
+#define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM		0x3
+#define ADV7180_STD_NTSC_J				0x4
+#define ADV7180_STD_NTSC_M				0x5
+#define ADV7180_STD_PAL60				0x6
+#define ADV7180_STD_NTSC_443				0x7
+#define ADV7180_STD_PAL_BG				0x8
+#define ADV7180_STD_PAL_N				0x9
+#define ADV7180_STD_PAL_M				0xa
+#define ADV7180_STD_PAL_M_PED				0xb
+#define ADV7180_STD_PAL_COMB_N				0xc
+#define ADV7180_STD_PAL_COMB_N_PED			0xd
+#define ADV7180_STD_PAL_SECAM				0xe
+#define ADV7180_STD_PAL_SECAM_PED			0xf
+
+#define ADV7180_REG_INPUT_CONTROL			0x0000
 #define ADV7180_INPUT_CONTROL_INSEL_MASK		0x0f
 
-#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG		0x04
+#define ADV7182_REG_INPUT_VIDSEL			0x0002
+
+#define ADV7180_REG_EXTENDED_OUTPUT_CONTROL		0x0004
 #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS		0xC5
 
-#define ADV7180_AUTODETECT_ENABLE_REG			0x07
+#define ADV7180_REG_AUTODETECT_ENABLE			0x07
 #define ADV7180_AUTODETECT_DEFAULT			0x7f
 /* Contrast */
-#define ADV7180_CON_REG		0x08	/*Unsigned */
+#define ADV7180_REG_CON		0x0008	/*Unsigned */
 #define ADV7180_CON_MIN		0
 #define ADV7180_CON_DEF		128
 #define ADV7180_CON_MAX		255
 /* Brightness*/
-#define ADV7180_BRI_REG		0x0a	/*Signed */
+#define ADV7180_REG_BRI		0x000a	/*Signed */
 #define ADV7180_BRI_MIN		-128
 #define ADV7180_BRI_DEF		0
 #define ADV7180_BRI_MAX		127
 /* Hue */
-#define ADV7180_HUE_REG		0x0b	/*Signed, inverted */
+#define ADV7180_REG_HUE		0x000b	/*Signed, inverted */
 #define ADV7180_HUE_MIN		-127
 #define ADV7180_HUE_DEF		0
 #define ADV7180_HUE_MAX		128
 
-#define ADV7180_ADI_CTRL_REG				0x0e
-#define ADV7180_ADI_CTRL_IRQ_SPACE			0x20
+#define ADV7180_REG_CTRL		0x000e
+#define ADV7180_CTRL_IRQ_SPACE		0x20
 
-#define ADV7180_PWR_MAN_REG		0x0f
+#define ADV7180_REG_PWR_MAN		0x0f
 #define ADV7180_PWR_MAN_ON		0x04
 #define ADV7180_PWR_MAN_OFF		0x24
 #define ADV7180_PWR_MAN_RES		0x80
 
-#define ADV7180_STATUS1_REG				0x10
+#define ADV7180_REG_STATUS1		0x0010
 #define ADV7180_STATUS1_IN_LOCK		0x01
 #define ADV7180_STATUS1_AUTOD_MASK	0x70
 #define ADV7180_STATUS1_AUTOD_NTSM_M_J	0x00
@@ -91,49 +95,161 @@
 #define ADV7180_STATUS1_AUTOD_PAL_COMB	0x60
 #define ADV7180_STATUS1_AUTOD_SECAM_525	0x70
 
-#define ADV7180_IDENT_REG 0x11
+#define ADV7180_REG_IDENT 0x0011
 #define ADV7180_ID_7180 0x18
 
-#define ADV7180_ICONF1_ADI		0x40
+#define ADV7180_REG_ICONF1		0x0040
 #define ADV7180_ICONF1_ACTIVE_LOW	0x01
 #define ADV7180_ICONF1_PSYNC_ONLY	0x10
 #define ADV7180_ICONF1_ACTIVE_TO_CLR	0xC0
 /* Saturation */
-#define ADV7180_SD_SAT_CB_REG	0xe3	/*Unsigned */
-#define ADV7180_SD_SAT_CR_REG	0xe4	/*Unsigned */
+#define ADV7180_REG_SD_SAT_CB	0x00e3	/*Unsigned */
+#define ADV7180_REG_SD_SAT_CR	0x00e4	/*Unsigned */
 #define ADV7180_SAT_MIN		0
 #define ADV7180_SAT_DEF		128
 #define ADV7180_SAT_MAX		255
 
 #define ADV7180_IRQ1_LOCK	0x01
 #define ADV7180_IRQ1_UNLOCK	0x02
-#define ADV7180_ISR1_ADI	0x42
-#define ADV7180_ICR1_ADI	0x43
-#define ADV7180_IMR1_ADI	0x44
-#define ADV7180_IMR2_ADI	0x48
+#define ADV7180_REG_ISR1	0x0042
+#define ADV7180_REG_ICR1	0x0043
+#define ADV7180_REG_IMR1	0x0044
+#define ADV7180_REG_IMR2	0x0048
 #define ADV7180_IRQ3_AD_CHANGE	0x08
-#define ADV7180_ISR3_ADI	0x4A
-#define ADV7180_ICR3_ADI	0x4B
-#define ADV7180_IMR3_ADI	0x4C
-#define ADV7180_IMR4_ADI	0x50
+#define ADV7180_REG_ISR3	0x004A
+#define ADV7180_REG_ICR3	0x004B
+#define ADV7180_REG_IMR3	0x004C
+#define ADV7180_REG_IMR4	0x50
 
-#define ADV7180_NTSC_V_BIT_END_REG	0xE6
+#define ADV7180_REG_NTSC_V_BIT_END	0x00E6
 #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND	0x4F
 
+#define ADV7180_REG_VPP_SLAVE_ADDR	0xFD
+#define ADV7180_REG_CSI_SLAVE_ADDR	0xFE
+
+#define ADV7180_REG_FLCONTROL 0x40e0
+#define ADV7180_FLCONTROL_FL_ENABLE 0x1
+
+#define ADV7180_CSI_REG_PWRDN	0x00
+#define ADV7180_CSI_PWRDN	0x80
+
+#define ADV7180_INPUT_CVBS_AIN1 0x00
+#define ADV7180_INPUT_CVBS_AIN2 0x01
+#define ADV7180_INPUT_CVBS_AIN3 0x02
+#define ADV7180_INPUT_CVBS_AIN4 0x03
+#define ADV7180_INPUT_CVBS_AIN5 0x04
+#define ADV7180_INPUT_CVBS_AIN6 0x05
+#define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06
+#define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07
+#define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08
+#define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09
+#define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a
+
+#define ADV7182_INPUT_CVBS_AIN1 0x00
+#define ADV7182_INPUT_CVBS_AIN2 0x01
+#define ADV7182_INPUT_CVBS_AIN3 0x02
+#define ADV7182_INPUT_CVBS_AIN4 0x03
+#define ADV7182_INPUT_CVBS_AIN5 0x04
+#define ADV7182_INPUT_CVBS_AIN6 0x05
+#define ADV7182_INPUT_CVBS_AIN7 0x06
+#define ADV7182_INPUT_CVBS_AIN8 0x07
+#define ADV7182_INPUT_SVIDEO_AIN1_AIN2 0x08
+#define ADV7182_INPUT_SVIDEO_AIN3_AIN4 0x09
+#define ADV7182_INPUT_SVIDEO_AIN5_AIN6 0x0a
+#define ADV7182_INPUT_SVIDEO_AIN7_AIN8 0x0b
+#define ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3 0x0c
+#define ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0d
+#define ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2 0x0e
+#define ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4 0x0f
+#define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
+#define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
+
+#define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
+#define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42
+
+#define V4L2_CID_ADV_FAST_SWITCH	(V4L2_CID_USER_ADV7180_BASE + 0x00)
+
+struct adv7180_state;
+
+#define ADV7180_FLAG_RESET_POWERED	BIT(0)
+#define ADV7180_FLAG_V2			BIT(1)
+#define ADV7180_FLAG_MIPI_CSI2		BIT(2)
+#define ADV7180_FLAG_I2P		BIT(3)
+
+struct adv7180_chip_info {
+	unsigned int flags;
+	unsigned int valid_input_mask;
+	int (*set_std)(struct adv7180_state *st, unsigned int std);
+	int (*select_input)(struct adv7180_state *st, unsigned int input);
+	int (*init)(struct adv7180_state *state);
+};
+
 struct adv7180_state {
 	struct v4l2_ctrl_handler ctrl_hdl;
 	struct v4l2_subdev	sd;
+	struct media_pad	pad;
 	struct mutex		mutex; /* mutual excl. when accessing chip */
 	int			irq;
 	v4l2_std_id		curr_norm;
 	bool			autodetect;
 	bool			powered;
 	u8			input;
+
+	struct i2c_client	*client;
+	unsigned int		register_page;
+	struct i2c_client	*csi_client;
+	struct i2c_client	*vpp_client;
+	const struct adv7180_chip_info *chip_info;
+	enum v4l2_field		field;
 };
 #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,		\
 					    struct adv7180_state,	\
 					    ctrl_hdl)->sd)
 
+static int adv7180_select_page(struct adv7180_state *state, unsigned int page)
+{
+	if (state->register_page != page) {
+		i2c_smbus_write_byte_data(state->client, ADV7180_REG_CTRL,
+			page);
+		state->register_page = page;
+	}
+
+	return 0;
+}
+
+static int adv7180_write(struct adv7180_state *state, unsigned int reg,
+	unsigned int value)
+{
+	lockdep_assert_held(&state->mutex);
+	adv7180_select_page(state, reg >> 8);
+	return i2c_smbus_write_byte_data(state->client, reg & 0xff, value);
+}
+
+static int adv7180_read(struct adv7180_state *state, unsigned int reg)
+{
+	lockdep_assert_held(&state->mutex);
+	adv7180_select_page(state, reg >> 8);
+	return i2c_smbus_read_byte_data(state->client, reg & 0xff);
+}
+
+static int adv7180_csi_write(struct adv7180_state *state, unsigned int reg,
+	unsigned int value)
+{
+	return i2c_smbus_write_byte_data(state->csi_client, reg, value);
+}
+
+static int adv7180_set_video_standard(struct adv7180_state *state,
+	unsigned int std)
+{
+	return state->chip_info->set_std(state, std);
+}
+
+static int adv7180_vpp_write(struct adv7180_state *state, unsigned int reg,
+	unsigned int value)
+{
+	return i2c_smbus_write_byte_data(state->vpp_client, reg, value);
+}
+
 static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
 {
 	/* in case V4L2_IN_ST_NO_SIGNAL */
@@ -165,22 +281,22 @@
 static int v4l2_std_to_adv7180(v4l2_std_id std)
 {
 	if (std == V4L2_STD_PAL_60)
-		return ADV7180_INPUT_CONTROL_PAL60;
+		return ADV7180_STD_PAL60;
 	if (std == V4L2_STD_NTSC_443)
-		return ADV7180_INPUT_CONTROL_NTSC_443;
+		return ADV7180_STD_NTSC_443;
 	if (std == V4L2_STD_PAL_N)
-		return ADV7180_INPUT_CONTROL_PAL_N;
+		return ADV7180_STD_PAL_N;
 	if (std == V4L2_STD_PAL_M)
-		return ADV7180_INPUT_CONTROL_PAL_M;
+		return ADV7180_STD_PAL_M;
 	if (std == V4L2_STD_PAL_Nc)
-		return ADV7180_INPUT_CONTROL_PAL_COMB_N;
+		return ADV7180_STD_PAL_COMB_N;
 
 	if (std & V4L2_STD_PAL)
-		return ADV7180_INPUT_CONTROL_PAL_BG;
+		return ADV7180_STD_PAL_BG;
 	if (std & V4L2_STD_NTSC)
-		return ADV7180_INPUT_CONTROL_NTSC_M;
+		return ADV7180_STD_NTSC_M;
 	if (std & V4L2_STD_SECAM)
-		return ADV7180_INPUT_CONTROL_PAL_SECAM;
+		return ADV7180_STD_PAL_SECAM;
 
 	return -EINVAL;
 }
@@ -193,10 +309,10 @@
 	return 0;
 }
 
-static int __adv7180_status(struct i2c_client *client, u32 *status,
+static int __adv7180_status(struct adv7180_state *state, u32 *status,
 			    v4l2_std_id *std)
 {
-	int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
+	int status1 = adv7180_read(state, ADV7180_REG_STATUS1);
 
 	if (status1 < 0)
 		return status1;
@@ -225,7 +341,7 @@
 	if (!state->autodetect || state->irq > 0)
 		*std = state->curr_norm;
 	else
-		err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std);
+		err = __adv7180_status(state, NULL, std);
 
 	mutex_unlock(&state->mutex);
 	return err;
@@ -236,26 +352,19 @@
 {
 	struct adv7180_state *state = to_state(sd);
 	int ret = mutex_lock_interruptible(&state->mutex);
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (ret)
 		return ret;
 
-	/* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
-	 * all inputs and let the card driver take care of validation
-	 */
-	if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input)
+	if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) {
+		ret = -EINVAL;
 		goto out;
+	}
 
-	ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG);
+	ret = state->chip_info->select_input(state, input);
 
-	if (ret < 0)
-		goto out;
-
-	ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
-	ret = i2c_smbus_write_byte_data(client,
-					ADV7180_INPUT_CONTROL_REG, ret | input);
-	state->input = input;
+	if (ret == 0)
+		state->input = input;
 out:
 	mutex_unlock(&state->mutex);
 	return ret;
@@ -268,74 +377,104 @@
 	if (ret)
 		return ret;
 
-	ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL);
+	ret = __adv7180_status(state, status, NULL);
 	mutex_unlock(&state->mutex);
 	return ret;
 }
 
+static int adv7180_program_std(struct adv7180_state *state)
+{
+	int ret;
+
+	if (state->autodetect) {
+		ret = adv7180_set_video_standard(state,
+			ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
+		if (ret < 0)
+			return ret;
+
+		__adv7180_status(state, NULL, &state->curr_norm);
+	} else {
+		ret = v4l2_std_to_adv7180(state->curr_norm);
+		if (ret < 0)
+			return ret;
+
+		ret = adv7180_set_video_standard(state, ret);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
 static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
 {
 	struct adv7180_state *state = to_state(sd);
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	int ret = mutex_lock_interruptible(&state->mutex);
+
 	if (ret)
 		return ret;
 
 	/* all standards -> autodetect */
 	if (std == V4L2_STD_ALL) {
-		ret =
-		    i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
-				ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
-					      | state->input);
-		if (ret < 0)
-			goto out;
-
-		__adv7180_status(client, NULL, &state->curr_norm);
 		state->autodetect = true;
 	} else {
+		/* Make sure we can support this std */
 		ret = v4l2_std_to_adv7180(std);
 		if (ret < 0)
 			goto out;
 
-		ret = i2c_smbus_write_byte_data(client,
-						ADV7180_INPUT_CONTROL_REG,
-						ret | state->input);
-		if (ret < 0)
-			goto out;
-
 		state->curr_norm = std;
 		state->autodetect = false;
 	}
-	ret = 0;
+
+	ret = adv7180_program_std(state);
 out:
 	mutex_unlock(&state->mutex);
 	return ret;
 }
 
-static int adv7180_set_power(struct adv7180_state *state,
-	struct i2c_client *client, bool on)
+static int adv7180_set_power(struct adv7180_state *state, bool on)
 {
 	u8 val;
+	int ret;
 
 	if (on)
 		val = ADV7180_PWR_MAN_ON;
 	else
 		val = ADV7180_PWR_MAN_OFF;
 
-	return i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, val);
+	ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val);
+	if (ret)
+		return ret;
+
+	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+		if (on) {
+			adv7180_csi_write(state, 0xDE, 0x02);
+			adv7180_csi_write(state, 0xD2, 0xF7);
+			adv7180_csi_write(state, 0xD8, 0x65);
+			adv7180_csi_write(state, 0xE0, 0x09);
+			adv7180_csi_write(state, 0x2C, 0x00);
+			if (state->field == V4L2_FIELD_NONE)
+				adv7180_csi_write(state, 0x1D, 0x80);
+			adv7180_csi_write(state, 0x00, 0x00);
+		} else {
+			adv7180_csi_write(state, 0x00, 0x80);
+		}
+	}
+
+	return 0;
 }
 
 static int adv7180_s_power(struct v4l2_subdev *sd, int on)
 {
 	struct adv7180_state *state = to_state(sd);
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	int ret;
 
 	ret = mutex_lock_interruptible(&state->mutex);
 	if (ret)
 		return ret;
 
-	ret = adv7180_set_power(state, client, on);
+	ret = adv7180_set_power(state, on);
 	if (ret == 0)
 		state->powered = on;
 
@@ -347,7 +486,6 @@
 {
 	struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
 	struct adv7180_state *state = to_state(sd);
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	int ret = mutex_lock_interruptible(&state->mutex);
 	int val;
 
@@ -356,26 +494,36 @@
 	val = ctrl->val;
 	switch (ctrl->id) {
 	case V4L2_CID_BRIGHTNESS:
-		ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val);
+		ret = adv7180_write(state, ADV7180_REG_BRI, val);
 		break;
 	case V4L2_CID_HUE:
 		/*Hue is inverted according to HSL chart */
-		ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val);
+		ret = adv7180_write(state, ADV7180_REG_HUE, -val);
 		break;
 	case V4L2_CID_CONTRAST:
-		ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val);
+		ret = adv7180_write(state, ADV7180_REG_CON, val);
 		break;
 	case V4L2_CID_SATURATION:
 		/*
 		 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
 		 *Let's not confuse the user, everybody understands saturation
 		 */
-		ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
-						val);
+		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CB, val);
 		if (ret < 0)
 			break;
-		ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
-						val);
+		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CR, val);
+		break;
+	case V4L2_CID_ADV_FAST_SWITCH:
+		if (ctrl->val) {
+			/* ADI required write */
+			adv7180_write(state, 0x80d9, 0x44);
+			adv7180_write(state, ADV7180_REG_FLCONTROL,
+				ADV7180_FLCONTROL_FL_ENABLE);
+		} else {
+			/* ADI required write */
+			adv7180_write(state, 0x80d9, 0xc4);
+			adv7180_write(state, ADV7180_REG_FLCONTROL, 0x00);
+		}
 		break;
 	default:
 		ret = -EINVAL;
@@ -389,6 +537,16 @@
 	.s_ctrl = adv7180_s_ctrl,
 };
 
+static const struct v4l2_ctrl_config adv7180_ctrl_fast_switch = {
+	.ops = &adv7180_ctrl_ops,
+	.id = V4L2_CID_ADV_FAST_SWITCH,
+	.name = "Fast Switching",
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+};
+
 static int adv7180_init_controls(struct adv7180_state *state)
 {
 	v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
@@ -405,6 +563,8 @@
 	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
 			  V4L2_CID_HUE, ADV7180_HUE_MIN,
 			  ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
+	v4l2_ctrl_new_custom(&state->ctrl_hdl, &adv7180_ctrl_fast_switch, NULL);
+
 	state->sd.ctrl_handler = &state->ctrl_hdl;
 	if (state->ctrl_hdl.error) {
 		int err = state->ctrl_hdl.error;
@@ -421,13 +581,14 @@
 	v4l2_ctrl_handler_free(&state->ctrl_hdl);
 }
 
-static int adv7180_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
-				 u32 *code)
+static int adv7180_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
 {
-	if (index > 0)
+	if (code->index != 0)
 		return -EINVAL;
 
-	*code = MEDIA_BUS_FMT_YUYV8_2X8;
+	code->code = MEDIA_BUS_FMT_YUYV8_2X8;
 
 	return 0;
 }
@@ -439,23 +600,118 @@
 
 	fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
 	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
-	fmt->field = V4L2_FIELD_INTERLACED;
 	fmt->width = 720;
 	fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
 
 	return 0;
 }
 
+static int adv7180_set_field_mode(struct adv7180_state *state)
+{
+	if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
+		return 0;
+
+	if (state->field == V4L2_FIELD_NONE) {
+		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+			adv7180_csi_write(state, 0x01, 0x20);
+			adv7180_csi_write(state, 0x02, 0x28);
+			adv7180_csi_write(state, 0x03, 0x38);
+			adv7180_csi_write(state, 0x04, 0x30);
+			adv7180_csi_write(state, 0x05, 0x30);
+			adv7180_csi_write(state, 0x06, 0x80);
+			adv7180_csi_write(state, 0x07, 0x70);
+			adv7180_csi_write(state, 0x08, 0x50);
+		}
+		adv7180_vpp_write(state, 0xa3, 0x00);
+		adv7180_vpp_write(state, 0x5b, 0x00);
+		adv7180_vpp_write(state, 0x55, 0x80);
+	} else {
+		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+			adv7180_csi_write(state, 0x01, 0x18);
+			adv7180_csi_write(state, 0x02, 0x18);
+			adv7180_csi_write(state, 0x03, 0x30);
+			adv7180_csi_write(state, 0x04, 0x20);
+			adv7180_csi_write(state, 0x05, 0x28);
+			adv7180_csi_write(state, 0x06, 0x40);
+			adv7180_csi_write(state, 0x07, 0x58);
+			adv7180_csi_write(state, 0x08, 0x30);
+		}
+		adv7180_vpp_write(state, 0xa3, 0x70);
+		adv7180_vpp_write(state, 0x5b, 0x80);
+		adv7180_vpp_write(state, 0x55, 0x00);
+	}
+
+	return 0;
+}
+
+static int adv7180_get_pad_format(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_format *format)
+{
+	struct adv7180_state *state = to_state(sd);
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+		format->format = *v4l2_subdev_get_try_format(sd, cfg, 0);
+	} else {
+		adv7180_mbus_fmt(sd, &format->format);
+		format->format.field = state->field;
+	}
+
+	return 0;
+}
+
+static int adv7180_set_pad_format(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_format *format)
+{
+	struct adv7180_state *state = to_state(sd);
+	struct v4l2_mbus_framefmt *framefmt;
+
+	switch (format->format.field) {
+	case V4L2_FIELD_NONE:
+		if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
+			format->format.field = V4L2_FIELD_INTERLACED;
+		break;
+	default:
+		format->format.field = V4L2_FIELD_INTERLACED;
+		break;
+	}
+
+	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		framefmt = &format->format;
+		if (state->field != format->format.field) {
+			state->field = format->format.field;
+			adv7180_set_power(state, false);
+			adv7180_set_field_mode(state);
+			adv7180_set_power(state, true);
+		}
+	} else {
+		framefmt = v4l2_subdev_get_try_format(sd, cfg, 0);
+		*framefmt = format->format;
+	}
+
+	return adv7180_mbus_fmt(sd, framefmt);
+}
+
 static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
 				 struct v4l2_mbus_config *cfg)
 {
-	/*
-	 * The ADV7180 sensor supports BT.601/656 output modes.
-	 * The BT.656 is default and not yet configurable by s/w.
-	 */
-	cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
-		     V4L2_MBUS_DATA_ACTIVE_HIGH;
-	cfg->type = V4L2_MBUS_BT656;
+	struct adv7180_state *state = to_state(sd);
+
+	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+		cfg->type = V4L2_MBUS_CSI2;
+		cfg->flags = V4L2_MBUS_CSI2_1_LANE |
+				V4L2_MBUS_CSI2_CHANNEL_0 |
+				V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	} else {
+		/*
+		 * The ADV7180 sensor supports BT.601/656 output modes.
+		 * The BT.656 is default and not yet configurable by s/w.
+		 */
+		cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+				 V4L2_MBUS_DATA_ACTIVE_HIGH;
+		cfg->type = V4L2_MBUS_BT656;
+	}
 
 	return 0;
 }
@@ -465,139 +721,439 @@
 	.querystd = adv7180_querystd,
 	.g_input_status = adv7180_g_input_status,
 	.s_routing = adv7180_s_routing,
-	.enum_mbus_fmt = adv7180_enum_mbus_fmt,
-	.try_mbus_fmt = adv7180_mbus_fmt,
-	.g_mbus_fmt = adv7180_mbus_fmt,
-	.s_mbus_fmt = adv7180_mbus_fmt,
 	.g_mbus_config = adv7180_g_mbus_config,
 };
 
+
 static const struct v4l2_subdev_core_ops adv7180_core_ops = {
 	.s_power = adv7180_s_power,
 };
 
+static const struct v4l2_subdev_pad_ops adv7180_pad_ops = {
+	.enum_mbus_code = adv7180_enum_mbus_code,
+	.set_fmt = adv7180_set_pad_format,
+	.get_fmt = adv7180_get_pad_format,
+};
+
 static const struct v4l2_subdev_ops adv7180_ops = {
 	.core = &adv7180_core_ops,
 	.video = &adv7180_video_ops,
+	.pad = &adv7180_pad_ops,
 };
 
 static irqreturn_t adv7180_irq(int irq, void *devid)
 {
 	struct adv7180_state *state = devid;
-	struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
 	u8 isr3;
 
 	mutex_lock(&state->mutex);
-	i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
-				  ADV7180_ADI_CTRL_IRQ_SPACE);
-	isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI);
+	isr3 = adv7180_read(state, ADV7180_REG_ISR3);
 	/* clear */
-	i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3);
-	i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0);
+	adv7180_write(state, ADV7180_REG_ICR3, isr3);
 
 	if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
-		__adv7180_status(client, NULL, &state->curr_norm);
+		__adv7180_status(state, NULL, &state->curr_norm);
 	mutex_unlock(&state->mutex);
 
 	return IRQ_HANDLED;
 }
 
-static int init_device(struct i2c_client *client, struct adv7180_state *state)
+static int adv7180_init(struct adv7180_state *state)
 {
 	int ret;
 
-	/* Initialize adv7180 */
-	/* Enable autodetection */
-	if (state->autodetect) {
-		ret =
-		    i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
-				ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
-					      | state->input);
-		if (ret < 0)
-			return ret;
-
-		ret =
-		    i2c_smbus_write_byte_data(client,
-					      ADV7180_AUTODETECT_ENABLE_REG,
-					      ADV7180_AUTODETECT_DEFAULT);
-		if (ret < 0)
-			return ret;
-	} else {
-		ret = v4l2_std_to_adv7180(state->curr_norm);
-		if (ret < 0)
-			return ret;
-
-		ret =
-		    i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
-					      ret | state->input);
-		if (ret < 0)
-			return ret;
-
-	}
 	/* ITU-R BT.656-4 compatible */
-	ret = i2c_smbus_write_byte_data(client,
-			ADV7180_EXTENDED_OUTPUT_CONTROL_REG,
+	ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
 			ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
 	if (ret < 0)
 		return ret;
 
 	/* Manually set V bit end position in NTSC mode */
-	ret = i2c_smbus_write_byte_data(client,
-					ADV7180_NTSC_V_BIT_END_REG,
+	return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
 					ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
+}
+
+static int adv7180_set_std(struct adv7180_state *state, unsigned int std)
+{
+	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
+		(std << 4) | state->input);
+}
+
+static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
+{
+	int ret;
+
+	ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
 	if (ret < 0)
 		return ret;
 
-	/* read current norm */
-	__adv7180_status(client, NULL, &state->curr_norm);
+	ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
+	ret |= input;
+	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret);
+}
 
-	/* register for interrupts */
-	if (state->irq > 0) {
-		ret = request_threaded_irq(state->irq, NULL, adv7180_irq,
-					   IRQF_ONESHOT, KBUILD_MODNAME, state);
-		if (ret)
-			return ret;
+static int adv7182_init(struct adv7180_state *state)
+{
+	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
+		adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR,
+			ADV7180_DEFAULT_CSI_I2C_ADDR << 1);
 
-		ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
-						ADV7180_ADI_CTRL_IRQ_SPACE);
-		if (ret < 0)
-			goto err;
+	if (state->chip_info->flags & ADV7180_FLAG_I2P)
+		adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR,
+			ADV7180_DEFAULT_VPP_I2C_ADDR << 1);
 
-		/* config the Interrupt pin to be active low */
-		ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
-						ADV7180_ICONF1_ACTIVE_LOW |
-						ADV7180_ICONF1_PSYNC_ONLY);
-		if (ret < 0)
-			goto err;
+	if (state->chip_info->flags & ADV7180_FLAG_V2) {
+		/* ADI recommended writes for improved video quality */
+		adv7180_write(state, 0x0080, 0x51);
+		adv7180_write(state, 0x0081, 0x51);
+		adv7180_write(state, 0x0082, 0x68);
+	}
 
-		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
-		if (ret < 0)
-			goto err;
+	/* ADI required writes */
+	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+		adv7180_write(state, 0x0003, 0x4e);
+		adv7180_write(state, 0x0004, 0x57);
+		adv7180_write(state, 0x001d, 0xc0);
+	} else {
+		if (state->chip_info->flags & ADV7180_FLAG_V2)
+			adv7180_write(state, 0x0004, 0x17);
+		else
+			adv7180_write(state, 0x0004, 0x07);
+		adv7180_write(state, 0x0003, 0x0c);
+		adv7180_write(state, 0x001d, 0x40);
+	}
 
-		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
-		if (ret < 0)
-			goto err;
+	adv7180_write(state, 0x0013, 0x00);
 
-		/* enable AD change interrupts interrupts */
-		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
-						ADV7180_IRQ3_AD_CHANGE);
-		if (ret < 0)
-			goto err;
+	return 0;
+}
 
-		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
-		if (ret < 0)
-			goto err;
+static int adv7182_set_std(struct adv7180_state *state, unsigned int std)
+{
+	return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, std << 4);
+}
 
-		ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
-						0);
-		if (ret < 0)
-			goto err;
+enum adv7182_input_type {
+	ADV7182_INPUT_TYPE_CVBS,
+	ADV7182_INPUT_TYPE_DIFF_CVBS,
+	ADV7182_INPUT_TYPE_SVIDEO,
+	ADV7182_INPUT_TYPE_YPBPR,
+};
+
+static enum adv7182_input_type adv7182_get_input_type(unsigned int input)
+{
+	switch (input) {
+	case ADV7182_INPUT_CVBS_AIN1:
+	case ADV7182_INPUT_CVBS_AIN2:
+	case ADV7182_INPUT_CVBS_AIN3:
+	case ADV7182_INPUT_CVBS_AIN4:
+	case ADV7182_INPUT_CVBS_AIN5:
+	case ADV7182_INPUT_CVBS_AIN6:
+	case ADV7182_INPUT_CVBS_AIN7:
+	case ADV7182_INPUT_CVBS_AIN8:
+		return ADV7182_INPUT_TYPE_CVBS;
+	case ADV7182_INPUT_SVIDEO_AIN1_AIN2:
+	case ADV7182_INPUT_SVIDEO_AIN3_AIN4:
+	case ADV7182_INPUT_SVIDEO_AIN5_AIN6:
+	case ADV7182_INPUT_SVIDEO_AIN7_AIN8:
+		return ADV7182_INPUT_TYPE_SVIDEO;
+	case ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3:
+	case ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6:
+		return ADV7182_INPUT_TYPE_YPBPR;
+	case ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2:
+	case ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4:
+	case ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6:
+	case ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8:
+		return ADV7182_INPUT_TYPE_DIFF_CVBS;
+	default: /* Will never happen */
+		return 0;
+	}
+}
+
+/* ADI recommended writes to registers 0x52, 0x53, 0x54 */
+static unsigned int adv7182_lbias_settings[][3] = {
+	[ADV7182_INPUT_TYPE_CVBS] = { 0xCB, 0x4E, 0x80 },
+	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
+	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
+	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
+};
+
+static unsigned int adv7280_lbias_settings[][3] = {
+	[ADV7182_INPUT_TYPE_CVBS] = { 0xCD, 0x4E, 0x80 },
+	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
+	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
+	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
+};
+
+static int adv7182_select_input(struct adv7180_state *state, unsigned int input)
+{
+	enum adv7182_input_type input_type;
+	unsigned int *lbias;
+	unsigned int i;
+	int ret;
+
+	ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, input);
+	if (ret)
+		return ret;
+
+	/* Reset clamp circuitry - ADI recommended writes */
+	adv7180_write(state, 0x809c, 0x00);
+	adv7180_write(state, 0x809c, 0xff);
+
+	input_type = adv7182_get_input_type(input);
+
+	switch (input_type) {
+	case ADV7182_INPUT_TYPE_CVBS:
+	case ADV7182_INPUT_TYPE_DIFF_CVBS:
+		/* ADI recommends to use the SH1 filter */
+		adv7180_write(state, 0x0017, 0x41);
+		break;
+	default:
+		adv7180_write(state, 0x0017, 0x01);
+		break;
+	}
+
+	if (state->chip_info->flags & ADV7180_FLAG_V2)
+		lbias = adv7280_lbias_settings[input_type];
+	else
+		lbias = adv7182_lbias_settings[input_type];
+
+	for (i = 0; i < ARRAY_SIZE(adv7182_lbias_settings[0]); i++)
+		adv7180_write(state, 0x0052 + i, lbias[i]);
+
+	if (input_type == ADV7182_INPUT_TYPE_DIFF_CVBS) {
+		/* ADI required writes to make differential CVBS work */
+		adv7180_write(state, 0x005f, 0xa8);
+		adv7180_write(state, 0x005a, 0x90);
+		adv7180_write(state, 0x0060, 0xb0);
+		adv7180_write(state, 0x80b6, 0x08);
+		adv7180_write(state, 0x80c0, 0xa0);
+	} else {
+		adv7180_write(state, 0x005f, 0xf0);
+		adv7180_write(state, 0x005a, 0xd0);
+		adv7180_write(state, 0x0060, 0x10);
+		adv7180_write(state, 0x80b6, 0x9c);
+		adv7180_write(state, 0x80c0, 0x00);
 	}
 
 	return 0;
+}
 
-err:
-	free_irq(state->irq, state);
+static const struct adv7180_chip_info adv7180_info = {
+	.flags = ADV7180_FLAG_RESET_POWERED,
+	/* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
+	 * all inputs and let the card driver take care of validation
+	 */
+	.valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) |
+		BIT(ADV7180_INPUT_CVBS_AIN2) |
+		BIT(ADV7180_INPUT_CVBS_AIN3) |
+		BIT(ADV7180_INPUT_CVBS_AIN4) |
+		BIT(ADV7180_INPUT_CVBS_AIN5) |
+		BIT(ADV7180_INPUT_CVBS_AIN6) |
+		BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) |
+		BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) |
+		BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) |
+		BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) |
+		BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6),
+	.init = adv7180_init,
+	.set_std = adv7180_set_std,
+	.select_input = adv7180_select_input,
+};
+
+static const struct adv7180_chip_info adv7182_info = {
+	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+		BIT(ADV7182_INPUT_CVBS_AIN2) |
+		BIT(ADV7182_INPUT_CVBS_AIN3) |
+		BIT(ADV7182_INPUT_CVBS_AIN4) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4),
+	.init = adv7182_init,
+	.set_std = adv7182_set_std,
+	.select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7280_info = {
+	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
+	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+		BIT(ADV7182_INPUT_CVBS_AIN2) |
+		BIT(ADV7182_INPUT_CVBS_AIN3) |
+		BIT(ADV7182_INPUT_CVBS_AIN4) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3),
+	.init = adv7182_init,
+	.set_std = adv7182_set_std,
+	.select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7280_m_info = {
+	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
+	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+		BIT(ADV7182_INPUT_CVBS_AIN2) |
+		BIT(ADV7182_INPUT_CVBS_AIN3) |
+		BIT(ADV7182_INPUT_CVBS_AIN4) |
+		BIT(ADV7182_INPUT_CVBS_AIN5) |
+		BIT(ADV7182_INPUT_CVBS_AIN6) |
+		BIT(ADV7182_INPUT_CVBS_AIN7) |
+		BIT(ADV7182_INPUT_CVBS_AIN8) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
+		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6),
+	.init = adv7182_init,
+	.set_std = adv7182_set_std,
+	.select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7281_info = {
+	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
+	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+		BIT(ADV7182_INPUT_CVBS_AIN2) |
+		BIT(ADV7182_INPUT_CVBS_AIN7) |
+		BIT(ADV7182_INPUT_CVBS_AIN8) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
+	.init = adv7182_init,
+	.set_std = adv7182_set_std,
+	.select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7281_m_info = {
+	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
+	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+		BIT(ADV7182_INPUT_CVBS_AIN2) |
+		BIT(ADV7182_INPUT_CVBS_AIN3) |
+		BIT(ADV7182_INPUT_CVBS_AIN4) |
+		BIT(ADV7182_INPUT_CVBS_AIN7) |
+		BIT(ADV7182_INPUT_CVBS_AIN8) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
+	.init = adv7182_init,
+	.set_std = adv7182_set_std,
+	.select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7281_ma_info = {
+	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
+	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+		BIT(ADV7182_INPUT_CVBS_AIN2) |
+		BIT(ADV7182_INPUT_CVBS_AIN3) |
+		BIT(ADV7182_INPUT_CVBS_AIN4) |
+		BIT(ADV7182_INPUT_CVBS_AIN5) |
+		BIT(ADV7182_INPUT_CVBS_AIN6) |
+		BIT(ADV7182_INPUT_CVBS_AIN7) |
+		BIT(ADV7182_INPUT_CVBS_AIN8) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
+		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
+	.init = adv7182_init,
+	.set_std = adv7182_set_std,
+	.select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7282_info = {
+	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
+	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+		BIT(ADV7182_INPUT_CVBS_AIN2) |
+		BIT(ADV7182_INPUT_CVBS_AIN7) |
+		BIT(ADV7182_INPUT_CVBS_AIN8) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
+	.init = adv7182_init,
+	.set_std = adv7182_set_std,
+	.select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7282_m_info = {
+	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
+	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+		BIT(ADV7182_INPUT_CVBS_AIN2) |
+		BIT(ADV7182_INPUT_CVBS_AIN3) |
+		BIT(ADV7182_INPUT_CVBS_AIN4) |
+		BIT(ADV7182_INPUT_CVBS_AIN7) |
+		BIT(ADV7182_INPUT_CVBS_AIN8) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
+		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
+	.init = adv7182_init,
+	.set_std = adv7182_set_std,
+	.select_input = adv7182_select_input,
+};
+
+static int init_device(struct adv7180_state *state)
+{
+	int ret;
+
+	mutex_lock(&state->mutex);
+
+	adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
+	usleep_range(2000, 10000);
+
+	ret = state->chip_info->init(state);
+	if (ret)
+		goto out_unlock;
+
+	ret = adv7180_program_std(state);
+	if (ret)
+		goto out_unlock;
+
+	adv7180_set_field_mode(state);
+
+	/* register for interrupts */
+	if (state->irq > 0) {
+		/* config the Interrupt pin to be active low */
+		ret = adv7180_write(state, ADV7180_REG_ICONF1,
+						ADV7180_ICONF1_ACTIVE_LOW |
+						ADV7180_ICONF1_PSYNC_ONLY);
+		if (ret < 0)
+			goto out_unlock;
+
+		ret = adv7180_write(state, ADV7180_REG_IMR1, 0);
+		if (ret < 0)
+			goto out_unlock;
+
+		ret = adv7180_write(state, ADV7180_REG_IMR2, 0);
+		if (ret < 0)
+			goto out_unlock;
+
+		/* enable AD change interrupts interrupts */
+		ret = adv7180_write(state, ADV7180_REG_IMR3,
+						ADV7180_IRQ3_AD_CHANGE);
+		if (ret < 0)
+			goto out_unlock;
+
+		ret = adv7180_write(state, ADV7180_REG_IMR4, 0);
+		if (ret < 0)
+			goto out_unlock;
+	}
+
+out_unlock:
+	mutex_unlock(&state->mutex);
+
 	return ret;
 }
 
@@ -616,26 +1172,63 @@
 		 client->addr, client->adapter->name);
 
 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
-	if (state == NULL) {
-		ret = -ENOMEM;
-		goto err;
+	if (state == NULL)
+		return -ENOMEM;
+
+	state->client = client;
+	state->field = V4L2_FIELD_INTERLACED;
+	state->chip_info = (struct adv7180_chip_info *)id->driver_data;
+
+	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+		state->csi_client = i2c_new_dummy(client->adapter,
+				ADV7180_DEFAULT_CSI_I2C_ADDR);
+		if (!state->csi_client)
+			return -ENOMEM;
+	}
+
+	if (state->chip_info->flags & ADV7180_FLAG_I2P) {
+		state->vpp_client = i2c_new_dummy(client->adapter,
+				ADV7180_DEFAULT_VPP_I2C_ADDR);
+		if (!state->vpp_client) {
+			ret = -ENOMEM;
+			goto err_unregister_csi_client;
+		}
 	}
 
 	state->irq = client->irq;
 	mutex_init(&state->mutex);
 	state->autodetect = true;
-	state->powered = true;
+	if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
+		state->powered = true;
+	else
+		state->powered = false;
 	state->input = 0;
 	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
+	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
 
 	ret = adv7180_init_controls(state);
 	if (ret)
-		goto err_unreg_subdev;
-	ret = init_device(client, state);
+		goto err_unregister_vpp_client;
+
+	state->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
+	ret = media_entity_init(&sd->entity, 1, &state->pad, 0);
 	if (ret)
 		goto err_free_ctrl;
 
+	ret = init_device(state);
+	if (ret)
+		goto err_media_entity_cleanup;
+
+	if (state->irq) {
+		ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
+					   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
+					   KBUILD_MODNAME, state);
+		if (ret)
+			goto err_media_entity_cleanup;
+	}
+
 	ret = v4l2_async_register_subdev(sd);
 	if (ret)
 		goto err_free_irq;
@@ -645,11 +1238,17 @@
 err_free_irq:
 	if (state->irq > 0)
 		free_irq(client->irq, state);
+err_media_entity_cleanup:
+	media_entity_cleanup(&sd->entity);
 err_free_ctrl:
 	adv7180_exit_controls(state);
-err_unreg_subdev:
+err_unregister_vpp_client:
+	if (state->chip_info->flags & ADV7180_FLAG_I2P)
+		i2c_unregister_device(state->vpp_client);
+err_unregister_csi_client:
+	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
+		i2c_unregister_device(state->csi_client);
 	mutex_destroy(&state->mutex);
-err:
 	return ret;
 }
 
@@ -663,15 +1262,32 @@
 	if (state->irq > 0)
 		free_irq(client->irq, state);
 
+	media_entity_cleanup(&sd->entity);
 	adv7180_exit_controls(state);
+
+	if (state->chip_info->flags & ADV7180_FLAG_I2P)
+		i2c_unregister_device(state->vpp_client);
+	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
+		i2c_unregister_device(state->csi_client);
+
 	mutex_destroy(&state->mutex);
+
 	return 0;
 }
 
 static const struct i2c_device_id adv7180_id[] = {
-	{KBUILD_MODNAME, 0},
+	{ "adv7180", (kernel_ulong_t)&adv7180_info },
+	{ "adv7182", (kernel_ulong_t)&adv7182_info },
+	{ "adv7280", (kernel_ulong_t)&adv7280_info },
+	{ "adv7280-m", (kernel_ulong_t)&adv7280_m_info },
+	{ "adv7281", (kernel_ulong_t)&adv7281_info },
+	{ "adv7281-m", (kernel_ulong_t)&adv7281_m_info },
+	{ "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info },
+	{ "adv7282", (kernel_ulong_t)&adv7282_info },
+	{ "adv7282-m", (kernel_ulong_t)&adv7282_m_info },
 	{},
 };
+MODULE_DEVICE_TABLE(i2c, adv7180_id);
 
 #ifdef CONFIG_PM_SLEEP
 static int adv7180_suspend(struct device *dev)
@@ -680,7 +1296,7 @@
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct adv7180_state *state = to_state(sd);
 
-	return adv7180_set_power(state, client, false);
+	return adv7180_set_power(state, false);
 }
 
 static int adv7180_resume(struct device *dev)
@@ -690,14 +1306,14 @@
 	struct adv7180_state *state = to_state(sd);
 	int ret;
 
-	if (state->powered) {
-		ret = adv7180_set_power(state, client, true);
-		if (ret)
-			return ret;
-	}
-	ret = init_device(client, state);
+	ret = init_device(state);
 	if (ret < 0)
 		return ret;
+
+	ret = adv7180_set_power(state, state->powered);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@@ -708,8 +1324,6 @@
 #define ADV7180_PM_OPS NULL
 #endif
 
-MODULE_DEVICE_TABLE(i2c, adv7180_id);
-
 static struct i2c_driver adv7180_driver = {
 	.driver = {
 		   .owner = THIS_MODULE,
diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c
index 9d38f7b..7c50833 100644
--- a/drivers/media/i2c/adv7343.c
+++ b/drivers/media/i2c/adv7343.c
@@ -506,7 +506,6 @@
 	struct adv7343_state *state = to_state(sd);
 
 	v4l2_async_unregister_subdev(&state->sd);
-	v4l2_device_unregister_subdev(sd);
 	v4l2_ctrl_handler_free(&state->hdl);
 
 	return 0;
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index 3a943c6..9e39254 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -312,8 +312,8 @@
 static void adv7511_set_IT_content_AVI_InfoFrame(struct v4l2_subdev *sd)
 {
 	struct adv7511_state *state = get_adv7511_state(sd);
-	if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
-		/* CEA format, not IT  */
+	if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
+		/* CE format, not IT  */
 		adv7511_wr_and_or(sd, 0x57, 0x7f, 0x00);
 	} else {
 		/* IT format */
@@ -331,11 +331,11 @@
 		/* automatic */
 		struct adv7511_state *state = get_adv7511_state(sd);
 
-		if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
-			/* cea format, RGB limited range (16-235) */
+		if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
+			/* CE format, RGB limited range (16-235) */
 			adv7511_csc_rgb_full2limit(sd, true);
 		} else {
-			/* not cea format, RGB full range (0-255) */
+			/* not CE format, RGB full range (0-255) */
 			adv7511_csc_rgb_full2limit(sd, false);
 		}
 	}
@@ -810,7 +810,7 @@
 }
 
 static int adv7511_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->pad != 0)
@@ -842,8 +842,9 @@
 	format->field = V4L2_FIELD_NONE;
 }
 
-static int adv7511_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
-			      struct v4l2_subdev_format *format)
+static int adv7511_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *format)
 {
 	struct adv7511_state *state = get_adv7511_state(sd);
 
@@ -855,7 +856,7 @@
 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
 		struct v4l2_mbus_framefmt *fmt;
 
-		fmt = v4l2_subdev_get_try_format(fh, format->pad);
+		fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
 		format->format.code = fmt->code;
 		format->format.colorspace = fmt->colorspace;
 		format->format.ycbcr_enc = fmt->ycbcr_enc;
@@ -870,8 +871,9 @@
 	return 0;
 }
 
-static int adv7511_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
-		       struct v4l2_subdev_format *format)
+static int adv7511_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *format)
 {
 	struct adv7511_state *state = get_adv7511_state(sd);
 	/*
@@ -905,7 +907,7 @@
 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
 		struct v4l2_mbus_framefmt *fmt;
 
-		fmt = v4l2_subdev_get_try_format(fh, format->pad);
+		fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
 		fmt->code = format->format.code;
 		fmt->colorspace = format->format.colorspace;
 		fmt->ycbcr_enc = format->format.ycbcr_enc;
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 764b2b7..f71406a 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -53,41 +53,41 @@
 MODULE_LICENSE("GPL");
 
 /* ADV7604 system clock frequency */
-#define ADV7604_fsc (28636360)
+#define ADV76XX_FSC (28636360)
 
-#define ADV7604_RGB_OUT					(1 << 1)
+#define ADV76XX_RGB_OUT					(1 << 1)
 
-#define ADV7604_OP_FORMAT_SEL_8BIT			(0 << 0)
+#define ADV76XX_OP_FORMAT_SEL_8BIT			(0 << 0)
 #define ADV7604_OP_FORMAT_SEL_10BIT			(1 << 0)
-#define ADV7604_OP_FORMAT_SEL_12BIT			(2 << 0)
+#define ADV76XX_OP_FORMAT_SEL_12BIT			(2 << 0)
 
-#define ADV7604_OP_MODE_SEL_SDR_422			(0 << 5)
+#define ADV76XX_OP_MODE_SEL_SDR_422			(0 << 5)
 #define ADV7604_OP_MODE_SEL_DDR_422			(1 << 5)
-#define ADV7604_OP_MODE_SEL_SDR_444			(2 << 5)
+#define ADV76XX_OP_MODE_SEL_SDR_444			(2 << 5)
 #define ADV7604_OP_MODE_SEL_DDR_444			(3 << 5)
-#define ADV7604_OP_MODE_SEL_SDR_422_2X			(4 << 5)
+#define ADV76XX_OP_MODE_SEL_SDR_422_2X			(4 << 5)
 #define ADV7604_OP_MODE_SEL_ADI_CM			(5 << 5)
 
-#define ADV7604_OP_CH_SEL_GBR				(0 << 5)
-#define ADV7604_OP_CH_SEL_GRB				(1 << 5)
-#define ADV7604_OP_CH_SEL_BGR				(2 << 5)
-#define ADV7604_OP_CH_SEL_RGB				(3 << 5)
-#define ADV7604_OP_CH_SEL_BRG				(4 << 5)
-#define ADV7604_OP_CH_SEL_RBG				(5 << 5)
+#define ADV76XX_OP_CH_SEL_GBR				(0 << 5)
+#define ADV76XX_OP_CH_SEL_GRB				(1 << 5)
+#define ADV76XX_OP_CH_SEL_BGR				(2 << 5)
+#define ADV76XX_OP_CH_SEL_RGB				(3 << 5)
+#define ADV76XX_OP_CH_SEL_BRG				(4 << 5)
+#define ADV76XX_OP_CH_SEL_RBG				(5 << 5)
 
-#define ADV7604_OP_SWAP_CB_CR				(1 << 0)
+#define ADV76XX_OP_SWAP_CB_CR				(1 << 0)
 
-enum adv7604_type {
+enum adv76xx_type {
 	ADV7604,
 	ADV7611,
 };
 
-struct adv7604_reg_seq {
+struct adv76xx_reg_seq {
 	unsigned int reg;
 	u8 val;
 };
 
-struct adv7604_format_info {
+struct adv76xx_format_info {
 	u32 code;
 	u8 op_ch_sel;
 	bool rgb_out;
@@ -95,8 +95,8 @@
 	u8 op_format_sel;
 };
 
-struct adv7604_chip_info {
-	enum adv7604_type type;
+struct adv76xx_chip_info {
+	enum adv76xx_type type;
 
 	bool has_afe;
 	unsigned int max_port;
@@ -109,8 +109,9 @@
 	unsigned int cable_det_mask;
 	unsigned int tdms_lock_mask;
 	unsigned int fmt_change_digital_mask;
+	unsigned int cp_csc;
 
-	const struct adv7604_format_info *formats;
+	const struct adv76xx_format_info *formats;
 	unsigned int nformats;
 
 	void (*set_termination)(struct v4l2_subdev *sd, bool enable);
@@ -119,7 +120,7 @@
 	unsigned int (*read_cable_det)(struct v4l2_subdev *sd);
 
 	/* 0 = AFE, 1 = HDMI */
-	const struct adv7604_reg_seq *recommended_settings[2];
+	const struct adv76xx_reg_seq *recommended_settings[2];
 	unsigned int num_recommended_settings[2];
 
 	unsigned long page_mask;
@@ -133,22 +134,22 @@
  **********************************************************************
  */
 
-struct adv7604_state {
-	const struct adv7604_chip_info *info;
-	struct adv7604_platform_data pdata;
+struct adv76xx_state {
+	const struct adv76xx_chip_info *info;
+	struct adv76xx_platform_data pdata;
 
 	struct gpio_desc *hpd_gpio[4];
 
 	struct v4l2_subdev sd;
-	struct media_pad pads[ADV7604_PAD_MAX];
+	struct media_pad pads[ADV76XX_PAD_MAX];
 	unsigned int source_pad;
 
 	struct v4l2_ctrl_handler hdl;
 
-	enum adv7604_pad selected_input;
+	enum adv76xx_pad selected_input;
 
 	struct v4l2_dv_timings timings;
-	const struct adv7604_format_info *format;
+	const struct adv76xx_format_info *format;
 
 	struct {
 		u8 edid[256];
@@ -163,7 +164,7 @@
 	bool restart_stdi_once;
 
 	/* i2c clients */
-	struct i2c_client *i2c_clients[ADV7604_PAGE_MAX];
+	struct i2c_client *i2c_clients[ADV76XX_PAGE_MAX];
 
 	/* controls */
 	struct v4l2_ctrl *detect_tx_5v_ctrl;
@@ -173,13 +174,13 @@
 	struct v4l2_ctrl *rgb_quantization_range_ctrl;
 };
 
-static bool adv7604_has_afe(struct adv7604_state *state)
+static bool adv76xx_has_afe(struct adv76xx_state *state)
 {
 	return state->info->has_afe;
 }
 
 /* Supported CEA and DMT timings */
-static const struct v4l2_dv_timings adv7604_timings[] = {
+static const struct v4l2_dv_timings adv76xx_timings[] = {
 	V4L2_DV_BT_CEA_720X480P59_94,
 	V4L2_DV_BT_CEA_720X576P50,
 	V4L2_DV_BT_CEA_1280X720P24,
@@ -243,14 +244,14 @@
 	{ },
 };
 
-struct adv7604_video_standards {
+struct adv76xx_video_standards {
 	struct v4l2_dv_timings timings;
 	u8 vid_std;
 	u8 v_freq;
 };
 
 /* sorted by number of lines */
-static const struct adv7604_video_standards adv7604_prim_mode_comp[] = {
+static const struct adv76xx_video_standards adv7604_prim_mode_comp[] = {
 	/* { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, TODO flickering */
 	{ V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 },
 	{ V4L2_DV_BT_CEA_1280X720P50, 0x19, 0x01 },
@@ -265,7 +266,7 @@
 };
 
 /* sorted by number of lines */
-static const struct adv7604_video_standards adv7604_prim_mode_gr[] = {
+static const struct adv76xx_video_standards adv7604_prim_mode_gr[] = {
 	{ V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 },
 	{ V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 },
 	{ V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 },
@@ -293,7 +294,7 @@
 };
 
 /* sorted by number of lines */
-static const struct adv7604_video_standards adv7604_prim_mode_hdmi_comp[] = {
+static const struct adv76xx_video_standards adv76xx_prim_mode_hdmi_comp[] = {
 	{ V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 },
 	{ V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 },
 	{ V4L2_DV_BT_CEA_1280X720P50, 0x13, 0x01 },
@@ -307,7 +308,7 @@
 };
 
 /* sorted by number of lines */
-static const struct adv7604_video_standards adv7604_prim_mode_hdmi_gr[] = {
+static const struct adv76xx_video_standards adv76xx_prim_mode_hdmi_gr[] = {
 	{ V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 },
 	{ V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 },
 	{ V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 },
@@ -328,14 +329,9 @@
 
 /* ----------------------------------------------------------------------- */
 
-static inline struct adv7604_state *to_state(struct v4l2_subdev *sd)
+static inline struct adv76xx_state *to_state(struct v4l2_subdev *sd)
 {
-	return container_of(sd, struct adv7604_state, sd);
-}
-
-static inline unsigned hblanking(const struct v4l2_bt_timings *t)
-{
-	return V4L2_DV_BT_BLANKING_WIDTH(t);
+	return container_of(sd, struct adv76xx_state, sd);
 }
 
 static inline unsigned htotal(const struct v4l2_bt_timings *t)
@@ -343,11 +339,6 @@
 	return V4L2_DV_BT_FRAME_WIDTH(t);
 }
 
-static inline unsigned vblanking(const struct v4l2_bt_timings *t)
-{
-	return V4L2_DV_BT_BLANKING_HEIGHT(t);
-}
-
 static inline unsigned vtotal(const struct v4l2_bt_timings *t)
 {
 	return V4L2_DV_BT_FRAME_HEIGHT(t);
@@ -370,15 +361,15 @@
 	return -EIO;
 }
 
-static s32 adv_smbus_read_byte_data(struct adv7604_state *state,
-				    enum adv7604_page page, u8 command)
+static s32 adv_smbus_read_byte_data(struct adv76xx_state *state,
+				    enum adv76xx_page page, u8 command)
 {
 	return adv_smbus_read_byte_data_check(state->i2c_clients[page],
 					      command, true);
 }
 
-static s32 adv_smbus_write_byte_data(struct adv7604_state *state,
-				     enum adv7604_page page, u8 command,
+static s32 adv_smbus_write_byte_data(struct adv76xx_state *state,
+				     enum adv76xx_page page, u8 command,
 				     u8 value)
 {
 	struct i2c_client *client = state->i2c_clients[page];
@@ -401,8 +392,8 @@
 	return err;
 }
 
-static s32 adv_smbus_write_i2c_block_data(struct adv7604_state *state,
-					  enum adv7604_page page, u8 command,
+static s32 adv_smbus_write_i2c_block_data(struct adv76xx_state *state,
+					  enum adv76xx_page page, u8 command,
 					  unsigned length, const u8 *values)
 {
 	struct i2c_client *client = state->i2c_clients[page];
@@ -421,16 +412,16 @@
 
 static inline int io_read(struct v4l2_subdev *sd, u8 reg)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_read_byte_data(state, ADV7604_PAGE_IO, reg);
+	return adv_smbus_read_byte_data(state, ADV76XX_PAGE_IO, reg);
 }
 
 static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_write_byte_data(state, ADV7604_PAGE_IO, reg, val);
+	return adv_smbus_write_byte_data(state, ADV76XX_PAGE_IO, reg, val);
 }
 
 static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
@@ -440,106 +431,73 @@
 
 static inline int avlink_read(struct v4l2_subdev *sd, u8 reg)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	return adv_smbus_read_byte_data(state, ADV7604_PAGE_AVLINK, reg);
 }
 
 static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	return adv_smbus_write_byte_data(state, ADV7604_PAGE_AVLINK, reg, val);
 }
 
 static inline int cec_read(struct v4l2_subdev *sd, u8 reg)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_read_byte_data(state, ADV7604_PAGE_CEC, reg);
+	return adv_smbus_read_byte_data(state, ADV76XX_PAGE_CEC, reg);
 }
 
 static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_write_byte_data(state, ADV7604_PAGE_CEC, reg, val);
-}
-
-static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
-{
-	return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val);
+	return adv_smbus_write_byte_data(state, ADV76XX_PAGE_CEC, reg, val);
 }
 
 static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_read_byte_data(state, ADV7604_PAGE_INFOFRAME, reg);
+	return adv_smbus_read_byte_data(state, ADV76XX_PAGE_INFOFRAME, reg);
 }
 
 static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_write_byte_data(state, ADV7604_PAGE_INFOFRAME,
+	return adv_smbus_write_byte_data(state, ADV76XX_PAGE_INFOFRAME,
 					 reg, val);
 }
 
-static inline int esdp_read(struct v4l2_subdev *sd, u8 reg)
-{
-	struct adv7604_state *state = to_state(sd);
-
-	return adv_smbus_read_byte_data(state, ADV7604_PAGE_ESDP, reg);
-}
-
-static inline int esdp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
-{
-	struct adv7604_state *state = to_state(sd);
-
-	return adv_smbus_write_byte_data(state, ADV7604_PAGE_ESDP, reg, val);
-}
-
-static inline int dpp_read(struct v4l2_subdev *sd, u8 reg)
-{
-	struct adv7604_state *state = to_state(sd);
-
-	return adv_smbus_read_byte_data(state, ADV7604_PAGE_DPP, reg);
-}
-
-static inline int dpp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
-{
-	struct adv7604_state *state = to_state(sd);
-
-	return adv_smbus_write_byte_data(state, ADV7604_PAGE_DPP, reg, val);
-}
-
 static inline int afe_read(struct v4l2_subdev *sd, u8 reg)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_read_byte_data(state, ADV7604_PAGE_AFE, reg);
+	return adv_smbus_read_byte_data(state, ADV76XX_PAGE_AFE, reg);
 }
 
 static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_write_byte_data(state, ADV7604_PAGE_AFE, reg, val);
+	return adv_smbus_write_byte_data(state, ADV76XX_PAGE_AFE, reg, val);
 }
 
 static inline int rep_read(struct v4l2_subdev *sd, u8 reg)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_read_byte_data(state, ADV7604_PAGE_REP, reg);
+	return adv_smbus_read_byte_data(state, ADV76XX_PAGE_REP, reg);
 }
 
 static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_write_byte_data(state, ADV7604_PAGE_REP, reg, val);
+	return adv_smbus_write_byte_data(state, ADV76XX_PAGE_REP, reg, val);
 }
 
 static inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
@@ -549,90 +507,60 @@
 
 static inline int edid_read(struct v4l2_subdev *sd, u8 reg)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_read_byte_data(state, ADV7604_PAGE_EDID, reg);
+	return adv_smbus_read_byte_data(state, ADV76XX_PAGE_EDID, reg);
 }
 
 static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_write_byte_data(state, ADV7604_PAGE_EDID, reg, val);
-}
-
-static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val)
-{
-	struct adv7604_state *state = to_state(sd);
-	struct i2c_client *client = state->i2c_clients[ADV7604_PAGE_EDID];
-	u8 msgbuf0[1] = { 0 };
-	u8 msgbuf1[256];
-	struct i2c_msg msg[2] = {
-		{
-			.addr = client->addr,
-			.len = 1,
-			.buf = msgbuf0
-		},
-		{
-			.addr = client->addr,
-			.flags = I2C_M_RD,
-			.len = len,
-			.buf = msgbuf1
-		},
-	};
-
-	if (i2c_transfer(client->adapter, msg, 2) < 0)
-		return -EIO;
-	memcpy(val, msgbuf1, len);
-	return 0;
+	return adv_smbus_write_byte_data(state, ADV76XX_PAGE_EDID, reg, val);
 }
 
 static inline int edid_write_block(struct v4l2_subdev *sd,
 					unsigned len, const u8 *val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 	int err = 0;
 	int i;
 
 	v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len);
 
 	for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX)
-		err = adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_EDID,
+		err = adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_EDID,
 				i, I2C_SMBUS_BLOCK_MAX, val + i);
 	return err;
 }
 
-static void adv7604_set_hpd(struct adv7604_state *state, unsigned int hpd)
+static void adv76xx_set_hpd(struct adv76xx_state *state, unsigned int hpd)
 {
 	unsigned int i;
 
-	for (i = 0; i < state->info->num_dv_ports; ++i) {
-		if (IS_ERR(state->hpd_gpio[i]))
-			continue;
-
+	for (i = 0; i < state->info->num_dv_ports; ++i)
 		gpiod_set_value_cansleep(state->hpd_gpio[i], hpd & BIT(i));
-	}
 
-	v4l2_subdev_notify(&state->sd, ADV7604_HOTPLUG, &hpd);
+	v4l2_subdev_notify(&state->sd, ADV76XX_HOTPLUG, &hpd);
 }
 
-static void adv7604_delayed_work_enable_hotplug(struct work_struct *work)
+static void adv76xx_delayed_work_enable_hotplug(struct work_struct *work)
 {
 	struct delayed_work *dwork = to_delayed_work(work);
-	struct adv7604_state *state = container_of(dwork, struct adv7604_state,
+	struct adv76xx_state *state = container_of(dwork, struct adv76xx_state,
 						delayed_work_enable_hotplug);
 	struct v4l2_subdev *sd = &state->sd;
 
 	v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__);
 
-	adv7604_set_hpd(state, state->edid.present);
+	adv76xx_set_hpd(state, state->edid.present);
 }
 
 static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_read_byte_data(state, ADV7604_PAGE_HDMI, reg);
+	return adv_smbus_read_byte_data(state, ADV76XX_PAGE_HDMI, reg);
 }
 
 static u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask)
@@ -642,9 +570,9 @@
 
 static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_write_byte_data(state, ADV7604_PAGE_HDMI, reg, val);
+	return adv_smbus_write_byte_data(state, ADV76XX_PAGE_HDMI, reg, val);
 }
 
 static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
@@ -652,25 +580,18 @@
 	return hdmi_write(sd, reg, (hdmi_read(sd, reg) & ~mask) | val);
 }
 
-static inline int test_read(struct v4l2_subdev *sd, u8 reg)
-{
-	struct adv7604_state *state = to_state(sd);
-
-	return adv_smbus_read_byte_data(state, ADV7604_PAGE_TEST, reg);
-}
-
 static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_write_byte_data(state, ADV7604_PAGE_TEST, reg, val);
+	return adv_smbus_write_byte_data(state, ADV76XX_PAGE_TEST, reg, val);
 }
 
 static inline int cp_read(struct v4l2_subdev *sd, u8 reg)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_read_byte_data(state, ADV7604_PAGE_CP, reg);
+	return adv_smbus_read_byte_data(state, ADV76XX_PAGE_CP, reg);
 }
 
 static u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask)
@@ -680,9 +601,9 @@
 
 static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return adv_smbus_write_byte_data(state, ADV7604_PAGE_CP, reg, val);
+	return adv_smbus_write_byte_data(state, ADV76XX_PAGE_CP, reg, val);
 }
 
 static inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
@@ -692,25 +613,25 @@
 
 static inline int vdp_read(struct v4l2_subdev *sd, u8 reg)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	return adv_smbus_read_byte_data(state, ADV7604_PAGE_VDP, reg);
 }
 
 static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	return adv_smbus_write_byte_data(state, ADV7604_PAGE_VDP, reg, val);
 }
 
-#define ADV7604_REG(page, offset)	(((page) << 8) | (offset))
-#define ADV7604_REG_SEQ_TERM		0xffff
+#define ADV76XX_REG(page, offset)	(((page) << 8) | (offset))
+#define ADV76XX_REG_SEQ_TERM		0xffff
 
 #ifdef CPTCFG_VIDEO_ADV_DEBUG
-static int adv7604_read_reg(struct v4l2_subdev *sd, unsigned int reg)
+static int adv76xx_read_reg(struct v4l2_subdev *sd, unsigned int reg)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 	unsigned int page = reg >> 8;
 
 	if (!(BIT(page) & state->info->page_mask))
@@ -722,9 +643,9 @@
 }
 #endif
 
-static int adv7604_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val)
+static int adv76xx_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 	unsigned int page = reg >> 8;
 
 	if (!(BIT(page) & state->info->page_mask))
@@ -735,91 +656,91 @@
 	return adv_smbus_write_byte_data(state, page, reg, val);
 }
 
-static void adv7604_write_reg_seq(struct v4l2_subdev *sd,
-				  const struct adv7604_reg_seq *reg_seq)
+static void adv76xx_write_reg_seq(struct v4l2_subdev *sd,
+				  const struct adv76xx_reg_seq *reg_seq)
 {
 	unsigned int i;
 
-	for (i = 0; reg_seq[i].reg != ADV7604_REG_SEQ_TERM; i++)
-		adv7604_write_reg(sd, reg_seq[i].reg, reg_seq[i].val);
+	for (i = 0; reg_seq[i].reg != ADV76XX_REG_SEQ_TERM; i++)
+		adv76xx_write_reg(sd, reg_seq[i].reg, reg_seq[i].val);
 }
 
 /* -----------------------------------------------------------------------------
  * Format helpers
  */
 
-static const struct adv7604_format_info adv7604_formats[] = {
-	{ MEDIA_BUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false,
-	  ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_YUYV10_2X10, ADV7604_OP_CH_SEL_RGB, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT },
-	{ MEDIA_BUS_FMT_YVYU10_2X10, ADV7604_OP_CH_SEL_RGB, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT },
-	{ MEDIA_BUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
-	{ MEDIA_BUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
-	{ MEDIA_BUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_UYVY10_1X20, ADV7604_OP_CH_SEL_RBG, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
-	{ MEDIA_BUS_FMT_VYUY10_1X20, ADV7604_OP_CH_SEL_RBG, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
-	{ MEDIA_BUS_FMT_YUYV10_1X20, ADV7604_OP_CH_SEL_RGB, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
-	{ MEDIA_BUS_FMT_YVYU10_1X20, ADV7604_OP_CH_SEL_RGB, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
-	{ MEDIA_BUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
-	{ MEDIA_BUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
-	{ MEDIA_BUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
-	{ MEDIA_BUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
+static const struct adv76xx_format_info adv7604_formats[] = {
+	{ MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false,
+	  ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_YUYV10_2X10, ADV76XX_OP_CH_SEL_RGB, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT },
+	{ MEDIA_BUS_FMT_YVYU10_2X10, ADV76XX_OP_CH_SEL_RGB, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT },
+	{ MEDIA_BUS_FMT_YUYV12_2X12, ADV76XX_OP_CH_SEL_RGB, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT },
+	{ MEDIA_BUS_FMT_YVYU12_2X12, ADV76XX_OP_CH_SEL_RGB, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT },
+	{ MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_UYVY10_1X20, ADV76XX_OP_CH_SEL_RBG, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
+	{ MEDIA_BUS_FMT_VYUY10_1X20, ADV76XX_OP_CH_SEL_RBG, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
+	{ MEDIA_BUS_FMT_YUYV10_1X20, ADV76XX_OP_CH_SEL_RGB, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
+	{ MEDIA_BUS_FMT_YVYU10_1X20, ADV76XX_OP_CH_SEL_RGB, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
+	{ MEDIA_BUS_FMT_UYVY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
+	{ MEDIA_BUS_FMT_VYUY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
+	{ MEDIA_BUS_FMT_YUYV12_1X24, ADV76XX_OP_CH_SEL_RGB, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
+	{ MEDIA_BUS_FMT_YVYU12_1X24, ADV76XX_OP_CH_SEL_RGB, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
 };
 
-static const struct adv7604_format_info adv7611_formats[] = {
-	{ MEDIA_BUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false,
-	  ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
-	{ MEDIA_BUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
-	{ MEDIA_BUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
-	{ MEDIA_BUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
-	{ MEDIA_BUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
-	{ MEDIA_BUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
-	{ MEDIA_BUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true,
-	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
+static const struct adv76xx_format_info adv7611_formats[] = {
+	{ MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false,
+	  ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_YUYV12_2X12, ADV76XX_OP_CH_SEL_RGB, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT },
+	{ MEDIA_BUS_FMT_YVYU12_2X12, ADV76XX_OP_CH_SEL_RGB, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT },
+	{ MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT },
+	{ MEDIA_BUS_FMT_UYVY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
+	{ MEDIA_BUS_FMT_VYUY12_1X24, ADV76XX_OP_CH_SEL_RBG, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
+	{ MEDIA_BUS_FMT_YUYV12_1X24, ADV76XX_OP_CH_SEL_RGB, false, false,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
+	{ MEDIA_BUS_FMT_YVYU12_1X24, ADV76XX_OP_CH_SEL_RGB, false, true,
+	  ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT },
 };
 
-static const struct adv7604_format_info *
-adv7604_format_info(struct adv7604_state *state, u32 code)
+static const struct adv76xx_format_info *
+adv76xx_format_info(struct adv76xx_state *state, u32 code)
 {
 	unsigned int i;
 
@@ -835,7 +756,7 @@
 
 static inline bool is_analog_input(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	return state->selected_input == ADV7604_PAD_VGA_RGB ||
 	       state->selected_input == ADV7604_PAD_VGA_COMP;
@@ -843,9 +764,9 @@
 
 static inline bool is_digital_input(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	return state->selected_input == ADV7604_PAD_HDMI_PORT_A ||
+	return state->selected_input == ADV76XX_PAD_HDMI_PORT_A ||
 	       state->selected_input == ADV7604_PAD_HDMI_PORT_B ||
 	       state->selected_input == ADV7604_PAD_HDMI_PORT_C ||
 	       state->selected_input == ADV7604_PAD_HDMI_PORT_D;
@@ -854,7 +775,7 @@
 /* ----------------------------------------------------------------------- */
 
 #ifdef CPTCFG_VIDEO_ADV_DEBUG
-static void adv7604_inv_register(struct v4l2_subdev *sd)
+static void adv76xx_inv_register(struct v4l2_subdev *sd)
 {
 	v4l2_info(sd, "0x000-0x0ff: IO Map\n");
 	v4l2_info(sd, "0x100-0x1ff: AVLink Map\n");
@@ -871,15 +792,15 @@
 	v4l2_info(sd, "0xc00-0xcff: VDP Map\n");
 }
 
-static int adv7604_g_register(struct v4l2_subdev *sd,
+static int adv76xx_g_register(struct v4l2_subdev *sd,
 					struct v4l2_dbg_register *reg)
 {
 	int ret;
 
-	ret = adv7604_read_reg(sd, reg->reg);
+	ret = adv76xx_read_reg(sd, reg->reg);
 	if (ret < 0) {
 		v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
-		adv7604_inv_register(sd);
+		adv76xx_inv_register(sd);
 		return ret;
 	}
 
@@ -889,15 +810,15 @@
 	return 0;
 }
 
-static int adv7604_s_register(struct v4l2_subdev *sd,
+static int adv76xx_s_register(struct v4l2_subdev *sd,
 					const struct v4l2_dbg_register *reg)
 {
 	int ret;
 
-	ret = adv7604_write_reg(sd, reg->reg, reg->val);
+	ret = adv76xx_write_reg(sd, reg->reg, reg->val);
 	if (ret < 0) {
 		v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
-		adv7604_inv_register(sd);
+		adv76xx_inv_register(sd);
 		return ret;
 	}
 
@@ -922,10 +843,10 @@
 	return value & 1;
 }
 
-static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
+static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
-	const struct adv7604_chip_info *info = state->info;
+	struct adv76xx_state *state = to_state(sd);
+	const struct adv76xx_chip_info *info = state->info;
 
 	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
 				info->read_cable_det(sd));
@@ -933,7 +854,7 @@
 
 static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
 		u8 prim_mode,
-		const struct adv7604_video_standards *predef_vid_timings,
+		const struct adv76xx_video_standards *predef_vid_timings,
 		const struct v4l2_dv_timings *timings)
 {
 	int i;
@@ -954,12 +875,12 @@
 static int configure_predefined_video_timings(struct v4l2_subdev *sd,
 		struct v4l2_dv_timings *timings)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 	int err;
 
 	v4l2_dbg(1, debug, sd, "%s", __func__);
 
-	if (adv7604_has_afe(state)) {
+	if (adv76xx_has_afe(state)) {
 		/* reset to default values */
 		io_write(sd, 0x16, 0x43);
 		io_write(sd, 0x17, 0x5a);
@@ -985,10 +906,10 @@
 					0x02, adv7604_prim_mode_gr, timings);
 	} else if (is_digital_input(sd)) {
 		err = find_and_set_predefined_video_timings(sd,
-				0x05, adv7604_prim_mode_hdmi_comp, timings);
+				0x05, adv76xx_prim_mode_hdmi_comp, timings);
 		if (err)
 			err = find_and_set_predefined_video_timings(sd,
-					0x06, adv7604_prim_mode_hdmi_gr, timings);
+					0x06, adv76xx_prim_mode_hdmi_gr, timings);
 	} else {
 		v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n",
 				__func__, state->selected_input);
@@ -1002,7 +923,7 @@
 static void configure_custom_video_timings(struct v4l2_subdev *sd,
 		const struct v4l2_bt_timings *bt)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 	u32 width = htotal(bt);
 	u32 height = vtotal(bt);
 	u16 cp_start_sav = bt->hsync + bt->hbackporch - 4;
@@ -1010,7 +931,7 @@
 	u16 cp_start_vbi = height - bt->vfrontporch;
 	u16 cp_end_vbi = bt->vsync + bt->vbackporch;
 	u16 ch1_fr_ll = (((u32)bt->pixelclock / 100) > 0) ?
-		((width * (ADV7604_fsc / 100)) / ((u32)bt->pixelclock / 100)) : 0;
+		((width * (ADV76XX_FSC / 100)) / ((u32)bt->pixelclock / 100)) : 0;
 	const u8 pll[2] = {
 		0xc0 | ((width >> 8) & 0x1f),
 		width & 0xff
@@ -1028,7 +949,7 @@
 		/* Should only be set in auto-graphics mode [REF_02, p. 91-92] */
 		/* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */
 		/* IO-map reg. 0x16 and 0x17 should be written in sequence */
-		if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_IO,
+		if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_IO,
 						   0x16, 2, pll))
 			v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n");
 
@@ -1059,9 +980,9 @@
 	cp_write(sd, 0xac, (height & 0x0f) << 4);
 }
 
-static void adv7604_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c)
+static void adv76xx_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 	u8 offset_buf[4];
 
 	if (auto_offset) {
@@ -1080,14 +1001,14 @@
 	offset_buf[3] = offset_c & 0x0ff;
 
 	/* Registers must be written in this order with no i2c access in between */
-	if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP,
+	if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_CP,
 					   0x77, 4, offset_buf))
 		v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__);
 }
 
-static void adv7604_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c)
+static void adv76xx_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 	u8 gain_buf[4];
 	u8 gain_man = 1;
 	u8 agc_mode_man = 1;
@@ -1110,14 +1031,14 @@
 	gain_buf[3] = ((gain_c & 0x0ff));
 
 	/* Registers must be written in this order with no i2c access in between */
-	if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP,
+	if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_CP,
 					   0x73, 4, gain_buf))
 		v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__);
 }
 
 static void set_rgb_quantization_range(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 	bool rgb_output = io_read(sd, 0x02) & 0x02;
 	bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80;
 
@@ -1125,8 +1046,8 @@
 			__func__, state->rgb_quantization_range,
 			rgb_output, hdmi_signal);
 
-	adv7604_set_gain(sd, true, 0x0, 0x0, 0x0);
-	adv7604_set_offset(sd, true, 0x0, 0x0, 0x0);
+	adv76xx_set_gain(sd, true, 0x0, 0x0, 0x0);
+	adv76xx_set_offset(sd, true, 0x0, 0x0, 0x0);
 
 	switch (state->rgb_quantization_range) {
 	case V4L2_DV_RGB_RANGE_AUTO:
@@ -1154,7 +1075,7 @@
 		/* Receiving DVI-D signal
 		 * ADV7604 selects RGB limited range regardless of
 		 * input format (CE/IT) in automatic mode */
-		if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
+		if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
 			/* RGB limited range (16-235) */
 			io_write_clr_set(sd, 0x02, 0xf0, 0x00);
 		} else {
@@ -1162,10 +1083,10 @@
 			io_write_clr_set(sd, 0x02, 0xf0, 0x10);
 
 			if (is_digital_input(sd) && rgb_output) {
-				adv7604_set_offset(sd, false, 0x40, 0x40, 0x40);
+				adv76xx_set_offset(sd, false, 0x40, 0x40, 0x40);
 			} else {
-				adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
-				adv7604_set_offset(sd, false, 0x70, 0x70, 0x70);
+				adv76xx_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
+				adv76xx_set_offset(sd, false, 0x70, 0x70, 0x70);
 			}
 		}
 		break;
@@ -1195,21 +1116,21 @@
 
 		/* Adjust gain/offset for DVI-D signals only */
 		if (rgb_output) {
-			adv7604_set_offset(sd, false, 0x40, 0x40, 0x40);
+			adv76xx_set_offset(sd, false, 0x40, 0x40, 0x40);
 		} else {
-			adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
-			adv7604_set_offset(sd, false, 0x70, 0x70, 0x70);
+			adv76xx_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
+			adv76xx_set_offset(sd, false, 0x70, 0x70, 0x70);
 		}
 		break;
 	}
 }
 
-static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl)
+static int adv76xx_s_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct v4l2_subdev *sd =
-		&container_of(ctrl->handler, struct adv7604_state, hdl)->sd;
+		&container_of(ctrl->handler, struct adv76xx_state, hdl)->sd;
 
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	switch (ctrl->id) {
 	case V4L2_CID_BRIGHTNESS:
@@ -1229,7 +1150,7 @@
 		set_rgb_quantization_range(sd);
 		return 0;
 	case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE:
-		if (!adv7604_has_afe(state))
+		if (!adv76xx_has_afe(state))
 			return -EINVAL;
 		/* Set the analog sampling phase. This is needed to find the
 		   best sampling phase for analog video: an application or
@@ -1261,15 +1182,15 @@
 
 static inline bool no_signal_tmds(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	return !(io_read(sd, 0x6a) & (0x10 >> state->selected_input));
 }
 
 static inline bool no_lock_tmds(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
-	const struct adv7604_chip_info *info = state->info;
+	struct adv76xx_state *state = to_state(sd);
+	const struct adv76xx_chip_info *info = state->info;
 
 	return (io_read(sd, 0x6a) & info->tdms_lock_mask) != info->tdms_lock_mask;
 }
@@ -1281,13 +1202,13 @@
 
 static inline bool no_lock_sspd(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	/*
 	 * Chips without a AFE don't expose registers for the SSPD, so just assume
 	 * that we have a lock.
 	 */
-	if (adv7604_has_afe(state))
+	if (adv76xx_has_afe(state))
 		return false;
 
 	/* TODO channel 2 */
@@ -1319,9 +1240,9 @@
 
 static inline bool no_lock_cp(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	if (!adv7604_has_afe(state))
+	if (!adv76xx_has_afe(state))
 		return false;
 
 	/* CP has detected a non standard number of lines on the incoming
@@ -1329,13 +1250,19 @@
 	return io_read(sd, 0x12) & 0x01;
 }
 
-static int adv7604_g_input_status(struct v4l2_subdev *sd, u32 *status)
+static inline bool in_free_run(struct v4l2_subdev *sd)
+{
+	return cp_read(sd, 0xff) & 0x10;
+}
+
+static int adv76xx_g_input_status(struct v4l2_subdev *sd, u32 *status)
 {
 	*status = 0;
 	*status |= no_power(sd) ? V4L2_IN_ST_NO_POWER : 0;
 	*status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0;
-	if (no_lock_cp(sd))
-		*status |= is_digital_input(sd) ? V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK;
+	if (!in_free_run(sd) && no_lock_cp(sd))
+		*status |= is_digital_input(sd) ?
+			   V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK;
 
 	v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status);
 
@@ -1354,22 +1281,22 @@
 		struct stdi_readback *stdi,
 		struct v4l2_dv_timings *timings)
 {
-	struct adv7604_state *state = to_state(sd);
-	u32 hfreq = (ADV7604_fsc * 8) / stdi->bl;
+	struct adv76xx_state *state = to_state(sd);
+	u32 hfreq = (ADV76XX_FSC * 8) / stdi->bl;
 	u32 pix_clk;
 	int i;
 
-	for (i = 0; adv7604_timings[i].bt.height; i++) {
-		if (vtotal(&adv7604_timings[i].bt) != stdi->lcf + 1)
+	for (i = 0; adv76xx_timings[i].bt.height; i++) {
+		if (vtotal(&adv76xx_timings[i].bt) != stdi->lcf + 1)
 			continue;
-		if (adv7604_timings[i].bt.vsync != stdi->lcvs)
+		if (adv76xx_timings[i].bt.vsync != stdi->lcvs)
 			continue;
 
-		pix_clk = hfreq * htotal(&adv7604_timings[i].bt);
+		pix_clk = hfreq * htotal(&adv76xx_timings[i].bt);
 
-		if ((pix_clk < adv7604_timings[i].bt.pixelclock + 1000000) &&
-		    (pix_clk > adv7604_timings[i].bt.pixelclock - 1000000)) {
-			*timings = adv7604_timings[i];
+		if ((pix_clk < adv76xx_timings[i].bt.pixelclock + 1000000) &&
+		    (pix_clk > adv76xx_timings[i].bt.pixelclock - 1000000)) {
+			*timings = adv76xx_timings[i];
 			return 0;
 		}
 	}
@@ -1395,8 +1322,8 @@
 
 static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi)
 {
-	struct adv7604_state *state = to_state(sd);
-	const struct adv7604_chip_info *info = state->info;
+	struct adv76xx_state *state = to_state(sd);
+	const struct adv76xx_chip_info *info = state->info;
 	u8 polarity;
 
 	if (no_lock_stdi(sd) || no_lock_sspd(sd)) {
@@ -1410,7 +1337,7 @@
 	stdi->lcvs = cp_read(sd, 0xb3) >> 3;
 	stdi->interlaced = io_read(sd, 0x12) & 0x10;
 
-	if (adv7604_has_afe(state)) {
+	if (adv76xx_has_afe(state)) {
 		/* read SSPD */
 		polarity = cp_read(sd, 0xb5);
 		if ((polarity & 0x03) == 0x01) {
@@ -1449,26 +1376,26 @@
 	return 0;
 }
 
-static int adv7604_enum_dv_timings(struct v4l2_subdev *sd,
+static int adv76xx_enum_dv_timings(struct v4l2_subdev *sd,
 			struct v4l2_enum_dv_timings *timings)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
-	if (timings->index >= ARRAY_SIZE(adv7604_timings) - 1)
+	if (timings->index >= ARRAY_SIZE(adv76xx_timings) - 1)
 		return -EINVAL;
 
 	if (timings->pad >= state->source_pad)
 		return -EINVAL;
 
 	memset(timings->reserved, 0, sizeof(timings->reserved));
-	timings->timings = adv7604_timings[timings->index];
+	timings->timings = adv76xx_timings[timings->index];
 	return 0;
 }
 
-static int adv7604_dv_timings_cap(struct v4l2_subdev *sd,
+static int adv76xx_dv_timings_cap(struct v4l2_subdev *sd,
 			struct v4l2_dv_timings_cap *cap)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	if (cap->pad >= state->source_pad)
 		return -EINVAL;
@@ -1479,7 +1406,7 @@
 	cap->bt.min_pixelclock = 25000000;
 
 	switch (cap->pad) {
-	case ADV7604_PAD_HDMI_PORT_A:
+	case ADV76XX_PAD_HDMI_PORT_A:
 	case ADV7604_PAD_HDMI_PORT_B:
 	case ADV7604_PAD_HDMI_PORT_C:
 	case ADV7604_PAD_HDMI_PORT_D:
@@ -1500,16 +1427,16 @@
 }
 
 /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings
-   if the format is listed in adv7604_timings[] */
-static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
+   if the format is listed in adv76xx_timings[] */
+static void adv76xx_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
 		struct v4l2_dv_timings *timings)
 {
 	int i;
 
-	for (i = 0; adv7604_timings[i].bt.width; i++) {
-		if (v4l2_match_dv_timings(timings, &adv7604_timings[i],
+	for (i = 0; adv76xx_timings[i].bt.width; i++) {
+		if (v4l2_match_dv_timings(timings, &adv76xx_timings[i],
 					is_digital_input(sd) ? 250000 : 1000000)) {
-			*timings = adv7604_timings[i];
+			*timings = adv76xx_timings[i];
 			break;
 		}
 	}
@@ -1547,11 +1474,11 @@
 	return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128;
 }
 
-static int adv7604_query_dv_timings(struct v4l2_subdev *sd,
+static int adv76xx_query_dv_timings(struct v4l2_subdev *sd,
 			struct v4l2_dv_timings *timings)
 {
-	struct adv7604_state *state = to_state(sd);
-	const struct adv7604_chip_info *info = state->info;
+	struct adv76xx_state *state = to_state(sd);
+	const struct adv76xx_chip_info *info = state->info;
 	struct v4l2_bt_timings *bt = &timings->bt;
 	struct stdi_readback stdi;
 
@@ -1595,7 +1522,7 @@
 			bt->il_vsync = hdmi_read16(sd, 0x30, 0x1fff) / 2;
 			bt->il_vbackporch = hdmi_read16(sd, 0x34, 0x1fff) / 2;
 		}
-		adv7604_fill_optional_dv_timings_fields(sd, timings);
+		adv76xx_fill_optional_dv_timings_fields(sd, timings);
 	} else {
 		/* find format
 		 * Since LCVS values are inaccurate [REF_03, p. 275-276],
@@ -1652,16 +1579,16 @@
 	}
 
 	if (debug > 1)
-		v4l2_print_dv_timings(sd->name, "adv7604_query_dv_timings: ",
+		v4l2_print_dv_timings(sd->name, "adv76xx_query_dv_timings: ",
 				      timings, true);
 
 	return 0;
 }
 
-static int adv7604_s_dv_timings(struct v4l2_subdev *sd,
+static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
 		struct v4l2_dv_timings *timings)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 	struct v4l2_bt_timings *bt;
 	int err;
 
@@ -1682,7 +1609,7 @@
 		return -ERANGE;
 	}
 
-	adv7604_fill_optional_dv_timings_fields(sd, timings);
+	adv76xx_fill_optional_dv_timings_fields(sd, timings);
 
 	state->timings = *timings;
 
@@ -1699,15 +1626,15 @@
 	set_rgb_quantization_range(sd);
 
 	if (debug > 1)
-		v4l2_print_dv_timings(sd->name, "adv7604_s_dv_timings: ",
+		v4l2_print_dv_timings(sd->name, "adv76xx_s_dv_timings: ",
 				      timings, true);
 	return 0;
 }
 
-static int adv7604_g_dv_timings(struct v4l2_subdev *sd,
+static int adv76xx_g_dv_timings(struct v4l2_subdev *sd,
 		struct v4l2_dv_timings *timings)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	*timings = state->timings;
 	return 0;
@@ -1725,7 +1652,7 @@
 
 static void enable_input(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	if (is_analog_input(sd)) {
 		io_write(sd, 0x15, 0xb0);   /* Disable Tristate of Pins (no audio) */
@@ -1742,7 +1669,7 @@
 
 static void disable_input(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	hdmi_write_clr_set(sd, 0x1a, 0x10, 0x10); /* Mute audio */
 	msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 7.16.10] */
@@ -1752,11 +1679,11 @@
 
 static void select_input(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
-	const struct adv7604_chip_info *info = state->info;
+	struct adv76xx_state *state = to_state(sd);
+	const struct adv76xx_chip_info *info = state->info;
 
 	if (is_analog_input(sd)) {
-		adv7604_write_reg_seq(sd, info->recommended_settings[0]);
+		adv76xx_write_reg_seq(sd, info->recommended_settings[0]);
 
 		afe_write(sd, 0x00, 0x08); /* power up ADC */
 		afe_write(sd, 0x01, 0x06); /* power up Analog Front End */
@@ -1764,9 +1691,9 @@
 	} else if (is_digital_input(sd)) {
 		hdmi_write(sd, 0x00, state->selected_input & 0x03);
 
-		adv7604_write_reg_seq(sd, info->recommended_settings[1]);
+		adv76xx_write_reg_seq(sd, info->recommended_settings[1]);
 
-		if (adv7604_has_afe(state)) {
+		if (adv76xx_has_afe(state)) {
 			afe_write(sd, 0x00, 0xff); /* power down ADC */
 			afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */
 			afe_write(sd, 0xc8, 0x40); /* phase control */
@@ -1781,10 +1708,10 @@
 	}
 }
 
-static int adv7604_s_routing(struct v4l2_subdev *sd,
+static int adv76xx_s_routing(struct v4l2_subdev *sd,
 		u32 input, u32 output, u32 config)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	v4l2_dbg(2, debug, sd, "%s: input %d, selected input %d",
 			__func__, input, state->selected_input);
@@ -1806,11 +1733,11 @@
 	return 0;
 }
 
-static int adv7604_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+static int adv76xx_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	if (code->index >= state->info->nformats)
 		return -EINVAL;
@@ -1820,7 +1747,7 @@
 	return 0;
 }
 
-static void adv7604_fill_format(struct adv7604_state *state,
+static void adv76xx_fill_format(struct adv76xx_state *state,
 				struct v4l2_mbus_framefmt *format)
 {
 	memset(format, 0, sizeof(*format));
@@ -1828,8 +1755,9 @@
 	format->width = state->timings.bt.width;
 	format->height = state->timings.bt.height;
 	format->field = V4L2_FIELD_NONE;
+	format->colorspace = V4L2_COLORSPACE_SRGB;
 
-	if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861)
+	if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO)
 		format->colorspace = (state->timings.bt.height <= 576) ?
 			V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
 }
@@ -1841,7 +1769,7 @@
  *
  * The following table gives the op_ch_value from the format component order
  * (expressed as op_ch_sel value in column) and the bus reordering (expressed as
- * adv7604_bus_order value in row).
+ * adv76xx_bus_order value in row).
  *
  *           |	GBR(0)	GRB(1)	BGR(2)	RGB(3)	BRG(4)	RBG(5)
  * ----------+-------------------------------------------------
@@ -1852,11 +1780,11 @@
  * BRG (ROR) |	BRG	RBG	GRB	GBR	RGB	BGR
  * GBR (ROL) |	RGB	BGR	RBG	BRG	GBR	GRB
  */
-static unsigned int adv7604_op_ch_sel(struct adv7604_state *state)
+static unsigned int adv76xx_op_ch_sel(struct adv76xx_state *state)
 {
 #define _SEL(a,b,c,d,e,f)	{ \
-	ADV7604_OP_CH_SEL_##a, ADV7604_OP_CH_SEL_##b, ADV7604_OP_CH_SEL_##c, \
-	ADV7604_OP_CH_SEL_##d, ADV7604_OP_CH_SEL_##e, ADV7604_OP_CH_SEL_##f }
+	ADV76XX_OP_CH_SEL_##a, ADV76XX_OP_CH_SEL_##b, ADV76XX_OP_CH_SEL_##c, \
+	ADV76XX_OP_CH_SEL_##d, ADV76XX_OP_CH_SEL_##e, ADV76XX_OP_CH_SEL_##f }
 #define _BUS(x)			[ADV7604_BUS_ORDER_##x]
 
 	static const unsigned int op_ch_sel[6][6] = {
@@ -1871,33 +1799,34 @@
 	return op_ch_sel[state->pdata.bus_order][state->format->op_ch_sel >> 5];
 }
 
-static void adv7604_setup_format(struct adv7604_state *state)
+static void adv76xx_setup_format(struct adv76xx_state *state)
 {
 	struct v4l2_subdev *sd = &state->sd;
 
 	io_write_clr_set(sd, 0x02, 0x02,
-			state->format->rgb_out ? ADV7604_RGB_OUT : 0);
+			state->format->rgb_out ? ADV76XX_RGB_OUT : 0);
 	io_write(sd, 0x03, state->format->op_format_sel |
 		 state->pdata.op_format_mode_sel);
-	io_write_clr_set(sd, 0x04, 0xe0, adv7604_op_ch_sel(state));
+	io_write_clr_set(sd, 0x04, 0xe0, adv76xx_op_ch_sel(state));
 	io_write_clr_set(sd, 0x05, 0x01,
-			state->format->swap_cb_cr ? ADV7604_OP_SWAP_CB_CR : 0);
+			state->format->swap_cb_cr ? ADV76XX_OP_SWAP_CB_CR : 0);
 }
 
-static int adv7604_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int adv76xx_get_format(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *format)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	if (format->pad != state->source_pad)
 		return -EINVAL;
 
-	adv7604_fill_format(state, &format->format);
+	adv76xx_fill_format(state, &format->format);
 
 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
 		struct v4l2_mbus_framefmt *fmt;
 
-		fmt = v4l2_subdev_get_try_format(fh, format->pad);
+		fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
 		format->format.code = fmt->code;
 	} else {
 		format->format.code = state->format->code;
@@ -1906,39 +1835,40 @@
 	return 0;
 }
 
-static int adv7604_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int adv76xx_set_format(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *format)
 {
-	struct adv7604_state *state = to_state(sd);
-	const struct adv7604_format_info *info;
+	struct adv76xx_state *state = to_state(sd);
+	const struct adv76xx_format_info *info;
 
 	if (format->pad != state->source_pad)
 		return -EINVAL;
 
-	info = adv7604_format_info(state, format->format.code);
+	info = adv76xx_format_info(state, format->format.code);
 	if (info == NULL)
-		info = adv7604_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
+		info = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
 
-	adv7604_fill_format(state, &format->format);
+	adv76xx_fill_format(state, &format->format);
 	format->format.code = info->code;
 
 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
 		struct v4l2_mbus_framefmt *fmt;
 
-		fmt = v4l2_subdev_get_try_format(fh, format->pad);
+		fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
 		fmt->code = format->format.code;
 	} else {
 		state->format = info;
-		adv7604_setup_format(state);
+		adv76xx_setup_format(state);
 	}
 
 	return 0;
 }
 
-static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
+static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
-	struct adv7604_state *state = to_state(sd);
-	const struct adv7604_chip_info *info = state->info;
+	struct adv76xx_state *state = to_state(sd);
+	const struct adv76xx_chip_info *info = state->info;
 	const u8 irq_reg_0x43 = io_read(sd, 0x43);
 	const u8 irq_reg_0x6b = io_read(sd, 0x6b);
 	const u8 irq_reg_0x70 = io_read(sd, 0x70);
@@ -1966,7 +1896,7 @@
 			"%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n",
 			__func__, fmt_change, fmt_change_digital);
 
-		v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL);
+		v4l2_subdev_notify(sd, ADV76XX_FMT_CHANGE, NULL);
 
 		if (handled)
 			*handled = true;
@@ -1985,22 +1915,22 @@
 	if (tx_5v) {
 		v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v);
 		io_write(sd, 0x71, tx_5v);
-		adv7604_s_detect_tx_5v_ctrl(sd);
+		adv76xx_s_detect_tx_5v_ctrl(sd);
 		if (handled)
 			*handled = true;
 	}
 	return 0;
 }
 
-static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
 {
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 	u8 *data = NULL;
 
 	memset(edid->reserved, 0, sizeof(edid->reserved));
 
 	switch (edid->pad) {
-	case ADV7604_PAD_HDMI_PORT_A:
+	case ADV76XX_PAD_HDMI_PORT_A:
 	case ADV7604_PAD_HDMI_PORT_B:
 	case ADV7604_PAD_HDMI_PORT_C:
 	case ADV7604_PAD_HDMI_PORT_D:
@@ -2058,10 +1988,10 @@
 	return -1;
 }
 
-static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
 {
-	struct adv7604_state *state = to_state(sd);
-	const struct adv7604_chip_info *info = state->info;
+	struct adv76xx_state *state = to_state(sd);
+	const struct adv76xx_chip_info *info = state->info;
 	int spa_loc;
 	int err;
 	int i;
@@ -2075,7 +2005,7 @@
 	if (edid->blocks == 0) {
 		/* Disable hotplug and I2C access to EDID RAM from DDC port */
 		state->edid.present &= ~(1 << edid->pad);
-		adv7604_set_hpd(state, state->edid.present);
+		adv76xx_set_hpd(state, state->edid.present);
 		rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present);
 
 		/* Fall back to a 16:9 aspect ratio */
@@ -2099,7 +2029,7 @@
 
 	/* Disable hotplug and I2C access to EDID RAM from DDC port */
 	cancel_delayed_work_sync(&state->delayed_work_enable_hotplug);
-	adv7604_set_hpd(state, 0);
+	adv76xx_set_hpd(state, 0);
 	rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00);
 
 	spa_loc = get_edid_spa_location(edid->edid);
@@ -2107,7 +2037,7 @@
 		spa_loc = 0xc0; /* Default value [REF_02, p. 116] */
 
 	switch (edid->pad) {
-	case ADV7604_PAD_HDMI_PORT_A:
+	case ADV76XX_PAD_HDMI_PORT_A:
 		state->spa_port_a[0] = edid->edid[spa_loc];
 		state->spa_port_a[1] = edid->edid[spa_loc + 1];
 		break;
@@ -2150,7 +2080,7 @@
 		return err;
 	}
 
-	/* adv7604 calculates the checksums and enables I2C access to internal
+	/* adv76xx calculates the checksums and enables I2C access to internal
 	   EDID RAM from DDC port. */
 	rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present);
 
@@ -2214,10 +2144,10 @@
 		buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]);
 }
 
-static int adv7604_log_status(struct v4l2_subdev *sd)
+static int adv76xx_log_status(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
-	const struct adv7604_chip_info *info = state->info;
+	struct adv76xx_state *state = to_state(sd);
+	const struct adv76xx_chip_info *info = state->info;
 	struct v4l2_dv_timings timings;
 	struct stdi_readback stdi;
 	u8 reg_io_0x02 = io_read(sd, 0x02);
@@ -2276,7 +2206,7 @@
 	v4l2_info(sd, "STDI locked: %s\n", no_lock_stdi(sd) ? "false" : "true");
 	v4l2_info(sd, "CP locked: %s\n", no_lock_cp(sd) ? "false" : "true");
 	v4l2_info(sd, "CP free run: %s\n",
-			(!!(cp_read(sd, 0xff) & 0x10) ? "on" : "off"));
+			(in_free_run(sd)) ? "on" : "off");
 	v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x, v_freq = 0x%x\n",
 			io_read(sd, 0x01) & 0x0f, io_read(sd, 0x00) & 0x3f,
 			(io_read(sd, 0x01) & 0x70) >> 4);
@@ -2289,7 +2219,7 @@
 				stdi.lcf, stdi.bl, stdi.lcvs,
 				stdi.interlaced ? "interlaced" : "progressive",
 				stdi.hs_pol, stdi.vs_pol);
-	if (adv7604_query_dv_timings(sd, &timings))
+	if (adv76xx_query_dv_timings(sd, &timings))
 		v4l2_info(sd, "No video detected\n");
 	else
 		v4l2_print_dv_timings(sd->name, "Detected format: ",
@@ -2311,7 +2241,7 @@
 			((reg_io_0x02 & 0x04) ^ (reg_io_0x02 & 0x01)) ?
 				"enabled" : "disabled");
 	v4l2_info(sd, "Color space conversion: %s\n",
-			csc_coeff_sel_rb[cp_read(sd, 0xfc) >> 4]);
+			csc_coeff_sel_rb[cp_read(sd, info->cp_csc) >> 4]);
 
 	if (!is_digital_input(sd))
 		return 0;
@@ -2355,47 +2285,47 @@
 
 /* ----------------------------------------------------------------------- */
 
-static const struct v4l2_ctrl_ops adv7604_ctrl_ops = {
-	.s_ctrl = adv7604_s_ctrl,
+static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = {
+	.s_ctrl = adv76xx_s_ctrl,
 };
 
-static const struct v4l2_subdev_core_ops adv7604_core_ops = {
-	.log_status = adv7604_log_status,
-	.interrupt_service_routine = adv7604_isr,
+static const struct v4l2_subdev_core_ops adv76xx_core_ops = {
+	.log_status = adv76xx_log_status,
+	.interrupt_service_routine = adv76xx_isr,
 #ifdef CPTCFG_VIDEO_ADV_DEBUG
-	.g_register = adv7604_g_register,
-	.s_register = adv7604_s_register,
+	.g_register = adv76xx_g_register,
+	.s_register = adv76xx_s_register,
 #endif
 };
 
-static const struct v4l2_subdev_video_ops adv7604_video_ops = {
-	.s_routing = adv7604_s_routing,
-	.g_input_status = adv7604_g_input_status,
-	.s_dv_timings = adv7604_s_dv_timings,
-	.g_dv_timings = adv7604_g_dv_timings,
-	.query_dv_timings = adv7604_query_dv_timings,
+static const struct v4l2_subdev_video_ops adv76xx_video_ops = {
+	.s_routing = adv76xx_s_routing,
+	.g_input_status = adv76xx_g_input_status,
+	.s_dv_timings = adv76xx_s_dv_timings,
+	.g_dv_timings = adv76xx_g_dv_timings,
+	.query_dv_timings = adv76xx_query_dv_timings,
 };
 
-static const struct v4l2_subdev_pad_ops adv7604_pad_ops = {
-	.enum_mbus_code = adv7604_enum_mbus_code,
-	.get_fmt = adv7604_get_format,
-	.set_fmt = adv7604_set_format,
-	.get_edid = adv7604_get_edid,
-	.set_edid = adv7604_set_edid,
-	.dv_timings_cap = adv7604_dv_timings_cap,
-	.enum_dv_timings = adv7604_enum_dv_timings,
+static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
+	.enum_mbus_code = adv76xx_enum_mbus_code,
+	.get_fmt = adv76xx_get_format,
+	.set_fmt = adv76xx_set_format,
+	.get_edid = adv76xx_get_edid,
+	.set_edid = adv76xx_set_edid,
+	.dv_timings_cap = adv76xx_dv_timings_cap,
+	.enum_dv_timings = adv76xx_enum_dv_timings,
 };
 
-static const struct v4l2_subdev_ops adv7604_ops = {
-	.core = &adv7604_core_ops,
-	.video = &adv7604_video_ops,
-	.pad = &adv7604_pad_ops,
+static const struct v4l2_subdev_ops adv76xx_ops = {
+	.core = &adv76xx_core_ops,
+	.video = &adv76xx_video_ops,
+	.pad = &adv76xx_pad_ops,
 };
 
 /* -------------------------- custom ctrls ---------------------------------- */
 
 static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = {
-	.ops = &adv7604_ctrl_ops,
+	.ops = &adv76xx_ctrl_ops,
 	.id = V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE,
 	.name = "Analog Sampling Phase",
 	.type = V4L2_CTRL_TYPE_INTEGER,
@@ -2405,8 +2335,8 @@
 	.def = 0,
 };
 
-static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color_manual = {
-	.ops = &adv7604_ctrl_ops,
+static const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color_manual = {
+	.ops = &adv76xx_ctrl_ops,
 	.id = V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL,
 	.name = "Free Running Color, Manual",
 	.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -2416,8 +2346,8 @@
 	.def = false,
 };
 
-static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color = {
-	.ops = &adv7604_ctrl_ops,
+static const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color = {
+	.ops = &adv76xx_ctrl_ops,
 	.id = V4L2_CID_ADV_RX_FREE_RUN_COLOR,
 	.name = "Free Running Color",
 	.type = V4L2_CTRL_TYPE_INTEGER,
@@ -2429,11 +2359,11 @@
 
 /* ----------------------------------------------------------------------- */
 
-static int adv7604_core_init(struct v4l2_subdev *sd)
+static int adv76xx_core_init(struct v4l2_subdev *sd)
 {
-	struct adv7604_state *state = to_state(sd);
-	const struct adv7604_chip_info *info = state->info;
-	struct adv7604_platform_data *pdata = &state->pdata;
+	struct adv76xx_state *state = to_state(sd);
+	const struct adv76xx_chip_info *info = state->info;
+	struct adv76xx_platform_data *pdata = &state->pdata;
 
 	hdmi_write(sd, 0x48,
 		(pdata->disable_pwrdnb ? 0x80 : 0) |
@@ -2461,7 +2391,7 @@
 	io_write_clr_set(sd, 0x05, 0x0e, pdata->blank_data << 3 |
 			pdata->insert_av_codes << 2 |
 			pdata->replicate_av_codes << 1);
-	adv7604_setup_format(state);
+	adv76xx_setup_format(state);
 
 	cp_write(sd, 0x69, 0x30);   /* Enable CP CSC */
 
@@ -2491,7 +2421,7 @@
 	/* TODO from platform data */
 	afe_write(sd, 0xb5, 0x01);  /* Setting MCLK to 256Fs */
 
-	if (adv7604_has_afe(state)) {
+	if (adv76xx_has_afe(state)) {
 		afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */
 		io_write_clr_set(sd, 0x30, 1 << 4, pdata->output_bus_lsb_to_msb << 4);
 	}
@@ -2516,7 +2446,7 @@
 	io_write(sd, 0x41, 0xd0); /* STDI irq for any change, disable INT2 */
 }
 
-static void adv7604_unregister_clients(struct adv7604_state *state)
+static void adv76xx_unregister_clients(struct adv76xx_state *state)
 {
 	unsigned int i;
 
@@ -2526,7 +2456,7 @@
 	}
 }
 
-static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd,
+static struct i2c_client *adv76xx_dummy_client(struct v4l2_subdev *sd,
 							u8 addr, u8 io_reg)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -2536,74 +2466,74 @@
 	return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1);
 }
 
-static const struct adv7604_reg_seq adv7604_recommended_settings_afe[] = {
+static const struct adv76xx_reg_seq adv7604_recommended_settings_afe[] = {
 	/* reset ADI recommended settings for HDMI: */
 	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x00 }, /* DDC bus active pull-up control */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x74 }, /* TMDS PLL optimization */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0x74 }, /* TMDS PLL optimization */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x63 }, /* TMDS PLL optimization */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x88 }, /* equaliser */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2e }, /* equaliser */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x00 }, /* enable automatic EQ changing */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3d), 0x00 }, /* DDC bus active pull-up control */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3e), 0x74 }, /* TMDS PLL optimization */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0x74 }, /* TMDS PLL optimization */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x63 }, /* TMDS PLL optimization */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x93), 0x88 }, /* equaliser */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x94), 0x2e }, /* equaliser */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x96), 0x00 }, /* enable automatic EQ changing */
 
 	/* set ADI recommended settings for digitizer */
 	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
-	{ ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0x7b }, /* ADC noise shaping filter controls */
-	{ ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x1f }, /* CP core gain controls */
-	{ ADV7604_REG(ADV7604_PAGE_CP, 0x3e), 0x04 }, /* CP core pre-gain control */
-	{ ADV7604_REG(ADV7604_PAGE_CP, 0xc3), 0x39 }, /* CP coast control. Graphics mode */
-	{ ADV7604_REG(ADV7604_PAGE_CP, 0x40), 0x5c }, /* CP core pre-gain control. Graphics mode */
+	{ ADV76XX_REG(ADV76XX_PAGE_AFE, 0x12), 0x7b }, /* ADC noise shaping filter controls */
+	{ ADV76XX_REG(ADV76XX_PAGE_AFE, 0x0c), 0x1f }, /* CP core gain controls */
+	{ ADV76XX_REG(ADV76XX_PAGE_CP, 0x3e), 0x04 }, /* CP core pre-gain control */
+	{ ADV76XX_REG(ADV76XX_PAGE_CP, 0xc3), 0x39 }, /* CP coast control. Graphics mode */
+	{ ADV76XX_REG(ADV76XX_PAGE_CP, 0x40), 0x5c }, /* CP core pre-gain control. Graphics mode */
 
-	{ ADV7604_REG_SEQ_TERM, 0 },
+	{ ADV76XX_REG_SEQ_TERM, 0 },
 };
 
-static const struct adv7604_reg_seq adv7604_recommended_settings_hdmi[] = {
+static const struct adv76xx_reg_seq adv7604_recommended_settings_hdmi[] = {
 	/* set ADI recommended settings for HDMI: */
 	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x84 }, /* HDMI filter optimization */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x10 }, /* DDC bus active pull-up control */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x39 }, /* TMDS PLL optimization */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xb6 }, /* TMDS PLL optimization */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x03 }, /* TMDS PLL optimization */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x8b }, /* equaliser */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2d }, /* equaliser */
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x01 }, /* enable automatic EQ changing */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x0d), 0x84 }, /* HDMI filter optimization */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3d), 0x10 }, /* DDC bus active pull-up control */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x3e), 0x39 }, /* TMDS PLL optimization */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xb6 }, /* TMDS PLL optimization */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x03 }, /* TMDS PLL optimization */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x93), 0x8b }, /* equaliser */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x94), 0x2d }, /* equaliser */
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x96), 0x01 }, /* enable automatic EQ changing */
 
 	/* reset ADI recommended settings for digitizer */
 	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
-	{ ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0xfb }, /* ADC noise shaping filter controls */
-	{ ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x0d }, /* CP core gain controls */
+	{ ADV76XX_REG(ADV76XX_PAGE_AFE, 0x12), 0xfb }, /* ADC noise shaping filter controls */
+	{ ADV76XX_REG(ADV76XX_PAGE_AFE, 0x0c), 0x0d }, /* CP core gain controls */
 
-	{ ADV7604_REG_SEQ_TERM, 0 },
+	{ ADV76XX_REG_SEQ_TERM, 0 },
 };
 
-static const struct adv7604_reg_seq adv7611_recommended_settings_hdmi[] = {
+static const struct adv76xx_reg_seq adv7611_recommended_settings_hdmi[] = {
 	/* ADV7611 Register Settings Recommendations Rev 1.5, May 2014 */
-	{ ADV7604_REG(ADV7604_PAGE_CP, 0x6c), 0x00 },
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x9b), 0x03 },
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x6f), 0x08 },
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x85), 0x1f },
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x87), 0x70 },
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xda },
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x01 },
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x03), 0x98 },
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x4c), 0x44 },
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x04 },
-	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x1e },
+	{ ADV76XX_REG(ADV76XX_PAGE_CP, 0x6c), 0x00 },
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x9b), 0x03 },
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x6f), 0x08 },
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x85), 0x1f },
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x87), 0x70 },
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xda },
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x01 },
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x03), 0x98 },
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4c), 0x44 },
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8d), 0x04 },
+	{ ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x8e), 0x1e },
 
-	{ ADV7604_REG_SEQ_TERM, 0 },
+	{ ADV76XX_REG_SEQ_TERM, 0 },
 };
 
-static const struct adv7604_chip_info adv7604_chip_info[] = {
+static const struct adv76xx_chip_info adv76xx_chip_info[] = {
 	[ADV7604] = {
 		.type = ADV7604,
 		.has_afe = true,
@@ -2615,6 +2545,7 @@
 		.tdms_lock_mask = 0xe0,
 		.cable_det_mask = 0x1e,
 		.fmt_change_digital_mask = 0xc1,
+		.cp_csc = 0xfc,
 		.formats = adv7604_formats,
 		.nformats = ARRAY_SIZE(adv7604_formats),
 		.set_termination = adv7604_set_termination,
@@ -2629,18 +2560,18 @@
 		    [0] = ARRAY_SIZE(adv7604_recommended_settings_afe),
 		    [1] = ARRAY_SIZE(adv7604_recommended_settings_hdmi),
 		},
-		.page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_AVLINK) |
-			BIT(ADV7604_PAGE_CEC) | BIT(ADV7604_PAGE_INFOFRAME) |
+		.page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV7604_PAGE_AVLINK) |
+			BIT(ADV76XX_PAGE_CEC) | BIT(ADV76XX_PAGE_INFOFRAME) |
 			BIT(ADV7604_PAGE_ESDP) | BIT(ADV7604_PAGE_DPP) |
-			BIT(ADV7604_PAGE_AFE) | BIT(ADV7604_PAGE_REP) |
-			BIT(ADV7604_PAGE_EDID) | BIT(ADV7604_PAGE_HDMI) |
-			BIT(ADV7604_PAGE_TEST) | BIT(ADV7604_PAGE_CP) |
+			BIT(ADV76XX_PAGE_AFE) | BIT(ADV76XX_PAGE_REP) |
+			BIT(ADV76XX_PAGE_EDID) | BIT(ADV76XX_PAGE_HDMI) |
+			BIT(ADV76XX_PAGE_TEST) | BIT(ADV76XX_PAGE_CP) |
 			BIT(ADV7604_PAGE_VDP),
 	},
 	[ADV7611] = {
 		.type = ADV7611,
 		.has_afe = false,
-		.max_port = ADV7604_PAD_HDMI_PORT_A,
+		.max_port = ADV76XX_PAD_HDMI_PORT_A,
 		.num_dv_ports = 1,
 		.edid_enable_reg = 0x74,
 		.edid_status_reg = 0x76,
@@ -2648,6 +2579,7 @@
 		.tdms_lock_mask = 0x43,
 		.cable_det_mask = 0x01,
 		.fmt_change_digital_mask = 0x03,
+		.cp_csc = 0xf4,
 		.formats = adv7611_formats,
 		.nformats = ARRAY_SIZE(adv7611_formats),
 		.set_termination = adv7611_set_termination,
@@ -2660,34 +2592,34 @@
 		.num_recommended_settings = {
 		    [1] = ARRAY_SIZE(adv7611_recommended_settings_hdmi),
 		},
-		.page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_CEC) |
-			BIT(ADV7604_PAGE_INFOFRAME) | BIT(ADV7604_PAGE_AFE) |
-			BIT(ADV7604_PAGE_REP) |  BIT(ADV7604_PAGE_EDID) |
-			BIT(ADV7604_PAGE_HDMI) | BIT(ADV7604_PAGE_CP),
+		.page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV76XX_PAGE_CEC) |
+			BIT(ADV76XX_PAGE_INFOFRAME) | BIT(ADV76XX_PAGE_AFE) |
+			BIT(ADV76XX_PAGE_REP) |  BIT(ADV76XX_PAGE_EDID) |
+			BIT(ADV76XX_PAGE_HDMI) | BIT(ADV76XX_PAGE_CP),
 	},
 };
 
-static struct i2c_device_id adv7604_i2c_id[] = {
-	{ "adv7604", (kernel_ulong_t)&adv7604_chip_info[ADV7604] },
-	{ "adv7611", (kernel_ulong_t)&adv7604_chip_info[ADV7611] },
+static struct i2c_device_id adv76xx_i2c_id[] = {
+	{ "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] },
+	{ "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] },
 	{ }
 };
-MODULE_DEVICE_TABLE(i2c, adv7604_i2c_id);
+MODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id);
 
-static struct of_device_id adv7604_of_id[] __maybe_unused = {
-	{ .compatible = "adi,adv7611", .data = &adv7604_chip_info[ADV7611] },
+static struct of_device_id adv76xx_of_id[] __maybe_unused = {
+	{ .compatible = "adi,adv7611", .data = &adv76xx_chip_info[ADV7611] },
 	{ }
 };
-MODULE_DEVICE_TABLE(of, adv7604_of_id);
+MODULE_DEVICE_TABLE(of, adv76xx_of_id);
 
-static int adv7604_parse_dt(struct adv7604_state *state)
+static int adv76xx_parse_dt(struct adv76xx_state *state)
 {
 	struct v4l2_of_endpoint bus_cfg;
 	struct device_node *endpoint;
 	struct device_node *np;
 	unsigned int flags;
 
-	np = state->i2c_clients[ADV7604_PAGE_IO]->dev.of_node;
+	np = state->i2c_clients[ADV76XX_PAGE_IO]->dev.of_node;
 
 	/* Parse the endpoint. */
 	endpoint = of_graph_get_next_endpoint(np, NULL);
@@ -2714,20 +2646,20 @@
 	}
 
 	/* Disable the interrupt for now as no DT-based board uses it. */
-	state->pdata.int1_config = ADV7604_INT1_CONFIG_DISABLED;
+	state->pdata.int1_config = ADV76XX_INT1_CONFIG_DISABLED;
 
 	/* Use the default I2C addresses. */
 	state->pdata.i2c_addresses[ADV7604_PAGE_AVLINK] = 0x42;
-	state->pdata.i2c_addresses[ADV7604_PAGE_CEC] = 0x40;
-	state->pdata.i2c_addresses[ADV7604_PAGE_INFOFRAME] = 0x3e;
+	state->pdata.i2c_addresses[ADV76XX_PAGE_CEC] = 0x40;
+	state->pdata.i2c_addresses[ADV76XX_PAGE_INFOFRAME] = 0x3e;
 	state->pdata.i2c_addresses[ADV7604_PAGE_ESDP] = 0x38;
 	state->pdata.i2c_addresses[ADV7604_PAGE_DPP] = 0x3c;
-	state->pdata.i2c_addresses[ADV7604_PAGE_AFE] = 0x26;
-	state->pdata.i2c_addresses[ADV7604_PAGE_REP] = 0x32;
-	state->pdata.i2c_addresses[ADV7604_PAGE_EDID] = 0x36;
-	state->pdata.i2c_addresses[ADV7604_PAGE_HDMI] = 0x34;
-	state->pdata.i2c_addresses[ADV7604_PAGE_TEST] = 0x30;
-	state->pdata.i2c_addresses[ADV7604_PAGE_CP] = 0x22;
+	state->pdata.i2c_addresses[ADV76XX_PAGE_AFE] = 0x26;
+	state->pdata.i2c_addresses[ADV76XX_PAGE_REP] = 0x32;
+	state->pdata.i2c_addresses[ADV76XX_PAGE_EDID] = 0x36;
+	state->pdata.i2c_addresses[ADV76XX_PAGE_HDMI] = 0x34;
+	state->pdata.i2c_addresses[ADV76XX_PAGE_TEST] = 0x30;
+	state->pdata.i2c_addresses[ADV76XX_PAGE_CP] = 0x22;
 	state->pdata.i2c_addresses[ADV7604_PAGE_VDP] = 0x24;
 
 	/* Hardcode the remaining platform data fields. */
@@ -2742,12 +2674,12 @@
 	return 0;
 }
 
-static int adv7604_probe(struct i2c_client *client,
+static int adv76xx_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
 	static const struct v4l2_dv_timings cea640x480 =
 		V4L2_DV_BT_CEA_640X480P59_94;
-	struct adv7604_state *state;
+	struct adv76xx_state *state;
 	struct v4l2_ctrl_handler *hdl;
 	struct v4l2_subdev *sd;
 	unsigned int i;
@@ -2757,16 +2689,16 @@
 	/* Check if the adapter supports the needed features */
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 		return -EIO;
-	v4l_dbg(1, debug, client, "detecting adv7604 client on address 0x%x\n",
+	v4l_dbg(1, debug, client, "detecting adv76xx client on address 0x%x\n",
 			client->addr << 1);
 
 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
 	if (!state) {
-		v4l_err(client, "Could not allocate adv7604_state memory!\n");
+		v4l_err(client, "Could not allocate adv76xx_state memory!\n");
 		return -ENOMEM;
 	}
 
-	state->i2c_clients[ADV7604_PAGE_IO] = client;
+	state->i2c_clients[ADV76XX_PAGE_IO] = client;
 
 	/* initialize variables */
 	state->restart_stdi_once = true;
@@ -2775,18 +2707,18 @@
 	if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
 		const struct of_device_id *oid;
 
-		oid = of_match_node(adv7604_of_id, client->dev.of_node);
+		oid = of_match_node(adv76xx_of_id, client->dev.of_node);
 		state->info = oid->data;
 
-		err = adv7604_parse_dt(state);
+		err = adv76xx_parse_dt(state);
 		if (err < 0) {
 			v4l_err(client, "DT parsing error\n");
 			return err;
 		}
 	} else if (client->dev.platform_data) {
-		struct adv7604_platform_data *pdata = client->dev.platform_data;
+		struct adv76xx_platform_data *pdata = client->dev.platform_data;
 
-		state->info = (const struct adv7604_chip_info *)id->driver_data;
+		state->info = (const struct adv76xx_chip_info *)id->driver_data;
 		state->pdata = *pdata;
 	} else {
 		v4l_err(client, "No platform data!\n");
@@ -2796,20 +2728,20 @@
 	/* Request GPIOs. */
 	for (i = 0; i < state->info->num_dv_ports; ++i) {
 		state->hpd_gpio[i] =
-			devm_gpiod_get_index(&client->dev, "hpd", i);
+			devm_gpiod_get_index_optional(&client->dev, "hpd", i,
+						      GPIOD_OUT_LOW);
 		if (IS_ERR(state->hpd_gpio[i]))
-			continue;
+			return PTR_ERR(state->hpd_gpio[i]);
 
-		gpiod_direction_output(state->hpd_gpio[i], 0);
-
-		v4l_info(client, "Handling HPD %u GPIO\n", i);
+		if (state->hpd_gpio[i])
+			v4l_info(client, "Handling HPD %u GPIO\n", i);
 	}
 
 	state->timings = cea640x480;
-	state->format = adv7604_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
+	state->format = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
 
 	sd = &state->sd;
-	v4l2_i2c_subdev_init(sd, client, &adv7604_ops);
+	v4l2_i2c_subdev_init(sd, client, &adv76xx_ops);
 	snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
 		id->name, i2c_adapter_id(client->adapter),
 		client->addr);
@@ -2839,15 +2771,15 @@
 
 	/* control handlers */
 	hdl = &state->hdl;
-	v4l2_ctrl_handler_init(hdl, adv7604_has_afe(state) ? 9 : 8);
+	v4l2_ctrl_handler_init(hdl, adv76xx_has_afe(state) ? 9 : 8);
 
-	v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops,
+	v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops,
 			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
-	v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops,
+	v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops,
 			V4L2_CID_CONTRAST, 0, 255, 1, 128);
-	v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops,
+	v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops,
 			V4L2_CID_SATURATION, 0, 255, 1, 128);
-	v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops,
+	v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops,
 			V4L2_CID_HUE, 0, 128, 1, 0);
 
 	/* private controls */
@@ -2855,18 +2787,18 @@
 			V4L2_CID_DV_RX_POWER_PRESENT, 0,
 			(1 << state->info->num_dv_ports) - 1, 0, 0);
 	state->rgb_quantization_range_ctrl =
-		v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops,
+		v4l2_ctrl_new_std_menu(hdl, &adv76xx_ctrl_ops,
 			V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
 			0, V4L2_DV_RGB_RANGE_AUTO);
 
 	/* custom controls */
-	if (adv7604_has_afe(state))
+	if (adv76xx_has_afe(state))
 		state->analog_sampling_phase_ctrl =
 			v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL);
 	state->free_run_color_manual_ctrl =
-		v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color_manual, NULL);
+		v4l2_ctrl_new_custom(hdl, &adv76xx_ctrl_free_run_color_manual, NULL);
 	state->free_run_color_ctrl =
-		v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color, NULL);
+		v4l2_ctrl_new_custom(hdl, &adv76xx_ctrl_free_run_color, NULL);
 
 	sd->ctrl_handler = hdl;
 	if (hdl->error) {
@@ -2875,22 +2807,22 @@
 	}
 	state->detect_tx_5v_ctrl->is_private = true;
 	state->rgb_quantization_range_ctrl->is_private = true;
-	if (adv7604_has_afe(state))
+	if (adv76xx_has_afe(state))
 		state->analog_sampling_phase_ctrl->is_private = true;
 	state->free_run_color_manual_ctrl->is_private = true;
 	state->free_run_color_ctrl->is_private = true;
 
-	if (adv7604_s_detect_tx_5v_ctrl(sd)) {
+	if (adv76xx_s_detect_tx_5v_ctrl(sd)) {
 		err = -ENODEV;
 		goto err_hdl;
 	}
 
-	for (i = 1; i < ADV7604_PAGE_MAX; ++i) {
+	for (i = 1; i < ADV76XX_PAGE_MAX; ++i) {
 		if (!(BIT(i) & state->info->page_mask))
 			continue;
 
 		state->i2c_clients[i] =
-			adv7604_dummy_client(sd, state->pdata.i2c_addresses[i],
+			adv76xx_dummy_client(sd, state->pdata.i2c_addresses[i],
 					     0xf2 + i);
 		if (state->i2c_clients[i] == NULL) {
 			err = -ENOMEM;
@@ -2908,7 +2840,7 @@
 	}
 
 	INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug,
-			adv7604_delayed_work_enable_hotplug);
+			adv76xx_delayed_work_enable_hotplug);
 
 	state->source_pad = state->info->num_dv_ports
 			  + (state->info->has_afe ? 2 : 0);
@@ -2921,7 +2853,7 @@
 	if (err)
 		goto err_work_queues;
 
-	err = adv7604_core_init(sd);
+	err = adv76xx_core_init(sd);
 	if (err)
 		goto err_entity;
 	v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
@@ -2939,7 +2871,7 @@
 	cancel_delayed_work(&state->delayed_work_enable_hotplug);
 	destroy_workqueue(state->work_queues);
 err_i2c:
-	adv7604_unregister_clients(state);
+	adv76xx_unregister_clients(state);
 err_hdl:
 	v4l2_ctrl_handler_free(hdl);
 	return err;
@@ -2947,32 +2879,31 @@
 
 /* ----------------------------------------------------------------------- */
 
-static int adv7604_remove(struct i2c_client *client)
+static int adv76xx_remove(struct i2c_client *client)
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
-	struct adv7604_state *state = to_state(sd);
+	struct adv76xx_state *state = to_state(sd);
 
 	cancel_delayed_work(&state->delayed_work_enable_hotplug);
 	destroy_workqueue(state->work_queues);
 	v4l2_async_unregister_subdev(sd);
-	v4l2_device_unregister_subdev(sd);
 	media_entity_cleanup(&sd->entity);
-	adv7604_unregister_clients(to_state(sd));
+	adv76xx_unregister_clients(to_state(sd));
 	v4l2_ctrl_handler_free(sd->ctrl_handler);
 	return 0;
 }
 
 /* ----------------------------------------------------------------------- */
 
-static struct i2c_driver adv7604_driver = {
+static struct i2c_driver adv76xx_driver = {
 	.driver = {
 		.owner = THIS_MODULE,
 		.name = "adv7604",
-		.of_match_table = of_match_ptr(adv7604_of_id),
+		.of_match_table = of_match_ptr(adv76xx_of_id),
 	},
-	.probe = adv7604_probe,
-	.remove = adv7604_remove,
-	.id_table = adv7604_i2c_id,
+	.probe = adv76xx_probe,
+	.remove = adv76xx_remove,
+	.id_table = adv76xx_i2c_id,
 };
 
-module_i2c_driver(adv7604_driver);
+module_i2c_driver(adv76xx_driver);
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index 9af02d3..4353d9b 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -38,6 +38,7 @@
 #include <linux/videodev2.h>
 #include <linux/workqueue.h>
 #include <linux/v4l2-dv-timings.h>
+#include <linux/hdmi.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-dv-timings.h>
@@ -220,21 +221,11 @@
 	return &container_of(ctrl->handler, struct adv7842_state, hdl)->sd;
 }
 
-static inline unsigned hblanking(const struct v4l2_bt_timings *t)
-{
-	return V4L2_DV_BT_BLANKING_WIDTH(t);
-}
-
 static inline unsigned htotal(const struct v4l2_bt_timings *t)
 {
 	return V4L2_DV_BT_FRAME_WIDTH(t);
 }
 
-static inline unsigned vblanking(const struct v4l2_bt_timings *t)
-{
-	return V4L2_DV_BT_BLANKING_HEIGHT(t);
-}
-
 static inline unsigned vtotal(const struct v4l2_bt_timings *t)
 {
 	return V4L2_DV_BT_FRAME_HEIGHT(t);
@@ -1128,7 +1119,7 @@
 		/* Receiving DVI-D signal
 		 * ADV7842 selects RGB limited range regardless of
 		 * input format (CE/IT) in automatic mode */
-		if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
+		if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
 			/* RGB limited range (16-235) */
 			io_write_and_or(sd, 0x02, 0x0f, 0x00);
 		} else {
@@ -1910,7 +1901,8 @@
 		return 0;
 	}
 
-	if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
+	fmt->colorspace = V4L2_COLORSPACE_SRGB;
+	if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
 		fmt->colorspace = (state->timings.bt.height <= 576) ?
 			V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
 	}
@@ -2108,149 +2100,65 @@
 	return err;
 }
 
-/*********** avi info frame CEA-861-E **************/
-/* TODO move to common library */
-
-struct avi_info_frame {
-	uint8_t f17;
-	uint8_t y10;
-	uint8_t a0;
-	uint8_t b10;
-	uint8_t s10;
-	uint8_t c10;
-	uint8_t m10;
-	uint8_t r3210;
-	uint8_t itc;
-	uint8_t ec210;
-	uint8_t q10;
-	uint8_t sc10;
-	uint8_t f47;
-	uint8_t vic;
-	uint8_t yq10;
-	uint8_t cn10;
-	uint8_t pr3210;
-	uint16_t etb;
-	uint16_t sbb;
-	uint16_t elb;
-	uint16_t srb;
+struct adv7842_cfg_read_infoframe {
+	const char *desc;
+	u8 present_mask;
+	u8 head_addr;
+	u8 payload_addr;
 };
 
-static const char *y10_txt[4] = {
-	"RGB",
-	"YCbCr 4:2:2",
-	"YCbCr 4:4:4",
-	"Future",
-};
-
-static const char *c10_txt[4] = {
-	"No Data",
-	"SMPTE 170M",
-	"ITU-R 709",
-	"Extended Colorimetry information valied",
-};
-
-static const char *itc_txt[2] = {
-	"No Data",
-	"IT content",
-};
-
-static const char *ec210_txt[8] = {
-	"xvYCC601",
-	"xvYCC709",
-	"sYCC601",
-	"AdobeYCC601",
-	"AdobeRGB",
-	"5 reserved",
-	"6 reserved",
-	"7 reserved",
-};
-
-static const char *q10_txt[4] = {
-	"Default",
-	"Limited Range",
-	"Full Range",
-	"Reserved",
-};
-
-static void parse_avi_infoframe(struct v4l2_subdev *sd, uint8_t *buf,
-				struct avi_info_frame *avi)
-{
-	avi->f17 = (buf[1] >> 7) & 0x1;
-	avi->y10 = (buf[1] >> 5) & 0x3;
-	avi->a0 = (buf[1] >> 4) & 0x1;
-	avi->b10 = (buf[1] >> 2) & 0x3;
-	avi->s10 = buf[1] & 0x3;
-	avi->c10 = (buf[2] >> 6) & 0x3;
-	avi->m10 = (buf[2] >> 4) & 0x3;
-	avi->r3210 = buf[2] & 0xf;
-	avi->itc = (buf[3] >> 7) & 0x1;
-	avi->ec210 = (buf[3] >> 4) & 0x7;
-	avi->q10 = (buf[3] >> 2) & 0x3;
-	avi->sc10 = buf[3] & 0x3;
-	avi->f47 = (buf[4] >> 7) & 0x1;
-	avi->vic = buf[4] & 0x7f;
-	avi->yq10 = (buf[5] >> 6) & 0x3;
-	avi->cn10 = (buf[5] >> 4) & 0x3;
-	avi->pr3210 = buf[5] & 0xf;
-	avi->etb = buf[6] + 256*buf[7];
-	avi->sbb = buf[8] + 256*buf[9];
-	avi->elb = buf[10] + 256*buf[11];
-	avi->srb = buf[12] + 256*buf[13];
-}
-
-static void print_avi_infoframe(struct v4l2_subdev *sd)
+static void log_infoframe(struct v4l2_subdev *sd, struct adv7842_cfg_read_infoframe *cri)
 {
 	int i;
-	uint8_t buf[14];
-	u8 avi_len;
-	u8 avi_ver;
-	struct avi_info_frame avi;
+	uint8_t buffer[32];
+	union hdmi_infoframe frame;
+	u8 len;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *dev = &client->dev;
+
+	if (!(io_read(sd, 0x60) & cri->present_mask)) {
+		v4l2_info(sd, "%s infoframe not received\n", cri->desc);
+		return;
+	}
+
+	for (i = 0; i < 3; i++)
+		buffer[i] = infoframe_read(sd, cri->head_addr + i);
+
+	len = buffer[2] + 1;
+
+	if (len + 3 > sizeof(buffer)) {
+		v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__, cri->desc, len);
+		return;
+	}
+
+	for (i = 0; i < len; i++)
+		buffer[i + 3] = infoframe_read(sd, cri->payload_addr + i);
+
+	if (hdmi_infoframe_unpack(&frame, buffer) < 0) {
+		v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc);
+		return;
+	}
+
+	hdmi_infoframe_log(KERN_INFO, dev, &frame);
+}
+
+static void adv7842_log_infoframes(struct v4l2_subdev *sd)
+{
+	int i;
+	struct adv7842_cfg_read_infoframe cri[] = {
+		{ "AVI", 0x01, 0xe0, 0x00 },
+		{ "Audio", 0x02, 0xe3, 0x1c },
+		{ "SDP", 0x04, 0xe6, 0x2a },
+		{ "Vendor", 0x10, 0xec, 0x54 }
+	};
 
 	if (!(hdmi_read(sd, 0x05) & 0x80)) {
-		v4l2_info(sd, "receive DVI-D signal (AVI infoframe not supported)\n");
-		return;
-	}
-	if (!(io_read(sd, 0x60) & 0x01)) {
-		v4l2_info(sd, "AVI infoframe not received\n");
+		v4l2_info(sd, "receive DVI-D signal, no infoframes\n");
 		return;
 	}
 
-	if (io_read(sd, 0x88) & 0x10) {
-		v4l2_info(sd, "AVI infoframe checksum error has occurred earlier\n");
-		io_write(sd, 0x8a, 0x10); /* clear AVI_INF_CKS_ERR_RAW */
-		if (io_read(sd, 0x88) & 0x10) {
-			v4l2_info(sd, "AVI infoframe checksum error still present\n");
-			io_write(sd, 0x8a, 0x10); /* clear AVI_INF_CKS_ERR_RAW */
-		}
-	}
-
-	avi_len = infoframe_read(sd, 0xe2);
-	avi_ver = infoframe_read(sd, 0xe1);
-	v4l2_info(sd, "AVI infoframe version %d (%d byte)\n",
-		  avi_ver, avi_len);
-
-	if (avi_ver != 0x02)
-		return;
-
-	for (i = 0; i < 14; i++)
-		buf[i] = infoframe_read(sd, i);
-
-	v4l2_info(sd, "\t%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-		  buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
-		  buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]);
-
-	parse_avi_infoframe(sd, buf, &avi);
-
-	if (avi.vic)
-		v4l2_info(sd, "\tVIC: %d\n", avi.vic);
-	if (avi.itc)
-		v4l2_info(sd, "\t%s\n", itc_txt[avi.itc]);
-
-	if (avi.y10)
-		v4l2_info(sd, "\t%s %s\n", y10_txt[avi.y10], !avi.c10 ? "" :
-			(avi.c10 == 0x3 ? ec210_txt[avi.ec210] : c10_txt[avi.c10]));
-	else
-		v4l2_info(sd, "\t%s %s\n", y10_txt[avi.y10], q10_txt[avi.q10]);
+	for (i = 0; i < ARRAY_SIZE(cri); i++)
+		log_infoframe(sd, &cri[i]);
 }
 
 static const char * const prim_mode_txt[] = {
@@ -2464,7 +2372,8 @@
 	v4l2_info(sd, "Deep color mode: %s\n",
 			deep_color_mode_txt[hdmi_read(sd, 0x0b) >> 6]);
 
-	print_avi_infoframe(sd);
+	adv7842_log_infoframes(sd);
+
 	return 0;
 }
 
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index 05b7cd6..4393ba5 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -5137,6 +5137,9 @@
 	int default_volume;
 	u32 id;
 	u16 device_id;
+#if defined(CPTCFG_MEDIA_CONTROLLER)
+	int ret;
+#endif
 
 	/* Check if the adapter supports the needed features */
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -5178,6 +5181,33 @@
 
 	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
+#if defined(CPTCFG_MEDIA_CONTROLLER)
+	/*
+	 * TODO: add media controller support for analog video inputs like
+	 * composite, svideo, etc.
+	 * A real input pad for this analog demod would be like:
+	 *                 ___________
+	 * TUNER --------> |         |
+	 *		   |         |
+	 * SVIDEO .......> | cx25840 |
+	 *		   |         |
+	 * COMPOSITE1 ...> |_________|
+	 *
+	 * However, at least for now, there's no much gain on modelling
+	 * those extra inputs. So, let's add it only when needed.
+	 */
+	state->pads[CX25840_PAD_INPUT].flags = MEDIA_PAD_FL_SINK;
+	state->pads[CX25840_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
+	state->pads[CX25840_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
+
+	ret = media_entity_init(&sd->entity, ARRAY_SIZE(state->pads),
+				state->pads, 0);
+	if (ret < 0) {
+		v4l_info(client, "failed to initialize media entity!\n");
+		return ret;
+	}
+#endif
 
 	switch (id) {
 	case CX23885_AV:
diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h
index 37bc042..b983298 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.h
+++ b/drivers/media/i2c/cx25840/cx25840-core.h
@@ -41,6 +41,14 @@
 	CX25837,
 };
 
+enum cx25840_media_pads {
+	CX25840_PAD_INPUT,
+	CX25840_PAD_VID_OUT,
+	CX25840_PAD_VBI_OUT,
+
+	CX25840_NUM_PADS
+};
+
 struct cx25840_state {
 	struct i2c_client *c;
 	struct v4l2_subdev sd;
@@ -64,6 +72,9 @@
 	wait_queue_head_t fw_wait;    /* wake up when the fw load is finished */
 	struct work_struct fw_work;   /* work entry for fw load */
 	struct cx25840_ir_state *ir_state;
+#if defined(CPTCFG_MEDIA_CONTROLLER)
+	struct media_pad	pads[CX25840_NUM_PADS];
+#endif
 };
 
 static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c
index 2820f7c..6404c0d 100644
--- a/drivers/media/i2c/m5mols/m5mols_core.c
+++ b/drivers/media/i2c/m5mols/m5mols_core.c
@@ -125,9 +125,9 @@
 	if (length == 1)
 		return *data;
 	else if (length == 2)
-		return be16_to_cpu(*((u16 *)data));
+		return be16_to_cpu(*((__be16 *)data));
 	else
-		return be32_to_cpu(*((u32 *)data));
+		return be32_to_cpu(*((__be32 *)data));
 }
 
 /**
@@ -454,11 +454,6 @@
 			return ret;
 	}
 
-	ver->fw = be16_to_cpu(ver->fw);
-	ver->hw = be16_to_cpu(ver->hw);
-	ver->param = be16_to_cpu(ver->param);
-	ver->awb = be16_to_cpu(ver->awb);
-
 	v4l2_info(sd, "Manufacturer\t[%s]\n",
 			is_manufacturer(info, REG_SAMSUNG_ELECTRO) ?
 			"Samsung Electro-Machanics" :
@@ -536,17 +531,17 @@
 }
 
 static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
-				struct v4l2_subdev_fh *fh,
+				struct v4l2_subdev_pad_config *cfg,
 				enum v4l2_subdev_format_whence which,
 				enum m5mols_restype type)
 {
 	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
+		return cfg ? v4l2_subdev_get_try_format(&info->sd, cfg, 0) : NULL;
 
 	return &info->ffmt[type];
 }
 
-static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct m5mols_info *info = to_m5mols(sd);
@@ -555,7 +550,7 @@
 
 	mutex_lock(&info->lock);
 
-	format = __find_format(info, fh, fmt->which, info->res_type);
+	format = __find_format(info, cfg, fmt->which, info->res_type);
 	if (format)
 		fmt->format = *format;
 	else
@@ -565,7 +560,7 @@
 	return ret;
 }
 
-static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct m5mols_info *info = to_m5mols(sd);
@@ -579,7 +574,7 @@
 	if (ret < 0)
 		return ret;
 
-	sfmt = __find_format(info, fh, fmt->which, type);
+	sfmt = __find_format(info, cfg, fmt->which, type);
 	if (!sfmt)
 		return 0;
 
@@ -645,7 +640,7 @@
 
 
 static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (!code || code->index >= SIZE_DEFAULT_FFMT)
@@ -900,7 +895,7 @@
  */
 static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
-	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
 
 	*format = m5mols_default_ffmt[0];
 	return 0;
diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c
index 4d9c6bc..dcc68ec 100644
--- a/drivers/media/i2c/msp3400-driver.c
+++ b/drivers/media/i2c/msp3400-driver.c
@@ -904,11 +904,3 @@
 };
 
 module_i2c_driver(msp_driver);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c
index 7a2b628..0ed7ff4 100644
--- a/drivers/media/i2c/mt9m032.c
+++ b/drivers/media/i2c/mt9m032.c
@@ -317,7 +317,7 @@
  */
 
 static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->index != 0)
@@ -328,7 +328,7 @@
 }
 
 static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
 {
 	if (fse->index != 0 || fse->code != MEDIA_BUS_FMT_Y8_1X8)
@@ -345,18 +345,18 @@
 /**
  * __mt9m032_get_pad_crop() - get crop rect
  * @sensor: pointer to the sensor struct
- * @fh: file handle for getting the try crop rect from
+ * @cfg: v4l2_subdev_pad_config for getting the try crop rect from
  * @which: select try or active crop rect
  *
  * Returns a pointer the current active or fh relative try crop rect
  */
 static struct v4l2_rect *
-__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
+__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_pad_config *cfg,
 		       enum v4l2_subdev_format_whence which)
 {
 	switch (which) {
 	case V4L2_SUBDEV_FORMAT_TRY:
-		return v4l2_subdev_get_try_crop(fh, 0);
+		return v4l2_subdev_get_try_crop(&sensor->subdev, cfg, 0);
 	case V4L2_SUBDEV_FORMAT_ACTIVE:
 		return &sensor->crop;
 	default:
@@ -367,18 +367,18 @@
 /**
  * __mt9m032_get_pad_format() - get format
  * @sensor: pointer to the sensor struct
- * @fh: file handle for getting the try format from
+ * @cfg: v4l2_subdev_pad_config for getting the try format from
  * @which: select try or active format
  *
  * Returns a pointer the current active or fh relative try format
  */
 static struct v4l2_mbus_framefmt *
-__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
+__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_pad_config *cfg,
 			 enum v4l2_subdev_format_whence which)
 {
 	switch (which) {
 	case V4L2_SUBDEV_FORMAT_TRY:
-		return v4l2_subdev_get_try_format(fh, 0);
+		return v4l2_subdev_get_try_format(&sensor->subdev, cfg, 0);
 	case V4L2_SUBDEV_FORMAT_ACTIVE:
 		return &sensor->format;
 	default:
@@ -387,20 +387,20 @@
 }
 
 static int mt9m032_get_pad_format(struct v4l2_subdev *subdev,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_format *fmt)
 {
 	struct mt9m032 *sensor = to_mt9m032(subdev);
 
 	mutex_lock(&sensor->lock);
-	fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which);
+	fmt->format = *__mt9m032_get_pad_format(sensor, cfg, fmt->which);
 	mutex_unlock(&sensor->lock);
 
 	return 0;
 }
 
 static int mt9m032_set_pad_format(struct v4l2_subdev *subdev,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_format *fmt)
 {
 	struct mt9m032 *sensor = to_mt9m032(subdev);
@@ -414,7 +414,7 @@
 	}
 
 	/* Scaling is not supported, the format is thus fixed. */
-	fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which);
+	fmt->format = *__mt9m032_get_pad_format(sensor, cfg, fmt->which);
 	ret = 0;
 
 done:
@@ -422,22 +422,25 @@
 	return ret;
 }
 
-static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev,
-				struct v4l2_subdev_fh *fh,
-				struct v4l2_subdev_crop *crop)
+static int mt9m032_get_pad_selection(struct v4l2_subdev *subdev,
+				     struct v4l2_subdev_pad_config *cfg,
+				     struct v4l2_subdev_selection *sel)
 {
 	struct mt9m032 *sensor = to_mt9m032(subdev);
 
+	if (sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
 	mutex_lock(&sensor->lock);
-	crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which);
+	sel->r = *__mt9m032_get_pad_crop(sensor, cfg, sel->which);
 	mutex_unlock(&sensor->lock);
 
 	return 0;
 }
 
-static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev,
-				struct v4l2_subdev_fh *fh,
-				struct v4l2_subdev_crop *crop)
+static int mt9m032_set_pad_selection(struct v4l2_subdev *subdev,
+				     struct v4l2_subdev_pad_config *cfg,
+				     struct v4l2_subdev_selection *sel)
 {
 	struct mt9m032 *sensor = to_mt9m032(subdev);
 	struct v4l2_mbus_framefmt *format;
@@ -445,9 +448,12 @@
 	struct v4l2_rect rect;
 	int ret = 0;
 
+	if (sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
 	mutex_lock(&sensor->lock);
 
-	if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+	if (sensor->streaming && sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
 		ret = -EBUSY;
 		goto done;
 	}
@@ -455,13 +461,13 @@
 	/* Clamp the crop rectangle boundaries and align them to a multiple of 2
 	 * pixels to ensure a GRBG Bayer pattern.
 	 */
-	rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN,
+	rect.left = clamp(ALIGN(sel->r.left, 2), MT9M032_COLUMN_START_MIN,
 			  MT9M032_COLUMN_START_MAX);
-	rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN,
+	rect.top = clamp(ALIGN(sel->r.top, 2), MT9M032_ROW_START_MIN,
 			 MT9M032_ROW_START_MAX);
-	rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2),
+	rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2),
 			     MT9M032_COLUMN_SIZE_MIN, MT9M032_COLUMN_SIZE_MAX);
-	rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2),
+	rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2),
 			      MT9M032_ROW_SIZE_MIN, MT9M032_ROW_SIZE_MAX);
 
 	rect.width = min_t(unsigned int, rect.width,
@@ -469,21 +475,21 @@
 	rect.height = min_t(unsigned int, rect.height,
 			    MT9M032_PIXEL_ARRAY_HEIGHT - rect.top);
 
-	__crop = __mt9m032_get_pad_crop(sensor, fh, crop->which);
+	__crop = __mt9m032_get_pad_crop(sensor, cfg, sel->which);
 
 	if (rect.width != __crop->width || rect.height != __crop->height) {
 		/* Reset the output image size if the crop rectangle size has
 		 * been modified.
 		 */
-		format = __mt9m032_get_pad_format(sensor, fh, crop->which);
+		format = __mt9m032_get_pad_format(sensor, cfg, sel->which);
 		format->width = rect.width;
 		format->height = rect.height;
 	}
 
 	*__crop = rect;
-	crop->rect = rect;
+	sel->r = rect;
 
-	if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
 		ret = mt9m032_update_geom_timing(sensor);
 
 done:
@@ -690,8 +696,8 @@
 	.enum_frame_size = mt9m032_enum_frame_size,
 	.get_fmt = mt9m032_get_pad_format,
 	.set_fmt = mt9m032_set_pad_format,
-	.set_crop = mt9m032_set_pad_crop,
-	.get_crop = mt9m032_get_pad_crop,
+	.set_selection = mt9m032_set_pad_selection,
+	.get_selection = mt9m032_get_pad_selection,
 };
 
 static const struct v4l2_subdev_ops mt9m032_ops = {
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index edb76bd..0db15f5 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -15,12 +15,11 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/log2.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_gpio.h>
 #include <linux/of_graph.h>
 #include <linux/pm.h>
 #include <linux/regulator/consumer.h>
@@ -28,6 +27,7 @@
 #include <linux/videodev2.h>
 
 #include <media/mt9p031.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-subdev.h>
@@ -135,7 +135,7 @@
 	struct aptina_pll pll;
 	unsigned int clk_div;
 	bool use_pll;
-	int reset;
+	struct gpio_desc *reset;
 
 	struct v4l2_ctrl_handler ctrls;
 	struct v4l2_ctrl *blc_auto;
@@ -251,7 +251,7 @@
 		div = DIV_ROUND_UP(pdata->ext_freq, pdata->target_freq);
 		div = roundup_pow_of_two(div) / 2;
 
-		mt9p031->clk_div = max_t(unsigned int, div, 64);
+		mt9p031->clk_div = min_t(unsigned int, div, 64);
 		mt9p031->use_pll = false;
 
 		return 0;
@@ -308,9 +308,9 @@
 {
 	int ret;
 
-	/* Ensure RESET_BAR is low */
-	if (gpio_is_valid(mt9p031->reset)) {
-		gpio_set_value(mt9p031->reset, 0);
+	/* Ensure RESET_BAR is active */
+	if (mt9p031->reset) {
+		gpiod_set_value(mt9p031->reset, 1);
 		usleep_range(1000, 2000);
 	}
 
@@ -331,8 +331,8 @@
 	}
 
 	/* Now RESET_BAR must be high */
-	if (gpio_is_valid(mt9p031->reset)) {
-		gpio_set_value(mt9p031->reset, 1);
+	if (mt9p031->reset) {
+		gpiod_set_value(mt9p031->reset, 0);
 		usleep_range(1000, 2000);
 	}
 
@@ -341,8 +341,8 @@
 
 static void mt9p031_power_off(struct mt9p031 *mt9p031)
 {
-	if (gpio_is_valid(mt9p031->reset)) {
-		gpio_set_value(mt9p031->reset, 0);
+	if (mt9p031->reset) {
+		gpiod_set_value(mt9p031->reset, 1);
 		usleep_range(1000, 2000);
 	}
 
@@ -474,7 +474,7 @@
 }
 
 static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
@@ -487,7 +487,7 @@
 }
 
 static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
 {
 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
@@ -505,12 +505,12 @@
 }
 
 static struct v4l2_mbus_framefmt *
-__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
+__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg,
 			 unsigned int pad, u32 which)
 {
 	switch (which) {
 	case V4L2_SUBDEV_FORMAT_TRY:
-		return v4l2_subdev_get_try_format(fh, pad);
+		return v4l2_subdev_get_try_format(&mt9p031->subdev, cfg, pad);
 	case V4L2_SUBDEV_FORMAT_ACTIVE:
 		return &mt9p031->format;
 	default:
@@ -519,12 +519,12 @@
 }
 
 static struct v4l2_rect *
-__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
+__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg,
 		     unsigned int pad, u32 which)
 {
 	switch (which) {
 	case V4L2_SUBDEV_FORMAT_TRY:
-		return v4l2_subdev_get_try_crop(fh, pad);
+		return v4l2_subdev_get_try_crop(&mt9p031->subdev, cfg, pad);
 	case V4L2_SUBDEV_FORMAT_ACTIVE:
 		return &mt9p031->crop;
 	default:
@@ -533,18 +533,18 @@
 }
 
 static int mt9p031_get_format(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *fmt)
 {
 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
 
-	fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad,
+	fmt->format = *__mt9p031_get_pad_format(mt9p031, cfg, fmt->pad,
 						fmt->which);
 	return 0;
 }
 
 static int mt9p031_set_format(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *format)
 {
 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
@@ -555,7 +555,7 @@
 	unsigned int hratio;
 	unsigned int vratio;
 
-	__crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad,
+	__crop = __mt9p031_get_pad_crop(mt9p031, cfg, format->pad,
 					format->which);
 
 	/* Clamp the width and height to avoid dividing by zero. */
@@ -571,7 +571,7 @@
 	hratio = DIV_ROUND_CLOSEST(__crop->width, width);
 	vratio = DIV_ROUND_CLOSEST(__crop->height, height);
 
-	__format = __mt9p031_get_pad_format(mt9p031, fh, format->pad,
+	__format = __mt9p031_get_pad_format(mt9p031, cfg, format->pad,
 					    format->which);
 	__format->width = __crop->width / hratio;
 	__format->height = __crop->height / vratio;
@@ -581,37 +581,42 @@
 	return 0;
 }
 
-static int mt9p031_get_crop(struct v4l2_subdev *subdev,
-			    struct v4l2_subdev_fh *fh,
-			    struct v4l2_subdev_crop *crop)
+static int mt9p031_get_selection(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_selection *sel)
 {
 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
 
-	crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad,
-					     crop->which);
+	if (sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	sel->r = *__mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which);
 	return 0;
 }
 
-static int mt9p031_set_crop(struct v4l2_subdev *subdev,
-			    struct v4l2_subdev_fh *fh,
-			    struct v4l2_subdev_crop *crop)
+static int mt9p031_set_selection(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_selection *sel)
 {
 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
 	struct v4l2_mbus_framefmt *__format;
 	struct v4l2_rect *__crop;
 	struct v4l2_rect rect;
 
+	if (sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
 	/* Clamp the crop rectangle boundaries and align them to a multiple of 2
 	 * pixels to ensure a GRBG Bayer pattern.
 	 */
-	rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN,
+	rect.left = clamp(ALIGN(sel->r.left, 2), MT9P031_COLUMN_START_MIN,
 			  MT9P031_COLUMN_START_MAX);
-	rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN,
+	rect.top = clamp(ALIGN(sel->r.top, 2), MT9P031_ROW_START_MIN,
 			 MT9P031_ROW_START_MAX);
-	rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2),
+	rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2),
 			     MT9P031_WINDOW_WIDTH_MIN,
 			     MT9P031_WINDOW_WIDTH_MAX);
-	rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2),
+	rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2),
 			      MT9P031_WINDOW_HEIGHT_MIN,
 			      MT9P031_WINDOW_HEIGHT_MAX);
 
@@ -620,20 +625,20 @@
 	rect.height = min_t(unsigned int, rect.height,
 			    MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
 
-	__crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which);
+	__crop = __mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which);
 
 	if (rect.width != __crop->width || rect.height != __crop->height) {
 		/* Reset the output image size if the crop rectangle size has
 		 * been modified.
 		 */
-		__format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad,
-						    crop->which);
+		__format = __mt9p031_get_pad_format(mt9p031, cfg, sel->pad,
+						    sel->which);
 		__format->width = rect.width;
 		__format->height = rect.height;
 	}
 
 	*__crop = rect;
-	crop->rect = rect;
+	sel->r = rect;
 
 	return 0;
 }
@@ -941,13 +946,13 @@
 	struct v4l2_mbus_framefmt *format;
 	struct v4l2_rect *crop;
 
-	crop = v4l2_subdev_get_try_crop(fh, 0);
+	crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0);
 	crop->left = MT9P031_COLUMN_START_DEF;
 	crop->top = MT9P031_ROW_START_DEF;
 	crop->width = MT9P031_WINDOW_WIDTH_DEF;
 	crop->height = MT9P031_WINDOW_HEIGHT_DEF;
 
-	format = v4l2_subdev_get_try_format(fh, 0);
+	format = v4l2_subdev_get_try_format(subdev, fh->pad, 0);
 
 	if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
 		format->code = MEDIA_BUS_FMT_Y12_1X12;
@@ -980,8 +985,8 @@
 	.enum_frame_size = mt9p031_enum_frame_size,
 	.get_fmt = mt9p031_get_format,
 	.set_fmt = mt9p031_set_format,
-	.get_crop = mt9p031_get_crop,
-	.set_crop = mt9p031_set_crop,
+	.get_selection = mt9p031_get_selection,
+	.set_selection = mt9p031_set_selection,
 };
 
 static struct v4l2_subdev_ops mt9p031_subdev_ops = {
@@ -1017,7 +1022,6 @@
 	if (!pdata)
 		goto done;
 
-	pdata->reset = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
 	of_property_read_u32(np, "input-clock-frequency", &pdata->ext_freq);
 	of_property_read_u32(np, "pixel-clock-frequency", &pdata->target_freq);
 
@@ -1054,7 +1058,6 @@
 	mt9p031->output_control	= MT9P031_OUTPUT_CONTROL_DEF;
 	mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
 	mt9p031->model = did->driver_data;
-	mt9p031->reset = -1;
 
 	mt9p031->regulators[0].supply = "vdd";
 	mt9p031->regulators[1].supply = "vdd_io";
@@ -1066,6 +1069,8 @@
 		return ret;
 	}
 
+	mutex_init(&mt9p031->power_lock);
+
 	v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6);
 
 	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
@@ -1103,7 +1108,6 @@
 	mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls,
 					     V4L2_CID_BLC_DIGITAL_OFFSET);
 
-	mutex_init(&mt9p031->power_lock);
 	v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
 	mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
 
@@ -1129,21 +1133,20 @@
 	mt9p031->format.field = V4L2_FIELD_NONE;
 	mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
 
-	if (gpio_is_valid(pdata->reset)) {
-		ret = devm_gpio_request_one(&client->dev, pdata->reset,
-					    GPIOF_OUT_INIT_LOW, "mt9p031_rst");
-		if (ret < 0)
-			goto done;
-
-		mt9p031->reset = pdata->reset;
-	}
+	mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset",
+						 GPIOD_OUT_HIGH);
 
 	ret = mt9p031_clk_setup(mt9p031);
+	if (ret)
+		goto done;
+
+	ret = v4l2_async_register_subdev(&mt9p031->subdev);
 
 done:
 	if (ret < 0) {
 		v4l2_ctrl_handler_free(&mt9p031->ctrls);
 		media_entity_cleanup(&mt9p031->subdev.entity);
+		mutex_destroy(&mt9p031->power_lock);
 	}
 
 	return ret;
@@ -1155,8 +1158,9 @@
 	struct mt9p031 *mt9p031 = to_mt9p031(subdev);
 
 	v4l2_ctrl_handler_free(&mt9p031->ctrls);
-	v4l2_device_unregister_subdev(subdev);
+	v4l2_async_unregister_subdev(subdev);
 	media_entity_cleanup(&subdev->entity);
+	mutex_destroy(&mt9p031->power_lock);
 
 	return 0;
 }
diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c
index d9e9889..8ae99f7 100644
--- a/drivers/media/i2c/mt9t001.c
+++ b/drivers/media/i2c/mt9t001.c
@@ -244,12 +244,12 @@
  */
 
 static struct v4l2_mbus_framefmt *
-__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh,
+__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_pad_config *cfg,
 			 unsigned int pad, enum v4l2_subdev_format_whence which)
 {
 	switch (which) {
 	case V4L2_SUBDEV_FORMAT_TRY:
-		return v4l2_subdev_get_try_format(fh, pad);
+		return v4l2_subdev_get_try_format(&mt9t001->subdev, cfg, pad);
 	case V4L2_SUBDEV_FORMAT_ACTIVE:
 		return &mt9t001->format;
 	default:
@@ -258,12 +258,12 @@
 }
 
 static struct v4l2_rect *
-__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh,
+__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_pad_config *cfg,
 		       unsigned int pad, enum v4l2_subdev_format_whence which)
 {
 	switch (which) {
 	case V4L2_SUBDEV_FORMAT_TRY:
-		return v4l2_subdev_get_try_crop(fh, pad);
+		return v4l2_subdev_get_try_crop(&mt9t001->subdev, cfg, pad);
 	case V4L2_SUBDEV_FORMAT_ACTIVE:
 		return &mt9t001->crop;
 	default:
@@ -327,7 +327,7 @@
 }
 
 static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->index > 0)
@@ -338,7 +338,7 @@
 }
 
 static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
 {
 	if (fse->index >= 8 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
@@ -353,18 +353,18 @@
 }
 
 static int mt9t001_get_format(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *format)
 {
 	struct mt9t001 *mt9t001 = to_mt9t001(subdev);
 
-	format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad,
+	format->format = *__mt9t001_get_pad_format(mt9t001, cfg, format->pad,
 						   format->which);
 	return 0;
 }
 
 static int mt9t001_set_format(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *format)
 {
 	struct mt9t001 *mt9t001 = to_mt9t001(subdev);
@@ -375,7 +375,7 @@
 	unsigned int hratio;
 	unsigned int vratio;
 
-	__crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad,
+	__crop = __mt9t001_get_pad_crop(mt9t001, cfg, format->pad,
 					format->which);
 
 	/* Clamp the width and height to avoid dividing by zero. */
@@ -391,7 +391,7 @@
 	hratio = DIV_ROUND_CLOSEST(__crop->width, width);
 	vratio = DIV_ROUND_CLOSEST(__crop->height, height);
 
-	__format = __mt9t001_get_pad_format(mt9t001, fh, format->pad,
+	__format = __mt9t001_get_pad_format(mt9t001, cfg, format->pad,
 					    format->which);
 	__format->width = __crop->width / hratio;
 	__format->height = __crop->height / vratio;
@@ -401,39 +401,44 @@
 	return 0;
 }
 
-static int mt9t001_get_crop(struct v4l2_subdev *subdev,
-			    struct v4l2_subdev_fh *fh,
-			    struct v4l2_subdev_crop *crop)
+static int mt9t001_get_selection(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_selection *sel)
 {
 	struct mt9t001 *mt9t001 = to_mt9t001(subdev);
 
-	crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad,
-					     crop->which);
+	if (sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	sel->r = *__mt9t001_get_pad_crop(mt9t001, cfg, sel->pad, sel->which);
 	return 0;
 }
 
-static int mt9t001_set_crop(struct v4l2_subdev *subdev,
-			    struct v4l2_subdev_fh *fh,
-			    struct v4l2_subdev_crop *crop)
+static int mt9t001_set_selection(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_selection *sel)
 {
 	struct mt9t001 *mt9t001 = to_mt9t001(subdev);
 	struct v4l2_mbus_framefmt *__format;
 	struct v4l2_rect *__crop;
 	struct v4l2_rect rect;
 
+	if (sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
 	/* Clamp the crop rectangle boundaries and align them to a multiple of 2
 	 * pixels.
 	 */
-	rect.left = clamp(ALIGN(crop->rect.left, 2),
+	rect.left = clamp(ALIGN(sel->r.left, 2),
 			  MT9T001_COLUMN_START_MIN,
 			  MT9T001_COLUMN_START_MAX);
-	rect.top = clamp(ALIGN(crop->rect.top, 2),
+	rect.top = clamp(ALIGN(sel->r.top, 2),
 			 MT9T001_ROW_START_MIN,
 			 MT9T001_ROW_START_MAX);
-	rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2),
+	rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2),
 			     MT9T001_WINDOW_WIDTH_MIN + 1,
 			     MT9T001_WINDOW_WIDTH_MAX + 1);
-	rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2),
+	rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2),
 			      MT9T001_WINDOW_HEIGHT_MIN + 1,
 			      MT9T001_WINDOW_HEIGHT_MAX + 1);
 
@@ -442,20 +447,20 @@
 	rect.height = min_t(unsigned int, rect.height,
 			    MT9T001_PIXEL_ARRAY_HEIGHT - rect.top);
 
-	__crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which);
+	__crop = __mt9t001_get_pad_crop(mt9t001, cfg, sel->pad, sel->which);
 
 	if (rect.width != __crop->width || rect.height != __crop->height) {
 		/* Reset the output image size if the crop rectangle size has
 		 * been modified.
 		 */
-		__format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad,
-						    crop->which);
+		__format = __mt9t001_get_pad_format(mt9t001, cfg, sel->pad,
+						    sel->which);
 		__format->width = rect.width;
 		__format->height = rect.height;
 	}
 
 	*__crop = rect;
-	crop->rect = rect;
+	sel->r = rect;
 
 	return 0;
 }
@@ -785,13 +790,13 @@
 	struct v4l2_mbus_framefmt *format;
 	struct v4l2_rect *crop;
 
-	crop = v4l2_subdev_get_try_crop(fh, 0);
+	crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0);
 	crop->left = MT9T001_COLUMN_START_DEF;
 	crop->top = MT9T001_ROW_START_DEF;
 	crop->width = MT9T001_WINDOW_WIDTH_DEF + 1;
 	crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
 
-	format = v4l2_subdev_get_try_format(fh, 0);
+	format = v4l2_subdev_get_try_format(subdev, fh->pad, 0);
 	format->code = MEDIA_BUS_FMT_SGRBG10_1X10;
 	format->width = MT9T001_WINDOW_WIDTH_DEF + 1;
 	format->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
@@ -819,8 +824,8 @@
 	.enum_frame_size = mt9t001_enum_frame_size,
 	.get_fmt = mt9t001_get_format,
 	.set_fmt = mt9t001_set_format,
-	.get_crop = mt9t001_get_crop,
-	.set_crop = mt9t001_set_crop,
+	.get_selection = mt9t001_get_selection,
+	.set_selection = mt9t001_set_selection,
 };
 
 static struct v4l2_subdev_ops mt9t001_subdev_ops = {
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
index 93687c1..977f400 100644
--- a/drivers/media/i2c/mt9v032.c
+++ b/drivers/media/i2c/mt9v032.c
@@ -17,6 +17,8 @@
 #include <linux/i2c.h>
 #include <linux/log2.h>
 #include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/videodev2.h>
@@ -26,6 +28,7 @@
 #include <media/mt9v032.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
 #include <media/v4l2-subdev.h>
 
 /* The first four rows are black rows. The active area spans 753x481 pixels. */
@@ -371,12 +374,12 @@
  */
 
 static struct v4l2_mbus_framefmt *
-__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh,
+__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg,
 			 unsigned int pad, enum v4l2_subdev_format_whence which)
 {
 	switch (which) {
 	case V4L2_SUBDEV_FORMAT_TRY:
-		return v4l2_subdev_get_try_format(fh, pad);
+		return v4l2_subdev_get_try_format(&mt9v032->subdev, cfg, pad);
 	case V4L2_SUBDEV_FORMAT_ACTIVE:
 		return &mt9v032->format;
 	default:
@@ -385,12 +388,12 @@
 }
 
 static struct v4l2_rect *
-__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh,
+__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg,
 		       unsigned int pad, enum v4l2_subdev_format_whence which)
 {
 	switch (which) {
 	case V4L2_SUBDEV_FORMAT_TRY:
-		return v4l2_subdev_get_try_crop(fh, pad);
+		return v4l2_subdev_get_try_crop(&mt9v032->subdev, cfg, pad);
 	case V4L2_SUBDEV_FORMAT_ACTIVE:
 		return &mt9v032->crop;
 	default:
@@ -448,7 +451,7 @@
 }
 
 static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->index > 0)
@@ -459,7 +462,7 @@
 }
 
 static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
 {
 	if (fse->index >= 3 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
@@ -474,12 +477,12 @@
 }
 
 static int mt9v032_get_format(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *format)
 {
 	struct mt9v032 *mt9v032 = to_mt9v032(subdev);
 
-	format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad,
+	format->format = *__mt9v032_get_pad_format(mt9v032, cfg, format->pad,
 						   format->which);
 	return 0;
 }
@@ -509,7 +512,7 @@
 }
 
 static int mt9v032_set_format(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *format)
 {
 	struct mt9v032 *mt9v032 = to_mt9v032(subdev);
@@ -520,7 +523,7 @@
 	unsigned int hratio;
 	unsigned int vratio;
 
-	__crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad,
+	__crop = __mt9v032_get_pad_crop(mt9v032, cfg, format->pad,
 					format->which);
 
 	/* Clamp the width and height to avoid dividing by zero. */
@@ -536,7 +539,7 @@
 	hratio = mt9v032_calc_ratio(__crop->width, width);
 	vratio = mt9v032_calc_ratio(__crop->height, height);
 
-	__format = __mt9v032_get_pad_format(mt9v032, fh, format->pad,
+	__format = __mt9v032_get_pad_format(mt9v032, cfg, format->pad,
 					    format->which);
 	__format->width = __crop->width / hratio;
 	__format->height = __crop->height / vratio;
@@ -552,39 +555,44 @@
 	return 0;
 }
 
-static int mt9v032_get_crop(struct v4l2_subdev *subdev,
-			    struct v4l2_subdev_fh *fh,
-			    struct v4l2_subdev_crop *crop)
+static int mt9v032_get_selection(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_selection *sel)
 {
 	struct mt9v032 *mt9v032 = to_mt9v032(subdev);
 
-	crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad,
-					     crop->which);
+	if (sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	sel->r = *__mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which);
 	return 0;
 }
 
-static int mt9v032_set_crop(struct v4l2_subdev *subdev,
-			    struct v4l2_subdev_fh *fh,
-			    struct v4l2_subdev_crop *crop)
+static int mt9v032_set_selection(struct v4l2_subdev *subdev,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_selection *sel)
 {
 	struct mt9v032 *mt9v032 = to_mt9v032(subdev);
 	struct v4l2_mbus_framefmt *__format;
 	struct v4l2_rect *__crop;
 	struct v4l2_rect rect;
 
+	if (sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
 	/* Clamp the crop rectangle boundaries and align them to a non multiple
 	 * of 2 pixels to ensure a GRBG Bayer pattern.
 	 */
-	rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1,
+	rect.left = clamp(ALIGN(sel->r.left + 1, 2) - 1,
 			  MT9V032_COLUMN_START_MIN,
 			  MT9V032_COLUMN_START_MAX);
-	rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1,
+	rect.top = clamp(ALIGN(sel->r.top + 1, 2) - 1,
 			 MT9V032_ROW_START_MIN,
 			 MT9V032_ROW_START_MAX);
-	rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2),
+	rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2),
 			     MT9V032_WINDOW_WIDTH_MIN,
 			     MT9V032_WINDOW_WIDTH_MAX);
-	rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2),
+	rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2),
 			      MT9V032_WINDOW_HEIGHT_MIN,
 			      MT9V032_WINDOW_HEIGHT_MAX);
 
@@ -593,17 +601,17 @@
 	rect.height = min_t(unsigned int,
 			    rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top);
 
-	__crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which);
+	__crop = __mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which);
 
 	if (rect.width != __crop->width || rect.height != __crop->height) {
 		/* Reset the output image size if the crop rectangle size has
 		 * been modified.
 		 */
-		__format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad,
-						    crop->which);
+		__format = __mt9v032_get_pad_format(mt9v032, cfg, sel->pad,
+						    sel->which);
 		__format->width = rect.width;
 		__format->height = rect.height;
-		if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
 			mt9v032->hratio = 1;
 			mt9v032->vratio = 1;
 			mt9v032_configure_pixel_rate(mt9v032);
@@ -611,7 +619,7 @@
 	}
 
 	*__crop = rect;
-	crop->rect = rect;
+	sel->r = rect;
 
 	return 0;
 }
@@ -805,13 +813,13 @@
 	struct v4l2_mbus_framefmt *format;
 	struct v4l2_rect *crop;
 
-	crop = v4l2_subdev_get_try_crop(fh, 0);
+	crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0);
 	crop->left = MT9V032_COLUMN_START_DEF;
 	crop->top = MT9V032_ROW_START_DEF;
 	crop->width = MT9V032_WINDOW_WIDTH_DEF;
 	crop->height = MT9V032_WINDOW_HEIGHT_DEF;
 
-	format = v4l2_subdev_get_try_format(fh, 0);
+	format = v4l2_subdev_get_try_format(subdev, fh->pad, 0);
 
 	if (mt9v032->model->color)
 		format->code = MEDIA_BUS_FMT_SGRBG10_1X10;
@@ -844,8 +852,8 @@
 	.enum_frame_size = mt9v032_enum_frame_size,
 	.get_fmt = mt9v032_get_format,
 	.set_fmt = mt9v032_set_format,
-	.get_crop = mt9v032_get_crop,
-	.set_crop = mt9v032_set_crop,
+	.get_selection = mt9v032_get_selection,
+	.set_selection = mt9v032_set_selection,
 };
 
 static struct v4l2_subdev_ops mt9v032_subdev_ops = {
@@ -871,10 +879,58 @@
  * Driver initialization and probing
  */
 
+static struct mt9v032_platform_data *
+mt9v032_get_pdata(struct i2c_client *client)
+{
+	struct mt9v032_platform_data *pdata;
+	struct v4l2_of_endpoint endpoint;
+	struct device_node *np;
+	struct property *prop;
+
+	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
+		return client->dev.platform_data;
+
+	np = of_graph_get_next_endpoint(client->dev.of_node, NULL);
+	if (!np)
+		return NULL;
+
+	if (v4l2_of_parse_endpoint(np, &endpoint) < 0)
+		goto done;
+
+	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		goto done;
+
+	prop = of_find_property(np, "link-frequencies", NULL);
+	if (prop) {
+		u64 *link_freqs;
+		size_t size = prop->length / sizeof(*link_freqs);
+
+		link_freqs = devm_kcalloc(&client->dev, size,
+					  sizeof(*link_freqs), GFP_KERNEL);
+		if (!link_freqs)
+			goto done;
+
+		if (of_property_read_u64_array(np, "link-frequencies",
+					       link_freqs, size) < 0)
+			goto done;
+
+		pdata->link_freqs = link_freqs;
+		pdata->link_def_freq = link_freqs[0];
+	}
+
+	pdata->clk_pol = !!(endpoint.bus.parallel.flags &
+			    V4L2_MBUS_PCLK_SAMPLE_RISING);
+
+done:
+	of_node_put(np);
+	return pdata;
+}
+
 static int mt9v032_probe(struct i2c_client *client,
 		const struct i2c_device_id *did)
 {
-	struct mt9v032_platform_data *pdata = client->dev.platform_data;
+	struct mt9v032_platform_data *pdata = mt9v032_get_pdata(client);
 	struct mt9v032 *mt9v032;
 	unsigned int i;
 	int ret;
@@ -956,9 +1012,12 @@
 
 	mt9v032->subdev.ctrl_handler = &mt9v032->ctrls;
 
-	if (mt9v032->ctrls.error)
-		printk(KERN_INFO "%s: control initialization error %d\n",
-		       __func__, mt9v032->ctrls.error);
+	if (mt9v032->ctrls.error) {
+		dev_err(&client->dev, "control initialization error %d\n",
+			mt9v032->ctrls.error);
+		ret = mt9v032->ctrls.error;
+		goto err;
+	}
 
 	mt9v032->crop.left = MT9V032_COLUMN_START_DEF;
 	mt9v032->crop.top = MT9V032_ROW_START_DEF;
@@ -1011,7 +1070,6 @@
 
 	v4l2_async_unregister_subdev(subdev);
 	v4l2_ctrl_handler_free(&mt9v032->ctrls);
-	v4l2_device_unregister_subdev(subdev);
 	media_entity_cleanup(&subdev->entity);
 
 	return 0;
@@ -1030,9 +1088,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, mt9v032_id);
 
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id mt9v032_of_match[] = {
+	{ .compatible = "aptina,mt9v022" },
+	{ .compatible = "aptina,mt9v022m" },
+	{ .compatible = "aptina,mt9v024" },
+	{ .compatible = "aptina,mt9v024m" },
+	{ .compatible = "aptina,mt9v032" },
+	{ .compatible = "aptina,mt9v032m" },
+	{ .compatible = "aptina,mt9v034" },
+	{ .compatible = "aptina,mt9v034m" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mt9v032_of_match);
+#endif
+
 static struct i2c_driver mt9v032_driver = {
 	.driver = {
 		.name = "mt9v032",
+		.of_match_table = of_match_ptr(mt9v032_of_match),
 	},
 	.probe		= mt9v032_probe,
 	.remove		= mt9v032_remove,
diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c
index 00c7b26..f197b6c 100644
--- a/drivers/media/i2c/noon010pc30.c
+++ b/drivers/media/i2c/noon010pc30.c
@@ -492,7 +492,7 @@
 }
 
 static int noon010_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->index >= ARRAY_SIZE(noon010_formats))
@@ -502,15 +502,16 @@
 	return 0;
 }
 
-static int noon010_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int noon010_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct noon010_info *info = to_noon010(sd);
 	struct v4l2_mbus_framefmt *mf;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		if (fh) {
-			mf = v4l2_subdev_get_try_format(fh, 0);
+		if (cfg) {
+			mf = v4l2_subdev_get_try_format(sd, cfg, 0);
 			fmt->format = *mf;
 		}
 		return 0;
@@ -542,7 +543,7 @@
 	return &noon010_formats[i];
 }
 
-static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct noon010_info *info = to_noon010(sd);
@@ -557,8 +558,8 @@
 	fmt->format.field = V4L2_FIELD_NONE;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		if (fh) {
-			mf = v4l2_subdev_get_try_format(fh, 0);
+		if (cfg) {
+			mf = v4l2_subdev_get_try_format(sd, cfg, 0);
 			*mf = fmt->format;
 		}
 		return 0;
@@ -640,7 +641,7 @@
 
 static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
-	struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0);
+	struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, fh->pad, 0);
 
 	mf->width = noon010_sizes[0].width;
 	mf->height = noon010_sizes[0].height;
diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
new file mode 100644
index 0000000..87855c0
--- /dev/null
+++ b/drivers/media/i2c/ov2659.c
@@ -0,0 +1,1509 @@
+/*
+ * Omnivision OV2659 CMOS Image Sensor driver
+ *
+ * Copyright (C) 2015 Texas Instruments, Inc.
+ *
+ * Benoit Parrot <bparrot@ti.com>
+ * Lad, Prabhakar <prabhakar.csengg@gmail.com>
+ *
+ * This program is free software; you may 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/media.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+
+#include <media/media-entity.h>
+#include <media/ov2659.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-subdev.h>
+
+#define DRIVER_NAME "ov2659"
+
+/*
+ * OV2659 register definitions
+ */
+#define REG_SOFTWARE_STANDBY		0x0100
+#define REG_SOFTWARE_RESET		0x0103
+#define REG_IO_CTRL00			0x3000
+#define REG_IO_CTRL01			0x3001
+#define REG_IO_CTRL02			0x3002
+#define REG_OUTPUT_VALUE00		0x3008
+#define REG_OUTPUT_VALUE01		0x3009
+#define REG_OUTPUT_VALUE02		0x300d
+#define REG_OUTPUT_SELECT00		0x300e
+#define REG_OUTPUT_SELECT01		0x300f
+#define REG_OUTPUT_SELECT02		0x3010
+#define REG_OUTPUT_DRIVE		0x3011
+#define REG_INPUT_READOUT00		0x302d
+#define REG_INPUT_READOUT01		0x302e
+#define REG_INPUT_READOUT02		0x302f
+
+#define REG_SC_PLL_CTRL0		0x3003
+#define REG_SC_PLL_CTRL1		0x3004
+#define REG_SC_PLL_CTRL2		0x3005
+#define REG_SC_PLL_CTRL3		0x3006
+#define REG_SC_CHIP_ID_H		0x300a
+#define REG_SC_CHIP_ID_L		0x300b
+#define REG_SC_PWC			0x3014
+#define REG_SC_CLKRST0			0x301a
+#define REG_SC_CLKRST1			0x301b
+#define REG_SC_CLKRST2			0x301c
+#define REG_SC_CLKRST3			0x301d
+#define REG_SC_SUB_ID			0x302a
+#define REG_SC_SCCB_ID			0x302b
+
+#define REG_GROUP_ADDRESS_00		0x3200
+#define REG_GROUP_ADDRESS_01		0x3201
+#define REG_GROUP_ADDRESS_02		0x3202
+#define REG_GROUP_ADDRESS_03		0x3203
+#define REG_GROUP_ACCESS		0x3208
+
+#define REG_AWB_R_GAIN_H		0x3400
+#define REG_AWB_R_GAIN_L		0x3401
+#define REG_AWB_G_GAIN_H		0x3402
+#define REG_AWB_G_GAIN_L		0x3403
+#define REG_AWB_B_GAIN_H		0x3404
+#define REG_AWB_B_GAIN_L		0x3405
+#define REG_AWB_MANUAL_CONTROL		0x3406
+
+#define REG_TIMING_HS_H			0x3800
+#define REG_TIMING_HS_L			0x3801
+#define REG_TIMING_VS_H			0x3802
+#define REG_TIMING_VS_L			0x3803
+#define REG_TIMING_HW_H			0x3804
+#define REG_TIMING_HW_L			0x3805
+#define REG_TIMING_VH_H			0x3806
+#define REG_TIMING_VH_L			0x3807
+#define REG_TIMING_DVPHO_H		0x3808
+#define REG_TIMING_DVPHO_L		0x3809
+#define REG_TIMING_DVPVO_H		0x380a
+#define REG_TIMING_DVPVO_L		0x380b
+#define REG_TIMING_HTS_H		0x380c
+#define REG_TIMING_HTS_L		0x380d
+#define REG_TIMING_VTS_H		0x380e
+#define REG_TIMING_VTS_L		0x380f
+#define REG_TIMING_HOFFS_H		0x3810
+#define REG_TIMING_HOFFS_L		0x3811
+#define REG_TIMING_VOFFS_H		0x3812
+#define REG_TIMING_VOFFS_L		0x3813
+#define REG_TIMING_XINC			0x3814
+#define REG_TIMING_YINC			0x3815
+#define REG_TIMING_VERT_FORMAT		0x3820
+#define REG_TIMING_HORIZ_FORMAT		0x3821
+
+#define REG_FORMAT_CTRL00		0x4300
+
+#define REG_VFIFO_READ_START_H		0x4608
+#define REG_VFIFO_READ_START_L		0x4609
+
+#define REG_DVP_CTRL02			0x4708
+
+#define REG_ISP_CTRL00			0x5000
+#define REG_ISP_CTRL01			0x5001
+#define REG_ISP_CTRL02			0x5002
+
+#define REG_LENC_RED_X0_H		0x500c
+#define REG_LENC_RED_X0_L		0x500d
+#define REG_LENC_RED_Y0_H		0x500e
+#define REG_LENC_RED_Y0_L		0x500f
+#define REG_LENC_RED_A1			0x5010
+#define REG_LENC_RED_B1			0x5011
+#define REG_LENC_RED_A2_B2		0x5012
+#define REG_LENC_GREEN_X0_H		0x5013
+#define REG_LENC_GREEN_X0_L		0x5014
+#define REG_LENC_GREEN_Y0_H		0x5015
+#define REG_LENC_GREEN_Y0_L		0x5016
+#define REG_LENC_GREEN_A1		0x5017
+#define REG_LENC_GREEN_B1		0x5018
+#define REG_LENC_GREEN_A2_B2		0x5019
+#define REG_LENC_BLUE_X0_H		0x501a
+#define REG_LENC_BLUE_X0_L		0x501b
+#define REG_LENC_BLUE_Y0_H		0x501c
+#define REG_LENC_BLUE_Y0_L		0x501d
+#define REG_LENC_BLUE_A1		0x501e
+#define REG_LENC_BLUE_B1		0x501f
+#define REG_LENC_BLUE_A2_B2		0x5020
+
+#define REG_AWB_CTRL00			0x5035
+#define REG_AWB_CTRL01			0x5036
+#define REG_AWB_CTRL02			0x5037
+#define REG_AWB_CTRL03			0x5038
+#define REG_AWB_CTRL04			0x5039
+#define REG_AWB_LOCAL_LIMIT		0x503a
+#define REG_AWB_CTRL12			0x5049
+#define REG_AWB_CTRL13			0x504a
+#define REG_AWB_CTRL14			0x504b
+
+#define REG_SHARPENMT_THRESH1		0x5064
+#define REG_SHARPENMT_THRESH2		0x5065
+#define REG_SHARPENMT_OFFSET1		0x5066
+#define REG_SHARPENMT_OFFSET2		0x5067
+#define REG_DENOISE_THRESH1		0x5068
+#define REG_DENOISE_THRESH2		0x5069
+#define REG_DENOISE_OFFSET1		0x506a
+#define REG_DENOISE_OFFSET2		0x506b
+#define REG_SHARPEN_THRESH1		0x506c
+#define REG_SHARPEN_THRESH2		0x506d
+#define REG_CIP_CTRL00			0x506e
+#define REG_CIP_CTRL01			0x506f
+
+#define REG_CMX_SIGN			0x5079
+#define REG_CMX_MISC_CTRL		0x507a
+
+#define REG_PRE_ISP_CTRL00		0x50a0
+#define TEST_PATTERN_ENABLE		BIT(7)
+#define VERTICAL_COLOR_BAR_MASK		0x53
+
+#define REG_NULL			0x0000	/* Array end token */
+
+#define OV265X_ID(_msb, _lsb)		((_msb) << 8 | (_lsb))
+#define OV2659_ID			0x2656
+
+struct sensor_register {
+	u16 addr;
+	u8 value;
+};
+
+struct ov2659_framesize {
+	u16 width;
+	u16 height;
+	u16 max_exp_lines;
+	const struct sensor_register *regs;
+};
+
+struct ov2659_pll_ctrl {
+	u8 ctrl1;
+	u8 ctrl2;
+	u8 ctrl3;
+};
+
+struct ov2659_pixfmt {
+	u32 code;
+	/* Output format Register Value (REG_FORMAT_CTRL00) */
+	struct sensor_register *format_ctrl_regs;
+};
+
+struct pll_ctrl_reg {
+	unsigned int div;
+	unsigned char reg;
+};
+
+struct ov2659 {
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+	struct v4l2_mbus_framefmt format;
+	unsigned int xvclk_frequency;
+	const struct ov2659_platform_data *pdata;
+	struct mutex lock;
+	struct i2c_client *client;
+	struct v4l2_ctrl_handler ctrls;
+	struct v4l2_ctrl *link_frequency;
+	const struct ov2659_framesize *frame_size;
+	struct sensor_register *format_ctrl_regs;
+	struct ov2659_pll_ctrl pll;
+	int streaming;
+};
+
+static const struct sensor_register ov2659_init_regs[] = {
+	{ REG_IO_CTRL00, 0x03 },
+	{ REG_IO_CTRL01, 0xff },
+	{ REG_IO_CTRL02, 0xe0 },
+	{ 0x3633, 0x3d },
+	{ 0x3620, 0x02 },
+	{ 0x3631, 0x11 },
+	{ 0x3612, 0x04 },
+	{ 0x3630, 0x20 },
+	{ 0x4702, 0x02 },
+	{ 0x370c, 0x34 },
+	{ REG_TIMING_HS_H, 0x00 },
+	{ REG_TIMING_HS_L, 0x00 },
+	{ REG_TIMING_VS_H, 0x00 },
+	{ REG_TIMING_VS_L, 0x00 },
+	{ REG_TIMING_HW_H, 0x06 },
+	{ REG_TIMING_HW_L, 0x5f },
+	{ REG_TIMING_VH_H, 0x04 },
+	{ REG_TIMING_VH_L, 0xb7 },
+	{ REG_TIMING_DVPHO_H, 0x03 },
+	{ REG_TIMING_DVPHO_L, 0x20 },
+	{ REG_TIMING_DVPVO_H, 0x02 },
+	{ REG_TIMING_DVPVO_L, 0x58 },
+	{ REG_TIMING_HTS_H, 0x05 },
+	{ REG_TIMING_HTS_L, 0x14 },
+	{ REG_TIMING_VTS_H, 0x02 },
+	{ REG_TIMING_VTS_L, 0x68 },
+	{ REG_TIMING_HOFFS_L, 0x08 },
+	{ REG_TIMING_VOFFS_L, 0x02 },
+	{ REG_TIMING_XINC, 0x31 },
+	{ REG_TIMING_YINC, 0x31 },
+	{ 0x3a02, 0x02 },
+	{ 0x3a03, 0x68 },
+	{ 0x3a08, 0x00 },
+	{ 0x3a09, 0x5c },
+	{ 0x3a0a, 0x00 },
+	{ 0x3a0b, 0x4d },
+	{ 0x3a0d, 0x08 },
+	{ 0x3a0e, 0x06 },
+	{ 0x3a14, 0x02 },
+	{ 0x3a15, 0x28 },
+	{ REG_DVP_CTRL02, 0x01 },
+	{ 0x3623, 0x00 },
+	{ 0x3634, 0x76 },
+	{ 0x3701, 0x44 },
+	{ 0x3702, 0x18 },
+	{ 0x3703, 0x24 },
+	{ 0x3704, 0x24 },
+	{ 0x3705, 0x0c },
+	{ REG_TIMING_VERT_FORMAT, 0x81 },
+	{ REG_TIMING_HORIZ_FORMAT, 0x01 },
+	{ 0x370a, 0x52 },
+	{ REG_VFIFO_READ_START_H, 0x00 },
+	{ REG_VFIFO_READ_START_L, 0x80 },
+	{ REG_FORMAT_CTRL00, 0x30 },
+	{ 0x5086, 0x02 },
+	{ REG_ISP_CTRL00, 0xfb },
+	{ REG_ISP_CTRL01, 0x1f },
+	{ REG_ISP_CTRL02, 0x00 },
+	{ 0x5025, 0x0e },
+	{ 0x5026, 0x18 },
+	{ 0x5027, 0x34 },
+	{ 0x5028, 0x4c },
+	{ 0x5029, 0x62 },
+	{ 0x502a, 0x74 },
+	{ 0x502b, 0x85 },
+	{ 0x502c, 0x92 },
+	{ 0x502d, 0x9e },
+	{ 0x502e, 0xb2 },
+	{ 0x502f, 0xc0 },
+	{ 0x5030, 0xcc },
+	{ 0x5031, 0xe0 },
+	{ 0x5032, 0xee },
+	{ 0x5033, 0xf6 },
+	{ 0x5034, 0x11 },
+	{ 0x5070, 0x1c },
+	{ 0x5071, 0x5b },
+	{ 0x5072, 0x05 },
+	{ 0x5073, 0x20 },
+	{ 0x5074, 0x94 },
+	{ 0x5075, 0xb4 },
+	{ 0x5076, 0xb4 },
+	{ 0x5077, 0xaf },
+	{ 0x5078, 0x05 },
+	{ REG_CMX_SIGN, 0x98 },
+	{ REG_CMX_MISC_CTRL, 0x21 },
+	{ REG_AWB_CTRL00, 0x6a },
+	{ REG_AWB_CTRL01, 0x11 },
+	{ REG_AWB_CTRL02, 0x92 },
+	{ REG_AWB_CTRL03, 0x21 },
+	{ REG_AWB_CTRL04, 0xe1 },
+	{ REG_AWB_LOCAL_LIMIT, 0x01 },
+	{ 0x503c, 0x05 },
+	{ 0x503d, 0x08 },
+	{ 0x503e, 0x08 },
+	{ 0x503f, 0x64 },
+	{ 0x5040, 0x58 },
+	{ 0x5041, 0x2a },
+	{ 0x5042, 0xc5 },
+	{ 0x5043, 0x2e },
+	{ 0x5044, 0x3a },
+	{ 0x5045, 0x3c },
+	{ 0x5046, 0x44 },
+	{ 0x5047, 0xf8 },
+	{ 0x5048, 0x08 },
+	{ REG_AWB_CTRL12, 0x70 },
+	{ REG_AWB_CTRL13, 0xf0 },
+	{ REG_AWB_CTRL14, 0xf0 },
+	{ REG_LENC_RED_X0_H, 0x03 },
+	{ REG_LENC_RED_X0_L, 0x20 },
+	{ REG_LENC_RED_Y0_H, 0x02 },
+	{ REG_LENC_RED_Y0_L, 0x5c },
+	{ REG_LENC_RED_A1, 0x48 },
+	{ REG_LENC_RED_B1, 0x00 },
+	{ REG_LENC_RED_A2_B2, 0x66 },
+	{ REG_LENC_GREEN_X0_H, 0x03 },
+	{ REG_LENC_GREEN_X0_L, 0x30 },
+	{ REG_LENC_GREEN_Y0_H, 0x02 },
+	{ REG_LENC_GREEN_Y0_L, 0x7c },
+	{ REG_LENC_GREEN_A1, 0x40 },
+	{ REG_LENC_GREEN_B1, 0x00 },
+	{ REG_LENC_GREEN_A2_B2, 0x66 },
+	{ REG_LENC_BLUE_X0_H, 0x03 },
+	{ REG_LENC_BLUE_X0_L, 0x10 },
+	{ REG_LENC_BLUE_Y0_H, 0x02 },
+	{ REG_LENC_BLUE_Y0_L, 0x7c },
+	{ REG_LENC_BLUE_A1, 0x3a },
+	{ REG_LENC_BLUE_B1, 0x00 },
+	{ REG_LENC_BLUE_A2_B2, 0x66 },
+	{ REG_CIP_CTRL00, 0x44 },
+	{ REG_SHARPENMT_THRESH1, 0x08 },
+	{ REG_SHARPENMT_THRESH2, 0x10 },
+	{ REG_SHARPENMT_OFFSET1, 0x12 },
+	{ REG_SHARPENMT_OFFSET2, 0x02 },
+	{ REG_SHARPEN_THRESH1, 0x08 },
+	{ REG_SHARPEN_THRESH2, 0x10 },
+	{ REG_CIP_CTRL01, 0xa6 },
+	{ REG_DENOISE_THRESH1, 0x08 },
+	{ REG_DENOISE_THRESH2, 0x10 },
+	{ REG_DENOISE_OFFSET1, 0x04 },
+	{ REG_DENOISE_OFFSET2, 0x12 },
+	{ 0x507e, 0x40 },
+	{ 0x507f, 0x20 },
+	{ 0x507b, 0x02 },
+	{ REG_CMX_MISC_CTRL, 0x01 },
+	{ 0x5084, 0x0c },
+	{ 0x5085, 0x3e },
+	{ 0x5005, 0x80 },
+	{ 0x3a0f, 0x30 },
+	{ 0x3a10, 0x28 },
+	{ 0x3a1b, 0x32 },
+	{ 0x3a1e, 0x26 },
+	{ 0x3a11, 0x60 },
+	{ 0x3a1f, 0x14 },
+	{ 0x5060, 0x69 },
+	{ 0x5061, 0x7d },
+	{ 0x5062, 0x7d },
+	{ 0x5063, 0x69 },
+	{ REG_NULL, 0x00 },
+};
+
+/* 1280X720 720p */
+static struct sensor_register ov2659_720p[] = {
+	{ REG_TIMING_HS_H, 0x00 },
+	{ REG_TIMING_HS_L, 0xa0 },
+	{ REG_TIMING_VS_H, 0x00 },
+	{ REG_TIMING_VS_L, 0xf0 },
+	{ REG_TIMING_HW_H, 0x05 },
+	{ REG_TIMING_HW_L, 0xbf },
+	{ REG_TIMING_VH_H, 0x03 },
+	{ REG_TIMING_VH_L, 0xcb },
+	{ REG_TIMING_DVPHO_H, 0x05 },
+	{ REG_TIMING_DVPHO_L, 0x00 },
+	{ REG_TIMING_DVPVO_H, 0x02 },
+	{ REG_TIMING_DVPVO_L, 0xd0 },
+	{ REG_TIMING_HTS_H, 0x06 },
+	{ REG_TIMING_HTS_L, 0x4c },
+	{ REG_TIMING_VTS_H, 0x02 },
+	{ REG_TIMING_VTS_L, 0xe8 },
+	{ REG_TIMING_HOFFS_L, 0x10 },
+	{ REG_TIMING_VOFFS_L, 0x06 },
+	{ REG_TIMING_XINC, 0x11 },
+	{ REG_TIMING_YINC, 0x11 },
+	{ REG_TIMING_VERT_FORMAT, 0x80 },
+	{ REG_TIMING_HORIZ_FORMAT, 0x00 },
+	{ 0x3a03, 0xe8 },
+	{ 0x3a09, 0x6f },
+	{ 0x3a0b, 0x5d },
+	{ 0x3a15, 0x9a },
+	{ REG_NULL, 0x00 },
+};
+
+/* 1600X1200 UXGA */
+static struct sensor_register ov2659_uxga[] = {
+	{ REG_TIMING_HS_H, 0x00 },
+	{ REG_TIMING_HS_L, 0x00 },
+	{ REG_TIMING_VS_H, 0x00 },
+	{ REG_TIMING_VS_L, 0x00 },
+	{ REG_TIMING_HW_H, 0x06 },
+	{ REG_TIMING_HW_L, 0x5f },
+	{ REG_TIMING_VH_H, 0x04 },
+	{ REG_TIMING_VH_L, 0xbb },
+	{ REG_TIMING_DVPHO_H, 0x06 },
+	{ REG_TIMING_DVPHO_L, 0x40 },
+	{ REG_TIMING_DVPVO_H, 0x04 },
+	{ REG_TIMING_DVPVO_L, 0xb0 },
+	{ REG_TIMING_HTS_H, 0x07 },
+	{ REG_TIMING_HTS_L, 0x9f },
+	{ REG_TIMING_VTS_H, 0x04 },
+	{ REG_TIMING_VTS_L, 0xd0 },
+	{ REG_TIMING_HOFFS_L, 0x10 },
+	{ REG_TIMING_VOFFS_L, 0x06 },
+	{ REG_TIMING_XINC, 0x11 },
+	{ REG_TIMING_YINC, 0x11 },
+	{ 0x3a02, 0x04 },
+	{ 0x3a03, 0xd0 },
+	{ 0x3a08, 0x00 },
+	{ 0x3a09, 0xb8 },
+	{ 0x3a0a, 0x00 },
+	{ 0x3a0b, 0x9a },
+	{ 0x3a0d, 0x08 },
+	{ 0x3a0e, 0x06 },
+	{ 0x3a14, 0x04 },
+	{ 0x3a15, 0x50 },
+	{ 0x3623, 0x00 },
+	{ 0x3634, 0x44 },
+	{ 0x3701, 0x44 },
+	{ 0x3702, 0x30 },
+	{ 0x3703, 0x48 },
+	{ 0x3704, 0x48 },
+	{ 0x3705, 0x18 },
+	{ REG_TIMING_VERT_FORMAT, 0x80 },
+	{ REG_TIMING_HORIZ_FORMAT, 0x00 },
+	{ 0x370a, 0x12 },
+	{ REG_VFIFO_READ_START_H, 0x00 },
+	{ REG_VFIFO_READ_START_L, 0x80 },
+	{ REG_ISP_CTRL02, 0x00 },
+	{ REG_NULL, 0x00 },
+};
+
+/* 1280X1024 SXGA */
+static struct sensor_register ov2659_sxga[] = {
+	{ REG_TIMING_HS_H, 0x00 },
+	{ REG_TIMING_HS_L, 0x00 },
+	{ REG_TIMING_VS_H, 0x00 },
+	{ REG_TIMING_VS_L, 0x00 },
+	{ REG_TIMING_HW_H, 0x06 },
+	{ REG_TIMING_HW_L, 0x5f },
+	{ REG_TIMING_VH_H, 0x04 },
+	{ REG_TIMING_VH_L, 0xb7 },
+	{ REG_TIMING_DVPHO_H, 0x05 },
+	{ REG_TIMING_DVPHO_L, 0x00 },
+	{ REG_TIMING_DVPVO_H, 0x04 },
+	{ REG_TIMING_DVPVO_L, 0x00 },
+	{ REG_TIMING_HTS_H, 0x07 },
+	{ REG_TIMING_HTS_L, 0x9c },
+	{ REG_TIMING_VTS_H, 0x04 },
+	{ REG_TIMING_VTS_L, 0xd0 },
+	{ REG_TIMING_HOFFS_L, 0x10 },
+	{ REG_TIMING_VOFFS_L, 0x06 },
+	{ REG_TIMING_XINC, 0x11 },
+	{ REG_TIMING_YINC, 0x11 },
+	{ 0x3a02, 0x02 },
+	{ 0x3a03, 0x68 },
+	{ 0x3a08, 0x00 },
+	{ 0x3a09, 0x5c },
+	{ 0x3a0a, 0x00 },
+	{ 0x3a0b, 0x4d },
+	{ 0x3a0d, 0x08 },
+	{ 0x3a0e, 0x06 },
+	{ 0x3a14, 0x02 },
+	{ 0x3a15, 0x28 },
+	{ 0x3623, 0x00 },
+	{ 0x3634, 0x76 },
+	{ 0x3701, 0x44 },
+	{ 0x3702, 0x18 },
+	{ 0x3703, 0x24 },
+	{ 0x3704, 0x24 },
+	{ 0x3705, 0x0c },
+	{ REG_TIMING_VERT_FORMAT, 0x80 },
+	{ REG_TIMING_HORIZ_FORMAT, 0x00 },
+	{ 0x370a, 0x52 },
+	{ REG_VFIFO_READ_START_H, 0x00 },
+	{ REG_VFIFO_READ_START_L, 0x80 },
+	{ REG_ISP_CTRL02, 0x00 },
+	{ REG_NULL, 0x00 },
+};
+
+/* 1024X768 SXGA */
+static struct sensor_register ov2659_xga[] = {
+	{ REG_TIMING_HS_H, 0x00 },
+	{ REG_TIMING_HS_L, 0x00 },
+	{ REG_TIMING_VS_H, 0x00 },
+	{ REG_TIMING_VS_L, 0x00 },
+	{ REG_TIMING_HW_H, 0x06 },
+	{ REG_TIMING_HW_L, 0x5f },
+	{ REG_TIMING_VH_H, 0x04 },
+	{ REG_TIMING_VH_L, 0xb7 },
+	{ REG_TIMING_DVPHO_H, 0x04 },
+	{ REG_TIMING_DVPHO_L, 0x00 },
+	{ REG_TIMING_DVPVO_H, 0x03 },
+	{ REG_TIMING_DVPVO_L, 0x00 },
+	{ REG_TIMING_HTS_H, 0x07 },
+	{ REG_TIMING_HTS_L, 0x9c },
+	{ REG_TIMING_VTS_H, 0x04 },
+	{ REG_TIMING_VTS_L, 0xd0 },
+	{ REG_TIMING_HOFFS_L, 0x10 },
+	{ REG_TIMING_VOFFS_L, 0x06 },
+	{ REG_TIMING_XINC, 0x11 },
+	{ REG_TIMING_YINC, 0x11 },
+	{ 0x3a02, 0x02 },
+	{ 0x3a03, 0x68 },
+	{ 0x3a08, 0x00 },
+	{ 0x3a09, 0x5c },
+	{ 0x3a0a, 0x00 },
+	{ 0x3a0b, 0x4d },
+	{ 0x3a0d, 0x08 },
+	{ 0x3a0e, 0x06 },
+	{ 0x3a14, 0x02 },
+	{ 0x3a15, 0x28 },
+	{ 0x3623, 0x00 },
+	{ 0x3634, 0x76 },
+	{ 0x3701, 0x44 },
+	{ 0x3702, 0x18 },
+	{ 0x3703, 0x24 },
+	{ 0x3704, 0x24 },
+	{ 0x3705, 0x0c },
+	{ REG_TIMING_VERT_FORMAT, 0x80 },
+	{ REG_TIMING_HORIZ_FORMAT, 0x00 },
+	{ 0x370a, 0x52 },
+	{ REG_VFIFO_READ_START_H, 0x00 },
+	{ REG_VFIFO_READ_START_L, 0x80 },
+	{ REG_ISP_CTRL02, 0x00 },
+	{ REG_NULL, 0x00 },
+};
+
+/* 800X600 SVGA */
+static struct sensor_register ov2659_svga[] = {
+	{ REG_TIMING_HS_H, 0x00 },
+	{ REG_TIMING_HS_L, 0x00 },
+	{ REG_TIMING_VS_H, 0x00 },
+	{ REG_TIMING_VS_L, 0x00 },
+	{ REG_TIMING_HW_H, 0x06 },
+	{ REG_TIMING_HW_L, 0x5f },
+	{ REG_TIMING_VH_H, 0x04 },
+	{ REG_TIMING_VH_L, 0xb7 },
+	{ REG_TIMING_DVPHO_H, 0x03 },
+	{ REG_TIMING_DVPHO_L, 0x20 },
+	{ REG_TIMING_DVPVO_H, 0x02 },
+	{ REG_TIMING_DVPVO_L, 0x58 },
+	{ REG_TIMING_HTS_H, 0x05 },
+	{ REG_TIMING_HTS_L, 0x14 },
+	{ REG_TIMING_VTS_H, 0x02 },
+	{ REG_TIMING_VTS_L, 0x68 },
+	{ REG_TIMING_HOFFS_L, 0x08 },
+	{ REG_TIMING_VOFFS_L, 0x02 },
+	{ REG_TIMING_XINC, 0x31 },
+	{ REG_TIMING_YINC, 0x31 },
+	{ 0x3a02, 0x02 },
+	{ 0x3a03, 0x68 },
+	{ 0x3a08, 0x00 },
+	{ 0x3a09, 0x5c },
+	{ 0x3a0a, 0x00 },
+	{ 0x3a0b, 0x4d },
+	{ 0x3a0d, 0x08 },
+	{ 0x3a0e, 0x06 },
+	{ 0x3a14, 0x02 },
+	{ 0x3a15, 0x28 },
+	{ 0x3623, 0x00 },
+	{ 0x3634, 0x76 },
+	{ 0x3701, 0x44 },
+	{ 0x3702, 0x18 },
+	{ 0x3703, 0x24 },
+	{ 0x3704, 0x24 },
+	{ 0x3705, 0x0c },
+	{ REG_TIMING_VERT_FORMAT, 0x81 },
+	{ REG_TIMING_HORIZ_FORMAT, 0x01 },
+	{ 0x370a, 0x52 },
+	{ REG_VFIFO_READ_START_H, 0x00 },
+	{ REG_VFIFO_READ_START_L, 0x80 },
+	{ REG_ISP_CTRL02, 0x00 },
+	{ REG_NULL, 0x00 },
+};
+
+/* 640X480 VGA */
+static struct sensor_register ov2659_vga[] = {
+	{ REG_TIMING_HS_H, 0x00 },
+	{ REG_TIMING_HS_L, 0x00 },
+	{ REG_TIMING_VS_H, 0x00 },
+	{ REG_TIMING_VS_L, 0x00 },
+	{ REG_TIMING_HW_H, 0x06 },
+	{ REG_TIMING_HW_L, 0x5f },
+	{ REG_TIMING_VH_H, 0x04 },
+	{ REG_TIMING_VH_L, 0xb7 },
+	{ REG_TIMING_DVPHO_H, 0x02 },
+	{ REG_TIMING_DVPHO_L, 0x80 },
+	{ REG_TIMING_DVPVO_H, 0x01 },
+	{ REG_TIMING_DVPVO_L, 0xe0 },
+	{ REG_TIMING_HTS_H, 0x05 },
+	{ REG_TIMING_HTS_L, 0x14 },
+	{ REG_TIMING_VTS_H, 0x02 },
+	{ REG_TIMING_VTS_L, 0x68 },
+	{ REG_TIMING_HOFFS_L, 0x08 },
+	{ REG_TIMING_VOFFS_L, 0x02 },
+	{ REG_TIMING_XINC, 0x31 },
+	{ REG_TIMING_YINC, 0x31 },
+	{ 0x3a02, 0x02 },
+	{ 0x3a03, 0x68 },
+	{ 0x3a08, 0x00 },
+	{ 0x3a09, 0x5c },
+	{ 0x3a0a, 0x00 },
+	{ 0x3a0b, 0x4d },
+	{ 0x3a0d, 0x08 },
+	{ 0x3a0e, 0x06 },
+	{ 0x3a14, 0x02 },
+	{ 0x3a15, 0x28 },
+	{ 0x3623, 0x00 },
+	{ 0x3634, 0x76 },
+	{ 0x3701, 0x44 },
+	{ 0x3702, 0x18 },
+	{ 0x3703, 0x24 },
+	{ 0x3704, 0x24 },
+	{ 0x3705, 0x0c },
+	{ REG_TIMING_VERT_FORMAT, 0x81 },
+	{ REG_TIMING_HORIZ_FORMAT, 0x01 },
+	{ 0x370a, 0x52 },
+	{ REG_VFIFO_READ_START_H, 0x00 },
+	{ REG_VFIFO_READ_START_L, 0x80 },
+	{ REG_ISP_CTRL02, 0x10 },
+	{ REG_NULL, 0x00 },
+};
+
+/* 320X240 QVGA */
+static  struct sensor_register ov2659_qvga[] = {
+	{ REG_TIMING_HS_H, 0x00 },
+	{ REG_TIMING_HS_L, 0x00 },
+	{ REG_TIMING_VS_H, 0x00 },
+	{ REG_TIMING_VS_L, 0x00 },
+	{ REG_TIMING_HW_H, 0x06 },
+	{ REG_TIMING_HW_L, 0x5f },
+	{ REG_TIMING_VH_H, 0x04 },
+	{ REG_TIMING_VH_L, 0xb7 },
+	{ REG_TIMING_DVPHO_H, 0x01 },
+	{ REG_TIMING_DVPHO_L, 0x40 },
+	{ REG_TIMING_DVPVO_H, 0x00 },
+	{ REG_TIMING_DVPVO_L, 0xf0 },
+	{ REG_TIMING_HTS_H, 0x05 },
+	{ REG_TIMING_HTS_L, 0x14 },
+	{ REG_TIMING_VTS_H, 0x02 },
+	{ REG_TIMING_VTS_L, 0x68 },
+	{ REG_TIMING_HOFFS_L, 0x08 },
+	{ REG_TIMING_VOFFS_L, 0x02 },
+	{ REG_TIMING_XINC, 0x31 },
+	{ REG_TIMING_YINC, 0x31 },
+	{ 0x3a02, 0x02 },
+	{ 0x3a03, 0x68 },
+	{ 0x3a08, 0x00 },
+	{ 0x3a09, 0x5c },
+	{ 0x3a0a, 0x00 },
+	{ 0x3a0b, 0x4d },
+	{ 0x3a0d, 0x08 },
+	{ 0x3a0e, 0x06 },
+	{ 0x3a14, 0x02 },
+	{ 0x3a15, 0x28 },
+	{ 0x3623, 0x00 },
+	{ 0x3634, 0x76 },
+	{ 0x3701, 0x44 },
+	{ 0x3702, 0x18 },
+	{ 0x3703, 0x24 },
+	{ 0x3704, 0x24 },
+	{ 0x3705, 0x0c },
+	{ REG_TIMING_VERT_FORMAT, 0x81 },
+	{ REG_TIMING_HORIZ_FORMAT, 0x01 },
+	{ 0x370a, 0x52 },
+	{ REG_VFIFO_READ_START_H, 0x00 },
+	{ REG_VFIFO_READ_START_L, 0x80 },
+	{ REG_ISP_CTRL02, 0x10 },
+	{ REG_NULL, 0x00 },
+};
+
+static const struct pll_ctrl_reg ctrl3[] = {
+	{ 1, 0x00 },
+	{ 2, 0x02 },
+	{ 3, 0x03 },
+	{ 4, 0x06 },
+	{ 6, 0x0d },
+	{ 8, 0x0e },
+	{ 12, 0x0f },
+	{ 16, 0x12 },
+	{ 24, 0x13 },
+	{ 32, 0x16 },
+	{ 48, 0x1b },
+	{ 64, 0x1e },
+	{ 96, 0x1f },
+	{ 0, 0x00 },
+};
+
+static const struct pll_ctrl_reg ctrl1[] = {
+	{ 2, 0x10 },
+	{ 4, 0x20 },
+	{ 6, 0x30 },
+	{ 8, 0x40 },
+	{ 10, 0x50 },
+	{ 12, 0x60 },
+	{ 14, 0x70 },
+	{ 16, 0x80 },
+	{ 18, 0x90 },
+	{ 20, 0xa0 },
+	{ 22, 0xb0 },
+	{ 24, 0xc0 },
+	{ 26, 0xd0 },
+	{ 28, 0xe0 },
+	{ 30, 0xf0 },
+	{ 0, 0x00 },
+};
+
+static const struct ov2659_framesize ov2659_framesizes[] = {
+	{ /* QVGA */
+		.width		= 320,
+		.height		= 240,
+		.regs		= ov2659_qvga,
+		.max_exp_lines	= 248,
+	}, { /* VGA */
+		.width		= 640,
+		.height		= 480,
+		.regs		= ov2659_vga,
+		.max_exp_lines	= 498,
+	}, { /* SVGA */
+		.width		= 800,
+		.height		= 600,
+		.regs		= ov2659_svga,
+		.max_exp_lines	= 498,
+	}, { /* XGA */
+		.width		= 1024,
+		.height		= 768,
+		.regs		= ov2659_xga,
+		.max_exp_lines	= 498,
+	}, { /* 720P */
+		.width		= 1280,
+		.height		= 720,
+		.regs		= ov2659_720p,
+		.max_exp_lines	= 498,
+	}, { /* SXGA */
+		.width		= 1280,
+		.height		= 1024,
+		.regs		= ov2659_sxga,
+		.max_exp_lines	= 1048,
+	}, { /* UXGA */
+		.width		= 1600,
+		.height		= 1200,
+		.regs		= ov2659_uxga,
+		.max_exp_lines	= 498,
+	},
+};
+
+/* YUV422 YUYV*/
+static struct sensor_register ov2659_format_yuyv[] = {
+	{ REG_FORMAT_CTRL00, 0x30 },
+	{ REG_NULL, 0x0 },
+};
+
+/* YUV422 UYVY  */
+static struct sensor_register ov2659_format_uyvy[] = {
+	{ REG_FORMAT_CTRL00, 0x32 },
+	{ REG_NULL, 0x0 },
+};
+
+/* Raw Bayer BGGR */
+static struct sensor_register ov2659_format_bggr[] = {
+	{ REG_FORMAT_CTRL00, 0x00 },
+	{ REG_NULL, 0x0 },
+};
+
+/* RGB565 */
+static struct sensor_register ov2659_format_rgb565[] = {
+	{ REG_FORMAT_CTRL00, 0x60 },
+	{ REG_NULL, 0x0 },
+};
+
+static const struct ov2659_pixfmt ov2659_formats[] = {
+	{
+		.code = MEDIA_BUS_FMT_YUYV8_2X8,
+		.format_ctrl_regs = ov2659_format_yuyv,
+	}, {
+		.code = MEDIA_BUS_FMT_UYVY8_2X8,
+		.format_ctrl_regs = ov2659_format_uyvy,
+	}, {
+		.code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+		.format_ctrl_regs = ov2659_format_rgb565,
+	}, {
+		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
+		.format_ctrl_regs = ov2659_format_bggr,
+	},
+};
+
+static inline struct ov2659 *to_ov2659(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov2659, sd);
+}
+
+/* sensor register write */
+static int ov2659_write(struct i2c_client *client, u16 reg, u8 val)
+{
+	struct i2c_msg msg;
+	u8 buf[3];
+	int ret;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xFF;
+	buf[2] = val;
+
+	msg.addr = client->addr;
+	msg.flags = client->flags;
+	msg.buf = buf;
+	msg.len = sizeof(buf);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret >= 0)
+		return 0;
+
+	dev_dbg(&client->dev,
+		"ov2659 write reg(0x%x val:0x%x) failed !\n", reg, val);
+
+	return ret;
+}
+
+/* sensor register read */
+static int ov2659_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+	struct i2c_msg msg[2];
+	u8 buf[2];
+	int ret;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xFF;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = client->flags;
+	msg[0].buf = buf;
+	msg[0].len = sizeof(buf);
+
+	msg[1].addr = client->addr;
+	msg[1].flags = client->flags | I2C_M_RD;
+	msg[1].buf = buf;
+	msg[1].len = 1;
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret >= 0) {
+		*val = buf[0];
+		return 0;
+	}
+
+	dev_dbg(&client->dev,
+		"ov2659 read reg(0x%x val:0x%x) failed !\n", reg, *val);
+
+	return ret;
+}
+
+static int ov2659_write_array(struct i2c_client *client,
+			      const struct sensor_register *regs)
+{
+	int i, ret = 0;
+
+	for (i = 0; ret == 0 && regs[i].addr; i++)
+		ret = ov2659_write(client, regs[i].addr, regs[i].value);
+
+	return ret;
+}
+
+static void ov2659_pll_calc_params(struct ov2659 *ov2659)
+{
+	const struct ov2659_platform_data *pdata = ov2659->pdata;
+	u8 ctrl1_reg = 0, ctrl2_reg = 0, ctrl3_reg = 0;
+	struct i2c_client *client = ov2659->client;
+	unsigned int desired = pdata->link_frequency;
+	u32 s_prediv = 1, s_postdiv = 1, s_mult = 1;
+	u32 prediv, postdiv, mult;
+	u32 bestdelta = -1;
+	u32 delta, actual;
+	int i, j;
+
+	for (i = 0; ctrl1[i].div != 0; i++) {
+		postdiv = ctrl1[i].div;
+		for (j = 0; ctrl3[j].div != 0; j++) {
+			prediv = ctrl3[j].div;
+			for (mult = 1; mult <= 63; mult++) {
+				actual  = ov2659->xvclk_frequency;
+				actual *= mult;
+				actual /= prediv;
+				actual /= postdiv;
+				delta = actual - desired;
+				delta = abs(delta);
+
+				if ((delta < bestdelta) || (bestdelta == -1)) {
+					bestdelta = delta;
+					s_mult    = mult;
+					s_prediv  = prediv;
+					s_postdiv = postdiv;
+					ctrl1_reg = ctrl1[i].reg;
+					ctrl2_reg = mult;
+					ctrl3_reg = ctrl3[j].reg;
+				}
+			}
+		}
+	}
+
+	ov2659->pll.ctrl1 = ctrl1_reg;
+	ov2659->pll.ctrl2 = ctrl2_reg;
+	ov2659->pll.ctrl3 = ctrl3_reg;
+
+	dev_dbg(&client->dev,
+		"Actual reg config: ctrl1_reg: %02x ctrl2_reg: %02x ctrl3_reg: %02x\n",
+		ctrl1_reg, ctrl2_reg, ctrl3_reg);
+}
+
+static int ov2659_set_pixel_clock(struct ov2659 *ov2659)
+{
+	struct i2c_client *client = ov2659->client;
+	struct sensor_register pll_regs[] = {
+		{REG_SC_PLL_CTRL1, ov2659->pll.ctrl1},
+		{REG_SC_PLL_CTRL2, ov2659->pll.ctrl2},
+		{REG_SC_PLL_CTRL3, ov2659->pll.ctrl3},
+		{REG_NULL, 0x00},
+	};
+
+	dev_dbg(&client->dev, "%s\n", __func__);
+
+	return ov2659_write_array(client, pll_regs);
+};
+
+static void ov2659_get_default_format(struct v4l2_mbus_framefmt *format)
+{
+	format->width = ov2659_framesizes[2].width;
+	format->height = ov2659_framesizes[2].height;
+	format->colorspace = V4L2_COLORSPACE_SRGB;
+	format->code = ov2659_formats[0].code;
+	format->field = V4L2_FIELD_NONE;
+}
+
+static void ov2659_set_streaming(struct ov2659 *ov2659, int on)
+{
+	struct i2c_client *client = ov2659->client;
+	int ret;
+
+	on = !!on;
+
+	dev_dbg(&client->dev, "%s: on: %d\n", __func__, on);
+
+	ret = ov2659_write(client, REG_SOFTWARE_STANDBY, on);
+	if (ret)
+		dev_err(&client->dev, "ov2659 soft standby failed\n");
+}
+
+static int ov2659_init(struct v4l2_subdev *sd, u32 val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	return ov2659_write_array(client, ov2659_init_regs);
+}
+
+/*
+ * V4L2 subdev video and pad level operations
+ */
+
+static int ov2659_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	if (code->index >= ARRAY_SIZE(ov2659_formats))
+		return -EINVAL;
+
+	code->code = ov2659_formats[code->index].code;
+
+	return 0;
+}
+
+static int ov2659_enum_frame_sizes(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int i = ARRAY_SIZE(ov2659_formats);
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	if (fse->index >= ARRAY_SIZE(ov2659_framesizes))
+		return -EINVAL;
+
+	while (--i)
+		if (fse->code == ov2659_formats[i].code)
+			break;
+
+	fse->code = ov2659_formats[i].code;
+
+	fse->min_width  = ov2659_framesizes[fse->index].width;
+	fse->max_width  = fse->min_width;
+	fse->max_height = ov2659_framesizes[fse->index].height;
+	fse->min_height = fse->max_height;
+
+	return 0;
+}
+
+static int ov2659_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov2659 *ov2659 = to_ov2659(sd);
+	struct v4l2_mbus_framefmt *mf;
+
+	dev_dbg(&client->dev, "ov2659_get_fmt\n");
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		mf = v4l2_subdev_get_try_format(sd, cfg, 0);
+		mutex_lock(&ov2659->lock);
+		fmt->format = *mf;
+		mutex_unlock(&ov2659->lock);
+		return 0;
+	}
+
+	mutex_lock(&ov2659->lock);
+	fmt->format = ov2659->format;
+	mutex_unlock(&ov2659->lock);
+
+	dev_dbg(&client->dev, "ov2659_get_fmt: %x %dx%d\n",
+		ov2659->format.code, ov2659->format.width,
+		ov2659->format.height);
+
+	return 0;
+}
+
+static void __ov2659_try_frame_size(struct v4l2_mbus_framefmt *mf,
+				    const struct ov2659_framesize **size)
+{
+	const struct ov2659_framesize *fsize = &ov2659_framesizes[0];
+	const struct ov2659_framesize *match = NULL;
+	int i = ARRAY_SIZE(ov2659_framesizes);
+	unsigned int min_err = UINT_MAX;
+
+	while (i--) {
+		int err = abs(fsize->width - mf->width)
+				+ abs(fsize->height - mf->height);
+		if ((err < min_err) && (fsize->regs[0].addr)) {
+			min_err = err;
+			match = fsize;
+		}
+		fsize++;
+	}
+
+	if (!match)
+		match = &ov2659_framesizes[2];
+
+	mf->width  = match->width;
+	mf->height = match->height;
+
+	if (size)
+		*size = match;
+}
+
+static int ov2659_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	unsigned int index = ARRAY_SIZE(ov2659_formats);
+	struct v4l2_mbus_framefmt *mf = &fmt->format;
+	const struct ov2659_framesize *size = NULL;
+	struct ov2659 *ov2659 = to_ov2659(sd);
+	int ret = 0;
+
+	dev_dbg(&client->dev, "ov2659_set_fmt\n");
+
+	__ov2659_try_frame_size(mf, &size);
+
+	while (--index >= 0)
+		if (ov2659_formats[index].code == mf->code)
+			break;
+
+	if (index < 0)
+		return -EINVAL;
+
+	mf->colorspace = V4L2_COLORSPACE_SRGB;
+	mf->code = ov2659_formats[index].code;
+	mf->field = V4L2_FIELD_NONE;
+
+	mutex_lock(&ov2659->lock);
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+		*mf = fmt->format;
+	} else {
+		s64 val;
+
+		if (ov2659->streaming) {
+			mutex_unlock(&ov2659->lock);
+			return -EBUSY;
+		}
+
+		ov2659->frame_size = size;
+		ov2659->format = fmt->format;
+		ov2659->format_ctrl_regs =
+			ov2659_formats[index].format_ctrl_regs;
+
+		if (ov2659->format.code != MEDIA_BUS_FMT_SBGGR8_1X8)
+			val = ov2659->pdata->link_frequency / 2;
+		else
+			val = ov2659->pdata->link_frequency;
+
+		ret = v4l2_ctrl_s_ctrl_int64(ov2659->link_frequency, val);
+		if (ret < 0)
+			dev_warn(&client->dev,
+				 "failed to set link_frequency rate (%d)\n",
+				 ret);
+	}
+
+	mutex_unlock(&ov2659->lock);
+	return ret;
+}
+
+static int ov2659_set_frame_size(struct ov2659 *ov2659)
+{
+	struct i2c_client *client = ov2659->client;
+
+	dev_dbg(&client->dev, "%s\n", __func__);
+
+	return ov2659_write_array(ov2659->client, ov2659->frame_size->regs);
+}
+
+static int ov2659_set_format(struct ov2659 *ov2659)
+{
+	struct i2c_client *client = ov2659->client;
+
+	dev_dbg(&client->dev, "%s\n", __func__);
+
+	return ov2659_write_array(ov2659->client, ov2659->format_ctrl_regs);
+}
+
+static int ov2659_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov2659 *ov2659 = to_ov2659(sd);
+	int ret = 0;
+
+	dev_dbg(&client->dev, "%s: on: %d\n", __func__, on);
+
+	mutex_lock(&ov2659->lock);
+
+	on = !!on;
+
+	if (ov2659->streaming == on)
+		goto unlock;
+
+	if (!on) {
+		/* Stop Streaming Sequence */
+		ov2659_set_streaming(ov2659, 0);
+		ov2659->streaming = on;
+		goto unlock;
+	}
+
+	ov2659_set_pixel_clock(ov2659);
+	ov2659_set_frame_size(ov2659);
+	ov2659_set_format(ov2659);
+	ov2659_set_streaming(ov2659, 1);
+	ov2659->streaming = on;
+
+unlock:
+	mutex_unlock(&ov2659->lock);
+	return ret;
+}
+
+static int ov2659_set_test_pattern(struct ov2659 *ov2659, int value)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&ov2659->sd);
+	int ret;
+	u8 val;
+
+	ret = ov2659_read(client, REG_PRE_ISP_CTRL00, &val);
+	if (ret < 0)
+		return ret;
+
+	switch (value) {
+	case 0:
+		val &= ~TEST_PATTERN_ENABLE;
+		break;
+	case 1:
+		val &= VERTICAL_COLOR_BAR_MASK;
+		val |= TEST_PATTERN_ENABLE;
+		break;
+	}
+
+	return ov2659_write(client, REG_PRE_ISP_CTRL00, val);
+}
+
+static int ov2659_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov2659 *ov2659 =
+			container_of(ctrl->handler, struct ov2659, ctrls);
+
+	switch (ctrl->id) {
+	case V4L2_CID_TEST_PATTERN:
+		return ov2659_set_test_pattern(ov2659, ctrl->val);
+	}
+
+	return 0;
+}
+
+static struct v4l2_ctrl_ops ov2659_ctrl_ops = {
+	.s_ctrl = ov2659_s_ctrl,
+};
+
+static const char * const ov2659_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Color Bars",
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int ov2659_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format =
+				v4l2_subdev_get_try_format(sd, fh->pad, 0);
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	ov2659_get_default_format(format);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops ov2659_subdev_core_ops = {
+	.log_status = v4l2_ctrl_subdev_log_status,
+	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_video_ops ov2659_subdev_video_ops = {
+	.s_stream = ov2659_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov2659_subdev_pad_ops = {
+	.enum_mbus_code = ov2659_enum_mbus_code,
+	.enum_frame_size = ov2659_enum_frame_sizes,
+	.get_fmt = ov2659_get_fmt,
+	.set_fmt = ov2659_set_fmt,
+};
+
+static const struct v4l2_subdev_ops ov2659_subdev_ops = {
+	.core  = &ov2659_subdev_core_ops,
+	.video = &ov2659_subdev_video_ops,
+	.pad   = &ov2659_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops ov2659_subdev_internal_ops = {
+	.open = ov2659_open,
+};
+
+static int ov2659_detect(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u8 pid, ver;
+	int ret;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	ret = ov2659_write(client, REG_SOFTWARE_RESET, 0x01);
+	if (ret != 0) {
+		dev_err(&client->dev, "Sensor soft reset failed\n");
+		return -ENODEV;
+	}
+	usleep_range(1000, 2000);
+
+	ret = ov2659_init(sd, 0);
+	if (ret < 0)
+		return ret;
+
+	/* Check sensor revision */
+	ret = ov2659_read(client, REG_SC_CHIP_ID_H, &pid);
+	if (!ret)
+		ret = ov2659_read(client, REG_SC_CHIP_ID_L, &ver);
+
+	if (!ret) {
+		unsigned short id;
+
+		id = OV265X_ID(pid, ver);
+		if (id != OV2659_ID)
+			dev_err(&client->dev,
+				"Sensor detection failed (%04X, %d)\n",
+				id, ret);
+		else
+			dev_info(&client->dev, "Found OV%04X sensor\n", id);
+	}
+
+	return ret;
+}
+
+static struct ov2659_platform_data *
+ov2659_get_pdata(struct i2c_client *client)
+{
+	struct ov2659_platform_data *pdata;
+	struct device_node *endpoint;
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
+		return client->dev.platform_data;
+
+	endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL);
+	if (!endpoint)
+		return NULL;
+
+	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		goto done;
+
+	ret = of_property_read_u64(endpoint, "link-frequencies",
+				   &pdata->link_frequency);
+	if (ret) {
+		dev_err(&client->dev, "link-frequencies property not found\n");
+		pdata = NULL;
+	}
+
+done:
+	of_node_put(endpoint);
+	return pdata;
+}
+
+static int ov2659_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	const struct ov2659_platform_data *pdata = ov2659_get_pdata(client);
+	struct v4l2_subdev *sd;
+	struct ov2659 *ov2659;
+	struct clk *clk;
+	int ret;
+
+	if (!pdata) {
+		dev_err(&client->dev, "platform data not specified\n");
+		return -EINVAL;
+	}
+
+	ov2659 = devm_kzalloc(&client->dev, sizeof(*ov2659), GFP_KERNEL);
+	if (!ov2659)
+		return -ENOMEM;
+
+	ov2659->pdata = pdata;
+	ov2659->client = client;
+
+	clk = devm_clk_get(&client->dev, "xvclk");
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	ov2659->xvclk_frequency = clk_get_rate(clk);
+	if (ov2659->xvclk_frequency < 6000000 ||
+	    ov2659->xvclk_frequency > 27000000)
+		return -EINVAL;
+
+	v4l2_ctrl_handler_init(&ov2659->ctrls, 2);
+	ov2659->link_frequency =
+			v4l2_ctrl_new_std(&ov2659->ctrls, &ov2659_ctrl_ops,
+					  V4L2_CID_PIXEL_RATE,
+					  pdata->link_frequency / 2,
+					  pdata->link_frequency, 1,
+					  pdata->link_frequency);
+	v4l2_ctrl_new_std_menu_items(&ov2659->ctrls, &ov2659_ctrl_ops,
+				     V4L2_CID_TEST_PATTERN,
+				     ARRAY_SIZE(ov2659_test_pattern_menu) - 1,
+				     0, 0, ov2659_test_pattern_menu);
+	ov2659->sd.ctrl_handler = &ov2659->ctrls;
+
+	if (ov2659->ctrls.error) {
+		dev_err(&client->dev, "%s: control initialization error %d\n",
+			__func__, ov2659->ctrls.error);
+		return  ov2659->ctrls.error;
+	}
+
+	sd = &ov2659->sd;
+	client->flags |= I2C_CLIENT_SCCB;
+	v4l2_i2c_subdev_init(sd, client, &ov2659_subdev_ops);
+
+	sd->internal_ops = &ov2659_subdev_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+		     V4L2_SUBDEV_FL_HAS_EVENTS;
+
+#if defined(CPTCFG_MEDIA_CONTROLLER)
+	ov2659->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+	ret = media_entity_init(&sd->entity, 1, &ov2659->pad, 0);
+	if (ret < 0) {
+		v4l2_ctrl_handler_free(&ov2659->ctrls);
+		return ret;
+	}
+#endif
+
+	mutex_init(&ov2659->lock);
+
+	ov2659_get_default_format(&ov2659->format);
+	ov2659->frame_size = &ov2659_framesizes[2];
+	ov2659->format_ctrl_regs = ov2659_formats[0].format_ctrl_regs;
+
+	ret = ov2659_detect(sd);
+	if (ret < 0)
+		goto error;
+
+	/* Calculate the PLL register value needed */
+	ov2659_pll_calc_params(ov2659);
+
+	ret = v4l2_async_register_subdev(&ov2659->sd);
+	if (ret)
+		goto error;
+
+	dev_info(&client->dev, "%s sensor driver registered !!\n", sd->name);
+
+	return 0;
+
+error:
+	v4l2_ctrl_handler_free(&ov2659->ctrls);
+#if defined(CPTCFG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+	mutex_destroy(&ov2659->lock);
+	return ret;
+}
+
+static int ov2659_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov2659 *ov2659 = to_ov2659(sd);
+
+	v4l2_ctrl_handler_free(&ov2659->ctrls);
+	v4l2_async_unregister_subdev(sd);
+#if defined(CPTCFG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+	mutex_destroy(&ov2659->lock);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov2659_id[] = {
+	{ "ov2659", 0 },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(i2c, ov2659_id);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id ov2659_of_match[] = {
+	{ .compatible = "ovti,ov2659", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ov2659_of_match);
+#endif
+
+static struct i2c_driver ov2659_i2c_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.of_match_table = of_match_ptr(ov2659_of_match),
+	},
+	.probe		= ov2659_probe,
+	.remove		= ov2659_remove,
+	.id_table	= ov2659_id,
+};
+
+module_i2c_driver(ov2659_i2c_driver);
+
+MODULE_AUTHOR("Benoit Parrot <bparrot@ti.com>");
+MODULE_DESCRIPTION("OV2659 CMOS Image Sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index 48eeb6a..6630484 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -1069,29 +1069,35 @@
 
 static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 };
 
-static int ov7670_enum_frameintervals(struct v4l2_subdev *sd,
-		struct v4l2_frmivalenum *interval)
+static int ov7670_enum_frame_interval(struct v4l2_subdev *sd,
+				      struct v4l2_subdev_pad_config *cfg,
+				      struct v4l2_subdev_frame_interval_enum *fie)
 {
-	if (interval->index >= ARRAY_SIZE(ov7670_frame_rates))
+	if (fie->pad)
 		return -EINVAL;
-	interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
-	interval->discrete.numerator = 1;
-	interval->discrete.denominator = ov7670_frame_rates[interval->index];
+	if (fie->index >= ARRAY_SIZE(ov7670_frame_rates))
+		return -EINVAL;
+	fie->interval.numerator = 1;
+	fie->interval.denominator = ov7670_frame_rates[fie->index];
 	return 0;
 }
 
 /*
  * Frame size enumeration
  */
-static int ov7670_enum_framesizes(struct v4l2_subdev *sd,
-		struct v4l2_frmsizeenum *fsize)
+static int ov7670_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
 {
 	struct ov7670_info *info = to_state(sd);
 	int i;
 	int num_valid = -1;
-	__u32 index = fsize->index;
+	__u32 index = fse->index;
 	unsigned int n_win_sizes = info->devtype->n_win_sizes;
 
+	if (fse->pad)
+		return -EINVAL;
+
 	/*
 	 * If a minimum width/height was requested, filter out the capture
 	 * windows that fall outside that.
@@ -1103,9 +1109,8 @@
 		if (info->min_height && win->height < info->min_height)
 			continue;
 		if (index == ++num_valid) {
-			fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
-			fsize->discrete.width = win->width;
-			fsize->discrete.height = win->height;
+			fse->min_width = fse->max_width = win->width;
+			fse->min_height = fse->max_height = win->height;
 			return 0;
 		}
 	}
@@ -1485,13 +1490,17 @@
 	.s_mbus_fmt = ov7670_s_mbus_fmt,
 	.s_parm = ov7670_s_parm,
 	.g_parm = ov7670_g_parm,
-	.enum_frameintervals = ov7670_enum_frameintervals,
-	.enum_framesizes = ov7670_enum_framesizes,
+};
+
+static const struct v4l2_subdev_pad_ops ov7670_pad_ops = {
+	.enum_frame_interval = ov7670_enum_frame_interval,
+	.enum_frame_size = ov7670_enum_frame_size,
 };
 
 static const struct v4l2_subdev_ops ov7670_ops = {
 	.core = &ov7670_core_ops,
 	.video = &ov7670_video_ops,
+	.pad = &ov7670_pad_ops,
 };
 
 /* ----------------------------------------------------------------------- */
diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
index 2246bd5..2bc4733 100644
--- a/drivers/media/i2c/ov9650.c
+++ b/drivers/media/i2c/ov9650.c
@@ -1067,7 +1067,7 @@
 }
 
 static int ov965x_enum_mbus_code(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->index >= ARRAY_SIZE(ov965x_formats))
@@ -1078,7 +1078,7 @@
 }
 
 static int ov965x_enum_frame_sizes(struct v4l2_subdev *sd,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
 {
 	int i = ARRAY_SIZE(ov965x_formats);
@@ -1164,14 +1164,14 @@
 	return ret;
 }
 
-static int ov965x_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int ov965x_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct ov965x *ov965x = to_ov965x(sd);
 	struct v4l2_mbus_framefmt *mf;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = v4l2_subdev_get_try_format(fh, 0);
+		mf = v4l2_subdev_get_try_format(sd, cfg, 0);
 		fmt->format = *mf;
 		return 0;
 	}
@@ -1208,7 +1208,7 @@
 		*size = match;
 }
 
-static int ov965x_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int ov965x_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	unsigned int index = ARRAY_SIZE(ov965x_formats);
@@ -1230,8 +1230,8 @@
 	mutex_lock(&ov965x->lock);
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		if (fh != NULL) {
-			mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+		if (cfg != NULL) {
+			mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 			*mf = fmt->format;
 		}
 	} else {
@@ -1361,7 +1361,7 @@
  */
 static int ov965x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
-	struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0);
+	struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, fh->pad, 0);
 
 	ov965x_get_default_format(mf);
 	return 0;
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index ee0f57e..08b234b 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -824,10 +824,11 @@
 }
 
 static void s5c73m3_oif_try_format(struct s5c73m3 *state,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_format *fmt,
 				   const struct s5c73m3_frame_size **fs)
 {
+	struct v4l2_subdev *sd = &state->sensor_sd;
 	u32 code;
 
 	switch (fmt->pad) {
@@ -850,7 +851,7 @@
 			*fs = state->oif_pix_size[RES_ISP];
 		else
 			*fs = s5c73m3_find_frame_size(
-						v4l2_subdev_get_try_format(fh,
+						v4l2_subdev_get_try_format(sd, cfg,
 							OIF_ISP_PAD),
 						RES_ISP);
 		break;
@@ -860,7 +861,7 @@
 }
 
 static void s5c73m3_try_format(struct s5c73m3 *state,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *fmt,
 			      const struct s5c73m3_frame_size **fs)
 {
@@ -952,7 +953,7 @@
 }
 
 static int s5c73m3_oif_enum_frame_interval(struct v4l2_subdev *sd,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_frame_interval_enum *fie)
 {
 	struct s5c73m3 *state = oif_sd_to_s5c73m3(sd);
@@ -990,7 +991,7 @@
 }
 
 static int s5c73m3_get_fmt(struct v4l2_subdev *sd,
-			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd);
@@ -998,7 +999,7 @@
 	u32 code;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad);
+		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 		return 0;
 	}
 
@@ -1024,7 +1025,7 @@
 }
 
 static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd,
-			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct s5c73m3 *state = oif_sd_to_s5c73m3(sd);
@@ -1032,7 +1033,7 @@
 	u32 code;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad);
+		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 		return 0;
 	}
 
@@ -1062,7 +1063,7 @@
 }
 
 static int s5c73m3_set_fmt(struct v4l2_subdev *sd,
-			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	const struct s5c73m3_frame_size *frame_size = NULL;
@@ -1072,10 +1073,10 @@
 
 	mutex_lock(&state->lock);
 
-	s5c73m3_try_format(state, fh, fmt, &frame_size);
+	s5c73m3_try_format(state, cfg, fmt, &frame_size);
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 		*mf = fmt->format;
 	} else {
 		switch (fmt->pad) {
@@ -1101,7 +1102,7 @@
 }
 
 static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd,
-			 struct v4l2_subdev_fh *fh,
+			 struct v4l2_subdev_pad_config *cfg,
 			 struct v4l2_subdev_format *fmt)
 {
 	const struct s5c73m3_frame_size *frame_size = NULL;
@@ -1111,13 +1112,13 @@
 
 	mutex_lock(&state->lock);
 
-	s5c73m3_oif_try_format(state, fh, fmt, &frame_size);
+	s5c73m3_oif_try_format(state, cfg, fmt, &frame_size);
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 		*mf = fmt->format;
 		if (fmt->pad == OIF_ISP_PAD) {
-			mf = v4l2_subdev_get_try_format(fh, OIF_SOURCE_PAD);
+			mf = v4l2_subdev_get_try_format(sd, cfg, OIF_SOURCE_PAD);
 			mf->width = fmt->format.width;
 			mf->height = fmt->format.height;
 		}
@@ -1189,7 +1190,7 @@
 }
 
 static int s5c73m3_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	static const int codes[] = {
@@ -1205,7 +1206,7 @@
 }
 
 static int s5c73m3_oif_enum_mbus_code(struct v4l2_subdev *sd,
-				struct v4l2_subdev_fh *fh,
+				struct v4l2_subdev_pad_config *cfg,
 				struct v4l2_subdev_mbus_code_enum *code)
 {
 	int ret;
@@ -1220,7 +1221,7 @@
 }
 
 static int s5c73m3_enum_frame_size(struct v4l2_subdev *sd,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
 {
 	int idx;
@@ -1247,9 +1248,10 @@
 }
 
 static int s5c73m3_oif_enum_frame_size(struct v4l2_subdev *sd,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
 {
+	struct s5c73m3 *state = oif_sd_to_s5c73m3(sd);
 	int idx;
 
 	if (fse->pad == OIF_SOURCE_PAD) {
@@ -1259,11 +1261,25 @@
 		switch (fse->code) {
 		case S5C73M3_JPEG_FMT:
 		case S5C73M3_ISP_FMT: {
-			struct v4l2_mbus_framefmt *mf =
-				v4l2_subdev_get_try_format(fh, OIF_ISP_PAD);
+			unsigned w, h;
 
-			fse->max_width = fse->min_width = mf->width;
-			fse->max_height = fse->min_height = mf->height;
+			if (fse->which == V4L2_SUBDEV_FORMAT_TRY) {
+				struct v4l2_mbus_framefmt *mf;
+
+				mf = v4l2_subdev_get_try_format(sd, cfg,
+								OIF_ISP_PAD);
+
+				w = mf->width;
+				h = mf->height;
+			} else {
+				const struct s5c73m3_frame_size *fs;
+
+				fs = state->oif_pix_size[RES_ISP];
+				w = fs->width;
+				h = fs->height;
+			}
+			fse->max_width = fse->min_width = w;
+			fse->max_height = fse->min_height = h;
 			return 0;
 		}
 		default:
@@ -1306,11 +1322,11 @@
 {
 	struct v4l2_mbus_framefmt *mf;
 
-	mf = v4l2_subdev_get_try_format(fh, S5C73M3_ISP_PAD);
+	mf = v4l2_subdev_get_try_format(sd, fh->pad, S5C73M3_ISP_PAD);
 	s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1],
 						S5C73M3_ISP_FMT);
 
-	mf = v4l2_subdev_get_try_format(fh, S5C73M3_JPEG_PAD);
+	mf = v4l2_subdev_get_try_format(sd, fh->pad, S5C73M3_JPEG_PAD);
 	s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1],
 					S5C73M3_JPEG_FMT);
 
@@ -1321,15 +1337,15 @@
 {
 	struct v4l2_mbus_framefmt *mf;
 
-	mf = v4l2_subdev_get_try_format(fh, OIF_ISP_PAD);
+	mf = v4l2_subdev_get_try_format(sd, fh->pad, OIF_ISP_PAD);
 	s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1],
 						S5C73M3_ISP_FMT);
 
-	mf = v4l2_subdev_get_try_format(fh, OIF_JPEG_PAD);
+	mf = v4l2_subdev_get_try_format(sd, fh->pad, OIF_JPEG_PAD);
 	s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1],
 					S5C73M3_JPEG_FMT);
 
-	mf = v4l2_subdev_get_try_format(fh, OIF_SOURCE_PAD);
+	mf = v4l2_subdev_get_try_format(sd, fh->pad, OIF_SOURCE_PAD);
 	s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1],
 						S5C73M3_ISP_FMT);
 	return 0;
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
index f60b265..63eb190 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
@@ -52,7 +52,7 @@
 		xfer.rx_buf = addr;
 
 	if (spi_dev == NULL) {
-		dev_err(&spi_dev->dev, "SPI device is uninitialized\n");
+		pr_err("SPI device is uninitialized\n");
 		return -ENODEV;
 	}
 
diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c
index d1c50c9..9708423 100644
--- a/drivers/media/i2c/s5k4ecgx.c
+++ b/drivers/media/i2c/s5k4ecgx.c
@@ -220,7 +220,7 @@
 	msg[1].buf = rbuf;
 
 	ret = i2c_transfer(client->adapter, msg, 2);
-	*val = be16_to_cpu(*((u16 *)rbuf));
+	*val = be16_to_cpu(*((__be16 *)rbuf));
 
 	v4l2_dbg(4, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val);
 
@@ -341,7 +341,7 @@
 		v4l2_err(sd, "Failed to read firmware %s\n", S5K4ECGX_FIRMWARE);
 		return err;
 	}
-	regs_num = le32_to_cpu(get_unaligned_le32(fw->data));
+	regs_num = get_unaligned_le32(fw->data);
 
 	v4l2_dbg(3, debug, sd, "FW: %s size %zu register sets %d\n",
 		 S5K4ECGX_FIRMWARE, fw->size, regs_num);
@@ -351,8 +351,7 @@
 		err = -EINVAL;
 		goto fw_out;
 	}
-	crc_file = le32_to_cpu(get_unaligned_le32(fw->data +
-						  regs_num * FW_RECORD_SIZE));
+	crc_file = get_unaligned_le32(fw->data + regs_num * FW_RECORD_SIZE);
 	crc = crc32_le(~0, fw->data, regs_num * FW_RECORD_SIZE);
 	if (crc != crc_file) {
 		v4l2_err(sd, "FW: invalid crc (%#x:%#x)\n", crc, crc_file);
@@ -361,9 +360,9 @@
 	}
 	ptr = fw->data + FW_RECORD_SIZE;
 	for (i = 1; i < regs_num; i++) {
-		addr = le32_to_cpu(get_unaligned_le32(ptr));
+		addr = get_unaligned_le32(ptr);
 		ptr += sizeof(u32);
-		val = le16_to_cpu(get_unaligned_le16(ptr));
+		val = get_unaligned_le16(ptr);
 		ptr += sizeof(u16);
 		if (addr - addr_inc != 2)
 			err = s5k4ecgx_write(client, addr, val);
@@ -532,7 +531,7 @@
 }
 
 static int s5k4ecgx_enum_mbus_code(struct v4l2_subdev *sd,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->index >= ARRAY_SIZE(s5k4ecgx_formats))
@@ -542,15 +541,15 @@
 	return 0;
 }
 
-static int s5k4ecgx_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int s5k4ecgx_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct s5k4ecgx *priv = to_s5k4ecgx(sd);
 	struct v4l2_mbus_framefmt *mf;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		if (fh) {
-			mf = v4l2_subdev_get_try_format(fh, 0);
+		if (cfg) {
+			mf = v4l2_subdev_get_try_format(sd, cfg, 0);
 			fmt->format = *mf;
 		}
 		return 0;
@@ -582,7 +581,7 @@
 	return &s5k4ecgx_formats[i];
 }
 
-static int s5k4ecgx_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int s5k4ecgx_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			    struct v4l2_subdev_format *fmt)
 {
 	struct s5k4ecgx *priv = to_s5k4ecgx(sd);
@@ -597,8 +596,8 @@
 	fmt->format.field = V4L2_FIELD_NONE;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		if (fh) {
-			mf = v4l2_subdev_get_try_format(fh, 0);
+		if (cfg) {
+			mf = v4l2_subdev_get_try_format(sd, cfg, 0);
 			*mf = fmt->format;
 		}
 		return 0;
@@ -693,7 +692,7 @@
  */
 static int s5k4ecgx_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
-	struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0);
+	struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, fh->pad, 0);
 
 	mf->width = s5k4ecgx_prev_sizes[0].size.width;
 	mf->height = s5k4ecgx_prev_sizes[0].size.height;
diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
index 60a74d8..297ef04 100644
--- a/drivers/media/i2c/s5k5baf.c
+++ b/drivers/media/i2c/s5k5baf.c
@@ -353,7 +353,7 @@
  *
  */
 static int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw,
-			    size_t count, const u16 *data)
+			    size_t count, const __le16 *data)
 {
 	struct s5k5baf_fw *f;
 	u16 *d, i, *end;
@@ -374,6 +374,8 @@
 	count -= S5K5BAG_FW_TAG_LEN;
 
 	d = devm_kzalloc(dev, count * sizeof(u16), GFP_KERNEL);
+	if (!d)
+		return -ENOMEM;
 
 	for (i = 0; i < count; ++i)
 		d[i] = le16_to_cpu(data[i]);
@@ -421,6 +423,7 @@
 {
 	struct i2c_client *c = v4l2_get_subdevdata(&state->sd);
 	__be16 w, r;
+	u16 res;
 	struct i2c_msg msg[] = {
 		{ .addr = c->addr, .flags = 0,
 		  .len = 2, .buf = (u8 *)&w },
@@ -434,15 +437,15 @@
 
 	w = cpu_to_be16(addr);
 	ret = i2c_transfer(c->adapter, msg, 2);
-	r = be16_to_cpu(r);
+	res = be16_to_cpu(r);
 
-	v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, r);
+	v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, res);
 
 	if (ret != 2) {
 		v4l2_err(c, "i2c_read: error during transfer (%d)\n", ret);
 		state->error = ret;
 	}
-	return r;
+	return res;
 }
 
 static void s5k5baf_i2c_write(struct s5k5baf *state, u16 addr, u16 val)
@@ -1037,7 +1040,7 @@
 	}
 
 	ret = s5k5baf_fw_parse(&c->dev, &state->fw, fw->size / 2,
-			       (u16 *)fw->data);
+			       (__le16 *)fw->data);
 
 	release_firmware(fw);
 
@@ -1181,7 +1184,7 @@
  * V4L2 subdev pad level and video operations
  */
 static int s5k5baf_enum_frame_interval(struct v4l2_subdev *sd,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_frame_interval_enum *fie)
 {
 	if (fie->index > S5K5BAF_MAX_FR_TIME - S5K5BAF_MIN_FR_TIME ||
@@ -1200,7 +1203,7 @@
 }
 
 static int s5k5baf_enum_mbus_code(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->pad == PAD_CIS) {
@@ -1218,7 +1221,7 @@
 }
 
 static int s5k5baf_enum_frame_size(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_frame_size_enum *fse)
 {
 	int i;
@@ -1275,7 +1278,7 @@
 	return pixfmt;
 }
 
-static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct s5k5baf *state = to_s5k5baf(sd);
@@ -1283,7 +1286,7 @@
 	struct v4l2_mbus_framefmt *mf;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 		fmt->format = *mf;
 		return 0;
 	}
@@ -1305,7 +1308,7 @@
 	return 0;
 }
 
-static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct v4l2_mbus_framefmt *mf = &fmt->format;
@@ -1316,7 +1319,7 @@
 	mf->field = V4L2_FIELD_NONE;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		*v4l2_subdev_get_try_format(fh, fmt->pad) = *mf;
+		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = *mf;
 		return 0;
 	}
 
@@ -1368,7 +1371,7 @@
 }
 
 static int s5k5baf_get_selection(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_selection *sel)
 {
 	static enum selection_rect rtype;
@@ -1388,9 +1391,9 @@
 
 	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
 		if (rtype == R_COMPOSE)
-			sel->r = *v4l2_subdev_get_try_compose(fh, sel->pad);
+			sel->r = *v4l2_subdev_get_try_compose(sd, cfg, sel->pad);
 		else
-			sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad);
+			sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
 		return 0;
 	}
 
@@ -1459,7 +1462,7 @@
 }
 
 static int s5k5baf_set_selection(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_selection *sel)
 {
 	static enum selection_rect rtype;
@@ -1480,9 +1483,9 @@
 	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
 		rects = (struct v4l2_rect * []) {
 				&s5k5baf_cis_rect,
-				v4l2_subdev_get_try_crop(fh, PAD_CIS),
-				v4l2_subdev_get_try_compose(fh, PAD_CIS),
-				v4l2_subdev_get_try_crop(fh, PAD_OUT)
+				v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
+				v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
+				v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
 			};
 		s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
 		return 0;
@@ -1700,22 +1703,22 @@
 {
 	struct v4l2_mbus_framefmt *mf;
 
-	mf = v4l2_subdev_get_try_format(fh, PAD_CIS);
+	mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_CIS);
 	s5k5baf_try_cis_format(mf);
 
 	if (s5k5baf_is_cis_subdev(sd))
 		return 0;
 
-	mf = v4l2_subdev_get_try_format(fh, PAD_OUT);
+	mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_OUT);
 	mf->colorspace = s5k5baf_formats[0].colorspace;
 	mf->code = s5k5baf_formats[0].code;
 	mf->width = s5k5baf_cis_rect.width;
 	mf->height = s5k5baf_cis_rect.height;
 	mf->field = V4L2_FIELD_NONE;
 
-	*v4l2_subdev_get_try_crop(fh, PAD_CIS) = s5k5baf_cis_rect;
-	*v4l2_subdev_get_try_compose(fh, PAD_CIS) = s5k5baf_cis_rect;
-	*v4l2_subdev_get_try_crop(fh, PAD_OUT) = s5k5baf_cis_rect;
+	*v4l2_subdev_get_try_crop(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect;
+	*v4l2_subdev_get_try_compose(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect;
+	*v4l2_subdev_get_try_crop(sd, fh->pad, PAD_OUT) = s5k5baf_cis_rect;
 
 	return 0;
 }
@@ -1793,7 +1796,7 @@
 
 static int s5k5baf_configure_gpios(struct s5k5baf *state)
 {
-	static const char const *name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" };
+	static const char * const name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" };
 	struct i2c_client *c = v4l2_get_subdevdata(&state->sd);
 	struct s5k5baf_gpio *g = state->gpios;
 	int ret, i;
diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c
index 91b841a..bc389d5 100644
--- a/drivers/media/i2c/s5k6a3.c
+++ b/drivers/media/i2c/s5k6a3.c
@@ -99,7 +99,7 @@
 }
 
 static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->index >= ARRAY_SIZE(s5k6a3_formats))
@@ -123,17 +123,17 @@
 }
 
 static struct v4l2_mbus_framefmt *__s5k6a3_get_format(
-		struct s5k6a3 *sensor, struct v4l2_subdev_fh *fh,
+		struct s5k6a3 *sensor, struct v4l2_subdev_pad_config *cfg,
 		u32 pad, enum v4l2_subdev_format_whence which)
 {
 	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
+		return cfg ? v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad) : NULL;
 
 	return &sensor->format;
 }
 
 static int s5k6a3_set_fmt(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_format *fmt)
 {
 	struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
@@ -141,7 +141,7 @@
 
 	s5k6a3_try_format(&fmt->format);
 
-	mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+	mf = __s5k6a3_get_format(sensor, cfg, fmt->pad, fmt->which);
 	if (mf) {
 		mutex_lock(&sensor->lock);
 		if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
@@ -152,13 +152,13 @@
 }
 
 static int s5k6a3_get_fmt(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
-				  struct v4l2_subdev_format *fmt)
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
 {
 	struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
 	struct v4l2_mbus_framefmt *mf;
 
-	mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+	mf = __s5k6a3_get_format(sensor, cfg, fmt->pad, fmt->which);
 
 	mutex_lock(&sensor->lock);
 	fmt->format = *mf;
@@ -174,7 +174,7 @@
 
 static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
-	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
 
 	*format		= s5k6a3_formats[0];
 	format->width	= S5K6A3_DEFAULT_WIDTH;
diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c
index 2851581..de803a1 100644
--- a/drivers/media/i2c/s5k6aa.c
+++ b/drivers/media/i2c/s5k6aa.c
@@ -348,7 +348,7 @@
 	msg[1].buf = rbuf;
 
 	ret = i2c_transfer(client->adapter, msg, 2);
-	*val = be16_to_cpu(*((u16 *)rbuf));
+	*val = be16_to_cpu(*((__be16 *)rbuf));
 
 	v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val);
 
@@ -996,7 +996,7 @@
  * V4L2 subdev pad level and video operations
  */
 static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_frame_interval_enum *fie)
 {
 	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
@@ -1023,7 +1023,7 @@
 }
 
 static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->index >= ARRAY_SIZE(s5k6aa_formats))
@@ -1034,7 +1034,7 @@
 }
 
 static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_frame_size_enum *fse)
 {
 	int i = ARRAY_SIZE(s5k6aa_formats);
@@ -1056,14 +1056,14 @@
 }
 
 static struct v4l2_rect *
-__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh,
+__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_pad_config *cfg,
 		       enum v4l2_subdev_format_whence which)
 {
 	if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
 		return &s5k6aa->ccd_rect;
 
 	WARN_ON(which != V4L2_SUBDEV_FORMAT_TRY);
-	return v4l2_subdev_get_try_crop(fh, 0);
+	return v4l2_subdev_get_try_crop(&s5k6aa->sd, cfg, 0);
 }
 
 static void s5k6aa_try_format(struct s5k6aa *s5k6aa,
@@ -1087,7 +1087,7 @@
 	mf->field	= V4L2_FIELD_NONE;
 }
 
-static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
@@ -1096,7 +1096,7 @@
 	memset(fmt->reserved, 0, sizeof(fmt->reserved));
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = v4l2_subdev_get_try_format(fh, 0);
+		mf = v4l2_subdev_get_try_format(sd, cfg, 0);
 		fmt->format = *mf;
 		return 0;
 	}
@@ -1108,7 +1108,7 @@
 	return 0;
 }
 
-static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
@@ -1121,8 +1121,8 @@
 	s5k6aa_try_format(s5k6aa, &fmt->format);
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = v4l2_subdev_get_try_format(fh, fmt->pad);
-		crop = v4l2_subdev_get_try_crop(fh, 0);
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+		crop = v4l2_subdev_get_try_crop(sd, cfg, 0);
 	} else {
 		if (s5k6aa->streaming) {
 			ret = -EBUSY;
@@ -1161,17 +1161,21 @@
 	return ret;
 }
 
-static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
-			   struct v4l2_subdev_crop *crop)
+static int s5k6aa_get_selection(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_selection *sel)
 {
 	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
 	struct v4l2_rect *rect;
 
-	memset(crop->reserved, 0, sizeof(crop->reserved));
+	if (sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	memset(sel->reserved, 0, sizeof(sel->reserved));
 
 	mutex_lock(&s5k6aa->lock);
-	rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
-	crop->rect = *rect;
+	rect = __s5k6aa_get_crop_rect(s5k6aa, cfg, sel->which);
+	sel->r = *rect;
 	mutex_unlock(&s5k6aa->lock);
 
 	v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n",
@@ -1180,35 +1184,39 @@
 	return 0;
 }
 
-static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
-			   struct v4l2_subdev_crop *crop)
+static int s5k6aa_set_selection(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_selection *sel)
 {
 	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
 	struct v4l2_mbus_framefmt *mf;
 	unsigned int max_x, max_y;
 	struct v4l2_rect *crop_r;
 
-	mutex_lock(&s5k6aa->lock);
-	crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
+	if (sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
 
-	if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+	mutex_lock(&s5k6aa->lock);
+	crop_r = __s5k6aa_get_crop_rect(s5k6aa, cfg, sel->which);
+
+	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
 		mf = &s5k6aa->preset->mbus_fmt;
 		s5k6aa->apply_crop = 1;
 	} else {
-		mf = v4l2_subdev_get_try_format(fh, 0);
+		mf = v4l2_subdev_get_try_format(sd, cfg, 0);
 	}
-	v4l_bound_align_image(&crop->rect.width, mf->width,
+	v4l_bound_align_image(&sel->r.width, mf->width,
 			      S5K6AA_WIN_WIDTH_MAX, 1,
-			      &crop->rect.height, mf->height,
+			      &sel->r.height, mf->height,
 			      S5K6AA_WIN_HEIGHT_MAX, 1, 0);
 
-	max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1;
-	max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1;
+	max_x = (S5K6AA_WIN_WIDTH_MAX - sel->r.width) & ~1;
+	max_y = (S5K6AA_WIN_HEIGHT_MAX - sel->r.height) & ~1;
 
-	crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x);
-	crop->rect.top  = clamp_t(unsigned int, crop->rect.top, 0, max_y);
+	sel->r.left = clamp_t(unsigned int, sel->r.left, 0, max_x);
+	sel->r.top  = clamp_t(unsigned int, sel->r.top, 0, max_y);
 
-	*crop_r = crop->rect;
+	*crop_r = sel->r;
 
 	mutex_unlock(&s5k6aa->lock);
 
@@ -1224,8 +1232,8 @@
 	.enum_frame_interval	= s5k6aa_enum_frame_interval,
 	.get_fmt		= s5k6aa_get_fmt,
 	.set_fmt		= s5k6aa_set_fmt,
-	.get_crop		= s5k6aa_get_crop,
-	.set_crop		= s5k6aa_set_crop,
+	.get_selection		= s5k6aa_get_selection,
+	.set_selection		= s5k6aa_set_selection,
 };
 
 static const struct v4l2_subdev_video_ops s5k6aa_video_ops = {
@@ -1416,8 +1424,8 @@
  */
 static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
-	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
-	struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0);
+	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0);
 
 	format->colorspace = s5k6aa_formats[0].colorspace;
 	format->code = s5k6aa_formats[0].code;
diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c
index e40d902..e3348db 100644
--- a/drivers/media/i2c/smiapp-pll.c
+++ b/drivers/media/i2c/smiapp-pll.c
@@ -14,14 +14,9 @@
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
  */
 
+#include <linux/device.h>
 #include <linux/gcd.h>
 #include <linux/lcm.h>
 #include <linux/module.h>
diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h
index e8f035a..b98d143 100644
--- a/drivers/media/i2c/smiapp-pll.h
+++ b/drivers/media/i2c/smiapp-pll.h
@@ -14,19 +14,11 @@
  * 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 SMIAPP_PLL_H
 #define SMIAPP_PLL_H
 
-#include <linux/device.h>
-
 /* CSI-2 or CCP-2 */
 #define SMIAPP_PLL_BUS_TYPE_CSI2				0x00
 #define SMIAPP_PLL_BUS_TYPE_PARALLEL				0x01
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index 0df5070..557f25d 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -18,12 +18,6 @@
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
  */
 
 #include <linux/clk.h>
@@ -31,11 +25,13 @@
 #include <linux/device.h>
 #include <linux/gpio.h>
 #include <linux/module.h>
+#include <linux/of_gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/smiapp.h>
 #include <linux/v4l2-mediabus.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
 
 #include "smiapp.h"
 
@@ -348,7 +344,7 @@
 	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, },
 };
 
-const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" };
+static const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" };
 
 #define to_csi_format_idx(fmt) (((unsigned long)(fmt)			\
 				 - (unsigned long)smiapp_csi_data_formats) \
@@ -523,14 +519,12 @@
 static int smiapp_init_controls(struct smiapp_sensor *sensor)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
-	unsigned long *valid_link_freqs = &sensor->valid_link_freqs[
-		sensor->csi_format->compressed - SMIAPP_COMPRESSED_BASE];
-	unsigned int max, i;
 	int rval;
 
 	rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 12);
 	if (rval)
 		return rval;
+
 	sensor->pixel_array->ctrl_handler.lock = &sensor->mutex;
 
 	sensor->analog_gain = v4l2_ctrl_new_std(
@@ -576,21 +570,11 @@
 				     ARRAY_SIZE(smiapp_test_patterns) - 1,
 				     0, 0, smiapp_test_patterns);
 
-	for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) {
-		int max_value = (1 << sensor->csi_format->width) - 1;
-		sensor->test_data[i] =
-			v4l2_ctrl_new_std(
-				&sensor->pixel_array->ctrl_handler,
-				&smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN_RED + i,
-				0, max_value, 1, max_value);
-	}
-
 	if (sensor->pixel_array->ctrl_handler.error) {
 		dev_err(&client->dev,
 			"pixel array controls initialization failed (%d)\n",
 			sensor->pixel_array->ctrl_handler.error);
-		rval = sensor->pixel_array->ctrl_handler.error;
-		goto error;
+		return sensor->pixel_array->ctrl_handler.error;
 	}
 
 	sensor->pixel_array->sd.ctrl_handler =
@@ -600,16 +584,10 @@
 
 	rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0);
 	if (rval)
-		goto error;
+		return rval;
+
 	sensor->src->ctrl_handler.lock = &sensor->mutex;
 
-	for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++);
-
-	sensor->link_freq = v4l2_ctrl_new_int_menu(
-		&sensor->src->ctrl_handler, &smiapp_ctrl_ops,
-		V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs),
-		__ffs(*valid_link_freqs), sensor->platform_data->op_sys_clock);
-
 	sensor->pixel_rate_csi = v4l2_ctrl_new_std(
 		&sensor->src->ctrl_handler, &smiapp_ctrl_ops,
 		V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1);
@@ -618,20 +596,41 @@
 		dev_err(&client->dev,
 			"src controls initialization failed (%d)\n",
 			sensor->src->ctrl_handler.error);
-		rval = sensor->src->ctrl_handler.error;
-		goto error;
+		return sensor->src->ctrl_handler.error;
 	}
 
-	sensor->src->sd.ctrl_handler =
-		&sensor->src->ctrl_handler;
+	sensor->src->sd.ctrl_handler = &sensor->src->ctrl_handler;
 
 	return 0;
+}
 
-error:
-	v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler);
-	v4l2_ctrl_handler_free(&sensor->src->ctrl_handler);
+/*
+ * For controls that require information on available media bus codes
+ * and linke frequencies.
+ */
+static int smiapp_init_late_controls(struct smiapp_sensor *sensor)
+{
+	unsigned long *valid_link_freqs = &sensor->valid_link_freqs[
+		sensor->csi_format->compressed - SMIAPP_COMPRESSED_BASE];
+	unsigned int max, i;
 
-	return rval;
+	for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) {
+		int max_value = (1 << sensor->csi_format->width) - 1;
+
+		sensor->test_data[i] = v4l2_ctrl_new_std(
+				&sensor->pixel_array->ctrl_handler,
+				&smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN_RED + i,
+				0, max_value, 1, max_value);
+	}
+
+	for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++);
+
+	sensor->link_freq = v4l2_ctrl_new_int_menu(
+		&sensor->src->ctrl_handler, &smiapp_ctrl_ops,
+		V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs),
+		__ffs(*valid_link_freqs), sensor->platform_data->op_sys_clock);
+
+	return sensor->src->ctrl_handler.error;
 }
 
 static void smiapp_free_controls(struct smiapp_sensor *sensor)
@@ -1487,7 +1486,7 @@
 	if (rval < 0)
 		goto out;
 
-	if ((sensor->flash_capability &
+	if ((sensor->limits[SMIAPP_LIMIT_FLASH_MODE_CAPABILITY] &
 	     (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE |
 	      SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) &&
 	    sensor->platform_data->strobe_setup != NULL &&
@@ -1558,7 +1557,7 @@
 }
 
 static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev,
-				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_mbus_code_enum *code)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(subdev);
@@ -1612,13 +1611,13 @@
 }
 
 static int __smiapp_get_format(struct v4l2_subdev *subdev,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_format *fmt)
 {
 	struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad);
+		fmt->format = *v4l2_subdev_get_try_format(subdev, cfg, fmt->pad);
 	} else {
 		struct v4l2_rect *r;
 
@@ -1637,21 +1636,21 @@
 }
 
 static int smiapp_get_format(struct v4l2_subdev *subdev,
-			     struct v4l2_subdev_fh *fh,
+			     struct v4l2_subdev_pad_config *cfg,
 			     struct v4l2_subdev_format *fmt)
 {
 	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
 	int rval;
 
 	mutex_lock(&sensor->mutex);
-	rval = __smiapp_get_format(subdev, fh, fmt);
+	rval = __smiapp_get_format(subdev, cfg, fmt);
 	mutex_unlock(&sensor->mutex);
 
 	return rval;
 }
 
 static void smiapp_get_crop_compose(struct v4l2_subdev *subdev,
-				    struct v4l2_subdev_fh *fh,
+				    struct v4l2_subdev_pad_config *cfg,
 				    struct v4l2_rect **crops,
 				    struct v4l2_rect **comps, int which)
 {
@@ -1667,12 +1666,12 @@
 	} else {
 		if (crops) {
 			for (i = 0; i < subdev->entity.num_pads; i++) {
-				crops[i] = v4l2_subdev_get_try_crop(fh, i);
+				crops[i] = v4l2_subdev_get_try_crop(subdev, cfg, i);
 				BUG_ON(!crops[i]);
 			}
 		}
 		if (comps) {
-			*comps = v4l2_subdev_get_try_compose(fh,
+			*comps = v4l2_subdev_get_try_compose(subdev, cfg,
 							     SMIAPP_PAD_SINK);
 			BUG_ON(!*comps);
 		}
@@ -1681,14 +1680,14 @@
 
 /* Changes require propagation only on sink pad. */
 static void smiapp_propagate(struct v4l2_subdev *subdev,
-			     struct v4l2_subdev_fh *fh, int which,
+			     struct v4l2_subdev_pad_config *cfg, int which,
 			     int target)
 {
 	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
 	struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
 	struct v4l2_rect *comp, *crops[SMIAPP_PADS];
 
-	smiapp_get_crop_compose(subdev, fh, crops, &comp, which);
+	smiapp_get_crop_compose(subdev, cfg, crops, &comp, which);
 
 	switch (target) {
 	case V4L2_SEL_TGT_CROP:
@@ -1731,7 +1730,7 @@
 }
 
 static int smiapp_set_format_source(struct v4l2_subdev *subdev,
-				    struct v4l2_subdev_fh *fh,
+				    struct v4l2_subdev_pad_config *cfg,
 				    struct v4l2_subdev_format *fmt)
 {
 	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
@@ -1742,7 +1741,7 @@
 	unsigned int i;
 	int rval;
 
-	rval = __smiapp_get_format(subdev, fh, fmt);
+	rval = __smiapp_get_format(subdev, cfg, fmt);
 	if (rval)
 		return rval;
 
@@ -1784,7 +1783,7 @@
 }
 
 static int smiapp_set_format(struct v4l2_subdev *subdev,
-			     struct v4l2_subdev_fh *fh,
+			     struct v4l2_subdev_pad_config *cfg,
 			     struct v4l2_subdev_format *fmt)
 {
 	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
@@ -1796,7 +1795,7 @@
 	if (fmt->pad == ssd->source_pad) {
 		int rval;
 
-		rval = smiapp_set_format_source(subdev, fh, fmt);
+		rval = smiapp_set_format_source(subdev, cfg, fmt);
 
 		mutex_unlock(&sensor->mutex);
 
@@ -1818,7 +1817,7 @@
 		      sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE],
 		      sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]);
 
-	smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which);
+	smiapp_get_crop_compose(subdev, cfg, crops, NULL, fmt->which);
 
 	crops[ssd->sink_pad]->left = 0;
 	crops[ssd->sink_pad]->top = 0;
@@ -1826,7 +1825,7 @@
 	crops[ssd->sink_pad]->height = fmt->format.height;
 	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
 		ssd->sink_fmt = *crops[ssd->sink_pad];
-	smiapp_propagate(subdev, fh, fmt->which,
+	smiapp_propagate(subdev, cfg, fmt->which,
 			 V4L2_SEL_TGT_CROP);
 
 	mutex_unlock(&sensor->mutex);
@@ -1879,7 +1878,7 @@
 }
 
 static void smiapp_set_compose_binner(struct v4l2_subdev *subdev,
-				      struct v4l2_subdev_fh *fh,
+				      struct v4l2_subdev_pad_config *cfg,
 				      struct v4l2_subdev_selection *sel,
 				      struct v4l2_rect **crops,
 				      struct v4l2_rect *comp)
@@ -1927,7 +1926,7 @@
  * result.
  */
 static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev,
-				      struct v4l2_subdev_fh *fh,
+				      struct v4l2_subdev_pad_config *cfg,
 				      struct v4l2_subdev_selection *sel,
 				      struct v4l2_rect **crops,
 				      struct v4l2_rect *comp)
@@ -2043,25 +2042,25 @@
 }
 /* We're only called on source pads. This function sets scaling. */
 static int smiapp_set_compose(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_selection *sel)
 {
 	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
 	struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
 	struct v4l2_rect *comp, *crops[SMIAPP_PADS];
 
-	smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which);
+	smiapp_get_crop_compose(subdev, cfg, crops, &comp, sel->which);
 
 	sel->r.top = 0;
 	sel->r.left = 0;
 
 	if (ssd == sensor->binner)
-		smiapp_set_compose_binner(subdev, fh, sel, crops, comp);
+		smiapp_set_compose_binner(subdev, cfg, sel, crops, comp);
 	else
-		smiapp_set_compose_scaler(subdev, fh, sel, crops, comp);
+		smiapp_set_compose_scaler(subdev, cfg, sel, crops, comp);
 
 	*comp = sel->r;
-	smiapp_propagate(subdev, fh, sel->which,
+	smiapp_propagate(subdev, cfg, sel->which,
 			 V4L2_SEL_TGT_COMPOSE);
 
 	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
@@ -2114,7 +2113,7 @@
 }
 
 static int smiapp_set_crop(struct v4l2_subdev *subdev,
-			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_selection *sel)
 {
 	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
@@ -2122,7 +2121,7 @@
 	struct v4l2_rect *src_size, *crops[SMIAPP_PADS];
 	struct v4l2_rect _r;
 
-	smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which);
+	smiapp_get_crop_compose(subdev, cfg, crops, NULL, sel->which);
 
 	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
 		if (sel->pad == ssd->sink_pad)
@@ -2133,15 +2132,15 @@
 		if (sel->pad == ssd->sink_pad) {
 			_r.left = 0;
 			_r.top = 0;
-			_r.width = v4l2_subdev_get_try_format(fh, sel->pad)
+			_r.width = v4l2_subdev_get_try_format(subdev, cfg, sel->pad)
 				->width;
-			_r.height = v4l2_subdev_get_try_format(fh, sel->pad)
+			_r.height = v4l2_subdev_get_try_format(subdev, cfg, sel->pad)
 				->height;
 			src_size = &_r;
 		} else {
 			src_size =
 				v4l2_subdev_get_try_compose(
-					fh, ssd->sink_pad);
+					subdev, cfg, ssd->sink_pad);
 		}
 	}
 
@@ -2159,14 +2158,14 @@
 	*crops[sel->pad] = sel->r;
 
 	if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK)
-		smiapp_propagate(subdev, fh, sel->which,
+		smiapp_propagate(subdev, cfg, sel->which,
 				 V4L2_SEL_TGT_CROP);
 
 	return 0;
 }
 
 static int __smiapp_get_selection(struct v4l2_subdev *subdev,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_selection *sel)
 {
 	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
@@ -2179,13 +2178,13 @@
 	if (ret)
 		return ret;
 
-	smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which);
+	smiapp_get_crop_compose(subdev, cfg, crops, &comp, sel->which);
 
 	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
 		sink_fmt = ssd->sink_fmt;
 	} else {
 		struct v4l2_mbus_framefmt *fmt =
-			v4l2_subdev_get_try_format(fh, ssd->sink_pad);
+			v4l2_subdev_get_try_format(subdev, cfg, ssd->sink_pad);
 
 		sink_fmt.left = 0;
 		sink_fmt.top = 0;
@@ -2221,20 +2220,20 @@
 }
 
 static int smiapp_get_selection(struct v4l2_subdev *subdev,
-				struct v4l2_subdev_fh *fh,
+				struct v4l2_subdev_pad_config *cfg,
 				struct v4l2_subdev_selection *sel)
 {
 	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
 	int rval;
 
 	mutex_lock(&sensor->mutex);
-	rval = __smiapp_get_selection(subdev, fh, sel);
+	rval = __smiapp_get_selection(subdev, cfg, sel);
 	mutex_unlock(&sensor->mutex);
 
 	return rval;
 }
 static int smiapp_set_selection(struct v4l2_subdev *subdev,
-				struct v4l2_subdev_fh *fh,
+				struct v4l2_subdev_pad_config *cfg,
 				struct v4l2_subdev_selection *sel)
 {
 	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
@@ -2260,10 +2259,10 @@
 
 	switch (sel->target) {
 	case V4L2_SEL_TGT_CROP:
-		ret = smiapp_set_crop(subdev, fh, sel);
+		ret = smiapp_set_crop(subdev, cfg, sel);
 		break;
 	case V4L2_SEL_TGT_COMPOSE:
-		ret = smiapp_set_compose(subdev, fh, sel);
+		ret = smiapp_set_compose(subdev, cfg, sel);
 		break;
 	default:
 		ret = -EINVAL;
@@ -2338,10 +2337,9 @@
  * V4L2 subdev core operations
  */
 
-static int smiapp_identify_module(struct v4l2_subdev *subdev)
+static int smiapp_identify_module(struct smiapp_sensor *sensor)
 {
-	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
-	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
 	struct smiapp_module_info *minfo = &sensor->minfo;
 	unsigned int i;
 	int rval = 0;
@@ -2464,8 +2462,6 @@
 		minfo->name, minfo->manufacturer_id, minfo->model_id,
 		minfo->revision_number_major);
 
-	strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name));
-
 	return 0;
 }
 
@@ -2473,13 +2469,71 @@
 static const struct v4l2_subdev_internal_ops smiapp_internal_ops;
 static const struct media_entity_operations smiapp_entity_ops;
 
-static int smiapp_registered(struct v4l2_subdev *subdev)
+static int smiapp_register_subdevs(struct smiapp_sensor *sensor)
 {
-	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
-	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+	struct smiapp_subdev *ssds[] = {
+		sensor->scaler,
+		sensor->binner,
+		sensor->pixel_array,
+	};
+	unsigned int i;
+	int rval;
+
+	for (i = 0; i < SMIAPP_SUBDEVS - 1; i++) {
+		struct smiapp_subdev *this = ssds[i + 1];
+		struct smiapp_subdev *last = ssds[i];
+
+		if (!last)
+			continue;
+
+		rval = media_entity_init(&this->sd.entity,
+					 this->npads, this->pads, 0);
+		if (rval) {
+			dev_err(&client->dev,
+				"media_entity_init failed\n");
+			return rval;
+		}
+
+		rval = media_entity_create_link(&this->sd.entity,
+						this->source_pad,
+						&last->sd.entity,
+						last->sink_pad,
+						MEDIA_LNK_FL_ENABLED |
+						MEDIA_LNK_FL_IMMUTABLE);
+		if (rval) {
+			dev_err(&client->dev,
+				"media_entity_create_link failed\n");
+			return rval;
+		}
+
+		rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev,
+						   &this->sd);
+		if (rval) {
+			dev_err(&client->dev,
+				"v4l2_device_register_subdev failed\n");
+			return rval;
+		}
+	}
+
+	return 0;
+}
+
+static void smiapp_cleanup(struct smiapp_sensor *sensor)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+
+	device_remove_file(&client->dev, &dev_attr_nvm);
+	device_remove_file(&client->dev, &dev_attr_ident);
+
+	smiapp_free_controls(sensor);
+}
+
+static int smiapp_init(struct smiapp_sensor *sensor)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
 	struct smiapp_pll *pll = &sensor->pll;
 	struct smiapp_subdev *last = NULL;
-	u32 tmp;
 	unsigned int i;
 	int rval;
 
@@ -2490,7 +2544,7 @@
 	}
 
 	if (!sensor->platform_data->set_xclk) {
-		sensor->ext_clk = devm_clk_get(&client->dev, "ext_clk");
+		sensor->ext_clk = devm_clk_get(&client->dev, NULL);
 		if (IS_ERR(sensor->ext_clk)) {
 			dev_err(&client->dev, "could not get clock\n");
 			return PTR_ERR(sensor->ext_clk);
@@ -2522,7 +2576,7 @@
 	if (rval)
 		return -ENODEV;
 
-	rval = smiapp_identify_module(subdev);
+	rval = smiapp_identify_module(sensor);
 	if (rval) {
 		rval = -ENODEV;
 		goto out_power_off;
@@ -2602,13 +2656,13 @@
 		if (sensor->nvm == NULL) {
 			dev_err(&client->dev, "nvm buf allocation failed\n");
 			rval = -ENOMEM;
-			goto out_ident_release;
+			goto out_cleanup;
 		}
 
 		if (device_create_file(&client->dev, &dev_attr_nvm) != 0) {
 			dev_err(&client->dev, "sysfs nvm entry failed\n");
 			rval = -EBUSY;
-			goto out_ident_release;
+			goto out_cleanup;
 		}
 	}
 
@@ -2643,18 +2697,11 @@
 	pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
 	pll->csi2.lanes = sensor->platform_data->lanes;
 	pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
-	pll->flags = smiapp_call_quirk(sensor, pll_flags);
 	pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
 	/* Profile 0 sensors have no separate OP clock branch. */
 	if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
 		pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
 
-	rval = smiapp_get_mbus_formats(sensor);
-	if (rval) {
-		rval = -ENODEV;
-		goto out_nvm_release;
-	}
-
 	for (i = 0; i < SMIAPP_SUBDEVS; i++) {
 		struct {
 			struct smiapp_subdev *ssd;
@@ -2711,34 +2758,6 @@
 		this->sd.owner = THIS_MODULE;
 		v4l2_set_subdevdata(&this->sd, client);
 
-		rval = media_entity_init(&this->sd.entity,
-					 this->npads, this->pads, 0);
-		if (rval) {
-			dev_err(&client->dev,
-				"media_entity_init failed\n");
-			goto out_nvm_release;
-		}
-
-		rval = media_entity_create_link(&this->sd.entity,
-						this->source_pad,
-						&last->sd.entity,
-						last->sink_pad,
-						MEDIA_LNK_FL_ENABLED |
-						MEDIA_LNK_FL_IMMUTABLE);
-		if (rval) {
-			dev_err(&client->dev,
-				"media_entity_create_link failed\n");
-			goto out_nvm_release;
-		}
-
-		rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev,
-						   &this->sd);
-		if (rval) {
-			dev_err(&client->dev,
-				"v4l2_device_register_subdev failed\n");
-			goto out_nvm_release;
-		}
-
 		last = this;
 	}
 
@@ -2750,40 +2769,66 @@
 	smiapp_read_frame_fmt(sensor);
 	rval = smiapp_init_controls(sensor);
 	if (rval < 0)
-		goto out_nvm_release;
+		goto out_cleanup;
+
+	rval = smiapp_call_quirk(sensor, init);
+	if (rval)
+		goto out_cleanup;
+
+	rval = smiapp_get_mbus_formats(sensor);
+	if (rval) {
+		rval = -ENODEV;
+		goto out_cleanup;
+	}
+
+	rval = smiapp_init_late_controls(sensor);
+	if (rval) {
+		rval = -ENODEV;
+		goto out_cleanup;
+	}
 
 	mutex_lock(&sensor->mutex);
 	rval = smiapp_update_mode(sensor);
 	mutex_unlock(&sensor->mutex);
 	if (rval) {
 		dev_err(&client->dev, "update mode failed\n");
-		goto out_nvm_release;
+		goto out_cleanup;
 	}
 
 	sensor->streaming = false;
 	sensor->dev_init_done = true;
 
-	/* check flash capability */
-	rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp);
-	sensor->flash_capability = tmp;
-	if (rval)
-		goto out_nvm_release;
-
 	smiapp_power_off(sensor);
 
 	return 0;
 
-out_nvm_release:
-	device_remove_file(&client->dev, &dev_attr_nvm);
-
-out_ident_release:
-	device_remove_file(&client->dev, &dev_attr_ident);
+out_cleanup:
+	smiapp_cleanup(sensor);
 
 out_power_off:
 	smiapp_power_off(sensor);
 	return rval;
 }
 
+static int smiapp_registered(struct v4l2_subdev *subdev)
+{
+	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+	struct i2c_client *client = v4l2_get_subdevdata(subdev);
+	int rval;
+
+	if (!client->dev.of_node) {
+		rval = smiapp_init(sensor);
+		if (rval)
+			return rval;
+	}
+
+	rval = smiapp_register_subdevs(sensor);
+	if (rval)
+		smiapp_cleanup(sensor);
+
+	return rval;
+}
+
 static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
 	struct smiapp_subdev *ssd = to_smiapp_subdev(sd);
@@ -2796,8 +2841,8 @@
 
 	for (i = 0; i < ssd->npads; i++) {
 		struct v4l2_mbus_framefmt *try_fmt =
-			v4l2_subdev_get_try_format(fh, i);
-		struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i);
+			v4l2_subdev_get_try_format(sd, fh->pad, i);
+		struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, i);
 		struct v4l2_rect *try_comp;
 
 		try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
@@ -2813,7 +2858,7 @@
 		if (ssd != sensor->pixel_array)
 			continue;
 
-		try_comp = v4l2_subdev_get_try_compose(fh, i);
+		try_comp = v4l2_subdev_get_try_compose(sd, fh->pad, i);
 		*try_comp = *try_crop;
 	}
 
@@ -2927,19 +2972,103 @@
 
 #endif /* CONFIG_PM */
 
+static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev)
+{
+	struct smiapp_platform_data *pdata;
+	struct v4l2_of_endpoint bus_cfg;
+	struct device_node *ep;
+	uint32_t asize;
+	int rval;
+
+	if (!dev->of_node)
+		return dev->platform_data;
+
+	ep = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (!ep)
+		return NULL;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		goto out_err;
+
+	v4l2_of_parse_endpoint(ep, &bus_cfg);
+
+	switch (bus_cfg.bus_type) {
+	case V4L2_MBUS_CSI2:
+		pdata->csi_signalling_mode = SMIAPP_CSI_SIGNALLING_MODE_CSI2;
+		break;
+		/* FIXME: add CCP2 support. */
+	default:
+		goto out_err;
+	}
+
+	pdata->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
+	dev_dbg(dev, "lanes %u\n", pdata->lanes);
+
+	/* xshutdown GPIO is optional */
+	pdata->xshutdown = of_get_named_gpio(dev->of_node, "reset-gpios", 0);
+
+	/* NVM size is not mandatory */
+	of_property_read_u32(dev->of_node, "nokia,nvm-size",
+				    &pdata->nvm_size);
+
+	rval = of_property_read_u32(dev->of_node, "clock-frequency",
+				    &pdata->ext_clk);
+	if (rval) {
+		dev_warn(dev, "can't get clock-frequency\n");
+		goto out_err;
+	}
+
+	dev_dbg(dev, "reset %d, nvm %d, clk %d, csi %d\n", pdata->xshutdown,
+		pdata->nvm_size, pdata->ext_clk, pdata->csi_signalling_mode);
+
+	rval = of_get_property(ep, "link-frequencies", &asize) ? 0 : -ENOENT;
+	if (rval) {
+		dev_warn(dev, "can't get link-frequencies array size\n");
+		goto out_err;
+	}
+
+	pdata->op_sys_clock = devm_kzalloc(dev, asize, GFP_KERNEL);
+	if (!pdata->op_sys_clock) {
+		rval = -ENOMEM;
+		goto out_err;
+	}
+
+	asize /= sizeof(*pdata->op_sys_clock);
+	rval = of_property_read_u64_array(
+		ep, "link-frequencies", pdata->op_sys_clock, asize);
+	if (rval) {
+		dev_warn(dev, "can't get link-frequencies\n");
+		goto out_err;
+	}
+
+	for (; asize > 0; asize--)
+		dev_dbg(dev, "freq %d: %lld\n", asize - 1,
+			pdata->op_sys_clock[asize - 1]);
+
+	of_node_put(ep);
+	return pdata;
+
+out_err:
+	of_node_put(ep);
+	return NULL;
+}
+
 static int smiapp_probe(struct i2c_client *client,
 			const struct i2c_device_id *devid)
 {
 	struct smiapp_sensor *sensor;
+	struct smiapp_platform_data *pdata = smiapp_get_pdata(&client->dev);
+	int rval;
 
-	if (client->dev.platform_data == NULL)
+	if (pdata == NULL)
 		return -ENODEV;
 
 	sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
 	if (sensor == NULL)
 		return -ENOMEM;
 
-	sensor->platform_data = client->dev.platform_data;
+	sensor->platform_data = pdata;
 	mutex_init(&sensor->mutex);
 	mutex_init(&sensor->power_mutex);
 	sensor->src = &sensor->ssds[sensor->ssds_used];
@@ -2950,8 +3079,27 @@
 	sensor->src->sensor = sensor;
 
 	sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE;
-	return media_entity_init(&sensor->src->sd.entity, 2,
+	rval = media_entity_init(&sensor->src->sd.entity, 2,
 				 sensor->src->pads, 0);
+	if (rval < 0)
+		return rval;
+
+	if (client->dev.of_node) {
+		rval = smiapp_init(sensor);
+		if (rval)
+			goto out_media_entity_cleanup;
+	}
+
+	rval = v4l2_async_register_subdev(&sensor->src->sd);
+	if (rval < 0)
+		goto out_media_entity_cleanup;
+
+	return 0;
+
+out_media_entity_cleanup:
+	media_entity_cleanup(&sensor->src->sd.entity);
+
+	return rval;
 }
 
 static int smiapp_remove(struct i2c_client *client)
@@ -2960,6 +3108,8 @@
 	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
 	unsigned int i;
 
+	v4l2_async_unregister_subdev(subdev);
+
 	if (sensor->power_count) {
 		if (gpio_is_valid(sensor->platform_data->xshutdown))
 			gpio_set_value(sensor->platform_data->xshutdown, 0);
@@ -2970,19 +3120,20 @@
 		sensor->power_count = 0;
 	}
 
-	device_remove_file(&client->dev, &dev_attr_ident);
-	if (sensor->nvm)
-		device_remove_file(&client->dev, &dev_attr_nvm);
-
 	for (i = 0; i < sensor->ssds_used; i++) {
 		v4l2_device_unregister_subdev(&sensor->ssds[i].sd);
 		media_entity_cleanup(&sensor->ssds[i].sd.entity);
 	}
-	smiapp_free_controls(sensor);
+	smiapp_cleanup(sensor);
 
 	return 0;
 }
 
+static const struct of_device_id smiapp_of_table[] = {
+	{ .compatible = "nokia,smia" },
+	{ },
+};
+
 static const struct i2c_device_id smiapp_id_table[] = {
 	{ SMIAPP_NAME, 0 },
 	{ },
@@ -2996,6 +3147,7 @@
 
 static struct i2c_driver smiapp_i2c_driver = {
 	.driver	= {
+		.of_match_table = smiapp_of_table,
 		.name = SMIAPP_NAME,
 		.pm = &smiapp_pm_ops,
 	},
diff --git a/drivers/media/i2c/smiapp/smiapp-limits.c b/drivers/media/i2c/smiapp/smiapp-limits.c
index 847cb23..784b114 100644
--- a/drivers/media/i2c/smiapp/smiapp-limits.c
+++ b/drivers/media/i2c/smiapp/smiapp-limits.c
@@ -14,12 +14,6 @@
  * 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 "smiapp.h"
diff --git a/drivers/media/i2c/smiapp/smiapp-limits.h b/drivers/media/i2c/smiapp/smiapp-limits.h
index 343e9c3..b201248 100644
--- a/drivers/media/i2c/smiapp/smiapp-limits.h
+++ b/drivers/media/i2c/smiapp/smiapp-limits.h
@@ -14,12 +14,6 @@
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
  */
 
 #define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY			0
diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c
index e0bee87..abf9ea7 100644
--- a/drivers/media/i2c/smiapp/smiapp-quirk.c
+++ b/drivers/media/i2c/smiapp/smiapp-quirk.c
@@ -14,12 +14,6 @@
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
  */
 
 #include <linux/delay.h>
@@ -220,9 +214,11 @@
 	return smiapp_write_8(sensor, 0x3328, 0x80);
 }
 
-static unsigned long jt8ev1_pll_flags(struct smiapp_sensor *sensor)
+static int jt8ev1_init(struct smiapp_sensor *sensor)
 {
-	return SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
+	sensor->pll.flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
+
+	return 0;
 }
 
 const struct smiapp_quirk smiapp_jt8ev1_quirk = {
@@ -230,7 +226,7 @@
 	.post_poweron = jt8ev1_post_poweron,
 	.pre_streamon = jt8ev1_pre_streamon,
 	.post_streamoff = jt8ev1_post_streamoff,
-	.pll_flags = jt8ev1_pll_flags,
+	.init = jt8ev1_init,
 };
 
 static int tcm8500md_limits(struct smiapp_sensor *sensor)
diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h
index 46e9ea8..dac5566 100644
--- a/drivers/media/i2c/smiapp/smiapp-quirk.h
+++ b/drivers/media/i2c/smiapp/smiapp-quirk.h
@@ -14,12 +14,6 @@
  * 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 __SMIAPP_QUIRK__
@@ -35,6 +29,9 @@
  * @post_poweron: Called always after the sensor has been fully powered on.
  * @pre_streamon: Called just before streaming is enabled.
  * @post_streamon: Called right after stopping streaming.
+ * @pll_flags: Return flags for the PLL calculator.
+ * @init: Quirk initialisation, called the last in probe(). This is
+ *	  also appropriate for adding sensor specific controls, for instance.
  * @reg_access: Register access quirk. The quirk may divert the access
  *		to another register, or no register at all.
  *
@@ -53,6 +50,7 @@
 	int (*pre_streamon)(struct smiapp_sensor *sensor);
 	int (*post_streamoff)(struct smiapp_sensor *sensor);
 	unsigned long (*pll_flags)(struct smiapp_sensor *sensor);
+	int (*init)(struct smiapp_sensor *sensor);
 	int (*reg_access)(struct smiapp_sensor *sensor, bool write, u32 *reg,
 			  u32 *val);
 	unsigned long flags;
@@ -74,14 +72,14 @@
 		.val = _val,		\
 	}
 
-#define smiapp_call_quirk(_sensor, _quirk, ...)				\
-	(_sensor->minfo.quirk &&					\
-	 _sensor->minfo.quirk->_quirk ?					\
-	 _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0)
+#define smiapp_call_quirk(sensor, _quirk, ...)				\
+	((sensor)->minfo.quirk &&					\
+	 (sensor)->minfo.quirk->_quirk ?				\
+	 (sensor)->minfo.quirk->_quirk(sensor, ##__VA_ARGS__) : 0)
 
-#define smiapp_needs_quirk(_sensor, _quirk)		\
-	(_sensor->minfo.quirk ?				\
-	 _sensor->minfo.quirk->flags & _quirk : 0)
+#define smiapp_needs_quirk(sensor, _quirk)		\
+	((sensor)->minfo.quirk ?			\
+	 (sensor)->minfo.quirk->flags & _quirk : 0)
 
 extern const struct smiapp_quirk smiapp_jt8ev1_quirk;
 extern const struct smiapp_quirk smiapp_imx125es_quirk;
diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h
index c488ef0..f928d4c 100644
--- a/drivers/media/i2c/smiapp/smiapp-reg-defs.h
+++ b/drivers/media/i2c/smiapp/smiapp-reg-defs.h
@@ -14,12 +14,6 @@
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
  */
 #define SMIAPP_REG_MK_U8(r) ((SMIAPP_REG_8BIT << 16) | (r))
 #define SMIAPP_REG_MK_U16(r) ((SMIAPP_REG_16BIT << 16) | (r))
diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h
index b0dcbb8..4c8b406 100644
--- a/drivers/media/i2c/smiapp/smiapp-reg.h
+++ b/drivers/media/i2c/smiapp/smiapp-reg.h
@@ -14,12 +14,6 @@
  * 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 __SMIAPP_REG_H_
diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c
index a209800..6b6c20b 100644
--- a/drivers/media/i2c/smiapp/smiapp-regs.c
+++ b/drivers/media/i2c/smiapp/smiapp-regs.c
@@ -14,12 +14,6 @@
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
  */
 
 #include <linux/delay.h>
diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h
index 3552112..6dd0e49 100644
--- a/drivers/media/i2c/smiapp/smiapp-regs.h
+++ b/drivers/media/i2c/smiapp/smiapp-regs.h
@@ -14,12 +14,6 @@
  * 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 SMIAPP_REGS_H
diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h
index f88f8ec..ed010a8 100644
--- a/drivers/media/i2c/smiapp/smiapp.h
+++ b/drivers/media/i2c/smiapp/smiapp.h
@@ -14,12 +14,6 @@
  * 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 __SMIAPP_PRIV_H_
@@ -222,7 +216,6 @@
 	u8 scaling_mode;
 
 	u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */
-	u8 flash_capability;
 	u8 frame_skip;
 
 	int power_count;
diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig
index b8e3f01..bc88e28 100644
--- a/drivers/media/i2c/soc_camera/Kconfig
+++ b/drivers/media/i2c/soc_camera/Kconfig
@@ -45,6 +45,7 @@
 	  This driver supports MT9V022 cameras from Micron
 
 config SOC_CAMERA_OV2640
+	depends on !KERNEL_3_17
 	tristate "ov2640 camera support"
 	depends on m
 	depends on SOC_CAMERA && I2C
diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c
index a35a2f9..64b2e7b 100644
--- a/drivers/media/i2c/soc_camera/mt9m111.c
+++ b/drivers/media/i2c/soc_camera/mt9m111.c
@@ -1016,7 +1016,6 @@
 
 	v4l2_async_unregister_subdev(&mt9m111->subdev);
 	v4l2_clk_put(mt9m111->clk);
-	v4l2_device_unregister_subdev(&mt9m111->subdev);
 	v4l2_ctrl_handler_free(&mt9m111->hdl);
 
 	return 0;
diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c
index 0c05283..e84a7cc 100644
--- a/drivers/media/i2c/soc_camera/ov2640.c
+++ b/drivers/media/i2c/soc_camera/ov2640.c
@@ -18,6 +18,9 @@
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
 #include <linux/v4l2-mediabus.h>
 #include <linux/videodev2.h>
 
@@ -25,6 +28,7 @@
 #include <media/v4l2-clk.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-ctrls.h>
+#include <media/v4l2-image-sizes.h>
 
 #define VAL_SET(x, mask, rshift, lshift)  \
 		((((x) >> rshift) & mask) << lshift)
@@ -268,33 +272,10 @@
 	u8 value;
 };
 
-/* Supported resolutions */
-enum ov2640_width {
-	W_QCIF	= 176,
-	W_QVGA	= 320,
-	W_CIF	= 352,
-	W_VGA	= 640,
-	W_SVGA	= 800,
-	W_XGA	= 1024,
-	W_SXGA	= 1280,
-	W_UXGA	= 1600,
-};
-
-enum ov2640_height {
-	H_QCIF	= 144,
-	H_QVGA	= 240,
-	H_CIF	= 288,
-	H_VGA	= 480,
-	H_SVGA	= 600,
-	H_XGA	= 768,
-	H_SXGA	= 1024,
-	H_UXGA	= 1200,
-};
-
 struct ov2640_win_size {
 	char				*name;
-	enum ov2640_width		width;
-	enum ov2640_height		height;
+	u32				width;
+	u32				height;
 	const struct regval_list	*regs;
 };
 
@@ -305,6 +286,10 @@
 	u32	cfmt_code;
 	struct v4l2_clk			*clk;
 	const struct ov2640_win_size	*win;
+
+	struct soc_camera_subdev_desc	ssdd_dt;
+	struct gpio_desc *resetb_gpio;
+	struct gpio_desc *pwdn_gpio;
 };
 
 /*
@@ -495,17 +480,17 @@
 static const struct regval_list ov2640_size_change_preamble_regs[] = {
 	{ BANK_SEL, BANK_SEL_DSP },
 	{ RESET, RESET_DVP },
-	{ HSIZE8, HSIZE8_SET(W_UXGA) },
-	{ VSIZE8, VSIZE8_SET(H_UXGA) },
+	{ HSIZE8, HSIZE8_SET(UXGA_WIDTH) },
+	{ VSIZE8, VSIZE8_SET(UXGA_HEIGHT) },
 	{ CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN |
 		 CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN },
-	{ HSIZE, HSIZE_SET(W_UXGA) },
-	{ VSIZE, VSIZE_SET(H_UXGA) },
+	{ HSIZE, HSIZE_SET(UXGA_WIDTH) },
+	{ VSIZE, VSIZE_SET(UXGA_HEIGHT) },
 	{ XOFFL, XOFFL_SET(0) },
 	{ YOFFL, YOFFL_SET(0) },
-	{ VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) |
+	{ VHYX, VHYX_HSIZE_SET(UXGA_WIDTH) | VHYX_VSIZE_SET(UXGA_HEIGHT) |
 		VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)},
-	{ TEST, TEST_HSIZE_SET(W_UXGA) },
+	{ TEST, TEST_HSIZE_SET(UXGA_WIDTH) },
 	ENDMARKER,
 };
 
@@ -519,45 +504,45 @@
 	{ RESET, 0x00}
 
 static const struct regval_list ov2640_qcif_regs[] = {
-	PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4),
+	PER_SIZE_REG_SEQ(QCIF_WIDTH, QCIF_HEIGHT, 3, 3, 4),
 	ENDMARKER,
 };
 
 static const struct regval_list ov2640_qvga_regs[] = {
-	PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4),
+	PER_SIZE_REG_SEQ(QVGA_WIDTH, QVGA_HEIGHT, 2, 2, 4),
 	ENDMARKER,
 };
 
 static const struct regval_list ov2640_cif_regs[] = {
-	PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8),
+	PER_SIZE_REG_SEQ(CIF_WIDTH, CIF_HEIGHT, 2, 2, 8),
 	ENDMARKER,
 };
 
 static const struct regval_list ov2640_vga_regs[] = {
-	PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2),
+	PER_SIZE_REG_SEQ(VGA_WIDTH, VGA_HEIGHT, 0, 0, 2),
 	ENDMARKER,
 };
 
 static const struct regval_list ov2640_svga_regs[] = {
-	PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2),
+	PER_SIZE_REG_SEQ(SVGA_WIDTH, SVGA_HEIGHT, 1, 1, 2),
 	ENDMARKER,
 };
 
 static const struct regval_list ov2640_xga_regs[] = {
-	PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2),
+	PER_SIZE_REG_SEQ(XGA_WIDTH, XGA_HEIGHT, 0, 0, 2),
 	{ CTRLI,    0x00},
 	ENDMARKER,
 };
 
 static const struct regval_list ov2640_sxga_regs[] = {
-	PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2),
+	PER_SIZE_REG_SEQ(SXGA_WIDTH, SXGA_HEIGHT, 0, 0, 2),
 	{ CTRLI,    0x00},
 	{ R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE },
 	ENDMARKER,
 };
 
 static const struct regval_list ov2640_uxga_regs[] = {
-	PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0),
+	PER_SIZE_REG_SEQ(UXGA_WIDTH, UXGA_HEIGHT, 0, 0, 0),
 	{ CTRLI,    0x00},
 	{ R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE },
 	ENDMARKER,
@@ -567,14 +552,14 @@
 	{.name = n, .width = w , .height = h, .regs = r }
 
 static const struct ov2640_win_size ov2640_supported_win_sizes[] = {
-	OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs),
-	OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs),
-	OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs),
-	OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs),
-	OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs),
-	OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs),
-	OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs),
-	OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs),
+	OV2640_SIZE("QCIF", QCIF_WIDTH, QCIF_HEIGHT, ov2640_qcif_regs),
+	OV2640_SIZE("QVGA", QVGA_WIDTH, QVGA_HEIGHT, ov2640_qvga_regs),
+	OV2640_SIZE("CIF", CIF_WIDTH, CIF_HEIGHT, ov2640_cif_regs),
+	OV2640_SIZE("VGA", VGA_WIDTH, VGA_HEIGHT, ov2640_vga_regs),
+	OV2640_SIZE("SVGA", SVGA_WIDTH, SVGA_HEIGHT, ov2640_svga_regs),
+	OV2640_SIZE("XGA", XGA_WIDTH, XGA_HEIGHT, ov2640_xga_regs),
+	OV2640_SIZE("SXGA", SXGA_WIDTH, SXGA_HEIGHT, ov2640_sxga_regs),
+	OV2640_SIZE("UXGA", UXGA_WIDTH, UXGA_HEIGHT, ov2640_uxga_regs),
 };
 
 /*
@@ -867,7 +852,7 @@
 	struct ov2640_priv *priv = to_ov2640(client);
 
 	if (!priv->win) {
-		u32 width = W_SVGA, height = H_SVGA;
+		u32 width = SVGA_WIDTH, height = SVGA_HEIGHT;
 		priv->win = ov2640_select_win(&width, &height);
 		priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8;
 	}
@@ -954,8 +939,8 @@
 {
 	a->c.left	= 0;
 	a->c.top	= 0;
-	a->c.width	= W_UXGA;
-	a->c.height	= H_UXGA;
+	a->c.width	= UXGA_WIDTH;
+	a->c.height	= UXGA_HEIGHT;
 	a->type		= V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
 	return 0;
@@ -965,8 +950,8 @@
 {
 	a->bounds.left			= 0;
 	a->bounds.top			= 0;
-	a->bounds.width			= W_UXGA;
-	a->bounds.height		= H_UXGA;
+	a->bounds.width			= UXGA_WIDTH;
+	a->bounds.height		= UXGA_HEIGHT;
 	a->defrect			= a->bounds;
 	a->type				= V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	a->pixelaspect.numerator	= 1;
@@ -1060,6 +1045,63 @@
 	.video	= &ov2640_subdev_video_ops,
 };
 
+/* OF probe functions */
+static int ov2640_hw_power(struct device *dev, int on)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ov2640_priv *priv = to_ov2640(client);
+
+	dev_dbg(&client->dev, "%s: %s the camera\n",
+			__func__, on ? "ENABLE" : "DISABLE");
+
+	if (priv->pwdn_gpio)
+		gpiod_direction_output(priv->pwdn_gpio, !on);
+
+	return 0;
+}
+
+static int ov2640_hw_reset(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ov2640_priv *priv = to_ov2640(client);
+
+	if (priv->resetb_gpio) {
+		/* Active the resetb pin to perform a reset pulse */
+		gpiod_direction_output(priv->resetb_gpio, 1);
+		usleep_range(3000, 5000);
+		gpiod_direction_output(priv->resetb_gpio, 0);
+	}
+
+	return 0;
+}
+
+static int ov2640_probe_dt(struct i2c_client *client,
+		struct ov2640_priv *priv)
+{
+	/* Request the reset GPIO deasserted */
+	priv->resetb_gpio = devm_gpiod_get_optional(&client->dev, "resetb",
+			GPIOD_OUT_LOW);
+	if (!priv->resetb_gpio)
+		dev_dbg(&client->dev, "resetb gpio is not assigned!\n");
+	else if (IS_ERR(priv->resetb_gpio))
+		return PTR_ERR(priv->resetb_gpio);
+
+	/* Request the power down GPIO asserted */
+	priv->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "pwdn",
+			GPIOD_OUT_HIGH);
+	if (!priv->pwdn_gpio)
+		dev_dbg(&client->dev, "pwdn gpio is not assigned!\n");
+	else if (IS_ERR(priv->pwdn_gpio))
+		return PTR_ERR(priv->pwdn_gpio);
+
+	/* Initialize the soc_camera_subdev_desc */
+	priv->ssdd_dt.power = ov2640_hw_power;
+	priv->ssdd_dt.reset = ov2640_hw_reset;
+	client->dev.platform_data = &priv->ssdd_dt;
+
+	return 0;
+}
+
 /*
  * i2c_driver functions
  */
@@ -1071,12 +1113,6 @@
 	struct i2c_adapter	*adapter = to_i2c_adapter(client->dev.parent);
 	int			ret;
 
-	if (!ssdd) {
-		dev_err(&adapter->dev,
-			"OV2640: Missing platform_data for driver\n");
-		return -EINVAL;
-	}
-
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
 		dev_err(&adapter->dev,
 			"OV2640: I2C-Adapter doesn't support SMBUS\n");
@@ -1090,6 +1126,22 @@
 		return -ENOMEM;
 	}
 
+	priv->clk = v4l2_clk_get(&client->dev, "xvclk");
+	if (IS_ERR(priv->clk))
+		return -EPROBE_DEFER;
+
+	if (!ssdd && !client->dev.of_node) {
+		dev_err(&client->dev, "Missing platform_data for driver\n");
+		ret = -EINVAL;
+		goto err_clk;
+	}
+
+	if (!ssdd) {
+		ret = ov2640_probe_dt(client, priv);
+		if (ret)
+			goto err_clk;
+	}
+
 	v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
 	v4l2_ctrl_handler_init(&priv->hdl, 2);
 	v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
@@ -1097,24 +1149,27 @@
 	v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
 			V4L2_CID_HFLIP, 0, 1, 1, 0);
 	priv->subdev.ctrl_handler = &priv->hdl;
-	if (priv->hdl.error)
-		return priv->hdl.error;
-
-	priv->clk = v4l2_clk_get(&client->dev, "mclk");
-	if (IS_ERR(priv->clk)) {
-		ret = PTR_ERR(priv->clk);
-		goto eclkget;
+	if (priv->hdl.error) {
+		ret = priv->hdl.error;
+		goto err_clk;
 	}
 
 	ret = ov2640_video_probe(client);
-	if (ret) {
-		v4l2_clk_put(priv->clk);
-eclkget:
-		v4l2_ctrl_handler_free(&priv->hdl);
-	} else {
-		dev_info(&adapter->dev, "OV2640 Probed\n");
-	}
+	if (ret < 0)
+		goto err_videoprobe;
 
+	ret = v4l2_async_register_subdev(&priv->subdev);
+	if (ret < 0)
+		goto err_videoprobe;
+
+	dev_info(&adapter->dev, "OV2640 Probed\n");
+
+	return 0;
+
+err_videoprobe:
+	v4l2_ctrl_handler_free(&priv->hdl);
+err_clk:
+	v4l2_clk_put(priv->clk);
 	return ret;
 }
 
@@ -1122,6 +1177,7 @@
 {
 	struct ov2640_priv       *priv = to_ov2640(client);
 
+	v4l2_async_unregister_subdev(&priv->subdev);
 	v4l2_clk_put(priv->clk);
 	v4l2_device_unregister_subdev(&priv->subdev);
 	v4l2_ctrl_handler_free(&priv->hdl);
@@ -1134,9 +1190,16 @@
 };
 MODULE_DEVICE_TABLE(i2c, ov2640_id);
 
+static const struct of_device_id ov2640_of_match[] = {
+	{.compatible = "ovti,ov2640", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ov2640_of_match);
+
 static struct i2c_driver ov2640_i2c_driver = {
 	.driver = {
 		.name = "ov2640",
+		.of_match_table = of_match_ptr(ov2640_of_match),
 	},
 	.probe    = ov2640_probe,
 	.remove   = ov2640_remove,
diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c
index ee66675..f5cee3d 100644
--- a/drivers/media/i2c/ths7303.c
+++ b/drivers/media/i2c/ths7303.c
@@ -52,10 +52,6 @@
 MODULE_AUTHOR("Chaithrika U S");
 MODULE_LICENSE("GPL");
 
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Debug level 0-1");
-
 static inline struct ths7303_state *to_state(struct v4l2_subdev *sd)
 {
 	return container_of(sd, struct ths7303_state, sd);
diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c
index be71d54..9b689d2 100644
--- a/drivers/media/i2c/ths8200.c
+++ b/drivers/media/i2c/ths8200.c
@@ -58,21 +58,11 @@
 	return container_of(sd, struct ths8200_state, sd);
 }
 
-static inline unsigned hblanking(const struct v4l2_bt_timings *t)
-{
-	return V4L2_DV_BT_BLANKING_WIDTH(t);
-}
-
 static inline unsigned htotal(const struct v4l2_bt_timings *t)
 {
 	return V4L2_DV_BT_FRAME_WIDTH(t);
 }
 
-static inline unsigned vblanking(const struct v4l2_bt_timings *t)
-{
-	return V4L2_DV_BT_BLANKING_HEIGHT(t);
-}
-
 static inline unsigned vtotal(const struct v4l2_bt_timings *t)
 {
 	return V4L2_DV_BT_FRAME_HEIGHT(t);
@@ -489,7 +479,6 @@
 
 	ths8200_s_power(sd, false);
 	v4l2_async_unregister_subdev(&decoder->sd);
-	v4l2_device_unregister_subdev(sd);
 
 	return 0;
 }
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index c692fdf..f57c192 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -923,13 +923,13 @@
 /**
  * tvp514x_enum_mbus_code() - V4L2 decoder interface handler for enum_mbus_code
  * @sd: pointer to standard V4L2 sub-device structure
- * @fh: file handle
+ * @cfg: pad configuration
  * @code: pointer to v4l2_subdev_mbus_code_enum structure
  *
  * Enumertaes mbus codes supported
  */
 static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	u32 pad = code->pad;
@@ -950,13 +950,13 @@
 /**
  * tvp514x_get_pad_format() - V4L2 decoder interface handler for get pad format
  * @sd: pointer to standard V4L2 sub-device structure
- * @fh: file handle
+ * @cfg: pad configuration
  * @format: pointer to v4l2_subdev_format structure
  *
  * Retrieves pad format which is active or tried based on requirement
  */
 static int tvp514x_get_pad_format(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_format *format)
 {
 	struct tvp514x_decoder *decoder = to_decoder(sd);
@@ -979,13 +979,13 @@
 /**
  * tvp514x_set_pad_format() - V4L2 decoder interface handler for set pad format
  * @sd: pointer to standard V4L2 sub-device structure
- * @fh: file handle
+ * @cfg: pad configuration
  * @format: pointer to v4l2_subdev_format structure
  *
  * Set pad format for the output pad
  */
 static int tvp514x_set_pad_format(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_format *fmt)
 {
 	struct tvp514x_decoder *decoder = to_decoder(sd);
@@ -1209,7 +1209,6 @@
 	struct tvp514x_decoder *decoder = to_decoder(sd);
 
 	v4l2_async_unregister_subdev(&decoder->sd);
-	v4l2_device_unregister_subdev(sd);
 #if defined(CPTCFG_MEDIA_CONTROLLER)
 	media_entity_cleanup(&decoder->sd.entity);
 #endif
diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c
index 8d8e714..6ad46ba 100644
--- a/drivers/media/i2c/tvp7002.c
+++ b/drivers/media/i2c/tvp7002.c
@@ -846,13 +846,13 @@
 /*
  * tvp7002_enum_mbus_code() - Enum supported digital video format on pad
  * @sd: pointer to standard V4L2 sub-device structure
- * @fh: file handle for the subdev
+ * @cfg: pad configuration
  * @code: pointer to subdev enum mbus code struct
  *
  * Enumerate supported digital video formats for pad.
  */
 static int
-tvp7002_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+tvp7002_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 		       struct v4l2_subdev_mbus_code_enum *code)
 {
 	/* Check requested format index is within range */
@@ -867,13 +867,13 @@
 /*
  * tvp7002_get_pad_format() - get video format on pad
  * @sd: pointer to standard V4L2 sub-device structure
- * @fh: file handle for the subdev
+ * @cfg: pad configuration
  * @fmt: pointer to subdev format struct
  *
  * get video format for pad.
  */
 static int
-tvp7002_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+tvp7002_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 		       struct v4l2_subdev_format *fmt)
 {
 	struct tvp7002 *tvp7002 = to_tvp7002(sd);
@@ -890,16 +890,16 @@
 /*
  * tvp7002_set_pad_format() - set video format on pad
  * @sd: pointer to standard V4L2 sub-device structure
- * @fh: file handle for the subdev
+ * @cfg: pad configuration
  * @fmt: pointer to subdev format struct
  *
  * set video format for pad.
  */
 static int
-tvp7002_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+tvp7002_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 		       struct v4l2_subdev_format *fmt)
 {
-	return tvp7002_get_pad_format(sd, fh, fmt);
+	return tvp7002_get_pad_format(sd, cfg, fmt);
 }
 
 /* V4L2 core operation handlers */
@@ -1116,7 +1116,6 @@
 #if defined(CPTCFG_MEDIA_CONTROLLER)
 	media_entity_cleanup(&device->sd.entity);
 #endif
-	v4l2_device_unregister_subdev(sd);
 	v4l2_ctrl_handler_free(&device->hdl);
 	return 0;
 }
diff --git a/drivers/media/mmc/siano/Kconfig b/drivers/media/mmc/siano/Kconfig
index 0e4e326..b7d7169 100644
--- a/drivers/media/mmc/siano/Kconfig
+++ b/drivers/media/mmc/siano/Kconfig
@@ -7,6 +7,8 @@
 	depends on m
 	depends on DVB_CORE && HAS_DMA
 	depends on MMC
+	depends on !RC_CORE || RC_CORE
 	select MEDIA_COMMON_OPTIONS
+	select SMS_SIANO_MDTV
 	---help---
 	  Choose if you would like to have Siano's support for SDIO interface
diff --git a/drivers/media/mmc/siano/smssdio.c b/drivers/media/mmc/siano/smssdio.c
index 912c281..fee2d71 100644
--- a/drivers/media/mmc/siano/smssdio.c
+++ b/drivers/media/mmc/siano/smssdio.c
@@ -32,6 +32,8 @@
  * Fix stop command
  */
 
+#include "smscoreapi.h"
+
 #include <linux/moduleparam.h>
 #include <linux/slab.h>
 #include <linux/firmware.h>
@@ -41,7 +43,6 @@
 #include <linux/mmc/sdio_ids.h>
 #include <linux/module.h>
 
-#include "smscoreapi.h"
 #include "sms-cards.h"
 #include "smsendian.h"
 
@@ -141,14 +142,14 @@
 	 */
 	(void)sdio_readb(func, SMSSDIO_INT, &ret);
 	if (ret) {
-		sms_err("Unable to read interrupt register!\n");
+		pr_err("Unable to read interrupt register!\n");
 		return;
 	}
 
 	if (smsdev->split_cb == NULL) {
 		cb = smscore_getbuffer(smsdev->coredev);
 		if (!cb) {
-			sms_err("Unable to allocate data buffer!\n");
+			pr_err("Unable to allocate data buffer!\n");
 			return;
 		}
 
@@ -157,7 +158,7 @@
 					 SMSSDIO_DATA,
 					 SMSSDIO_BLOCK_SIZE);
 		if (ret) {
-			sms_err("Error %d reading initial block!\n", ret);
+			pr_err("Error %d reading initial block!\n", ret);
 			return;
 		}
 
@@ -198,7 +199,7 @@
 					 size);
 		if (ret && ret != -EINVAL) {
 			smscore_putbuffer(smsdev->coredev, cb);
-			sms_err("Error %d reading data from card!\n", ret);
+			pr_err("Error %d reading data from card!\n", ret);
 			return;
 		}
 
@@ -216,8 +217,8 @@
 						  smsdev->func->cur_blksize);
 				if (ret) {
 					smscore_putbuffer(smsdev->coredev, cb);
-					sms_err("Error %d reading "
-						"data from card!\n", ret);
+					pr_err("Error %d reading data from card!\n",
+					       ret);
 					return;
 				}
 
@@ -278,7 +279,7 @@
 		goto free;
 	}
 
-	ret = smscore_register_device(&params, &smsdev->coredev);
+	ret = smscore_register_device(&params, &smsdev->coredev, NULL);
 	if (ret < 0)
 		goto free;
 
diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig
index 1056e4e..9c8c88d 100644
--- a/drivers/media/pci/bt8xx/Kconfig
+++ b/drivers/media/pci/bt8xx/Kconfig
@@ -3,15 +3,17 @@
 	depends on m
 	depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2
 	depends on I2C_ALGOBIT
-	select VIDEO_BTCX
 	select VIDEOBUF_DMA_SG
 	depends on RC_CORE
+	depends on MEDIA_RADIO_SUPPORT
 	select VIDEO_TUNER
 	select VIDEO_TVEEPROM
 	select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT
 	select VIDEO_TVAUDIO if MEDIA_SUBDRV_AUTOSELECT
 	select VIDEO_TDA7432 if MEDIA_SUBDRV_AUTOSELECT
 	select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT
+	select RADIO_ADAPTERS
+	select RADIO_TEA575X
 	---help---
 	  Support for BT848 based frame grabber/overlay boards. This includes
 	  the Miro, Hauppauge and STB boards. Please read the material in
diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile
index 1b9ef08..7313631 100644
--- a/drivers/media/pci/bt8xx/Makefile
+++ b/drivers/media/pci/bt8xx/Makefile
@@ -1,6 +1,6 @@
 bttv-objs      :=      bttv-driver.o bttv-cards.o bttv-if.o \
 		       bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \
-		       bttv-input.o bttv-audio-hook.o
+		       bttv-input.o bttv-audio-hook.o btcx-risc.o
 
 obj-$(CPTCFG_VIDEO_BT848) += bttv.o
 obj-$(CPTCFG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o
diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c
index 1176583..8aa7266 100644
--- a/drivers/media/pci/bt8xx/bt878.c
+++ b/drivers/media/pci/bt8xx/bt878.c
@@ -416,9 +416,6 @@
 	int result = 0;
 	unsigned char lat;
 	struct bt878 *bt;
-#if defined(__powerpc__)
-	unsigned int cmd;
-#endif
 	unsigned int cardid;
 
 	printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n",
@@ -461,15 +458,6 @@
 	printk("irq: %d, latency: %d, memory: 0x%lx\n",
 	       bt->irq, lat, bt->bt878_adr);
 
-
-#if defined(__powerpc__)
-	/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
-	/* response on cards with no firmware is not enabled by OF */
-	pci_read_config_dword(dev, PCI_COMMAND, &cmd);
-	cmd = (cmd | PCI_COMMAND_MEMORY);
-	pci_write_config_dword(dev, PCI_COMMAND, cmd);
-#endif
-
 #ifdef __sparc__
 	bt->bt878_mem = (unsigned char *) bt->bt878_adr;
 #else
@@ -590,9 +578,3 @@
 module_exit(bt878_cleanup_module);
 
 MODULE_LICENSE("GPL");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/pci/bt8xx/bt878.h b/drivers/media/pci/bt8xx/bt878.h
index d19b592..49af240 100644
--- a/drivers/media/pci/bt8xx/bt878.h
+++ b/drivers/media/pci/bt8xx/bt878.h
@@ -142,18 +142,7 @@
 		u32 irq_err_ignore);
 void bt878_stop(struct bt878 *bt);
 
-#if defined(__powerpc__)	/* big-endian */
-static inline void io_st_le32(volatile unsigned __iomem *addr, unsigned val)
-{
-	st_le32(addr, val);
-	eieio();
-}
-
-#define bmtwrite(dat,adr)  io_st_le32((adr),(dat))
-#define bmtread(adr)       ld_le32((adr))
-#else
 #define bmtwrite(dat,adr)  writel((dat), (adr))
 #define bmtread(adr)       readl(adr)
-#endif
 
 #endif
diff --git a/drivers/media/common/btcx-risc.c b/drivers/media/pci/bt8xx/btcx-risc.c
similarity index 90%
rename from drivers/media/common/btcx-risc.c
rename to drivers/media/pci/bt8xx/btcx-risc.c
index ac1b268..00f0880 100644
--- a/drivers/media/common/btcx-risc.c
+++ b/drivers/media/pci/bt8xx/btcx-risc.c
@@ -32,13 +32,9 @@
 
 #include "btcx-risc.h"
 
-MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers");
-MODULE_AUTHOR("Gerd Knorr");
-MODULE_LICENSE("GPL");
-
-static unsigned int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)");
+static unsigned int btcx_debug;
+module_param(btcx_debug, int, 0644);
+MODULE_PARM_DESC(btcx_debug,"debug messages, default is 0 (no)");
 
 /* ---------------------------------------------------------- */
 /* allocate/free risc memory                                  */
@@ -50,7 +46,7 @@
 {
 	if (NULL == risc->cpu)
 		return;
-	if (debug) {
+	if (btcx_debug) {
 		memcnt--;
 		printk("btcx: riscmem free [%d] dma=%lx\n",
 		       memcnt, (unsigned long)risc->dma);
@@ -75,7 +71,7 @@
 		risc->cpu  = cpu;
 		risc->dma  = dma;
 		risc->size = size;
-		if (debug) {
+		if (btcx_debug) {
 			memcnt++;
 			printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n",
 			       memcnt, (unsigned long)dma, cpu, size);
@@ -141,7 +137,7 @@
 	dx = nx - win->left;
 	win->left  = nx;
 	win->width = nw;
-	if (debug)
+	if (btcx_debug)
 		printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n",
 		       win->width, win->height, win->left, win->top, dx);
 
@@ -153,7 +149,7 @@
 			nw += mask+1;
 		clips[i].c.left  = nx;
 		clips[i].c.width = nw;
-		if (debug)
+		if (btcx_debug)
 			printk(KERN_DEBUG "btcx:   clip align %dx%d+%d+%d\n",
 			       clips[i].c.width, clips[i].c.height,
 			       clips[i].c.left, clips[i].c.top);
@@ -234,7 +230,7 @@
 	*nskips = skip;
 	*maxy = maxline;
 
-	if (debug) {
+	if (btcx_debug) {
 		printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline);
 		for (skip = 0; skip < *nskips; skip++) {
 			printk(" %d-%d",skips[skip].start,skips[skip].end);
@@ -242,19 +238,3 @@
 		printk("\n");
 	}
 }
-
-/* ---------------------------------------------------------- */
-
-EXPORT_SYMBOL(btcx_riscmem_alloc);
-EXPORT_SYMBOL(btcx_riscmem_free);
-
-EXPORT_SYMBOL(btcx_screen_clips);
-EXPORT_SYMBOL(btcx_align);
-EXPORT_SYMBOL(btcx_sort_clips);
-EXPORT_SYMBOL(btcx_calc_skips);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/pci/bt8xx/btcx-risc.h b/drivers/media/pci/bt8xx/btcx-risc.h
new file mode 100644
index 0000000..1ed7a00
--- /dev/null
+++ b/drivers/media/pci/bt8xx/btcx-risc.h
@@ -0,0 +1,26 @@
+struct btcx_riscmem {
+	unsigned int   size;
+	__le32         *cpu;
+	__le32         *jmp;
+	dma_addr_t     dma;
+};
+
+struct btcx_skiplist {
+	int start;
+	int end;
+};
+
+int  btcx_riscmem_alloc(struct pci_dev *pci,
+			struct btcx_riscmem *risc,
+			unsigned int size);
+void btcx_riscmem_free(struct pci_dev *pci,
+		       struct btcx_riscmem *risc);
+
+int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win,
+		      struct v4l2_clip *clips, unsigned int n);
+int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips,
+	       unsigned int n, int mask);
+void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips);
+void btcx_calc_skips(int line, int width, int *maxy,
+		     struct btcx_skiplist *skips, unsigned int *nskips,
+		     const struct v4l2_clip *clips, unsigned int nclips);
diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c
index 4105560..4654fb6 100644
--- a/drivers/media/pci/bt8xx/bttv-cards.c
+++ b/drivers/media/pci/bt8xx/bttv-cards.c
@@ -84,8 +84,7 @@
 static void td3116_muxsel(struct bttv *btv, unsigned int input);
 
 static int terratec_active_radio_upgrade(struct bttv *btv);
-static int tea5757_read(struct bttv *btv);
-static int tea5757_write(struct bttv *btv, int value);
+static int tea575x_init(struct bttv *btv);
 static void identify_by_eeprom(struct bttv *btv,
 			       unsigned char eeprom_data[256]);
 static int pvr_boot(struct bttv *btv);
@@ -3085,12 +3084,12 @@
 		if (0 == (gpio & 0x20)) {
 			btv->has_radio = 1;
 			if (!miro_fmtuner[id]) {
-				btv->has_matchbox = 1;
-				btv->mbox_we    = (1<<6);
-				btv->mbox_most  = (1<<7);
-				btv->mbox_clk   = (1<<8);
-				btv->mbox_data  = (1<<9);
-				btv->mbox_mask  = (1<<6)|(1<<7)|(1<<8)|(1<<9);
+				btv->has_tea575x = 1;
+				btv->tea_gpio.wren = 6;
+				btv->tea_gpio.most = 7;
+				btv->tea_gpio.clk  = 8;
+				btv->tea_gpio.data = 9;
+				tea575x_init(btv);
 			}
 		} else {
 			btv->has_radio = 0;
@@ -3104,7 +3103,7 @@
 		pr_info("%d: miro: id=%d tuner=%d radio=%s stereo=%s\n",
 			btv->c.nr, id+1, btv->tuner_type,
 			!btv->has_radio ? "no" :
-			(btv->has_matchbox ? "matchbox" : "fmtuner"),
+			(btv->has_tea575x ? "tea575x" : "fmtuner"),
 			(-1 == msp) ? "no" : "yes");
 	} else {
 		/* new cards with microtune tuner */
@@ -3382,12 +3381,12 @@
 		break;
 	case BTTV_BOARD_VHX:
 		btv->has_radio    = 1;
-		btv->has_matchbox = 1;
-		btv->mbox_we      = 0x20;
-		btv->mbox_most    = 0;
-		btv->mbox_clk     = 0x08;
-		btv->mbox_data    = 0x10;
-		btv->mbox_mask    = 0x38;
+		btv->has_tea575x  = 1;
+		btv->tea_gpio.wren = 5;
+		btv->tea_gpio.most = 6;
+		btv->tea_gpio.clk  = 3;
+		btv->tea_gpio.data = 4;
+		tea575x_init(btv);
 		break;
 	case BTTV_BOARD_VOBIS_BOOSTAR:
 	case BTTV_BOARD_TERRATV:
@@ -3745,33 +3744,112 @@
 		btv->radio_uses_msp_demodulator = 1;
 }
 
+/* ----------------------------------------------------------------------- */
+
+static void bttv_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
+{
+	struct bttv *btv = tea->private_data;
+	struct bttv_tea575x_gpio gpio = btv->tea_gpio;
+	u16 val = 0;
+
+	val |= (pins & TEA575X_DATA) ? (1 << gpio.data) : 0;
+	val |= (pins & TEA575X_CLK)  ? (1 << gpio.clk)  : 0;
+	val |= (pins & TEA575X_WREN) ? (1 << gpio.wren) : 0;
+
+	gpio_bits((1 << gpio.data) | (1 << gpio.clk) | (1 << gpio.wren), val);
+	if (btv->mbox_ior) {
+		/* IOW and CSEL active */
+		gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
+		udelay(5);
+		/* all inactive */
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+	}
+}
+
+static u8 bttv_tea575x_get_pins(struct snd_tea575x *tea)
+{
+	struct bttv *btv = tea->private_data;
+	struct bttv_tea575x_gpio gpio = btv->tea_gpio;
+	u8 ret = 0;
+	u16 val;
+
+	if (btv->mbox_ior) {
+		/* IOR and CSEL active */
+		gpio_bits(btv->mbox_ior | btv->mbox_csel, 0);
+		udelay(5);
+	}
+	val = gpio_read();
+	if (btv->mbox_ior) {
+		/* all inactive */
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+	}
+
+	if (val & (1 << gpio.data))
+		ret |= TEA575X_DATA;
+	if (val & (1 << gpio.most))
+		ret |= TEA575X_MOST;
+
+	return ret;
+}
+
+static void bttv_tea575x_set_direction(struct snd_tea575x *tea, bool output)
+{
+	struct bttv *btv = tea->private_data;
+	struct bttv_tea575x_gpio gpio = btv->tea_gpio;
+	u32 mask = (1 << gpio.clk) | (1 << gpio.wren) | (1 << gpio.data) |
+		   (1 << gpio.most);
+
+	if (output)
+		gpio_inout(mask, (1 << gpio.data) | (1 << gpio.clk) |
+				 (1 << gpio.wren));
+	else
+		gpio_inout(mask, (1 << gpio.clk) | (1 << gpio.wren));
+}
+
+static struct snd_tea575x_ops bttv_tea_ops = {
+	.set_pins = bttv_tea575x_set_pins,
+	.get_pins = bttv_tea575x_get_pins,
+	.set_direction = bttv_tea575x_set_direction,
+};
+
+static int tea575x_init(struct bttv *btv)
+{
+	btv->tea.private_data = btv;
+	btv->tea.ops = &bttv_tea_ops;
+	if (!snd_tea575x_hw_init(&btv->tea)) {
+		pr_info("%d: detected TEA575x radio\n", btv->c.nr);
+		btv->tea.mute = false;
+		return 0;
+	}
+
+	btv->has_tea575x = 0;
+	btv->has_radio = 0;
+
+	return -ENODEV;
+}
+
+/* ----------------------------------------------------------------------- */
+
 static int terratec_active_radio_upgrade(struct bttv *btv)
 {
-	int freq;
-
 	btv->has_radio    = 1;
-	btv->has_matchbox = 1;
-	btv->mbox_we      = 0x10;
-	btv->mbox_most    = 0x20;
-	btv->mbox_clk     = 0x08;
-	btv->mbox_data    = 0x04;
-	btv->mbox_mask    = 0x3c;
+	btv->has_tea575x  = 1;
+	btv->tea_gpio.wren = 4;
+	btv->tea_gpio.most = 5;
+	btv->tea_gpio.clk  = 3;
+	btv->tea_gpio.data = 2;
 
 	btv->mbox_iow     = 1 <<  8;
 	btv->mbox_ior     = 1 <<  9;
 	btv->mbox_csel    = 1 << 10;
 
-	freq=88000/62.5;
-	tea5757_write(btv, 5 * freq + 0x358); /* write 0x1ed8 */
-	if (0x1ed8 == tea5757_read(btv)) {
+	if (!tea575x_init(btv)) {
 		pr_info("%d: Terratec Active Radio Upgrade found\n", btv->c.nr);
-		btv->has_radio    = 1;
-		btv->has_saa6588  = 1;
-		btv->has_matchbox = 1;
-	} else {
-		btv->has_radio    = 0;
-		btv->has_matchbox = 0;
+		btv->has_saa6588 = 1;
 	}
+
 	return 0;
 }
 
@@ -4292,181 +4370,6 @@
 	}
 }
 
-
-
-/* ----------------------------------------------------------------------- */
-/* Miro Pro radio stuff -- the tea5757 is connected to some GPIO ports     */
-/*
- * Copyright (c) 1999 Csaba Halasz <qgehali@uni-miskolc.hu>
- * This code is placed under the terms of the GNU General Public License
- *
- * Brutally hacked by Dan Sheridan <dan.sheridan@contact.org.uk> djs52 8/3/00
- */
-
-static void bus_low(struct bttv *btv, int bit)
-{
-	if (btv->mbox_ior) {
-		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
-			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
-		udelay(5);
-	}
-
-	gpio_bits(bit,0);
-	udelay(5);
-
-	if (btv->mbox_ior) {
-		gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
-		udelay(5);
-	}
-}
-
-static void bus_high(struct bttv *btv, int bit)
-{
-	if (btv->mbox_ior) {
-		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
-			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
-		udelay(5);
-	}
-
-	gpio_bits(bit,bit);
-	udelay(5);
-
-	if (btv->mbox_ior) {
-		gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
-		udelay(5);
-	}
-}
-
-static int bus_in(struct bttv *btv, int bit)
-{
-	if (btv->mbox_ior) {
-		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
-			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
-		udelay(5);
-
-		gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
-		udelay(5);
-	}
-	return gpio_read() & (bit);
-}
-
-/* TEA5757 register bits */
-#define TEA_FREQ		0:14
-#define TEA_BUFFER		15:15
-
-#define TEA_SIGNAL_STRENGTH	16:17
-
-#define TEA_PORT1		18:18
-#define TEA_PORT0		19:19
-
-#define TEA_BAND		20:21
-#define TEA_BAND_FM		0
-#define TEA_BAND_MW		1
-#define TEA_BAND_LW		2
-#define TEA_BAND_SW		3
-
-#define TEA_MONO		22:22
-#define TEA_ALLOW_STEREO	0
-#define TEA_FORCE_MONO		1
-
-#define TEA_SEARCH_DIRECTION	23:23
-#define TEA_SEARCH_DOWN		0
-#define TEA_SEARCH_UP		1
-
-#define TEA_STATUS		24:24
-#define TEA_STATUS_TUNED	0
-#define TEA_STATUS_SEARCHING	1
-
-/* Low-level stuff */
-static int tea5757_read(struct bttv *btv)
-{
-	unsigned long timeout;
-	int value = 0;
-	int i;
-
-	/* better safe than sorry */
-	gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we);
-
-	if (btv->mbox_ior) {
-		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
-			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
-		udelay(5);
-	}
-
-	if (bttv_gpio)
-		bttv_gpio_tracking(btv,"tea5757 read");
-
-	bus_low(btv,btv->mbox_we);
-	bus_low(btv,btv->mbox_clk);
-
-	udelay(10);
-	timeout= jiffies + msecs_to_jiffies(1000);
-
-	/* wait for DATA line to go low; error if it doesn't */
-	while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout))
-		schedule();
-	if (bus_in(btv,btv->mbox_data)) {
-		pr_warn("%d: tea5757: read timeout\n", btv->c.nr);
-		return -1;
-	}
-
-	dprintk("%d: tea5757:", btv->c.nr);
-	for (i = 0; i < 24; i++) {
-		udelay(5);
-		bus_high(btv,btv->mbox_clk);
-		udelay(5);
-		dprintk_cont("%c",
-			     bus_in(btv, btv->mbox_most) == 0 ? 'T' : '-');
-		bus_low(btv,btv->mbox_clk);
-		value <<= 1;
-		value |= (bus_in(btv,btv->mbox_data) == 0)?0:1;  /* MSB first */
-		dprintk_cont("%c",
-			     bus_in(btv, btv->mbox_most) == 0 ? 'S' : 'M');
-	}
-	dprintk_cont("\n");
-	dprintk("%d: tea5757: read 0x%X\n", btv->c.nr, value);
-	return value;
-}
-
-static int tea5757_write(struct bttv *btv, int value)
-{
-	int i;
-	int reg = value;
-
-	gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we | btv->mbox_data);
-
-	if (btv->mbox_ior) {
-		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
-			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
-		udelay(5);
-	}
-	if (bttv_gpio)
-		bttv_gpio_tracking(btv,"tea5757 write");
-
-	dprintk("%d: tea5757: write 0x%X\n", btv->c.nr, value);
-	bus_low(btv,btv->mbox_clk);
-	bus_high(btv,btv->mbox_we);
-	for (i = 0; i < 25; i++) {
-		if (reg & 0x1000000)
-			bus_high(btv,btv->mbox_data);
-		else
-			bus_low(btv,btv->mbox_data);
-		reg <<= 1;
-		bus_high(btv,btv->mbox_clk);
-		udelay(10);
-		bus_low(btv,btv->mbox_clk);
-		udelay(10);
-	}
-	bus_low(btv,btv->mbox_we);  /* unmute !!! */
-	return 0;
-}
-
-void tea5757_set_freq(struct bttv *btv, unsigned short freq)
-{
-	dprintk("tea5757_set_freq %d\n",freq);
-	tea5757_write(btv, 5 * freq + 0x358); /* add 10.7MHz (see docs) */
-}
-
 /* RemoteVision MX (rv605) muxsel helper [Miguel Freitas]
  *
  * This is needed because rv605 don't use a normal multiplex, but a crosspoint
@@ -5048,10 +4951,3 @@
 		pci_write_config_byte(btv->c.pci, PCI_LATENCY_TIMER, latency);
 	return 0;
 }
-
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
index 7a787be..5e87e21 100644
--- a/drivers/media/pci/bt8xx/bttv-driver.c
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -1874,8 +1874,10 @@
 	if (new_freq.type == V4L2_TUNER_RADIO) {
 		radio_enable(btv);
 		btv->radio_freq = new_freq.frequency;
-		if (btv->has_matchbox)
-			tea5757_set_freq(btv, btv->radio_freq);
+		if (btv->has_tea575x) {
+			btv->tea.freq = btv->radio_freq;
+			snd_tea575x_set_freq(&btv->tea);
+		}
 	} else {
 		btv->tv_freq = new_freq.frequency;
 	}
@@ -2472,7 +2474,7 @@
 		return -EINVAL;
 
 	strlcpy(cap->driver, "bttv", sizeof(cap->driver));
-	strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
+	strlcpy(cap->card, btv->video_dev.name, sizeof(cap->card));
 	snprintf(cap->bus_info, sizeof(cap->bus_info),
 		 "PCI:%s", pci_name(btv->c.pci));
 	cap->capabilities =
@@ -2482,9 +2484,9 @@
 		V4L2_CAP_DEVICE_CAPS;
 	if (no_overlay <= 0)
 		cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
-	if (btv->vbi_dev)
+	if (video_is_registered(&btv->vbi_dev))
 		cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
-	if (btv->radio_dev)
+	if (video_is_registered(&btv->radio_dev))
 		cap->capabilities |= V4L2_CAP_RADIO;
 
 	/*
@@ -2513,6 +2515,8 @@
 		if (btv->has_saa6588)
 			cap->device_caps |= V4L2_CAP_READWRITE |
 						V4L2_CAP_RDS_CAPTURE;
+		if (btv->has_tea575x)
+			cap->device_caps |= V4L2_CAP_HW_FREQ_SEEK;
 	}
 	return 0;
 }
@@ -3242,6 +3246,9 @@
 	if (btv->audio_mode_gpio)
 		btv->audio_mode_gpio(btv, t, 0);
 
+	if (btv->has_tea575x)
+		return snd_tea575x_g_tuner(&btv->tea, t);
+
 	return 0;
 }
 
@@ -3259,6 +3266,30 @@
 	return 0;
 }
 
+static int radio_s_hw_freq_seek(struct file *file, void *priv,
+					const struct v4l2_hw_freq_seek *a)
+{
+	struct bttv_fh *fh = priv;
+	struct bttv *btv = fh->btv;
+
+	if (btv->has_tea575x)
+		return snd_tea575x_s_hw_freq_seek(file, &btv->tea, a);
+
+	return -ENOTTY;
+}
+
+static int radio_enum_freq_bands(struct file *file, void *priv,
+					 struct v4l2_frequency_band *band)
+{
+	struct bttv_fh *fh = priv;
+	struct bttv *btv = fh->btv;
+
+	if (btv->has_tea575x)
+		return snd_tea575x_enum_freq_bands(&btv->tea, band);
+
+	return -ENOTTY;
+}
+
 static ssize_t radio_read(struct file *file, char __user *data,
 			 size_t count, loff_t *ppos)
 {
@@ -3316,6 +3347,8 @@
 	.vidioc_s_tuner         = radio_s_tuner,
 	.vidioc_g_frequency     = bttv_g_frequency,
 	.vidioc_s_frequency     = bttv_s_frequency,
+	.vidioc_s_hw_freq_seek	= radio_s_hw_freq_seek,
+	.vidioc_enum_freq_bands	= radio_enum_freq_bands,
 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 };
@@ -3872,19 +3905,14 @@
 /* ----------------------------------------------------------------------- */
 /* initialization                                                          */
 
-static struct video_device *vdev_init(struct bttv *btv,
-				      const struct video_device *template,
-				      const char *type_name)
+static void vdev_init(struct bttv *btv,
+		      struct video_device *vfd,
+		      const struct video_device *template,
+		      const char *type_name)
 {
-	struct video_device *vfd;
-
-	vfd = video_device_alloc();
-	if (NULL == vfd)
-		return NULL;
 	*vfd = *template;
 	vfd->v4l2_dev = &btv->c.v4l2_dev;
-	vfd->release = video_device_release;
-	vfd->debug   = bttv_debug;
+	vfd->release = video_device_release_empty;
 	video_set_drvdata(vfd, btv);
 	snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
 		 btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
@@ -3895,32 +3923,13 @@
 		v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER);
 		v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER);
 	}
-	return vfd;
 }
 
 static void bttv_unregister_video(struct bttv *btv)
 {
-	if (btv->video_dev) {
-		if (video_is_registered(btv->video_dev))
-			video_unregister_device(btv->video_dev);
-		else
-			video_device_release(btv->video_dev);
-		btv->video_dev = NULL;
-	}
-	if (btv->vbi_dev) {
-		if (video_is_registered(btv->vbi_dev))
-			video_unregister_device(btv->vbi_dev);
-		else
-			video_device_release(btv->vbi_dev);
-		btv->vbi_dev = NULL;
-	}
-	if (btv->radio_dev) {
-		if (video_is_registered(btv->radio_dev))
-			video_unregister_device(btv->radio_dev);
-		else
-			video_device_release(btv->radio_dev);
-		btv->radio_dev = NULL;
-	}
+	video_unregister_device(&btv->video_dev);
+	video_unregister_device(&btv->vbi_dev);
+	video_unregister_device(&btv->radio_dev);
 }
 
 /* register video4linux devices */
@@ -3930,44 +3939,38 @@
 		pr_notice("Overlay support disabled\n");
 
 	/* video */
-	btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
+	vdev_init(btv, &btv->video_dev, &bttv_video_template, "video");
 
-	if (NULL == btv->video_dev)
-		goto err;
-	if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
+	if (video_register_device(&btv->video_dev, VFL_TYPE_GRABBER,
 				  video_nr[btv->c.nr]) < 0)
 		goto err;
 	pr_info("%d: registered device %s\n",
-		btv->c.nr, video_device_node_name(btv->video_dev));
-	if (device_create_file(&btv->video_dev->dev,
+		btv->c.nr, video_device_node_name(&btv->video_dev));
+	if (device_create_file(&btv->video_dev.dev,
 				     &dev_attr_card)<0) {
 		pr_err("%d: device_create_file 'card' failed\n", btv->c.nr);
 		goto err;
 	}
 
 	/* vbi */
-	btv->vbi_dev = vdev_init(btv, &bttv_video_template, "vbi");
+	vdev_init(btv, &btv->vbi_dev, &bttv_video_template, "vbi");
 
-	if (NULL == btv->vbi_dev)
-		goto err;
-	if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
+	if (video_register_device(&btv->vbi_dev, VFL_TYPE_VBI,
 				  vbi_nr[btv->c.nr]) < 0)
 		goto err;
 	pr_info("%d: registered device %s\n",
-		btv->c.nr, video_device_node_name(btv->vbi_dev));
+		btv->c.nr, video_device_node_name(&btv->vbi_dev));
 
 	if (!btv->has_radio)
 		return 0;
 	/* radio */
-	btv->radio_dev = vdev_init(btv, &radio_template, "radio");
-	if (NULL == btv->radio_dev)
-		goto err;
-	btv->radio_dev->ctrl_handler = &btv->radio_ctrl_handler;
-	if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
+	vdev_init(btv, &btv->radio_dev, &radio_template, "radio");
+	btv->radio_dev.ctrl_handler = &btv->radio_ctrl_handler;
+	if (video_register_device(&btv->radio_dev, VFL_TYPE_RADIO,
 				  radio_nr[btv->c.nr]) < 0)
 		goto err;
 	pr_info("%d: registered device %s\n",
-		btv->c.nr, video_device_node_name(btv->radio_dev));
+		btv->c.nr, video_device_node_name(&btv->radio_dev));
 
 	/* all done */
 	return 0;
@@ -4429,9 +4432,3 @@
 
 module_init(bttv_init_module);
 module_exit(bttv_cleanup_module);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/pci/bt8xx/bttv-gpio.c b/drivers/media/pci/bt8xx/bttv-gpio.c
index 3f364b7..25b9916 100644
--- a/drivers/media/pci/bt8xx/bttv-gpio.c
+++ b/drivers/media/pci/bt8xx/bttv-gpio.c
@@ -181,9 +181,3 @@
 	btwrite(data,BT848_GPIO_DATA);
 	spin_unlock_irqrestore(&btv->gpio_lock,flags);
 }
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/pci/bt8xx/bttv-if.c b/drivers/media/pci/bt8xx/bttv-if.c
index a6a540d..538652e 100644
--- a/drivers/media/pci/bt8xx/bttv-if.c
+++ b/drivers/media/pci/bt8xx/bttv-if.c
@@ -113,9 +113,3 @@
 		bttv_gpio_tracking(btv,"extern write");
 	return 0;
 }
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c
index 4d3f05a..3859dde 100644
--- a/drivers/media/pci/bt8xx/bttv-risc.c
+++ b/drivers/media/pci/bt8xx/bttv-risc.c
@@ -901,9 +901,3 @@
 	buf->vb.field = ov->field;
 	return 0;
 }
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/pci/bt8xx/bttv-vbi.c b/drivers/media/pci/bt8xx/bttv-vbi.c
index b433267..e77129c 100644
--- a/drivers/media/pci/bt8xx/bttv-vbi.c
+++ b/drivers/media/pci/bt8xx/bttv-vbi.c
@@ -450,10 +450,3 @@
 	/* See bttv_vbi_fmt_set(). */
 	f->end                  = tvnorm->vbistart[0] * 2 + 2;
 }
-
-/* ----------------------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h
index f081262..91301c3 100644
--- a/drivers/media/pci/bt8xx/bttv.h
+++ b/drivers/media/pci/bt8xx/bttv.h
@@ -378,8 +378,3 @@
 extern void bttv_input_irq(struct bttv *dev);
 
 #endif /* _BTTV_H_ */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h
index 9fe1948..a444cfb 100644
--- a/drivers/media/pci/bt8xx/bttvp.h
+++ b/drivers/media/pci/bt8xx/bttvp.h
@@ -42,6 +42,7 @@
 #include <media/tveeprom.h>
 #include <media/rc-core.h>
 #include <media/ir-kbd-i2c.h>
+#include <media/tea575x.h>
 
 #include "bt848.h"
 #include "bttv.h"
@@ -359,6 +360,10 @@
 	struct bttv_buffer     *vbi;
 };
 
+struct bttv_tea575x_gpio {
+	u8 data, clk, wren, most;
+};
+
 struct bttv {
 	struct bttv_core c;
 
@@ -399,9 +404,9 @@
 	struct v4l2_subdev	  *sd_tda7432;
 
 	/* video4linux (1) */
-	struct video_device *video_dev;
-	struct video_device *radio_dev;
-	struct video_device *vbi_dev;
+	struct video_device video_dev;
+	struct video_device radio_dev;
+	struct video_device vbi_dev;
 
 	/* controls */
 	struct v4l2_ctrl_handler   ctrl_handler;
@@ -445,12 +450,9 @@
 
 	/* miro/pinnacle + Aimslab VHX
 	   philips matchbox (tea5757 radio tuner) support */
-	int has_matchbox;
-	int mbox_we;
-	int mbox_data;
-	int mbox_clk;
-	int mbox_most;
-	int mbox_mask;
+	int has_tea575x;
+	struct bttv_tea575x_gpio tea_gpio;
+	struct snd_tea575x tea;
 
 	/* ISA stuff (Terratec Active Radio Upgrade) */
 	int mbox_ior;
@@ -531,9 +533,3 @@
 #define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
 
 #endif /* _BTTVP_H_ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/pci/cx18/cx18-alsa-main.c b/drivers/media/pci/cx18/cx18-alsa-main.c
index ea272bc..0b0e801 100644
--- a/drivers/media/pci/cx18/cx18-alsa-main.c
+++ b/drivers/media/pci/cx18/cx18-alsa-main.c
@@ -216,7 +216,7 @@
 	}
 
 	s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
-	if (s->video_dev == NULL) {
+	if (s->video_dev.v4l2_dev == NULL) {
 		CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - "
 				     "skipping\n", __func__);
 		return 0;
diff --git a/drivers/media/pci/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h
index 207d6e8..b15beed 100644
--- a/drivers/media/pci/cx18/cx18-driver.h
+++ b/drivers/media/pci/cx18/cx18-driver.h
@@ -373,7 +373,7 @@
 struct cx18_stream {
 	/* These first five fields are always set, even if the stream
 	   is not actually created. */
-	struct video_device *video_dev;	/* NULL when stream not created */
+	struct video_device video_dev;	/* v4l2_dev is NULL when stream not created */
 	struct cx18_dvb *dvb;		/* DVB / Digital Transport */
 	struct cx18 *cx; 		/* for ease of use */
 	const char *name;		/* name of the stream */
@@ -409,6 +409,7 @@
 	/* Videobuf for YUV video */
 	u32 pixelformat;
 	u32 vb_bytes_per_frame;
+	u32 vb_bytes_per_line;
 	struct list_head vb_capture;    /* video capture queue */
 	spinlock_t vb_lock;
 	struct timer_list vb_timeout;
diff --git a/drivers/media/pci/cx18/cx18-fileops.c b/drivers/media/pci/cx18/cx18-fileops.c
index 76a3b4a..df83740 100644
--- a/drivers/media/pci/cx18/cx18-fileops.c
+++ b/drivers/media/pci/cx18/cx18-fileops.c
@@ -34,6 +34,7 @@
 #include "cx18-controls.h"
 #include "cx18-ioctl.h"
 #include "cx18-cards.h"
+#include <media/v4l2-event.h>
 
 /* This function tries to claim the stream for a specific file descriptor.
    If no one else is using this stream then the stream is claimed and
@@ -609,13 +610,16 @@
 
 unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
 {
+	unsigned long req_events = poll_requested_events(wait);
 	struct cx18_open_id *id = file2id(filp);
 	struct cx18 *cx = id->cx;
 	struct cx18_stream *s = &cx->streams[id->type];
 	int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+	unsigned res = 0;
 
 	/* Start a capture if there is none */
-	if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+	if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags) &&
+			(req_events & (POLLIN | POLLRDNORM))) {
 		int rc;
 
 		mutex_lock(&cx->serialize_lock);
@@ -632,21 +636,26 @@
 	if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
 		(id->type == CX18_ENC_STREAM_TYPE_YUV)) {
 		int videobuf_poll = videobuf_poll_stream(filp, &s->vbuf_q, wait);
+
+		if (v4l2_event_pending(&id->fh))
+			res |= POLLPRI;
                 if (eof && videobuf_poll == POLLERR)
-                        return POLLHUP;
-                else
-                        return videobuf_poll;
+			return res | POLLHUP;
+		return res | videobuf_poll;
 	}
 
 	/* add stream's waitq to the poll list */
 	CX18_DEBUG_HI_FILE("Encoder poll\n");
-	poll_wait(filp, &s->waitq, wait);
+	if (v4l2_event_pending(&id->fh))
+		res |= POLLPRI;
+	else
+		poll_wait(filp, &s->waitq, wait);
 
 	if (atomic_read(&s->q_full.depth))
-		return POLLIN | POLLRDNORM;
+		return res | POLLIN | POLLRDNORM;
 	if (eof)
-		return POLLHUP;
-	return 0;
+		return res | POLLHUP;
+	return res;
 }
 
 int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
@@ -797,7 +806,7 @@
 		CX18_DEBUG_WARN("nomem on v4l2 open\n");
 		return -ENOMEM;
 	}
-	v4l2_fh_init(&item->fh, s->video_dev);
+	v4l2_fh_init(&item->fh, &s->video_dev);
 
 	item->cx = cx;
 	item->type = s->type;
diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c
index 27fdad8..19d049f 100644
--- a/drivers/media/pci/cx18/cx18-ioctl.c
+++ b/drivers/media/pci/cx18/cx18-ioctl.c
@@ -39,6 +39,7 @@
 #include "cx18-cards.h"
 #include "cx18-av-core.h"
 #include <media/tveeprom.h>
+#include <media/v4l2-event.h>
 
 u16 cx18_service2vbi(int type)
 {
@@ -159,7 +160,7 @@
 	if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
 		pixfmt->pixelformat = s->pixelformat;
 		pixfmt->sizeimage = s->vb_bytes_per_frame;
-		pixfmt->bytesperline = 720;
+		pixfmt->bytesperline = s->vb_bytes_per_line;
 	} else {
 		pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
 		pixfmt->sizeimage = 128 * 1024;
@@ -287,10 +288,13 @@
 	s->pixelformat = fmt->fmt.pix.pixelformat;
 	/* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
 	   UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
-	if (s->pixelformat == V4L2_PIX_FMT_HM12)
+	if (s->pixelformat == V4L2_PIX_FMT_HM12) {
 		s->vb_bytes_per_frame = h * 720 * 3 / 2;
-	else
+		s->vb_bytes_per_line = 720; /* First plane */
+	} else {
 		s->vb_bytes_per_frame = h * 720 * 2;
+		s->vb_bytes_per_line = 1440; /* Packed */
+	}
 
 	mbus_fmt.width = cx->cxhdl.width = w;
 	mbus_fmt.height = cx->cxhdl.height = h;
@@ -447,34 +451,29 @@
 
 	if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
-	cropcap->bounds.top = cropcap->bounds.left = 0;
-	cropcap->bounds.width = 720;
-	cropcap->bounds.height = cx->is_50hz ? 576 : 480;
 	cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
 	cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
-	cropcap->defrect = cropcap->bounds;
 	return 0;
 }
 
-static int cx18_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop)
-{
-	struct cx18_open_id *id = fh2id(fh);
-	struct cx18 *cx = id->cx;
-
-	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		return -EINVAL;
-	CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n");
-	return -EINVAL;
-}
-
-static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+static int cx18_g_selection(struct file *file, void *fh,
+			    struct v4l2_selection *sel)
 {
 	struct cx18 *cx = fh2id(fh)->cx;
 
-	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
-	CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n");
-	return -EINVAL;
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r.top = sel->r.left = 0;
+		sel->r.width = 720;
+		sel->r.height = cx->is_50hz ? 576 : 480;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
 }
 
 static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
@@ -510,6 +509,9 @@
 {
 	struct cx18_open_id *id = fh2id(fh);
 	struct cx18 *cx = id->cx;
+	v4l2_std_id std = V4L2_STD_ALL;
+	const struct cx18_card_video_input *card_input =
+				cx->card->video_inputs + inp;
 
 	if (inp >= cx->nof_inputs)
 		return -EINVAL;
@@ -525,6 +527,11 @@
 	cx->active_input = inp;
 	/* Set the audio input to whatever is appropriate for the input type. */
 	cx->audio_input = cx->card->video_inputs[inp].audio_index;
+	if (card_input->video_type == V4L2_INPUT_TYPE_TUNER)
+		std = cx->tuner_std;
+	cx->streams[CX18_ENC_STREAM_TYPE_MPG].video_dev.tvnorms = std;
+	cx->streams[CX18_ENC_STREAM_TYPE_YUV].video_dev.tvnorms = std;
+	cx->streams[CX18_ENC_STREAM_TYPE_VBI].video_dev.tvnorms = std;
 
 	/* prevent others from messing with the streams until
 	   we're finished changing inputs. */
@@ -1036,7 +1043,7 @@
 	for (i = 0; i < CX18_MAX_STREAMS; i++) {
 		struct cx18_stream *s = &cx->streams[i];
 
-		if (s->video_dev == NULL || s->buffers == 0)
+		if (s->video_dev.v4l2_dev == NULL || s->buffers == 0)
 			continue;
 		CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
 			  s->name, s->s_flags,
@@ -1078,8 +1085,7 @@
 	.vidioc_enumaudio               = cx18_enumaudio,
 	.vidioc_enum_input              = cx18_enum_input,
 	.vidioc_cropcap                 = cx18_cropcap,
-	.vidioc_s_crop                  = cx18_s_crop,
-	.vidioc_g_crop                  = cx18_g_crop,
+	.vidioc_g_selection             = cx18_g_selection,
 	.vidioc_g_input                 = cx18_g_input,
 	.vidioc_s_input                 = cx18_s_input,
 	.vidioc_g_frequency             = cx18_g_frequency,
@@ -1114,6 +1120,8 @@
 	.vidioc_querybuf                = cx18_querybuf,
 	.vidioc_qbuf                    = cx18_qbuf,
 	.vidioc_dqbuf                   = cx18_dqbuf,
+	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
 };
 
 void cx18_set_funcs(struct video_device *vdev)
diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c
index 369445f..c82d25d 100644
--- a/drivers/media/pci/cx18/cx18-streams.c
+++ b/drivers/media/pci/cx18/cx18-streams.c
@@ -254,11 +254,8 @@
 static void cx18_stream_init(struct cx18 *cx, int type)
 {
 	struct cx18_stream *s = &cx->streams[type];
-	struct video_device *video_dev = s->video_dev;
 
-	/* we need to keep video_dev, so restore it afterwards */
 	memset(s, 0, sizeof(*s));
-	s->video_dev = video_dev;
 
 	/* initialize cx18_stream fields */
 	s->dvb = NULL;
@@ -307,6 +304,7 @@
 		/* Assume the previous pixel default */
 		s->pixelformat = V4L2_PIX_FMT_HM12;
 		s->vb_bytes_per_frame = cx->cxhdl.height * 720 * 3 / 2;
+		s->vb_bytes_per_line = 720;
 	}
 }
 
@@ -319,12 +317,12 @@
 
 	/*
 	 * These five fields are always initialized.
-	 * For analog capture related streams, if video_dev == NULL then the
+	 * For analog capture related streams, if video_dev.v4l2_dev == NULL then the
 	 * stream is not in use.
 	 * For the TS stream, if dvb == NULL then the stream is not in use.
 	 * In those cases no other fields but these four can be used.
 	 */
-	s->video_dev = NULL;
+	s->video_dev.v4l2_dev = NULL;
 	s->dvb = NULL;
 	s->cx = cx;
 	s->type = type;
@@ -367,24 +365,20 @@
 	if (num_offset == -1)
 		return 0;
 
-	/* allocate and initialize the v4l2 video device structure */
-	s->video_dev = video_device_alloc();
-	if (s->video_dev == NULL) {
-		CX18_ERR("Couldn't allocate v4l2 video_device for %s\n",
-				s->name);
-		return -ENOMEM;
-	}
-
-	snprintf(s->video_dev->name, sizeof(s->video_dev->name), "%s %s",
+	/* initialize the v4l2 video device structure */
+	snprintf(s->video_dev.name, sizeof(s->video_dev.name), "%s %s",
 		 cx->v4l2_dev.name, s->name);
 
-	s->video_dev->num = num;
-	s->video_dev->v4l2_dev = &cx->v4l2_dev;
-	s->video_dev->fops = &cx18_v4l2_enc_fops;
-	s->video_dev->release = video_device_release;
-	s->video_dev->tvnorms = V4L2_STD_ALL;
-	s->video_dev->lock = &cx->serialize_lock;
-	cx18_set_funcs(s->video_dev);
+	s->video_dev.num = num;
+	s->video_dev.v4l2_dev = &cx->v4l2_dev;
+	s->video_dev.fops = &cx18_v4l2_enc_fops;
+	s->video_dev.release = video_device_release_empty;
+	if (cx->card->video_inputs->video_type == CX18_CARD_INPUT_VID_TUNER)
+		s->video_dev.tvnorms = cx->tuner_std;
+	else
+		s->video_dev.tvnorms = V4L2_STD_ALL;
+	s->video_dev.lock = &cx->serialize_lock;
+	cx18_set_funcs(&s->video_dev);
 	return 0;
 }
 
@@ -428,31 +422,30 @@
 		}
 	}
 
-	if (s->video_dev == NULL)
+	if (s->video_dev.v4l2_dev == NULL)
 		return 0;
 
-	num = s->video_dev->num;
+	num = s->video_dev.num;
 	/* card number + user defined offset + device offset */
 	if (type != CX18_ENC_STREAM_TYPE_MPG) {
 		struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
 
-		if (s_mpg->video_dev)
-			num = s_mpg->video_dev->num
+		if (s_mpg->video_dev.v4l2_dev)
+			num = s_mpg->video_dev.num
 			    + cx18_stream_info[type].num_offset;
 	}
-	video_set_drvdata(s->video_dev, s);
+	video_set_drvdata(&s->video_dev, s);
 
 	/* Register device. First try the desired minor, then any free one. */
-	ret = video_register_device_no_warn(s->video_dev, vfl_type, num);
+	ret = video_register_device_no_warn(&s->video_dev, vfl_type, num);
 	if (ret < 0) {
 		CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
 			s->name, num);
-		video_device_release(s->video_dev);
-		s->video_dev = NULL;
+		s->video_dev.v4l2_dev = NULL;
 		return ret;
 	}
 
-	name = video_device_node_name(s->video_dev);
+	name = video_device_node_name(&s->video_dev);
 
 	switch (vfl_type) {
 	case VFL_TYPE_GRABBER:
@@ -542,10 +535,9 @@
 		}
 
 		/* If struct video_device exists, can have buffers allocated */
-		vdev = cx->streams[type].video_dev;
+		vdev = &cx->streams[type].video_dev;
 
-		cx->streams[type].video_dev = NULL;
-		if (vdev == NULL)
+		if (vdev->v4l2_dev == NULL)
 			continue;
 
 		if (type == CX18_ENC_STREAM_TYPE_YUV)
@@ -553,11 +545,7 @@
 
 		cx18_stream_free(&cx->streams[type]);
 
-		/* Unregister or release device */
-		if (unregister)
-			video_unregister_device(vdev);
-		else
-			video_device_release(vdev);
+		video_unregister_device(vdev);
 	}
 }
 
@@ -1042,7 +1030,7 @@
 	for (i = 0; i < CX18_MAX_STREAMS; i++) {
 		struct cx18_stream *s = &cx->streams[i];
 
-		if (s->video_dev && (s->handle != CX18_INVALID_TASK_HANDLE))
+		if (s->video_dev.v4l2_dev && (s->handle != CX18_INVALID_TASK_HANDLE))
 			return s->handle;
 	}
 	return CX18_INVALID_TASK_HANDLE;
diff --git a/drivers/media/pci/cx18/cx18-streams.h b/drivers/media/pci/cx18/cx18-streams.h
index 713b0e6..27f8af9 100644
--- a/drivers/media/pci/cx18/cx18-streams.h
+++ b/drivers/media/pci/cx18/cx18-streams.h
@@ -33,7 +33,7 @@
 
 static inline bool cx18_stream_enabled(struct cx18_stream *s)
 {
-	return s->video_dev ||
+	return s->video_dev.v4l2_dev ||
 	       (s->dvb && s->dvb->enabled) ||
 	       (s->type == CX18_ENC_STREAM_TYPE_IDX &&
 		s->cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] != 0);
diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig
index fcbcf43..f8c1355 100644
--- a/drivers/media/pci/cx23885/Kconfig
+++ b/drivers/media/pci/cx23885/Kconfig
@@ -42,7 +42,7 @@
 	select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
-	select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_M88RS6000T if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT
 	---help---
 	  This is a video4linux driver for Conexant 23885 based
diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c
index 2bbbf54..0a91df2 100644
--- a/drivers/media/pci/cx23885/altera-ci.c
+++ b/drivers/media/pci/cx23885/altera-ci.c
@@ -483,7 +483,6 @@
 	}
 
 }
-EXPORT_SYMBOL(altera_hw_filt_release);
 
 void altera_ci_release(void *dev, int ci_nr)
 {
@@ -598,7 +597,6 @@
 
 	return 0;
 }
-EXPORT_SYMBOL(altera_pid_feed_control);
 
 static int altera_ci_start_feed(struct dvb_demux_feed *feed, int num)
 {
@@ -699,7 +697,6 @@
 
 	return ret;
 }
-EXPORT_SYMBOL(altera_hw_filt_init);
 
 int altera_ci_init(struct altera_ci_config *config, int ci_nr)
 {
diff --git a/drivers/media/pci/cx23885/altera-ci.h b/drivers/media/pci/cx23885/altera-ci.h
index 0ade583..da21f3b 100644
--- a/drivers/media/pci/cx23885/altera-ci.h
+++ b/drivers/media/pci/cx23885/altera-ci.h
@@ -39,7 +39,7 @@
 	int (*fpga_rw) (void *dev, int ad_rg, int val, int rw);
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_ALTERA_CI)
+#if IS_REACHABLE(CPTCFG_MEDIA_ALTERA_CI)
 
 extern int altera_ci_init(struct altera_ci_config *config, int ci_nr);
 extern void altera_ci_release(void *dev, int ci_nr);
diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c
index 5a0f130..0ab732c 100644
--- a/drivers/media/pci/cx23885/cx23885-417.c
+++ b/drivers/media/pci/cx23885/cx23885-417.c
@@ -1339,14 +1339,13 @@
 	strlcpy(cap->driver, dev->name, sizeof(cap->driver));
 	strlcpy(cap->card, cx23885_boards[tsport->dev->board].name,
 		sizeof(cap->card));
-	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
-	cap->capabilities =
-		V4L2_CAP_VIDEO_CAPTURE |
-		V4L2_CAP_READWRITE     |
-		V4L2_CAP_STREAMING     |
-		0;
+	sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+			   V4L2_CAP_STREAMING;
 	if (dev->tuner_type != TUNER_ABSENT)
-		cap->capabilities |= V4L2_CAP_TUNER;
+		cap->device_caps |= V4L2_CAP_TUNER;
+	cap->capabilities = cap->device_caps | V4L2_CAP_VBI_CAPTURE |
+		V4L2_CAP_AUDIO | V4L2_CAP_DEVICE_CAPS;
 
 	return 0;
 }
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index db99ca2..f384f29 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -614,7 +614,7 @@
 		.portb		= CX23885_MPEG_DVB,
 	},
 	[CX23885_BOARD_HAUPPAUGE_HVR4400] = {
-		.name		= "Hauppauge WinTV-HVR4400",
+		.name		= "Hauppauge WinTV-HVR4400/HVR5500",
 		.porta		= CX23885_ANALOG_VIDEO,
 		.portb		= CX23885_MPEG_DVB,
 		.portc		= CX23885_MPEG_DVB,
@@ -622,6 +622,10 @@
 		.tuner_addr	= 0x60, /* 0xc0 >> 1 */
 		.tuner_bus	= 1,
 	},
+	[CX23885_BOARD_HAUPPAUGE_STARBURST] = {
+		.name		= "Hauppauge WinTV Starburst",
+		.portb		= CX23885_MPEG_DVB,
+	},
 	[CX23885_BOARD_AVERMEDIA_HC81R] = {
 		.name		= "AVerTV Hybrid Express Slim HC81R",
 		.tuner_type	= TUNER_XC2028,
@@ -706,6 +710,11 @@
 		.portb		= CX23885_MPEG_DVB,
 		.portc		= CX23885_MPEG_DVB,
 	},
+	[CX23885_BOARD_HAUPPAUGE_HVR5525] = {
+		.name		= "Hauppauge WinTV-HVR5525",
+		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+	},
 };
 const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
 
@@ -936,19 +945,19 @@
 	}, {
 		.subvendor = 0x0070,
 		.subdevice = 0xc108,
-		.card      = CX23885_BOARD_HAUPPAUGE_HVR4400,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR4400, /* Hauppauge WinTV HVR-4400 (Model 121xxx, Hybrid DVB-T/S2, IR) */
 	}, {
 		.subvendor = 0x0070,
 		.subdevice = 0xc138,
-		.card      = CX23885_BOARD_HAUPPAUGE_HVR4400,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR4400, /* Hauppauge WinTV HVR-5500 (Model 121xxx, Hybrid DVB-T/C/S2, IR) */
 	}, {
 		.subvendor = 0x0070,
 		.subdevice = 0xc12a,
-		.card      = CX23885_BOARD_HAUPPAUGE_HVR4400,
+		.card      = CX23885_BOARD_HAUPPAUGE_STARBURST, /* Hauppauge WinTV Starburst (Model 121x00, DVB-S2, IR) */
 	}, {
 		.subvendor = 0x0070,
 		.subdevice = 0xc1f8,
-		.card      = CX23885_BOARD_HAUPPAUGE_HVR4400,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR4400, /* Hauppauge WinTV HVR-5500 (Model 121xxx, Hybrid DVB-T/C/S2, IR) */
 	}, {
 		.subvendor = 0x1461,
 		.subdevice = 0xd939,
@@ -989,6 +998,10 @@
 		.subvendor = 0x4254,
 		.subdevice = 0x0982,
 		.card      = CX23885_BOARD_DVBSKY_T982,
+	}, {
+		.subvendor = 0x0070,
+		.subdevice = 0xf038,
+		.card      = CX23885_BOARD_HAUPPAUGE_HVR5525,
 	},
 };
 const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -1161,6 +1174,8 @@
 	case 85721:
 		/* WinTV-HVR1290 (PCIe, OEM, RCA in, IR,
 			Dual channel ATSC and Basic analog */
+	case 150329:
+		/* WinTV-HVR5525 (PCIe, DVB-S/S2, DVB-T/T2/C) */
 		break;
 	default:
 		printk(KERN_WARNING "%s: warning: "
@@ -1545,8 +1560,9 @@
 		cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
 		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR4400:
+	case CX23885_BOARD_HAUPPAUGE_STARBURST:
 		/* GPIO-8 tda10071 demod reset */
-		/* GPIO-9 si2165 demod reset */
+		/* GPIO-9 si2165 demod reset (only HVR4400/HVR5500)*/
 
 		/* Put the parts into reset and back */
 		cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1);
@@ -1632,6 +1648,29 @@
 		msleep(100);
 		cx23885_gpio_set(dev, GPIO_2);
 		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR5525:
+		/*
+		 * GPIO-00 IR_WIDE
+		 * GPIO-02 wake#
+		 * GPIO-03 VAUX Pres.
+		 * GPIO-07 PROG#
+		 * GPIO-08 SAT_RESN
+		 * GPIO-09 TER_RESN
+		 * GPIO-10 B2_SENSE
+		 * GPIO-11 B1_SENSE
+		 * GPIO-15 IR_LED_STATUS
+		 * GPIO-19 IR_NARROW
+		 * GPIO-20 Blauster1
+		 * ALTGPIO VAUX_SWITCH
+		 * AUX_PLL_CLK : Blaster2
+		 */
+		/* Put the parts into reset and back */
+		cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1);
+		cx23885_gpio_clear(dev, GPIO_8 | GPIO_9);
+		msleep(100);
+		cx23885_gpio_set(dev, GPIO_8 | GPIO_9);
+		msleep(100);
+		break;
 	}
 }
 
@@ -1872,7 +1911,9 @@
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	case CX23885_BOARD_HAUPPAUGE_HVR4400:
+	case CX23885_BOARD_HAUPPAUGE_STARBURST:
 	case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE:
+	case CX23885_BOARD_HAUPPAUGE_HVR5525:
 		if (dev->i2c_bus[0].i2c_rc == 0)
 			hauppauge_eeprom(dev, eeprom+0xc0);
 		break;
@@ -1980,6 +2021,11 @@
 		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
 		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
 		break;
+	case CX23885_BOARD_HAUPPAUGE_STARBURST:
+		ts1->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
 	case CX23885_BOARD_DVBSKY_T9580:
 	case CX23885_BOARD_DVBSKY_T982:
 		ts1->gen_ctrl_val  = 0x5; /* Parallel */
@@ -1997,6 +2043,14 @@
 		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
 		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
 		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR5525:
+		ts1->gen_ctrl_val  = 0x5; /* Parallel */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index 1d9d0f8..7aee76a 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -825,6 +825,7 @@
 	int i;
 
 	spin_lock_init(&dev->pci_irqmask_lock);
+	spin_lock_init(&dev->slock);
 
 	mutex_init(&dev->lock);
 	mutex_init(&dev->gpio_lock);
@@ -2049,11 +2050,11 @@
 
 	cx23885_shutdown(dev);
 
-	pci_disable_device(pci_dev);
-
 	/* unregister stuff */
 	free_irq(pci_dev->irq, dev);
 
+	pci_disable_device(pci_dev);
+
 	cx23885_dev_unregister(dev);
 	vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
 	v4l2_ctrl_handler_free(&dev->ctrl_handler);
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index c47d182..745caab 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -73,7 +73,7 @@
 #include "si2157.h"
 #include "sp2.h"
 #include "m88ds3103.h"
-#include "m88ts2022.h"
+#include "m88rs6000t.h"
 
 static unsigned int debug;
 
@@ -915,6 +915,16 @@
 	.agc = 0x99,
 };
 
+static const struct m88ds3103_config hauppauge_hvr5525_m88ds3103_config = {
+	.i2c_addr = 0x69,
+	.clock = 27000000,
+	.i2c_wr_max = 33,
+	.ts_mode = M88DS3103_TS_PARALLEL,
+	.ts_clk = 16000,
+	.ts_clk_pol = 1,
+	.agc = 0x99,
+};
+
 static int netup_altera_fpga_rw(void *device, int flag, int data, int read)
 {
 	struct cx23885_dev *dev = (struct cx23885_dev *)device;
@@ -1058,6 +1068,116 @@
 	.hostbus_diversity = 1,
 };
 
+static int dvb_register_ci_mac(struct cx23885_tsport *port)
+{
+	struct cx23885_dev *dev = port->dev;
+	struct i2c_client *client_ci = NULL;
+	struct vb2_dvb_frontend *fe0;
+
+	fe0 = vb2_dvb_get_frontend(&port->frontends, 1);
+	if (!fe0)
+		return -EINVAL;
+
+	switch (dev->board) {
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: {
+		static struct netup_card_info cinfo;
+
+		netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
+		memcpy(port->frontends.adapter.proposed_mac,
+				cinfo.port[port->nr - 1].mac, 6);
+		printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n",
+			port->nr, port->frontends.adapter.proposed_mac);
+
+		netup_ci_init(port);
+		return 0;
+		}
+	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
+		struct altera_ci_config netup_ci_cfg = {
+			.dev = dev,/* magic number to identify*/
+			.adapter = &port->frontends.adapter,/* for CI */
+			.demux = &fe0->dvb.demux,/* for hw pid filter */
+			.fpga_rw = netup_altera_fpga_rw,
+		};
+
+		altera_ci_init(&netup_ci_cfg, port->nr);
+		return 0;
+		}
+	case CX23885_BOARD_TEVII_S470: {
+		u8 eeprom[256]; /* 24C02 i2c eeprom */
+
+		if (port->nr != 1)
+			return 0;
+
+		/* Read entire EEPROM */
+		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
+		printk(KERN_INFO "TeVii S470 MAC= %pM\n", eeprom + 0xa0);
+		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6);
+		return 0;
+		}
+	case CX23885_BOARD_DVBSKY_T9580:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_T982: {
+		u8 eeprom[256]; /* 24C02 i2c eeprom */
+
+		if (port->nr > 2)
+			return 0;
+
+		/* Read entire EEPROM */
+		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom,
+				sizeof(eeprom));
+		printk(KERN_INFO "%s port %d MAC address: %pM\n",
+			cx23885_boards[dev->board].name, port->nr,
+			eeprom + 0xc0 + (port->nr-1) * 8);
+		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 +
+			(port->nr-1) * 8, 6);
+		return 0;
+		}
+	case CX23885_BOARD_DVBSKY_S950C:
+	case CX23885_BOARD_DVBSKY_T980C:
+	case CX23885_BOARD_TT_CT2_4500_CI: {
+		u8 eeprom[256]; /* 24C02 i2c eeprom */
+		struct sp2_config sp2_config;
+		struct i2c_board_info info;
+		struct cx23885_i2c *i2c_bus2 = &dev->i2c_bus[1];
+
+		/* attach CI */
+		memset(&sp2_config, 0, sizeof(sp2_config));
+		sp2_config.dvb_adap = &port->frontends.adapter;
+		sp2_config.priv = port;
+		sp2_config.ci_control = cx23885_sp2_ci_ctrl;
+		memset(&info, 0, sizeof(struct i2c_board_info));
+		strlcpy(info.type, "sp2", I2C_NAME_SIZE);
+		info.addr = 0x40;
+		info.platform_data = &sp2_config;
+		request_module(info.type);
+		client_ci = i2c_new_device(&i2c_bus2->i2c_adap, &info);
+		if (client_ci == NULL || client_ci->dev.driver == NULL)
+			return -ENODEV;
+		if (!try_module_get(client_ci->dev.driver->owner)) {
+			i2c_unregister_device(client_ci);
+			return -ENODEV;
+		}
+		port->i2c_client_ci = client_ci;
+
+		if (port->nr != 1)
+			return 0;
+
+		/* Read entire EEPROM */
+		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom,
+				sizeof(eeprom));
+		printk(KERN_INFO "%s MAC address: %pM\n",
+			cx23885_boards[dev->board].name, eeprom + 0xc0);
+		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0, 6);
+		return 0;
+		}
+	}
+	return 0;
+}
+
 static int dvb_register(struct cx23885_tsport *port)
 {
 	struct dib7000p_ops dib7000p_ops;
@@ -1066,11 +1186,10 @@
 	struct vb2_dvb_frontend *fe0, *fe1 = NULL;
 	struct si2168_config si2168_config;
 	struct si2157_config si2157_config;
-	struct sp2_config sp2_config;
-	struct m88ts2022_config m88ts2022_config;
+	struct ts2020_config ts2020_config;
 	struct i2c_board_info info;
 	struct i2c_adapter *adapter;
-	struct i2c_client *client_demod = NULL, *client_tuner = NULL, *client_ci = NULL;
+	struct i2c_client *client_demod = NULL, *client_tuner = NULL;
 	const struct m88ds3103_config *p_m88ds3103_config = NULL;
 	int (*p_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage) = NULL;
 	int mfe_shared = 0; /* bus not shared by default */
@@ -1710,6 +1829,17 @@
 			break;
 		}
 		break;
+	case CX23885_BOARD_HAUPPAUGE_STARBURST:
+		i2c_bus = &dev->i2c_bus[0];
+		fe0->dvb.frontend = dvb_attach(tda10071_attach,
+						&hauppauge_tda10071_config,
+						&i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(a8293_attach, fe0->dvb.frontend,
+				   &i2c_bus->i2c_adap,
+				   &hauppauge_a8293_config);
+		}
+		break;
 	case CX23885_BOARD_DVBSKY_T9580:
 	case CX23885_BOARD_DVBSKY_S950:
 		i2c_bus = &dev->i2c_bus[0];
@@ -1725,13 +1855,12 @@
 				break;
 
 			/* attach tuner */
-			memset(&m88ts2022_config, 0, sizeof(m88ts2022_config));
-			m88ts2022_config.fe = fe0->dvb.frontend;
-			m88ts2022_config.clock = 27000000;
+			memset(&ts2020_config, 0, sizeof(ts2020_config));
+			ts2020_config.fe = fe0->dvb.frontend;
 			memset(&info, 0, sizeof(struct i2c_board_info));
-			strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+			strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
 			info.addr = 0x60;
-			info.platform_data = &m88ts2022_config;
+			info.platform_data = &ts2020_config;
 			request_module(info.type);
 			client_tuner = i2c_new_device(adapter, &info);
 			if (client_tuner == NULL ||
@@ -1790,15 +1919,11 @@
 			request_module(info.type);
 			client_tuner = i2c_new_device(adapter, &info);
 			if (client_tuner == NULL ||
-					client_tuner->dev.driver == NULL) {
-				module_put(client_demod->dev.driver->owner);
-				i2c_unregister_device(client_demod);
+					client_tuner->dev.driver == NULL)
 				goto frontend_detach;
-			}
+
 			if (!try_module_get(client_tuner->dev.driver->owner)) {
 				i2c_unregister_device(client_tuner);
-				module_put(client_demod->dev.driver->owner);
-				i2c_unregister_device(client_demod);
 				goto frontend_detach;
 			}
 			port->i2c_client_tuner = client_tuner;
@@ -1821,8 +1946,7 @@
 		info.platform_data = &si2168_config;
 		request_module(info.type);
 		client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info);
-		if (client_demod == NULL ||
-				client_demod->dev.driver == NULL)
+		if (client_demod == NULL || client_demod->dev.driver == NULL)
 			goto frontend_detach;
 		if (!try_module_get(client_demod->dev.driver->owner)) {
 			i2c_unregister_device(client_demod);
@@ -1840,15 +1964,10 @@
 		request_module(info.type);
 		client_tuner = i2c_new_device(adapter, &info);
 		if (client_tuner == NULL ||
-				client_tuner->dev.driver == NULL) {
-			module_put(client_demod->dev.driver->owner);
-			i2c_unregister_device(client_demod);
+				client_tuner->dev.driver == NULL)
 			goto frontend_detach;
-		}
 		if (!try_module_get(client_tuner->dev.driver->owner)) {
 			i2c_unregister_device(client_tuner);
-			module_put(client_demod->dev.driver->owner);
-			i2c_unregister_device(client_demod);
 			goto frontend_detach;
 		}
 		port->i2c_client_tuner = client_tuner;
@@ -1865,17 +1984,15 @@
 			break;
 
 		/* attach tuner */
-		memset(&m88ts2022_config, 0, sizeof(m88ts2022_config));
-		m88ts2022_config.fe = fe0->dvb.frontend;
-		m88ts2022_config.clock = 27000000;
+		memset(&ts2020_config, 0, sizeof(ts2020_config));
+		ts2020_config.fe = fe0->dvb.frontend;
 		memset(&info, 0, sizeof(struct i2c_board_info));
-		strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+		strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
 		info.addr = 0x60;
-		info.platform_data = &m88ts2022_config;
+		info.platform_data = &ts2020_config;
 		request_module(info.type);
 		client_tuner = i2c_new_device(adapter, &info);
-		if (client_tuner == NULL ||
-				client_tuner->dev.driver == NULL)
+		if (client_tuner == NULL || client_tuner->dev.driver == NULL)
 			goto frontend_detach;
 		if (!try_module_get(client_tuner->dev.driver->owner)) {
 			i2c_unregister_device(client_tuner);
@@ -1912,17 +2029,15 @@
 			break;
 
 		/* attach tuner */
-		memset(&m88ts2022_config, 0, sizeof(m88ts2022_config));
-		m88ts2022_config.fe = fe0->dvb.frontend;
-		m88ts2022_config.clock = 27000000;
+		memset(&ts2020_config, 0, sizeof(ts2020_config));
+		ts2020_config.fe = fe0->dvb.frontend;
 		memset(&info, 0, sizeof(struct i2c_board_info));
-		strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+		strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
 		info.addr = 0x60;
-		info.platform_data = &m88ts2022_config;
+		info.platform_data = &ts2020_config;
 		request_module(info.type);
 		client_tuner = i2c_new_device(adapter, &info);
-		if (client_tuner == NULL ||
-				client_tuner->dev.driver == NULL)
+		if (client_tuner == NULL || client_tuner->dev.driver == NULL)
 			goto frontend_detach;
 		if (!try_module_get(client_tuner->dev.driver->owner)) {
 			i2c_unregister_device(client_tuner);
@@ -1967,8 +2082,7 @@
 		info.platform_data = &si2168_config;
 		request_module(info.type);
 		client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info);
-		if (client_demod == NULL ||
-				client_demod->dev.driver == NULL)
+		if (client_demod == NULL || client_demod->dev.driver == NULL)
 			goto frontend_detach;
 		if (!try_module_get(client_demod->dev.driver->owner)) {
 			i2c_unregister_device(client_demod);
@@ -1986,20 +2100,101 @@
 		request_module(info.type);
 		client_tuner = i2c_new_device(adapter, &info);
 		if (client_tuner == NULL ||
-				client_tuner->dev.driver == NULL) {
-			module_put(client_demod->dev.driver->owner);
-			i2c_unregister_device(client_demod);
+				client_tuner->dev.driver == NULL)
 			goto frontend_detach;
-		}
 		if (!try_module_get(client_tuner->dev.driver->owner)) {
 			i2c_unregister_device(client_tuner);
-			module_put(client_demod->dev.driver->owner);
-			i2c_unregister_device(client_demod);
-			port->i2c_client_demod = NULL;
 			goto frontend_detach;
 		}
 		port->i2c_client_tuner = client_tuner;
 		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR5525:
+		switch (port->nr) {
+		struct m88rs6000t_config m88rs6000t_config;
+
+		/* port b - satellite */
+		case 1:
+			/* attach frontend */
+			fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
+					&hauppauge_hvr5525_m88ds3103_config,
+					&dev->i2c_bus[0].i2c_adap, &adapter);
+			if (fe0->dvb.frontend == NULL)
+				break;
+
+			/* attach SEC */
+			if (!dvb_attach(a8293_attach, fe0->dvb.frontend,
+					&dev->i2c_bus[0].i2c_adap,
+					&hauppauge_a8293_config))
+				goto frontend_detach;
+
+			/* attach tuner */
+			memset(&m88rs6000t_config, 0, sizeof(m88rs6000t_config));
+			m88rs6000t_config.fe = fe0->dvb.frontend;
+			memset(&info, 0, sizeof(struct i2c_board_info));
+			strlcpy(info.type, "m88rs6000t", I2C_NAME_SIZE);
+			info.addr = 0x21;
+			info.platform_data = &m88rs6000t_config;
+			request_module("%s", info.type);
+			client_tuner = i2c_new_device(adapter, &info);
+			if (!client_tuner || !client_tuner->dev.driver)
+				goto frontend_detach;
+			if (!try_module_get(client_tuner->dev.driver->owner)) {
+				i2c_unregister_device(client_tuner);
+				goto frontend_detach;
+			}
+			port->i2c_client_tuner = client_tuner;
+
+			/* delegate signal strength measurement to tuner */
+			fe0->dvb.frontend->ops.read_signal_strength =
+				fe0->dvb.frontend->ops.tuner_ops.get_rf_strength;
+			break;
+		/* port c - terrestrial/cable */
+		case 2:
+			/* attach frontend */
+			memset(&si2168_config, 0, sizeof(si2168_config));
+			si2168_config.i2c_adapter = &adapter;
+			si2168_config.fe = &fe0->dvb.frontend;
+			si2168_config.ts_mode = SI2168_TS_SERIAL;
+			memset(&info, 0, sizeof(struct i2c_board_info));
+			strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+			info.addr = 0x64;
+			info.platform_data = &si2168_config;
+			request_module("%s", info.type);
+			client_demod = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info);
+			if (!client_demod || !client_demod->dev.driver)
+				goto frontend_detach;
+			if (!try_module_get(client_demod->dev.driver->owner)) {
+				i2c_unregister_device(client_demod);
+				goto frontend_detach;
+			}
+			port->i2c_client_demod = client_demod;
+
+			/* attach tuner */
+			memset(&si2157_config, 0, sizeof(si2157_config));
+			si2157_config.fe = fe0->dvb.frontend;
+			memset(&info, 0, sizeof(struct i2c_board_info));
+			strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+			info.addr = 0x60;
+			info.platform_data = &si2157_config;
+			request_module("%s", info.type);
+			client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info);
+			if (!client_tuner || !client_tuner->dev.driver) {
+				module_put(client_demod->dev.driver->owner);
+				i2c_unregister_device(client_demod);
+				port->i2c_client_demod = NULL;
+				goto frontend_detach;
+			}
+			if (!try_module_get(client_tuner->dev.driver->owner)) {
+				i2c_unregister_device(client_tuner);
+				module_put(client_demod->dev.driver->owner);
+				i2c_unregister_device(client_demod);
+				port->i2c_client_demod = NULL;
+				goto frontend_detach;
+			}
+			port->i2c_client_tuner = client_tuner;
+			break;
+		}
+		break;
 	default:
 		printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
 			" isn't supported yet\n",
@@ -2036,123 +2231,29 @@
 	if (ret)
 		goto frontend_detach;
 
-	/* init CI & MAC */
-	switch (dev->board) {
-	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: {
-		static struct netup_card_info cinfo;
+	ret = dvb_register_ci_mac(port);
+	if (ret)
+		goto frontend_detach;
 
-		netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
-		memcpy(port->frontends.adapter.proposed_mac,
-				cinfo.port[port->nr - 1].mac, 6);
-		printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n",
-			port->nr, port->frontends.adapter.proposed_mac);
-
-		netup_ci_init(port);
-		break;
-		}
-	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
-		struct altera_ci_config netup_ci_cfg = {
-			.dev = dev,/* magic number to identify*/
-			.adapter = &port->frontends.adapter,/* for CI */
-			.demux = &fe0->dvb.demux,/* for hw pid filter */
-			.fpga_rw = netup_altera_fpga_rw,
-		};
-
-		altera_ci_init(&netup_ci_cfg, port->nr);
-		break;
-		}
-	case CX23885_BOARD_TEVII_S470: {
-		u8 eeprom[256]; /* 24C02 i2c eeprom */
-
-		if (port->nr != 1)
-			break;
-
-		/* Read entire EEPROM */
-		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
-		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
-		printk(KERN_INFO "TeVii S470 MAC= %pM\n", eeprom + 0xa0);
-		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6);
-		break;
-		}
-	case CX23885_BOARD_DVBSKY_T9580:
-	case CX23885_BOARD_DVBSKY_S950:
-	case CX23885_BOARD_DVBSKY_S952:
-	case CX23885_BOARD_DVBSKY_T982: {
-		u8 eeprom[256]; /* 24C02 i2c eeprom */
-
-		if (port->nr > 2)
-			break;
-
-		/* Read entire EEPROM */
-		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
-		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom,
-				sizeof(eeprom));
-		printk(KERN_INFO "%s port %d MAC address: %pM\n",
-			cx23885_boards[dev->board].name, port->nr,
-			eeprom + 0xc0 + (port->nr-1) * 8);
-		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 +
-			(port->nr-1) * 8, 6);
-		break;
-		}
-	case CX23885_BOARD_DVBSKY_S950C:
-	case CX23885_BOARD_DVBSKY_T980C:
-	case CX23885_BOARD_TT_CT2_4500_CI: {
-		u8 eeprom[256]; /* 24C02 i2c eeprom */
-
-		/* attach CI */
-		memset(&sp2_config, 0, sizeof(sp2_config));
-		sp2_config.dvb_adap = &port->frontends.adapter;
-		sp2_config.priv = port;
-		sp2_config.ci_control = cx23885_sp2_ci_ctrl;
-		memset(&info, 0, sizeof(struct i2c_board_info));
-		strlcpy(info.type, "sp2", I2C_NAME_SIZE);
-		info.addr = 0x40;
-		info.platform_data = &sp2_config;
-		request_module(info.type);
-		client_ci = i2c_new_device(&i2c_bus2->i2c_adap, &info);
-		if (client_ci == NULL ||
-				client_ci->dev.driver == NULL) {
-			if (client_tuner) {
-				module_put(client_tuner->dev.driver->owner);
-				i2c_unregister_device(client_tuner);
-			}
-			if (client_demod) {
-				module_put(client_demod->dev.driver->owner);
-				i2c_unregister_device(client_demod);
-			}
-			goto frontend_detach;
-		}
-		if (!try_module_get(client_ci->dev.driver->owner)) {
-			i2c_unregister_device(client_ci);
-			if (client_tuner) {
-				module_put(client_tuner->dev.driver->owner);
-				i2c_unregister_device(client_tuner);
-			}
-			if (client_demod) {
-				module_put(client_demod->dev.driver->owner);
-				i2c_unregister_device(client_demod);
-			}
-			goto frontend_detach;
-		}
-		port->i2c_client_ci = client_ci;
-
-		if (port->nr != 1)
-			break;
-
-		/* Read entire EEPROM */
-		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
-		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom,
-				sizeof(eeprom));
-		printk(KERN_INFO "%s MAC address: %pM\n",
-			cx23885_boards[dev->board].name, eeprom + 0xc0);
-		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0, 6);
-		break;
-		}
-	}
-
-	return ret;
+	return 0;
 
 frontend_detach:
+	/* remove I2C client for tuner */
+	client_tuner = port->i2c_client_tuner;
+	if (client_tuner) {
+		module_put(client_tuner->dev.driver->owner);
+		i2c_unregister_device(client_tuner);
+		port->i2c_client_tuner = NULL;
+	}
+
+	/* remove I2C client for demodulator */
+	client_demod = port->i2c_client_demod;
+	if (client_demod) {
+		module_put(client_demod->dev.driver->owner);
+		i2c_unregister_device(client_demod);
+		port->i2c_client_demod = NULL;
+	}
+
 	port->gate_ctrl = NULL;
 	vb2_dvb_dealloc_frontends(&port->frontends);
 	return -EINVAL;
diff --git a/drivers/media/pci/cx23885/cx23885-i2c.c b/drivers/media/pci/cx23885/cx23885-i2c.c
index fd71306..1135ea3 100644
--- a/drivers/media/pci/cx23885/cx23885-i2c.c
+++ b/drivers/media/pci/cx23885/cx23885-i2c.c
@@ -300,8 +300,8 @@
 		rc = i2c_master_recv(c, &buf, 0);
 		if (rc < 0)
 			continue;
-		printk(KERN_INFO "%s: i2c scan: found device @ 0x%x  [%s]\n",
-		       name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+		printk(KERN_INFO "%s: i2c scan: found device @ 0x%04x  [%s]\n",
+		       name, i, i2c_devs[i] ? i2c_devs[i] : "???");
 	}
 }
 
diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c
index 2cd103d..12f9127 100644
--- a/drivers/media/pci/cx23885/cx23885-video.c
+++ b/drivers/media/pci/cx23885/cx23885-video.c
@@ -1137,7 +1137,6 @@
 	int err;
 
 	dprintk(1, "%s()\n", __func__);
-	spin_lock_init(&dev->slock);
 
 	/* Initialize VBI template */
 	cx23885_vbi_template = cx23885_video_template;
diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h
index f55cd12..aeda8d3 100644
--- a/drivers/media/pci/cx23885/cx23885.h
+++ b/drivers/media/pci/cx23885/cx23885.h
@@ -99,6 +99,8 @@
 #define CX23885_BOARD_DVBSKY_S950              49
 #define CX23885_BOARD_DVBSKY_S952              50
 #define CX23885_BOARD_DVBSKY_T982              51
+#define CX23885_BOARD_HAUPPAUGE_HVR5525        52
+#define CX23885_BOARD_HAUPPAUGE_STARBURST      53
 
 #define GPIO_0 0x00000001
 #define GPIO_1 0x00000002
diff --git a/drivers/media/pci/cx25821/Kconfig b/drivers/media/pci/cx25821/Kconfig
index 05b135e..1acca62 100644
--- a/drivers/media/pci/cx25821/Kconfig
+++ b/drivers/media/pci/cx25821/Kconfig
@@ -3,8 +3,7 @@
 	depends on m
 	depends on VIDEO_DEV && PCI && I2C
 	depends on I2C_ALGOBIT
-	select VIDEO_BTCX
-	select VIDEOBUF_DMA_SG
+	select VIDEOBUF2_DMA_SG
 	---help---
 	  This is a video4linux driver for Conexant 25821 based
 	  TV cards.
diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile
index 0c26750..4939392 100644
--- a/drivers/media/pci/cx25821/Makefile
+++ b/drivers/media/pci/cx25821/Makefile
@@ -1,9 +1,8 @@
 cx25821-y   := cx25821-core.o cx25821-cards.o cx25821-i2c.o \
 		       cx25821-gpio.o cx25821-medusa-video.o \
-		       cx25821-video.o cx25821-video-upstream.o
+		       cx25821-video.o
 
 obj-$(CPTCFG_VIDEO_CX25821) += cx25821.o
 obj-$(CPTCFG_VIDEO_CX25821_ALSA) += cx25821-alsa.o
 
 ccflags-y += -I$(backport_srctree)/drivers/media/i2c
-ccflags-y += -I$(backport_srctree)/drivers/media/common
diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c
index 2dd5bca..24f964b 100644
--- a/drivers/media/pci/cx25821/cx25821-alsa.c
+++ b/drivers/media/pci/cx25821/cx25821-alsa.c
@@ -63,8 +63,11 @@
 
 struct cx25821_audio_buffer {
 	unsigned int bpl;
-	struct btcx_riscmem risc;
-	struct videobuf_dmabuf dma;
+	struct cx25821_riscmem risc;
+	void			*vaddr;
+	struct scatterlist	*sglist;
+	int                     sglen;
+	int                     nr_pages;
 };
 
 struct cx25821_audio_dev {
@@ -87,8 +90,6 @@
 	unsigned int period_size;
 	unsigned int num_periods;
 
-	struct videobuf_dmabuf *dma_risc;
-
 	struct cx25821_audio_buffer *buf;
 
 	struct snd_pcm_substream *substream;
@@ -142,6 +143,83 @@
 
 #define PCI_MSK_AUD_EXT   (1 <<  4)
 #define PCI_MSK_AUD_INT   (1 <<  3)
+
+static int cx25821_alsa_dma_init(struct cx25821_audio_dev *chip, int nr_pages)
+{
+	struct cx25821_audio_buffer *buf = chip->buf;
+	struct page *pg;
+	int i;
+
+	buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
+	if (NULL == buf->vaddr) {
+		dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages);
+		return -ENOMEM;
+	}
+
+	dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n",
+				(unsigned long)buf->vaddr,
+				nr_pages << PAGE_SHIFT);
+
+	memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT);
+	buf->nr_pages = nr_pages;
+
+	buf->sglist = vzalloc(buf->nr_pages * sizeof(*buf->sglist));
+	if (NULL == buf->sglist)
+		goto vzalloc_err;
+
+	sg_init_table(buf->sglist, buf->nr_pages);
+	for (i = 0; i < buf->nr_pages; i++) {
+		pg = vmalloc_to_page(buf->vaddr + i * PAGE_SIZE);
+		if (NULL == pg)
+			goto vmalloc_to_page_err;
+		sg_set_page(&buf->sglist[i], pg, PAGE_SIZE, 0);
+	}
+	return 0;
+
+vmalloc_to_page_err:
+	vfree(buf->sglist);
+	buf->sglist = NULL;
+vzalloc_err:
+	vfree(buf->vaddr);
+	buf->vaddr = NULL;
+	return -ENOMEM;
+}
+
+static int cx25821_alsa_dma_map(struct cx25821_audio_dev *dev)
+{
+	struct cx25821_audio_buffer *buf = dev->buf;
+
+	buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist,
+			buf->nr_pages, PCI_DMA_FROMDEVICE);
+
+	if (0 == buf->sglen) {
+		pr_warn("%s: cx25821_alsa_map_sg failed\n", __func__);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static int cx25821_alsa_dma_unmap(struct cx25821_audio_dev *dev)
+{
+	struct cx25821_audio_buffer *buf = dev->buf;
+
+	if (!buf->sglen)
+		return 0;
+
+	dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->sglen, PCI_DMA_FROMDEVICE);
+	buf->sglen = 0;
+	return 0;
+}
+
+static int cx25821_alsa_dma_free(struct cx25821_audio_buffer *buf)
+{
+	vfree(buf->sglist);
+	buf->sglist = NULL;
+	vfree(buf->vaddr);
+	buf->vaddr = NULL;
+	return 0;
+}
+
 /*
  * BOARD Specific: Sets audio DMA
  */
@@ -330,15 +408,17 @@
 
 static int dsp_buffer_free(struct cx25821_audio_dev *chip)
 {
+	struct cx25821_riscmem *risc = &chip->buf->risc;
+
 	BUG_ON(!chip->dma_size);
 
 	dprintk(2, "Freeing buffer\n");
-	videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
-	videobuf_dma_free(chip->dma_risc);
-	btcx_riscmem_free(chip->pci, &chip->buf->risc);
+	cx25821_alsa_dma_unmap(chip);
+	cx25821_alsa_dma_free(chip->buf);
+	pci_free_consistent(chip->pci, risc->size, risc->cpu, risc->dma);
 	kfree(chip->buf);
 
-	chip->dma_risc = NULL;
+	chip->buf = NULL;
 	chip->dma_size = 0;
 
 	return 0;
@@ -430,8 +510,6 @@
 				 struct snd_pcm_hw_params *hw_params)
 {
 	struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream);
-	struct videobuf_dmabuf *dma;
-
 	struct cx25821_audio_buffer *buf;
 	int ret;
 
@@ -455,19 +533,18 @@
 		chip->period_size = AUDIO_LINE_SIZE;
 
 	buf->bpl = chip->period_size;
+	chip->buf = buf;
 
-	dma = &buf->dma;
-	videobuf_dma_init(dma);
-	ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+	ret = cx25821_alsa_dma_init(chip,
 			(PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
 	if (ret < 0)
 		goto error;
 
-	ret = videobuf_dma_map(&chip->pci->dev, dma);
+	ret = cx25821_alsa_dma_map(chip);
 	if (ret < 0)
 		goto error;
 
-	ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, dma->sglist,
+	ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, buf->sglist,
 			chip->period_size, chip->num_periods, 1);
 	if (ret < 0) {
 		pr_info("DEBUG: ERROR after cx25821_risc_databuffer_audio()\n");
@@ -479,16 +556,14 @@
 	buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
 	buf->risc.jmp[2] = cpu_to_le32(0);	/* bits 63-32 */
 
-	chip->buf = buf;
-	chip->dma_risc = dma;
-
-	substream->runtime->dma_area = chip->dma_risc->vaddr;
+	substream->runtime->dma_area = chip->buf->vaddr;
 	substream->runtime->dma_bytes = chip->dma_size;
 	substream->runtime->dma_addr = 0;
 
 	return 0;
 
 error:
+	chip->buf = NULL;
 	kfree(buf);
 	return ret;
 }
diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c
index 389fffd..559f829 100644
--- a/drivers/media/pci/cx25821/cx25821-core.c
+++ b/drivers/media/pci/cx25821/cx25821-core.c
@@ -874,10 +874,9 @@
 	if (dev->pci->device != 0x8210) {
 		pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n",
 			__func__, dev->pci->device);
-		return -1;
-	} else {
-		pr_info("Athena Hardware device = 0x%02x\n", dev->pci->device);
+		return -ENODEV;
 	}
+	pr_info("Athena Hardware device = 0x%02x\n", dev->pci->device);
 
 	/* Apply a sensible clock frequency for the PCIe bridge */
 	dev->clk_freq = 28000000;
@@ -966,11 +965,15 @@
 
 	release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0));
 
-	for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; i++) {
+	for (i = 0; i < MAX_VID_CAP_CHANNEL_NUM - 1; i++) {
 		if (i == SRAM_CH08) /* audio channel */
 			continue;
+		/*
+		 * TODO: enable when video output is properly
+		 * supported.
 		if (i == SRAM_CH09 || i == SRAM_CH10)
 			cx25821_free_mem_upstream(&dev->channels[i]);
+		 */
 		cx25821_video_unregister(dev, i);
 	}
 
@@ -979,14 +982,41 @@
 }
 EXPORT_SYMBOL(cx25821_dev_unregister);
 
+int cx25821_riscmem_alloc(struct pci_dev *pci,
+		       struct cx25821_riscmem *risc,
+		       unsigned int size)
+{
+	__le32 *cpu;
+	dma_addr_t dma = 0;
+
+	if (NULL != risc->cpu && risc->size < size)
+		pci_free_consistent(pci, risc->size, risc->cpu, risc->dma);
+	if (NULL == risc->cpu) {
+		cpu = pci_zalloc_consistent(pci, size, &dma);
+		if (NULL == cpu)
+			return -ENOMEM;
+		risc->cpu  = cpu;
+		risc->dma  = dma;
+		risc->size = size;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(cx25821_riscmem_alloc);
+
 static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist,
 				  unsigned int offset, u32 sync_line,
 				  unsigned int bpl, unsigned int padding,
-				  unsigned int lines)
+				  unsigned int lines, bool jump)
 {
 	struct scatterlist *sg;
 	unsigned int line, todo;
 
+	if (jump) {
+		*(rp++) = cpu_to_le32(RISC_JUMP);
+		*(rp++) = cpu_to_le32(0);
+		*(rp++) = cpu_to_le32(0); /* bits 63-32 */
+	}
+
 	/* sync instruction */
 	if (sync_line != NO_SYNC_LINE)
 		*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
@@ -1035,7 +1065,7 @@
 	return rp;
 }
 
-int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+int cx25821_risc_buffer(struct pci_dev *pci, struct cx25821_riscmem *risc,
 			struct scatterlist *sglist, unsigned int top_offset,
 			unsigned int bottom_offset, unsigned int bpl,
 			unsigned int padding, unsigned int lines)
@@ -1052,14 +1082,14 @@
 		fields++;
 
 	/* estimate risc mem: worst case is one write per page border +
-	   one write per scan line + syncs + jump (all 2 dwords).  Padding
+	   one write per scan line + syncs + jump (all 3 dwords).  Padding
 	   can cause next bpl to start close to a page border.  First DMA
 	   region may be smaller than PAGE_SIZE */
 	/* write and jump need and extra dword */
 	instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE +
 			lines);
-	instructions += 2;
-	rc = btcx_riscmem_alloc(pci, risc, instructions * 12);
+	instructions += 5;
+	rc = cx25821_riscmem_alloc(pci, risc, instructions * 12);
 
 	if (rc < 0)
 		return rc;
@@ -1069,17 +1099,17 @@
 
 	if (UNSET != top_offset) {
 		rp = cx25821_risc_field(rp, sglist, top_offset, 0, bpl, padding,
-					lines);
+					lines, true);
 	}
 
 	if (UNSET != bottom_offset) {
 		rp = cx25821_risc_field(rp, sglist, bottom_offset, 0x200, bpl,
-					padding, lines);
+					padding, lines, UNSET == top_offset);
 	}
 
 	/* save pointer to jmp instruction address */
 	risc->jmp = rp;
-	BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+	BUG_ON((risc->jmp - risc->cpu + 3) * sizeof(*risc->cpu) > risc->size);
 
 	return 0;
 }
@@ -1146,7 +1176,7 @@
 }
 
 int cx25821_risc_databuffer_audio(struct pci_dev *pci,
-				  struct btcx_riscmem *risc,
+				  struct cx25821_riscmem *risc,
 				  struct scatterlist *sglist,
 				  unsigned int bpl,
 				  unsigned int lines, unsigned int lpi)
@@ -1163,7 +1193,7 @@
 	instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
 	instructions += 1;
 
-	rc = btcx_riscmem_alloc(pci, risc, instructions * 12);
+	rc = cx25821_riscmem_alloc(pci, risc, instructions * 12);
 	if (rc < 0)
 		return rc;
 
@@ -1179,40 +1209,14 @@
 }
 EXPORT_SYMBOL(cx25821_risc_databuffer_audio);
 
-int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
-			 u32 reg, u32 mask, u32 value)
+void cx25821_free_buffer(struct cx25821_dev *dev, struct cx25821_buffer *buf)
 {
-	__le32 *rp;
-	int rc;
-
-	rc = btcx_riscmem_alloc(pci, risc, 4 * 16);
-
-	if (rc < 0)
-		return rc;
-
-	/* write risc instructions */
-	rp = risc->cpu;
-
-	*(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ1);
-	*(rp++) = cpu_to_le32(reg);
-	*(rp++) = cpu_to_le32(value);
-	*(rp++) = cpu_to_le32(mask);
-	*(rp++) = cpu_to_le32(RISC_JUMP);
-	*(rp++) = cpu_to_le32(risc->dma);
-	*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
-	return 0;
-}
-
-void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf)
-{
-	struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
-
 	BUG_ON(in_interrupt());
-	videobuf_waiton(q, &buf->vb, 0, 0);
-	videobuf_dma_unmap(q->dev, dma);
-	videobuf_dma_free(dma);
-	btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
-	buf->vb.state = VIDEOBUF_NEEDS_INIT;
+	if (WARN_ON(buf->risc.size == 0))
+		return;
+	pci_free_consistent(dev->pci,
+			buf->risc.size, buf->risc.cpu, buf->risc.dma);
+	memset(&buf->risc, 0, sizeof(buf->risc));
 }
 
 static irqreturn_t cx25821_irq(int irq, void *dev_id)
@@ -1297,14 +1301,15 @@
 
 		goto fail_unregister_device;
 	}
+	dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
+	if (IS_ERR(dev->alloc_ctx)) {
+		err = PTR_ERR(dev->alloc_ctx);
+		goto fail_unregister_pci;
+	}
 
 	err = cx25821_dev_setup(dev);
-	if (err) {
-		if (err == -EBUSY)
-			goto fail_unregister_device;
-		else
-			goto fail_unregister_pci;
-	}
+	if (err)
+		goto fail_free_ctx;
 
 	/* print pci info */
 	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
@@ -1334,6 +1339,8 @@
 	pr_info("cx25821_initdev() can't get IRQ !\n");
 	cx25821_dev_unregister(dev);
 
+fail_free_ctx:
+	vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
 fail_unregister_pci:
 	pci_disable_device(pci_dev);
 fail_unregister_device:
@@ -1357,6 +1364,7 @@
 		free_irq(pci_dev->irq, dev);
 
 	cx25821_dev_unregister(dev);
+	vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
 	v4l2_device_unregister(v4l2_dev);
 	kfree(dev);
 }
diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c
index 3a419f1..7bc495e 100644
--- a/drivers/media/pci/cx25821/cx25821-video.c
+++ b/drivers/media/pci/cx25821/cx25821-video.c
@@ -3,7 +3,7 @@
  *
  *  Copyright (C) 2009 Conexant Systems Inc.
  *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
- *  Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *  Based on Steven Toth <stoth@linuxtv.org> cx25821 driver
  *  Parts adapted/taken from Eduardo Moscoso Rubino
  *  Copyright (C) 2009 Eduardo Moscoso Rubino <moscoso@TopoLogica.com>
  *
@@ -46,10 +46,6 @@
 module_param(irq_debug, int, 0644);
 MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
 
-static unsigned int vid_limit = 16;
-module_param(vid_limit, int, 0644);
-MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
-
 #define FORMAT_FLAGS_PACKED       0x01
 
 static const struct cx25821_fmt formats[] = {
@@ -76,41 +72,6 @@
 	return NULL;
 }
 
-void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q,
-			  u32 count)
-{
-	struct cx25821_buffer *buf;
-	int bc;
-
-	for (bc = 0;; bc++) {
-		if (list_empty(&q->active)) {
-			dprintk(1, "bc=%d (=0: active empty)\n", bc);
-			break;
-		}
-
-		buf = list_entry(q->active.next, struct cx25821_buffer,
-				vb.queue);
-
-		/* count comes from the hw and it is 16bit wide --
-		 * this trick handles wrap-arounds correctly for
-		 * up to 32767 buffers in flight... */
-		if ((s16) (count - buf->count) < 0)
-			break;
-
-		v4l2_get_timestamp(&buf->vb.ts);
-		buf->vb.state = VIDEOBUF_DONE;
-		list_del(&buf->vb.queue);
-		wake_up(&buf->vb.done);
-	}
-
-	if (list_empty(&q->active))
-		del_timer(&q->timeout);
-	else
-		mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
-	if (bc != 1)
-		pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc);
-}
-
 int cx25821_start_video_dma(struct cx25821_dev *dev,
 			    struct cx25821_dmaqueue *q,
 			    struct cx25821_buffer *buf,
@@ -123,7 +84,6 @@
 
 	/* reset counter */
 	cx_write(channel->gpcnt_ctl, 3);
-	q->count = 1;
 
 	/* enable irq */
 	cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i));
@@ -139,86 +99,8 @@
 	return 0;
 }
 
-static int cx25821_restart_video_queue(struct cx25821_dev *dev,
-				       struct cx25821_dmaqueue *q,
-				       const struct sram_channel *channel)
-{
-	struct cx25821_buffer *buf, *prev;
-	struct list_head *item;
-
-	if (!list_empty(&q->active)) {
-		buf = list_entry(q->active.next, struct cx25821_buffer,
-				vb.queue);
-
-		cx25821_start_video_dma(dev, q, buf, channel);
-
-		list_for_each(item, &q->active) {
-			buf = list_entry(item, struct cx25821_buffer, vb.queue);
-			buf->count = q->count++;
-		}
-
-		mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
-		return 0;
-	}
-
-	prev = NULL;
-	for (;;) {
-		if (list_empty(&q->queued))
-			return 0;
-
-		buf = list_entry(q->queued.next, struct cx25821_buffer,
-				vb.queue);
-
-		if (NULL == prev) {
-			list_move_tail(&buf->vb.queue, &q->active);
-			cx25821_start_video_dma(dev, q, buf, channel);
-			buf->vb.state = VIDEOBUF_ACTIVE;
-			buf->count = q->count++;
-			mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
-		} else if (prev->vb.width == buf->vb.width &&
-			   prev->vb.height == buf->vb.height &&
-			   prev->fmt == buf->fmt) {
-			list_move_tail(&buf->vb.queue, &q->active);
-			buf->vb.state = VIDEOBUF_ACTIVE;
-			buf->count = q->count++;
-			prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-			prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
-		} else {
-			return 0;
-		}
-		prev = buf;
-	}
-}
-
-static void cx25821_vid_timeout(unsigned long data)
-{
-	struct cx25821_data *timeout_data = (struct cx25821_data *)data;
-	struct cx25821_dev *dev = timeout_data->dev;
-	const struct sram_channel *channel = timeout_data->channel;
-	struct cx25821_dmaqueue *q = &dev->channels[channel->i].dma_vidq;
-	struct cx25821_buffer *buf;
-	unsigned long flags;
-
-	/* cx25821_sram_channel_dump(dev, channel); */
-	cx_clear(channel->dma_ctl, 0x11);
-
-	spin_lock_irqsave(&dev->slock, flags);
-	while (!list_empty(&q->active)) {
-		buf = list_entry(q->active.next, struct cx25821_buffer,
-				vb.queue);
-		list_del(&buf->vb.queue);
-
-		buf->vb.state = VIDEOBUF_ERROR;
-		wake_up(&buf->vb.done);
-	}
-
-	cx25821_restart_video_queue(dev, q, channel);
-	spin_unlock_irqrestore(&dev->slock, flags);
-}
-
 int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
 {
-	u32 count = 0;
 	int handled = 0;
 	u32 mask;
 	const struct sram_channel *channel = dev->channels[chan_num].sram_channels;
@@ -239,317 +121,201 @@
 
 	/* risc1 y */
 	if (status & FLD_VID_DST_RISC1) {
-		spin_lock(&dev->slock);
-		count = cx_read(channel->gpcnt);
-		cx25821_video_wakeup(dev, &dev->channels[channel->i].dma_vidq,
-				count);
-		spin_unlock(&dev->slock);
-		handled++;
-	}
+		struct cx25821_dmaqueue *dmaq =
+			&dev->channels[channel->i].dma_vidq;
+		struct cx25821_buffer *buf;
 
-	/* risc2 y */
-	if (status & 0x10) {
-		dprintk(2, "stopper video\n");
 		spin_lock(&dev->slock);
-		cx25821_restart_video_queue(dev,
-				&dev->channels[channel->i].dma_vidq, channel);
+		if (!list_empty(&dmaq->active)) {
+			buf = list_entry(dmaq->active.next,
+					 struct cx25821_buffer, queue);
+
+			v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+			buf->vb.v4l2_buf.sequence = dmaq->count++;
+			list_del(&buf->queue);
+			vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+		}
 		spin_unlock(&dev->slock);
 		handled++;
 	}
 	return handled;
 }
 
-static int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count,
-		 unsigned int *size)
+static int cx25821_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+			   unsigned int *num_buffers, unsigned int *num_planes,
+			   unsigned int sizes[], void *alloc_ctxs[])
 {
-	struct cx25821_channel *chan = q->priv_data;
+	struct cx25821_channel *chan = q->drv_priv;
+	unsigned size = (chan->fmt->depth * chan->width * chan->height) >> 3;
 
-	*size = chan->fmt->depth * chan->width * chan->height >> 3;
+	if (fmt && fmt->fmt.pix.sizeimage < size)
+		return -EINVAL;
 
-	if (0 == *count)
-		*count = 32;
-
-	if (*size * *count > vid_limit * 1024 * 1024)
-		*count = (vid_limit * 1024 * 1024) / *size;
-
+	*num_planes = 1;
+	sizes[0] = fmt ? fmt->fmt.pix.sizeimage : size;
+	alloc_ctxs[0] = chan->dev->alloc_ctx;
 	return 0;
 }
 
-static int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
-		   enum v4l2_field field)
+static int cx25821_buffer_prepare(struct vb2_buffer *vb)
 {
-	struct cx25821_channel *chan = q->priv_data;
+	struct cx25821_channel *chan = vb->vb2_queue->drv_priv;
 	struct cx25821_dev *dev = chan->dev;
 	struct cx25821_buffer *buf =
 		container_of(vb, struct cx25821_buffer, vb);
-	int rc, init_buffer = 0;
+	struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
 	u32 line0_offset;
-	struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
 	int bpl_local = LINE_SIZE_D1;
+	int ret;
 
-	BUG_ON(NULL == chan->fmt);
-	if (chan->width < 48 || chan->width > 720 ||
-	    chan->height < 32 || chan->height > 576)
+	if (chan->pixel_formats == PIXEL_FRMT_411)
+		buf->bpl = (chan->fmt->depth * chan->width) >> 3;
+	else
+		buf->bpl = (chan->fmt->depth >> 3) * chan->width;
+
+	if (vb2_plane_size(vb, 0) < chan->height * buf->bpl)
 		return -EINVAL;
+	vb2_set_plane_payload(vb, 0, chan->height * buf->bpl);
+	buf->vb.v4l2_buf.field = chan->field;
 
-	buf->vb.size = (chan->width * chan->height * chan->fmt->depth) >> 3;
+	if (chan->pixel_formats == PIXEL_FRMT_411) {
+		bpl_local = buf->bpl;
+	} else {
+		bpl_local = buf->bpl;   /* Default */
 
-	if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
-		return -EINVAL;
-
-	if (buf->fmt != chan->fmt ||
-	    buf->vb.width != chan->width ||
-	    buf->vb.height != chan->height || buf->vb.field != field) {
-		buf->fmt = chan->fmt;
-		buf->vb.width = chan->width;
-		buf->vb.height = chan->height;
-		buf->vb.field = field;
-		init_buffer = 1;
-	}
-
-	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-		init_buffer = 1;
-		rc = videobuf_iolock(q, &buf->vb, NULL);
-		if (0 != rc) {
-			printk(KERN_DEBUG pr_fmt("videobuf_iolock failed!\n"));
-			goto fail;
+		if (chan->use_cif_resolution) {
+			if (dev->tvnorm & V4L2_STD_625_50)
+				bpl_local = 352 << 1;
+			else
+				bpl_local = chan->cif_width << 1;
 		}
 	}
 
-	dprintk(1, "init_buffer=%d\n", init_buffer);
+	switch (chan->field) {
+	case V4L2_FIELD_TOP:
+		ret = cx25821_risc_buffer(dev->pci, &buf->risc,
+				sgt->sgl, 0, UNSET,
+				buf->bpl, 0, chan->height);
+		break;
+	case V4L2_FIELD_BOTTOM:
+		ret = cx25821_risc_buffer(dev->pci, &buf->risc,
+				sgt->sgl, UNSET, 0,
+				buf->bpl, 0, chan->height);
+		break;
+	case V4L2_FIELD_INTERLACED:
+		/* All other formats are top field first */
+		line0_offset = 0;
+		dprintk(1, "top field first\n");
 
-	if (init_buffer) {
-		if (chan->pixel_formats == PIXEL_FRMT_411)
-			buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3;
-		else
-			buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width);
-
-		if (chan->pixel_formats == PIXEL_FRMT_411) {
-			bpl_local = buf->bpl;
-		} else {
-			bpl_local = buf->bpl;   /* Default */
-
-			if (chan->use_cif_resolution) {
-				if (dev->tvnorm & V4L2_STD_625_50)
-					bpl_local = 352 << 1;
-				else
-					bpl_local = chan->cif_width << 1;
-			}
-		}
-
-		switch (buf->vb.field) {
-		case V4L2_FIELD_TOP:
-			cx25821_risc_buffer(dev->pci, &buf->risc,
-					    dma->sglist, 0, UNSET,
-					    buf->bpl, 0, buf->vb.height);
-			break;
-		case V4L2_FIELD_BOTTOM:
-			cx25821_risc_buffer(dev->pci, &buf->risc,
-					    dma->sglist, UNSET, 0,
-					    buf->bpl, 0, buf->vb.height);
-			break;
-		case V4L2_FIELD_INTERLACED:
-			/* All other formats are top field first */
-			line0_offset = 0;
-			dprintk(1, "top field first\n");
-
-			cx25821_risc_buffer(dev->pci, &buf->risc,
-					    dma->sglist, line0_offset,
-					    bpl_local, bpl_local, bpl_local,
-					    buf->vb.height >> 1);
-			break;
-		case V4L2_FIELD_SEQ_TB:
-			cx25821_risc_buffer(dev->pci, &buf->risc,
-					    dma->sglist,
-					    0, buf->bpl * (buf->vb.height >> 1),
-					    buf->bpl, 0, buf->vb.height >> 1);
-			break;
-		case V4L2_FIELD_SEQ_BT:
-			cx25821_risc_buffer(dev->pci, &buf->risc,
-					    dma->sglist,
-					    buf->bpl * (buf->vb.height >> 1), 0,
-					    buf->bpl, 0, buf->vb.height >> 1);
-			break;
-		default:
-			BUG();
-		}
+		ret = cx25821_risc_buffer(dev->pci, &buf->risc,
+				sgt->sgl, line0_offset,
+				bpl_local, bpl_local, bpl_local,
+				chan->height >> 1);
+		break;
+	case V4L2_FIELD_SEQ_TB:
+		ret = cx25821_risc_buffer(dev->pci, &buf->risc,
+				sgt->sgl,
+				0, buf->bpl * (chan->height >> 1),
+				buf->bpl, 0, chan->height >> 1);
+		break;
+	case V4L2_FIELD_SEQ_BT:
+		ret = cx25821_risc_buffer(dev->pci, &buf->risc,
+				sgt->sgl,
+				buf->bpl * (chan->height >> 1), 0,
+				buf->bpl, 0, chan->height >> 1);
+		break;
+	default:
+		WARN_ON(1);
+		ret = -EINVAL;
+		break;
 	}
 
 	dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
-		buf, buf->vb.i, chan->width, chan->height, chan->fmt->depth,
-		chan->fmt->name, (unsigned long)buf->risc.dma);
+		buf, buf->vb.v4l2_buf.index, chan->width, chan->height,
+		chan->fmt->depth, chan->fmt->name,
+		(unsigned long)buf->risc.dma);
 
-	buf->vb.state = VIDEOBUF_PREPARED;
-
-	return 0;
-
-fail:
-	cx25821_free_buffer(q, buf);
-	return rc;
+	return ret;
 }
 
-static void cx25821_buffer_release(struct videobuf_queue *q,
-			    struct videobuf_buffer *vb)
+static void cx25821_buffer_finish(struct vb2_buffer *vb)
 {
 	struct cx25821_buffer *buf =
 		container_of(vb, struct cx25821_buffer, vb);
-
-	cx25821_free_buffer(q, buf);
-}
-
-static int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma)
-{
-	struct cx25821_channel *chan = video_drvdata(file);
-
-	return videobuf_mmap_mapper(&chan->vidq, vma);
-}
-
-
-static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
-	struct cx25821_buffer *buf =
-		container_of(vb, struct cx25821_buffer, vb);
-	struct cx25821_buffer *prev;
-	struct cx25821_channel *chan = vq->priv_data;
+	struct cx25821_channel *chan = vb->vb2_queue->drv_priv;
 	struct cx25821_dev *dev = chan->dev;
+
+	cx25821_free_buffer(dev, buf);
+}
+
+static void cx25821_buffer_queue(struct vb2_buffer *vb)
+{
+	struct cx25821_buffer *buf =
+		container_of(vb, struct cx25821_buffer, vb);
+	struct cx25821_channel *chan = vb->vb2_queue->drv_priv;
+	struct cx25821_dev *dev = chan->dev;
+	struct cx25821_buffer *prev;
 	struct cx25821_dmaqueue *q = &dev->channels[chan->id].dma_vidq;
 
-	/* add jump to stopper */
-	buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
-	buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
-	buf->risc.jmp[2] = cpu_to_le32(0);      /* bits 63-32 */
+	buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12);
+	buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
+	buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12);
+	buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
 
-	dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
-
-	if (!list_empty(&q->queued)) {
-		list_add_tail(&buf->vb.queue, &q->queued);
-		buf->vb.state = VIDEOBUF_QUEUED;
-		dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf,
-				buf->vb.i);
-
-	} else if (list_empty(&q->active)) {
-		list_add_tail(&buf->vb.queue, &q->active);
-		cx25821_start_video_dma(dev, q, buf, chan->sram_channels);
-		buf->vb.state = VIDEOBUF_ACTIVE;
-		buf->count = q->count++;
-		mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
-		dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
-				buf, buf->vb.i, buf->count, q->count);
+	if (list_empty(&q->active)) {
+		list_add_tail(&buf->queue, &q->active);
 	} else {
+		buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
 		prev = list_entry(q->active.prev, struct cx25821_buffer,
-				vb.queue);
-		if (prev->vb.width == buf->vb.width
-		   && prev->vb.height == buf->vb.height
-		   && prev->fmt == buf->fmt) {
-			list_add_tail(&buf->vb.queue, &q->active);
-			buf->vb.state = VIDEOBUF_ACTIVE;
-			buf->count = q->count++;
-			prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-
-			/* 64 bit bits 63-32 */
-			prev->risc.jmp[2] = cpu_to_le32(0);
-			dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n",
-					buf, buf->vb.i, buf->count);
-
-		} else {
-			list_add_tail(&buf->vb.queue, &q->queued);
-			buf->vb.state = VIDEOBUF_QUEUED;
-			dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf,
-					buf->vb.i);
-		}
+				queue);
+		list_add_tail(&buf->queue, &q->active);
+		prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
 	}
-
-	if (list_empty(&q->active))
-		dprintk(2, "active queue empty!\n");
 }
 
-static struct videobuf_queue_ops cx25821_video_qops = {
-	.buf_setup = cx25821_buffer_setup,
-	.buf_prepare = cx25821_buffer_prepare,
-	.buf_queue = buffer_queue,
-	.buf_release = cx25821_buffer_release,
+static int cx25821_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct cx25821_channel *chan = q->drv_priv;
+	struct cx25821_dev *dev = chan->dev;
+	struct cx25821_dmaqueue *dmaq = &dev->channels[chan->id].dma_vidq;
+	struct cx25821_buffer *buf = list_entry(dmaq->active.next,
+			struct cx25821_buffer, queue);
+
+	dmaq->count = 0;
+	cx25821_start_video_dma(dev, dmaq, buf, chan->sram_channels);
+	return 0;
+}
+
+static void cx25821_stop_streaming(struct vb2_queue *q)
+{
+	struct cx25821_channel *chan = q->drv_priv;
+	struct cx25821_dev *dev = chan->dev;
+	struct cx25821_dmaqueue *dmaq = &dev->channels[chan->id].dma_vidq;
+	unsigned long flags;
+
+	cx_write(chan->sram_channels->dma_ctl, 0); /* FIFO and RISC disable */
+	spin_lock_irqsave(&dev->slock, flags);
+	while (!list_empty(&dmaq->active)) {
+		struct cx25821_buffer *buf = list_entry(dmaq->active.next,
+			struct cx25821_buffer, queue);
+
+		list_del(&buf->queue);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+	}
+	spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static struct vb2_ops cx25821_video_qops = {
+	.queue_setup    = cx25821_queue_setup,
+	.buf_prepare  = cx25821_buffer_prepare,
+	.buf_finish = cx25821_buffer_finish,
+	.buf_queue    = cx25821_buffer_queue,
+	.wait_prepare = vb2_ops_wait_prepare,
+	.wait_finish = vb2_ops_wait_finish,
+	.start_streaming = cx25821_start_streaming,
+	.stop_streaming = cx25821_stop_streaming,
 };
 
-static ssize_t video_read(struct file *file, char __user * data, size_t count,
-			 loff_t *ppos)
-{
-	struct v4l2_fh *fh = file->private_data;
-	struct cx25821_channel *chan = video_drvdata(file);
-	struct cx25821_dev *dev = chan->dev;
-	int err = 0;
-
-	if (mutex_lock_interruptible(&dev->lock))
-		return -ERESTARTSYS;
-	if (chan->streaming_fh && chan->streaming_fh != fh) {
-		err = -EBUSY;
-		goto unlock;
-	}
-	chan->streaming_fh = fh;
-
-	err = videobuf_read_one(&chan->vidq, data, count, ppos,
-				file->f_flags & O_NONBLOCK);
-unlock:
-	mutex_unlock(&dev->lock);
-	return err;
-}
-
-static unsigned int video_poll(struct file *file,
-			      struct poll_table_struct *wait)
-{
-	struct cx25821_channel *chan = video_drvdata(file);
-	unsigned long req_events = poll_requested_events(wait);
-	unsigned int res = v4l2_ctrl_poll(file, wait);
-
-	if (req_events & (POLLIN | POLLRDNORM))
-		res |= videobuf_poll_stream(file, &chan->vidq, wait);
-	return res;
-
-	/* This doesn't belong in poll(). This can be done
-	 * much better with vb2. We keep this code here as a
-	 * reminder.
-	if ((res & POLLIN) && buf->vb.state == VIDEOBUF_DONE) {
-		struct cx25821_dev *dev = chan->dev;
-
-		if (dev && chan->use_cif_resolution) {
-			u8 cam_id = *((char *)buf->vb.baddr + 3);
-			memcpy((char *)buf->vb.baddr,
-					(char *)buf->vb.baddr + (chan->width * 2),
-					(chan->width * 2));
-			*((char *)buf->vb.baddr + 3) = cam_id;
-		}
-	}
-	 */
-}
-
-static int video_release(struct file *file)
-{
-	struct cx25821_channel *chan = video_drvdata(file);
-	struct v4l2_fh *fh = file->private_data;
-	struct cx25821_dev *dev = chan->dev;
-	const struct sram_channel *sram_ch =
-		dev->channels[0].sram_channels;
-
-	mutex_lock(&dev->lock);
-	/* stop the risc engine and fifo */
-	cx_write(sram_ch->dma_ctl, 0); /* FIFO and RISC disable */
-
-	/* stop video capture */
-	if (chan->streaming_fh == fh) {
-		videobuf_queue_cancel(&chan->vidq);
-		chan->streaming_fh = NULL;
-	}
-
-	if (chan->vidq.read_buf) {
-		cx25821_buffer_release(&chan->vidq, chan->vidq.read_buf);
-		kfree(chan->vidq.read_buf);
-	}
-
-	videobuf_mmap_free(&chan->vidq);
-	mutex_unlock(&dev->lock);
-
-	return v4l2_fh_release(file);
-}
-
 /* VIDEO IOCTLS */
 
 static int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
@@ -571,7 +337,7 @@
 
 	f->fmt.pix.width = chan->width;
 	f->fmt.pix.height = chan->height;
-	f->fmt.pix.field = chan->vidq.field;
+	f->fmt.pix.field = chan->field;
 	f->fmt.pix.pixelformat = chan->fmt->fourcc;
 	f->fmt.pix.bytesperline = (chan->width * chan->fmt->depth) >> 3;
 	f->fmt.pix.sizeimage = chan->height * f->fmt.pix.bytesperline;
@@ -632,7 +398,7 @@
 		return err;
 
 	chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
-	chan->vidq.field = f->fmt.pix.field;
+	chan->field = f->fmt.pix.field;
 	chan->width = f->fmt.pix.width;
 	chan->height = f->fmt.pix.height;
 
@@ -654,47 +420,6 @@
 	return 0;
 }
 
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
-	struct cx25821_channel *chan = video_drvdata(file);
-
-	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		return -EINVAL;
-
-	if (chan->streaming_fh && chan->streaming_fh != priv)
-		return -EBUSY;
-	chan->streaming_fh = priv;
-
-	return videobuf_streamon(&chan->vidq);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
-	struct cx25821_channel *chan = video_drvdata(file);
-
-	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		return -EINVAL;
-
-	if (chan->streaming_fh && chan->streaming_fh != priv)
-		return -EBUSY;
-	if (chan->streaming_fh == NULL)
-		return 0;
-
-	chan->streaming_fh = NULL;
-	return videobuf_streamoff(&chan->vidq);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
-	int ret_val = 0;
-	struct cx25821_channel *chan = video_drvdata(file);
-
-	ret_val = videobuf_dqbuf(&chan->vidq, p, file->f_flags & O_NONBLOCK);
-	p->sequence = chan->dma_vidq.count;
-
-	return ret_val;
-}
-
 static int vidioc_log_status(struct file *file, void *priv)
 {
 	struct cx25821_channel *chan = video_drvdata(file);
@@ -729,29 +454,6 @@
 	return 0;
 }
 
-static int cx25821_vidioc_reqbufs(struct file *file, void *priv,
-			   struct v4l2_requestbuffers *p)
-{
-	struct cx25821_channel *chan = video_drvdata(file);
-
-	return videobuf_reqbufs(&chan->vidq, p);
-}
-
-static int cx25821_vidioc_querybuf(struct file *file, void *priv,
-			    struct v4l2_buffer *p)
-{
-	struct cx25821_channel *chan = video_drvdata(file);
-
-	return videobuf_querybuf(&chan->vidq, p);
-}
-
-static int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
-	struct cx25821_channel *chan = video_drvdata(file);
-
-	return videobuf_qbuf(&chan->vidq, p);
-}
-
 static int cx25821_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms)
 {
 	struct cx25821_channel *chan = video_drvdata(file);
@@ -880,7 +582,7 @@
 		return err;
 
 	chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
-	chan->vidq.field = f->fmt.pix.field;
+	chan->field = f->fmt.pix.field;
 	chan->width = f->fmt.pix.width;
 	chan->height = f->fmt.pix.height;
 	if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
@@ -890,52 +592,6 @@
 	return 0;
 }
 
-static ssize_t video_write(struct file *file, const char __user *data, size_t count,
-			 loff_t *ppos)
-{
-	struct cx25821_channel *chan = video_drvdata(file);
-	struct cx25821_dev *dev = chan->dev;
-	struct v4l2_fh *fh = file->private_data;
-	int err = 0;
-
-	if (mutex_lock_interruptible(&dev->lock))
-		return -ERESTARTSYS;
-	if (chan->streaming_fh && chan->streaming_fh != fh) {
-		err = -EBUSY;
-		goto unlock;
-	}
-	if (!chan->streaming_fh) {
-		err = cx25821_vidupstream_init(chan, chan->pixel_formats);
-		if (err)
-			goto unlock;
-		chan->streaming_fh = fh;
-	}
-
-	err = cx25821_write_frame(chan, data, count);
-	count -= err;
-	*ppos += err;
-
-unlock:
-	mutex_unlock(&dev->lock);
-	return err;
-}
-
-static int video_out_release(struct file *file)
-{
-	struct cx25821_channel *chan = video_drvdata(file);
-	struct cx25821_dev *dev = chan->dev;
-	struct v4l2_fh *fh = file->private_data;
-
-	mutex_lock(&dev->lock);
-	if (chan->streaming_fh == fh) {
-		cx25821_stop_upstream_video(chan);
-		chan->streaming_fh = NULL;
-	}
-	mutex_unlock(&dev->lock);
-
-	return v4l2_fh_release(file);
-}
-
 static const struct v4l2_ctrl_ops cx25821_ctrl_ops = {
 	.s_ctrl = cx25821_s_ctrl,
 };
@@ -943,11 +599,11 @@
 static const struct v4l2_file_operations video_fops = {
 	.owner = THIS_MODULE,
 	.open = v4l2_fh_open,
-	.release = video_release,
-	.read = video_read,
-	.poll = video_poll,
-	.mmap = cx25821_video_mmap,
+	.release        = vb2_fop_release,
+	.read           = vb2_fop_read,
+	.poll		= vb2_fop_poll,
 	.unlocked_ioctl = video_ioctl2,
+	.mmap           = vb2_fop_mmap,
 };
 
 static const struct v4l2_ioctl_ops video_ioctl_ops = {
@@ -956,17 +612,19 @@
 	.vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap,
 	.vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap,
 	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
-	.vidioc_reqbufs = cx25821_vidioc_reqbufs,
-	.vidioc_querybuf = cx25821_vidioc_querybuf,
-	.vidioc_qbuf = cx25821_vidioc_qbuf,
-	.vidioc_dqbuf = vidioc_dqbuf,
+	.vidioc_reqbufs       = vb2_ioctl_reqbufs,
+	.vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
+	.vidioc_create_bufs   = vb2_ioctl_create_bufs,
+	.vidioc_querybuf      = vb2_ioctl_querybuf,
+	.vidioc_qbuf          = vb2_ioctl_qbuf,
+	.vidioc_dqbuf         = vb2_ioctl_dqbuf,
+	.vidioc_streamon      = vb2_ioctl_streamon,
+	.vidioc_streamoff     = vb2_ioctl_streamoff,
 	.vidioc_g_std = cx25821_vidioc_g_std,
 	.vidioc_s_std = cx25821_vidioc_s_std,
 	.vidioc_enum_input = cx25821_vidioc_enum_input,
 	.vidioc_g_input = cx25821_vidioc_g_input,
 	.vidioc_s_input = cx25821_vidioc_s_input,
-	.vidioc_streamon = vidioc_streamon,
-	.vidioc_streamoff = vidioc_streamoff,
 	.vidioc_log_status = vidioc_log_status,
 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
@@ -984,9 +642,11 @@
 static const struct v4l2_file_operations video_out_fops = {
 	.owner = THIS_MODULE,
 	.open = v4l2_fh_open,
-	.write = video_write,
-	.release = video_out_release,
+	.release        = vb2_fop_release,
+	.write          = vb2_fop_write,
+	.poll		= vb2_fop_poll,
 	.unlocked_ioctl = video_ioctl2,
+	.mmap           = vb2_fop_mmap,
 };
 
 static const struct v4l2_ioctl_ops video_out_ioctl_ops = {
@@ -1019,9 +679,6 @@
 	if (video_is_registered(&dev->channels[chan_num].vdev)) {
 		video_unregister_device(&dev->channels[chan_num].vdev);
 		v4l2_ctrl_handler_free(&dev->channels[chan_num].hdl);
-
-		btcx_riscmem_free(dev->pci,
-				&dev->channels[chan_num].dma_vidq.stopper);
 	}
 }
 
@@ -1035,10 +692,11 @@
 
 	spin_lock_init(&dev->slock);
 
-	for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) {
+	for (i = 0; i < MAX_VID_CAP_CHANNEL_NUM - 1; ++i) {
 		struct cx25821_channel *chan = &dev->channels[i];
 		struct video_device *vdev = &chan->vdev;
 		struct v4l2_ctrl_handler *hdl = &chan->hdl;
+		struct vb2_queue *q;
 		bool is_output = i > SRAM_CH08;
 
 		if (i == SRAM_CH08) /* audio channel */
@@ -1066,11 +724,9 @@
 			chan->out->chan = chan;
 		}
 
-		cx25821_risc_stopper(dev->pci, &chan->dma_vidq.stopper,
-			chan->sram_channels->dma_ctl, 0x11, 0);
-
 		chan->sram_channels = &cx25821_sram_channels[i];
 		chan->width = 720;
+		chan->field = V4L2_FIELD_INTERLACED;
 		if (dev->tvnorm & V4L2_STD_625_50)
 			chan->height = 576;
 		else
@@ -1084,19 +740,27 @@
 		cx_write(chan->sram_channels->int_stat, 0xffffffff);
 
 		INIT_LIST_HEAD(&chan->dma_vidq.active);
-		INIT_LIST_HEAD(&chan->dma_vidq.queued);
 
-		chan->timeout_data.dev = dev;
-		chan->timeout_data.channel = &cx25821_sram_channels[i];
-		chan->dma_vidq.timeout.function = cx25821_vid_timeout;
-		chan->dma_vidq.timeout.data = (unsigned long)&chan->timeout_data;
-		init_timer(&chan->dma_vidq.timeout);
+		q = &chan->vidq;
 
-		if (!is_output)
-			videobuf_queue_sg_init(&chan->vidq, &cx25821_video_qops, &dev->pci->dev,
-				&dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
-				V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer),
-				chan, &dev->lock);
+		q->type = is_output ? V4L2_BUF_TYPE_VIDEO_OUTPUT :
+				      V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+		q->io_modes |= is_output ? VB2_WRITE : VB2_READ;
+		q->gfp_flags = GFP_DMA32;
+		q->min_buffers_needed = 2;
+		q->drv_priv = chan;
+		q->buf_struct_size = sizeof(struct cx25821_buffer);
+		q->ops = &cx25821_video_qops;
+		q->mem_ops = &vb2_dma_sg_memops;
+		q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+		q->lock = &dev->lock;
+
+		if (!is_output) {
+			err = vb2_queue_init(q);
+			if (err < 0)
+				goto fail_unreg;
+		}
 
 		/* register v4l devices */
 		*vdev = is_output ? cx25821_video_out_device : cx25821_video_device;
@@ -1106,6 +770,7 @@
 		else
 			vdev->vfl_dir = VFL_DIR_TX;
 		vdev->lock = &dev->lock;
+		vdev->queue = q;
 		snprintf(vdev->name, sizeof(vdev->name), "%s #%d", dev->name, i);
 		video_set_drvdata(vdev, chan);
 
diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h
index 90bdc19..d81a08a 100644
--- a/drivers/media/pci/cx25821/cx25821.h
+++ b/drivers/media/pci/cx25821/cx25821.h
@@ -34,9 +34,8 @@
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ctrls.h>
-#include <media/videobuf-dma-sg.h>
+#include <media/videobuf2-dma-sg.h>
 
-#include "btcx-risc.h"
 #include "cx25821-reg.h"
 #include "cx25821-medusa-reg.h"
 #include "cx25821-sram.h"
@@ -89,6 +88,13 @@
 
 #define CX25821_BOARD_CONEXANT_ATHENA10 1
 #define MAX_VID_CHANNEL_NUM     12
+
+/*
+ * Maximum capture-only channels. This can go away once video/audio output
+ * is fully supported in this driver.
+ */
+#define MAX_VID_CAP_CHANNEL_NUM     10
+
 #define VID_CHANNEL_NUM 8
 
 struct cx25821_fmt {
@@ -111,16 +117,23 @@
 	CX25821_SRC_SEL_PARALLEL_MPEG_VIDEO
 };
 
+struct cx25821_riscmem {
+	unsigned int   size;
+	__le32         *cpu;
+	__le32         *jmp;
+	dma_addr_t     dma;
+};
+
 /* buffer for one video frame */
 struct cx25821_buffer {
 	/* common v4l buffer stuff -- must be first */
-	struct videobuf_buffer vb;
+	struct vb2_buffer vb;
+	struct list_head queue;
 
 	/* cx25821 specific */
 	unsigned int bpl;
-	struct btcx_riscmem risc;
+	struct cx25821_riscmem risc;
 	const struct cx25821_fmt *fmt;
-	u32 count;
 };
 
 enum port {
@@ -159,17 +172,9 @@
 
 struct cx25821_dmaqueue {
 	struct list_head active;
-	struct list_head queued;
-	struct timer_list timeout;
-	struct btcx_riscmem stopper;
 	u32 count;
 };
 
-struct cx25821_data {
-	struct cx25821_dev *dev;
-	const struct sram_channel *channel;
-};
-
 struct cx25821_dev;
 
 struct cx25821_channel;
@@ -207,18 +212,17 @@
 struct cx25821_channel {
 	unsigned id;
 	struct cx25821_dev *dev;
-	struct v4l2_fh *streaming_fh;
 
 	struct v4l2_ctrl_handler hdl;
-	struct cx25821_data timeout_data;
 
 	struct video_device vdev;
 	struct cx25821_dmaqueue dma_vidq;
-	struct videobuf_queue vidq;
+	struct vb2_queue vidq;
 
 	const struct sram_channel *sram_channels;
 
 	const struct cx25821_fmt *fmt;
+	unsigned field;
 	unsigned int width, height;
 	int pixel_formats;
 	int use_cif_resolution;
@@ -244,6 +248,7 @@
 	int hwrevision;
 	/* used by cx25821-alsa */
 	struct snd_card *card;
+	void *alloc_ctx;
 
 	u32 clk_freq;
 
@@ -405,21 +410,22 @@
 				      const struct sram_channel *ch, unsigned int bpl,
 				      u32 risc);
 
-extern int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+extern int cx25821_riscmem_alloc(struct pci_dev *pci,
+				 struct cx25821_riscmem *risc,
+				 unsigned int size);
+extern int cx25821_risc_buffer(struct pci_dev *pci, struct cx25821_riscmem *risc,
 			       struct scatterlist *sglist,
 			       unsigned int top_offset,
 			       unsigned int bottom_offset,
 			       unsigned int bpl,
 			       unsigned int padding, unsigned int lines);
 extern int cx25821_risc_databuffer_audio(struct pci_dev *pci,
-					 struct btcx_riscmem *risc,
+					 struct cx25821_riscmem *risc,
 					 struct scatterlist *sglist,
 					 unsigned int bpl,
 					 unsigned int lines, unsigned int lpi);
-extern void cx25821_free_buffer(struct videobuf_queue *q,
+extern void cx25821_free_buffer(struct cx25821_dev *dev,
 				struct cx25821_buffer *buf);
-extern int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
-				u32 reg, u32 mask, u32 value);
 extern void cx25821_sram_channel_dump(struct cx25821_dev *dev,
 				      const struct sram_channel *ch);
 extern void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev,
diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c
index d3c79d9..24216ef 100644
--- a/drivers/media/pci/cx88/cx88-blackbird.c
+++ b/drivers/media/pci/cx88/cx88-blackbird.c
@@ -1102,32 +1102,26 @@
 
 static void blackbird_unregister_video(struct cx8802_dev *dev)
 {
-	if (dev->mpeg_dev) {
-		if (video_is_registered(dev->mpeg_dev))
-			video_unregister_device(dev->mpeg_dev);
-		else
-			video_device_release(dev->mpeg_dev);
-		dev->mpeg_dev = NULL;
-	}
+	video_unregister_device(&dev->mpeg_dev);
 }
 
 static int blackbird_register_video(struct cx8802_dev *dev)
 {
 	int err;
 
-	dev->mpeg_dev = cx88_vdev_init(dev->core, dev->pci,
-				       &cx8802_mpeg_template, "mpeg");
-	dev->mpeg_dev->ctrl_handler = &dev->cxhdl.hdl;
-	video_set_drvdata(dev->mpeg_dev, dev);
-	dev->mpeg_dev->queue = &dev->vb2_mpegq;
-	err = video_register_device(dev->mpeg_dev, VFL_TYPE_GRABBER, -1);
+	cx88_vdev_init(dev->core, dev->pci, &dev->mpeg_dev,
+		       &cx8802_mpeg_template, "mpeg");
+	dev->mpeg_dev.ctrl_handler = &dev->cxhdl.hdl;
+	video_set_drvdata(&dev->mpeg_dev, dev);
+	dev->mpeg_dev.queue = &dev->vb2_mpegq;
+	err = video_register_device(&dev->mpeg_dev, VFL_TYPE_GRABBER, -1);
 	if (err < 0) {
 		printk(KERN_INFO "%s/2: can't register mpeg device\n",
 		       dev->core->name);
 		return err;
 	}
 	printk(KERN_INFO "%s/2: registered device %s [mpeg]\n",
-	       dev->core->name, video_device_node_name(dev->mpeg_dev));
+	       dev->core->name, video_device_node_name(&dev->mpeg_dev));
 	return 0;
 }
 
@@ -1234,6 +1228,3 @@
 
 module_init(blackbird_init);
 module_exit(blackbird_fini);
-
-module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644);
-MODULE_PARM_DESC(debug,"enable debug messages [video]");
diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c
index dee177e..3501be9 100644
--- a/drivers/media/pci/cx88/cx88-core.c
+++ b/drivers/media/pci/cx88/cx88-core.c
@@ -985,17 +985,14 @@
 
 /* ------------------------------------------------------------------ */
 
-struct video_device *cx88_vdev_init(struct cx88_core *core,
-				    struct pci_dev *pci,
-				    const struct video_device *template_,
-				    const char *type)
+void cx88_vdev_init(struct cx88_core *core,
+		    struct pci_dev *pci,
+		    struct video_device *vfd,
+		    const struct video_device *template_,
+		    const char *type)
 {
-	struct video_device *vfd;
-
-	vfd = video_device_alloc();
-	if (NULL == vfd)
-		return NULL;
 	*vfd = *template_;
+
 	/*
 	 * The dev pointer of v4l2_device is NULL, instead we set the
 	 * video_device dev_parent pointer to the correct PCI bus device.
@@ -1004,11 +1001,10 @@
 	 */
 	vfd->v4l2_dev = &core->v4l2_dev;
 	vfd->dev_parent = &pci->dev;
-	vfd->release = video_device_release;
+	vfd->release = video_device_release_empty;
 	vfd->lock = &core->lock;
 	snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
 		 core->name, type, core->board.name);
-	return vfd;
 }
 
 struct cx88_core* cx88_core_get(struct pci_dev *pci)
@@ -1091,10 +1087,3 @@
 
 EXPORT_SYMBOL(cx88_ir_start);
 EXPORT_SYMBOL(cx88_ir_stop);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
- */
diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c
index 8b19efa..678ce61 100644
--- a/drivers/media/pci/cx88/cx88-dvb.c
+++ b/drivers/media/pci/cx88/cx88-dvb.c
@@ -1504,8 +1504,8 @@
 			fe0->dvb.frontend = dvb_attach(stv0288_attach,
 							    &tevii_tuner_earda_config,
 							    &core->i2c_adap);
-				if (fe0->dvb.frontend != NULL) {
-					if (!dvb_attach(stb6000_attach, fe0->dvb.frontend, 0x61,
+			if (fe0->dvb.frontend != NULL) {
+				if (!dvb_attach(stb6000_attach, fe0->dvb.frontend, 0x61,
 						&core->i2c_adap))
 					goto frontend_detach;
 				core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c
index 1c1f69e..9834454 100644
--- a/drivers/media/pci/cx88/cx88-mpeg.c
+++ b/drivers/media/pci/cx88/cx88-mpeg.c
@@ -732,7 +732,7 @@
 	dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
 	if (IS_ERR(dev->alloc_ctx)) {
 		err = PTR_ERR(dev->alloc_ctx);
-		goto fail_core;
+		goto fail_dev;
 	}
 	dev->core = core;
 
@@ -754,6 +754,7 @@
 
  fail_free:
 	vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
+ fail_dev:
 	kfree(dev);
  fail_core:
 	core->dvbdev = NULL;
@@ -833,10 +834,3 @@
 EXPORT_SYMBOL(cx8802_register_driver);
 EXPORT_SYMBOL(cx8802_unregister_driver);
 EXPORT_SYMBOL(cx8802_get_driver);
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
- */
diff --git a/drivers/media/pci/cx88/cx88-tvaudio.c b/drivers/media/pci/cx88/cx88-tvaudio.c
index 424fd97..6bbce6a 100644
--- a/drivers/media/pci/cx88/cx88-tvaudio.c
+++ b/drivers/media/pci/cx88/cx88-tvaudio.c
@@ -1050,10 +1050,3 @@
 EXPORT_SYMBOL(cx88_set_stereo);
 EXPORT_SYMBOL(cx88_get_stereo);
 EXPORT_SYMBOL(cx88_audio_thread);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
- */
diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
index 05d5844..47e88b3 100644
--- a/drivers/media/pci/cx88/cx88-video.c
+++ b/drivers/media/pci/cx88/cx88-video.c
@@ -1274,27 +1274,9 @@
 
 static void cx8800_unregister_video(struct cx8800_dev *dev)
 {
-	if (dev->radio_dev) {
-		if (video_is_registered(dev->radio_dev))
-			video_unregister_device(dev->radio_dev);
-		else
-			video_device_release(dev->radio_dev);
-		dev->radio_dev = NULL;
-	}
-	if (dev->vbi_dev) {
-		if (video_is_registered(dev->vbi_dev))
-			video_unregister_device(dev->vbi_dev);
-		else
-			video_device_release(dev->vbi_dev);
-		dev->vbi_dev = NULL;
-	}
-	if (dev->video_dev) {
-		if (video_is_registered(dev->video_dev))
-			video_unregister_device(dev->video_dev);
-		else
-			video_device_release(dev->video_dev);
-		dev->video_dev = NULL;
-	}
+	video_unregister_device(&dev->radio_dev);
+	video_unregister_device(&dev->vbi_dev);
+	video_unregister_device(&dev->video_dev);
 }
 
 static int cx8800_initdev(struct pci_dev *pci_dev,
@@ -1485,12 +1467,12 @@
 		goto fail_unreg;
 
 	/* register v4l devices */
-	dev->video_dev = cx88_vdev_init(core,dev->pci,
-					&cx8800_video_template,"video");
-	video_set_drvdata(dev->video_dev, dev);
-	dev->video_dev->ctrl_handler = &core->video_hdl;
-	dev->video_dev->queue = &dev->vb2_vidq;
-	err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
+	cx88_vdev_init(core, dev->pci, &dev->video_dev,
+		       &cx8800_video_template, "video");
+	video_set_drvdata(&dev->video_dev, dev);
+	dev->video_dev.ctrl_handler = &core->video_hdl;
+	dev->video_dev.queue = &dev->vb2_vidq;
+	err = video_register_device(&dev->video_dev, VFL_TYPE_GRABBER,
 				    video_nr[core->nr]);
 	if (err < 0) {
 		printk(KERN_ERR "%s/0: can't register video device\n",
@@ -1498,12 +1480,13 @@
 		goto fail_unreg;
 	}
 	printk(KERN_INFO "%s/0: registered device %s [v4l2]\n",
-	       core->name, video_device_node_name(dev->video_dev));
+	       core->name, video_device_node_name(&dev->video_dev));
 
-	dev->vbi_dev = cx88_vdev_init(core,dev->pci,&cx8800_vbi_template,"vbi");
-	video_set_drvdata(dev->vbi_dev, dev);
-	dev->vbi_dev->queue = &dev->vb2_vbiq;
-	err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
+	cx88_vdev_init(core, dev->pci, &dev->vbi_dev,
+		       &cx8800_vbi_template, "vbi");
+	video_set_drvdata(&dev->vbi_dev, dev);
+	dev->vbi_dev.queue = &dev->vb2_vbiq;
+	err = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI,
 				    vbi_nr[core->nr]);
 	if (err < 0) {
 		printk(KERN_ERR "%s/0: can't register vbi device\n",
@@ -1511,14 +1494,14 @@
 		goto fail_unreg;
 	}
 	printk(KERN_INFO "%s/0: registered device %s\n",
-	       core->name, video_device_node_name(dev->vbi_dev));
+	       core->name, video_device_node_name(&dev->vbi_dev));
 
 	if (core->board.radio.type == CX88_RADIO) {
-		dev->radio_dev = cx88_vdev_init(core,dev->pci,
-						&cx8800_radio_template,"radio");
-		video_set_drvdata(dev->radio_dev, dev);
-		dev->radio_dev->ctrl_handler = &core->audio_hdl;
-		err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
+		cx88_vdev_init(core, dev->pci, &dev->radio_dev,
+			       &cx8800_radio_template, "radio");
+		video_set_drvdata(&dev->radio_dev, dev);
+		dev->radio_dev.ctrl_handler = &core->audio_hdl;
+		err = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO,
 					    radio_nr[core->nr]);
 		if (err < 0) {
 			printk(KERN_ERR "%s/0: can't register radio device\n",
@@ -1526,7 +1509,7 @@
 			goto fail_unreg;
 		}
 		printk(KERN_INFO "%s/0: registered device %s\n",
-		       core->name, video_device_node_name(dev->radio_dev));
+		       core->name, video_device_node_name(&dev->radio_dev));
 	}
 
 	/* start tvaudio thread */
diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h
index f3eccd7..b93b4b0 100644
--- a/drivers/media/pci/cx88/cx88.h
+++ b/drivers/media/pci/cx88/cx88.h
@@ -478,9 +478,9 @@
 
 	/* various device info */
 	unsigned int               resources;
-	struct video_device        *video_dev;
-	struct video_device        *vbi_dev;
-	struct video_device        *radio_dev;
+	struct video_device        video_dev;
+	struct video_device        vbi_dev;
+	struct video_device        radio_dev;
 
 	/* pci i/o */
 	struct pci_dev             *pci;
@@ -563,7 +563,7 @@
 	/* for blackbird only */
 	struct list_head           devlist;
 #if IS_ENABLED(CPTCFG_VIDEO_CX88_BLACKBIRD)
-	struct video_device        *mpeg_dev;
+	struct video_device        mpeg_dev;
 	u32                        mailbox;
 
 	/* mpeg params */
@@ -647,10 +647,11 @@
 			  unsigned int height, enum v4l2_field field);
 extern int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm);
 
-extern struct video_device *cx88_vdev_init(struct cx88_core *core,
-					   struct pci_dev *pci,
-					   const struct video_device *template_,
-					   const char *type);
+extern void cx88_vdev_init(struct cx88_core *core,
+			   struct pci_dev *pci,
+			   struct video_device *vfd,
+			   const struct video_device *template_,
+			   const char *type);
 extern struct cx88_core *cx88_core_get(struct pci_dev *pci);
 extern void cx88_core_put(struct cx88_core *core,
 			  struct pci_dev *pci);
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-main.c b/drivers/media/pci/ivtv/ivtv-alsa-main.c
index 39b5292..41fa215 100644
--- a/drivers/media/pci/ivtv/ivtv-alsa-main.c
+++ b/drivers/media/pci/ivtv/ivtv-alsa-main.c
@@ -224,7 +224,7 @@
 	}
 
 	s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
-	if (s->vdev == NULL) {
+	if (s->vdev.v4l2_dev == NULL) {
 		IVTV_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - "
 				     "skipping\n", __func__);
 		return 0;
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
index 7bf9cbc..f198b98 100644
--- a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
+++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
@@ -167,7 +167,7 @@
 
 	s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
 
-	v4l2_fh_init(&item.fh, s->vdev);
+	v4l2_fh_init(&item.fh, &s->vdev);
 	item.itv = itv;
 	item.type = s->type;
 
diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c
index 0195c34..1a0e66d 100644
--- a/drivers/media/pci/ivtv/ivtv-driver.c
+++ b/drivers/media/pci/ivtv/ivtv-driver.c
@@ -1284,7 +1284,7 @@
 	return 0;
 
 free_streams:
-	ivtv_streams_cleanup(itv, 1);
+	ivtv_streams_cleanup(itv);
 free_irq:
 	free_irq(itv->pdev->irq, (void *)itv);
 free_i2c:
@@ -1444,7 +1444,7 @@
 	flush_kthread_worker(&itv->irq_worker);
 	kthread_stop(itv->irq_worker_task);
 
-	ivtv_streams_cleanup(itv, 1);
+	ivtv_streams_cleanup(itv);
 	ivtv_udma_free(itv);
 
 	v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
diff --git a/drivers/media/pci/ivtv/ivtv-driver.h b/drivers/media/pci/ivtv/ivtv-driver.h
index f1a7386..531cd4d 100644
--- a/drivers/media/pci/ivtv/ivtv-driver.h
+++ b/drivers/media/pci/ivtv/ivtv-driver.h
@@ -327,7 +327,7 @@
 struct ivtv_stream {
 	/* These first four fields are always set, even if the stream
 	   is not actually created. */
-	struct video_device *vdev;	/* NULL when stream not created */
+	struct video_device vdev;	/* vdev.v4l2_dev is NULL if there is no device */
 	struct ivtv *itv; 		/* for ease of use */
 	const char *name;		/* name of the stream */
 	int type;			/* stream type */
diff --git a/drivers/media/pci/ivtv/ivtv-fileops.c b/drivers/media/pci/ivtv/ivtv-fileops.c
index bcb5c3b..76462ef 100644
--- a/drivers/media/pci/ivtv/ivtv-fileops.c
+++ b/drivers/media/pci/ivtv/ivtv-fileops.c
@@ -995,7 +995,7 @@
 		IVTV_DEBUG_WARN("nomem on v4l2 open\n");
 		return -ENOMEM;
 	}
-	v4l2_fh_init(&item->fh, s->vdev);
+	v4l2_fh_init(&item->fh, &s->vdev);
 	item->itv = itv;
 	item->type = s->type;
 
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c
index 9c5f710..e7f8d0b 100644
--- a/drivers/media/pci/ivtv/ivtv-ioctl.c
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.c
@@ -448,9 +448,12 @@
 static int ivtv_g_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
 {
 	struct ivtv *itv = fh2id(fh)->itv;
+	struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
 	struct v4l2_window *winfmt = &fmt->fmt.win;
 
-	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+	if (!(s->caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+		return -EINVAL;
+	if (!itv->osd_video_pbase)
 		return -EINVAL;
 	winfmt->chromakey = itv->osd_chroma_key;
 	winfmt->global_alpha = itv->osd_global_alpha;
@@ -555,10 +558,13 @@
 static int ivtv_try_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
 {
 	struct ivtv *itv = fh2id(fh)->itv;
+	struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
 	u32 chromakey = fmt->fmt.win.chromakey;
 	u8 global_alpha = fmt->fmt.win.global_alpha;
 
-	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+	if (!(s->caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+		return -EINVAL;
+	if (!itv->osd_video_pbase)
 		return -EINVAL;
 	ivtv_g_fmt_vid_out_overlay(file, fh, fmt);
 	fmt->fmt.win.chromakey = chromakey;
@@ -741,6 +747,11 @@
 	snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev));
 	vcap->capabilities = itv->v4l2_cap | V4L2_CAP_DEVICE_CAPS;
 	vcap->device_caps = s->caps;
+	if ((s->caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY) &&
+	    !itv->osd_video_pbase) {
+		vcap->capabilities &= ~V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
+		vcap->device_caps &= ~V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
+	}
 	return 0;
 }
 
@@ -816,80 +827,103 @@
 {
 	struct ivtv_open_id *id = fh2id(fh);
 	struct ivtv *itv = id->itv;
-	struct yuv_playback_info *yi = &itv->yuv_info;
-	int streamtype;
 
-	streamtype = id->type;
-
-	if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
-		return -EINVAL;
-	cropcap->bounds.top = cropcap->bounds.left = 0;
-	cropcap->bounds.width = 720;
 	if (cropcap->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-		cropcap->bounds.height = itv->is_50hz ? 576 : 480;
 		cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10;
 		cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11;
-	} else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
-		if (yi->track_osd) {
-			cropcap->bounds.width = yi->osd_full_w;
-			cropcap->bounds.height = yi->osd_full_h;
-		} else {
-			cropcap->bounds.width = 720;
-			cropcap->bounds.height =
-					itv->is_out_50hz ? 576 : 480;
-		}
+	} else if (cropcap->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
 		cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
 		cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
 	} else {
-		cropcap->bounds.height = itv->is_out_50hz ? 576 : 480;
-		cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
-		cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
+		return -EINVAL;
 	}
-	cropcap->defrect = cropcap->bounds;
 	return 0;
 }
 
-static int ivtv_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop)
+static int ivtv_s_selection(struct file *file, void *fh,
+			    struct v4l2_selection *sel)
 {
 	struct ivtv_open_id *id = fh2id(fh);
 	struct ivtv *itv = id->itv;
 	struct yuv_playback_info *yi = &itv->yuv_info;
-	int streamtype;
+	struct v4l2_rect r = { 0, 0, 720, 0 };
+	int streamtype = id->type;
 
-	streamtype = id->type;
-
-	if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
-	    (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
-		if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
-			yi->main_rect = crop->c;
-			return 0;
-		} else {
-			if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
-				crop->c.width, crop->c.height, crop->c.left, crop->c.top)) {
-				itv->main_rect = crop->c;
-				return 0;
-			}
-		}
+	if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+	    !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
 		return -EINVAL;
+
+	if (sel->target != V4L2_SEL_TGT_COMPOSE)
+		return -EINVAL;
+
+
+	if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+	    !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return -EINVAL;
+
+	r.height = itv->is_out_50hz ? 576 : 480;
+	if (streamtype == IVTV_DEC_STREAM_TYPE_YUV && yi->track_osd) {
+		r.width = yi->osd_full_w;
+		r.height = yi->osd_full_h;
+	}
+	sel->r.width = clamp(sel->r.width, 16U, r.width);
+	sel->r.height = clamp(sel->r.height, 16U, r.height);
+	sel->r.left = clamp_t(unsigned, sel->r.left, 0, r.width - sel->r.width);
+	sel->r.top = clamp_t(unsigned, sel->r.top, 0, r.height - sel->r.height);
+
+	if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+		yi->main_rect = sel->r;
+		return 0;
+	}
+	if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+			sel->r.width, sel->r.height, sel->r.left, sel->r.top)) {
+		itv->main_rect = sel->r;
+		return 0;
 	}
 	return -EINVAL;
 }
 
-static int ivtv_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+static int ivtv_g_selection(struct file *file, void *fh,
+			    struct v4l2_selection *sel)
 {
 	struct ivtv_open_id *id = fh2id(fh);
 	struct ivtv *itv = id->itv;
 	struct yuv_playback_info *yi = &itv->yuv_info;
-	int streamtype;
+	struct v4l2_rect r = { 0, 0, 720, 0 };
+	int streamtype = id->type;
 
-	streamtype = id->type;
+	if (sel->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		switch (sel->target) {
+		case V4L2_SEL_TGT_CROP_DEFAULT:
+		case V4L2_SEL_TGT_CROP_BOUNDS:
+			sel->r.top = sel->r.left = 0;
+			sel->r.width = 720;
+			sel->r.height = itv->is_50hz ? 576 : 480;
+			return 0;
+		default:
+			return -EINVAL;
+		}
+	}
 
-	if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
-	    (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+	if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+	    !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_COMPOSE:
 		if (streamtype == IVTV_DEC_STREAM_TYPE_YUV)
-			crop->c = yi->main_rect;
+			sel->r = yi->main_rect;
 		else
-			crop->c = itv->main_rect;
+			sel->r = itv->main_rect;
+		return 0;
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+		r.height = itv->is_out_50hz ? 576 : 480;
+		if (streamtype == IVTV_DEC_STREAM_TYPE_YUV && yi->track_osd) {
+			r.width = yi->osd_full_w;
+			r.height = yi->osd_full_h;
+		}
+		sel->r = r;
 		return 0;
 	}
 	return -EINVAL;
@@ -987,7 +1021,7 @@
 	else
 		std = V4L2_STD_ALL;
 	for (i = 0; i <= IVTV_ENC_STREAM_TYPE_VBI; i++)
-		itv->streams[i].vdev->tvnorms = std;
+		itv->streams[i].vdev.tvnorms = std;
 
 	/* prevent others from messing with the streams until
 	   we're finished changing inputs. */
@@ -1038,7 +1072,7 @@
 	struct ivtv *itv = fh2id(fh)->itv;
 	struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
 
-	if (s->vdev->vfl_dir)
+	if (s->vdev.vfl_dir)
 		return -ENOTTY;
 	if (vf->tuner != 0)
 		return -EINVAL;
@@ -1052,7 +1086,7 @@
 	struct ivtv *itv = fh2id(fh)->itv;
 	struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
 
-	if (s->vdev->vfl_dir)
+	if (s->vdev.vfl_dir)
 		return -ENOTTY;
 	if (vf->tuner != 0)
 		return -EINVAL;
@@ -1340,6 +1374,7 @@
 static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
 {
 	struct ivtv *itv = fh2id(fh)->itv;
+	struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
 	u32 data[CX2341X_MBOX_MAX_DATA];
 	struct yuv_playback_info *yi = &itv->yuv_info;
 
@@ -1363,10 +1398,10 @@
 		0,
 	};
 
-	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
-		return -EINVAL;
+	if (!(s->caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+		return -ENOTTY;
 	if (!itv->osd_video_pbase)
-		return -EINVAL;
+		return -ENOTTY;
 
 	fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY |
 		V4L2_FBUF_CAP_GLOBAL_ALPHA;
@@ -1427,12 +1462,13 @@
 {
 	struct ivtv_open_id *id = fh2id(fh);
 	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
 	struct yuv_playback_info *yi = &itv->yuv_info;
 
-	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
-		return -EINVAL;
+	if (!(s->caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+		return -ENOTTY;
 	if (!itv->osd_video_pbase)
-		return -EINVAL;
+		return -ENOTTY;
 
 	itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
 	itv->osd_local_alpha_state =
@@ -1447,9 +1483,12 @@
 {
 	struct ivtv_open_id *id = fh2id(fh);
 	struct ivtv *itv = id->itv;
+	struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
 
-	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
-		return -EINVAL;
+	if (!(s->caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+		return -ENOTTY;
+	if (!itv->osd_video_pbase)
+		return -ENOTTY;
 
 	ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, on != 0);
 
@@ -1547,7 +1586,7 @@
 	for (i = 0; i < IVTV_MAX_STREAMS; i++) {
 		struct ivtv_stream *s = &itv->streams[i];
 
-		if (s->vdev == NULL || s->buffers == 0)
+		if (s->vdev.v4l2_dev == NULL || s->buffers == 0)
 			continue;
 		IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags,
 				(s->buffers - s->q_free.buffers) * 100 / s->buffers,
@@ -1837,8 +1876,8 @@
 	.vidioc_enum_output   		    = ivtv_enum_output,
 	.vidioc_enumaudout   		    = ivtv_enumaudout,
 	.vidioc_cropcap       		    = ivtv_cropcap,
-	.vidioc_s_crop       		    = ivtv_s_crop,
-	.vidioc_g_crop       		    = ivtv_g_crop,
+	.vidioc_s_selection		    = ivtv_s_selection,
+	.vidioc_g_selection		    = ivtv_g_selection,
 	.vidioc_g_input      		    = ivtv_g_input,
 	.vidioc_s_input      		    = ivtv_s_input,
 	.vidioc_g_output     		    = ivtv_g_output,
diff --git a/drivers/media/pci/ivtv/ivtv-irq.c b/drivers/media/pci/ivtv/ivtv-irq.c
index ab6d5d2..36ca2d6 100644
--- a/drivers/media/pci/ivtv/ivtv-irq.c
+++ b/drivers/media/pci/ivtv/ivtv-irq.c
@@ -75,7 +75,7 @@
 
 	IVTV_DEBUG_HI_DMA("ivtv_pio_work_handler\n");
 	if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS ||
-			s->vdev == NULL || !ivtv_use_pio(s)) {
+			s->vdev.v4l2_dev == NULL || !ivtv_use_pio(s)) {
 		itv->cur_pio_stream = -1;
 		/* trigger PIO complete user interrupt */
 		write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
@@ -132,7 +132,7 @@
 	int rc;
 
 	/* sanity checks */
-	if (s->vdev == NULL) {
+	if (s->vdev.v4l2_dev == NULL) {
 		IVTV_DEBUG_WARN("Stream %s not started\n", s->name);
 		return -1;
 	}
@@ -357,7 +357,6 @@
 	u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
 	int y_done = 0;
 	int bytes_written = 0;
-	unsigned long flags = 0;
 	int idx = 0;
 
 	IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
@@ -407,16 +406,21 @@
 
 	/* Sync Hardware SG List of buffers */
 	ivtv_stream_sync_for_device(s);
-	if (lock)
+	if (lock) {
+		unsigned long flags = 0;
+
 		spin_lock_irqsave(&itv->dma_reg_lock, flags);
-	if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
-		ivtv_dma_dec_start(s);
-	}
-	else {
-		set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
-	}
-	if (lock)
+		if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+			ivtv_dma_dec_start(s);
+		else
+			set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
 		spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+	} else {
+		if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+			ivtv_dma_dec_start(s);
+		else
+			set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+	}
 }
 
 static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s)
@@ -886,8 +890,8 @@
 			if (s)
 				wake_up(&s->waitq);
 		}
-		if (s && s->vdev)
-			v4l2_event_queue(s->vdev, frame ? &evtop : &evbottom);
+		if (s && s->vdev.v4l2_dev)
+			v4l2_event_queue(&s->vdev, frame ? &evtop : &evbottom);
 		wake_up(&itv->vsync_waitq);
 
 		/* Send VBI to saa7127 */
diff --git a/drivers/media/pci/ivtv/ivtv-streams.c b/drivers/media/pci/ivtv/ivtv-streams.c
index f0a1cc4..d27c6df 100644
--- a/drivers/media/pci/ivtv/ivtv-streams.c
+++ b/drivers/media/pci/ivtv/ivtv-streams.c
@@ -130,7 +130,8 @@
 		"decoder MPG",
 		VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET,
 		PCI_DMA_TODEVICE, 0,
-		V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+		V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE |
+		V4L2_CAP_VIDEO_OUTPUT_OVERLAY,
 		&ivtv_v4l2_dec_fops
 	},
 	{	/* IVTV_DEC_STREAM_TYPE_VBI */
@@ -151,7 +152,8 @@
 		"decoder YUV",
 		VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET,
 		PCI_DMA_TODEVICE, 0,
-		V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+		V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE |
+		V4L2_CAP_VIDEO_OUTPUT_OVERLAY,
 		&ivtv_v4l2_dec_fops
 	}
 };
@@ -159,11 +161,9 @@
 static void ivtv_stream_init(struct ivtv *itv, int type)
 {
 	struct ivtv_stream *s = &itv->streams[type];
-	struct video_device *vdev = s->vdev;
 
 	/* we need to keep vdev, so restore it afterwards */
 	memset(s, 0, sizeof(*s));
-	s->vdev = vdev;
 
 	/* initialize ivtv_stream fields */
 	s->itv = itv;
@@ -194,10 +194,10 @@
 	int num_offset = ivtv_stream_info[type].num_offset;
 	int num = itv->instance + ivtv_first_minor + num_offset;
 
-	/* These four fields are always initialized. If vdev == NULL, then
+	/* These four fields are always initialized. If vdev.v4l2_dev == NULL, then
 	   this stream is not in use. In that case no other fields but these
 	   four can be used. */
-	s->vdev = NULL;
+	s->vdev.v4l2_dev = NULL;
 	s->itv = itv;
 	s->type = type;
 	s->name = ivtv_stream_info[type].name;
@@ -218,40 +218,33 @@
 
 	ivtv_stream_init(itv, type);
 
-	/* allocate and initialize the v4l2 video device structure */
-	s->vdev = video_device_alloc();
-	if (s->vdev == NULL) {
-		IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name);
-		return -ENOMEM;
-	}
-
-	snprintf(s->vdev->name, sizeof(s->vdev->name), "%s %s",
+	snprintf(s->vdev.name, sizeof(s->vdev.name), "%s %s",
 			itv->v4l2_dev.name, s->name);
 
-	s->vdev->num = num;
-	s->vdev->v4l2_dev = &itv->v4l2_dev;
+	s->vdev.num = num;
+	s->vdev.v4l2_dev = &itv->v4l2_dev;
 	if (ivtv_stream_info[type].v4l2_caps &
 			(V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT))
-		s->vdev->vfl_dir = VFL_DIR_TX;
-	s->vdev->fops = ivtv_stream_info[type].fops;
-	s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
-	s->vdev->release = video_device_release;
-	s->vdev->tvnorms = V4L2_STD_ALL;
-	s->vdev->lock = &itv->serialize_lock;
+		s->vdev.vfl_dir = VFL_DIR_TX;
+	s->vdev.fops = ivtv_stream_info[type].fops;
+	s->vdev.ctrl_handler = itv->v4l2_dev.ctrl_handler;
+	s->vdev.release = video_device_release_empty;
+	s->vdev.tvnorms = V4L2_STD_ALL;
+	s->vdev.lock = &itv->serialize_lock;
 	if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
-		v4l2_disable_ioctl(s->vdev, VIDIOC_S_AUDIO);
-		v4l2_disable_ioctl(s->vdev, VIDIOC_G_AUDIO);
-		v4l2_disable_ioctl(s->vdev, VIDIOC_ENUMAUDIO);
-		v4l2_disable_ioctl(s->vdev, VIDIOC_ENUMINPUT);
-		v4l2_disable_ioctl(s->vdev, VIDIOC_S_INPUT);
-		v4l2_disable_ioctl(s->vdev, VIDIOC_G_INPUT);
-		v4l2_disable_ioctl(s->vdev, VIDIOC_S_FREQUENCY);
-		v4l2_disable_ioctl(s->vdev, VIDIOC_G_FREQUENCY);
-		v4l2_disable_ioctl(s->vdev, VIDIOC_S_TUNER);
-		v4l2_disable_ioctl(s->vdev, VIDIOC_G_TUNER);
-		v4l2_disable_ioctl(s->vdev, VIDIOC_S_STD);
+		v4l2_disable_ioctl(&s->vdev, VIDIOC_S_AUDIO);
+		v4l2_disable_ioctl(&s->vdev, VIDIOC_G_AUDIO);
+		v4l2_disable_ioctl(&s->vdev, VIDIOC_ENUMAUDIO);
+		v4l2_disable_ioctl(&s->vdev, VIDIOC_ENUMINPUT);
+		v4l2_disable_ioctl(&s->vdev, VIDIOC_S_INPUT);
+		v4l2_disable_ioctl(&s->vdev, VIDIOC_G_INPUT);
+		v4l2_disable_ioctl(&s->vdev, VIDIOC_S_FREQUENCY);
+		v4l2_disable_ioctl(&s->vdev, VIDIOC_G_FREQUENCY);
+		v4l2_disable_ioctl(&s->vdev, VIDIOC_S_TUNER);
+		v4l2_disable_ioctl(&s->vdev, VIDIOC_G_TUNER);
+		v4l2_disable_ioctl(&s->vdev, VIDIOC_S_STD);
 	}
-	ivtv_set_funcs(s->vdev);
+	ivtv_set_funcs(&s->vdev);
 	return 0;
 }
 
@@ -266,7 +259,7 @@
 		if (ivtv_prep_dev(itv, type))
 			break;
 
-		if (itv->streams[type].vdev == NULL)
+		if (itv->streams[type].vdev.v4l2_dev == NULL)
 			continue;
 
 		/* Allocate Stream */
@@ -277,7 +270,7 @@
 		return 0;
 
 	/* One or more streams could not be initialized. Clean 'em all up. */
-	ivtv_streams_cleanup(itv, 0);
+	ivtv_streams_cleanup(itv);
 	return -ENOMEM;
 }
 
@@ -288,28 +281,26 @@
 	const char *name;
 	int num;
 
-	if (s->vdev == NULL)
+	if (s->vdev.v4l2_dev == NULL)
 		return 0;
 
-	num = s->vdev->num;
+	num = s->vdev.num;
 	/* card number + user defined offset + device offset */
 	if (type != IVTV_ENC_STREAM_TYPE_MPG) {
 		struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
 
-		if (s_mpg->vdev)
-			num = s_mpg->vdev->num + ivtv_stream_info[type].num_offset;
+		if (s_mpg->vdev.v4l2_dev)
+			num = s_mpg->vdev.num + ivtv_stream_info[type].num_offset;
 	}
-	video_set_drvdata(s->vdev, s);
+	video_set_drvdata(&s->vdev, s);
 
 	/* Register device. First try the desired minor, then any free one. */
-	if (video_register_device_no_warn(s->vdev, vfl_type, num)) {
+	if (video_register_device_no_warn(&s->vdev, vfl_type, num)) {
 		IVTV_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
 				s->name, num);
-		video_device_release(s->vdev);
-		s->vdev = NULL;
 		return -ENOMEM;
 	}
-	name = video_device_node_name(s->vdev);
+	name = video_device_node_name(&s->vdev);
 
 	switch (vfl_type) {
 	case VFL_TYPE_GRABBER:
@@ -346,29 +337,25 @@
 		return 0;
 
 	/* One or more streams could not be initialized. Clean 'em all up. */
-	ivtv_streams_cleanup(itv, 1);
+	ivtv_streams_cleanup(itv);
 	return -ENOMEM;
 }
 
 /* Unregister v4l2 devices */
-void ivtv_streams_cleanup(struct ivtv *itv, int unregister)
+void ivtv_streams_cleanup(struct ivtv *itv)
 {
 	int type;
 
 	/* Teardown all streams */
 	for (type = 0; type < IVTV_MAX_STREAMS; type++) {
-		struct video_device *vdev = itv->streams[type].vdev;
+		struct video_device *vdev = &itv->streams[type].vdev;
 
-		itv->streams[type].vdev = NULL;
-		if (vdev == NULL)
+		if (vdev->v4l2_dev == NULL)
 			continue;
 
+		video_unregister_device(vdev);
 		ivtv_stream_free(&itv->streams[type]);
-		/* Unregister or release device */
-		if (unregister)
-			video_unregister_device(vdev);
-		else
-			video_device_release(vdev);
+		itv->streams[type].vdev.v4l2_dev = NULL;
 	}
 }
 
@@ -492,7 +479,7 @@
 	int captype = 0, subtype = 0;
 	int enable_passthrough = 0;
 
-	if (s->vdev == NULL)
+	if (s->vdev.v4l2_dev == NULL)
 		return -EINVAL;
 
 	IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name);
@@ -661,7 +648,7 @@
 	u16 width;
 	u16 height;
 
-	if (s->vdev == NULL)
+	if (s->vdev.v4l2_dev == NULL)
 		return -EINVAL;
 
 	IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
@@ -723,7 +710,7 @@
 	struct ivtv *itv = s->itv;
 	int rc;
 
-	if (s->vdev == NULL)
+	if (s->vdev.v4l2_dev == NULL)
 		return -EINVAL;
 
 	if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags))
@@ -778,7 +765,7 @@
 	for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) {
 		struct ivtv_stream *s = &itv->streams[i];
 
-		if (s->vdev == NULL)
+		if (s->vdev.v4l2_dev == NULL)
 			continue;
 		if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
 			ivtv_stop_v4l2_encode_stream(s, 0);
@@ -793,7 +780,7 @@
 	int cap_type;
 	int stopmode;
 
-	if (s->vdev == NULL)
+	if (s->vdev.v4l2_dev == NULL)
 		return -EINVAL;
 
 	/* This function assumes that you are allowed to stop the capture
@@ -917,7 +904,7 @@
 	};
 	struct ivtv *itv = s->itv;
 
-	if (s->vdev == NULL)
+	if (s->vdev.v4l2_dev == NULL)
 		return -EINVAL;
 
 	if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG)
@@ -969,7 +956,7 @@
 
 	set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags);
 	wake_up(&itv->event_waitq);
-	v4l2_event_queue(s->vdev, &ev);
+	v4l2_event_queue(&s->vdev, &ev);
 
 	/* wake up wait queues */
 	wake_up(&s->waitq);
@@ -982,7 +969,7 @@
 	struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV];
 	struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
 
-	if (yuv_stream->vdev == NULL || dec_stream->vdev == NULL)
+	if (yuv_stream->vdev.v4l2_dev == NULL || dec_stream->vdev.v4l2_dev == NULL)
 		return -EINVAL;
 
 	IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n");
diff --git a/drivers/media/pci/ivtv/ivtv-streams.h b/drivers/media/pci/ivtv/ivtv-streams.h
index a653a51..3d76a41 100644
--- a/drivers/media/pci/ivtv/ivtv-streams.h
+++ b/drivers/media/pci/ivtv/ivtv-streams.h
@@ -23,7 +23,7 @@
 
 int ivtv_streams_setup(struct ivtv *itv);
 int ivtv_streams_register(struct ivtv *itv);
-void ivtv_streams_cleanup(struct ivtv *itv, int unregister);
+void ivtv_streams_cleanup(struct ivtv *itv);
 
 /* Capture related */
 int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s);
diff --git a/drivers/media/pci/ivtv/ivtv-udma.c b/drivers/media/pci/ivtv/ivtv-udma.c
index bee2329..24152ac 100644
--- a/drivers/media/pci/ivtv/ivtv-udma.c
+++ b/drivers/media/pci/ivtv/ivtv-udma.c
@@ -124,10 +124,8 @@
 	}
 
 	/* Get user pages for DMA Xfer */
-	down_read(&current->mm->mmap_sem);
-	err = get_user_pages(current, current->mm,
-			user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL);
-	up_read(&current->mm->mmap_sem);
+	err = get_user_pages_unlocked(current, current->mm,
+			user_dma.uaddr, user_dma.page_count, 0, 1, dma->map);
 
 	if (user_dma.page_count != err) {
 		IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
diff --git a/drivers/media/pci/mantis/mantis_core.c b/drivers/media/pci/mantis/mantis_core.c
index 684d906..82220ea 100644
--- a/drivers/media/pci/mantis/mantis_core.c
+++ b/drivers/media/pci/mantis/mantis_core.c
@@ -56,29 +56,6 @@
 	return 0;
 }
 
-static int write_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length)
-{
-	int err;
-
-	struct i2c_msg msg = {
-		.addr = 0x50,
-		.flags = 0,
-		.buf = data,
-		.len = length
-	};
-
-	err = i2c_transfer(&mantis->adapter, &msg, 1);
-	if (err < 0) {
-		dprintk(verbose, MANTIS_ERROR, 1,
-			"ERROR: i2c write: < err=%i length=0x%02x d0=0x%02x, d1=0x%02x >",
-			err, length, data[0], data[1]);
-
-		return err;
-	}
-
-	return 0;
-}
-
 static int get_mac_address(struct mantis_pci *mantis)
 {
 	int err;
diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c
index 9d9f90c..ba887e8 100644
--- a/drivers/media/pci/meye/meye.c
+++ b/drivers/media/pci/meye/meye.c
@@ -1546,7 +1546,7 @@
 	.name		= "meye",
 	.fops		= &meye_fops,
 	.ioctl_ops 	= &meye_ioctl_ops,
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
 };
 
 static const struct v4l2_ctrl_ops meye_ctrl_ops = {
@@ -1623,7 +1623,7 @@
 
 	if (meye.mchip_dev != NULL) {
 		printk(KERN_ERR "meye: only one device allowed!\n");
-		goto outnotdev;
+		return ret;
 	}
 
 	ret = v4l2_device_register(&pcidev->dev, v4l2_dev);
@@ -1633,11 +1633,6 @@
 	}
 	ret = -ENOMEM;
 	meye.mchip_dev = pcidev;
-	meye.vdev = video_device_alloc();
-	if (!meye.vdev) {
-		v4l2_err(v4l2_dev, "video_device_alloc() failed!\n");
-		goto outnotdev;
-	}
 
 	meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE);
 	if (!meye.grab_temp) {
@@ -1658,8 +1653,8 @@
 		goto outkfifoalloc2;
 	}
 
-	memcpy(meye.vdev, &meye_template, sizeof(meye_template));
-	meye.vdev->v4l2_dev = &meye.v4l2_dev;
+	meye.vdev = meye_template;
+	meye.vdev.v4l2_dev = &meye.v4l2_dev;
 
 	ret = -EIO;
 	if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) {
@@ -1743,9 +1738,9 @@
 	}
 
 	v4l2_ctrl_handler_setup(&meye.hdl);
-	meye.vdev->ctrl_handler = &meye.hdl;
+	meye.vdev.ctrl_handler = &meye.hdl;
 
-	if (video_register_device(meye.vdev, VFL_TYPE_GRABBER,
+	if (video_register_device(&meye.vdev, VFL_TYPE_GRABBER,
 				  video_nr) < 0) {
 		v4l2_err(v4l2_dev, "video_register_device failed\n");
 		goto outvideoreg;
@@ -1777,14 +1772,12 @@
 outkfifoalloc1:
 	vfree(meye.grab_temp);
 outvmalloc:
-	video_device_release(meye.vdev);
-outnotdev:
 	return ret;
 }
 
 static void meye_remove(struct pci_dev *pcidev)
 {
-	video_unregister_device(meye.vdev);
+	video_unregister_device(&meye.vdev);
 
 	mchip_hic_stop();
 
diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h
index 6fed927..751be5e 100644
--- a/drivers/media/pci/meye/meye.h
+++ b/drivers/media/pci/meye/meye.h
@@ -311,7 +311,7 @@
 	struct kfifo doneq;		/* queue for grabbed buffers */
 	spinlock_t doneq_lock;		/* lock protecting the queue */
 	wait_queue_head_t proc_list;	/* wait queue */
-	struct video_device *vdev;	/* video device parameters */
+	struct video_device vdev;	/* video device parameters */
 	u16 brightness;
 	u16 hue;
 	u16 contrast;
diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c
index ec33541..fe39516 100644
--- a/drivers/media/pci/ngene/ngene-cards.c
+++ b/drivers/media/pci/ngene/ngene-cards.c
@@ -798,11 +798,11 @@
 	printk(KERN_INFO DEVICE_NAME ": resume\n");
 }
 
+static 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
-static const struct pci_error_handlers ngene_errors = {
-#else
-static struct pci_error_handlers ngene_errors = {
+const 
 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) */
+struct pci_error_handlers ngene_errors = {
 	.error_detected = ngene_error_detected,
 	.link_reset = ngene_link_reset,
 	.slot_reset = ngene_slot_reset,
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index 87ca35a..ea896ac 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -1084,11 +1084,6 @@
 
 /* ------------------------------------------------------------------ */
 
-static inline struct vb2_queue *saa7134_queue(struct file *file)
-{
-	return video_devdata(file)->queue;
-}
-
 static int video_open(struct file *file)
 {
 	struct video_device *vdev = video_devdata(file);
diff --git a/drivers/media/pci/saa7146/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c
index 366434f..03cbcd2 100644
--- a/drivers/media/pci/saa7146/hexium_gemini.c
+++ b/drivers/media/pci/saa7146/hexium_gemini.c
@@ -66,7 +66,7 @@
 {
 	int type;
 
-	struct video_device	*video_dev;
+	struct video_device	video_dev;
 	struct i2c_adapter	i2c_adapter;
 
 	int 		cur_input;	/* current input */
diff --git a/drivers/media/pci/saa7146/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c
index a1eb26d..15f0d66 100644
--- a/drivers/media/pci/saa7146/hexium_orion.c
+++ b/drivers/media/pci/saa7146/hexium_orion.c
@@ -63,7 +63,7 @@
 struct hexium
 {
 	int type;
-	struct video_device	*video_dev;
+	struct video_device	video_dev;
 	struct i2c_adapter	i2c_adapter;
 
 	int cur_input;	/* current input */
diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c
index 8c3d98a..1dffa83 100644
--- a/drivers/media/pci/saa7146/mxb.c
+++ b/drivers/media/pci/saa7146/mxb.c
@@ -151,8 +151,8 @@
 
 struct mxb
 {
-	struct video_device	*video_dev;
-	struct video_device	*vbi_dev;
+	struct video_device	video_dev;
+	struct video_device	vbi_dev;
 
 	struct i2c_adapter	i2c_adapter;
 
diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c
index 4b0bec3..9cf3c6c 100644
--- a/drivers/media/pci/saa7164/saa7164-core.c
+++ b/drivers/media/pci/saa7164/saa7164-core.c
@@ -1436,11 +1436,11 @@
 	saa7164_i2c_unregister(&dev->i2c_bus[1]);
 	saa7164_i2c_unregister(&dev->i2c_bus[2]);
 
-	pci_disable_device(pci_dev);
-
 	/* unregister stuff */
 	free_irq(pci_dev->irq, dev);
 
+	pci_disable_device(pci_dev);
+
 	mutex_lock(&devlist);
 	list_del(&dev->devlist);
 	mutex_unlock(&devlist);
diff --git a/drivers/media/pci/smipcie/Kconfig b/drivers/media/pci/smipcie/Kconfig
index 45d6a31..d0070cb 100644
--- a/drivers/media/pci/smipcie/Kconfig
+++ b/drivers/media/pci/smipcie/Kconfig
@@ -6,7 +6,7 @@
 	depends on I2C_ALGOBIT
 	select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
-	select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_M88RS6000T if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
 	help
diff --git a/drivers/media/pci/smipcie/smipcie.c b/drivers/media/pci/smipcie/smipcie.c
index f773350..4115925 100644
--- a/drivers/media/pci/smipcie/smipcie.c
+++ b/drivers/media/pci/smipcie/smipcie.c
@@ -16,7 +16,7 @@
 
 #include "smipcie.h"
 #include "m88ds3103.h"
-#include "m88ts2022.h"
+#include "ts2020.h"
 #include "m88rs6000t.h"
 #include "si2168.h"
 #include "si2157.h"
@@ -448,16 +448,19 @@
 	port->enable = 0;
 }
 
-static void smi_port_irq(struct smi_port *port, u32 int_status)
+static int smi_port_irq(struct smi_port *port, u32 int_status)
 {
 	u32 port_req_irq = port->_dmaInterruptCH0 | port->_dmaInterruptCH1;
+	int handled = 0;
 
 	if (int_status & port_req_irq) {
 		smi_port_disableInterrupt(port);
 		port->_int_status = int_status;
 		smi_port_clearInterrupt(port);
 		tasklet_schedule(&port->tasklet);
+		handled = 1;
 	}
+	return handled;
 }
 
 static irqreturn_t smi_irq_handler(int irq, void *dev_id)
@@ -465,18 +468,19 @@
 	struct smi_dev *dev = dev_id;
 	struct smi_port *port0 = &dev->ts_port[0];
 	struct smi_port *port1 = &dev->ts_port[1];
+	int handled = 0;
 
 	u32 intr_status = smi_read(MSI_INT_STATUS);
 
 	/* ts0 interrupt.*/
 	if (dev->info->ts_0)
-		smi_port_irq(port0, intr_status);
+		handled += smi_port_irq(port0, intr_status);
 
 	/* ts1 interrupt.*/
 	if (dev->info->ts_1)
-		smi_port_irq(port1, intr_status);
+		handled += smi_port_irq(port1, intr_status);
 
-	return IRQ_HANDLED;
+	return IRQ_RETVAL(handled);
 }
 
 static struct i2c_client *smi_add_i2c_client(struct i2c_adapter *adapter,
@@ -528,9 +532,7 @@
 	struct i2c_adapter *tuner_i2c_adapter;
 	struct i2c_client *tuner_client;
 	struct i2c_board_info tuner_info;
-	struct m88ts2022_config m88ts2022_config = {
-		.clock = 27000000,
-	};
+	struct ts2020_config ts2020_config = {};
 	memset(&tuner_info, 0, sizeof(struct i2c_board_info));
 	i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1];
 
@@ -542,10 +544,10 @@
 		return ret;
 	}
 	/* attach tuner */
-	m88ts2022_config.fe = port->fe;
-	strlcpy(tuner_info.type, "m88ts2022", I2C_NAME_SIZE);
+	ts2020_config.fe = port->fe;
+	strlcpy(tuner_info.type, "ts2020", I2C_NAME_SIZE);
 	tuner_info.addr = 0x60;
-	tuner_info.platform_data = &m88ts2022_config;
+	tuner_info.platform_data = &ts2020_config;
 	tuner_client = smi_add_i2c_client(tuner_i2c_adapter, &tuner_info);
 	if (!tuner_client) {
 		ret = -ENODEV;
diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c
index 8cbe6b4..570d119 100644
--- a/drivers/media/pci/solo6x10/solo6x10-core.c
+++ b/drivers/media/pci/solo6x10/solo6x10-core.c
@@ -182,7 +182,7 @@
 {
 	struct solo_dev *solo_dev =
 		container_of(dev, struct solo_dev, dev);
-	unsigned short *p = (unsigned short *)buf;
+	u16 *p = (u16 *)buf;
 	int i;
 
 	if (count & 0x1)
@@ -212,7 +212,7 @@
 {
 	struct solo_dev *solo_dev =
 		container_of(dev, struct solo_dev, dev);
-	unsigned short *p = (unsigned short *)buf;
+	u16 *p = (u16 *)buf;
 	int count = (full_eeprom ? 128 : 64);
 	int i;
 
diff --git a/drivers/media/pci/solo6x10/solo6x10-eeprom.c b/drivers/media/pci/solo6x10/solo6x10-eeprom.c
index da25ce4..8e81186 100644
--- a/drivers/media/pci/solo6x10/solo6x10-eeprom.c
+++ b/drivers/media/pci/solo6x10/solo6x10-eeprom.c
@@ -103,7 +103,7 @@
 __be16 solo_eeprom_read(struct solo_dev *solo_dev, int loc)
 {
 	int read_cmd = loc | (EE_READ_CMD << ADDR_LEN);
-	unsigned short retval = 0;
+	u16 retval = 0;
 	int i;
 
 	solo_eeprom_cmd(solo_dev, read_cmd);
diff --git a/drivers/media/pci/solo6x10/solo6x10-enc.c b/drivers/media/pci/solo6x10/solo6x10-enc.c
index d19c0ae..d28211b 100644
--- a/drivers/media/pci/solo6x10/solo6x10-enc.c
+++ b/drivers/media/pci/solo6x10/solo6x10-enc.c
@@ -136,11 +136,11 @@
 int solo_osd_print(struct solo_enc_dev *solo_enc)
 {
 	struct solo_dev *solo_dev = solo_enc->solo_dev;
-	unsigned char *str = solo_enc->osd_text;
+	u8 *str = solo_enc->osd_text;
 	u8 *buf = solo_enc->osd_buf;
 	u32 reg;
 	const struct font_desc *vga = find_font("VGA8x16");
-	const unsigned char *vga_data;
+	const u8 *vga_data;
 	int i, j;
 
 	if (WARN_ON_ONCE(!vga))
@@ -154,7 +154,7 @@
 	}
 
 	memset(buf, 0, SOLO_OSD_WRITE_SIZE);
-	vga_data = (const unsigned char *)vga->data;
+	vga_data = (const u8 *)vga->data;
 
 	for (i = 0; *str; i++, str++) {
 		for (j = 0; j < 16; j++) {
diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c
index c7141f2..7ddc767 100644
--- a/drivers/media/pci/solo6x10/solo6x10-g723.c
+++ b/drivers/media/pci/solo6x10/solo6x10-g723.c
@@ -56,8 +56,8 @@
 struct solo_snd_pcm {
 	int				on;
 	spinlock_t			lock;
-	struct solo_dev		*solo_dev;
-	unsigned char			*g723_buf;
+	struct solo_dev			*solo_dev;
+	u8				*g723_buf;
 	dma_addr_t			g723_dma;
 };
 
diff --git a/drivers/media/pci/solo6x10/solo6x10-jpeg.h b/drivers/media/pci/solo6x10/solo6x10-jpeg.h
index 1c66a46..3c611bd 100644
--- a/drivers/media/pci/solo6x10/solo6x10-jpeg.h
+++ b/drivers/media/pci/solo6x10/solo6x10-jpeg.h
@@ -21,7 +21,7 @@
 #ifndef __SOLO6X10_JPEG_H
 #define __SOLO6X10_JPEG_H
 
-static const unsigned char jpeg_header[] = {
+static const u8 jpeg_header[] = {
 	0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c,
 	0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79,
 	0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16,
@@ -106,7 +106,7 @@
 /* This is the byte marker for the start of the DQT */
 #define DQT_START	17
 #define DQT_LEN		138
-static const unsigned char jpeg_dqt[4][DQT_LEN] = {
+static const u8 jpeg_dqt[4][DQT_LEN] = {
 	{
 		0xff, 0xdb, 0x00, 0x43, 0x00,
 		0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07,
diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.c b/drivers/media/pci/solo6x10/solo6x10-tw28.c
index edd0781..0632d3f 100644
--- a/drivers/media/pci/solo6x10/solo6x10-tw28.c
+++ b/drivers/media/pci/solo6x10/solo6x10-tw28.c
@@ -510,7 +510,7 @@
 #define FIRST_ACTIVE_LINE	0x0008
 #define LAST_ACTIVE_LINE	0x0102
 
-static void saa712x_write_regs(struct solo_dev *dev, const uint8_t *vals,
+static void saa712x_write_regs(struct solo_dev *dev, const u8 *vals,
 		int start, int n)
 {
 	for (; start < n; start++, vals++) {
@@ -532,7 +532,7 @@
 static void saa712x_setup(struct solo_dev *dev)
 {
 	const int reg_start = 0x26;
-	const uint8_t saa7128_regs_ntsc[] = {
+	const u8 saa7128_regs_ntsc[] = {
 	/* :0x26 */
 		0x0d, 0x00,
 	/* :0x28 */
diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c
index 6e933d3..53fff54 100644
--- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c
+++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c
@@ -38,28 +38,28 @@
 #define DMA_ALIGN		4096
 
 /* 6010 M4V */
-static unsigned char vop_6010_ntsc_d1[] = {
+static u8 vop_6010_ntsc_d1[] = {
 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
 	0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40,
 	0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04,
 	0x1f, 0x4c, 0x58, 0x10, 0xf0, 0x71, 0x18, 0x3f,
 };
 
-static unsigned char vop_6010_ntsc_cif[] = {
+static u8 vop_6010_ntsc_cif[] = {
 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
 	0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40,
 	0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04,
 	0x1f, 0x4c, 0x2c, 0x10, 0x78, 0x51, 0x18, 0x3f,
 };
 
-static unsigned char vop_6010_pal_d1[] = {
+static u8 vop_6010_pal_d1[] = {
 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
 	0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40,
 	0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04,
 	0x1f, 0x4c, 0x58, 0x11, 0x20, 0x71, 0x18, 0x3f,
 };
 
-static unsigned char vop_6010_pal_cif[] = {
+static u8 vop_6010_pal_cif[] = {
 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20,
 	0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40,
 	0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04,
@@ -67,25 +67,25 @@
 };
 
 /* 6110 h.264 */
-static unsigned char vop_6110_ntsc_d1[] = {
+static u8 vop_6110_ntsc_d1[] = {
 	0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e,
 	0x9a, 0x74, 0x05, 0x81, 0xec, 0x80, 0x00, 0x00,
 	0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00,
 };
 
-static unsigned char vop_6110_ntsc_cif[] = {
+static u8 vop_6110_ntsc_cif[] = {
 	0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e,
 	0x9a, 0x74, 0x0b, 0x0f, 0xc8, 0x00, 0x00, 0x00,
 	0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00,
 };
 
-static unsigned char vop_6110_pal_d1[] = {
+static u8 vop_6110_pal_d1[] = {
 	0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e,
 	0x9a, 0x74, 0x05, 0x80, 0x93, 0x20, 0x00, 0x00,
 	0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00,
 };
 
-static unsigned char vop_6110_pal_cif[] = {
+static u8 vop_6110_pal_cif[] = {
 	0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e,
 	0x9a, 0x74, 0x0b, 0x04, 0xb2, 0x00, 0x00, 0x00,
 	0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00,
@@ -149,7 +149,7 @@
 {
 	struct solo_dev *solo_dev = solo_enc->solo_dev;
 	int vop_len;
-	unsigned char *vop;
+	u8 *vop;
 
 	solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0;
 	solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1);
@@ -239,8 +239,6 @@
 	if (solo_enc->bw_weight > solo_dev->enc_bw_remain)
 		return -EBUSY;
 	solo_enc->sequence = 0;
-	solo_enc->motion_last_state = false;
-	solo_enc->frames_since_last_motion = 0;
 	solo_dev->enc_bw_remain -= solo_enc->bw_weight;
 
 	if (solo_enc->type == SOLO_ENC_TYPE_EXT)
@@ -529,36 +527,12 @@
 	}
 
 	if (!ret) {
-		bool send_event = false;
-
 		vb->v4l2_buf.sequence = solo_enc->sequence++;
 		vb->v4l2_buf.timestamp.tv_sec = vop_sec(vh);
 		vb->v4l2_buf.timestamp.tv_usec = vop_usec(vh);
 
 		/* Check for motion flags */
-		if (solo_is_motion_on(solo_enc)) {
-			/* It takes a few frames for the hardware to detect
-			 * motion. Once it does it clears the motion detection
-			 * register and it takes again a few frames before
-			 * motion is seen. This means in practice that when the
-			 * motion field is 1, it will go back to 0 for the next
-			 * frame. This leads to motion detection event being
-			 * sent all the time, which is not what we want.
-			 * Instead wait a few frames before deciding that the
-			 * motion has halted. After some experimentation it
-			 * turns out that waiting for 5 frames works well.
-			 */
-			if (enc_buf->motion == 0 &&
-			    solo_enc->motion_last_state &&
-			    solo_enc->frames_since_last_motion++ > 5)
-				send_event = true;
-			else if (enc_buf->motion) {
-				solo_enc->frames_since_last_motion = 0;
-				send_event = !solo_enc->motion_last_state;
-			}
-		}
-
-		if (send_event) {
+		if (solo_is_motion_on(solo_enc) && enc_buf->motion) {
 			struct v4l2_event ev = {
 				.type = V4L2_EVENT_MOTION_DET,
 				.u.motion_det = {
@@ -568,8 +542,6 @@
 				},
 			};
 
-			solo_enc->motion_last_state = enc_buf->motion;
-			solo_enc->frames_since_last_motion = 0;
 			v4l2_event_queue(solo_enc->vfd, &ev);
 		}
 	}
diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h
index bd8edfa..1ca54b0 100644
--- a/drivers/media/pci/solo6x10/solo6x10.h
+++ b/drivers/media/pci/solo6x10/solo6x10.h
@@ -159,8 +159,6 @@
 	u16			motion_thresh;
 	bool			motion_global;
 	bool			motion_enabled;
-	bool			motion_last_state;
-	u8			frames_since_last_motion;
 	u16			width;
 	u16			height;
 
@@ -170,9 +168,9 @@
 					__aligned(4);
 
 	/* VOP stuff */
-	unsigned char		vop[64];
+	u8			vop[64];
 	int			vop_len;
-	unsigned char		jpeg_header[1024];
+	u8			jpeg_header[1024];
 	int			jpeg_len;
 
 	u32			fmt;
diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig
index db259af..a253bc7 100644
--- a/drivers/media/pci/sta2x11/Kconfig
+++ b/drivers/media/pci/sta2x11/Kconfig
@@ -6,6 +6,7 @@
 	select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT
 	select VIDEOBUF2_DMA_CONTIG
 	depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS
+	depends on VIDEO_V4L2_SUBDEV_API
 	depends on I2C
 	help
 	  Say Y for support for STA2X11 VIP (Video Input Port) capture
diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
index 22450f5..d384a6b 100644
--- a/drivers/media/pci/sta2x11/sta2x11_vip.c
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
@@ -127,7 +127,7 @@
  */
 struct sta2x11_vip {
 	struct v4l2_device v4l2_dev;
-	struct video_device *video_dev;
+	struct video_device video_dev;
 	struct pci_dev *pdev;
 	struct i2c_adapter *adapter;
 	unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT];
@@ -763,7 +763,7 @@
 
 static struct video_device video_dev_template = {
 	.name = KBUILD_MODNAME,
-	.release = video_device_release,
+	.release = video_device_release_empty,
 	.fops = &vip_fops,
 	.ioctl_ops = &vip_ioctl_ops,
 	.tvnorms = V4L2_STD_ALL,
@@ -1082,19 +1082,13 @@
 		goto release_buf;
 	}
 
-	/* Alloc, initialize and register video device */
-	vip->video_dev = video_device_alloc();
-	if (!vip->video_dev) {
-		ret = -ENOMEM;
-		goto release_irq;
-	}
+	/* Initialize and register video device */
+	vip->video_dev = video_dev_template;
+	vip->video_dev.v4l2_dev = &vip->v4l2_dev;
+	vip->video_dev.queue = &vip->vb_vidq;
+	video_set_drvdata(&vip->video_dev, vip);
 
-	vip->video_dev = &video_dev_template;
-	vip->video_dev->v4l2_dev = &vip->v4l2_dev;
-	vip->video_dev->queue = &vip->vb_vidq;
-	video_set_drvdata(vip->video_dev, vip);
-
-	ret = video_register_device(vip->video_dev, VFL_TYPE_GRABBER, -1);
+	ret = video_register_device(&vip->video_dev, VFL_TYPE_GRABBER, -1);
 	if (ret)
 		goto vrelease;
 
@@ -1124,13 +1118,9 @@
 	return 0;
 
 vunreg:
-	video_set_drvdata(vip->video_dev, NULL);
+	video_set_drvdata(&vip->video_dev, NULL);
 vrelease:
-	if (video_is_registered(vip->video_dev))
-		video_unregister_device(vip->video_dev);
-	else
-		video_device_release(vip->video_dev);
-release_irq:
+	video_unregister_device(&vip->video_dev);
 	free_irq(pdev->irq, vip);
 release_buf:
 	sta2x11_vip_release_buffer(vip);
@@ -1175,9 +1165,8 @@
 
 	sta2x11_vip_clear_register(vip);
 
-	video_set_drvdata(vip->video_dev, NULL);
-	video_unregister_device(vip->video_dev);
-	/*do not call video_device_release() here, is already done */
+	video_set_drvdata(&vip->video_dev, NULL);
+	video_unregister_device(&vip->video_dev);
 	free_irq(pdev->irq, vip);
 	pci_disable_msi(pdev);
 	vb2_queue_release(&vip->vb_vidq);
diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c
index 45a2852..7bddc4b 100644
--- a/drivers/media/pci/ttpci/av7110.c
+++ b/drivers/media/pci/ttpci/av7110.c
@@ -1219,11 +1219,14 @@
 
 static int start_ts_capture(struct av7110 *budget)
 {
+	unsigned y;
+
 	dprintk(2, "budget: %p\n", budget);
 
 	if (budget->feeding1)
 		return ++budget->feeding1;
-	memset(budget->grabbing, 0x00, TS_BUFLEN);
+	for (y = 0; y < TS_HEIGHT; y++)
+		memset(budget->grabbing + y * TS_WIDTH, 0x00, TS_WIDTH);
 	budget->ttbp = 0;
 	SAA7146_ISR_CLEAR(budget->dev, MASK_10);  /* VPE */
 	SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */
diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h
index ef3d960..835635b 100644
--- a/drivers/media/pci/ttpci/av7110.h
+++ b/drivers/media/pci/ttpci/av7110.h
@@ -102,8 +102,8 @@
 	struct dvb_device	dvb_dev;
 	struct dvb_net		dvb_net;
 
-	struct video_device	*v4l_dev;
-	struct video_device	*vbi_dev;
+	struct video_device	v4l_dev;
+	struct video_device	vbi_dev;
 
 	struct saa7146_dev	*dev;
 
diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c
index 0ba3875..54c9910 100644
--- a/drivers/media/pci/ttpci/budget-av.c
+++ b/drivers/media/pci/ttpci/budget-av.c
@@ -68,7 +68,7 @@
 
 struct budget_av {
 	struct budget budget;
-	struct video_device *vd;
+	struct video_device vd;
 	int cur_input;
 	int has_saa7113;
 	struct tasklet_struct ciintf_irq_tasklet;
diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c
index 37d02fe..23e0549 100644
--- a/drivers/media/pci/ttpci/budget-core.c
+++ b/drivers/media/pci/ttpci/budget-core.c
@@ -231,63 +231,59 @@
 }
 
 
-int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count,
-			  int uselocks, int nobusyloop)
+static int ttpci_budget_debiread_nolock(struct budget *budget, u32 config,
+		int addr, int count, int nobusyloop)
 {
 	struct saa7146_dev *saa = budget->dev;
-	int result = 0;
-	unsigned long flags = 0;
+	int result;
 
-	if (count > 4 || count <= 0)
-		return 0;
-
-	if (uselocks)
-		spin_lock_irqsave(&budget->debilock, flags);
-
-	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
-		if (uselocks)
-			spin_unlock_irqrestore(&budget->debilock, flags);
+	result = saa7146_wait_for_debi_done(saa, nobusyloop);
+	if (result < 0)
 		return result;
-	}
 
 	saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
 	saa7146_write(saa, DEBI_CONFIG, config);
 	saa7146_write(saa, DEBI_PAGE, 0);
 	saa7146_write(saa, MC2, (2 << 16) | 2);
 
-	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
-		if (uselocks)
-			spin_unlock_irqrestore(&budget->debilock, flags);
+	result = saa7146_wait_for_debi_done(saa, nobusyloop);
+	if (result < 0)
 		return result;
-	}
 
 	result = saa7146_read(saa, DEBI_AD);
 	result &= (0xffffffffUL >> ((4 - count) * 8));
-
-	if (uselocks)
-		spin_unlock_irqrestore(&budget->debilock, flags);
-
 	return result;
 }
 
-int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr,
-			   int count, u32 value, int uselocks, int nobusyloop)
+int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count,
+			  int uselocks, int nobusyloop)
 {
-	struct saa7146_dev *saa = budget->dev;
-	unsigned long flags = 0;
-	int result;
-
 	if (count > 4 || count <= 0)
 		return 0;
 
-	if (uselocks)
-		spin_lock_irqsave(&budget->debilock, flags);
+	if (uselocks) {
+		unsigned long flags;
+		int result;
 
-	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
-		if (uselocks)
-			spin_unlock_irqrestore(&budget->debilock, flags);
+		spin_lock_irqsave(&budget->debilock, flags);
+		result = ttpci_budget_debiread_nolock(budget, config, addr,
+						      count, nobusyloop);
+		spin_unlock_irqrestore(&budget->debilock, flags);
 		return result;
 	}
+	return ttpci_budget_debiread_nolock(budget, config, addr,
+					    count, nobusyloop);
+}
+
+static int ttpci_budget_debiwrite_nolock(struct budget *budget, u32 config,
+		int addr, int count, u32 value, int nobusyloop)
+{
+	struct saa7146_dev *saa = budget->dev;
+	int result;
+
+	result = saa7146_wait_for_debi_done(saa, nobusyloop);
+	if (result < 0)
+		return result;
 
 	saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff));
 	saa7146_write(saa, DEBI_CONFIG, config);
@@ -295,15 +291,28 @@
 	saa7146_write(saa, DEBI_AD, value);
 	saa7146_write(saa, MC2, (2 << 16) | 2);
 
-	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
-		if (uselocks)
-			spin_unlock_irqrestore(&budget->debilock, flags);
+	result = saa7146_wait_for_debi_done(saa, nobusyloop);
+	return result < 0 ? result : 0;
+}
+
+int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr,
+			   int count, u32 value, int uselocks, int nobusyloop)
+{
+	if (count > 4 || count <= 0)
+		return 0;
+
+	if (uselocks) {
+		unsigned long flags;
+		int result;
+
+		spin_lock_irqsave(&budget->debilock, flags);
+		result = ttpci_budget_debiwrite_nolock(budget, config, addr,
+						count, value, nobusyloop);
+		spin_unlock_irqrestore(&budget->debilock, flags);
 		return result;
 	}
-
-	if (uselocks)
-		spin_unlock_irqrestore(&budget->debilock, flags);
-	return 0;
+	return ttpci_budget_debiwrite_nolock(budget, config, addr,
+					     count, value, nobusyloop);
 }
 
 
diff --git a/drivers/media/pci/tw68/tw68.h b/drivers/media/pci/tw68/tw68.h
index 7a7501b..93f2335 100644
--- a/drivers/media/pci/tw68/tw68.h
+++ b/drivers/media/pci/tw68/tw68.h
@@ -25,7 +25,6 @@
  *  GNU General Public License for more details.
  */
 
-#include <linux/version.h>
 #include <linux/pci.h>
 #include <linux/videodev2.h>
 #include <linux/notifier.h>
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 4a5f12a..fadca2f 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -61,10 +61,8 @@
 	depends on !KERNEL_3_4
 	tristate "Support for timberdale Video In/LogiWIN"
 	depends on m
-	depends on VIDEO_V4L2 && I2C && DMADEVICES
-	depends on MFD_TIMBERDALE || COMPILE_TEST
-	depends on DMA_ENGINE
-	depends on TIMB_DMA
+	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+	depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST
 	select VIDEO_ADV7180
 	select VIDEOBUF_DMA_CONTIG
 	---help---
@@ -100,6 +98,7 @@
 	depends on ARM_DMA_USE_IOMMU
 	depends on OMAP_IOMMU
 	select VIDEOBUF2_DMA_CONTIG
+	depends on MFD_SYSCON
 	---help---
 	  Driver for an OMAP 3 camera controller.
 
@@ -127,6 +126,8 @@
 source "drivers/media/platform/soc_camera/Kconfig"
 source "drivers/media/platform/exynos4-is/Kconfig"
 source "drivers/media/platform/s5p-tv/Kconfig"
+source "drivers/media/platform/am437x/Kconfig"
+source "drivers/media/platform/xilinx/Kconfig"
 
 endif # V4L_PLATFORM_DRIVERS
 
@@ -150,6 +151,7 @@
 	depends on HAS_DMA
 	depends on SRAM
 	select VIDEOBUF2_DMA_CONTIG
+	select VIDEOBUF2_VMALLOC
 	select V4L2_MEM2MEM_DEV
 	depends on GENERIC_ALLOCATOR
 	---help---
@@ -231,7 +233,6 @@
 	tristate "SuperH VEU mem2mem video processing driver"
 	depends on m
 	depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
-	depends on HAS_DMA
 	select VIDEOBUF2_DMA_CONTIG
 	select V4L2_MEM2MEM_DEV
 	help
@@ -243,7 +244,7 @@
 	tristate "Renesas VSP1 Video Processing Engine"
 	depends on m
 	depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
-	depends on ARCH_SHMOBILE || COMPILE_TEST
+	depends on (ARCH_SHMOBILE && OF) || COMPILE_TEST
 	select VIDEOBUF2_DMA_CONTIG
 	---help---
 	  This is a V4L2 driver for the Renesas VSP1 video processing engine.
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 6449a06..1981096 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -46,4 +46,8 @@
 
 obj-y	+= omap/
 
+obj-$(CPTCFG_VIDEO_AM437X_VPFE)		+= am437x/
+
+obj-$(CPTCFG_VIDEO_XILINX)		+= xilinx/
+
 ccflags-y += -I$(backport_srctree)/drivers/media/i2c
diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/am437x/Kconfig
new file mode 100644
index 0000000..63964c9
--- /dev/null
+++ b/drivers/media/platform/am437x/Kconfig
@@ -0,0 +1,12 @@
+config VIDEO_AM437X_VPFE
+	tristate "TI AM437x VPFE video capture driver"
+	depends on m
+	depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+	depends on SOC_AM43XX || COMPILE_TEST
+	select VIDEOBUF2_DMA_CONTIG
+	help
+	   Support for AM437x Video Processing Front End based Video
+	   Capture Driver.
+
+	   To compile this driver as a module, choose M here. The module
+	   will be called am437x-vpfe.
diff --git a/drivers/media/platform/am437x/Makefile b/drivers/media/platform/am437x/Makefile
new file mode 100644
index 0000000..eb15270
--- /dev/null
+++ b/drivers/media/platform/am437x/Makefile
@@ -0,0 +1,3 @@
+# Makefile for AM437x VPFE driver
+
+obj-$(CPTCFG_VIDEO_AM437X_VPFE) += am437x-vpfe.o
diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
new file mode 100644
index 0000000..451e687
--- /dev/null
+++ b/drivers/media/platform/am437x/am437x-vpfe.c
@@ -0,0 +1,2772 @@
+/*
+ * TI VPFE capture Driver
+ *
+ * Copyright (C) 2013 - 2014 Texas Instruments, Inc.
+ *
+ * Benoit Parrot <bparrot@ti.com>
+ * Lad, Prabhakar <prabhakar.csengg@gmail.com>
+ *
+ * This program is free software; you may 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-of.h>
+
+#include "am437x-vpfe.h"
+
+#define VPFE_MODULE_NAME	"vpfe"
+#define VPFE_VERSION		"0.1.0"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-8");
+
+#define vpfe_dbg(level, dev, fmt, arg...)	\
+		v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ##arg)
+#define vpfe_info(dev, fmt, arg...)	\
+		v4l2_info(&dev->v4l2_dev, fmt, ##arg)
+#define vpfe_err(dev, fmt, arg...)	\
+		v4l2_err(&dev->v4l2_dev, fmt, ##arg)
+
+/* standard information */
+struct vpfe_standard {
+	v4l2_std_id std_id;
+	unsigned int width;
+	unsigned int height;
+	struct v4l2_fract pixelaspect;
+	int frame_format;
+};
+
+static const struct vpfe_standard vpfe_standards[] = {
+	{V4L2_STD_525_60, 720, 480, {11, 10}, 1},
+	{V4L2_STD_625_50, 720, 576, {54, 59}, 1},
+};
+
+struct bus_format {
+	unsigned int width;
+	unsigned int bpp;
+};
+
+/*
+ * struct vpfe_fmt - VPFE media bus format information
+ * @name: V4L2 format description
+ * @code: V4L2 media bus format code
+ * @shifted: V4L2 media bus format code for the same pixel layout but
+ *	shifted to be 8 bits per pixel. =0 if format is not shiftable.
+ * @pixelformat: V4L2 pixel format FCC identifier
+ * @width: Bits per pixel (when transferred over a bus)
+ * @bpp: Bytes per pixel (when stored in memory)
+ * @supported: Indicates format supported by subdev
+ */
+struct vpfe_fmt {
+	const char *name;
+	u32 fourcc;
+	u32 code;
+	struct bus_format l;
+	struct bus_format s;
+	bool supported;
+	u32 index;
+};
+
+static struct vpfe_fmt formats[] = {
+	{
+		.name		= "YUV 4:2:2 packed, YCbYCr",
+		.fourcc		= V4L2_PIX_FMT_YUYV,
+		.code		= MEDIA_BUS_FMT_YUYV8_2X8,
+		.l.width	= 10,
+		.l.bpp		= 4,
+		.s.width	= 8,
+		.s.bpp		= 2,
+		.supported	= false,
+	}, {
+		.name		= "YUV 4:2:2 packed, CbYCrY",
+		.fourcc		= V4L2_PIX_FMT_UYVY,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.l.width	= 10,
+		.l.bpp		= 4,
+		.s.width	= 8,
+		.s.bpp		= 2,
+		.supported	= false,
+	}, {
+		.name		= "YUV 4:2:2 packed, YCrYCb",
+		.fourcc		= V4L2_PIX_FMT_YVYU,
+		.code		= MEDIA_BUS_FMT_YVYU8_2X8,
+		.l.width	= 10,
+		.l.bpp		= 4,
+		.s.width	= 8,
+		.s.bpp		= 2,
+		.supported	= false,
+	}, {
+		.name		= "YUV 4:2:2 packed, CrYCbY",
+		.fourcc		= V4L2_PIX_FMT_VYUY,
+		.code		= MEDIA_BUS_FMT_VYUY8_2X8,
+		.l.width	= 10,
+		.l.bpp		= 4,
+		.s.width	= 8,
+		.s.bpp		= 2,
+		.supported	= false,
+	}, {
+		.name		= "RAW8 BGGR",
+		.fourcc		= V4L2_PIX_FMT_SBGGR8,
+		.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
+		.l.width	= 10,
+		.l.bpp		= 2,
+		.s.width	= 8,
+		.s.bpp		= 1,
+		.supported	= false,
+	}, {
+		.name		= "RAW8 GBRG",
+		.fourcc		= V4L2_PIX_FMT_SGBRG8,
+		.code		= MEDIA_BUS_FMT_SGBRG8_1X8,
+		.l.width	= 10,
+		.l.bpp		= 2,
+		.s.width	= 8,
+		.s.bpp		= 1,
+		.supported	= false,
+	}, {
+		.name		= "RAW8 GRBG",
+		.fourcc		= V4L2_PIX_FMT_SGRBG8,
+		.code		= MEDIA_BUS_FMT_SGRBG8_1X8,
+		.l.width	= 10,
+		.l.bpp		= 2,
+		.s.width	= 8,
+		.s.bpp		= 1,
+		.supported	= false,
+	}, {
+		.name		= "RAW8 RGGB",
+		.fourcc		= V4L2_PIX_FMT_SRGGB8,
+		.code		= MEDIA_BUS_FMT_SRGGB8_1X8,
+		.l.width	= 10,
+		.l.bpp		= 2,
+		.s.width	= 8,
+		.s.bpp		= 1,
+		.supported	= false,
+	}, {
+		.name		= "RGB565 (LE)",
+		.fourcc		= V4L2_PIX_FMT_RGB565,
+		.code		= MEDIA_BUS_FMT_RGB565_2X8_LE,
+		.l.width	= 10,
+		.l.bpp		= 4,
+		.s.width	= 8,
+		.s.bpp		= 2,
+		.supported	= false,
+	}, {
+		.name		= "RGB565 (BE)",
+		.fourcc		= V4L2_PIX_FMT_RGB565X,
+		.code		= MEDIA_BUS_FMT_RGB565_2X8_BE,
+		.l.width	= 10,
+		.l.bpp		= 4,
+		.s.width	= 8,
+		.s.bpp		= 2,
+		.supported	= false,
+	},
+};
+
+static int
+__vpfe_get_format(struct vpfe_device *vpfe,
+		  struct v4l2_format *format, unsigned int *bpp);
+
+static struct vpfe_fmt *find_format_by_code(unsigned int code)
+{
+	struct vpfe_fmt *fmt;
+	unsigned int k;
+
+	for (k = 0; k < ARRAY_SIZE(formats); k++) {
+		fmt = &formats[k];
+		if (fmt->code == code)
+			return fmt;
+	}
+
+	return NULL;
+}
+
+static struct vpfe_fmt *find_format_by_pix(unsigned int pixelformat)
+{
+	struct vpfe_fmt *fmt;
+	unsigned int k;
+
+	for (k = 0; k < ARRAY_SIZE(formats); k++) {
+		fmt = &formats[k];
+		if (fmt->fourcc == pixelformat)
+			return fmt;
+	}
+
+	return NULL;
+}
+
+static void
+mbus_to_pix(struct vpfe_device *vpfe,
+	    const struct v4l2_mbus_framefmt *mbus,
+	    struct v4l2_pix_format *pix, unsigned int *bpp)
+{
+	struct vpfe_subdev_info *sdinfo = vpfe->current_subdev;
+	unsigned int bus_width = sdinfo->vpfe_param.bus_width;
+	struct vpfe_fmt *fmt;
+
+	fmt = find_format_by_code(mbus->code);
+	if (WARN_ON(fmt == NULL)) {
+		pr_err("Invalid mbus code set\n");
+		*bpp = 1;
+		return;
+	}
+
+	memset(pix, 0, sizeof(*pix));
+	v4l2_fill_pix_format(pix, mbus);
+	pix->pixelformat = fmt->fourcc;
+	*bpp = (bus_width == 10) ?  fmt->l.bpp : fmt->s.bpp;
+
+	/* pitch should be 32 bytes aligned */
+	pix->bytesperline = ALIGN(pix->width * *bpp, 32);
+	pix->sizeimage = pix->bytesperline * pix->height;
+}
+
+static void pix_to_mbus(struct vpfe_device *vpfe,
+			struct v4l2_pix_format *pix_fmt,
+			struct v4l2_mbus_framefmt *mbus_fmt)
+{
+	struct vpfe_fmt *fmt;
+
+	fmt = find_format_by_pix(pix_fmt->pixelformat);
+	if (!fmt) {
+		/* default to first entry */
+		vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n",
+			pix_fmt->pixelformat);
+		fmt = &formats[0];
+	}
+
+	memset(mbus_fmt, 0, sizeof(*mbus_fmt));
+	v4l2_fill_mbus_format(mbus_fmt, pix_fmt, fmt->code);
+}
+
+/*  Print Four-character-code (FOURCC) */
+static char *print_fourcc(u32 fmt)
+{
+	static char code[5];
+
+	code[0] = (unsigned char)(fmt & 0xff);
+	code[1] = (unsigned char)((fmt >> 8) & 0xff);
+	code[2] = (unsigned char)((fmt >> 16) & 0xff);
+	code[3] = (unsigned char)((fmt >> 24) & 0xff);
+	code[4] = '\0';
+
+	return code;
+}
+
+static int
+cmp_v4l2_format(const struct v4l2_format *lhs, const struct v4l2_format *rhs)
+{
+	return lhs->type == rhs->type &&
+		lhs->fmt.pix.width == rhs->fmt.pix.width &&
+		lhs->fmt.pix.height == rhs->fmt.pix.height &&
+		lhs->fmt.pix.pixelformat == rhs->fmt.pix.pixelformat &&
+		lhs->fmt.pix.field == rhs->fmt.pix.field &&
+		lhs->fmt.pix.colorspace == rhs->fmt.pix.colorspace &&
+		lhs->fmt.pix.ycbcr_enc == rhs->fmt.pix.ycbcr_enc &&
+		lhs->fmt.pix.quantization == rhs->fmt.pix.quantization;
+}
+
+static inline u32 vpfe_reg_read(struct vpfe_ccdc *ccdc, u32 offset)
+{
+	return ioread32(ccdc->ccdc_cfg.base_addr + offset);
+}
+
+static inline void vpfe_reg_write(struct vpfe_ccdc *ccdc, u32 val, u32 offset)
+{
+	iowrite32(val, ccdc->ccdc_cfg.base_addr + offset);
+}
+
+static inline struct vpfe_device *to_vpfe(struct vpfe_ccdc *ccdc)
+{
+	return container_of(ccdc, struct vpfe_device, ccdc);
+}
+
+static inline struct vpfe_cap_buffer *to_vpfe_buffer(struct vb2_buffer *vb)
+{
+	return container_of(vb, struct vpfe_cap_buffer, vb);
+}
+
+static inline void vpfe_pcr_enable(struct vpfe_ccdc *ccdc, int flag)
+{
+	vpfe_reg_write(ccdc, !!flag, VPFE_PCR);
+}
+
+static void vpfe_config_enable(struct vpfe_ccdc *ccdc, int flag)
+{
+	unsigned int cfg;
+
+	if (!flag) {
+		cfg = vpfe_reg_read(ccdc, VPFE_CONFIG);
+		cfg &= ~(VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT);
+	} else {
+		cfg = VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT;
+	}
+
+	vpfe_reg_write(ccdc, cfg, VPFE_CONFIG);
+}
+
+static void vpfe_ccdc_setwin(struct vpfe_ccdc *ccdc,
+			     struct v4l2_rect *image_win,
+			     enum ccdc_frmfmt frm_fmt,
+			     int bpp)
+{
+	int horz_start, horz_nr_pixels;
+	int vert_start, vert_nr_lines;
+	int val, mid_img;
+
+	/*
+	 * ppc - per pixel count. indicates how many pixels per cell
+	 * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
+	 * raw capture this is 1
+	 */
+	horz_start = image_win->left * bpp;
+	horz_nr_pixels = (image_win->width * bpp) - 1;
+	vpfe_reg_write(ccdc, (horz_start << VPFE_HORZ_INFO_SPH_SHIFT) |
+				horz_nr_pixels, VPFE_HORZ_INFO);
+
+	vert_start = image_win->top;
+
+	if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
+		vert_nr_lines = (image_win->height >> 1) - 1;
+		vert_start >>= 1;
+		/* Since first line doesn't have any data */
+		vert_start += 1;
+		/* configure VDINT0 */
+		val = (vert_start << VPFE_VDINT_VDINT0_SHIFT);
+	} else {
+		/* Since first line doesn't have any data */
+		vert_start += 1;
+		vert_nr_lines = image_win->height - 1;
+		/*
+		 * configure VDINT0 and VDINT1. VDINT1 will be at half
+		 * of image height
+		 */
+		mid_img = vert_start + (image_win->height / 2);
+		val = (vert_start << VPFE_VDINT_VDINT0_SHIFT) |
+				(mid_img & VPFE_VDINT_VDINT1_MASK);
+	}
+
+	vpfe_reg_write(ccdc, val, VPFE_VDINT);
+
+	vpfe_reg_write(ccdc, (vert_start << VPFE_VERT_START_SLV0_SHIFT) |
+				vert_start, VPFE_VERT_START);
+	vpfe_reg_write(ccdc, vert_nr_lines, VPFE_VERT_LINES);
+}
+
+static void vpfe_reg_dump(struct vpfe_ccdc *ccdc)
+{
+	struct vpfe_device *vpfe = to_vpfe(ccdc);
+
+	vpfe_dbg(3, vpfe, "ALAW: 0x%x\n", vpfe_reg_read(ccdc, VPFE_ALAW));
+	vpfe_dbg(3, vpfe, "CLAMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_CLAMP));
+	vpfe_dbg(3, vpfe, "DCSUB: 0x%x\n", vpfe_reg_read(ccdc, VPFE_DCSUB));
+	vpfe_dbg(3, vpfe, "BLKCMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_BLKCMP));
+	vpfe_dbg(3, vpfe, "COLPTN: 0x%x\n", vpfe_reg_read(ccdc, VPFE_COLPTN));
+	vpfe_dbg(3, vpfe, "SDOFST: 0x%x\n", vpfe_reg_read(ccdc, VPFE_SDOFST));
+	vpfe_dbg(3, vpfe, "SYN_MODE: 0x%x\n",
+		 vpfe_reg_read(ccdc, VPFE_SYNMODE));
+	vpfe_dbg(3, vpfe, "HSIZE_OFF: 0x%x\n",
+		 vpfe_reg_read(ccdc, VPFE_HSIZE_OFF));
+	vpfe_dbg(3, vpfe, "HORZ_INFO: 0x%x\n",
+		 vpfe_reg_read(ccdc, VPFE_HORZ_INFO));
+	vpfe_dbg(3, vpfe, "VERT_START: 0x%x\n",
+		 vpfe_reg_read(ccdc, VPFE_VERT_START));
+	vpfe_dbg(3, vpfe, "VERT_LINES: 0x%x\n",
+		 vpfe_reg_read(ccdc, VPFE_VERT_LINES));
+}
+
+static int
+vpfe_ccdc_validate_param(struct vpfe_ccdc *ccdc,
+			 struct vpfe_ccdc_config_params_raw *ccdcparam)
+{
+	struct vpfe_device *vpfe = to_vpfe(ccdc);
+	u8 max_gamma, max_data;
+
+	if (!ccdcparam->alaw.enable)
+		return 0;
+
+	max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd);
+	max_data = ccdc_data_size_max_bit(ccdcparam->data_sz);
+
+	if (ccdcparam->alaw.gamma_wd > VPFE_CCDC_GAMMA_BITS_09_0 ||
+	    ccdcparam->alaw.gamma_wd < VPFE_CCDC_GAMMA_BITS_15_6 ||
+	    max_gamma > max_data) {
+		vpfe_dbg(1, vpfe, "Invalid data line select\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void
+vpfe_ccdc_update_raw_params(struct vpfe_ccdc *ccdc,
+			    struct vpfe_ccdc_config_params_raw *raw_params)
+{
+	struct vpfe_ccdc_config_params_raw *config_params =
+				&ccdc->ccdc_cfg.bayer.config_params;
+
+	config_params = raw_params;
+}
+
+/*
+ * vpfe_ccdc_restore_defaults()
+ * This function will write defaults to all CCDC registers
+ */
+static void vpfe_ccdc_restore_defaults(struct vpfe_ccdc *ccdc)
+{
+	int i;
+
+	/* Disable CCDC */
+	vpfe_pcr_enable(ccdc, 0);
+
+	/* set all registers to default value */
+	for (i = 4; i <= 0x94; i += 4)
+		vpfe_reg_write(ccdc, 0,  i);
+
+	vpfe_reg_write(ccdc, VPFE_NO_CULLING, VPFE_CULLING);
+	vpfe_reg_write(ccdc, VPFE_CCDC_GAMMA_BITS_11_2, VPFE_ALAW);
+}
+
+static int vpfe_ccdc_close(struct vpfe_ccdc *ccdc, struct device *dev)
+{
+	int dma_cntl, i, pcr;
+
+	/* If the CCDC module is still busy wait for it to be done */
+	for (i = 0; i < 10; i++) {
+		usleep_range(5000, 6000);
+		pcr = vpfe_reg_read(ccdc, VPFE_PCR);
+		if (!pcr)
+			break;
+
+		/* make sure it it is disabled */
+		vpfe_pcr_enable(ccdc, 0);
+	}
+
+	/* Disable CCDC by resetting all register to default POR values */
+	vpfe_ccdc_restore_defaults(ccdc);
+
+	/* if DMA_CNTL overflow bit is set. Clear it
+	 *  It appears to take a while for this to become quiescent ~20ms
+	 */
+	for (i = 0; i < 10; i++) {
+		dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL);
+		if (!(dma_cntl & VPFE_DMA_CNTL_OVERFLOW))
+			break;
+
+		/* Clear the overflow bit */
+		vpfe_reg_write(ccdc, dma_cntl, VPFE_DMA_CNTL);
+		usleep_range(5000, 6000);
+	}
+
+	/* Disabled the module at the CONFIG level */
+	vpfe_config_enable(ccdc, 0);
+
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static int vpfe_ccdc_set_params(struct vpfe_ccdc *ccdc, void __user *params)
+{
+	struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
+	struct vpfe_ccdc_config_params_raw raw_params;
+	int x;
+
+	if (ccdc->ccdc_cfg.if_type != VPFE_RAW_BAYER)
+		return -EINVAL;
+
+	x = copy_from_user(&raw_params, params, sizeof(raw_params));
+	if (x) {
+		vpfe_dbg(1, vpfe,
+			"vpfe_ccdc_set_params: error in copying ccdc params, %d\n",
+			x);
+		return -EFAULT;
+	}
+
+	if (!vpfe_ccdc_validate_param(ccdc, &raw_params)) {
+		vpfe_ccdc_update_raw_params(ccdc, &raw_params);
+			return 0;
+	}
+
+	return -EINVAL;
+}
+
+/*
+ * vpfe_ccdc_config_ycbcr()
+ * This function will configure CCDC for YCbCr video capture
+ */
+static void vpfe_ccdc_config_ycbcr(struct vpfe_ccdc *ccdc)
+{
+	struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
+	struct ccdc_params_ycbcr *params = &ccdc->ccdc_cfg.ycbcr;
+	u32 syn_mode;
+
+	vpfe_dbg(3, vpfe, "vpfe_ccdc_config_ycbcr:\n");
+	/*
+	 * first restore the CCDC registers to default values
+	 * This is important since we assume default values to be set in
+	 * a lot of registers that we didn't touch
+	 */
+	vpfe_ccdc_restore_defaults(ccdc);
+
+	/*
+	 * configure pixel format, frame format, configure video frame
+	 * format, enable output to SDRAM, enable internal timing generator
+	 * and 8bit pack mode
+	 */
+	syn_mode = (((params->pix_fmt & VPFE_SYN_MODE_INPMOD_MASK) <<
+		    VPFE_SYN_MODE_INPMOD_SHIFT) |
+		    ((params->frm_fmt & VPFE_SYN_FLDMODE_MASK) <<
+		    VPFE_SYN_FLDMODE_SHIFT) | VPFE_VDHDEN_ENABLE |
+		    VPFE_WEN_ENABLE | VPFE_DATA_PACK_ENABLE);
+
+	/* setup BT.656 sync mode */
+	if (params->bt656_enable) {
+		vpfe_reg_write(ccdc, VPFE_REC656IF_BT656_EN, VPFE_REC656IF);
+
+		/*
+		 * configure the FID, VD, HD pin polarity,
+		 * fld,hd pol positive, vd negative, 8-bit data
+		 */
+		syn_mode |= VPFE_SYN_MODE_VD_POL_NEGATIVE;
+		if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT)
+			syn_mode |= VPFE_SYN_MODE_10BITS;
+		else
+			syn_mode |= VPFE_SYN_MODE_8BITS;
+	} else {
+		/* y/c external sync mode */
+		syn_mode |= (((params->fid_pol & VPFE_FID_POL_MASK) <<
+			     VPFE_FID_POL_SHIFT) |
+			     ((params->hd_pol & VPFE_HD_POL_MASK) <<
+			     VPFE_HD_POL_SHIFT) |
+			     ((params->vd_pol & VPFE_VD_POL_MASK) <<
+			     VPFE_VD_POL_SHIFT));
+	}
+	vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE);
+
+	/* configure video window */
+	vpfe_ccdc_setwin(ccdc, &params->win,
+			 params->frm_fmt, params->bytesperpixel);
+
+	/*
+	 * configure the order of y cb cr in SDRAM, and disable latch
+	 * internal register on vsync
+	 */
+	if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT)
+		vpfe_reg_write(ccdc,
+			       (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) |
+			       VPFE_LATCH_ON_VSYNC_DISABLE |
+			       VPFE_CCDCFG_BW656_10BIT, VPFE_CCDCFG);
+	else
+		vpfe_reg_write(ccdc,
+			       (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) |
+			       VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG);
+
+	/*
+	 * configure the horizontal line offset. This should be a
+	 * on 32 byte boundary. So clear LSB 5 bits
+	 */
+	vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF);
+
+	/* configure the memory line offset */
+	if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)
+		/* two fields are interleaved in memory */
+		vpfe_reg_write(ccdc, VPFE_SDOFST_FIELD_INTERLEAVED,
+			       VPFE_SDOFST);
+}
+
+static void
+vpfe_ccdc_config_black_clamp(struct vpfe_ccdc *ccdc,
+			     struct vpfe_ccdc_black_clamp *bclamp)
+{
+	u32 val;
+
+	if (!bclamp->enable) {
+		/* configure DCSub */
+		val = (bclamp->dc_sub) & VPFE_BLK_DC_SUB_MASK;
+		vpfe_reg_write(ccdc, val, VPFE_DCSUB);
+		vpfe_reg_write(ccdc, VPFE_CLAMP_DEFAULT_VAL, VPFE_CLAMP);
+		return;
+	}
+	/*
+	 * Configure gain,  Start pixel, No of line to be avg,
+	 * No of pixel/line to be avg, & Enable the Black clamping
+	 */
+	val = ((bclamp->sgain & VPFE_BLK_SGAIN_MASK) |
+	       ((bclamp->start_pixel & VPFE_BLK_ST_PXL_MASK) <<
+		VPFE_BLK_ST_PXL_SHIFT) |
+	       ((bclamp->sample_ln & VPFE_BLK_SAMPLE_LINE_MASK) <<
+		VPFE_BLK_SAMPLE_LINE_SHIFT) |
+	       ((bclamp->sample_pixel & VPFE_BLK_SAMPLE_LN_MASK) <<
+		VPFE_BLK_SAMPLE_LN_SHIFT) | VPFE_BLK_CLAMP_ENABLE);
+	vpfe_reg_write(ccdc, val, VPFE_CLAMP);
+	/* If Black clamping is enable then make dcsub 0 */
+	vpfe_reg_write(ccdc, VPFE_DCSUB_DEFAULT_VAL, VPFE_DCSUB);
+}
+
+static void
+vpfe_ccdc_config_black_compense(struct vpfe_ccdc *ccdc,
+				struct vpfe_ccdc_black_compensation *bcomp)
+{
+	u32 val;
+
+	val = ((bcomp->b & VPFE_BLK_COMP_MASK) |
+	      ((bcomp->gb & VPFE_BLK_COMP_MASK) <<
+	       VPFE_BLK_COMP_GB_COMP_SHIFT) |
+	      ((bcomp->gr & VPFE_BLK_COMP_MASK) <<
+	       VPFE_BLK_COMP_GR_COMP_SHIFT) |
+	      ((bcomp->r & VPFE_BLK_COMP_MASK) <<
+	       VPFE_BLK_COMP_R_COMP_SHIFT));
+	vpfe_reg_write(ccdc, val, VPFE_BLKCMP);
+}
+
+/*
+ * vpfe_ccdc_config_raw()
+ * This function will configure CCDC for Raw capture mode
+ */
+static void vpfe_ccdc_config_raw(struct vpfe_ccdc *ccdc)
+{
+	struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
+	struct vpfe_ccdc_config_params_raw *config_params =
+				&ccdc->ccdc_cfg.bayer.config_params;
+	struct ccdc_params_raw *params = &ccdc->ccdc_cfg.bayer;
+	unsigned int syn_mode;
+	unsigned int val;
+
+	vpfe_dbg(3, vpfe, "vpfe_ccdc_config_raw:\n");
+
+	/* Reset CCDC */
+	vpfe_ccdc_restore_defaults(ccdc);
+
+	/* Disable latching function registers on VSYNC  */
+	vpfe_reg_write(ccdc, VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG);
+
+	/*
+	 * Configure the vertical sync polarity(SYN_MODE.VDPOL),
+	 * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity
+	 * (SYN_MODE.FLDPOL), frame format(progressive or interlace),
+	 * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output
+	 * SDRAM, enable internal timing generator
+	 */
+	syn_mode = (((params->vd_pol & VPFE_VD_POL_MASK) << VPFE_VD_POL_SHIFT) |
+		   ((params->hd_pol & VPFE_HD_POL_MASK) << VPFE_HD_POL_SHIFT) |
+		   ((params->fid_pol & VPFE_FID_POL_MASK) <<
+		   VPFE_FID_POL_SHIFT) | ((params->frm_fmt &
+		   VPFE_FRM_FMT_MASK) << VPFE_FRM_FMT_SHIFT) |
+		   ((config_params->data_sz & VPFE_DATA_SZ_MASK) <<
+		   VPFE_DATA_SZ_SHIFT) | ((params->pix_fmt &
+		   VPFE_PIX_FMT_MASK) << VPFE_PIX_FMT_SHIFT) |
+		   VPFE_WEN_ENABLE | VPFE_VDHDEN_ENABLE);
+
+	/* Enable and configure aLaw register if needed */
+	if (config_params->alaw.enable) {
+		val = ((config_params->alaw.gamma_wd &
+		      VPFE_ALAW_GAMMA_WD_MASK) | VPFE_ALAW_ENABLE);
+		vpfe_reg_write(ccdc, val, VPFE_ALAW);
+		vpfe_dbg(3, vpfe, "\nWriting 0x%x to ALAW...\n", val);
+	}
+
+	/* Configure video window */
+	vpfe_ccdc_setwin(ccdc, &params->win, params->frm_fmt,
+			 params->bytesperpixel);
+
+	/* Configure Black Clamp */
+	vpfe_ccdc_config_black_clamp(ccdc, &config_params->blk_clamp);
+
+	/* Configure Black level compensation */
+	vpfe_ccdc_config_black_compense(ccdc, &config_params->blk_comp);
+
+	/* If data size is 8 bit then pack the data */
+	if ((config_params->data_sz == VPFE_CCDC_DATA_8BITS) ||
+	    config_params->alaw.enable)
+		syn_mode |= VPFE_DATA_PACK_ENABLE;
+
+	/*
+	 * Configure Horizontal offset register. If pack 8 is enabled then
+	 * 1 pixel will take 1 byte
+	 */
+	vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF);
+
+	vpfe_dbg(3, vpfe, "Writing %d (%x) to HSIZE_OFF\n",
+		params->bytesperline, params->bytesperline);
+
+	/* Set value for SDOFST */
+	if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
+		if (params->image_invert_enable) {
+			/* For interlace inverse mode */
+			vpfe_reg_write(ccdc, VPFE_INTERLACED_IMAGE_INVERT,
+				   VPFE_SDOFST);
+		} else {
+			/* For interlace non inverse mode */
+			vpfe_reg_write(ccdc, VPFE_INTERLACED_NO_IMAGE_INVERT,
+				   VPFE_SDOFST);
+		}
+	} else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
+		vpfe_reg_write(ccdc, VPFE_PROGRESSIVE_NO_IMAGE_INVERT,
+			   VPFE_SDOFST);
+	}
+
+	vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE);
+
+	vpfe_reg_dump(ccdc);
+}
+
+static inline int
+vpfe_ccdc_set_buftype(struct vpfe_ccdc *ccdc,
+		      enum ccdc_buftype buf_type)
+{
+	if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
+		ccdc->ccdc_cfg.bayer.buf_type = buf_type;
+	else
+		ccdc->ccdc_cfg.ycbcr.buf_type = buf_type;
+
+	return 0;
+}
+
+static inline enum ccdc_buftype vpfe_ccdc_get_buftype(struct vpfe_ccdc *ccdc)
+{
+	if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
+		return ccdc->ccdc_cfg.bayer.buf_type;
+
+	return ccdc->ccdc_cfg.ycbcr.buf_type;
+}
+
+static int vpfe_ccdc_set_pixel_format(struct vpfe_ccdc *ccdc, u32 pixfmt)
+{
+	struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
+
+	vpfe_dbg(1, vpfe, "vpfe_ccdc_set_pixel_format: if_type: %d, pixfmt:%s\n",
+		 ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt));
+
+	if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+		ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+		/*
+		 * Need to clear it in case it was left on
+		 * after the last capture.
+		 */
+		ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 0;
+
+		switch (pixfmt) {
+		case V4L2_PIX_FMT_SBGGR8:
+			ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 1;
+			break;
+
+		case V4L2_PIX_FMT_YUYV:
+		case V4L2_PIX_FMT_UYVY:
+		case V4L2_PIX_FMT_YUV420:
+		case V4L2_PIX_FMT_NV12:
+		case V4L2_PIX_FMT_RGB565X:
+			break;
+
+		case V4L2_PIX_FMT_SBGGR16:
+		default:
+			return -EINVAL;
+		}
+	} else {
+		switch (pixfmt) {
+		case V4L2_PIX_FMT_YUYV:
+			ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
+			break;
+
+		case V4L2_PIX_FMT_UYVY:
+			ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static u32 vpfe_ccdc_get_pixel_format(struct vpfe_ccdc *ccdc)
+{
+	u32 pixfmt;
+
+	if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+		pixfmt = V4L2_PIX_FMT_YUYV;
+	} else {
+		if (ccdc->ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
+			pixfmt = V4L2_PIX_FMT_YUYV;
+		else
+			pixfmt = V4L2_PIX_FMT_UYVY;
+	}
+
+	return pixfmt;
+}
+
+static int
+vpfe_ccdc_set_image_window(struct vpfe_ccdc *ccdc,
+			   struct v4l2_rect *win, unsigned int bpp)
+{
+	if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+		ccdc->ccdc_cfg.bayer.win = *win;
+		ccdc->ccdc_cfg.bayer.bytesperpixel = bpp;
+		ccdc->ccdc_cfg.bayer.bytesperline = ALIGN(win->width * bpp, 32);
+	} else {
+		ccdc->ccdc_cfg.ycbcr.win = *win;
+		ccdc->ccdc_cfg.ycbcr.bytesperpixel = bpp;
+		ccdc->ccdc_cfg.ycbcr.bytesperline = ALIGN(win->width * bpp, 32);
+	}
+
+	return 0;
+}
+
+static inline void
+vpfe_ccdc_get_image_window(struct vpfe_ccdc *ccdc,
+			   struct v4l2_rect *win)
+{
+	if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
+		*win = ccdc->ccdc_cfg.bayer.win;
+	else
+		*win = ccdc->ccdc_cfg.ycbcr.win;
+}
+
+static inline unsigned int vpfe_ccdc_get_line_length(struct vpfe_ccdc *ccdc)
+{
+	if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
+		return ccdc->ccdc_cfg.bayer.bytesperline;
+
+	return ccdc->ccdc_cfg.ycbcr.bytesperline;
+}
+
+static inline int
+vpfe_ccdc_set_frame_format(struct vpfe_ccdc *ccdc,
+			   enum ccdc_frmfmt frm_fmt)
+{
+	if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
+		ccdc->ccdc_cfg.bayer.frm_fmt = frm_fmt;
+	else
+		ccdc->ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
+
+	return 0;
+}
+
+static inline enum ccdc_frmfmt
+vpfe_ccdc_get_frame_format(struct vpfe_ccdc *ccdc)
+{
+	if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)
+		return ccdc->ccdc_cfg.bayer.frm_fmt;
+
+	return ccdc->ccdc_cfg.ycbcr.frm_fmt;
+}
+
+static inline int vpfe_ccdc_getfid(struct vpfe_ccdc *ccdc)
+{
+	return (vpfe_reg_read(ccdc, VPFE_SYNMODE) >> 15) & 1;
+}
+
+static inline void vpfe_set_sdr_addr(struct vpfe_ccdc *ccdc, unsigned long addr)
+{
+	vpfe_reg_write(ccdc, addr & 0xffffffe0, VPFE_SDR_ADDR);
+}
+
+static int vpfe_ccdc_set_hw_if_params(struct vpfe_ccdc *ccdc,
+				      struct vpfe_hw_if_param *params)
+{
+	struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
+
+	ccdc->ccdc_cfg.if_type = params->if_type;
+
+	switch (params->if_type) {
+	case VPFE_BT656:
+	case VPFE_YCBCR_SYNC_16:
+	case VPFE_YCBCR_SYNC_8:
+	case VPFE_BT656_10BIT:
+		ccdc->ccdc_cfg.ycbcr.vd_pol = params->vdpol;
+		ccdc->ccdc_cfg.ycbcr.hd_pol = params->hdpol;
+		break;
+
+	case VPFE_RAW_BAYER:
+		ccdc->ccdc_cfg.bayer.vd_pol = params->vdpol;
+		ccdc->ccdc_cfg.bayer.hd_pol = params->hdpol;
+		if (params->bus_width == 10)
+			ccdc->ccdc_cfg.bayer.config_params.data_sz =
+				VPFE_CCDC_DATA_10BITS;
+		else
+			ccdc->ccdc_cfg.bayer.config_params.data_sz =
+				VPFE_CCDC_DATA_8BITS;
+		vpfe_dbg(1, vpfe, "params.bus_width: %d\n",
+			params->bus_width);
+		vpfe_dbg(1, vpfe, "config_params.data_sz: %d\n",
+			ccdc->ccdc_cfg.bayer.config_params.data_sz);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void vpfe_clear_intr(struct vpfe_ccdc *ccdc, int vdint)
+{
+	unsigned int vpfe_int_status;
+
+	vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS);
+
+	switch (vdint) {
+	/* VD0 interrupt */
+	case VPFE_VDINT0:
+		vpfe_int_status &= ~VPFE_VDINT0;
+		vpfe_int_status |= VPFE_VDINT0;
+		break;
+
+	/* VD1 interrupt */
+	case VPFE_VDINT1:
+		vpfe_int_status &= ~VPFE_VDINT1;
+		vpfe_int_status |= VPFE_VDINT1;
+		break;
+
+	/* VD2 interrupt */
+	case VPFE_VDINT2:
+		vpfe_int_status &= ~VPFE_VDINT2;
+		vpfe_int_status |= VPFE_VDINT2;
+		break;
+
+	/* Clear all interrupts */
+	default:
+		vpfe_int_status &= ~(VPFE_VDINT0 |
+				VPFE_VDINT1 |
+				VPFE_VDINT2);
+		vpfe_int_status |= (VPFE_VDINT0 |
+				VPFE_VDINT1 |
+				VPFE_VDINT2);
+		break;
+	}
+	/* Clear specific VDINT from the status register */
+	vpfe_reg_write(ccdc, vpfe_int_status, VPFE_IRQ_STS);
+
+	vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS);
+
+	/* Acknowledge that we are done with all interrupts */
+	vpfe_reg_write(ccdc, 1, VPFE_IRQ_EOI);
+}
+
+static void vpfe_ccdc_config_defaults(struct vpfe_ccdc *ccdc)
+{
+	ccdc->ccdc_cfg.if_type = VPFE_RAW_BAYER;
+
+	ccdc->ccdc_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT;
+	ccdc->ccdc_cfg.ycbcr.frm_fmt = CCDC_FRMFMT_INTERLACED;
+	ccdc->ccdc_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE;
+	ccdc->ccdc_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE;
+	ccdc->ccdc_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE;
+	ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+	ccdc->ccdc_cfg.ycbcr.buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED;
+
+	ccdc->ccdc_cfg.ycbcr.win.left = 0;
+	ccdc->ccdc_cfg.ycbcr.win.top = 0;
+	ccdc->ccdc_cfg.ycbcr.win.width = 720;
+	ccdc->ccdc_cfg.ycbcr.win.height = 576;
+	ccdc->ccdc_cfg.ycbcr.bt656_enable = 1;
+
+	ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+	ccdc->ccdc_cfg.bayer.frm_fmt = CCDC_FRMFMT_PROGRESSIVE;
+	ccdc->ccdc_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE;
+	ccdc->ccdc_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE;
+	ccdc->ccdc_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE;
+
+	ccdc->ccdc_cfg.bayer.win.left = 0;
+	ccdc->ccdc_cfg.bayer.win.top = 0;
+	ccdc->ccdc_cfg.bayer.win.width = 800;
+	ccdc->ccdc_cfg.bayer.win.height = 600;
+	ccdc->ccdc_cfg.bayer.config_params.data_sz = VPFE_CCDC_DATA_8BITS;
+	ccdc->ccdc_cfg.bayer.config_params.alaw.gamma_wd =
+						VPFE_CCDC_GAMMA_BITS_09_0;
+}
+
+/*
+ * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings
+ */
+static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe,
+				      struct v4l2_format *f)
+{
+	struct v4l2_rect image_win;
+	enum ccdc_buftype buf_type;
+	enum ccdc_frmfmt frm_fmt;
+
+	memset(f, 0, sizeof(*f));
+	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win);
+	f->fmt.pix.width = image_win.width;
+	f->fmt.pix.height = image_win.height;
+	f->fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc);
+	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+				f->fmt.pix.height;
+	buf_type = vpfe_ccdc_get_buftype(&vpfe->ccdc);
+	f->fmt.pix.pixelformat = vpfe_ccdc_get_pixel_format(&vpfe->ccdc);
+	frm_fmt = vpfe_ccdc_get_frame_format(&vpfe->ccdc);
+
+	if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
+		f->fmt.pix.field = V4L2_FIELD_NONE;
+	} else if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
+		if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) {
+			f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+		 } else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) {
+			f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
+		} else {
+			vpfe_err(vpfe, "Invalid buf_type\n");
+			return -EINVAL;
+		}
+	} else {
+		vpfe_err(vpfe, "Invalid frm_fmt\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe)
+{
+	enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED;
+	int ret;
+
+	vpfe_dbg(2, vpfe, "vpfe_config_ccdc_image_format\n");
+
+	vpfe_dbg(1, vpfe, "pixelformat: %s\n",
+		print_fourcc(vpfe->fmt.fmt.pix.pixelformat));
+
+	if (vpfe_ccdc_set_pixel_format(&vpfe->ccdc,
+			vpfe->fmt.fmt.pix.pixelformat) < 0) {
+		vpfe_err(vpfe, "couldn't set pix format in ccdc\n");
+		return -EINVAL;
+	}
+
+	/* configure the image window */
+	vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, vpfe->bpp);
+
+	switch (vpfe->fmt.fmt.pix.field) {
+	case V4L2_FIELD_INTERLACED:
+		/* do nothing, since it is default */
+		ret = vpfe_ccdc_set_buftype(
+				&vpfe->ccdc,
+				CCDC_BUFTYPE_FLD_INTERLEAVED);
+		break;
+
+	case V4L2_FIELD_NONE:
+		frm_fmt = CCDC_FRMFMT_PROGRESSIVE;
+		/* buffer type only applicable for interlaced scan */
+		break;
+
+	case V4L2_FIELD_SEQ_TB:
+		ret = vpfe_ccdc_set_buftype(
+				&vpfe->ccdc,
+				CCDC_BUFTYPE_FLD_SEPARATED);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if (ret)
+		return ret;
+
+	return vpfe_ccdc_set_frame_format(&vpfe->ccdc, frm_fmt);
+}
+
+/*
+ * vpfe_config_image_format()
+ * For a given standard, this functions sets up the default
+ * pix format & crop values in the vpfe device and ccdc.  It first
+ * starts with defaults based values from the standard table.
+ * It then checks if sub device support g_mbus_fmt and then override the
+ * values based on that.Sets crop values to match with scan resolution
+ * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the
+ * values in ccdc
+ */
+static int vpfe_config_image_format(struct vpfe_device *vpfe,
+				    v4l2_std_id std_id)
+{
+	struct v4l2_pix_format *pix = &vpfe->fmt.fmt.pix;
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) {
+		if (vpfe_standards[i].std_id & std_id) {
+			vpfe->std_info.active_pixels =
+					vpfe_standards[i].width;
+			vpfe->std_info.active_lines =
+					vpfe_standards[i].height;
+			vpfe->std_info.frame_format =
+					vpfe_standards[i].frame_format;
+			vpfe->std_index = i;
+
+			break;
+		}
+	}
+
+	if (i ==  ARRAY_SIZE(vpfe_standards)) {
+		vpfe_err(vpfe, "standard not supported\n");
+		return -EINVAL;
+	}
+
+	vpfe->crop.top = vpfe->crop.left = 0;
+	vpfe->crop.width = vpfe->std_info.active_pixels;
+	vpfe->crop.height = vpfe->std_info.active_lines;
+	pix->width = vpfe->crop.width;
+	pix->height = vpfe->crop.height;
+	pix->pixelformat = V4L2_PIX_FMT_YUYV;
+
+	/* first field and frame format based on standard frame format */
+	if (vpfe->std_info.frame_format)
+		pix->field = V4L2_FIELD_INTERLACED;
+	else
+		pix->field = V4L2_FIELD_NONE;
+
+	ret = __vpfe_get_format(vpfe, &vpfe->fmt, &vpfe->bpp);
+	if (ret)
+		return ret;
+
+	/* Update the crop window based on found values */
+	vpfe->crop.width = pix->width;
+	vpfe->crop.height = pix->height;
+
+	return vpfe_config_ccdc_image_format(vpfe);
+}
+
+static int vpfe_initialize_device(struct vpfe_device *vpfe)
+{
+	struct vpfe_subdev_info *sdinfo;
+	int ret;
+
+	sdinfo = &vpfe->cfg->sub_devs[0];
+	sdinfo->sd = vpfe->sd[0];
+	vpfe->current_input = 0;
+	vpfe->std_index = 0;
+	/* Configure the default format information */
+	ret = vpfe_config_image_format(vpfe,
+				       vpfe_standards[vpfe->std_index].std_id);
+	if (ret)
+		return ret;
+
+	pm_runtime_get_sync(vpfe->pdev);
+
+	vpfe_config_enable(&vpfe->ccdc, 1);
+
+	vpfe_ccdc_restore_defaults(&vpfe->ccdc);
+
+	/* Clear all VPFE interrupts */
+	vpfe_clear_intr(&vpfe->ccdc, -1);
+
+	return ret;
+}
+
+/*
+ * vpfe_release : This function is based on the vb2_fop_release
+ * helper function.
+ * It has been augmented to handle module power management,
+ * by disabling/enabling h/w module fcntl clock when necessary.
+ */
+static int vpfe_release(struct file *file)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	int ret;
+
+	mutex_lock(&vpfe->lock);
+
+	if (v4l2_fh_is_singular_file(file))
+		vpfe_ccdc_close(&vpfe->ccdc, vpfe->pdev);
+	ret = _vb2_fop_release(file, NULL);
+
+	mutex_unlock(&vpfe->lock);
+
+	return ret;
+}
+
+/*
+ * vpfe_open : This function is based on the v4l2_fh_open helper function.
+ * It has been augmented to handle module power management,
+ * by disabling/enabling h/w module fcntl clock when necessary.
+ */
+static int vpfe_open(struct file *file)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	int ret;
+
+	mutex_lock(&vpfe->lock);
+
+	ret = v4l2_fh_open(file);
+	if (ret) {
+		vpfe_err(vpfe, "v4l2_fh_open failed\n");
+		goto unlock;
+	}
+
+	if (!v4l2_fh_is_singular_file(file))
+		goto unlock;
+
+	if (vpfe_initialize_device(vpfe)) {
+		v4l2_fh_release(file);
+		ret = -ENODEV;
+	}
+
+unlock:
+	mutex_unlock(&vpfe->lock);
+	return ret;
+}
+
+/**
+ * vpfe_schedule_next_buffer: set next buffer address for capture
+ * @vpfe : ptr to vpfe device
+ *
+ * This function will get next buffer from the dma queue and
+ * set the buffer address in the vpfe register for capture.
+ * the buffer is marked active
+ *
+ * Assumes caller is holding vpfe->dma_queue_lock already
+ */
+static inline void vpfe_schedule_next_buffer(struct vpfe_device *vpfe)
+{
+	vpfe->next_frm = list_entry(vpfe->dma_queue.next,
+				    struct vpfe_cap_buffer, list);
+	list_del(&vpfe->next_frm->list);
+
+	vpfe_set_sdr_addr(&vpfe->ccdc,
+		       vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0));
+}
+
+static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe)
+{
+	unsigned long addr;
+
+	addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0) +
+					vpfe->field_off;
+
+	vpfe_set_sdr_addr(&vpfe->ccdc, addr);
+}
+
+/*
+ * vpfe_process_buffer_complete: process a completed buffer
+ * @vpfe : ptr to vpfe device
+ *
+ * This function time stamp the buffer and mark it as DONE. It also
+ * wake up any process waiting on the QUEUE and set the next buffer
+ * as current
+ */
+static inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe)
+{
+	v4l2_get_timestamp(&vpfe->cur_frm->vb.v4l2_buf.timestamp);
+	vpfe->cur_frm->vb.v4l2_buf.field = vpfe->fmt.fmt.pix.field;
+	vpfe->cur_frm->vb.v4l2_buf.sequence = vpfe->sequence++;
+	vb2_buffer_done(&vpfe->cur_frm->vb, VB2_BUF_STATE_DONE);
+	vpfe->cur_frm = vpfe->next_frm;
+}
+
+/*
+ * vpfe_isr : ISR handler for vpfe capture (VINT0)
+ * @irq: irq number
+ * @dev_id: dev_id ptr
+ *
+ * It changes status of the captured buffer, takes next buffer from the queue
+ * and sets its address in VPFE registers
+ */
+static irqreturn_t vpfe_isr(int irq, void *dev)
+{
+	struct vpfe_device *vpfe = (struct vpfe_device *)dev;
+	enum v4l2_field field;
+	int intr_status;
+	int fid;
+
+	intr_status = vpfe_reg_read(&vpfe->ccdc, VPFE_IRQ_STS);
+
+	if (intr_status & VPFE_VDINT0) {
+		field = vpfe->fmt.fmt.pix.field;
+
+		if (field == V4L2_FIELD_NONE) {
+			/* handle progressive frame capture */
+			if (vpfe->cur_frm != vpfe->next_frm)
+				vpfe_process_buffer_complete(vpfe);
+			goto next_intr;
+		}
+
+		/* interlaced or TB capture check which field
+		   we are in hardware */
+		fid = vpfe_ccdc_getfid(&vpfe->ccdc);
+
+		/* switch the software maintained field id */
+		vpfe->field ^= 1;
+		if (fid == vpfe->field) {
+			/* we are in-sync here,continue */
+			if (fid == 0) {
+				/*
+				 * One frame is just being captured. If the
+				 * next frame is available, release the
+				 * current frame and move on
+				 */
+				if (vpfe->cur_frm != vpfe->next_frm)
+					vpfe_process_buffer_complete(vpfe);
+				/*
+				 * based on whether the two fields are stored
+				 * interleave or separately in memory,
+				 * reconfigure the CCDC memory address
+				 */
+				if (field == V4L2_FIELD_SEQ_TB)
+					vpfe_schedule_bottom_field(vpfe);
+
+				goto next_intr;
+			}
+			/*
+			 * if one field is just being captured configure
+			 * the next frame get the next frame from the empty
+			 * queue if no frame is available hold on to the
+			 * current buffer
+			 */
+			spin_lock(&vpfe->dma_queue_lock);
+			if (!list_empty(&vpfe->dma_queue) &&
+			    vpfe->cur_frm == vpfe->next_frm)
+				vpfe_schedule_next_buffer(vpfe);
+			spin_unlock(&vpfe->dma_queue_lock);
+		} else if (fid == 0) {
+			/*
+			 * out of sync. Recover from any hardware out-of-sync.
+			 * May loose one frame
+			 */
+			vpfe->field = fid;
+		}
+	}
+
+next_intr:
+	if (intr_status & VPFE_VDINT1) {
+		spin_lock(&vpfe->dma_queue_lock);
+		if (vpfe->fmt.fmt.pix.field == V4L2_FIELD_NONE &&
+		    !list_empty(&vpfe->dma_queue) &&
+		    vpfe->cur_frm == vpfe->next_frm)
+			vpfe_schedule_next_buffer(vpfe);
+		spin_unlock(&vpfe->dma_queue_lock);
+	}
+
+	vpfe_clear_intr(&vpfe->ccdc, intr_status);
+
+	return IRQ_HANDLED;
+}
+
+static inline void vpfe_detach_irq(struct vpfe_device *vpfe)
+{
+	unsigned int intr = VPFE_VDINT0;
+	enum ccdc_frmfmt frame_format;
+
+	frame_format = vpfe_ccdc_get_frame_format(&vpfe->ccdc);
+	if (frame_format == CCDC_FRMFMT_PROGRESSIVE)
+		intr |= VPFE_VDINT1;
+
+	vpfe_reg_write(&vpfe->ccdc, intr, VPFE_IRQ_EN_CLR);
+}
+
+static inline void vpfe_attach_irq(struct vpfe_device *vpfe)
+{
+	unsigned int intr = VPFE_VDINT0;
+	enum ccdc_frmfmt frame_format;
+
+	frame_format = vpfe_ccdc_get_frame_format(&vpfe->ccdc);
+	if (frame_format == CCDC_FRMFMT_PROGRESSIVE)
+		intr |= VPFE_VDINT1;
+
+	vpfe_reg_write(&vpfe->ccdc, intr, VPFE_IRQ_EN_SET);
+}
+
+static int vpfe_querycap(struct file *file, void  *priv,
+			 struct v4l2_capability *cap)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+
+	vpfe_dbg(2, vpfe, "vpfe_querycap\n");
+
+	strlcpy(cap->driver, VPFE_MODULE_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, "TI AM437x VPFE", sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+			"platform:%s", vpfe->v4l2_dev.name);
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+			    V4L2_CAP_READWRITE;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+/* get the format set at output pad of the adjacent subdev */
+static int __vpfe_get_format(struct vpfe_device *vpfe,
+			     struct v4l2_format *format, unsigned int *bpp)
+{
+	struct v4l2_mbus_framefmt mbus_fmt;
+	struct vpfe_subdev_info *sdinfo;
+	struct v4l2_subdev_format fmt;
+	int ret;
+
+	sdinfo = vpfe->current_subdev;
+	if (!sdinfo->sd)
+		return -EINVAL;
+
+	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fmt.pad = 0;
+
+	ret = v4l2_subdev_call(sdinfo->sd, pad, get_fmt, NULL, &fmt);
+	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		return ret;
+
+	if (!ret) {
+		v4l2_fill_pix_format(&format->fmt.pix, &fmt.format);
+		mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp);
+	} else {
+		ret = v4l2_device_call_until_err(&vpfe->v4l2_dev,
+						 sdinfo->grp_id,
+						 video, g_mbus_fmt,
+						 &mbus_fmt);
+		if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+			return ret;
+		v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt);
+		mbus_to_pix(vpfe, &mbus_fmt, &format->fmt.pix, bpp);
+	}
+
+	format->type = vpfe->fmt.type;
+
+	vpfe_dbg(1, vpfe,
+		 "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n",
+		 __func__, format->fmt.pix.width, format->fmt.pix.height,
+		 print_fourcc(format->fmt.pix.pixelformat),
+		 format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp);
+
+	return 0;
+}
+
+/* set the format at output pad of the adjacent subdev */
+static int __vpfe_set_format(struct vpfe_device *vpfe,
+			     struct v4l2_format *format, unsigned int *bpp)
+{
+	struct v4l2_mbus_framefmt mbus_fmt;
+	struct vpfe_subdev_info *sdinfo;
+	struct v4l2_subdev_format fmt;
+	int ret;
+
+	vpfe_dbg(2, vpfe, "__vpfe_set_format\n");
+
+	sdinfo = vpfe->current_subdev;
+	if (!sdinfo->sd)
+		return -EINVAL;
+
+	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fmt.pad = 0;
+
+	pix_to_mbus(vpfe, &format->fmt.pix, &fmt.format);
+
+	ret = v4l2_subdev_call(sdinfo->sd, pad, set_fmt, NULL, &fmt);
+	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		return ret;
+
+	if (!ret) {
+		v4l2_fill_pix_format(&format->fmt.pix, &fmt.format);
+		mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp);
+	} else {
+		ret = v4l2_device_call_until_err(&vpfe->v4l2_dev,
+						 sdinfo->grp_id,
+						 video, s_mbus_fmt,
+						 &mbus_fmt);
+		if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+			return ret;
+
+		v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt);
+		mbus_to_pix(vpfe, &mbus_fmt, &format->fmt.pix, bpp);
+	}
+
+	format->type = vpfe->fmt.type;
+
+	vpfe_dbg(1, vpfe,
+		 "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n",
+		 __func__,  format->fmt.pix.width, format->fmt.pix.height,
+		 print_fourcc(format->fmt.pix.pixelformat),
+		 format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp);
+
+	return 0;
+}
+
+static int vpfe_g_fmt(struct file *file, void *priv,
+		      struct v4l2_format *fmt)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+
+	vpfe_dbg(2, vpfe, "vpfe_g_fmt\n");
+
+	*fmt = vpfe->fmt;
+
+	return 0;
+}
+
+static int vpfe_enum_fmt(struct file *file, void  *priv,
+			 struct v4l2_fmtdesc *f)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	struct vpfe_subdev_info *sdinfo;
+	struct vpfe_fmt *fmt = NULL;
+	unsigned int k;
+
+	vpfe_dbg(2, vpfe, "vpfe_enum_format index:%d\n",
+		f->index);
+
+	sdinfo = vpfe->current_subdev;
+	if (!sdinfo->sd)
+		return -EINVAL;
+
+	if (f->index > ARRAY_SIZE(formats))
+		return -EINVAL;
+
+	for (k = 0; k < ARRAY_SIZE(formats); k++) {
+		if (formats[k].index == f->index) {
+			fmt = &formats[k];
+			break;
+		}
+	}
+	if (!fmt)
+		return -EINVAL;
+
+	strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+	f->pixelformat = fmt->fourcc;
+	f->type = vpfe->fmt.type;
+
+	vpfe_dbg(1, vpfe, "vpfe_enum_format: mbus index: %d code: %x pixelformat: %s [%s]\n",
+		f->index, fmt->code, print_fourcc(fmt->fourcc), fmt->name);
+
+	return 0;
+}
+
+static int vpfe_try_fmt(struct file *file, void *priv,
+			struct v4l2_format *fmt)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	unsigned int bpp;
+
+	vpfe_dbg(2, vpfe, "vpfe_try_fmt\n");
+
+	return __vpfe_get_format(vpfe, fmt, &bpp);
+}
+
+static int vpfe_s_fmt(struct file *file, void *priv,
+		      struct v4l2_format *fmt)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	struct v4l2_format format;
+	unsigned int bpp;
+	int ret;
+
+	vpfe_dbg(2, vpfe, "vpfe_s_fmt\n");
+
+	/* If streaming is started, return error */
+	if (vb2_is_busy(&vpfe->buffer_queue)) {
+		vpfe_err(vpfe, "%s device busy\n", __func__);
+		return -EBUSY;
+	}
+
+	ret = vpfe_try_fmt(file, priv, fmt);
+	if (ret)
+		return ret;
+
+
+	if (!cmp_v4l2_format(fmt, &format)) {
+		/* Sensor format is different from the requested format
+		 * so we need to change it
+		 */
+		ret = __vpfe_set_format(vpfe, fmt, &bpp);
+		if (ret)
+			return ret;
+	} else /* Just make sure all of the fields are consistent */
+		*fmt = format;
+
+	/* First detach any IRQ if currently attached */
+	vpfe_detach_irq(vpfe);
+	vpfe->fmt = *fmt;
+	vpfe->bpp = bpp;
+
+	/* Update the crop window based on found values */
+	vpfe->crop.width = fmt->fmt.pix.width;
+	vpfe->crop.height = fmt->fmt.pix.height;
+
+	/* set image capture parameters in the ccdc */
+	return vpfe_config_ccdc_image_format(vpfe);
+}
+
+static int vpfe_enum_size(struct file *file, void  *priv,
+			  struct v4l2_frmsizeenum *fsize)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	struct v4l2_subdev_frame_size_enum fse;
+	struct vpfe_subdev_info *sdinfo;
+	struct v4l2_mbus_framefmt mbus;
+	struct v4l2_pix_format pix;
+	struct vpfe_fmt *fmt;
+	int ret;
+
+	vpfe_dbg(2, vpfe, "vpfe_enum_size\n");
+
+	/* check for valid format */
+	fmt = find_format_by_pix(fsize->pixel_format);
+	if (!fmt) {
+		vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n",
+			fsize->pixel_format);
+		return -EINVAL;
+	}
+
+	memset(fsize->reserved, 0x0, sizeof(fsize->reserved));
+
+	sdinfo = vpfe->current_subdev;
+	if (!sdinfo->sd)
+		return -EINVAL;
+
+	memset(&pix, 0x0, sizeof(pix));
+	/* Construct pix from parameter and use default for the rest */
+	pix.pixelformat = fsize->pixel_format;
+	pix.width = 640;
+	pix.height = 480;
+	pix.colorspace = V4L2_COLORSPACE_SRGB;
+	pix.field = V4L2_FIELD_NONE;
+	pix_to_mbus(vpfe, &pix, &mbus);
+
+	memset(&fse, 0x0, sizeof(fse));
+	fse.index = fsize->index;
+	fse.pad = 0;
+	fse.code = mbus.code;
+	fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	ret = v4l2_subdev_call(sdinfo->sd, pad, enum_frame_size, NULL, &fse);
+	if (ret)
+		return -EINVAL;
+
+	vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
+		fse.index, fse.code, fse.min_width, fse.max_width,
+		fse.min_height, fse.max_height);
+
+	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+	fsize->discrete.width = fse.max_width;
+	fsize->discrete.height = fse.max_height;
+
+	vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d pixformat: %s size: %dx%d\n",
+		fsize->index, print_fourcc(fsize->pixel_format),
+		fsize->discrete.width, fsize->discrete.height);
+
+	return 0;
+}
+
+/*
+ * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a
+ * given app input index
+ */
+static int
+vpfe_get_subdev_input_index(struct vpfe_device *vpfe,
+			    int *subdev_index,
+			    int *subdev_input_index,
+			    int app_input_index)
+{
+	struct vpfe_config *cfg = vpfe->cfg;
+	struct vpfe_subdev_info *sdinfo;
+	int i, j = 0;
+
+	for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) {
+		sdinfo = &cfg->sub_devs[i];
+		if (app_input_index < (j + 1)) {
+			*subdev_index = i;
+			*subdev_input_index = app_input_index - j;
+			return 0;
+		}
+		j++;
+	}
+	return -EINVAL;
+}
+
+/*
+ * vpfe_get_app_input - Get app input index for a given subdev input index
+ * driver stores the input index of the current sub device and translate it
+ * when application request the current input
+ */
+static int vpfe_get_app_input_index(struct vpfe_device *vpfe,
+				    int *app_input_index)
+{
+	struct vpfe_config *cfg = vpfe->cfg;
+	struct vpfe_subdev_info *sdinfo;
+	struct i2c_client *client;
+	struct i2c_client *curr_client;
+	int i, j = 0;
+
+	curr_client = v4l2_get_subdevdata(vpfe->current_subdev->sd);
+	for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) {
+		sdinfo = &cfg->sub_devs[i];
+		client = v4l2_get_subdevdata(sdinfo->sd);
+		if (client->addr == curr_client->addr &&
+		    client->adapter->nr == client->adapter->nr) {
+			if (vpfe->current_input >= 1)
+				return -1;
+			*app_input_index = j + vpfe->current_input;
+			return 0;
+		}
+		j++;
+	}
+	return -EINVAL;
+}
+
+static int vpfe_enum_input(struct file *file, void *priv,
+			   struct v4l2_input *inp)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	struct vpfe_subdev_info *sdinfo;
+	int subdev, index;
+
+	vpfe_dbg(2, vpfe, "vpfe_enum_input\n");
+
+	if (vpfe_get_subdev_input_index(vpfe, &subdev, &index,
+					inp->index) < 0) {
+		vpfe_dbg(1, vpfe,
+			"input information not found for the subdev\n");
+		return -EINVAL;
+	}
+	sdinfo = &vpfe->cfg->sub_devs[subdev];
+	*inp = sdinfo->inputs[index];
+
+	return 0;
+}
+
+static int vpfe_g_input(struct file *file, void *priv, unsigned int *index)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+
+	vpfe_dbg(2, vpfe, "vpfe_g_input\n");
+
+	return vpfe_get_app_input_index(vpfe, index);
+}
+
+/* Assumes caller is holding vpfe_dev->lock */
+static int vpfe_set_input(struct vpfe_device *vpfe, unsigned int index)
+{
+	int subdev_index = 0, inp_index = 0;
+	struct vpfe_subdev_info *sdinfo;
+	struct vpfe_route *route;
+	u32 input, output;
+	int ret;
+
+	vpfe_dbg(2, vpfe, "vpfe_set_input: index: %d\n", index);
+
+	/* If streaming is started, return error */
+	if (vb2_is_busy(&vpfe->buffer_queue)) {
+		vpfe_err(vpfe, "%s device busy\n", __func__);
+		return -EBUSY;
+	}
+	ret = vpfe_get_subdev_input_index(vpfe,
+					  &subdev_index,
+					  &inp_index,
+					  index);
+	if (ret < 0) {
+		vpfe_err(vpfe, "invalid input index: %d\n", index);
+		goto get_out;
+	}
+
+	sdinfo = &vpfe->cfg->sub_devs[subdev_index];
+	sdinfo->sd = vpfe->sd[subdev_index];
+	route = &sdinfo->routes[inp_index];
+	if (route && sdinfo->can_route) {
+		input = route->input;
+		output = route->output;
+		if (sdinfo->sd) {
+			ret = v4l2_subdev_call(sdinfo->sd, video,
+					s_routing, input, output, 0);
+			if (ret) {
+				vpfe_err(vpfe, "s_routing failed\n");
+				ret = -EINVAL;
+				goto get_out;
+			}
+		}
+
+	}
+
+	vpfe->current_subdev = sdinfo;
+	if (sdinfo->sd)
+		vpfe->v4l2_dev.ctrl_handler = sdinfo->sd->ctrl_handler;
+	vpfe->current_input = index;
+	vpfe->std_index = 0;
+
+	/* set the bus/interface parameter for the sub device in ccdc */
+	ret = vpfe_ccdc_set_hw_if_params(&vpfe->ccdc, &sdinfo->vpfe_param);
+	if (ret)
+		return ret;
+
+	/* set the default image parameters in the device */
+	return vpfe_config_image_format(vpfe,
+					vpfe_standards[vpfe->std_index].std_id);
+
+get_out:
+	return ret;
+}
+
+static int vpfe_s_input(struct file *file, void *priv, unsigned int index)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+
+	vpfe_dbg(2, vpfe,
+		"vpfe_s_input: index: %d\n", index);
+
+	return vpfe_set_input(vpfe, index);
+}
+
+static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	struct vpfe_subdev_info *sdinfo;
+
+	vpfe_dbg(2, vpfe, "vpfe_querystd\n");
+
+	sdinfo = vpfe->current_subdev;
+	if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD))
+		return -ENODATA;
+
+	/* Call querystd function of decoder device */
+	return v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id,
+					 video, querystd, std_id);
+}
+
+static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	struct vpfe_subdev_info *sdinfo;
+	int ret;
+
+	vpfe_dbg(2, vpfe, "vpfe_s_std\n");
+
+	sdinfo = vpfe->current_subdev;
+	if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD))
+		return -ENODATA;
+
+	/* If streaming is started, return error */
+	if (vb2_is_busy(&vpfe->buffer_queue)) {
+		vpfe_err(vpfe, "%s device busy\n", __func__);
+		ret = -EBUSY;
+		return ret;
+	}
+
+	ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id,
+					 video, s_std, std_id);
+	if (ret < 0) {
+		vpfe_err(vpfe, "Failed to set standard\n");
+		return ret;
+	}
+	ret = vpfe_config_image_format(vpfe, std_id);
+
+	return ret;
+}
+
+static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	struct vpfe_subdev_info *sdinfo;
+
+	vpfe_dbg(2, vpfe, "vpfe_g_std\n");
+
+	sdinfo = vpfe->current_subdev;
+	if (sdinfo->inputs[0].capabilities != V4L2_IN_CAP_STD)
+		return -ENODATA;
+
+	*std_id = vpfe_standards[vpfe->std_index].std_id;
+
+	return 0;
+}
+
+/*
+ * vpfe_calculate_offsets : This function calculates buffers offset
+ * for top and bottom field
+ */
+static void vpfe_calculate_offsets(struct vpfe_device *vpfe)
+{
+	struct v4l2_rect image_win;
+
+	vpfe_dbg(2, vpfe, "vpfe_calculate_offsets\n");
+
+	vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win);
+	vpfe->field_off = image_win.height * image_win.width;
+}
+
+/*
+ * vpfe_queue_setup - Callback function for buffer setup.
+ * @vq: vb2_queue ptr
+ * @fmt: v4l2 format
+ * @nbuffers: ptr to number of buffers requested by application
+ * @nplanes:: contains number of distinct video planes needed to hold a frame
+ * @sizes[]: contains the size (in bytes) of each plane.
+ * @alloc_ctxs: ptr to allocation context
+ *
+ * This callback function is called when reqbuf() is called to adjust
+ * the buffer count and buffer size
+ */
+static int vpfe_queue_setup(struct vb2_queue *vq,
+			    const struct v4l2_format *fmt,
+			    unsigned int *nbuffers, unsigned int *nplanes,
+			    unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
+
+	if (fmt && fmt->fmt.pix.sizeimage < vpfe->fmt.fmt.pix.sizeimage)
+		return -EINVAL;
+
+	if (vq->num_buffers + *nbuffers < 3)
+		*nbuffers = 3 - vq->num_buffers;
+
+	*nplanes = 1;
+	sizes[0] = fmt ? fmt->fmt.pix.sizeimage : vpfe->fmt.fmt.pix.sizeimage;
+	alloc_ctxs[0] = vpfe->alloc_ctx;
+
+	vpfe_dbg(1, vpfe,
+		"nbuffers=%d, size=%u\n", *nbuffers, sizes[0]);
+
+	/* Calculate field offset */
+	vpfe_calculate_offsets(vpfe);
+
+	return 0;
+}
+
+/*
+ * vpfe_buffer_prepare :  callback function for buffer prepare
+ * @vb: ptr to vb2_buffer
+ *
+ * This is the callback function for buffer prepare when vb2_qbuf()
+ * function is called. The buffer is prepared and user space virtual address
+ * or user address is converted into  physical address
+ */
+static int vpfe_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue);
+
+	vb2_set_plane_payload(vb, 0, vpfe->fmt.fmt.pix.sizeimage);
+
+	if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
+		return -EINVAL;
+
+	vb->v4l2_buf.field = vpfe->fmt.fmt.pix.field;
+
+	return 0;
+}
+
+/*
+ * vpfe_buffer_queue : Callback function to add buffer to DMA queue
+ * @vb: ptr to vb2_buffer
+ */
+static void vpfe_buffer_queue(struct vb2_buffer *vb)
+{
+	struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue);
+	struct vpfe_cap_buffer *buf = to_vpfe_buffer(vb);
+	unsigned long flags = 0;
+
+	/* add the buffer to the DMA queue */
+	spin_lock_irqsave(&vpfe->dma_queue_lock, flags);
+	list_add_tail(&buf->list, &vpfe->dma_queue);
+	spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
+}
+
+/*
+ * vpfe_start_streaming : Starts the DMA engine for streaming
+ * @vb: ptr to vb2_buffer
+ * @count: number of buffers
+ */
+static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
+	struct vpfe_cap_buffer *buf, *tmp;
+	struct vpfe_subdev_info *sdinfo;
+	unsigned long flags;
+	unsigned long addr;
+	int ret;
+
+	spin_lock_irqsave(&vpfe->dma_queue_lock, flags);
+
+	vpfe->field = 0;
+	vpfe->sequence = 0;
+
+	sdinfo = vpfe->current_subdev;
+
+	vpfe_attach_irq(vpfe);
+
+	if (vpfe->ccdc.ccdc_cfg.if_type == VPFE_RAW_BAYER)
+		vpfe_ccdc_config_raw(&vpfe->ccdc);
+	else
+		vpfe_ccdc_config_ycbcr(&vpfe->ccdc);
+
+	/* Get the next frame from the buffer queue */
+	vpfe->next_frm = list_entry(vpfe->dma_queue.next,
+				    struct vpfe_cap_buffer, list);
+	vpfe->cur_frm = vpfe->next_frm;
+	/* Remove buffer from the buffer queue */
+	list_del(&vpfe->cur_frm->list);
+	spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
+
+	addr = vb2_dma_contig_plane_dma_addr(&vpfe->cur_frm->vb, 0);
+
+	vpfe_set_sdr_addr(&vpfe->ccdc, (unsigned long)(addr));
+
+	vpfe_pcr_enable(&vpfe->ccdc, 1);
+
+	ret = v4l2_subdev_call(sdinfo->sd, video, s_stream, 1);
+	if (ret < 0) {
+		vpfe_err(vpfe, "Error in attaching interrupt handle\n");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	list_for_each_entry_safe(buf, tmp, &vpfe->dma_queue, list) {
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+	}
+
+	return ret;
+}
+
+/*
+ * vpfe_stop_streaming : Stop the DMA engine
+ * @vq: ptr to vb2_queue
+ *
+ * This callback stops the DMA engine and any remaining buffers
+ * in the DMA queue are released.
+ */
+static void vpfe_stop_streaming(struct vb2_queue *vq)
+{
+	struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
+	struct vpfe_subdev_info *sdinfo;
+	unsigned long flags;
+	int ret;
+
+	vpfe_pcr_enable(&vpfe->ccdc, 0);
+
+	vpfe_detach_irq(vpfe);
+
+	sdinfo = vpfe->current_subdev;
+	ret = v4l2_subdev_call(sdinfo->sd, video, s_stream, 0);
+	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		vpfe_dbg(1, vpfe, "stream off failed in subdev\n");
+
+	/* release all active buffers */
+	spin_lock_irqsave(&vpfe->dma_queue_lock, flags);
+	if (vpfe->cur_frm == vpfe->next_frm) {
+		vb2_buffer_done(&vpfe->cur_frm->vb, VB2_BUF_STATE_ERROR);
+	} else {
+		if (vpfe->cur_frm != NULL)
+			vb2_buffer_done(&vpfe->cur_frm->vb,
+					VB2_BUF_STATE_ERROR);
+		if (vpfe->next_frm != NULL)
+			vb2_buffer_done(&vpfe->next_frm->vb,
+					VB2_BUF_STATE_ERROR);
+	}
+
+	while (!list_empty(&vpfe->dma_queue)) {
+		vpfe->next_frm = list_entry(vpfe->dma_queue.next,
+						struct vpfe_cap_buffer, list);
+		list_del(&vpfe->next_frm->list);
+		vb2_buffer_done(&vpfe->next_frm->vb, VB2_BUF_STATE_ERROR);
+	}
+	spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
+}
+
+static int vpfe_cropcap(struct file *file, void *priv,
+			struct v4l2_cropcap *crop)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+
+	vpfe_dbg(2, vpfe, "vpfe_cropcap\n");
+
+	if (vpfe->std_index >= ARRAY_SIZE(vpfe_standards))
+		return -EINVAL;
+
+	memset(crop, 0, sizeof(struct v4l2_cropcap));
+
+	crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	crop->defrect.width = vpfe_standards[vpfe->std_index].width;
+	crop->bounds.width = crop->defrect.width;
+	crop->defrect.height = vpfe_standards[vpfe->std_index].height;
+	crop->bounds.height = crop->defrect.height;
+	crop->pixelaspect = vpfe_standards[vpfe->std_index].pixelaspect;
+
+	return 0;
+}
+
+static int
+vpfe_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		s->r.left = s->r.top = 0;
+		s->r.width = vpfe->crop.width;
+		s->r.height = vpfe->crop.height;
+		break;
+
+	case V4L2_SEL_TGT_CROP:
+		s->r = vpfe->crop;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+	if (a->left < b->left || a->top < b->top)
+		return 0;
+
+	if (a->left + a->width > b->left + b->width)
+		return 0;
+
+	if (a->top + a->height > b->top + b->height)
+		return 0;
+
+	return 1;
+}
+
+static int
+vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	struct v4l2_rect cr = vpfe->crop;
+	struct v4l2_rect r = s->r;
+
+	/* If streaming is started, return error */
+	if (vb2_is_busy(&vpfe->buffer_queue)) {
+		vpfe_err(vpfe, "%s device busy\n", __func__);
+		return -EBUSY;
+	}
+
+	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+			s->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	v4l_bound_align_image(&r.width, 0, cr.width, 0,
+			      &r.height, 0, cr.height, 0, 0);
+
+	r.left = clamp_t(unsigned int, r.left, 0, cr.width - r.width);
+	r.top  = clamp_t(unsigned int, r.top, 0, cr.height - r.height);
+
+	if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r))
+		return -ERANGE;
+
+	if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r))
+		return -ERANGE;
+
+	s->r = vpfe->crop = r;
+
+	vpfe_ccdc_set_image_window(&vpfe->ccdc, &r, vpfe->bpp);
+	vpfe->fmt.fmt.pix.width = r.width;
+	vpfe->fmt.fmt.pix.height = r.height;
+	vpfe->fmt.fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc);
+	vpfe->fmt.fmt.pix.sizeimage = vpfe->fmt.fmt.pix.bytesperline *
+						vpfe->fmt.fmt.pix.height;
+
+	vpfe_dbg(1, vpfe, "cropped (%d,%d)/%dx%d of %dx%d\n",
+		 r.left, r.top, r.width, r.height, cr.width, cr.height);
+
+	return 0;
+}
+
+static long vpfe_ioctl_default(struct file *file, void *priv,
+			       bool valid_prio, unsigned int cmd, void *param)
+{
+	struct vpfe_device *vpfe = video_drvdata(file);
+	int ret;
+
+	vpfe_dbg(2, vpfe, "vpfe_ioctl_default\n");
+
+	if (!valid_prio) {
+		vpfe_err(vpfe, "%s device busy\n", __func__);
+		return -EBUSY;
+	}
+
+	/* If streaming is started, return error */
+	if (vb2_is_busy(&vpfe->buffer_queue)) {
+		vpfe_err(vpfe, "%s device busy\n", __func__);
+		return -EBUSY;
+	}
+
+	switch (cmd) {
+	case VIDIOC_AM437X_CCDC_CFG:
+		ret = vpfe_ccdc_set_params(&vpfe->ccdc, (void __user *)param);
+		if (ret) {
+			vpfe_dbg(2, vpfe,
+				"Error setting parameters in CCDC\n");
+			return ret;
+		}
+		ret = vpfe_get_ccdc_image_format(vpfe,
+						 &vpfe->fmt);
+		if (ret < 0) {
+			vpfe_dbg(2, vpfe,
+				"Invalid image format at CCDC\n");
+			return ret;
+		}
+		break;
+
+	default:
+		ret = -ENOTTY;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct vb2_ops vpfe_video_qops = {
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+	.queue_setup		= vpfe_queue_setup,
+	.buf_prepare		= vpfe_buffer_prepare,
+	.buf_queue		= vpfe_buffer_queue,
+	.start_streaming	= vpfe_start_streaming,
+	.stop_streaming		= vpfe_stop_streaming,
+};
+
+/* vpfe capture driver file operations */
+static const struct v4l2_file_operations vpfe_fops = {
+	.owner		= THIS_MODULE,
+	.open		= vpfe_open,
+	.release	= vpfe_release,
+	.read		= vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= vb2_fop_mmap,
+};
+
+/* vpfe capture ioctl operations */
+static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
+	.vidioc_querycap		= vpfe_querycap,
+	.vidioc_enum_fmt_vid_cap	= vpfe_enum_fmt,
+	.vidioc_g_fmt_vid_cap		= vpfe_g_fmt,
+	.vidioc_s_fmt_vid_cap		= vpfe_s_fmt,
+	.vidioc_try_fmt_vid_cap		= vpfe_try_fmt,
+
+	.vidioc_enum_framesizes		= vpfe_enum_size,
+
+	.vidioc_enum_input		= vpfe_enum_input,
+	.vidioc_g_input			= vpfe_g_input,
+	.vidioc_s_input			= vpfe_s_input,
+
+	.vidioc_querystd		= vpfe_querystd,
+	.vidioc_s_std			= vpfe_s_std,
+	.vidioc_g_std			= vpfe_g_std,
+
+	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
+	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
+	.vidioc_querybuf		= vb2_ioctl_querybuf,
+	.vidioc_qbuf			= vb2_ioctl_qbuf,
+	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+	.vidioc_expbuf			= vb2_ioctl_expbuf,
+#endif
+	.vidioc_streamon		= vb2_ioctl_streamon,
+	.vidioc_streamoff		= vb2_ioctl_streamoff,
+
+	.vidioc_log_status		= v4l2_ctrl_log_status,
+	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
+
+	.vidioc_cropcap			= vpfe_cropcap,
+	.vidioc_g_selection		= vpfe_g_selection,
+	.vidioc_s_selection		= vpfe_s_selection,
+
+	.vidioc_default			= vpfe_ioctl_default,
+};
+
+static int
+vpfe_async_bound(struct v4l2_async_notifier *notifier,
+		 struct v4l2_subdev *subdev,
+		 struct v4l2_async_subdev *asd)
+{
+	struct vpfe_device *vpfe = container_of(notifier->v4l2_dev,
+					       struct vpfe_device, v4l2_dev);
+	struct v4l2_subdev_mbus_code_enum mbus_code;
+	struct vpfe_subdev_info *sdinfo;
+	bool found = false;
+	int i, j;
+
+	vpfe_dbg(1, vpfe, "vpfe_async_bound\n");
+
+	for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) {
+		if (vpfe->cfg->asd[i]->match.of.node == asd[i].match.of.node) {
+			sdinfo = &vpfe->cfg->sub_devs[i];
+			vpfe->sd[i] = subdev;
+			vpfe->sd[i]->grp_id = sdinfo->grp_id;
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		vpfe_info(vpfe, "sub device (%s) not matched\n", subdev->name);
+		return -EINVAL;
+	}
+
+	vpfe->video_dev.tvnorms |= sdinfo->inputs[0].std;
+
+	/* setup the supported formats & indexes */
+	for (j = 0, i = 0; ; ++j) {
+		struct vpfe_fmt *fmt;
+		int ret;
+
+		memset(&mbus_code, 0, sizeof(mbus_code));
+		mbus_code.index = j;
+		mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+		ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
+			       NULL, &mbus_code);
+		if (ret)
+			break;
+
+		fmt = find_format_by_code(mbus_code.code);
+		if (!fmt)
+			continue;
+
+		fmt->supported = true;
+		fmt->index = i++;
+	}
+
+	return 0;
+}
+
+static int vpfe_probe_complete(struct vpfe_device *vpfe)
+{
+	struct video_device *vdev;
+	struct vb2_queue *q;
+	int err;
+
+	spin_lock_init(&vpfe->dma_queue_lock);
+	mutex_init(&vpfe->lock);
+
+	vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	/* set first sub device as current one */
+	vpfe->current_subdev = &vpfe->cfg->sub_devs[0];
+	vpfe->v4l2_dev.ctrl_handler = vpfe->sd[0]->ctrl_handler;
+
+	err = vpfe_set_input(vpfe, 0);
+	if (err)
+		goto probe_out;
+
+	/* Initialize videobuf2 queue as per the buffer type */
+	vpfe->alloc_ctx = vb2_dma_contig_init_ctx(vpfe->pdev);
+	if (IS_ERR(vpfe->alloc_ctx)) {
+		vpfe_err(vpfe, "Failed to get the context\n");
+		err = PTR_ERR(vpfe->alloc_ctx);
+		goto probe_out;
+	}
+
+	q = &vpfe->buffer_queue;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+	q->drv_priv = vpfe;
+	q->ops = &vpfe_video_qops;
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->buf_struct_size = sizeof(struct vpfe_cap_buffer);
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &vpfe->lock;
+	q->min_buffers_needed = 1;
+
+	err = vb2_queue_init(q);
+	if (err) {
+		vpfe_err(vpfe, "vb2_queue_init() failed\n");
+		vb2_dma_contig_cleanup_ctx(vpfe->alloc_ctx);
+		goto probe_out;
+	}
+
+	INIT_LIST_HEAD(&vpfe->dma_queue);
+
+	vdev = &vpfe->video_dev;
+	strlcpy(vdev->name, VPFE_MODULE_NAME, sizeof(vdev->name));
+	vdev->release = video_device_release_empty;
+	vdev->fops = &vpfe_fops;
+	vdev->ioctl_ops = &vpfe_ioctl_ops;
+	vdev->v4l2_dev = &vpfe->v4l2_dev;
+	vdev->vfl_dir = VFL_DIR_RX;
+	vdev->queue = q;
+	vdev->lock = &vpfe->lock;
+	video_set_drvdata(vdev, vpfe);
+	err = video_register_device(&vpfe->video_dev, VFL_TYPE_GRABBER, -1);
+	if (err) {
+		vpfe_err(vpfe,
+			"Unable to register video device.\n");
+		goto probe_out;
+	}
+
+	return 0;
+
+probe_out:
+	v4l2_device_unregister(&vpfe->v4l2_dev);
+	return err;
+}
+
+static int vpfe_async_complete(struct v4l2_async_notifier *notifier)
+{
+	struct vpfe_device *vpfe = container_of(notifier->v4l2_dev,
+					struct vpfe_device, v4l2_dev);
+
+	return vpfe_probe_complete(vpfe);
+}
+
+static struct vpfe_config *
+vpfe_get_pdata(struct platform_device *pdev)
+{
+	struct device_node *endpoint = NULL;
+	struct v4l2_of_endpoint bus_cfg;
+	struct vpfe_subdev_info *sdinfo;
+	struct vpfe_config *pdata;
+	unsigned int flags;
+	unsigned int i;
+	int err;
+
+	dev_dbg(&pdev->dev, "vpfe_get_pdata\n");
+
+	if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
+		return pdev->dev.platform_data;
+
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return NULL;
+
+	for (i = 0; ; i++) {
+		struct device_node *rem;
+
+		endpoint = of_graph_get_next_endpoint(pdev->dev.of_node,
+						      endpoint);
+		if (!endpoint)
+			break;
+
+		sdinfo = &pdata->sub_devs[i];
+		sdinfo->grp_id = 0;
+
+		/* we only support camera */
+		sdinfo->inputs[0].index = i;
+		strcpy(sdinfo->inputs[0].name, "Camera");
+		sdinfo->inputs[0].type = V4L2_INPUT_TYPE_CAMERA;
+		sdinfo->inputs[0].std = V4L2_STD_ALL;
+		sdinfo->inputs[0].capabilities = V4L2_IN_CAP_STD;
+
+		sdinfo->can_route = 0;
+		sdinfo->routes = NULL;
+
+		of_property_read_u32(endpoint, "ti,am437x-vpfe-interface",
+				     &sdinfo->vpfe_param.if_type);
+		if (sdinfo->vpfe_param.if_type < 0 ||
+			sdinfo->vpfe_param.if_type > 4) {
+			sdinfo->vpfe_param.if_type = VPFE_RAW_BAYER;
+		}
+
+		err = v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+		if (err) {
+			dev_err(&pdev->dev, "Could not parse the endpoint\n");
+			goto done;
+		}
+
+		sdinfo->vpfe_param.bus_width = bus_cfg.bus.parallel.bus_width;
+
+		if (sdinfo->vpfe_param.bus_width < 8 ||
+			sdinfo->vpfe_param.bus_width > 16) {
+			dev_err(&pdev->dev, "Invalid bus width.\n");
+			goto done;
+		}
+
+		flags = bus_cfg.bus.parallel.flags;
+
+		if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+			sdinfo->vpfe_param.hdpol = 1;
+
+		if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+			sdinfo->vpfe_param.vdpol = 1;
+
+		rem = of_graph_get_remote_port_parent(endpoint);
+		if (!rem) {
+			dev_err(&pdev->dev, "Remote device at %s not found\n",
+				endpoint->full_name);
+			goto done;
+		}
+
+		pdata->asd[i] = devm_kzalloc(&pdev->dev,
+					     sizeof(struct v4l2_async_subdev),
+					     GFP_KERNEL);
+		if (!pdata->asd[i]) {
+			of_node_put(rem);
+			pdata = NULL;
+			goto done;
+		}
+
+		pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_OF;
+		pdata->asd[i]->match.of.node = rem;
+		of_node_put(rem);
+	}
+
+	of_node_put(endpoint);
+	return pdata;
+
+done:
+	of_node_put(endpoint);
+	return NULL;
+}
+
+/*
+ * vpfe_probe : This function creates device entries by register
+ * itself to the V4L2 driver and initializes fields of each
+ * device objects
+ */
+static int vpfe_probe(struct platform_device *pdev)
+{
+	struct vpfe_config *vpfe_cfg = vpfe_get_pdata(pdev);
+	struct vpfe_device *vpfe;
+	struct vpfe_ccdc *ccdc;
+	struct resource	*res;
+	int ret;
+
+	if (!vpfe_cfg) {
+		dev_err(&pdev->dev, "No platform data\n");
+		return -EINVAL;
+	}
+
+	vpfe = devm_kzalloc(&pdev->dev, sizeof(*vpfe), GFP_KERNEL);
+	if (!vpfe)
+		return -ENOMEM;
+
+	vpfe->pdev = &pdev->dev;
+	vpfe->cfg = vpfe_cfg;
+	ccdc = &vpfe->ccdc;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ccdc->ccdc_cfg.base_addr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ccdc->ccdc_cfg.base_addr))
+		return PTR_ERR(ccdc->ccdc_cfg.base_addr);
+
+	vpfe->irq = platform_get_irq(pdev, 0);
+	if (vpfe->irq <= 0) {
+		dev_err(&pdev->dev, "No IRQ resource\n");
+		return -ENODEV;
+	}
+
+	ret = devm_request_irq(vpfe->pdev, vpfe->irq, vpfe_isr, 0,
+			       "vpfe_capture0", vpfe);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to request interrupt\n");
+		return -EINVAL;
+	}
+
+	ret = v4l2_device_register(&pdev->dev, &vpfe->v4l2_dev);
+	if (ret) {
+		vpfe_err(vpfe,
+			"Unable to register v4l2 device.\n");
+		return ret;
+	}
+
+	/* set the driver data in platform device */
+	platform_set_drvdata(pdev, vpfe);
+	/* Enabling module functional clock */
+	pm_runtime_enable(&pdev->dev);
+
+	/* for now just enable it here instead of waiting for the open */
+	pm_runtime_get_sync(&pdev->dev);
+
+	vpfe_ccdc_config_defaults(ccdc);
+
+	pm_runtime_put_sync(&pdev->dev);
+
+	vpfe->sd = devm_kzalloc(&pdev->dev, sizeof(struct v4l2_subdev *) *
+				ARRAY_SIZE(vpfe->cfg->asd), GFP_KERNEL);
+	if (!vpfe->sd) {
+		ret = -ENOMEM;
+		goto probe_out_v4l2_unregister;
+	}
+
+	vpfe->notifier.subdevs = vpfe->cfg->asd;
+	vpfe->notifier.num_subdevs = ARRAY_SIZE(vpfe->cfg->asd);
+	vpfe->notifier.bound = vpfe_async_bound;
+	vpfe->notifier.complete = vpfe_async_complete;
+	ret = v4l2_async_notifier_register(&vpfe->v4l2_dev,
+						&vpfe->notifier);
+	if (ret) {
+		vpfe_err(vpfe, "Error registering async notifier\n");
+		ret = -EINVAL;
+		goto probe_out_v4l2_unregister;
+	}
+
+	return 0;
+
+probe_out_v4l2_unregister:
+	v4l2_device_unregister(&vpfe->v4l2_dev);
+	return ret;
+}
+
+/*
+ * vpfe_remove : It un-register device from V4L2 driver
+ */
+static int vpfe_remove(struct platform_device *pdev)
+{
+	struct vpfe_device *vpfe = platform_get_drvdata(pdev);
+
+	vpfe_dbg(2, vpfe, "vpfe_remove\n");
+
+	pm_runtime_disable(&pdev->dev);
+
+	v4l2_async_notifier_unregister(&vpfe->notifier);
+	v4l2_device_unregister(&vpfe->v4l2_dev);
+	video_unregister_device(&vpfe->video_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static void vpfe_save_context(struct vpfe_ccdc *ccdc)
+{
+	ccdc->ccdc_ctx[VPFE_PCR >> 2] = vpfe_reg_read(ccdc, VPFE_PCR);
+	ccdc->ccdc_ctx[VPFE_SYNMODE >> 2] = vpfe_reg_read(ccdc, VPFE_SYNMODE);
+	ccdc->ccdc_ctx[VPFE_SDOFST >> 2] = vpfe_reg_read(ccdc, VPFE_SDOFST);
+	ccdc->ccdc_ctx[VPFE_SDR_ADDR >> 2] = vpfe_reg_read(ccdc, VPFE_SDR_ADDR);
+	ccdc->ccdc_ctx[VPFE_CLAMP >> 2] = vpfe_reg_read(ccdc, VPFE_CLAMP);
+	ccdc->ccdc_ctx[VPFE_DCSUB >> 2] = vpfe_reg_read(ccdc, VPFE_DCSUB);
+	ccdc->ccdc_ctx[VPFE_COLPTN >> 2] = vpfe_reg_read(ccdc, VPFE_COLPTN);
+	ccdc->ccdc_ctx[VPFE_BLKCMP >> 2] = vpfe_reg_read(ccdc, VPFE_BLKCMP);
+	ccdc->ccdc_ctx[VPFE_VDINT >> 2] = vpfe_reg_read(ccdc, VPFE_VDINT);
+	ccdc->ccdc_ctx[VPFE_ALAW >> 2] = vpfe_reg_read(ccdc, VPFE_ALAW);
+	ccdc->ccdc_ctx[VPFE_REC656IF >> 2] = vpfe_reg_read(ccdc, VPFE_REC656IF);
+	ccdc->ccdc_ctx[VPFE_CCDCFG >> 2] = vpfe_reg_read(ccdc, VPFE_CCDCFG);
+	ccdc->ccdc_ctx[VPFE_CULLING >> 2] = vpfe_reg_read(ccdc, VPFE_CULLING);
+	ccdc->ccdc_ctx[VPFE_HD_VD_WID >> 2] = vpfe_reg_read(ccdc,
+							    VPFE_HD_VD_WID);
+	ccdc->ccdc_ctx[VPFE_PIX_LINES >> 2] = vpfe_reg_read(ccdc,
+							    VPFE_PIX_LINES);
+	ccdc->ccdc_ctx[VPFE_HORZ_INFO >> 2] = vpfe_reg_read(ccdc,
+							    VPFE_HORZ_INFO);
+	ccdc->ccdc_ctx[VPFE_VERT_START >> 2] = vpfe_reg_read(ccdc,
+							     VPFE_VERT_START);
+	ccdc->ccdc_ctx[VPFE_VERT_LINES >> 2] = vpfe_reg_read(ccdc,
+							     VPFE_VERT_LINES);
+	ccdc->ccdc_ctx[VPFE_HSIZE_OFF >> 2] = vpfe_reg_read(ccdc,
+							    VPFE_HSIZE_OFF);
+}
+
+static int vpfe_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vpfe_device *vpfe = platform_get_drvdata(pdev);
+	struct vpfe_ccdc *ccdc = &vpfe->ccdc;
+
+	/* if streaming has not started we don't care */
+	if (!vb2_start_streaming_called(&vpfe->buffer_queue))
+		return 0;
+
+	pm_runtime_get_sync(dev);
+	vpfe_config_enable(ccdc, 1);
+
+	/* Save VPFE context */
+	vpfe_save_context(ccdc);
+
+	/* Disable CCDC */
+	vpfe_pcr_enable(ccdc, 0);
+	vpfe_config_enable(ccdc, 0);
+
+	/* Disable both master and slave clock */
+	pm_runtime_put_sync(dev);
+
+	/* Select sleep pin state */
+	pinctrl_pm_select_sleep_state(dev);
+
+	return 0;
+}
+
+static void vpfe_restore_context(struct vpfe_ccdc *ccdc)
+{
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SYNMODE >> 2], VPFE_SYNMODE);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CULLING >> 2], VPFE_CULLING);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SDOFST >> 2], VPFE_SDOFST);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SDR_ADDR >> 2], VPFE_SDR_ADDR);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CLAMP >> 2], VPFE_CLAMP);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_DCSUB >> 2], VPFE_DCSUB);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_COLPTN >> 2], VPFE_COLPTN);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_BLKCMP >> 2], VPFE_BLKCMP);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VDINT >> 2], VPFE_VDINT);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_ALAW >> 2], VPFE_ALAW);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_REC656IF >> 2], VPFE_REC656IF);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CCDCFG >> 2], VPFE_CCDCFG);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_PCR >> 2], VPFE_PCR);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HD_VD_WID >> 2],
+						VPFE_HD_VD_WID);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_PIX_LINES >> 2],
+						VPFE_PIX_LINES);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HORZ_INFO >> 2],
+						VPFE_HORZ_INFO);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VERT_START >> 2],
+						VPFE_VERT_START);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VERT_LINES >> 2],
+						VPFE_VERT_LINES);
+	vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HSIZE_OFF >> 2],
+						VPFE_HSIZE_OFF);
+}
+
+static int vpfe_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vpfe_device *vpfe = platform_get_drvdata(pdev);
+	struct vpfe_ccdc *ccdc = &vpfe->ccdc;
+
+	/* if streaming has not started we don't care */
+	if (!vb2_start_streaming_called(&vpfe->buffer_queue))
+		return 0;
+
+	/* Enable both master and slave clock */
+	pm_runtime_get_sync(dev);
+	vpfe_config_enable(ccdc, 1);
+
+	/* Restore VPFE context */
+	vpfe_restore_context(ccdc);
+
+	vpfe_config_enable(ccdc, 0);
+	pm_runtime_put_sync(dev);
+
+	/* Select default pin state */
+	pinctrl_pm_select_default_state(dev);
+
+	return 0;
+}
+
+#endif
+
+static SIMPLE_DEV_PM_OPS(vpfe_pm_ops, vpfe_suspend, vpfe_resume);
+
+static const struct of_device_id vpfe_of_match[] = {
+	{ .compatible = "ti,am437x-vpfe", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, vpfe_of_match);
+
+static struct platform_driver vpfe_driver = {
+	.probe		= vpfe_probe,
+	.remove		= vpfe_remove,
+	.driver = {
+		.name	= VPFE_MODULE_NAME,
+		.pm	= &vpfe_pm_ops,
+		.of_match_table = of_match_ptr(vpfe_of_match),
+	},
+};
+
+module_platform_driver(vpfe_driver);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TI AM437x VPFE driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(VPFE_VERSION);
diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h
new file mode 100644
index 0000000..5bfb356
--- /dev/null
+++ b/drivers/media/platform/am437x/am437x-vpfe.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2013 - 2014 Texas Instruments, Inc.
+ *
+ * Benoit Parrot <bparrot@ti.com>
+ * Lad, Prabhakar <prabhakar.csengg@gmail.com>
+ *
+ * This program is free software; you may 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * 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 AM437X_VPFE_H
+#define AM437X_VPFE_H
+
+#include <linux/am437x-vpfe.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "am437x-vpfe_regs.h"
+
+enum vpfe_pin_pol {
+	VPFE_PINPOL_POSITIVE = 0,
+	VPFE_PINPOL_NEGATIVE,
+};
+
+enum vpfe_hw_if_type {
+	/* Raw Bayer */
+	VPFE_RAW_BAYER = 0,
+	/* BT656 - 8 bit */
+	VPFE_BT656,
+	/* BT656 - 10 bit */
+	VPFE_BT656_10BIT,
+	/* YCbCr - 8 bit with external sync */
+	VPFE_YCBCR_SYNC_8,
+	/* YCbCr - 16 bit with external sync */
+	VPFE_YCBCR_SYNC_16,
+};
+
+/* interface description */
+struct vpfe_hw_if_param {
+	enum vpfe_hw_if_type if_type;
+	enum vpfe_pin_pol hdpol;
+	enum vpfe_pin_pol vdpol;
+	unsigned int bus_width;
+};
+
+#define VPFE_MAX_SUBDEV		1
+#define VPFE_MAX_INPUTS		1
+
+struct vpfe_pixel_format {
+	struct v4l2_fmtdesc fmtdesc;
+	/* bytes per pixel */
+	int bpp;
+};
+
+struct vpfe_std_info {
+	int active_pixels;
+	int active_lines;
+	/* current frame format */
+	int frame_format;
+};
+
+struct vpfe_route {
+	u32 input;
+	u32 output;
+};
+
+struct vpfe_subdev_info {
+	/* Sub device group id */
+	int grp_id;
+	/* inputs available at the sub device */
+	struct v4l2_input inputs[VPFE_MAX_INPUTS];
+	/* Sub dev routing information for each input */
+	struct vpfe_route *routes;
+	/* check if sub dev supports routing */
+	int can_route;
+	/* ccdc bus/interface configuration */
+	struct vpfe_hw_if_param vpfe_param;
+	struct v4l2_subdev *sd;
+};
+
+struct vpfe_config {
+	/* information about each subdev */
+	struct vpfe_subdev_info sub_devs[VPFE_MAX_SUBDEV];
+	/* Flat array, arranged in groups */
+	struct v4l2_async_subdev *asd[VPFE_MAX_SUBDEV];
+};
+
+struct vpfe_cap_buffer {
+	struct vb2_buffer vb;
+	struct list_head list;
+};
+
+enum ccdc_pixfmt {
+	CCDC_PIXFMT_RAW = 0,
+	CCDC_PIXFMT_YCBCR_16BIT,
+	CCDC_PIXFMT_YCBCR_8BIT,
+};
+
+enum ccdc_frmfmt {
+	CCDC_FRMFMT_PROGRESSIVE = 0,
+	CCDC_FRMFMT_INTERLACED,
+};
+
+/* PIXEL ORDER IN MEMORY from LSB to MSB */
+/* only applicable for 8-bit input mode  */
+enum ccdc_pixorder {
+	CCDC_PIXORDER_YCBYCR,
+	CCDC_PIXORDER_CBYCRY,
+};
+
+enum ccdc_buftype {
+	CCDC_BUFTYPE_FLD_INTERLEAVED,
+	CCDC_BUFTYPE_FLD_SEPARATED
+};
+
+
+/* returns the highest bit used for the gamma */
+static inline u8 ccdc_gamma_width_max_bit(enum vpfe_ccdc_gamma_width width)
+{
+	return 15 - width;
+}
+
+/* returns the highest bit used for this data size */
+static inline u8 ccdc_data_size_max_bit(enum vpfe_ccdc_data_size sz)
+{
+	return sz == VPFE_CCDC_DATA_8BITS ? 7 : 15 - sz;
+}
+
+/* Structure for CCDC configuration parameters for raw capture mode */
+struct ccdc_params_raw {
+	/* pixel format */
+	enum ccdc_pixfmt pix_fmt;
+	/* progressive or interlaced frame */
+	enum ccdc_frmfmt frm_fmt;
+	struct v4l2_rect win;
+	/* Current Format Bytes Per Pixels */
+	unsigned int bytesperpixel;
+	/* Current Format Bytes per Lines
+	 * (Aligned to 32 bytes) used for HORZ_INFO
+	 */
+	unsigned int bytesperline;
+	/* field id polarity */
+	enum vpfe_pin_pol fid_pol;
+	/* vertical sync polarity */
+	enum vpfe_pin_pol vd_pol;
+	/* horizontal sync polarity */
+	enum vpfe_pin_pol hd_pol;
+	/* interleaved or separated fields */
+	enum ccdc_buftype buf_type;
+	/*
+	 * enable to store the image in inverse
+	 * order in memory(bottom to top)
+	 */
+	unsigned char image_invert_enable;
+	/* configurable parameters */
+	struct vpfe_ccdc_config_params_raw config_params;
+};
+
+struct ccdc_params_ycbcr {
+	/* pixel format */
+	enum ccdc_pixfmt pix_fmt;
+	/* progressive or interlaced frame */
+	enum ccdc_frmfmt frm_fmt;
+	struct v4l2_rect win;
+	/* Current Format Bytes Per Pixels */
+	unsigned int bytesperpixel;
+	/* Current Format Bytes per Lines
+	 * (Aligned to 32 bytes) used for HORZ_INFO
+	 */
+	unsigned int bytesperline;
+	/* field id polarity */
+	enum vpfe_pin_pol fid_pol;
+	/* vertical sync polarity */
+	enum vpfe_pin_pol vd_pol;
+	/* horizontal sync polarity */
+	enum vpfe_pin_pol hd_pol;
+	/* enable BT.656 embedded sync mode */
+	int bt656_enable;
+	/* cb:y:cr:y or y:cb:y:cr in memory */
+	enum ccdc_pixorder pix_order;
+	/* interleaved or separated fields  */
+	enum ccdc_buftype buf_type;
+};
+
+/*
+ * CCDC operational configuration
+ */
+struct ccdc_config {
+	/* CCDC interface type */
+	enum vpfe_hw_if_type if_type;
+	/* Raw Bayer configuration */
+	struct ccdc_params_raw bayer;
+	/* YCbCr configuration */
+	struct ccdc_params_ycbcr ycbcr;
+	/* ccdc base address */
+	void __iomem *base_addr;
+};
+
+struct vpfe_ccdc {
+	struct ccdc_config ccdc_cfg;
+	u32 ccdc_ctx[VPFE_REG_END / sizeof(u32)];
+};
+
+struct vpfe_device {
+	/* V4l2 specific parameters */
+	/* Identifies video device for this channel */
+	struct video_device video_dev;
+	/* sub devices */
+	struct v4l2_subdev **sd;
+	/* vpfe cfg */
+	struct vpfe_config *cfg;
+	/* V4l2 device */
+	struct v4l2_device v4l2_dev;
+	/* parent device */
+	struct device *pdev;
+	/* subdevice async Notifier */
+	struct v4l2_async_notifier notifier;
+	/* Indicates id of the field which is being displayed */
+	unsigned field;
+	unsigned sequence;
+	/* current interface type */
+	struct vpfe_hw_if_param vpfe_if_params;
+	/* ptr to currently selected sub device */
+	struct vpfe_subdev_info *current_subdev;
+	/* current input at the sub device */
+	int current_input;
+	/* Keeps track of the information about the standard */
+	struct vpfe_std_info std_info;
+	/* std index into std table */
+	int std_index;
+	/* IRQs used when CCDC output to SDRAM */
+	unsigned int irq;
+	/* Pointer pointing to current v4l2_buffer */
+	struct vpfe_cap_buffer *cur_frm;
+	/* Pointer pointing to next v4l2_buffer */
+	struct vpfe_cap_buffer *next_frm;
+	/* Used to store pixel format */
+	struct v4l2_format fmt;
+	/* Used to store current bytes per pixel based on current format */
+	unsigned int bpp;
+	/*
+	 * used when IMP is chained to store the crop window which
+	 * is different from the image window
+	 */
+	struct v4l2_rect crop;
+	/* Buffer queue used in video-buf */
+	struct vb2_queue buffer_queue;
+	/* Allocator-specific contexts for each plane */
+	struct vb2_alloc_ctx *alloc_ctx;
+	/* Queue of filled frames */
+	struct list_head dma_queue;
+	/* IRQ lock for DMA queue */
+	spinlock_t dma_queue_lock;
+	/* lock used to access this structure */
+	struct mutex lock;
+	/*
+	 * offset where second field starts from the starting of the
+	 * buffer for field separated YCbCr formats
+	 */
+	u32 field_off;
+	struct vpfe_ccdc ccdc;
+};
+
+#endif	/* AM437X_VPFE_H */
diff --git a/drivers/media/platform/am437x/am437x-vpfe_regs.h b/drivers/media/platform/am437x/am437x-vpfe_regs.h
new file mode 100644
index 0000000..4a0ed29
--- /dev/null
+++ b/drivers/media/platform/am437x/am437x-vpfe_regs.h
@@ -0,0 +1,140 @@
+/*
+ * TI AM437x Image Sensor Interface Registers
+ *
+ * Copyright (C) 2013 - 2014 Texas Instruments, Inc.
+ *
+ * Benoit Parrot <bparrot@ti.com>
+ * Lad, Prabhakar <prabhakar.csengg@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 "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 AM437X_VPFE_REGS_H
+#define AM437X_VPFE_REGS_H
+
+/* VPFE module register offset */
+#define VPFE_REVISION				0x0
+#define VPFE_PCR				0x4
+#define VPFE_SYNMODE				0x8
+#define VPFE_HD_VD_WID				0xc
+#define VPFE_PIX_LINES				0x10
+#define VPFE_HORZ_INFO				0x14
+#define VPFE_VERT_START				0x18
+#define VPFE_VERT_LINES				0x1c
+#define VPFE_CULLING				0x20
+#define VPFE_HSIZE_OFF				0x24
+#define VPFE_SDOFST				0x28
+#define VPFE_SDR_ADDR				0x2c
+#define VPFE_CLAMP				0x30
+#define VPFE_DCSUB				0x34
+#define VPFE_COLPTN				0x38
+#define VPFE_BLKCMP				0x3c
+#define VPFE_VDINT				0x48
+#define VPFE_ALAW				0x4c
+#define VPFE_REC656IF				0x50
+#define VPFE_CCDCFG				0x54
+#define VPFE_DMA_CNTL				0x98
+#define VPFE_SYSCONFIG				0x104
+#define VPFE_CONFIG				0x108
+#define VPFE_IRQ_EOI				0x110
+#define VPFE_IRQ_STS_RAW			0x114
+#define VPFE_IRQ_STS				0x118
+#define VPFE_IRQ_EN_SET				0x11c
+#define VPFE_IRQ_EN_CLR				0x120
+#define VPFE_REG_END				0x124
+
+/* Define bit fields within selected registers */
+#define VPFE_FID_POL_MASK			1
+#define VPFE_FID_POL_SHIFT			4
+#define VPFE_HD_POL_MASK			1
+#define VPFE_HD_POL_SHIFT			3
+#define VPFE_VD_POL_MASK			1
+#define VPFE_VD_POL_SHIFT			2
+#define VPFE_HSIZE_OFF_MASK			0xffffffe0
+#define VPFE_32BYTE_ALIGN_VAL			31
+#define VPFE_FRM_FMT_MASK			0x1
+#define VPFE_FRM_FMT_SHIFT			7
+#define VPFE_DATA_SZ_MASK			7
+#define VPFE_DATA_SZ_SHIFT			8
+#define VPFE_PIX_FMT_MASK			3
+#define VPFE_PIX_FMT_SHIFT			12
+#define VPFE_VP2SDR_DISABLE			0xfffbffff
+#define VPFE_WEN_ENABLE				(1 << 17)
+#define VPFE_SDR2RSZ_DISABLE			0xfff7ffff
+#define VPFE_VDHDEN_ENABLE			(1 << 16)
+#define VPFE_LPF_ENABLE				(1 << 14)
+#define VPFE_ALAW_ENABLE			(1 << 3)
+#define VPFE_ALAW_GAMMA_WD_MASK			7
+#define VPFE_BLK_CLAMP_ENABLE			(1 << 31)
+#define VPFE_BLK_SGAIN_MASK			0x1f
+#define VPFE_BLK_ST_PXL_MASK			0x7fff
+#define VPFE_BLK_ST_PXL_SHIFT			10
+#define VPFE_BLK_SAMPLE_LN_MASK			7
+#define VPFE_BLK_SAMPLE_LN_SHIFT		28
+#define VPFE_BLK_SAMPLE_LINE_MASK		7
+#define VPFE_BLK_SAMPLE_LINE_SHIFT		25
+#define VPFE_BLK_DC_SUB_MASK			0x03fff
+#define VPFE_BLK_COMP_MASK			0xff
+#define VPFE_BLK_COMP_GB_COMP_SHIFT		8
+#define VPFE_BLK_COMP_GR_COMP_SHIFT		16
+#define VPFE_BLK_COMP_R_COMP_SHIFT		24
+#define VPFE_LATCH_ON_VSYNC_DISABLE		(1 << 15)
+#define VPFE_DATA_PACK_ENABLE			(1 << 11)
+#define VPFE_HORZ_INFO_SPH_SHIFT		16
+#define VPFE_VERT_START_SLV0_SHIFT		16
+#define VPFE_VDINT_VDINT0_SHIFT			16
+#define VPFE_VDINT_VDINT1_MASK			0xffff
+#define VPFE_PPC_RAW				1
+#define VPFE_DCSUB_DEFAULT_VAL			0
+#define VPFE_CLAMP_DEFAULT_VAL			0
+#define VPFE_COLPTN_VAL				0xbb11bb11
+#define VPFE_TWO_BYTES_PER_PIXEL		2
+#define VPFE_INTERLACED_IMAGE_INVERT		0x4b6d
+#define VPFE_INTERLACED_NO_IMAGE_INVERT		0x0249
+#define VPFE_PROGRESSIVE_IMAGE_INVERT		0x4000
+#define VPFE_PROGRESSIVE_NO_IMAGE_INVERT	0
+#define VPFE_INTERLACED_HEIGHT_SHIFT		1
+#define VPFE_SYN_MODE_INPMOD_SHIFT		12
+#define VPFE_SYN_MODE_INPMOD_MASK		3
+#define VPFE_SYN_MODE_8BITS			(7 << 8)
+#define VPFE_SYN_MODE_10BITS			(6 << 8)
+#define VPFE_SYN_MODE_11BITS			(5 << 8)
+#define VPFE_SYN_MODE_12BITS			(4 << 8)
+#define VPFE_SYN_MODE_13BITS			(3 << 8)
+#define VPFE_SYN_MODE_14BITS			(2 << 8)
+#define VPFE_SYN_MODE_15BITS			(1 << 8)
+#define VPFE_SYN_MODE_16BITS			(0 << 8)
+#define VPFE_SYN_FLDMODE_MASK			1
+#define VPFE_SYN_FLDMODE_SHIFT			7
+#define VPFE_REC656IF_BT656_EN			3
+#define VPFE_SYN_MODE_VD_POL_NEGATIVE		(1 << 2)
+#define VPFE_CCDCFG_Y8POS_SHIFT			11
+#define VPFE_CCDCFG_BW656_10BIT			(1 << 5)
+#define VPFE_SDOFST_FIELD_INTERLEAVED		0x249
+#define VPFE_NO_CULLING				0xffff00ff
+#define VPFE_VDINT0				(1 << 0)
+#define VPFE_VDINT1				(1 << 1)
+#define VPFE_VDINT2				(1 << 2)
+#define VPFE_DMA_CNTL_OVERFLOW			(1 << 31)
+
+#define VPFE_CONFIG_PCLK_INV_SHIFT		0
+#define VPFE_CONFIG_PCLK_INV_MASK		1
+#define VPFE_CONFIG_PCLK_INV_NOT_INV		0
+#define VPFE_CONFIG_PCLK_INV_INV		1
+#define VPFE_CONFIG_EN_SHIFT			1
+#define VPFE_CONFIG_EN_MASK			2
+#define VPFE_CONFIG_EN_DISABLE			0
+#define VPFE_CONFIG_EN_ENABLE			1
+#define VPFE_CONFIG_ST_SHIFT			2
+#define VPFE_CONFIG_ST_MASK			4
+#define VPFE_CONFIG_ST_OCP_ACTIVE		0
+#define VPFE_CONFIG_ST_OCP_STANDBY		1
+
+#endif		/* AM437X_VPFE_REGS_H */
diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c
index 8f66986..6a437f8 100644
--- a/drivers/media/platform/blackfin/bfin_capture.c
+++ b/drivers/media/platform/blackfin/bfin_capture.c
@@ -44,7 +44,6 @@
 #include <media/blackfin/ppi.h>
 
 #define CAPTURE_DRV_NAME        "bfin_capture"
-#define BCAP_MIN_NUM_BUF        2
 
 struct bcap_format {
 	char *desc;
@@ -65,7 +64,7 @@
 	/* v4l2 control handler */
 	struct v4l2_ctrl_handler ctrl_handler;
 	/* device node data */
-	struct video_device *video_dev;
+	struct video_device video_dev;
 	/* sub device instance */
 	struct v4l2_subdev *sd;
 	/* capture config */
@@ -104,12 +103,8 @@
 	struct completion comp;
 	/* prepare to stop */
 	bool stop;
-};
-
-struct bcap_fh {
-	struct v4l2_fh fh;
-	/* indicates whether this file handle is doing IO */
-	bool io_allowed;
+	/* vb2 buffer sequence counter */
+	unsigned sequence;
 };
 
 static const struct bcap_format bcap_formats[] = {
@@ -201,90 +196,6 @@
 	bcap_dev->sensor_formats = NULL;
 }
 
-static int bcap_open(struct file *file)
-{
-	struct bcap_device *bcap_dev = video_drvdata(file);
-	struct video_device *vfd = bcap_dev->video_dev;
-	struct bcap_fh *bcap_fh;
-
-	if (!bcap_dev->sd) {
-		v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n");
-		return -ENODEV;
-	}
-
-	bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL);
-	if (!bcap_fh) {
-		v4l2_err(&bcap_dev->v4l2_dev,
-			 "unable to allocate memory for file handle object\n");
-		return -ENOMEM;
-	}
-
-	v4l2_fh_init(&bcap_fh->fh, vfd);
-
-	/* store pointer to v4l2_fh in private_data member of file */
-	file->private_data = &bcap_fh->fh;
-	v4l2_fh_add(&bcap_fh->fh);
-	bcap_fh->io_allowed = false;
-	return 0;
-}
-
-static int bcap_release(struct file *file)
-{
-	struct bcap_device *bcap_dev = video_drvdata(file);
-	struct v4l2_fh *fh = file->private_data;
-	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
-
-	/* if this instance is doing IO */
-	if (bcap_fh->io_allowed)
-		vb2_queue_release(&bcap_dev->buffer_queue);
-
-	file->private_data = NULL;
-	v4l2_fh_del(&bcap_fh->fh);
-	v4l2_fh_exit(&bcap_fh->fh);
-	kfree(bcap_fh);
-	return 0;
-}
-
-static int bcap_mmap(struct file *file, struct vm_area_struct *vma)
-{
-	struct bcap_device *bcap_dev = video_drvdata(file);
-	int ret;
-
-	if (mutex_lock_interruptible(&bcap_dev->mutex))
-		return -ERESTARTSYS;
-	ret = vb2_mmap(&bcap_dev->buffer_queue, vma);
-	mutex_unlock(&bcap_dev->mutex);
-	return ret;
-}
-
-#ifndef CONFIG_MMU
-static unsigned long bcap_get_unmapped_area(struct file *file,
-					    unsigned long addr,
-					    unsigned long len,
-					    unsigned long pgoff,
-					    unsigned long flags)
-{
-	struct bcap_device *bcap_dev = video_drvdata(file);
-
-	return vb2_get_unmapped_area(&bcap_dev->buffer_queue,
-				     addr,
-				     len,
-				     pgoff,
-				     flags);
-}
-#endif
-
-static unsigned int bcap_poll(struct file *file, poll_table *wait)
-{
-	struct bcap_device *bcap_dev = video_drvdata(file);
-	unsigned int res;
-
-	mutex_lock(&bcap_dev->mutex);
-	res = vb2_poll(&bcap_dev->buffer_queue, file, wait);
-	mutex_unlock(&bcap_dev->mutex);
-	return res;
-}
-
 static int bcap_queue_setup(struct vb2_queue *vq,
 				const struct v4l2_format *fmt,
 				unsigned int *nbuffers, unsigned int *nplanes,
@@ -292,37 +203,32 @@
 {
 	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
 
-	if (*nbuffers < BCAP_MIN_NUM_BUF)
-		*nbuffers = BCAP_MIN_NUM_BUF;
+	if (fmt && fmt->fmt.pix.sizeimage < bcap_dev->fmt.sizeimage)
+		return -EINVAL;
+
+	if (vq->num_buffers + *nbuffers < 2)
+		*nbuffers = 2;
 
 	*nplanes = 1;
-	sizes[0] = bcap_dev->fmt.sizeimage;
+	sizes[0] = fmt ? fmt->fmt.pix.sizeimage : bcap_dev->fmt.sizeimage;
 	alloc_ctxs[0] = bcap_dev->alloc_ctx;
 
 	return 0;
 }
 
-static int bcap_buffer_init(struct vb2_buffer *vb)
-{
-	struct bcap_buffer *buf = to_bcap_vb(vb);
-
-	INIT_LIST_HEAD(&buf->list);
-	return 0;
-}
-
 static int bcap_buffer_prepare(struct vb2_buffer *vb)
 {
 	struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
-	struct bcap_buffer *buf = to_bcap_vb(vb);
-	unsigned long size;
+	unsigned long size = bcap_dev->fmt.sizeimage;
 
-	size = bcap_dev->fmt.sizeimage;
 	if (vb2_plane_size(vb, 0) < size) {
 		v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n",
 				vb2_plane_size(vb, 0), size);
 		return -EINVAL;
 	}
-	vb2_set_plane_payload(&buf->vb, 0, size);
+	vb2_set_plane_payload(vb, 0, size);
+
+	vb->v4l2_buf.field = bcap_dev->fmt.field;
 
 	return 0;
 }
@@ -353,14 +259,16 @@
 {
 	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
 	struct ppi_if *ppi = bcap_dev->ppi;
+	struct bcap_buffer *buf, *tmp;
 	struct ppi_params params;
+	dma_addr_t addr;
 	int ret;
 
 	/* enable streamon on the sub device */
 	ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1);
 	if (ret && (ret != -ENOIOCTLCMD)) {
 		v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n");
-		return ret;
+		goto err;
 	}
 
 	/* set ppi params */
@@ -399,7 +307,7 @@
 	if (ret < 0) {
 		v4l2_err(&bcap_dev->v4l2_dev,
 				"Error in setting ppi params\n");
-		return ret;
+		goto err;
 	}
 
 	/* attach ppi DMA irq handler */
@@ -407,12 +315,34 @@
 	if (ret < 0) {
 		v4l2_err(&bcap_dev->v4l2_dev,
 				"Error in attaching interrupt handler\n");
-		return ret;
+		goto err;
 	}
 
+	bcap_dev->sequence = 0;
+
 	reinit_completion(&bcap_dev->comp);
 	bcap_dev->stop = false;
+
+	/* get the next frame from the dma queue */
+	bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next,
+					struct bcap_buffer, list);
+	/* remove buffer from the dma queue */
+	list_del_init(&bcap_dev->cur_frm->list);
+	addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0);
+	/* update DMA address */
+	ppi->ops->update_addr(ppi, (unsigned long)addr);
+	/* enable ppi */
+	ppi->ops->start(ppi);
+
 	return 0;
+
+err:
+	list_for_each_entry_safe(buf, tmp, &bcap_dev->dma_queue, list) {
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+	}
+
+	return ret;
 }
 
 static void bcap_stop_streaming(struct vb2_queue *vq)
@@ -431,6 +361,9 @@
 				"stream off failed in subdev\n");
 
 	/* release all active buffers */
+	if (bcap_dev->cur_frm)
+		vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR);
+
 	while (!list_empty(&bcap_dev->dma_queue)) {
 		bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next,
 						struct bcap_buffer, list);
@@ -441,7 +374,6 @@
 
 static struct vb2_ops bcap_video_qops = {
 	.queue_setup            = bcap_queue_setup,
-	.buf_init               = bcap_buffer_init,
 	.buf_prepare            = bcap_buffer_prepare,
 	.buf_cleanup            = bcap_buffer_cleanup,
 	.buf_queue              = bcap_buffer_queue,
@@ -451,57 +383,6 @@
 	.stop_streaming         = bcap_stop_streaming,
 };
 
-static int bcap_reqbufs(struct file *file, void *priv,
-			struct v4l2_requestbuffers *req_buf)
-{
-	struct bcap_device *bcap_dev = video_drvdata(file);
-	struct vb2_queue *vq = &bcap_dev->buffer_queue;
-	struct v4l2_fh *fh = file->private_data;
-	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
-
-	if (vb2_is_busy(vq))
-		return -EBUSY;
-
-	bcap_fh->io_allowed = true;
-
-	return vb2_reqbufs(vq, req_buf);
-}
-
-static int bcap_querybuf(struct file *file, void *priv,
-				struct v4l2_buffer *buf)
-{
-	struct bcap_device *bcap_dev = video_drvdata(file);
-
-	return vb2_querybuf(&bcap_dev->buffer_queue, buf);
-}
-
-static int bcap_qbuf(struct file *file, void *priv,
-			struct v4l2_buffer *buf)
-{
-	struct bcap_device *bcap_dev = video_drvdata(file);
-	struct v4l2_fh *fh = file->private_data;
-	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
-
-	if (!bcap_fh->io_allowed)
-		return -EBUSY;
-
-	return vb2_qbuf(&bcap_dev->buffer_queue, buf);
-}
-
-static int bcap_dqbuf(struct file *file, void *priv,
-			struct v4l2_buffer *buf)
-{
-	struct bcap_device *bcap_dev = video_drvdata(file);
-	struct v4l2_fh *fh = file->private_data;
-	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
-
-	if (!bcap_fh->io_allowed)
-		return -EBUSY;
-
-	return vb2_dqbuf(&bcap_dev->buffer_queue,
-				buf, file->f_flags & O_NONBLOCK);
-}
-
 static irqreturn_t bcap_isr(int irq, void *dev_id)
 {
 	struct ppi_if *ppi = dev_id;
@@ -517,6 +398,7 @@
 			vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
 			ppi->err = false;
 		} else {
+			vb->v4l2_buf.sequence = bcap_dev->sequence++;
 			vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
 		}
 		bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next,
@@ -543,62 +425,14 @@
 	return IRQ_HANDLED;
 }
 
-static int bcap_streamon(struct file *file, void *priv,
-				enum v4l2_buf_type buf_type)
-{
-	struct bcap_device *bcap_dev = video_drvdata(file);
-	struct bcap_fh *fh = file->private_data;
-	struct ppi_if *ppi = bcap_dev->ppi;
-	dma_addr_t addr;
-	int ret;
-
-	if (!fh->io_allowed)
-		return -EBUSY;
-
-	/* call streamon to start streaming in videobuf */
-	ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type);
-	if (ret)
-		return ret;
-
-	/* if dma queue is empty, return error */
-	if (list_empty(&bcap_dev->dma_queue)) {
-		v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n");
-		ret = -EINVAL;
-		goto err;
-	}
-
-	/* get the next frame from the dma queue */
-	bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next,
-					struct bcap_buffer, list);
-	/* remove buffer from the dma queue */
-	list_del_init(&bcap_dev->cur_frm->list);
-	addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0);
-	/* update DMA address */
-	ppi->ops->update_addr(ppi, (unsigned long)addr);
-	/* enable ppi */
-	ppi->ops->start(ppi);
-
-	return 0;
-err:
-	vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
-	return ret;
-}
-
-static int bcap_streamoff(struct file *file, void *priv,
-				enum v4l2_buf_type buf_type)
-{
-	struct bcap_device *bcap_dev = video_drvdata(file);
-	struct bcap_fh *fh = file->private_data;
-
-	if (!fh->io_allowed)
-		return -EBUSY;
-
-	return vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
-}
-
 static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std)
 {
 	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_input input;
+
+	input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
+	if (!(input.capabilities & V4L2_IN_CAP_STD))
+		return -ENODATA;
 
 	return v4l2_subdev_call(bcap_dev->sd, video, querystd, std);
 }
@@ -606,6 +440,11 @@
 static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std)
 {
 	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_input input;
+
+	input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
+	if (!(input.capabilities & V4L2_IN_CAP_STD))
+		return -ENODATA;
 
 	*std = bcap_dev->std;
 	return 0;
@@ -614,8 +453,13 @@
 static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std)
 {
 	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_input input;
 	int ret;
 
+	input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
+	if (!(input.capabilities & V4L2_IN_CAP_STD))
+		return -ENODATA;
+
 	if (vb2_is_busy(&bcap_dev->buffer_queue))
 		return -EBUSY;
 
@@ -631,6 +475,11 @@
 				struct v4l2_enum_dv_timings *timings)
 {
 	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_input input;
+
+	input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
+	if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS))
+		return -ENODATA;
 
 	timings->pad = 0;
 
@@ -642,6 +491,11 @@
 				struct v4l2_dv_timings *timings)
 {
 	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_input input;
+
+	input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
+	if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS))
+		return -ENODATA;
 
 	return v4l2_subdev_call(bcap_dev->sd, video,
 				query_dv_timings, timings);
@@ -651,6 +505,11 @@
 				struct v4l2_dv_timings *timings)
 {
 	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_input input;
+
+	input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
+	if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS))
+		return -ENODATA;
 
 	*timings = bcap_dev->dv_timings;
 	return 0;
@@ -660,7 +519,13 @@
 				struct v4l2_dv_timings *timings)
 {
 	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_input input;
 	int ret;
+
+	input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
+	if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS))
+		return -ENODATA;
+
 	if (vb2_is_busy(&bcap_dev->buffer_queue))
 		return -EBUSY;
 
@@ -881,12 +746,14 @@
 	.vidioc_g_dv_timings     = bcap_g_dv_timings,
 	.vidioc_query_dv_timings = bcap_query_dv_timings,
 	.vidioc_enum_dv_timings  = bcap_enum_dv_timings,
-	.vidioc_reqbufs          = bcap_reqbufs,
-	.vidioc_querybuf         = bcap_querybuf,
-	.vidioc_qbuf             = bcap_qbuf,
-	.vidioc_dqbuf            = bcap_dqbuf,
-	.vidioc_streamon         = bcap_streamon,
-	.vidioc_streamoff        = bcap_streamoff,
+	.vidioc_reqbufs          = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs      = vb2_ioctl_create_bufs,
+	.vidioc_querybuf         = vb2_ioctl_querybuf,
+	.vidioc_qbuf             = vb2_ioctl_qbuf,
+	.vidioc_dqbuf            = vb2_ioctl_dqbuf,
+	.vidioc_expbuf           = vb2_ioctl_expbuf,
+	.vidioc_streamon         = vb2_ioctl_streamon,
+	.vidioc_streamoff        = vb2_ioctl_streamoff,
 	.vidioc_g_parm           = bcap_g_parm,
 	.vidioc_s_parm           = bcap_s_parm,
 	.vidioc_log_status       = bcap_log_status,
@@ -894,14 +761,14 @@
 
 static struct v4l2_file_operations bcap_fops = {
 	.owner = THIS_MODULE,
-	.open = bcap_open,
-	.release = bcap_release,
+	.open = v4l2_fh_open,
+	.release = vb2_fop_release,
 	.unlocked_ioctl = video_ioctl2,
-	.mmap = bcap_mmap,
+	.mmap = vb2_fop_mmap,
 #ifndef CONFIG_MMU
-	.get_unmapped_area = bcap_get_unmapped_area,
+	.get_unmapped_area = vb2_fop_get_unmapped_area,
 #endif
-	.poll = bcap_poll
+	.poll = vb2_fop_poll
 };
 
 static int bcap_probe(struct platform_device *pdev)
@@ -942,27 +809,20 @@
 		goto err_free_ppi;
 	}
 
-	vfd = video_device_alloc();
-	if (!vfd) {
-		ret = -ENOMEM;
-		v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
-		goto err_cleanup_ctx;
-	}
-
+	vfd = &bcap_dev->video_dev;
 	/* initialize field of video device */
-	vfd->release            = video_device_release;
+	vfd->release            = video_device_release_empty;
 	vfd->fops               = &bcap_fops;
 	vfd->ioctl_ops          = &bcap_ioctl_ops;
 	vfd->tvnorms            = 0;
 	vfd->v4l2_dev           = &bcap_dev->v4l2_dev;
 	strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name));
-	bcap_dev->video_dev     = vfd;
 
 	ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev);
 	if (ret) {
 		v4l2_err(pdev->dev.driver,
 				"Unable to register v4l2 device\n");
-		goto err_release_vdev;
+		goto err_cleanup_ctx;
 	}
 	v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n");
 
@@ -978,13 +838,14 @@
 	/* initialize queue */
 	q = &bcap_dev->buffer_queue;
 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	q->io_modes = VB2_MMAP;
+	q->io_modes = VB2_MMAP | VB2_DMABUF;
 	q->drv_priv = bcap_dev;
 	q->buf_struct_size = sizeof(struct bcap_buffer);
 	q->ops = &bcap_video_qops;
 	q->mem_ops = &vb2_dma_contig_memops;
 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
 	q->lock = &bcap_dev->mutex;
+	q->min_buffers_needed = 1;
 
 	ret = vb2_queue_init(q);
 	if (ret)
@@ -997,15 +858,16 @@
 	INIT_LIST_HEAD(&bcap_dev->dma_queue);
 
 	vfd->lock = &bcap_dev->mutex;
+	vfd->queue = q;
 
 	/* register video device */
-	ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
+	ret = video_register_device(&bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
 	if (ret) {
 		v4l2_err(&bcap_dev->v4l2_dev,
 				"Unable to register video device\n");
 		goto err_free_handler;
 	}
-	video_set_drvdata(bcap_dev->video_dev, bcap_dev);
+	video_set_drvdata(&bcap_dev->video_dev, bcap_dev);
 	v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n",
 			video_device_node_name(vfd));
 
@@ -1083,15 +945,11 @@
 	}
 	return 0;
 err_unreg_vdev:
-	video_unregister_device(bcap_dev->video_dev);
-	bcap_dev->video_dev = NULL;
+	video_unregister_device(&bcap_dev->video_dev);
 err_free_handler:
 	v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
 err_unreg_v4l2:
 	v4l2_device_unregister(&bcap_dev->v4l2_dev);
-err_release_vdev:
-	if (bcap_dev->video_dev)
-		video_device_release(bcap_dev->video_dev);
 err_cleanup_ctx:
 	vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
 err_free_ppi:
@@ -1108,7 +966,7 @@
 						struct bcap_device, v4l2_dev);
 
 	bcap_free_sensor_formats(bcap_dev);
-	video_unregister_device(bcap_dev->video_dev);
+	video_unregister_device(&bcap_dev->video_dev);
 	v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
 	v4l2_device_unregister(v4l2_dev);
 	vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/coda/Makefile
index eee8998..b693914 100644
--- a/drivers/media/platform/coda/Makefile
+++ b/drivers/media/platform/coda/Makefile
@@ -1,3 +1,5 @@
+ccflags-y += -I$(src)
+
 coda-objs := coda-common.o coda-bit.o coda-h264.o coda-jpeg.o
 
 obj-$(CPTCFG_VIDEO_CODA) += coda.o
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index b4029ae..d043007 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -15,6 +15,7 @@
 #include <linux/clk.h>
 #include <linux/irqreturn.h>
 #include <linux/kernel.h>
+#include <linux/log2.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
@@ -29,13 +30,18 @@
 #include <media/videobuf2-vmalloc.h>
 
 #include "coda.h"
+#define CREATE_TRACE_POINTS
+#include "trace.h"
 
+#define CODA_PARA_BUF_SIZE	(10 * 1024)
 #define CODA7_PS_BUF_SIZE	0x28000
 #define CODA9_PS_SAVE_SIZE	(512 * 1024)
 
 #define CODA_DEFAULT_GAMMA	4096
 #define CODA9_DEFAULT_GAMMA	24576	/* 0.75 * 32768 */
 
+static void coda_free_bitstream_buffer(struct coda_ctx *ctx);
+
 static inline int coda_is_initialized(struct coda_dev *dev)
 {
 	return coda_read(dev, CODA_REG_BIT_CUR_PC) != 0;
@@ -84,15 +90,21 @@
 	coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD);
 	coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD);
 
+	trace_coda_bit_run(ctx, cmd);
+
 	coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND);
 }
 
 static int coda_command_sync(struct coda_ctx *ctx, int cmd)
 {
 	struct coda_dev *dev = ctx->dev;
+	int ret;
 
 	coda_command_async(ctx, cmd);
-	return coda_wait_timeout(dev);
+	ret = coda_wait_timeout(dev);
+	trace_coda_bit_done(ctx);
+
+	return ret;
 }
 
 int coda_hw_reset(struct coda_ctx *ctx)
@@ -177,10 +189,6 @@
 	if (n < src_size)
 		return -ENOSPC;
 
-	dma_sync_single_for_device(&ctx->dev->plat_dev->dev,
-				   ctx->bitstream.paddr, ctx->bitstream.size,
-				   DMA_TO_DEVICE);
-
 	src_buf->v4l2_buf.sequence = ctx->qsequence++;
 
 	return 0;
@@ -214,7 +222,7 @@
 	return true;
 }
 
-void coda_fill_bitstream(struct coda_ctx *ctx)
+void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming)
 {
 	struct vb2_buffer *src_buf;
 	struct coda_buffer_meta *meta;
@@ -235,9 +243,12 @@
 		if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG &&
 		    !coda_jpeg_check_buffer(ctx, src_buf)) {
 			v4l2_err(&ctx->dev->v4l2_dev,
-				 "dropping invalid JPEG frame\n");
+				 "dropping invalid JPEG frame %d\n",
+				 ctx->qsequence);
 			src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
-			v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+			v4l2_m2m_buf_done(src_buf, streaming ?
+					  VB2_BUF_STATE_ERROR :
+					  VB2_BUF_STATE_QUEUED);
 			continue;
 		}
 
@@ -262,6 +273,8 @@
 					    ctx->bitstream_fifo.kfifo.mask;
 				list_add_tail(&meta->list,
 					      &ctx->buffer_meta_list);
+
+				trace_coda_bit_queue(ctx, src_buf, meta);
 			}
 
 			v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
@@ -297,6 +310,14 @@
 		p[index ^ 1] = value;
 }
 
+static inline int coda_alloc_context_buf(struct coda_ctx *ctx,
+					 struct coda_aux_buf *buf, size_t size,
+					 const char *name)
+{
+	return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry);
+}
+
+
 static void coda_free_framebuffers(struct coda_ctx *ctx)
 {
 	int i;
@@ -377,6 +398,7 @@
 	coda_free_aux_buf(dev, &ctx->psbuf);
 	if (dev->devtype->product != CODA_DX6)
 		coda_free_aux_buf(dev, &ctx->workbuf);
+	coda_free_aux_buf(dev, &ctx->parabuf);
 }
 
 static int coda_alloc_context_buffers(struct coda_ctx *ctx,
@@ -386,57 +408,42 @@
 	size_t size;
 	int ret;
 
+	if (!ctx->parabuf.vaddr) {
+		ret = coda_alloc_context_buf(ctx, &ctx->parabuf,
+					     CODA_PARA_BUF_SIZE, "parabuf");
+		if (ret < 0)
+			return ret;
+	}
+
 	if (dev->devtype->product == CODA_DX6)
 		return 0;
 
-	if (ctx->psbuf.vaddr) {
-		v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n");
-		return -EBUSY;
-	}
-	if (ctx->slicebuf.vaddr) {
-		v4l2_err(&dev->v4l2_dev, "slicebuf still allocated\n");
-		return -EBUSY;
-	}
-	if (ctx->workbuf.vaddr) {
-		v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n");
-		ret = -EBUSY;
-		return -ENOMEM;
-	}
-
-	if (q_data->fourcc == V4L2_PIX_FMT_H264) {
+	if (!ctx->slicebuf.vaddr && q_data->fourcc == V4L2_PIX_FMT_H264) {
 		/* worst case slice size */
 		size = (DIV_ROUND_UP(q_data->width, 16) *
 			DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512;
 		ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size,
 					     "slicebuf");
-		if (ret < 0) {
-			v4l2_err(&dev->v4l2_dev,
-				 "failed to allocate %d byte slice buffer",
-				 ctx->slicebuf.size);
-			return ret;
-		}
+		if (ret < 0)
+			goto err;
 	}
 
-	if (dev->devtype->product == CODA_7541) {
+	if (!ctx->psbuf.vaddr && dev->devtype->product == CODA_7541) {
 		ret = coda_alloc_context_buf(ctx, &ctx->psbuf,
 					     CODA7_PS_BUF_SIZE, "psbuf");
-		if (ret < 0) {
-			v4l2_err(&dev->v4l2_dev,
-				 "failed to allocate psmem buffer");
+		if (ret < 0)
 			goto err;
-		}
 	}
 
-	size = dev->devtype->workbuf_size;
-	if (dev->devtype->product == CODA_960 &&
-	    q_data->fourcc == V4L2_PIX_FMT_H264)
-		size += CODA9_PS_SAVE_SIZE;
-	ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, "workbuf");
-	if (ret < 0) {
-		v4l2_err(&dev->v4l2_dev,
-			 "failed to allocate %d byte context buffer",
-			 ctx->workbuf.size);
-		goto err;
+	if (!ctx->workbuf.vaddr) {
+		size = dev->devtype->workbuf_size;
+		if (dev->devtype->product == CODA_960 &&
+		    q_data->fourcc == V4L2_PIX_FMT_H264)
+			size += CODA9_PS_SAVE_SIZE;
+		ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size,
+					     "workbuf");
+		if (ret < 0)
+			goto err;
 	}
 
 	return 0;
@@ -709,6 +716,27 @@
  * Encoder context operations
  */
 
+static int coda_encoder_reqbufs(struct coda_ctx *ctx,
+				struct v4l2_requestbuffers *rb)
+{
+	struct coda_q_data *q_data_src;
+	int ret;
+
+	if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		return 0;
+
+	if (rb->count) {
+		q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		ret = coda_alloc_context_buffers(ctx, q_data_src);
+		if (ret < 0)
+			return ret;
+	} else {
+		coda_free_context_buffers(ctx);
+	}
+
+	return 0;
+}
+
 static int coda_start_encoding(struct coda_ctx *ctx)
 {
 	struct coda_dev *dev = ctx->dev;
@@ -718,17 +746,13 @@
 	struct vb2_buffer *buf;
 	int gamma, ret, value;
 	u32 dst_fourcc;
+	int num_fb;
 	u32 stride;
 
 	q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
 	q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
 	dst_fourcc = q_data_dst->fourcc;
 
-	/* Allocate per-instance buffers */
-	ret = coda_alloc_context_buffers(ctx, q_data_src);
-	if (ret < 0)
-		return ret;
-
 	buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
 	bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0);
 	bitstream_size = q_data_dst->sizeimage;
@@ -983,12 +1007,14 @@
 			v4l2_err(v4l2_dev, "failed to allocate framebuffers\n");
 			goto out;
 		}
+		num_fb = 2;
 		stride = q_data_src->bytesperline;
 	} else {
 		ctx->num_internal_frames = 0;
+		num_fb = 0;
 		stride = 0;
 	}
-	coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
+	coda_write(dev, num_fb, CODA_CMD_SET_FRAME_BUF_NUM);
 	coda_write(dev, stride, CODA_CMD_SET_FRAME_BUF_STRIDE);
 
 	if (dev->devtype->product == CODA_7541) {
@@ -1224,6 +1250,8 @@
 		coda_write(dev, ctx->iram_info.axi_sram_use,
 				CODA7_REG_BIT_AXI_SRAM_USE);
 
+	trace_coda_enc_pic_run(ctx, src_buf);
+
 	coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
 
 	return 0;
@@ -1238,6 +1266,8 @@
 	src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
 	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
 
+	trace_coda_enc_pic_done(ctx, dst_buf);
+
 	/* Get results from the coda */
 	start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START);
 	wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
@@ -1308,7 +1338,6 @@
 		ctx->bitstream.vaddr, ctx->bitstream.size);
 
 	coda_free_framebuffers(ctx);
-	coda_free_context_buffers(ctx);
 
 	mutex_unlock(&dev->coda_mutex);
 	mutex_unlock(&ctx->buffer_mutex);
@@ -1316,12 +1345,16 @@
 
 static void coda_bit_release(struct coda_ctx *ctx)
 {
+	mutex_lock(&ctx->buffer_mutex);
 	coda_free_framebuffers(ctx);
 	coda_free_context_buffers(ctx);
+	coda_free_bitstream_buffer(ctx);
+	mutex_unlock(&ctx->buffer_mutex);
 }
 
 const struct coda_context_ops coda_bit_encode_ops = {
 	.queue_init = coda_encoder_queue_init,
+	.reqbufs = coda_encoder_reqbufs,
 	.start_streaming = coda_start_encoding,
 	.prepare_run = coda_prepare_encode,
 	.finish_run = coda_finish_encode,
@@ -1333,6 +1366,65 @@
  * Decoder context operations
  */
 
+static int coda_alloc_bitstream_buffer(struct coda_ctx *ctx,
+				       struct coda_q_data *q_data)
+{
+	if (ctx->bitstream.vaddr)
+		return 0;
+
+	ctx->bitstream.size = roundup_pow_of_two(q_data->sizeimage * 2);
+	ctx->bitstream.vaddr = dma_alloc_writecombine(
+			&ctx->dev->plat_dev->dev, ctx->bitstream.size,
+			&ctx->bitstream.paddr, GFP_KERNEL);
+	if (!ctx->bitstream.vaddr) {
+		v4l2_err(&ctx->dev->v4l2_dev,
+			 "failed to allocate bitstream ringbuffer");
+		return -ENOMEM;
+	}
+	kfifo_init(&ctx->bitstream_fifo,
+		   ctx->bitstream.vaddr, ctx->bitstream.size);
+
+	return 0;
+}
+
+static void coda_free_bitstream_buffer(struct coda_ctx *ctx)
+{
+	if (ctx->bitstream.vaddr == NULL)
+		return;
+
+	dma_free_writecombine(&ctx->dev->plat_dev->dev, ctx->bitstream.size,
+			      ctx->bitstream.vaddr, ctx->bitstream.paddr);
+	ctx->bitstream.vaddr = NULL;
+	kfifo_init(&ctx->bitstream_fifo, NULL, 0);
+}
+
+static int coda_decoder_reqbufs(struct coda_ctx *ctx,
+				struct v4l2_requestbuffers *rb)
+{
+	struct coda_q_data *q_data_src;
+	int ret;
+
+	if (rb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		return 0;
+
+	if (rb->count) {
+		q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		ret = coda_alloc_context_buffers(ctx, q_data_src);
+		if (ret < 0)
+			return ret;
+		ret = coda_alloc_bitstream_buffer(ctx, q_data_src);
+		if (ret < 0) {
+			coda_free_context_buffers(ctx);
+			return ret;
+		}
+	} else {
+		coda_free_bitstream_buffer(ctx);
+		coda_free_context_buffers(ctx);
+	}
+
+	return 0;
+}
+
 static int __coda_start_decoding(struct coda_ctx *ctx)
 {
 	struct coda_q_data *q_data_src, *q_data_dst;
@@ -1351,11 +1443,6 @@
 	src_fourcc = q_data_src->fourcc;
 	dst_fourcc = q_data_dst->fourcc;
 
-	/* Allocate per-instance buffers */
-	ret = coda_alloc_context_buffers(ctx, q_data_src);
-	if (ret < 0)
-		return ret;
-
 	coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
 
 	/* Update coda bitstream read and write pointers from kfifo */
@@ -1431,9 +1518,10 @@
 		height = val & CODA7_PICHEIGHT_MASK;
 	}
 
-	if (width > q_data_dst->width || height > q_data_dst->height) {
+	if (width > q_data_dst->bytesperline || height > q_data_dst->height) {
 		v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n",
-			 width, height, q_data_dst->width, q_data_dst->height);
+			 width, height, q_data_dst->bytesperline,
+			 q_data_dst->height);
 		return -EINVAL;
 	}
 
@@ -1565,6 +1653,7 @@
 	struct vb2_buffer *dst_buf;
 	struct coda_dev *dev = ctx->dev;
 	struct coda_q_data *q_data_dst;
+	struct coda_buffer_meta *meta;
 	u32 reg_addr, reg_stride;
 
 	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
@@ -1572,7 +1661,7 @@
 
 	/* Try to copy source buffer contents into the bitstream ringbuffer */
 	mutex_lock(&ctx->bitstream_mutex);
-	coda_fill_bitstream(ctx);
+	coda_fill_bitstream(ctx, true);
 	mutex_unlock(&ctx->bitstream_mutex);
 
 	if (coda_get_bitstream_payload(ctx) < 512 &&
@@ -1643,12 +1732,12 @@
 		coda_write(dev, ctx->iram_info.axi_sram_use,
 				CODA7_REG_BIT_AXI_SRAM_USE);
 
-	if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) {
-		struct coda_buffer_meta *meta;
+	meta = list_first_entry_or_null(&ctx->buffer_meta_list,
+					struct coda_buffer_meta, list);
+
+	if (meta && ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) {
 
 		/* If this is the last buffer in the bitstream, add padding */
-		meta = list_first_entry(&ctx->buffer_meta_list,
-				      struct coda_buffer_meta, list);
 		if (meta->end == (ctx->bitstream_fifo.kfifo.in &
 				  ctx->bitstream_fifo.kfifo.mask)) {
 			static unsigned char buf[512];
@@ -1665,6 +1754,11 @@
 
 	coda_kfifo_sync_to_device_full(ctx);
 
+	/* Clear decode success flag */
+	coda_write(dev, 0, CODA_RET_DEC_PIC_SUCCESS);
+
+	trace_coda_dec_pic_run(ctx, meta);
+
 	coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
 
 	return 0;
@@ -1694,7 +1788,7 @@
 	 * by up to 512 bytes
 	 */
 	if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) {
-		if (coda_get_bitstream_payload(ctx) >= CODA_MAX_FRAME_SIZE - 512)
+		if (coda_get_bitstream_payload(ctx) >= ctx->bitstream.size - 512)
 			kfifo_init(&ctx->bitstream_fifo,
 				ctx->bitstream.vaddr, ctx->bitstream.size);
 	}
@@ -1821,9 +1915,12 @@
 			memset(&ctx->frame_metas[decoded_idx], 0,
 			       sizeof(struct coda_buffer_meta));
 			ctx->frame_metas[decoded_idx].sequence = val;
+			ctx->sequence_offset++;
 		}
 		mutex_unlock(&ctx->bitstream_mutex);
 
+		trace_coda_dec_pic_done(ctx, &ctx->frame_metas[decoded_idx]);
+
 		val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7;
 		if (val == 0)
 			ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_KEYFRAME;
@@ -1863,6 +1960,8 @@
 		dst_buf->v4l2_buf.timecode = meta->timecode;
 		dst_buf->v4l2_buf.timestamp = meta->timestamp;
 
+		trace_coda_dec_rot_done(ctx, meta, dst_buf);
+
 		switch (q_data_dst->fourcc) {
 		case V4L2_PIX_FMT_YUV420:
 		case V4L2_PIX_FMT_YVU420:
@@ -1895,6 +1994,7 @@
 
 const struct coda_context_ops coda_bit_decode_ops = {
 	.queue_init = coda_decoder_queue_init,
+	.reqbufs = coda_decoder_reqbufs,
 	.start_streaming = coda_start_decoding,
 	.prepare_run = coda_prepare_decode,
 	.finish_run = coda_finish_decode,
@@ -1920,6 +2020,8 @@
 		return IRQ_HANDLED;
 	}
 
+	trace_coda_bit_done(ctx);
+
 	if (ctx->aborting) {
 		v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
 			 "task has been aborted\n");
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index 0813f46..81aa0f2 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -37,6 +37,7 @@
 #include <media/v4l2-mem2mem.h>
 #include <media/videobuf2-core.h>
 #include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
 
 #include "coda.h"
 
@@ -45,7 +46,6 @@
 #define CODADX6_MAX_INSTANCES	4
 #define CODA_MAX_FORMATS	4
 
-#define CODA_PARA_BUF_SIZE	(10 * 1024)
 #define CODA_ISRAM_SIZE	(2048 * 2)
 
 #define MIN_W 176
@@ -180,6 +180,7 @@
 	const char *name;
 	enum coda_inst_type type;
 	const struct coda_context_ops *ops;
+	bool direct;
 	u32 src_formats[CODA_MAX_FORMATS];
 	u32 dst_formats[CODA_MAX_FORMATS];
 };
@@ -468,6 +469,18 @@
 	return 0;
 }
 
+static unsigned int coda_estimate_sizeimage(struct coda_ctx *ctx, u32 sizeimage,
+					    u32 width, u32 height)
+{
+	/*
+	 * This is a rough estimate for sensible compressed buffer
+	 * sizes (between 1 and 16 bits per pixel). This could be
+	 * improved by better format specific worst case estimates.
+	 */
+	return round_up(clamp(sizeimage, width * height / 8,
+					 width * height * 2), PAGE_SIZE);
+}
+
 static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec,
 			struct v4l2_format *f)
 {
@@ -513,15 +526,10 @@
 	case V4L2_PIX_FMT_H264:
 	case V4L2_PIX_FMT_MPEG4:
 		f->fmt.pix.bytesperline = 0;
-		/*
-		 * This is a rough estimate for sensible compressed buffer
-		 * sizes (between 1 and 16 bits per pixel). This could be
-		 * improved by better format specific worst case estimates.
-		 */
-		f->fmt.pix.sizeimage = round_up(clamp(f->fmt.pix.sizeimage,
-				f->fmt.pix.width * f->fmt.pix.height / 8,
-				f->fmt.pix.width * f->fmt.pix.height * 2),
-				PAGE_SIZE);
+		f->fmt.pix.sizeimage = coda_estimate_sizeimage(ctx,
+							f->fmt.pix.sizeimage,
+							f->fmt.pix.width,
+							f->fmt.pix.height);
 		break;
 	default:
 		BUG();
@@ -592,7 +600,11 @@
 	if (ret < 0)
 		return ret;
 
-	if (!f->fmt.pix.colorspace) {
+	switch (f->fmt.pix.colorspace) {
+	case V4L2_COLORSPACE_REC709:
+	case V4L2_COLORSPACE_JPEG:
+		break;
+	default:
 		if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG)
 			f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
 		else
@@ -670,6 +682,7 @@
 
 	ctx->colorspace = f->fmt.pix.colorspace;
 
+	memset(&f_cap, 0, sizeof(f_cap));
 	f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	coda_g_fmt(file, priv, &f_cap);
 	f_cap.fmt.pix.width = f->fmt.pix.width;
@@ -682,6 +695,26 @@
 	return coda_s_fmt(ctx, &f_cap);
 }
 
+static int coda_reqbufs(struct file *file, void *priv,
+			struct v4l2_requestbuffers *rb)
+{
+	struct coda_ctx *ctx = fh_to_ctx(priv);
+	int ret;
+
+	ret = v4l2_m2m_reqbufs(file, ctx->fh.m2m_ctx, rb);
+	if (ret)
+		return ret;
+
+	/*
+	 * Allow to allocate instance specific per-context buffers, such as
+	 * bitstream ringbuffer, slice buffer, work buffer, etc. if needed.
+	 */
+	if (rb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && ctx->ops->reqbufs)
+		return ctx->ops->reqbufs(ctx, rb);
+
+	return 0;
+}
+
 static int coda_qbuf(struct file *file, void *priv,
 		     struct v4l2_buffer *buf)
 {
@@ -827,7 +860,7 @@
 	.vidioc_try_fmt_vid_out	= coda_try_fmt_vid_out,
 	.vidioc_s_fmt_vid_out	= coda_s_fmt_vid_out,
 
-	.vidioc_reqbufs		= v4l2_m2m_ioctl_reqbufs,
+	.vidioc_reqbufs		= coda_reqbufs,
 	.vidioc_querybuf	= v4l2_m2m_ioctl_querybuf,
 
 	.vidioc_qbuf		= coda_qbuf,
@@ -910,7 +943,8 @@
 		ctx->ops->finish_run(ctx);
 	}
 
-	if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out))
+	if ((ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) &&
+	    ctx->ops->seq_end_work)
 		queue_work(dev->workqueue, &ctx->seq_end_work);
 
 	mutex_unlock(&dev->coda_mutex);
@@ -941,15 +975,43 @@
 		return 0;
 	}
 
-	if (ctx->hold ||
-	    ((ctx->inst_type == CODA_INST_DECODER) &&
-	     !v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) &&
-	     (coda_get_bitstream_payload(ctx) < 512) &&
-	     !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
-		v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
-			 "%d: not ready: not enough bitstream data.\n",
-			 ctx->idx);
-		return 0;
+	if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) {
+		struct list_head *meta;
+		bool stream_end;
+		int num_metas;
+		int src_bufs;
+
+		if (ctx->hold && !v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) {
+			v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+				 "%d: not ready: on hold for more buffers.\n",
+				 ctx->idx);
+			return 0;
+		}
+
+		stream_end = ctx->bit_stream_param &
+			     CODA_BIT_STREAM_END_FLAG;
+
+		num_metas = 0;
+		list_for_each(meta, &ctx->buffer_meta_list)
+			num_metas++;
+
+		src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx);
+
+		if (!stream_end && (num_metas + src_bufs) < 2) {
+			v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+				 "%d: not ready: need 2 buffers available (%d, %d)\n",
+				 ctx->idx, num_metas, src_bufs);
+			return 0;
+		}
+
+
+		if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) &&
+		    !stream_end && (coda_get_bitstream_payload(ctx) < 512)) {
+			v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+				 "%d: not ready: not enough bitstream data (%d).\n",
+				 ctx->idx, coda_get_bitstream_payload(ctx));
+			return 0;
+		}
 	}
 
 	if (ctx->aborting) {
@@ -1025,13 +1087,14 @@
 
 static void set_default_params(struct coda_ctx *ctx)
 {
-	unsigned int max_w, max_h, size;
+	unsigned int max_w, max_h, usize, csize;
 
 	ctx->codec = coda_find_codec(ctx->dev, ctx->cvd->src_formats[0],
 				     ctx->cvd->dst_formats[0]);
 	max_w = min(ctx->codec->max_w, 1920U);
 	max_h = min(ctx->codec->max_h, 1088U);
-	size = max_w * max_h * 3 / 2;
+	usize = max_w * max_h * 3 / 2;
+	csize = coda_estimate_sizeimage(ctx, usize, max_w, max_h);
 
 	ctx->params.codec_mode = ctx->codec->mode;
 	ctx->colorspace = V4L2_COLORSPACE_REC709;
@@ -1046,14 +1109,14 @@
 	ctx->q_data[V4L2_M2M_DST].height = max_h;
 	if (ctx->codec->src_fourcc == V4L2_PIX_FMT_YUV420) {
 		ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w;
-		ctx->q_data[V4L2_M2M_SRC].sizeimage = size;
+		ctx->q_data[V4L2_M2M_SRC].sizeimage = usize;
 		ctx->q_data[V4L2_M2M_DST].bytesperline = 0;
-		ctx->q_data[V4L2_M2M_DST].sizeimage = round_up(size, PAGE_SIZE);
+		ctx->q_data[V4L2_M2M_DST].sizeimage = csize;
 	} else {
 		ctx->q_data[V4L2_M2M_SRC].bytesperline = 0;
-		ctx->q_data[V4L2_M2M_SRC].sizeimage = round_up(size, PAGE_SIZE);
+		ctx->q_data[V4L2_M2M_SRC].sizeimage = csize;
 		ctx->q_data[V4L2_M2M_DST].bytesperline = max_w;
-		ctx->q_data[V4L2_M2M_DST].sizeimage = size;
+		ctx->q_data[V4L2_M2M_DST].sizeimage = usize;
 	}
 	ctx->q_data[V4L2_M2M_SRC].rect.width = max_w;
 	ctx->q_data[V4L2_M2M_SRC].rect.height = max_h;
@@ -1082,6 +1145,7 @@
 	*nplanes = 1;
 	sizes[0] = size;
 
+	/* Set to vb2-dma-contig allocator context, ignored by vb2-vmalloc */
 	alloc_ctxs[0] = ctx->dev->alloc_ctx;
 
 	v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
@@ -1111,6 +1175,7 @@
 static void coda_buf_queue(struct vb2_buffer *vb)
 {
 	struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_queue *vq = vb->vb2_queue;
 	struct coda_q_data *q_data;
 
 	q_data = get_q_data(ctx, vb->vb2_queue->type);
@@ -1119,8 +1184,7 @@
 	 * In the decoder case, immediately try to copy the buffer into the
 	 * bitstream ringbuffer and mark it as ready to be dequeued.
 	 */
-	if (ctx->inst_type == CODA_INST_DECODER &&
-	    vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+	if (ctx->bitstream.size && vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
 		/*
 		 * For backwards compatibility, queuing an empty buffer marks
 		 * the stream end
@@ -1130,7 +1194,7 @@
 		mutex_lock(&ctx->bitstream_mutex);
 		v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
 		if (vb2_is_streaming(vb->vb2_queue))
-			coda_fill_bitstream(ctx);
+			coda_fill_bitstream(ctx, true);
 		mutex_unlock(&ctx->bitstream_mutex);
 	} else {
 		v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
@@ -1172,8 +1236,9 @@
 				  buf->vaddr, buf->paddr);
 		buf->vaddr = NULL;
 		buf->size = 0;
+		debugfs_remove(buf->dentry);
+		buf->dentry = NULL;
 	}
-	debugfs_remove(buf->dentry);
 }
 
 static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
@@ -1189,9 +1254,9 @@
 		if (q_data_src->fourcc == V4L2_PIX_FMT_H264 ||
 		    (q_data_src->fourcc == V4L2_PIX_FMT_JPEG &&
 		     ctx->dev->devtype->product == CODA_7541)) {
-			/* copy the buffers that where queued before streamon */
+			/* copy the buffers that were queued before streamon */
 			mutex_lock(&ctx->bitstream_mutex);
-			coda_fill_bitstream(ctx);
+			coda_fill_bitstream(ctx, false);
 			mutex_unlock(&ctx->bitstream_mutex);
 
 			if (coda_get_bitstream_payload(ctx) < 512) {
@@ -1219,12 +1284,23 @@
 	if (!(ctx->streamon_out & ctx->streamon_cap))
 		return 0;
 
+	q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	if ((q_data_src->width != q_data_dst->width &&
+	     round_up(q_data_src->width, 16) != q_data_dst->width) ||
+	    (q_data_src->height != q_data_dst->height &&
+	     round_up(q_data_src->height, 16) != q_data_dst->height)) {
+		v4l2_err(v4l2_dev, "can't convert %dx%d to %dx%d\n",
+			 q_data_src->width, q_data_src->height,
+			 q_data_dst->width, q_data_dst->height);
+		ret = -EINVAL;
+		goto err;
+	}
+
 	/* Allow BIT decoder device_run with no new buffers queued */
-	if (ctx->inst_type == CODA_INST_DECODER)
+	if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit)
 		v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true);
 
 	ctx->gopcounter = ctx->params.gop_size - 1;
-	q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
 
 	ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
 				     q_data_dst->fourcc);
@@ -1265,6 +1341,9 @@
 	struct coda_ctx *ctx = vb2_get_drv_priv(q);
 	struct coda_dev *dev = ctx->dev;
 	struct vb2_buffer *buf;
+	bool stop;
+
+	stop = ctx->streamon_out && ctx->streamon_cap;
 
 	if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
 		v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
@@ -1273,7 +1352,7 @@
 
 		coda_bit_stream_end_flag(ctx);
 
-		ctx->isequence = 0;
+		ctx->qsequence = 0;
 
 		while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
 			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
@@ -1289,9 +1368,13 @@
 			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
 	}
 
-	if (!ctx->streamon_out && !ctx->streamon_cap) {
+	if (stop) {
 		struct coda_buffer_meta *meta;
 
+		if (ctx->ops->seq_end_work) {
+			queue_work(dev->workqueue, &ctx->seq_end_work);
+			flush_work(&ctx->seq_end_work);
+		}
 		mutex_lock(&ctx->bitstream_mutex);
 		while (!list_empty(&ctx->buffer_meta_list)) {
 			meta = list_first_entry(&ctx->buffer_meta_list,
@@ -1302,6 +1385,7 @@
 		mutex_unlock(&ctx->bitstream_mutex);
 		kfifo_init(&ctx->bitstream_fifo,
 			ctx->bitstream.vaddr, ctx->bitstream.size);
+		ctx->initialized = 0;
 		ctx->runcounter = 0;
 		ctx->aborting = 0;
 	}
@@ -1409,7 +1493,7 @@
 static void coda_encode_ctrls(struct coda_ctx *ctx)
 {
 	v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
-		V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0);
+		V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1000, 0);
 	v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
 		V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16);
 	v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
@@ -1493,6 +1577,13 @@
 	vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 	vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
 	vq->lock = &ctx->dev->dev_mutex;
+	/* One way to indicate end-of-stream for coda is to set the
+	 * bytesused == 0. However by default videobuf2 handles bytesused
+	 * equal to 0 as a special case and changes its value to the size
+	 * of the buffer. Set the allow_zero_bytesused flag, so
+	 * that videobuf2 will keep the value of bytesused intact.
+	 */
+	vq->allow_zero_bytesused = 1;
 
 	return vb2_queue_init(vq);
 }
@@ -1523,8 +1614,8 @@
 	int ret;
 
 	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-	src_vq->io_modes = VB2_DMABUF | VB2_MMAP;
-	src_vq->mem_ops = &vb2_dma_contig_memops;
+	src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
+	src_vq->mem_ops = &vb2_vmalloc_memops;
 
 	ret = coda_queue_init(priv, src_vq);
 	if (ret)
@@ -1573,15 +1664,22 @@
 	set_bit(idx, &dev->instance_mask);
 
 	name = kasprintf(GFP_KERNEL, "context%d", idx);
+	if (!name) {
+		ret = -ENOMEM;
+		goto err_coda_name_init;
+	}
+
 	ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root);
 	kfree(name);
 
 	ctx->cvd = to_coda_video_device(vdev);
 	ctx->inst_type = ctx->cvd->type;
 	ctx->ops = ctx->cvd->ops;
+	ctx->use_bit = !ctx->cvd->direct;
 	init_completion(&ctx->completion);
 	INIT_WORK(&ctx->pic_run_work, coda_pic_run_work);
-	INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work);
+	if (ctx->ops->seq_end_work)
+		INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work);
 	v4l2_fh_init(&ctx->fh, video_devdata(file));
 	file->private_data = &ctx->fh;
 	v4l2_fh_add(&ctx->fh);
@@ -1632,25 +1730,6 @@
 
 	ctx->fh.ctrl_handler = &ctx->ctrls;
 
-	ret = coda_alloc_context_buf(ctx, &ctx->parabuf,
-				     CODA_PARA_BUF_SIZE, "parabuf");
-	if (ret < 0) {
-		v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf");
-		goto err_dma_alloc;
-	}
-
-	ctx->bitstream.size = CODA_MAX_FRAME_SIZE;
-	ctx->bitstream.vaddr = dma_alloc_writecombine(
-			&dev->plat_dev->dev, ctx->bitstream.size,
-			&ctx->bitstream.paddr, GFP_KERNEL);
-	if (!ctx->bitstream.vaddr) {
-		v4l2_err(&dev->v4l2_dev,
-			 "failed to allocate bitstream ringbuffer");
-		ret = -ENOMEM;
-		goto err_dma_writecombine;
-	}
-	kfifo_init(&ctx->bitstream_fifo,
-		ctx->bitstream.vaddr, ctx->bitstream.size);
 	mutex_init(&ctx->bitstream_mutex);
 	mutex_init(&ctx->buffer_mutex);
 	INIT_LIST_HEAD(&ctx->buffer_meta_list);
@@ -1664,12 +1743,6 @@
 
 	return 0;
 
-err_dma_writecombine:
-	if (ctx->dev->devtype->product == CODA_DX6)
-		coda_free_aux_buf(dev, &ctx->workbuf);
-	coda_free_aux_buf(dev, &ctx->parabuf);
-err_dma_alloc:
-	v4l2_ctrl_handler_free(&ctx->ctrls);
 err_ctrls_setup:
 	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
 err_ctx_init:
@@ -1682,6 +1755,7 @@
 	v4l2_fh_del(&ctx->fh);
 	v4l2_fh_exit(&ctx->fh);
 	clear_bit(ctx->idx, &dev->instance_mask);
+err_coda_name_init:
 err_coda_max:
 	kfree(ctx);
 	return ret;
@@ -1695,16 +1769,14 @@
 	v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n",
 		 ctx);
 
-	debugfs_remove_recursive(ctx->debugfs_entry);
-
-	if (ctx->inst_type == CODA_INST_DECODER)
+	if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit)
 		coda_bit_stream_end_flag(ctx);
 
 	/* If this instance is running, call .job_abort and wait for it to end */
 	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
 
 	/* In case the instance was not running, we still need to call SEQ_END */
-	if (ctx->initialized) {
+	if (ctx->initialized && ctx->ops->seq_end_work) {
 		queue_work(dev->workqueue, &ctx->seq_end_work);
 		flush_work(&ctx->seq_end_work);
 	}
@@ -1713,14 +1785,9 @@
 	list_del(&ctx->list);
 	coda_unlock(ctx);
 
-	if (ctx->bitstream.vaddr) {
-		dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size,
-			ctx->bitstream.vaddr, ctx->bitstream.paddr);
-	}
 	if (ctx->dev->devtype->product == CODA_DX6)
 		coda_free_aux_buf(dev, &ctx->workbuf);
 
-	coda_free_aux_buf(dev, &ctx->parabuf);
 	v4l2_ctrl_handler_free(&ctx->ctrls);
 	clk_disable_unprepare(dev->clk_ahb);
 	clk_disable_unprepare(dev->clk_per);
@@ -1730,6 +1797,7 @@
 	clear_bit(ctx->idx, &dev->instance_mask);
 	if (ctx->ops->release)
 		ctx->ops->release(ctx);
+	debugfs_remove_recursive(ctx->debugfs_entry);
 	kfree(ctx);
 
 	return 0;
@@ -1846,10 +1914,10 @@
 {
 	struct video_device *vfd = &dev->vfd[i];
 
-	if (i > ARRAY_SIZE(dev->vfd))
+	if (i >= dev->devtype->num_vdevs)
 		return -EINVAL;
 
-	snprintf(vfd->name, sizeof(vfd->name), dev->devtype->vdevs[i]->name);
+	strlcpy(vfd->name, dev->devtype->vdevs[i]->name, sizeof(vfd->name));
 	vfd->fops	= &coda_fops;
 	vfd->ioctl_ops	= &coda_ioctl_ops;
 	vfd->release	= video_device_release_empty,
@@ -1880,10 +1948,8 @@
 	/* allocate auxiliary per-device code buffer for the BIT processor */
 	ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf",
 				 dev->debugfs_root);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to allocate code buffer\n");
+	if (ret < 0)
 		goto put_pm;
-	}
 
 	/* Copy the whole firmware image to the code buffer */
 	memcpy(dev->codebuf.vaddr, fw->data, fw->size);
@@ -2003,7 +2069,6 @@
 
 static struct platform_device_id coda_platform_ids[] = {
 	{ .name = "coda-imx27", .driver_data = CODA_IMX27 },
-	{ .name = "coda-imx53", .driver_data = CODA_IMX53 },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(platform, coda_platform_ids);
@@ -2122,20 +2187,16 @@
 		ret = coda_alloc_aux_buf(dev, &dev->workbuf,
 					 dev->devtype->workbuf_size, "workbuf",
 					 dev->debugfs_root);
-		if (ret < 0) {
-			dev_err(&pdev->dev, "failed to allocate work buffer\n");
+		if (ret < 0)
 			goto err_v4l2_register;
-		}
 	}
 
 	if (dev->devtype->tempbuf_size) {
 		ret = coda_alloc_aux_buf(dev, &dev->tempbuf,
 					 dev->devtype->tempbuf_size, "tempbuf",
 					 dev->debugfs_root);
-		if (ret < 0) {
-			dev_err(&pdev->dev, "failed to allocate temp buffer\n");
+		if (ret < 0)
 			goto err_v4l2_register;
-		}
 	}
 
 	dev->iram.size = dev->devtype->iram_size;
@@ -2144,6 +2205,7 @@
 	if (!dev->iram.vaddr) {
 		dev_warn(&pdev->dev, "unable to alloc iram\n");
 	} else {
+		memset(dev->iram.vaddr, 0, dev->iram.size);
 		dev->iram.blob.data = dev->iram.vaddr;
 		dev->iram.blob.size = dev->iram.size;
 		dev->iram.dentry = debugfs_create_blob("iram", 0644,
diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/coda/coda-jpeg.c
index 8fa3e35..11e734b 100644
--- a/drivers/media/platform/coda/coda-jpeg.c
+++ b/drivers/media/platform/coda/coda-jpeg.c
@@ -13,6 +13,7 @@
 #include <linux/swab.h>
 
 #include "coda.h"
+#include "trace.h"
 
 #define SOI_MARKER	0xffd8
 #define EOI_MARKER	0xffd9
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
index 5dd47e5..6a5c8f6 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/coda/coda.h
@@ -12,6 +12,9 @@
  * (at your option) any later version.
  */
 
+#ifndef __CODA_H__
+#define __CODA_H__
+
 #include <linux/debugfs.h>
 #include <linux/irqreturn.h>
 #include <linux/mutex.h>
@@ -26,7 +29,6 @@
 #include "coda_regs.h"
 
 #define CODA_MAX_FRAMEBUFFERS	8
-#define CODA_MAX_FRAME_SIZE	0x100000
 #define FMO_SLICE_SAVE_BUF_SIZE	(32)
 
 enum {
@@ -178,6 +180,7 @@
 struct coda_context_ops {
 	int (*queue_init)(void *priv, struct vb2_queue *src_vq,
 			  struct vb2_queue *dst_vq);
+	int (*reqbufs)(struct coda_ctx *ctx, struct v4l2_requestbuffers *rb);
 	int (*start_streaming)(struct coda_ctx *ctx);
 	int (*prepare_run)(struct coda_ctx *ctx);
 	void (*finish_run)(struct coda_ctx *ctx);
@@ -198,7 +201,6 @@
 	int				initialized;
 	int				streamon_out;
 	int				streamon_cap;
-	u32				isequence;
 	u32				qsequence;
 	u32				osequence;
 	u32				sequence_offset;
@@ -236,6 +238,7 @@
 	u32				frame_mem_ctrl;
 	int				display_idx;
 	struct dentry			*debugfs_entry;
+	bool				use_bit;
 };
 
 extern int coda_debug;
@@ -249,13 +252,6 @@
 		       size_t size, const char *name, struct dentry *parent);
 void coda_free_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf);
 
-static inline int coda_alloc_context_buf(struct coda_ctx *ctx,
-					 struct coda_aux_buf *buf, size_t size,
-					 const char *name)
-{
-	return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry);
-}
-
 int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq,
 			    struct vb2_queue *dst_vq);
 int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq,
@@ -263,7 +259,7 @@
 
 int coda_hw_reset(struct coda_ctx *ctx);
 
-void coda_fill_bitstream(struct coda_ctx *ctx);
+void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming);
 
 void coda_set_gdi_regs(struct coda_ctx *ctx);
 
@@ -284,7 +280,7 @@
 
 int coda_check_firmware(struct coda_dev *dev);
 
-static inline int coda_get_bitstream_payload(struct coda_ctx *ctx)
+static inline unsigned int coda_get_bitstream_payload(struct coda_ctx *ctx)
 {
 	return kfifo_len(&ctx->bitstream_fifo);
 }
@@ -301,3 +297,5 @@
 extern const struct coda_context_ops coda_bit_decode_ops;
 
 irqreturn_t coda_irq_handler(int irq, void *data);
+
+#endif /* __CODA_H__ */
diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h
index 8e015b8..7d02624 100644
--- a/drivers/media/platform/coda/coda_regs.h
+++ b/drivers/media/platform/coda/coda_regs.h
@@ -304,9 +304,9 @@
 #define		CODA_RATECONTROL_AUTOSKIP_OFFSET		31
 #define		CODA_RATECONTROL_AUTOSKIP_MASK			0x01
 #define		CODA_RATECONTROL_INITIALDELAY_OFFSET		16
-#define		CODA_RATECONTROL_INITIALDELAY_MASK		0x7f
+#define		CODA_RATECONTROL_INITIALDELAY_MASK		0x7fff
 #define		CODA_RATECONTROL_BITRATE_OFFSET		1
-#define		CODA_RATECONTROL_BITRATE_MASK			0x7f
+#define		CODA_RATECONTROL_BITRATE_MASK			0x7fff
 #define		CODA_RATECONTROL_ENABLE_OFFSET			0
 #define		CODA_RATECONTROL_ENABLE_MASK			0x01
 #define CODA_CMD_ENC_SEQ_RC_BUF_SIZE				0x1b0
diff --git a/drivers/media/platform/coda/trace.h b/drivers/media/platform/coda/trace.h
new file mode 100644
index 0000000..d1d06cb
--- /dev/null
+++ b/drivers/media/platform/coda/trace.h
@@ -0,0 +1,203 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM coda
+
+#if !defined(__CODA_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ)
+#define __CODA_TRACE_H__
+
+#include <linux/tracepoint.h>
+#include <media/videobuf2-core.h>
+
+#include "coda.h"
+
+#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM)
+
+TRACE_EVENT(coda_bit_run,
+	TP_PROTO(struct coda_ctx *ctx, int cmd),
+
+	TP_ARGS(ctx, cmd),
+
+	TP_STRUCT__entry(
+		__field(int, minor)
+		__field(int, ctx)
+		__field(int, cmd)
+	),
+
+	TP_fast_assign(
+		__entry->minor = ctx->fh.vdev->minor;
+		__entry->ctx = ctx->idx;
+		__entry->cmd = cmd;
+	),
+
+	TP_printk("minor = %d, ctx = %d, cmd = %d",
+		  __entry->minor, __entry->ctx, __entry->cmd)
+);
+
+TRACE_EVENT(coda_bit_done,
+	TP_PROTO(struct coda_ctx *ctx),
+
+	TP_ARGS(ctx),
+
+	TP_STRUCT__entry(
+		__field(int, minor)
+		__field(int, ctx)
+	),
+
+	TP_fast_assign(
+		__entry->minor = ctx->fh.vdev->minor;
+		__entry->ctx = ctx->idx;
+	),
+
+	TP_printk("minor = %d, ctx = %d", __entry->minor, __entry->ctx)
+);
+
+TRACE_EVENT(coda_enc_pic_run,
+	TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf),
+
+	TP_ARGS(ctx, buf),
+
+	TP_STRUCT__entry(
+		__field(int, minor)
+		__field(int, index)
+		__field(int, ctx)
+	),
+
+	TP_fast_assign(
+		__entry->minor = ctx->fh.vdev->minor;
+		__entry->index = buf->v4l2_buf.index;
+		__entry->ctx = ctx->idx;
+	),
+
+	TP_printk("minor = %d, index = %d, ctx = %d",
+		  __entry->minor, __entry->index, __entry->ctx)
+);
+
+TRACE_EVENT(coda_enc_pic_done,
+	TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf),
+
+	TP_ARGS(ctx, buf),
+
+	TP_STRUCT__entry(
+		__field(int, minor)
+		__field(int, index)
+		__field(int, ctx)
+	),
+
+	TP_fast_assign(
+		__entry->minor = ctx->fh.vdev->minor;
+		__entry->index = buf->v4l2_buf.index;
+		__entry->ctx = ctx->idx;
+	),
+
+	TP_printk("minor = %d, index = %d, ctx = %d",
+		  __entry->minor, __entry->index, __entry->ctx)
+);
+
+TRACE_EVENT(coda_bit_queue,
+	TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf,
+		 struct coda_buffer_meta *meta),
+
+	TP_ARGS(ctx, buf, meta),
+
+	TP_STRUCT__entry(
+		__field(int, minor)
+		__field(int, index)
+		__field(int, start)
+		__field(int, end)
+		__field(int, ctx)
+	),
+
+	TP_fast_assign(
+		__entry->minor = ctx->fh.vdev->minor;
+		__entry->index = buf->v4l2_buf.index;
+		__entry->start = meta->start;
+		__entry->end = meta->end;
+		__entry->ctx = ctx->idx;
+	),
+
+	TP_printk("minor = %d, index = %d, start = 0x%x, end = 0x%x, ctx = %d",
+		  __entry->minor, __entry->index, __entry->start, __entry->end,
+		  __entry->ctx)
+);
+
+TRACE_EVENT(coda_dec_pic_run,
+	TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta),
+
+	TP_ARGS(ctx, meta),
+
+	TP_STRUCT__entry(
+		__field(int, minor)
+		__field(int, start)
+		__field(int, end)
+		__field(int, ctx)
+	),
+
+	TP_fast_assign(
+		__entry->minor = ctx->fh.vdev->minor;
+		__entry->start = meta ? meta->start : 0;
+		__entry->end = meta ? meta->end : 0;
+		__entry->ctx = ctx->idx;
+	),
+
+	TP_printk("minor = %d, start = 0x%x, end = 0x%x, ctx = %d",
+		  __entry->minor, __entry->start, __entry->end, __entry->ctx)
+);
+
+TRACE_EVENT(coda_dec_pic_done,
+	TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta),
+
+	TP_ARGS(ctx, meta),
+
+	TP_STRUCT__entry(
+		__field(int, minor)
+		__field(int, start)
+		__field(int, end)
+		__field(int, ctx)
+	),
+
+	TP_fast_assign(
+		__entry->minor = ctx->fh.vdev->minor;
+		__entry->start = meta->start;
+		__entry->end = meta->end;
+		__entry->ctx = ctx->idx;
+	),
+
+	TP_printk("minor = %d, start = 0x%x, end = 0x%x, ctx = %d",
+		  __entry->minor, __entry->start, __entry->end, __entry->ctx)
+);
+
+TRACE_EVENT(coda_dec_rot_done,
+	TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta,
+		 struct vb2_buffer *buf),
+
+	TP_ARGS(ctx, meta, buf),
+
+	TP_STRUCT__entry(
+		__field(int, minor)
+		__field(int, start)
+		__field(int, end)
+		__field(int, index)
+		__field(int, ctx)
+	),
+
+	TP_fast_assign(
+		__entry->minor = ctx->fh.vdev->minor;
+		__entry->start = meta->start;
+		__entry->end = meta->end;
+		__entry->index = buf->v4l2_buf.index;
+		__entry->ctx = ctx->idx;
+	),
+
+	TP_printk("minor = %d, start = 0x%x, end = 0x%x, index = %d, ctx = %d",
+		  __entry->minor, __entry->start, __entry->end, __entry->index,
+		  __entry->ctx)
+);
+
+#endif /* __CODA_TRACE_H__ */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig
index 6434baf..8b9aa34 100644
--- a/drivers/media/platform/davinci/Kconfig
+++ b/drivers/media/platform/davinci/Kconfig
@@ -1,7 +1,7 @@
 config VIDEO_DAVINCI_VPIF_DISPLAY
 	tristate "TI DaVinci VPIF V4L2-Display driver"
 	depends on m
-	depends on VIDEO_DEV
+	depends on VIDEO_V4L2
 	depends on ARCH_DAVINCI || COMPILE_TEST
 	depends on HAS_DMA
 	select VIDEOBUF2_DMA_CONTIG
@@ -18,7 +18,7 @@
 config VIDEO_DAVINCI_VPIF_CAPTURE
 	tristate "TI DaVinci VPIF video capture driver"
 	depends on m
-	depends on VIDEO_DEV
+	depends on VIDEO_V4L2
 	depends on ARCH_DAVINCI || COMPILE_TEST
 	depends on HAS_DMA
 	select VIDEOBUF2_DMA_CONTIG
@@ -81,7 +81,7 @@
 config VIDEO_DAVINCI_VPBE_DISPLAY
 	tristate "TI DaVinci VPBE V4L2-Display driver"
 	depends on m
-	depends on ARCH_DAVINCI
+	depends on VIDEO_V4L2 && ARCH_DAVINCI
 	depends on HAS_DMA
 	select VIDEOBUF2_DMA_CONTIG
 	help
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index c4ab46f..cc80cb9 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -1249,7 +1249,9 @@
 	.vidioc_dqbuf		 = vb2_ioctl_dqbuf,
 	.vidioc_streamon	 = vb2_ioctl_streamon,
 	.vidioc_streamoff	 = vb2_ioctl_streamoff,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
 	.vidioc_expbuf		 = vb2_ioctl_expbuf,
+#endif
 
 	.vidioc_cropcap		 = vpbe_display_cropcap,
 	.vidioc_g_crop		 = vpbe_display_g_crop,
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index b41bf7e..ccfcf3f 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -1871,16 +1871,9 @@
 		goto probe_free_ccdc_cfg_mem;
 	}
 
-	/* Allocate memory for video device */
-	vfd = video_device_alloc();
-	if (NULL == vfd) {
-		ret = -ENOMEM;
-		v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
-		goto probe_out_release_irq;
-	}
-
+	vfd = &vpfe_dev->video_dev;
 	/* Initialize field of video device */
-	vfd->release		= video_device_release;
+	vfd->release		= video_device_release_empty;
 	vfd->fops		= &vpfe_fops;
 	vfd->ioctl_ops		= &vpfe_ioctl_ops;
 	vfd->tvnorms		= 0;
@@ -1891,14 +1884,12 @@
 		 (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff,
 		 (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff,
 		 (VPFE_CAPTURE_VERSION_CODE) & 0xff);
-	/* Set video_dev to the video device */
-	vpfe_dev->video_dev	= vfd;
 
 	ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev);
 	if (ret) {
 		v4l2_err(pdev->dev.driver,
 			"Unable to register v4l2 device.\n");
-		goto probe_out_video_release;
+		goto probe_out_release_irq;
 	}
 	v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n");
 	spin_lock_init(&vpfe_dev->irqlock);
@@ -1914,7 +1905,7 @@
 	v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
 		"video_dev=%p\n", &vpfe_dev->video_dev);
 	vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	ret = video_register_device(vpfe_dev->video_dev,
+	ret = video_register_device(&vpfe_dev->video_dev,
 				    VFL_TYPE_GRABBER, -1);
 
 	if (ret) {
@@ -1927,7 +1918,7 @@
 	/* set the driver data in platform device */
 	platform_set_drvdata(pdev, vpfe_dev);
 	/* set driver private data */
-	video_set_drvdata(vpfe_dev->video_dev, vpfe_dev);
+	video_set_drvdata(&vpfe_dev->video_dev, vpfe_dev);
 	i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id);
 	num_subdevs = vpfe_cfg->num_subdevs;
 	vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs,
@@ -1979,12 +1970,9 @@
 probe_sd_out:
 	kfree(vpfe_dev->sd);
 probe_out_video_unregister:
-	video_unregister_device(vpfe_dev->video_dev);
+	video_unregister_device(&vpfe_dev->video_dev);
 probe_out_v4l2_unregister:
 	v4l2_device_unregister(&vpfe_dev->v4l2_dev);
-probe_out_video_release:
-	if (!video_is_registered(vpfe_dev->video_dev))
-		video_device_release(vpfe_dev->video_dev);
 probe_out_release_irq:
 	free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
 probe_free_ccdc_cfg_mem:
@@ -2007,7 +1995,7 @@
 	free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
 	kfree(vpfe_dev->sd);
 	v4l2_device_unregister(&vpfe_dev->v4l2_dev);
-	video_unregister_device(vpfe_dev->video_dev);
+	video_unregister_device(&vpfe_dev->video_dev);
 	kfree(vpfe_dev);
 	kfree(ccdc_cfg);
 	return 0;
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index fa0a515..ea516a7 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -712,7 +712,7 @@
 	ch->vpifparams.iface = chan_cfg->vpif_if;
 
 	/* update tvnorms from the sub device input info */
-	ch->video_dev->tvnorms = chan_cfg->inputs[index].input.std;
+	ch->video_dev.tvnorms = chan_cfg->inputs[index].input.std;
 	return 0;
 }
 
@@ -1260,7 +1260,9 @@
 	.vidioc_querybuf		= vb2_ioctl_querybuf,
 	.vidioc_qbuf			= vb2_ioctl_qbuf,
 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
 	.vidioc_expbuf			= vb2_ioctl_expbuf,
+#endif
 	.vidioc_streamon		= vb2_ioctl_streamon,
 	.vidioc_streamoff		= vb2_ioctl_streamoff,
 
@@ -1337,7 +1339,7 @@
 	struct video_device *vdev;
 	struct channel_obj *ch;
 	struct vb2_queue *q;
-	int i, j, err, k;
+	int j, err, k;
 
 	for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
 		ch = vpif_obj.dev[j];
@@ -1384,16 +1386,16 @@
 		INIT_LIST_HEAD(&common->dma_queue);
 
 		/* Initialize the video_device structure */
-		vdev = ch->video_dev;
+		vdev = &ch->video_dev;
 		strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name));
-		vdev->release = video_device_release;
+		vdev->release = video_device_release_empty;
 		vdev->fops = &vpif_fops;
 		vdev->ioctl_ops = &vpif_ioctl_ops;
 		vdev->v4l2_dev = &vpif_obj.v4l2_dev;
 		vdev->vfl_dir = VFL_DIR_RX;
 		vdev->queue = q;
 		vdev->lock = &common->lock;
-		video_set_drvdata(ch->video_dev, ch);
+		video_set_drvdata(&ch->video_dev, ch);
 		err = video_register_device(vdev,
 					    VFL_TYPE_GRABBER, (j ? 1 : 0));
 		if (err)
@@ -1410,14 +1412,9 @@
 		common = &ch->common[k];
 		vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
 		/* Unregister video device */
-		video_unregister_device(ch->video_dev);
+		video_unregister_device(&ch->video_dev);
 	}
 	kfree(vpif_obj.sd);
-	for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
-		ch = vpif_obj.dev[i];
-		/* Note: does nothing if ch->video_dev == NULL */
-		video_device_release(ch->video_dev);
-	}
 	v4l2_device_unregister(&vpif_obj.v4l2_dev);
 
 	return err;
@@ -1438,13 +1435,11 @@
 static __init int vpif_probe(struct platform_device *pdev)
 {
 	struct vpif_subdev_info *subdevdata;
-	int i, j, err;
-	int res_idx = 0;
 	struct i2c_adapter *i2c_adap;
-	struct channel_obj *ch;
-	struct video_device *vfd;
 	struct resource *res;
 	int subdev_count;
+	int res_idx = 0;
+	int i, err;
 
 	vpif_dev = &pdev->dev;
 
@@ -1472,24 +1467,6 @@
 		res_idx++;
 	}
 
-	for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
-		/* Get the pointer to the channel object */
-		ch = vpif_obj.dev[i];
-		/* Allocate memory for video device */
-		vfd = video_device_alloc();
-		if (NULL == vfd) {
-			for (j = 0; j < i; j++) {
-				ch = vpif_obj.dev[j];
-				video_device_release(ch->video_dev);
-			}
-			err = -ENOMEM;
-			goto vpif_unregister;
-		}
-
-		/* Set video_dev to the video device */
-		ch->video_dev = vfd;
-	}
-
 	vpif_obj.config = pdev->dev.platform_data;
 
 	subdev_count = vpif_obj.config->subdev_count;
@@ -1498,7 +1475,7 @@
 	if (vpif_obj.sd == NULL) {
 		vpif_err("unable to allocate memory for subdevice pointers\n");
 		err = -ENOMEM;
-		goto vpif_sd_error;
+		goto vpif_unregister;
 	}
 
 	if (!vpif_obj.config->asd_sizes) {
@@ -1541,13 +1518,6 @@
 probe_subdev_out:
 	/* free sub devices memory */
 	kfree(vpif_obj.sd);
-
-vpif_sd_error:
-	for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
-		ch = vpif_obj.dev[i];
-		/* Note: does nothing if ch->video_dev == NULL */
-		video_device_release(ch->video_dev);
-	}
 vpif_unregister:
 	v4l2_device_unregister(&vpif_obj.v4l2_dev);
 
@@ -1576,7 +1546,7 @@
 		common = &ch->common[VPIF_VIDEO_INDEX];
 		vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
 		/* Unregister video device */
-		video_unregister_device(ch->video_dev);
+		video_unregister_device(&ch->video_dev);
 		kfree(vpif_obj.dev[i]);
 	}
 	return 0;
diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h
index f65d28d..8b8a663 100644
--- a/drivers/media/platform/davinci/vpif_capture.h
+++ b/drivers/media/platform/davinci/vpif_capture.h
@@ -92,7 +92,7 @@
 
 struct channel_obj {
 	/* Identifies video device for this channel */
-	struct video_device *video_dev;
+	struct video_device video_dev;
 	/* Indicates id of the field which is being displayed */
 	u32 field_id;
 	/* flag to indicate whether decoder is initialized */
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index 839c24d..fd0f72c 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -829,7 +829,7 @@
 	ch->sd = sd;
 	if (chan_cfg->outputs != NULL)
 		/* update tvnorms from the sub device output info */
-		ch->video_dev->tvnorms = chan_cfg->outputs[index].output.std;
+		ch->video_dev.tvnorms = chan_cfg->outputs[index].output.std;
 	return 0;
 }
 
@@ -1059,7 +1059,9 @@
 	.vidioc_querybuf		= vb2_ioctl_querybuf,
 	.vidioc_qbuf			= vb2_ioctl_qbuf,
 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
 	.vidioc_expbuf			= vb2_ioctl_expbuf,
+#endif
 	.vidioc_streamon		= vb2_ioctl_streamon,
 	.vidioc_streamoff		= vb2_ioctl_streamoff,
 
@@ -1204,16 +1206,16 @@
 			 ch, &ch->video_dev);
 
 		/* Initialize the video_device structure */
-		vdev = ch->video_dev;
+		vdev = &ch->video_dev;
 		strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name));
-		vdev->release = video_device_release;
+		vdev->release = video_device_release_empty;
 		vdev->fops = &vpif_fops;
 		vdev->ioctl_ops = &vpif_ioctl_ops;
 		vdev->v4l2_dev = &vpif_obj.v4l2_dev;
 		vdev->vfl_dir = VFL_DIR_TX;
 		vdev->queue = q;
 		vdev->lock = &common->lock;
-		video_set_drvdata(ch->video_dev, ch);
+		video_set_drvdata(&ch->video_dev, ch);
 		err = video_register_device(vdev, VFL_TYPE_GRABBER,
 					    (j ? 3 : 2));
 		if (err < 0)
@@ -1227,9 +1229,7 @@
 		ch = vpif_obj.dev[k];
 		common = &ch->common[k];
 		vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
-		video_unregister_device(ch->video_dev);
-		video_device_release(ch->video_dev);
-		ch->video_dev = NULL;
+		video_unregister_device(&ch->video_dev);
 	}
 	return err;
 }
@@ -1246,13 +1246,11 @@
 static __init int vpif_probe(struct platform_device *pdev)
 {
 	struct vpif_subdev_info *subdevdata;
-	int i, j = 0, err = 0;
-	int res_idx = 0;
 	struct i2c_adapter *i2c_adap;
-	struct channel_obj *ch;
-	struct video_device *vfd;
 	struct resource *res;
 	int subdev_count;
+	int res_idx = 0;
+	int i, err;
 
 	vpif_dev = &pdev->dev;
 	err = initialize_vpif();
@@ -1281,25 +1279,6 @@
 		res_idx++;
 	}
 
-	for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
-		/* Get the pointer to the channel object */
-		ch = vpif_obj.dev[i];
-
-		/* Allocate memory for video device */
-		vfd = video_device_alloc();
-		if (vfd == NULL) {
-			for (j = 0; j < i; j++) {
-				ch = vpif_obj.dev[j];
-				video_device_release(ch->video_dev);
-			}
-			err = -ENOMEM;
-			goto vpif_unregister;
-		}
-
-		/* Set video_dev to the video device */
-		ch->video_dev = vfd;
-	}
-
 	vpif_obj.config = pdev->dev.platform_data;
 	subdev_count = vpif_obj.config->subdev_count;
 	subdevdata = vpif_obj.config->subdevinfo;
@@ -1308,7 +1287,7 @@
 	if (vpif_obj.sd == NULL) {
 		vpif_err("unable to allocate memory for subdevice pointers\n");
 		err = -ENOMEM;
-		goto vpif_sd_error;
+		goto vpif_unregister;
 	}
 
 	if (!vpif_obj.config->asd_sizes) {
@@ -1348,12 +1327,6 @@
 
 probe_subdev_out:
 	kfree(vpif_obj.sd);
-vpif_sd_error:
-	for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
-		ch = vpif_obj.dev[i];
-		/* Note: does nothing if ch->video_dev == NULL */
-		video_device_release(ch->video_dev);
-	}
 vpif_unregister:
 	v4l2_device_unregister(&vpif_obj.v4l2_dev);
 
@@ -1379,9 +1352,7 @@
 		common = &ch->common[VPIF_VIDEO_INDEX];
 		vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
 		/* Unregister video device */
-		video_unregister_device(ch->video_dev);
-
-		ch->video_dev = NULL;
+		video_unregister_device(&ch->video_dev);
 		kfree(vpif_obj.dev[i]);
 	}
 
diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h
index 7b21a76..849e0e3 100644
--- a/drivers/media/platform/davinci/vpif_display.h
+++ b/drivers/media/platform/davinci/vpif_display.h
@@ -100,7 +100,7 @@
 
 struct channel_obj {
 	/* V4l2 specific parameters */
-	struct video_device *video_dev;	/* Identifies video device for
+	struct video_device video_dev;	/* Identifies video device for
 					 * this channel */
 	u32 field_id;			/* Indicates id of the field
 					 * which is being displayed */
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h
index 0abdb17..fa572aa 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.h
+++ b/drivers/media/platform/exynos-gsc/gsc-core.h
@@ -466,18 +466,6 @@
 	writel(cfg, dev->regs + GSC_IRQ);
 }
 
-static inline void gsc_lock(struct vb2_queue *vq)
-{
-	struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
-	mutex_lock(&ctx->gsc_dev->lock);
-}
-
-static inline void gsc_unlock(struct vb2_queue *vq)
-{
-	struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
-	mutex_unlock(&ctx->gsc_dev->lock);
-}
-
 static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx)
 {
 	unsigned long flags;
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c
index 74e1de6..d5cffef 100644
--- a/drivers/media/platform/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c
@@ -267,8 +267,8 @@
 	.queue_setup	 = gsc_m2m_queue_setup,
 	.buf_prepare	 = gsc_m2m_buf_prepare,
 	.buf_queue	 = gsc_m2m_buf_queue,
-	.wait_prepare	 = gsc_unlock,
-	.wait_finish	 = gsc_lock,
+	.wait_prepare	 = vb2_ops_wait_prepare,
+	.wait_finish	 = vb2_ops_wait_finish,
 	.stop_streaming	 = gsc_m2m_stop_streaming,
 	.start_streaming = gsc_m2m_start_streaming,
 };
@@ -590,6 +590,7 @@
 	src_vq->mem_ops = &vb2_dma_contig_memops;
 	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	src_vq->lock = &ctx->gsc_dev->lock;
 
 	ret = vb2_queue_init(src_vq);
 	if (ret)
@@ -603,6 +604,7 @@
 	dst_vq->mem_ops = &vb2_dma_contig_memops;
 	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	dst_vq->lock = &ctx->gsc_dev->lock;
 
 	return vb2_queue_init(dst_vq);
 }
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
index 8a2fd8c..e314d73 100644
--- a/drivers/media/platform/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/exynos4-is/fimc-capture.c
@@ -1372,7 +1372,9 @@
 	.vidioc_querybuf		= vb2_ioctl_querybuf,
 	.vidioc_qbuf			= vb2_ioctl_qbuf,
 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
 	.vidioc_expbuf			= vb2_ioctl_expbuf,
+#endif
 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
 
@@ -1482,7 +1484,7 @@
 }
 
 static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd,
-				      struct v4l2_subdev_fh *fh,
+				      struct v4l2_subdev_pad_config *cfg,
 				      struct v4l2_subdev_mbus_code_enum *code)
 {
 	struct fimc_fmt *fmt;
@@ -1495,7 +1497,7 @@
 }
 
 static int fimc_subdev_get_fmt(struct v4l2_subdev *sd,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_format *fmt)
 {
 	struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
@@ -1504,7 +1506,7 @@
 	struct v4l2_mbus_framefmt *mf;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 		fmt->format = *mf;
 		return 0;
 	}
@@ -1536,7 +1538,7 @@
 }
 
 static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_format *fmt)
 {
 	struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
@@ -1559,7 +1561,7 @@
 	mf->colorspace = V4L2_COLORSPACE_JPEG;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 		*mf = fmt->format;
 		return 0;
 	}
@@ -1602,7 +1604,7 @@
 }
 
 static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
-				     struct v4l2_subdev_fh *fh,
+				     struct v4l2_subdev_pad_config *cfg,
 				     struct v4l2_subdev_selection *sel)
 {
 	struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
@@ -1628,10 +1630,10 @@
 		return 0;
 
 	case V4L2_SEL_TGT_CROP:
-		try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
+		try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
 		break;
 	case V4L2_SEL_TGT_COMPOSE:
-		try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
+		try_sel = v4l2_subdev_get_try_compose(sd, cfg, sel->pad);
 		f = &ctx->d_frame;
 		break;
 	default:
@@ -1657,7 +1659,7 @@
 }
 
 static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
-				     struct v4l2_subdev_fh *fh,
+				     struct v4l2_subdev_pad_config *cfg,
 				     struct v4l2_subdev_selection *sel)
 {
 	struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
@@ -1675,10 +1677,10 @@
 
 	switch (sel->target) {
 	case V4L2_SEL_TGT_CROP:
-		try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
+		try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
 		break;
 	case V4L2_SEL_TGT_COMPOSE:
-		try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
+		try_sel = v4l2_subdev_get_try_compose(sd, cfg, sel->pad);
 		f = &ctx->d_frame;
 		break;
 	default:
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c
index 60c7449..5d78f57 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp.c
@@ -112,7 +112,7 @@
 };
 
 static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd,
-				struct v4l2_subdev_fh *fh,
+				struct v4l2_subdev_pad_config *cfg,
 				struct v4l2_subdev_mbus_code_enum *code)
 {
 	const struct fimc_fmt *fmt;
@@ -125,14 +125,14 @@
 }
 
 static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_format *fmt)
 {
 	struct fimc_isp *isp = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *mf = &fmt->format;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		*mf = *v4l2_subdev_get_try_format(fh, fmt->pad);
+		*mf = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 		return 0;
 	}
 
@@ -162,7 +162,7 @@
 }
 
 static void __isp_subdev_try_format(struct fimc_isp *isp,
-				    struct v4l2_subdev_fh *fh,
+				    struct v4l2_subdev_pad_config *cfg,
 				    struct v4l2_subdev_format *fmt)
 {
 	struct v4l2_mbus_framefmt *mf = &fmt->format;
@@ -178,7 +178,7 @@
 		mf->code = MEDIA_BUS_FMT_SGRBG10_1X10;
 	} else {
 		if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
-			format = v4l2_subdev_get_try_format(fh,
+			format = v4l2_subdev_get_try_format(&isp->subdev, cfg,
 						FIMC_ISP_SD_PAD_SINK);
 		else
 			format = &isp->sink_fmt;
@@ -197,7 +197,7 @@
 }
 
 static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_format *fmt)
 {
 	struct fimc_isp *isp = v4l2_get_subdevdata(sd);
@@ -209,10 +209,10 @@
 		 __func__, fmt->pad, mf->code, mf->width, mf->height);
 
 	mutex_lock(&isp->subdev_lock);
-	__isp_subdev_try_format(isp, fh, fmt);
+	__isp_subdev_try_format(isp, cfg, fmt);
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 		*mf = fmt->format;
 
 		/* Propagate format to the source pads */
@@ -223,8 +223,8 @@
 			for (pad = FIMC_ISP_SD_PAD_SRC_FIFO;
 					pad < FIMC_ISP_SD_PADS_NUM; pad++) {
 				format.pad = pad;
-				__isp_subdev_try_format(isp, fh, &format);
-				mf = v4l2_subdev_get_try_format(fh, pad);
+				__isp_subdev_try_format(isp, cfg, &format);
+				mf = v4l2_subdev_get_try_format(sd, cfg, pad);
 				*mf = format.format;
 			}
 		}
@@ -236,7 +236,7 @@
 				isp->sink_fmt = *mf;
 
 				format.pad = FIMC_ISP_SD_PAD_SRC_DMA;
-				__isp_subdev_try_format(isp, fh, &format);
+				__isp_subdev_try_format(isp, cfg, &format);
 
 				isp->src_fmt = format.format;
 				__is_set_frame_size(is, &isp->src_fmt);
@@ -369,7 +369,7 @@
 	struct v4l2_mbus_framefmt fmt;
 	struct v4l2_mbus_framefmt *format;
 
-	format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SINK);
+	format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SINK);
 
 	fmt.colorspace = V4L2_COLORSPACE_SRGB;
 	fmt.code = fimc_isp_formats[0].mbus_code;
@@ -378,12 +378,12 @@
 	fmt.field = V4L2_FIELD_NONE;
 	*format = fmt;
 
-	format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_FIFO);
+	format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_FIFO);
 	fmt.width = DEFAULT_PREVIEW_STILL_WIDTH;
 	fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT;
 	*format = fmt;
 
-	format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_DMA);
+	format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_DMA);
 	*format = fmt;
 
 	return 0;
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c
index 2510f18..ca6261a 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite.c
@@ -568,7 +568,7 @@
  */
 
 static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc,
-					struct v4l2_subdev_fh *fh,
+					struct v4l2_subdev_pad_config *cfg,
 					struct v4l2_subdev_format *format)
 {
 	struct flite_drvdata *dd = fimc->dd;
@@ -592,13 +592,13 @@
 		struct v4l2_rect *rect;
 
 		if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
-			sink_fmt = v4l2_subdev_get_try_format(fh,
+			sink_fmt = v4l2_subdev_get_try_format(&fimc->subdev, cfg,
 						FLITE_SD_PAD_SINK);
 
 			mf->code = sink_fmt->code;
 			mf->colorspace = sink_fmt->colorspace;
 
-			rect = v4l2_subdev_get_try_crop(fh,
+			rect = v4l2_subdev_get_try_crop(&fimc->subdev, cfg,
 						FLITE_SD_PAD_SINK);
 		} else {
 			mf->code = sink->fmt->mbus_code;
@@ -1047,7 +1047,7 @@
 };
 
 static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd,
-					   struct v4l2_subdev_fh *fh,
+					   struct v4l2_subdev_pad_config *cfg,
 					   struct v4l2_subdev_mbus_code_enum *code)
 {
 	const struct fimc_fmt *fmt;
@@ -1060,16 +1060,17 @@
 }
 
 static struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt(
-			struct v4l2_subdev_fh *fh, unsigned int pad)
+		struct v4l2_subdev *sd,
+		struct v4l2_subdev_pad_config *cfg, unsigned int pad)
 {
 	if (pad != FLITE_SD_PAD_SINK)
 		pad = FLITE_SD_PAD_SOURCE_DMA;
 
-	return v4l2_subdev_get_try_format(fh, pad);
+	return v4l2_subdev_get_try_format(sd, cfg, pad);
 }
 
 static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd,
-				    struct v4l2_subdev_fh *fh,
+				    struct v4l2_subdev_pad_config *cfg,
 				    struct v4l2_subdev_format *fmt)
 {
 	struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
@@ -1077,7 +1078,7 @@
 	struct flite_frame *f = &fimc->inp_frame;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad);
+		mf = __fimc_lite_subdev_get_try_fmt(sd, cfg, fmt->pad);
 		fmt->format = *mf;
 		return 0;
 	}
@@ -1100,7 +1101,7 @@
 }
 
 static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd,
-				    struct v4l2_subdev_fh *fh,
+				    struct v4l2_subdev_pad_config *cfg,
 				    struct v4l2_subdev_format *fmt)
 {
 	struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
@@ -1122,17 +1123,17 @@
 		return -EBUSY;
 	}
 
-	ffmt = fimc_lite_subdev_try_fmt(fimc, fh, fmt);
+	ffmt = fimc_lite_subdev_try_fmt(fimc, cfg, fmt);
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
 		struct v4l2_mbus_framefmt *src_fmt;
 
-		mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad);
+		mf = __fimc_lite_subdev_get_try_fmt(sd, cfg, fmt->pad);
 		*mf = fmt->format;
 
 		if (fmt->pad == FLITE_SD_PAD_SINK) {
 			unsigned int pad = FLITE_SD_PAD_SOURCE_DMA;
-			src_fmt = __fimc_lite_subdev_get_try_fmt(fh, pad);
+			src_fmt = __fimc_lite_subdev_get_try_fmt(sd, cfg, pad);
 			*src_fmt = *mf;
 		}
 
@@ -1160,7 +1161,7 @@
 }
 
 static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
-					  struct v4l2_subdev_fh *fh,
+					  struct v4l2_subdev_pad_config *cfg,
 					  struct v4l2_subdev_selection *sel)
 {
 	struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
@@ -1172,7 +1173,7 @@
 		return -EINVAL;
 
 	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
-		sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad);
+		sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
 		return 0;
 	}
 
@@ -1195,7 +1196,7 @@
 }
 
 static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
-					  struct v4l2_subdev_fh *fh,
+					  struct v4l2_subdev_pad_config *cfg,
 					  struct v4l2_subdev_selection *sel)
 {
 	struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
@@ -1209,7 +1210,7 @@
 	fimc_lite_try_crop(fimc, &sel->r);
 
 	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
-		*v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r;
+		*v4l2_subdev_get_try_crop(sd, cfg, sel->pad) = sel->r;
 	} else {
 		unsigned long flags;
 		spin_lock_irqsave(&fimc->slock, flags);
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index 2504aa8..d74e1be 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -540,7 +540,7 @@
 }
 
 static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->index >= ARRAY_SIZE(s5pcsis_formats))
@@ -568,23 +568,23 @@
 }
 
 static struct v4l2_mbus_framefmt *__s5pcsis_get_format(
-		struct csis_state *state, struct v4l2_subdev_fh *fh,
+		struct csis_state *state, struct v4l2_subdev_pad_config *cfg,
 		enum v4l2_subdev_format_whence which)
 {
 	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
+		return cfg ? v4l2_subdev_get_try_format(&state->sd, cfg, 0) : NULL;
 
 	return &state->format;
 }
 
-static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct csis_state *state = sd_to_csis_state(sd);
 	struct csis_pix_format const *csis_fmt;
 	struct v4l2_mbus_framefmt *mf;
 
-	mf = __s5pcsis_get_format(state, fh, fmt->which);
+	mf = __s5pcsis_get_format(state, cfg, fmt->which);
 
 	if (fmt->pad == CSIS_PAD_SOURCE) {
 		if (mf) {
@@ -605,13 +605,13 @@
 	return 0;
 }
 
-static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct csis_state *state = sd_to_csis_state(sd);
 	struct v4l2_mbus_framefmt *mf;
 
-	mf = __s5pcsis_get_format(state, fh, fmt->which);
+	mf = __s5pcsis_get_format(state, cfg, fmt->which);
 	if (!mf)
 		return -EINVAL;
 
@@ -651,7 +651,7 @@
 
 static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
-	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
 
 	format->colorspace = V4L2_COLORSPACE_JPEG;
 	format->code = s5pcsis_formats[0].code;
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index b70c1ae..92d9549 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -127,7 +127,7 @@
 
 struct deinterlace_dev {
 	struct v4l2_device	v4l2_dev;
-	struct video_device	*vfd;
+	struct video_device	vfd;
 
 	atomic_t		busy;
 	struct mutex		dev_mutex;
@@ -983,7 +983,7 @@
 	.fops		= &deinterlace_fops,
 	.ioctl_ops	= &deinterlace_ioctl_ops,
 	.minor		= -1,
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
 	.vfl_dir	= VFL_DIR_M2M,
 };
 
@@ -1026,13 +1026,7 @@
 	atomic_set(&pcdev->busy, 0);
 	mutex_init(&pcdev->dev_mutex);
 
-	vfd = video_device_alloc();
-	if (!vfd) {
-		v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n");
-		ret = -ENOMEM;
-		goto unreg_dev;
-	}
-
+	vfd = &pcdev->vfd;
 	*vfd = deinterlace_videodev;
 	vfd->lock = &pcdev->dev_mutex;
 	vfd->v4l2_dev = &pcdev->v4l2_dev;
@@ -1040,12 +1034,11 @@
 	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
 	if (ret) {
 		v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n");
-		goto rel_vdev;
+		goto unreg_dev;
 	}
 
 	video_set_drvdata(vfd, pcdev);
 	snprintf(vfd->name, sizeof(vfd->name), "%s", deinterlace_videodev.name);
-	pcdev->vfd = vfd;
 	v4l2_info(&pcdev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME
 			" Device registered as /dev/video%d\n", vfd->num);
 
@@ -1069,11 +1062,9 @@
 
 	v4l2_m2m_release(pcdev->m2m_dev);
 err_m2m:
-	video_unregister_device(pcdev->vfd);
+	video_unregister_device(&pcdev->vfd);
 err_ctx:
 	vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
-rel_vdev:
-	video_device_release(vfd);
 unreg_dev:
 	v4l2_device_unregister(&pcdev->v4l2_dev);
 rel_dma:
@@ -1088,7 +1079,7 @@
 
 	v4l2_info(&pcdev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
 	v4l2_m2m_release(pcdev->m2m_dev);
-	video_unregister_device(pcdev->vfd);
+	video_unregister_device(&pcdev->vfd);
 	v4l2_device_unregister(&pcdev->v4l2_dev);
 	vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
 	dma_release_channel(pcdev->dma_chan);
diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell-ccic/Kconfig
index 9c2e8f4..db69ddd 100644
--- a/drivers/media/platform/marvell-ccic/Kconfig
+++ b/drivers/media/platform/marvell-ccic/Kconfig
@@ -6,6 +6,7 @@
 	select VIDEO_OV7670
 	select VIDEOBUF2_VMALLOC
 	select VIDEOBUF2_DMA_CONTIG
+	select VIDEOBUF2_DMA_SG
 	---help---
 	  This is a video4linux2 driver for the Marvell 88ALP01 integrated
 	  CMOS camera controller.  This is the controller found on first-
@@ -15,7 +16,7 @@
 	tristate "Marvell Armada 610 integrated camera controller support"
 	depends on m
 	depends on ARCH_MMP && I2C && VIDEO_V4L2
-	depends on HAS_DMA
+	depends on HAS_DMA && BROKEN
 	select VIDEO_OV7670
 	depends on I2C_GPIO
 	select VIDEOBUF2_DMA_SG
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 137ffa2..e87ede7 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -1568,24 +1568,64 @@
 		struct v4l2_frmsizeenum *sizes)
 {
 	struct mcam_camera *cam = priv;
+	struct mcam_format_struct *f;
+	struct v4l2_subdev_frame_size_enum fse = {
+		.index = sizes->index,
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
 	int ret;
 
+	f = mcam_find_format(sizes->pixel_format);
+	if (f->pixelformat != sizes->pixel_format)
+		return -EINVAL;
+	fse.code = f->mbus_code;
 	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, video, enum_framesizes, sizes);
+	ret = sensor_call(cam, pad, enum_frame_size, NULL, &fse);
 	mutex_unlock(&cam->s_mutex);
-	return ret;
+	if (ret)
+		return ret;
+	if (fse.min_width == fse.max_width &&
+	    fse.min_height == fse.max_height) {
+		sizes->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+		sizes->discrete.width = fse.min_width;
+		sizes->discrete.height = fse.min_height;
+		return 0;
+	}
+	sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+	sizes->stepwise.min_width = fse.min_width;
+	sizes->stepwise.max_width = fse.max_width;
+	sizes->stepwise.min_height = fse.min_height;
+	sizes->stepwise.max_height = fse.max_height;
+	sizes->stepwise.step_width = 1;
+	sizes->stepwise.step_height = 1;
+	return 0;
 }
 
 static int mcam_vidioc_enum_frameintervals(struct file *filp, void *priv,
 		struct v4l2_frmivalenum *interval)
 {
 	struct mcam_camera *cam = priv;
+	struct mcam_format_struct *f;
+	struct v4l2_subdev_frame_interval_enum fie = {
+		.index = interval->index,
+		.width = interval->width,
+		.height = interval->height,
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
 	int ret;
 
+	f = mcam_find_format(interval->pixel_format);
+	if (f->pixelformat != interval->pixel_format)
+		return -EINVAL;
+	fie.code = f->mbus_code;
 	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, video, enum_frameintervals, interval);
+	ret = sensor_call(cam, pad, enum_frame_interval, NULL, &fie);
 	mutex_unlock(&cam->s_mutex);
-	return ret;
+	if (ret)
+		return ret;
+	interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+	interval->discrete = fie.interval;
+	return 0;
 }
 
 #ifdef CPTCFG_VIDEO_ADV_DEBUG
@@ -1913,7 +1953,6 @@
 
 	mutex_lock(&cam->s_mutex);
 	cam->vdev = mcam_v4l_template;
-	cam->vdev.debug = 0;
 	cam->vdev.v4l2_dev = &cam->v4l2_dev;
 	video_set_drvdata(&cam->vdev, cam);
 	ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index ba2d8f9..17b189a 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -1978,7 +1978,7 @@
 	vout->cropped_offset = 0;
 
 	if (ovid->rotation_type == VOUT_ROT_VRFB) {
-		int static_vrfb_allocation = (vid_num == 0) ?
+		bool static_vrfb_allocation = (vid_num == 0) ?
 			vid1_static_vrfb_alloc : vid2_static_vrfb_alloc;
 		ret = omap_vout_setup_vrfb_bufs(pdev, vid_num,
 				static_vrfb_allocation);
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c
index aa39306..c6e2527 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.c
+++ b/drivers/media/platform/omap/omap_vout_vrfb.c
@@ -21,6 +21,7 @@
 
 #include "omap_voutdef.h"
 #include "omap_voutlib.h"
+#include "omap_vout_vrfb.h"
 
 #define OMAP_DMA_NO_DEVICE	0
 
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.h b/drivers/media/platform/omap/omap_vout_vrfb.h
index ce06cb1..5000984 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.h
+++ b/drivers/media/platform/omap/omap_vout_vrfb.h
@@ -15,7 +15,7 @@
 #ifdef CPTCFG_VIDEO_OMAP2_VOUT_VRFB
 void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout);
 int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
-			u32 static_vrfb_allocation);
+			bool static_vrfb_allocation);
 void omap_vout_release_vrfb(struct omap_vout_device *vout);
 int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
 			unsigned int *count, unsigned int startindex);
@@ -25,7 +25,7 @@
 #else
 static inline void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) { };
 static inline int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
-			u32 static_vrfb_allocation)
+			bool static_vrfb_allocation)
 		{ return 0; };
 static inline void omap_vout_release_vrfb(struct omap_vout_device *vout) { };
 static inline int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 51c2129..18d0a87 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -51,6 +51,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/omap-iommu.h>
 #include <linux/platform_device.h>
@@ -63,6 +64,7 @@
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
 
 #include "isp.h"
 #include "ispreg.h"
@@ -85,35 +87,45 @@
 static const struct isp_res_mapping isp_res_maps[] = {
 	{
 		.isp_rev = ISP_REVISION_2_0,
-		.map = 1 << OMAP3_ISP_IOMEM_MAIN |
-		       1 << OMAP3_ISP_IOMEM_CCP2 |
-		       1 << OMAP3_ISP_IOMEM_CCDC |
-		       1 << OMAP3_ISP_IOMEM_HIST |
-		       1 << OMAP3_ISP_IOMEM_H3A |
-		       1 << OMAP3_ISP_IOMEM_PREV |
-		       1 << OMAP3_ISP_IOMEM_RESZ |
-		       1 << OMAP3_ISP_IOMEM_SBL |
-		       1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
-		       1 << OMAP3_ISP_IOMEM_CSIPHY2 |
-		       1 << OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE,
+		.offset = {
+			/* first MMIO area */
+			0x0000, /* base, len 0x0070 */
+			0x0400, /* ccp2, len 0x01f0 */
+			0x0600, /* ccdc, len 0x00a8 */
+			0x0a00, /* hist, len 0x0048 */
+			0x0c00, /* h3a, len 0x0060 */
+			0x0e00, /* preview, len 0x00a0 */
+			0x1000, /* resizer, len 0x00ac */
+			0x1200, /* sbl, len 0x00fc */
+			/* second MMIO area */
+			0x0000, /* csi2a, len 0x0170 */
+			0x0170, /* csiphy2, len 0x000c */
+		},
+		.syscon_offset = 0xdc,
+		.phy_type = ISP_PHY_TYPE_3430,
 	},
 	{
 		.isp_rev = ISP_REVISION_15_0,
-		.map = 1 << OMAP3_ISP_IOMEM_MAIN |
-		       1 << OMAP3_ISP_IOMEM_CCP2 |
-		       1 << OMAP3_ISP_IOMEM_CCDC |
-		       1 << OMAP3_ISP_IOMEM_HIST |
-		       1 << OMAP3_ISP_IOMEM_H3A |
-		       1 << OMAP3_ISP_IOMEM_PREV |
-		       1 << OMAP3_ISP_IOMEM_RESZ |
-		       1 << OMAP3_ISP_IOMEM_SBL |
-		       1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
-		       1 << OMAP3_ISP_IOMEM_CSIPHY2 |
-		       1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 |
-		       1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 |
-		       1 << OMAP3_ISP_IOMEM_CSIPHY1 |
-		       1 << OMAP3_ISP_IOMEM_CSI2C_REGS2 |
-		       1 << OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL,
+		.offset = {
+			/* first MMIO area */
+			0x0000, /* base, len 0x0070 */
+			0x0400, /* ccp2, len 0x01f0 */
+			0x0600, /* ccdc, len 0x00a8 */
+			0x0a00, /* hist, len 0x0048 */
+			0x0c00, /* h3a, len 0x0060 */
+			0x0e00, /* preview, len 0x00a0 */
+			0x1000, /* resizer, len 0x00ac */
+			0x1200, /* sbl, len 0x00fc */
+			/* second MMIO area */
+			0x0000, /* csi2a, len 0x0170 (1st area) */
+			0x0170, /* csiphy2, len 0x000c */
+			0x01c0, /* csi2a, len 0x0040 (2nd area) */
+			0x0400, /* csi2c, len 0x0170 (1st area) */
+			0x0570, /* csiphy1, len 0x000c */
+			0x05c0, /* csi2c, len 0x0040 (2nd area) */
+		},
+		.syscon_offset = 0x2f0,
+		.phy_type = ISP_PHY_TYPE_3630,
 	},
 };
 
@@ -220,6 +232,9 @@
 		return ISPTCTRL_CTRL_DIV_BYPASS;
 	}
 
+	if (*rate == 0)
+		*rate = 1;
+
 	divider = DIV_ROUND_CLOSEST(parent_rate, *rate);
 	if (divider >= ISPTCTRL_CTRL_DIV_BYPASS)
 		divider = ISPTCTRL_CTRL_DIV_BYPASS - 1;
@@ -276,9 +291,20 @@
 	.num_parents = 1,
 };
 
+static struct clk *isp_xclk_src_get(struct of_phandle_args *clkspec, void *data)
+{
+	unsigned int idx = clkspec->args[0];
+	struct isp_device *isp = data;
+
+	if (idx >= ARRAY_SIZE(isp->xclks))
+		return ERR_PTR(-ENOENT);
+
+	return isp->xclks[idx].clk;
+}
+
 static int isp_xclk_init(struct isp_device *isp)
 {
-	struct isp_platform_data *pdata = isp->pdata;
+	struct device_node *np = isp->dev->of_node;
 	struct clk_init_data init;
 	unsigned int i;
 
@@ -308,37 +334,27 @@
 		xclk->clk = clk_register(NULL, &xclk->hw);
 		if (IS_ERR(xclk->clk))
 			return PTR_ERR(xclk->clk);
-
-		if (pdata->xclks[i].con_id == NULL &&
-		    pdata->xclks[i].dev_id == NULL)
-			continue;
-
-		xclk->lookup = kzalloc(sizeof(*xclk->lookup), GFP_KERNEL);
-		if (xclk->lookup == NULL)
-			return -ENOMEM;
-
-		xclk->lookup->con_id = pdata->xclks[i].con_id;
-		xclk->lookup->dev_id = pdata->xclks[i].dev_id;
-		xclk->lookup->clk = xclk->clk;
-
-		clkdev_add(xclk->lookup);
 	}
 
+	if (np)
+		of_clk_add_provider(np, isp_xclk_src_get, isp);
+
 	return 0;
 }
 
 static void isp_xclk_cleanup(struct isp_device *isp)
 {
+	struct device_node *np = isp->dev->of_node;
 	unsigned int i;
 
+	if (np)
+		of_clk_del_provider(np);
+
 	for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
 		struct isp_xclk *xclk = &isp->xclks[i];
 
 		if (!IS_ERR(xclk->clk))
 			clk_unregister(xclk->clk);
-
-		if (xclk->lookup)
-			clkdev_drop(xclk->lookup);
 	}
 }
 
@@ -419,7 +435,7 @@
  */
 void omap3isp_configure_bridge(struct isp_device *isp,
 			       enum ccdc_input_entity input,
-			       const struct isp_parallel_platform_data *pdata,
+			       const struct isp_parallel_cfg *parcfg,
 			       unsigned int shift, unsigned int bridge)
 {
 	u32 ispctrl_val;
@@ -434,8 +450,8 @@
 	switch (input) {
 	case CCDC_INPUT_PARALLEL:
 		ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
-		ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
-		shift += pdata->data_lane_shift * 2;
+		ispctrl_val |= parcfg->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
+		shift += parcfg->data_lane_shift * 2;
 		break;
 
 	case CCDC_INPUT_CSI2A:
@@ -1781,58 +1797,121 @@
 }
 
 /*
- * isp_register_subdev_group - Register a group of subdevices
+ * isp_register_subdev - Register a sub-device
  * @isp: OMAP3 ISP device
- * @board_info: I2C subdevs board information array
+ * @isp_subdev: platform data related to a sub-device
  *
- * Register all I2C subdevices in the board_info array. The array must be
- * terminated by a NULL entry, and the first entry must be the sensor.
+ * Register an I2C sub-device which has not been registered by other
+ * means (such as the Device Tree).
  *
- * Return a pointer to the sensor media entity if it has been successfully
+ * Return a pointer to the sub-device if it has been successfully
  * registered, or NULL otherwise.
  */
 static struct v4l2_subdev *
-isp_register_subdev_group(struct isp_device *isp,
-		     struct isp_subdev_i2c_board_info *board_info)
+isp_register_subdev(struct isp_device *isp,
+		    struct isp_platform_subdev *isp_subdev)
 {
-	struct v4l2_subdev *sensor = NULL;
-	unsigned int first;
+	struct i2c_adapter *adapter;
+	struct v4l2_subdev *sd;
 
-	if (board_info->board_info == NULL)
+	if (isp_subdev->board_info == NULL)
 		return NULL;
 
-	for (first = 1; board_info->board_info; ++board_info, first = 0) {
-		struct v4l2_subdev *subdev;
-		struct i2c_adapter *adapter;
-
-		adapter = i2c_get_adapter(board_info->i2c_adapter_id);
-		if (adapter == NULL) {
-			dev_err(isp->dev, "%s: Unable to get I2C adapter %d for "
-				"device %s\n", __func__,
-				board_info->i2c_adapter_id,
-				board_info->board_info->type);
-			continue;
-		}
-
-		subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter,
-				board_info->board_info, NULL);
-		if (subdev == NULL) {
-			dev_err(isp->dev, "%s: Unable to register subdev %s\n",
-				__func__, board_info->board_info->type);
-			continue;
-		}
-
-		if (first)
-			sensor = subdev;
+	adapter = i2c_get_adapter(isp_subdev->i2c_adapter_id);
+	if (adapter == NULL) {
+		dev_err(isp->dev,
+			"%s: Unable to get I2C adapter %d for device %s\n",
+			__func__, isp_subdev->i2c_adapter_id,
+			isp_subdev->board_info->type);
+		return NULL;
 	}
 
-	return sensor;
+	sd = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter,
+				       isp_subdev->board_info, NULL);
+	if (sd == NULL) {
+		dev_err(isp->dev, "%s: Unable to register subdev %s\n",
+			__func__, isp_subdev->board_info->type);
+		return NULL;
+	}
+
+	return sd;
+}
+
+static int isp_link_entity(
+	struct isp_device *isp, struct media_entity *entity,
+	enum isp_interface_type interface)
+{
+	struct media_entity *input;
+	unsigned int flags;
+	unsigned int pad;
+	unsigned int i;
+
+	/* Connect the sensor to the correct interface module.
+	 * Parallel sensors are connected directly to the CCDC, while
+	 * serial sensors are connected to the CSI2a, CCP2b or CSI2c
+	 * receiver through CSIPHY1 or CSIPHY2.
+	 */
+	switch (interface) {
+	case ISP_INTERFACE_PARALLEL:
+		input = &isp->isp_ccdc.subdev.entity;
+		pad = CCDC_PAD_SINK;
+		flags = 0;
+		break;
+
+	case ISP_INTERFACE_CSI2A_PHY2:
+		input = &isp->isp_csi2a.subdev.entity;
+		pad = CSI2_PAD_SINK;
+		flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+		break;
+
+	case ISP_INTERFACE_CCP2B_PHY1:
+	case ISP_INTERFACE_CCP2B_PHY2:
+		input = &isp->isp_ccp2.subdev.entity;
+		pad = CCP2_PAD_SINK;
+		flags = 0;
+		break;
+
+	case ISP_INTERFACE_CSI2C_PHY1:
+		input = &isp->isp_csi2c.subdev.entity;
+		pad = CSI2_PAD_SINK;
+		flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+		break;
+
+	default:
+		dev_err(isp->dev, "%s: invalid interface type %u\n", __func__,
+			interface);
+		return -EINVAL;
+	}
+
+	/*
+	 * Not all interfaces are available on all revisions of the
+	 * ISP. The sub-devices of those interfaces aren't initialised
+	 * in such a case. Check this by ensuring the num_pads is
+	 * non-zero.
+	 */
+	if (!input->num_pads) {
+		dev_err(isp->dev, "%s: invalid input %u\n", entity->name,
+			interface);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < entity->num_pads; i++) {
+		if (entity->pads[i].flags & MEDIA_PAD_FL_SOURCE)
+			break;
+	}
+	if (i == entity->num_pads) {
+		dev_err(isp->dev, "%s: no source pad in external entity\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	return media_entity_create_link(entity, i, input, pad, flags);
 }
 
 static int isp_register_entities(struct isp_device *isp)
 {
 	struct isp_platform_data *pdata = isp->pdata;
-	struct isp_v4l2_subdevs_group *subdevs;
+	struct isp_platform_subdev *isp_subdev;
 	int ret;
 
 	isp->media_dev.dev = isp->dev;
@@ -1889,74 +1968,31 @@
 	if (ret < 0)
 		goto done;
 
-	/* Register external entities */
-	for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
-		struct v4l2_subdev *sensor;
-		struct media_entity *input;
-		unsigned int flags;
-		unsigned int pad;
-		unsigned int i;
+	/*
+	 * Device Tree --- the external sub-devices will be registered
+	 * later. The same goes for the sub-device node registration.
+	 */
+	if (isp->dev->of_node)
+		return 0;
 
-		sensor = isp_register_subdev_group(isp, subdevs->subdevs);
-		if (sensor == NULL)
+	/* Register external entities */
+	for (isp_subdev = pdata ? pdata->subdevs : NULL;
+	     isp_subdev && isp_subdev->board_info; isp_subdev++) {
+		struct v4l2_subdev *sd;
+
+		sd = isp_register_subdev(isp, isp_subdev);
+
+		/*
+		 * No bus information --- this is either a flash or a
+		 * lens subdev.
+		 */
+		if (!sd || !isp_subdev->bus)
 			continue;
 
-		sensor->host_priv = subdevs;
+		sd->host_priv = isp_subdev->bus;
 
-		/* Connect the sensor to the correct interface module. Parallel
-		 * sensors are connected directly to the CCDC, while serial
-		 * sensors are connected to the CSI2a, CCP2b or CSI2c receiver
-		 * through CSIPHY1 or CSIPHY2.
-		 */
-		switch (subdevs->interface) {
-		case ISP_INTERFACE_PARALLEL:
-			input = &isp->isp_ccdc.subdev.entity;
-			pad = CCDC_PAD_SINK;
-			flags = 0;
-			break;
-
-		case ISP_INTERFACE_CSI2A_PHY2:
-			input = &isp->isp_csi2a.subdev.entity;
-			pad = CSI2_PAD_SINK;
-			flags = MEDIA_LNK_FL_IMMUTABLE
-			      | MEDIA_LNK_FL_ENABLED;
-			break;
-
-		case ISP_INTERFACE_CCP2B_PHY1:
-		case ISP_INTERFACE_CCP2B_PHY2:
-			input = &isp->isp_ccp2.subdev.entity;
-			pad = CCP2_PAD_SINK;
-			flags = 0;
-			break;
-
-		case ISP_INTERFACE_CSI2C_PHY1:
-			input = &isp->isp_csi2c.subdev.entity;
-			pad = CSI2_PAD_SINK;
-			flags = MEDIA_LNK_FL_IMMUTABLE
-			      | MEDIA_LNK_FL_ENABLED;
-			break;
-
-		default:
-			dev_err(isp->dev, "%s: invalid interface type %u\n",
-				__func__, subdevs->interface);
-			ret = -EINVAL;
-			goto done;
-		}
-
-		for (i = 0; i < sensor->entity.num_pads; i++) {
-			if (sensor->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
-				break;
-		}
-		if (i == sensor->entity.num_pads) {
-			dev_err(isp->dev,
-				"%s: no source pad in external entity\n",
-				__func__);
-			ret = -EINVAL;
-			goto done;
-		}
-
-		ret = media_entity_create_link(&sensor->entity, i, input, pad,
-					       flags);
+		ret = isp_link_entity(isp, &sd->entity,
+				      isp_subdev->bus->interface);
 		if (ret < 0)
 			goto done;
 	}
@@ -1964,8 +2000,10 @@
 	ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
 
 done:
-	if (ret < 0)
+	if (ret < 0) {
 		isp_unregister_entities(isp);
+		v4l2_async_notifier_unregister(&isp->notifier);
+	}
 
 	return ret;
 }
@@ -2180,6 +2218,7 @@
 {
 	struct isp_device *isp = platform_get_drvdata(pdev);
 
+	v4l2_async_notifier_unregister(&isp->notifier);
 	isp_unregister_entities(isp);
 	isp_cleanup_modules(isp);
 	isp_xclk_cleanup(isp);
@@ -2191,26 +2230,156 @@
 	return 0;
 }
 
-static int isp_map_mem_resource(struct platform_device *pdev,
-				struct isp_device *isp,
-				enum isp_mem_resources res)
+enum isp_of_phy {
+	ISP_OF_PHY_PARALLEL = 0,
+	ISP_OF_PHY_CSIPHY1,
+	ISP_OF_PHY_CSIPHY2,
+};
+
+static int isp_of_parse_node(struct device *dev, struct device_node *node,
+			     struct isp_async_subdev *isd)
 {
-	struct resource *mem;
+	struct isp_bus_cfg *buscfg = &isd->bus;
+	struct v4l2_of_endpoint vep;
+	unsigned int i;
 
-	/* request the mem region for the camera registers */
+	v4l2_of_parse_endpoint(node, &vep);
 
-	mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
+	dev_dbg(dev, "parsing endpoint %s, interface %u\n", node->full_name,
+		vep.base.port);
 
-	/* map the region */
-	isp->mmio_base[res] = devm_ioremap_resource(isp->dev, mem);
-	if (IS_ERR(isp->mmio_base[res]))
-		return PTR_ERR(isp->mmio_base[res]);
+	switch (vep.base.port) {
+	case ISP_OF_PHY_PARALLEL:
+		buscfg->interface = ISP_INTERFACE_PARALLEL;
+		buscfg->bus.parallel.data_lane_shift =
+			vep.bus.parallel.data_shift;
+		buscfg->bus.parallel.clk_pol =
+			!!(vep.bus.parallel.flags
+			   & V4L2_MBUS_PCLK_SAMPLE_FALLING);
+		buscfg->bus.parallel.hs_pol =
+			!!(vep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW);
+		buscfg->bus.parallel.vs_pol =
+			!!(vep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW);
+		buscfg->bus.parallel.fld_pol =
+			!!(vep.bus.parallel.flags & V4L2_MBUS_FIELD_EVEN_LOW);
+		buscfg->bus.parallel.data_pol =
+			!!(vep.bus.parallel.flags & V4L2_MBUS_DATA_ACTIVE_LOW);
+		break;
 
-	isp->mmio_base_phys[res] = mem->start;
+	case ISP_OF_PHY_CSIPHY1:
+	case ISP_OF_PHY_CSIPHY2:
+		/* FIXME: always assume CSI-2 for now. */
+		switch (vep.base.port) {
+		case ISP_OF_PHY_CSIPHY1:
+			buscfg->interface = ISP_INTERFACE_CSI2C_PHY1;
+			break;
+		case ISP_OF_PHY_CSIPHY2:
+			buscfg->interface = ISP_INTERFACE_CSI2A_PHY2;
+			break;
+		}
+		buscfg->bus.csi2.lanecfg.clk.pos = vep.bus.mipi_csi2.clock_lane;
+		buscfg->bus.csi2.lanecfg.clk.pol =
+			vep.bus.mipi_csi2.lane_polarities[0];
+		dev_dbg(dev, "clock lane polarity %u, pos %u\n",
+			buscfg->bus.csi2.lanecfg.clk.pol,
+			buscfg->bus.csi2.lanecfg.clk.pos);
+
+		for (i = 0; i < ISP_CSIPHY2_NUM_DATA_LANES; i++) {
+			buscfg->bus.csi2.lanecfg.data[i].pos =
+				vep.bus.mipi_csi2.data_lanes[i];
+			buscfg->bus.csi2.lanecfg.data[i].pol =
+				vep.bus.mipi_csi2.lane_polarities[i + 1];
+			dev_dbg(dev, "data lane %u polarity %u, pos %u\n", i,
+				buscfg->bus.csi2.lanecfg.data[i].pol,
+				buscfg->bus.csi2.lanecfg.data[i].pos);
+		}
+
+		/*
+		 * FIXME: now we assume the CRC is always there.
+		 * Implement a way to obtain this information from the
+		 * sensor. Frame descriptors, perhaps?
+		 */
+		buscfg->bus.csi2.crc = 1;
+		break;
+
+	default:
+		dev_warn(dev, "%s: invalid interface %u\n", node->full_name,
+			 vep.base.port);
+		break;
+	}
 
 	return 0;
 }
 
+static int isp_of_parse_nodes(struct device *dev,
+			      struct v4l2_async_notifier *notifier)
+{
+	struct device_node *node = NULL;
+
+	notifier->subdevs = devm_kcalloc(
+		dev, ISP_MAX_SUBDEVS, sizeof(*notifier->subdevs), GFP_KERNEL);
+	if (!notifier->subdevs)
+		return -ENOMEM;
+
+	while (notifier->num_subdevs < ISP_MAX_SUBDEVS &&
+	       (node = of_graph_get_next_endpoint(dev->of_node, node))) {
+		struct isp_async_subdev *isd;
+
+		isd = devm_kzalloc(dev, sizeof(*isd), GFP_KERNEL);
+		if (!isd) {
+			of_node_put(node);
+			return -ENOMEM;
+		}
+
+		notifier->subdevs[notifier->num_subdevs] = &isd->asd;
+
+		if (isp_of_parse_node(dev, node, isd)) {
+			of_node_put(node);
+			return -EINVAL;
+		}
+
+		isd->asd.match.of.node = of_graph_get_remote_port_parent(node);
+		of_node_put(node);
+		if (!isd->asd.match.of.node) {
+			dev_warn(dev, "bad remote port parent\n");
+			return -EINVAL;
+		}
+
+		isd->asd.match_type = V4L2_ASYNC_MATCH_OF;
+		notifier->num_subdevs++;
+	}
+
+	return notifier->num_subdevs;
+}
+
+static int isp_subdev_notifier_bound(struct v4l2_async_notifier *async,
+				     struct v4l2_subdev *subdev,
+				     struct v4l2_async_subdev *asd)
+{
+	struct isp_device *isp = container_of(async, struct isp_device,
+					      notifier);
+	struct isp_async_subdev *isd =
+		container_of(asd, struct isp_async_subdev, asd);
+	int ret;
+
+	ret = isp_link_entity(isp, &subdev->entity, isd->bus.interface);
+	if (ret < 0)
+		return ret;
+
+	isd->sd = subdev;
+	isd->sd->host_priv = &isd->bus;
+
+	return ret;
+}
+
+static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async)
+{
+	struct isp_device *isp = container_of(async, struct isp_device,
+					      notifier);
+
+	return v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
+}
+
 /*
  * isp_probe - Probe ISP platform device
  * @pdev: Pointer to ISP platform device
@@ -2224,47 +2393,86 @@
  */
 static int isp_probe(struct platform_device *pdev)
 {
-	struct isp_platform_data *pdata = pdev->dev.platform_data;
 	struct isp_device *isp;
+	struct resource *mem;
 	int ret;
 	int i, m;
 
-	if (pdata == NULL)
-		return -EINVAL;
-
 	isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
 	if (!isp) {
 		dev_err(&pdev->dev, "could not allocate memory\n");
 		return -ENOMEM;
 	}
 
+	if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+		ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type",
+					   &isp->phy_type);
+		if (ret)
+			return ret;
+
+		isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+							      "syscon");
+		if (IS_ERR(isp->syscon))
+			return PTR_ERR(isp->syscon);
+
+		ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1,
+						 &isp->syscon_offset);
+		if (ret)
+			return ret;
+
+		ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier);
+		if (ret < 0)
+			return ret;
+		ret = v4l2_async_notifier_register(&isp->v4l2_dev,
+						   &isp->notifier);
+		if (ret)
+			return ret;
+	} else {
+		isp->pdata = pdev->dev.platform_data;
+		isp->syscon = syscon_regmap_lookup_by_pdevname("syscon.0");
+		if (IS_ERR(isp->syscon))
+			return PTR_ERR(isp->syscon);
+		dev_warn(&pdev->dev,
+			 "Platform data support is deprecated! Please move to DT now!\n");
+	}
+
 	isp->autoidle = autoidle;
 
 	mutex_init(&isp->isp_mutex);
 	spin_lock_init(&isp->stat_lock);
 
 	isp->dev = &pdev->dev;
-	isp->pdata = pdata;
 	isp->ref_count = 0;
 
 	ret = dma_coerce_mask_and_coherent(isp->dev, DMA_BIT_MASK(32));
 	if (ret)
-		return ret;
+		goto error;
 
 	platform_set_drvdata(pdev, isp);
 
 	/* Regulators */
-	isp->isp_csiphy1.vdd = devm_regulator_get(&pdev->dev, "VDD_CSIPHY1");
-	isp->isp_csiphy2.vdd = devm_regulator_get(&pdev->dev, "VDD_CSIPHY2");
+	isp->isp_csiphy1.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy1");
+	isp->isp_csiphy2.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy2");
 
 	/* Clocks
 	 *
 	 * The ISP clock tree is revision-dependent. We thus need to enable ICLK
 	 * manually to read the revision before calling __omap3isp_get().
+	 *
+	 * Start by mapping the ISP MMIO area, which is in two pieces.
+	 * The ISP IOMMU is in between. Map both now, and fill in the
+	 * ISP revision specific portions a little later in the
+	 * function.
 	 */
-	ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN);
-	if (ret < 0)
-		goto error;
+	for (i = 0; i < 2; i++) {
+		unsigned int map_idx = i ? OMAP3_ISP_IOMEM_CSI2A_REGS1 : 0;
+
+		mem = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		isp->mmio_base[map_idx] =
+			devm_ioremap_resource(isp->dev, mem);
+		if (IS_ERR(isp->mmio_base[map_idx]))
+			return PTR_ERR(isp->mmio_base[map_idx]);
+	}
 
 	ret = isp_get_clocks(isp);
 	if (ret < 0)
@@ -2305,14 +2513,23 @@
 		goto error_isp;
 	}
 
-	for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) {
-		if (isp_res_maps[m].map & 1 << i) {
-			ret = isp_map_mem_resource(pdev, isp, i);
-			if (ret)
-				goto error_isp;
-		}
+	if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) {
+		isp->syscon_offset = isp_res_maps[m].syscon_offset;
+		isp->phy_type = isp_res_maps[m].phy_type;
 	}
 
+	for (i = 1; i < OMAP3_ISP_IOMEM_CSI2A_REGS1; i++)
+		isp->mmio_base[i] =
+			isp->mmio_base[0] + isp_res_maps[m].offset[i];
+
+	for (i = OMAP3_ISP_IOMEM_CSIPHY2; i < OMAP3_ISP_IOMEM_LAST; i++)
+		isp->mmio_base[i] =
+			isp->mmio_base[OMAP3_ISP_IOMEM_CSI2A_REGS1]
+			+ isp_res_maps[m].offset[i];
+
+	isp->mmio_hist_base_phys =
+		mem->start + isp_res_maps[m].offset[OMAP3_ISP_IOMEM_HIST];
+
 	/* IOMMU */
 	ret = isp_attach_iommu(isp);
 	if (ret < 0) {
@@ -2340,6 +2557,9 @@
 	if (ret < 0)
 		goto error_iommu;
 
+	isp->notifier.bound = isp_subdev_notifier_bound;
+	isp->notifier.complete = isp_subdev_notifier_complete;
+
 	ret = isp_register_entities(isp);
 	if (ret < 0)
 		goto error_modules;
@@ -2375,6 +2595,11 @@
 };
 MODULE_DEVICE_TABLE(platform, omap3isp_id_table);
 
+static const struct of_device_id omap3isp_of_table[] = {
+	{ .compatible = "ti,omap3-isp" },
+	{ },
+};
+
 static struct platform_driver omap3isp_driver = {
 	.probe = isp_probe,
 	.remove = isp_remove,
@@ -2382,6 +2607,7 @@
 	.driver = {
 		.name = "omap3isp",
 		.pm	= &omap3isp_pm_ops,
+		.of_match_table = omap3isp_of_table,
 	},
 };
 
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index cfdfc87..e579943 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -18,6 +18,7 @@
 #define OMAP3_ISP_CORE_H
 
 #include <media/omap3isp.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-device.h>
 #include <linux/clk-provider.h>
 #include <linux/device.h>
@@ -59,8 +60,6 @@
 	OMAP3_ISP_IOMEM_CSI2C_REGS1,
 	OMAP3_ISP_IOMEM_CSIPHY1,
 	OMAP3_ISP_IOMEM_CSI2C_REGS2,
-	OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE,
-	OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL,
 	OMAP3_ISP_IOMEM_LAST
 };
 
@@ -93,14 +92,25 @@
 /* ISP2P: OMAP 36xx */
 #define ISP_REVISION_15_0		0xF0
 
+#define ISP_PHY_TYPE_3430		0
+#define ISP_PHY_TYPE_3630		1
+
+struct regmap;
+
 /*
  * struct isp_res_mapping - Map ISP io resources to ISP revision.
  * @isp_rev: ISP_REVISION_x_x
- * @map: bitmap for enum isp_mem_resources
+ * @offset: register offsets of various ISP sub-blocks
+ * @syscon_offset: offset of the syscon register for 343x / 3630
+ *	    (CONTROL_CSIRXFE / CONTROL_CAMERA_PHY_CTRL, respectively)
+ *	    from the syscon base address
+ * @phy_type: ISP_PHY_TYPE_{3430,3630}
  */
 struct isp_res_mapping {
 	u32 isp_rev;
-	u32 map;
+	u32 offset[OMAP3_ISP_IOMEM_LAST];
+	u32 syscon_offset;
+	u32 phy_type;
 };
 
 /*
@@ -122,7 +132,6 @@
 struct isp_xclk {
 	struct isp_device *isp;
 	struct clk_hw hw;
-	struct clk_lookup *lookup;
 	struct clk *clk;
 	enum isp_xclk_id id;
 
@@ -138,8 +147,11 @@
  * @irq_num: Currently used IRQ number.
  * @mmio_base: Array with kernel base addresses for ioremapped ISP register
  *             regions.
- * @mmio_base_phys: Array with physical L4 bus addresses for ISP register
- *                  regions.
+ * @mmio_hist_base_phys: Physical L4 bus address for ISP hist block register
+ *			 region.
+ * @syscon: Regmap for the syscon register space
+ * @syscon_offset: Offset of the CSIPHY control register in syscon
+ * @phy_type: ISP_PHY_TYPE_{3430,3630}
  * @mapping: IOMMU mapping
  * @stat_lock: Spinlock for handling statistics
  * @isp_mutex: Mutex for serializing requests to ISP.
@@ -166,6 +178,7 @@
  */
 struct isp_device {
 	struct v4l2_device v4l2_dev;
+	struct v4l2_async_notifier notifier;
 	struct media_device media_dev;
 	struct device *dev;
 	u32 revision;
@@ -175,7 +188,10 @@
 	unsigned int irq_num;
 
 	void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST];
-	unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST];
+	unsigned long mmio_hist_base_phys;
+	struct regmap *syscon;
+	u32 syscon_offset;
+	u32 phy_type;
 
 	struct dma_iommu_mapping *mapping;
 
@@ -209,6 +225,15 @@
 
 	unsigned int sbl_resources;
 	unsigned int subclk_resources;
+
+#define ISP_MAX_SUBDEVS		8
+	struct v4l2_subdev *subdevs[ISP_MAX_SUBDEVS];
+};
+
+struct isp_async_subdev {
+	struct v4l2_subdev *sd;
+	struct isp_bus_cfg bus;
+	struct v4l2_async_subdev asd;
 };
 
 #define v4l2_dev_to_isp_device(dev) \
@@ -229,7 +254,7 @@
 void omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe);
 void omap3isp_configure_bridge(struct isp_device *isp,
 			       enum ccdc_input_entity input,
-			       const struct isp_parallel_platform_data *pdata,
+			       const struct isp_parallel_cfg *buscfg,
 			       unsigned int shift, unsigned int bridge);
 
 struct isp_device *omap3isp_get(struct isp_device *isp);
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index 587489a..a6a61cc 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -32,7 +32,7 @@
 #define CCDC_MIN_HEIGHT		32
 
 static struct v4l2_mbus_framefmt *
-__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg,
 		  unsigned int pad, enum v4l2_subdev_format_whence which);
 
 static const unsigned int ccdc_fmts[] = {
@@ -958,11 +958,11 @@
 /*
  * ccdc_config_sync_if - Set CCDC sync interface configuration
  * @ccdc: Pointer to ISP CCDC device.
- * @pdata: Parallel interface platform data (may be NULL)
+ * @parcfg: Parallel interface platform data (may be NULL)
  * @data_size: Data size
  */
 static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
-				struct isp_parallel_platform_data *pdata,
+				struct isp_parallel_cfg *parcfg,
 				unsigned int data_size)
 {
 	struct isp_device *isp = to_isp_device(ccdc);
@@ -1000,19 +1000,19 @@
 		break;
 	}
 
-	if (pdata && pdata->data_pol)
+	if (parcfg && parcfg->data_pol)
 		syn_mode |= ISPCCDC_SYN_MODE_DATAPOL;
 
-	if (pdata && pdata->hs_pol)
+	if (parcfg && parcfg->hs_pol)
 		syn_mode |= ISPCCDC_SYN_MODE_HDPOL;
 
 	/* The polarity of the vertical sync signal output by the BT.656
 	 * decoder is not documented and seems to be active low.
 	 */
-	if ((pdata && pdata->vs_pol) || ccdc->bt656)
+	if ((parcfg && parcfg->vs_pol) || ccdc->bt656)
 		syn_mode |= ISPCCDC_SYN_MODE_VDPOL;
 
-	if (pdata && pdata->fld_pol)
+	if (parcfg && parcfg->fld_pol)
 		syn_mode |= ISPCCDC_SYN_MODE_FLDPOL;
 
 	isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
@@ -1115,7 +1115,7 @@
 static void ccdc_configure(struct isp_ccdc_device *ccdc)
 {
 	struct isp_device *isp = to_isp_device(ccdc);
-	struct isp_parallel_platform_data *pdata = NULL;
+	struct isp_parallel_cfg *parcfg = NULL;
 	struct v4l2_subdev *sensor;
 	struct v4l2_mbus_framefmt *format;
 	const struct v4l2_rect *crop;
@@ -1145,7 +1145,7 @@
 		if (!ret)
 			ccdc->bt656 = cfg.type == V4L2_MBUS_BT656;
 
-		pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv)
+		parcfg = &((struct isp_bus_cfg *)sensor->host_priv)
 			->bus.parallel;
 	}
 
@@ -1175,10 +1175,10 @@
 	else
 		bridge = ISPCTRL_PAR_BRIDGE_DISABLE;
 
-	omap3isp_configure_bridge(isp, ccdc->input, pdata, shift, bridge);
+	omap3isp_configure_bridge(isp, ccdc->input, parcfg, shift, bridge);
 
 	/* Configure the sync interface. */
-	ccdc_config_sync_if(ccdc, pdata, depth_out);
+	ccdc_config_sync_if(ccdc, parcfg, depth_out);
 
 	syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
 
@@ -1935,21 +1935,21 @@
 }
 
 static struct v4l2_mbus_framefmt *
-__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg,
 		  unsigned int pad, enum v4l2_subdev_format_whence which)
 {
 	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return v4l2_subdev_get_try_format(fh, pad);
+		return v4l2_subdev_get_try_format(&ccdc->subdev, cfg, pad);
 	else
 		return &ccdc->formats[pad];
 }
 
 static struct v4l2_rect *
-__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg,
 		enum v4l2_subdev_format_whence which)
 {
 	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF);
+		return v4l2_subdev_get_try_crop(&ccdc->subdev, cfg, CCDC_PAD_SOURCE_OF);
 	else
 		return &ccdc->crop;
 }
@@ -1957,12 +1957,12 @@
 /*
  * ccdc_try_format - Try video format on a pad
  * @ccdc: ISP CCDC device
- * @fh : V4L2 subdev file handle
+ * @cfg : V4L2 subdev pad configuration
  * @pad: Pad number
  * @fmt: Format
  */
 static void
-ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg,
 		unsigned int pad, struct v4l2_mbus_framefmt *fmt,
 		enum v4l2_subdev_format_whence which)
 {
@@ -1998,7 +1998,7 @@
 	case CCDC_PAD_SOURCE_OF:
 		pixelcode = fmt->code;
 		field = fmt->field;
-		*fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
+		*fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which);
 
 		/* In SYNC mode the bridge converts YUV formats from 2X8 to
 		 * 1X16. In BT.656 no such conversion occurs. As we don't know
@@ -2023,7 +2023,7 @@
 		}
 
 		/* Hardcode the output size to the crop rectangle size. */
-		crop = __ccdc_get_crop(ccdc, fh, which);
+		crop = __ccdc_get_crop(ccdc, cfg, which);
 		fmt->width = crop->width;
 		fmt->height = crop->height;
 
@@ -2040,7 +2040,7 @@
 		break;
 
 	case CCDC_PAD_SOURCE_VP:
-		*fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
+		*fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which);
 
 		/* The video port interface truncates the data to 10 bits. */
 		info = omap3isp_video_format_info(fmt->code);
@@ -2112,12 +2112,12 @@
 /*
  * ccdc_enum_mbus_code - Handle pixel format enumeration
  * @sd     : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
+ * @cfg : V4L2 subdev pad configuration
  * @code   : pointer to v4l2_subdev_mbus_code_enum structure
  * return -EINVAL or zero on success
  */
 static int ccdc_enum_mbus_code(struct v4l2_subdev *sd,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_mbus_code_enum *code)
 {
 	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
@@ -2132,8 +2132,8 @@
 		break;
 
 	case CCDC_PAD_SOURCE_OF:
-		format = __ccdc_get_format(ccdc, fh, code->pad,
-					   V4L2_SUBDEV_FORMAT_TRY);
+		format = __ccdc_get_format(ccdc, cfg, code->pad,
+					   code->which);
 
 		if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
 		    format->code == MEDIA_BUS_FMT_UYVY8_2X8) {
@@ -2163,8 +2163,8 @@
 		if (code->index != 0)
 			return -EINVAL;
 
-		format = __ccdc_get_format(ccdc, fh, code->pad,
-					   V4L2_SUBDEV_FORMAT_TRY);
+		format = __ccdc_get_format(ccdc, cfg, code->pad,
+					   code->which);
 
 		/* A pixel code equal to 0 means that the video port doesn't
 		 * support the input format. Don't enumerate any pixel code.
@@ -2183,7 +2183,7 @@
 }
 
 static int ccdc_enum_frame_size(struct v4l2_subdev *sd,
-				struct v4l2_subdev_fh *fh,
+				struct v4l2_subdev_pad_config *cfg,
 				struct v4l2_subdev_frame_size_enum *fse)
 {
 	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
@@ -2195,7 +2195,7 @@
 	format.code = fse->code;
 	format.width = 1;
 	format.height = 1;
-	ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	ccdc_try_format(ccdc, cfg, fse->pad, &format, fse->which);
 	fse->min_width = format.width;
 	fse->min_height = format.height;
 
@@ -2205,7 +2205,7 @@
 	format.code = fse->code;
 	format.width = -1;
 	format.height = -1;
-	ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	ccdc_try_format(ccdc, cfg, fse->pad, &format, fse->which);
 	fse->max_width = format.width;
 	fse->max_height = format.height;
 
@@ -2215,7 +2215,7 @@
 /*
  * ccdc_get_selection - Retrieve a selection rectangle on a pad
  * @sd: ISP CCDC V4L2 subdevice
- * @fh: V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @sel: Selection rectangle
  *
  * The only supported rectangles are the crop rectangles on the output formatter
@@ -2223,7 +2223,7 @@
  *
  * Return 0 on success or a negative error code otherwise.
  */
-static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_selection *sel)
 {
 	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
@@ -2239,12 +2239,12 @@
 		sel->r.width = INT_MAX;
 		sel->r.height = INT_MAX;
 
-		format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which);
+		format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which);
 		ccdc_try_crop(ccdc, format, &sel->r);
 		break;
 
 	case V4L2_SEL_TGT_CROP:
-		sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
+		sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which);
 		break;
 
 	default:
@@ -2257,7 +2257,7 @@
 /*
  * ccdc_set_selection - Set a selection rectangle on a pad
  * @sd: ISP CCDC V4L2 subdevice
- * @fh: V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @sel: Selection rectangle
  *
  * The only supported rectangle is the actual crop rectangle on the output
@@ -2265,7 +2265,7 @@
  *
  * Return 0 on success or a negative error code otherwise.
  */
-static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_selection *sel)
 {
 	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
@@ -2284,17 +2284,17 @@
 	 * rectangle.
 	 */
 	if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
-		sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
+		sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which);
 		return 0;
 	}
 
-	format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which);
+	format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which);
 	ccdc_try_crop(ccdc, format, &sel->r);
-	*__ccdc_get_crop(ccdc, fh, sel->which) = sel->r;
+	*__ccdc_get_crop(ccdc, cfg, sel->which) = sel->r;
 
 	/* Update the source format. */
-	format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which);
-	ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which);
+	format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, sel->which);
+	ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format, sel->which);
 
 	return 0;
 }
@@ -2302,19 +2302,19 @@
 /*
  * ccdc_get_format - Retrieve the video format on a pad
  * @sd : ISP CCDC V4L2 subdevice
- * @fh : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @fmt: Format
  *
  * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
  * to the format type.
  */
-static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *format;
 
-	format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which);
+	format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which);
 	if (format == NULL)
 		return -EINVAL;
 
@@ -2325,30 +2325,30 @@
 /*
  * ccdc_set_format - Set the video format on a pad
  * @sd : ISP CCDC V4L2 subdevice
- * @fh : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @fmt: Format
  *
  * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
  * to the format type.
  */
-static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *format;
 	struct v4l2_rect *crop;
 
-	format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which);
+	format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which);
 	if (format == NULL)
 		return -EINVAL;
 
-	ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which);
+	ccdc_try_format(ccdc, cfg, fmt->pad, &fmt->format, fmt->which);
 	*format = fmt->format;
 
 	/* Propagate the format from sink to source */
 	if (fmt->pad == CCDC_PAD_SINK) {
 		/* Reset the crop rectangle. */
-		crop = __ccdc_get_crop(ccdc, fh, fmt->which);
+		crop = __ccdc_get_crop(ccdc, cfg, fmt->which);
 		crop->left = 0;
 		crop->top = 0;
 		crop->width = fmt->format.width;
@@ -2357,16 +2357,16 @@
 		ccdc_try_crop(ccdc, &fmt->format, crop);
 
 		/* Update the source formats. */
-		format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF,
+		format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF,
 					   fmt->which);
 		*format = fmt->format;
-		ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format,
+		ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format,
 				fmt->which);
 
-		format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP,
+		format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_VP,
 					   fmt->which);
 		*format = fmt->format;
-		ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format,
+		ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_VP, format,
 				fmt->which);
 	}
 
@@ -2417,11 +2417,11 @@
 
 	/* We've got a parallel sensor here. */
 	if (ccdc->input == CCDC_INPUT_PARALLEL) {
-		struct isp_parallel_platform_data *pdata =
-			&((struct isp_v4l2_subdevs_group *)
+		struct isp_parallel_cfg *parcfg =
+			&((struct isp_bus_cfg *)
 			  media_entity_to_v4l2_subdev(link->source->entity)
 			  ->host_priv)->bus.parallel;
-		parallel_shift = pdata->data_lane_shift * 2;
+		parallel_shift = parcfg->data_lane_shift * 2;
 	} else {
 		parallel_shift = 0;
 	}
@@ -2453,7 +2453,7 @@
 	format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
 	format.format.width = 4096;
 	format.format.height = 4096;
-	ccdc_set_format(sd, fh, &format);
+	ccdc_set_format(sd, fh ? fh->pad : NULL, &format);
 
 	return 0;
 }
diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index f4aedb3..38e6a97 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
@@ -201,14 +201,14 @@
 /*
  * ccp2_phyif_config - Initialize CCP2 phy interface config
  * @ccp2: Pointer to ISP CCP2 device
- * @pdata: CCP2 platform data
+ * @buscfg: CCP2 platform data
  *
  * Configure the CCP2 physical interface module from platform data.
  *
  * Returns -EIO if strobe is chosen in CSI1 mode, or 0 on success.
  */
 static int ccp2_phyif_config(struct isp_ccp2_device *ccp2,
-			     const struct isp_ccp2_platform_data *pdata)
+			     const struct isp_ccp2_cfg *buscfg)
 {
 	struct isp_device *isp = to_isp_device(ccp2);
 	u32 val;
@@ -218,16 +218,16 @@
 			    ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE;
 	/* Data/strobe physical layer */
 	BIT_SET(val, ISPCCP2_CTRL_PHY_SEL_SHIFT, ISPCCP2_CTRL_PHY_SEL_MASK,
-		pdata->phy_layer);
+		buscfg->phy_layer);
 	BIT_SET(val, ISPCCP2_CTRL_INV_SHIFT, ISPCCP2_CTRL_INV_MASK,
-		pdata->strobe_clk_pol);
+		buscfg->strobe_clk_pol);
 	isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
 
 	val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
 	if (!(val & ISPCCP2_CTRL_MODE)) {
-		if (pdata->ccp2_mode == ISP_CCP2_MODE_CCP2)
+		if (buscfg->ccp2_mode == ISP_CCP2_MODE_CCP2)
 			dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n");
-		if (pdata->phy_layer == ISP_CCP2_PHY_DATA_STROBE)
+		if (buscfg->phy_layer == ISP_CCP2_PHY_DATA_STROBE)
 			/* Strobe mode requires CCP2 */
 			return -EIO;
 	}
@@ -347,7 +347,7 @@
  */
 static int ccp2_if_configure(struct isp_ccp2_device *ccp2)
 {
-	const struct isp_v4l2_subdevs_group *pdata;
+	const struct isp_bus_cfg *buscfg;
 	struct v4l2_mbus_framefmt *format;
 	struct media_pad *pad;
 	struct v4l2_subdev *sensor;
@@ -358,20 +358,20 @@
 
 	pad = media_entity_remote_pad(&ccp2->pads[CCP2_PAD_SINK]);
 	sensor = media_entity_to_v4l2_subdev(pad->entity);
-	pdata = sensor->host_priv;
+	buscfg = sensor->host_priv;
 
-	ret = ccp2_phyif_config(ccp2, &pdata->bus.ccp2);
+	ret = ccp2_phyif_config(ccp2, &buscfg->bus.ccp2);
 	if (ret < 0)
 		return ret;
 
-	ccp2_vp_config(ccp2, pdata->bus.ccp2.vpclk_div + 1);
+	ccp2_vp_config(ccp2, buscfg->bus.ccp2.vpclk_div + 1);
 
 	v4l2_subdev_call(sensor, sensor, g_skip_top_lines, &lines);
 
 	format = &ccp2->formats[CCP2_PAD_SINK];
 
 	ccp2->if_cfg.data_start = lines;
-	ccp2->if_cfg.crc = pdata->bus.ccp2.crc;
+	ccp2->if_cfg.crc = buscfg->bus.ccp2.crc;
 	ccp2->if_cfg.format = format->code;
 	ccp2->if_cfg.data_size = format->height;
 
@@ -611,17 +611,17 @@
 /*
  * __ccp2_get_format - helper function for getting ccp2 format
  * @ccp2  : Pointer to ISP CCP2 device
- * @fh    : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @pad   : pad number
  * @which : wanted subdev format
  * return format structure or NULL on error
  */
 static struct v4l2_mbus_framefmt *
-__ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_fh *fh,
+__ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_pad_config *cfg,
 		     unsigned int pad, enum v4l2_subdev_format_whence which)
 {
 	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return v4l2_subdev_get_try_format(fh, pad);
+		return v4l2_subdev_get_try_format(&ccp2->subdev, cfg, pad);
 	else
 		return &ccp2->formats[pad];
 }
@@ -629,13 +629,13 @@
 /*
  * ccp2_try_format - Handle try format by pad subdev method
  * @ccp2  : Pointer to ISP CCP2 device
- * @fh    : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @pad   : pad num
  * @fmt   : pointer to v4l2 mbus format structure
  * @which : wanted subdev format
  */
 static void ccp2_try_format(struct isp_ccp2_device *ccp2,
-			       struct v4l2_subdev_fh *fh, unsigned int pad,
+			       struct v4l2_subdev_pad_config *cfg, unsigned int pad,
 			       struct v4l2_mbus_framefmt *fmt,
 			       enum v4l2_subdev_format_whence which)
 {
@@ -669,7 +669,7 @@
 		 * When CCP2 write to memory feature will be added this
 		 * should be changed properly.
 		 */
-		format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, which);
+		format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SINK, which);
 		memcpy(fmt, format, sizeof(*fmt));
 		fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
 		break;
@@ -682,12 +682,12 @@
 /*
  * ccp2_enum_mbus_code - Handle pixel format enumeration
  * @sd     : pointer to v4l2 subdev structure
- * @fh     : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @code   : pointer to v4l2_subdev_mbus_code_enum structure
  * return -EINVAL or zero on success
  */
 static int ccp2_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
@@ -702,8 +702,8 @@
 		if (code->index != 0)
 			return -EINVAL;
 
-		format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK,
-					      V4L2_SUBDEV_FORMAT_TRY);
+		format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SINK,
+					      code->which);
 		code->code = format->code;
 	}
 
@@ -711,7 +711,7 @@
 }
 
 static int ccp2_enum_frame_size(struct v4l2_subdev *sd,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
 {
 	struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
@@ -723,7 +723,7 @@
 	format.code = fse->code;
 	format.width = 1;
 	format.height = 1;
-	ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	ccp2_try_format(ccp2, cfg, fse->pad, &format, fse->which);
 	fse->min_width = format.width;
 	fse->min_height = format.height;
 
@@ -733,7 +733,7 @@
 	format.code = fse->code;
 	format.width = -1;
 	format.height = -1;
-	ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	ccp2_try_format(ccp2, cfg, fse->pad, &format, fse->which);
 	fse->max_width = format.width;
 	fse->max_height = format.height;
 
@@ -743,17 +743,17 @@
 /*
  * ccp2_get_format - Handle get format by pads subdev method
  * @sd    : pointer to v4l2 subdev structure
- * @fh    : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @fmt   : pointer to v4l2 subdev format structure
  * return -EINVAL or zero on success
  */
-static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *fmt)
 {
 	struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *format;
 
-	format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which);
+	format = __ccp2_get_format(ccp2, cfg, fmt->pad, fmt->which);
 	if (format == NULL)
 		return -EINVAL;
 
@@ -764,29 +764,29 @@
 /*
  * ccp2_set_format - Handle set format by pads subdev method
  * @sd    : pointer to v4l2 subdev structure
- * @fh    : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @fmt   : pointer to v4l2 subdev format structure
  * returns zero
  */
-static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *fmt)
 {
 	struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *format;
 
-	format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which);
+	format = __ccp2_get_format(ccp2, cfg, fmt->pad, fmt->which);
 	if (format == NULL)
 		return -EINVAL;
 
-	ccp2_try_format(ccp2, fh, fmt->pad, &fmt->format, fmt->which);
+	ccp2_try_format(ccp2, cfg, fmt->pad, &fmt->format, fmt->which);
 	*format = fmt->format;
 
 	/* Propagate the format from sink to source */
 	if (fmt->pad == CCP2_PAD_SINK) {
-		format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SOURCE,
+		format = __ccp2_get_format(ccp2, cfg, CCP2_PAD_SOURCE,
 					   fmt->which);
 		*format = fmt->format;
-		ccp2_try_format(ccp2, fh, CCP2_PAD_SOURCE, format, fmt->which);
+		ccp2_try_format(ccp2, cfg, CCP2_PAD_SOURCE, format, fmt->which);
 	}
 
 	return 0;
@@ -811,7 +811,7 @@
 	format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
 	format.format.width = 4096;
 	format.format.height = 4096;
-	ccp2_set_format(sd, fh, &format);
+	ccp2_set_format(sd, fh ? fh->pad : NULL, &format);
 
 	return 0;
 }
diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c
index 09c686d..a78338d 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/omap3isp/ispcsi2.c
@@ -548,7 +548,8 @@
 
 static int csi2_configure(struct isp_csi2_device *csi2)
 {
-	const struct isp_v4l2_subdevs_group *pdata;
+	struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity);
+	const struct isp_bus_cfg *buscfg;
 	struct isp_device *isp = csi2->isp;
 	struct isp_csi2_timing_cfg *timing = &csi2->timing[0];
 	struct v4l2_subdev *sensor;
@@ -565,14 +566,19 @@
 
 	pad = media_entity_remote_pad(&csi2->pads[CSI2_PAD_SINK]);
 	sensor = media_entity_to_v4l2_subdev(pad->entity);
-	pdata = sensor->host_priv;
+	buscfg = sensor->host_priv;
 
 	csi2->frame_skip = 0;
 	v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
 
-	csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
+	csi2->ctrl.vp_out_ctrl =
+		clamp_t(unsigned int, pipe->l3_ick / pipe->external_rate - 1,
+			1, 3);
+	dev_dbg(isp->dev, "%s: l3_ick %lu, external_rate %u, vp_out_ctrl %u\n",
+		__func__, pipe->l3_ick,  pipe->external_rate,
+		csi2->ctrl.vp_out_ctrl);
 	csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE;
-	csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
+	csi2->ctrl.ecc_enable = buscfg->bus.csi2.crc;
 
 	timing->ionum = 1;
 	timing->force_rx_mode = 1;
@@ -829,17 +835,17 @@
  */
 
 static struct v4l2_mbus_framefmt *
-__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh,
+__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg,
 		  unsigned int pad, enum v4l2_subdev_format_whence which)
 {
 	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return v4l2_subdev_get_try_format(fh, pad);
+		return v4l2_subdev_get_try_format(&csi2->subdev, cfg, pad);
 	else
 		return &csi2->formats[pad];
 }
 
 static void
-csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh,
+csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg,
 		unsigned int pad, struct v4l2_mbus_framefmt *fmt,
 		enum v4l2_subdev_format_whence which)
 {
@@ -869,7 +875,7 @@
 		 * compression.
 		 */
 		pixelcode = fmt->code;
-		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which);
+		format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, which);
 		memcpy(fmt, format, sizeof(*fmt));
 
 		/*
@@ -890,12 +896,12 @@
 /*
  * csi2_enum_mbus_code - Handle pixel format enumeration
  * @sd     : pointer to v4l2 subdev structure
- * @fh     : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @code   : pointer to v4l2_subdev_mbus_code_enum structure
  * return -EINVAL or zero on success
  */
 static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_mbus_code_enum *code)
 {
 	struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
@@ -908,8 +914,8 @@
 
 		code->code = csi2_input_fmts[code->index];
 	} else {
-		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK,
-					   V4L2_SUBDEV_FORMAT_TRY);
+		format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK,
+					   code->which);
 		switch (code->index) {
 		case 0:
 			/* Passthrough sink pad code */
@@ -932,7 +938,7 @@
 }
 
 static int csi2_enum_frame_size(struct v4l2_subdev *sd,
-				struct v4l2_subdev_fh *fh,
+				struct v4l2_subdev_pad_config *cfg,
 				struct v4l2_subdev_frame_size_enum *fse)
 {
 	struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
@@ -944,7 +950,7 @@
 	format.code = fse->code;
 	format.width = 1;
 	format.height = 1;
-	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	csi2_try_format(csi2, cfg, fse->pad, &format, fse->which);
 	fse->min_width = format.width;
 	fse->min_height = format.height;
 
@@ -954,7 +960,7 @@
 	format.code = fse->code;
 	format.width = -1;
 	format.height = -1;
-	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	csi2_try_format(csi2, cfg, fse->pad, &format, fse->which);
 	fse->max_width = format.width;
 	fse->max_height = format.height;
 
@@ -964,17 +970,17 @@
 /*
  * csi2_get_format - Handle get format by pads subdev method
  * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @fmt: pointer to v4l2 subdev format structure
  * return -EINVAL or zero on success
  */
-static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *format;
 
-	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
+	format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which);
 	if (format == NULL)
 		return -EINVAL;
 
@@ -985,29 +991,29 @@
 /*
  * csi2_set_format - Handle set format by pads subdev method
  * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @fmt: pointer to v4l2 subdev format structure
  * return -EINVAL or zero on success
  */
-static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *format;
 
-	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
+	format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which);
 	if (format == NULL)
 		return -EINVAL;
 
-	csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which);
+	csi2_try_format(csi2, cfg, fmt->pad, &fmt->format, fmt->which);
 	*format = fmt->format;
 
 	/* Propagate the format from sink to source */
 	if (fmt->pad == CSI2_PAD_SINK) {
-		format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE,
+		format = __csi2_get_format(csi2, cfg, CSI2_PAD_SOURCE,
 					   fmt->which);
 		*format = fmt->format;
-		csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which);
+		csi2_try_format(csi2, cfg, CSI2_PAD_SOURCE, format, fmt->which);
 	}
 
 	return 0;
@@ -1032,7 +1038,7 @@
 	format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
 	format.format.width = 4096;
 	format.format.height = 4096;
-	csi2_set_format(sd, fh, &format);
+	csi2_set_format(sd, fh ? fh->pad : NULL, &format);
 
 	return 0;
 }
diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c
index e033f22..495447d 100644
--- a/drivers/media/platform/omap3isp/ispcsiphy.c
+++ b/drivers/media/platform/omap3isp/ispcsiphy.c
@@ -16,6 +16,7 @@
 
 #include <linux/delay.h>
 #include <linux/device.h>
+#include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 
 #include "isp.h"
@@ -26,10 +27,11 @@
 				    enum isp_interface_type iface,
 				    bool ccp2_strobe)
 {
-	u32 reg = isp_reg_readl(
-		phy->isp, OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, 0);
+	u32 reg;
 	u32 shift, mode;
 
+	regmap_read(phy->isp->syscon, phy->isp->syscon_offset, &reg);
+
 	switch (iface) {
 	default:
 	/* Should not happen in practice, but let's keep the compiler happy. */
@@ -63,8 +65,7 @@
 	reg &= ~(OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_MASK << shift);
 	reg |= mode << shift;
 
-	isp_reg_writel(phy->isp, reg,
-		       OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, 0);
+	regmap_write(phy->isp->syscon, phy->isp->syscon_offset, reg);
 }
 
 static void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on,
@@ -78,16 +79,14 @@
 		return;
 
 	if (!on) {
-		isp_reg_writel(phy->isp, 0,
-			       OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, 0);
+		regmap_write(phy->isp->syscon, phy->isp->syscon_offset, 0);
 		return;
 	}
 
 	if (ccp2_strobe)
 		csirxfe |= OMAP343X_CONTROL_CSIRXFE_SELFORM;
 
-	isp_reg_writel(phy->isp, csirxfe,
-		       OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, 0);
+	regmap_write(phy->isp->syscon, phy->isp->syscon_offset, csirxfe);
 }
 
 /*
@@ -106,10 +105,9 @@
 			       enum isp_interface_type iface, bool on,
 			       bool ccp2_strobe)
 {
-	if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL]
-	    && on)
+	if (phy->isp->phy_type == ISP_PHY_TYPE_3630 && on)
 		return csiphy_routing_cfg_3630(phy, iface, ccp2_strobe);
-	if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE])
+	if (phy->isp->phy_type == ISP_PHY_TYPE_3430)
 		return csiphy_routing_cfg_3430(phy, iface, on, ccp2_strobe);
 }
 
@@ -168,18 +166,25 @@
 {
 	struct isp_csi2_device *csi2 = phy->csi2;
 	struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity);
-	struct isp_v4l2_subdevs_group *subdevs = pipe->external->host_priv;
+	struct isp_bus_cfg *buscfg = pipe->external->host_priv;
 	struct isp_csiphy_lanes_cfg *lanes;
 	int csi2_ddrclk_khz;
 	unsigned int used_lanes = 0;
 	unsigned int i;
 	u32 reg;
 
-	if (subdevs->interface == ISP_INTERFACE_CCP2B_PHY1
-	    || subdevs->interface == ISP_INTERFACE_CCP2B_PHY2)
-		lanes = &subdevs->bus.ccp2.lanecfg;
+	if (!buscfg) {
+		struct isp_async_subdev *isd =
+			container_of(pipe->external->asd,
+				     struct isp_async_subdev, asd);
+		buscfg = &isd->bus;
+	}
+
+	if (buscfg->interface == ISP_INTERFACE_CCP2B_PHY1
+	    || buscfg->interface == ISP_INTERFACE_CCP2B_PHY2)
+		lanes = &buscfg->bus.ccp2.lanecfg;
 	else
-		lanes = &subdevs->bus.csi2.lanecfg;
+		lanes = &buscfg->bus.csi2.lanecfg;
 
 	/* Clock and data lanes verification */
 	for (i = 0; i < phy->num_data_lanes; i++) {
@@ -203,8 +208,8 @@
 	 * issue since the MPU power domain is forced on whilst the
 	 * ISP is in use.
 	 */
-	csiphy_routing_cfg(phy, subdevs->interface, true,
-			   subdevs->bus.ccp2.phy_layer);
+	csiphy_routing_cfg(phy, buscfg->interface, true,
+			   buscfg->bus.ccp2.phy_layer);
 
 	/* DPHY timing configuration */
 	/* CSI-2 is DDR and we only count used lanes. */
@@ -302,11 +307,10 @@
 		struct isp_csi2_device *csi2 = phy->csi2;
 		struct isp_pipeline *pipe =
 			to_isp_pipeline(&csi2->subdev.entity);
-		struct isp_v4l2_subdevs_group *subdevs =
-			pipe->external->host_priv;
+		struct isp_bus_cfg *buscfg = pipe->external->host_priv;
 
-		csiphy_routing_cfg(phy, subdevs->interface, false,
-				   subdevs->bus.ccp2.phy_layer);
+		csiphy_routing_cfg(phy, buscfg->interface, false,
+				   buscfg->bus.ccp2.phy_layer);
 		csiphy_power_autoswitch_enable(phy, false);
 		csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF);
 		regulator_disable(phy->vdd);
diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/omap3isp/isph3a_aewb.c
index b208c54..ccaf92f 100644
--- a/drivers/media/platform/omap3isp/isph3a_aewb.c
+++ b/drivers/media/platform/omap3isp/isph3a_aewb.c
@@ -297,7 +297,6 @@
 
 	aewb->ops = &h3a_aewb_ops;
 	aewb->priv = aewb_cfg;
-	aewb->dma_ch = -1;
 	aewb->event_type = V4L2_EVENT_OMAP3ISP_AEWB;
 	aewb->isp = isp;
 
diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/omap3isp/isph3a_af.c
index 8a83e19..92937f7 100644
--- a/drivers/media/platform/omap3isp/isph3a_af.c
+++ b/drivers/media/platform/omap3isp/isph3a_af.c
@@ -360,7 +360,6 @@
 
 	af->ops = &h3a_af_ops;
 	af->priv = af_cfg;
-	af->dma_ch = -1;
 	af->event_type = V4L2_EVENT_OMAP3ISP_AF;
 	af->isp = isp;
 
diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c
index ce822c3..7138b04 100644
--- a/drivers/media/platform/omap3isp/isphist.c
+++ b/drivers/media/platform/omap3isp/isphist.c
@@ -16,20 +16,18 @@
  */
 
 #include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/omap-dmaengine.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
-#include <linux/device.h>
 
 #include "isp.h"
 #include "ispreg.h"
 #include "isphist.h"
 
-#define OMAP24XX_DMA_NO_DEVICE		0
-
 #define HIST_CONFIG_DMA	1
 
-#define HIST_USING_DMA(hist) ((hist)->dma_ch >= 0)
-
 /*
  * hist_reset_mem - clear Histogram memory before start stats engine.
  */
@@ -62,20 +60,6 @@
 	hist->wait_acc_frames = conf->num_acc_frames;
 }
 
-static void hist_dma_config(struct ispstat *hist)
-{
-	struct isp_device *isp = hist->isp;
-
-	hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32;
-	hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT;
-	hist->dma_config.frame_count = 1;
-	hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT;
-	hist->dma_config.src_start = isp->mmio_base_phys[OMAP3_ISP_IOMEM_HIST]
-				   + ISPHIST_DATA;
-	hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC;
-	hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
-}
-
 /*
  * hist_setup_regs - Helper function to update Histogram registers.
  */
@@ -176,17 +160,12 @@
 						& ISPHIST_PCR_BUSY;
 }
 
-static void hist_dma_cb(int lch, u16 ch_status, void *data)
+static void hist_dma_cb(void *data)
 {
 	struct ispstat *hist = data;
 
-	if (ch_status & ~OMAP_DMA_BLOCK_IRQ) {
-		dev_dbg(hist->isp->dev, "hist: DMA error. status = 0x%04x\n",
-			ch_status);
-		omap_stop_dma(lch);
-		hist_reset_mem(hist);
-		atomic_set(&hist->buf_err, 1);
-	}
+	/* FIXME: The DMA engine API can't report transfer errors :-/ */
+
 	isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT,
 		    ISPHIST_CNT_CLEAR);
 
@@ -198,24 +177,57 @@
 static int hist_buf_dma(struct ispstat *hist)
 {
 	dma_addr_t dma_addr = hist->active_buf->dma_addr;
+	struct dma_async_tx_descriptor *tx;
+	struct dma_slave_config cfg;
+	dma_cookie_t cookie;
+	int ret;
 
 	if (unlikely(!dma_addr)) {
 		dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n");
-		hist_reset_mem(hist);
-		return STAT_NO_BUF;
+		goto error;
 	}
 
 	isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR);
 	isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT,
 		    ISPHIST_CNT_CLEAR);
 	omap3isp_flush(hist->isp);
-	hist->dma_config.dst_start = dma_addr;
-	hist->dma_config.elem_count = hist->buf_size / sizeof(u32);
-	omap_set_dma_params(hist->dma_ch, &hist->dma_config);
 
-	omap_start_dma(hist->dma_ch);
+	memset(&cfg, 0, sizeof(cfg));
+	cfg.src_addr = hist->isp->mmio_hist_base_phys + ISPHIST_DATA;
+	cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	cfg.src_maxburst = hist->buf_size / 4;
+
+	ret = dmaengine_slave_config(hist->dma_ch, &cfg);
+	if (ret < 0) {
+		dev_dbg(hist->isp->dev,
+			"hist: DMA slave configuration failed\n");
+		goto error;
+	}
+
+	tx = dmaengine_prep_slave_single(hist->dma_ch, dma_addr,
+					 hist->buf_size, DMA_DEV_TO_MEM,
+					 DMA_CTRL_ACK);
+	if (tx == NULL) {
+		dev_dbg(hist->isp->dev,
+			"hist: DMA slave preparation failed\n");
+		goto error;
+	}
+
+	tx->callback = hist_dma_cb;
+	tx->callback_param = hist;
+	cookie = tx->tx_submit(tx);
+	if (dma_submit_error(cookie)) {
+		dev_dbg(hist->isp->dev, "hist: DMA submission failed\n");
+		goto error;
+	}
+
+	dma_async_issue_pending(hist->dma_ch);
 
 	return STAT_BUF_WAITING_DMA;
+
+error:
+	hist_reset_mem(hist);
+	return STAT_NO_BUF;
 }
 
 static int hist_buf_pio(struct ispstat *hist)
@@ -272,7 +284,7 @@
 	if (--(hist->wait_acc_frames))
 		return STAT_NO_BUF;
 
-	if (HIST_USING_DMA(hist))
+	if (hist->dma_ch)
 		ret = hist_buf_dma(hist);
 	else
 		ret = hist_buf_pio(hist);
@@ -473,18 +485,28 @@
 
 	hist->isp = isp;
 
-	if (HIST_CONFIG_DMA)
-		ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST",
-				       hist_dma_cb, hist, &hist->dma_ch);
-	if (ret) {
-		if (HIST_CONFIG_DMA)
-			dev_warn(isp->dev, "hist: DMA request channel failed. "
-					   "Using PIO only.\n");
-		hist->dma_ch = -1;
-	} else {
-		dev_dbg(isp->dev, "hist: DMA channel = %d\n", hist->dma_ch);
-		hist_dma_config(hist);
-		omap_enable_dma_irq(hist->dma_ch, OMAP_DMA_BLOCK_IRQ);
+	if (HIST_CONFIG_DMA) {
+		struct platform_device *pdev = to_platform_device(isp->dev);
+		struct resource *res;
+		unsigned int sig = 0;
+		dma_cap_mask_t mask;
+
+		dma_cap_zero(mask);
+		dma_cap_set(DMA_SLAVE, mask);
+
+		res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+						   "hist");
+		if (res)
+			sig = res->start;
+
+		hist->dma_ch = dma_request_slave_channel_compat(mask,
+				omap_dma_filter_fn, &sig, isp->dev, "hist");
+		if (!hist->dma_ch)
+			dev_warn(isp->dev,
+				 "hist: DMA channel request failed, using PIO\n");
+		else
+			dev_dbg(isp->dev, "hist: using DMA channel %s\n",
+				dma_chan_name(hist->dma_ch));
 	}
 
 	hist->ops = &hist_ops;
@@ -493,8 +515,8 @@
 
 	ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops);
 	if (ret) {
-		if (HIST_USING_DMA(hist))
-			omap_free_dma(hist->dma_ch);
+		if (hist->dma_ch)
+			dma_release_channel(hist->dma_ch);
 	}
 
 	return ret;
@@ -505,7 +527,10 @@
  */
 void omap3isp_hist_cleanup(struct isp_device *isp)
 {
-	if (HIST_USING_DMA(&isp->isp_hist))
-		omap_free_dma(isp->isp_hist.dma_ch);
-	omap3isp_stat_cleanup(&isp->isp_hist);
+	struct ispstat *hist = &isp->isp_hist;
+
+	if (hist->dma_ch)
+		dma_release_channel(hist->dma_ch);
+
+	omap3isp_stat_cleanup(hist);
 }
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
index dd9eed4..15cb254 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/drivers/media/platform/omap3isp/isppreview.c
@@ -1686,21 +1686,21 @@
 }
 
 static struct v4l2_mbus_framefmt *
-__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
+__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_pad_config *cfg,
 		     unsigned int pad, enum v4l2_subdev_format_whence which)
 {
 	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return v4l2_subdev_get_try_format(fh, pad);
+		return v4l2_subdev_get_try_format(&prev->subdev, cfg, pad);
 	else
 		return &prev->formats[pad];
 }
 
 static struct v4l2_rect *
-__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
+__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_pad_config *cfg,
 		   enum v4l2_subdev_format_whence which)
 {
 	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK);
+		return v4l2_subdev_get_try_crop(&prev->subdev, cfg, PREV_PAD_SINK);
 	else
 		return &prev->crop;
 }
@@ -1727,7 +1727,7 @@
 /*
  * preview_try_format - Validate a format
  * @prev: ISP preview engine
- * @fh: V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @pad: pad number
  * @fmt: format to be validated
  * @which: try/active format selector
@@ -1736,7 +1736,7 @@
  * engine limits and the format and crop rectangles on other pads.
  */
 static void preview_try_format(struct isp_prev_device *prev,
-			       struct v4l2_subdev_fh *fh, unsigned int pad,
+			       struct v4l2_subdev_pad_config *cfg, unsigned int pad,
 			       struct v4l2_mbus_framefmt *fmt,
 			       enum v4l2_subdev_format_whence which)
 {
@@ -1777,7 +1777,7 @@
 
 	case PREV_PAD_SOURCE:
 		pixelcode = fmt->code;
-		*fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which);
+		*fmt = *__preview_get_format(prev, cfg, PREV_PAD_SINK, which);
 
 		switch (pixelcode) {
 		case MEDIA_BUS_FMT_YUYV8_1X16:
@@ -1795,7 +1795,7 @@
 		 * is not supported yet, hardcode the output size to the crop
 		 * rectangle size.
 		 */
-		crop = __preview_get_crop(prev, fh, which);
+		crop = __preview_get_crop(prev, cfg, which);
 		fmt->width = crop->width;
 		fmt->height = crop->height;
 
@@ -1864,12 +1864,12 @@
 /*
  * preview_enum_mbus_code - Handle pixel format enumeration
  * @sd     : pointer to v4l2 subdev structure
- * @fh     : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @code   : pointer to v4l2_subdev_mbus_code_enum structure
  * return -EINVAL or zero on success
  */
 static int preview_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	switch (code->pad) {
@@ -1893,7 +1893,7 @@
 }
 
 static int preview_enum_frame_size(struct v4l2_subdev *sd,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
 {
 	struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
@@ -1905,7 +1905,7 @@
 	format.code = fse->code;
 	format.width = 1;
 	format.height = 1;
-	preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	preview_try_format(prev, cfg, fse->pad, &format, fse->which);
 	fse->min_width = format.width;
 	fse->min_height = format.height;
 
@@ -1915,7 +1915,7 @@
 	format.code = fse->code;
 	format.width = -1;
 	format.height = -1;
-	preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	preview_try_format(prev, cfg, fse->pad, &format, fse->which);
 	fse->max_width = format.width;
 	fse->max_height = format.height;
 
@@ -1925,7 +1925,7 @@
 /*
  * preview_get_selection - Retrieve a selection rectangle on a pad
  * @sd: ISP preview V4L2 subdevice
- * @fh: V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @sel: Selection rectangle
  *
  * The only supported rectangles are the crop rectangles on the sink pad.
@@ -1933,7 +1933,7 @@
  * Return 0 on success or a negative error code otherwise.
  */
 static int preview_get_selection(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_selection *sel)
 {
 	struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
@@ -1949,13 +1949,13 @@
 		sel->r.width = INT_MAX;
 		sel->r.height = INT_MAX;
 
-		format = __preview_get_format(prev, fh, PREV_PAD_SINK,
+		format = __preview_get_format(prev, cfg, PREV_PAD_SINK,
 					      sel->which);
 		preview_try_crop(prev, format, &sel->r);
 		break;
 
 	case V4L2_SEL_TGT_CROP:
-		sel->r = *__preview_get_crop(prev, fh, sel->which);
+		sel->r = *__preview_get_crop(prev, cfg, sel->which);
 		break;
 
 	default:
@@ -1968,7 +1968,7 @@
 /*
  * preview_set_selection - Set a selection rectangle on a pad
  * @sd: ISP preview V4L2 subdevice
- * @fh: V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @sel: Selection rectangle
  *
  * The only supported rectangle is the actual crop rectangle on the sink pad.
@@ -1976,7 +1976,7 @@
  * Return 0 on success or a negative error code otherwise.
  */
 static int preview_set_selection(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_selection *sel)
 {
 	struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
@@ -1995,17 +1995,17 @@
 	 * rectangle.
 	 */
 	if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
-		sel->r = *__preview_get_crop(prev, fh, sel->which);
+		sel->r = *__preview_get_crop(prev, cfg, sel->which);
 		return 0;
 	}
 
-	format = __preview_get_format(prev, fh, PREV_PAD_SINK, sel->which);
+	format = __preview_get_format(prev, cfg, PREV_PAD_SINK, sel->which);
 	preview_try_crop(prev, format, &sel->r);
-	*__preview_get_crop(prev, fh, sel->which) = sel->r;
+	*__preview_get_crop(prev, cfg, sel->which) = sel->r;
 
 	/* Update the source format. */
-	format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, sel->which);
-	preview_try_format(prev, fh, PREV_PAD_SOURCE, format, sel->which);
+	format = __preview_get_format(prev, cfg, PREV_PAD_SOURCE, sel->which);
+	preview_try_format(prev, cfg, PREV_PAD_SOURCE, format, sel->which);
 
 	return 0;
 }
@@ -2013,17 +2013,17 @@
 /*
  * preview_get_format - Handle get format by pads subdev method
  * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @fmt: pointer to v4l2 subdev format structure
  * return -EINVAL or zero on success
  */
-static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *fmt)
 {
 	struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *format;
 
-	format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
+	format = __preview_get_format(prev, cfg, fmt->pad, fmt->which);
 	if (format == NULL)
 		return -EINVAL;
 
@@ -2034,28 +2034,28 @@
 /*
  * preview_set_format - Handle set format by pads subdev method
  * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @fmt: pointer to v4l2 subdev format structure
  * return -EINVAL or zero on success
  */
-static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *fmt)
 {
 	struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *format;
 	struct v4l2_rect *crop;
 
-	format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
+	format = __preview_get_format(prev, cfg, fmt->pad, fmt->which);
 	if (format == NULL)
 		return -EINVAL;
 
-	preview_try_format(prev, fh, fmt->pad, &fmt->format, fmt->which);
+	preview_try_format(prev, cfg, fmt->pad, &fmt->format, fmt->which);
 	*format = fmt->format;
 
 	/* Propagate the format from sink to source */
 	if (fmt->pad == PREV_PAD_SINK) {
 		/* Reset the crop rectangle. */
-		crop = __preview_get_crop(prev, fh, fmt->which);
+		crop = __preview_get_crop(prev, cfg, fmt->which);
 		crop->left = 0;
 		crop->top = 0;
 		crop->width = fmt->format.width;
@@ -2064,9 +2064,9 @@
 		preview_try_crop(prev, &fmt->format, crop);
 
 		/* Update the source format. */
-		format = __preview_get_format(prev, fh, PREV_PAD_SOURCE,
+		format = __preview_get_format(prev, cfg, PREV_PAD_SOURCE,
 					      fmt->which);
-		preview_try_format(prev, fh, PREV_PAD_SOURCE, format,
+		preview_try_format(prev, cfg, PREV_PAD_SOURCE, format,
 				   fmt->which);
 	}
 
@@ -2093,7 +2093,7 @@
 	format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
 	format.format.width = 4096;
 	format.format.height = 4096;
-	preview_set_format(sd, fh, &format);
+	preview_set_format(sd, fh ? fh->pad : NULL, &format);
 
 	return 0;
 }
diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c
index 2b9bc48..7cfb43d 100644
--- a/drivers/media/platform/omap3isp/ispresizer.c
+++ b/drivers/media/platform/omap3isp/ispresizer.c
@@ -112,16 +112,16 @@
  * __resizer_get_format - helper function for getting resizer format
  * @res   : pointer to resizer private structure
  * @pad   : pad number
- * @fh    : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @which : wanted subdev format
  * return zero
  */
 static struct v4l2_mbus_framefmt *
-__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh,
+__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_pad_config *cfg,
 		     unsigned int pad, enum v4l2_subdev_format_whence which)
 {
 	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return v4l2_subdev_get_try_format(fh, pad);
+		return v4l2_subdev_get_try_format(&res->subdev, cfg, pad);
 	else
 		return &res->formats[pad];
 }
@@ -129,15 +129,15 @@
 /*
  * __resizer_get_crop - helper function for getting resizer crop rectangle
  * @res   : pointer to resizer private structure
- * @fh    : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @which : wanted subdev crop rectangle
  */
 static struct v4l2_rect *
-__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_fh *fh,
+__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_pad_config *cfg,
 		   enum v4l2_subdev_format_whence which)
 {
 	if (which == V4L2_SUBDEV_FORMAT_TRY)
-		return v4l2_subdev_get_try_crop(fh, RESZ_PAD_SINK);
+		return v4l2_subdev_get_try_crop(&res->subdev, cfg, RESZ_PAD_SINK);
 	else
 		return &res->crop.request;
 }
@@ -1215,7 +1215,7 @@
 /*
  * resizer_get_selection - Retrieve a selection rectangle on a pad
  * @sd: ISP resizer V4L2 subdevice
- * @fh: V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @sel: Selection rectangle
  *
  * The only supported rectangles are the crop rectangles on the sink pad.
@@ -1223,7 +1223,7 @@
  * Return 0 on success or a negative error code otherwise.
  */
 static int resizer_get_selection(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_selection *sel)
 {
 	struct isp_res_device *res = v4l2_get_subdevdata(sd);
@@ -1234,9 +1234,9 @@
 	if (sel->pad != RESZ_PAD_SINK)
 		return -EINVAL;
 
-	format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
+	format_sink = __resizer_get_format(res, cfg, RESZ_PAD_SINK,
 					   sel->which);
-	format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
+	format_source = __resizer_get_format(res, cfg, RESZ_PAD_SOURCE,
 					     sel->which);
 
 	switch (sel->target) {
@@ -1251,7 +1251,7 @@
 		break;
 
 	case V4L2_SEL_TGT_CROP:
-		sel->r = *__resizer_get_crop(res, fh, sel->which);
+		sel->r = *__resizer_get_crop(res, cfg, sel->which);
 		resizer_calc_ratios(res, &sel->r, format_source, &ratio);
 		break;
 
@@ -1265,7 +1265,7 @@
 /*
  * resizer_set_selection - Set a selection rectangle on a pad
  * @sd: ISP resizer V4L2 subdevice
- * @fh: V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @sel: Selection rectangle
  *
  * The only supported rectangle is the actual crop rectangle on the sink pad.
@@ -1276,7 +1276,7 @@
  * Return 0 on success or a negative error code otherwise.
  */
 static int resizer_set_selection(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_selection *sel)
 {
 	struct isp_res_device *res = v4l2_get_subdevdata(sd);
@@ -1290,9 +1290,9 @@
 	    sel->pad != RESZ_PAD_SINK)
 		return -EINVAL;
 
-	format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
+	format_sink = __resizer_get_format(res, cfg, RESZ_PAD_SINK,
 					   sel->which);
-	format_source = *__resizer_get_format(res, fh, RESZ_PAD_SOURCE,
+	format_source = *__resizer_get_format(res, cfg, RESZ_PAD_SOURCE,
 					      sel->which);
 
 	dev_dbg(isp->dev, "%s(%s): req %ux%u -> (%d,%d)/%ux%u -> %ux%u\n",
@@ -1310,7 +1310,7 @@
 	 * stored the mangled rectangle.
 	 */
 	resizer_try_crop(format_sink, &format_source, &sel->r);
-	*__resizer_get_crop(res, fh, sel->which) = sel->r;
+	*__resizer_get_crop(res, cfg, sel->which) = sel->r;
 	resizer_calc_ratios(res, &sel->r, &format_source, &ratio);
 
 	dev_dbg(isp->dev, "%s(%s): got %ux%u -> (%d,%d)/%ux%u -> %ux%u\n",
@@ -1320,7 +1320,7 @@
 		format_source.width, format_source.height);
 
 	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
-		*__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) =
+		*__resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which) =
 			format_source;
 		return 0;
 	}
@@ -1331,7 +1331,7 @@
 	 */
 	spin_lock_irqsave(&res->lock, flags);
 
-	*__resizer_get_format(res, fh, RESZ_PAD_SOURCE, sel->which) =
+	*__resizer_get_format(res, cfg, RESZ_PAD_SOURCE, sel->which) =
 		format_source;
 
 	res->ratio = ratio;
@@ -1368,13 +1368,13 @@
 /*
  * resizer_try_format - Handle try format by pad subdev method
  * @res   : ISP resizer device
- * @fh    : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @pad   : pad num
  * @fmt   : pointer to v4l2 format structure
  * @which : wanted subdev format
  */
 static void resizer_try_format(struct isp_res_device *res,
-			       struct v4l2_subdev_fh *fh, unsigned int pad,
+			       struct v4l2_subdev_pad_config *cfg, unsigned int pad,
 			       struct v4l2_mbus_framefmt *fmt,
 			       enum v4l2_subdev_format_whence which)
 {
@@ -1395,10 +1395,10 @@
 		break;
 
 	case RESZ_PAD_SOURCE:
-		format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which);
+		format = __resizer_get_format(res, cfg, RESZ_PAD_SINK, which);
 		fmt->code = format->code;
 
-		crop = *__resizer_get_crop(res, fh, which);
+		crop = *__resizer_get_crop(res, cfg, which);
 		resizer_calc_ratios(res, &crop, fmt, &ratio);
 		break;
 	}
@@ -1410,12 +1410,12 @@
 /*
  * resizer_enum_mbus_code - Handle pixel format enumeration
  * @sd     : pointer to v4l2 subdev structure
- * @fh     : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @code   : pointer to v4l2_subdev_mbus_code_enum structure
  * return -EINVAL or zero on success
  */
 static int resizer_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
 	struct isp_res_device *res = v4l2_get_subdevdata(sd);
@@ -1430,8 +1430,8 @@
 		if (code->index != 0)
 			return -EINVAL;
 
-		format = __resizer_get_format(res, fh, RESZ_PAD_SINK,
-					      V4L2_SUBDEV_FORMAT_TRY);
+		format = __resizer_get_format(res, cfg, RESZ_PAD_SINK,
+					      code->which);
 		code->code = format->code;
 	}
 
@@ -1439,7 +1439,7 @@
 }
 
 static int resizer_enum_frame_size(struct v4l2_subdev *sd,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
 {
 	struct isp_res_device *res = v4l2_get_subdevdata(sd);
@@ -1451,7 +1451,7 @@
 	format.code = fse->code;
 	format.width = 1;
 	format.height = 1;
-	resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	resizer_try_format(res, cfg, fse->pad, &format, fse->which);
 	fse->min_width = format.width;
 	fse->min_height = format.height;
 
@@ -1461,7 +1461,7 @@
 	format.code = fse->code;
 	format.width = -1;
 	format.height = -1;
-	resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+	resizer_try_format(res, cfg, fse->pad, &format, fse->which);
 	fse->max_width = format.width;
 	fse->max_height = format.height;
 
@@ -1471,17 +1471,17 @@
 /*
  * resizer_get_format - Handle get format by pads subdev method
  * @sd    : pointer to v4l2 subdev structure
- * @fh    : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @fmt   : pointer to v4l2 subdev format structure
  * return -EINVAL or zero on success
  */
-static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *fmt)
 {
 	struct isp_res_device *res = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *format;
 
-	format = __resizer_get_format(res, fh, fmt->pad, fmt->which);
+	format = __resizer_get_format(res, cfg, fmt->pad, fmt->which);
 	if (format == NULL)
 		return -EINVAL;
 
@@ -1492,37 +1492,37 @@
 /*
  * resizer_set_format - Handle set format by pads subdev method
  * @sd    : pointer to v4l2 subdev structure
- * @fh    : V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  * @fmt   : pointer to v4l2 subdev format structure
  * return -EINVAL or zero on success
  */
-static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_format *fmt)
 {
 	struct isp_res_device *res = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *format;
 	struct v4l2_rect *crop;
 
-	format = __resizer_get_format(res, fh, fmt->pad, fmt->which);
+	format = __resizer_get_format(res, cfg, fmt->pad, fmt->which);
 	if (format == NULL)
 		return -EINVAL;
 
-	resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which);
+	resizer_try_format(res, cfg, fmt->pad, &fmt->format, fmt->which);
 	*format = fmt->format;
 
 	if (fmt->pad == RESZ_PAD_SINK) {
 		/* reset crop rectangle */
-		crop = __resizer_get_crop(res, fh, fmt->which);
+		crop = __resizer_get_crop(res, cfg, fmt->which);
 		crop->left = 0;
 		crop->top = 0;
 		crop->width = fmt->format.width;
 		crop->height = fmt->format.height;
 
 		/* Propagate the format from sink to source */
-		format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
+		format = __resizer_get_format(res, cfg, RESZ_PAD_SOURCE,
 					      fmt->which);
 		*format = fmt->format;
-		resizer_try_format(res, fh, RESZ_PAD_SOURCE, format,
+		resizer_try_format(res, cfg, RESZ_PAD_SOURCE, format,
 				   fmt->which);
 	}
 
@@ -1573,7 +1573,7 @@
 	format.format.code = MEDIA_BUS_FMT_YUYV8_1X16;
 	format.format.width = 4096;
 	format.format.height = 4096;
-	resizer_set_format(sd, fh, &format);
+	resizer_set_format(sd, fh ? fh->pad : NULL, &format);
 
 	return 0;
 }
diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c
index a94e834..20434e8 100644
--- a/drivers/media/platform/omap3isp/ispstat.c
+++ b/drivers/media/platform/omap3isp/ispstat.c
@@ -21,7 +21,7 @@
 
 #include "isp.h"
 
-#define ISP_STAT_USES_DMAENGINE(stat)	((stat)->dma_ch >= 0)
+#define ISP_STAT_USES_DMAENGINE(stat)	((stat)->dma_ch != NULL)
 
 /*
  * MAGIC_SIZE must always be the greatest common divisor of
diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h
index b32b296..b79380d 100644
--- a/drivers/media/platform/omap3isp/ispstat.h
+++ b/drivers/media/platform/omap3isp/ispstat.h
@@ -20,7 +20,6 @@
 
 #include <linux/types.h>
 #include <linux/omap3isp.h>
-#include <linux/omap-dma.h>
 #include <media/v4l2-event.h>
 
 #include "isp.h"
@@ -33,6 +32,7 @@
 #define STAT_NO_BUF		1	/* An error has occurred */
 #define STAT_BUF_WAITING_DMA	2	/* Histogram only: DMA is running */
 
+struct dma_chan;
 struct ispstat;
 
 struct ispstat_buffer {
@@ -96,7 +96,6 @@
 	u8 inc_config;
 	atomic_t buf_err;
 	enum ispstat_state_t state;	/* enabling/disabling state */
-	struct omap_dma_channel_params dma_config;
 	struct isp_device *isp;
 	void *priv;		/* pointer to priv config struct */
 	void *recover_priv;	/* pointer to recover priv configuration */
@@ -110,7 +109,7 @@
 	u32 frame_number;
 	u32 buf_size;
 	u32 buf_alloc_size;
-	int dma_ch;
+	struct dma_chan *dma_ch;
 	unsigned long event_type;
 	struct ispstat_buffer *buf;
 	struct ispstat_buffer *active_buf;
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index b463fe1..d285af1 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -452,7 +452,6 @@
 	enum isp_pipeline_state state;
 	struct isp_buffer *buf;
 	unsigned long flags;
-	struct timespec ts;
 
 	spin_lock_irqsave(&video->irqlock, flags);
 	if (WARN_ON(list_empty(&video->dmaqueue))) {
@@ -465,9 +464,7 @@
 	list_del(&buf->irqlist);
 	spin_unlock_irqrestore(&video->irqlock, flags);
 
-	ktime_get_ts(&ts);
-	buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
-	buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+	v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
 
 	/* Do frame number propagation only if this is the output video node.
 	 * Frame number either comes from the CSI receivers or it gets
@@ -524,7 +521,6 @@
 
 	buf = list_first_entry(&video->dmaqueue, struct isp_buffer,
 			       irqlist);
-	buf->vb.state = VB2_BUF_STATE_ACTIVE;
 
 	spin_unlock_irqrestore(&video->irqlock, flags);
 
@@ -602,10 +598,13 @@
 	strlcpy(cap->card, video->video.name, sizeof(cap->card));
 	strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
 
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
+		| V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS;
+
 	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+		cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
 	else
-		cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+		cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
 
 	return 0;
 }
@@ -1019,7 +1018,7 @@
 
 	pipe->entities = 0;
 
-	if (video->isp->pdata->set_constraints)
+	if (video->isp->pdata && video->isp->pdata->set_constraints)
 		video->isp->pdata->set_constraints(video->isp, true);
 	pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]);
 	pipe->max_rate = pipe->l3_ick;
@@ -1101,7 +1100,7 @@
 err_check_format:
 	media_entity_pipeline_stop(&video->video.entity);
 err_pipeline_start:
-	if (video->isp->pdata->set_constraints)
+	if (video->isp->pdata && video->isp->pdata->set_constraints)
 		video->isp->pdata->set_constraints(video->isp, false);
 	/* The DMA queue must be emptied here, otherwise CCDC interrupts that
 	 * will get triggered the next time the CCDC is powered up will try to
@@ -1162,7 +1161,7 @@
 	video->queue = NULL;
 	video->error = false;
 
-	if (video->isp->pdata->set_constraints)
+	if (video->isp->pdata && video->isp->pdata->set_constraints)
 		video->isp->pdata->set_constraints(video->isp, false);
 	media_entity_pipeline_stop(&video->video.entity);
 
@@ -1323,14 +1322,8 @@
 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 ret;
+	return vb2_mmap(&vfh->queue, vma);
 }
 
 static struct v4l2_file_operations isp_video_fops = {
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index aa40c82..f6a61b9 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -536,24 +536,12 @@
 	spin_unlock_irqrestore(&camif->slock, flags);
 }
 
-static void camif_lock(struct vb2_queue *vq)
-{
-	struct camif_vp *vp = vb2_get_drv_priv(vq);
-	mutex_lock(&vp->camif->lock);
-}
-
-static void camif_unlock(struct vb2_queue *vq)
-{
-	struct camif_vp *vp = vb2_get_drv_priv(vq);
-	mutex_unlock(&vp->camif->lock);
-}
-
 static const struct vb2_ops s3c_camif_qops = {
 	.queue_setup	 = queue_setup,
 	.buf_prepare	 = buffer_prepare,
 	.buf_queue	 = buffer_queue,
-	.wait_prepare	 = camif_unlock,
-	.wait_finish	 = camif_lock,
+	.wait_prepare	 = vb2_ops_wait_prepare,
+	.wait_finish	 = vb2_ops_wait_finish,
 	.start_streaming = start_streaming,
 	.stop_streaming	 = stop_streaming,
 };
@@ -1161,6 +1149,7 @@
 	q->buf_struct_size = sizeof(struct camif_buffer);
 	q->drv_priv = vp;
 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &vp->camif->lock;
 
 	ret = vb2_queue_init(q);
 	if (ret)
@@ -1230,7 +1219,7 @@
  */
 
 static int s3c_camif_subdev_enum_mbus_code(struct v4l2_subdev *sd,
-					struct v4l2_subdev_fh *fh,
+					struct v4l2_subdev_pad_config *cfg,
 					struct v4l2_subdev_mbus_code_enum *code)
 {
 	if (code->index >= ARRAY_SIZE(camif_mbus_formats))
@@ -1241,14 +1230,14 @@
 }
 
 static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
-				    struct v4l2_subdev_fh *fh,
+				    struct v4l2_subdev_pad_config *cfg,
 				    struct v4l2_subdev_format *fmt)
 {
 	struct camif_dev *camif = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *mf = &fmt->format;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 		fmt->format = *mf;
 		return 0;
 	}
@@ -1308,7 +1297,7 @@
 }
 
 static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
-				    struct v4l2_subdev_fh *fh,
+				    struct v4l2_subdev_pad_config *cfg,
 				    struct v4l2_subdev_format *fmt)
 {
 	struct camif_dev *camif = v4l2_get_subdevdata(sd);
@@ -1336,7 +1325,7 @@
 	__camif_subdev_try_format(camif, mf, fmt->pad);
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 		*mf = fmt->format;
 		mutex_unlock(&camif->lock);
 		return 0;
@@ -1375,7 +1364,7 @@
 }
 
 static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd,
-					  struct v4l2_subdev_fh *fh,
+					  struct v4l2_subdev_pad_config *cfg,
 					  struct v4l2_subdev_selection *sel)
 {
 	struct camif_dev *camif = v4l2_get_subdevdata(sd);
@@ -1388,7 +1377,7 @@
 		return -EINVAL;
 
 	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
-		sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad);
+		sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
 		return 0;
 	}
 
@@ -1462,7 +1451,7 @@
 }
 
 static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd,
-					  struct v4l2_subdev_fh *fh,
+					  struct v4l2_subdev_pad_config *cfg,
 					  struct v4l2_subdev_selection *sel)
 {
 	struct camif_dev *camif = v4l2_get_subdevdata(sd);
@@ -1476,7 +1465,7 @@
 	__camif_try_crop(camif, &sel->r);
 
 	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
-		*v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r;
+		*v4l2_subdev_get_try_crop(sd, cfg, sel->pad) = sel->r;
 	} else {
 		unsigned long flags;
 		unsigned int i;
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index 47ba8fb..ec3e124 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -12,7 +12,6 @@
 
 #include <linux/module.h>
 #include <linux/fs.h>
-#include <linux/version.h>
 #include <linux/timer.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index 12f7452..bfbf157 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -621,6 +621,7 @@
 			return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY;
 		return ctx->subsampling;
 	case SJPEG_EXYNOS3250:
+	case SJPEG_EXYNOS5420:
 		if (ctx->subsampling > 3)
 			return V4L2_JPEG_CHROMA_SUBSAMPLING_411;
 		return exynos3250_decoded_subsampling[ctx->subsampling];
@@ -1142,13 +1143,13 @@
 	w_step = 1 << walign;
 	h_step = 1 << halign;
 
-	if (ctx->jpeg->variant->version == SJPEG_EXYNOS3250) {
+	if (ctx->jpeg->variant->hw3250_compat) {
 		/*
 		 * Rightmost and bottommost pixels are cropped by the
-		 * Exynos3250 JPEG IP for RGB formats, for the specific
-		 * width and height values respectively. This assignment
-		 * will result in v4l_bound_align_image returning dimensions
-		 * reduced by 1 for the aforementioned cases.
+		 * Exynos3250/compatible JPEG IP for RGB formats, for the
+		 * specific width and height values respectively. This
+		 * assignment will result in v4l_bound_align_image returning
+		 * dimensions reduced by 1 for the aforementioned cases.
 		 */
 		if (w_step == 4 && ((width & 3) == 1)) {
 			wmax = width;
@@ -1384,12 +1385,12 @@
 
 	/*
 	 * Prevent downscaling to YUV420 format by more than 2
-	 * for Exynos3250 SoC as it produces broken raw image
+	 * for Exynos3250/compatible SoC as it produces broken raw image
 	 * in such cases.
 	 */
 	if (ct->mode == S5P_JPEG_DECODE &&
 	    f_type == FMT_TYPE_CAPTURE &&
-	    ct->jpeg->variant->version == SJPEG_EXYNOS3250 &&
+	    ct->jpeg->variant->hw3250_compat &&
 	    pix->pixelformat == V4L2_PIX_FMT_YUV420 &&
 	    ct->scale_factor > 2) {
 		scale_rect.width = ct->out_q.w / 2;
@@ -1569,12 +1570,12 @@
 	if (s->target == V4L2_SEL_TGT_COMPOSE) {
 		if (ctx->mode != S5P_JPEG_DECODE)
 			return -EINVAL;
-		if (ctx->jpeg->variant->version == SJPEG_EXYNOS3250)
+		if (ctx->jpeg->variant->hw3250_compat)
 			ret = exynos3250_jpeg_try_downscale(ctx, rect);
 	} else if (s->target == V4L2_SEL_TGT_CROP) {
 		if (ctx->mode != S5P_JPEG_ENCODE)
 			return -EINVAL;
-		if (ctx->jpeg->variant->version == SJPEG_EXYNOS3250)
+		if (ctx->jpeg->variant->hw3250_compat)
 			ret = exynos3250_jpeg_try_crop(ctx, rect);
 	}
 
@@ -1604,8 +1605,9 @@
 	case SJPEG_S5P:
 		return 0;
 	case SJPEG_EXYNOS3250:
+	case SJPEG_EXYNOS5420:
 		/*
-		 * The exynos3250 device can produce JPEG image only
+		 * The exynos3250/compatible device can produce JPEG image only
 		 * of 4:4:4 subsampling when given RGB32 source image.
 		 */
 		if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB32)
@@ -1624,7 +1626,7 @@
 	}
 
 	/*
-	 * The exynos4x12 and exynos3250 devices require resulting
+	 * The exynos4x12 and exynos3250/compatible devices require resulting
 	 * jpeg subsampling not to be lower than the input raw image
 	 * subsampling.
 	 */
@@ -1842,9 +1844,12 @@
 	struct s5p_jpeg *jpeg = ctx->jpeg;
 	struct s5p_jpeg_fmt *fmt;
 	struct vb2_buffer *vb;
-	struct s5p_jpeg_addr jpeg_addr;
+	struct s5p_jpeg_addr jpeg_addr = {};
 	u32 pix_size, padding_bytes = 0;
 
+	jpeg_addr.cb = 0;
+	jpeg_addr.cr = 0;
+
 	pix_size = ctx->cap_q.w * ctx->cap_q.h;
 
 	if (ctx->mode == S5P_JPEG_ENCODE) {
@@ -1943,7 +1948,7 @@
 	struct s5p_jpeg *jpeg = ctx->jpeg;
 	struct s5p_jpeg_fmt *fmt;
 	struct vb2_buffer *vb;
-	struct s5p_jpeg_addr jpeg_addr;
+	struct s5p_jpeg_addr jpeg_addr = {};
 	u32 pix_size;
 
 	pix_size = ctx->cap_q.w * ctx->cap_q.h;
@@ -2017,6 +2022,16 @@
 		exynos3250_jpeg_qtbl(jpeg->regs, 2, 1);
 		exynos3250_jpeg_qtbl(jpeg->regs, 3, 1);
 
+		/*
+		 * Some SoCs require setting Huffman tables before each run
+		 */
+		if (jpeg->variant->htbl_reinit) {
+			s5p_jpeg_set_hdctbl(jpeg->regs);
+			s5p_jpeg_set_hdctblg(jpeg->regs);
+			s5p_jpeg_set_hactbl(jpeg->regs);
+			s5p_jpeg_set_hactblg(jpeg->regs);
+		}
+
 		/* Y, Cb, Cr use Huffman table 0 */
 		exynos3250_jpeg_htbl_ac(jpeg->regs, 1);
 		exynos3250_jpeg_htbl_dc(jpeg->regs, 1);
@@ -2660,13 +2675,12 @@
 	/*
 	 * JPEG IP allows storing two Huffman tables for each component.
 	 * We fill table 0 for each component and do this here only
-	 * for S5PC210 and Exynos3250 SoCs. Exynos4x12 SoC requires
-	 * programming its Huffman tables each time the encoding process
-	 * is initialized, and thus it is accomplished in the device_run
-	 * callback of m2m_ops.
+	 * for S5PC210 and Exynos3250 SoCs. Exynos4x12 and Exynos542x SoC
+	 * require programming their Huffman tables each time the encoding
+	 * process is initialized, and thus it is accomplished in the
+	 * device_run callback of m2m_ops.
 	 */
-	if (jpeg->variant->version == SJPEG_S5P ||
-	    jpeg->variant->version == SJPEG_EXYNOS3250) {
+	if (!jpeg->variant->htbl_reinit) {
 		s5p_jpeg_set_hdctbl(jpeg->regs);
 		s5p_jpeg_set_hdctblg(jpeg->regs);
 		s5p_jpeg_set_hactbl(jpeg->regs);
@@ -2714,6 +2728,7 @@
 	.jpeg_irq	= exynos3250_jpeg_irq,
 	.m2m_ops	= &exynos3250_jpeg_m2m_ops,
 	.fmt_ver_flag	= SJPEG_FMT_FLAG_EXYNOS3250,
+	.hw3250_compat	= 1,
 };
 
 static struct s5p_jpeg_variant exynos4_jpeg_drvdata = {
@@ -2721,6 +2736,16 @@
 	.jpeg_irq	= exynos4_jpeg_irq,
 	.m2m_ops	= &exynos4_jpeg_m2m_ops,
 	.fmt_ver_flag	= SJPEG_FMT_FLAG_EXYNOS4,
+	.htbl_reinit	= 1,
+};
+
+static struct s5p_jpeg_variant exynos5420_jpeg_drvdata = {
+	.version	= SJPEG_EXYNOS5420,
+	.jpeg_irq	= exynos3250_jpeg_irq,		/* intentionally 3250 */
+	.m2m_ops	= &exynos3250_jpeg_m2m_ops,	/* intentionally 3250 */
+	.fmt_ver_flag	= SJPEG_FMT_FLAG_EXYNOS3250,	/* intentionally 3250 */
+	.hw3250_compat	= 1,
+	.htbl_reinit	= 1,
 };
 
 static const struct of_device_id samsung_jpeg_match[] = {
@@ -2736,6 +2761,9 @@
 	}, {
 		.compatible = "samsung,exynos4212-jpeg",
 		.data = &exynos4_jpeg_drvdata,
+	}, {
+		.compatible = "samsung,exynos5420-jpeg",
+		.data = &exynos5420_jpeg_drvdata,
 	},
 	{},
 };
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h
index 764b32d..7d9a9ed 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h
@@ -67,10 +67,12 @@
 #define SJPEG_SUBSAMPLING_420	0x22
 
 /* Version numbers */
-
-#define SJPEG_S5P		1
-#define SJPEG_EXYNOS3250	2
-#define SJPEG_EXYNOS4		3
+enum sjpeg_version {
+	SJPEG_S5P,
+	SJPEG_EXYNOS3250,
+	SJPEG_EXYNOS4,
+	SJPEG_EXYNOS5420,
+};
 
 enum exynos4_jpeg_result {
 	OK_ENC_OR_DEC,
@@ -130,6 +132,8 @@
 struct s5p_jpeg_variant {
 	unsigned int		version;
 	unsigned int		fmt_ver_flag;
+	unsigned int		hw3250_compat:1;
+	unsigned int		htbl_reinit:1;
 	struct v4l2_m2m_ops	*m2m_ops;
 	irqreturn_t		(*jpeg_irq)(int irq, void *priv);
 };
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c
index e8c2cad..0974b9a 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c
@@ -20,7 +20,7 @@
 
 void exynos3250_jpeg_reset(void __iomem *regs)
 {
-	u32 reg = 0;
+	u32 reg = 1;
 	int count = 1000;
 
 	writel(1, regs + EXYNOS3250_SW_RESET);
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
index e3b8e67..b5f20e7 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
@@ -51,18 +51,6 @@
 	writel(reg, regs + S5P_JPGCMOD);
 }
 
-void s5p_jpeg_input_raw_y16(void __iomem *regs, bool y16)
-{
-	unsigned long reg;
-
-	reg = readl(regs + S5P_JPGCMOD);
-	if (y16)
-		reg |= S5P_MODE_Y16;
-	else
-		reg &= ~S5P_MODE_Y16_MASK;
-	writel(reg, regs + S5P_JPGCMOD);
-}
-
 void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode)
 {
 	unsigned long reg, m;
@@ -208,26 +196,6 @@
 	writel(reg, regs + S5P_JPGINTSE);
 }
 
-void s5p_jpeg_timer_enable(void __iomem *regs, unsigned long val)
-{
-	unsigned long reg;
-
-	reg = readl(regs + S5P_JPG_TIMER_SE);
-	reg |= S5P_TIMER_INT_EN;
-	reg &= ~S5P_TIMER_INIT_MASK;
-	reg |= val & S5P_TIMER_INIT_MASK;
-	writel(reg, regs + S5P_JPG_TIMER_SE);
-}
-
-void s5p_jpeg_timer_disable(void __iomem *regs)
-{
-	unsigned long reg;
-
-	reg = readl(regs + S5P_JPG_TIMER_SE);
-	reg &= ~S5P_TIMER_INT_EN_MASK;
-	writel(reg, regs + S5P_JPG_TIMER_SE);
-}
-
 int s5p_jpeg_timer_stat(void __iomem *regs)
 {
 	return (int)((readl(regs + S5P_JPG_TIMER_ST) & S5P_TIMER_INT_STAT_MASK)
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h
index c11ebe8..f208fa3 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h
@@ -29,7 +29,6 @@
 void s5p_jpeg_reset(void __iomem *regs);
 void s5p_jpeg_poweron(void __iomem *regs);
 void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode);
-void s5p_jpeg_input_raw_y16(void __iomem *regs, bool y16);
 void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode);
 void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode);
 unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs);
@@ -42,8 +41,6 @@
 void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable);
 void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable);
 void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl);
-void s5p_jpeg_timer_enable(void __iomem *regs, unsigned long val);
-void s5p_jpeg_timer_disable(void __iomem *regs);
 int s5p_jpeg_timer_stat(void __iomem *regs);
 void s5p_jpeg_clear_timer_stat(void __iomem *regs);
 void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index fbfdf03..8333fbc 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -810,6 +810,7 @@
 	q = &ctx->vq_dst;
 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
 	q->drv_priv = &ctx->fh;
+	q->lock = &dev->mfc_mutex;
 	if (vdev == dev->vfd_dec) {
 		q->io_modes = VB2_MMAP;
 		q->ops = get_dec_queue_ops();
@@ -832,6 +833,7 @@
 	q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
 	q->io_modes = VB2_MMAP;
 	q->drv_priv = &ctx->fh;
+	q->lock = &dev->mfc_mutex;
 	if (vdev == dev->vfd_dec) {
 		q->io_modes = VB2_MMAP;
 		q->ops = get_dec_queue_ops();
@@ -842,6 +844,13 @@
 		ret = -ENOENT;
 		goto err_queue_init;
 	}
+	/* One way to indicate end-of-stream for MFC is to set the
+	 * bytesused == 0. However by default videobuf2 handles bytesused
+	 * equal to 0 as a special case and changes its value to the size
+	 * of the buffer. Set the allow_zero_bytesused flag so that videobuf2
+	 * will keep the value of bytesused intact.
+	 */
+	q->allow_zero_bytesused = 1;
 	q->mem_ops = &vb2_dma_contig_memops;
 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
 	ret = vb2_queue_init(q);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
index 15f7663..24262bb 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -29,7 +29,7 @@
 
 /* Offset base used to differentiate between CAPTURE and OUTPUT
 *  while mmaping */
-#define DST_QUEUE_OFF_BASE      (TASK_SIZE / 2)
+#define DST_QUEUE_OFF_BASE	(1 << 30)
 
 #define MFC_BANK1_ALLOC_CTX	0
 #define MFC_BANK2_ALLOC_CTX	1
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index c6c3452..aebe4fd 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -18,7 +18,6 @@
 #include <linux/platform_device.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
-#include <linux/version.h>
 #include <linux/videodev2.h>
 #include <linux/workqueue.h>
 #include <media/v4l2-ctrls.h>
@@ -813,7 +812,7 @@
 	unsigned long flags;
 
 	switch (cmd->cmd) {
-	case V4L2_ENC_CMD_STOP:
+	case V4L2_DEC_CMD_STOP:
 		if (cmd->flags != 0)
 			return -EINVAL;
 
@@ -944,22 +943,6 @@
 	return 0;
 }
 
-static void s5p_mfc_unlock(struct vb2_queue *q)
-{
-	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
-	struct s5p_mfc_dev *dev = ctx->dev;
-
-	mutex_unlock(&dev->mfc_mutex);
-}
-
-static void s5p_mfc_lock(struct vb2_queue *q)
-{
-	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
-	struct s5p_mfc_dev *dev = ctx->dev;
-
-	mutex_lock(&dev->mfc_mutex);
-}
-
 static int s5p_mfc_buf_init(struct vb2_buffer *vb)
 {
 	struct vb2_queue *vq = vb->vb2_queue;
@@ -1107,8 +1090,8 @@
 
 static struct vb2_ops s5p_mfc_dec_qops = {
 	.queue_setup		= s5p_mfc_queue_setup,
-	.wait_prepare		= s5p_mfc_unlock,
-	.wait_finish		= s5p_mfc_lock,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
 	.buf_init		= s5p_mfc_buf_init,
 	.start_streaming	= s5p_mfc_start_streaming,
 	.stop_streaming		= s5p_mfc_stop_streaming,
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index bd64f1d..e65993f 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -19,7 +19,6 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/sched.h>
-#include <linux/version.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-event.h>
 #include <linux/workqueue.h>
@@ -1867,22 +1866,6 @@
 	return 0;
 }
 
-static void s5p_mfc_unlock(struct vb2_queue *q)
-{
-	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
-	struct s5p_mfc_dev *dev = ctx->dev;
-
-	mutex_unlock(&dev->mfc_mutex);
-}
-
-static void s5p_mfc_lock(struct vb2_queue *q)
-{
-	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
-	struct s5p_mfc_dev *dev = ctx->dev;
-
-	mutex_lock(&dev->mfc_mutex);
-}
-
 static int s5p_mfc_buf_init(struct vb2_buffer *vb)
 {
 	struct vb2_queue *vq = vb->vb2_queue;
@@ -2052,8 +2035,8 @@
 
 static struct vb2_ops s5p_mfc_enc_qops = {
 	.queue_setup		= s5p_mfc_queue_setup,
-	.wait_prepare		= s5p_mfc_unlock,
-	.wait_finish		= s5p_mfc_lock,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
 	.buf_init		= s5p_mfc_buf_init,
 	.buf_prepare		= s5p_mfc_buf_prepare,
 	.start_streaming	= s5p_mfc_start_streaming,
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
index de2b8c6..22dfb3e 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
@@ -302,7 +302,7 @@
 	void (*write_info)(struct s5p_mfc_ctx *ctx, unsigned int data,
 			unsigned int ofs);
 	unsigned int (*read_info)(struct s5p_mfc_ctx *ctx,
-			unsigned int ofs);
+			unsigned long ofs);
 	int (*get_dspl_y_adr)(struct s5p_mfc_dev *dev);
 	int (*get_dec_y_adr)(struct s5p_mfc_dev *dev);
 	int (*get_dspl_status)(struct s5p_mfc_dev *dev);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
index 0c4fcf2..b09bcd1 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
@@ -263,15 +263,15 @@
 static void s5p_mfc_write_info_v5(struct s5p_mfc_ctx *ctx, unsigned int data,
 			unsigned int ofs)
 {
-	writel(data, (volatile void __iomem *)(ctx->shm.virt + ofs));
+	writel(data, (void *)(ctx->shm.virt + ofs));
 	wmb();
 }
 
 static unsigned int s5p_mfc_read_info_v5(struct s5p_mfc_ctx *ctx,
-				unsigned int ofs)
+				unsigned long ofs)
 {
 	rmb();
-	return readl((volatile void __iomem *)(ctx->shm.virt + ofs));
+	return readl((void *)(ctx->shm.virt + ofs));
 }
 
 static void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
index 9aea179..cefad18 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -1340,11 +1340,7 @@
 	/* FMO_ASO_CTRL - 0: Enable, 1: Disable */
 	reg |= (fmo_aso_ctrl << S5P_FIMV_D_OPT_FMO_ASO_CTRL_MASK_V6);
 
-	/* When user sets desplay_delay to 0,
-	 * It works as "display_delay enable" and delay set to 0.
-	 * If user wants display_delay disable, It should be
-	 * set to negative value. */
-	if (ctx->display_delay >= 0) {
+	if (ctx->display_delay_enable) {
 		reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6);
 		writel(ctx->display_delay, mfc_regs->d_display_delay);
 	}
@@ -1856,17 +1852,17 @@
 		unsigned int ofs)
 {
 	s5p_mfc_clock_on();
-	writel(data, (volatile void __iomem *)((unsigned long)ofs));
+	writel(data, (void *)((unsigned long)ofs));
 	s5p_mfc_clock_off();
 }
 
 static unsigned int
-s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs)
+s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned long ofs)
 {
 	int ret;
 
 	s5p_mfc_clock_on();
-	ret = readl((volatile void __iomem *)((unsigned long)ofs));
+	ret = readl((void *)ofs);
 	s5p_mfc_clock_off();
 
 	return ret;
diff --git a/drivers/media/platform/s5p-tv/Kconfig b/drivers/media/platform/s5p-tv/Kconfig
index 8f78739..7c3b6db 100644
--- a/drivers/media/platform/s5p-tv/Kconfig
+++ b/drivers/media/platform/s5p-tv/Kconfig
@@ -21,6 +21,7 @@
 	tristate "Samsung HDMI Driver"
 	depends on m
 	depends on VIDEO_V4L2
+	depends on I2C
 	depends on VIDEO_SAMSUNG_S5P_TV
 	select VIDEO_SAMSUNG_S5P_HDMIPHY
 	help
diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c
index b4d2696..751f3b6 100644
--- a/drivers/media/platform/s5p-tv/mixer_video.c
+++ b/drivers/media/platform/s5p-tv/mixer_video.c
@@ -287,7 +287,7 @@
 		u32 bl_width = divup(width, blk->width);
 		u32 bl_height = divup(height, blk->height);
 		u32 sizeimage = bl_width * bl_height * blk->size;
-		u16 bytesperline = bl_width * blk->size / blk->height;
+		u32 bytesperline = bl_width * blk->size / blk->height;
 
 		plane->sizeimage += sizeimage;
 		plane->bytesperline = max(plane->bytesperline, bytesperline);
@@ -926,22 +926,6 @@
 	mxr_dbg(mdev, "queuing buffer\n");
 }
 
-static void wait_lock(struct vb2_queue *vq)
-{
-	struct mxr_layer *layer = vb2_get_drv_priv(vq);
-
-	mxr_dbg(layer->mdev, "%s\n", __func__);
-	mutex_lock(&layer->mutex);
-}
-
-static void wait_unlock(struct vb2_queue *vq)
-{
-	struct mxr_layer *layer = vb2_get_drv_priv(vq);
-
-	mxr_dbg(layer->mdev, "%s\n", __func__);
-	mutex_unlock(&layer->mutex);
-}
-
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
 	struct mxr_layer *layer = vb2_get_drv_priv(vq);
@@ -1040,8 +1024,8 @@
 static struct vb2_ops mxr_video_qops = {
 	.queue_setup = queue_setup,
 	.buf_queue = buf_queue,
-	.wait_prepare = wait_unlock,
-	.wait_finish = wait_lock,
+	.wait_prepare = vb2_ops_wait_prepare,
+	.wait_finish = vb2_ops_wait_finish,
 	.start_streaming = start_streaming,
 	.stop_streaming = stop_streaming,
 };
@@ -1122,6 +1106,7 @@
 		.ops = &mxr_video_qops,
 		.min_buffers_needed = 1,
 		.mem_ops = &vb2_dma_contig_memops,
+		.lock = &layer->mutex,
 	};
 
 	return layer;
diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c
index aaa1f6f..2554f37 100644
--- a/drivers/media/platform/sh_veu.c
+++ b/drivers/media/platform/sh_veu.c
@@ -242,20 +242,6 @@
 	veu->aborting = true;
 }
 
-static void sh_veu_lock(void *priv)
-{
-	struct sh_veu_dev *veu = priv;
-
-	mutex_lock(&veu->fop_lock);
-}
-
-static void sh_veu_unlock(void *priv)
-{
-	struct sh_veu_dev *veu = priv;
-
-	mutex_unlock(&veu->fop_lock);
-}
-
 static void sh_veu_process(struct sh_veu_dev *veu,
 			   struct vb2_buffer *src_buf,
 			   struct vb2_buffer *dst_buf)
@@ -950,36 +936,28 @@
 	v4l2_m2m_buf_queue(veu->m2m_ctx, vb);
 }
 
-static void sh_veu_wait_prepare(struct vb2_queue *q)
-{
-	sh_veu_unlock(vb2_get_drv_priv(q));
-}
-
-static void sh_veu_wait_finish(struct vb2_queue *q)
-{
-	sh_veu_lock(vb2_get_drv_priv(q));
-}
-
 static const struct vb2_ops sh_veu_qops = {
 	.queue_setup	 = sh_veu_queue_setup,
 	.buf_prepare	 = sh_veu_buf_prepare,
 	.buf_queue	 = sh_veu_buf_queue,
-	.wait_prepare	 = sh_veu_wait_prepare,
-	.wait_finish	 = sh_veu_wait_finish,
+	.wait_prepare	 = vb2_ops_wait_prepare,
+	.wait_finish	 = vb2_ops_wait_finish,
 };
 
 static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq,
 			     struct vb2_queue *dst_vq)
 {
+	struct sh_veu_dev *veu = priv;
 	int ret;
 
 	memset(src_vq, 0, sizeof(*src_vq));
 	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
 	src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
-	src_vq->drv_priv = priv;
+	src_vq->drv_priv = veu;
 	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 	src_vq->ops = &sh_veu_qops;
 	src_vq->mem_ops = &vb2_dma_contig_memops;
+	src_vq->lock = &veu->fop_lock;
 
 	ret = vb2_queue_init(src_vq);
 	if (ret < 0)
@@ -988,10 +966,11 @@
 	memset(dst_vq, 0, sizeof(*dst_vq));
 	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
-	dst_vq->drv_priv = priv;
+	dst_vq->drv_priv = veu;
 	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 	dst_vq->ops = &sh_veu_qops;
 	dst_vq->mem_ops = &vb2_dma_contig_memops;
+	dst_vq->lock = &veu->fop_lock;
 
 	return vb2_queue_init(dst_vq);
 }
@@ -1179,6 +1158,7 @@
 	}
 
 	*vdev = sh_veu_videodev;
+	vdev->v4l2_dev = &veu->v4l2_dev;
 	spin_lock_init(&veu->lock);
 	mutex_init(&veu->fop_lock);
 	vdev->lock = &veu->fop_lock;
diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c
index 261f119..dde1ccc 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/sh_vou.c
@@ -62,7 +62,7 @@
 
 struct sh_vou_device {
 	struct v4l2_device v4l2_dev;
-	struct video_device *vdev;
+	struct video_device vdev;
 	atomic_t use_count;
 	struct sh_vou_pdata *pdata;
 	spinlock_t lock;
@@ -890,7 +890,7 @@
 
 	dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, std_id);
 
-	if (std_id & ~vou_dev->vdev->tvnorms)
+	if (std_id & ~vou_dev->vdev.tvnorms)
 		return -EINVAL;
 
 	ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
@@ -1168,10 +1168,10 @@
 
 	dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
 
-	file->private_data = vou_file;
-
-	if (mutex_lock_interruptible(&vou_dev->fop_lock))
+	if (mutex_lock_interruptible(&vou_dev->fop_lock)) {
+		kfree(vou_file);
 		return -ERESTARTSYS;
+	}
 	if (atomic_inc_return(&vou_dev->use_count) == 1) {
 		int ret;
 		/* First open */
@@ -1183,6 +1183,7 @@
 			pm_runtime_put(vou_dev->v4l2_dev.dev);
 			vou_dev->status = SH_VOU_IDLE;
 			mutex_unlock(&vou_dev->fop_lock);
+			kfree(vou_file);
 			return ret;
 		}
 	}
@@ -1192,9 +1193,11 @@
 				       V4L2_BUF_TYPE_VIDEO_OUTPUT,
 				       V4L2_FIELD_NONE,
 				       sizeof(struct videobuf_buffer),
-				       vou_dev->vdev, &vou_dev->fop_lock);
+				       &vou_dev->vdev, &vou_dev->fop_lock);
 	mutex_unlock(&vou_dev->fop_lock);
 
+	file->private_data = vou_file;
+
 	return 0;
 }
 
@@ -1358,21 +1361,14 @@
 		goto ev4l2devreg;
 	}
 
-	/* Allocate memory for video device */
-	vdev = video_device_alloc();
-	if (vdev == NULL) {
-		ret = -ENOMEM;
-		goto evdevalloc;
-	}
-
+	vdev = &vou_dev->vdev;
 	*vdev = sh_vou_video_template;
 	if (vou_pdata->bus_fmt == SH_VOU_BUS_8BIT)
 		vdev->tvnorms |= V4L2_STD_PAL;
 	vdev->v4l2_dev = &vou_dev->v4l2_dev;
-	vdev->release = video_device_release;
+	vdev->release = video_device_release_empty;
 	vdev->lock = &vou_dev->fop_lock;
 
-	vou_dev->vdev = vdev;
 	video_set_drvdata(vdev, vou_dev);
 
 	pm_runtime_enable(&pdev->dev);
@@ -1406,9 +1402,7 @@
 ereset:
 	i2c_put_adapter(i2c_adap);
 ei2cgadap:
-	video_device_release(vdev);
 	pm_runtime_disable(&pdev->dev);
-evdevalloc:
 	v4l2_device_unregister(&vou_dev->v4l2_dev);
 ev4l2devreg:
 	free_irq(irq, vou_dev);
@@ -1435,7 +1429,7 @@
 	if (irq > 0)
 		free_irq(irq, vou_dev);
 	pm_runtime_disable(&pdev->dev);
-	video_unregister_device(vou_dev->vdev);
+	video_unregister_device(&vou_dev->vdev);
 	i2c_put_adapter(client->adapter);
 	v4l2_device_unregister(&vou_dev->v4l2_dev);
 	iounmap(vou_dev->base);
diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index 8efe403..c835beb 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -455,8 +455,8 @@
 	.buf_queue		= buffer_queue,
 	.start_streaming	= start_streaming,
 	.stop_streaming		= stop_streaming,
-	.wait_prepare		= soc_camera_unlock,
-	.wait_finish		= soc_camera_lock,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
 };
 
 /* ------------------------------------------------------------------
@@ -465,6 +465,8 @@
 static int isi_camera_init_videobuf(struct vb2_queue *q,
 				     struct soc_camera_device *icd)
 {
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	q->io_modes = VB2_MMAP;
 	q->drv_priv = icd;
@@ -472,6 +474,7 @@
 	q->ops = &isi_video_qops;
 	q->mem_ops = &vb2_dma_contig_memops;
 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &ici->host_lock;
 
 	return vb2_queue_init(q);
 }
@@ -760,8 +763,9 @@
 {
 	strcpy(cap->driver, "atmel-isi");
 	strcpy(cap->card, "Atmel Image Sensor Interface");
-	cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE |
-				V4L2_CAP_STREAMING);
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
 	return 0;
 }
 
@@ -839,6 +843,8 @@
 	if (isi->pdata.full_mode)
 		cfg1 |= ISI_CFG1_FULL_MODE;
 
+	cfg1 |= ISI_CFG1_THMASK_BEATS_16;
+
 	isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
 	isi_writel(isi, ISI_CFG1, cfg1);
 
diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index ce72bd2..192377f 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -1256,7 +1256,8 @@
 {
 	/* cap->name is set by the friendly caller:-> */
 	strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
-	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
 
 	return 0;
 }
diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index a60c3bb..3435fd2 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -435,14 +435,16 @@
 	.buf_queue	= mx3_videobuf_queue,
 	.buf_cleanup	= mx3_videobuf_release,
 	.buf_init	= mx3_videobuf_init,
-	.wait_prepare	= soc_camera_unlock,
-	.wait_finish	= soc_camera_lock,
+	.wait_prepare	= vb2_ops_wait_prepare,
+	.wait_finish	= vb2_ops_wait_finish,
 	.stop_streaming	= mx3_stop_streaming,
 };
 
 static int mx3_camera_init_videobuf(struct vb2_queue *q,
 				     struct soc_camera_device *icd)
 {
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	q->io_modes = VB2_MMAP | VB2_USERPTR;
 	q->drv_priv = icd;
@@ -450,6 +452,7 @@
 	q->mem_ops = &vb2_dma_contig_memops;
 	q->buf_struct_size = sizeof(struct mx3_camera_buffer);
 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &ici->host_lock;
 
 	return vb2_queue_init(q);
 }
@@ -967,7 +970,8 @@
 {
 	/* cap->name is set by the firendly caller:-> */
 	strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
-	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
 
 	return 0;
 }
diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c
index e6b9328..16f65ec 100644
--- a/drivers/media/platform/soc_camera/omap1_camera.c
+++ b/drivers/media/platform/soc_camera/omap1_camera.c
@@ -1427,7 +1427,8 @@
 {
 	/* cap->name is set by the friendly caller:-> */
 	strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
-	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
 
 	return 0;
 }
diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index 951226a..8d6e343 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
@@ -1576,7 +1576,8 @@
 {
 	/* cap->name is set by the firendly caller:-> */
 	strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card));
-	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
 
 	return 0;
 }
diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
index 0c1f556..9351f64 100644
--- a/drivers/media/platform/soc_camera/rcar_vin.c
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -804,62 +804,26 @@
 	vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
 }
 
-static void rcar_vin_videobuf_release(struct vb2_buffer *vb)
+/*
+ * Wait for capture to stop and all in-flight buffers to be finished with by
+ * the video hardware. This must be called under &priv->lock
+ *
+ */
+static void rcar_vin_wait_stop_streaming(struct rcar_vin_priv *priv)
 {
-	struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-	struct rcar_vin_priv *priv = ici->priv;
-	unsigned int i;
-	int buf_in_use = 0;
+	while (priv->state != STOPPED) {
+		/* issue stop if running */
+		if (priv->state == RUNNING)
+			rcar_vin_request_capture_stop(priv);
 
-	spin_lock_irq(&priv->lock);
-
-	/* Is the buffer in use by the VIN hardware? */
-	for (i = 0; i < MAX_BUFFER_NUM; i++) {
-		if (priv->queue_buf[i] == vb) {
-			buf_in_use = 1;
-			break;
+		/* wait until capturing has been stopped */
+		if (priv->state == STOPPING) {
+			priv->request_to_stop = true;
+			spin_unlock_irq(&priv->lock);
+			wait_for_completion(&priv->capture_stop);
+			spin_lock_irq(&priv->lock);
 		}
 	}
-
-	if (buf_in_use) {
-		while (priv->state != STOPPED) {
-
-			/* issue stop if running */
-			if (priv->state == RUNNING)
-				rcar_vin_request_capture_stop(priv);
-
-			/* wait until capturing has been stopped */
-			if (priv->state == STOPPING) {
-				priv->request_to_stop = true;
-				spin_unlock_irq(&priv->lock);
-				wait_for_completion(&priv->capture_stop);
-				spin_lock_irq(&priv->lock);
-			}
-		}
-		/*
-		 * Capturing has now stopped. The buffer we have been asked
-		 * to release could be any of the current buffers in use, so
-		 * release all buffers that are in use by HW
-		 */
-		for (i = 0; i < MAX_BUFFER_NUM; i++) {
-			if (priv->queue_buf[i]) {
-				vb2_buffer_done(priv->queue_buf[i],
-					VB2_BUF_STATE_ERROR);
-				priv->queue_buf[i] = NULL;
-			}
-		}
-	} else {
-		list_del_init(to_buf_list(vb));
-	}
-
-	spin_unlock_irq(&priv->lock);
-}
-
-static int rcar_vin_videobuf_init(struct vb2_buffer *vb)
-{
-	INIT_LIST_HEAD(to_buf_list(vb));
-	return 0;
 }
 
 static void rcar_vin_stop_streaming(struct vb2_queue *vq)
@@ -868,21 +832,34 @@
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct rcar_vin_priv *priv = ici->priv;
 	struct list_head *buf_head, *tmp;
+	int i;
 
 	spin_lock_irq(&priv->lock);
-	list_for_each_safe(buf_head, tmp, &priv->capture)
+	rcar_vin_wait_stop_streaming(priv);
+
+	for (i = 0; i < MAX_BUFFER_NUM; i++) {
+		if (priv->queue_buf[i]) {
+			vb2_buffer_done(priv->queue_buf[i],
+					VB2_BUF_STATE_ERROR);
+			priv->queue_buf[i] = NULL;
+		}
+	}
+
+	list_for_each_safe(buf_head, tmp, &priv->capture) {
+		vb2_buffer_done(&list_entry(buf_head,
+					struct rcar_vin_buffer, list)->vb,
+				VB2_BUF_STATE_ERROR);
 		list_del_init(buf_head);
+	}
 	spin_unlock_irq(&priv->lock);
 }
 
 static struct vb2_ops rcar_vin_vb2_ops = {
 	.queue_setup	= rcar_vin_videobuf_setup,
-	.buf_init	= rcar_vin_videobuf_init,
-	.buf_cleanup	= rcar_vin_videobuf_release,
 	.buf_queue	= rcar_vin_videobuf_queue,
 	.stop_streaming	= rcar_vin_stop_streaming,
-	.wait_prepare	= soc_camera_unlock,
-	.wait_finish	= soc_camera_lock,
+	.wait_prepare	= vb2_ops_wait_prepare,
+	.wait_finish	= vb2_ops_wait_finish,
 };
 
 static irqreturn_t rcar_vin_irq(int irq, void *data)
@@ -1000,19 +977,6 @@
 		icd->devnum);
 }
 
-/* Called with .host_lock held */
-static int rcar_vin_clock_start(struct soc_camera_host *ici)
-{
-	/* VIN does not have "mclk" */
-	return 0;
-}
-
-/* Called with .host_lock held */
-static void rcar_vin_clock_stop(struct soc_camera_host *ici)
-{
-	/* VIN does not have "mclk" */
-}
-
 static void set_coeff(struct rcar_vin_priv *priv, unsigned short xs)
 {
 	int i;
@@ -1799,13 +1763,17 @@
 			     struct v4l2_capability *cap)
 {
 	strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card));
-	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
 	return 0;
 }
 
 static int rcar_vin_init_videobuf2(struct vb2_queue *vq,
 				   struct soc_camera_device *icd)
 {
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
 	vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	vq->io_modes = VB2_MMAP | VB2_USERPTR;
 	vq->drv_priv = icd;
@@ -1813,6 +1781,7 @@
 	vq->mem_ops = &vb2_dma_contig_memops;
 	vq->buf_struct_size = sizeof(struct rcar_vin_buffer);
 	vq->timestamp_flags  = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	vq->lock = &ici->host_lock;
 
 	return vb2_queue_init(vq);
 }
@@ -1821,8 +1790,6 @@
 	.owner		= THIS_MODULE,
 	.add		= rcar_vin_add_device,
 	.remove		= rcar_vin_remove_device,
-	.clock_start	= rcar_vin_clock_start,
-	.clock_stop	= rcar_vin_clock_stop,
 	.get_formats	= rcar_vin_get_formats,
 	.put_formats	= rcar_vin_put_formats,
 	.get_crop	= rcar_vin_get_crop,
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 8b27b3e..9ce202f 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -496,8 +496,8 @@
 	.buf_queue	= sh_mobile_ceu_videobuf_queue,
 	.buf_cleanup	= sh_mobile_ceu_videobuf_release,
 	.buf_init	= sh_mobile_ceu_videobuf_init,
-	.wait_prepare	= soc_camera_unlock,
-	.wait_finish	= soc_camera_lock,
+	.wait_prepare	= vb2_ops_wait_prepare,
+	.wait_finish	= vb2_ops_wait_finish,
 	.stop_streaming	= sh_mobile_ceu_stop_streaming,
 };
 
@@ -1652,13 +1652,17 @@
 				  struct v4l2_capability *cap)
 {
 	strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card));
-	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
 	return 0;
 }
 
 static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
 				       struct soc_camera_device *icd)
 {
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	q->io_modes = VB2_MMAP | VB2_USERPTR;
 	q->drv_priv = icd;
@@ -1666,6 +1670,7 @@
 	q->mem_ops = &vb2_dma_contig_memops;
 	q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer);
 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &ici->host_lock;
 
 	return vb2_queue_init(q);
 }
diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
index c4e7aa0..cd93241 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
@@ -380,7 +380,6 @@
 	struct sh_csi2 *priv = container_of(subdev, struct sh_csi2, subdev);
 
 	v4l2_async_unregister_subdev(&priv->subdev);
-	v4l2_device_unregister_subdev(subdev);
 	pm_runtime_disable(&pdev->dev);
 
 	return 0;
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index b3db51c..7bfe766 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -177,6 +177,30 @@
 	return 0;
 }
 
+static int soc_camera_clock_start(struct soc_camera_host *ici)
+{
+	int ret;
+
+	if (!ici->ops->clock_start)
+		return 0;
+
+	mutex_lock(&ici->clk_lock);
+	ret = ici->ops->clock_start(ici);
+	mutex_unlock(&ici->clk_lock);
+
+	return ret;
+}
+
+static void soc_camera_clock_stop(struct soc_camera_host *ici)
+{
+	if (!ici->ops->clock_stop)
+		return;
+
+	mutex_lock(&ici->clk_lock);
+	ici->ops->clock_stop(ici);
+	mutex_unlock(&ici->clk_lock);
+}
+
 const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
 	struct soc_camera_device *icd, unsigned int fourcc)
 {
@@ -584,9 +608,7 @@
 		return -EBUSY;
 
 	if (!icd->clk) {
-		mutex_lock(&ici->clk_lock);
-		ret = ici->ops->clock_start(ici);
-		mutex_unlock(&ici->clk_lock);
+		ret = soc_camera_clock_start(ici);
 		if (ret < 0)
 			return ret;
 	}
@@ -602,11 +624,8 @@
 	return 0;
 
 eadd:
-	if (!icd->clk) {
-		mutex_lock(&ici->clk_lock);
-		ici->ops->clock_stop(ici);
-		mutex_unlock(&ici->clk_lock);
-	}
+	if (!icd->clk)
+		soc_camera_clock_stop(ici);
 	return ret;
 }
 
@@ -619,11 +638,8 @@
 
 	if (ici->ops->remove)
 		ici->ops->remove(icd);
-	if (!icd->clk) {
-		mutex_lock(&ici->clk_lock);
-		ici->ops->clock_stop(ici);
-		mutex_unlock(&ici->clk_lock);
-	}
+	if (!icd->clk)
+		soc_camera_clock_stop(ici);
 	ici->icd = NULL;
 }
 
@@ -688,7 +704,8 @@
 
 		/* The camera could have been already on, try to reset */
 		if (sdesc->subdev_desc.reset)
-			sdesc->subdev_desc.reset(icd->pdev);
+			if (icd->control)
+				sdesc->subdev_desc.reset(icd->control);
 
 		ret = soc_camera_add_device(icd);
 		if (ret < 0) {
@@ -843,22 +860,6 @@
 	return res;
 }
 
-void soc_camera_lock(struct vb2_queue *vq)
-{
-	struct soc_camera_device *icd = vb2_get_drv_priv(vq);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-	mutex_lock(&ici->host_lock);
-}
-EXPORT_SYMBOL(soc_camera_lock);
-
-void soc_camera_unlock(struct vb2_queue *vq)
-{
-	struct soc_camera_device *icd = vb2_get_drv_priv(vq);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-	mutex_unlock(&ici->host_lock);
-}
-EXPORT_SYMBOL(soc_camera_unlock);
-
 static struct v4l2_file_operations soc_camera_fops = {
 	.owner		= THIS_MODULE,
 	.open		= soc_camera_open,
@@ -1175,7 +1176,8 @@
 
 			/* The camera could have been already on, try to reset */
 			if (ssdd->reset)
-				ssdd->reset(icd->pdev);
+				if (icd->control)
+					ssdd->reset(icd->control);
 
 			icd->parent = ici->v4l2_dev.dev;
 
@@ -1194,7 +1196,6 @@
 {
 	struct soc_camera_device *icd = clk->priv;
 	struct soc_camera_host *ici;
-	int ret;
 
 	if (!icd || !icd->parent)
 		return -ENODEV;
@@ -1208,10 +1209,7 @@
 	 * If a different client is currently being probed, the host will tell
 	 * you to go
 	 */
-	mutex_lock(&ici->clk_lock);
-	ret = ici->ops->clock_start(ici);
-	mutex_unlock(&ici->clk_lock);
-	return ret;
+	return soc_camera_clock_start(ici);
 }
 
 static void soc_camera_clk_disable(struct v4l2_clk *clk)
@@ -1224,9 +1222,7 @@
 
 	ici = to_soc_camera_host(icd->parent);
 
-	mutex_lock(&ici->clk_lock);
-	ici->ops->clock_stop(ici);
-	mutex_unlock(&ici->clk_lock);
+	soc_camera_clock_stop(ici);
 
 	module_put(ici->ops->owner);
 }
@@ -1380,7 +1376,7 @@
 	snprintf(clk_name, sizeof(clk_name), "%d-%04x",
 		 shd->i2c_adapter_id, shd->board_info->addr);
 
-	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);
+	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd);
 	if (IS_ERR(icd->clk)) {
 		ret = PTR_ERR(icd->clk);
 		goto eclkreg;
@@ -1461,7 +1457,7 @@
 				memcpy(&sdesc->subdev_desc, ssdd,
 				       sizeof(sdesc->subdev_desc));
 				if (ssdd->reset)
-					ssdd->reset(icd->pdev);
+					ssdd->reset(&client->dev);
 			}
 
 			icd->control = &client->dev;
@@ -1561,7 +1557,7 @@
 	snprintf(clk_name, sizeof(clk_name), "%d-%04x",
 		 sasd->asd.match.i2c.adapter_id, sasd->asd.match.i2c.address);
 
-	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);
+	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd);
 	if (IS_ERR(icd->clk)) {
 		ret = PTR_ERR(icd->clk);
 		goto eclkreg;
@@ -1666,7 +1662,7 @@
 		snprintf(clk_name, sizeof(clk_name), "of-%s",
 			 of_node_full_name(remote));
 
-	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);
+	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd);
 	if (IS_ERR(icd->clk)) {
 		ret = PTR_ERR(icd->clk);
 		goto eclkreg;
@@ -1675,13 +1671,15 @@
 	ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier);
 	if (!ret)
 		return 0;
+
+	v4l2_clk_unregister(icd->clk);
 eclkreg:
 	icd->clk = NULL;
 	platform_device_del(sasc->pdev);
 eaddpdev:
 	platform_device_put(sasc->pdev);
 eallocpdev:
-	devm_kfree(ici->v4l2_dev.dev, sasc);
+	devm_kfree(ici->v4l2_dev.dev, info);
 	dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret);
 
 	return ret;
@@ -1710,7 +1708,6 @@
 		if (!i)
 			soc_of_bind(ici, epn, ren->parent);
 
-		of_node_put(epn);
 		of_node_put(ren);
 
 		if (i) {
@@ -1718,6 +1715,8 @@
 			break;
 		}
 	}
+
+	of_node_put(epn);
 }
 
 #else
@@ -1766,9 +1765,7 @@
 		ret = -EINVAL;
 		goto eadd;
 	} else {
-		mutex_lock(&ici->clk_lock);
-		ret = ici->ops->clock_start(ici);
-		mutex_unlock(&ici->clk_lock);
+		ret = soc_camera_clock_start(ici);
 		if (ret < 0)
 			goto eadd;
 
@@ -1808,13 +1805,9 @@
 		module_put(control->driver->owner);
 enodrv:
 eadddev:
-		mutex_lock(&ici->clk_lock);
-		ici->ops->clock_stop(ici);
-		mutex_unlock(&ici->clk_lock);
+		soc_camera_clock_stop(ici);
 	}
 eadd:
-	video_device_release(icd->vdev);
-	icd->vdev = NULL;
 	if (icd->vdev) {
 		video_device_release(icd->vdev);
 		icd->vdev = NULL;
@@ -1906,22 +1899,34 @@
 	int ret;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	const struct soc_camera_format_xlate *xlate;
-	__u32 pixfmt = fsize->pixel_format;
-	struct v4l2_frmsizeenum fsize_mbus = *fsize;
+	struct v4l2_subdev_frame_size_enum fse = {
+		.index = fsize->index,
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
 
-	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+	xlate = soc_camera_xlate_by_fourcc(icd, fsize->pixel_format);
 	if (!xlate)
 		return -EINVAL;
-	/* map xlate-code to pixel_format, sensor only handle xlate-code*/
-	fsize_mbus.pixel_format = xlate->code;
+	fse.code = xlate->code;
 
-	ret = v4l2_subdev_call(sd, video, enum_framesizes, &fsize_mbus);
+	ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse);
 	if (ret < 0)
 		return ret;
 
-	*fsize = fsize_mbus;
-	fsize->pixel_format = pixfmt;
-
+	if (fse.min_width == fse.max_width &&
+	    fse.min_height == fse.max_height) {
+		fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+		fsize->discrete.width = fse.min_width;
+		fsize->discrete.height = fse.min_height;
+		return 0;
+	}
+	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+	fsize->stepwise.min_width = fse.min_width;
+	fsize->stepwise.max_width = fse.max_width;
+	fsize->stepwise.min_height = fse.min_height;
+	fsize->stepwise.max_height = fse.max_height;
+	fsize->stepwise.step_width = 1;
+	fsize->stepwise.step_height = 1;
 	return 0;
 }
 
@@ -1938,8 +1943,6 @@
 	    ((!ici->ops->init_videobuf ||
 	      !ici->ops->reqbufs) &&
 	     !ici->ops->init_videobuf2) ||
-	    !ici->ops->clock_start ||
-	    !ici->ops->clock_stop ||
 	    !ici->ops->poll ||
 	    !ici->v4l2_dev.dev)
 		return -EINVAL;
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
index d628d1a..c44760b 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -25,6 +25,7 @@
 #include <linux/io.h>
 #include <linux/ioctl.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/sched.h>
@@ -74,7 +75,7 @@
 #define VPE_DEF_BUFS_PER_JOB	1	/* default one buffer per batch job */
 
 /*
- * each VPE context can need up to 3 config desciptors, 7 input descriptors,
+ * each VPE context can need up to 3 config descriptors, 7 input descriptors,
  * 3 output descriptors, and 10 control descriptors
  */
 #define VPE_DESC_LIST_SIZE	(10 * VPDMA_DTD_DESC_SIZE +	\
@@ -373,7 +374,6 @@
 struct vpe_ctx {
 	struct v4l2_fh		fh;
 	struct vpe_dev		*dev;
-	struct v4l2_m2m_ctx	*m2m_ctx;
 	struct v4l2_ctrl_handler hdl;
 
 	unsigned int		field;			/* current field */
@@ -887,10 +887,10 @@
 	if (ctx->deinterlacing && ctx->src_vbs[2] == NULL)
 		needed += 2;	/* need additional two most recent fields */
 
-	if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < needed)
+	if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < needed)
 		return 0;
 
-	if (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < needed)
+	if (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < needed)
 		return 0;
 
 	return 1;
@@ -1100,15 +1100,15 @@
 	struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST];
 
 	if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) {
-		ctx->src_vbs[2] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+		ctx->src_vbs[2] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
 		WARN_ON(ctx->src_vbs[2] == NULL);
-		ctx->src_vbs[1] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+		ctx->src_vbs[1] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
 		WARN_ON(ctx->src_vbs[1] == NULL);
 	}
 
-	ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+	ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
 	WARN_ON(ctx->src_vbs[0] == NULL);
-	ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+	ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
 	WARN_ON(ctx->dst_vb == NULL);
 
 	/* config descriptors */
@@ -1334,7 +1334,7 @@
 finished:
 	vpe_dbg(ctx->dev, "finishing transaction\n");
 	ctx->bufs_completed = 0;
-	v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
+	v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
 handled:
 	return IRQ_HANDLED;
 }
@@ -1395,7 +1395,7 @@
 	struct vpe_q_data *q_data;
 	int i;
 
-	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
 	if (!vq)
 		return -EINVAL;
 
@@ -1527,7 +1527,7 @@
 	struct vb2_queue *vq;
 	int i;
 
-	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
 	if (!vq)
 		return -EINVAL;
 
@@ -1739,52 +1739,6 @@
 	return set_srcdst_params(ctx);
 }
 
-static int vpe_reqbufs(struct file *file, void *priv,
-		       struct v4l2_requestbuffers *reqbufs)
-{
-	struct vpe_ctx *ctx = file2ctx(file);
-
-	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int vpe_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
-	struct vpe_ctx *ctx = file2ctx(file);
-
-	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vpe_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
-	struct vpe_ctx *ctx = file2ctx(file);
-
-	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vpe_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
-	struct vpe_ctx *ctx = file2ctx(file);
-
-	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vpe_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
-{
-	struct vpe_ctx *ctx = file2ctx(file);
-
-	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int vpe_streamoff(struct file *file, void *priv, enum v4l2_buf_type type)
-{
-	struct vpe_ctx *ctx = file2ctx(file);
-
-	vpe_dump_regs(ctx->dev);
-	vpdma_dump_regs(ctx->dev->vpdma);
-
-	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
 /*
  * defines number of buffers/frames a context can process with VPE before
  * switching to a different context. default value is 1 buffer per context
@@ -1814,14 +1768,14 @@
 };
 
 static const struct v4l2_ioctl_ops vpe_ioctl_ops = {
-	.vidioc_querycap	= vpe_querycap,
+	.vidioc_querycap		= vpe_querycap,
 
-	.vidioc_enum_fmt_vid_cap_mplane = vpe_enum_fmt,
+	.vidioc_enum_fmt_vid_cap_mplane	= vpe_enum_fmt,
 	.vidioc_g_fmt_vid_cap_mplane	= vpe_g_fmt,
 	.vidioc_try_fmt_vid_cap_mplane	= vpe_try_fmt,
 	.vidioc_s_fmt_vid_cap_mplane	= vpe_s_fmt,
 
-	.vidioc_enum_fmt_vid_out_mplane = vpe_enum_fmt,
+	.vidioc_enum_fmt_vid_out_mplane	= vpe_enum_fmt,
 	.vidioc_g_fmt_vid_out_mplane	= vpe_g_fmt,
 	.vidioc_try_fmt_vid_out_mplane	= vpe_try_fmt,
 	.vidioc_s_fmt_vid_out_mplane	= vpe_s_fmt,
@@ -1829,16 +1783,15 @@
 	.vidioc_g_selection		= vpe_g_selection,
 	.vidioc_s_selection		= vpe_s_selection,
 
-	.vidioc_reqbufs		= vpe_reqbufs,
-	.vidioc_querybuf	= vpe_querybuf,
+	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
+	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
+	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
+	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
+	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
+	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
 
-	.vidioc_qbuf		= vpe_qbuf,
-	.vidioc_dqbuf		= vpe_dqbuf,
-
-	.vidioc_streamon	= vpe_streamon,
-	.vidioc_streamoff	= vpe_streamoff,
-	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
-	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
 };
 
 /*
@@ -1910,33 +1863,40 @@
 static void vpe_buf_queue(struct vb2_buffer *vb)
 {
 	struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-	v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+
+	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
 }
 
-static void vpe_wait_prepare(struct vb2_queue *q)
+static int vpe_start_streaming(struct vb2_queue *q, unsigned int count)
 {
-	struct vpe_ctx *ctx = vb2_get_drv_priv(q);
-	vpe_unlock(ctx);
+	/* currently we do nothing here */
+
+	return 0;
 }
 
-static void vpe_wait_finish(struct vb2_queue *q)
+static void vpe_stop_streaming(struct vb2_queue *q)
 {
 	struct vpe_ctx *ctx = vb2_get_drv_priv(q);
-	vpe_lock(ctx);
+
+	vpe_dump_regs(ctx->dev);
+	vpdma_dump_regs(ctx->dev->vpdma);
 }
 
 static struct vb2_ops vpe_qops = {
 	.queue_setup	 = vpe_queue_setup,
 	.buf_prepare	 = vpe_buf_prepare,
 	.buf_queue	 = vpe_buf_queue,
-	.wait_prepare	 = vpe_wait_prepare,
-	.wait_finish	 = vpe_wait_finish,
+	.wait_prepare	 = vb2_ops_wait_prepare,
+	.wait_finish	 = vb2_ops_wait_finish,
+	.start_streaming = vpe_start_streaming,
+	.stop_streaming  = vpe_stop_streaming,
 };
 
 static int queue_init(void *priv, struct vb2_queue *src_vq,
 		      struct vb2_queue *dst_vq)
 {
 	struct vpe_ctx *ctx = priv;
+	struct vpe_dev *dev = ctx->dev;
 	int ret;
 
 	memset(src_vq, 0, sizeof(*src_vq));
@@ -1947,6 +1907,7 @@
 	src_vq->ops = &vpe_qops;
 	src_vq->mem_ops = &vb2_dma_contig_memops;
 	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	src_vq->lock = &dev->dev_mutex;
 
 	ret = vb2_queue_init(src_vq);
 	if (ret)
@@ -1960,6 +1921,7 @@
 	dst_vq->ops = &vpe_qops;
 	dst_vq->mem_ops = &vb2_dma_contig_memops;
 	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	dst_vq->lock = &dev->dev_mutex;
 
 	return vb2_queue_init(dst_vq);
 }
@@ -1981,9 +1943,9 @@
 static int vpe_open(struct file *file)
 {
 	struct vpe_dev *dev = video_drvdata(file);
-	struct vpe_ctx *ctx = NULL;
 	struct vpe_q_data *s_q_data;
 	struct v4l2_ctrl_handler *hdl;
+	struct vpe_ctx *ctx;
 	int ret;
 
 	vpe_dbg(dev, "vpe_open\n");
@@ -2056,10 +2018,10 @@
 	if (ret)
 		goto exit_fh;
 
-	ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
 
-	if (IS_ERR(ctx->m2m_ctx)) {
-		ret = PTR_ERR(ctx->m2m_ctx);
+	if (IS_ERR(ctx->fh.m2m_ctx)) {
+		ret = PTR_ERR(ctx->fh.m2m_ctx);
 		goto exit_fh;
 	}
 
@@ -2078,7 +2040,7 @@
 	ctx->load_mmrs = true;
 
 	vpe_dbg(dev, "created instance %p, m2m_ctx: %p\n",
-		ctx, ctx->m2m_ctx);
+		ctx, ctx->fh.m2m_ctx);
 
 	mutex_unlock(&dev->dev_mutex);
 
@@ -2116,7 +2078,7 @@
 	v4l2_fh_del(&ctx->fh);
 	v4l2_fh_exit(&ctx->fh);
 	v4l2_ctrl_handler_free(&ctx->hdl);
-	v4l2_m2m_ctx_release(ctx->m2m_ctx);
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
 
 	kfree(ctx);
 
@@ -2133,39 +2095,13 @@
 	return 0;
 }
 
-static unsigned int vpe_poll(struct file *file,
-			     struct poll_table_struct *wait)
-{
-	struct vpe_ctx *ctx = file2ctx(file);
-	struct vpe_dev *dev = ctx->dev;
-	int ret;
-
-	mutex_lock(&dev->dev_mutex);
-	ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
-	mutex_unlock(&dev->dev_mutex);
-	return ret;
-}
-
-static int vpe_mmap(struct file *file, struct vm_area_struct *vma)
-{
-	struct vpe_ctx *ctx = file2ctx(file);
-	struct vpe_dev *dev = ctx->dev;
-	int ret;
-
-	if (mutex_lock_interruptible(&dev->dev_mutex))
-		return -ERESTARTSYS;
-	ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
-	mutex_unlock(&dev->dev_mutex);
-	return ret;
-}
-
 static const struct v4l2_file_operations vpe_fops = {
 	.owner		= THIS_MODULE,
 	.open		= vpe_open,
 	.release	= vpe_release,
-	.poll		= vpe_poll,
+	.poll		= v4l2_m2m_fop_poll,
 	.unlocked_ioctl	= video_ioctl2,
-	.mmap		= vpe_mmap,
+	.mmap		= v4l2_m2m_fop_mmap,
 };
 
 static struct video_device vpe_videodev = {
@@ -2367,8 +2303,6 @@
 	},
 	{},
 };
-#else
-#define vpe_of_match NULL
 #endif
 
 static struct platform_driver vpe_pdrv = {
@@ -2376,7 +2310,7 @@
 	.remove		= vpe_remove,
 	.driver		= {
 		.name	= VPE_MODULE_NAME,
-		.of_match_table = vpe_of_match,
+		.of_match_table = of_match_ptr(vpe_of_match),
 	},
 };
 
diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c
index 86989d8..678ed9f 100644
--- a/drivers/media/platform/via-camera.c
+++ b/drivers/media/platform/via-camera.c
@@ -1147,12 +1147,23 @@
 		struct v4l2_frmivalenum *interval)
 {
 	struct via_camera *cam = priv;
+	struct v4l2_subdev_frame_interval_enum fie = {
+		.index = interval->index,
+		.code = cam->mbus_code,
+		.width = cam->sensor_format.width,
+		.height = cam->sensor_format.height,
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
 	int ret;
 
 	mutex_lock(&cam->lock);
-	ret = sensor_call(cam, video, enum_frameintervals, interval);
+	ret = sensor_call(cam, pad, enum_frame_interval, NULL, &fie);
 	mutex_unlock(&cam->lock);
-	return ret;
+	if (ret)
+		return ret;
+	interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+	interval->discrete = fie.interval;
+	return 0;
 }
 
 
diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
index a3b6383..35e41c0 100644
--- a/drivers/media/platform/vim2m.c
+++ b/drivers/media/platform/vim2m.c
@@ -142,7 +142,7 @@
 
 struct vim2m_dev {
 	struct v4l2_device	v4l2_dev;
-	struct video_device	*vfd;
+	struct video_device	vfd;
 
 	atomic_t		num_inst;
 	struct mutex		dev_mutex;
@@ -970,7 +970,7 @@
 	.fops		= &vim2m_fops,
 	.ioctl_ops	= &vim2m_ioctl_ops,
 	.minor		= -1,
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
 };
 
 static struct v4l2_m2m_ops m2m_ops = {
@@ -998,26 +998,19 @@
 	atomic_set(&dev->num_inst, 0);
 	mutex_init(&dev->dev_mutex);
 
-	vfd = video_device_alloc();
-	if (!vfd) {
-		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
-		ret = -ENOMEM;
-		goto unreg_dev;
-	}
-
-	*vfd = vim2m_videodev;
+	dev->vfd = vim2m_videodev;
+	vfd = &dev->vfd;
 	vfd->lock = &dev->dev_mutex;
 	vfd->v4l2_dev = &dev->v4l2_dev;
 
 	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
 	if (ret) {
 		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
-		goto rel_vdev;
+		goto unreg_dev;
 	}
 
 	video_set_drvdata(vfd, dev);
 	snprintf(vfd->name, sizeof(vfd->name), "%s", vim2m_videodev.name);
-	dev->vfd = vfd;
 	v4l2_info(&dev->v4l2_dev,
 			"Device registered as /dev/video%d\n", vfd->num);
 
@@ -1035,9 +1028,7 @@
 
 err_m2m:
 	v4l2_m2m_release(dev->m2m_dev);
-	video_unregister_device(dev->vfd);
-rel_vdev:
-	video_device_release(vfd);
+	video_unregister_device(&dev->vfd);
 unreg_dev:
 	v4l2_device_unregister(&dev->v4l2_dev);
 
@@ -1051,7 +1042,7 @@
 	v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME);
 	v4l2_m2m_release(dev->m2m_dev);
 	del_timer_sync(&dev->timer);
-	video_unregister_device(dev->vfd);
+	video_unregister_device(&dev->vfd);
 	v4l2_device_unregister(&dev->v4l2_dev);
 
 	return 0;
diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
index a7e033a..a9454d5 100644
--- a/drivers/media/platform/vivid/vivid-core.c
+++ b/drivers/media/platform/vivid/vivid-core.c
@@ -26,6 +26,7 @@
 #include <linux/vmalloc.h>
 #include <linux/font.h>
 #include <linux/mutex.h>
+#include <linux/platform_device.h>
 #include <linux/videodev2.h>
 #include <linux/v4l2-dv-timings.h>
 #include <media/videobuf2-vmalloc.h>
@@ -572,7 +573,9 @@
 	.vidioc_querybuf		= vb2_ioctl_querybuf,
 	.vidioc_qbuf			= vb2_ioctl_qbuf,
 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
 	.vidioc_expbuf			= vb2_ioctl_expbuf,
+#endif
 	.vidioc_streamon		= vb2_ioctl_streamon,
 	.vidioc_streamoff		= vb2_ioctl_streamoff,
 
@@ -618,7 +621,23 @@
 	Initialization and module stuff
    ------------------------------------------------------------------*/
 
-static int __init vivid_create_instance(int inst)
+static void vivid_dev_release(struct v4l2_device *v4l2_dev)
+{
+	struct vivid_dev *dev = container_of(v4l2_dev, struct vivid_dev, v4l2_dev);
+
+	vivid_free_controls(dev);
+	v4l2_device_unregister(&dev->v4l2_dev);
+	vfree(dev->scaled_line);
+	vfree(dev->blended_line);
+	vfree(dev->edid);
+	vfree(dev->bitmap_cap);
+	vfree(dev->bitmap_out);
+	tpg_free(&dev->tpg);
+	kfree(dev->query_dv_timings_qmenu);
+	kfree(dev);
+}
+
+static int vivid_create_instance(struct platform_device *pdev, int inst)
 {
 	static const struct v4l2_dv_timings def_dv_timings =
 					V4L2_DV_BT_CEA_1280X720P60;
@@ -646,9 +665,12 @@
 	/* register v4l2_device */
 	snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
 			"%s-%03d", VIVID_MODULE_NAME, inst);
-	ret = v4l2_device_register(NULL, &dev->v4l2_dev);
-	if (ret)
-		goto free_dev;
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+	if (ret) {
+		kfree(dev);
+		return ret;
+	}
+	dev->v4l2_dev.release = vivid_dev_release;
 
 	/* start detecting feature set */
 
@@ -1256,15 +1278,8 @@
 	video_unregister_device(&dev->vbi_cap_dev);
 	video_unregister_device(&dev->vid_out_dev);
 	video_unregister_device(&dev->vid_cap_dev);
-	vivid_free_controls(dev);
-	v4l2_device_unregister(&dev->v4l2_dev);
 free_dev:
-	vfree(dev->scaled_line);
-	vfree(dev->blended_line);
-	vfree(dev->edid);
-	tpg_free(&dev->tpg);
-	kfree(dev->query_dv_timings_qmenu);
-	kfree(dev);
+	v4l2_device_put(&dev->v4l2_dev);
 	return ret;
 }
 
@@ -1274,7 +1289,7 @@
    will succeed. This is limited to the maximum number of devices that
    videodev supports, which is equal to VIDEO_NUM_DEVICES.
  */
-static int __init vivid_init(void)
+static int vivid_probe(struct platform_device *pdev)
 {
 	const struct font_desc *font = find_font("VGA8x16");
 	int ret = 0, i;
@@ -1289,7 +1304,7 @@
 	n_devs = clamp_t(unsigned, n_devs, 1, VIVID_MAX_DEVS);
 
 	for (i = 0; i < n_devs; i++) {
-		ret = vivid_create_instance(i);
+		ret = vivid_create_instance(pdev, i);
 		if (ret) {
 			/* If some instantiations succeeded, keep driver */
 			if (i)
@@ -1309,7 +1324,7 @@
 	return ret;
 }
 
-static void __exit vivid_exit(void)
+static int vivid_remove(struct platform_device *pdev)
 {
 	struct vivid_dev *dev;
 	unsigned i;
@@ -1358,18 +1373,48 @@
 			unregister_framebuffer(&dev->fb_info);
 			vivid_fb_release_buffers(dev);
 		}
-		v4l2_device_unregister(&dev->v4l2_dev);
-		vivid_free_controls(dev);
-		vfree(dev->scaled_line);
-		vfree(dev->blended_line);
-		vfree(dev->edid);
-		vfree(dev->bitmap_cap);
-		vfree(dev->bitmap_out);
-		tpg_free(&dev->tpg);
-		kfree(dev->query_dv_timings_qmenu);
-		kfree(dev);
+		v4l2_device_put(&dev->v4l2_dev);
 		vivid_devs[i] = NULL;
 	}
+	return 0;
+}
+
+static void vivid_pdev_release(struct device *dev)
+{
+}
+
+static struct platform_device vivid_pdev = {
+	.name		= "vivid",
+	.dev.release	= vivid_pdev_release,
+};
+
+static struct platform_driver vivid_pdrv = {
+	.probe		= vivid_probe,
+	.remove		= vivid_remove,
+	.driver		= {
+		.name	= "vivid",
+	},
+};
+
+static int __init vivid_init(void)
+{
+	int ret;
+
+	ret = platform_device_register(&vivid_pdev);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&vivid_pdrv);
+	if (ret)
+		platform_device_unregister(&vivid_pdev);
+
+	return ret;
+}
+
+static void __exit vivid_exit(void)
+{
+	platform_driver_unregister(&vivid_pdrv);
+	platform_device_unregister(&vivid_pdev);
 }
 
 module_init(vivid_init);
diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h
index 4b497df..9e15aee 100644
--- a/drivers/media/platform/vivid/vivid-core.h
+++ b/drivers/media/platform/vivid/vivid-core.h
@@ -79,12 +79,14 @@
 struct vivid_fmt {
 	const char *name;
 	u32	fourcc;          /* v4l2 format id */
-	u8	depth;
 	bool	is_yuv;
 	bool	can_do_overlay;
+	u8	vdownsampling[TPG_MAX_PLANES];
 	u32	alpha_mask;
 	u8	planes;
-	u32	data_offset[2];
+	u8	buffers;
+	u32	data_offset[TPG_MAX_PLANES];
+	u32	bit_depth[TPG_MAX_PLANES];
 };
 
 extern struct vivid_fmt vivid_formats[];
@@ -332,7 +334,7 @@
 	u32				ycbcr_enc_out;
 	u32				quantization_out;
 	u32				service_set_out;
-	u32				bytesperline_out[2];
+	unsigned			bytesperline_out[TPG_MAX_PLANES];
 	unsigned			tv_field_out;
 	unsigned			tv_audio_output;
 	bool				vbi_out_have_wss;
diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
index 857e786..2b90700 100644
--- a/drivers/media/platform/vivid/vivid-ctrls.c
+++ b/drivers/media/platform/vivid/vivid-ctrls.c
@@ -689,7 +689,7 @@
 
 static const char * const vivid_ctrl_colorspace_strings[] = {
 	"SMPTE 170M",
-	"REC 709",
+	"Rec. 709",
 	"sRGB",
 	"AdobeRGB",
 	"BT.2020",
@@ -716,7 +716,7 @@
 	"xvYCC 601",
 	"xvYCC 709",
 	"sYCC",
-	"BT.2020 Non-Constant Luminance",
+	"BT.2020",
 	"BT.2020 Constant Luminance",
 	"SMPTE 240M",
 	NULL,
@@ -818,7 +818,7 @@
 		dev->dvi_d_out = ctrl->val == V4L2_DV_TX_MODE_DVI_D;
 		if (!vivid_is_hdmi_out(dev))
 			break;
-		if (!dev->dvi_d_out && (bt->standards & V4L2_DV_BT_STD_CEA861)) {
+		if (!dev->dvi_d_out && (bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) {
 			if (bt->width == 720 && bt->height <= 576)
 				dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M;
 			else
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c
index 39a67cf..1727f54 100644
--- a/drivers/media/platform/vivid/vivid-kthread-cap.c
+++ b/drivers/media/platform/vivid/vivid-kthread-cap.c
@@ -229,14 +229,29 @@
 		dev->loop_vid_overlay_cap.left, dev->loop_vid_overlay_cap.top);
 }
 
+static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf,
+			 unsigned p, unsigned bpl[TPG_MAX_PLANES], unsigned h)
+{
+	unsigned i;
+	void *vbuf;
+
+	if (p == 0 || tpg_g_buffers(tpg) > 1)
+		return vb2_plane_vaddr(&buf->vb, p);
+	vbuf = vb2_plane_vaddr(&buf->vb, 0);
+	for (i = 0; i < p; i++)
+		vbuf += bpl[i] * h / tpg->vdownsampling[i];
+	return vbuf;
+}
+
 static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf,
 		struct vivid_buffer *vid_cap_buf)
 {
 	bool blank = dev->must_blank[vid_cap_buf->vb.v4l2_buf.index];
 	struct tpg_data *tpg = &dev->tpg;
 	struct vivid_buffer *vid_out_buf = NULL;
-	unsigned pixsize = tpg_g_twopixelsize(tpg, p) / 2;
-	unsigned img_width = dev->compose_cap.width;
+	unsigned vdiv = dev->fmt_out->vdownsampling[p];
+	unsigned twopixsize = tpg_g_twopixelsize(tpg, p);
+	unsigned img_width = tpg_hdiv(tpg, p, dev->compose_cap.width);
 	unsigned img_height = dev->compose_cap.height;
 	unsigned stride_cap = tpg->bytesperline[p];
 	unsigned stride_out = dev->bytesperline_out[p];
@@ -255,6 +270,7 @@
 	unsigned vid_overlay_fract_part = 0;
 	unsigned vid_overlay_y = 0;
 	unsigned vid_overlay_error = 0;
+	unsigned vid_cap_left = tpg_hdiv(tpg, p, dev->loop_vid_cap.left);
 	unsigned vid_cap_right;
 	bool quick;
 
@@ -269,25 +285,29 @@
 
 	vid_cap_buf->vb.v4l2_buf.field = vid_out_buf->vb.v4l2_buf.field;
 
-	voutbuf = vb2_plane_vaddr(&vid_out_buf->vb, p) +
-				  vid_out_buf->vb.v4l2_planes[p].data_offset;
-	voutbuf += dev->loop_vid_out.left * pixsize + dev->loop_vid_out.top * stride_out;
-	vcapbuf += dev->compose_cap.left * pixsize + dev->compose_cap.top * stride_cap;
+	voutbuf = plane_vaddr(tpg, vid_out_buf, p,
+			      dev->bytesperline_out, dev->fmt_out_rect.height);
+	if (p < dev->fmt_out->buffers)
+		voutbuf += vid_out_buf->vb.v4l2_planes[p].data_offset;
+	voutbuf += tpg_hdiv(tpg, p, dev->loop_vid_out.left) +
+		(dev->loop_vid_out.top / vdiv) * stride_out;
+	vcapbuf += tpg_hdiv(tpg, p, dev->compose_cap.left) +
+		(dev->compose_cap.top / vdiv) * stride_cap;
 
 	if (dev->loop_vid_copy.width == 0 || dev->loop_vid_copy.height == 0) {
 		/*
 		 * If there is nothing to copy, then just fill the capture window
 		 * with black.
 		 */
-		for (y = 0; y < hmax; y++, vcapbuf += stride_cap)
-			memcpy(vcapbuf, tpg->black_line[p], img_width * pixsize);
+		for (y = 0; y < hmax / vdiv; y++, vcapbuf += stride_cap)
+			memcpy(vcapbuf, tpg->black_line[p], img_width);
 		return 0;
 	}
 
 	if (dev->overlay_out_enabled &&
 	    dev->loop_vid_overlay.width && dev->loop_vid_overlay.height) {
 		vosdbuf = dev->video_vbase;
-		vosdbuf += dev->loop_fb_copy.left * pixsize +
+		vosdbuf += (dev->loop_fb_copy.left * twopixsize) / 2 +
 			   dev->loop_fb_copy.top * stride_osd;
 		vid_overlay_int_part = dev->loop_vid_overlay.height /
 				       dev->loop_vid_overlay_cap.height;
@@ -295,12 +315,12 @@
 					 dev->loop_vid_overlay_cap.height;
 	}
 
-	vid_cap_right = dev->loop_vid_cap.left + dev->loop_vid_cap.width;
+	vid_cap_right = tpg_hdiv(tpg, p, dev->loop_vid_cap.left + dev->loop_vid_cap.width);
 	/* quick is true if no video scaling is needed */
 	quick = dev->loop_vid_out.width == dev->loop_vid_cap.width;
 
 	dev->cur_scaled_line = dev->loop_vid_out.height;
-	for (y = 0; y < hmax; y++, vcapbuf += stride_cap) {
+	for (y = 0; y < hmax; y += vdiv, vcapbuf += stride_cap) {
 		/* osdline is true if this line requires overlay blending */
 		bool osdline = vosdbuf && y >= dev->loop_vid_overlay_cap.top &&
 			  y < dev->loop_vid_overlay_cap.top + dev->loop_vid_overlay_cap.height;
@@ -311,34 +331,34 @@
 		 */
 		if (y < dev->loop_vid_cap.top ||
 		    y >= dev->loop_vid_cap.top + dev->loop_vid_cap.height) {
-			memcpy(vcapbuf, tpg->black_line[p], img_width * pixsize);
+			memcpy(vcapbuf, tpg->black_line[p], img_width);
 			continue;
 		}
 
 		/* fill the left border with black */
 		if (dev->loop_vid_cap.left)
-			memcpy(vcapbuf, tpg->black_line[p], dev->loop_vid_cap.left * pixsize);
+			memcpy(vcapbuf, tpg->black_line[p], vid_cap_left);
 
 		/* fill the right border with black */
 		if (vid_cap_right < img_width)
-			memcpy(vcapbuf + vid_cap_right * pixsize,
-				tpg->black_line[p], (img_width - vid_cap_right) * pixsize);
+			memcpy(vcapbuf + vid_cap_right, tpg->black_line[p],
+				img_width - vid_cap_right);
 
 		if (quick && !osdline) {
-			memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize,
+			memcpy(vcapbuf + vid_cap_left,
 			       voutbuf + vid_out_y * stride_out,
-			       dev->loop_vid_cap.width * pixsize);
+			       tpg_hdiv(tpg, p, dev->loop_vid_cap.width));
 			goto update_vid_out_y;
 		}
 		if (dev->cur_scaled_line == vid_out_y) {
-			memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize,
-			       dev->scaled_line,
-			       dev->loop_vid_cap.width * pixsize);
+			memcpy(vcapbuf + vid_cap_left, dev->scaled_line,
+			       tpg_hdiv(tpg, p, dev->loop_vid_cap.width));
 			goto update_vid_out_y;
 		}
 		if (!osdline) {
 			scale_line(voutbuf + vid_out_y * stride_out, dev->scaled_line,
-				dev->loop_vid_out.width, dev->loop_vid_cap.width,
+				tpg_hdiv(tpg, p, dev->loop_vid_out.width),
+				tpg_hdiv(tpg, p, dev->loop_vid_cap.width),
 				tpg_g_twopixelsize(tpg, p));
 		} else {
 			/*
@@ -346,7 +366,8 @@
 			 * loop_vid_overlay rectangle.
 			 */
 			unsigned offset =
-				(dev->loop_vid_overlay.left - dev->loop_vid_copy.left) * pixsize;
+				((dev->loop_vid_overlay.left - dev->loop_vid_copy.left) *
+				 twopixsize) / 2;
 			u8 *osd = vosdbuf + vid_overlay_y * stride_osd;
 
 			scale_line(voutbuf + vid_out_y * stride_out, dev->blended_line,
@@ -356,18 +377,17 @@
 				blend_line(dev, vid_overlay_y + dev->loop_vid_overlay.top,
 					   dev->loop_vid_overlay.left,
 					   dev->blended_line + offset, osd,
-					   dev->loop_vid_overlay.width, pixsize);
+					   dev->loop_vid_overlay.width, twopixsize / 2);
 			else
 				memcpy(dev->blended_line + offset,
-				       osd, dev->loop_vid_overlay.width * pixsize);
+				       osd, (dev->loop_vid_overlay.width * twopixsize) / 2);
 			scale_line(dev->blended_line, dev->scaled_line,
 					dev->loop_vid_copy.width, dev->loop_vid_cap.width,
 					tpg_g_twopixelsize(tpg, p));
 		}
 		dev->cur_scaled_line = vid_out_y;
-		memcpy(vcapbuf + dev->loop_vid_cap.left * pixsize,
-		       dev->scaled_line,
-		       dev->loop_vid_cap.width * pixsize);
+		memcpy(vcapbuf + vid_cap_left, dev->scaled_line,
+		       tpg_hdiv(tpg, p, dev->loop_vid_cap.width));
 
 update_vid_out_y:
 		if (osdline) {
@@ -380,21 +400,22 @@
 		}
 		vid_out_y += vid_out_int_part;
 		vid_out_error += vid_out_fract_part;
-		if (vid_out_error >= dev->loop_vid_cap.height) {
-			vid_out_error -= dev->loop_vid_cap.height;
+		if (vid_out_error >= dev->loop_vid_cap.height / vdiv) {
+			vid_out_error -= dev->loop_vid_cap.height / vdiv;
 			vid_out_y++;
 		}
 	}
 
 	if (!blank)
 		return 0;
-	for (; y < img_height; y++, vcapbuf += stride_cap)
-		memcpy(vcapbuf, tpg->contrast_line[p], img_width * pixsize);
+	for (; y < img_height; y += vdiv, vcapbuf += stride_cap)
+		memcpy(vcapbuf, tpg->contrast_line[p], img_width);
 	return 0;
 }
 
 static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
 {
+	struct tpg_data *tpg = &dev->tpg;
 	unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1;
 	unsigned line_height = 16 / factor;
 	bool is_tv = vivid_is_sdtv_cap(dev);
@@ -427,7 +448,7 @@
 		 * standards.
 		 */
 		buf->vb.v4l2_buf.field = ((dev->vid_cap_seq_count & 1) ^ is_60hz) ?
-			V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+			V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
 		/*
 		 * The sequence counter counts frames, not fields. So divide
 		 * by two.
@@ -436,27 +457,29 @@
 	} else {
 		buf->vb.v4l2_buf.field = dev->field_cap;
 	}
-	tpg_s_field(&dev->tpg, buf->vb.v4l2_buf.field);
-	tpg_s_perc_fill_blank(&dev->tpg, dev->must_blank[buf->vb.v4l2_buf.index]);
+	tpg_s_field(tpg, buf->vb.v4l2_buf.field,
+		    dev->field_cap == V4L2_FIELD_ALTERNATE);
+	tpg_s_perc_fill_blank(tpg, dev->must_blank[buf->vb.v4l2_buf.index]);
 
 	vivid_precalc_copy_rects(dev);
 
-	for (p = 0; p < tpg_g_planes(&dev->tpg); p++) {
-		void *vbuf = vb2_plane_vaddr(&buf->vb, p);
+	for (p = 0; p < tpg_g_planes(tpg); p++) {
+		void *vbuf = plane_vaddr(tpg, buf, p,
+					 tpg->bytesperline, tpg->buf_height);
 
 		/*
 		 * The first plane of a multiplanar format has a non-zero
 		 * data_offset. This helps testing whether the application
 		 * correctly supports non-zero data offsets.
 		 */
-		if (dev->fmt_cap->data_offset[p]) {
+		if (p < tpg_g_buffers(tpg) && dev->fmt_cap->data_offset[p]) {
 			memset(vbuf, dev->fmt_cap->data_offset[p] & 0xff,
 			       dev->fmt_cap->data_offset[p]);
 			vbuf += dev->fmt_cap->data_offset[p];
 		}
-		tpg_calc_text_basep(&dev->tpg, basep, p, vbuf);
+		tpg_calc_text_basep(tpg, basep, p, vbuf);
 		if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf))
-			tpg_fillbuffer(&dev->tpg, vivid_get_std_cap(dev), p, vbuf);
+			tpg_fill_plane_buffer(tpg, vivid_get_std_cap(dev), p, vbuf);
 	}
 	dev->must_blank[buf->vb.v4l2_buf.index] = false;
 
@@ -475,12 +498,12 @@
 				(dev->field_cap == V4L2_FIELD_ALTERNATE) ?
 					(buf->vb.v4l2_buf.field == V4L2_FIELD_TOP ?
 					 " top" : " bottom") : "");
-		tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+		tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
 	}
 	if (dev->osd_mode == 0) {
 		snprintf(str, sizeof(str), " %dx%d, input %d ",
 				dev->src_rect.width, dev->src_rect.height, dev->input);
-		tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+		tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
 
 		gain = v4l2_ctrl_g_ctrl(dev->gain);
 		mutex_lock(dev->ctrl_hdl_user_vid.lock);
@@ -490,38 +513,38 @@
 			dev->contrast->cur.val,
 			dev->saturation->cur.val,
 			dev->hue->cur.val);
-		tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+		tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
 		snprintf(str, sizeof(str),
 			" autogain %d, gain %3d, alpha 0x%02x ",
 			dev->autogain->cur.val, gain, dev->alpha->cur.val);
 		mutex_unlock(dev->ctrl_hdl_user_vid.lock);
-		tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+		tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
 		mutex_lock(dev->ctrl_hdl_user_aud.lock);
 		snprintf(str, sizeof(str),
 			" volume %3d, mute %d ",
 			dev->volume->cur.val, dev->mute->cur.val);
 		mutex_unlock(dev->ctrl_hdl_user_aud.lock);
-		tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+		tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
 		mutex_lock(dev->ctrl_hdl_user_gen.lock);
 		snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
 			dev->int32->cur.val,
 			*dev->int64->p_cur.p_s64,
 			dev->bitmask->cur.val);
-		tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+		tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
 		snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ",
 			dev->boolean->cur.val,
 			dev->menu->qmenu[dev->menu->cur.val],
 			dev->string->p_cur.p_char);
-		tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+		tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
 		snprintf(str, sizeof(str), " integer_menu %lld, value %d ",
 			dev->int_menu->qmenu_int[dev->int_menu->cur.val],
 			dev->int_menu->cur.val);
 		mutex_unlock(dev->ctrl_hdl_user_gen.lock);
-		tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+		tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
 		if (dev->button_pressed) {
 			dev->button_pressed--;
 			snprintf(str, sizeof(str), " button pressed!");
-			tpg_gen_text(&dev->tpg, basep, line++ * line_height, 16, str);
+			tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
 		}
 	}
 
@@ -585,6 +608,12 @@
 	bool quick = dev->bitmap_cap == NULL && dev->clipcount_cap == 0;
 	int x, y, w, out_x = 0;
 
+	/*
+	 * Overlay support is only supported for formats that have a twopixelsize
+	 * that's >= 2. Warn and bail out if that's not the case.
+	 */
+	if (WARN_ON(pixsize == 0))
+		return;
 	if ((dev->overlay_cap_field == V4L2_FIELD_TOP ||
 	     dev->overlay_cap_field == V4L2_FIELD_BOTTOM) &&
 	    dev->overlay_cap_field != buf->vb.v4l2_buf.field)
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c
index 4af55f1..caf1316 100644
--- a/drivers/media/platform/vivid/vivid-sdr-cap.c
+++ b/drivers/media/platform/vivid/vivid-sdr-cap.c
@@ -27,6 +27,7 @@
 #include <media/v4l2-common.h>
 #include <media/v4l2-event.h>
 #include <media/v4l2-dv-timings.h>
+#include <linux/fixp-arith.h>
 
 #include "vivid-core.h"
 #include "vivid-ctrls.h"
@@ -423,40 +424,19 @@
 	return 0;
 }
 
-#define FIXP_FRAC    (1 << 15)
-#define FIXP_PI      ((int)(FIXP_FRAC * 3.141592653589))
-
-/* cos() from cx88 driver: cx88-dsp.c */
-static s32 fixp_cos(unsigned int x)
-{
-	u32 t2, t4, t6, t8;
-	u16 period = x / FIXP_PI;
-
-	if (period % 2)
-		return -fixp_cos(x - FIXP_PI);
-	x = x % FIXP_PI;
-	if (x > FIXP_PI/2)
-		return -fixp_cos(FIXP_PI/2 - (x % (FIXP_PI/2)));
-	/* Now x is between 0 and FIXP_PI/2.
-	 * To calculate cos(x) we use it's Taylor polinom. */
-	t2 = x*x/FIXP_FRAC/2;
-	t4 = t2*x/FIXP_FRAC*x/FIXP_FRAC/3/4;
-	t6 = t4*x/FIXP_FRAC*x/FIXP_FRAC/5/6;
-	t8 = t6*x/FIXP_FRAC*x/FIXP_FRAC/7/8;
-	return FIXP_FRAC-t2+t4-t6+t8;
-}
-
-static inline s32 fixp_sin(unsigned int x)
-{
-	return -fixp_cos(x + (FIXP_PI / 2));
-}
+#define FIXP_N    (15)
+#define FIXP_FRAC (1 << FIXP_N)
+#define FIXP_2PI  ((int)(2 * 3.141592653589 * FIXP_FRAC))
 
 void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf)
 {
 	u8 *vbuf = vb2_plane_vaddr(&buf->vb, 0);
 	unsigned long i;
 	unsigned long plane_size = vb2_plane_size(&buf->vb, 0);
-	int fixp_src_phase_step, fixp_i, fixp_q;
+	s32 src_phase_step;
+	s32 mod_phase_step;
+	s32 fixp_i;
+	s32 fixp_q;
 
 	/*
 	 * TODO: Generated beep tone goes very crackly when sample rate is
@@ -466,28 +446,36 @@
 
 	/* calculate phase step */
 	#define BEEP_FREQ 1000 /* 1kHz beep */
-	fixp_src_phase_step = DIV_ROUND_CLOSEST(2 * FIXP_PI * BEEP_FREQ,
+	src_phase_step = DIV_ROUND_CLOSEST(FIXP_2PI * BEEP_FREQ,
 			dev->sdr_adc_freq);
 
 	for (i = 0; i < plane_size; i += 2) {
-		dev->sdr_fixp_mod_phase += fixp_cos(dev->sdr_fixp_src_phase);
-		dev->sdr_fixp_src_phase += fixp_src_phase_step;
+		mod_phase_step = fixp_cos32_rad(dev->sdr_fixp_src_phase,
+						FIXP_2PI) >> (31 - FIXP_N);
+
+		dev->sdr_fixp_src_phase += src_phase_step;
+		dev->sdr_fixp_mod_phase += mod_phase_step / 4;
 
 		/*
 		 * Transfer phases to [0 / 2xPI] in order to avoid variable
 		 * overflow and make it suitable for cosine implementation
 		 * used, which does not support negative angles.
 		 */
-		while (dev->sdr_fixp_mod_phase < (0 * FIXP_PI))
-			dev->sdr_fixp_mod_phase += (2 * FIXP_PI);
-		while (dev->sdr_fixp_mod_phase > (2 * FIXP_PI))
-			dev->sdr_fixp_mod_phase -= (2 * FIXP_PI);
+		while (dev->sdr_fixp_mod_phase < FIXP_2PI)
+			dev->sdr_fixp_mod_phase += FIXP_2PI;
+		while (dev->sdr_fixp_mod_phase > FIXP_2PI)
+			dev->sdr_fixp_mod_phase -= FIXP_2PI;
 
-		while (dev->sdr_fixp_src_phase > (2 * FIXP_PI))
-			dev->sdr_fixp_src_phase -= (2 * FIXP_PI);
+		while (dev->sdr_fixp_src_phase > FIXP_2PI)
+			dev->sdr_fixp_src_phase -= FIXP_2PI;
 
-		fixp_i = fixp_cos(dev->sdr_fixp_mod_phase);
-		fixp_q = fixp_sin(dev->sdr_fixp_mod_phase);
+		fixp_i = fixp_cos32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI);
+		fixp_q = fixp_sin32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI);
+
+		/* Normalize fraction values represented with 32 bit precision
+		 * to fixed point representation with FIXP_N bits */
+		fixp_i >>= (31 - FIXP_N);
+		fixp_q >>= (31 - FIXP_N);
 
 		/* convert 'fixp float' to u8 */
 		/* u8 = X * 127.5f + 127.5f; where X is float [-1.0 / +1.0] */
diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/platform/vivid/vivid-tpg.c
index fc9c653..cb766eb 100644
--- a/drivers/media/platform/vivid/vivid-tpg.c
+++ b/drivers/media/platform/vivid/vivid-tpg.c
@@ -35,7 +35,10 @@
 	"100% Green",
 	"100% Blue",
 	"16x16 Checkers",
+	"2x2 Checkers",
 	"1x1 Checkers",
+	"2x2 Red/Green Checkers",
+	"1x1 Red/Green Checkers",
 	"Alternating Hor Lines",
 	"Alternating Vert Lines",
 	"One Pixel Wide Cross",
@@ -120,15 +123,20 @@
 	tpg->max_line_width = max_w;
 	for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++) {
 		for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
-			unsigned pixelsz = plane ? 1 : 4;
+			unsigned pixelsz = plane ? 2 : 4;
 
 			tpg->lines[pat][plane] = vzalloc(max_w * 2 * pixelsz);
 			if (!tpg->lines[pat][plane])
 				return -ENOMEM;
+			if (plane == 0)
+				continue;
+			tpg->downsampled_lines[pat][plane] = vzalloc(max_w * 2 * pixelsz);
+			if (!tpg->downsampled_lines[pat][plane])
+				return -ENOMEM;
 		}
 	}
 	for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
-		unsigned pixelsz = plane ? 1 : 4;
+		unsigned pixelsz = plane ? 2 : 4;
 
 		tpg->contrast_line[plane] = vzalloc(max_w * pixelsz);
 		if (!tpg->contrast_line[plane])
@@ -152,6 +160,10 @@
 		for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
 			vfree(tpg->lines[pat][plane]);
 			tpg->lines[pat][plane] = NULL;
+			if (plane == 0)
+				continue;
+			vfree(tpg->downsampled_lines[pat][plane]);
+			tpg->downsampled_lines[pat][plane] = NULL;
 		}
 	for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
 		vfree(tpg->contrast_line[plane]);
@@ -167,14 +179,38 @@
 {
 	tpg->fourcc = fourcc;
 	tpg->planes = 1;
+	tpg->buffers = 1;
 	tpg->recalc_colors = true;
+	tpg->interleaved = false;
+	tpg->vdownsampling[0] = 1;
+	tpg->hdownsampling[0] = 1;
+	tpg->hmask[0] = ~0;
+	tpg->hmask[1] = ~0;
+	tpg->hmask[2] = ~0;
+
 	switch (fourcc) {
+	case V4L2_PIX_FMT_SBGGR8:
+	case V4L2_PIX_FMT_SGBRG8:
+	case V4L2_PIX_FMT_SGRBG8:
+	case V4L2_PIX_FMT_SRGGB8:
+		tpg->interleaved = true;
+		tpg->vdownsampling[1] = 1;
+		tpg->hdownsampling[1] = 1;
+		tpg->planes = 2;
+		/* fall through */
+	case V4L2_PIX_FMT_RGB332:
 	case V4L2_PIX_FMT_RGB565:
 	case V4L2_PIX_FMT_RGB565X:
+	case V4L2_PIX_FMT_RGB444:
+	case V4L2_PIX_FMT_XRGB444:
+	case V4L2_PIX_FMT_ARGB444:
 	case V4L2_PIX_FMT_RGB555:
 	case V4L2_PIX_FMT_XRGB555:
 	case V4L2_PIX_FMT_ARGB555:
 	case V4L2_PIX_FMT_RGB555X:
+	case V4L2_PIX_FMT_XRGB555X:
+	case V4L2_PIX_FMT_ARGB555X:
+	case V4L2_PIX_FMT_BGR666:
 	case V4L2_PIX_FMT_RGB24:
 	case V4L2_PIX_FMT_BGR24:
 	case V4L2_PIX_FMT_RGB32:
@@ -183,16 +219,72 @@
 	case V4L2_PIX_FMT_XBGR32:
 	case V4L2_PIX_FMT_ARGB32:
 	case V4L2_PIX_FMT_ABGR32:
+	case V4L2_PIX_FMT_GREY:
 		tpg->is_yuv = false;
 		break;
+	case V4L2_PIX_FMT_YUV444:
+	case V4L2_PIX_FMT_YUV555:
+	case V4L2_PIX_FMT_YUV565:
+	case V4L2_PIX_FMT_YUV32:
+		tpg->is_yuv = true;
+		break;
+	case V4L2_PIX_FMT_YUV420M:
+	case V4L2_PIX_FMT_YVU420M:
+		tpg->buffers = 3;
+		/* fall through */
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+		tpg->vdownsampling[1] = 2;
+		tpg->vdownsampling[2] = 2;
+		tpg->hdownsampling[1] = 2;
+		tpg->hdownsampling[2] = 2;
+		tpg->planes = 3;
+		tpg->is_yuv = true;
+		break;
+	case V4L2_PIX_FMT_YUV422P:
+		tpg->vdownsampling[1] = 1;
+		tpg->vdownsampling[2] = 1;
+		tpg->hdownsampling[1] = 2;
+		tpg->hdownsampling[2] = 2;
+		tpg->planes = 3;
+		tpg->is_yuv = true;
+		break;
 	case V4L2_PIX_FMT_NV16M:
 	case V4L2_PIX_FMT_NV61M:
+		tpg->buffers = 2;
+		/* fall through */
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_NV61:
+		tpg->vdownsampling[1] = 1;
+		tpg->hdownsampling[1] = 1;
+		tpg->hmask[1] = ~1;
 		tpg->planes = 2;
-		/* fall-through */
+		tpg->is_yuv = true;
+		break;
+	case V4L2_PIX_FMT_NV12M:
+	case V4L2_PIX_FMT_NV21M:
+		tpg->buffers = 2;
+		/* fall through */
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+		tpg->vdownsampling[1] = 2;
+		tpg->hdownsampling[1] = 1;
+		tpg->hmask[1] = ~1;
+		tpg->planes = 2;
+		tpg->is_yuv = true;
+		break;
+	case V4L2_PIX_FMT_NV24:
+	case V4L2_PIX_FMT_NV42:
+		tpg->vdownsampling[1] = 1;
+		tpg->hdownsampling[1] = 1;
+		tpg->planes = 2;
+		tpg->is_yuv = true;
+		break;
 	case V4L2_PIX_FMT_YUYV:
 	case V4L2_PIX_FMT_UYVY:
 	case V4L2_PIX_FMT_YVYU:
 	case V4L2_PIX_FMT_VYUY:
+		tpg->hmask[0] = ~1;
 		tpg->is_yuv = true;
 		break;
 	default:
@@ -200,35 +292,75 @@
 	}
 
 	switch (fourcc) {
+	case V4L2_PIX_FMT_RGB332:
+		tpg->twopixelsize[0] = 2;
+		break;
 	case V4L2_PIX_FMT_RGB565:
 	case V4L2_PIX_FMT_RGB565X:
+	case V4L2_PIX_FMT_RGB444:
+	case V4L2_PIX_FMT_XRGB444:
+	case V4L2_PIX_FMT_ARGB444:
 	case V4L2_PIX_FMT_RGB555:
 	case V4L2_PIX_FMT_XRGB555:
 	case V4L2_PIX_FMT_ARGB555:
 	case V4L2_PIX_FMT_RGB555X:
+	case V4L2_PIX_FMT_XRGB555X:
+	case V4L2_PIX_FMT_ARGB555X:
 	case V4L2_PIX_FMT_YUYV:
 	case V4L2_PIX_FMT_UYVY:
 	case V4L2_PIX_FMT_YVYU:
 	case V4L2_PIX_FMT_VYUY:
+	case V4L2_PIX_FMT_YUV444:
+	case V4L2_PIX_FMT_YUV555:
+	case V4L2_PIX_FMT_YUV565:
 		tpg->twopixelsize[0] = 2 * 2;
 		break;
 	case V4L2_PIX_FMT_RGB24:
 	case V4L2_PIX_FMT_BGR24:
 		tpg->twopixelsize[0] = 2 * 3;
 		break;
+	case V4L2_PIX_FMT_BGR666:
 	case V4L2_PIX_FMT_RGB32:
 	case V4L2_PIX_FMT_BGR32:
 	case V4L2_PIX_FMT_XRGB32:
 	case V4L2_PIX_FMT_XBGR32:
 	case V4L2_PIX_FMT_ARGB32:
 	case V4L2_PIX_FMT_ABGR32:
+	case V4L2_PIX_FMT_YUV32:
 		tpg->twopixelsize[0] = 2 * 4;
 		break;
+	case V4L2_PIX_FMT_GREY:
+		tpg->twopixelsize[0] = 2;
+		break;
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_NV12M:
+	case V4L2_PIX_FMT_NV21M:
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_NV61:
 	case V4L2_PIX_FMT_NV16M:
 	case V4L2_PIX_FMT_NV61M:
+	case V4L2_PIX_FMT_SBGGR8:
+	case V4L2_PIX_FMT_SGBRG8:
+	case V4L2_PIX_FMT_SGRBG8:
+	case V4L2_PIX_FMT_SRGGB8:
 		tpg->twopixelsize[0] = 2;
 		tpg->twopixelsize[1] = 2;
 		break;
+	case V4L2_PIX_FMT_YUV422P:
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+	case V4L2_PIX_FMT_YUV420M:
+	case V4L2_PIX_FMT_YVU420M:
+		tpg->twopixelsize[0] = 2;
+		tpg->twopixelsize[1] = 2;
+		tpg->twopixelsize[2] = 2;
+		break;
+	case V4L2_PIX_FMT_NV24:
+	case V4L2_PIX_FMT_NV42:
+		tpg->twopixelsize[0] = 2;
+		tpg->twopixelsize[1] = 4;
+		break;
 	}
 	return true;
 }
@@ -267,7 +399,8 @@
 	tpg->compose.width = width;
 	tpg->compose.height = tpg->buf_height;
 	for (p = 0; p < tpg->planes; p++)
-		tpg->bytesperline[p] = width * tpg->twopixelsize[p] / 2;
+		tpg->bytesperline[p] = (width * tpg->twopixelsize[p]) /
+				       (2 * tpg->hdownsampling[p]);
 	tpg->recalc_square_border = true;
 }
 
@@ -347,18 +480,19 @@
 		{ COEFF(0.5, 224),    COEFF(-0.445, 224), COEFF(-0.055, 224) },
 	};
 	static const int bt2020[3][3] = {
-		{ COEFF(0.2726, 219),  COEFF(0.6780, 219),  COEFF(0.0593, 219)  },
+		{ COEFF(0.2627, 219),  COEFF(0.6780, 219),  COEFF(0.0593, 219)  },
 		{ COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224)     },
-		{ COEFF(0.5, 224),     COEFF(-0.4629, 224), COEFF(-0.0405, 224) },
+		{ COEFF(0.5, 224),     COEFF(-0.4598, 224), COEFF(-0.0402, 224) },
 	};
 	bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
+	unsigned y_offset = full ? 0 : 16;
 	int lin_y, yc;
 
 	switch (tpg->real_ycbcr_enc) {
 	case V4L2_YCBCR_ENC_601:
 	case V4L2_YCBCR_ENC_XV601:
 	case V4L2_YCBCR_ENC_SYCC:
-		rgb2ycbcr(full ? bt601_full : bt601, r, g, b, 16, y, cb, cr);
+		rgb2ycbcr(full ? bt601_full : bt601, r, g, b, y_offset, y, cb, cr);
 		break;
 	case V4L2_YCBCR_ENC_BT2020:
 		rgb2ycbcr(bt2020, r, g, b, 16, y, cb, cr);
@@ -384,7 +518,7 @@
 	case V4L2_YCBCR_ENC_709:
 	case V4L2_YCBCR_ENC_XV709:
 	default:
-		rgb2ycbcr(full ? rec709_full : rec709, r, g, b, 0, y, cb, cr);
+		rgb2ycbcr(full ? rec709_full : rec709, r, g, b, y_offset, y, cb, cr);
 		break;
 	}
 }
@@ -439,13 +573,14 @@
 		{ COEFF(1, 219), COEFF(1.8814, 224),  COEFF(0, 224)       },
 	};
 	bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
+	unsigned y_offset = full ? 0 : 16;
 	int lin_r, lin_g, lin_b, lin_y;
 
 	switch (tpg->real_ycbcr_enc) {
 	case V4L2_YCBCR_ENC_601:
 	case V4L2_YCBCR_ENC_XV601:
 	case V4L2_YCBCR_ENC_SYCC:
-		ycbcr2rgb(full ? bt601_full : bt601, y, cb, cr, 16, r, g, b);
+		ycbcr2rgb(full ? bt601_full : bt601, y, cb, cr, y_offset, r, g, b);
 		break;
 	case V4L2_YCBCR_ENC_BT2020:
 		ycbcr2rgb(bt2020, y, cb, cr, 16, r, g, b);
@@ -480,7 +615,7 @@
 	case V4L2_YCBCR_ENC_709:
 	case V4L2_YCBCR_ENC_XV709:
 	default:
-		ycbcr2rgb(full ? rec709_full : rec709, y, cb, cr, 16, r, g, b);
+		ycbcr2rgb(full ? rec709_full : rec709, y, cb, cr, y_offset, r, g, b);
 		break;
 	}
 }
@@ -522,10 +657,10 @@
 		g <<= 4;
 		b <<= 4;
 	}
-	if (tpg->qual == TPG_QUAL_GRAY) {
+	if (tpg->qual == TPG_QUAL_GRAY || tpg->fourcc == V4L2_PIX_FMT_GREY) {
 		/* Rec. 709 Luma function */
 		/* (0.2126, 0.7152, 0.0722) * (255 * 256) */
-		r = g = b = ((13879 * r + 46688 * g + 4713 * b) >> 16) + (16 << 4);
+		r = g = b = (13879 * r + 46688 * g + 4713 * b) >> 16;
 	}
 
 	/*
@@ -599,9 +734,29 @@
 			cb = clamp(cb, 16 << 4, 240 << 4);
 			cr = clamp(cr, 16 << 4, 240 << 4);
 		}
-		tpg->colors[k][0] = clamp(y >> 4, 1, 254);
-		tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
-		tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
+		y = clamp(y >> 4, 1, 254);
+		cb = clamp(cb >> 4, 1, 254);
+		cr = clamp(cr >> 4, 1, 254);
+		switch (tpg->fourcc) {
+		case V4L2_PIX_FMT_YUV444:
+			y >>= 4;
+			cb >>= 4;
+			cr >>= 4;
+			break;
+		case V4L2_PIX_FMT_YUV555:
+			y >>= 3;
+			cb >>= 3;
+			cr >>= 3;
+			break;
+		case V4L2_PIX_FMT_YUV565:
+			y >>= 3;
+			cb >>= 2;
+			cr >>= 3;
+			break;
+		}
+		tpg->colors[k][0] = y;
+		tpg->colors[k][1] = cb;
+		tpg->colors[k][2] = cr;
 	} else {
 		if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE) {
 			r = (r * 219) / 255 + (16 << 4);
@@ -609,20 +764,39 @@
 			b = (b * 219) / 255 + (16 << 4);
 		}
 		switch (tpg->fourcc) {
+		case V4L2_PIX_FMT_RGB332:
+			r >>= 9;
+			g >>= 9;
+			b >>= 10;
+			break;
 		case V4L2_PIX_FMT_RGB565:
 		case V4L2_PIX_FMT_RGB565X:
 			r >>= 7;
 			g >>= 6;
 			b >>= 7;
 			break;
+		case V4L2_PIX_FMT_RGB444:
+		case V4L2_PIX_FMT_XRGB444:
+		case V4L2_PIX_FMT_ARGB444:
+			r >>= 8;
+			g >>= 8;
+			b >>= 8;
+			break;
 		case V4L2_PIX_FMT_RGB555:
 		case V4L2_PIX_FMT_XRGB555:
 		case V4L2_PIX_FMT_ARGB555:
 		case V4L2_PIX_FMT_RGB555X:
+		case V4L2_PIX_FMT_XRGB555X:
+		case V4L2_PIX_FMT_ARGB555X:
 			r >>= 7;
 			g >>= 7;
 			b >>= 7;
 			break;
+		case V4L2_PIX_FMT_BGR666:
+			r >>= 6;
+			g >>= 6;
+			b >>= 6;
+			break;
 		default:
 			r >>= 4;
 			g >>= 4;
@@ -663,31 +837,120 @@
 	b_v = tpg->colors[color][2]; /* B or precalculated V */
 
 	switch (tpg->fourcc) {
+	case V4L2_PIX_FMT_GREY:
+		buf[0][offset] = r_y;
+		break;
+	case V4L2_PIX_FMT_YUV422P:
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YUV420M:
+		buf[0][offset] = r_y;
+		if (odd) {
+			buf[1][0] = (buf[1][0] + g_u) / 2;
+			buf[2][0] = (buf[2][0] + b_v) / 2;
+			buf[1][1] = buf[1][0];
+			buf[2][1] = buf[2][0];
+			break;
+		}
+		buf[1][0] = g_u;
+		buf[2][0] = b_v;
+		break;
+	case V4L2_PIX_FMT_YVU420:
+	case V4L2_PIX_FMT_YVU420M:
+		buf[0][offset] = r_y;
+		if (odd) {
+			buf[1][0] = (buf[1][0] + b_v) / 2;
+			buf[2][0] = (buf[2][0] + g_u) / 2;
+			buf[1][1] = buf[1][0];
+			buf[2][1] = buf[2][0];
+			break;
+		}
+		buf[1][0] = b_v;
+		buf[2][0] = g_u;
+		break;
+
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV12M:
+	case V4L2_PIX_FMT_NV16:
 	case V4L2_PIX_FMT_NV16M:
 		buf[0][offset] = r_y;
-		buf[1][offset] = odd ? b_v : g_u;
+		if (odd) {
+			buf[1][0] = (buf[1][0] + g_u) / 2;
+			buf[1][1] = (buf[1][1] + b_v) / 2;
+			break;
+		}
+		buf[1][0] = g_u;
+		buf[1][1] = b_v;
 		break;
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_NV21M:
+	case V4L2_PIX_FMT_NV61:
 	case V4L2_PIX_FMT_NV61M:
 		buf[0][offset] = r_y;
-		buf[1][offset] = odd ? g_u : b_v;
+		if (odd) {
+			buf[1][0] = (buf[1][0] + b_v) / 2;
+			buf[1][1] = (buf[1][1] + g_u) / 2;
+			break;
+		}
+		buf[1][0] = b_v;
+		buf[1][1] = g_u;
+		break;
+
+	case V4L2_PIX_FMT_NV24:
+		buf[0][offset] = r_y;
+		buf[1][2 * offset] = g_u;
+		buf[1][2 * offset + 1] = b_v;
+		break;
+
+	case V4L2_PIX_FMT_NV42:
+		buf[0][offset] = r_y;
+		buf[1][2 * offset] = b_v;
+		buf[1][2 * offset + 1] = g_u;
 		break;
 
 	case V4L2_PIX_FMT_YUYV:
 		buf[0][offset] = r_y;
-		buf[0][offset + 1] = odd ? b_v : g_u;
+		if (odd) {
+			buf[0][1] = (buf[0][1] + g_u) / 2;
+			buf[0][3] = (buf[0][3] + b_v) / 2;
+			break;
+		}
+		buf[0][1] = g_u;
+		buf[0][3] = b_v;
 		break;
 	case V4L2_PIX_FMT_UYVY:
-		buf[0][offset] = odd ? b_v : g_u;
 		buf[0][offset + 1] = r_y;
+		if (odd) {
+			buf[0][0] = (buf[0][0] + g_u) / 2;
+			buf[0][2] = (buf[0][2] + b_v) / 2;
+			break;
+		}
+		buf[0][0] = g_u;
+		buf[0][2] = b_v;
 		break;
 	case V4L2_PIX_FMT_YVYU:
 		buf[0][offset] = r_y;
-		buf[0][offset + 1] = odd ? g_u : b_v;
+		if (odd) {
+			buf[0][1] = (buf[0][1] + b_v) / 2;
+			buf[0][3] = (buf[0][3] + g_u) / 2;
+			break;
+		}
+		buf[0][1] = b_v;
+		buf[0][3] = g_u;
 		break;
 	case V4L2_PIX_FMT_VYUY:
-		buf[0][offset] = odd ? g_u : b_v;
 		buf[0][offset + 1] = r_y;
+		if (odd) {
+			buf[0][0] = (buf[0][0] + b_v) / 2;
+			buf[0][2] = (buf[0][2] + g_u) / 2;
+			break;
+		}
+		buf[0][0] = b_v;
+		buf[0][2] = g_u;
 		break;
+	case V4L2_PIX_FMT_RGB332:
+		buf[0][offset] = (r_y << 5) | (g_u << 2) | b_v;
+		break;
+	case V4L2_PIX_FMT_YUV565:
 	case V4L2_PIX_FMT_RGB565:
 		buf[0][offset] = (g_u << 5) | b_v;
 		buf[0][offset + 1] = (r_y << 3) | (g_u >> 3);
@@ -696,15 +959,29 @@
 		buf[0][offset] = (r_y << 3) | (g_u >> 3);
 		buf[0][offset + 1] = (g_u << 5) | b_v;
 		break;
+	case V4L2_PIX_FMT_RGB444:
+	case V4L2_PIX_FMT_XRGB444:
+		alpha = 0;
+		/* fall through */
+	case V4L2_PIX_FMT_YUV444:
+	case V4L2_PIX_FMT_ARGB444:
+		buf[0][offset] = (g_u << 4) | b_v;
+		buf[0][offset + 1] = (alpha & 0xf0) | r_y;
+		break;
 	case V4L2_PIX_FMT_RGB555:
 	case V4L2_PIX_FMT_XRGB555:
 		alpha = 0;
 		/* fall through */
+	case V4L2_PIX_FMT_YUV555:
 	case V4L2_PIX_FMT_ARGB555:
 		buf[0][offset] = (g_u << 5) | b_v;
 		buf[0][offset + 1] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
 		break;
 	case V4L2_PIX_FMT_RGB555X:
+	case V4L2_PIX_FMT_XRGB555X:
+		alpha = 0;
+		/* fall through */
+	case V4L2_PIX_FMT_ARGB555X:
 		buf[0][offset] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
 		buf[0][offset + 1] = (g_u << 5) | b_v;
 		break;
@@ -718,10 +995,17 @@
 		buf[0][offset + 1] = g_u;
 		buf[0][offset + 2] = r_y;
 		break;
+	case V4L2_PIX_FMT_BGR666:
+		buf[0][offset] = (b_v << 2) | (g_u >> 4);
+		buf[0][offset + 1] = (g_u << 4) | (r_y >> 2);
+		buf[0][offset + 2] = r_y << 6;
+		buf[0][offset + 3] = 0;
+		break;
 	case V4L2_PIX_FMT_RGB32:
 	case V4L2_PIX_FMT_XRGB32:
 		alpha = 0;
 		/* fall through */
+	case V4L2_PIX_FMT_YUV32:
 	case V4L2_PIX_FMT_ARGB32:
 		buf[0][offset] = alpha;
 		buf[0][offset + 1] = r_y;
@@ -738,15 +1022,47 @@
 		buf[0][offset + 2] = r_y;
 		buf[0][offset + 3] = alpha;
 		break;
+	case V4L2_PIX_FMT_SBGGR8:
+		buf[0][offset] = odd ? g_u : b_v;
+		buf[1][offset] = odd ? r_y : g_u;
+		break;
+	case V4L2_PIX_FMT_SGBRG8:
+		buf[0][offset] = odd ? b_v : g_u;
+		buf[1][offset] = odd ? g_u : r_y;
+		break;
+	case V4L2_PIX_FMT_SGRBG8:
+		buf[0][offset] = odd ? r_y : g_u;
+		buf[1][offset] = odd ? g_u : b_v;
+		break;
+	case V4L2_PIX_FMT_SRGGB8:
+		buf[0][offset] = odd ? g_u : r_y;
+		buf[1][offset] = odd ? b_v : g_u;
+		break;
+	}
+}
+
+unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line)
+{
+	switch (tpg->fourcc) {
+	case V4L2_PIX_FMT_SBGGR8:
+	case V4L2_PIX_FMT_SGBRG8:
+	case V4L2_PIX_FMT_SGRBG8:
+	case V4L2_PIX_FMT_SRGGB8:
+		return buf_line & 1;
+	default:
+		return 0;
 	}
 }
 
 /* Return how many pattern lines are used by the current pattern. */
-static unsigned tpg_get_pat_lines(struct tpg_data *tpg)
+static unsigned tpg_get_pat_lines(const struct tpg_data *tpg)
 {
 	switch (tpg->pattern) {
 	case TPG_PAT_CHECKERS_16X16:
+	case TPG_PAT_CHECKERS_2X2:
 	case TPG_PAT_CHECKERS_1X1:
+	case TPG_PAT_COLOR_CHECKERS_2X2:
+	case TPG_PAT_COLOR_CHECKERS_1X1:
 	case TPG_PAT_ALTERNATING_HLINES:
 	case TPG_PAT_CROSS_1_PIXEL:
 	case TPG_PAT_CROSS_2_PIXELS:
@@ -761,14 +1077,18 @@
 }
 
 /* Which pattern line should be used for the given frame line. */
-static unsigned tpg_get_pat_line(struct tpg_data *tpg, unsigned line)
+static unsigned tpg_get_pat_line(const struct tpg_data *tpg, unsigned line)
 {
 	switch (tpg->pattern) {
 	case TPG_PAT_CHECKERS_16X16:
 		return (line >> 4) & 1;
 	case TPG_PAT_CHECKERS_1X1:
+	case TPG_PAT_COLOR_CHECKERS_1X1:
 	case TPG_PAT_ALTERNATING_HLINES:
 		return line & 1;
+	case TPG_PAT_CHECKERS_2X2:
+	case TPG_PAT_COLOR_CHECKERS_2X2:
+		return (line & 2) >> 1;
 	case TPG_PAT_100_COLORSQUARES:
 	case TPG_PAT_100_HCOLORBAR:
 		return (line * 8) / tpg->src_height;
@@ -787,7 +1107,8 @@
  * Which color should be used for the given pattern line and X coordinate.
  * Note: x is in the range 0 to 2 * tpg->src_width.
  */
-static enum tpg_color tpg_get_color(struct tpg_data *tpg, unsigned pat_line, unsigned x)
+static enum tpg_color tpg_get_color(const struct tpg_data *tpg,
+				    unsigned pat_line, unsigned x)
 {
 	/* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
 	   should be modified */
@@ -834,6 +1155,15 @@
 	case TPG_PAT_CHECKERS_1X1:
 		return ((x & 1) ^ (pat_line & 1)) ?
 			TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+	case TPG_PAT_COLOR_CHECKERS_1X1:
+		return ((x & 1) ^ (pat_line & 1)) ?
+			TPG_COLOR_100_RED : TPG_COLOR_100_BLUE;
+	case TPG_PAT_CHECKERS_2X2:
+		return (((x >> 1) & 1) ^ (pat_line & 1)) ?
+			TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+	case TPG_PAT_COLOR_CHECKERS_2X2:
+		return (((x >> 1) & 1) ^ (pat_line & 1)) ?
+			TPG_COLOR_100_RED : TPG_COLOR_100_BLUE;
 	case TPG_PAT_ALTERNATING_HLINES:
 		return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
 	case TPG_PAT_ALTERNATING_VLINES:
@@ -946,6 +1276,7 @@
 static void tpg_precalculate_line(struct tpg_data *tpg)
 {
 	enum tpg_color contrast;
+	u8 pix[TPG_MAX_PLANES][8];
 	unsigned pat;
 	unsigned p;
 	unsigned x;
@@ -972,7 +1303,6 @@
 		for (x = 0; x < tpg->scaled_width * 2; x += 2) {
 			unsigned real_x = src_x;
 			enum tpg_color color1, color2;
-			u8 pix[TPG_MAX_PLANES][8];
 
 			real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
 			color1 = tpg_get_color(tpg, pat, real_x);
@@ -999,39 +1329,53 @@
 			gen_twopix(tpg, pix, tpg->hflip ? color1 : color2, 1);
 			for (p = 0; p < tpg->planes; p++) {
 				unsigned twopixsize = tpg->twopixelsize[p];
-				u8 *pos = tpg->lines[pat][p] + x * twopixsize / 2;
+				unsigned hdiv = tpg->hdownsampling[p];
+				u8 *pos = tpg->lines[pat][p] + tpg_hdiv(tpg, p, x);
 
-				memcpy(pos, pix[p], twopixsize);
+				memcpy(pos, pix[p], twopixsize / hdiv);
 			}
 		}
 	}
-	for (x = 0; x < tpg->scaled_width; x += 2) {
-		u8 pix[TPG_MAX_PLANES][8];
 
-		gen_twopix(tpg, pix, contrast, 0);
-		gen_twopix(tpg, pix, contrast, 1);
-		for (p = 0; p < tpg->planes; p++) {
-			unsigned twopixsize = tpg->twopixelsize[p];
-			u8 *pos = tpg->contrast_line[p] + x * twopixsize / 2;
+	if (tpg->vdownsampling[tpg->planes - 1] > 1) {
+		unsigned pat_lines = tpg_get_pat_lines(tpg);
 
-			memcpy(pos, pix[p], twopixsize);
+		for (pat = 0; pat < pat_lines; pat++) {
+			unsigned next_pat = (pat + 1) % pat_lines;
+
+			for (p = 1; p < tpg->planes; p++) {
+				unsigned w = tpg_hdiv(tpg, p, tpg->scaled_width * 2);
+				u8 *pos1 = tpg->lines[pat][p];
+				u8 *pos2 = tpg->lines[next_pat][p];
+				u8 *dest = tpg->downsampled_lines[pat][p];
+
+				for (x = 0; x < w; x++, pos1++, pos2++, dest++)
+					*dest = ((u16)*pos1 + (u16)*pos2) / 2;
+			}
 		}
 	}
-	for (x = 0; x < tpg->scaled_width; x += 2) {
-		u8 pix[TPG_MAX_PLANES][8];
 
-		gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0);
-		gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1);
-		for (p = 0; p < tpg->planes; p++) {
-			unsigned twopixsize = tpg->twopixelsize[p];
-			u8 *pos = tpg->black_line[p] + x * twopixsize / 2;
+	gen_twopix(tpg, pix, contrast, 0);
+	gen_twopix(tpg, pix, contrast, 1);
+	for (p = 0; p < tpg->planes; p++) {
+		unsigned twopixsize = tpg->twopixelsize[p];
+		u8 *pos = tpg->contrast_line[p];
 
+		for (x = 0; x < tpg->scaled_width; x += 2, pos += twopixsize)
 			memcpy(pos, pix[p], twopixsize);
-		}
 	}
+
+	gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0);
+	gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1);
+	for (p = 0; p < tpg->planes; p++) {
+		unsigned twopixsize = tpg->twopixelsize[p];
+		u8 *pos = tpg->black_line[p];
+
+		for (x = 0; x < tpg->scaled_width; x += 2, pos += twopixsize)
+			memcpy(pos, pix[p], twopixsize);
+	}
+
 	for (x = 0; x < tpg->scaled_width * 2; x += 2) {
-		u8 pix[TPG_MAX_PLANES][8];
-
 		gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 0);
 		gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 1);
 		for (p = 0; p < tpg->planes; p++) {
@@ -1041,6 +1385,7 @@
 			memcpy(pos, pix[p], twopixsize);
 		}
 	}
+
 	gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 0);
 	gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 1);
 	gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 0);
@@ -1050,8 +1395,8 @@
 /* need this to do rgb24 rendering */
 typedef struct { u16 __; u8 _; } __packed x24;
 
-void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
-		int y, int x, char *text)
+void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+		  int y, int x, char *text)
 {
 	int line;
 	unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
@@ -1081,24 +1426,37 @@
 		div = 2;
 
 	for (p = 0; p < tpg->planes; p++) {
-		/* Print stream time */
+		unsigned vdiv = tpg->vdownsampling[p];
+		unsigned hdiv = tpg->hdownsampling[p];
+
+		/* Print text */
 #define PRINTSTR(PIXTYPE) do {	\
 	PIXTYPE fg;	\
 	PIXTYPE bg;	\
 	memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE));	\
 	memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE));	\
 	\
-	for (line = first; line < 16; line += step) {	\
+	for (line = first; line < 16; line += vdiv * step) {	\
 		int l = tpg->vflip ? 15 - line : line; \
-		PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \
-			       ((y * step + l) / div) * tpg->bytesperline[p] + \
-			       x * sizeof(PIXTYPE));	\
+		PIXTYPE *pos = (PIXTYPE *)(basep[p][(line / vdiv) & 1] + \
+			       ((y * step + l) / (vdiv * div)) * tpg->bytesperline[p] + \
+			       (x / hdiv) * sizeof(PIXTYPE));	\
 		unsigned s;	\
 	\
 		for (s = 0; s < len; s++) {	\
 			u8 chr = font8x16[text[s] * 16 + line];	\
 	\
-			if (tpg->hflip) { \
+			if (hdiv == 2 && tpg->hflip) { \
+				pos[3] = (chr & (0x01 << 6) ? fg : bg);	\
+				pos[2] = (chr & (0x01 << 4) ? fg : bg);	\
+				pos[1] = (chr & (0x01 << 2) ? fg : bg);	\
+				pos[0] = (chr & (0x01 << 0) ? fg : bg);	\
+			} else if (hdiv == 2) { \
+				pos[0] = (chr & (0x01 << 7) ? fg : bg);	\
+				pos[1] = (chr & (0x01 << 5) ? fg : bg);	\
+				pos[2] = (chr & (0x01 << 3) ? fg : bg);	\
+				pos[3] = (chr & (0x01 << 1) ? fg : bg);	\
+			} else if (tpg->hflip) { \
 				pos[7] = (chr & (0x01 << 7) ? fg : bg);	\
 				pos[6] = (chr & (0x01 << 6) ? fg : bg);	\
 				pos[5] = (chr & (0x01 << 5) ? fg : bg);	\
@@ -1118,7 +1476,7 @@
 				pos[7] = (chr & (0x01 << 0) ? fg : bg);	\
 			} \
 	\
-			pos += tpg->hflip ? -8 : 8;	\
+			pos += (tpg->hflip ? -8 : 8) / hdiv;	\
 		}	\
 	}	\
 } while (0)
@@ -1185,7 +1543,7 @@
 }
 
 /* Map the line number relative to the crop rectangle to a frame line number */
-static unsigned tpg_calc_frameline(struct tpg_data *tpg, unsigned src_y,
+static unsigned tpg_calc_frameline(const struct tpg_data *tpg, unsigned src_y,
 				    unsigned field)
 {
 	switch (field) {
@@ -1202,7 +1560,7 @@
  * Map the line number relative to the compose rectangle to a destination
  * buffer line number.
  */
-static unsigned tpg_calc_buffer_line(struct tpg_data *tpg, unsigned y,
+static unsigned tpg_calc_buffer_line(const struct tpg_data *tpg, unsigned y,
 				    unsigned field)
 {
 	y += tpg->compose.top;
@@ -1263,6 +1621,10 @@
 						V4L2_QUANTIZATION_LIM_RANGE;
 					break;
 				}
+			} else if (tpg->colorspace == V4L2_COLORSPACE_BT2020) {
+				/* R'G'B' BT.2020 is limited range */
+				tpg->real_quantization =
+					V4L2_QUANTIZATION_LIM_RANGE;
 			}
 		}
 		tpg_precalculate_colors(tpg);
@@ -1281,88 +1643,387 @@
 		u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf)
 {
 	unsigned stride = tpg->bytesperline[p];
+	unsigned h = tpg->buf_height;
 
 	tpg_recalc(tpg);
 
 	basep[p][0] = vbuf;
 	basep[p][1] = vbuf;
+	h /= tpg->vdownsampling[p];
 	if (tpg->field == V4L2_FIELD_SEQ_TB)
-		basep[p][1] += tpg->buf_height * stride / 2;
+		basep[p][1] += h * stride / 2;
 	else if (tpg->field == V4L2_FIELD_SEQ_BT)
-		basep[p][0] += tpg->buf_height * stride / 2;
+		basep[p][0] += h * stride / 2;
+	if (p == 0 && tpg->interleaved)
+		tpg_calc_text_basep(tpg, basep, 1, vbuf);
 }
 
-void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
+static int tpg_pattern_avg(const struct tpg_data *tpg,
+			   unsigned pat1, unsigned pat2)
 {
-	bool is_tv = std;
-	bool is_60hz = is_tv && (std & V4L2_STD_525_60);
-	unsigned mv_hor_old = tpg->mv_hor_count % tpg->src_width;
-	unsigned mv_hor_new = (tpg->mv_hor_count + tpg->mv_hor_step) % tpg->src_width;
-	unsigned mv_vert_old = tpg->mv_vert_count % tpg->src_height;
-	unsigned mv_vert_new = (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height;
+	unsigned pat_lines = tpg_get_pat_lines(tpg);
+
+	if (pat1 == (pat2 + 1) % pat_lines)
+		return pat2;
+	if (pat2 == (pat1 + 1) % pat_lines)
+		return pat1;
+	return -1;
+}
+
+/*
+ * This struct contains common parameters used by both the drawing of the
+ * test pattern and the drawing of the extras (borders, square, etc.)
+ */
+struct tpg_draw_params {
+	/* common data */
+	bool is_tv;
+	bool is_60hz;
+	unsigned twopixsize;
+	unsigned img_width;
+	unsigned stride;
+	unsigned hmax;
+	unsigned frame_line;
+	unsigned frame_line_next;
+
+	/* test pattern */
+	unsigned mv_hor_old;
+	unsigned mv_hor_new;
+	unsigned mv_vert_old;
+	unsigned mv_vert_new;
+
+	/* extras */
 	unsigned wss_width;
-	unsigned f;
-	int hmax = (tpg->compose.height * tpg->perc_fill) / 100;
-	int h;
-	unsigned twopixsize = tpg->twopixelsize[p];
-	unsigned img_width = tpg->compose.width * twopixsize / 2;
-	unsigned line_offset;
+	unsigned wss_random_offset;
+	unsigned sav_eav_f;
+	unsigned left_pillar_width;
+	unsigned right_pillar_start;
+};
+
+static void tpg_fill_params_pattern(const struct tpg_data *tpg, unsigned p,
+				    struct tpg_draw_params *params)
+{
+	params->mv_hor_old =
+		tpg_hscale_div(tpg, p, tpg->mv_hor_count % tpg->src_width);
+	params->mv_hor_new =
+		tpg_hscale_div(tpg, p, (tpg->mv_hor_count + tpg->mv_hor_step) %
+			       tpg->src_width);
+	params->mv_vert_old = tpg->mv_vert_count % tpg->src_height;
+	params->mv_vert_new =
+		(tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height;
+}
+
+static void tpg_fill_params_extras(const struct tpg_data *tpg,
+				   unsigned p,
+				   struct tpg_draw_params *params)
+{
 	unsigned left_pillar_width = 0;
-	unsigned right_pillar_start = img_width;
-	unsigned stride = tpg->bytesperline[p];
+	unsigned right_pillar_start = params->img_width;
+
+	params->wss_width = tpg->crop.left < tpg->src_width / 2 ?
+		tpg->src_width / 2 - tpg->crop.left : 0;
+	if (params->wss_width > tpg->crop.width)
+		params->wss_width = tpg->crop.width;
+	params->wss_width = tpg_hscale_div(tpg, p, params->wss_width);
+	params->wss_random_offset =
+		params->twopixsize * prandom_u32_max(tpg->src_width / 2);
+
+	if (tpg->crop.left < tpg->border.left) {
+		left_pillar_width = tpg->border.left - tpg->crop.left;
+		if (left_pillar_width > tpg->crop.width)
+			left_pillar_width = tpg->crop.width;
+		left_pillar_width = tpg_hscale_div(tpg, p, left_pillar_width);
+	}
+	params->left_pillar_width = left_pillar_width;
+
+	if (tpg->crop.left + tpg->crop.width >
+	    tpg->border.left + tpg->border.width) {
+		right_pillar_start =
+			tpg->border.left + tpg->border.width - tpg->crop.left;
+		right_pillar_start =
+			tpg_hscale_div(tpg, p, right_pillar_start);
+		if (right_pillar_start > params->img_width)
+			right_pillar_start = params->img_width;
+	}
+	params->right_pillar_start = right_pillar_start;
+
+	params->sav_eav_f = tpg->field ==
+			(params->is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+}
+
+static void tpg_fill_plane_extras(const struct tpg_data *tpg,
+				  const struct tpg_draw_params *params,
+				  unsigned p, unsigned h, u8 *vbuf)
+{
+	unsigned twopixsize = params->twopixsize;
+	unsigned img_width = params->img_width;
+	unsigned frame_line = params->frame_line;
+	const struct v4l2_rect *sq = &tpg->square;
+	const struct v4l2_rect *b = &tpg->border;
+	const struct v4l2_rect *c = &tpg->crop;
+
+	if (params->is_tv && !params->is_60hz &&
+	    frame_line == 0 && params->wss_width) {
+		/*
+		 * Replace the first half of the top line of a 50 Hz frame
+		 * with random data to simulate a WSS signal.
+		 */
+		u8 *wss = tpg->random_line[p] + params->wss_random_offset;
+
+		memcpy(vbuf, wss, params->wss_width);
+	}
+
+	if (tpg->show_border && frame_line >= b->top &&
+	    frame_line < b->top + b->height) {
+		unsigned bottom = b->top + b->height - 1;
+		unsigned left = params->left_pillar_width;
+		unsigned right = params->right_pillar_start;
+
+		if (frame_line == b->top || frame_line == b->top + 1 ||
+		    frame_line == bottom || frame_line == bottom - 1) {
+			memcpy(vbuf + left, tpg->contrast_line[p],
+					right - left);
+		} else {
+			if (b->left >= c->left &&
+			    b->left < c->left + c->width)
+				memcpy(vbuf + left,
+					tpg->contrast_line[p], twopixsize);
+			if (b->left + b->width > c->left &&
+			    b->left + b->width <= c->left + c->width)
+				memcpy(vbuf + right - twopixsize,
+					tpg->contrast_line[p], twopixsize);
+		}
+	}
+	if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top &&
+	    frame_line < b->top + b->height) {
+		memcpy(vbuf, tpg->black_line[p], params->left_pillar_width);
+		memcpy(vbuf + params->right_pillar_start, tpg->black_line[p],
+		       img_width - params->right_pillar_start);
+	}
+	if (tpg->show_square && frame_line >= sq->top &&
+	    frame_line < sq->top + sq->height &&
+	    sq->left < c->left + c->width &&
+	    sq->left + sq->width >= c->left) {
+		unsigned left = sq->left;
+		unsigned width = sq->width;
+
+		if (c->left > left) {
+			width -= c->left - left;
+			left = c->left;
+		}
+		if (c->left + c->width < left + width)
+			width -= left + width - c->left - c->width;
+		left -= c->left;
+		left = tpg_hscale_div(tpg, p, left);
+		width = tpg_hscale_div(tpg, p, width);
+		memcpy(vbuf + left, tpg->contrast_line[p], width);
+	}
+	if (tpg->insert_sav) {
+		unsigned offset = tpg_hdiv(tpg, p, tpg->compose.width / 3);
+		u8 *p = vbuf + offset;
+		unsigned vact = 0, hact = 0;
+
+		p[0] = 0xff;
+		p[1] = 0;
+		p[2] = 0;
+		p[3] = 0x80 | (params->sav_eav_f << 6) |
+			(vact << 5) | (hact << 4) |
+			((hact ^ vact) << 3) |
+			((hact ^ params->sav_eav_f) << 2) |
+			((params->sav_eav_f ^ vact) << 1) |
+			(hact ^ vact ^ params->sav_eav_f);
+	}
+	if (tpg->insert_eav) {
+		unsigned offset = tpg_hdiv(tpg, p, tpg->compose.width * 2 / 3);
+		u8 *p = vbuf + offset;
+		unsigned vact = 0, hact = 1;
+
+		p[0] = 0xff;
+		p[1] = 0;
+		p[2] = 0;
+		p[3] = 0x80 | (params->sav_eav_f << 6) |
+			(vact << 5) | (hact << 4) |
+			((hact ^ vact) << 3) |
+			((hact ^ params->sav_eav_f) << 2) |
+			((params->sav_eav_f ^ vact) << 1) |
+			(hact ^ vact ^ params->sav_eav_f);
+	}
+}
+
+static void tpg_fill_plane_pattern(const struct tpg_data *tpg,
+				   const struct tpg_draw_params *params,
+				   unsigned p, unsigned h, u8 *vbuf)
+{
+	unsigned twopixsize = params->twopixsize;
+	unsigned img_width = params->img_width;
+	unsigned mv_hor_old = params->mv_hor_old;
+	unsigned mv_hor_new = params->mv_hor_new;
+	unsigned mv_vert_old = params->mv_vert_old;
+	unsigned mv_vert_new = params->mv_vert_new;
+	unsigned frame_line = params->frame_line;
+	unsigned frame_line_next = params->frame_line_next;
+	unsigned line_offset = tpg_hscale_div(tpg, p, tpg->crop.left);
+	bool even;
+	bool fill_blank = false;
+	unsigned pat_line_old;
+	unsigned pat_line_new;
+	u8 *linestart_older;
+	u8 *linestart_newer;
+	u8 *linestart_top;
+	u8 *linestart_bottom;
+
+	even = !(frame_line & 1);
+
+	if (h >= params->hmax) {
+		if (params->hmax == tpg->compose.height)
+			return;
+		if (!tpg->perc_fill_blank)
+			return;
+		fill_blank = true;
+	}
+
+	if (tpg->vflip) {
+		frame_line = tpg->src_height - frame_line - 1;
+		frame_line_next = tpg->src_height - frame_line_next - 1;
+	}
+
+	if (fill_blank) {
+		linestart_older = tpg->contrast_line[p];
+		linestart_newer = tpg->contrast_line[p];
+	} else if (tpg->qual != TPG_QUAL_NOISE &&
+		   (frame_line < tpg->border.top ||
+		    frame_line >= tpg->border.top + tpg->border.height)) {
+		linestart_older = tpg->black_line[p];
+		linestart_newer = tpg->black_line[p];
+	} else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) {
+		linestart_older = tpg->random_line[p] +
+				  twopixsize * prandom_u32_max(tpg->src_width / 2);
+		linestart_newer = tpg->random_line[p] +
+				  twopixsize * prandom_u32_max(tpg->src_width / 2);
+	} else {
+		unsigned frame_line_old =
+			(frame_line + mv_vert_old) % tpg->src_height;
+		unsigned frame_line_new =
+			(frame_line + mv_vert_new) % tpg->src_height;
+		unsigned pat_line_next_old;
+		unsigned pat_line_next_new;
+
+		pat_line_old = tpg_get_pat_line(tpg, frame_line_old);
+		pat_line_new = tpg_get_pat_line(tpg, frame_line_new);
+		linestart_older = tpg->lines[pat_line_old][p] + mv_hor_old;
+		linestart_newer = tpg->lines[pat_line_new][p] + mv_hor_new;
+
+		if (tpg->vdownsampling[p] > 1 && frame_line != frame_line_next) {
+			int avg_pat;
+
+			/*
+			 * Now decide whether we need to use downsampled_lines[].
+			 * That's necessary if the two lines use different patterns.
+			 */
+			pat_line_next_old = tpg_get_pat_line(tpg,
+					(frame_line_next + mv_vert_old) % tpg->src_height);
+			pat_line_next_new = tpg_get_pat_line(tpg,
+					(frame_line_next + mv_vert_new) % tpg->src_height);
+
+			switch (tpg->field) {
+			case V4L2_FIELD_INTERLACED:
+			case V4L2_FIELD_INTERLACED_BT:
+			case V4L2_FIELD_INTERLACED_TB:
+				avg_pat = tpg_pattern_avg(tpg, pat_line_old, pat_line_new);
+				if (avg_pat < 0)
+					break;
+				linestart_older = tpg->downsampled_lines[avg_pat][p] + mv_hor_old;
+				linestart_newer = linestart_older;
+				break;
+			case V4L2_FIELD_NONE:
+			case V4L2_FIELD_TOP:
+			case V4L2_FIELD_BOTTOM:
+			case V4L2_FIELD_SEQ_BT:
+			case V4L2_FIELD_SEQ_TB:
+				avg_pat = tpg_pattern_avg(tpg, pat_line_old, pat_line_next_old);
+				if (avg_pat >= 0)
+					linestart_older = tpg->downsampled_lines[avg_pat][p] +
+						mv_hor_old;
+				avg_pat = tpg_pattern_avg(tpg, pat_line_new, pat_line_next_new);
+				if (avg_pat >= 0)
+					linestart_newer = tpg->downsampled_lines[avg_pat][p] +
+						mv_hor_new;
+				break;
+			}
+		}
+		linestart_older += line_offset;
+		linestart_newer += line_offset;
+	}
+	if (tpg->field_alternate) {
+		linestart_top = linestart_bottom = linestart_older;
+	} else if (params->is_60hz) {
+		linestart_top = linestart_newer;
+		linestart_bottom = linestart_older;
+	} else {
+		linestart_top = linestart_older;
+		linestart_bottom = linestart_newer;
+	}
+
+	switch (tpg->field) {
+	case V4L2_FIELD_INTERLACED:
+	case V4L2_FIELD_INTERLACED_TB:
+	case V4L2_FIELD_SEQ_TB:
+	case V4L2_FIELD_SEQ_BT:
+		if (even)
+			memcpy(vbuf, linestart_top, img_width);
+		else
+			memcpy(vbuf, linestart_bottom, img_width);
+		break;
+	case V4L2_FIELD_INTERLACED_BT:
+		if (even)
+			memcpy(vbuf, linestart_bottom, img_width);
+		else
+			memcpy(vbuf, linestart_top, img_width);
+		break;
+	case V4L2_FIELD_TOP:
+		memcpy(vbuf, linestart_top, img_width);
+		break;
+	case V4L2_FIELD_BOTTOM:
+		memcpy(vbuf, linestart_bottom, img_width);
+		break;
+	case V4L2_FIELD_NONE:
+	default:
+		memcpy(vbuf, linestart_older, img_width);
+		break;
+	}
+}
+
+void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std,
+			   unsigned p, u8 *vbuf)
+{
+	struct tpg_draw_params params;
 	unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
-	u8 *orig_vbuf = vbuf;
 
 	/* Coarse scaling with Bresenham */
 	unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height;
 	unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height;
 	unsigned src_y = 0;
 	unsigned error = 0;
+	unsigned h;
 
 	tpg_recalc(tpg);
 
-	mv_hor_old = (mv_hor_old * tpg->scaled_width / tpg->src_width) & ~1;
-	mv_hor_new = (mv_hor_new * tpg->scaled_width / tpg->src_width) & ~1;
-	wss_width = tpg->crop.left < tpg->src_width / 2 ?
-			tpg->src_width / 2 - tpg->crop.left : 0;
-	if (wss_width > tpg->crop.width)
-		wss_width = tpg->crop.width;
-	wss_width = wss_width * tpg->scaled_width / tpg->src_width;
+	params.is_tv = std;
+	params.is_60hz = std & V4L2_STD_525_60;
+	params.twopixsize = tpg->twopixelsize[p];
+	params.img_width = tpg_hdiv(tpg, p, tpg->compose.width);
+	params.stride = tpg->bytesperline[p];
+	params.hmax = (tpg->compose.height * tpg->perc_fill) / 100;
 
-	vbuf += tpg->compose.left * twopixsize / 2;
-	line_offset = tpg->crop.left * tpg->scaled_width / tpg->src_width;
-	line_offset = (line_offset & ~1) * twopixsize / 2;
-	if (tpg->crop.left < tpg->border.left) {
-		left_pillar_width = tpg->border.left - tpg->crop.left;
-		if (left_pillar_width > tpg->crop.width)
-			left_pillar_width = tpg->crop.width;
-		left_pillar_width = (left_pillar_width * tpg->scaled_width) / tpg->src_width;
-		left_pillar_width = (left_pillar_width & ~1) * twopixsize / 2;
-	}
-	if (tpg->crop.left + tpg->crop.width > tpg->border.left + tpg->border.width) {
-		right_pillar_start = tpg->border.left + tpg->border.width - tpg->crop.left;
-		right_pillar_start = (right_pillar_start * tpg->scaled_width) / tpg->src_width;
-		right_pillar_start = (right_pillar_start & ~1) * twopixsize / 2;
-		if (right_pillar_start > img_width)
-			right_pillar_start = img_width;
-	}
+	tpg_fill_params_pattern(tpg, p, &params);
+	tpg_fill_params_extras(tpg, p, &params);
 
-	f = tpg->field == (is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+	vbuf += tpg_hdiv(tpg, p, tpg->compose.left);
 
 	for (h = 0; h < tpg->compose.height; h++) {
-		bool even;
-		bool fill_blank = false;
-		unsigned frame_line;
 		unsigned buf_line;
-		unsigned pat_line_old;
-		unsigned pat_line_new;
-		u8 *linestart_older;
-		u8 *linestart_newer;
-		u8 *linestart_top;
-		u8 *linestart_bottom;
 
-		frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
-		even = !(frame_line & 1);
+		params.frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
+		params.frame_line_next = params.frame_line;
 		buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
 		src_y += int_part;
 		error += fract_part;
@@ -1371,182 +2032,61 @@
 			src_y++;
 		}
 
-		if (h >= hmax) {
-			if (hmax == tpg->compose.height)
-				continue;
-			if (!tpg->perc_fill_blank)
-				continue;
-			fill_blank = true;
-		}
+		/*
+		 * For line-interleaved formats determine the 'plane'
+		 * based on the buffer line.
+		 */
+		if (tpg_g_interleaved(tpg))
+			p = tpg_g_interleaved_plane(tpg, buf_line);
 
-		if (tpg->vflip)
-			frame_line = tpg->src_height - frame_line - 1;
-
-		if (fill_blank) {
-			linestart_older = tpg->contrast_line[p];
-			linestart_newer = tpg->contrast_line[p];
-		} else if (tpg->qual != TPG_QUAL_NOISE &&
-			   (frame_line < tpg->border.top ||
-			    frame_line >= tpg->border.top + tpg->border.height)) {
-			linestart_older = tpg->black_line[p];
-			linestart_newer = tpg->black_line[p];
-		} else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) {
-			linestart_older = tpg->random_line[p] +
-					  twopixsize * prandom_u32_max(tpg->src_width / 2);
-			linestart_newer = tpg->random_line[p] +
-					  twopixsize * prandom_u32_max(tpg->src_width / 2);
-		} else {
-			pat_line_old = tpg_get_pat_line(tpg,
-						(frame_line + mv_vert_old) % tpg->src_height);
-			pat_line_new = tpg_get_pat_line(tpg,
-						(frame_line + mv_vert_new) % tpg->src_height);
-			linestart_older = tpg->lines[pat_line_old][p] +
-					  mv_hor_old * twopixsize / 2;
-			linestart_newer = tpg->lines[pat_line_new][p] +
-					  mv_hor_new * twopixsize / 2;
-			linestart_older += line_offset;
-			linestart_newer += line_offset;
-		}
-		if (is_60hz) {
-			linestart_top = linestart_newer;
-			linestart_bottom = linestart_older;
-		} else {
-			linestart_top = linestart_older;
-			linestart_bottom = linestart_newer;
-		}
-
-		switch (tpg->field) {
-		case V4L2_FIELD_INTERLACED:
-		case V4L2_FIELD_INTERLACED_TB:
-		case V4L2_FIELD_SEQ_TB:
-		case V4L2_FIELD_SEQ_BT:
-			if (even)
-				memcpy(vbuf + buf_line * stride, linestart_top, img_width);
-			else
-				memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
-			break;
-		case V4L2_FIELD_INTERLACED_BT:
-			if (even)
-				memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
-			else
-				memcpy(vbuf + buf_line * stride, linestart_top, img_width);
-			break;
-		case V4L2_FIELD_TOP:
-			memcpy(vbuf + buf_line * stride, linestart_top, img_width);
-			break;
-		case V4L2_FIELD_BOTTOM:
-			memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
-			break;
-		case V4L2_FIELD_NONE:
-		default:
-			memcpy(vbuf + buf_line * stride, linestart_older, img_width);
-			break;
-		}
-
-		if (is_tv && !is_60hz && frame_line == 0 && wss_width) {
+		if (tpg->vdownsampling[p] > 1) {
 			/*
-			 * Replace the first half of the top line of a 50 Hz frame
-			 * with random data to simulate a WSS signal.
+			 * When doing vertical downsampling the field setting
+			 * matters: for SEQ_BT/TB we downsample each field
+			 * separately (i.e. lines 0+2 are combined, as are
+			 * lines 1+3), for the other field settings we combine
+			 * odd and even lines. Doing that for SEQ_BT/TB would
+			 * be really weird.
 			 */
-			u8 *wss = tpg->random_line[p] +
-				  twopixsize * prandom_u32_max(tpg->src_width / 2);
+			if (tpg->field == V4L2_FIELD_SEQ_BT ||
+			    tpg->field == V4L2_FIELD_SEQ_TB) {
+				unsigned next_src_y = src_y;
 
-			memcpy(vbuf + buf_line * stride, wss, wss_width * twopixsize / 2);
+				if ((h & 3) >= 2)
+					continue;
+				next_src_y += int_part;
+				if (error + fract_part >= tpg->compose.height)
+					next_src_y++;
+				params.frame_line_next =
+					tpg_calc_frameline(tpg, next_src_y, tpg->field);
+			} else {
+				if (h & 1)
+					continue;
+				params.frame_line_next =
+					tpg_calc_frameline(tpg, src_y, tpg->field);
+			}
+
+			buf_line /= tpg->vdownsampling[p];
 		}
+		tpg_fill_plane_pattern(tpg, &params, p, h,
+				vbuf + buf_line * params.stride);
+		tpg_fill_plane_extras(tpg, &params, p, h,
+				vbuf + buf_line * params.stride);
+	}
+}
+
+void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
+{
+	unsigned offset = 0;
+	unsigned i;
+
+	if (tpg->buffers > 1) {
+		tpg_fill_plane_buffer(tpg, std, p, vbuf);
+		return;
 	}
 
-	vbuf = orig_vbuf;
-	vbuf += tpg->compose.left * twopixsize / 2;
-	src_y = 0;
-	error = 0;
-	for (h = 0; h < tpg->compose.height; h++) {
-		unsigned frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
-		unsigned buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
-		const struct v4l2_rect *sq = &tpg->square;
-		const struct v4l2_rect *b = &tpg->border;
-		const struct v4l2_rect *c = &tpg->crop;
-
-		src_y += int_part;
-		error += fract_part;
-		if (error >= tpg->compose.height) {
-			error -= tpg->compose.height;
-			src_y++;
-		}
-
-		if (tpg->show_border && frame_line >= b->top &&
-		    frame_line < b->top + b->height) {
-			unsigned bottom = b->top + b->height - 1;
-			unsigned left = left_pillar_width;
-			unsigned right = right_pillar_start;
-
-			if (frame_line == b->top || frame_line == b->top + 1 ||
-			    frame_line == bottom || frame_line == bottom - 1) {
-				memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p],
-						right - left);
-			} else {
-				if (b->left >= c->left &&
-				    b->left < c->left + c->width)
-					memcpy(vbuf + buf_line * stride + left,
-						tpg->contrast_line[p], twopixsize);
-				if (b->left + b->width > c->left &&
-				    b->left + b->width <= c->left + c->width)
-					memcpy(vbuf + buf_line * stride + right - twopixsize,
-						tpg->contrast_line[p], twopixsize);
-			}
-		}
-		if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top &&
-		    frame_line < b->top + b->height) {
-			memcpy(vbuf + buf_line * stride, tpg->black_line[p], left_pillar_width);
-			memcpy(vbuf + buf_line * stride + right_pillar_start, tpg->black_line[p],
-			       img_width - right_pillar_start);
-		}
-		if (tpg->show_square && frame_line >= sq->top &&
-		    frame_line < sq->top + sq->height &&
-		    sq->left < c->left + c->width &&
-		    sq->left + sq->width >= c->left) {
-			unsigned left = sq->left;
-			unsigned width = sq->width;
-
-			if (c->left > left) {
-				width -= c->left - left;
-				left = c->left;
-			}
-			if (c->left + c->width < left + width)
-				width -= left + width - c->left - c->width;
-			left -= c->left;
-			left = (left * tpg->scaled_width) / tpg->src_width;
-			left = (left & ~1) * twopixsize / 2;
-			width = (width * tpg->scaled_width) / tpg->src_width;
-			width = (width & ~1) * twopixsize / 2;
-			memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p], width);
-		}
-		if (tpg->insert_sav) {
-			unsigned offset = (tpg->compose.width / 6) * twopixsize;
-			u8 *p = vbuf + buf_line * stride + offset;
-			unsigned vact = 0, hact = 0;
-
-			p[0] = 0xff;
-			p[1] = 0;
-			p[2] = 0;
-			p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
-				((hact ^ vact) << 3) |
-				((hact ^ f) << 2) |
-				((f ^ vact) << 1) |
-				(hact ^ vact ^ f);
-		}
-		if (tpg->insert_eav) {
-			unsigned offset = (tpg->compose.width / 6) * 2 * twopixsize;
-			u8 *p = vbuf + buf_line * stride + offset;
-			unsigned vact = 0, hact = 1;
-
-			p[0] = 0xff;
-			p[1] = 0;
-			p[2] = 0;
-			p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
-				((hact ^ vact) << 3) |
-				((hact ^ f) << 2) |
-				((f ^ vact) << 1) |
-				(hact ^ vact ^ f);
-		}
+	for (i = 0; i < tpg_g_planes(tpg); i++) {
+		tpg_fill_plane_buffer(tpg, std, i, vbuf + offset);
+		offset += tpg_calc_plane_size(tpg, i);
 	}
 }
diff --git a/drivers/media/platform/vivid/vivid-tpg.h b/drivers/media/platform/vivid/vivid-tpg.h
index 9dc463a..a50cd2e 100644
--- a/drivers/media/platform/vivid/vivid-tpg.h
+++ b/drivers/media/platform/vivid/vivid-tpg.h
@@ -20,7 +20,6 @@
 #ifndef _VIVID_TPG_H_
 #define _VIVID_TPG_H_
 
-#include <linux/version.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/random.h>
@@ -42,7 +41,10 @@
 	TPG_PAT_GREEN,
 	TPG_PAT_BLUE,
 	TPG_PAT_CHECKERS_16X16,
+	TPG_PAT_CHECKERS_2X2,
 	TPG_PAT_CHECKERS_1X1,
+	TPG_PAT_COLOR_CHECKERS_2X2,
+	TPG_PAT_COLOR_CHECKERS_1X1,
 	TPG_PAT_ALTERNATING_HLINES,
 	TPG_PAT_ALTERNATING_VLINES,
 	TPG_PAT_CROSS_1_PIXEL,
@@ -88,7 +90,7 @@
 
 extern const char * const tpg_aspect_strings[];
 
-#define TPG_MAX_PLANES 2
+#define TPG_MAX_PLANES 3
 #define TPG_MAX_PAT_LINES 8
 
 struct tpg_data {
@@ -99,6 +101,7 @@
 	/* Scaled output frame size */
 	unsigned			scaled_width;
 	u32				field;
+	bool				field_alternate;
 	/* crop coordinates are frame-based */
 	struct v4l2_rect		crop;
 	/* compose coordinates are format-based */
@@ -135,7 +138,16 @@
 	enum tpg_pixel_aspect		pix_aspect;
 	unsigned			rgb_range;
 	unsigned			real_rgb_range;
+	unsigned			buffers;
 	unsigned			planes;
+	bool				interleaved;
+	u8				vdownsampling[TPG_MAX_PLANES];
+	u8				hdownsampling[TPG_MAX_PLANES];
+	/*
+	 * horizontal positions must be ANDed with this value to enforce
+	 * correct boundaries for packed YUYV values.
+	 */
+	unsigned			hmask[TPG_MAX_PLANES];
 	/* Used to store the colors in native format, either RGB or YUV */
 	u8				colors[TPG_COLOR_MAX][3];
 	u8				textfg[TPG_MAX_PLANES][8], textbg[TPG_MAX_PLANES][8];
@@ -169,6 +181,7 @@
 	/* Used to store TPG_MAX_PAT_LINES lines, each with up to two planes */
 	unsigned			max_line_width;
 	u8				*lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES];
+	u8				*downsampled_lines[TPG_MAX_PAT_LINES][TPG_MAX_PLANES];
 	u8				*random_line[TPG_MAX_PLANES];
 	u8				*contrast_line[TPG_MAX_PLANES];
 	u8				*black_line[TPG_MAX_PLANES];
@@ -181,11 +194,15 @@
 		       u32 field);
 
 void tpg_set_font(const u8 *f);
-void tpg_gen_text(struct tpg_data *tpg,
+void tpg_gen_text(const struct tpg_data *tpg,
 		u8 *basep[TPG_MAX_PLANES][2], int y, int x, char *text);
 void tpg_calc_text_basep(struct tpg_data *tpg,
 		u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf);
-void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf);
+unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line);
+void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std,
+			   unsigned p, u8 *vbuf);
+void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std,
+		    unsigned p, u8 *vbuf);
 bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc);
 void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
 		const struct v4l2_rect *compose);
@@ -324,9 +341,19 @@
 	return tpg->quantization;
 }
 
+static inline unsigned tpg_g_buffers(const struct tpg_data *tpg)
+{
+	return tpg->buffers;
+}
+
 static inline unsigned tpg_g_planes(const struct tpg_data *tpg)
 {
-	return tpg->planes;
+	return tpg->interleaved ? 1 : tpg->planes;
+}
+
+static inline bool tpg_g_interleaved(const struct tpg_data *tpg)
+{
+	return tpg->interleaved;
 }
 
 static inline unsigned tpg_g_twopixelsize(const struct tpg_data *tpg, unsigned plane)
@@ -334,6 +361,24 @@
 	return tpg->twopixelsize[plane];
 }
 
+static inline unsigned tpg_hdiv(const struct tpg_data *tpg,
+				  unsigned plane, unsigned x)
+{
+	return ((x / tpg->hdownsampling[plane]) & tpg->hmask[plane]) *
+		tpg->twopixelsize[plane] / 2;
+}
+
+static inline unsigned tpg_hscale(const struct tpg_data *tpg, unsigned x)
+{
+	return (x * tpg->scaled_width) / tpg->src_width;
+}
+
+static inline unsigned tpg_hscale_div(const struct tpg_data *tpg,
+				      unsigned plane, unsigned x)
+{
+	return tpg_hdiv(tpg, plane, tpg_hscale(tpg, x));
+}
+
 static inline unsigned tpg_g_bytesperline(const struct tpg_data *tpg, unsigned plane)
 {
 	return tpg->bytesperline[plane];
@@ -341,7 +386,60 @@
 
 static inline void tpg_s_bytesperline(struct tpg_data *tpg, unsigned plane, unsigned bpl)
 {
-	tpg->bytesperline[plane] = bpl;
+	unsigned p;
+
+	if (tpg->buffers > 1) {
+		tpg->bytesperline[plane] = bpl;
+		return;
+	}
+
+	for (p = 0; p < tpg_g_planes(tpg); p++) {
+		unsigned plane_w = bpl * tpg->twopixelsize[p] / tpg->twopixelsize[0];
+
+		tpg->bytesperline[p] = plane_w / tpg->hdownsampling[p];
+	}
+}
+
+
+static inline unsigned tpg_g_line_width(const struct tpg_data *tpg, unsigned plane)
+{
+	unsigned w = 0;
+	unsigned p;
+
+	if (tpg->buffers > 1)
+		return tpg_g_bytesperline(tpg, plane);
+	for (p = 0; p < tpg_g_planes(tpg); p++) {
+		unsigned plane_w = tpg_g_bytesperline(tpg, p);
+
+		w += plane_w / tpg->vdownsampling[p];
+	}
+	return w;
+}
+
+static inline unsigned tpg_calc_line_width(const struct tpg_data *tpg,
+					   unsigned plane, unsigned bpl)
+{
+	unsigned w = 0;
+	unsigned p;
+
+	if (tpg->buffers > 1)
+		return bpl;
+	for (p = 0; p < tpg_g_planes(tpg); p++) {
+		unsigned plane_w = bpl * tpg->twopixelsize[p] / tpg->twopixelsize[0];
+
+		plane_w /= tpg->hdownsampling[p];
+		w += plane_w / tpg->vdownsampling[p];
+	}
+	return w;
+}
+
+static inline unsigned tpg_calc_plane_size(const struct tpg_data *tpg, unsigned plane)
+{
+	if (plane >= tpg_g_planes(tpg))
+		return 0;
+
+	return tpg_g_bytesperline(tpg, plane) * tpg->buf_height /
+	       tpg->vdownsampling[plane];
 }
 
 static inline void tpg_s_buf_height(struct tpg_data *tpg, unsigned h)
@@ -349,9 +447,10 @@
 	tpg->buf_height = h;
 }
 
-static inline void tpg_s_field(struct tpg_data *tpg, unsigned field)
+static inline void tpg_s_field(struct tpg_data *tpg, unsigned field, bool alternate)
 {
 	tpg->field = field;
+	tpg->field_alternate = alternate;
 }
 
 static inline void tpg_s_perc_fill(struct tpg_data *tpg,
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
index 867a29a..dab5990 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -42,20 +42,26 @@
 	{
 		.name     = "RGB565 (LE)",
 		.fourcc   = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
-		.depth    = 16,
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.planes   = 1,
+		.buffers = 1,
 	},
 	{
 		.name     = "XRGB555 (LE)",
 		.fourcc   = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */
-		.depth    = 16,
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.planes   = 1,
+		.buffers = 1,
 	},
 	{
 		.name     = "ARGB555 (LE)",
 		.fourcc   = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */
-		.depth    = 16,
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.planes   = 1,
+		.buffers = 1,
 	},
 };
 
@@ -94,7 +100,7 @@
 		       unsigned sizes[], void *alloc_ctxs[])
 {
 	struct vivid_dev *dev = vb2_get_drv_priv(vq);
-	unsigned planes = tpg_g_planes(&dev->tpg);
+	unsigned buffers = tpg_g_buffers(&dev->tpg);
 	unsigned h = dev->fmt_cap_rect.height;
 	unsigned p;
 
@@ -127,39 +133,36 @@
 		mp = &fmt->fmt.pix_mp;
 		/*
 		 * Check if the number of planes in the specified format match
-		 * the number of planes in the current format. You can't mix that.
+		 * the number of buffers in the current format. You can't mix that.
 		 */
-		if (mp->num_planes != planes)
+		if (mp->num_planes != buffers)
 			return -EINVAL;
 		vfmt = vivid_get_format(dev, mp->pixelformat);
-		for (p = 0; p < planes; p++) {
+		for (p = 0; p < buffers; p++) {
 			sizes[p] = mp->plane_fmt[p].sizeimage;
-			if (sizes[0] < tpg_g_bytesperline(&dev->tpg, 0) * h +
+			if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h +
 							vfmt->data_offset[p])
 				return -EINVAL;
 		}
 	} else {
-		for (p = 0; p < planes; p++)
-			sizes[p] = tpg_g_bytesperline(&dev->tpg, p) * h +
+		for (p = 0; p < buffers; p++)
+			sizes[p] = tpg_g_line_width(&dev->tpg, p) * h +
 					dev->fmt_cap->data_offset[p];
 	}
 
 	if (vq->num_buffers + *nbuffers < 2)
 		*nbuffers = 2 - vq->num_buffers;
 
-	*nplanes = planes;
+	*nplanes = buffers;
 
 	/*
 	 * videobuf2-vmalloc allocator is context-less so no need to set
 	 * alloc_ctxs array.
 	 */
 
-	if (planes == 2)
-		dprintk(dev, 1, "%s, count=%d, sizes=%u, %u\n", __func__,
-			*nbuffers, sizes[0], sizes[1]);
-	else
-		dprintk(dev, 1, "%s, count=%d, size=%u\n", __func__,
-			*nbuffers, sizes[0]);
+	dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers);
+	for (p = 0; p < buffers; p++)
+		dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]);
 
 	return 0;
 }
@@ -168,7 +171,7 @@
 {
 	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
 	unsigned long size;
-	unsigned planes = tpg_g_planes(&dev->tpg);
+	unsigned buffers = tpg_g_buffers(&dev->tpg);
 	unsigned p;
 
 	dprintk(dev, 1, "%s\n", __func__);
@@ -184,13 +187,13 @@
 		dev->buf_prepare_error = false;
 		return -EINVAL;
 	}
-	for (p = 0; p < planes; p++) {
-		size = tpg_g_bytesperline(&dev->tpg, p) * dev->fmt_cap_rect.height +
+	for (p = 0; p < buffers; p++) {
+		size = tpg_g_line_width(&dev->tpg, p) * dev->fmt_cap_rect.height +
 			dev->fmt_cap->data_offset[p];
 
-		if (vb2_plane_size(vb, 0) < size) {
+		if (vb2_plane_size(vb, p) < size) {
 			dprintk(dev, 1, "%s data will not fit into plane %u (%lu < %lu)\n",
-					__func__, p, vb2_plane_size(vb, 0), size);
+					__func__, p, vb2_plane_size(vb, p), size);
 			return -EINVAL;
 		}
 
@@ -441,7 +444,7 @@
 		 */
 		if (keep_controls || !dev->colorspace)
 			break;
-		if (bt->standards & V4L2_DV_BT_STD_CEA861) {
+		if (bt->flags & V4L2_DV_FL_IS_CE_VIDEO) {
 			if (bt->width == 720 && bt->height <= 576)
 				v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M);
 			else
@@ -526,11 +529,11 @@
 	mp->colorspace   = vivid_colorspace_cap(dev);
 	mp->ycbcr_enc    = vivid_ycbcr_enc_cap(dev);
 	mp->quantization = vivid_quantization_cap(dev);
-	mp->num_planes = dev->fmt_cap->planes;
+	mp->num_planes = dev->fmt_cap->buffers;
 	for (p = 0; p < mp->num_planes; p++) {
 		mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p);
 		mp->plane_fmt[p].sizeimage =
-			mp->plane_fmt[p].bytesperline * mp->height +
+			tpg_g_line_width(&dev->tpg, p) * mp->height +
 			dev->fmt_cap->data_offset[p];
 	}
 	return 0;
@@ -596,18 +599,19 @@
 
 	/* This driver supports custom bytesperline values */
 
-	/* Calculate the minimum supported bytesperline value */
-	bytesperline = (mp->width * fmt->depth) >> 3;
-	/* Calculate the maximum supported bytesperline value */
-	max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->depth) >> 3;
-	mp->num_planes = fmt->planes;
+	mp->num_planes = fmt->buffers;
 	for (p = 0; p < mp->num_planes; p++) {
+		/* Calculate the minimum supported bytesperline value */
+		bytesperline = (mp->width * fmt->bit_depth[p]) >> 3;
+		/* Calculate the maximum supported bytesperline value */
+		max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[p]) >> 3;
+
 		if (pfmt[p].bytesperline > max_bpl)
 			pfmt[p].bytesperline = max_bpl;
 		if (pfmt[p].bytesperline < bytesperline)
 			pfmt[p].bytesperline = bytesperline;
-		pfmt[p].sizeimage = pfmt[p].bytesperline * mp->height +
-			fmt->data_offset[p];
+		pfmt[p].sizeimage = tpg_calc_line_width(&dev->tpg, p, pfmt[p].bytesperline) *
+			mp->height + fmt->data_offset[p];
 		memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
 	}
 	mp->colorspace = vivid_colorspace_cap(dev);
@@ -627,6 +631,7 @@
 	struct vb2_queue *q = &dev->vb_vid_cap_q;
 	int ret = vivid_try_fmt_vid_cap(file, priv, f);
 	unsigned factor = 1;
+	unsigned p;
 	unsigned i;
 
 	if (ret < 0)
@@ -729,13 +734,15 @@
 	dev->fmt_cap_rect.width = mp->width;
 	dev->fmt_cap_rect.height = mp->height;
 	tpg_s_buf_height(&dev->tpg, mp->height);
-	tpg_s_bytesperline(&dev->tpg, 0, mp->plane_fmt[0].bytesperline);
-	if (tpg_g_planes(&dev->tpg) > 1)
-		tpg_s_bytesperline(&dev->tpg, 1, mp->plane_fmt[1].bytesperline);
-	dev->field_cap = mp->field;
-	tpg_s_field(&dev->tpg, dev->field_cap);
-	tpg_s_crop_compose(&dev->tpg, &dev->crop_cap, &dev->compose_cap);
 	tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc);
+	for (p = 0; p < tpg_g_buffers(&dev->tpg); p++)
+		tpg_s_bytesperline(&dev->tpg, p, mp->plane_fmt[p].bytesperline);
+	dev->field_cap = mp->field;
+	if (dev->field_cap == V4L2_FIELD_ALTERNATE)
+		tpg_s_field(&dev->tpg, V4L2_FIELD_TOP, true);
+	else
+		tpg_s_field(&dev->tpg, dev->field_cap, false);
+	tpg_s_crop_compose(&dev->tpg, &dev->crop_cap, &dev->compose_cap);
 	if (vivid_is_sdtv_cap(dev))
 		dev->tv_field_cap = mp->field;
 	tpg_update_mv_step(&dev->tpg);
@@ -1012,8 +1019,12 @@
 int vidioc_enum_fmt_vid_overlay(struct file *file, void  *priv,
 					struct v4l2_fmtdesc *f)
 {
+	struct vivid_dev *dev = video_drvdata(file);
 	const struct vivid_fmt *fmt;
 
+	if (dev->multiplanar)
+		return -ENOTTY;
+
 	if (f->index >= ARRAY_SIZE(formats_ovl))
 		return -EINVAL;
 
@@ -1032,6 +1043,9 @@
 	struct v4l2_window *win = &f->fmt.win;
 	unsigned clipcount = win->clipcount;
 
+	if (dev->multiplanar)
+		return -ENOTTY;
+
 	win->w.top = dev->overlay_cap_top;
 	win->w.left = dev->overlay_cap_left;
 	win->w.width = compose->width;
@@ -1063,6 +1077,9 @@
 	struct v4l2_window *win = &f->fmt.win;
 	int i, j;
 
+	if (dev->multiplanar)
+		return -ENOTTY;
+
 	win->w.left = clamp_t(int, win->w.left,
 			      -dev->fb_cap.fmt.width, dev->fb_cap.fmt.width);
 	win->w.top = clamp_t(int, win->w.top,
@@ -1150,6 +1167,9 @@
 {
 	struct vivid_dev *dev = video_drvdata(file);
 
+	if (dev->multiplanar)
+		return -ENOTTY;
+
 	if (i && dev->fb_vbase_cap == NULL)
 		return -EINVAL;
 
@@ -1169,6 +1189,9 @@
 {
 	struct vivid_dev *dev = video_drvdata(file);
 
+	if (dev->multiplanar)
+		return -ENOTTY;
+
 	*a = dev->fb_cap;
 	a->capability = V4L2_FBUF_CAP_BITMAP_CLIPPING |
 			V4L2_FBUF_CAP_LIST_CLIPPING;
@@ -1185,6 +1208,9 @@
 	struct vivid_dev *dev = video_drvdata(file);
 	const struct vivid_fmt *fmt;
 
+	if (dev->multiplanar)
+		return -ENOTTY;
+
 	if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
 		return -EPERM;
 
@@ -1202,7 +1228,7 @@
 	fmt = vivid_get_format(dev, a->fmt.pixelformat);
 	if (!fmt || !fmt->can_do_overlay)
 		return -EINVAL;
-	if (a->fmt.bytesperline < (a->fmt.width * fmt->depth) / 8)
+	if (a->fmt.bytesperline < (a->fmt.width * fmt->bit_depth[0]) / 8)
 		return -EINVAL;
 	if (a->fmt.height * a->fmt.bytesperline < a->fmt.sizeimage)
 		return -EINVAL;
@@ -1332,7 +1358,7 @@
 			v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M);
 			break;
 		case HDMI:
-			if (bt->standards & V4L2_DV_BT_STD_CEA861) {
+			if (bt->flags & V4L2_DV_FL_IS_CE_VIDEO) {
 				if (dev->src_rect.width == 720 && dev->src_rect.height <= 576)
 					v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M);
 				else
@@ -1552,6 +1578,65 @@
 	return 0;
 }
 
+static void find_aspect_ratio(u32 width, u32 height,
+			       u32 *num, u32 *denom)
+{
+	if (!(height % 3) && ((height * 4 / 3) == width)) {
+		*num = 4;
+		*denom = 3;
+	} else if (!(height % 9) && ((height * 16 / 9) == width)) {
+		*num = 16;
+		*denom = 9;
+	} else if (!(height % 10) && ((height * 16 / 10) == width)) {
+		*num = 16;
+		*denom = 10;
+	} else if (!(height % 4) && ((height * 5 / 4) == width)) {
+		*num = 5;
+		*denom = 4;
+	} else if (!(height % 9) && ((height * 15 / 9) == width)) {
+		*num = 15;
+		*denom = 9;
+	} else { /* default to 16:9 */
+		*num = 16;
+		*denom = 9;
+	}
+}
+
+static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings)
+{
+	struct v4l2_bt_timings *bt = &timings->bt;
+	u32 total_h_pixel;
+	u32 total_v_lines;
+	u32 h_freq;
+
+	if (!v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap,
+				NULL, NULL))
+		return false;
+
+	total_h_pixel = V4L2_DV_BT_FRAME_WIDTH(bt);
+	total_v_lines = V4L2_DV_BT_FRAME_HEIGHT(bt);
+
+	h_freq = (u32)bt->pixelclock / total_h_pixel;
+
+	if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_CVT)) {
+		if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync,
+				    bt->polarities, timings))
+			return true;
+	}
+
+	if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_GTF)) {
+		struct v4l2_fract aspect_ratio;
+
+		find_aspect_ratio(bt->width, bt->height,
+				  &aspect_ratio.numerator,
+				  &aspect_ratio.denominator);
+		if (v4l2_detect_gtf(total_v_lines, h_freq, bt->vsync,
+				    bt->polarities, aspect_ratio, timings))
+			return true;
+	}
+	return false;
+}
+
 int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh,
 				    struct v4l2_dv_timings *timings)
 {
@@ -1559,13 +1644,16 @@
 
 	if (!vivid_is_hdmi_cap(dev))
 		return -ENODATA;
-	if (vb2_is_busy(&dev->vb_vid_cap_q))
-		return -EBUSY;
 	if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap,
-				0, NULL, NULL))
+				      0, NULL, NULL) &&
+	    !valid_cvt_gtf_timings(timings))
 		return -EINVAL;
+
 	if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap, 0))
 		return 0;
+	if (vb2_is_busy(&dev->vb_vid_cap_q))
+		return -EBUSY;
+
 	dev->dv_timings_cap = *timings;
 	vivid_update_format_cap(dev, false);
 	return 0;
@@ -1663,18 +1751,14 @@
 		return -EINVAL;
 
 	if (!vivid_is_webcam(dev)) {
-		static const struct v4l2_fract step = { 1, 1 };
-
 		if (fival->index)
 			return -EINVAL;
 		if (fival->width < MIN_WIDTH || fival->width > MAX_WIDTH * MAX_ZOOM)
 			return -EINVAL;
 		if (fival->height < MIN_HEIGHT || fival->height > MAX_HEIGHT * MAX_ZOOM)
 			return -EINVAL;
-		fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
-		fival->stepwise.min = tpf_min;
-		fival->stepwise.max = tpf_max;
-		fival->stepwise.step = step;
+		fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+		fival->discrete = dev->timeperframe_vid_cap;
 		return 0;
 	}
 
diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c
index 6bef1e6..aa44627 100644
--- a/drivers/media/platform/vivid/vivid-vid-common.c
+++ b/drivers/media/platform/vivid/vivid-vid-common.c
@@ -33,8 +33,9 @@
 	.type = V4L2_DV_BT_656_1120,
 	/* keep this initialization for compatibility with GCC < 4.4.6 */
 	.reserved = { 0 },
-	V4L2_INIT_BT_TIMINGS(0, MAX_WIDTH, 0, MAX_HEIGHT, 25000000, 600000000,
-		V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT,
+	V4L2_INIT_BT_TIMINGS(0, MAX_WIDTH, 0, MAX_HEIGHT, 14000000, 775000000,
+		V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+		V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF,
 		V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_INTERLACED)
 };
 
@@ -46,145 +47,435 @@
 	{
 		.name     = "4:2:2, packed, YUYV",
 		.fourcc   = V4L2_PIX_FMT_YUYV,
-		.depth    = 16,
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.is_yuv   = true,
 		.planes   = 1,
-		.data_offset = { PLANE0_DATA_OFFSET, 0 },
+		.buffers = 1,
+		.data_offset = { PLANE0_DATA_OFFSET },
 	},
 	{
 		.name     = "4:2:2, packed, UYVY",
 		.fourcc   = V4L2_PIX_FMT_UYVY,
-		.depth    = 16,
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.is_yuv   = true,
 		.planes   = 1,
+		.buffers = 1,
 	},
 	{
 		.name     = "4:2:2, packed, YVYU",
 		.fourcc   = V4L2_PIX_FMT_YVYU,
-		.depth    = 16,
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.is_yuv   = true,
 		.planes   = 1,
+		.buffers = 1,
 	},
 	{
 		.name     = "4:2:2, packed, VYUY",
 		.fourcc   = V4L2_PIX_FMT_VYUY,
-		.depth    = 16,
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.is_yuv   = true,
 		.planes   = 1,
+		.buffers = 1,
+	},
+	{
+		.name     = "YUV 4:2:2 triplanar",
+		.fourcc   = V4L2_PIX_FMT_YUV422P,
+		.vdownsampling = { 1, 1, 1 },
+		.bit_depth = { 8, 4, 4 },
+		.is_yuv   = true,
+		.planes   = 3,
+		.buffers = 1,
+	},
+	{
+		.name     = "YUV 4:2:0 triplanar",
+		.fourcc   = V4L2_PIX_FMT_YUV420,
+		.vdownsampling = { 1, 2, 2 },
+		.bit_depth = { 8, 4, 4 },
+		.is_yuv   = true,
+		.planes   = 3,
+		.buffers = 1,
+	},
+	{
+		.name     = "YVU 4:2:0 triplanar",
+		.fourcc   = V4L2_PIX_FMT_YVU420,
+		.vdownsampling = { 1, 2, 2 },
+		.bit_depth = { 8, 4, 4 },
+		.is_yuv   = true,
+		.planes   = 3,
+		.buffers = 1,
+	},
+	{
+		.name     = "YUV 4:2:0 biplanar",
+		.fourcc   = V4L2_PIX_FMT_NV12,
+		.vdownsampling = { 1, 2 },
+		.bit_depth = { 8, 8 },
+		.is_yuv   = true,
+		.planes   = 2,
+		.buffers = 1,
+	},
+	{
+		.name     = "YVU 4:2:0 biplanar",
+		.fourcc   = V4L2_PIX_FMT_NV21,
+		.vdownsampling = { 1, 2 },
+		.bit_depth = { 8, 8 },
+		.is_yuv   = true,
+		.planes   = 2,
+		.buffers = 1,
+	},
+	{
+		.name     = "YUV 4:2:2 biplanar",
+		.fourcc   = V4L2_PIX_FMT_NV16,
+		.vdownsampling = { 1, 1 },
+		.bit_depth = { 8, 8 },
+		.is_yuv   = true,
+		.planes   = 2,
+		.buffers = 1,
+	},
+	{
+		.name     = "YVU 4:2:2 biplanar",
+		.fourcc   = V4L2_PIX_FMT_NV61,
+		.vdownsampling = { 1, 1 },
+		.bit_depth = { 8, 8 },
+		.is_yuv   = true,
+		.planes   = 2,
+		.buffers = 1,
+	},
+	{
+		.name     = "YUV 4:4:4 biplanar",
+		.fourcc   = V4L2_PIX_FMT_NV24,
+		.vdownsampling = { 1, 1 },
+		.bit_depth = { 8, 16 },
+		.is_yuv   = true,
+		.planes   = 2,
+		.buffers = 1,
+	},
+	{
+		.name     = "YVU 4:4:4 biplanar",
+		.fourcc   = V4L2_PIX_FMT_NV42,
+		.vdownsampling = { 1, 1 },
+		.bit_depth = { 8, 16 },
+		.is_yuv   = true,
+		.planes   = 2,
+		.buffers = 1,
+	},
+	{
+		.name     = "YUV555 (LE)",
+		.fourcc   = V4L2_PIX_FMT_YUV555, /* uuuvvvvv ayyyyyuu */
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
+		.planes   = 1,
+		.buffers = 1,
+		.alpha_mask = 0x8000,
+	},
+	{
+		.name     = "YUV565 (LE)",
+		.fourcc   = V4L2_PIX_FMT_YUV565, /* uuuvvvvv yyyyyuuu */
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
+		.planes   = 1,
+		.buffers = 1,
+	},
+	{
+		.name     = "YUV444",
+		.fourcc   = V4L2_PIX_FMT_YUV444, /* uuuuvvvv aaaayyyy */
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
+		.planes   = 1,
+		.buffers = 1,
+		.alpha_mask = 0xf000,
+	},
+	{
+		.name     = "YUV32 (LE)",
+		.fourcc   = V4L2_PIX_FMT_YUV32, /* ayuv */
+		.vdownsampling = { 1 },
+		.bit_depth = { 32 },
+		.planes   = 1,
+		.buffers = 1,
+		.alpha_mask = 0x000000ff,
+	},
+	{
+		.name     = "Monochrome",
+		.fourcc   = V4L2_PIX_FMT_GREY,
+		.vdownsampling = { 1 },
+		.bit_depth = { 8 },
+		.is_yuv   = true,
+		.planes   = 1,
+		.buffers = 1,
+	},
+	{
+		.name     = "RGB332",
+		.fourcc   = V4L2_PIX_FMT_RGB332, /* rrrgggbb */
+		.vdownsampling = { 1 },
+		.bit_depth = { 8 },
+		.planes   = 1,
+		.buffers = 1,
 	},
 	{
 		.name     = "RGB565 (LE)",
 		.fourcc   = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
-		.depth    = 16,
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.planes   = 1,
+		.buffers = 1,
 		.can_do_overlay = true,
 	},
 	{
 		.name     = "RGB565 (BE)",
 		.fourcc   = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
-		.depth    = 16,
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.planes   = 1,
+		.buffers = 1,
 		.can_do_overlay = true,
 	},
 	{
-		.name     = "RGB555 (LE)",
-		.fourcc   = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
-		.depth    = 16,
+		.name     = "RGB444",
+		.fourcc   = V4L2_PIX_FMT_RGB444, /* xxxxrrrr ggggbbbb */
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.planes   = 1,
+		.buffers = 1,
+	},
+	{
+		.name     = "XRGB444",
+		.fourcc   = V4L2_PIX_FMT_XRGB444, /* xxxxrrrr ggggbbbb */
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
+		.planes   = 1,
+		.buffers = 1,
+	},
+	{
+		.name     = "ARGB444",
+		.fourcc   = V4L2_PIX_FMT_ARGB444, /* aaaarrrr ggggbbbb */
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
+		.planes   = 1,
+		.buffers = 1,
+		.alpha_mask = 0x00f0,
+	},
+	{
+		.name     = "RGB555 (LE)",
+		.fourcc   = V4L2_PIX_FMT_RGB555, /* gggbbbbb xrrrrrgg */
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
+		.planes   = 1,
+		.buffers = 1,
 		.can_do_overlay = true,
 	},
 	{
 		.name     = "XRGB555 (LE)",
-		.fourcc   = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */
-		.depth    = 16,
+		.fourcc   = V4L2_PIX_FMT_XRGB555, /* gggbbbbb xrrrrrgg */
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.planes   = 1,
+		.buffers = 1,
 		.can_do_overlay = true,
 	},
 	{
 		.name     = "ARGB555 (LE)",
 		.fourcc   = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */
-		.depth    = 16,
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.planes   = 1,
+		.buffers = 1,
 		.can_do_overlay = true,
 		.alpha_mask = 0x8000,
 	},
 	{
 		.name     = "RGB555 (BE)",
-		.fourcc   = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
-		.depth    = 16,
+		.fourcc   = V4L2_PIX_FMT_RGB555X, /* xrrrrrgg gggbbbbb */
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
 		.planes   = 1,
-		.can_do_overlay = true,
+		.buffers = 1,
+	},
+	{
+		.name     = "XRGB555 (BE)",
+		.fourcc   = V4L2_PIX_FMT_XRGB555X, /* xrrrrrgg gggbbbbb */
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
+		.planes   = 1,
+		.buffers = 1,
+	},
+	{
+		.name     = "ARGB555 (BE)",
+		.fourcc   = V4L2_PIX_FMT_ARGB555X, /* arrrrrgg gggbbbbb */
+		.vdownsampling = { 1 },
+		.bit_depth = { 16 },
+		.planes   = 1,
+		.buffers = 1,
+		.alpha_mask = 0x0080,
 	},
 	{
 		.name     = "RGB24 (LE)",
 		.fourcc   = V4L2_PIX_FMT_RGB24, /* rgb */
-		.depth    = 24,
+		.vdownsampling = { 1 },
+		.bit_depth = { 24 },
 		.planes   = 1,
+		.buffers = 1,
 	},
 	{
 		.name     = "RGB24 (BE)",
 		.fourcc   = V4L2_PIX_FMT_BGR24, /* bgr */
-		.depth    = 24,
+		.vdownsampling = { 1 },
+		.bit_depth = { 24 },
 		.planes   = 1,
+		.buffers = 1,
+	},
+	{
+		.name     = "BGR666",
+		.fourcc   = V4L2_PIX_FMT_BGR666, /* bbbbbbgg ggggrrrr rrxxxxxx */
+		.vdownsampling = { 1 },
+		.bit_depth = { 32 },
+		.planes   = 1,
+		.buffers = 1,
 	},
 	{
 		.name     = "RGB32 (LE)",
-		.fourcc   = V4L2_PIX_FMT_RGB32, /* argb */
-		.depth    = 32,
+		.fourcc   = V4L2_PIX_FMT_RGB32, /* xrgb */
+		.vdownsampling = { 1 },
+		.bit_depth = { 32 },
 		.planes   = 1,
+		.buffers = 1,
 	},
 	{
 		.name     = "RGB32 (BE)",
-		.fourcc   = V4L2_PIX_FMT_BGR32, /* bgra */
-		.depth    = 32,
+		.fourcc   = V4L2_PIX_FMT_BGR32, /* bgrx */
+		.vdownsampling = { 1 },
+		.bit_depth = { 32 },
 		.planes   = 1,
+		.buffers = 1,
 	},
 	{
 		.name     = "XRGB32 (LE)",
-		.fourcc   = V4L2_PIX_FMT_XRGB32, /* argb */
-		.depth    = 32,
+		.fourcc   = V4L2_PIX_FMT_XRGB32, /* xrgb */
+		.vdownsampling = { 1 },
+		.bit_depth = { 32 },
 		.planes   = 1,
+		.buffers = 1,
 	},
 	{
 		.name     = "XRGB32 (BE)",
-		.fourcc   = V4L2_PIX_FMT_XBGR32, /* bgra */
-		.depth    = 32,
+		.fourcc   = V4L2_PIX_FMT_XBGR32, /* bgrx */
+		.vdownsampling = { 1 },
+		.bit_depth = { 32 },
 		.planes   = 1,
+		.buffers = 1,
 	},
 	{
 		.name     = "ARGB32 (LE)",
 		.fourcc   = V4L2_PIX_FMT_ARGB32, /* argb */
-		.depth    = 32,
+		.vdownsampling = { 1 },
+		.bit_depth = { 32 },
 		.planes   = 1,
+		.buffers = 1,
 		.alpha_mask = 0x000000ff,
 	},
 	{
 		.name     = "ARGB32 (BE)",
 		.fourcc   = V4L2_PIX_FMT_ABGR32, /* bgra */
-		.depth    = 32,
+		.vdownsampling = { 1 },
+		.bit_depth = { 32 },
 		.planes   = 1,
+		.buffers = 1,
 		.alpha_mask = 0xff000000,
 	},
 	{
-		.name     = "4:2:2, planar, YUV",
+		.name     = "Bayer BG/GR",
+		.fourcc   = V4L2_PIX_FMT_SBGGR8, /* Bayer BG/GR */
+		.vdownsampling = { 1 },
+		.bit_depth = { 8 },
+		.planes   = 1,
+		.buffers = 1,
+	},
+	{
+		.name     = "Bayer GB/RG",
+		.fourcc   = V4L2_PIX_FMT_SGBRG8, /* Bayer GB/RG */
+		.vdownsampling = { 1 },
+		.bit_depth = { 8 },
+		.planes   = 1,
+		.buffers = 1,
+	},
+	{
+		.name     = "Bayer GR/BG",
+		.fourcc   = V4L2_PIX_FMT_SGRBG8, /* Bayer GR/BG */
+		.vdownsampling = { 1 },
+		.bit_depth = { 8 },
+		.planes   = 1,
+		.buffers = 1,
+	},
+	{
+		.name     = "Bayer RG/GB",
+		.fourcc   = V4L2_PIX_FMT_SRGGB8, /* Bayer RG/GB */
+		.vdownsampling = { 1 },
+		.bit_depth = { 8 },
+		.planes   = 1,
+		.buffers = 1,
+	},
+	{
+		.name     = "4:2:2, biplanar, YUV",
 		.fourcc   = V4L2_PIX_FMT_NV16M,
-		.depth    = 8,
+		.vdownsampling = { 1, 1 },
+		.bit_depth = { 8, 8 },
 		.is_yuv   = true,
 		.planes   = 2,
+		.buffers = 2,
 		.data_offset = { PLANE0_DATA_OFFSET, 0 },
 	},
 	{
-		.name     = "4:2:2, planar, YVU",
+		.name     = "4:2:2, biplanar, YVU",
 		.fourcc   = V4L2_PIX_FMT_NV61M,
-		.depth    = 8,
+		.vdownsampling = { 1, 1 },
+		.bit_depth = { 8, 8 },
 		.is_yuv   = true,
 		.planes   = 2,
+		.buffers = 2,
 		.data_offset = { 0, PLANE0_DATA_OFFSET },
 	},
+	{
+		.name     = "4:2:0, triplanar, YUV",
+		.fourcc   = V4L2_PIX_FMT_YUV420M,
+		.vdownsampling = { 1, 2, 2 },
+		.bit_depth = { 8, 4, 4 },
+		.is_yuv   = true,
+		.planes   = 3,
+		.buffers = 3,
+	},
+	{
+		.name     = "4:2:0, triplanar, YVU",
+		.fourcc   = V4L2_PIX_FMT_YVU420M,
+		.vdownsampling = { 1, 2, 2 },
+		.bit_depth = { 8, 4, 4 },
+		.is_yuv   = true,
+		.planes   = 3,
+		.buffers = 3,
+	},
+	{
+		.name     = "4:2:0, biplanar, YUV",
+		.fourcc   = V4L2_PIX_FMT_NV12M,
+		.vdownsampling = { 1, 2 },
+		.bit_depth = { 8, 8 },
+		.is_yuv   = true,
+		.planes   = 2,
+		.buffers = 2,
+	},
+	{
+		.name     = "4:2:0, biplanar, YVU",
+		.fourcc   = V4L2_PIX_FMT_NV21M,
+		.vdownsampling = { 1, 2 },
+		.bit_depth = { 8, 8 },
+		.is_yuv   = true,
+		.planes   = 2,
+		.buffers = 2,
+	},
 };
 
-/* There are 2 multiplanar formats in the list */
-#define VIVID_MPLANAR_FORMATS 2
+/* There are 6 multiplanar formats in the list */
+#define VIVID_MPLANAR_FORMATS 6
 
 const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat)
 {
@@ -194,7 +485,7 @@
 	for (k = 0; k < ARRAY_SIZE(vivid_formats); k++) {
 		fmt = &vivid_formats[k];
 		if (fmt->fourcc == pixelformat)
-			if (fmt->planes == 1 || dev->multiplanar)
+			if (fmt->buffers == 1 || dev->multiplanar)
 				return fmt;
 	}
 
@@ -210,6 +501,13 @@
 		return false;
 	if (dev->field_cap != dev->field_out)
 		return false;
+	/*
+	 * While this can be supported, it is just too much work
+	 * to actually implement.
+	 */
+	if (dev->field_cap == V4L2_FIELD_SEQ_TB ||
+	    dev->field_cap == V4L2_FIELD_SEQ_BT)
+		return false;
 	if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) {
 		if (!(dev->std_cap & V4L2_STD_525_60) !=
 		    !(dev->std_out & V4L2_STD_525_60))
@@ -397,6 +695,9 @@
 	unsigned w = r->width;
 	unsigned h = r->height;
 
+	/* sanitize w and h in case someone passes ~0 as the value */
+	w &= 0xffff;
+	h &= 0xffff;
 	if (!(flags & V4L2_SEL_FLAG_LE)) {
 		w++;
 		h++;
@@ -421,8 +722,9 @@
 		r->top = 0;
 	if (r->left < 0)
 		r->left = 0;
-	r->left &= ~1;
-	r->top &= ~1;
+	/* sanitize left and top in case someone passes ~0 as the value */
+	r->left &= 0xfffe;
+	r->top &= 0xfffe;
 	if (r->left + w > MAX_WIDTH)
 		r->left = MAX_WIDTH - w;
 	if (r->top + h > MAX_HEIGHT)
diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
index 39ff79f..0af43dc 100644
--- a/drivers/media/platform/vivid/vivid-vid-out.c
+++ b/drivers/media/platform/vivid/vivid-vid-out.c
@@ -36,9 +36,14 @@
 		       unsigned sizes[], void *alloc_ctxs[])
 {
 	struct vivid_dev *dev = vb2_get_drv_priv(vq);
-	unsigned planes = dev->fmt_out->planes;
+	const struct vivid_fmt *vfmt = dev->fmt_out;
+	unsigned planes = vfmt->buffers;
 	unsigned h = dev->fmt_out_rect.height;
 	unsigned size = dev->bytesperline_out[0] * h;
+	unsigned p;
+
+	for (p = vfmt->buffers; p < vfmt->planes; p++)
+		size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p];
 
 	if (dev->field_out == V4L2_FIELD_ALTERNATE) {
 		/*
@@ -74,21 +79,16 @@
 		if (mp->num_planes != planes)
 			return -EINVAL;
 		sizes[0] = mp->plane_fmt[0].sizeimage;
-		if (planes == 2) {
-			sizes[1] = mp->plane_fmt[1].sizeimage;
-			if (sizes[0] < dev->bytesperline_out[0] * h ||
-			    sizes[1] < dev->bytesperline_out[1] * h)
-				return -EINVAL;
-		} else if (sizes[0] < size) {
+		if (sizes[0] < size)
 			return -EINVAL;
+		for (p = 1; p < planes; p++) {
+			sizes[p] = mp->plane_fmt[p].sizeimage;
+			if (sizes[p] < dev->bytesperline_out[p] * h)
+				return -EINVAL;
 		}
 	} else {
-		if (planes == 2) {
-			sizes[0] = dev->bytesperline_out[0] * h;
-			sizes[1] = dev->bytesperline_out[1] * h;
-		} else {
-			sizes[0] = size;
-		}
+		for (p = 0; p < planes; p++)
+			sizes[p] = p ? dev->bytesperline_out[p] * h : size;
 	}
 
 	if (vq->num_buffers + *nbuffers < 2)
@@ -101,12 +101,9 @@
 	 * alloc_ctxs array.
 	 */
 
-	if (planes == 2)
-		dprintk(dev, 1, "%s, count=%d, sizes=%u, %u\n", __func__,
-			*nbuffers, sizes[0], sizes[1]);
-	else
-		dprintk(dev, 1, "%s, count=%d, size=%u\n", __func__,
-			*nbuffers, sizes[0]);
+	dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers);
+	for (p = 0; p < planes; p++)
+		dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]);
 	return 0;
 }
 
@@ -114,7 +111,7 @@
 {
 	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
 	unsigned long size;
-	unsigned planes = dev->fmt_out->planes;
+	unsigned planes;
 	unsigned p;
 
 	dprintk(dev, 1, "%s\n", __func__);
@@ -122,6 +119,8 @@
 	if (WARN_ON(NULL == dev->fmt_out))
 		return -EINVAL;
 
+	planes = dev->fmt_out->planes;
+
 	if (dev->buf_prepare_error) {
 		/*
 		 * Error injection: test what happens if buf_prepare() returns
@@ -220,7 +219,7 @@
 void vivid_update_format_out(struct vivid_dev *dev)
 {
 	struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;
-	unsigned size;
+	unsigned size, p;
 
 	switch (dev->output_type[dev->output]) {
 	case SVID:
@@ -249,7 +248,7 @@
 			dev->field_out = V4L2_FIELD_ALTERNATE;
 		else
 			dev->field_out = V4L2_FIELD_NONE;
-		if (!dev->dvi_d_out && (bt->standards & V4L2_DV_BT_STD_CEA861)) {
+		if (!dev->dvi_d_out && (bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) {
 			if (bt->width == 720 && bt->height <= 576)
 				dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M;
 			else
@@ -267,9 +266,9 @@
 	if (V4L2_FIELD_HAS_T_OR_B(dev->field_out))
 		dev->crop_out.height /= 2;
 	dev->fmt_out_rect = dev->crop_out;
-	dev->bytesperline_out[0] = (dev->sink_rect.width * dev->fmt_out->depth) / 8;
-	if (dev->fmt_out->planes == 2)
-		dev->bytesperline_out[1] = (dev->sink_rect.width * dev->fmt_out->depth) / 8;
+	for (p = 0; p < dev->fmt_out->planes; p++)
+		dev->bytesperline_out[p] =
+			(dev->sink_rect.width * dev->fmt_out->bit_depth[p]) / 8;
 }
 
 /* Map the field to something that is valid for the current output */
@@ -313,21 +312,28 @@
 {
 	struct vivid_dev *dev = video_drvdata(file);
 	struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
+	const struct vivid_fmt *fmt = dev->fmt_out;
 	unsigned p;
 
 	mp->width        = dev->fmt_out_rect.width;
 	mp->height       = dev->fmt_out_rect.height;
 	mp->field        = dev->field_out;
-	mp->pixelformat  = dev->fmt_out->fourcc;
+	mp->pixelformat  = fmt->fourcc;
 	mp->colorspace   = dev->colorspace_out;
 	mp->ycbcr_enc    = dev->ycbcr_enc_out;
 	mp->quantization = dev->quantization_out;
-	mp->num_planes = dev->fmt_out->planes;
+	mp->num_planes = fmt->buffers;
 	for (p = 0; p < mp->num_planes; p++) {
 		mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p];
 		mp->plane_fmt[p].sizeimage =
 			mp->plane_fmt[p].bytesperline * mp->height;
 	}
+	for (p = fmt->buffers; p < fmt->planes; p++) {
+		unsigned stride = dev->bytesperline_out[p];
+
+		mp->plane_fmt[0].sizeimage +=
+			(stride * mp->height) / fmt->vdownsampling[p];
+	}
 	return 0;
 }
 
@@ -386,10 +392,10 @@
 	/* This driver supports custom bytesperline values */
 
 	/* Calculate the minimum supported bytesperline value */
-	bytesperline = (mp->width * fmt->depth) >> 3;
+	bytesperline = (mp->width * fmt->bit_depth[0]) >> 3;
 	/* Calculate the maximum supported bytesperline value */
-	max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->depth) >> 3;
-	mp->num_planes = fmt->planes;
+	max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[0]) >> 3;
+	mp->num_planes = fmt->buffers;
 	for (p = 0; p < mp->num_planes; p++) {
 		if (pfmt[p].bytesperline > max_bpl)
 			pfmt[p].bytesperline = max_bpl;
@@ -398,11 +404,14 @@
 		pfmt[p].sizeimage = pfmt[p].bytesperline * mp->height;
 		memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
 	}
+	for (p = fmt->buffers; p < fmt->planes; p++)
+		pfmt[0].sizeimage += (pfmt[0].bytesperline * fmt->bit_depth[p]) /
+				     (fmt->bit_depth[0] * fmt->vdownsampling[p]);
 	mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
 	mp->quantization = V4L2_QUANTIZATION_DEFAULT;
 	if (vivid_is_svid_out(dev)) {
 		mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
-	} else if (dev->dvi_d_out || !(bt->standards & V4L2_DV_BT_STD_CEA861)) {
+	} else if (dev->dvi_d_out || !(bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) {
 		mp->colorspace = V4L2_COLORSPACE_SRGB;
 		if (dev->dvi_d_out)
 			mp->quantization = V4L2_QUANTIZATION_LIM_RANGE;
@@ -429,6 +438,7 @@
 	struct vb2_queue *q = &dev->vb_vid_out_q;
 	int ret = vivid_try_fmt_vid_out(file, priv, f);
 	unsigned factor = 1;
+	unsigned p;
 
 	if (ret < 0)
 		return ret;
@@ -524,9 +534,12 @@
 
 	dev->fmt_out_rect.width = mp->width;
 	dev->fmt_out_rect.height = mp->height;
-	dev->bytesperline_out[0] = mp->plane_fmt[0].bytesperline;
-	if (mp->num_planes > 1)
-		dev->bytesperline_out[1] = mp->plane_fmt[1].bytesperline;
+	for (p = 0; p < mp->num_planes; p++)
+		dev->bytesperline_out[p] = mp->plane_fmt[p].bytesperline;
+	for (p = dev->fmt_out->buffers; p < dev->fmt_out->planes; p++)
+		dev->bytesperline_out[p] =
+			(dev->bytesperline_out[0] * dev->fmt_out->bit_depth[p]) /
+			dev->fmt_out->bit_depth[0];
 	dev->field_out = mp->field;
 	if (vivid_is_svid_out(dev))
 		dev->tv_field_out = mp->field;
@@ -1114,13 +1127,13 @@
 
 	if (!vivid_is_hdmi_out(dev))
 		return -ENODATA;
-	if (vb2_is_busy(&dev->vb_vid_out_q))
-		return -EBUSY;
 	if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap,
 				0, NULL, NULL))
 		return -EINVAL;
 	if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0))
 		return 0;
+	if (vb2_is_busy(&dev->vb_vid_out_q))
+		return -EBUSY;
 	dev->dv_timings_out = *timings;
 	vivid_update_format_out(dev);
 	return 0;
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index 1246719..989e96f 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -16,7 +16,6 @@
 #include <linux/io.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
-#include <linux/platform_data/vsp1.h>
 
 #include <media/media-device.h>
 #include <media/v4l2-device.h>
@@ -40,9 +39,20 @@
 #define VSP1_MAX_UDS		3
 #define VSP1_MAX_WPF		4
 
+#define VSP1_HAS_LIF		(1 << 0)
+#define VSP1_HAS_LUT		(1 << 1)
+#define VSP1_HAS_SRU		(1 << 2)
+
+struct vsp1_platform_data {
+	unsigned int features;
+	unsigned int rpf_count;
+	unsigned int uds_count;
+	unsigned int wpf_count;
+};
+
 struct vsp1_device {
 	struct device *dev;
-	struct vsp1_platform_data *pdata;
+	struct vsp1_platform_data pdata;
 
 	void __iomem *mmio;
 	struct clk *clock;
diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c
index b21f381..7dd7633 100644
--- a/drivers/media/platform/vsp1/vsp1_bru.c
+++ b/drivers/media/platform/vsp1/vsp1_bru.c
@@ -20,7 +20,7 @@
 #include "vsp1_bru.h"
 #include "vsp1_rwpf.h"
 
-#define BRU_MIN_SIZE				4U
+#define BRU_MIN_SIZE				1U
 #define BRU_MAX_SIZE				8190U
 
 /* -----------------------------------------------------------------------------
@@ -183,13 +183,14 @@
  */
 
 static int bru_enum_mbus_code(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_mbus_code_enum *code)
 {
 	static const unsigned int codes[] = {
 		MEDIA_BUS_FMT_ARGB8888_1X32,
 		MEDIA_BUS_FMT_AYUV8_1X32,
 	};
+	struct vsp1_bru *bru = to_bru(subdev);
 	struct v4l2_mbus_framefmt *format;
 
 	if (code->pad == BRU_PAD_SINK(0)) {
@@ -201,7 +202,8 @@
 		if (code->index)
 			return -EINVAL;
 
-		format = v4l2_subdev_get_try_format(fh, BRU_PAD_SINK(0));
+		format = vsp1_entity_get_pad_format(&bru->entity, cfg,
+						    BRU_PAD_SINK(0), code->which);
 		code->code = format->code;
 	}
 
@@ -209,7 +211,7 @@
 }
 
 static int bru_enum_frame_size(struct v4l2_subdev *subdev,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_frame_size_enum *fse)
 {
 	if (fse->index)
@@ -228,12 +230,12 @@
 }
 
 static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru,
-					 struct v4l2_subdev_fh *fh,
+					 struct v4l2_subdev_pad_config *cfg,
 					 unsigned int pad, u32 which)
 {
 	switch (which) {
 	case V4L2_SUBDEV_FORMAT_TRY:
-		return v4l2_subdev_get_try_crop(fh, pad);
+		return v4l2_subdev_get_try_crop(&bru->entity.subdev, cfg, pad);
 	case V4L2_SUBDEV_FORMAT_ACTIVE:
 		return &bru->inputs[pad].compose;
 	default:
@@ -241,18 +243,18 @@
 	}
 }
 
-static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_bru *bru = to_bru(subdev);
 
-	fmt->format = *vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad,
+	fmt->format = *vsp1_entity_get_pad_format(&bru->entity, cfg, fmt->pad,
 						  fmt->which);
 
 	return 0;
 }
 
-static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh,
+static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_pad_config *cfg,
 			   unsigned int pad, struct v4l2_mbus_framefmt *fmt,
 			   enum v4l2_subdev_format_whence which)
 {
@@ -268,7 +270,7 @@
 
 	default:
 		/* The BRU can't perform format conversion. */
-		format = vsp1_entity_get_pad_format(&bru->entity, fh,
+		format = vsp1_entity_get_pad_format(&bru->entity, cfg,
 						    BRU_PAD_SINK(0), which);
 		fmt->code = format->code;
 		break;
@@ -280,15 +282,15 @@
 	fmt->colorspace = V4L2_COLORSPACE_SRGB;
 }
 
-static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_bru *bru = to_bru(subdev);
 	struct v4l2_mbus_framefmt *format;
 
-	bru_try_format(bru, fh, fmt->pad, &fmt->format, fmt->which);
+	bru_try_format(bru, cfg, fmt->pad, &fmt->format, fmt->which);
 
-	format = vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad,
+	format = vsp1_entity_get_pad_format(&bru->entity, cfg, fmt->pad,
 					    fmt->which);
 	*format = fmt->format;
 
@@ -296,7 +298,7 @@
 	if (fmt->pad != BRU_PAD_SOURCE) {
 		struct v4l2_rect *compose;
 
-		compose = bru_get_compose(bru, fh, fmt->pad, fmt->which);
+		compose = bru_get_compose(bru, cfg, fmt->pad, fmt->which);
 		compose->left = 0;
 		compose->top = 0;
 		compose->width = format->width;
@@ -308,7 +310,7 @@
 		unsigned int i;
 
 		for (i = 0; i <= BRU_PAD_SOURCE; ++i) {
-			format = vsp1_entity_get_pad_format(&bru->entity, fh,
+			format = vsp1_entity_get_pad_format(&bru->entity, cfg,
 							    i, fmt->which);
 			format->code = fmt->format.code;
 		}
@@ -318,7 +320,7 @@
 }
 
 static int bru_get_selection(struct v4l2_subdev *subdev,
-			     struct v4l2_subdev_fh *fh,
+			     struct v4l2_subdev_pad_config *cfg,
 			     struct v4l2_subdev_selection *sel)
 {
 	struct vsp1_bru *bru = to_bru(subdev);
@@ -335,7 +337,7 @@
 		return 0;
 
 	case V4L2_SEL_TGT_COMPOSE:
-		sel->r = *bru_get_compose(bru, fh, sel->pad, sel->which);
+		sel->r = *bru_get_compose(bru, cfg, sel->pad, sel->which);
 		return 0;
 
 	default:
@@ -344,7 +346,7 @@
 }
 
 static int bru_set_selection(struct v4l2_subdev *subdev,
-			     struct v4l2_subdev_fh *fh,
+			     struct v4l2_subdev_pad_config *cfg,
 			     struct v4l2_subdev_selection *sel)
 {
 	struct vsp1_bru *bru = to_bru(subdev);
@@ -360,7 +362,7 @@
 	/* The compose rectangle top left corner must be inside the output
 	 * frame.
 	 */
-	format = vsp1_entity_get_pad_format(&bru->entity, fh, BRU_PAD_SOURCE,
+	format = vsp1_entity_get_pad_format(&bru->entity, cfg, BRU_PAD_SOURCE,
 					    sel->which);
 	sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
 	sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
@@ -368,12 +370,12 @@
 	/* Scaling isn't supported, the compose rectangle size must be identical
 	 * to the sink format size.
 	 */
-	format = vsp1_entity_get_pad_format(&bru->entity, fh, sel->pad,
+	format = vsp1_entity_get_pad_format(&bru->entity, cfg, sel->pad,
 					    sel->which);
 	sel->r.width = format->width;
 	sel->r.height = format->height;
 
-	compose = bru_get_compose(bru, fh, sel->pad, sel->which);
+	compose = bru_get_compose(bru, cfg, sel->pad, sel->which);
 	*compose = sel->r;
 
 	return 0;
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index 5eb16e8..913485a 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -40,7 +40,7 @@
 	irqreturn_t ret = IRQ_NONE;
 	unsigned int i;
 
-	for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
+	for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
 		struct vsp1_rwpf *wpf = vsp1->wpf[i];
 		struct vsp1_pipeline *pipe;
 		u32 status;
@@ -181,7 +181,7 @@
 
 	list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
 
-	if (vsp1->pdata->features & VSP1_HAS_LIF) {
+	if (vsp1->pdata.features & VSP1_HAS_LIF) {
 		vsp1->lif = vsp1_lif_create(vsp1);
 		if (IS_ERR(vsp1->lif)) {
 			ret = PTR_ERR(vsp1->lif);
@@ -191,7 +191,7 @@
 		list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities);
 	}
 
-	if (vsp1->pdata->features & VSP1_HAS_LUT) {
+	if (vsp1->pdata.features & VSP1_HAS_LUT) {
 		vsp1->lut = vsp1_lut_create(vsp1);
 		if (IS_ERR(vsp1->lut)) {
 			ret = PTR_ERR(vsp1->lut);
@@ -201,7 +201,7 @@
 		list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities);
 	}
 
-	for (i = 0; i < vsp1->pdata->rpf_count; ++i) {
+	for (i = 0; i < vsp1->pdata.rpf_count; ++i) {
 		struct vsp1_rwpf *rpf;
 
 		rpf = vsp1_rpf_create(vsp1, i);
@@ -214,7 +214,7 @@
 		list_add_tail(&rpf->entity.list_dev, &vsp1->entities);
 	}
 
-	if (vsp1->pdata->features & VSP1_HAS_SRU) {
+	if (vsp1->pdata.features & VSP1_HAS_SRU) {
 		vsp1->sru = vsp1_sru_create(vsp1);
 		if (IS_ERR(vsp1->sru)) {
 			ret = PTR_ERR(vsp1->sru);
@@ -224,7 +224,7 @@
 		list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities);
 	}
 
-	for (i = 0; i < vsp1->pdata->uds_count; ++i) {
+	for (i = 0; i < vsp1->pdata.uds_count; ++i) {
 		struct vsp1_uds *uds;
 
 		uds = vsp1_uds_create(vsp1, i);
@@ -237,7 +237,7 @@
 		list_add_tail(&uds->entity.list_dev, &vsp1->entities);
 	}
 
-	for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
+	for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
 		struct vsp1_rwpf *wpf;
 
 		wpf = vsp1_wpf_create(vsp1, i);
@@ -261,7 +261,7 @@
 			goto done;
 	}
 
-	if (vsp1->pdata->features & VSP1_HAS_LIF) {
+	if (vsp1->pdata.features & VSP1_HAS_LIF) {
 		ret = media_entity_create_link(
 			&vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE,
 			&vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0);
@@ -294,7 +294,7 @@
 	/* Reset any channel that might be running. */
 	status = vsp1_read(vsp1, VI6_STATUS);
 
-	for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
+	for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
 		unsigned int timeout;
 
 		if (!(status & VI6_STATUS_SYS_ACT(i)))
@@ -318,10 +318,10 @@
 	vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) |
 		   (8 << VI6_CLK_DCSWT_CSTRW_SHIFT));
 
-	for (i = 0; i < vsp1->pdata->rpf_count; ++i)
+	for (i = 0; i < vsp1->pdata.rpf_count; ++i)
 		vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED);
 
-	for (i = 0; i < vsp1->pdata->uds_count; ++i)
+	for (i = 0; i < vsp1->pdata.uds_count; ++i)
 		vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);
 
 	vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);
@@ -428,47 +428,10 @@
  * Platform Driver
  */
 
-static int vsp1_validate_platform_data(struct platform_device *pdev,
-				       struct vsp1_platform_data *pdata)
+static int vsp1_parse_dt(struct vsp1_device *vsp1)
 {
-	if (pdata == NULL) {
-		dev_err(&pdev->dev, "missing platform data\n");
-		return -EINVAL;
-	}
-
-	if (pdata->rpf_count <= 0 || pdata->rpf_count > VSP1_MAX_RPF) {
-		dev_err(&pdev->dev, "invalid number of RPF (%u)\n",
-			pdata->rpf_count);
-		return -EINVAL;
-	}
-
-	if (pdata->uds_count <= 0 || pdata->uds_count > VSP1_MAX_UDS) {
-		dev_err(&pdev->dev, "invalid number of UDS (%u)\n",
-			pdata->uds_count);
-		return -EINVAL;
-	}
-
-	if (pdata->wpf_count <= 0 || pdata->wpf_count > VSP1_MAX_WPF) {
-		dev_err(&pdev->dev, "invalid number of WPF (%u)\n",
-			pdata->wpf_count);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static struct vsp1_platform_data *
-vsp1_get_platform_data(struct platform_device *pdev)
-{
-	struct device_node *np = pdev->dev.of_node;
-	struct vsp1_platform_data *pdata;
-
-	if (!IS_ENABLED(CONFIG_OF) || np == NULL)
-		return pdev->dev.platform_data;
-
-	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-	if (pdata == NULL)
-		return NULL;
+	struct device_node *np = vsp1->dev->of_node;
+	struct vsp1_platform_data *pdata = &vsp1->pdata;
 
 	if (of_property_read_bool(np, "renesas,has-lif"))
 		pdata->features |= VSP1_HAS_LIF;
@@ -481,7 +444,25 @@
 	of_property_read_u32(np, "renesas,#uds", &pdata->uds_count);
 	of_property_read_u32(np, "renesas,#wpf", &pdata->wpf_count);
 
-	return pdata;
+	if (pdata->rpf_count <= 0 || pdata->rpf_count > VSP1_MAX_RPF) {
+		dev_err(vsp1->dev, "invalid number of RPF (%u)\n",
+			pdata->rpf_count);
+		return -EINVAL;
+	}
+
+	if (pdata->uds_count <= 0 || pdata->uds_count > VSP1_MAX_UDS) {
+		dev_err(vsp1->dev, "invalid number of UDS (%u)\n",
+			pdata->uds_count);
+		return -EINVAL;
+	}
+
+	if (pdata->wpf_count <= 0 || pdata->wpf_count > VSP1_MAX_WPF) {
+		dev_err(vsp1->dev, "invalid number of WPF (%u)\n",
+			pdata->wpf_count);
+		return -EINVAL;
+	}
+
+	return 0;
 }
 
 static int vsp1_probe(struct platform_device *pdev)
@@ -499,11 +480,7 @@
 	mutex_init(&vsp1->lock);
 	INIT_LIST_HEAD(&vsp1->entities);
 
-	vsp1->pdata = vsp1_get_platform_data(pdev);
-	if (vsp1->pdata == NULL)
-		return -ENODEV;
-
-	ret = vsp1_validate_platform_data(pdev, vsp1->pdata);
+	ret = vsp1_parse_dt(vsp1);
 	if (ret < 0)
 		return ret;
 
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
index 79af71d..a453bb4 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -63,12 +63,12 @@
 
 struct v4l2_mbus_framefmt *
 vsp1_entity_get_pad_format(struct vsp1_entity *entity,
-			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_pad_config *cfg,
 			   unsigned int pad, u32 which)
 {
 	switch (which) {
 	case V4L2_SUBDEV_FORMAT_TRY:
-		return v4l2_subdev_get_try_format(fh, pad);
+		return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad);
 	case V4L2_SUBDEV_FORMAT_ACTIVE:
 		return &entity->formats[pad];
 	default:
@@ -79,14 +79,14 @@
 /*
  * vsp1_entity_init_formats - Initialize formats on all pads
  * @subdev: V4L2 subdevice
- * @fh: V4L2 subdev file handle
+ * @cfg: V4L2 subdev pad configuration
  *
- * Initialize all pad formats with default values. If fh is not NULL, try
+ * Initialize all pad formats with default values. If cfg is not NULL, try
  * formats are initialized on the file handle. Otherwise active formats are
  * initialized on the device.
  */
 void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
-			    struct v4l2_subdev_fh *fh)
+			    struct v4l2_subdev_pad_config *cfg)
 {
 	struct v4l2_subdev_format format;
 	unsigned int pad;
@@ -95,17 +95,17 @@
 		memset(&format, 0, sizeof(format));
 
 		format.pad = pad;
-		format.which = fh ? V4L2_SUBDEV_FORMAT_TRY
+		format.which = cfg ? V4L2_SUBDEV_FORMAT_TRY
 			     : V4L2_SUBDEV_FORMAT_ACTIVE;
 
-		v4l2_subdev_call(subdev, pad, set_fmt, fh, &format);
+		v4l2_subdev_call(subdev, pad, set_fmt, cfg, &format);
 	}
 }
 
 static int vsp1_entity_open(struct v4l2_subdev *subdev,
 			    struct v4l2_subdev_fh *fh)
 {
-	vsp1_entity_init_formats(subdev, fh);
+	vsp1_entity_init_formats(subdev, fh->pad);
 
 	return 0;
 }
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
index aa20aaa..62c768d 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/vsp1/vsp1_entity.h
@@ -91,10 +91,10 @@
 
 struct v4l2_mbus_framefmt *
 vsp1_entity_get_pad_format(struct vsp1_entity *entity,
-			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_pad_config *cfg,
 			   unsigned int pad, u32 which);
 void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh);
+			      struct v4l2_subdev_pad_config *cfg);
 
 bool vsp1_entity_is_streaming(struct vsp1_entity *entity);
 int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming);
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c
index 80bedc5..8ffb817 100644
--- a/drivers/media/platform/vsp1/vsp1_hsit.c
+++ b/drivers/media/platform/vsp1/vsp1_hsit.c
@@ -26,11 +26,6 @@
  * Device Access
  */
 
-static inline u32 vsp1_hsit_read(struct vsp1_hsit *hsit, u32 reg)
-{
-	return vsp1_read(hsit->entity.vsp1, reg);
-}
-
 static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data)
 {
 	vsp1_write(hsit->entity.vsp1, reg, data);
@@ -60,7 +55,7 @@
  */
 
 static int hsit_enum_mbus_code(struct v4l2_subdev *subdev,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_mbus_code_enum *code)
 {
 	struct vsp1_hsit *hsit = to_hsit(subdev);
@@ -78,12 +73,14 @@
 }
 
 static int hsit_enum_frame_size(struct v4l2_subdev *subdev,
-				struct v4l2_subdev_fh *fh,
+				struct v4l2_subdev_pad_config *cfg,
 				struct v4l2_subdev_frame_size_enum *fse)
 {
+	struct vsp1_hsit *hsit = to_hsit(subdev);
 	struct v4l2_mbus_framefmt *format;
 
-	format = v4l2_subdev_get_try_format(fh, fse->pad);
+	format = vsp1_entity_get_pad_format(&hsit->entity, cfg, fse->pad,
+					    fse->which);
 
 	if (fse->index || fse->code != format->code)
 		return -EINVAL;
@@ -107,25 +104,25 @@
 }
 
 static int hsit_get_format(struct v4l2_subdev *subdev,
-			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_hsit *hsit = to_hsit(subdev);
 
-	fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad,
+	fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad,
 						  fmt->which);
 
 	return 0;
 }
 
 static int hsit_set_format(struct v4l2_subdev *subdev,
-			   struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_hsit *hsit = to_hsit(subdev);
 	struct v4l2_mbus_framefmt *format;
 
-	format = vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad,
+	format = vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad,
 					    fmt->which);
 
 	if (fmt->pad == HSIT_PAD_SOURCE) {
@@ -148,7 +145,7 @@
 	fmt->format = *format;
 
 	/* Propagate the format to the source pad. */
-	format = vsp1_entity_get_pad_format(&hsit->entity, fh, HSIT_PAD_SOURCE,
+	format = vsp1_entity_get_pad_format(&hsit->entity, cfg, HSIT_PAD_SOURCE,
 					    fmt->which);
 	*format = fmt->format;
 	format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32
diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c
index 17a6ca7..39fa5ef 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.c
+++ b/drivers/media/platform/vsp1/vsp1_lif.c
@@ -74,13 +74,14 @@
  */
 
 static int lif_enum_mbus_code(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_mbus_code_enum *code)
 {
 	static const unsigned int codes[] = {
 		MEDIA_BUS_FMT_ARGB8888_1X32,
 		MEDIA_BUS_FMT_AYUV8_1X32,
 	};
+	struct vsp1_lif *lif = to_lif(subdev);
 
 	if (code->pad == LIF_PAD_SINK) {
 		if (code->index >= ARRAY_SIZE(codes))
@@ -96,7 +97,8 @@
 		if (code->index)
 			return -EINVAL;
 
-		format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK);
+		format = vsp1_entity_get_pad_format(&lif->entity, cfg,
+						    LIF_PAD_SINK, code->which);
 		code->code = format->code;
 	}
 
@@ -104,12 +106,14 @@
 }
 
 static int lif_enum_frame_size(struct v4l2_subdev *subdev,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_frame_size_enum *fse)
 {
+	struct vsp1_lif *lif = to_lif(subdev);
 	struct v4l2_mbus_framefmt *format;
 
-	format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK);
+	format = vsp1_entity_get_pad_format(&lif->entity, cfg, LIF_PAD_SINK,
+					    fse->which);
 
 	if (fse->index || fse->code != format->code)
 		return -EINVAL;
@@ -129,18 +133,18 @@
 	return 0;
 }
 
-static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_lif *lif = to_lif(subdev);
 
-	fmt->format = *vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad,
+	fmt->format = *vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad,
 						  fmt->which);
 
 	return 0;
 }
 
-static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_lif *lif = to_lif(subdev);
@@ -151,7 +155,7 @@
 	    fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
 		fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
 
-	format = vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad,
+	format = vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad,
 					    fmt->which);
 
 	if (fmt->pad == LIF_PAD_SOURCE) {
@@ -173,7 +177,7 @@
 	fmt->format = *format;
 
 	/* Propagate the format to the source pad. */
-	format = vsp1_entity_get_pad_format(&lif->entity, fh, LIF_PAD_SOURCE,
+	format = vsp1_entity_get_pad_format(&lif->entity, cfg, LIF_PAD_SOURCE,
 					    fmt->which);
 	*format = fmt->format;
 
diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c
index 6f185c3..656ec27 100644
--- a/drivers/media/platform/vsp1/vsp1_lut.c
+++ b/drivers/media/platform/vsp1/vsp1_lut.c
@@ -82,7 +82,7 @@
  */
 
 static int lut_enum_mbus_code(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_mbus_code_enum *code)
 {
 	static const unsigned int codes[] = {
@@ -90,6 +90,7 @@
 		MEDIA_BUS_FMT_AHSV8888_1X32,
 		MEDIA_BUS_FMT_AYUV8_1X32,
 	};
+	struct vsp1_lut *lut = to_lut(subdev);
 	struct v4l2_mbus_framefmt *format;
 
 	if (code->pad == LUT_PAD_SINK) {
@@ -104,7 +105,8 @@
 		if (code->index)
 			return -EINVAL;
 
-		format = v4l2_subdev_get_try_format(fh, LUT_PAD_SINK);
+		format = vsp1_entity_get_pad_format(&lut->entity, cfg,
+						    LUT_PAD_SINK, code->which);
 		code->code = format->code;
 	}
 
@@ -112,12 +114,14 @@
 }
 
 static int lut_enum_frame_size(struct v4l2_subdev *subdev,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_frame_size_enum *fse)
 {
+	struct vsp1_lut *lut = to_lut(subdev);
 	struct v4l2_mbus_framefmt *format;
 
-	format = v4l2_subdev_get_try_format(fh, fse->pad);
+	format = vsp1_entity_get_pad_format(&lut->entity, cfg,
+					    fse->pad, fse->which);
 
 	if (fse->index || fse->code != format->code)
 		return -EINVAL;
@@ -140,18 +144,18 @@
 	return 0;
 }
 
-static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_lut *lut = to_lut(subdev);
 
-	fmt->format = *vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad,
+	fmt->format = *vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad,
 						  fmt->which);
 
 	return 0;
 }
 
-static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_lut *lut = to_lut(subdev);
@@ -163,7 +167,7 @@
 	    fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
 		fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
 
-	format = vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad,
+	format = vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad,
 					    fmt->which);
 
 	if (fmt->pad == LUT_PAD_SOURCE) {
@@ -182,7 +186,7 @@
 	fmt->format = *format;
 
 	/* Propagate the format to the source pad. */
-	format = vsp1_entity_get_pad_format(&lut->entity, fh, LUT_PAD_SOURCE,
+	format = vsp1_entity_get_pad_format(&lut->entity, cfg, LUT_PAD_SOURCE,
 					    fmt->which);
 	*format = fmt->format;
 
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index 55f163d..da3c573 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -43,12 +43,12 @@
 #define VI6_DISP_IRQ_ENB		0x0078
 #define VI6_DISP_IRQ_ENB_DSTE		(1 << 8)
 #define VI6_DISP_IRQ_ENB_MAEE		(1 << 5)
-#define VI6_DISP_IRQ_ENB_LNEE(n)	(1 << ((n) + 4))
+#define VI6_DISP_IRQ_ENB_LNEE(n)	(1 << (n))
 
 #define VI6_DISP_IRQ_STA		0x007c
 #define VI6_DISP_IRQ_STA_DSE		(1 << 8)
 #define VI6_DISP_IRQ_STA_MAE		(1 << 5)
-#define VI6_DISP_IRQ_STA_LNE(n)		(1 << ((n) + 4))
+#define VI6_DISP_IRQ_STA_LNE(n)		(1 << (n))
 
 #define VI6_WPF_LINE_COUNT(n)		(0x0084 + (n) * 4)
 #define VI6_WPF_LINE_COUNT_MASK		(0x1fffff << 0)
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
index d14d26b..3294529 100644
--- a/drivers/media/platform/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -106,11 +106,22 @@
 			+ crop->left * fmtinfo->bpp[0] / 8;
 	pstride = format->plane_fmt[0].bytesperline
 		<< VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
+
+	vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y,
+		       rpf->buf_addr[0] + rpf->offsets[0]);
+
 	if (format->num_planes > 1) {
 		rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline
 				+ crop->left * fmtinfo->bpp[1] / 8;
 		pstride |= format->plane_fmt[1].bytesperline
 			<< VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
+
+		vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0,
+			       rpf->buf_addr[1] + rpf->offsets[1]);
+
+		if (format->num_planes > 2)
+			vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1,
+				       rpf->buf_addr[2] + rpf->offsets[1]);
 	}
 
 	vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride);
@@ -179,6 +190,13 @@
 			   struct vsp1_video_buffer *buf)
 {
 	struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video);
+	unsigned int i;
+
+	for (i = 0; i < 3; ++i)
+		rpf->buf_addr[i] = buf->addr[i];
+
+	if (!vsp1_entity_is_streaming(&rpf->entity))
+		return;
 
 	vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y,
 		       buf->addr[0] + rpf->offsets[0]);
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c
index 1f1ba26..fa71f46 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.c
@@ -25,7 +25,7 @@
  */
 
 int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
-			     struct v4l2_subdev_fh *fh,
+			     struct v4l2_subdev_pad_config *cfg,
 			     struct v4l2_subdev_mbus_code_enum *code)
 {
 	static const unsigned int codes[] = {
@@ -42,13 +42,14 @@
 }
 
 int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_frame_size_enum *fse)
 {
 	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
 	struct v4l2_mbus_framefmt *format;
 
-	format = v4l2_subdev_get_try_format(fh, fse->pad);
+	format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, fse->pad,
+					    fse->which);
 
 	if (fse->index || fse->code != format->code)
 		return -EINVAL;
@@ -72,11 +73,11 @@
 }
 
 static struct v4l2_rect *
-vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_fh *fh, u32 which)
+vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *cfg, u32 which)
 {
 	switch (which) {
 	case V4L2_SUBDEV_FORMAT_TRY:
-		return v4l2_subdev_get_try_crop(fh, RWPF_PAD_SINK);
+		return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, cfg, RWPF_PAD_SINK);
 	case V4L2_SUBDEV_FORMAT_ACTIVE:
 		return &rwpf->crop;
 	default:
@@ -84,18 +85,18 @@
 	}
 }
 
-int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			 struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
 
-	fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad,
+	fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad,
 						  fmt->which);
 
 	return 0;
 }
 
-int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			 struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
@@ -107,7 +108,7 @@
 	    fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
 		fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
 
-	format = vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad,
+	format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad,
 					    fmt->which);
 
 	if (fmt->pad == RWPF_PAD_SOURCE) {
@@ -130,14 +131,14 @@
 	fmt->format = *format;
 
 	/* Update the sink crop rectangle. */
-	crop = vsp1_rwpf_get_crop(rwpf, fh, fmt->which);
+	crop = vsp1_rwpf_get_crop(rwpf, cfg, fmt->which);
 	crop->left = 0;
 	crop->top = 0;
 	crop->width = fmt->format.width;
 	crop->height = fmt->format.height;
 
 	/* Propagate the format to the source pad. */
-	format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE,
+	format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE,
 					    fmt->which);
 	*format = fmt->format;
 
@@ -145,7 +146,7 @@
 }
 
 int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
-			    struct v4l2_subdev_fh *fh,
+			    struct v4l2_subdev_pad_config *cfg,
 			    struct v4l2_subdev_selection *sel)
 {
 	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
@@ -157,11 +158,11 @@
 
 	switch (sel->target) {
 	case V4L2_SEL_TGT_CROP:
-		sel->r = *vsp1_rwpf_get_crop(rwpf, fh, sel->which);
+		sel->r = *vsp1_rwpf_get_crop(rwpf, cfg, sel->which);
 		break;
 
 	case V4L2_SEL_TGT_CROP_BOUNDS:
-		format = vsp1_entity_get_pad_format(&rwpf->entity, fh,
+		format = vsp1_entity_get_pad_format(&rwpf->entity, cfg,
 						    RWPF_PAD_SINK, sel->which);
 		sel->r.left = 0;
 		sel->r.top = 0;
@@ -177,7 +178,7 @@
 }
 
 int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
-			    struct v4l2_subdev_fh *fh,
+			    struct v4l2_subdev_pad_config *cfg,
 			    struct v4l2_subdev_selection *sel)
 {
 	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
@@ -194,7 +195,7 @@
 	/* Make sure the crop rectangle is entirely contained in the image. The
 	 * WPF top and left offsets are limited to 255.
 	 */
-	format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SINK,
+	format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SINK,
 					    sel->which);
 	sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);
 	sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);
@@ -207,11 +208,11 @@
 	sel->r.height = min_t(unsigned int, sel->r.height,
 			      format->height - sel->r.top);
 
-	crop = vsp1_rwpf_get_crop(rwpf, fh, sel->which);
+	crop = vsp1_rwpf_get_crop(rwpf, cfg, sel->which);
 	*crop = sel->r;
 
 	/* Propagate the format to the source pad. */
-	format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE,
+	format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE,
 					    sel->which);
 	format->width = crop->width;
 	format->height = crop->height;
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index 28dd9e7..f452dce 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -39,6 +39,7 @@
 	struct v4l2_rect crop;
 
 	unsigned int offsets[2];
+	dma_addr_t buf_addr[3];
 };
 
 static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev)
@@ -50,20 +51,20 @@
 struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index);
 
 int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
-			     struct v4l2_subdev_fh *fh,
+			     struct v4l2_subdev_pad_config *cfg,
 			     struct v4l2_subdev_mbus_code_enum *code);
 int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_frame_size_enum *fse);
-int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			 struct v4l2_subdev_format *fmt);
-int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			 struct v4l2_subdev_format *fmt);
 int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
-			    struct v4l2_subdev_fh *fh,
+			    struct v4l2_subdev_pad_config *cfg,
 			    struct v4l2_subdev_selection *sel);
 int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
-			    struct v4l2_subdev_fh *fh,
+			    struct v4l2_subdev_pad_config *cfg,
 			    struct v4l2_subdev_selection *sel);
 
 #endif /* __VSP1_RWPF_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c
index 1129494..6310aca 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/vsp1/vsp1_sru.c
@@ -166,13 +166,14 @@
  */
 
 static int sru_enum_mbus_code(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_mbus_code_enum *code)
 {
 	static const unsigned int codes[] = {
 		MEDIA_BUS_FMT_ARGB8888_1X32,
 		MEDIA_BUS_FMT_AYUV8_1X32,
 	};
+	struct vsp1_sru *sru = to_sru(subdev);
 	struct v4l2_mbus_framefmt *format;
 
 	if (code->pad == SRU_PAD_SINK) {
@@ -187,7 +188,8 @@
 		if (code->index)
 			return -EINVAL;
 
-		format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK);
+		format = vsp1_entity_get_pad_format(&sru->entity, cfg,
+						    SRU_PAD_SINK, code->which);
 		code->code = format->code;
 	}
 
@@ -195,12 +197,14 @@
 }
 
 static int sru_enum_frame_size(struct v4l2_subdev *subdev,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_frame_size_enum *fse)
 {
+	struct vsp1_sru *sru = to_sru(subdev);
 	struct v4l2_mbus_framefmt *format;
 
-	format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK);
+	format = vsp1_entity_get_pad_format(&sru->entity, cfg,
+					    SRU_PAD_SINK, fse->which);
 
 	if (fse->index || fse->code != format->code)
 		return -EINVAL;
@@ -226,18 +230,18 @@
 	return 0;
 }
 
-static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_sru *sru = to_sru(subdev);
 
-	fmt->format = *vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad,
+	fmt->format = *vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad,
 						  fmt->which);
 
 	return 0;
 }
 
-static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh,
+static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_pad_config *cfg,
 			   unsigned int pad, struct v4l2_mbus_framefmt *fmt,
 			   enum v4l2_subdev_format_whence which)
 {
@@ -258,7 +262,7 @@
 
 	case SRU_PAD_SOURCE:
 		/* The SRU can't perform format conversion. */
-		format = vsp1_entity_get_pad_format(&sru->entity, fh,
+		format = vsp1_entity_get_pad_format(&sru->entity, cfg,
 						    SRU_PAD_SINK, which);
 		fmt->code = format->code;
 
@@ -288,25 +292,25 @@
 	fmt->colorspace = V4L2_COLORSPACE_SRGB;
 }
 
-static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_sru *sru = to_sru(subdev);
 	struct v4l2_mbus_framefmt *format;
 
-	sru_try_format(sru, fh, fmt->pad, &fmt->format, fmt->which);
+	sru_try_format(sru, cfg, fmt->pad, &fmt->format, fmt->which);
 
-	format = vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad,
+	format = vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad,
 					    fmt->which);
 	*format = fmt->format;
 
 	if (fmt->pad == SRU_PAD_SINK) {
 		/* Propagate the format to the source pad. */
-		format = vsp1_entity_get_pad_format(&sru->entity, fh,
+		format = vsp1_entity_get_pad_format(&sru->entity, cfg,
 						    SRU_PAD_SOURCE, fmt->which);
 		*format = fmt->format;
 
-		sru_try_format(sru, fh, SRU_PAD_SOURCE, format, fmt->which);
+		sru_try_format(sru, cfg, SRU_PAD_SOURCE, format, fmt->which);
 	}
 
 	return 0;
diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c
index a4afec1..ccc8243 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/vsp1/vsp1_uds.c
@@ -169,13 +169,14 @@
  */
 
 static int uds_enum_mbus_code(struct v4l2_subdev *subdev,
-			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_mbus_code_enum *code)
 {
 	static const unsigned int codes[] = {
 		MEDIA_BUS_FMT_ARGB8888_1X32,
 		MEDIA_BUS_FMT_AYUV8_1X32,
 	};
+	struct vsp1_uds *uds = to_uds(subdev);
 
 	if (code->pad == UDS_PAD_SINK) {
 		if (code->index >= ARRAY_SIZE(codes))
@@ -191,7 +192,8 @@
 		if (code->index)
 			return -EINVAL;
 
-		format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK);
+		format = vsp1_entity_get_pad_format(&uds->entity, cfg,
+						    UDS_PAD_SINK, code->which);
 		code->code = format->code;
 	}
 
@@ -199,12 +201,14 @@
 }
 
 static int uds_enum_frame_size(struct v4l2_subdev *subdev,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_frame_size_enum *fse)
 {
+	struct vsp1_uds *uds = to_uds(subdev);
 	struct v4l2_mbus_framefmt *format;
 
-	format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK);
+	format = vsp1_entity_get_pad_format(&uds->entity, cfg,
+					    UDS_PAD_SINK, fse->which);
 
 	if (fse->index || fse->code != format->code)
 		return -EINVAL;
@@ -224,18 +228,18 @@
 	return 0;
 }
 
-static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_uds *uds = to_uds(subdev);
 
-	fmt->format = *vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad,
+	fmt->format = *vsp1_entity_get_pad_format(&uds->entity, cfg, fmt->pad,
 						  fmt->which);
 
 	return 0;
 }
 
-static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh,
+static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_pad_config *cfg,
 			   unsigned int pad, struct v4l2_mbus_framefmt *fmt,
 			   enum v4l2_subdev_format_whence which)
 {
@@ -256,7 +260,7 @@
 
 	case UDS_PAD_SOURCE:
 		/* The UDS scales but can't perform format conversion. */
-		format = vsp1_entity_get_pad_format(&uds->entity, fh,
+		format = vsp1_entity_get_pad_format(&uds->entity, cfg,
 						    UDS_PAD_SINK, which);
 		fmt->code = format->code;
 
@@ -271,25 +275,25 @@
 	fmt->colorspace = V4L2_COLORSPACE_SRGB;
 }
 
-static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 			  struct v4l2_subdev_format *fmt)
 {
 	struct vsp1_uds *uds = to_uds(subdev);
 	struct v4l2_mbus_framefmt *format;
 
-	uds_try_format(uds, fh, fmt->pad, &fmt->format, fmt->which);
+	uds_try_format(uds, cfg, fmt->pad, &fmt->format, fmt->which);
 
-	format = vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad,
+	format = vsp1_entity_get_pad_format(&uds->entity, cfg, fmt->pad,
 					    fmt->which);
 	*format = fmt->format;
 
 	if (fmt->pad == UDS_PAD_SINK) {
 		/* Propagate the format to the source pad. */
-		format = vsp1_entity_get_pad_format(&uds->entity, fh,
+		format = vsp1_entity_get_pad_format(&uds->entity, cfg,
 						    UDS_PAD_SOURCE, fmt->which);
 		*format = fmt->format;
 
-		uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which);
+		uds_try_format(uds, cfg, UDS_PAD_SOURCE, format, fmt->which);
 	}
 
 	return 0;
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 6e05776..1d2b3a2 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -92,19 +92,20 @@
 		return 0;
 	}
 
-	/* Sources. If the pipeline has a single input configure it as the
-	 * master layer. Otherwise configure all inputs as sub-layers and
-	 * select the virtual RPF as the master layer.
+	/* Sources. If the pipeline has a single input and BRU is not used,
+	 * configure it as the master layer. Otherwise configure all
+	 * inputs as sub-layers and select the virtual RPF as the master
+	 * layer.
 	 */
 	for (i = 0; i < pipe->num_inputs; ++i) {
 		struct vsp1_rwpf *input = pipe->inputs[i];
 
-		srcrpf |= pipe->num_inputs == 1
+		srcrpf |= (!pipe->bru && pipe->num_inputs == 1)
 			? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index)
 			: VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
 	}
 
-	if (pipe->num_inputs > 1)
+	if (pipe->bru || pipe->num_inputs > 1)
 		srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST;
 
 	vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf);
@@ -280,7 +281,7 @@
 	 * except for the WPF0 source link if a LIF is present.
 	 */
 	flags = MEDIA_LNK_FL_ENABLED;
-	if (!(vsp1->pdata->features & VSP1_HAS_LIF) || index != 0)
+	if (!(vsp1->pdata.features & VSP1_HAS_LIF) || index != 0)
 		flags |= MEDIA_LNK_FL_IMMUTABLE;
 
 	ret = media_entity_create_link(&wpf->entity.subdev.entity,
diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
new file mode 100644
index 0000000..887da41
--- /dev/null
+++ b/drivers/media/platform/xilinx/Kconfig
@@ -0,0 +1,26 @@
+config VIDEO_XILINX
+	tristate "Xilinx Video IP (EXPERIMENTAL)"
+	depends on m
+	depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF
+	select VIDEOBUF2_DMA_CONTIG
+	---help---
+	  Driver for Xilinx Video IP Pipelines
+
+if VIDEO_XILINX
+
+config VIDEO_XILINX_TPG
+	tristate "Xilinx Video Test Pattern Generator"
+	depends on m
+	depends on VIDEO_XILINX
+	select VIDEO_XILINX_VTC
+	---help---
+	   Driver for the Xilinx Video Test Pattern Generator
+
+config VIDEO_XILINX_VTC
+	tristate "Xilinx Video Timing Controller"
+	depends on m
+	depends on VIDEO_XILINX
+	---help---
+	   Driver for the Xilinx Video Timing Controller
+
+endif #VIDEO_XILINX
diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile
new file mode 100644
index 0000000..70d6aab
--- /dev/null
+++ b/drivers/media/platform/xilinx/Makefile
@@ -0,0 +1,5 @@
+xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
+
+obj-$(CPTCFG_VIDEO_XILINX) += xilinx-video.o
+obj-$(CPTCFG_VIDEO_XILINX_TPG) += xilinx-tpg.o
+obj-$(CPTCFG_VIDEO_XILINX_VTC) += xilinx-vtc.o
diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c
new file mode 100644
index 0000000..efde88a
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-dma.c
@@ -0,0 +1,766 @@
+/*
+ * Xilinx Video DMA
+ *
+ * Copyright (C) 2013-2015 Ideas on Board
+ * Copyright (C) 2013-2015 Xilinx, Inc.
+ *
+ * Contacts: Hyun Kwon <hyun.kwon@xilinx.com>
+ *           Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/dma/xilinx_dma.h>
+#include <linux/lcm.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "xilinx-dma.h"
+#include "xilinx-vip.h"
+#include "xilinx-vipp.h"
+
+#define XVIP_DMA_DEF_FORMAT		V4L2_PIX_FMT_YUYV
+#define XVIP_DMA_DEF_WIDTH		1920
+#define XVIP_DMA_DEF_HEIGHT		1080
+
+/* Minimum and maximum widths are expressed in bytes */
+#define XVIP_DMA_MIN_WIDTH		1U
+#define XVIP_DMA_MAX_WIDTH		65535U
+#define XVIP_DMA_MIN_HEIGHT		1U
+#define XVIP_DMA_MAX_HEIGHT		8191U
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+static struct v4l2_subdev *
+xvip_dma_remote_subdev(struct media_pad *local, u32 *pad)
+{
+	struct media_pad *remote;
+
+	remote = media_entity_remote_pad(local);
+	if (remote == NULL ||
+	    media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+		return NULL;
+
+	if (pad)
+		*pad = remote->index;
+
+	return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+static int xvip_dma_verify_format(struct xvip_dma *dma)
+{
+	struct v4l2_subdev_format fmt;
+	struct v4l2_subdev *subdev;
+	int ret;
+
+	subdev = xvip_dma_remote_subdev(&dma->pad, &fmt.pad);
+	if (subdev == NULL)
+		return -EPIPE;
+
+	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+	if (ret < 0)
+		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+
+	if (dma->fmtinfo->code != fmt.format.code ||
+	    dma->format.height != fmt.format.height ||
+	    dma->format.width != fmt.format.width ||
+	    dma->format.colorspace != fmt.format.colorspace)
+		return -EINVAL;
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline Stream Management
+ */
+
+/**
+ * xvip_pipeline_start_stop - Start ot stop streaming on a pipeline
+ * @pipe: The pipeline
+ * @start: Start (when true) or stop (when false) the pipeline
+ *
+ * Walk the entities chain starting at the pipeline output video node and start
+ * or stop all of them.
+ *
+ * Return: 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise.
+ */
+static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start)
+{
+	struct xvip_dma *dma = pipe->output;
+	struct media_entity *entity;
+	struct media_pad *pad;
+	struct v4l2_subdev *subdev;
+	int ret;
+
+	entity = &dma->video.entity;
+	while (1) {
+		pad = &entity->pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			break;
+
+		pad = media_entity_remote_pad(pad);
+		if (pad == NULL ||
+		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+			break;
+
+		entity = pad->entity;
+		subdev = media_entity_to_v4l2_subdev(entity);
+
+		ret = v4l2_subdev_call(subdev, video, s_stream, start);
+		if (start && ret < 0 && ret != -ENOIOCTLCMD)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * xvip_pipeline_set_stream - Enable/disable streaming on a pipeline
+ * @pipe: The pipeline
+ * @on: Turn the stream on when true or off when false
+ *
+ * The pipeline is shared between all DMA engines connect at its input and
+ * output. While the stream state of DMA engines can be controlled
+ * independently, pipelines have a shared stream state that enable or disable
+ * all entities in the pipeline. For this reason the pipeline uses a streaming
+ * counter that tracks the number of DMA engines that have requested the stream
+ * to be enabled.
+ *
+ * When called with the @on argument set to true, this function will increment
+ * the pipeline streaming count. If the streaming count reaches the number of
+ * DMA engines in the pipeline it will enable all entities that belong to the
+ * pipeline.
+ *
+ * Similarly, when called with the @on argument set to false, this function will
+ * decrement the pipeline streaming count and disable all entities in the
+ * pipeline when the streaming count reaches zero.
+ *
+ * Return: 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise. Stopping the pipeline never fails. The pipeline state is
+ * not updated when the operation fails.
+ */
+static int xvip_pipeline_set_stream(struct xvip_pipeline *pipe, bool on)
+{
+	int ret = 0;
+
+	mutex_lock(&pipe->lock);
+
+	if (on) {
+		if (pipe->stream_count == pipe->num_dmas - 1) {
+			ret = xvip_pipeline_start_stop(pipe, true);
+			if (ret < 0)
+				goto done;
+		}
+		pipe->stream_count++;
+	} else {
+		if (--pipe->stream_count == 0)
+			xvip_pipeline_start_stop(pipe, false);
+	}
+
+done:
+	mutex_unlock(&pipe->lock);
+	return ret;
+}
+
+static int xvip_pipeline_validate(struct xvip_pipeline *pipe,
+				  struct xvip_dma *start)
+{
+	struct media_entity_graph graph;
+	struct media_entity *entity = &start->video.entity;
+	struct media_device *mdev = entity->parent;
+	unsigned int num_inputs = 0;
+	unsigned int num_outputs = 0;
+
+	mutex_lock(&mdev->graph_mutex);
+
+	/* Walk the graph to locate the video nodes. */
+	media_entity_graph_walk_start(&graph, entity);
+
+	while ((entity = media_entity_graph_walk_next(&graph))) {
+		struct xvip_dma *dma;
+
+		if (entity->type != MEDIA_ENT_T_DEVNODE_V4L)
+			continue;
+
+		dma = to_xvip_dma(media_entity_to_video_device(entity));
+
+		if (dma->pad.flags & MEDIA_PAD_FL_SINK) {
+			pipe->output = dma;
+			num_outputs++;
+		} else {
+			num_inputs++;
+		}
+	}
+
+	mutex_unlock(&mdev->graph_mutex);
+
+	/* We need exactly one output and zero or one input. */
+	if (num_outputs != 1 || num_inputs > 1)
+		return -EPIPE;
+
+	pipe->num_dmas = num_inputs + num_outputs;
+
+	return 0;
+}
+
+static void __xvip_pipeline_cleanup(struct xvip_pipeline *pipe)
+{
+	pipe->num_dmas = 0;
+	pipe->output = NULL;
+}
+
+/**
+ * xvip_pipeline_cleanup - Cleanup the pipeline after streaming
+ * @pipe: the pipeline
+ *
+ * Decrease the pipeline use count and clean it up if we were the last user.
+ */
+static void xvip_pipeline_cleanup(struct xvip_pipeline *pipe)
+{
+	mutex_lock(&pipe->lock);
+
+	/* If we're the last user clean up the pipeline. */
+	if (--pipe->use_count == 0)
+		__xvip_pipeline_cleanup(pipe);
+
+	mutex_unlock(&pipe->lock);
+}
+
+/**
+ * xvip_pipeline_prepare - Prepare the pipeline for streaming
+ * @pipe: the pipeline
+ * @dma: DMA engine at one end of the pipeline
+ *
+ * Validate the pipeline if no user exists yet, otherwise just increase the use
+ * count.
+ *
+ * Return: 0 if successful or -EPIPE if the pipeline is not valid.
+ */
+static int xvip_pipeline_prepare(struct xvip_pipeline *pipe,
+				 struct xvip_dma *dma)
+{
+	int ret;
+
+	mutex_lock(&pipe->lock);
+
+	/* If we're the first user validate and initialize the pipeline. */
+	if (pipe->use_count == 0) {
+		ret = xvip_pipeline_validate(pipe, dma);
+		if (ret < 0) {
+			__xvip_pipeline_cleanup(pipe);
+			goto done;
+		}
+	}
+
+	pipe->use_count++;
+	ret = 0;
+
+done:
+	mutex_unlock(&pipe->lock);
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * videobuf2 queue operations
+ */
+
+/**
+ * struct xvip_dma_buffer - Video DMA buffer
+ * @buf: vb2 buffer base object
+ * @queue: buffer list entry in the DMA engine queued buffers list
+ * @dma: DMA channel that uses the buffer
+ */
+struct xvip_dma_buffer {
+	struct vb2_buffer buf;
+	struct list_head queue;
+	struct xvip_dma *dma;
+};
+
+#define to_xvip_dma_buffer(vb)	container_of(vb, struct xvip_dma_buffer, buf)
+
+static void xvip_dma_complete(void *param)
+{
+	struct xvip_dma_buffer *buf = param;
+	struct xvip_dma *dma = buf->dma;
+
+	spin_lock(&dma->queued_lock);
+	list_del(&buf->queue);
+	spin_unlock(&dma->queued_lock);
+
+	buf->buf.v4l2_buf.field = V4L2_FIELD_NONE;
+	buf->buf.v4l2_buf.sequence = dma->sequence++;
+	v4l2_get_timestamp(&buf->buf.v4l2_buf.timestamp);
+	vb2_set_plane_payload(&buf->buf, 0, dma->format.sizeimage);
+	vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE);
+}
+
+static int
+xvip_dma_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+		     unsigned int *nbuffers, unsigned int *nplanes,
+		     unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct xvip_dma *dma = vb2_get_drv_priv(vq);
+
+	/* Make sure the image size is large enough. */
+	if (fmt && fmt->fmt.pix.sizeimage < dma->format.sizeimage)
+		return -EINVAL;
+
+	*nplanes = 1;
+
+	sizes[0] = fmt ? fmt->fmt.pix.sizeimage : dma->format.sizeimage;
+	alloc_ctxs[0] = dma->alloc_ctx;
+
+	return 0;
+}
+
+static int xvip_dma_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue);
+	struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vb);
+
+	buf->dma = dma;
+
+	return 0;
+}
+
+static void xvip_dma_buffer_queue(struct vb2_buffer *vb)
+{
+	struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue);
+	struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vb);
+	struct dma_async_tx_descriptor *desc;
+	dma_addr_t addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+	u32 flags;
+
+	if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
+		dma->xt.dir = DMA_DEV_TO_MEM;
+		dma->xt.src_sgl = false;
+		dma->xt.dst_sgl = true;
+		dma->xt.dst_start = addr;
+	} else {
+		flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
+		dma->xt.dir = DMA_MEM_TO_DEV;
+		dma->xt.src_sgl = true;
+		dma->xt.dst_sgl = false;
+		dma->xt.src_start = addr;
+	}
+
+	dma->xt.frame_size = 1;
+	dma->sgl[0].size = dma->format.width * dma->fmtinfo->bpp;
+	dma->sgl[0].icg = dma->format.bytesperline - dma->sgl[0].size;
+	dma->xt.numf = dma->format.height;
+
+	desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags);
+	if (!desc) {
+		dev_err(dma->xdev->dev, "Failed to prepare DMA transfer\n");
+		vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
+		return;
+	}
+	desc->callback = xvip_dma_complete;
+	desc->callback_param = buf;
+
+	spin_lock_irq(&dma->queued_lock);
+	list_add_tail(&buf->queue, &dma->queued_bufs);
+	spin_unlock_irq(&dma->queued_lock);
+
+	dmaengine_submit(desc);
+
+	if (vb2_is_streaming(&dma->queue))
+		dma_async_issue_pending(dma->dma);
+}
+
+static int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct xvip_dma *dma = vb2_get_drv_priv(vq);
+	struct xvip_dma_buffer *buf, *nbuf;
+	struct xvip_pipeline *pipe;
+	int ret;
+
+	dma->sequence = 0;
+
+	/*
+	 * Start streaming on the pipeline. No link touching an entity in the
+	 * pipeline can be activated or deactivated once streaming is started.
+	 *
+	 * Use the pipeline object embedded in the first DMA object that starts
+	 * streaming.
+	 */
+	pipe = dma->video.entity.pipe
+	     ? to_xvip_pipeline(&dma->video.entity) : &dma->pipe;
+
+	ret = media_entity_pipeline_start(&dma->video.entity, &pipe->pipe);
+	if (ret < 0)
+		goto error;
+
+	/* Verify that the configured format matches the output of the
+	 * connected subdev.
+	 */
+	ret = xvip_dma_verify_format(dma);
+	if (ret < 0)
+		goto error_stop;
+
+	ret = xvip_pipeline_prepare(pipe, dma);
+	if (ret < 0)
+		goto error_stop;
+
+	/* Start the DMA engine. This must be done before starting the blocks
+	 * in the pipeline to avoid DMA synchronization issues.
+	 */
+	dma_async_issue_pending(dma->dma);
+
+	/* Start the pipeline. */
+	xvip_pipeline_set_stream(pipe, true);
+
+	return 0;
+
+error_stop:
+	media_entity_pipeline_stop(&dma->video.entity);
+
+error:
+	/* Give back all queued buffers to videobuf2. */
+	spin_lock_irq(&dma->queued_lock);
+	list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) {
+		vb2_buffer_done(&buf->buf, VB2_BUF_STATE_QUEUED);
+		list_del(&buf->queue);
+	}
+	spin_unlock_irq(&dma->queued_lock);
+
+	return ret;
+}
+
+static void xvip_dma_stop_streaming(struct vb2_queue *vq)
+{
+	struct xvip_dma *dma = vb2_get_drv_priv(vq);
+	struct xvip_pipeline *pipe = to_xvip_pipeline(&dma->video.entity);
+	struct xvip_dma_buffer *buf, *nbuf;
+
+	/* Stop the pipeline. */
+	xvip_pipeline_set_stream(pipe, false);
+
+	/* Stop and reset the DMA engine. */
+	dmaengine_terminate_all(dma->dma);
+
+	/* Cleanup the pipeline and mark it as being stopped. */
+	xvip_pipeline_cleanup(pipe);
+	media_entity_pipeline_stop(&dma->video.entity);
+
+	/* Give back all queued buffers to videobuf2. */
+	spin_lock_irq(&dma->queued_lock);
+	list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) {
+		vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
+		list_del(&buf->queue);
+	}
+	spin_unlock_irq(&dma->queued_lock);
+}
+
+static struct vb2_ops xvip_dma_queue_qops = {
+	.queue_setup = xvip_dma_queue_setup,
+	.buf_prepare = xvip_dma_buffer_prepare,
+	.buf_queue = xvip_dma_buffer_queue,
+	.wait_prepare = vb2_ops_wait_prepare,
+	.wait_finish = vb2_ops_wait_finish,
+	.start_streaming = xvip_dma_start_streaming,
+	.stop_streaming = xvip_dma_stop_streaming,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int
+xvip_dma_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+	struct v4l2_fh *vfh = file->private_data;
+	struct xvip_dma *dma = to_xvip_dma(vfh->vdev);
+
+	cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
+			  | dma->xdev->v4l2_caps;
+
+	if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	else
+		cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+
+	strlcpy(cap->driver, "xilinx-vipp", sizeof(cap->driver));
+	strlcpy(cap->card, dma->video.name, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s:%u",
+		 dma->xdev->dev->of_node->name, dma->port);
+
+	return 0;
+}
+
+/* FIXME: without this callback function, some applications are not configured
+ * with correct formats, and it results in frames in wrong format. Whether this
+ * callback needs to be required is not clearly defined, so it should be
+ * clarified through the mailing list.
+ */
+static int
+xvip_dma_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+	struct v4l2_fh *vfh = file->private_data;
+	struct xvip_dma *dma = to_xvip_dma(vfh->vdev);
+
+	if (f->index > 0)
+		return -EINVAL;
+
+	f->pixelformat = dma->format.pixelformat;
+	strlcpy(f->description, dma->fmtinfo->description,
+		sizeof(f->description));
+
+	return 0;
+}
+
+static int
+xvip_dma_get_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+	struct v4l2_fh *vfh = file->private_data;
+	struct xvip_dma *dma = to_xvip_dma(vfh->vdev);
+
+	format->fmt.pix = dma->format;
+
+	return 0;
+}
+
+static void
+__xvip_dma_try_format(struct xvip_dma *dma, struct v4l2_pix_format *pix,
+		      const struct xvip_video_format **fmtinfo)
+{
+	const struct xvip_video_format *info;
+	unsigned int min_width;
+	unsigned int max_width;
+	unsigned int min_bpl;
+	unsigned int max_bpl;
+	unsigned int width;
+	unsigned int align;
+	unsigned int bpl;
+
+	/* Retrieve format information and select the default format if the
+	 * requested format isn't supported.
+	 */
+	info = xvip_get_format_by_fourcc(pix->pixelformat);
+	if (IS_ERR(info))
+		info = xvip_get_format_by_fourcc(XVIP_DMA_DEF_FORMAT);
+
+	pix->pixelformat = info->fourcc;
+	pix->field = V4L2_FIELD_NONE;
+
+	/* The transfer alignment requirements are expressed in bytes. Compute
+	 * the minimum and maximum values, clamp the requested width and convert
+	 * it back to pixels.
+	 */
+	align = lcm(dma->align, info->bpp);
+	min_width = roundup(XVIP_DMA_MIN_WIDTH, align);
+	max_width = rounddown(XVIP_DMA_MAX_WIDTH, align);
+	width = rounddown(pix->width * info->bpp, align);
+
+	pix->width = clamp(width, min_width, max_width) / info->bpp;
+	pix->height = clamp(pix->height, XVIP_DMA_MIN_HEIGHT,
+			    XVIP_DMA_MAX_HEIGHT);
+
+	/* Clamp the requested bytes per line value. If the maximum bytes per
+	 * line value is zero, the module doesn't support user configurable line
+	 * sizes. Override the requested value with the minimum in that case.
+	 */
+	min_bpl = pix->width * info->bpp;
+	max_bpl = rounddown(XVIP_DMA_MAX_WIDTH, dma->align);
+	bpl = rounddown(pix->bytesperline, dma->align);
+
+	pix->bytesperline = clamp(bpl, min_bpl, max_bpl);
+	pix->sizeimage = pix->bytesperline * pix->height;
+
+	if (fmtinfo)
+		*fmtinfo = info;
+}
+
+static int
+xvip_dma_try_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+	struct v4l2_fh *vfh = file->private_data;
+	struct xvip_dma *dma = to_xvip_dma(vfh->vdev);
+
+	__xvip_dma_try_format(dma, &format->fmt.pix, NULL);
+	return 0;
+}
+
+static int
+xvip_dma_set_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+	struct v4l2_fh *vfh = file->private_data;
+	struct xvip_dma *dma = to_xvip_dma(vfh->vdev);
+	const struct xvip_video_format *info;
+
+	__xvip_dma_try_format(dma, &format->fmt.pix, &info);
+
+	if (vb2_is_busy(&dma->queue))
+		return -EBUSY;
+
+	dma->format = format->fmt.pix;
+	dma->fmtinfo = info;
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops xvip_dma_ioctl_ops = {
+	.vidioc_querycap		= xvip_dma_querycap,
+	.vidioc_enum_fmt_vid_cap	= xvip_dma_enum_format,
+	.vidioc_g_fmt_vid_cap		= xvip_dma_get_format,
+	.vidioc_g_fmt_vid_out		= xvip_dma_get_format,
+	.vidioc_s_fmt_vid_cap		= xvip_dma_set_format,
+	.vidioc_s_fmt_vid_out		= xvip_dma_set_format,
+	.vidioc_try_fmt_vid_cap		= xvip_dma_try_format,
+	.vidioc_try_fmt_vid_out		= xvip_dma_try_format,
+	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
+	.vidioc_querybuf		= vb2_ioctl_querybuf,
+	.vidioc_qbuf			= vb2_ioctl_qbuf,
+	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
+	.vidioc_expbuf			= vb2_ioctl_expbuf,
+	.vidioc_streamon		= vb2_ioctl_streamon,
+	.vidioc_streamoff		= vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static const struct v4l2_file_operations xvip_dma_fops = {
+	.owner		= THIS_MODULE,
+	.unlocked_ioctl	= video_ioctl2,
+	.open		= v4l2_fh_open,
+	.release	= vb2_fop_release,
+	.poll		= vb2_fop_poll,
+	.mmap		= vb2_fop_mmap,
+};
+
+/* -----------------------------------------------------------------------------
+ * Xilinx Video DMA Core
+ */
+
+int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma,
+		  enum v4l2_buf_type type, unsigned int port)
+{
+	char name[14];
+	int ret;
+
+	dma->xdev = xdev;
+	dma->port = port;
+	mutex_init(&dma->lock);
+	mutex_init(&dma->pipe.lock);
+	INIT_LIST_HEAD(&dma->queued_bufs);
+	spin_lock_init(&dma->queued_lock);
+
+	dma->fmtinfo = xvip_get_format_by_fourcc(XVIP_DMA_DEF_FORMAT);
+	dma->format.pixelformat = dma->fmtinfo->fourcc;
+	dma->format.colorspace = V4L2_COLORSPACE_SRGB;
+	dma->format.field = V4L2_FIELD_NONE;
+	dma->format.width = XVIP_DMA_DEF_WIDTH;
+	dma->format.height = XVIP_DMA_DEF_HEIGHT;
+	dma->format.bytesperline = dma->format.width * dma->fmtinfo->bpp;
+	dma->format.sizeimage = dma->format.bytesperline * dma->format.height;
+
+	/* Initialize the media entity... */
+	dma->pad.flags = type == V4L2_BUF_TYPE_VIDEO_CAPTURE
+		       ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_init(&dma->video.entity, 1, &dma->pad, 0);
+	if (ret < 0)
+		goto error;
+
+	/* ... and the video node... */
+	dma->video.fops = &xvip_dma_fops;
+	dma->video.v4l2_dev = &xdev->v4l2_dev;
+	dma->video.queue = &dma->queue;
+	snprintf(dma->video.name, sizeof(dma->video.name), "%s %s %u",
+		 xdev->dev->of_node->name,
+		 type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "output" : "input",
+		 port);
+	dma->video.vfl_type = VFL_TYPE_GRABBER;
+	dma->video.vfl_dir = type == V4L2_BUF_TYPE_VIDEO_CAPTURE
+			   ? VFL_DIR_RX : VFL_DIR_TX;
+	dma->video.release = video_device_release_empty;
+	dma->video.ioctl_ops = &xvip_dma_ioctl_ops;
+	dma->video.lock = &dma->lock;
+
+	video_set_drvdata(&dma->video, dma);
+
+	/* ... and the buffers queue... */
+	dma->alloc_ctx = vb2_dma_contig_init_ctx(dma->xdev->dev);
+	if (IS_ERR(dma->alloc_ctx))
+		goto error;
+
+	/* Don't enable VB2_READ and VB2_WRITE, as using the read() and write()
+	 * V4L2 APIs would be inefficient. Testing on the command line with a
+	 * 'cat /dev/video?' thus won't be possible, but given that the driver
+	 * anyway requires a test tool to setup the pipeline before any video
+	 * stream can be started, requiring a specific V4L2 test tool as well
+	 * instead of 'cat' isn't really a drawback.
+	 */
+	dma->queue.type = type;
+	dma->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	dma->queue.lock = &dma->lock;
+	dma->queue.drv_priv = dma;
+	dma->queue.buf_struct_size = sizeof(struct xvip_dma_buffer);
+	dma->queue.ops = &xvip_dma_queue_qops;
+	dma->queue.mem_ops = &vb2_dma_contig_memops;
+	dma->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
+				   | V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
+	ret = vb2_queue_init(&dma->queue);
+	if (ret < 0) {
+		dev_err(dma->xdev->dev, "failed to initialize VB2 queue\n");
+		goto error;
+	}
+
+	/* ... and the DMA channel. */
+	sprintf(name, "port%u", port);
+	dma->dma = dma_request_slave_channel(dma->xdev->dev, name);
+	if (dma->dma == NULL) {
+		dev_err(dma->xdev->dev, "no VDMA channel found\n");
+		ret = -ENODEV;
+		goto error;
+	}
+
+	dma->align = 1 << dma->dma->device->copy_align;
+
+	ret = video_register_device(&dma->video, VFL_TYPE_GRABBER, -1);
+	if (ret < 0) {
+		dev_err(dma->xdev->dev, "failed to register video device\n");
+		goto error;
+	}
+
+	return 0;
+
+error:
+	xvip_dma_cleanup(dma);
+	return ret;
+}
+
+void xvip_dma_cleanup(struct xvip_dma *dma)
+{
+	if (video_is_registered(&dma->video))
+		video_unregister_device(&dma->video);
+
+	if (dma->dma)
+		dma_release_channel(dma->dma);
+
+	if (!IS_ERR_OR_NULL(dma->alloc_ctx))
+		vb2_dma_contig_cleanup_ctx(dma->alloc_ctx);
+
+	media_entity_cleanup(&dma->video.entity);
+
+	mutex_destroy(&dma->lock);
+	mutex_destroy(&dma->pipe.lock);
+}
diff --git a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h
new file mode 100644
index 0000000..a540111
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-dma.h
@@ -0,0 +1,109 @@
+/*
+ * Xilinx Video DMA
+ *
+ * Copyright (C) 2013-2015 Ideas on Board
+ * Copyright (C) 2013-2015 Xilinx, Inc.
+ *
+ * Contacts: Hyun Kwon <hyun.kwon@xilinx.com>
+ *           Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __XILINX_VIP_DMA_H__
+#define __XILINX_VIP_DMA_H__
+
+#include <linux/dmaengine.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-core.h>
+
+struct dma_chan;
+struct xvip_composite_device;
+struct xvip_video_format;
+
+/**
+ * struct xvip_pipeline - Xilinx Video IP pipeline structure
+ * @pipe: media pipeline
+ * @lock: protects the pipeline @stream_count
+ * @use_count: number of DMA engines using the pipeline
+ * @stream_count: number of DMA engines currently streaming
+ * @num_dmas: number of DMA engines in the pipeline
+ * @output: DMA engine at the output of the pipeline
+ */
+struct xvip_pipeline {
+	struct media_pipeline pipe;
+
+	struct mutex lock;
+	unsigned int use_count;
+	unsigned int stream_count;
+
+	unsigned int num_dmas;
+	struct xvip_dma *output;
+};
+
+static inline struct xvip_pipeline *to_xvip_pipeline(struct media_entity *e)
+{
+	return container_of(e->pipe, struct xvip_pipeline, pipe);
+}
+
+/**
+ * struct xvip_dma - Video DMA channel
+ * @list: list entry in a composite device dmas list
+ * @video: V4L2 video device associated with the DMA channel
+ * @pad: media pad for the video device entity
+ * @xdev: composite device the DMA channel belongs to
+ * @pipe: pipeline belonging to the DMA channel
+ * @port: composite device DT node port number for the DMA channel
+ * @lock: protects the @format, @fmtinfo and @queue fields
+ * @format: active V4L2 pixel format
+ * @fmtinfo: format information corresponding to the active @format
+ * @queue: vb2 buffers queue
+ * @alloc_ctx: allocation context for the vb2 @queue
+ * @sequence: V4L2 buffers sequence number
+ * @queued_bufs: list of queued buffers
+ * @queued_lock: protects the buf_queued list
+ * @dma: DMA engine channel
+ * @align: transfer alignment required by the DMA channel (in bytes)
+ * @xt: dma interleaved template for dma configuration
+ * @sgl: data chunk structure for dma_interleaved_template
+ */
+struct xvip_dma {
+	struct list_head list;
+	struct video_device video;
+	struct media_pad pad;
+
+	struct xvip_composite_device *xdev;
+	struct xvip_pipeline pipe;
+	unsigned int port;
+
+	struct mutex lock;
+	struct v4l2_pix_format format;
+	const struct xvip_video_format *fmtinfo;
+
+	struct vb2_queue queue;
+	void *alloc_ctx;
+	unsigned int sequence;
+
+	struct list_head queued_bufs;
+	spinlock_t queued_lock;
+
+	struct dma_chan *dma;
+	unsigned int align;
+	struct dma_interleaved_template xt;
+	struct data_chunk sgl[1];
+};
+
+#define to_xvip_dma(vdev)	container_of(vdev, struct xvip_dma, video)
+
+int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma,
+		  enum v4l2_buf_type type, unsigned int port);
+void xvip_dma_cleanup(struct xvip_dma *dma);
+
+#endif /* __XILINX_VIP_DMA_H__ */
diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c
new file mode 100644
index 0000000..b5f7d5e
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-tpg.c
@@ -0,0 +1,931 @@
+/*
+ * Xilinx Test Pattern Generator
+ *
+ * Copyright (C) 2013-2015 Ideas on Board
+ * Copyright (C) 2013-2015 Xilinx, Inc.
+ *
+ * Contacts: Hyun Kwon <hyun.kwon@xilinx.com>
+ *           Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/xilinx-v4l2-controls.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "xilinx-vip.h"
+#include "xilinx-vtc.h"
+
+#define XTPG_CTRL_STATUS_SLAVE_ERROR		(1 << 16)
+#define XTPG_CTRL_IRQ_SLAVE_ERROR		(1 << 16)
+
+#define XTPG_PATTERN_CONTROL			0x0100
+#define XTPG_PATTERN_MASK			(0xf << 0)
+#define XTPG_PATTERN_CONTROL_CROSS_HAIRS	(1 << 4)
+#define XTPG_PATTERN_CONTROL_MOVING_BOX		(1 << 5)
+#define XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT	6
+#define XTPG_PATTERN_CONTROL_COLOR_MASK_MASK	(0xf << 6)
+#define XTPG_PATTERN_CONTROL_STUCK_PIXEL	(1 << 9)
+#define XTPG_PATTERN_CONTROL_NOISE		(1 << 10)
+#define XTPG_PATTERN_CONTROL_MOTION		(1 << 12)
+#define XTPG_MOTION_SPEED			0x0104
+#define XTPG_CROSS_HAIRS			0x0108
+#define XTPG_CROSS_HAIRS_ROW_SHIFT		0
+#define XTPG_CROSS_HAIRS_ROW_MASK		(0xfff << 0)
+#define XTPG_CROSS_HAIRS_COLUMN_SHIFT		16
+#define XTPG_CROSS_HAIRS_COLUMN_MASK		(0xfff << 16)
+#define XTPG_ZPLATE_HOR_CONTROL			0x010c
+#define XTPG_ZPLATE_VER_CONTROL			0x0110
+#define XTPG_ZPLATE_START_SHIFT			0
+#define XTPG_ZPLATE_START_MASK			(0xffff << 0)
+#define XTPG_ZPLATE_SPEED_SHIFT			16
+#define XTPG_ZPLATE_SPEED_MASK			(0xffff << 16)
+#define XTPG_BOX_SIZE				0x0114
+#define XTPG_BOX_COLOR				0x0118
+#define XTPG_STUCK_PIXEL_THRESH			0x011c
+#define XTPG_NOISE_GAIN				0x0120
+#define XTPG_BAYER_PHASE			0x0124
+#define XTPG_BAYER_PHASE_RGGB			0
+#define XTPG_BAYER_PHASE_GRBG			1
+#define XTPG_BAYER_PHASE_GBRG			2
+#define XTPG_BAYER_PHASE_BGGR			3
+#define XTPG_BAYER_PHASE_OFF			4
+
+/*
+ * The minimum blanking value is one clock cycle for the front porch, one clock
+ * cycle for the sync pulse and one clock cycle for the back porch.
+ */
+#define XTPG_MIN_HBLANK			3
+#define XTPG_MAX_HBLANK			(XVTC_MAX_HSIZE - XVIP_MIN_WIDTH)
+#define XTPG_MIN_VBLANK			3
+#define XTPG_MAX_VBLANK			(XVTC_MAX_VSIZE - XVIP_MIN_HEIGHT)
+
+/**
+ * struct xtpg_device - Xilinx Test Pattern Generator device structure
+ * @xvip: Xilinx Video IP device
+ * @pads: media pads
+ * @npads: number of pads (1 or 2)
+ * @has_input: whether an input is connected to the sink pad
+ * @formats: active V4L2 media bus format for each pad
+ * @default_format: default V4L2 media bus format
+ * @vip_format: format information corresponding to the active format
+ * @bayer: boolean flag if TPG is set to any bayer format
+ * @ctrl_handler: control handler
+ * @hblank: horizontal blanking control
+ * @vblank: vertical blanking control
+ * @pattern: test pattern control
+ * @streaming: is the video stream active
+ * @vtc: video timing controller
+ * @vtmux_gpio: video timing mux GPIO
+ */
+struct xtpg_device {
+	struct xvip_device xvip;
+
+	struct media_pad pads[2];
+	unsigned int npads;
+	bool has_input;
+
+	struct v4l2_mbus_framefmt formats[2];
+	struct v4l2_mbus_framefmt default_format;
+	const struct xvip_video_format *vip_format;
+	bool bayer;
+
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *pattern;
+	bool streaming;
+
+	struct xvtc_device *vtc;
+	struct gpio_desc *vtmux_gpio;
+};
+
+static inline struct xtpg_device *to_tpg(struct v4l2_subdev *subdev)
+{
+	return container_of(subdev, struct xtpg_device, xvip.subdev);
+}
+
+static u32 xtpg_get_bayer_phase(unsigned int code)
+{
+	switch (code) {
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+		return XTPG_BAYER_PHASE_RGGB;
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+		return XTPG_BAYER_PHASE_GRBG;
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+		return XTPG_BAYER_PHASE_GBRG;
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+		return XTPG_BAYER_PHASE_BGGR;
+	default:
+		return XTPG_BAYER_PHASE_OFF;
+	}
+}
+
+static void __xtpg_update_pattern_control(struct xtpg_device *xtpg,
+					  bool passthrough, bool pattern)
+{
+	u32 pattern_mask = (1 << (xtpg->pattern->maximum + 1)) - 1;
+
+	/*
+	 * If the TPG has no sink pad or no input connected to its sink pad
+	 * passthrough mode can't be enabled.
+	 */
+	if (xtpg->npads == 1 || !xtpg->has_input)
+		passthrough = false;
+
+	/* If passthrough mode is allowed unmask bit 0. */
+	if (passthrough)
+		pattern_mask &= ~1;
+
+	/* If test pattern mode is allowed unmask all other bits. */
+	if (pattern)
+		pattern_mask &= 1;
+
+	__v4l2_ctrl_modify_range(xtpg->pattern, 0, xtpg->pattern->maximum,
+				 pattern_mask, pattern ? 9 : 0);
+}
+
+static void xtpg_update_pattern_control(struct xtpg_device *xtpg,
+					bool passthrough, bool pattern)
+{
+	mutex_lock(xtpg->ctrl_handler.lock);
+	__xtpg_update_pattern_control(xtpg, passthrough, pattern);
+	mutex_unlock(xtpg->ctrl_handler.lock);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Video Operations
+ */
+
+static int xtpg_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+	struct xtpg_device *xtpg = to_tpg(subdev);
+	unsigned int width = xtpg->formats[0].width;
+	unsigned int height = xtpg->formats[0].height;
+	bool passthrough;
+	u32 bayer_phase;
+
+	if (!enable) {
+		xvip_stop(&xtpg->xvip);
+		if (xtpg->vtc)
+			xvtc_generator_stop(xtpg->vtc);
+
+		xtpg_update_pattern_control(xtpg, true, true);
+		xtpg->streaming = false;
+		return 0;
+	}
+
+	xvip_set_frame_size(&xtpg->xvip, &xtpg->formats[0]);
+
+	if (xtpg->vtc) {
+		struct xvtc_config config = {
+			.hblank_start = width,
+			.hsync_start = width + 1,
+			.vblank_start = height,
+			.vsync_start = height + 1,
+		};
+		unsigned int htotal;
+		unsigned int vtotal;
+
+		htotal = min_t(unsigned int, XVTC_MAX_HSIZE,
+			       v4l2_ctrl_g_ctrl(xtpg->hblank) + width);
+		vtotal = min_t(unsigned int, XVTC_MAX_VSIZE,
+			       v4l2_ctrl_g_ctrl(xtpg->vblank) + height);
+
+		config.hsync_end = htotal - 1;
+		config.hsize = htotal;
+		config.vsync_end = vtotal - 1;
+		config.vsize = vtotal;
+
+		xvtc_generator_start(xtpg->vtc, &config);
+	}
+
+	/*
+	 * Configure the bayer phase and video timing mux based on the
+	 * operation mode (passthrough or test pattern generation). The test
+	 * pattern can be modified by the control set handler, we thus need to
+	 * take the control lock here to avoid races.
+	 */
+	mutex_lock(xtpg->ctrl_handler.lock);
+
+	xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
+			 XTPG_PATTERN_MASK, xtpg->pattern->cur.val);
+
+	/*
+	 * Switching between passthrough and test pattern generation modes isn't
+	 * allowed during streaming, update the control range accordingly.
+	 */
+	passthrough = xtpg->pattern->cur.val == 0;
+	__xtpg_update_pattern_control(xtpg, passthrough, !passthrough);
+
+	xtpg->streaming = true;
+
+	mutex_unlock(xtpg->ctrl_handler.lock);
+
+	/*
+	 * For TPG v5.0, the bayer phase needs to be off for the pass through
+	 * mode, otherwise the external input would be subsampled.
+	 */
+	bayer_phase = passthrough ? XTPG_BAYER_PHASE_OFF
+		    : xtpg_get_bayer_phase(xtpg->formats[0].code);
+	xvip_write(&xtpg->xvip, XTPG_BAYER_PHASE, bayer_phase);
+
+	if (xtpg->vtmux_gpio)
+		gpiod_set_value_cansleep(xtpg->vtmux_gpio, !passthrough);
+
+	xvip_start(&xtpg->xvip);
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+static struct v4l2_mbus_framefmt *
+__xtpg_get_pad_format(struct xtpg_device *xtpg,
+		      struct v4l2_subdev_pad_config *cfg,
+		      unsigned int pad, u32 which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &xtpg->formats[pad];
+	default:
+		return NULL;
+	}
+}
+
+static int xtpg_get_format(struct v4l2_subdev *subdev,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct xtpg_device *xtpg = to_tpg(subdev);
+
+	fmt->format = *__xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which);
+
+	return 0;
+}
+
+static int xtpg_set_format(struct v4l2_subdev *subdev,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct xtpg_device *xtpg = to_tpg(subdev);
+	struct v4l2_mbus_framefmt *__format;
+	u32 bayer_phase;
+
+	__format = __xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which);
+
+	/* In two pads mode the source pad format is always identical to the
+	 * sink pad format.
+	 */
+	if (xtpg->npads == 2 && fmt->pad == 1) {
+		fmt->format = *__format;
+		return 0;
+	}
+
+	/* Bayer phase is configurable at runtime */
+	if (xtpg->bayer) {
+		bayer_phase = xtpg_get_bayer_phase(fmt->format.code);
+		if (bayer_phase != XTPG_BAYER_PHASE_OFF)
+			__format->code = fmt->format.code;
+	}
+
+	xvip_set_format_size(__format, fmt);
+
+	fmt->format = *__format;
+
+	/* Propagate the format to the source pad. */
+	if (xtpg->npads == 2) {
+		__format = __xtpg_get_pad_format(xtpg, cfg, 1, fmt->which);
+		*__format = fmt->format;
+	}
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static int xtpg_enum_frame_size(struct v4l2_subdev *subdev,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct v4l2_mbus_framefmt *format;
+
+	format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad);
+
+	if (fse->index || fse->code != format->code)
+		return -EINVAL;
+
+	/* Min / max values for pad 0 is always fixed in both one and two pads
+	 * modes. In two pads mode, the source pad(= 1) size is identical to
+	 * the sink pad size */
+	if (fse->pad == 0) {
+		fse->min_width = XVIP_MIN_WIDTH;
+		fse->max_width = XVIP_MAX_WIDTH;
+		fse->min_height = XVIP_MIN_HEIGHT;
+		fse->max_height = XVIP_MAX_HEIGHT;
+	} else {
+		fse->min_width = format->width;
+		fse->max_width = format->width;
+		fse->min_height = format->height;
+		fse->max_height = format->height;
+	}
+
+	return 0;
+}
+
+static int xtpg_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+	struct xtpg_device *xtpg = to_tpg(subdev);
+	struct v4l2_mbus_framefmt *format;
+
+	format = v4l2_subdev_get_try_format(subdev, fh->pad, 0);
+	*format = xtpg->default_format;
+
+	if (xtpg->npads == 2) {
+		format = v4l2_subdev_get_try_format(subdev, fh->pad, 1);
+		*format = xtpg->default_format;
+	}
+
+	return 0;
+}
+
+static int xtpg_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+	return 0;
+}
+
+static int xtpg_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct xtpg_device *xtpg = container_of(ctrl->handler,
+						struct xtpg_device,
+						ctrl_handler);
+	switch (ctrl->id) {
+	case V4L2_CID_TEST_PATTERN:
+		xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
+				 XTPG_PATTERN_MASK, ctrl->val);
+		return 0;
+	case V4L2_CID_XILINX_TPG_CROSS_HAIRS:
+		xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
+				XTPG_PATTERN_CONTROL_CROSS_HAIRS, ctrl->val);
+		return 0;
+	case V4L2_CID_XILINX_TPG_MOVING_BOX:
+		xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
+				XTPG_PATTERN_CONTROL_MOVING_BOX, ctrl->val);
+		return 0;
+	case V4L2_CID_XILINX_TPG_COLOR_MASK:
+		xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
+				 XTPG_PATTERN_CONTROL_COLOR_MASK_MASK,
+				 ctrl->val <<
+				 XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT);
+		return 0;
+	case V4L2_CID_XILINX_TPG_STUCK_PIXEL:
+		xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
+				XTPG_PATTERN_CONTROL_STUCK_PIXEL, ctrl->val);
+		return 0;
+	case V4L2_CID_XILINX_TPG_NOISE:
+		xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
+				XTPG_PATTERN_CONTROL_NOISE, ctrl->val);
+		return 0;
+	case V4L2_CID_XILINX_TPG_MOTION:
+		xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
+				XTPG_PATTERN_CONTROL_MOTION, ctrl->val);
+		return 0;
+	case V4L2_CID_XILINX_TPG_MOTION_SPEED:
+		xvip_write(&xtpg->xvip, XTPG_MOTION_SPEED, ctrl->val);
+		return 0;
+	case V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW:
+		xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS,
+				 XTPG_CROSS_HAIRS_ROW_MASK,
+				 ctrl->val << XTPG_CROSS_HAIRS_ROW_SHIFT);
+		return 0;
+	case V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN:
+		xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS,
+				 XTPG_CROSS_HAIRS_COLUMN_MASK,
+				 ctrl->val << XTPG_CROSS_HAIRS_COLUMN_SHIFT);
+		return 0;
+	case V4L2_CID_XILINX_TPG_ZPLATE_HOR_START:
+		xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL,
+				 XTPG_ZPLATE_START_MASK,
+				 ctrl->val << XTPG_ZPLATE_START_SHIFT);
+		return 0;
+	case V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED:
+		xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL,
+				 XTPG_ZPLATE_SPEED_MASK,
+				 ctrl->val << XTPG_ZPLATE_SPEED_SHIFT);
+		return 0;
+	case V4L2_CID_XILINX_TPG_ZPLATE_VER_START:
+		xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL,
+				 XTPG_ZPLATE_START_MASK,
+				 ctrl->val << XTPG_ZPLATE_START_SHIFT);
+		return 0;
+	case V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED:
+		xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL,
+				 XTPG_ZPLATE_SPEED_MASK,
+				 ctrl->val << XTPG_ZPLATE_SPEED_SHIFT);
+		return 0;
+	case V4L2_CID_XILINX_TPG_BOX_SIZE:
+		xvip_write(&xtpg->xvip, XTPG_BOX_SIZE, ctrl->val);
+		return 0;
+	case V4L2_CID_XILINX_TPG_BOX_COLOR:
+		xvip_write(&xtpg->xvip, XTPG_BOX_COLOR, ctrl->val);
+		return 0;
+	case V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH:
+		xvip_write(&xtpg->xvip, XTPG_STUCK_PIXEL_THRESH, ctrl->val);
+		return 0;
+	case V4L2_CID_XILINX_TPG_NOISE_GAIN:
+		xvip_write(&xtpg->xvip, XTPG_NOISE_GAIN, ctrl->val);
+		return 0;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops xtpg_ctrl_ops = {
+	.s_ctrl	= xtpg_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops xtpg_core_ops = {
+};
+
+static struct v4l2_subdev_video_ops xtpg_video_ops = {
+	.s_stream = xtpg_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops xtpg_pad_ops = {
+	.enum_mbus_code		= xvip_enum_mbus_code,
+	.enum_frame_size	= xtpg_enum_frame_size,
+	.get_fmt		= xtpg_get_format,
+	.set_fmt		= xtpg_set_format,
+};
+
+static struct v4l2_subdev_ops xtpg_ops = {
+	.core   = &xtpg_core_ops,
+	.video  = &xtpg_video_ops,
+	.pad    = &xtpg_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops xtpg_internal_ops = {
+	.open	= xtpg_open,
+	.close	= xtpg_close,
+};
+
+/*
+ * Control Config
+ */
+
+static const char *const xtpg_pattern_strings[] = {
+	"Passthrough",
+	"Horizontal Ramp",
+	"Vertical Ramp",
+	"Temporal Ramp",
+	"Solid Red",
+	"Solid Green",
+	"Solid Blue",
+	"Solid Black",
+	"Solid White",
+	"Color Bars",
+	"Zone Plate",
+	"Tartan Color Bars",
+	"Cross Hatch",
+	"None",
+	"Vertical/Horizontal Ramps",
+	"Black/White Checker Board",
+};
+
+static struct v4l2_ctrl_config xtpg_ctrls[] = {
+	{
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_CROSS_HAIRS,
+		.name	= "Test Pattern: Cross Hairs",
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.min	= false,
+		.max	= true,
+		.step	= 1,
+		.def	= 0,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_MOVING_BOX,
+		.name	= "Test Pattern: Moving Box",
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.min	= false,
+		.max	= true,
+		.step	= 1,
+		.def	= 0,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_COLOR_MASK,
+		.name	= "Test Pattern: Color Mask",
+		.type	= V4L2_CTRL_TYPE_BITMASK,
+		.min	= 0,
+		.max	= 0xf,
+		.def	= 0,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_STUCK_PIXEL,
+		.name	= "Test Pattern: Stuck Pixel",
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.min	= false,
+		.max	= true,
+		.step	= 1,
+		.def	= 0,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_NOISE,
+		.name	= "Test Pattern: Noise",
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.min	= false,
+		.max	= true,
+		.step	= 1,
+		.def	= 0,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_MOTION,
+		.name	= "Test Pattern: Motion",
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.min	= false,
+		.max	= true,
+		.step	= 1,
+		.def	= 0,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_MOTION_SPEED,
+		.name	= "Test Pattern: Motion Speed",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= (1 << 8) - 1,
+		.step	= 1,
+		.def	= 4,
+		.flags	= V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW,
+		.name	= "Test Pattern: Cross Hairs Row",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= (1 << 12) - 1,
+		.step	= 1,
+		.def	= 0x64,
+		.flags	= V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN,
+		.name	= "Test Pattern: Cross Hairs Column",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= (1 << 12) - 1,
+		.step	= 1,
+		.def	= 0x64,
+		.flags	= V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_ZPLATE_HOR_START,
+		.name	= "Test Pattern: Zplate Horizontal Start Pos",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= (1 << 16) - 1,
+		.step	= 1,
+		.def	= 0x1e,
+		.flags	= V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED,
+		.name	= "Test Pattern: Zplate Horizontal Speed",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= (1 << 16) - 1,
+		.step	= 1,
+		.def	= 0,
+		.flags	= V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_ZPLATE_VER_START,
+		.name	= "Test Pattern: Zplate Vertical Start Pos",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= (1 << 16) - 1,
+		.step	= 1,
+		.def	= 1,
+		.flags	= V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED,
+		.name	= "Test Pattern: Zplate Vertical Speed",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= (1 << 16) - 1,
+		.step	= 1,
+		.def	= 0,
+		.flags	= V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_BOX_SIZE,
+		.name	= "Test Pattern: Box Size",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= (1 << 12) - 1,
+		.step	= 1,
+		.def	= 0x32,
+		.flags	= V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_BOX_COLOR,
+		.name	= "Test Pattern: Box Color(RGB)",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= (1 << 24) - 1,
+		.step	= 1,
+		.def	= 0,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH,
+		.name	= "Test Pattern: Stuck Pixel threshold",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= (1 << 16) - 1,
+		.step	= 1,
+		.def	= 0,
+		.flags	= V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops	= &xtpg_ctrl_ops,
+		.id	= V4L2_CID_XILINX_TPG_NOISE_GAIN,
+		.name	= "Test Pattern: Noise Gain",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= (1 << 8) - 1,
+		.step	= 1,
+		.def	= 0,
+		.flags	= V4L2_CTRL_FLAG_SLIDER,
+	},
+};
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+
+static const struct media_entity_operations xtpg_media_ops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power Management
+ */
+
+static int __maybe_unused xtpg_pm_suspend(struct device *dev)
+{
+	struct xtpg_device *xtpg = dev_get_drvdata(dev);
+
+	xvip_suspend(&xtpg->xvip);
+
+	return 0;
+}
+
+static int __maybe_unused xtpg_pm_resume(struct device *dev)
+{
+	struct xtpg_device *xtpg = dev_get_drvdata(dev);
+
+	xvip_resume(&xtpg->xvip);
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Platform Device Driver
+ */
+
+static int xtpg_parse_of(struct xtpg_device *xtpg)
+{
+	struct device *dev = xtpg->xvip.dev;
+	struct device_node *node = xtpg->xvip.dev->of_node;
+	struct device_node *ports;
+	struct device_node *port;
+	unsigned int nports = 0;
+	bool has_endpoint = false;
+
+	ports = of_get_child_by_name(node, "ports");
+	if (ports == NULL)
+		ports = node;
+
+	for_each_child_of_node(ports, port) {
+		const struct xvip_video_format *format;
+		struct device_node *endpoint;
+
+		if (!port->name || of_node_cmp(port->name, "port"))
+			continue;
+
+		format = xvip_of_get_format(port);
+		if (IS_ERR(format)) {
+			dev_err(dev, "invalid format in DT");
+			return PTR_ERR(format);
+		}
+
+		/* Get and check the format description */
+		if (!xtpg->vip_format) {
+			xtpg->vip_format = format;
+		} else if (xtpg->vip_format != format) {
+			dev_err(dev, "in/out format mismatch in DT");
+			return -EINVAL;
+		}
+
+		if (nports == 0) {
+			endpoint = of_get_next_child(port, NULL);
+			if (endpoint)
+				has_endpoint = true;
+			of_node_put(endpoint);
+		}
+
+		/* Count the number of ports. */
+		nports++;
+	}
+
+	if (nports != 1 && nports != 2) {
+		dev_err(dev, "invalid number of ports %u\n", nports);
+		return -EINVAL;
+	}
+
+	xtpg->npads = nports;
+	if (nports == 2 && has_endpoint)
+		xtpg->has_input = true;
+
+	return 0;
+}
+
+static int xtpg_probe(struct platform_device *pdev)
+{
+	struct v4l2_subdev *subdev;
+	struct xtpg_device *xtpg;
+	u32 i, bayer_phase;
+	int ret;
+
+	xtpg = devm_kzalloc(&pdev->dev, sizeof(*xtpg), GFP_KERNEL);
+	if (!xtpg)
+		return -ENOMEM;
+
+	xtpg->xvip.dev = &pdev->dev;
+
+	ret = xtpg_parse_of(xtpg);
+	if (ret < 0)
+		return ret;
+
+	ret = xvip_init_resources(&xtpg->xvip);
+	if (ret < 0)
+		return ret;
+
+	xtpg->vtmux_gpio = devm_gpiod_get_optional(&pdev->dev, "timing",
+						   GPIOD_OUT_HIGH);
+	if (IS_ERR(xtpg->vtmux_gpio)) {
+		ret = PTR_ERR(xtpg->vtmux_gpio);
+		goto error_resource;
+	}
+
+	xtpg->vtc = xvtc_of_get(pdev->dev.of_node);
+	if (IS_ERR(xtpg->vtc)) {
+		ret = PTR_ERR(xtpg->vtc);
+		goto error_resource;
+	}
+
+	/* Reset and initialize the core */
+	xvip_reset(&xtpg->xvip);
+
+	/* Initialize V4L2 subdevice and media entity. Pad numbers depend on the
+	 * number of pads.
+	 */
+	if (xtpg->npads == 2) {
+		xtpg->pads[0].flags = MEDIA_PAD_FL_SINK;
+		xtpg->pads[1].flags = MEDIA_PAD_FL_SOURCE;
+	} else {
+		xtpg->pads[0].flags = MEDIA_PAD_FL_SOURCE;
+	}
+
+	/* Initialize the default format */
+	xtpg->default_format.code = xtpg->vip_format->code;
+	xtpg->default_format.field = V4L2_FIELD_NONE;
+	xtpg->default_format.colorspace = V4L2_COLORSPACE_SRGB;
+	xvip_get_frame_size(&xtpg->xvip, &xtpg->default_format);
+
+	bayer_phase = xtpg_get_bayer_phase(xtpg->vip_format->code);
+	if (bayer_phase != XTPG_BAYER_PHASE_OFF)
+		xtpg->bayer = true;
+
+	xtpg->formats[0] = xtpg->default_format;
+	if (xtpg->npads == 2)
+		xtpg->formats[1] = xtpg->default_format;
+
+	/* Initialize V4L2 subdevice and media entity */
+	subdev = &xtpg->xvip.subdev;
+	v4l2_subdev_init(subdev, &xtpg_ops);
+	subdev->dev = &pdev->dev;
+	subdev->internal_ops = &xtpg_internal_ops;
+	strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
+	v4l2_set_subdevdata(subdev, xtpg);
+	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	subdev->entity.ops = &xtpg_media_ops;
+
+	ret = media_entity_init(&subdev->entity, xtpg->npads, xtpg->pads, 0);
+	if (ret < 0)
+		goto error;
+
+	v4l2_ctrl_handler_init(&xtpg->ctrl_handler, 3 + ARRAY_SIZE(xtpg_ctrls));
+
+	xtpg->vblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops,
+					 V4L2_CID_VBLANK, XTPG_MIN_VBLANK,
+					 XTPG_MAX_VBLANK, 1, 100);
+	xtpg->hblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops,
+					 V4L2_CID_HBLANK, XTPG_MIN_HBLANK,
+					 XTPG_MAX_HBLANK, 1, 100);
+	xtpg->pattern = v4l2_ctrl_new_std_menu_items(&xtpg->ctrl_handler,
+					&xtpg_ctrl_ops, V4L2_CID_TEST_PATTERN,
+					ARRAY_SIZE(xtpg_pattern_strings) - 1,
+					1, 9, xtpg_pattern_strings);
+
+	for (i = 0; i < ARRAY_SIZE(xtpg_ctrls); i++)
+		v4l2_ctrl_new_custom(&xtpg->ctrl_handler, &xtpg_ctrls[i], NULL);
+
+	if (xtpg->ctrl_handler.error) {
+		dev_err(&pdev->dev, "failed to add controls\n");
+		ret = xtpg->ctrl_handler.error;
+		goto error;
+	}
+	subdev->ctrl_handler = &xtpg->ctrl_handler;
+
+	xtpg_update_pattern_control(xtpg, true, true);
+
+	ret = v4l2_ctrl_handler_setup(&xtpg->ctrl_handler);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to set controls\n");
+		goto error;
+	}
+
+	platform_set_drvdata(pdev, xtpg);
+
+	xvip_print_version(&xtpg->xvip);
+
+	ret = v4l2_async_register_subdev(subdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register subdev\n");
+		goto error;
+	}
+
+	return 0;
+
+error:
+	v4l2_ctrl_handler_free(&xtpg->ctrl_handler);
+	media_entity_cleanup(&subdev->entity);
+	xvtc_put(xtpg->vtc);
+error_resource:
+	xvip_cleanup_resources(&xtpg->xvip);
+	return ret;
+}
+
+static int xtpg_remove(struct platform_device *pdev)
+{
+	struct xtpg_device *xtpg = platform_get_drvdata(pdev);
+	struct v4l2_subdev *subdev = &xtpg->xvip.subdev;
+
+	v4l2_async_unregister_subdev(subdev);
+	v4l2_ctrl_handler_free(&xtpg->ctrl_handler);
+	media_entity_cleanup(&subdev->entity);
+
+	xvip_cleanup_resources(&xtpg->xvip);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(xtpg_pm_ops, xtpg_pm_suspend, xtpg_pm_resume);
+
+static const struct of_device_id xtpg_of_id_table[] = {
+	{ .compatible = "xlnx,v-tpg-5.0" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, xtpg_of_id_table);
+
+static struct platform_driver xtpg_driver = {
+	.driver = {
+		.name		= "xilinx-tpg",
+		.pm		= &xtpg_pm_ops,
+		.of_match_table	= xtpg_of_id_table,
+	},
+	.probe			= xtpg_probe,
+	.remove			= xtpg_remove,
+};
+
+module_platform_driver(xtpg_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Xilinx Test Pattern Generator Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/xilinx/xilinx-vip.c b/drivers/media/platform/xilinx/xilinx-vip.c
new file mode 100644
index 0000000..3112591
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-vip.c
@@ -0,0 +1,323 @@
+/*
+ * Xilinx Video IP Core
+ *
+ * Copyright (C) 2013-2015 Ideas on Board
+ * Copyright (C) 2013-2015 Xilinx, Inc.
+ *
+ * Contacts: Hyun Kwon <hyun.kwon@xilinx.com>
+ *           Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/media/xilinx-vip.h>
+
+#include "xilinx-vip.h"
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+static const struct xvip_video_format xvip_video_formats[] = {
+	{ XVIP_VF_YUV_422, 8, NULL, MEDIA_BUS_FMT_UYVY8_1X16,
+	  2, V4L2_PIX_FMT_YUYV, "4:2:2, packed, YUYV" },
+	{ XVIP_VF_YUV_444, 8, NULL, MEDIA_BUS_FMT_VUY8_1X24,
+	  3, V4L2_PIX_FMT_YUV444, "4:4:4, packed, YUYV" },
+	{ XVIP_VF_RBG, 8, NULL, MEDIA_BUS_FMT_RBG888_1X24,
+	  3, 0, NULL },
+	{ XVIP_VF_MONO_SENSOR, 8, "mono", MEDIA_BUS_FMT_Y8_1X8,
+	  1, V4L2_PIX_FMT_GREY, "Greyscale 8-bit" },
+	{ XVIP_VF_MONO_SENSOR, 8, "rggb", MEDIA_BUS_FMT_SRGGB8_1X8,
+	  1, V4L2_PIX_FMT_SGRBG8, "Bayer 8-bit RGGB" },
+	{ XVIP_VF_MONO_SENSOR, 8, "grbg", MEDIA_BUS_FMT_SGRBG8_1X8,
+	  1, V4L2_PIX_FMT_SGRBG8, "Bayer 8-bit GRBG" },
+	{ XVIP_VF_MONO_SENSOR, 8, "gbrg", MEDIA_BUS_FMT_SGBRG8_1X8,
+	  1, V4L2_PIX_FMT_SGBRG8, "Bayer 8-bit GBRG" },
+	{ XVIP_VF_MONO_SENSOR, 8, "bggr", MEDIA_BUS_FMT_SBGGR8_1X8,
+	  1, V4L2_PIX_FMT_SBGGR8, "Bayer 8-bit BGGR" },
+};
+
+/**
+ * xvip_get_format_by_code - Retrieve format information for a media bus code
+ * @code: the format media bus code
+ *
+ * Return: a pointer to the format information structure corresponding to the
+ * given V4L2 media bus format @code, or ERR_PTR if no corresponding format can
+ * be found.
+ */
+const struct xvip_video_format *xvip_get_format_by_code(unsigned int code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) {
+		const struct xvip_video_format *format = &xvip_video_formats[i];
+
+		if (format->code == code)
+			return format;
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(xvip_get_format_by_code);
+
+/**
+ * xvip_get_format_by_fourcc - Retrieve format information for a 4CC
+ * @fourcc: the format 4CC
+ *
+ * Return: a pointer to the format information structure corresponding to the
+ * given V4L2 format @fourcc, or ERR_PTR if no corresponding format can be
+ * found.
+ */
+const struct xvip_video_format *xvip_get_format_by_fourcc(u32 fourcc)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) {
+		const struct xvip_video_format *format = &xvip_video_formats[i];
+
+		if (format->fourcc == fourcc)
+			return format;
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(xvip_get_format_by_fourcc);
+
+/**
+ * xvip_of_get_format - Parse a device tree node and return format information
+ * @node: the device tree node
+ *
+ * Read the xlnx,video-format, xlnx,video-width and xlnx,cfa-pattern properties
+ * from the device tree @node passed as an argument and return the corresponding
+ * format information.
+ *
+ * Return: a pointer to the format information structure corresponding to the
+ * format name and width, or ERR_PTR if no corresponding format can be found.
+ */
+const struct xvip_video_format *xvip_of_get_format(struct device_node *node)
+{
+	const char *pattern = "mono";
+	unsigned int vf_code;
+	unsigned int i;
+	u32 width;
+	int ret;
+
+	ret = of_property_read_u32(node, "xlnx,video-format", &vf_code);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	ret = of_property_read_u32(node, "xlnx,video-width", &width);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	if (vf_code == XVIP_VF_MONO_SENSOR)
+		of_property_read_string(node, "xlnx,cfa-pattern", &pattern);
+
+	for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) {
+		const struct xvip_video_format *format = &xvip_video_formats[i];
+
+		if (format->vf_code != vf_code || format->width != width)
+			continue;
+
+		if (vf_code == XVIP_VF_MONO_SENSOR &&
+		    strcmp(pattern, format->pattern))
+			continue;
+
+		return format;
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(xvip_of_get_format);
+
+/**
+ * xvip_set_format_size - Set the media bus frame format size
+ * @format: V4L2 frame format on media bus
+ * @fmt: media bus format
+ *
+ * Set the media bus frame format size. The width / height from the subdevice
+ * format are set to the given media bus format. The new format size is stored
+ * in @format. The width and height are clamped using default min / max values.
+ */
+void xvip_set_format_size(struct v4l2_mbus_framefmt *format,
+			  const struct v4l2_subdev_format *fmt)
+{
+	format->width = clamp_t(unsigned int, fmt->format.width,
+				XVIP_MIN_WIDTH, XVIP_MAX_WIDTH);
+	format->height = clamp_t(unsigned int, fmt->format.height,
+			 XVIP_MIN_HEIGHT, XVIP_MAX_HEIGHT);
+}
+EXPORT_SYMBOL_GPL(xvip_set_format_size);
+
+/**
+ * xvip_clr_or_set - Clear or set the register with a bitmask
+ * @xvip: Xilinx Video IP device
+ * @addr: address of register
+ * @mask: bitmask to be set or cleared
+ * @set: boolean flag indicating whether to set or clear
+ *
+ * Clear or set the register at address @addr with a bitmask @mask depending on
+ * the boolean flag @set. When the flag @set is true, the bitmask is set in
+ * the register, otherwise the bitmask is cleared from the register
+ * when the flag @set is false.
+ *
+ * Fox eample, this function can be used to set a control with a boolean value
+ * requested by users. If the caller knows whether to set or clear in the first
+ * place, the caller should call xvip_clr() or xvip_set() directly instead of
+ * using this function.
+ */
+void xvip_clr_or_set(struct xvip_device *xvip, u32 addr, u32 mask, bool set)
+{
+	u32 reg;
+
+	reg = xvip_read(xvip, addr);
+	reg = set ? reg | mask : reg & ~mask;
+	xvip_write(xvip, addr, reg);
+}
+EXPORT_SYMBOL_GPL(xvip_clr_or_set);
+
+/**
+ * xvip_clr_and_set - Clear and set the register with a bitmask
+ * @xvip: Xilinx Video IP device
+ * @addr: address of register
+ * @clr: bitmask to be cleared
+ * @set: bitmask to be set
+ *
+ * Clear a bit(s) of mask @clr in the register at address @addr, then set
+ * a bit(s) of mask @set in the register after.
+ */
+void xvip_clr_and_set(struct xvip_device *xvip, u32 addr, u32 clr, u32 set)
+{
+	u32 reg;
+
+	reg = xvip_read(xvip, addr);
+	reg &= ~clr;
+	reg |= set;
+	xvip_write(xvip, addr, reg);
+}
+EXPORT_SYMBOL_GPL(xvip_clr_and_set);
+
+int xvip_init_resources(struct xvip_device *xvip)
+{
+	struct platform_device *pdev = to_platform_device(xvip->dev);
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xvip->iomem = devm_ioremap_resource(xvip->dev, res);
+	if (IS_ERR(xvip->iomem))
+		return PTR_ERR(xvip->iomem);
+
+	xvip->clk = devm_clk_get(xvip->dev, NULL);
+	if (IS_ERR(xvip->clk))
+		return PTR_ERR(xvip->clk);
+
+	clk_prepare_enable(xvip->clk);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(xvip_init_resources);
+
+void xvip_cleanup_resources(struct xvip_device *xvip)
+{
+	clk_disable_unprepare(xvip->clk);
+}
+EXPORT_SYMBOL_GPL(xvip_cleanup_resources);
+
+/* -----------------------------------------------------------------------------
+ * Subdev operations handlers
+ */
+
+/**
+ * xvip_enum_mbus_code - Enumerate the media format code
+ * @subdev: V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @code: returning media bus code
+ *
+ * Enumerate the media bus code of the subdevice. Return the corresponding
+ * pad format code. This function only works for subdevices with fixed format
+ * on all pads. Subdevices with multiple format should have their own
+ * function to enumerate mbus codes.
+ *
+ * Return: 0 if the media bus code is found, or -EINVAL if the format index
+ * is not valid.
+ */
+int xvip_enum_mbus_code(struct v4l2_subdev *subdev,
+			struct v4l2_subdev_pad_config *cfg,
+			struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct v4l2_mbus_framefmt *format;
+
+	/* Enumerating frame sizes based on the active configuration isn't
+	 * supported yet.
+	 */
+	if (code->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+		return -EINVAL;
+
+	if (code->index)
+		return -EINVAL;
+
+	format = v4l2_subdev_get_try_format(subdev, cfg, code->pad);
+
+	code->code = format->code;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(xvip_enum_mbus_code);
+
+/**
+ * xvip_enum_frame_size - Enumerate the media bus frame size
+ * @subdev: V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fse: returning media bus frame size
+ *
+ * This function is a drop-in implementation of the subdev enum_frame_size pad
+ * operation. It assumes that the subdevice has one sink pad and one source
+ * pad, and that the format on the source pad is always identical to the
+ * format on the sink pad. Entities with different requirements need to
+ * implement their own enum_frame_size handlers.
+ *
+ * Return: 0 if the media bus frame size is found, or -EINVAL
+ * if the index or the code is not valid.
+ */
+int xvip_enum_frame_size(struct v4l2_subdev *subdev,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct v4l2_mbus_framefmt *format;
+
+	/* Enumerating frame sizes based on the active configuration isn't
+	 * supported yet.
+	 */
+	if (fse->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+		return -EINVAL;
+
+	format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad);
+
+	if (fse->index || fse->code != format->code)
+		return -EINVAL;
+
+	if (fse->pad == XVIP_PAD_SINK) {
+		fse->min_width = XVIP_MIN_WIDTH;
+		fse->max_width = XVIP_MAX_WIDTH;
+		fse->min_height = XVIP_MIN_HEIGHT;
+		fse->max_height = XVIP_MAX_HEIGHT;
+	} else {
+		/* The size on the source pad is fixed and always identical to
+		 * the size on the sink pad.
+		 */
+		fse->min_width = format->width;
+		fse->max_width = format->width;
+		fse->min_height = format->height;
+		fse->max_height = format->height;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(xvip_enum_frame_size);
diff --git a/drivers/media/platform/xilinx/xilinx-vip.h b/drivers/media/platform/xilinx/xilinx-vip.h
new file mode 100644
index 0000000..42fee20
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-vip.h
@@ -0,0 +1,238 @@
+/*
+ * Xilinx Video IP Core
+ *
+ * Copyright (C) 2013-2015 Ideas on Board
+ * Copyright (C) 2013-2015 Xilinx, Inc.
+ *
+ * Contacts: Hyun Kwon <hyun.kwon@xilinx.com>
+ *           Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __XILINX_VIP_H__
+#define __XILINX_VIP_H__
+
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+
+struct clk;
+
+/*
+ * Minimum and maximum width and height common to most video IP cores. IP
+ * cores with different requirements must define their own values.
+ */
+#define XVIP_MIN_WIDTH			32
+#define XVIP_MAX_WIDTH			7680
+#define XVIP_MIN_HEIGHT			32
+#define XVIP_MAX_HEIGHT			7680
+
+/*
+ * Pad IDs. IP cores with with multiple inputs or outputs should define
+ * their own values.
+ */
+#define XVIP_PAD_SINK			0
+#define XVIP_PAD_SOURCE			1
+
+/* Xilinx Video IP Control Registers */
+#define XVIP_CTRL_CONTROL			0x0000
+#define XVIP_CTRL_CONTROL_SW_ENABLE		(1 << 0)
+#define XVIP_CTRL_CONTROL_REG_UPDATE		(1 << 1)
+#define XVIP_CTRL_CONTROL_BYPASS		(1 << 4)
+#define XVIP_CTRL_CONTROL_TEST_PATTERN		(1 << 5)
+#define XVIP_CTRL_CONTROL_FRAME_SYNC_RESET	(1 << 30)
+#define XVIP_CTRL_CONTROL_SW_RESET		(1 << 31)
+#define XVIP_CTRL_STATUS			0x0004
+#define XVIP_CTRL_STATUS_PROC_STARTED		(1 << 0)
+#define XVIP_CTRL_STATUS_EOF			(1 << 1)
+#define XVIP_CTRL_ERROR				0x0008
+#define XVIP_CTRL_ERROR_SLAVE_EOL_EARLY		(1 << 0)
+#define XVIP_CTRL_ERROR_SLAVE_EOL_LATE		(1 << 1)
+#define XVIP_CTRL_ERROR_SLAVE_SOF_EARLY		(1 << 2)
+#define XVIP_CTRL_ERROR_SLAVE_SOF_LATE		(1 << 3)
+#define XVIP_CTRL_IRQ_ENABLE			0x000c
+#define XVIP_CTRL_IRQ_ENABLE_PROC_STARTED	(1 << 0)
+#define XVIP_CTRL_IRQ_EOF			(1 << 1)
+#define XVIP_CTRL_VERSION			0x0010
+#define XVIP_CTRL_VERSION_MAJOR_MASK		(0xff << 24)
+#define XVIP_CTRL_VERSION_MAJOR_SHIFT		24
+#define XVIP_CTRL_VERSION_MINOR_MASK		(0xff << 16)
+#define XVIP_CTRL_VERSION_MINOR_SHIFT		16
+#define XVIP_CTRL_VERSION_REVISION_MASK		(0xf << 12)
+#define XVIP_CTRL_VERSION_REVISION_SHIFT	12
+#define XVIP_CTRL_VERSION_PATCH_MASK		(0xf << 8)
+#define XVIP_CTRL_VERSION_PATCH_SHIFT		8
+#define XVIP_CTRL_VERSION_INTERNAL_MASK		(0xff << 0)
+#define XVIP_CTRL_VERSION_INTERNAL_SHIFT	0
+
+/* Xilinx Video IP Timing Registers */
+#define XVIP_ACTIVE_SIZE			0x0020
+#define XVIP_ACTIVE_VSIZE_MASK			(0x7ff << 16)
+#define XVIP_ACTIVE_VSIZE_SHIFT			16
+#define XVIP_ACTIVE_HSIZE_MASK			(0x7ff << 0)
+#define XVIP_ACTIVE_HSIZE_SHIFT			0
+#define XVIP_ENCODING				0x0028
+#define XVIP_ENCODING_NBITS_8			(0 << 4)
+#define XVIP_ENCODING_NBITS_10			(1 << 4)
+#define XVIP_ENCODING_NBITS_12			(2 << 4)
+#define XVIP_ENCODING_NBITS_16			(3 << 4)
+#define XVIP_ENCODING_NBITS_MASK		(3 << 4)
+#define XVIP_ENCODING_NBITS_SHIFT		4
+#define XVIP_ENCODING_VIDEO_FORMAT_YUV422	(0 << 0)
+#define XVIP_ENCODING_VIDEO_FORMAT_YUV444	(1 << 0)
+#define XVIP_ENCODING_VIDEO_FORMAT_RGB		(2 << 0)
+#define XVIP_ENCODING_VIDEO_FORMAT_YUV420	(3 << 0)
+#define XVIP_ENCODING_VIDEO_FORMAT_MASK		(3 << 0)
+#define XVIP_ENCODING_VIDEO_FORMAT_SHIFT	0
+
+/**
+ * struct xvip_device - Xilinx Video IP device structure
+ * @subdev: V4L2 subdevice
+ * @dev: (OF) device
+ * @iomem: device I/O register space remapped to kernel virtual memory
+ * @clk: video core clock
+ * @saved_ctrl: saved control register for resume / suspend
+ */
+struct xvip_device {
+	struct v4l2_subdev subdev;
+	struct device *dev;
+	void __iomem *iomem;
+	struct clk *clk;
+	u32 saved_ctrl;
+};
+
+/**
+ * struct xvip_video_format - Xilinx Video IP video format description
+ * @vf_code: AXI4 video format code
+ * @width: AXI4 format width in bits per component
+ * @pattern: CFA pattern for Mono/Sensor formats
+ * @code: media bus format code
+ * @bpp: bytes per pixel (when stored in memory)
+ * @fourcc: V4L2 pixel format FCC identifier
+ * @description: format description, suitable for userspace
+ */
+struct xvip_video_format {
+	unsigned int vf_code;
+	unsigned int width;
+	const char *pattern;
+	unsigned int code;
+	unsigned int bpp;
+	u32 fourcc;
+	const char *description;
+};
+
+const struct xvip_video_format *xvip_get_format_by_code(unsigned int code);
+const struct xvip_video_format *xvip_get_format_by_fourcc(u32 fourcc);
+const struct xvip_video_format *xvip_of_get_format(struct device_node *node);
+void xvip_set_format_size(struct v4l2_mbus_framefmt *format,
+			  const struct v4l2_subdev_format *fmt);
+int xvip_enum_mbus_code(struct v4l2_subdev *subdev,
+			struct v4l2_subdev_pad_config *cfg,
+			struct v4l2_subdev_mbus_code_enum *code);
+int xvip_enum_frame_size(struct v4l2_subdev *subdev,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_frame_size_enum *fse);
+
+static inline u32 xvip_read(struct xvip_device *xvip, u32 addr)
+{
+	return ioread32(xvip->iomem + addr);
+}
+
+static inline void xvip_write(struct xvip_device *xvip, u32 addr, u32 value)
+{
+	iowrite32(value, xvip->iomem + addr);
+}
+
+static inline void xvip_clr(struct xvip_device *xvip, u32 addr, u32 clr)
+{
+	xvip_write(xvip, addr, xvip_read(xvip, addr) & ~clr);
+}
+
+static inline void xvip_set(struct xvip_device *xvip, u32 addr, u32 set)
+{
+	xvip_write(xvip, addr, xvip_read(xvip, addr) | set);
+}
+
+void xvip_clr_or_set(struct xvip_device *xvip, u32 addr, u32 mask, bool set);
+void xvip_clr_and_set(struct xvip_device *xvip, u32 addr, u32 clr, u32 set);
+
+int xvip_init_resources(struct xvip_device *xvip);
+void xvip_cleanup_resources(struct xvip_device *xvip);
+
+static inline void xvip_reset(struct xvip_device *xvip)
+{
+	xvip_write(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_SW_RESET);
+}
+
+static inline void xvip_start(struct xvip_device *xvip)
+{
+	xvip_set(xvip, XVIP_CTRL_CONTROL,
+		 XVIP_CTRL_CONTROL_SW_ENABLE | XVIP_CTRL_CONTROL_REG_UPDATE);
+}
+
+static inline void xvip_stop(struct xvip_device *xvip)
+{
+	xvip_clr(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_SW_ENABLE);
+}
+
+static inline void xvip_resume(struct xvip_device *xvip)
+{
+	xvip_write(xvip, XVIP_CTRL_CONTROL,
+		   xvip->saved_ctrl | XVIP_CTRL_CONTROL_SW_ENABLE);
+}
+
+static inline void xvip_suspend(struct xvip_device *xvip)
+{
+	xvip->saved_ctrl = xvip_read(xvip, XVIP_CTRL_CONTROL);
+	xvip_write(xvip, XVIP_CTRL_CONTROL,
+		   xvip->saved_ctrl & ~XVIP_CTRL_CONTROL_SW_ENABLE);
+}
+
+static inline void xvip_set_frame_size(struct xvip_device *xvip,
+				       const struct v4l2_mbus_framefmt *format)
+{
+	xvip_write(xvip, XVIP_ACTIVE_SIZE,
+		   (format->height << XVIP_ACTIVE_VSIZE_SHIFT) |
+		   (format->width << XVIP_ACTIVE_HSIZE_SHIFT));
+}
+
+static inline void xvip_get_frame_size(struct xvip_device *xvip,
+				       struct v4l2_mbus_framefmt *format)
+{
+	u32 reg;
+
+	reg = xvip_read(xvip, XVIP_ACTIVE_SIZE);
+	format->width = (reg & XVIP_ACTIVE_HSIZE_MASK) >>
+			XVIP_ACTIVE_HSIZE_SHIFT;
+	format->height = (reg & XVIP_ACTIVE_VSIZE_MASK) >>
+			 XVIP_ACTIVE_VSIZE_SHIFT;
+}
+
+static inline void xvip_enable_reg_update(struct xvip_device *xvip)
+{
+	xvip_set(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_REG_UPDATE);
+}
+
+static inline void xvip_disable_reg_update(struct xvip_device *xvip)
+{
+	xvip_clr(xvip, XVIP_CTRL_CONTROL, XVIP_CTRL_CONTROL_REG_UPDATE);
+}
+
+static inline void xvip_print_version(struct xvip_device *xvip)
+{
+	u32 version;
+
+	version = xvip_read(xvip, XVIP_CTRL_VERSION);
+
+	dev_info(xvip->dev, "device found, version %u.%02x%x\n",
+		 ((version & XVIP_CTRL_VERSION_MAJOR_MASK) >>
+		  XVIP_CTRL_VERSION_MAJOR_SHIFT),
+		 ((version & XVIP_CTRL_VERSION_MINOR_MASK) >>
+		  XVIP_CTRL_VERSION_MINOR_SHIFT),
+		 ((version & XVIP_CTRL_VERSION_REVISION_MASK) >>
+		  XVIP_CTRL_VERSION_REVISION_SHIFT));
+}
+
+#endif /* __XILINX_VIP_H__ */
diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c
new file mode 100644
index 0000000..7b7cb9c
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-vipp.c
@@ -0,0 +1,669 @@
+/*
+ * Xilinx Video IP Composite Device
+ *
+ * Copyright (C) 2013-2015 Ideas on Board
+ * Copyright (C) 2013-2015 Xilinx, Inc.
+ *
+ * Contacts: Hyun Kwon <hyun.kwon@xilinx.com>
+ *           Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
+
+#include "xilinx-dma.h"
+#include "xilinx-vipp.h"
+
+#define XVIPP_DMA_S2MM				0
+#define XVIPP_DMA_MM2S				1
+
+/**
+ * struct xvip_graph_entity - Entity in the video graph
+ * @list: list entry in a graph entities list
+ * @node: the entity's DT node
+ * @entity: media entity, from the corresponding V4L2 subdev
+ * @asd: subdev asynchronous registration information
+ * @subdev: V4L2 subdev
+ */
+struct xvip_graph_entity {
+	struct list_head list;
+	struct device_node *node;
+	struct media_entity *entity;
+
+	struct v4l2_async_subdev asd;
+	struct v4l2_subdev *subdev;
+};
+
+/* -----------------------------------------------------------------------------
+ * Graph Management
+ */
+
+static struct xvip_graph_entity *
+xvip_graph_find_entity(struct xvip_composite_device *xdev,
+		       const struct device_node *node)
+{
+	struct xvip_graph_entity *entity;
+
+	list_for_each_entry(entity, &xdev->entities, list) {
+		if (entity->node == node)
+			return entity;
+	}
+
+	return NULL;
+}
+
+static int xvip_graph_build_one(struct xvip_composite_device *xdev,
+				struct xvip_graph_entity *entity)
+{
+	u32 link_flags = MEDIA_LNK_FL_ENABLED;
+	struct media_entity *local = entity->entity;
+	struct media_entity *remote;
+	struct media_pad *local_pad;
+	struct media_pad *remote_pad;
+	struct xvip_graph_entity *ent;
+	struct v4l2_of_link link;
+	struct device_node *ep = NULL;
+	struct device_node *next;
+	int ret = 0;
+
+	dev_dbg(xdev->dev, "creating links for entity %s\n", local->name);
+
+	while (1) {
+		/* Get the next endpoint and parse its link. */
+		next = of_graph_get_next_endpoint(entity->node, ep);
+		if (next == NULL)
+			break;
+
+		of_node_put(ep);
+		ep = next;
+
+		dev_dbg(xdev->dev, "processing endpoint %s\n", ep->full_name);
+
+		ret = v4l2_of_parse_link(ep, &link);
+		if (ret < 0) {
+			dev_err(xdev->dev, "failed to parse link for %s\n",
+				ep->full_name);
+			continue;
+		}
+
+		/* Skip sink ports, they will be processed from the other end of
+		 * the link.
+		 */
+		if (link.local_port >= local->num_pads) {
+			dev_err(xdev->dev, "invalid port number %u on %s\n",
+				link.local_port, link.local_node->full_name);
+			v4l2_of_put_link(&link);
+			ret = -EINVAL;
+			break;
+		}
+
+		local_pad = &local->pads[link.local_port];
+
+		if (local_pad->flags & MEDIA_PAD_FL_SINK) {
+			dev_dbg(xdev->dev, "skipping sink port %s:%u\n",
+				link.local_node->full_name, link.local_port);
+			v4l2_of_put_link(&link);
+			continue;
+		}
+
+		/* Skip DMA engines, they will be processed separately. */
+		if (link.remote_node == xdev->dev->of_node) {
+			dev_dbg(xdev->dev, "skipping DMA port %s:%u\n",
+				link.local_node->full_name, link.local_port);
+			v4l2_of_put_link(&link);
+			continue;
+		}
+
+		/* Find the remote entity. */
+		ent = xvip_graph_find_entity(xdev, link.remote_node);
+		if (ent == NULL) {
+			dev_err(xdev->dev, "no entity found for %s\n",
+				link.remote_node->full_name);
+			v4l2_of_put_link(&link);
+			ret = -ENODEV;
+			break;
+		}
+
+		remote = ent->entity;
+
+		if (link.remote_port >= remote->num_pads) {
+			dev_err(xdev->dev, "invalid port number %u on %s\n",
+				link.remote_port, link.remote_node->full_name);
+			v4l2_of_put_link(&link);
+			ret = -EINVAL;
+			break;
+		}
+
+		remote_pad = &remote->pads[link.remote_port];
+
+		v4l2_of_put_link(&link);
+
+		/* Create the media link. */
+		dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n",
+			local->name, local_pad->index,
+			remote->name, remote_pad->index);
+
+		ret = media_entity_create_link(local, local_pad->index,
+					       remote, remote_pad->index,
+					       link_flags);
+		if (ret < 0) {
+			dev_err(xdev->dev,
+				"failed to create %s:%u -> %s:%u link\n",
+				local->name, local_pad->index,
+				remote->name, remote_pad->index);
+			break;
+		}
+	}
+
+	of_node_put(ep);
+	return ret;
+}
+
+static struct xvip_dma *
+xvip_graph_find_dma(struct xvip_composite_device *xdev, unsigned int port)
+{
+	struct xvip_dma *dma;
+
+	list_for_each_entry(dma, &xdev->dmas, list) {
+		if (dma->port == port)
+			return dma;
+	}
+
+	return NULL;
+}
+
+static int xvip_graph_build_dma(struct xvip_composite_device *xdev)
+{
+	u32 link_flags = MEDIA_LNK_FL_ENABLED;
+	struct device_node *node = xdev->dev->of_node;
+	struct media_entity *source;
+	struct media_entity *sink;
+	struct media_pad *source_pad;
+	struct media_pad *sink_pad;
+	struct xvip_graph_entity *ent;
+	struct v4l2_of_link link;
+	struct device_node *ep = NULL;
+	struct device_node *next;
+	struct xvip_dma *dma;
+	int ret = 0;
+
+	dev_dbg(xdev->dev, "creating links for DMA engines\n");
+
+	while (1) {
+		/* Get the next endpoint and parse its link. */
+		next = of_graph_get_next_endpoint(node, ep);
+		if (next == NULL)
+			break;
+
+		of_node_put(ep);
+		ep = next;
+
+		dev_dbg(xdev->dev, "processing endpoint %s\n", ep->full_name);
+
+		ret = v4l2_of_parse_link(ep, &link);
+		if (ret < 0) {
+			dev_err(xdev->dev, "failed to parse link for %s\n",
+				ep->full_name);
+			continue;
+		}
+
+		/* Find the DMA engine. */
+		dma = xvip_graph_find_dma(xdev, link.local_port);
+		if (dma == NULL) {
+			dev_err(xdev->dev, "no DMA engine found for port %u\n",
+				link.local_port);
+			v4l2_of_put_link(&link);
+			ret = -EINVAL;
+			break;
+		}
+
+		dev_dbg(xdev->dev, "creating link for DMA engine %s\n",
+			dma->video.name);
+
+		/* Find the remote entity. */
+		ent = xvip_graph_find_entity(xdev, link.remote_node);
+		if (ent == NULL) {
+			dev_err(xdev->dev, "no entity found for %s\n",
+				link.remote_node->full_name);
+			v4l2_of_put_link(&link);
+			ret = -ENODEV;
+			break;
+		}
+
+		if (link.remote_port >= ent->entity->num_pads) {
+			dev_err(xdev->dev, "invalid port number %u on %s\n",
+				link.remote_port, link.remote_node->full_name);
+			v4l2_of_put_link(&link);
+			ret = -EINVAL;
+			break;
+		}
+
+		if (dma->pad.flags & MEDIA_PAD_FL_SOURCE) {
+			source = &dma->video.entity;
+			source_pad = &dma->pad;
+			sink = ent->entity;
+			sink_pad = &sink->pads[link.remote_port];
+		} else {
+			source = ent->entity;
+			source_pad = &source->pads[link.remote_port];
+			sink = &dma->video.entity;
+			sink_pad = &dma->pad;
+		}
+
+		v4l2_of_put_link(&link);
+
+		/* Create the media link. */
+		dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n",
+			source->name, source_pad->index,
+			sink->name, sink_pad->index);
+
+		ret = media_entity_create_link(source, source_pad->index,
+					       sink, sink_pad->index,
+					       link_flags);
+		if (ret < 0) {
+			dev_err(xdev->dev,
+				"failed to create %s:%u -> %s:%u link\n",
+				source->name, source_pad->index,
+				sink->name, sink_pad->index);
+			break;
+		}
+	}
+
+	of_node_put(ep);
+	return ret;
+}
+
+static int xvip_graph_notify_complete(struct v4l2_async_notifier *notifier)
+{
+	struct xvip_composite_device *xdev =
+		container_of(notifier, struct xvip_composite_device, notifier);
+	struct xvip_graph_entity *entity;
+	int ret;
+
+	dev_dbg(xdev->dev, "notify complete, all subdevs registered\n");
+
+	/* Create links for every entity. */
+	list_for_each_entry(entity, &xdev->entities, list) {
+		ret = xvip_graph_build_one(xdev, entity);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Create links for DMA channels. */
+	ret = xvip_graph_build_dma(xdev);
+	if (ret < 0)
+		return ret;
+
+	ret = v4l2_device_register_subdev_nodes(&xdev->v4l2_dev);
+	if (ret < 0)
+		dev_err(xdev->dev, "failed to register subdev nodes\n");
+
+	return ret;
+}
+
+static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier,
+				   struct v4l2_subdev *subdev,
+				   struct v4l2_async_subdev *asd)
+{
+	struct xvip_composite_device *xdev =
+		container_of(notifier, struct xvip_composite_device, notifier);
+	struct xvip_graph_entity *entity;
+
+	/* Locate the entity corresponding to the bound subdev and store the
+	 * subdev pointer.
+	 */
+	list_for_each_entry(entity, &xdev->entities, list) {
+		if (entity->node != subdev->dev->of_node)
+			continue;
+
+		if (entity->subdev) {
+			dev_err(xdev->dev, "duplicate subdev for node %s\n",
+				entity->node->full_name);
+			return -EINVAL;
+		}
+
+		dev_dbg(xdev->dev, "subdev %s bound\n", subdev->name);
+		entity->entity = &subdev->entity;
+		entity->subdev = subdev;
+		return 0;
+	}
+
+	dev_err(xdev->dev, "no entity for subdev %s\n", subdev->name);
+	return -EINVAL;
+}
+
+static int xvip_graph_parse_one(struct xvip_composite_device *xdev,
+				struct device_node *node)
+{
+	struct xvip_graph_entity *entity;
+	struct device_node *remote;
+	struct device_node *ep = NULL;
+	struct device_node *next;
+	int ret = 0;
+
+	dev_dbg(xdev->dev, "parsing node %s\n", node->full_name);
+
+	while (1) {
+		next = of_graph_get_next_endpoint(node, ep);
+		if (next == NULL)
+			break;
+
+		of_node_put(ep);
+		ep = next;
+
+		dev_dbg(xdev->dev, "handling endpoint %s\n", ep->full_name);
+
+		remote = of_graph_get_remote_port_parent(ep);
+		if (remote == NULL) {
+			ret = -EINVAL;
+			break;
+		}
+
+		/* Skip entities that we have already processed. */
+		if (remote == xdev->dev->of_node ||
+		    xvip_graph_find_entity(xdev, remote)) {
+			of_node_put(remote);
+			continue;
+		}
+
+		entity = devm_kzalloc(xdev->dev, sizeof(*entity), GFP_KERNEL);
+		if (entity == NULL) {
+			of_node_put(remote);
+			ret = -ENOMEM;
+			break;
+		}
+
+		entity->node = remote;
+		entity->asd.match_type = V4L2_ASYNC_MATCH_OF;
+		entity->asd.match.of.node = remote;
+		list_add_tail(&entity->list, &xdev->entities);
+		xdev->num_subdevs++;
+	}
+
+	of_node_put(ep);
+	return ret;
+}
+
+static int xvip_graph_parse(struct xvip_composite_device *xdev)
+{
+	struct xvip_graph_entity *entity;
+	int ret;
+
+	/*
+	 * Walk the links to parse the full graph. Start by parsing the
+	 * composite node and then parse entities in turn. The list_for_each
+	 * loop will handle entities added at the end of the list while walking
+	 * the links.
+	 */
+	ret = xvip_graph_parse_one(xdev, xdev->dev->of_node);
+	if (ret < 0)
+		return 0;
+
+	list_for_each_entry(entity, &xdev->entities, list) {
+		ret = xvip_graph_parse_one(xdev, entity->node);
+		if (ret < 0)
+			break;
+	}
+
+	return ret;
+}
+
+static int xvip_graph_dma_init_one(struct xvip_composite_device *xdev,
+				   struct device_node *node)
+{
+	struct xvip_dma *dma;
+	enum v4l2_buf_type type;
+	const char *direction;
+	unsigned int index;
+	int ret;
+
+	ret = of_property_read_string(node, "direction", &direction);
+	if (ret < 0)
+		return ret;
+
+	if (strcmp(direction, "input") == 0)
+		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	else if (strcmp(direction, "output") == 0)
+		type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	else
+		return -EINVAL;
+
+	of_property_read_u32(node, "reg", &index);
+
+	dma = devm_kzalloc(xdev->dev, sizeof(*dma), GFP_KERNEL);
+	if (dma == NULL)
+		return -ENOMEM;
+
+	ret = xvip_dma_init(xdev, dma, type, index);
+	if (ret < 0) {
+		dev_err(xdev->dev, "%s initialization failed\n",
+			node->full_name);
+		return ret;
+	}
+
+	list_add_tail(&dma->list, &xdev->dmas);
+
+	xdev->v4l2_caps |= type == V4L2_BUF_TYPE_VIDEO_CAPTURE
+			 ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_VIDEO_OUTPUT;
+
+	return 0;
+}
+
+static int xvip_graph_dma_init(struct xvip_composite_device *xdev)
+{
+	struct device_node *ports;
+	struct device_node *port;
+	int ret;
+
+	ports = of_get_child_by_name(xdev->dev->of_node, "ports");
+	if (ports == NULL) {
+		dev_err(xdev->dev, "ports node not present\n");
+		return -EINVAL;
+	}
+
+	for_each_child_of_node(ports, port) {
+		ret = xvip_graph_dma_init_one(xdev, port);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void xvip_graph_cleanup(struct xvip_composite_device *xdev)
+{
+	struct xvip_graph_entity *entityp;
+	struct xvip_graph_entity *entity;
+	struct xvip_dma *dmap;
+	struct xvip_dma *dma;
+
+	v4l2_async_notifier_unregister(&xdev->notifier);
+
+	list_for_each_entry_safe(entity, entityp, &xdev->entities, list) {
+		of_node_put(entity->node);
+		list_del(&entity->list);
+	}
+
+	list_for_each_entry_safe(dma, dmap, &xdev->dmas, list) {
+		xvip_dma_cleanup(dma);
+		list_del(&dma->list);
+	}
+}
+
+static int xvip_graph_init(struct xvip_composite_device *xdev)
+{
+	struct xvip_graph_entity *entity;
+	struct v4l2_async_subdev **subdevs = NULL;
+	unsigned int num_subdevs;
+	unsigned int i;
+	int ret;
+
+	/* Init the DMA channels. */
+	ret = xvip_graph_dma_init(xdev);
+	if (ret < 0) {
+		dev_err(xdev->dev, "DMA initialization failed\n");
+		goto done;
+	}
+
+	/* Parse the graph to extract a list of subdevice DT nodes. */
+	ret = xvip_graph_parse(xdev);
+	if (ret < 0) {
+		dev_err(xdev->dev, "graph parsing failed\n");
+		goto done;
+	}
+
+	if (!xdev->num_subdevs) {
+		dev_err(xdev->dev, "no subdev found in graph\n");
+		goto done;
+	}
+
+	/* Register the subdevices notifier. */
+	num_subdevs = xdev->num_subdevs;
+	subdevs = devm_kzalloc(xdev->dev, sizeof(*subdevs) * num_subdevs,
+			       GFP_KERNEL);
+	if (subdevs == NULL) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	i = 0;
+	list_for_each_entry(entity, &xdev->entities, list)
+		subdevs[i++] = &entity->asd;
+
+	xdev->notifier.subdevs = subdevs;
+	xdev->notifier.num_subdevs = num_subdevs;
+	xdev->notifier.bound = xvip_graph_notify_bound;
+	xdev->notifier.complete = xvip_graph_notify_complete;
+
+	ret = v4l2_async_notifier_register(&xdev->v4l2_dev, &xdev->notifier);
+	if (ret < 0) {
+		dev_err(xdev->dev, "notifier registration failed\n");
+		goto done;
+	}
+
+	ret = 0;
+
+done:
+	if (ret < 0)
+		xvip_graph_cleanup(xdev);
+
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Media Controller and V4L2
+ */
+
+static void xvip_composite_v4l2_cleanup(struct xvip_composite_device *xdev)
+{
+	v4l2_device_unregister(&xdev->v4l2_dev);
+	media_device_unregister(&xdev->media_dev);
+}
+
+static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev)
+{
+	int ret;
+
+	xdev->media_dev.dev = xdev->dev;
+	strlcpy(xdev->media_dev.model, "Xilinx Video Composite Device",
+		sizeof(xdev->media_dev.model));
+	xdev->media_dev.hw_revision = 0;
+
+	ret = media_device_register(&xdev->media_dev);
+	if (ret < 0) {
+		dev_err(xdev->dev, "media device registration failed (%d)\n",
+			ret);
+		return ret;
+	}
+
+	xdev->v4l2_dev.mdev = &xdev->media_dev;
+	ret = v4l2_device_register(xdev->dev, &xdev->v4l2_dev);
+	if (ret < 0) {
+		dev_err(xdev->dev, "V4L2 device registration failed (%d)\n",
+			ret);
+		media_device_unregister(&xdev->media_dev);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Platform Device Driver
+ */
+
+static int xvip_composite_probe(struct platform_device *pdev)
+{
+	struct xvip_composite_device *xdev;
+	int ret;
+
+	xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
+	if (!xdev)
+		return -ENOMEM;
+
+	xdev->dev = &pdev->dev;
+	INIT_LIST_HEAD(&xdev->entities);
+	INIT_LIST_HEAD(&xdev->dmas);
+
+	ret = xvip_composite_v4l2_init(xdev);
+	if (ret < 0)
+		return ret;
+
+	ret = xvip_graph_init(xdev);
+	if (ret < 0)
+		goto error;
+
+	platform_set_drvdata(pdev, xdev);
+
+	dev_info(xdev->dev, "device registered\n");
+
+	return 0;
+
+error:
+	xvip_composite_v4l2_cleanup(xdev);
+	return ret;
+}
+
+static int xvip_composite_remove(struct platform_device *pdev)
+{
+	struct xvip_composite_device *xdev = platform_get_drvdata(pdev);
+
+	xvip_graph_cleanup(xdev);
+	xvip_composite_v4l2_cleanup(xdev);
+
+	return 0;
+}
+
+static const struct of_device_id xvip_composite_of_id_table[] = {
+	{ .compatible = "xlnx,video" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, xvip_composite_of_id_table);
+
+static struct platform_driver xvip_composite_driver = {
+	.driver = {
+		.name = "xilinx-video",
+		.of_match_table = xvip_composite_of_id_table,
+	},
+	.probe = xvip_composite_probe,
+	.remove = xvip_composite_remove,
+};
+
+module_platform_driver(xvip_composite_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Xilinx Video IP Composite Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/xilinx/xilinx-vipp.h b/drivers/media/platform/xilinx/xilinx-vipp.h
new file mode 100644
index 0000000..faf6b6e
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-vipp.h
@@ -0,0 +1,49 @@
+/*
+ * Xilinx Video IP Composite Device
+ *
+ * Copyright (C) 2013-2015 Ideas on Board
+ * Copyright (C) 2013-2015 Xilinx, Inc.
+ *
+ * Contacts: Hyun Kwon <hyun.kwon@xilinx.com>
+ *           Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __XILINX_VIPP_H__
+#define __XILINX_VIPP_H__
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <media/media-device.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+/**
+ * struct xvip_composite_device - Xilinx Video IP device structure
+ * @v4l2_dev: V4L2 device
+ * @media_dev: media device
+ * @dev: (OF) device
+ * @notifier: V4L2 asynchronous subdevs notifier
+ * @entities: entities in the graph as a list of xvip_graph_entity
+ * @num_subdevs: number of subdevs in the pipeline
+ * @dmas: list of DMA channels at the pipeline output and input
+ * @v4l2_caps: V4L2 capabilities of the whole device (see VIDIOC_QUERYCAP)
+ */
+struct xvip_composite_device {
+	struct v4l2_device v4l2_dev;
+	struct media_device media_dev;
+	struct device *dev;
+
+	struct v4l2_async_notifier notifier;
+	struct list_head entities;
+	unsigned int num_subdevs;
+
+	struct list_head dmas;
+	u32 v4l2_caps;
+};
+
+#endif /* __XILINX_VIPP_H__ */
diff --git a/drivers/media/platform/xilinx/xilinx-vtc.c b/drivers/media/platform/xilinx/xilinx-vtc.c
new file mode 100644
index 0000000..01c750e
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-vtc.c
@@ -0,0 +1,380 @@
+/*
+ * Xilinx Video Timing Controller
+ *
+ * Copyright (C) 2013-2015 Ideas on Board
+ * Copyright (C) 2013-2015 Xilinx, Inc.
+ *
+ * Contacts: Hyun Kwon <hyun.kwon@xilinx.com>
+ *           Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "xilinx-vip.h"
+#include "xilinx-vtc.h"
+
+#define XVTC_CONTROL_FIELD_ID_POL_SRC		(1 << 26)
+#define XVTC_CONTROL_ACTIVE_CHROMA_POL_SRC	(1 << 25)
+#define XVTC_CONTROL_ACTIVE_VIDEO_POL_SRC	(1 << 24)
+#define XVTC_CONTROL_HSYNC_POL_SRC		(1 << 23)
+#define XVTC_CONTROL_VSYNC_POL_SRC		(1 << 22)
+#define XVTC_CONTROL_HBLANK_POL_SRC		(1 << 21)
+#define XVTC_CONTROL_VBLANK_POL_SRC		(1 << 20)
+#define XVTC_CONTROL_CHROMA_SRC			(1 << 18)
+#define XVTC_CONTROL_VBLANK_HOFF_SRC		(1 << 17)
+#define XVTC_CONTROL_VSYNC_END_SRC		(1 << 16)
+#define XVTC_CONTROL_VSYNC_START_SRC		(1 << 15)
+#define XVTC_CONTROL_ACTIVE_VSIZE_SRC		(1 << 14)
+#define XVTC_CONTROL_FRAME_VSIZE_SRC		(1 << 13)
+#define XVTC_CONTROL_HSYNC_END_SRC		(1 << 11)
+#define XVTC_CONTROL_HSYNC_START_SRC		(1 << 10)
+#define XVTC_CONTROL_ACTIVE_HSIZE_SRC		(1 << 9)
+#define XVTC_CONTROL_FRAME_HSIZE_SRC		(1 << 8)
+#define XVTC_CONTROL_SYNC_ENABLE		(1 << 5)
+#define XVTC_CONTROL_DET_ENABLE			(1 << 3)
+#define XVTC_CONTROL_GEN_ENABLE			(1 << 2)
+
+#define XVTC_STATUS_FSYNC(n)			((n) << 16)
+#define XVTC_STATUS_GEN_ACTIVE_VIDEO		(1 << 13)
+#define XVTC_STATUS_GEN_VBLANK			(1 << 12)
+#define XVTC_STATUS_DET_ACTIVE_VIDEO		(1 << 11)
+#define XVTC_STATUS_DET_VBLANK			(1 << 10)
+#define XVTC_STATUS_LOCK_LOSS			(1 << 9)
+#define XVTC_STATUS_LOCK			(1 << 8)
+
+#define XVTC_ERROR_ACTIVE_CHROMA_LOCK		(1 << 21)
+#define XVTC_ERROR_ACTIVE_VIDEO_LOCK		(1 << 20)
+#define XVTC_ERROR_HSYNC_LOCK			(1 << 19)
+#define XVTC_ERROR_VSYNC_LOCK			(1 << 18)
+#define XVTC_ERROR_HBLANK_LOCK			(1 << 17)
+#define XVTC_ERROR_VBLANK_LOCK			(1 << 16)
+
+#define XVTC_IRQ_ENABLE_FSYNC(n)		((n) << 16)
+#define XVTC_IRQ_ENABLE_GEN_ACTIVE_VIDEO	(1 << 13)
+#define XVTC_IRQ_ENABLE_GEN_VBLANK		(1 << 12)
+#define XVTC_IRQ_ENABLE_DET_ACTIVE_VIDEO	(1 << 11)
+#define XVTC_IRQ_ENABLE_DET_VBLANK		(1 << 10)
+#define XVTC_IRQ_ENABLE_LOCK_LOSS		(1 << 9)
+#define XVTC_IRQ_ENABLE_LOCK			(1 << 8)
+
+/*
+ * The following registers exist in two blocks, one at 0x0020 for the detector
+ * and one at 0x0060 for the generator.
+ */
+
+#define XVTC_DETECTOR_OFFSET			0x0020
+#define XVTC_GENERATOR_OFFSET			0x0060
+
+#define XVTC_ACTIVE_SIZE			0x0000
+#define XVTC_ACTIVE_VSIZE_SHIFT			16
+#define XVTC_ACTIVE_VSIZE_MASK			(0x1fff << 16)
+#define XVTC_ACTIVE_HSIZE_SHIFT			0
+#define XVTC_ACTIVE_HSIZE_MASK			(0x1fff << 0)
+
+#define XVTC_TIMING_STATUS			0x0004
+#define XVTC_TIMING_STATUS_ACTIVE_VIDEO		(1 << 2)
+#define XVTC_TIMING_STATUS_VBLANK		(1 << 1)
+#define XVTC_TIMING_STATUS_LOCKED		(1 << 0)
+
+#define XVTC_ENCODING				0x0008
+#define XVTC_ENCODING_CHROMA_PARITY_SHIFT	8
+#define XVTC_ENCODING_CHROMA_PARITY_MASK	(3 << 8)
+#define XVTC_ENCODING_CHROMA_PARITY_EVEN_ALL	(0 << 8)
+#define XVTC_ENCODING_CHROMA_PARITY_ODD_ALL	(1 << 8)
+#define XVTC_ENCODING_CHROMA_PARITY_EVEN_EVEN	(2 << 8)
+#define XVTC_ENCODING_CHROMA_PARITY_ODD_EVEN	(3 << 8)
+#define XVTC_ENCODING_VIDEO_FORMAT_SHIFT	0
+#define XVTC_ENCODING_VIDEO_FORMAT_MASK		(0xf << 0)
+#define XVTC_ENCODING_VIDEO_FORMAT_YUV422	(0 << 0)
+#define XVTC_ENCODING_VIDEO_FORMAT_YUV444	(1 << 0)
+#define XVTC_ENCODING_VIDEO_FORMAT_RGB		(2 << 0)
+#define XVTC_ENCODING_VIDEO_FORMAT_YUV420	(3 << 0)
+
+#define XVTC_POLARITY				0x000c
+#define XVTC_POLARITY_ACTIVE_CHROMA_POL		(1 << 5)
+#define XVTC_POLARITY_ACTIVE_VIDEO_POL		(1 << 4)
+#define XVTC_POLARITY_HSYNC_POL			(1 << 3)
+#define XVTC_POLARITY_VSYNC_POL			(1 << 2)
+#define XVTC_POLARITY_HBLANK_POL		(1 << 1)
+#define XVTC_POLARITY_VBLANK_POL		(1 << 0)
+
+#define XVTC_HSIZE				0x0010
+#define XVTC_HSIZE_MASK				(0x1fff << 0)
+
+#define XVTC_VSIZE				0x0014
+#define XVTC_VSIZE_MASK				(0x1fff << 0)
+
+#define XVTC_HSYNC				0x0018
+#define XVTC_HSYNC_END_SHIFT			16
+#define XVTC_HSYNC_END_MASK			(0x1fff << 16)
+#define XVTC_HSYNC_START_SHIFT			0
+#define XVTC_HSYNC_START_MASK			(0x1fff << 0)
+
+#define XVTC_F0_VBLANK_H			0x001c
+#define XVTC_F0_VBLANK_HEND_SHIFT		16
+#define XVTC_F0_VBLANK_HEND_MASK		(0x1fff << 16)
+#define XVTC_F0_VBLANK_HSTART_SHIFT		0
+#define XVTC_F0_VBLANK_HSTART_MASK		(0x1fff << 0)
+
+#define XVTC_F0_VSYNC_V				0x0020
+#define XVTC_F0_VSYNC_VEND_SHIFT		16
+#define XVTC_F0_VSYNC_VEND_MASK			(0x1fff << 16)
+#define XVTC_F0_VSYNC_VSTART_SHIFT		0
+#define XVTC_F0_VSYNC_VSTART_MASK		(0x1fff << 0)
+
+#define XVTC_F0_VSYNC_H				0x0024
+#define XVTC_F0_VSYNC_HEND_SHIFT		16
+#define XVTC_F0_VSYNC_HEND_MASK			(0x1fff << 16)
+#define XVTC_F0_VSYNC_HSTART_SHIFT		0
+#define XVTC_F0_VSYNC_HSTART_MASK		(0x1fff << 0)
+
+#define XVTC_FRAME_SYNC_CONFIG(n)		(0x0100 + 4 * (n))
+#define XVTC_FRAME_SYNC_V_START_SHIFT		16
+#define XVTC_FRAME_SYNC_V_START_MASK		(0x1fff << 16)
+#define XVTC_FRAME_SYNC_H_START_SHIFT		0
+#define XVTC_FRAME_SYNC_H_START_MASK		(0x1fff << 0)
+
+#define XVTC_GENERATOR_GLOBAL_DELAY		0x0104
+
+/**
+ * struct xvtc_device - Xilinx Video Timing Controller device structure
+ * @xvip: Xilinx Video IP device
+ * @list: entry in the global VTC list
+ * @has_detector: the VTC has a timing detector
+ * @has_generator: the VTC has a timing generator
+ * @config: generator timings configuration
+ */
+struct xvtc_device {
+	struct xvip_device xvip;
+	struct list_head list;
+
+	bool has_detector;
+	bool has_generator;
+
+	struct xvtc_config config;
+};
+
+static LIST_HEAD(xvtc_list);
+static DEFINE_MUTEX(xvtc_lock);
+
+static inline void xvtc_gen_write(struct xvtc_device *xvtc, u32 addr, u32 value)
+{
+	xvip_write(&xvtc->xvip, XVTC_GENERATOR_OFFSET + addr, value);
+}
+
+/* -----------------------------------------------------------------------------
+ * Generator Operations
+ */
+
+int xvtc_generator_start(struct xvtc_device *xvtc,
+			 const struct xvtc_config *config)
+{
+	int ret;
+
+	if (!xvtc->has_generator)
+		return -ENXIO;
+
+	ret = clk_prepare_enable(xvtc->xvip.clk);
+	if (ret < 0)
+		return ret;
+
+	/* We don't care about the chroma active signal, encoding parameters are
+	 * not important for now.
+	 */
+	xvtc_gen_write(xvtc, XVTC_POLARITY,
+		       XVTC_POLARITY_ACTIVE_CHROMA_POL |
+		       XVTC_POLARITY_ACTIVE_VIDEO_POL |
+		       XVTC_POLARITY_HSYNC_POL | XVTC_POLARITY_VSYNC_POL |
+		       XVTC_POLARITY_HBLANK_POL | XVTC_POLARITY_VBLANK_POL);
+
+	/* Hardcode the polarity to active high, as required by the video in to
+	 * AXI4-stream core.
+	 */
+	xvtc_gen_write(xvtc, XVTC_ENCODING, 0);
+
+	/* Configure the timings. The VBLANK and VSYNC signals assertion and
+	 * deassertion are hardcoded to the first pixel of the line.
+	 */
+	xvtc_gen_write(xvtc, XVTC_ACTIVE_SIZE,
+		       (config->vblank_start << XVTC_ACTIVE_VSIZE_SHIFT) |
+		       (config->hblank_start << XVTC_ACTIVE_HSIZE_SHIFT));
+	xvtc_gen_write(xvtc, XVTC_HSIZE, config->hsize);
+	xvtc_gen_write(xvtc, XVTC_VSIZE, config->vsize);
+	xvtc_gen_write(xvtc, XVTC_HSYNC,
+		       (config->hsync_end << XVTC_HSYNC_END_SHIFT) |
+		       (config->hsync_start << XVTC_HSYNC_START_SHIFT));
+	xvtc_gen_write(xvtc, XVTC_F0_VBLANK_H, 0);
+	xvtc_gen_write(xvtc, XVTC_F0_VSYNC_V,
+		       (config->vsync_end << XVTC_F0_VSYNC_VEND_SHIFT) |
+		       (config->vsync_start << XVTC_F0_VSYNC_VSTART_SHIFT));
+	xvtc_gen_write(xvtc, XVTC_F0_VSYNC_H, 0);
+
+	/* Enable the generator. Set the source of all generator parameters to
+	 * generator registers.
+	 */
+	xvip_write(&xvtc->xvip, XVIP_CTRL_CONTROL,
+		   XVTC_CONTROL_ACTIVE_CHROMA_POL_SRC |
+		   XVTC_CONTROL_ACTIVE_VIDEO_POL_SRC |
+		   XVTC_CONTROL_HSYNC_POL_SRC | XVTC_CONTROL_VSYNC_POL_SRC |
+		   XVTC_CONTROL_HBLANK_POL_SRC | XVTC_CONTROL_VBLANK_POL_SRC |
+		   XVTC_CONTROL_CHROMA_SRC | XVTC_CONTROL_VBLANK_HOFF_SRC |
+		   XVTC_CONTROL_VSYNC_END_SRC | XVTC_CONTROL_VSYNC_START_SRC |
+		   XVTC_CONTROL_ACTIVE_VSIZE_SRC |
+		   XVTC_CONTROL_FRAME_VSIZE_SRC | XVTC_CONTROL_HSYNC_END_SRC |
+		   XVTC_CONTROL_HSYNC_START_SRC |
+		   XVTC_CONTROL_ACTIVE_HSIZE_SRC |
+		   XVTC_CONTROL_FRAME_HSIZE_SRC | XVTC_CONTROL_GEN_ENABLE |
+		   XVIP_CTRL_CONTROL_REG_UPDATE);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(xvtc_generator_start);
+
+int xvtc_generator_stop(struct xvtc_device *xvtc)
+{
+	if (!xvtc->has_generator)
+		return -ENXIO;
+
+	xvip_write(&xvtc->xvip, XVIP_CTRL_CONTROL, 0);
+
+	clk_disable_unprepare(xvtc->xvip.clk);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(xvtc_generator_stop);
+
+struct xvtc_device *xvtc_of_get(struct device_node *np)
+{
+	struct device_node *xvtc_node;
+	struct xvtc_device *found = NULL;
+	struct xvtc_device *xvtc;
+
+	if (!of_find_property(np, "xlnx,vtc", NULL))
+		return NULL;
+
+	xvtc_node = of_parse_phandle(np, "xlnx,vtc", 0);
+	if (xvtc_node == NULL)
+		return ERR_PTR(-EINVAL);
+
+	mutex_lock(&xvtc_lock);
+	list_for_each_entry(xvtc, &xvtc_list, list) {
+		if (xvtc->xvip.dev->of_node == xvtc_node) {
+			found = xvtc;
+			break;
+		}
+	}
+	mutex_unlock(&xvtc_lock);
+
+	of_node_put(xvtc_node);
+
+	if (!found)
+		return ERR_PTR(-EPROBE_DEFER);
+
+	return found;
+}
+EXPORT_SYMBOL_GPL(xvtc_of_get);
+
+void xvtc_put(struct xvtc_device *xvtc)
+{
+}
+EXPORT_SYMBOL_GPL(xvtc_put);
+
+/* -----------------------------------------------------------------------------
+ * Registration and Unregistration
+ */
+
+static void xvtc_register_device(struct xvtc_device *xvtc)
+{
+	mutex_lock(&xvtc_lock);
+	list_add_tail(&xvtc->list, &xvtc_list);
+	mutex_unlock(&xvtc_lock);
+}
+
+static void xvtc_unregister_device(struct xvtc_device *xvtc)
+{
+	mutex_lock(&xvtc_lock);
+	list_del(&xvtc->list);
+	mutex_unlock(&xvtc_lock);
+}
+
+/* -----------------------------------------------------------------------------
+ * Platform Device Driver
+ */
+
+static int xvtc_parse_of(struct xvtc_device *xvtc)
+{
+	struct device_node *node = xvtc->xvip.dev->of_node;
+
+	xvtc->has_detector = of_property_read_bool(node, "xlnx,detector");
+	xvtc->has_generator = of_property_read_bool(node, "xlnx,generator");
+
+	return 0;
+}
+
+static int xvtc_probe(struct platform_device *pdev)
+{
+	struct xvtc_device *xvtc;
+	int ret;
+
+	xvtc = devm_kzalloc(&pdev->dev, sizeof(*xvtc), GFP_KERNEL);
+	if (!xvtc)
+		return -ENOMEM;
+
+	xvtc->xvip.dev = &pdev->dev;
+
+	ret = xvtc_parse_of(xvtc);
+	if (ret < 0)
+		return ret;
+
+	ret = xvip_init_resources(&xvtc->xvip);
+	if (ret < 0)
+		return ret;
+
+	platform_set_drvdata(pdev, xvtc);
+
+	xvip_print_version(&xvtc->xvip);
+
+	xvtc_register_device(xvtc);
+
+	return 0;
+}
+
+static int xvtc_remove(struct platform_device *pdev)
+{
+	struct xvtc_device *xvtc = platform_get_drvdata(pdev);
+
+	xvtc_unregister_device(xvtc);
+
+	xvip_cleanup_resources(&xvtc->xvip);
+
+	return 0;
+}
+
+static const struct of_device_id xvtc_of_id_table[] = {
+	{ .compatible = "xlnx,v-tc-6.1" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, xvtc_of_id_table);
+
+static struct platform_driver xvtc_driver = {
+	.driver = {
+		.name = "xilinx-vtc",
+		.of_match_table = xvtc_of_id_table,
+	},
+	.probe = xvtc_probe,
+	.remove = xvtc_remove,
+};
+
+module_platform_driver(xvtc_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Xilinx Video Timing Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/xilinx/xilinx-vtc.h b/drivers/media/platform/xilinx/xilinx-vtc.h
new file mode 100644
index 0000000..e1bb2cf
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-vtc.h
@@ -0,0 +1,42 @@
+/*
+ * Xilinx Video Timing Controller
+ *
+ * Copyright (C) 2013-2015 Ideas on Board
+ * Copyright (C) 2013-2015 Xilinx, Inc.
+ *
+ * Contacts: Hyun Kwon <hyun.kwon@xilinx.com>
+ *           Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __XILINX_VTC_H__
+#define __XILINX_VTC_H__
+
+struct device_node;
+struct xvtc_device;
+
+#define XVTC_MAX_HSIZE			8191
+#define XVTC_MAX_VSIZE			8191
+
+struct xvtc_config {
+	unsigned int hblank_start;
+	unsigned int hsync_start;
+	unsigned int hsync_end;
+	unsigned int hsize;
+	unsigned int vblank_start;
+	unsigned int vsync_start;
+	unsigned int vsync_end;
+	unsigned int vsize;
+};
+
+struct xvtc_device *xvtc_of_get(struct device_node *np);
+void xvtc_put(struct xvtc_device *xvtc);
+
+int xvtc_generator_start(struct xvtc_device *xvtc,
+			 const struct xvtc_config *config);
+int xvtc_generator_stop(struct xvtc_device *xvtc);
+
+#endif /* __XILINX_VTC_H__ */
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 8b7e337..0aa8f13 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -129,11 +129,11 @@
 	} else if (curvol < vol) {
 		outb(0x98, isa->io);	/* volume up + sigstr + on	*/
 		for (; curvol < vol; curvol++)
-			udelay(3000);
+			mdelay(3);
 	} else if (curvol > vol) {
 		outb(0x58, isa->io);	/* volume down + sigstr + on	*/
 		for (; curvol > vol; curvol--)
-			udelay(3000);
+			mdelay(3);
 	}
 	outb(0xd8, isa->io);		/* volume steady + sigstr + on	*/
 	rt->curvol = vol;
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index b8f3644..a93f681 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -347,6 +347,7 @@
 {
 	struct wl1273_core *core = radio->core;
 	int r = 0;
+	unsigned long t;
 
 	if (freq < WL1273_BAND_TX_LOW) {
 		dev_err(radio->dev,
@@ -378,11 +379,11 @@
 	reinit_completion(&radio->busy);
 
 	/* wait for the FR IRQ */
-	r = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(2000));
-	if (!r)
+	t = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(2000));
+	if (!t)
 		return -ETIMEDOUT;
 
-	dev_dbg(radio->dev, "WL1273_CHANL_SET: %d\n", r);
+	dev_dbg(radio->dev, "WL1273_CHANL_SET: %lu\n", t);
 
 	/* Enable the output power */
 	r = core->write(core, WL1273_POWER_ENB_SET, 1);
@@ -392,12 +393,12 @@
 	reinit_completion(&radio->busy);
 
 	/* wait for the POWER_ENB IRQ */
-	r = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000));
-	if (!r)
+	t = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000));
+	if (!t)
 		return -ETIMEDOUT;
 
 	radio->tx_frequency = freq;
-	dev_dbg(radio->dev, "WL1273_POWER_ENB_SET: %d\n", r);
+	dev_dbg(radio->dev, "WL1273_POWER_ENB_SET: %lu\n", t);
 
 	return	0;
 }
@@ -406,6 +407,7 @@
 {
 	struct wl1273_core *core = radio->core;
 	int r, f;
+	unsigned long t;
 
 	if (freq < radio->rangelow) {
 		dev_err(radio->dev,
@@ -446,8 +448,8 @@
 
 	reinit_completion(&radio->busy);
 
-	r = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(2000));
-	if (!r) {
+	t = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(2000));
+	if (!t) {
 		dev_err(radio->dev, "%s: TIMEOUT\n", __func__);
 		return -ETIMEDOUT;
 	}
@@ -826,9 +828,12 @@
 	if (r)
 		goto out;
 
+	/* wait for the FR IRQ */
 	wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000));
-	if (!(radio->irq_received & WL1273_BL_EVENT))
+	if (!(radio->irq_received & WL1273_BL_EVENT)) {
+		r = -ETIMEDOUT;
 		goto out;
+	}
 
 	radio->irq_received &= ~WL1273_BL_EVENT;
 
@@ -854,7 +859,9 @@
 	if (r)
 		goto out;
 
-	wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000));
+	/* wait for the FR IRQ */
+	if (!wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000)))
+		r = -ETIMEDOUT;
 out:
 	dev_dbg(radio->dev, "%s: Err: %d\n", __func__, r);
 	return r;
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index 909c3f9..1d827ad 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -208,6 +208,7 @@
 static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
 {
 	int retval;
+	unsigned long time_left;
 	bool timed_out = false;
 
 	/* start tuning */
@@ -219,9 +220,9 @@
 
 	/* wait till tune operation has completed */
 	reinit_completion(&radio->completion);
-	retval = wait_for_completion_timeout(&radio->completion,
-			msecs_to_jiffies(tune_timeout));
-	if (!retval)
+	time_left = wait_for_completion_timeout(&radio->completion,
+						msecs_to_jiffies(tune_timeout));
+	if (time_left == 0)
 		timed_out = true;
 
 	if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
@@ -301,6 +302,7 @@
 	int band, retval;
 	unsigned int freq;
 	bool timed_out = false;
+	unsigned long time_left;
 
 	/* set band */
 	if (seek->rangelow || seek->rangehigh) {
@@ -342,9 +344,9 @@
 
 	/* wait till tune operation has completed */
 	reinit_completion(&radio->completion);
-	retval = wait_for_completion_timeout(&radio->completion,
-			msecs_to_jiffies(seek_timeout));
-	if (!retval)
+	time_left = wait_for_completion_timeout(&radio->completion,
+						msecs_to_jiffies(seek_timeout));
+	if (time_left == 0)
 		timed_out = true;
 
 	if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
diff --git a/drivers/media/radio/si4713/Kconfig b/drivers/media/radio/si4713/Kconfig
index 19f9fde..fd87a7c 100644
--- a/drivers/media/radio/si4713/Kconfig
+++ b/drivers/media/radio/si4713/Kconfig
@@ -1,5 +1,5 @@
 config USB_SI4713
-	depends on !KERNEL_3_13
+	depends on !KERNEL_3_17
 	tristate "Silicon Labs Si4713 FM Radio Transmitter support with USB"
 	depends on m
 	depends on USB && I2C && RADIO_SI4713
@@ -16,7 +16,7 @@
 	  module will be called radio-usb-si4713.
 
 config PLATFORM_SI4713
-	depends on !KERNEL_3_13
+	depends on !KERNEL_3_17
 	tristate "Silicon Labs Si4713 FM Radio Transmitter support with I2C"
 	depends on m
 	depends on I2C && RADIO_SI4713
@@ -32,7 +32,7 @@
 	  module will be called radio-platform-si4713.
 
 config I2C_SI4713
-	depends on !KERNEL_3_13
+	depends on !KERNEL_3_17
 	tristate "Silicon Labs Si4713 FM Radio Transmitter support"
 	depends on m
 	depends on I2C && RADIO_SI4713
diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c
index c90004d..e9d03ac 100644
--- a/drivers/media/radio/si4713/si4713.c
+++ b/drivers/media/radio/si4713/si4713.c
@@ -383,7 +383,7 @@
 		}
 	}
 
-	if (!IS_ERR(sdev->gpio_reset)) {
+	if (sdev->gpio_reset) {
 		udelay(50);
 		gpiod_set_value(sdev->gpio_reset, 1);
 	}
@@ -407,8 +407,7 @@
 						SI4713_STC_INT | SI4713_CTS);
 		return err;
 	}
-	if (!IS_ERR(sdev->gpio_reset))
-		gpiod_set_value(sdev->gpio_reset, 0);
+	gpiod_set_value(sdev->gpio_reset, 0);
 
 
 	if (sdev->vdd) {
@@ -447,7 +446,7 @@
 		v4l2_dbg(1, debug, &sdev->sd, "Power down response: 0x%02x\n",
 				resp[0]);
 		v4l2_dbg(1, debug, &sdev->sd, "Device in reset mode\n");
-		if (!IS_ERR(sdev->gpio_reset))
+		if (sdev->gpio_reset)
 			gpiod_set_value(sdev->gpio_reset, 0);
 
 		if (sdev->vdd) {
@@ -1460,14 +1459,9 @@
 		goto exit;
 	}
 
-	sdev->gpio_reset = devm_gpiod_get(&client->dev, "reset");
-	if (!IS_ERR(sdev->gpio_reset)) {
-		gpiod_direction_output(sdev->gpio_reset, 0);
-	} else if (PTR_ERR(sdev->gpio_reset) == -ENOENT) {
-		dev_dbg(&client->dev, "No reset GPIO assigned\n");
-	} else if (PTR_ERR(sdev->gpio_reset) == -ENOSYS) {
-		dev_dbg(&client->dev, "No reset GPIO support\n");
-	} else {
+	sdev->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset",
+						   GPIOD_OUT_LOW);
+	if (IS_ERR(sdev->gpio_reset)) {
 		rval = PTR_ERR(sdev->gpio_reset);
 		dev_err(&client->dev, "Failed to request gpio: %d\n", rval);
 		goto exit;
diff --git a/drivers/media/radio/tea575x.c b/drivers/media/radio/tea575x.c
index f1a0867..43d1ea5 100644
--- a/drivers/media/radio/tea575x.c
+++ b/drivers/media/radio/tea575x.c
@@ -247,10 +247,9 @@
 	return 0;
 }
 
-static int vidioc_enum_freq_bands(struct file *file, void *priv,
-					 struct v4l2_frequency_band *band)
+int snd_tea575x_enum_freq_bands(struct snd_tea575x *tea,
+					struct v4l2_frequency_band *band)
 {
-	struct snd_tea575x *tea = video_drvdata(file);
 	int index;
 
 	if (band->tuner != 0)
@@ -279,18 +278,25 @@
 
 	return 0;
 }
+EXPORT_SYMBOL(snd_tea575x_enum_freq_bands);
 
-static int vidioc_g_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
+static int vidioc_enum_freq_bands(struct file *file, void *priv,
+					 struct v4l2_frequency_band *band)
 {
 	struct snd_tea575x *tea = video_drvdata(file);
+
+	return snd_tea575x_enum_freq_bands(tea, band);
+}
+
+int snd_tea575x_g_tuner(struct snd_tea575x *tea, struct v4l2_tuner *v)
+{
 	struct v4l2_frequency_band band_fm = { 0, };
 
 	if (v->index > 0)
 		return -EINVAL;
 
 	snd_tea575x_read(tea);
-	vidioc_enum_freq_bands(file, priv, &band_fm);
+	snd_tea575x_enum_freq_bands(tea, &band_fm);
 
 	memset(v, 0, sizeof(*v));
 	strlcpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name));
@@ -304,6 +310,15 @@
 	v->signal = tea->tuned ? 0xffff : 0;
 	return 0;
 }
+EXPORT_SYMBOL(snd_tea575x_g_tuner);
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+					struct v4l2_tuner *v)
+{
+	struct snd_tea575x *tea = video_drvdata(file);
+
+	return snd_tea575x_g_tuner(tea, v);
+}
 
 static int vidioc_s_tuner(struct file *file, void *priv,
 					const struct v4l2_tuner *v)
@@ -356,10 +371,9 @@
 	return 0;
 }
 
-static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
-					const struct v4l2_hw_freq_seek *a)
+int snd_tea575x_s_hw_freq_seek(struct file *file, struct snd_tea575x *tea,
+				const struct v4l2_hw_freq_seek *a)
 {
-	struct snd_tea575x *tea = video_drvdata(file);
 	unsigned long timeout;
 	int i, spacing;
 
@@ -442,6 +456,15 @@
 	snd_tea575x_set_freq(tea);
 	return -ENODATA;
 }
+EXPORT_SYMBOL(snd_tea575x_s_hw_freq_seek);
+
+static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
+					const struct v4l2_hw_freq_seek *a)
+{
+	struct snd_tea575x *tea = video_drvdata(file);
+
+	return snd_tea575x_s_hw_freq_seek(file, tea, a);
+}
 
 static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
diff --git a/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig
index 2ce3f37..ce4e24b 100644
--- a/drivers/media/radio/wl128x/Kconfig
+++ b/drivers/media/radio/wl128x/Kconfig
@@ -6,7 +6,7 @@
 	tristate "Texas Instruments WL128x FM Radio"
 	depends on m
 	depends on VIDEO_V4L2 && RFKILL && GPIOLIB && TTY
-	select TI_ST if NET
+	depends on TI_ST
 	help
 	Choose Y here if you have this FM radio chip.
 
diff --git a/drivers/media/radio/wl128x/fmdrv_rx.c b/drivers/media/radio/wl128x/fmdrv_rx.c
index 09632cb..cfaeb24 100644
--- a/drivers/media/radio/wl128x/fmdrv_rx.c
+++ b/drivers/media/radio/wl128x/fmdrv_rx.c
@@ -785,22 +785,6 @@
 	return 0;
 }
 
-/* Returns current RDS operation mode */
-int fm_rx_get_rds_system(struct fmdev *fmdev, u8 *rds_mode)
-{
-	if (fmdev->curr_fmmode != FM_MODE_RX)
-		return -EPERM;
-
-	if (rds_mode == NULL) {
-		fmerr("Invalid memory\n");
-		return -ENOMEM;
-	}
-
-	*rds_mode = fmdev->rx.rds_mode;
-
-	return 0;
-}
-
 /* Configures Alternate Frequency switch mode */
 int fm_rx_set_af_switch(struct fmdev *fmdev, u8 af_mode)
 {
diff --git a/drivers/media/radio/wl128x/fmdrv_rx.h b/drivers/media/radio/wl128x/fmdrv_rx.h
index 32add81..2392218 100644
--- a/drivers/media/radio/wl128x/fmdrv_rx.h
+++ b/drivers/media/radio/wl128x/fmdrv_rx.h
@@ -40,7 +40,6 @@
 int fm_rx_seek(struct fmdev *, u32, u32, u32);
 
 int fm_rx_get_rds_mode(struct fmdev *, u8 *);
-int fm_rx_get_rds_system(struct fmdev *, u8 *);
 int fm_rx_get_mute_mode(struct fmdev *, u8 *);
 int fm_rx_get_volume(struct fmdev *, u16 *);
 int fm_rx_get_band_freq_range(struct fmdev *,
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
index a5bd3f6..fb42f0f 100644
--- a/drivers/media/radio/wl128x/fmdrv_v4l2.c
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
@@ -36,7 +36,7 @@
 #include "fmdrv_rx.h"
 #include "fmdrv_tx.h"
 
-static struct video_device *gradio_dev;
+static struct video_device gradio_dev;
 static u8 radio_disconnected;
 
 /* -- V4L2 RADIO (/dev/radioX) device file operation interfaces --- */
@@ -517,7 +517,7 @@
 	.fops = &fm_drv_fops,
 	.ioctl_ops = &fm_drv_ioctl_ops,
 	.name = FM_DRV_NAME,
-	.release = video_device_release,
+	.release = video_device_release_empty,
 	/*
 	 * To ensure both the tuner and modulator ioctls are accessible we
 	 * set the vfl_dir to M2M to indicate this.
@@ -543,29 +543,21 @@
 	/* Init mutex for core locking */
 	mutex_init(&fmdev->mutex);
 
-	/* Allocate new video device */
-	gradio_dev = video_device_alloc();
-	if (NULL == gradio_dev) {
-		fmerr("Can't allocate video device\n");
-		return -ENOMEM;
-	}
-
 	/* Setup FM driver's V4L2 properties */
-	memcpy(gradio_dev, &fm_viddev_template, sizeof(fm_viddev_template));
+	gradio_dev = fm_viddev_template;
 
-	video_set_drvdata(gradio_dev, fmdev);
+	video_set_drvdata(&gradio_dev, fmdev);
 
-	gradio_dev->lock = &fmdev->mutex;
-	gradio_dev->v4l2_dev = &fmdev->v4l2_dev;
+	gradio_dev.lock = &fmdev->mutex;
+	gradio_dev.v4l2_dev = &fmdev->v4l2_dev;
 
 	/* Register with V4L2 subsystem as RADIO device */
-	if (video_register_device(gradio_dev, VFL_TYPE_RADIO, radio_nr)) {
-		video_device_release(gradio_dev);
+	if (video_register_device(&gradio_dev, VFL_TYPE_RADIO, radio_nr)) {
 		fmerr("Could not register video device\n");
 		return -ENOMEM;
 	}
 
-	fmdev->radio_dev = gradio_dev;
+	fmdev->radio_dev = &gradio_dev;
 
 	/* Register to v4l2 ctrl handler framework */
 	fmdev->radio_dev->ctrl_handler = &fmdev->ctrl_handler;
@@ -611,13 +603,13 @@
 	struct fmdev *fmdev;
 
 
-	fmdev = video_get_drvdata(gradio_dev);
+	fmdev = video_get_drvdata(&gradio_dev);
 
 	/* Unregister to v4l2 ctrl handler framework*/
 	v4l2_ctrl_handler_free(&fmdev->ctrl_handler);
 
 	/* Unregister RADIO device from V4L2 subsystem */
-	video_unregister_device(gradio_dev);
+	video_unregister_device(&gradio_dev);
 
 	v4l2_device_unregister(&fmdev->v4l2_dev);
 
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index e80f2c6..8d77e1c 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1195,16 +1195,6 @@
 	.shutdown = ene_shutdown,
 };
 
-static int __init ene_init(void)
-{
-	return pnp_register_driver(&ene_driver);
-}
-
-static void ene_exit(void)
-{
-	pnp_unregister_driver(&ene_driver);
-}
-
 module_param(sample_period, int, S_IRUGO);
 MODULE_PARM_DESC(sample_period, "Hardware sample period (50 us default)");
 
@@ -1226,5 +1216,4 @@
 MODULE_AUTHOR("Maxim Levitsky");
 MODULE_LICENSE("GPL");
 
-module_init(ene_init);
-module_exit(ene_exit);
+module_pnp_driver(ene_driver);
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index b516757..5c63c2e 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -684,16 +684,6 @@
 	.shutdown	= fintek_shutdown,
 };
 
-static int __init fintek_init(void)
-{
-	return pnp_register_driver(&fintek_driver);
-}
-
-static void __exit fintek_exit(void)
-{
-	pnp_unregister_driver(&fintek_driver);
-}
-
 module_param(debug, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Enable debugging output");
 
@@ -703,5 +693,4 @@
 MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
 MODULE_LICENSE("GPL");
 
-module_init(fintek_init);
-module_exit(fintek_exit);
+module_pnp_driver(fintek_driver);
diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig
index 74ba6bb..cf2339f 100644
--- a/drivers/media/rc/img-ir/Kconfig
+++ b/drivers/media/rc/img-ir/Kconfig
@@ -62,3 +62,18 @@
 	help
 	   Say Y here to enable support for the Sanyo protocol (used by Sanyo,
 	   Aiwa, Chinon remotes) in the ImgTec infrared decoder block.
+
+config IR_IMG_RC5
+	bool "Philips RC5 protocol support"
+	depends on IR_IMG_HW
+	help
+	   Say Y here to enable support for the RC5 protocol in the ImgTec
+	   infrared decoder block.
+
+config IR_IMG_RC6
+	bool "Philips RC6 protocol support"
+	depends on IR_IMG_HW
+	help
+	   Say Y here to enable support for the RC6 protocol in the ImgTec
+	   infrared decoder block.
+	   Note: This version only supports mode 0.
diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile
index df12d3e..777d6b7 100644
--- a/drivers/media/rc/img-ir/Makefile
+++ b/drivers/media/rc/img-ir/Makefile
@@ -6,6 +6,8 @@
 img-ir-$(CPTCFG_IR_IMG_SONY)	+= img-ir-sony.o
 img-ir-$(CPTCFG_IR_IMG_SHARP)	+= img-ir-sharp.o
 img-ir-$(CPTCFG_IR_IMG_SANYO)	+= img-ir-sanyo.o
+img-ir-$(CPTCFG_IR_IMG_RC5)	+= img-ir-rc5.o
+img-ir-$(CPTCFG_IR_IMG_RC6)	+= img-ir-rc6.o
 img-ir-objs			:= $(img-ir-y)
 
 obj-$(CPTCFG_IR_IMG)		+= img-ir.o
diff --git a/drivers/media/rc/img-ir/img-ir-core.c b/drivers/media/rc/img-ir/img-ir-core.c
index 77c78de..03fe080 100644
--- a/drivers/media/rc/img-ir/img-ir-core.c
+++ b/drivers/media/rc/img-ir/img-ir-core.c
@@ -110,16 +110,32 @@
 	priv->clk = devm_clk_get(&pdev->dev, "core");
 	if (IS_ERR(priv->clk))
 		dev_warn(&pdev->dev, "cannot get core clock resource\n");
+
+	/* Get sys clock */
+	priv->sys_clk = devm_clk_get(&pdev->dev, "sys");
+	if (IS_ERR(priv->sys_clk))
+		dev_warn(&pdev->dev, "cannot get sys clock resource\n");
 	/*
-	 * The driver doesn't need to know about the system ("sys") or power
-	 * modulation ("mod") clocks yet
+	 * Enabling the system clock before the register interface is
+	 * accessed. ISR shouldn't get called with Sys Clock disabled,
+	 * hence exiting probe with an error.
 	 */
+	if (!IS_ERR(priv->sys_clk)) {
+		error = clk_prepare_enable(priv->sys_clk);
+		if (error) {
+			dev_err(&pdev->dev, "cannot enable sys clock\n");
+			return error;
+		}
+	}
 
 	/* Set up raw & hw decoder */
 	error = img_ir_probe_raw(priv);
 	error2 = img_ir_probe_hw(priv);
-	if (error && error2)
-		return (error == -ENODEV) ? error2 : error;
+	if (error && error2) {
+		if (error == -ENODEV)
+			error = error2;
+		goto err_probe;
+	}
 
 	/* Get the IRQ */
 	priv->irq = irq;
@@ -139,6 +155,9 @@
 err_irq:
 	img_ir_remove_hw(priv);
 	img_ir_remove_raw(priv);
+err_probe:
+	if (!IS_ERR(priv->sys_clk))
+		clk_disable_unprepare(priv->sys_clk);
 	return error;
 }
 
@@ -146,12 +165,14 @@
 {
 	struct img_ir_priv *priv = platform_get_drvdata(pdev);
 
-	free_irq(priv->irq, img_ir_isr);
+	free_irq(priv->irq, priv);
 	img_ir_remove_hw(priv);
 	img_ir_remove_raw(priv);
 
 	if (!IS_ERR(priv->clk))
 		clk_disable_unprepare(priv->clk);
+	if (!IS_ERR(priv->sys_clk))
+		clk_disable_unprepare(priv->sys_clk);
 	return 0;
 }
 
diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
index 44e3c51..be4d1fd 100644
--- a/drivers/media/rc/img-ir/img-ir-hw.c
+++ b/drivers/media/rc/img-ir/img-ir-hw.c
@@ -42,6 +42,12 @@
 #ifdef CPTCFG_IR_IMG_SANYO
 	&img_ir_sanyo,
 #endif
+#ifdef CPTCFG_IR_IMG_RC5
+	&img_ir_rc5,
+#endif
+#ifdef CPTCFG_IR_IMG_RC6
+	&img_ir_rc6,
+#endif
 	NULL
 };
 
@@ -52,6 +58,11 @@
 
 #define IMG_IR_QUIRK_CODE_BROKEN	0x1	/* Decode is broken */
 #define IMG_IR_QUIRK_CODE_LEN_INCR	0x2	/* Bit length needs increment */
+/*
+ * The decoder generates rapid interrupts without actually having
+ * received any new data after an incomplete IR code is decoded.
+ */
+#define IMG_IR_QUIRK_CODE_IRQ		0x4
 
 /* functions for preprocessing timings, ensuring max is set */
 
@@ -542,6 +553,7 @@
 	 */
 	spin_unlock_irq(&priv->lock);
 	del_timer_sync(&hw->end_timer);
+	del_timer_sync(&hw->suspend_timer);
 	spin_lock_irq(&priv->lock);
 
 	hw->stopping = false;
@@ -806,20 +818,24 @@
 	struct img_ir_priv_hw *hw = &priv->hw;
 	const struct img_ir_decoder *dec = hw->decoder;
 	int ret = IMG_IR_SCANCODE;
-	u32 scancode;
-	enum rc_type protocol = RC_TYPE_UNKNOWN;
+	struct img_ir_scancode_req request;
+
+	request.protocol = RC_TYPE_UNKNOWN;
+	request.toggle   = 0;
 
 	if (dec->scancode)
-		ret = dec->scancode(len, raw, &protocol, &scancode, hw->enabled_protocols);
+		ret = dec->scancode(len, raw, hw->enabled_protocols, &request);
 	else if (len >= 32)
-		scancode = (u32)raw;
+		request.scancode = (u32)raw;
 	else if (len < 32)
-		scancode = (u32)raw & ((1 << len)-1);
+		request.scancode = (u32)raw & ((1 << len)-1);
 	dev_dbg(priv->dev, "data (%u bits) = %#llx\n",
 		len, (unsigned long long)raw);
 	if (ret == IMG_IR_SCANCODE) {
-		dev_dbg(priv->dev, "decoded scan code %#x\n", scancode);
-		rc_keydown(hw->rdev, protocol, scancode, 0);
+		dev_dbg(priv->dev, "decoded scan code %#x, toggle %u\n",
+			request.scancode, request.toggle);
+		rc_keydown(hw->rdev, request.protocol, request.scancode,
+			   request.toggle);
 		img_ir_end_repeat(priv);
 	} else if (ret == IMG_IR_REPEATCODE) {
 		if (hw->mode == IMG_IR_M_REPEATING) {
@@ -857,6 +873,29 @@
 	spin_unlock_irq(&priv->lock);
 }
 
+/*
+ * Timer function to re-enable the current protocol after it had been
+ * cleared when invalid interrupts were generated due to a quirk in the
+ * img-ir decoder.
+ */
+static void img_ir_suspend_timer(unsigned long arg)
+{
+	struct img_ir_priv *priv = (struct img_ir_priv *)arg;
+
+	spin_lock_irq(&priv->lock);
+	/*
+	 * Don't overwrite enabled valid/match IRQs if they have already been
+	 * changed by e.g. a filter change.
+	 */
+	if ((priv->hw.quirk_suspend_irq & IMG_IR_IRQ_EDGE) ==
+				img_ir_read(priv, IMG_IR_IRQ_ENABLE))
+		img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+					priv->hw.quirk_suspend_irq);
+	/* enable */
+	img_ir_write(priv, IMG_IR_CONTROL, priv->hw.reg_timings.ctrl);
+	spin_unlock_irq(&priv->lock);
+}
+
 #ifdef CONFIG_COMMON_CLK
 static void img_ir_change_frequency(struct img_ir_priv *priv,
 				    struct clk_notifier_data *change)
@@ -922,15 +961,38 @@
 	if (!hw->decoder)
 		return;
 
+	ct = hw->decoder->control.code_type;
+
 	ir_status = img_ir_read(priv, IMG_IR_STATUS);
-	if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)))
+	if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2))) {
+		if (!(priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_IRQ) ||
+				hw->stopping)
+			return;
+		/*
+		 * The below functionality is added as a work around to stop
+		 * multiple Interrupts generated when an incomplete IR code is
+		 * received by the decoder.
+		 * The decoder generates rapid interrupts without actually
+		 * having received any new data. After a single interrupt it's
+		 * expected to clear up, but instead multiple interrupts are
+		 * rapidly generated. only way to get out of this loop is to
+		 * reset the control register after a short delay.
+		 */
+		img_ir_write(priv, IMG_IR_CONTROL, 0);
+		hw->quirk_suspend_irq = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+		img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+			     hw->quirk_suspend_irq & IMG_IR_IRQ_EDGE);
+
+		/* Timer activated to re-enable the protocol. */
+		mod_timer(&hw->suspend_timer,
+			  jiffies + msecs_to_jiffies(5));
 		return;
+	}
 	ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2);
 	img_ir_write(priv, IMG_IR_STATUS, ir_status);
 
 	len = (ir_status & IMG_IR_RXDLEN) >> IMG_IR_RXDLEN_SHIFT;
 	/* some versions report wrong length for certain code types */
-	ct = hw->decoder->control.code_type;
 	if (hw->ct_quirks[ct] & IMG_IR_QUIRK_CODE_LEN_INCR)
 		++len;
 
@@ -972,7 +1034,7 @@
 	hw->ct_quirks[IMG_IR_CODETYPE_PULSELEN]
 		|= IMG_IR_QUIRK_CODE_LEN_INCR;
 	hw->ct_quirks[IMG_IR_CODETYPE_BIPHASE]
-		|= IMG_IR_QUIRK_CODE_BROKEN;
+		|= IMG_IR_QUIRK_CODE_IRQ;
 	hw->ct_quirks[IMG_IR_CODETYPE_2BITPULSEPOS]
 		|= IMG_IR_QUIRK_CODE_BROKEN;
 }
@@ -991,6 +1053,8 @@
 
 	/* Set up the end timer */
 	setup_timer(&hw->end_timer, img_ir_end_timer, (unsigned long)priv);
+	setup_timer(&hw->suspend_timer, img_ir_suspend_timer,
+				(unsigned long)priv);
 
 	/* Register a clock notifier */
 	if (!IS_ERR(priv->clk)) {
diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h
index 463bddf..27a2bf8 100644
--- a/drivers/media/rc/img-ir/img-ir-hw.h
+++ b/drivers/media/rc/img-ir/img-ir-hw.h
@@ -133,6 +133,20 @@
 #define IMG_IR_REPEATCODE	1	/* repeat the previous code */
 
 /**
+ * struct img_ir_scancode_req - Scancode request data.
+ * @protocol:	Protocol code of received message (defaults to
+ *		RC_TYPE_UNKNOWN).
+ * @scancode:	Scan code of received message (must be written by
+ *		handler if IMG_IR_SCANCODE is returned).
+ * @toggle:	Toggle bit (defaults to 0).
+ */
+struct img_ir_scancode_req {
+	enum rc_type protocol;
+	u32 scancode;
+	u8 toggle;
+};
+
+/**
  * struct img_ir_decoder - Decoder settings for an IR protocol.
  * @type:	Protocol types bitmap.
  * @tolerance:	Timing tolerance as a percentage (default 10%).
@@ -162,8 +176,8 @@
 	struct img_ir_control		control;
 
 	/* scancode logic */
-	int (*scancode)(int len, u64 raw, enum rc_type *protocol,
-			u32 *scancode, u64 enabled_protocols);
+	int (*scancode)(int len, u64 raw, u64 enabled_protocols,
+			struct img_ir_scancode_req *request);
 	int (*filter)(const struct rc_scancode_filter *in,
 		      struct img_ir_filter *out, u64 protocols);
 };
@@ -173,6 +187,8 @@
 extern struct img_ir_decoder img_ir_sony;
 extern struct img_ir_decoder img_ir_sharp;
 extern struct img_ir_decoder img_ir_sanyo;
+extern struct img_ir_decoder img_ir_rc5;
+extern struct img_ir_decoder img_ir_rc6;
 
 /**
  * struct img_ir_reg_timings - Reg values for decoder timings at clock rate.
@@ -204,6 +220,7 @@
  * @rdev:		Remote control device
  * @clk_nb:		Notifier block for clock notify events.
  * @end_timer:		Timer until repeat timeout.
+ * @suspend_timer:	Timer to re-enable protocol.
  * @decoder:		Current decoder settings.
  * @enabled_protocols:	Currently enabled protocols.
  * @clk_hz:		Current core clock rate in Hz.
@@ -214,12 +231,14 @@
  * @stopping:		Indicates that decoder is being taken down and timers
  *			should not be restarted.
  * @suspend_irqen:	Saved IRQ enable mask over suspend.
+ * @quirk_suspend_irq:	Saved IRQ enable mask over quirk suspend timer.
  */
 struct img_ir_priv_hw {
 	unsigned int			ct_quirks[4];
 	struct rc_dev			*rdev;
 	struct notifier_block		clk_nb;
 	struct timer_list		end_timer;
+	struct timer_list		suspend_timer;
 	const struct img_ir_decoder	*decoder;
 	u64				enabled_protocols;
 	unsigned long			clk_hz;
@@ -230,6 +249,7 @@
 	enum img_ir_mode		mode;
 	bool				stopping;
 	u32				suspend_irqen;
+	u32				quirk_suspend_irq;
 };
 
 static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw)
diff --git a/drivers/media/rc/img-ir/img-ir-jvc.c b/drivers/media/rc/img-ir/img-ir-jvc.c
index a60dda8..d3e2fc0 100644
--- a/drivers/media/rc/img-ir/img-ir-jvc.c
+++ b/drivers/media/rc/img-ir/img-ir-jvc.c
@@ -12,8 +12,8 @@
 #include "img-ir-hw.h"
 
 /* Convert JVC data to a scancode */
-static int img_ir_jvc_scancode(int len, u64 raw, enum rc_type *protocol,
-			       u32 *scancode, u64 enabled_protocols)
+static int img_ir_jvc_scancode(int len, u64 raw, u64 enabled_protocols,
+			       struct img_ir_scancode_req *request)
 {
 	unsigned int cust, data;
 
@@ -23,8 +23,8 @@
 	cust = (raw >> 0) & 0xff;
 	data = (raw >> 8) & 0xff;
 
-	*protocol = RC_TYPE_JVC;
-	*scancode = cust << 8 | data;
+	request->protocol = RC_TYPE_JVC;
+	request->scancode = cust << 8 | data;
 	return IMG_IR_SCANCODE;
 }
 
diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c
index 7398975..27a7ea8 100644
--- a/drivers/media/rc/img-ir/img-ir-nec.c
+++ b/drivers/media/rc/img-ir/img-ir-nec.c
@@ -13,8 +13,8 @@
 #include <linux/bitrev.h>
 
 /* Convert NEC data to a scancode */
-static int img_ir_nec_scancode(int len, u64 raw, enum rc_type *protocol,
-			       u32 *scancode, u64 enabled_protocols)
+static int img_ir_nec_scancode(int len, u64 raw, u64 enabled_protocols,
+			       struct img_ir_scancode_req *request)
 {
 	unsigned int addr, addr_inv, data, data_inv;
 	/* a repeat code has no data */
@@ -30,23 +30,23 @@
 	if ((data_inv ^ data) != 0xff) {
 		/* 32-bit NEC (used by Apple and TiVo remotes) */
 		/* scan encoding: as transmitted, MSBit = first received bit */
-		*scancode = bitrev8(addr)     << 24 |
-			    bitrev8(addr_inv) << 16 |
-			    bitrev8(data)     <<  8 |
-			    bitrev8(data_inv);
+		request->scancode = bitrev8(addr)     << 24 |
+				bitrev8(addr_inv) << 16 |
+				bitrev8(data)     <<  8 |
+				bitrev8(data_inv);
 	} else if ((addr_inv ^ addr) != 0xff) {
 		/* Extended NEC */
 		/* scan encoding: AAaaDD */
-		*scancode = addr     << 16 |
-			    addr_inv <<  8 |
-			    data;
+		request->scancode = addr     << 16 |
+				addr_inv <<  8 |
+				data;
 	} else {
 		/* Normal NEC */
 		/* scan encoding: AADD */
-		*scancode = addr << 8 |
-			    data;
+		request->scancode = addr << 8 |
+				data;
 	}
-	*protocol = RC_TYPE_NEC;
+	request->protocol = RC_TYPE_NEC;
 	return IMG_IR_SCANCODE;
 }
 
diff --git a/drivers/media/rc/img-ir/img-ir-rc5.c b/drivers/media/rc/img-ir/img-ir-rc5.c
new file mode 100644
index 0000000..a8a28a3
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-rc5.c
@@ -0,0 +1,88 @@
+/*
+ * ImgTec IR Decoder setup for Philips RC-5 protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ *
+ * 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 "img-ir-hw.h"
+
+/* Convert RC5 data to a scancode */
+static int img_ir_rc5_scancode(int len, u64 raw, u64 enabled_protocols,
+				struct img_ir_scancode_req *request)
+{
+	unsigned int addr, cmd, tgl, start;
+
+	/* Quirk in the decoder shifts everything by 2 to the left. */
+	raw   >>= 2;
+
+	start	=  (raw >> 13)	& 0x01;
+	tgl	=  (raw >> 11)	& 0x01;
+	addr	=  (raw >>  6)	& 0x1f;
+	cmd	=   raw		& 0x3f;
+	/*
+	 * 12th bit is used to extend the command in extended RC5 and has
+	 * no effect on standard RC5.
+	 */
+	cmd	+= ((raw >> 12) & 0x01) ? 0 : 0x40;
+
+	if (!start)
+		return -EINVAL;
+
+	request->protocol = RC_TYPE_RC5;
+	request->scancode = addr << 8 | cmd;
+	request->toggle   = tgl;
+	return IMG_IR_SCANCODE;
+}
+
+/* Convert RC5 scancode to RC5 data filter */
+static int img_ir_rc5_filter(const struct rc_scancode_filter *in,
+				 struct img_ir_filter *out, u64 protocols)
+{
+	/* Not supported by the hw. */
+	return -EINVAL;
+}
+
+/*
+ * RC-5 decoder
+ * see http://www.sbprojects.com/knowledge/ir/rc5.php
+ */
+struct img_ir_decoder img_ir_rc5 = {
+	.type      = RC_BIT_RC5,
+	.control   = {
+		.bitoriend2	= 1,
+		.code_type	= IMG_IR_CODETYPE_BIPHASE,
+		.decodend2	= 1,
+	},
+	/* main timings */
+	.tolerance	= 16,
+	.unit		= 888888, /* 1/36k*32=888.888microseconds */
+	.timings	= {
+		/* 10 symbol */
+		.s10 = {
+			.pulse	= { 1 },
+			.space	= { 1 },
+		},
+
+		/* 11 symbol */
+		.s11 = {
+			.pulse	= { 1 },
+			.space	= { 1 },
+		},
+
+		/* free time */
+		.ft  = {
+			.minlen = 14,
+			.maxlen = 14,
+			.ft_min = 5,
+		},
+	},
+
+	/* scancode logic */
+	.scancode	= img_ir_rc5_scancode,
+	.filter		= img_ir_rc5_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-rc6.c b/drivers/media/rc/img-ir/img-ir-rc6.c
new file mode 100644
index 0000000..de1e275
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-rc6.c
@@ -0,0 +1,117 @@
+/*
+ * ImgTec IR Decoder setup for Philips RC-6 protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ *
+ * 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 "img-ir-hw.h"
+
+/* Convert RC6 data to a scancode */
+static int img_ir_rc6_scancode(int len, u64 raw, u64 enabled_protocols,
+				struct img_ir_scancode_req *request)
+{
+	unsigned int addr, cmd, mode, trl1, trl2;
+
+	/*
+	 * Due to a side effect of the decoder handling the double length
+	 * Trailer bit, the header information is a bit scrambled, and the
+	 * raw data is shifted incorrectly.
+	 * This workaround effectively recovers the header bits.
+	 *
+	 * The Header field should look like this:
+	 *
+	 * StartBit ModeBit2 ModeBit1 ModeBit0 TrailerBit
+	 *
+	 * But what we get is:
+	 *
+	 * ModeBit2 ModeBit1 ModeBit0 TrailerBit1 TrailerBit2
+	 *
+	 * The start bit is not important to recover the scancode.
+	 */
+
+	raw	>>= 27;
+
+	trl1	= (raw >>  17)	& 0x01;
+	trl2	= (raw >>  16)	& 0x01;
+
+	mode	= (raw >>  18)	& 0x07;
+	addr	= (raw >>   8)	& 0xff;
+	cmd	=  raw		& 0xff;
+
+	/*
+	 * Due to the above explained irregularity the trailer bits cannot
+	 * have the same value.
+	 */
+	if (trl1 == trl2)
+		return -EINVAL;
+
+	/* Only mode 0 supported for now */
+	if (mode)
+		return -EINVAL;
+
+	request->protocol = RC_TYPE_RC6_0;
+	request->scancode = addr << 8 | cmd;
+	request->toggle	  = trl2;
+	return IMG_IR_SCANCODE;
+}
+
+/* Convert RC6 scancode to RC6 data filter */
+static int img_ir_rc6_filter(const struct rc_scancode_filter *in,
+				 struct img_ir_filter *out, u64 protocols)
+{
+	/* Not supported by the hw. */
+	return -EINVAL;
+}
+
+/*
+ * RC-6 decoder
+ * see http://www.sbprojects.com/knowledge/ir/rc6.php
+ */
+struct img_ir_decoder img_ir_rc6 = {
+	.type		= RC_BIT_RC6_0,
+	.control	= {
+		.bitorien	= 1,
+		.code_type	= IMG_IR_CODETYPE_BIPHASE,
+		.decoden	= 1,
+		.decodinpol	= 1,
+	},
+	/* main timings */
+	.tolerance	= 20,
+	/*
+	 * Due to a quirk in the img-ir decoder, default header values do
+	 * not work, the values described below were extracted from
+	 * successful RTL test cases.
+	 */
+	.timings	= {
+		/* leader symbol */
+		.ldr = {
+			.pulse	= { 650 },
+			.space	= { 660 },
+		},
+		/* 0 symbol */
+		.s00 = {
+			.pulse	= { 370 },
+			.space	= { 370 },
+		},
+		/* 01 symbol */
+		.s01 = {
+			.pulse	= { 370 },
+			.space	= { 370 },
+		},
+		/* free time */
+		.ft  = {
+			.minlen = 21,
+			.maxlen = 21,
+			.ft_min = 2666,	/* 2.666 ms */
+		},
+	},
+
+	/* scancode logic */
+	.scancode	= img_ir_rc6_scancode,
+	.filter		= img_ir_rc6_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-sanyo.c b/drivers/media/rc/img-ir/img-ir-sanyo.c
index 6b0653e..f394994 100644
--- a/drivers/media/rc/img-ir/img-ir-sanyo.c
+++ b/drivers/media/rc/img-ir/img-ir-sanyo.c
@@ -23,8 +23,8 @@
 #include "img-ir-hw.h"
 
 /* Convert Sanyo data to a scancode */
-static int img_ir_sanyo_scancode(int len, u64 raw, enum rc_type *protocol,
-				 u32 *scancode, u64 enabled_protocols)
+static int img_ir_sanyo_scancode(int len, u64 raw, u64 enabled_protocols,
+				 struct img_ir_scancode_req *request)
 {
 	unsigned int addr, addr_inv, data, data_inv;
 	/* a repeat code has no data */
@@ -44,8 +44,8 @@
 		return -EINVAL;
 
 	/* Normal Sanyo */
-	*protocol = RC_TYPE_SANYO;
-	*scancode = addr << 8 | data;
+	request->protocol = RC_TYPE_SANYO;
+	request->scancode = addr << 8 | data;
 	return IMG_IR_SCANCODE;
 }
 
diff --git a/drivers/media/rc/img-ir/img-ir-sharp.c b/drivers/media/rc/img-ir/img-ir-sharp.c
index 3300a38..fe5acc4 100644
--- a/drivers/media/rc/img-ir/img-ir-sharp.c
+++ b/drivers/media/rc/img-ir/img-ir-sharp.c
@@ -12,8 +12,8 @@
 #include "img-ir-hw.h"
 
 /* Convert Sharp data to a scancode */
-static int img_ir_sharp_scancode(int len, u64 raw, enum rc_type *protocol,
-				 u32 *scancode, u64 enabled_protocols)
+static int img_ir_sharp_scancode(int len, u64 raw, u64 enabled_protocols,
+				 struct img_ir_scancode_req *request)
 {
 	unsigned int addr, cmd, exp, chk;
 
@@ -32,8 +32,8 @@
 		/* probably the second half of the message */
 		return -EINVAL;
 
-	*protocol = RC_TYPE_SHARP;
-	*scancode = addr << 8 | cmd;
+	request->protocol = RC_TYPE_SHARP;
+	request->scancode = addr << 8 | cmd;
 	return IMG_IR_SCANCODE;
 }
 
diff --git a/drivers/media/rc/img-ir/img-ir-sony.c b/drivers/media/rc/img-ir/img-ir-sony.c
index 3a0f17b..7f7375f 100644
--- a/drivers/media/rc/img-ir/img-ir-sony.c
+++ b/drivers/media/rc/img-ir/img-ir-sony.c
@@ -12,8 +12,8 @@
 #include "img-ir-hw.h"
 
 /* Convert Sony data to a scancode */
-static int img_ir_sony_scancode(int len, u64 raw, enum rc_type *protocol,
-				u32 *scancode, u64 enabled_protocols)
+static int img_ir_sony_scancode(int len, u64 raw, u64 enabled_protocols,
+				struct img_ir_scancode_req *request)
 {
 	unsigned int dev, subdev, func;
 
@@ -25,7 +25,7 @@
 		raw    >>= 7;
 		dev    = raw & 0x1f;	/* next 5 bits */
 		subdev = 0;
-		*protocol = RC_TYPE_SONY12;
+		request->protocol = RC_TYPE_SONY12;
 		break;
 	case 15:
 		if (!(enabled_protocols & RC_BIT_SONY15))
@@ -34,7 +34,7 @@
 		raw    >>= 7;
 		dev    = raw & 0xff;	/* next 8 bits */
 		subdev = 0;
-		*protocol = RC_TYPE_SONY15;
+		request->protocol = RC_TYPE_SONY15;
 		break;
 	case 20:
 		if (!(enabled_protocols & RC_BIT_SONY20))
@@ -44,12 +44,12 @@
 		dev    = raw & 0x1f;	/* next 5 bits */
 		raw    >>= 5;
 		subdev = raw & 0xff;	/* next 8 bits */
-		*protocol = RC_TYPE_SONY20;
+		request->protocol = RC_TYPE_SONY20;
 		break;
 	default:
 		return -EINVAL;
 	}
-	*scancode = dev << 16 | subdev << 8 | func;
+	request->scancode = dev << 16 | subdev << 8 | func;
 	return IMG_IR_SCANCODE;
 }
 
diff --git a/drivers/media/rc/img-ir/img-ir.h b/drivers/media/rc/img-ir/img-ir.h
index 2ddf560..f1387c0 100644
--- a/drivers/media/rc/img-ir/img-ir.h
+++ b/drivers/media/rc/img-ir/img-ir.h
@@ -138,6 +138,7 @@
  * @dev:		Platform device.
  * @irq:		IRQ number.
  * @clk:		Input clock.
+ * @sys_clk:		System clock.
  * @reg_base:		Iomem base address of IR register block.
  * @lock:		Protects IR registers and variables in this struct.
  * @raw:		Driver data for raw decoder.
@@ -147,6 +148,7 @@
 	struct device		*dev;
 	int			irq;
 	struct clk		*clk;
+	struct clk		*sys_clk;
 	void __iomem		*reg_base;
 	spinlock_t		lock;
 
diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
index 3a3192c..7ce369e 100644
--- a/drivers/media/rc/ir-hix5hd2.c
+++ b/drivers/media/rc/ir-hix5hd2.c
@@ -17,14 +17,6 @@
 #include <media/rc-core.h>
 #include <linux/of.h>
 
-/* Allow the driver to compile on all architectures */
-#ifndef writel_relaxed
-# define writel_relaxed writel
-#endif
-#ifndef readl_relaxed
-# define readl_relaxed readl
-#endif
-
 #define IR_ENABLE		0x00
 #define IR_CONFIG		0x04
 #define CNT_LEADS		0x08
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 56abf91..0f30190 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1708,21 +1708,10 @@
 	.shutdown	= ite_shutdown,
 };
 
-static int __init ite_init(void)
-{
-	return pnp_register_driver(&ite_driver);
-}
-
-static void __exit ite_exit(void)
-{
-	pnp_unregister_driver(&ite_driver);
-}
-
 MODULE_DEVICE_TABLE(pnp, ite_ids);
 MODULE_DESCRIPTION("ITE Tech Inc. IT8712F/ITE8512F CIR driver");
 
 MODULE_AUTHOR("Juan J. Garcia de Soria <skandalfo@gmail.com>");
 MODULE_LICENSE("GPL");
 
-module_init(ite_init);
-module_exit(ite_exit);
+module_pnp_driver(ite_driver);
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 1e0545a..4de0e85 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -553,14 +553,14 @@
 	if (!ir->attached)
 		return POLLERR;
 
-	poll_wait(file, &ir->buf->wait_poll, wait);
+	if (ir->buf) {
+		poll_wait(file, &ir->buf->wait_poll, wait);
 
-	if (ir->buf)
 		if (lirc_buffer_empty(ir->buf))
 			ret = 0;
 		else
 			ret = POLLIN | POLLRDNORM;
-	else
+	} else
 		ret = POLLERR;
 
 	dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n",
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 9c2c863..85af7a8 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -1219,16 +1219,6 @@
 	.shutdown	= nvt_shutdown,
 };
 
-static int __init nvt_init(void)
-{
-	return pnp_register_driver(&nvt_driver);
-}
-
-static void __exit nvt_exit(void)
-{
-	pnp_unregister_driver(&nvt_driver);
-}
-
 module_param(debug, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Enable debugging output");
 
@@ -1238,5 +1228,4 @@
 MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
 MODULE_LICENSE("GPL");
 
-module_init(nvt_init);
-module_exit(nvt_exit);
+module_pnp_driver(nvt_driver);
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 46ee99c..0269c37 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -1025,16 +1025,16 @@
 		goto out;
 	}
 
-	if (new_protocols == old_protocols) {
-		rc = len;
-		goto out;
+	if (new_protocols != old_protocols) {
+		*current_protocols = new_protocols;
+		IR_dprintk(1, "Protocols changed to 0x%llx\n",
+			   (long long)new_protocols);
 	}
 
-	*current_protocols = new_protocols;
-	IR_dprintk(1, "Protocols changed to 0x%llx\n", (long long)new_protocols);
-
 	/*
-	 * If the protocol is changed the filter needs updating.
+	 * If a protocol change was attempted the filter may need updating, even
+	 * if the actual protocol mask hasn't changed (since the driver may have
+	 * cleared the filter).
 	 * Try setting the same filter with the new protocol (if any).
 	 * Fall back to clearing the filter.
 	 */
diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 340f7f5..7830aef 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -23,6 +23,7 @@
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/of_platform.h>
+#include <linux/reset.h>
 #include <media/rc-core.h>
 
 #define SUNXI_IR_DEV "sunxi-ir"
@@ -55,12 +56,12 @@
 #define REG_RXINT_RAI_EN		BIT(4)
 
 /* Rx FIFO available byte level */
-#define REG_RXINT_RAL(val)    (((val) << 8) & (GENMASK(11, 8)))
+#define REG_RXINT_RAL(val)    ((val) << 8)
 
 /* Rx Interrupt Status */
 #define SUNXI_IR_RXSTA_REG    0x30
 /* RX FIFO Get Available Counter */
-#define REG_RXSTA_GET_AC(val) (((val) >> 8) & (GENMASK(5, 0)))
+#define REG_RXSTA_GET_AC(val) (((val) >> 8) & (ir->fifo_size * 2 - 1))
 /* Clear all interrupt status value */
 #define REG_RXSTA_CLEARALL    0xff
 
@@ -71,10 +72,6 @@
 /* CIR_REG register idle threshold */
 #define REG_CIR_ITHR(val)    (((val) << 8) & (GENMASK(15, 8)))
 
-/* Hardware supported fifo size */
-#define SUNXI_IR_FIFO_SIZE    16
-/* How many messages in FIFO trigger IRQ */
-#define TRIGGER_LEVEL         8
 /* Required frequency for IR0 or IR1 clock in CIR mode */
 #define SUNXI_IR_BASE_CLK     8000000
 /* Frequency after IR internal divider  */
@@ -93,8 +90,10 @@
 	struct rc_dev   *rc;
 	void __iomem    *base;
 	int             irq;
+	int		fifo_size;
 	struct clk      *clk;
 	struct clk      *apb_clk;
+	struct reset_control *rst;
 	const char      *map_name;
 };
 
@@ -113,11 +112,11 @@
 	/* clean all pending statuses */
 	writel(status | REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
 
-	if (status & REG_RXINT_RAI_EN) {
+	if (status & (REG_RXINT_RAI_EN | REG_RXINT_RPEI_EN)) {
 		/* How many messages in fifo */
 		rc  = REG_RXSTA_GET_AC(status);
 		/* Sanity check */
-		rc = rc > SUNXI_IR_FIFO_SIZE ? SUNXI_IR_FIFO_SIZE : rc;
+		rc = rc > ir->fifo_size ? ir->fifo_size : rc;
 		/* If we have data */
 		for (cnt = 0; cnt < rc; cnt++) {
 			/* for each bit in fifo */
@@ -154,6 +153,11 @@
 	if (!ir)
 		return -ENOMEM;
 
+	if (of_device_is_compatible(dn, "allwinner,sun5i-a13-ir"))
+		ir->fifo_size = 64;
+	else
+		ir->fifo_size = 16;
+
 	/* Clock */
 	ir->apb_clk = devm_clk_get(dev, "apb");
 	if (IS_ERR(ir->apb_clk)) {
@@ -166,15 +170,29 @@
 		return PTR_ERR(ir->clk);
 	}
 
+	/* Reset (optional) */
+	ir->rst = devm_reset_control_get_optional(dev, NULL);
+	if (IS_ERR(ir->rst)) {
+		ret = PTR_ERR(ir->rst);
+		if (ret == -EPROBE_DEFER)
+			return ret;
+		ir->rst = NULL;
+	} else {
+		ret = reset_control_deassert(ir->rst);
+		if (ret)
+			return ret;
+	}
+
 	ret = clk_set_rate(ir->clk, SUNXI_IR_BASE_CLK);
 	if (ret) {
 		dev_err(dev, "set ir base clock failed!\n");
-		return ret;
+		goto exit_reset_assert;
 	}
 
 	if (clk_prepare_enable(ir->apb_clk)) {
 		dev_err(dev, "try to enable apb_ir_clk failed\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto exit_reset_assert;
 	}
 
 	if (clk_prepare_enable(ir->clk)) {
@@ -255,7 +273,7 @@
 	 * level
 	 */
 	writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN |
-	       REG_RXINT_RAI_EN | REG_RXINT_RAL(TRIGGER_LEVEL - 1),
+	       REG_RXINT_RAI_EN | REG_RXINT_RAL(ir->fifo_size / 2 - 1),
 	       ir->base + SUNXI_IR_RXINT_REG);
 
 	/* Enable IR Module */
@@ -271,6 +289,9 @@
 	clk_disable_unprepare(ir->clk);
 exit_clkdisable_apb_clk:
 	clk_disable_unprepare(ir->apb_clk);
+exit_reset_assert:
+	if (ir->rst)
+		reset_control_assert(ir->rst);
 
 	return ret;
 }
@@ -282,6 +303,8 @@
 
 	clk_disable_unprepare(ir->clk);
 	clk_disable_unprepare(ir->apb_clk);
+	if (ir->rst)
+		reset_control_assert(ir->rst);
 
 	spin_lock_irqsave(&ir->ir_lock, flags);
 	/* disable IR IRQ */
@@ -298,6 +321,7 @@
 
 static const struct of_device_id sunxi_ir_match[] = {
 	{ .compatible = "allwinner,sun4i-a10-ir", },
+	{ .compatible = "allwinner,sun5i-a13-ir", },
 	{},
 };
 
diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig
index 86602cc..0fec0cc 100644
--- a/drivers/media/tuners/Kconfig
+++ b/drivers/media/tuners/Kconfig
@@ -256,16 +256,6 @@
 	help
 	  FCI FC2580 silicon tuner driver.
 
-config MEDIA_TUNER_M88TS2022
-	depends on !KERNEL_3_4
-	tristate "Montage M88TS2022 silicon tuner"
-	depends on m
-	depends on MEDIA_SUPPORT && I2C
-	depends on REGMAP_I2C
-	default m if !MEDIA_SUBDRV_AUTOSELECT
-	help
-	  Montage M88TS2022 silicon tuner driver.
-
 config MEDIA_TUNER_M88RS6000T
 	depends on !KERNEL_3_3
 	tristate "Montage M88RS6000 internal tuner"
diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile
index bc7b685..9efa3b3 100644
--- a/drivers/media/tuners/Makefile
+++ b/drivers/media/tuners/Makefile
@@ -33,7 +33,6 @@
 obj-$(CPTCFG_MEDIA_TUNER_FC2580) += fc2580.o
 obj-$(CPTCFG_MEDIA_TUNER_TUA9001) += tua9001.o
 obj-$(CPTCFG_MEDIA_TUNER_SI2157) += si2157.o
-obj-$(CPTCFG_MEDIA_TUNER_M88TS2022) += m88ts2022.o
 obj-$(CPTCFG_MEDIA_TUNER_FC0011) += fc0011.o
 obj-$(CPTCFG_MEDIA_TUNER_FC0012) += fc0012.o
 obj-$(CPTCFG_MEDIA_TUNER_FC0013) += fc0013.o
diff --git a/drivers/media/tuners/fc0011.h b/drivers/media/tuners/fc0011.h
index d06bc45..e87fadb 100644
--- a/drivers/media/tuners/fc0011.h
+++ b/drivers/media/tuners/fc0011.h
@@ -23,7 +23,7 @@
 	FC0011_FE_CALLBACK_RESET,
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_FC0011)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_FC0011)
 struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
 				   struct i2c_adapter *i2c,
 				   const struct fc0011_config *config);
diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h
index a44b892..f96e457 100644
--- a/drivers/media/tuners/fc0012.h
+++ b/drivers/media/tuners/fc0012.h
@@ -49,7 +49,7 @@
 	bool clock_out;
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_FC0012)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_FC0012)
 extern struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe,
 					struct i2c_adapter *i2c,
 					const struct fc0012_config *cfg);
diff --git a/drivers/media/tuners/fc0013.h b/drivers/media/tuners/fc0013.h
index 8f140fc..36394f7 100644
--- a/drivers/media/tuners/fc0013.h
+++ b/drivers/media/tuners/fc0013.h
@@ -26,7 +26,7 @@
 #include "dvb_frontend.h"
 #include "fc001x-common.h"
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_FC0013)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_FC0013)
 extern struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe,
 					struct i2c_adapter *i2c,
 					u8 i2c_address, int dual_master,
diff --git a/drivers/media/tuners/fc2580.h b/drivers/media/tuners/fc2580.h
index 5b302bf..84bf83f 100644
--- a/drivers/media/tuners/fc2580.h
+++ b/drivers/media/tuners/fc2580.h
@@ -37,7 +37,7 @@
 	u32 clock;
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_FC2580)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_FC2580)
 extern struct dvb_frontend *fc2580_attach(struct dvb_frontend *fe,
 	struct i2c_adapter *i2c, const struct fc2580_config *cfg);
 #else
diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c
deleted file mode 100644
index 066e543..0000000
--- a/drivers/media/tuners/m88ts2022.c
+++ /dev/null
@@ -1,579 +0,0 @@
-/*
- * Montage M88TS2022 silicon tuner driver
- *
- * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
- *
- *    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.
- *
- * Some calculations are taken from existing TS2020 driver.
- */
-
-#include "m88ts2022_priv.h"
-
-static int m88ts2022_cmd(struct m88ts2022_dev *dev, int op, int sleep, u8 reg,
-		u8 mask, u8 val, u8 *reg_val)
-{
-	int ret, i;
-	unsigned int utmp;
-	struct m88ts2022_reg_val reg_vals[] = {
-		{0x51, 0x1f - op},
-		{0x51, 0x1f},
-		{0x50, 0x00 + op},
-		{0x50, 0x00},
-	};
-
-	for (i = 0; i < 2; i++) {
-		dev_dbg(&dev->client->dev,
-				"i=%d op=%02x reg=%02x mask=%02x val=%02x\n",
-				i, op, reg, mask, val);
-
-		for (i = 0; i < ARRAY_SIZE(reg_vals); i++) {
-			ret = regmap_write(dev->regmap, reg_vals[i].reg,
-					reg_vals[i].val);
-			if (ret)
-				goto err;
-		}
-
-		usleep_range(sleep * 1000, sleep * 10000);
-
-		ret = regmap_read(dev->regmap, reg, &utmp);
-		if (ret)
-			goto err;
-
-		if ((utmp & mask) != val)
-			break;
-	}
-
-	if (reg_val)
-		*reg_val = utmp;
-err:
-	return ret;
-}
-
-static int m88ts2022_set_params(struct dvb_frontend *fe)
-{
-	struct m88ts2022_dev *dev = fe->tuner_priv;
-	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-	int ret;
-	unsigned int utmp, frequency_khz, frequency_offset_khz, f_3db_hz;
-	unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n, gdiv28;
-	u8 buf[3], u8tmp, cap_code, lpf_gm, lpf_mxdiv, div_max, div_min;
-	u16 u16tmp;
-
-	dev_dbg(&dev->client->dev,
-			"frequency=%d symbol_rate=%d rolloff=%d\n",
-			c->frequency, c->symbol_rate, c->rolloff);
-	/*
-	 * Integer-N PLL synthesizer
-	 * kHz is used for all calculations to keep calculations within 32-bit
-	 */
-	f_ref_khz = DIV_ROUND_CLOSEST(dev->cfg.clock, 1000);
-	div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000);
-
-	if (c->symbol_rate < 5000000)
-		frequency_offset_khz = 3000; /* 3 MHz */
-	else
-		frequency_offset_khz = 0;
-
-	frequency_khz = c->frequency + frequency_offset_khz;
-
-	if (frequency_khz < 1103000) {
-		div_out = 4;
-		u8tmp = 0x1b;
-	} else {
-		div_out = 2;
-		u8tmp = 0x0b;
-	}
-
-	buf[0] = u8tmp;
-	buf[1] = 0x40;
-	ret = regmap_bulk_write(dev->regmap, 0x10, buf, 2);
-	if (ret)
-		goto err;
-
-	f_vco_khz = frequency_khz * div_out;
-	pll_n = f_vco_khz * div_ref / f_ref_khz;
-	pll_n += pll_n % 2;
-	dev->frequency_khz = pll_n * f_ref_khz / div_ref / div_out;
-
-	if (pll_n < 4095)
-		u16tmp = pll_n - 1024;
-	else if (pll_n < 6143)
-		u16tmp = pll_n + 1024;
-	else
-		u16tmp = pll_n + 3072;
-
-	buf[0] = (u16tmp >> 8) & 0x3f;
-	buf[1] = (u16tmp >> 0) & 0xff;
-	buf[2] = div_ref - 8;
-	ret = regmap_bulk_write(dev->regmap, 0x01, buf, 3);
-	if (ret)
-		goto err;
-
-	dev_dbg(&dev->client->dev,
-			"frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n",
-			dev->frequency_khz, dev->frequency_khz - c->frequency,
-			f_vco_khz, pll_n, div_ref, div_out);
-
-	ret = m88ts2022_cmd(dev, 0x10, 5, 0x15, 0x40, 0x00, NULL);
-	if (ret)
-		goto err;
-
-	ret = regmap_read(dev->regmap, 0x14, &utmp);
-	if (ret)
-		goto err;
-
-	utmp &= 0x7f;
-	if (utmp < 64) {
-		ret = regmap_update_bits(dev->regmap, 0x10, 0x80, 0x80);
-		if (ret)
-			goto err;
-
-		ret = regmap_write(dev->regmap, 0x11, 0x6f);
-		if (ret)
-			goto err;
-
-		ret = m88ts2022_cmd(dev, 0x10, 5, 0x15, 0x40, 0x00, NULL);
-		if (ret)
-			goto err;
-	}
-
-	ret = regmap_read(dev->regmap, 0x14, &utmp);
-	if (ret)
-		goto err;
-
-	utmp &= 0x1f;
-	if (utmp > 19) {
-		ret = regmap_update_bits(dev->regmap, 0x10, 0x02, 0x00);
-		if (ret)
-			goto err;
-	}
-
-	ret = m88ts2022_cmd(dev, 0x08, 5, 0x3c, 0xff, 0x00, NULL);
-	if (ret)
-		goto err;
-
-	ret = regmap_write(dev->regmap, 0x25, 0x00);
-	if (ret)
-		goto err;
-
-	ret = regmap_write(dev->regmap, 0x27, 0x70);
-	if (ret)
-		goto err;
-
-	ret = regmap_write(dev->regmap, 0x41, 0x09);
-	if (ret)
-		goto err;
-
-	ret = regmap_write(dev->regmap, 0x08, 0x0b);
-	if (ret)
-		goto err;
-
-	/* filters */
-	gdiv28 = DIV_ROUND_CLOSEST(f_ref_khz * 1694U, 1000000U);
-
-	ret = regmap_write(dev->regmap, 0x04, gdiv28);
-	if (ret)
-		goto err;
-
-	ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
-	if (ret)
-		goto err;
-
-	cap_code = u8tmp & 0x3f;
-
-	ret = regmap_write(dev->regmap, 0x41, 0x0d);
-	if (ret)
-		goto err;
-
-	ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
-	if (ret)
-		goto err;
-
-	u8tmp &= 0x3f;
-	cap_code = (cap_code + u8tmp) / 2;
-	gdiv28 = gdiv28 * 207 / (cap_code * 2 + 151);
-	div_max = gdiv28 * 135 / 100;
-	div_min = gdiv28 * 78 / 100;
-	div_max = clamp_val(div_max, 0U, 63U);
-
-	f_3db_hz = mult_frac(c->symbol_rate, 135, 200);
-	f_3db_hz +=  2000000U + (frequency_offset_khz * 1000U);
-	f_3db_hz = clamp(f_3db_hz, 7000000U, 40000000U);
-
-#define LPF_COEFF 3200U
-	lpf_gm = DIV_ROUND_CLOSEST(f_3db_hz * gdiv28, LPF_COEFF * f_ref_khz);
-	lpf_gm = clamp_val(lpf_gm, 1U, 23U);
-
-	lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz);
-	if (lpf_mxdiv < div_min)
-		lpf_mxdiv = DIV_ROUND_CLOSEST(++lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz);
-	lpf_mxdiv = clamp_val(lpf_mxdiv, 0U, div_max);
-
-	ret = regmap_write(dev->regmap, 0x04, lpf_mxdiv);
-	if (ret)
-		goto err;
-
-	ret = regmap_write(dev->regmap, 0x06, lpf_gm);
-	if (ret)
-		goto err;
-
-	ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
-	if (ret)
-		goto err;
-
-	cap_code = u8tmp & 0x3f;
-
-	ret = regmap_write(dev->regmap, 0x41, 0x09);
-	if (ret)
-		goto err;
-
-	ret = m88ts2022_cmd(dev, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
-	if (ret)
-		goto err;
-
-	u8tmp &= 0x3f;
-	cap_code = (cap_code + u8tmp) / 2;
-
-	u8tmp = cap_code | 0x80;
-	ret = regmap_write(dev->regmap, 0x25, u8tmp);
-	if (ret)
-		goto err;
-
-	ret = regmap_write(dev->regmap, 0x27, 0x30);
-	if (ret)
-		goto err;
-
-	ret = regmap_write(dev->regmap, 0x08, 0x09);
-	if (ret)
-		goto err;
-
-	ret = m88ts2022_cmd(dev, 0x01, 20, 0x21, 0xff, 0x00, NULL);
-	if (ret)
-		goto err;
-err:
-	if (ret)
-		dev_dbg(&dev->client->dev, "failed=%d\n", ret);
-
-	return ret;
-}
-
-static int m88ts2022_init(struct dvb_frontend *fe)
-{
-	struct m88ts2022_dev *dev = fe->tuner_priv;
-	int ret, i;
-	u8 u8tmp;
-	static const struct m88ts2022_reg_val reg_vals[] = {
-		{0x7d, 0x9d},
-		{0x7c, 0x9a},
-		{0x7a, 0x76},
-		{0x3b, 0x01},
-		{0x63, 0x88},
-		{0x61, 0x85},
-		{0x22, 0x30},
-		{0x30, 0x40},
-		{0x20, 0x23},
-		{0x24, 0x02},
-		{0x12, 0xa0},
-	};
-
-	dev_dbg(&dev->client->dev, "\n");
-
-	ret = regmap_write(dev->regmap, 0x00, 0x01);
-	if (ret)
-		goto err;
-
-	ret = regmap_write(dev->regmap, 0x00, 0x03);
-	if (ret)
-		goto err;
-
-	switch (dev->cfg.clock_out) {
-	case M88TS2022_CLOCK_OUT_DISABLED:
-		u8tmp = 0x60;
-		break;
-	case M88TS2022_CLOCK_OUT_ENABLED:
-		u8tmp = 0x70;
-		ret = regmap_write(dev->regmap, 0x05, dev->cfg.clock_out_div);
-		if (ret)
-			goto err;
-		break;
-	case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT:
-		u8tmp = 0x6c;
-		break;
-	default:
-		goto err;
-	}
-
-	ret = regmap_write(dev->regmap, 0x42, u8tmp);
-	if (ret)
-		goto err;
-
-	if (dev->cfg.loop_through)
-		u8tmp = 0xec;
-	else
-		u8tmp = 0x6c;
-
-	ret = regmap_write(dev->regmap, 0x62, u8tmp);
-	if (ret)
-		goto err;
-
-	for (i = 0; i < ARRAY_SIZE(reg_vals); i++) {
-		ret = regmap_write(dev->regmap, reg_vals[i].reg, reg_vals[i].val);
-		if (ret)
-			goto err;
-	}
-err:
-	if (ret)
-		dev_dbg(&dev->client->dev, "failed=%d\n", ret);
-	return ret;
-}
-
-static int m88ts2022_sleep(struct dvb_frontend *fe)
-{
-	struct m88ts2022_dev *dev = fe->tuner_priv;
-	int ret;
-
-	dev_dbg(&dev->client->dev, "\n");
-
-	ret = regmap_write(dev->regmap, 0x00, 0x00);
-	if (ret)
-		goto err;
-err:
-	if (ret)
-		dev_dbg(&dev->client->dev, "failed=%d\n", ret);
-	return ret;
-}
-
-static int m88ts2022_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
-	struct m88ts2022_dev *dev = fe->tuner_priv;
-
-	dev_dbg(&dev->client->dev, "\n");
-
-	*frequency = dev->frequency_khz;
-	return 0;
-}
-
-static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
-	struct m88ts2022_dev *dev = fe->tuner_priv;
-
-	dev_dbg(&dev->client->dev, "\n");
-
-	*frequency = 0; /* Zero-IF */
-	return 0;
-}
-
-static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
-{
-	struct m88ts2022_dev *dev = fe->tuner_priv;
-	int ret;
-	u16 gain, u16tmp;
-	unsigned int utmp, gain1, gain2, gain3;
-
-	ret = regmap_read(dev->regmap, 0x3d, &utmp);
-	if (ret)
-		goto err;
-
-	gain1 = (utmp >> 0) & 0x1f;
-	gain1 = clamp(gain1, 0U, 15U);
-
-	ret = regmap_read(dev->regmap, 0x21, &utmp);
-	if (ret)
-		goto err;
-
-	gain2 = (utmp >> 0) & 0x1f;
-	gain2 = clamp(gain2, 2U, 16U);
-
-	ret = regmap_read(dev->regmap, 0x66, &utmp);
-	if (ret)
-		goto err;
-
-	gain3 = (utmp >> 3) & 0x07;
-	gain3 = clamp(gain3, 0U, 6U);
-
-	gain = gain1 * 265 + gain2 * 338 + gain3 * 285;
-
-	/* scale value to 0x0000-0xffff */
-	u16tmp = (0xffff - gain);
-	u16tmp = clamp_val(u16tmp, 59000U, 61500U);
-
-	*strength = (u16tmp - 59000) * 0xffff / (61500 - 59000);
-err:
-	if (ret)
-		dev_dbg(&dev->client->dev, "failed=%d\n", ret);
-	return ret;
-}
-
-static const struct dvb_tuner_ops m88ts2022_tuner_ops = {
-	.info = {
-		.name          = "Montage M88TS2022",
-		.frequency_min = 950000,
-		.frequency_max = 2150000,
-	},
-
-	.init = m88ts2022_init,
-	.sleep = m88ts2022_sleep,
-	.set_params = m88ts2022_set_params,
-
-	.get_frequency = m88ts2022_get_frequency,
-	.get_if_frequency = m88ts2022_get_if_frequency,
-	.get_rf_strength = m88ts2022_get_rf_strength,
-};
-
-static int m88ts2022_probe(struct i2c_client *client,
-		const struct i2c_device_id *id)
-{
-	struct m88ts2022_config *cfg = client->dev.platform_data;
-	struct dvb_frontend *fe = cfg->fe;
-	struct m88ts2022_dev *dev;
-	int ret;
-	u8 u8tmp;
-	unsigned int utmp;
-	static const struct regmap_config regmap_config = {
-		.reg_bits = 8,
-		.val_bits = 8,
-	};
-
-	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-	if (!dev) {
-		ret = -ENOMEM;
-		dev_err(&client->dev, "kzalloc() failed\n");
-		goto err;
-	}
-
-	memcpy(&dev->cfg, cfg, sizeof(struct m88ts2022_config));
-	dev->client = client;
-	dev->regmap = devm_regmap_init_i2c(client, &regmap_config);
-	if (IS_ERR(dev->regmap)) {
-		ret = PTR_ERR(dev->regmap);
-		goto err;
-	}
-
-	/* check if the tuner is there */
-	ret = regmap_read(dev->regmap, 0x00, &utmp);
-	if (ret)
-		goto err;
-
-	if ((utmp & 0x03) == 0x00) {
-		ret = regmap_write(dev->regmap, 0x00, 0x01);
-		if (ret)
-			goto err;
-
-		usleep_range(2000, 50000);
-	}
-
-	ret = regmap_write(dev->regmap, 0x00, 0x03);
-	if (ret)
-		goto err;
-
-	usleep_range(2000, 50000);
-
-	ret = regmap_read(dev->regmap, 0x00, &utmp);
-	if (ret)
-		goto err;
-
-	dev_dbg(&dev->client->dev, "chip_id=%02x\n", utmp);
-
-	switch (utmp) {
-	case 0xc3:
-	case 0x83:
-		break;
-	default:
-		ret = -ENODEV;
-		goto err;
-	}
-
-	switch (dev->cfg.clock_out) {
-	case M88TS2022_CLOCK_OUT_DISABLED:
-		u8tmp = 0x60;
-		break;
-	case M88TS2022_CLOCK_OUT_ENABLED:
-		u8tmp = 0x70;
-		ret = regmap_write(dev->regmap, 0x05, dev->cfg.clock_out_div);
-		if (ret)
-			goto err;
-		break;
-	case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT:
-		u8tmp = 0x6c;
-		break;
-	default:
-		ret = -EINVAL;
-		goto err;
-	}
-
-	ret = regmap_write(dev->regmap, 0x42, u8tmp);
-	if (ret)
-		goto err;
-
-	if (dev->cfg.loop_through)
-		u8tmp = 0xec;
-	else
-		u8tmp = 0x6c;
-
-	ret = regmap_write(dev->regmap, 0x62, u8tmp);
-	if (ret)
-		goto err;
-
-	/* sleep */
-	ret = regmap_write(dev->regmap, 0x00, 0x00);
-	if (ret)
-		goto err;
-
-	dev_info(&dev->client->dev, "Montage M88TS2022 successfully identified\n");
-
-	fe->tuner_priv = dev;
-	memcpy(&fe->ops.tuner_ops, &m88ts2022_tuner_ops,
-			sizeof(struct dvb_tuner_ops));
-
-	i2c_set_clientdata(client, dev);
-	return 0;
-err:
-	dev_dbg(&client->dev, "failed=%d\n", ret);
-	kfree(dev);
-	return ret;
-}
-
-static int m88ts2022_remove(struct i2c_client *client)
-{
-	struct m88ts2022_dev *dev = i2c_get_clientdata(client);
-	struct dvb_frontend *fe = dev->cfg.fe;
-
-	dev_dbg(&client->dev, "\n");
-
-	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
-	fe->tuner_priv = NULL;
-	kfree(dev);
-
-	return 0;
-}
-
-static const struct i2c_device_id m88ts2022_id[] = {
-	{"m88ts2022", 0},
-	{}
-};
-MODULE_DEVICE_TABLE(i2c, m88ts2022_id);
-
-static struct i2c_driver m88ts2022_driver = {
-	.driver = {
-		.owner	= THIS_MODULE,
-		.name	= "m88ts2022",
-	},
-	.probe		= m88ts2022_probe,
-	.remove		= m88ts2022_remove,
-	.id_table	= m88ts2022_id,
-};
-
-module_i2c_driver(m88ts2022_driver);
-
-MODULE_DESCRIPTION("Montage M88TS2022 silicon tuner driver");
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/m88ts2022.h b/drivers/media/tuners/m88ts2022.h
deleted file mode 100644
index 659fa1b..0000000
--- a/drivers/media/tuners/m88ts2022.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Montage M88TS2022 silicon tuner driver
- *
- * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
- *
- *    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.
- */
-
-#ifndef M88TS2022_H
-#define M88TS2022_H
-
-#include "dvb_frontend.h"
-
-struct m88ts2022_config {
-	/*
-	 * clock
-	 * 16000000 - 32000000
-	 */
-	u32 clock;
-
-	/*
-	 * RF loop-through
-	 */
-	u8 loop_through:1;
-
-	/*
-	 * clock output
-	 */
-#define M88TS2022_CLOCK_OUT_DISABLED        0
-#define M88TS2022_CLOCK_OUT_ENABLED         1
-#define M88TS2022_CLOCK_OUT_ENABLED_XTALOUT 2
-	u8 clock_out:2;
-
-	/*
-	 * clock output divider
-	 * 1 - 31
-	 */
-	u8 clock_out_div:5;
-
-	/*
-	 * pointer to DVB frontend
-	 */
-	struct dvb_frontend *fe;
-};
-
-#endif
diff --git a/drivers/media/tuners/m88ts2022_priv.h b/drivers/media/tuners/m88ts2022_priv.h
deleted file mode 100644
index feeb5ad..0000000
--- a/drivers/media/tuners/m88ts2022_priv.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Montage M88TS2022 silicon tuner driver
- *
- * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
- *
- *    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.
- */
-
-#ifndef M88TS2022_PRIV_H
-#define M88TS2022_PRIV_H
-
-#include "m88ts2022.h"
-#include <linux/regmap.h>
-
-struct m88ts2022_dev {
-	struct m88ts2022_config cfg;
-	struct i2c_client *client;
-	struct regmap *regmap;
-	u32 frequency_khz;
-};
-
-struct m88ts2022_reg_val {
-	u8 reg;
-	u8 val;
-};
-
-#endif
diff --git a/drivers/media/tuners/max2165.h b/drivers/media/tuners/max2165.h
index 6aec4b9..4105829 100644
--- a/drivers/media/tuners/max2165.h
+++ b/drivers/media/tuners/max2165.h
@@ -32,7 +32,7 @@
 	u8 osc_clk; /* in MHz, selectable values: 4,16,18,20,22,24,26,28 */
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_MAX2165)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_MAX2165)
 extern struct dvb_frontend *max2165_attach(struct dvb_frontend *fe,
 	struct i2c_adapter *i2c,
 	struct max2165_config *cfg);
diff --git a/drivers/media/tuners/mc44s803.h b/drivers/media/tuners/mc44s803.h
index 74829ee..2fe2017 100644
--- a/drivers/media/tuners/mc44s803.h
+++ b/drivers/media/tuners/mc44s803.h
@@ -32,7 +32,7 @@
 	u8 dig_out;
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_MC44S803)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_MC44S803)
 extern struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe,
 	 struct i2c_adapter *i2c, struct mc44s803_config *cfg);
 #else
diff --git a/drivers/media/tuners/msi001.c b/drivers/media/tuners/msi001.c
index 26019e7..74cfc3c 100644
--- a/drivers/media/tuners/msi001.c
+++ b/drivers/media/tuners/msi001.c
@@ -408,7 +408,7 @@
 				s->mixer_gain->cur.val, s->if_gain->val);
 		break;
 	default:
-		dev_dbg(&s->spi->dev, "unkown control %d\n", ctrl->id);
+		dev_dbg(&s->spi->dev, "unknown control %d\n", ctrl->id);
 		ret = -EINVAL;
 	}
 
diff --git a/drivers/media/tuners/mt2060.h b/drivers/media/tuners/mt2060.h
index 004d048..8fa5050 100644
--- a/drivers/media/tuners/mt2060.h
+++ b/drivers/media/tuners/mt2060.h
@@ -30,7 +30,7 @@
 	u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_MT2060)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_MT2060)
 extern struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1);
 #else
 static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1)
diff --git a/drivers/media/tuners/mt2063.h b/drivers/media/tuners/mt2063.h
index 170083d..2a5bbec 100644
--- a/drivers/media/tuners/mt2063.h
+++ b/drivers/media/tuners/mt2063.h
@@ -8,7 +8,7 @@
 	u32 refclock;
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_MT2063)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_MT2063)
 struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
 				   struct mt2063_config *config,
 				   struct i2c_adapter *i2c);
diff --git a/drivers/media/tuners/mt20xx.c b/drivers/media/tuners/mt20xx.c
index 0e74e97..9e03104 100644
--- a/drivers/media/tuners/mt20xx.c
+++ b/drivers/media/tuners/mt20xx.c
@@ -660,11 +660,3 @@
 MODULE_DESCRIPTION("Microtune tuner driver");
 MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
 MODULE_LICENSE("GPL");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/tuners/mt20xx.h b/drivers/media/tuners/mt20xx.h
index 23a5191..51cd459 100644
--- a/drivers/media/tuners/mt20xx.h
+++ b/drivers/media/tuners/mt20xx.h
@@ -20,7 +20,7 @@
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_MT20XX)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_MT20XX)
 extern struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
 					     struct i2c_adapter* i2c_adap,
 					     u8 i2c_addr);
diff --git a/drivers/media/tuners/mt2131.c b/drivers/media/tuners/mt2131.c
index f83b0c1..6e2cdd2 100644
--- a/drivers/media/tuners/mt2131.c
+++ b/drivers/media/tuners/mt2131.c
@@ -294,8 +294,3 @@
 MODULE_AUTHOR("Steven Toth");
 MODULE_DESCRIPTION("Microtune MT2131 silicon tuner driver");
 MODULE_LICENSE("GPL");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/tuners/mt2131.h b/drivers/media/tuners/mt2131.h
index c6fdd11..24c77d2 100644
--- a/drivers/media/tuners/mt2131.h
+++ b/drivers/media/tuners/mt2131.h
@@ -30,7 +30,7 @@
 	u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_MT2131)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_MT2131)
 extern struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe,
 					  struct i2c_adapter *i2c,
 					  struct mt2131_config *cfg,
@@ -47,8 +47,3 @@
 #endif /* CPTCFG_MEDIA_TUNER_MT2131 */
 
 #endif /* __MT2131_H__ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/tuners/mt2131_priv.h b/drivers/media/tuners/mt2131_priv.h
index 62aeedf..91283b5 100644
--- a/drivers/media/tuners/mt2131_priv.h
+++ b/drivers/media/tuners/mt2131_priv.h
@@ -41,8 +41,3 @@
 };
 
 #endif /* __MT2131_PRIV_H__ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/tuners/mt2266.h b/drivers/media/tuners/mt2266.h
index 2cbd340..a38df11 100644
--- a/drivers/media/tuners/mt2266.h
+++ b/drivers/media/tuners/mt2266.h
@@ -24,7 +24,7 @@
 	u8 i2c_address;
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_MT2266)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_MT2266)
 extern struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg);
 #else
 static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg)
diff --git a/drivers/media/tuners/mxl5005s.h b/drivers/media/tuners/mxl5005s.h
index 4f5dafb..fffdf15 100644
--- a/drivers/media/tuners/mxl5005s.h
+++ b/drivers/media/tuners/mxl5005s.h
@@ -118,7 +118,7 @@
 	u8 AgcMasterByte;
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_MXL5005S)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_MXL5005S)
 extern struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
 					    struct i2c_adapter *i2c,
 					    struct mxl5005s_config *config);
diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c
index 1810ad6..f4ae04c 100644
--- a/drivers/media/tuners/mxl5007t.c
+++ b/drivers/media/tuners/mxl5007t.c
@@ -938,11 +938,3 @@
 MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("0.2");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/tuners/mxl5007t.h b/drivers/media/tuners/mxl5007t.h
index d2598ff..4f35b08 100644
--- a/drivers/media/tuners/mxl5007t.h
+++ b/drivers/media/tuners/mxl5007t.h
@@ -77,7 +77,7 @@
 	unsigned int clk_out_enable:1;
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_MXL5007T)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_MXL5007T)
 extern struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe,
 					    struct i2c_adapter *i2c, u8 addr,
 					    struct mxl5007t_config *cfg);
@@ -93,12 +93,3 @@
 #endif
 
 #endif /* __MXL5007T_H__ */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
-
diff --git a/drivers/media/tuners/qt1010.h b/drivers/media/tuners/qt1010.h
index 886451f..f1c560e 100644
--- a/drivers/media/tuners/qt1010.h
+++ b/drivers/media/tuners/qt1010.h
@@ -36,7 +36,7 @@
  * @param cfg  tuner hw based configuration
  * @return fe  pointer on success, NULL on failure
  */
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_QT1010)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_QT1010)
 extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe,
 					  struct i2c_adapter *i2c,
 					  struct qt1010_config *cfg);
diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c
index 8e040cf..71159a5 100644
--- a/drivers/media/tuners/r820t.c
+++ b/drivers/media/tuners/r820t.c
@@ -775,6 +775,19 @@
 		div_buf_cur = 0x30;	/* 11, 150u */
 		filter_cur = 0x40;	/* 10, low */
 		break;
+	case SYS_DVBC_ANNEX_A:
+		mixer_top = 0x24;       /* mixer top:13 , top-1, low-discharge */
+		lna_top = 0xe5;
+		lna_vth_l = 0x62;
+		mixer_vth_l = 0x75;
+		air_cable1_in = 0x60;
+		cable2_in = 0x00;
+		pre_dect = 0x40;
+		lna_discharge = 14;
+		cp_cur = 0x38;          /* 111, auto */
+		div_buf_cur = 0x30;     /* 11, 150u */
+		filter_cur = 0x40;      /* 10, low */
+		break;
 	default: /* DVB-T 8M */
 		mixer_top = 0x24;	/* mixer top:13 , top-1, low-discharge */
 		lna_top = 0xe5;		/* detect bw 3, lna top:4, predet top:2 */
@@ -957,7 +970,7 @@
 		ext_enable = 0x40;	/* r30[6], ext enable; r30[5]:0 ext at lna max */
 		loop_through = 0x00;	/* r5[7], lt on */
 		lt_att = 0x00;		/* r31[7], lt att enable */
-		flt_ext_widest = 0x00;	/* r15[7]: flt_ext_wide off */
+		flt_ext_widest = 0x80;	/* r15[7]: flt_ext_wide on */
 		polyfil_cur = 0x60;	/* r25[6:5]:min */
 	} else if (delsys == SYS_DVBC_ANNEX_A) {
 		if_khz = 5070;
@@ -971,6 +984,18 @@
 		lt_att = 0x00;		/* r31[7], lt att enable */
 		flt_ext_widest = 0x00;	/* r15[7]: flt_ext_wide off */
 		polyfil_cur = 0x60;	/* r25[6:5]:min */
+	} else if (delsys == SYS_DVBC_ANNEX_C) {
+		if_khz = 4063;
+		filt_cal_lo = 55000;
+		filt_gain = 0x10;	/* +3db, 6mhz on */
+		img_r = 0x00;		/* image negative */
+		filt_q = 0x10;		/* r10[4]:low q(1'b1) */
+		hp_cor = 0x6a;		/* 1.7m disable, +0cap, 1.0mhz */
+		ext_enable = 0x40;	/* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */
+		loop_through = 0x00;	/* r5[7], lt on */
+		lt_att = 0x00;		/* r31[7], lt att enable */
+		flt_ext_widest = 0x80;	/* r15[7]: flt_ext_wide on */
+		polyfil_cur = 0x60;	/* r25[6:5]:min */
 	} else {
 		if (bw <= 6) {
 			if_khz = 3570;
@@ -1186,7 +1211,7 @@
 	if (rc < 0)
 		return rc;
 
-	return ((data[3] & 0x0f) << 1) + ((data[3] & 0xf0) >> 4);
+	return ((data[3] & 0x08) << 1) + ((data[3] & 0xf0) >> 4);
 }
 
 #if 0
diff --git a/drivers/media/tuners/r820t.h b/drivers/media/tuners/r820t.h
index 331e113..317e600 100644
--- a/drivers/media/tuners/r820t.h
+++ b/drivers/media/tuners/r820t.h
@@ -42,7 +42,7 @@
 	bool use_predetect;
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_R820T)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_R820T)
 struct dvb_frontend *r820t_attach(struct dvb_frontend *fe,
 				  struct i2c_adapter *i2c,
 				  const struct r820t_config *cfg);
diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
index 2180de9..d74ae26 100644
--- a/drivers/media/tuners/si2157.c
+++ b/drivers/media/tuners/si2157.c
@@ -19,16 +19,17 @@
 static const struct dvb_tuner_ops si2157_ops;
 
 /* execute firmware command */
-static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd)
+static int si2157_cmd_execute(struct i2c_client *client, struct si2157_cmd *cmd)
 {
+	struct si2157_dev *dev = i2c_get_clientdata(client);
 	int ret;
 	unsigned long timeout;
 
-	mutex_lock(&s->i2c_mutex);
+	mutex_lock(&dev->i2c_mutex);
 
 	if (cmd->wlen) {
 		/* write cmd and args for firmware */
-		ret = i2c_master_send(s->client, cmd->args, cmd->wlen);
+		ret = i2c_master_send(client, cmd->args, cmd->wlen);
 		if (ret < 0) {
 			goto err_mutex_unlock;
 		} else if (ret != cmd->wlen) {
@@ -42,7 +43,7 @@
 		#define TIMEOUT 80
 		timeout = jiffies + msecs_to_jiffies(TIMEOUT);
 		while (!time_after(jiffies, timeout)) {
-			ret = i2c_master_recv(s->client, cmd->args, cmd->rlen);
+			ret = i2c_master_recv(client, cmd->args, cmd->rlen);
 			if (ret < 0) {
 				goto err_mutex_unlock;
 			} else if (ret != cmd->rlen) {
@@ -55,7 +56,7 @@
 				break;
 		}
 
-		dev_dbg(&s->client->dev, "cmd execution took %d ms\n",
+		dev_dbg(&client->dev, "cmd execution took %d ms\n",
 				jiffies_to_msecs(jiffies) -
 				(jiffies_to_msecs(timeout) - TIMEOUT));
 
@@ -65,35 +66,32 @@
 		}
 	}
 
-	ret = 0;
+	mutex_unlock(&dev->i2c_mutex);
+	return 0;
 
 err_mutex_unlock:
-	mutex_unlock(&s->i2c_mutex);
-	if (ret)
-		goto err;
-
-	return 0;
-err:
-	dev_dbg(&s->client->dev, "failed=%d\n", ret);
+	mutex_unlock(&dev->i2c_mutex);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int si2157_init(struct dvb_frontend *fe)
 {
-	struct si2157 *s = fe->tuner_priv;
+	struct i2c_client *client = fe->tuner_priv;
+	struct si2157_dev *dev = i2c_get_clientdata(client);
 	int ret, len, remaining;
 	struct si2157_cmd cmd;
-	const struct firmware *fw = NULL;
-	u8 *fw_file;
+	const struct firmware *fw;
+	const char *fw_name;
 	unsigned int chip_id;
 
-	dev_dbg(&s->client->dev, "\n");
+	dev_dbg(&client->dev, "\n");
 
-	if (s->fw_loaded)
+	if (dev->fw_loaded)
 		goto warm;
 
 	/* power up */
-	if (s->chiptype == SI2157_CHIPTYPE_SI2146) {
+	if (dev->chiptype == SI2157_CHIPTYPE_SI2146) {
 		memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9);
 		cmd.wlen = 9;
 	} else {
@@ -101,7 +99,7 @@
 		cmd.wlen = 15;
 	}
 	cmd.rlen = 1;
-	ret = si2157_cmd_execute(s, &cmd);
+	ret = si2157_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
@@ -109,7 +107,7 @@
 	memcpy(cmd.args, "\x02", 1);
 	cmd.wlen = 1;
 	cmd.rlen = 13;
-	ret = si2157_cmd_execute(s, &cmd);
+	ret = si2157_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
@@ -125,121 +123,134 @@
 	switch (chip_id) {
 	case SI2158_A20:
 	case SI2148_A20:
-		fw_file = SI2158_A20_FIRMWARE;
+		fw_name = SI2158_A20_FIRMWARE;
 		break;
 	case SI2157_A30:
 	case SI2147_A30:
 	case SI2146_A10:
-		goto skip_fw_download;
+		fw_name = NULL;
+		break;
 	default:
-		dev_err(&s->client->dev,
-				"unknown chip version Si21%d-%c%c%c\n",
+		dev_err(&client->dev, "unknown chip version Si21%d-%c%c%c\n",
 				cmd.args[2], cmd.args[1],
 				cmd.args[3], cmd.args[4]);
 		ret = -EINVAL;
 		goto err;
 	}
 
-	/* cold state - try to download firmware */
-	dev_info(&s->client->dev, "found a '%s' in cold state\n",
-			si2157_ops.info.name);
+	dev_info(&client->dev, "found a 'Silicon Labs Si21%d-%c%c%c'\n",
+			cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]);
+
+	if (fw_name == NULL)
+		goto skip_fw_download;
 
 	/* request the firmware, this will block and timeout */
-	ret = request_firmware(&fw, fw_file, &s->client->dev);
+	ret = request_firmware(&fw, fw_name, &client->dev);
 	if (ret) {
-		dev_err(&s->client->dev, "firmware file '%s' not found\n",
-				fw_file);
+		dev_err(&client->dev, "firmware file '%s' not found\n",
+				fw_name);
 		goto err;
 	}
 
 	/* firmware should be n chunks of 17 bytes */
 	if (fw->size % 17 != 0) {
-		dev_err(&s->client->dev, "firmware file '%s' is invalid\n",
-				fw_file);
+		dev_err(&client->dev, "firmware file '%s' is invalid\n",
+				fw_name);
 		ret = -EINVAL;
-		goto fw_release_exit;
+		goto err_release_firmware;
 	}
 
-	dev_info(&s->client->dev, "downloading firmware from file '%s'\n",
-			fw_file);
+	dev_info(&client->dev, "downloading firmware from file '%s'\n",
+			fw_name);
 
 	for (remaining = fw->size; remaining > 0; remaining -= 17) {
 		len = fw->data[fw->size - remaining];
 		memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len);
 		cmd.wlen = len;
 		cmd.rlen = 1;
-		ret = si2157_cmd_execute(s, &cmd);
+		ret = si2157_cmd_execute(client, &cmd);
 		if (ret) {
-			dev_err(&s->client->dev,
-					"firmware download failed=%d\n",
+			dev_err(&client->dev, "firmware download failed %d\n",
 					ret);
-			goto fw_release_exit;
+			goto err_release_firmware;
 		}
 	}
 
 	release_firmware(fw);
-	fw = NULL;
 
 skip_fw_download:
 	/* reboot the tuner with new firmware? */
 	memcpy(cmd.args, "\x01\x01", 2);
 	cmd.wlen = 2;
 	cmd.rlen = 1;
-	ret = si2157_cmd_execute(s, &cmd);
+	ret = si2157_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
-	s->fw_loaded = true;
+	/* query firmware version */
+	memcpy(cmd.args, "\x11", 1);
+	cmd.wlen = 1;
+	cmd.rlen = 10;
+	ret = si2157_cmd_execute(client, &cmd);
+	if (ret)
+		goto err;
+
+	dev_info(&client->dev, "firmware version: %c.%c.%d\n",
+			cmd.args[6], cmd.args[7], cmd.args[8]);
+
+	dev->fw_loaded = true;
 
 warm:
-	s->active = true;
+	dev->active = true;
 	return 0;
 
-fw_release_exit:
+err_release_firmware:
 	release_firmware(fw);
 err:
-	dev_dbg(&s->client->dev, "failed=%d\n", ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int si2157_sleep(struct dvb_frontend *fe)
 {
-	struct si2157 *s = fe->tuner_priv;
+	struct i2c_client *client = fe->tuner_priv;
+	struct si2157_dev *dev = i2c_get_clientdata(client);
 	int ret;
 	struct si2157_cmd cmd;
 
-	dev_dbg(&s->client->dev, "\n");
+	dev_dbg(&client->dev, "\n");
 
-	s->active = false;
+	dev->active = false;
 
 	/* standby */
 	memcpy(cmd.args, "\x16\x00", 2);
 	cmd.wlen = 2;
 	cmd.rlen = 1;
-	ret = si2157_cmd_execute(s, &cmd);
+	ret = si2157_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	return 0;
 err:
-	dev_dbg(&s->client->dev, "failed=%d\n", ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int si2157_set_params(struct dvb_frontend *fe)
 {
-	struct si2157 *s = fe->tuner_priv;
+	struct i2c_client *client = fe->tuner_priv;
+	struct si2157_dev *dev = i2c_get_clientdata(client);
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret;
 	struct si2157_cmd cmd;
 	u8 bandwidth, delivery_system;
+	u32 if_frequency = 5000000;
 
-	dev_dbg(&s->client->dev,
+	dev_dbg(&client->dev,
 			"delivery_system=%d frequency=%u bandwidth_hz=%u\n",
-			c->delivery_system, c->frequency,
-			c->bandwidth_hz);
+			c->delivery_system, c->frequency, c->bandwidth_hz);
 
-	if (!s->active) {
+	if (!dev->active) {
 		ret = -EAGAIN;
 		goto err;
 	}
@@ -256,9 +267,11 @@
 	switch (c->delivery_system) {
 	case SYS_ATSC:
 			delivery_system = 0x00;
+			if_frequency = 3250000;
 			break;
 	case SYS_DVBC_ANNEX_B:
 			delivery_system = 0x10;
+			if_frequency = 4000000;
 			break;
 	case SYS_DVBT:
 	case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */
@@ -274,24 +287,38 @@
 
 	memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6);
 	cmd.args[4] = delivery_system | bandwidth;
-	if (s->inversion)
+	if (dev->inversion)
 		cmd.args[5] = 0x01;
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2157_cmd_execute(s, &cmd);
+	ret = si2157_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
-	if (s->chiptype == SI2157_CHIPTYPE_SI2146)
+	if (dev->chiptype == SI2157_CHIPTYPE_SI2146)
 		memcpy(cmd.args, "\x14\x00\x02\x07\x00\x01", 6);
 	else
 		memcpy(cmd.args, "\x14\x00\x02\x07\x01\x00", 6);
 	cmd.wlen = 6;
 	cmd.rlen = 4;
-	ret = si2157_cmd_execute(s, &cmd);
+	ret = si2157_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
+	/* set if frequency if needed */
+	if (if_frequency != dev->if_frequency) {
+		memcpy(cmd.args, "\x14\x00\x06\x07", 4);
+		cmd.args[4] = (if_frequency / 1000) & 0xff;
+		cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff;
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		dev->if_frequency = if_frequency;
+	}
+
 	/* set frequency */
 	memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8);
 	cmd.args[4] = (c->frequency >>  0) & 0xff;
@@ -300,26 +327,29 @@
 	cmd.args[7] = (c->frequency >> 24) & 0xff;
 	cmd.wlen = 8;
 	cmd.rlen = 1;
-	ret = si2157_cmd_execute(s, &cmd);
+	ret = si2157_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
 
 	return 0;
 err:
-	dev_dbg(&s->client->dev, "failed=%d\n", ret);
+	dev_dbg(&client->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
-	*frequency = 5000000; /* default value of property 0x0706 */
+	struct i2c_client *client = fe->tuner_priv;
+	struct si2157_dev *dev = i2c_get_clientdata(client);
+
+	*frequency = dev->if_frequency;
 	return 0;
 }
 
 static const struct dvb_tuner_ops si2157_ops = {
 	.info = {
 		.name           = "Silicon Labs Si2146/2147/2148/2157/2158",
-		.frequency_min  = 110000000,
+		.frequency_min  = 55000000,
 		.frequency_max  = 862000000,
 	},
 
@@ -334,70 +364,68 @@
 {
 	struct si2157_config *cfg = client->dev.platform_data;
 	struct dvb_frontend *fe = cfg->fe;
-	struct si2157 *s;
+	struct si2157_dev *dev;
 	struct si2157_cmd cmd;
 	int ret;
 
-	s = kzalloc(sizeof(struct si2157), GFP_KERNEL);
-	if (!s) {
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
 		ret = -ENOMEM;
 		dev_err(&client->dev, "kzalloc() failed\n");
 		goto err;
 	}
 
-	s->client = client;
-	s->fe = cfg->fe;
-	s->inversion = cfg->inversion;
-	s->fw_loaded = false;
-	s->chiptype = (u8)id->driver_data;
-	mutex_init(&s->i2c_mutex);
+	i2c_set_clientdata(client, dev);
+	dev->fe = cfg->fe;
+	dev->inversion = cfg->inversion;
+	dev->fw_loaded = false;
+	dev->chiptype = (u8)id->driver_data;
+	dev->if_frequency = 5000000; /* default value of property 0x0706 */
+	mutex_init(&dev->i2c_mutex);
 
 	/* check if the tuner is there */
 	cmd.wlen = 0;
 	cmd.rlen = 1;
-	ret = si2157_cmd_execute(s, &cmd);
+	ret = si2157_cmd_execute(client, &cmd);
 	if (ret)
-		goto err;
+		goto err_kfree;
 
-	fe->tuner_priv = s;
-	memcpy(&fe->ops.tuner_ops, &si2157_ops,
-			sizeof(struct dvb_tuner_ops));
+	memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops));
+	fe->tuner_priv = client;
 
-	i2c_set_clientdata(client, s);
-
-	dev_info(&s->client->dev,
-			"Silicon Labs %s successfully attached\n",
-			s->chiptype == SI2157_CHIPTYPE_SI2146 ?
+	dev_info(&client->dev, "Silicon Labs %s successfully attached\n",
+			dev->chiptype == SI2157_CHIPTYPE_SI2146 ?
 			"Si2146" : "Si2147/2148/2157/2158");
 
 	return 0;
+
+err_kfree:
+	kfree(dev);
 err:
 	dev_dbg(&client->dev, "failed=%d\n", ret);
-	kfree(s);
-
 	return ret;
 }
 
 static int si2157_remove(struct i2c_client *client)
 {
-	struct si2157 *s = i2c_get_clientdata(client);
-	struct dvb_frontend *fe = s->fe;
+	struct si2157_dev *dev = i2c_get_clientdata(client);
+	struct dvb_frontend *fe = dev->fe;
 
 	dev_dbg(&client->dev, "\n");
 
 	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
 	fe->tuner_priv = NULL;
-	kfree(s);
+	kfree(dev);
 
 	return 0;
 }
 
-static const struct i2c_device_id si2157_id[] = {
-	{"si2157", 0},
-	{"si2146", 1},
+static const struct i2c_device_id si2157_id_table[] = {
+	{"si2157", SI2157_CHIPTYPE_SI2157},
+	{"si2146", SI2157_CHIPTYPE_SI2146},
 	{}
 };
-MODULE_DEVICE_TABLE(i2c, si2157_id);
+MODULE_DEVICE_TABLE(i2c, si2157_id_table);
 
 static struct i2c_driver si2157_driver = {
 	.driver = {
@@ -406,7 +434,7 @@
 	},
 	.probe		= si2157_probe,
 	.remove		= si2157_remove,
-	.id_table	= si2157_id,
+	.id_table	= si2157_id_table,
 };
 
 module_i2c_driver(si2157_driver);
diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h
index d6e07cd..cd8fa5b 100644
--- a/drivers/media/tuners/si2157_priv.h
+++ b/drivers/media/tuners/si2157_priv.h
@@ -21,14 +21,14 @@
 #include "si2157.h"
 
 /* state struct */
-struct si2157 {
+struct si2157_dev {
 	struct mutex i2c_mutex;
-	struct i2c_client *client;
 	struct dvb_frontend *fe;
 	bool active;
 	bool fw_loaded;
 	bool inversion;
 	u8 chiptype;
+	u32 if_frequency;
 };
 
 #define SI2157_CHIPTYPE_SI2157 0
diff --git a/drivers/media/tuners/tda18218.h b/drivers/media/tuners/tda18218.h
index edeeb7b..5a01369 100644
--- a/drivers/media/tuners/tda18218.h
+++ b/drivers/media/tuners/tda18218.h
@@ -30,7 +30,7 @@
 	u8 loop_through:1;
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_TDA18218)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_TDA18218)
 extern struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
 	struct i2c_adapter *i2c, struct tda18218_config *cfg);
 #else
diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c
index 4995b89..f862074 100644
--- a/drivers/media/tuners/tda18271-fe.c
+++ b/drivers/media/tuners/tda18271-fe.c
@@ -1355,11 +1355,3 @@
 MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("0.4");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/tuners/tda18271-maps.c b/drivers/media/tuners/tda18271-maps.c
index b62e925..1e89dd9 100644
--- a/drivers/media/tuners/tda18271-maps.c
+++ b/drivers/media/tuners/tda18271-maps.c
@@ -1305,11 +1305,3 @@
 
 	return ret;
 }
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/tuners/tda18271-priv.h b/drivers/media/tuners/tda18271-priv.h
index b36a7b7..cc80f54 100644
--- a/drivers/media/tuners/tda18271-priv.h
+++ b/drivers/media/tuners/tda18271-priv.h
@@ -226,11 +226,3 @@
 extern int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq);
 
 #endif /* __TDA18271_PRIV_H__ */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/tuners/tda18271.h b/drivers/media/tuners/tda18271.h
index 86506b2..45a9a6c 100644
--- a/drivers/media/tuners/tda18271.h
+++ b/drivers/media/tuners/tda18271.h
@@ -121,7 +121,7 @@
 	TDA18271_DIGITAL,
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_TDA18271)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_TDA18271)
 extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
 					    struct i2c_adapter *i2c,
 					    struct tda18271_config *cfg);
diff --git a/drivers/media/tuners/tda827x.c b/drivers/media/tuners/tda827x.c
index 73453a2..edcb4a7 100644
--- a/drivers/media/tuners/tda827x.c
+++ b/drivers/media/tuners/tda827x.c
@@ -907,11 +907,3 @@
 MODULE_AUTHOR("Hartmut Hackmann <hartmut.hackmann@t-online.de>");
 MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
 MODULE_LICENSE("GPL");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/tuners/tda827x.h b/drivers/media/tuners/tda827x.h
index 323a45e..f66fe87 100644
--- a/drivers/media/tuners/tda827x.h
+++ b/drivers/media/tuners/tda827x.h
@@ -51,7 +51,7 @@
  * @param cfg optional callback function pointers.
  * @return FE pointer on success, NULL on failure.
  */
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_TDA827X)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_TDA827X)
 extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr,
 					   struct i2c_adapter *i2c,
 					   struct tda827x_config *cfg);
diff --git a/drivers/media/tuners/tda8290.c b/drivers/media/tuners/tda8290.c
index ab4106c..998e82b 100644
--- a/drivers/media/tuners/tda8290.c
+++ b/drivers/media/tuners/tda8290.c
@@ -881,11 +881,3 @@
 MODULE_DESCRIPTION("Philips/NXP TDA8290/TDA8295 analog IF demodulator driver");
 MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky");
 MODULE_LICENSE("GPL");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/tuners/tda8290.h b/drivers/media/tuners/tda8290.h
index a7cc26f..6b1003e 100644
--- a/drivers/media/tuners/tda8290.h
+++ b/drivers/media/tuners/tda8290.h
@@ -38,7 +38,7 @@
 	struct tda18271_std_map *tda18271_std_map;
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_TDA8290)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_TDA8290)
 extern int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr);
 
 extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/tuners/tda9887.c b/drivers/media/tuners/tda9887.c
index 9823248..56be6c2 100644
--- a/drivers/media/tuners/tda9887.c
+++ b/drivers/media/tuners/tda9887.c
@@ -707,11 +707,3 @@
 EXPORT_SYMBOL_GPL(tda9887_attach);
 
 MODULE_LICENSE("GPL");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/tuners/tda9887.h b/drivers/media/tuners/tda9887.h
index 542de5f..a5583e5 100644
--- a/drivers/media/tuners/tda9887.h
+++ b/drivers/media/tuners/tda9887.h
@@ -21,7 +21,7 @@
 #include "dvb_frontend.h"
 
 /* ------------------------------------------------------------------------ */
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_TDA9887)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_TDA9887)
 extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
 					   struct i2c_adapter *i2c_adap,
 					   u8 i2c_addr);
diff --git a/drivers/media/tuners/tea5761.h b/drivers/media/tuners/tea5761.h
index 26f1895..75c2966 100644
--- a/drivers/media/tuners/tea5761.h
+++ b/drivers/media/tuners/tea5761.h
@@ -20,7 +20,7 @@
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_TEA5761)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_TEA5761)
 extern int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
 
 extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/tuners/tea5767.h b/drivers/media/tuners/tea5767.h
index 27ea1ff..5ecab72 100644
--- a/drivers/media/tuners/tea5767.h
+++ b/drivers/media/tuners/tea5767.h
@@ -39,7 +39,7 @@
 	enum tea5767_xtal	xtal_freq;
 };
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_TEA5767)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_TEA5767)
 extern int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
 
 extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/tuners/tua9001.h b/drivers/media/tuners/tua9001.h
index f247d21..fb77312 100644
--- a/drivers/media/tuners/tua9001.h
+++ b/drivers/media/tuners/tua9001.h
@@ -51,7 +51,7 @@
 #define TUA9001_CMD_RESETN  1
 #define TUA9001_CMD_RXEN    2
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_TUA9001)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_TUA9001)
 extern struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
 		struct i2c_adapter *i2c, struct tua9001_config *cfg);
 #else
diff --git a/drivers/media/tuners/tuner-simple.c b/drivers/media/tuners/tuner-simple.c
index ca274c2..8e9ce14 100644
--- a/drivers/media/tuners/tuner-simple.c
+++ b/drivers/media/tuners/tuner-simple.c
@@ -1148,11 +1148,3 @@
 MODULE_DESCRIPTION("Simple 4-control-bytes style tuner driver");
 MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
 MODULE_LICENSE("GPL");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/tuners/tuner-simple.h b/drivers/media/tuners/tuner-simple.h
index 496b0d8..46a8b86 100644
--- a/drivers/media/tuners/tuner-simple.h
+++ b/drivers/media/tuners/tuner-simple.h
@@ -20,7 +20,7 @@
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_SIMPLE)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_SIMPLE)
 extern struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
 						struct i2c_adapter *i2c_adap,
 						u8 i2c_addr,
diff --git a/drivers/media/tuners/tuner-xc2028.h b/drivers/media/tuners/tuner-xc2028.h
index 739f2fa..8801aae 100644
--- a/drivers/media/tuners/tuner-xc2028.h
+++ b/drivers/media/tuners/tuner-xc2028.h
@@ -56,7 +56,7 @@
 #define XC2028_RESET_CLK	1
 #define XC2028_I2C_FLUSH	2
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_XC2028)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_XC2028)
 extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
 					  struct xc2028_config *cfg);
 #else
diff --git a/drivers/media/tuners/xc4000.h b/drivers/media/tuners/xc4000.h
index 9c2f722..5ca2a81 100644
--- a/drivers/media/tuners/xc4000.h
+++ b/drivers/media/tuners/xc4000.h
@@ -50,7 +50,7 @@
  * it's passed back to a bridge during tuner_callback().
  */
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_XC4000)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_XC4000)
 extern struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
 					  struct i2c_adapter *i2c,
 					  struct xc4000_config *cfg);
diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c
index 2a039de..e6e5e90 100644
--- a/drivers/media/tuners/xc5000.c
+++ b/drivers/media/tuners/xc5000.c
@@ -1336,7 +1336,10 @@
 
 	if (priv) {
 		cancel_delayed_work(&priv->timer_sleep);
-		release_firmware(priv->firmware);
+		if (priv->firmware) {
+			release_firmware(priv->firmware);
+			priv->firmware = NULL;
+		}
 		hybrid_tuner_release_state(priv);
 	}
 
diff --git a/drivers/media/tuners/xc5000.h b/drivers/media/tuners/xc5000.h
index d3ffe4b..d754044 100644
--- a/drivers/media/tuners/xc5000.h
+++ b/drivers/media/tuners/xc5000.h
@@ -58,7 +58,7 @@
  * it's passed back to a bridge during tuner_callback().
  */
 
-#if IS_ENABLED(CPTCFG_MEDIA_TUNER_XC5000)
+#if IS_REACHABLE(CPTCFG_MEDIA_TUNER_XC5000)
 extern struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
 					  struct i2c_adapter *i2c,
 					  const struct xc5000_config *cfg);
diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig
index d89c436..ca5fefd 100644
--- a/drivers/media/usb/au0828/Kconfig
+++ b/drivers/media/usb/au0828/Kconfig
@@ -5,7 +5,7 @@
 	depends on I2C && INPUT && DVB_CORE && USB
 	depends on I2C_ALGOBIT
 	select VIDEO_TVEEPROM
-	select VIDEOBUF_VMALLOC
+	select VIDEOBUF2_VMALLOC
 	select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c
index b52f470..89d7f3c 100644
--- a/drivers/media/usb/au0828/au0828-cards.c
+++ b/drivers/media/usb/au0828/au0828-cards.c
@@ -44,7 +44,7 @@
 struct au0828_board au0828_boards[] = {
 	[AU0828_BOARD_UNKNOWN] = {
 		.name	= "Unknown board",
-		.tuner_type = UNSET,
+		.tuner_type = -1U,
 		.tuner_addr = ADDR_UNSET,
 	},
 	[AU0828_BOARD_HAUPPAUGE_HVR850] = {
diff --git a/drivers/media/usb/au0828/au0828-vbi.c b/drivers/media/usb/au0828/au0828-vbi.c
index 932d24f..f67247c 100644
--- a/drivers/media/usb/au0828/au0828-vbi.c
+++ b/drivers/media/usb/au0828/au0828-vbi.c
@@ -28,111 +28,67 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 
-static unsigned int vbibufs = 5;
-module_param(vbibufs, int, 0644);
-MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
-
 /* ------------------------------------------------------------------ */
 
-static void
-free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf)
+static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+			   unsigned int *nbuffers, unsigned int *nplanes,
+			   unsigned int sizes[], void *alloc_ctxs[])
 {
-	struct au0828_fh     *fh  = vq->priv_data;
-	struct au0828_dev    *dev = fh->dev;
-	unsigned long flags = 0;
-	if (in_interrupt())
-		BUG();
+	struct au0828_dev *dev = vb2_get_drv_priv(vq);
+	unsigned long img_size = dev->vbi_width * dev->vbi_height * 2;
+	unsigned long size;
 
-	/* We used to wait for the buffer to finish here, but this didn't work
-	   because, as we were keeping the state as VIDEOBUF_QUEUED,
-	   videobuf_queue_cancel marked it as finished for us.
-	   (Also, it could wedge forever if the hardware was misconfigured.)
-
-	   This should be safe; by the time we get here, the buffer isn't
-	   queued anymore. If we ever start marking the buffers as
-	   VIDEOBUF_ACTIVE, it won't be, though.
-	*/
-	spin_lock_irqsave(&dev->slock, flags);
-	if (dev->isoc_ctl.vbi_buf == buf)
-		dev->isoc_ctl.vbi_buf = NULL;
-	spin_unlock_irqrestore(&dev->slock, flags);
-
-	videobuf_vmalloc_free(&buf->vb);
-	buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int
-vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
-{
-	struct au0828_fh     *fh  = q->priv_data;
-	struct au0828_dev    *dev = fh->dev;
-
-	*size = dev->vbi_width * dev->vbi_height * 2;
-
-	if (0 == *count)
-		*count = vbibufs;
-	if (*count < 2)
-		*count = 2;
-	if (*count > 32)
-		*count = 32;
-	return 0;
-}
-
-static int
-vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
-	    enum v4l2_field field)
-{
-	struct au0828_fh     *fh  = q->priv_data;
-	struct au0828_dev    *dev = fh->dev;
-	struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
-	int                  rc = 0;
-
-	buf->vb.size = dev->vbi_width * dev->vbi_height * 2;
-
-	if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+	size = fmt ? (fmt->fmt.vbi.samples_per_line *
+		(fmt->fmt.vbi.count[0] + fmt->fmt.vbi.count[1])) : img_size;
+	if (size < img_size)
 		return -EINVAL;
 
-	buf->vb.width  = dev->vbi_width;
-	buf->vb.height = dev->vbi_height;
-	buf->vb.field  = field;
+	*nplanes = 1;
+	sizes[0] = size;
 
-	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-		rc = videobuf_iolock(q, &buf->vb, NULL);
-		if (rc < 0)
-			goto fail;
-	}
-
-	buf->vb.state = VIDEOBUF_PREPARED;
 	return 0;
+}
 
-fail:
-	free_buffer(q, buf);
-	return rc;
+static int vbi_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct au0828_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+	struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
+	unsigned long size;
+
+	size = dev->vbi_width * dev->vbi_height * 2;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		pr_err("%s data will not fit into plane (%lu < %lu)\n",
+			__func__, vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+	vb2_set_plane_payload(&buf->vb, 0, size);
+
+	return 0;
 }
 
 static void
-vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+vbi_buffer_queue(struct vb2_buffer *vb)
 {
-	struct au0828_buffer    *buf     = container_of(vb,
-							struct au0828_buffer,
-							vb);
-	struct au0828_fh        *fh      = vq->priv_data;
-	struct au0828_dev       *dev     = fh->dev;
-	struct au0828_dmaqueue  *vbiq    = &dev->vbiq;
-
-	buf->vb.state = VIDEOBUF_QUEUED;
-	list_add_tail(&buf->vb.queue, &vbiq->active);
-}
-
-static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
+	struct au0828_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
 	struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
-	free_buffer(q, buf);
+	struct au0828_dmaqueue *vbiq = &dev->vbiq;
+	unsigned long flags = 0;
+
+	buf->mem = vb2_plane_vaddr(vb, 0);
+	buf->length = vb2_plane_size(vb, 0);
+
+	spin_lock_irqsave(&dev->slock, flags);
+	list_add_tail(&buf->list, &vbiq->active);
+	spin_unlock_irqrestore(&dev->slock, flags);
 }
 
-struct videobuf_queue_ops au0828_vbi_qops = {
-	.buf_setup    = vbi_setup,
-	.buf_prepare  = vbi_prepare,
-	.buf_queue    = vbi_queue,
-	.buf_release  = vbi_release,
+struct vb2_ops au0828_vbi_qops = {
+	.queue_setup     = vbi_queue_setup,
+	.buf_prepare     = vbi_buffer_prepare,
+	.buf_queue       = vbi_buffer_queue,
+	.start_streaming = au0828_start_analog_streaming,
+	.stop_streaming  = au0828_stop_vbi_streaming,
+	.wait_prepare    = vb2_ops_wait_prepare,
+	.wait_finish     = vb2_ops_wait_finish,
 };
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
index 2e94235..a1f0083 100644
--- a/drivers/media/usb/au0828/au0828-video.c
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -22,7 +22,6 @@
 
 /* Developer Notes:
  *
- * VBI support is not yet working
  * The hardware scaler supported is unimplemented
  * AC97 audio support is unimplemented (only i2s audio mode)
  *
@@ -219,9 +218,6 @@
 
 	au0828_isocdbg("au0828: called au0828_prepare_isoc\n");
 
-	/* De-allocates all pending stuff */
-	au0828_uninit_isoc(dev);
-
 	dev->isoc_ctl.isoc_copy = isoc_copy;
 	dev->isoc_ctl.num_bufs = num_bufs;
 
@@ -285,8 +281,6 @@
 		}
 	}
 
-	init_waitqueue_head(&dma_q->wq);
-
 	/* submit urbs and enables IRQ */
 	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
 		rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
@@ -305,37 +299,23 @@
  * Announces that a buffer were filled and request the next
  */
 static inline void buffer_filled(struct au0828_dev *dev,
-				  struct au0828_dmaqueue *dma_q,
-				  struct au0828_buffer *buf)
+				 struct au0828_dmaqueue *dma_q,
+				 struct au0828_buffer *buf)
 {
+	struct vb2_buffer *vb = &buf->vb;
+	struct vb2_queue *q = vb->vb2_queue;
+
 	/* Advice that buffer was filled */
-	au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+	au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field);
 
-	buf->vb.state = VIDEOBUF_DONE;
-	buf->vb.field_count++;
-	v4l2_get_timestamp(&buf->vb.ts);
+	if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		vb->v4l2_buf.sequence = dev->frame_count++;
+	else
+		vb->v4l2_buf.sequence = dev->vbi_frame_count++;
 
-	dev->isoc_ctl.buf = NULL;
-
-	list_del(&buf->vb.queue);
-	wake_up(&buf->vb.done);
-}
-
-static inline void vbi_buffer_filled(struct au0828_dev *dev,
-				     struct au0828_dmaqueue *dma_q,
-				     struct au0828_buffer *buf)
-{
-	/* Advice that buffer was filled */
-	au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
-
-	buf->vb.state = VIDEOBUF_DONE;
-	buf->vb.field_count++;
-	v4l2_get_timestamp(&buf->vb.ts);
-
-	dev->isoc_ctl.vbi_buf = NULL;
-
-	list_del(&buf->vb.queue);
-	wake_up(&buf->vb.done);
+	vb->v4l2_buf.field = V4L2_FIELD_INTERLACED;
+	v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
+	vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
 }
 
 /*
@@ -354,8 +334,8 @@
 	if (len == 0)
 		return;
 
-	if (dma_q->pos + len > buf->vb.size)
-		len = buf->vb.size - dma_q->pos;
+	if (dma_q->pos + len > buf->length)
+		len = buf->length - dma_q->pos;
 
 	startread = p;
 	remain = len;
@@ -373,11 +353,11 @@
 	lencopy = bytesperline - currlinedone;
 	lencopy = lencopy > remain ? remain : lencopy;
 
-	if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {
+	if ((char *)startwrite + lencopy > (char *)outp + buf->length) {
 		au0828_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
 			       ((char *)startwrite + lencopy) -
-			       ((char *)outp + buf->vb.size));
-		remain = (char *)outp + buf->vb.size - (char *)startwrite;
+			       ((char *)outp + buf->length));
+		remain = (char *)outp + buf->length - (char *)startwrite;
 		lencopy = remain;
 	}
 	if (lencopy <= 0)
@@ -395,11 +375,11 @@
 			lencopy = bytesperline;
 
 		if ((char *)startwrite + lencopy > (char *)outp +
-		    buf->vb.size) {
+		    buf->length) {
 			au0828_isocdbg("Overflow %zi bytes past buf end (2)\n",
 				       ((char *)startwrite + lencopy) -
-				       ((char *)outp + buf->vb.size));
-			lencopy = remain = (char *)outp + buf->vb.size -
+				       ((char *)outp + buf->length));
+			lencopy = remain = (char *)outp + buf->length -
 					   (char *)startwrite;
 		}
 		if (lencopy <= 0)
@@ -435,7 +415,11 @@
 	}
 
 	/* Get the next buffer */
-	*buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue);
+	*buf = list_entry(dma_q->active.next, struct au0828_buffer, list);
+	/* Cleans up buffer - Useful for testing for frame/URB loss */
+	list_del(&(*buf)->list);
+	dma_q->pos = 0;
+	(*buf)->vb_buf = (*buf)->mem;
 	dev->isoc_ctl.buf = *buf;
 
 	return;
@@ -473,8 +457,8 @@
 
 	bytesperline = dev->vbi_width;
 
-	if (dma_q->pos + len > buf->vb.size)
-		len = buf->vb.size - dma_q->pos;
+	if (dma_q->pos + len > buf->length)
+		len = buf->length - dma_q->pos;
 
 	startread = p;
 	startwrite = outp + (dma_q->pos / 2);
@@ -497,7 +481,6 @@
 				    struct au0828_buffer **buf)
 {
 	struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vbiq);
-	char *outp;
 
 	if (list_empty(&dma_q->active)) {
 		au0828_isocdbg("No active queue to serve\n");
@@ -507,13 +490,12 @@
 	}
 
 	/* Get the next buffer */
-	*buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue);
+	*buf = list_entry(dma_q->active.next, struct au0828_buffer, list);
 	/* Cleans up buffer - Useful for testing for frame/URB loss */
-	outp = videobuf_to_vmalloc(&(*buf)->vb);
-	memset(outp, 0x00, (*buf)->vb.size);
-
+	list_del(&(*buf)->list);
+	dma_q->pos = 0;
+	(*buf)->vb_buf = (*buf)->mem;
 	dev->isoc_ctl.vbi_buf = *buf;
-
 	return;
 }
 
@@ -549,11 +531,11 @@
 
 	buf = dev->isoc_ctl.buf;
 	if (buf != NULL)
-		outp = videobuf_to_vmalloc(&buf->vb);
+		outp = vb2_plane_vaddr(&buf->vb, 0);
 
 	vbi_buf = dev->isoc_ctl.vbi_buf;
 	if (vbi_buf != NULL)
-		vbioutp = videobuf_to_vmalloc(&vbi_buf->vb);
+		vbioutp = vb2_plane_vaddr(&vbi_buf->vb, 0);
 
 	for (i = 0; i < urb->number_of_packets; i++) {
 		int status = urb->iso_frame_desc[i].status;
@@ -586,15 +568,13 @@
 			if (fbyte & 0x40) {
 				/* VBI */
 				if (vbi_buf != NULL)
-					vbi_buffer_filled(dev,
-							  vbi_dma_q,
-							  vbi_buf);
+					buffer_filled(dev, vbi_dma_q, vbi_buf);
 				vbi_get_next_buf(vbi_dma_q, &vbi_buf);
 				if (vbi_buf == NULL)
 					vbioutp = NULL;
 				else
-					vbioutp = videobuf_to_vmalloc(
-						&vbi_buf->vb);
+					vbioutp = vb2_plane_vaddr(
+						&vbi_buf->vb, 0);
 
 				/* Video */
 				if (buf != NULL)
@@ -603,7 +583,7 @@
 				if (buf == NULL)
 					outp = NULL;
 				else
-					outp = videobuf_to_vmalloc(&buf->vb);
+					outp = vb2_plane_vaddr(&buf->vb, 0);
 
 				/* As long as isoc traffic is arriving, keep
 				   resetting the timer */
@@ -657,130 +637,59 @@
 	return rc;
 }
 
-static int
-buffer_setup(struct videobuf_queue *vq, unsigned int *count,
-	     unsigned int *size)
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+		       unsigned int *nbuffers, unsigned int *nplanes,
+		       unsigned int sizes[], void *alloc_ctxs[])
 {
-	struct au0828_fh *fh = vq->priv_data;
-	*size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3;
+	struct au0828_dev *dev = vb2_get_drv_priv(vq);
+	unsigned long img_size = dev->height * dev->bytesperline;
+	unsigned long size;
 
-	if (0 == *count)
-		*count = AU0828_DEF_BUF;
-
-	if (*count < AU0828_MIN_BUF)
-		*count = AU0828_MIN_BUF;
-	return 0;
-}
-
-/* This is called *without* dev->slock held; please keep it that way */
-static void free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf)
-{
-	struct au0828_fh     *fh  = vq->priv_data;
-	struct au0828_dev    *dev = fh->dev;
-	unsigned long flags = 0;
-	if (in_interrupt())
-		BUG();
-
-	/* We used to wait for the buffer to finish here, but this didn't work
-	   because, as we were keeping the state as VIDEOBUF_QUEUED,
-	   videobuf_queue_cancel marked it as finished for us.
-	   (Also, it could wedge forever if the hardware was misconfigured.)
-
-	   This should be safe; by the time we get here, the buffer isn't
-	   queued anymore. If we ever start marking the buffers as
-	   VIDEOBUF_ACTIVE, it won't be, though.
-	*/
-	spin_lock_irqsave(&dev->slock, flags);
-	if (dev->isoc_ctl.buf == buf)
-		dev->isoc_ctl.buf = NULL;
-	spin_unlock_irqrestore(&dev->slock, flags);
-
-	videobuf_vmalloc_free(&buf->vb);
-	buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int
-buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
-						enum v4l2_field field)
-{
-	struct au0828_fh     *fh  = vq->priv_data;
-	struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
-	struct au0828_dev    *dev = fh->dev;
-	int                  rc = 0, urb_init = 0;
-
-	buf->vb.size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3;
-
-	if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+	size = fmt ? fmt->fmt.pix.sizeimage : img_size;
+	if (size < img_size)
 		return -EINVAL;
 
-	buf->vb.width  = dev->width;
-	buf->vb.height = dev->height;
-	buf->vb.field  = field;
+	*nplanes = 1;
+	sizes[0] = size;
 
-	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-		rc = videobuf_iolock(vq, &buf->vb, NULL);
-		if (rc < 0) {
-			pr_info("videobuf_iolock failed\n");
-			goto fail;
-		}
-	}
-
-	if (!dev->isoc_ctl.num_bufs)
-		urb_init = 1;
-
-	if (urb_init) {
-		rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB,
-				      AU0828_MAX_ISO_BUFS, dev->max_pkt_size,
-				      au0828_isoc_copy);
-		if (rc < 0) {
-			pr_info("au0828_init_isoc failed\n");
-			goto fail;
-		}
-	}
-
-	buf->vb.state = VIDEOBUF_PREPARED;
 	return 0;
+}
 
-fail:
-	free_buffer(vq, buf);
-	return rc;
+static int
+buffer_prepare(struct vb2_buffer *vb)
+{
+	struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
+	struct au0828_dev    *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+	buf->length = dev->height * dev->bytesperline;
+
+	if (vb2_plane_size(vb, 0) < buf->length) {
+		pr_err("%s data will not fit into plane (%lu < %lu)\n",
+			__func__, vb2_plane_size(vb, 0), buf->length);
+		return -EINVAL;
+	}
+	vb2_set_plane_payload(&buf->vb, 0, buf->length);
+	return 0;
 }
 
 static void
-buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+buffer_queue(struct vb2_buffer *vb)
 {
 	struct au0828_buffer    *buf     = container_of(vb,
 							struct au0828_buffer,
 							vb);
-	struct au0828_fh        *fh      = vq->priv_data;
-	struct au0828_dev       *dev     = fh->dev;
+	struct au0828_dev       *dev     = vb2_get_drv_priv(vb->vb2_queue);
 	struct au0828_dmaqueue  *vidq    = &dev->vidq;
+	unsigned long flags = 0;
 
-	buf->vb.state = VIDEOBUF_QUEUED;
-	list_add_tail(&buf->vb.queue, &vidq->active);
+	buf->mem = vb2_plane_vaddr(vb, 0);
+	buf->length = vb2_plane_size(vb, 0);
+
+	spin_lock_irqsave(&dev->slock, flags);
+	list_add_tail(&buf->list, &vidq->active);
+	spin_unlock_irqrestore(&dev->slock, flags);
 }
 
-static void buffer_release(struct videobuf_queue *vq,
-				struct videobuf_buffer *vb)
-{
-	struct au0828_buffer   *buf  = container_of(vb,
-						    struct au0828_buffer,
-						    vb);
-
-	free_buffer(vq, buf);
-}
-
-static struct videobuf_queue_ops au0828_video_qops = {
-	.buf_setup      = buffer_setup,
-	.buf_prepare    = buffer_prepare,
-	.buf_queue      = buffer_queue,
-	.buf_release    = buffer_release,
-};
-
-/* ------------------------------------------------------------------
-   V4L2 interface
-   ------------------------------------------------------------------*/
-
 static int au0828_i2s_init(struct au0828_dev *dev)
 {
 	/* Enable i2s mode */
@@ -829,7 +738,7 @@
 	return 0;
 }
 
-int au0828_analog_stream_disable(struct au0828_dev *d)
+static int au0828_analog_stream_disable(struct au0828_dev *d)
 {
 	dprintk(1, "au0828_analog_stream_disable called\n");
 	au0828_writereg(d, AU0828_SENSORCTRL_100, 0x0);
@@ -862,80 +771,131 @@
 	return 0;
 }
 
+int au0828_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct au0828_dev *dev = vb2_get_drv_priv(vq);
+	int rc = 0;
+
+	dprintk(1, "au0828_start_analog_streaming called %d\n",
+		dev->streaming_users);
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		dev->frame_count = 0;
+	else
+		dev->vbi_frame_count = 0;
+
+	if (dev->streaming_users == 0) {
+		/* If we were doing ac97 instead of i2s, it would go here...*/
+		au0828_i2s_init(dev);
+		rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB,
+				   AU0828_MAX_ISO_BUFS, dev->max_pkt_size,
+				   au0828_isoc_copy);
+		if (rc < 0) {
+			pr_info("au0828_init_isoc failed\n");
+			return rc;
+		}
+
+		if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+			v4l2_device_call_all(&dev->v4l2_dev, 0, video,
+						s_stream, 1);
+			dev->vid_timeout_running = 1;
+			mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
+		} else if (vq->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+			dev->vbi_timeout_running = 1;
+			mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
+		}
+	}
+	dev->streaming_users++;
+	return rc;
+}
+
+static void au0828_stop_streaming(struct vb2_queue *vq)
+{
+	struct au0828_dev *dev = vb2_get_drv_priv(vq);
+	struct au0828_dmaqueue *vidq = &dev->vidq;
+	unsigned long flags = 0;
+
+	dprintk(1, "au0828_stop_streaming called %d\n", dev->streaming_users);
+
+	if (dev->streaming_users-- == 1)
+		au0828_uninit_isoc(dev);
+
+	v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
+	dev->vid_timeout_running = 0;
+	del_timer_sync(&dev->vid_timeout);
+
+	spin_lock_irqsave(&dev->slock, flags);
+	if (dev->isoc_ctl.buf != NULL) {
+		vb2_buffer_done(&dev->isoc_ctl.buf->vb, VB2_BUF_STATE_ERROR);
+		dev->isoc_ctl.buf = NULL;
+	}
+	while (!list_empty(&vidq->active)) {
+		struct au0828_buffer *buf;
+
+		buf = list_entry(vidq->active.next, struct au0828_buffer, list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+		list_del(&buf->list);
+	}
+	spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+void au0828_stop_vbi_streaming(struct vb2_queue *vq)
+{
+	struct au0828_dev *dev = vb2_get_drv_priv(vq);
+	struct au0828_dmaqueue *vbiq = &dev->vbiq;
+	unsigned long flags = 0;
+
+	dprintk(1, "au0828_stop_vbi_streaming called %d\n",
+		dev->streaming_users);
+
+	if (dev->streaming_users-- == 1)
+		au0828_uninit_isoc(dev);
+
+	spin_lock_irqsave(&dev->slock, flags);
+	if (dev->isoc_ctl.vbi_buf != NULL) {
+		vb2_buffer_done(&dev->isoc_ctl.vbi_buf->vb,
+				VB2_BUF_STATE_ERROR);
+		dev->isoc_ctl.vbi_buf = NULL;
+	}
+	while (!list_empty(&vbiq->active)) {
+		struct au0828_buffer *buf;
+
+		buf = list_entry(vbiq->active.next, struct au0828_buffer, list);
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+	}
+	spin_unlock_irqrestore(&dev->slock, flags);
+
+	dev->vbi_timeout_running = 0;
+	del_timer_sync(&dev->vbi_timeout);
+}
+
+static struct vb2_ops au0828_video_qops = {
+	.queue_setup     = queue_setup,
+	.buf_prepare     = buffer_prepare,
+	.buf_queue       = buffer_queue,
+	.start_streaming = au0828_start_analog_streaming,
+	.stop_streaming  = au0828_stop_streaming,
+	.wait_prepare    = vb2_ops_wait_prepare,
+	.wait_finish     = vb2_ops_wait_finish,
+};
+
+/* ------------------------------------------------------------------
+   V4L2 interface
+   ------------------------------------------------------------------*/
 /*
- * au0828_release_resources
+ * au0828_analog_unregister
  * unregister v4l2 devices
  */
 void au0828_analog_unregister(struct au0828_dev *dev)
 {
-	dprintk(1, "au0828_release_resources called\n");
+	dprintk(1, "au0828_analog_unregister called\n");
 	mutex_lock(&au0828_sysfs_lock);
-
-	if (dev->vdev)
-		video_unregister_device(dev->vdev);
-	if (dev->vbi_dev)
-		video_unregister_device(dev->vbi_dev);
-
+	video_unregister_device(&dev->vdev);
+	video_unregister_device(&dev->vbi_dev);
 	mutex_unlock(&au0828_sysfs_lock);
 }
 
-
-/* Usage lock check functions */
-static int res_get(struct au0828_fh *fh, unsigned int bit)
-{
-	struct au0828_dev    *dev = fh->dev;
-
-	if (fh->resources & bit)
-		/* have it already allocated */
-		return 1;
-
-	/* is it free? */
-	if (dev->resources & bit) {
-		/* no, someone else uses it */
-		return 0;
-	}
-	/* it's free, grab it */
-	fh->resources  |= bit;
-	dev->resources |= bit;
-	dprintk(1, "res: get %d\n", bit);
-
-	return 1;
-}
-
-static int res_check(struct au0828_fh *fh, unsigned int bit)
-{
-	return fh->resources & bit;
-}
-
-static int res_locked(struct au0828_dev *dev, unsigned int bit)
-{
-	return dev->resources & bit;
-}
-
-static void res_free(struct au0828_fh *fh, unsigned int bits)
-{
-	struct au0828_dev    *dev = fh->dev;
-
-	BUG_ON((fh->resources & bits) != bits);
-
-	fh->resources  &= ~bits;
-	dev->resources &= ~bits;
-	dprintk(1, "res: put %d\n", bits);
-}
-
-static int get_ressource(struct au0828_fh *fh)
-{
-	switch (fh->type) {
-	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		return AU0828_RESOURCE_VIDEO;
-	case V4L2_BUF_TYPE_VBI_CAPTURE:
-		return AU0828_RESOURCE_VBI;
-	default:
-		BUG();
-		return 0;
-	}
-}
-
 /* This function ensures that video frames continue to be delivered even if
    the ITU-656 input isn't receiving any data (thereby preventing applications
    such as tvtime from hanging) */
@@ -951,8 +911,8 @@
 
 	buf = dev->isoc_ctl.buf;
 	if (buf != NULL) {
-		vid_data = videobuf_to_vmalloc(&buf->vb);
-		memset(vid_data, 0x00, buf->vb.size); /* Blank green frame */
+		vid_data = vb2_plane_vaddr(&buf->vb, 0);
+		memset(vid_data, 0x00, buf->length); /* Blank green frame */
 		buffer_filled(dev, dma_q, buf);
 	}
 	get_next_buf(dma_q, &buf);
@@ -975,9 +935,9 @@
 
 	buf = dev->isoc_ctl.vbi_buf;
 	if (buf != NULL) {
-		vbi_data = videobuf_to_vmalloc(&buf->vb);
-		memset(vbi_data, 0x00, buf->vb.size);
-		vbi_buffer_filled(dev, dma_q, buf);
+		vbi_data = vb2_plane_vaddr(&buf->vb, 0);
+		memset(vbi_data, 0x00, buf->length);
+		buffer_filled(dev, dma_q, buf);
 	}
 	vbi_get_next_buf(dma_q, &buf);
 
@@ -986,105 +946,65 @@
 	spin_unlock_irqrestore(&dev->slock, flags);
 }
 
-
 static int au0828_v4l2_open(struct file *filp)
 {
-	int ret = 0;
-	struct video_device *vdev = video_devdata(filp);
 	struct au0828_dev *dev = video_drvdata(filp);
-	struct au0828_fh *fh;
-	int type;
+	int ret;
 
-	switch (vdev->vfl_type) {
-	case VFL_TYPE_GRABBER:
-		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-		break;
-	case VFL_TYPE_VBI:
-		type = V4L2_BUF_TYPE_VBI_CAPTURE;
-		break;
-	default:
-		return -EINVAL;
-	}
+	dprintk(1,
+		"%s called std_set %d dev_state %d stream users %d users %d\n",
+		__func__, dev->std_set_in_tuner_core, dev->dev_state,
+		dev->streaming_users, dev->users);
 
-	fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL);
-	if (NULL == fh) {
-		dprintk(1, "Failed allocate au0828_fh struct!\n");
-		return -ENOMEM;
-	}
-
-	fh->type = type;
-	fh->dev = dev;
-	v4l2_fh_init(&fh->fh, vdev);
-	filp->private_data = fh;
-
-	if (mutex_lock_interruptible(&dev->lock)) {
-		kfree(fh);
+	if (mutex_lock_interruptible(&dev->lock))
 		return -ERESTARTSYS;
+
+	ret = v4l2_fh_open(filp);
+	if (ret) {
+		au0828_isocdbg("%s: v4l2_fh_open() returned error %d\n",
+				__func__, ret);
+		mutex_unlock(&dev->lock);
+		return ret;
 	}
+
 	if (dev->users == 0) {
 		au0828_analog_stream_enable(dev);
 		au0828_analog_stream_reset(dev);
-
-		/* If we were doing ac97 instead of i2s, it would go here...*/
-		au0828_i2s_init(dev);
-
 		dev->stream_state = STREAM_OFF;
 		dev->dev_state |= DEV_INITIALIZED;
 	}
-
 	dev->users++;
 	mutex_unlock(&dev->lock);
-
-	videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops,
-				    NULL, &dev->slock,
-				    V4L2_BUF_TYPE_VIDEO_CAPTURE,
-				    V4L2_FIELD_INTERLACED,
-				    sizeof(struct au0828_buffer), fh,
-				    &dev->lock);
-
-	/* VBI Setup */
-	videobuf_queue_vmalloc_init(&fh->vb_vbiq, &au0828_vbi_qops,
-				    NULL, &dev->slock,
-				    V4L2_BUF_TYPE_VBI_CAPTURE,
-				    V4L2_FIELD_SEQ_TB,
-				    sizeof(struct au0828_buffer), fh,
-				    &dev->lock);
-	v4l2_fh_add(&fh->fh);
 	return ret;
 }
 
 static int au0828_v4l2_close(struct file *filp)
 {
 	int ret;
-	struct au0828_fh *fh = filp->private_data;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(filp);
+	struct video_device *vdev = video_devdata(filp);
 
-	v4l2_fh_del(&fh->fh);
-	v4l2_fh_exit(&fh->fh);
+	dprintk(1,
+		"%s called std_set %d dev_state %d stream users %d users %d\n",
+		__func__, dev->std_set_in_tuner_core, dev->dev_state,
+		dev->streaming_users, dev->users);
+
 	mutex_lock(&dev->lock);
-	if (res_check(fh, AU0828_RESOURCE_VIDEO)) {
+	if (vdev->vfl_type == VFL_TYPE_GRABBER && dev->vid_timeout_running) {
 		/* Cancel timeout thread in case they didn't call streamoff */
 		dev->vid_timeout_running = 0;
 		del_timer_sync(&dev->vid_timeout);
-
-		videobuf_stop(&fh->vb_vidq);
-		res_free(fh, AU0828_RESOURCE_VIDEO);
-	}
-
-	if (res_check(fh, AU0828_RESOURCE_VBI)) {
+	} else if (vdev->vfl_type == VFL_TYPE_VBI &&
+			dev->vbi_timeout_running) {
 		/* Cancel timeout thread in case they didn't call streamoff */
 		dev->vbi_timeout_running = 0;
 		del_timer_sync(&dev->vbi_timeout);
-
-		videobuf_stop(&fh->vb_vbiq);
-		res_free(fh, AU0828_RESOURCE_VBI);
 	}
 
-	if (dev->users == 1 && video_is_registered(video_devdata(filp))) {
-		au0828_analog_stream_disable(dev);
+	if (dev->dev_state == DEV_DISCONNECTED)
+		goto end;
 
-		au0828_uninit_isoc(dev);
-
+	if (dev->users == 1) {
 		/* Save some power by putting tuner to sleep */
 		v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
 		dev->std_set_in_tuner_core = 0;
@@ -1095,13 +1015,10 @@
 		if (ret < 0)
 			pr_info("Au0828 can't set alternate to 0!\n");
 	}
-	mutex_unlock(&dev->lock);
-
-	videobuf_mmap_free(&fh->vb_vidq);
-	videobuf_mmap_free(&fh->vb_vbiq);
-	kfree(fh);
+end:
+	_vb2_fop_release(filp, NULL);
 	dev->users--;
-	wake_up_interruptible_nr(&dev->open, 1);
+	mutex_unlock(&dev->lock);
 	return 0;
 }
 
@@ -1113,6 +1030,9 @@
 		.type = V4L2_TUNER_ANALOG_TV,
 	};
 
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
+
 	if (dev->std_set_in_tuner_core)
 		return;
 	dev->std_set_in_tuner_core = 1;
@@ -1125,98 +1045,6 @@
 	i2c_gate_ctrl(dev, 0);
 }
 
-static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf,
-				size_t count, loff_t *pos)
-{
-	struct au0828_fh *fh = filp->private_data;
-	struct au0828_dev *dev = fh->dev;
-	int rc;
-
-	rc = check_dev(dev);
-	if (rc < 0)
-		return rc;
-
-	if (mutex_lock_interruptible(&dev->lock))
-		return -ERESTARTSYS;
-	au0828_init_tuner(dev);
-	mutex_unlock(&dev->lock);
-
-	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-		if (res_locked(dev, AU0828_RESOURCE_VIDEO))
-			return -EBUSY;
-
-		return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
-					filp->f_flags & O_NONBLOCK);
-	}
-
-	if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
-		if (!res_get(fh, AU0828_RESOURCE_VBI))
-			return -EBUSY;
-
-		if (dev->vbi_timeout_running == 0) {
-			/* Handle case where caller tries to read without
-			   calling streamon first */
-			dev->vbi_timeout_running = 1;
-			mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
-		}
-
-		return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
-					    filp->f_flags & O_NONBLOCK);
-	}
-
-	return 0;
-}
-
-static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait)
-{
-	struct au0828_fh *fh = filp->private_data;
-	struct au0828_dev *dev = fh->dev;
-	unsigned long req_events = poll_requested_events(wait);
-	unsigned int res;
-
-	if (check_dev(dev) < 0)
-		return POLLERR;
-
-	res = v4l2_ctrl_poll(filp, wait);
-	if (!(req_events & (POLLIN | POLLRDNORM)))
-		return res;
-
-	if (mutex_lock_interruptible(&dev->lock))
-		return -ERESTARTSYS;
-	au0828_init_tuner(dev);
-	mutex_unlock(&dev->lock);
-
-	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-		if (!res_get(fh, AU0828_RESOURCE_VIDEO))
-			return POLLERR;
-		return res | videobuf_poll_stream(filp, &fh->vb_vidq, wait);
-	}
-	if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
-		if (!res_get(fh, AU0828_RESOURCE_VBI))
-			return POLLERR;
-		return res | videobuf_poll_stream(filp, &fh->vb_vbiq, wait);
-	}
-	return POLLERR;
-}
-
-static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
-{
-	struct au0828_fh *fh    = filp->private_data;
-	struct au0828_dev *dev   = fh->dev;
-	int		 rc;
-
-	rc = check_dev(dev);
-	if (rc < 0)
-		return rc;
-
-	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
-	else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
-		rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma);
-
-	return rc;
-}
-
 static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd,
 			     struct v4l2_format *format)
 {
@@ -1268,13 +1096,14 @@
 	return 0;
 }
 
-
 static int vidioc_querycap(struct file *file, void  *priv,
 			   struct v4l2_capability *cap)
 {
 	struct video_device *vdev = video_devdata(file);
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
+
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
 
 	strlcpy(cap->driver, "au0828", sizeof(cap->driver));
 	strlcpy(cap->card, dev->board.name, sizeof(cap->card));
@@ -1300,6 +1129,8 @@
 	if (f->index)
 		return -EINVAL;
 
+	dprintk(1, "%s called\n", __func__);
+
 	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	strcpy(f->description, "Packed YUV2");
 
@@ -1312,8 +1143,10 @@
 static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
 					struct v4l2_format *f)
 {
-	struct au0828_fh *fh  = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
+
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
 
 	f->fmt.pix.width = dev->width;
 	f->fmt.pix.height = dev->height;
@@ -1329,8 +1162,10 @@
 static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
 				  struct v4l2_format *f)
 {
-	struct au0828_fh *fh  = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
+
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
 
 	return au0828_set_format(dev, VIDIOC_TRY_FMT, f);
 }
@@ -1338,15 +1173,17 @@
 static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 				struct v4l2_format *f)
 {
-	struct au0828_fh *fh  = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
 	int rc;
 
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
+
 	rc = check_dev(dev);
 	if (rc < 0)
 		return rc;
 
-	if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+	if (vb2_is_busy(&dev->vb_vidq)) {
 		pr_info("%s queue busy\n", __func__);
 		rc = -EBUSY;
 		goto out;
@@ -1359,8 +1196,16 @@
 
 static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
+
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
+
+	if (norm == dev->std)
+		return 0;
+
+	if (dev->streaming_users > 0)
+		return -EBUSY;
 
 	dev->std = norm;
 
@@ -1383,8 +1228,10 @@
 
 static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
+
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
 
 	*norm = dev->std;
 	return 0;
@@ -1393,8 +1240,7 @@
 static int vidioc_enum_input(struct file *file, void *priv,
 				struct v4l2_input *input)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
 	unsigned int tmp;
 
 	static const char *inames[] = {
@@ -1407,6 +1253,9 @@
 		[AU0828_VMUX_DEBUG] = "tv debug"
 	};
 
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
+
 	tmp = input->index;
 
 	if (tmp >= AU0828_MAX_INPUT)
@@ -1425,15 +1274,18 @@
 		input->audioset = 2;
 	}
 
-	input->std = dev->vdev->tvnorms;
+	input->std = dev->vdev.tvnorms;
 
 	return 0;
 }
 
 static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
+
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
+
 	*i = dev->ctrl_input;
 	return 0;
 }
@@ -1442,6 +1294,9 @@
 {
 	int i;
 
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
+
 	switch (AUVI_INPUT(index).type) {
 	case AU0828_VMUX_SVIDEO:
 		dev->input_type = AU0828_VMUX_SVIDEO;
@@ -1491,8 +1346,7 @@
 
 static int vidioc_s_input(struct file *file, void *priv, unsigned int index)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
 
 	dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__,
 		index);
@@ -1510,6 +1364,8 @@
 	if (a->index > 1)
 		return -EINVAL;
 
+	dprintk(1, "%s called\n", __func__);
+
 	if (a->index == 0)
 		strcpy(a->name, "Television");
 	else
@@ -1521,8 +1377,10 @@
 
 static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
+
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
 
 	a->index = dev->ctrl_ainput;
 	if (a->index == 0)
@@ -1536,22 +1394,26 @@
 
 static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
 
 	if (a->index != dev->ctrl_ainput)
 		return -EINVAL;
+
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
 	return 0;
 }
 
 static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
 
 	if (t->index != 0)
 		return -EINVAL;
 
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
+
 	strcpy(t->name, "Auvitek tuner");
 
 	au0828_init_tuner(dev);
@@ -1564,12 +1426,14 @@
 static int vidioc_s_tuner(struct file *file, void *priv,
 				const struct v4l2_tuner *t)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
 
 	if (t->index != 0)
 		return -EINVAL;
 
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
+
 	au0828_init_tuner(dev);
 	i2c_gate_ctrl(dev, 1);
 	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
@@ -1585,11 +1449,12 @@
 static int vidioc_g_frequency(struct file *file, void *priv,
 				struct v4l2_frequency *freq)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
 
 	if (freq->tuner != 0)
 		return -EINVAL;
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
 	freq->frequency = dev->ctrl_freq;
 	return 0;
 }
@@ -1597,13 +1462,15 @@
 static int vidioc_s_frequency(struct file *file, void *priv,
 				const struct v4l2_frequency *freq)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
 	struct v4l2_frequency new_freq = *freq;
 
 	if (freq->tuner != 0)
 		return -EINVAL;
 
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
+
 	au0828_init_tuner(dev);
 	i2c_gate_ctrl(dev, 1);
 
@@ -1625,8 +1492,10 @@
 static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
 				struct v4l2_format *format)
 {
-	struct au0828_fh      *fh  = priv;
-	struct au0828_dev     *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
+
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
 
 	format->fmt.vbi.samples_per_line = dev->vbi_width;
 	format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
@@ -1646,12 +1515,14 @@
 static int vidioc_cropcap(struct file *file, void *priv,
 			  struct v4l2_cropcap *cc)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
 
 	if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
 
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
+
 	cc->bounds.left = 0;
 	cc->bounds.top = 0;
 	cc->bounds.width = dev->width;
@@ -1665,105 +1536,14 @@
 	return 0;
 }
 
-static int vidioc_streamon(struct file *file, void *priv,
-			   enum v4l2_buf_type type)
-{
-	struct au0828_fh      *fh  = priv;
-	struct au0828_dev     *dev = fh->dev;
-	int                   rc = -EINVAL;
-
-	rc = check_dev(dev);
-	if (rc < 0)
-		return rc;
-
-	if (unlikely(type != fh->type))
-		return -EINVAL;
-
-	dprintk(1, "vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n",
-		fh, type, fh->resources, dev->resources);
-
-	if (unlikely(!res_get(fh, get_ressource(fh))))
-		return -EBUSY;
-
-	au0828_init_tuner(dev);
-	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-		au0828_analog_stream_enable(dev);
-		v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
-	}
-
-	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-		rc = videobuf_streamon(&fh->vb_vidq);
-		dev->vid_timeout_running = 1;
-		mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
-	} else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
-		rc = videobuf_streamon(&fh->vb_vbiq);
-		dev->vbi_timeout_running = 1;
-		mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
-	}
-
-	return rc;
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
-			    enum v4l2_buf_type type)
-{
-	struct au0828_fh      *fh  = priv;
-	struct au0828_dev     *dev = fh->dev;
-	int                   rc;
-	int                   i;
-
-	rc = check_dev(dev);
-	if (rc < 0)
-		return rc;
-
-	if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
-	    fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)
-		return -EINVAL;
-	if (type != fh->type)
-		return -EINVAL;
-
-	dprintk(1, "vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n",
-		fh, type, fh->resources, dev->resources);
-
-	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-		dev->vid_timeout_running = 0;
-		del_timer_sync(&dev->vid_timeout);
-
-		au0828_analog_stream_disable(dev);
-		v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
-		rc = au0828_stream_interrupt(dev);
-		if (rc != 0)
-			return rc;
-
-		for (i = 0; i < AU0828_MAX_INPUT; i++) {
-			if (AUVI_INPUT(i).audio_setup == NULL)
-				continue;
-			(AUVI_INPUT(i).audio_setup)(dev, 0);
-		}
-
-		if (res_check(fh, AU0828_RESOURCE_VIDEO)) {
-			videobuf_streamoff(&fh->vb_vidq);
-			res_free(fh, AU0828_RESOURCE_VIDEO);
-		}
-	} else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
-		dev->vbi_timeout_running = 0;
-		del_timer_sync(&dev->vbi_timeout);
-
-		if (res_check(fh, AU0828_RESOURCE_VBI)) {
-			videobuf_streamoff(&fh->vb_vbiq);
-			res_free(fh, AU0828_RESOURCE_VBI);
-		}
-	}
-
-	return 0;
-}
-
 #ifdef CPTCFG_VIDEO_ADV_DEBUG
 static int vidioc_g_register(struct file *file, void *priv,
 			     struct v4l2_dbg_register *reg)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
+
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
 
 	reg->val = au0828_read(dev, reg->reg);
 	reg->size = 1;
@@ -1773,8 +1553,10 @@
 static int vidioc_s_register(struct file *file, void *priv,
 			     const struct v4l2_dbg_register *reg)
 {
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
+	struct au0828_dev *dev = video_drvdata(file);
+
+	dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+		dev->std_set_in_tuner_core, dev->dev_state);
 
 	return au0828_writereg(dev, reg->reg, reg->val);
 }
@@ -1784,93 +1566,13 @@
 {
 	struct video_device *vdev = video_devdata(file);
 
+	dprintk(1, "%s called\n", __func__);
+
 	v4l2_ctrl_log_status(file, fh);
 	v4l2_device_call_all(vdev->v4l2_dev, 0, core, log_status);
 	return 0;
 }
 
-static int vidioc_reqbufs(struct file *file, void *priv,
-			  struct v4l2_requestbuffers *rb)
-{
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
-	int rc;
-
-	rc = check_dev(dev);
-	if (rc < 0)
-		return rc;
-
-	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		rc = videobuf_reqbufs(&fh->vb_vidq, rb);
-	else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
-		rc = videobuf_reqbufs(&fh->vb_vbiq, rb);
-
-	return rc;
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
-			   struct v4l2_buffer *b)
-{
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
-	int rc;
-
-	rc = check_dev(dev);
-	if (rc < 0)
-		return rc;
-
-	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		rc = videobuf_querybuf(&fh->vb_vidq, b);
-	else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
-		rc = videobuf_querybuf(&fh->vb_vbiq, b);
-
-	return rc;
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
-	int rc;
-
-	rc = check_dev(dev);
-	if (rc < 0)
-		return rc;
-
-	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		rc = videobuf_qbuf(&fh->vb_vidq, b);
-	else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
-		rc = videobuf_qbuf(&fh->vb_vbiq, b);
-
-	return rc;
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
-	struct au0828_fh *fh = priv;
-	struct au0828_dev *dev = fh->dev;
-	int rc;
-
-	rc = check_dev(dev);
-	if (rc < 0)
-		return rc;
-
-	/* Workaround for a bug in the au0828 hardware design that sometimes
-	   results in the colorspace being inverted */
-	if (dev->greenscreen_detected == 1) {
-		dprintk(1, "Detected green frame.  Resetting stream...\n");
-		au0828_analog_stream_reset(dev);
-		dev->greenscreen_detected = 0;
-	}
-
-	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		rc = videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
-	else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
-		rc = videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & O_NONBLOCK);
-
-	return rc;
-}
-
 void au0828_v4l2_suspend(struct au0828_dev *dev)
 {
 	struct urb *urb;
@@ -1938,9 +1640,9 @@
 	.owner      = THIS_MODULE,
 	.open       = au0828_v4l2_open,
 	.release    = au0828_v4l2_close,
-	.read       = au0828_v4l2_read,
-	.poll       = au0828_v4l2_poll,
-	.mmap       = au0828_v4l2_mmap,
+	.read       = vb2_fop_read,
+	.poll       = vb2_fop_poll,
+	.mmap       = vb2_fop_mmap,
 	.unlocked_ioctl = video_ioctl2,
 };
 
@@ -1957,17 +1659,26 @@
 	.vidioc_g_audio             = vidioc_g_audio,
 	.vidioc_s_audio             = vidioc_s_audio,
 	.vidioc_cropcap             = vidioc_cropcap,
-	.vidioc_reqbufs             = vidioc_reqbufs,
-	.vidioc_querybuf            = vidioc_querybuf,
-	.vidioc_qbuf                = vidioc_qbuf,
-	.vidioc_dqbuf               = vidioc_dqbuf,
+
+	.vidioc_reqbufs             = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs         = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf         = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf            = vb2_ioctl_querybuf,
+	.vidioc_qbuf                = vb2_ioctl_qbuf,
+	.vidioc_dqbuf               = vb2_ioctl_dqbuf,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+	.vidioc_expbuf               = vb2_ioctl_expbuf,
+#endif
+
 	.vidioc_s_std               = vidioc_s_std,
 	.vidioc_g_std               = vidioc_g_std,
 	.vidioc_enum_input          = vidioc_enum_input,
 	.vidioc_g_input             = vidioc_g_input,
 	.vidioc_s_input             = vidioc_s_input,
-	.vidioc_streamon            = vidioc_streamon,
-	.vidioc_streamoff           = vidioc_streamoff,
+
+	.vidioc_streamon            = vb2_ioctl_streamon,
+	.vidioc_streamoff           = vb2_ioctl_streamoff,
+
 	.vidioc_g_tuner             = vidioc_g_tuner,
 	.vidioc_s_tuner             = vidioc_s_tuner,
 	.vidioc_g_frequency         = vidioc_g_frequency,
@@ -1983,11 +1694,47 @@
 
 static const struct video_device au0828_video_template = {
 	.fops                       = &au0828_v4l_fops,
-	.release                    = video_device_release,
+	.release                    = video_device_release_empty,
 	.ioctl_ops 		    = &video_ioctl_ops,
 	.tvnorms                    = V4L2_STD_NTSC_M | V4L2_STD_PAL_M,
 };
 
+static int au0828_vb2_setup(struct au0828_dev *dev)
+{
+	int rc;
+	struct vb2_queue *q;
+
+	/* Setup Videobuf2 for Video capture */
+	q = &dev->vb_vidq;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->drv_priv = dev;
+	q->buf_struct_size = sizeof(struct au0828_buffer);
+	q->ops = &au0828_video_qops;
+	q->mem_ops = &vb2_vmalloc_memops;
+
+	rc = vb2_queue_init(q);
+	if (rc < 0)
+		return rc;
+
+	/* Setup Videobuf2 for VBI capture */
+	q = &dev->vb_vbiq;
+	q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+	q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->drv_priv = dev;
+	q->buf_struct_size = sizeof(struct au0828_buffer);
+	q->ops = &au0828_vbi_qops;
+	q->mem_ops = &vb2_vmalloc_memops;
+
+	rc = vb2_queue_init(q);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
 /**************************************************************************/
 
 int au0828_analog_register(struct au0828_dev *dev,
@@ -2030,7 +1777,6 @@
 	}
 	if (!(dev->isoc_in_endpointaddr)) {
 		pr_info("Could not locate isoc endpoint\n");
-		kfree(dev);
 		return -ENODEV;
 	}
 
@@ -2039,17 +1785,12 @@
 
 	/* init video dma queues */
 	INIT_LIST_HEAD(&dev->vidq.active);
-	INIT_LIST_HEAD(&dev->vidq.queued);
 	INIT_LIST_HEAD(&dev->vbiq.active);
-	INIT_LIST_HEAD(&dev->vbiq.queued);
 
-	dev->vid_timeout.function = au0828_vid_buffer_timeout;
-	dev->vid_timeout.data = (unsigned long) dev;
-	init_timer(&dev->vid_timeout);
-
-	dev->vbi_timeout.function = au0828_vbi_buffer_timeout;
-	dev->vbi_timeout.data = (unsigned long) dev;
-	init_timer(&dev->vbi_timeout);
+	setup_timer(&dev->vid_timeout, au0828_vid_buffer_timeout,
+		    (unsigned long)dev);
+	setup_timer(&dev->vbi_timeout, au0828_vbi_buffer_timeout,
+		    (unsigned long)dev);
 
 	dev->width = NTSC_STD_W;
 	dev->height = NTSC_STD_H;
@@ -2063,61 +1804,62 @@
 	dev->std = V4L2_STD_NTSC_M;
 	au0828_s_input(dev, 0);
 
-	/* allocate and fill v4l2 video struct */
-	dev->vdev = video_device_alloc();
-	if (NULL == dev->vdev) {
-		dprintk(1, "Can't allocate video_device.\n");
-		return -ENOMEM;
-	}
-
-	/* allocate the VBI struct */
-	dev->vbi_dev = video_device_alloc();
-	if (NULL == dev->vbi_dev) {
-		dprintk(1, "Can't allocate vbi_device.\n");
-		ret = -ENOMEM;
-		goto err_vdev;
-	}
+	mutex_init(&dev->vb_queue_lock);
+	mutex_init(&dev->vb_vbi_queue_lock);
 
 	/* Fill the video capture device struct */
-	*dev->vdev = au0828_video_template;
-	dev->vdev->v4l2_dev = &dev->v4l2_dev;
-	dev->vdev->lock = &dev->lock;
-	strcpy(dev->vdev->name, "au0828a video");
+	dev->vdev = au0828_video_template;
+	dev->vdev.v4l2_dev = &dev->v4l2_dev;
+	dev->vdev.lock = &dev->lock;
+	dev->vdev.queue = &dev->vb_vidq;
+	dev->vdev.queue->lock = &dev->vb_queue_lock;
+	strcpy(dev->vdev.name, "au0828a video");
 
 	/* Setup the VBI device */
-	*dev->vbi_dev = au0828_video_template;
-	dev->vbi_dev->v4l2_dev = &dev->v4l2_dev;
-	dev->vbi_dev->lock = &dev->lock;
-	strcpy(dev->vbi_dev->name, "au0828a vbi");
+	dev->vbi_dev = au0828_video_template;
+	dev->vbi_dev.v4l2_dev = &dev->v4l2_dev;
+	dev->vbi_dev.lock = &dev->lock;
+	dev->vbi_dev.queue = &dev->vb_vbiq;
+	dev->vbi_dev.queue->lock = &dev->vb_vbi_queue_lock;
+	strcpy(dev->vbi_dev.name, "au0828a vbi");
+
+	/* initialize videobuf2 stuff */
+	retval = au0828_vb2_setup(dev);
+	if (retval != 0) {
+		dprintk(1, "unable to setup videobuf2 queues (error = %d).\n",
+			retval);
+		return -ENODEV;
+	}
 
 	/* Register the v4l2 device */
-	video_set_drvdata(dev->vdev, dev);
-	retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1);
+	video_set_drvdata(&dev->vdev, dev);
+	retval = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
 	if (retval != 0) {
 		dprintk(1, "unable to register video device (error = %d).\n",
 			retval);
 		ret = -ENODEV;
-		goto err_vbi_dev;
+		goto err_reg_vdev;
 	}
 
 	/* Register the vbi device */
-	video_set_drvdata(dev->vbi_dev, dev);
-	retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1);
+	video_set_drvdata(&dev->vbi_dev, dev);
+	retval = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI, -1);
 	if (retval != 0) {
 		dprintk(1, "unable to register vbi device (error = %d).\n",
 			retval);
 		ret = -ENODEV;
-		goto err_vbi_dev;
+		goto err_reg_vbi_dev;
 	}
 
 	dprintk(1, "%s completed!\n", __func__);
 
 	return 0;
 
-err_vbi_dev:
-	video_device_release(dev->vbi_dev);
-err_vdev:
-	video_device_release(dev->vdev);
+err_reg_vbi_dev:
+	video_unregister_device(&dev->vdev);
+err_reg_vdev:
+	vb2_queue_release(&dev->vb_vidq);
+	vb2_queue_release(&dev->vb_vbiq);
 	return ret;
 }
 
diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h
index 53025e2..9c6c402 100644
--- a/drivers/media/usb/au0828/au0828.h
+++ b/drivers/media/usb/au0828/au0828.h
@@ -28,7 +28,7 @@
 
 /* Analog */
 #include <linux/videodev2.h>
-#include <media/videobuf-vmalloc.h>
+#include <media/videobuf2-vmalloc.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-fh.h>
@@ -126,17 +126,7 @@
 	DEV_MISCONFIGURED = 0x04
 };
 
-struct au0828_fh {
-	/* must be the first field of this struct! */
-	struct v4l2_fh fh;
-
-	struct au0828_dev *dev;
-	unsigned int  resources;
-
-	struct videobuf_queue        vb_vidq;
-	struct videobuf_queue        vb_vbiq;
-	enum v4l2_buf_type           type;
-};
+struct au0828_dev;
 
 struct au0828_usb_isoc_ctl {
 		/* max packet size of isoc transaction */
@@ -177,21 +167,20 @@
 /* buffer for one video frame */
 struct au0828_buffer {
 	/* common v4l buffer stuff -- must be first */
-	struct videobuf_buffer vb;
+	struct vb2_buffer vb;
+	struct list_head list;
 
-	struct list_head frame;
+	void *mem;
+	unsigned long length;
 	int top_field;
-	int receiving;
+	/* pointer to vmalloc memory address in vb */
+	char *vb_buf;
 };
 
 struct au0828_dmaqueue {
 	struct list_head       active;
-	struct list_head       queued;
-
-	wait_queue_head_t          wq;
-
 	/* Counters to control buffer fill */
-	int                        pos;
+	int                    pos;
 };
 
 struct au0828_dev {
@@ -220,14 +209,26 @@
 	struct au0828_rc *ir;
 #endif
 
-	int users;
-	unsigned int resources;	/* resources in use */
-	struct video_device *vdev;
-	struct video_device *vbi_dev;
+	struct video_device vdev;
+	struct video_device vbi_dev;
+
+	/* Videobuf2 */
+	struct vb2_queue vb_vidq;
+	struct vb2_queue vb_vbiq;
+	struct mutex vb_queue_lock;
+	struct mutex vb_vbi_queue_lock;
+
+	unsigned int frame_count;
+	unsigned int vbi_frame_count;
+
 	struct timer_list vid_timeout;
 	int vid_timeout_running;
 	struct timer_list vbi_timeout;
 	int vbi_timeout_running;
+
+	int users;
+	int streaming_users;
+
 	int width;
 	int height;
 	int vbi_width;
@@ -242,7 +243,6 @@
 	__u8 isoc_in_endpointaddr;
 	u8 isoc_init_ok;
 	int greenscreen_detected;
-	unsigned int frame_count;
 	int ctrl_freq;
 	int input_type;
 	int std_set_in_tuner_core;
@@ -277,6 +277,7 @@
 	char *dig_transfer_buffer[URB_COUNT];
 };
 
+
 /* ----------------------------------------------------------- */
 #define au0828_read(dev, reg) au0828_readreg(dev, reg)
 #define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value)
@@ -309,13 +310,15 @@
 
 /* ----------------------------------------------------------- */
 /* au0828-video.c */
-int au0828_analog_register(struct au0828_dev *dev,
+extern int au0828_analog_register(struct au0828_dev *dev,
 			   struct usb_interface *interface);
-int au0828_analog_stream_disable(struct au0828_dev *d);
-void au0828_analog_unregister(struct au0828_dev *dev);
+extern void au0828_analog_unregister(struct au0828_dev *dev);
+extern int au0828_start_analog_streaming(struct vb2_queue *vq,
+						unsigned int count);
+extern void au0828_stop_vbi_streaming(struct vb2_queue *vq);
 #ifdef CPTCFG_VIDEO_AU0828_V4L2
-void au0828_v4l2_suspend(struct au0828_dev *dev);
-void au0828_v4l2_resume(struct au0828_dev *dev);
+extern void au0828_v4l2_suspend(struct au0828_dev *dev);
+extern void au0828_v4l2_resume(struct au0828_dev *dev);
 #else
 static inline void au0828_v4l2_suspend(struct au0828_dev *dev) { };
 static inline void au0828_v4l2_resume(struct au0828_dev *dev) { };
@@ -329,7 +332,7 @@
 void au0828_dvb_resume(struct au0828_dev *dev);
 
 /* au0828-vbi.c */
-extern struct videobuf_queue_ops au0828_vbi_qops;
+extern struct vb2_ops au0828_vbi_qops;
 
 #define dprintk(level, fmt, arg...)\
 	do { if (au0828_debug & level)\
diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig
index 0d1ff87..e4570ce 100644
--- a/drivers/media/usb/cx231xx/Kconfig
+++ b/drivers/media/usb/cx231xx/Kconfig
@@ -50,6 +50,7 @@
 	select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LGDT3306A if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c
index 64f345d..d6ed547 100644
--- a/drivers/media/usb/cx231xx/cx231xx-417.c
+++ b/drivers/media/usb/cx231xx/cx231xx-417.c
@@ -1868,13 +1868,9 @@
 	dprintk(1, "%s()\n", __func__);
 	dprintk(3, "%s()\n", __func__);
 
-	if (dev->v4l_device) {
-		if (-1 != dev->v4l_device->minor)
-			video_unregister_device(dev->v4l_device);
-		else
-			video_device_release(dev->v4l_device);
+	if (video_is_registered(&dev->v4l_device)) {
+		video_unregister_device(&dev->v4l_device);
 		v4l2_ctrl_handler_free(&dev->mpeg_ctrl_handler.hdl);
-		dev->v4l_device = NULL;
 	}
 }
 
@@ -1911,25 +1907,21 @@
 	.s_video_encoding = cx231xx_s_video_encoding,
 };
 
-static struct video_device *cx231xx_video_dev_alloc(
+static void cx231xx_video_dev_init(
 	struct cx231xx *dev,
 	struct usb_device *usbdev,
-	struct video_device *template,
-	char *type)
+	struct video_device *vfd,
+	const struct video_device *template,
+	const char *type)
 {
-	struct video_device *vfd;
-
 	dprintk(1, "%s()\n", __func__);
-	vfd = video_device_alloc();
-	if (NULL == vfd)
-		return NULL;
 	*vfd = *template;
 	snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
 		type, cx231xx_boards[dev->model].name);
 
 	vfd->v4l2_dev = &dev->v4l2_dev;
 	vfd->lock = &dev->lock;
-	vfd->release = video_device_release;
+	vfd->release = video_device_release_empty;
 	vfd->ctrl_handler = &dev->mpeg_ctrl_handler.hdl;
 	video_set_drvdata(vfd, dev);
 	if (dev->tuner_type == TUNER_ABSENT) {
@@ -1938,9 +1930,6 @@
 		v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER);
 		v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER);
 	}
-
-	return vfd;
-
 }
 
 int cx231xx_417_register(struct cx231xx *dev)
@@ -1983,9 +1972,9 @@
 	cx2341x_handler_set_50hz(&dev->mpeg_ctrl_handler, false);
 
 	/* Allocate and initialize V4L video device */
-	dev->v4l_device = cx231xx_video_dev_alloc(dev,
-		dev->udev, &cx231xx_mpeg_template, "mpeg");
-	err = video_register_device(dev->v4l_device,
+	cx231xx_video_dev_init(dev, dev->udev,
+			&dev->v4l_device, &cx231xx_mpeg_template, "mpeg");
+	err = video_register_device(&dev->v4l_device,
 		VFL_TYPE_GRABBER, -1);
 	if (err < 0) {
 		dprintk(3, "%s: can't register mpeg device\n", dev->name);
@@ -1994,7 +1983,7 @@
 	}
 
 	dprintk(3, "%s: registered device video%d [mpeg]\n",
-	       dev->name, dev->v4l_device->num);
+	       dev->name, dev->v4l_device.num);
 
 	return 0;
 }
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index ae05d59..af0c9c1 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -776,6 +776,45 @@
 			.gpio = NULL,
 		} },
 	},
+	[CX231XX_BOARD_HAUPPAUGE_955Q] = {
+		.name = "Hauppauge WinTV-HVR-955Q (111401)",
+		.tuner_type = TUNER_ABSENT,
+		.tuner_addr = 0x60,
+		.tuner_gpio = RDE250_XCV_TUNER,
+		.tuner_sif_gpio = 0x05,
+		.tuner_scl_gpio = 0x1a,
+		.tuner_sda_gpio = 0x1b,
+		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
+		.demod_xfer_mode = 0,
+		.ctl_pin_status_mask = 0xFFFFFFC4,
+		.agc_analog_digital_select_gpio = 0x0c,
+		.gpio_pin_status_mask = 0x4001000,
+		.tuner_i2c_master = I2C_1_MUX_3,
+		.demod_i2c_master = I2C_2,
+		.has_dvb = 1,
+		.demod_addr = 0x0e,
+		.norm = V4L2_STD_NTSC,
+
+		.input = {{
+			.type = CX231XX_VMUX_TELEVISION,
+			.vmux = CX231XX_VIN_3_1,
+			.amux = CX231XX_AMUX_VIDEO,
+			.gpio = NULL,
+		}, {
+			.type = CX231XX_VMUX_COMPOSITE1,
+			.vmux = CX231XX_VIN_2_1,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = NULL,
+		}, {
+			.type = CX231XX_VMUX_SVIDEO,
+			.vmux = CX231XX_VIN_1_1 |
+				(CX231XX_VIN_1_2 << 8) |
+				CX25840_SVIDEO_ON,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = NULL,
+		} },
+	},
 };
 const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
 
@@ -805,6 +844,8 @@
 	 .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC},
 	{USB_DEVICE(0x2040, 0xb120),
 	 .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
+	{USB_DEVICE(0x2040, 0xb123),
+	 .driver_info = CX231XX_BOARD_HAUPPAUGE_955Q},
 	{USB_DEVICE(0x2040, 0xb130),
 	 .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx},
 	{USB_DEVICE(0x2040, 0xb131),
@@ -912,9 +953,6 @@
  */
 void cx231xx_pre_card_setup(struct cx231xx *dev)
 {
-
-	cx231xx_set_model(dev);
-
 	dev_info(dev->dev, "Identified as %s (card=%d)\n",
 		dev->board.name, dev->model);
 
@@ -1052,6 +1090,7 @@
 	switch (dev->model) {
 	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx:
 	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
+	case CX231XX_BOARD_HAUPPAUGE_955Q:
 		{
 			struct tveeprom tvee;
 			static u8 eeprom[256];
@@ -1092,6 +1131,17 @@
 	call_all(dev, video, s_stream, 1);
 }
 
+static void cx231xx_unregister_media_device(struct cx231xx *dev)
+{
+#ifdef CPTCFG_MEDIA_CONTROLLER
+	if (dev->media_dev) {
+		media_device_unregister(dev->media_dev);
+		kfree(dev->media_dev);
+		dev->media_dev = NULL;
+	}
+#endif
+}
+
 /*
  * cx231xx_realease_resources()
  * unregisters the v4l2,i2c and usb devices
@@ -1099,6 +1149,8 @@
 */
 void cx231xx_release_resources(struct cx231xx *dev)
 {
+	cx231xx_unregister_media_device(dev);
+
 	cx231xx_release_analog_resources(dev);
 
 	cx231xx_remove_from_devlist(dev);
@@ -1117,6 +1169,74 @@
 	clear_bit(dev->devno, &cx231xx_devused);
 }
 
+static void cx231xx_media_device_register(struct cx231xx *dev,
+					  struct usb_device *udev)
+{
+#ifdef CPTCFG_MEDIA_CONTROLLER
+	struct media_device *mdev;
+	int ret;
+
+	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+	if (!mdev)
+		return;
+
+	mdev->dev = dev->dev;
+	strlcpy(mdev->model, dev->board.name, sizeof(mdev->model));
+	if (udev->serial)
+		strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
+	strcpy(mdev->bus_info, udev->devpath);
+	mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+	mdev->driver_version = LINUX_VERSION_CODE;
+
+	ret = media_device_register(mdev);
+	if (ret) {
+		dev_err(dev->dev,
+			"Couldn't create a media device. Error: %d\n",
+			ret);
+		kfree(mdev);
+		return;
+	}
+
+	dev->media_dev = mdev;
+#endif
+}
+
+static void cx231xx_create_media_graph(struct cx231xx *dev)
+{
+#ifdef CPTCFG_MEDIA_CONTROLLER
+	struct media_device *mdev = dev->media_dev;
+	struct media_entity *entity;
+	struct media_entity *tuner = NULL, *decoder = NULL;
+
+	if (!mdev)
+		return;
+
+	media_device_for_each_entity(entity, mdev) {
+		switch (entity->type) {
+		case MEDIA_ENT_T_V4L2_SUBDEV_TUNER:
+			tuner = entity;
+			break;
+		case MEDIA_ENT_T_V4L2_SUBDEV_DECODER:
+			decoder = entity;
+			break;
+		}
+	}
+
+	/* Analog setup, using tuner as a link */
+
+	if (!decoder)
+		return;
+
+	if (tuner)
+		media_entity_create_link(tuner, 0, decoder, 0,
+					 MEDIA_LNK_FL_ENABLED);
+	media_entity_create_link(decoder, 1, &dev->vdev.entity, 0,
+				 MEDIA_LNK_FL_ENABLED);
+	media_entity_create_link(decoder, 2, &dev->vbi_dev.entity, 0,
+				 MEDIA_LNK_FL_ENABLED);
+#endif
+}
+
 /*
  * cx231xx_init_dev()
  * allocates and inits the device structs, registers i2c bus and v4l device
@@ -1225,10 +1345,8 @@
 	}
 
 	retval = cx231xx_register_analog_devices(dev);
-	if (retval) {
-		cx231xx_release_analog_resources(dev);
+	if (retval)
 		goto err_analog;
-	}
 
 	cx231xx_ir_init(dev);
 
@@ -1236,6 +1354,8 @@
 
 	return 0;
 err_analog:
+	cx231xx_unregister_media_device(dev);
+	cx231xx_release_analog_resources(dev);
 	cx231xx_remove_from_devlist(dev);
 err_dev_init:
 	cx231xx_dev_uninit(dev);
@@ -1403,7 +1523,6 @@
 	struct usb_interface_assoc_descriptor *assoc_desc;
 
 	ifnum = interface->altsetting[0].desc.bInterfaceNumber;
-	udev = usb_get_dev(interface_to_usbdev(interface));
 
 	/*
 	 * Interface number 0 - IR interface (handled by mceusb driver)
@@ -1424,11 +1543,13 @@
 		}
 	} while (test_and_set_bit(nr, &cx231xx_devused));
 
+	udev = usb_get_dev(interface_to_usbdev(interface));
+
 	/* allocate memory for our device state and initialize it */
 	dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL);
 	if (dev == NULL) {
-		clear_bit(nr, &cx231xx_devused);
-		return -ENOMEM;
+		retval = -ENOMEM;
+		goto err_if;
 	}
 
 	snprintf(dev->name, 29, "cx231xx #%d", nr);
@@ -1437,6 +1558,8 @@
 	dev->video_mode.alt = -1;
 	dev->dev = d;
 
+	cx231xx_set_model(dev);
+
 	dev->interface_count++;
 	/* reset gpio dir and value */
 	dev->gpio_dir = 0;
@@ -1501,7 +1624,13 @@
 	/* save our data pointer in this interface device */
 	usb_set_intfdata(interface, dev);
 
+	/* Register the media controller */
+	cx231xx_media_device_register(dev, udev);
+
 	/* Create v4l2 device */
+#ifdef CPTCFG_MEDIA_CONTROLLER
+	dev->v4l2_dev.mdev = dev->media_dev;
+#endif
 	retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
 	if (retval) {
 		dev_err(d, "v4l2_device_register failed\n");
@@ -1567,6 +1696,8 @@
 	/* load other modules required */
 	request_modules(dev);
 
+	cx231xx_create_media_graph(dev);
+
 	return 0;
 err_video_alt:
 	/* cx231xx_uninit_dev: */
@@ -1582,7 +1713,7 @@
 	usb_set_intfdata(interface, NULL);
 err_if:
 	usb_put_dev(udev);
-	clear_bit(dev->devno, &cx231xx_devused);
+	clear_bit(nr, &cx231xx_devused);
 	return retval;
 }
 
@@ -1617,7 +1748,7 @@
 	if (dev->users) {
 		dev_warn(dev->dev,
 			 "device %s is open! Deregistration and memory deallocation are deferred on close.\n",
-			 video_device_node_name(dev->vdev));
+			 video_device_node_name(&dev->vdev));
 
 		/* Even having users, it is safe to remove the RC i2c driver */
 		cx231xx_ir_exit(dev);
diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
index 4a3f28c..e42bde0 100644
--- a/drivers/media/usb/cx231xx/cx231xx-core.c
+++ b/drivers/media/usb/cx231xx/cx231xx-core.c
@@ -176,16 +176,9 @@
 	saddr_len = req_data->saddr_len;
 
 	/* Set wValue */
-	if (saddr_len == 1)	/* need check saddr_len == 0  */
-		ven_req.wValue =
-		    req_data->
-		    dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 |
-		    _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6;
-	else
-		ven_req.wValue =
-		    req_data->
-		    dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 |
-		    _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6;
+	ven_req.wValue = (req_data->dev_addr << 9 | _i2c_period << 4 |
+			  saddr_len << 2 | _i2c_nostop << 1 | I2C_SYNC |
+			  _i2c_reserve << 6);
 
 	/* set channel number */
 	if (req_data->direction & I2C_M_RD) {
diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c
index dd600b9..610d567 100644
--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c
+++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c
@@ -34,6 +34,7 @@
 #include "si2165.h"
 #include "mb86a20s.h"
 #include "si2157.h"
+#include "lgdt3306a.h"
 
 MODULE_DESCRIPTION("driver for cx231xx based DVB cards");
 MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
@@ -160,6 +161,18 @@
 	.ref_freq_Hz	= 24000000,
 };
 
+static struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = {
+	.i2c_addr           = 0x59,
+	.qam_if_khz         = 4000,
+	.vsb_if_khz         = 3250,
+	.deny_i2c_rptr      = 1,
+	.spectral_inversion = 1,
+	.mpeg_mode          = LGDT3306A_MPEG_SERIAL,
+	.tpclk_edge         = LGDT3306A_TPCLK_RISING_EDGE,
+	.tpvalid_polarity   = LGDT3306A_TP_VALID_HIGH,
+	.xtalMHz            = 25,
+};
+
 static inline void print_err_status(struct cx231xx *dev, int packet, int status)
 {
 	char *errmsg = "Unknown";
@@ -455,6 +468,7 @@
 
 	mutex_init(&dvb->lock);
 
+
 	/* register adapter */
 	result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
 				      adapter_nr);
@@ -464,6 +478,7 @@
 		       dev->name, result);
 		goto fail_adapter;
 	}
+	dvb_register_media_controller(&dvb->adapter, dev->media_dev);
 
 	/* Ensure all frontends negotiate bus access */
 	dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
@@ -536,6 +551,8 @@
 
 	/* register network adapter */
 	dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
+	dvb_create_media_graph(&dvb->adapter);
+
 	return 0;
 
 fail_fe_conn:
@@ -807,7 +824,61 @@
 		dev->dvb->i2c_client_tuner = client;
 		break;
 	}
+	case CX231XX_BOARD_HAUPPAUGE_955Q:
+	{
+		struct i2c_client *client;
+		struct i2c_board_info info;
+		struct si2157_config si2157_config;
 
+		memset(&info, 0, sizeof(struct i2c_board_info));
+
+		dev->dvb->frontend = dvb_attach(lgdt3306a_attach,
+			&hauppauge_955q_lgdt3306a_config,
+			tuner_i2c
+			);
+
+		if (dev->dvb->frontend == NULL) {
+			dev_err(dev->dev,
+				"Failed to attach LGDT3306A frontend.\n");
+			result = -EINVAL;
+			goto out_free;
+		}
+
+		dev->dvb->frontend->ops.i2c_gate_ctrl = NULL;
+
+		/* define general-purpose callback pointer */
+		dvb->frontend->callback = cx231xx_tuner_callback;
+
+		/* attach tuner */
+		memset(&si2157_config, 0, sizeof(si2157_config));
+		si2157_config.fe = dev->dvb->frontend;
+		si2157_config.inversion = true;
+		strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+		info.addr = 0x60;
+		info.platform_data = &si2157_config;
+		request_module("si2157");
+
+		client = i2c_new_device(
+			tuner_i2c,
+			&info);
+		if (client == NULL || client->dev.driver == NULL) {
+			dvb_frontend_detach(dev->dvb->frontend);
+			result = -ENODEV;
+			goto out_free;
+		}
+
+		if (!try_module_get(client->dev.driver->owner)) {
+			i2c_unregister_device(client);
+			dvb_frontend_detach(dev->dvb->frontend);
+			result = -ENODEV;
+			goto out_free;
+		}
+
+		dev->cx231xx_reset_analog_tuner = NULL;
+
+		dev->dvb->i2c_client_tuner = client;
+		break;
+	}
 	case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
 	case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID:
 
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index d72d74e..21c1b30 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -100,6 +100,75 @@
 };
 
 
+static int cx231xx_enable_analog_tuner(struct cx231xx *dev)
+{
+#ifdef CPTCFG_MEDIA_CONTROLLER
+	struct media_device *mdev = dev->media_dev;
+	struct media_entity  *entity, *decoder = NULL, *source;
+	struct media_link *link, *found_link = NULL;
+	int i, ret, active_links = 0;
+
+	if (!mdev)
+		return 0;
+
+	/*
+	 * This will find the tuner that is connected into the decoder.
+	 * Technically, this is not 100% correct, as the device may be
+	 * using an analog input instead of the tuner. However, as we can't
+	 * do DVB streaming while the DMA engine is being used for V4L2,
+	 * this should be enough for the actual needs.
+	 */
+	media_device_for_each_entity(entity, mdev) {
+		if (entity->type == MEDIA_ENT_T_V4L2_SUBDEV_DECODER) {
+			decoder = entity;
+			break;
+		}
+	}
+	if (!decoder)
+		return 0;
+
+	for (i = 0; i < decoder->num_links; i++) {
+		link = &decoder->links[i];
+		if (link->sink->entity == decoder) {
+			found_link = link;
+			if (link->flags & MEDIA_LNK_FL_ENABLED)
+				active_links++;
+			break;
+		}
+	}
+
+	if (active_links == 1 || !found_link)
+		return 0;
+
+	source = found_link->source->entity;
+	for (i = 0; i < source->num_links; i++) {
+		struct media_entity *sink;
+		int flags = 0;
+
+		link = &source->links[i];
+		sink = link->sink->entity;
+
+		if (sink == entity)
+			flags = MEDIA_LNK_FL_ENABLED;
+
+		ret = media_entity_setup_link(link, flags);
+		if (ret) {
+			dev_err(dev->dev,
+				"Couldn't change link %s->%s to %s. Error %d\n",
+				source->name, sink->name,
+				flags ? "enabled" : "disabled",
+				ret);
+			return ret;
+		} else
+			dev_dbg(dev->dev,
+				"link %s->%s was %s\n",
+				source->name, sink->name,
+				flags ? "ENABLED" : "disabled");
+	}
+#endif
+	return 0;
+}
+
 /* ------------------------------------------------------------------
 	Video buffer and parser functions
    ------------------------------------------------------------------*/
@@ -667,6 +736,9 @@
 	if (*count < CX231XX_MIN_BUF)
 		*count = CX231XX_MIN_BUF;
 
+
+	cx231xx_enable_analog_tuner(dev);
+
 	return 0;
 }
 
@@ -756,6 +828,7 @@
 	}
 
 	buf->vb.state = VIDEOBUF_PREPARED;
+
 	return 0;
 
 fail:
@@ -1056,7 +1129,7 @@
 	    (CX231XX_VMUX_CABLE == INPUT(n)->type))
 		i->type = V4L2_INPUT_TYPE_TUNER;
 
-	i->std = dev->vdev->tvnorms;
+	i->std = dev->vdev.tvnorms;
 
 	/* If they are asking about the active input, read signal status */
 	if (n == dev->video_input) {
@@ -1451,7 +1524,7 @@
 	cap->capabilities = cap->device_caps | V4L2_CAP_READWRITE |
 		V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE |
 		V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS;
-	if (dev->radio_dev)
+	if (video_is_registered(&dev->radio_dev))
 		cap->capabilities |= V4L2_CAP_RADIO;
 
 	return 0;
@@ -1729,34 +1802,21 @@
 
 	/*FIXME: I2C IR should be disconnected */
 
-	if (dev->radio_dev) {
-		if (video_is_registered(dev->radio_dev))
-			video_unregister_device(dev->radio_dev);
-		else
-			video_device_release(dev->radio_dev);
-		dev->radio_dev = NULL;
-	}
-	if (dev->vbi_dev) {
+	if (video_is_registered(&dev->radio_dev))
+		video_unregister_device(&dev->radio_dev);
+	if (video_is_registered(&dev->vbi_dev)) {
 		dev_info(dev->dev, "V4L2 device %s deregistered\n",
-			video_device_node_name(dev->vbi_dev));
-		if (video_is_registered(dev->vbi_dev))
-			video_unregister_device(dev->vbi_dev);
-		else
-			video_device_release(dev->vbi_dev);
-		dev->vbi_dev = NULL;
+			video_device_node_name(&dev->vbi_dev));
+		video_unregister_device(&dev->vbi_dev);
 	}
-	if (dev->vdev) {
+	if (video_is_registered(&dev->vdev)) {
 		dev_info(dev->dev, "V4L2 device %s deregistered\n",
-			video_device_node_name(dev->vdev));
+			video_device_node_name(&dev->vdev));
 
 		if (dev->board.has_417)
 			cx231xx_417_unregister(dev);
 
-		if (video_is_registered(dev->vdev))
-			video_unregister_device(dev->vdev);
-		else
-			video_device_release(dev->vdev);
-		dev->vdev = NULL;
+		video_unregister_device(&dev->vdev);
 	}
 	v4l2_ctrl_handler_free(&dev->ctrl_handler);
 	v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
@@ -2013,7 +2073,7 @@
 
 static const struct video_device cx231xx_video_template = {
 	.fops         = &cx231xx_v4l_fops,
-	.release      = video_device_release,
+	.release      = video_device_release_empty,
 	.ioctl_ops    = &video_ioctl_ops,
 	.tvnorms      = V4L2_STD_ALL,
 };
@@ -2049,20 +2109,14 @@
 
 /******************************** usb interface ******************************/
 
-static struct video_device *cx231xx_vdev_init(struct cx231xx *dev,
-		const struct video_device
-		*template, const char *type_name)
+static void cx231xx_vdev_init(struct cx231xx *dev,
+		struct video_device *vfd,
+		const struct video_device *template,
+		const char *type_name)
 {
-	struct video_device *vfd;
-
-	vfd = video_device_alloc();
-	if (NULL == vfd)
-		return NULL;
-
 	*vfd = *template;
 	vfd->v4l2_dev = &dev->v4l2_dev;
-	vfd->release = video_device_release;
-	vfd->debug = video_debug;
+	vfd->release = video_device_release_empty;
 	vfd->lock = &dev->lock;
 
 	snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
@@ -2074,7 +2128,6 @@
 		v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER);
 		v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER);
 	}
-	return vfd;
 }
 
 int cx231xx_register_analog_devices(struct cx231xx *dev)
@@ -2117,15 +2170,16 @@
 	/* write code here...  */
 
 	/* allocate and fill video video_device struct */
-	dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video");
-	if (!dev->vdev) {
-		dev_err(dev->dev, "cannot allocate video_device.\n");
-		return -ENODEV;
-	}
-
-	dev->vdev->ctrl_handler = &dev->ctrl_handler;
+	cx231xx_vdev_init(dev, &dev->vdev, &cx231xx_video_template, "video");
+#if defined(CPTCFG_MEDIA_CONTROLLER)
+	dev->video_pad.flags = MEDIA_PAD_FL_SINK;
+	ret = media_entity_init(&dev->vdev.entity, 1, &dev->video_pad, 0);
+	if (ret < 0)
+		dev_err(dev->dev, "failed to initialize video media entity!\n");
+#endif
+	dev->vdev.ctrl_handler = &dev->ctrl_handler;
 	/* register v4l2 video video_device */
-	ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
+	ret = video_register_device(&dev->vdev, VFL_TYPE_GRABBER,
 				    video_nr[dev->devno]);
 	if (ret) {
 		dev_err(dev->dev,
@@ -2135,22 +2189,24 @@
 	}
 
 	dev_info(dev->dev, "Registered video device %s [v4l2]\n",
-		video_device_node_name(dev->vdev));
+		video_device_node_name(&dev->vdev));
 
 	/* Initialize VBI template */
 	cx231xx_vbi_template = cx231xx_video_template;
 	strcpy(cx231xx_vbi_template.name, "cx231xx-vbi");
 
 	/* Allocate and fill vbi video_device struct */
-	dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi");
+	cx231xx_vdev_init(dev, &dev->vbi_dev, &cx231xx_vbi_template, "vbi");
 
-	if (!dev->vbi_dev) {
-		dev_err(dev->dev, "cannot allocate video_device.\n");
-		return -ENODEV;
-	}
-	dev->vbi_dev->ctrl_handler = &dev->ctrl_handler;
+#if defined(CPTCFG_MEDIA_CONTROLLER)
+	dev->vbi_pad.flags = MEDIA_PAD_FL_SINK;
+	ret = media_entity_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad, 0);
+	if (ret < 0)
+		dev_err(dev->dev, "failed to initialize vbi media entity!\n");
+#endif
+	dev->vbi_dev.ctrl_handler = &dev->ctrl_handler;
 	/* register v4l2 vbi video_device */
-	ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+	ret = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI,
 				    vbi_nr[dev->devno]);
 	if (ret < 0) {
 		dev_err(dev->dev, "unable to register vbi device\n");
@@ -2158,18 +2214,13 @@
 	}
 
 	dev_info(dev->dev, "Registered VBI device %s\n",
-		video_device_node_name(dev->vbi_dev));
+		video_device_node_name(&dev->vbi_dev));
 
 	if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
-		dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template,
-						   "radio");
-		if (!dev->radio_dev) {
-			dev_err(dev->dev,
-				"cannot allocate video_device.\n");
-			return -ENODEV;
-		}
-		dev->radio_dev->ctrl_handler = &dev->radio_ctrl_handler;
-		ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+		cx231xx_vdev_init(dev, &dev->radio_dev,
+				&cx231xx_radio_template, "radio");
+		dev->radio_dev.ctrl_handler = &dev->radio_ctrl_handler;
+		ret = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO,
 					    radio_nr[dev->devno]);
 		if (ret < 0) {
 			dev_err(dev->dev,
@@ -2177,7 +2228,7 @@
 			return ret;
 		}
 		dev_info(dev->dev, "Registered radio device as %s\n",
-			video_device_node_name(dev->radio_dev));
+			video_device_node_name(&dev->radio_dev));
 	}
 
 	return 0;
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index 2ebe7c4..d05f31d 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -76,6 +76,7 @@
 #define CX231XX_BOARD_KWORLD_UB445_USB_HYBRID 18
 #define CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx 19
 #define CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx 20
+#define CX231XX_BOARD_HAUPPAUGE_955Q 21
 
 /* Limits minimum and default number of buffers */
 #define CX231XX_MIN_BUF                 4
@@ -532,15 +533,7 @@
 	unsigned int *alt_max_pkt_size;	/* array of wMaxPacketSize */
 	u16 end_point_addr;
 };
-/*
-struct cx23885_dmaqueue {
-	struct list_head       active;
-	struct list_head       queued;
-	struct timer_list      timeout;
-	struct btcx_riscmem    stopper;
-	u32                    count;
-};
-*/
+
 struct cx231xx_tsport {
 	struct cx231xx *dev;
 
@@ -641,7 +634,7 @@
 
 	/* video for linux */
 	int users;		/* user count for exclusive use */
-	struct video_device *vdev;	/* video for linux device struct */
+	struct video_device vdev;	/* video for linux device struct */
 	v4l2_std_id norm;	/* selected tv norm */
 	int ctl_freq;		/* selected frequency */
 	unsigned int ctl_ainput;	/* selected audio input */
@@ -663,8 +656,13 @@
 	struct mutex ctrl_urb_lock;	/* protects urb_buf */
 	struct list_head inqueue, outqueue;
 	wait_queue_head_t open, wait_frame, wait_stream;
-	struct video_device *vbi_dev;
-	struct video_device *radio_dev;
+	struct video_device vbi_dev;
+	struct video_device radio_dev;
+
+#if defined(CPTCFG_MEDIA_CONTROLLER)
+	struct media_device *media_dev;
+	struct media_pad video_pad, vbi_pad;
+#endif
 
 	unsigned char eedata[256];
 
@@ -726,7 +724,7 @@
 	u8 USE_ISO;
 	struct cx231xx_tvnorm      encodernorm;
 	struct cx231xx_tsport      ts1, ts2;
-	struct video_device        *v4l_device;
+	struct video_device        v4l_device;
 	atomic_t                   v4l_reader_count;
 	u32                        freq;
 	unsigned int               input;
@@ -980,8 +978,11 @@
 int cx231xx_ir_init(struct cx231xx *dev);
 void cx231xx_ir_exit(struct cx231xx *dev);
 #else
-#define cx231xx_ir_init(dev)	(0)
-#define cx231xx_ir_exit(dev)	(0)
+static inline int cx231xx_ir_init(struct cx231xx *dev)
+{
+	return 0;
+}
+static inline void cx231xx_ir_exit(struct cx231xx *dev) {}
 #endif
 
 static inline unsigned int norm_maxw(struct cx231xx *dev)
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
index cf2482f..3cb80fe 100644
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -139,6 +139,7 @@
 
 config DVB_USB_RTL28XXU
 	depends on !KERNEL_3_4
+	depends on !KERNEL_3_8
 	tristate "Realtek RTL28xxU DVB USB support"
 	depends on m
 	depends on DVB_USB_V2 && I2C_MUX
@@ -163,7 +164,7 @@
 	depends on DVB_USB_V2
 	select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
-	select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_SP2 if MEDIA_SUBDRV_AUTOSELECT
 	help
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
index 14e111e..023d91f 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
@@ -25,6 +25,7 @@
 #include <linux/usb/input.h>
 #include <linux/firmware.h>
 #include <media/rc-core.h>
+#include <media/media-device.h>
 
 #include "dvb_frontend.h"
 #include "dvb_demux.h"
@@ -354,6 +355,7 @@
  * @name: device name
  * @rc_map: name of rc codes table
  * @rc_polling_active: set when RC polling is active
+ * @intf: pointer to the device's struct usb_interface
  * @udev: pointer to the device's struct usb_device
  * @rc: remote controller configuration
  * @powered: indicated whether the device is power or not
@@ -370,6 +372,7 @@
 	const char *name;
 	const char *rc_map;
 	bool rc_polling_active;
+	struct usb_interface *intf;
 	struct usb_device *udev;
 	struct dvb_usb_rc rc;
 	int powered;
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
index 6d0c29f..a1dae32 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
@@ -400,10 +400,61 @@
 	return ret;
 }
 
+static void dvb_usbv2_media_device_register(struct dvb_usb_adapter *adap)
+{
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+	struct media_device *mdev;
+	struct dvb_usb_device *d = adap_to_d(adap);
+	struct usb_device *udev = d->udev;
+	int ret;
+
+	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+	if (!mdev)
+		return;
+
+	mdev->dev = &udev->dev;
+	strlcpy(mdev->model, d->name, sizeof(mdev->model));
+	if (udev->serial)
+		strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
+	strcpy(mdev->bus_info, udev->devpath);
+	mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+	mdev->driver_version = LINUX_VERSION_CODE;
+
+	ret = media_device_register(mdev);
+	if (ret) {
+		dev_err(&d->udev->dev,
+			"Couldn't create a media device. Error: %d\n",
+			ret);
+		kfree(mdev);
+		return;
+	}
+
+	dvb_register_media_controller(&adap->dvb_adap, mdev);
+
+	dev_info(&d->udev->dev, "media controller created\n");
+
+#endif
+}
+
+static void dvb_usbv2_media_device_unregister(struct dvb_usb_adapter *adap)
+{
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+
+	if (!adap->dvb_adap.mdev)
+		return;
+
+	media_device_unregister(adap->dvb_adap.mdev);
+	kfree(adap->dvb_adap.mdev);
+	adap->dvb_adap.mdev = NULL;
+
+#endif
+}
+
 static int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap)
 {
 	int ret;
 	struct dvb_usb_device *d = adap_to_d(adap);
+
 	dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, adap->id);
 
 	ret = dvb_register_adapter(&adap->dvb_adap, d->name, d->props->owner,
@@ -416,6 +467,8 @@
 
 	adap->dvb_adap.priv = adap;
 
+	dvb_usbv2_media_device_register(adap);
+
 	if (d->props->read_mac_address) {
 		ret = d->props->read_mac_address(adap,
 				adap->dvb_adap.proposed_mac);
@@ -464,6 +517,7 @@
 err_dvb_dmxdev_init:
 	dvb_dmx_release(&adap->demux);
 err_dvb_dmx_init:
+	dvb_usbv2_media_device_unregister(adap);
 	dvb_unregister_adapter(&adap->dvb_adap);
 err_dvb_register_adapter:
 	adap->dvb_adap.priv = NULL;
@@ -480,6 +534,7 @@
 		adap->demux.dmx.close(&adap->demux.dmx);
 		dvb_dmxdev_release(&adap->dmxdev);
 		dvb_dmx_release(&adap->demux);
+		dvb_usbv2_media_device_unregister(adap);
 		dvb_unregister_adapter(&adap->dvb_adap);
 	}
 
@@ -643,6 +698,8 @@
 		}
 	}
 
+	dvb_create_media_graph(&adap->dvb_adap);
+
 	return 0;
 
 err_dvb_unregister_frontend:
@@ -868,6 +925,7 @@
 		goto err;
 	}
 
+	d->intf = intf;
 	d->name = driver_info->name;
 	d->rc_map = driver_info->rc_map;
 	d->udev = udev;
@@ -954,6 +1012,7 @@
 	struct dvb_usb_device *d = usb_get_intfdata(intf);
 	const char *name = d->name;
 	struct device dev = d->udev->dev;
+
 	dev_dbg(&d->udev->dev, "%s: bInterfaceNumber=%d\n", __func__,
 			intf->cur_altsetting->desc.bInterfaceNumber);
 
diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c
index bfc85ed..534875f 100644
--- a/drivers/media/usb/dvb-usb-v2/dvbsky.c
+++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c
@@ -20,7 +20,7 @@
 
 #include "dvb_usb.h"
 #include "m88ds3103.h"
-#include "m88ts2022.h"
+#include "ts2020.h"
 #include "sp2.h"
 #include "si2168.h"
 #include "si2157.h"
@@ -315,9 +315,7 @@
 	struct i2c_adapter *i2c_adapter;
 	struct i2c_client *client;
 	struct i2c_board_info info;
-	struct m88ts2022_config m88ts2022_config = {
-			.clock = 27000000,
-		};
+	struct ts2020_config ts2020_config = {};
 	memset(&info, 0, sizeof(struct i2c_board_info));
 
 	/* attach demod */
@@ -332,11 +330,11 @@
 	}
 
 	/* attach tuner */
-	m88ts2022_config.fe = adap->fe[0];
-	strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+	ts2020_config.fe = adap->fe[0];
+	strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
 	info.addr = 0x60;
-	info.platform_data = &m88ts2022_config;
-	request_module("m88ts2022");
+	info.platform_data = &ts2020_config;
+	request_module("ts2020");
 	client = i2c_new_device(i2c_adapter, &info);
 	if (client == NULL || client->dev.driver == NULL) {
 		dvb_frontend_detach(adap->fe[0]);
@@ -439,9 +437,7 @@
 	struct i2c_client *client_tuner, *client_ci;
 	struct i2c_board_info info;
 	struct sp2_config sp2_config;
-	struct m88ts2022_config m88ts2022_config = {
-			.clock = 27000000,
-		};
+	struct ts2020_config ts2020_config = {};
 	memset(&info, 0, sizeof(struct i2c_board_info));
 
 	/* attach demod */
@@ -456,11 +452,11 @@
 	}
 
 	/* attach tuner */
-	m88ts2022_config.fe = adap->fe[0];
-	strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+	ts2020_config.fe = adap->fe[0];
+	strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
 	info.addr = 0x60;
-	info.platform_data = &m88ts2022_config;
-	request_module("m88ts2022");
+	info.platform_data = &ts2020_config;
+	request_module("ts2020");
 	client_tuner = i2c_new_device(i2c_adapter, &info);
 	if (client_tuner == NULL || client_tuner->dev.driver == NULL) {
 		ret = -ENODEV;
diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c
index 994de53..5de6f7c 100644
--- a/drivers/media/usb/dvb-usb-v2/lmedm04.c
+++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c
@@ -126,9 +126,9 @@
 
 struct lme2510_state {
 	unsigned long int_urb_due;
+	fe_status_t lock_status;
 	u8 id;
 	u8 tuner_config;
-	u8 signal_lock;
 	u8 signal_level;
 	u8 signal_sn;
 	u8 time_key;
@@ -143,6 +143,12 @@
 	void *buffer;
 	struct urb *lme_urb;
 	void *usb_buffer;
+	/* Frontend original calls */
+	int (*fe_read_status)(struct dvb_frontend *, fe_status_t *);
+	int (*fe_read_signal_strength)(struct dvb_frontend *, u16 *);
+	int (*fe_read_snr)(struct dvb_frontend *, u16 *);
+	int (*fe_read_ber)(struct dvb_frontend *, u32 *);
+	int (*fe_read_ucblocks)(struct dvb_frontend *, u32 *);
 	int (*fe_set_voltage)(struct dvb_frontend *, fe_sec_voltage_t);
 	u8 dvb_usb_lme2510_firmware;
 };
@@ -258,6 +264,7 @@
 	static u8 *ibuf, *rbuf;
 	int i = 0, offset;
 	u32 key;
+	u8 signal_lock = 0;
 
 	switch (lme_urb->status) {
 	case 0:
@@ -298,8 +305,7 @@
 		case 0xbb:
 			switch (st->tuner_config) {
 			case TUNER_LG:
-				if (ibuf[2] > 0)
-					st->signal_lock = ibuf[2];
+				signal_lock = ibuf[2] & BIT(5);
 				st->signal_level = ibuf[4];
 				st->signal_sn = ibuf[3];
 				st->time_key = ibuf[7];
@@ -308,29 +314,29 @@
 			case TUNER_S0194:
 				/* Tweak for earlier firmware*/
 				if (ibuf[1] == 0x03) {
-					if (ibuf[2] > 1)
-						st->signal_lock = ibuf[2];
+					signal_lock = ibuf[2] & BIT(4);
 					st->signal_level = ibuf[3];
 					st->signal_sn = ibuf[4];
 				} else {
 					st->signal_level = ibuf[4];
 					st->signal_sn = ibuf[5];
-					st->signal_lock =
-						(st->signal_lock & 0xf7) +
-						((ibuf[2] & 0x01) << 0x03);
 				}
 				break;
 			case TUNER_RS2000:
-				if (ibuf[2] & 0x1)
-					st->signal_lock = 0xff;
-				else
-					st->signal_lock = 0x00;
+				signal_lock = ibuf[2] & 0xee;
 				st->signal_level = ibuf[5];
 				st->signal_sn = ibuf[4];
 				st->time_key = ibuf[7];
 			default:
 				break;
 			}
+
+			/* Interrupt will also throw just BIT 0 as lock */
+			signal_lock |= ibuf[2] & BIT(0);
+
+			if (!signal_lock)
+				st->lock_status &= ~FE_HAS_LOCK;
+
 			debug_data_snipet(5, "INT Remote data snipet in", ibuf);
 		break;
 		case 0xcc:
@@ -344,15 +350,17 @@
 
 	usb_submit_urb(lme_urb, GFP_ATOMIC);
 
-	/* interrupt urb is due every 48 msecs while streaming
-	 *	add 12msecs for system lag */
-	st->int_urb_due = jiffies + msecs_to_jiffies(60);
+	/* Interrupt urb is due every 48 msecs while streaming the buffer
+	 * stores up to 4 periods if missed. Allow 200 msec for next interrupt.
+	 */
+	st->int_urb_due = jiffies + msecs_to_jiffies(200);
 }
 
 static int lme2510_int_read(struct dvb_usb_adapter *adap)
 {
 	struct dvb_usb_device *d = adap_to_d(adap);
 	struct lme2510_state *lme_int = adap_to_priv(adap);
+	struct usb_host_endpoint *ep;
 
 	lme_int->lme_urb = usb_alloc_urb(0, GFP_ATOMIC);
 
@@ -374,6 +382,12 @@
 				adap,
 				8);
 
+	/* Quirk of pipe reporting PIPE_BULK but behaves as interrupt */
+	ep = usb_pipe_endpoint(d->udev, lme_int->lme_urb->pipe);
+
+	if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK)
+		lme_int->lme_urb->pipe = usb_rcvbulkpipe(d->udev, 0xa),
+
 	lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
 	usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC);
@@ -449,170 +463,13 @@
 static int lme2510_msg(struct dvb_usb_device *d,
 		u8 *wbuf, int wlen, u8 *rbuf, int rlen)
 {
-	int ret = 0;
 	struct lme2510_state *st = d->priv;
 
-	if (st->i2c_talk_onoff == 1) {
+	st->i2c_talk_onoff = 1;
 
-		ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
-
-		switch (st->tuner_config) {
-		case TUNER_LG:
-			if (wbuf[2] == 0x1c) {
-				if (wbuf[3] == 0x0e) {
-					st->signal_lock = rbuf[1];
-					if ((st->stream_on & 1) &&
-						(st->signal_lock & 0x10)) {
-						lme2510_stream_restart(d);
-						st->i2c_talk_onoff = 0;
-					}
-					msleep(80);
-				}
-			}
-			break;
-		case TUNER_S7395:
-			if (wbuf[2] == 0xd0) {
-				if (wbuf[3] == 0x24) {
-					st->signal_lock = rbuf[1];
-					if ((st->stream_on & 1) &&
-						(st->signal_lock & 0x8)) {
-						lme2510_stream_restart(d);
-						st->i2c_talk_onoff = 0;
-					}
-				}
-			}
-			break;
-		case TUNER_S0194:
-			if (wbuf[2] == 0xd0) {
-				if (wbuf[3] == 0x1b) {
-					st->signal_lock = rbuf[1];
-					if ((st->stream_on & 1) &&
-						(st->signal_lock & 0x8)) {
-						lme2510_stream_restart(d);
-						st->i2c_talk_onoff = 0;
-					}
-				}
-			}
-			break;
-		case TUNER_RS2000:
-		default:
-			break;
-		}
-	} else {
-		/* TODO rewrite this section */
-		switch (st->tuner_config) {
-		case TUNER_LG:
-			switch (wbuf[3]) {
-			case 0x0e:
-				rbuf[0] = 0x55;
-				rbuf[1] = st->signal_lock;
-				break;
-			case 0x43:
-				rbuf[0] = 0x55;
-				rbuf[1] = st->signal_level;
-				break;
-			case 0x1c:
-				rbuf[0] = 0x55;
-				rbuf[1] = st->signal_sn;
-				break;
-			case 0x15:
-			case 0x16:
-			case 0x17:
-			case 0x18:
-				rbuf[0] = 0x55;
-				rbuf[1] = 0x00;
-				break;
-			default:
-				lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
-				st->i2c_talk_onoff = 1;
-				break;
-			}
-			break;
-		case TUNER_S7395:
-			switch (wbuf[3]) {
-			case 0x10:
-				rbuf[0] = 0x55;
-				rbuf[1] = (st->signal_level & 0x80)
-						? 0 : (st->signal_level * 2);
-				break;
-			case 0x2d:
-				rbuf[0] = 0x55;
-				rbuf[1] = st->signal_sn;
-				break;
-			case 0x24:
-				rbuf[0] = 0x55;
-				rbuf[1] = st->signal_lock;
-				break;
-			case 0x2e:
-			case 0x26:
-			case 0x27:
-				rbuf[0] = 0x55;
-				rbuf[1] = 0x00;
-				break;
-			default:
-				lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
-				st->i2c_talk_onoff = 1;
-				break;
-			}
-			break;
-		case TUNER_S0194:
-			switch (wbuf[3]) {
-			case 0x18:
-				rbuf[0] = 0x55;
-				rbuf[1] = (st->signal_level & 0x80)
-						? 0 : (st->signal_level * 2);
-				break;
-			case 0x24:
-				rbuf[0] = 0x55;
-				rbuf[1] = st->signal_sn;
-				break;
-			case 0x1b:
-				rbuf[0] = 0x55;
-				rbuf[1] = st->signal_lock;
-				break;
-			case 0x19:
-			case 0x25:
-			case 0x1e:
-			case 0x1d:
-				rbuf[0] = 0x55;
-				rbuf[1] = 0x00;
-				break;
-			default:
-				lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
-				st->i2c_talk_onoff = 1;
-				break;
-			}
-			break;
-		case TUNER_RS2000:
-			switch (wbuf[3]) {
-			case 0x8c:
-				rbuf[0] = 0x55;
-				rbuf[1] = st->signal_lock;
-
-				/* If int_urb_due overdue
-				 *  set rbuf[1] to 0 to clear lock */
-				if (time_after(jiffies,	st->int_urb_due))
-					rbuf[1] = 0;
-
-				break;
-			default:
-				lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
-				st->i2c_talk_onoff = 1;
-				break;
-			}
-		default:
-			break;
-		}
-
-		deb_info(4, "I2C From Interrupt Message out(%02x) in(%02x)",
-				wbuf[3], rbuf[1]);
-
-	}
-
-	return ret;
+	return lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
 }
 
-
 static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
 				 int num)
 {
@@ -935,26 +792,8 @@
 	.set_symbol_rate = sharp_z0194a_set_symbol_rate,
 };
 
-static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe,
-	int caller)
-{
-	struct dvb_usb_adapter *adap = fe_to_adap(fe);
-	struct dvb_usb_device *d = adap_to_d(adap);
-	struct lme2510_state *st = d->priv;
-
-	mutex_lock(&d->i2c_mutex);
-	if ((st->i2c_talk_onoff == 1) && (st->stream_on & 1)) {
-		st->i2c_talk_onoff = 0;
-		lme2510_stream_restart(d);
-	}
-	mutex_unlock(&d->i2c_mutex);
-
-	return 0;
-}
-
 static struct m88rs2000_config m88rs2000_config = {
-	.demod_addr = 0x68,
-	.set_ts_params = dm04_rs2000_set_ts_param,
+	.demod_addr = 0x68
 };
 
 static struct ts2020_config ts2020_config = {
@@ -998,27 +837,101 @@
 	return (ret < 0) ? -ENODEV : 0;
 }
 
-static int dm04_rs2000_read_signal_strength(struct dvb_frontend *fe,
-	u16 *strength)
+static int dm04_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct dvb_usb_device *d = fe_to_d(fe);
+	struct lme2510_state *st = d->priv;
+	int ret = 0;
+
+	if (st->i2c_talk_onoff) {
+		if (st->fe_read_status) {
+			ret = st->fe_read_status(fe, status);
+			if (ret < 0)
+				return ret;
+		}
+
+		st->lock_status = *status;
+
+		if (*status & FE_HAS_LOCK && st->stream_on) {
+			mutex_lock(&d->i2c_mutex);
+
+			st->i2c_talk_onoff = 0;
+			ret = lme2510_stream_restart(d);
+
+			mutex_unlock(&d->i2c_mutex);
+		}
+
+		return ret;
+	}
+
+	/* Timeout of interrupt reached on RS2000 */
+	if (st->tuner_config == TUNER_RS2000 &&
+	    time_after(jiffies, st->int_urb_due))
+		st->lock_status &= ~FE_HAS_LOCK;
+
+	*status = st->lock_status;
+
+	if (!(*status & FE_HAS_LOCK))
+		st->i2c_talk_onoff = 1;
+
+	return ret;
+}
+
+static int dm04_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
 {
 	struct lme2510_state *st = fe_to_priv(fe);
 
-	*strength = (u16)((u32)st->signal_level * 0xffff / 0xff);
+	if (st->fe_read_signal_strength && !st->stream_on)
+		return st->fe_read_signal_strength(fe, strength);
+
+	switch (st->tuner_config) {
+	case TUNER_LG:
+		*strength = 0xff - st->signal_level;
+		*strength |= *strength << 8;
+		break;
+	/* fall through */
+	case TUNER_S7395:
+	case TUNER_S0194:
+		*strength = 0xffff - (((st->signal_level * 2) << 8) * 5 / 4);
+		break;
+	case TUNER_RS2000:
+		*strength = (u16)((u32)st->signal_level * 0xffff / 0xff);
+	}
 
 	return 0;
 }
 
-static int dm04_rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
+static int dm04_read_snr(struct dvb_frontend *fe, u16 *snr)
 {
 	struct lme2510_state *st = fe_to_priv(fe);
 
-	*snr = (u16)((u32)st->signal_sn * 0xffff / 0x7f);
+	if (st->fe_read_snr && !st->stream_on)
+		return st->fe_read_snr(fe, snr);
+
+	switch (st->tuner_config) {
+	case TUNER_LG:
+		*snr = 0xff - st->signal_sn;
+		*snr |= *snr << 8;
+		break;
+	/* fall through */
+	case TUNER_S7395:
+	case TUNER_S0194:
+		*snr = (u16)((0xff - st->signal_sn - 0xa1) * 3) << 8;
+		break;
+	case TUNER_RS2000:
+		*snr = (u16)((u32)st->signal_sn * 0xffff / 0x7f);
+	}
 
 	return 0;
 }
 
 static int dm04_read_ber(struct dvb_frontend *fe, u32 *ber)
 {
+	struct lme2510_state *st = fe_to_priv(fe);
+
+	if (st->fe_read_ber && !st->stream_on)
+		return st->fe_read_ber(fe, ber);
+
 	*ber = 0;
 
 	return 0;
@@ -1026,6 +939,11 @@
 
 static int dm04_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
 {
+	struct lme2510_state *st = fe_to_priv(fe);
+
+	if (st->fe_read_ucblocks && !st->stream_on)
+		return st->fe_read_ucblocks(fe, ucblocks);
+
 	*ucblocks = 0;
 
 	return 0;
@@ -1119,15 +1037,6 @@
 			st->tuner_config = TUNER_RS2000;
 			st->fe_set_voltage =
 				adap->fe[0]->ops.set_voltage;
-
-			adap->fe[0]->ops.read_signal_strength =
-				dm04_rs2000_read_signal_strength;
-			adap->fe[0]->ops.read_snr =
-				dm04_rs2000_read_snr;
-			adap->fe[0]->ops.read_ber =
-				dm04_read_ber;
-			adap->fe[0]->ops.read_ucblocks =
-				dm04_read_ucblocks;
 		}
 		break;
 	}
@@ -1146,7 +1055,19 @@
 		return -ENODEV;
 	}
 
+	st->fe_read_status = adap->fe[0]->ops.read_status;
+	st->fe_read_signal_strength = adap->fe[0]->ops.read_signal_strength;
+	st->fe_read_snr = adap->fe[0]->ops.read_snr;
+	st->fe_read_ber = adap->fe[0]->ops.read_ber;
+	st->fe_read_ucblocks = adap->fe[0]->ops.read_ucblocks;
+
+	adap->fe[0]->ops.read_status = dm04_read_status;
+	adap->fe[0]->ops.read_signal_strength = dm04_read_signal_strength;
+	adap->fe[0]->ops.read_snr = dm04_read_snr;
+	adap->fe[0]->ops.read_ber = dm04_read_ber;
+	adap->fe[0]->ops.read_ucblocks = dm04_read_ucblocks;
 	adap->fe[0]->ops.set_voltage = dm04_lme2510_set_voltage;
+
 	ret = lme_name(adap);
 	return ret;
 }
@@ -1288,7 +1209,6 @@
 
 	if (st->usb_buffer != NULL) {
 		st->i2c_talk_onoff = 1;
-		st->signal_lock = 0;
 		st->signal_level = 0;
 		st->signal_sn = 0;
 		buffer = st->usb_buffer;
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c
index 0a98d04..ecefa5c 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c
@@ -604,9 +604,3 @@
 MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("0.1");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h
index e546bc8..db4ec62 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h
@@ -47,9 +47,3 @@
 #endif /* CPTCFG_DVB_USB_MXL111SF */
 
 #endif /* __MXL111SF_DEMOD_H__ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c
index a619410..2180c13 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c
@@ -755,9 +755,3 @@
 	}
 	return 0;
 }
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h
index b85a577..16fa4d4 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h
@@ -48,9 +48,3 @@
 				  enum mxl111sf_mux_config pin_mux_config);
 
 #endif /* _DVB_USB_MXL111SF_GPIO_H_ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c
index a101d06..283495c 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c
@@ -842,9 +842,3 @@
 
 	return i == num ? num : -EREMOTEIO;
 }
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h
index 4657621..c486fe0 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h
@@ -27,9 +27,3 @@
 		      struct i2c_msg msg[], int num);
 
 #endif /* _DVB_USB_MXL111SF_I2C_H_ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c
index f6b3480..5b01911 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c
@@ -335,9 +335,3 @@
 
 	return ret;
 }
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h
index 0643738..25aa4a1 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h
@@ -45,9 +45,3 @@
 			 u8 current_value, u8 hysteresis_value);
 
 #endif /* _DVB_USB_MXL111SF_PHY_H_ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h
index 89bf115..1f4bfbc 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h
@@ -171,9 +171,3 @@
 #define V6_DIG_RF_PWR_MSB_REG  0x47
 
 #endif /* _DVB_USB_MXL111SF_REG_H_ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c
index a8d2c70..444579b 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c
@@ -515,11 +515,3 @@
 MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("0.1");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h
index f00bb05..f33f720 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h
@@ -77,12 +77,3 @@
 #endif
 
 #endif /* __MXL111SF_TUNER_H__ */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
-
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
index c3447ea..bec12b0 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
@@ -1425,9 +1425,3 @@
 MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF");
 MODULE_VERSION("1.0");
 MODULE_LICENSE("GPL");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h
index 8516c01..ee70df1 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h
@@ -152,9 +152,3 @@
 })
 
 #endif /* _DVB_USB_MXL111SF_H_ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 026a68e..395bfc2 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -22,42 +22,6 @@
 
 #include "rtl28xxu.h"
 
-#include "rtl2830.h"
-#include "rtl2832.h"
-#include "rtl2832_sdr.h"
-#include "mn88472.h"
-#include "mn88473.h"
-
-#include "qt1010.h"
-#include "mt2060.h"
-#include "mxl5005s.h"
-#include "fc0012.h"
-#include "fc0013.h"
-#include "e4000.h"
-#include "fc2580.h"
-#include "tua9001.h"
-#include "r820t.h"
-
-
-#ifdef CPTCFG_MEDIA_ATTACH
-#define dvb_attach_sdr(FUNCTION, ARGS...) ({ \
-	void *__r = NULL; \
-	typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
-	if (__a) { \
-		__r = (void *) __a(ARGS); \
-		if (__r == NULL) \
-			symbol_put(FUNCTION); \
-	} \
-	__r; \
-})
-
-#else
-#define dvb_attach_sdr(FUNCTION, ARGS...) ({ \
-	FUNCTION(ARGS); \
-})
-
-#endif
-
 static int rtl28xxu_disable_rc;
 module_param_named(disable_rc, rtl28xxu_disable_rc, int, 0644);
 MODULE_PARM_DESC(disable_rc, "disable RTL2832U remote controller");
@@ -65,20 +29,14 @@
 
 static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req)
 {
+	struct rtl28xxu_dev *dev = d->priv;
 	int ret;
 	unsigned int pipe;
 	u8 requesttype;
-	u8 *buf;
-
-	buf = kmalloc(req->size, GFP_KERNEL);
-	if (!buf) {
-		ret = -ENOMEM;
-		goto err;
-	}
 
 	if (req->index & CMD_WR_FLAG) {
 		/* write */
-		memcpy(buf, req->data, req->size);
+		memcpy(dev->buf, req->data, req->size);
 		requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
 		pipe = usb_sndctrlpipe(d->udev, 0);
 	} else {
@@ -88,30 +46,23 @@
 	}
 
 	ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value,
-			req->index, buf, req->size, 1000);
-
+			req->index, dev->buf, req->size, 1000);
 	dvb_usb_dbg_usb_control_msg(d->udev, 0, requesttype, req->value,
-			req->index, buf, req->size);
-
-	if (ret > 0)
-		ret = 0;
-
-	/* read request, copy returned data to return buf */
-	if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
-		memcpy(req->data, buf, req->size);
-
-	kfree(buf);
-
-	if (ret)
+			req->index, dev->buf, req->size);
+	if (ret < 0)
 		goto err;
 
-	return ret;
+	/* read request, copy returned data to return buf */
+	if (requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
+		memcpy(req->data, dev->buf, req->size);
+
+	return 0;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
-static int rtl28xx_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+static int rtl28xxu_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
 {
 	struct rtl28xxu_req req;
 
@@ -129,7 +80,7 @@
 	return rtl28xxu_ctrl_msg(d, &req);
 }
 
-static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+static int rtl28xxu_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
 {
 	struct rtl28xxu_req req;
 
@@ -147,17 +98,17 @@
 	return rtl28xxu_ctrl_msg(d, &req);
 }
 
-static int rtl28xx_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
+static int rtl28xxu_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
 {
-	return rtl28xx_wr_regs(d, reg, &val, 1);
+	return rtl28xxu_wr_regs(d, reg, &val, 1);
 }
 
-static int rtl28xx_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
+static int rtl28xxu_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
 {
-	return rtl2831_rd_regs(d, reg, val, 1);
+	return rtl28xxu_rd_regs(d, reg, val, 1);
 }
 
-static int rtl28xx_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val,
+static int rtl28xxu_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val,
 		u8 mask)
 {
 	int ret;
@@ -165,7 +116,7 @@
 
 	/* no need for read if whole reg is written */
 	if (mask != 0xff) {
-		ret = rtl28xx_rd_reg(d, reg, &tmp);
+		ret = rtl28xxu_rd_reg(d, reg, &tmp);
 		if (ret)
 			return ret;
 
@@ -174,7 +125,7 @@
 		val |= tmp;
 	}
 
-	return rtl28xx_wr_reg(d, reg, val);
+	return rtl28xxu_wr_reg(d, reg, val);
 }
 
 /* I2C */
@@ -183,7 +134,7 @@
 {
 	int ret;
 	struct dvb_usb_device *d = i2c_get_adapdata(adap);
-	struct rtl28xxu_priv *priv = d->priv;
+	struct rtl28xxu_dev *dev = d->priv;
 	struct rtl28xxu_req req;
 
 	/*
@@ -220,7 +171,7 @@
 		} else if (msg[0].addr == 0x10) {
 			/* method 1 - integrated demod */
 			req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
-			req.index = CMD_DEMOD_RD | priv->page;
+			req.index = CMD_DEMOD_RD | dev->page;
 			req.size = msg[1].len;
 			req.data = &msg[1].buf[0];
 			ret = rtl28xxu_ctrl_msg(d, &req);
@@ -256,12 +207,12 @@
 			/* method 1 - integrated demod */
 			if (msg[0].buf[0] == 0x00) {
 				/* save demod page for later demod access */
-				priv->page = msg[0].buf[1];
+				dev->page = msg[0].buf[1];
 				ret = 0;
 			} else {
 				req.value = (msg[0].buf[0] << 8) |
 					(msg[0].addr << 1);
-				req.index = CMD_DEMOD_WR | priv->page;
+				req.index = CMD_DEMOD_WR | dev->page;
 				req.size = msg[0].len-1;
 				req.data = &msg[0].buf[1];
 				ret = rtl28xxu_ctrl_msg(d, &req);
@@ -303,7 +254,7 @@
 
 static int rtl2831u_read_config(struct dvb_usb_device *d)
 {
-	struct rtl28xxu_priv *priv = d_to_priv(d);
+	struct rtl28xxu_dev *dev = d_to_priv(d);
 	int ret;
 	u8 buf[1];
 	/* open RTL2831U/RTL2830 I2C gate */
@@ -312,7 +263,7 @@
 	struct rtl28xxu_req req_mt2060 = {0x00c0, CMD_I2C_RD, 1, buf};
 	struct rtl28xxu_req req_qt1010 = {0x0fc4, CMD_I2C_RD, 1, buf};
 
-	dev_dbg(&d->udev->dev, "%s:\n", __func__);
+	dev_dbg(&d->intf->dev, "\n");
 
 	/*
 	 * RTL2831U GPIOs
@@ -323,12 +274,12 @@
 	 */
 
 	/* GPIO direction */
-	ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, 0x0a);
+	ret = rtl28xxu_wr_reg(d, SYS_GPIO_DIR, 0x0a);
 	if (ret)
 		goto err;
 
 	/* enable as output GPIO0, GPIO2, GPIO4 */
-	ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, 0x15);
+	ret = rtl28xxu_wr_reg(d, SYS_GPIO_OUT_EN, 0x15);
 	if (ret)
 		goto err;
 
@@ -340,7 +291,7 @@
 	/* demod needs some time to wake up */
 	msleep(20);
 
-	priv->tuner_name = "NONE";
+	dev->tuner_name = "NONE";
 
 	/* open demod I2C gate */
 	ret = rtl28xxu_ctrl_msg(d, &req_gate_open);
@@ -350,8 +301,8 @@
 	/* check QT1010 ID(?) register; reg=0f val=2c */
 	ret = rtl28xxu_ctrl_msg(d, &req_qt1010);
 	if (ret == 0 && buf[0] == 0x2c) {
-		priv->tuner = TUNER_RTL2830_QT1010;
-		priv->tuner_name = "QT1010";
+		dev->tuner = TUNER_RTL2830_QT1010;
+		dev->tuner_name = "QT1010";
 		goto found;
 	}
 
@@ -363,28 +314,28 @@
 	/* check MT2060 ID register; reg=00 val=63 */
 	ret = rtl28xxu_ctrl_msg(d, &req_mt2060);
 	if (ret == 0 && buf[0] == 0x63) {
-		priv->tuner = TUNER_RTL2830_MT2060;
-		priv->tuner_name = "MT2060";
+		dev->tuner = TUNER_RTL2830_MT2060;
+		dev->tuner_name = "MT2060";
 		goto found;
 	}
 
 	/* assume MXL5005S */
-	priv->tuner = TUNER_RTL2830_MXL5005S;
-	priv->tuner_name = "MXL5005S";
+	dev->tuner = TUNER_RTL2830_MXL5005S;
+	dev->tuner_name = "MXL5005S";
 	goto found;
 
 found:
-	dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name);
+	dev_dbg(&d->intf->dev, "tuner=%s\n", dev->tuner_name);
 
 	return 0;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
 static int rtl2832u_read_config(struct dvb_usb_device *d)
 {
-	struct rtl28xxu_priv *priv = d_to_priv(d);
+	struct rtl28xxu_dev *dev = d_to_priv(d);
 	int ret;
 	u8 buf[2];
 	/* open RTL2832U/RTL2832 I2C gate */
@@ -407,14 +358,14 @@
 	struct rtl28xxu_req req_mn88472 = {0xff38, CMD_I2C_RD, 1, buf};
 	struct rtl28xxu_req req_mn88473 = {0xff38, CMD_I2C_RD, 1, buf};
 
-	dev_dbg(&d->udev->dev, "%s:\n", __func__);
+	dev_dbg(&d->intf->dev, "\n");
 
 	/* enable GPIO3 and GPIO6 as output */
-	ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x40);
+	ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x40);
 	if (ret)
 		goto err;
 
-	ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x48, 0x48);
+	ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x48, 0x48);
 	if (ret)
 		goto err;
 
@@ -428,134 +379,134 @@
 	if (ret)
 		goto err;
 
-	priv->tuner_name = "NONE";
+	dev->tuner_name = "NONE";
 
 	/* check FC0012 ID register; reg=00 val=a1 */
 	ret = rtl28xxu_ctrl_msg(d, &req_fc0012);
 	if (ret == 0 && buf[0] == 0xa1) {
-		priv->tuner = TUNER_RTL2832_FC0012;
-		priv->tuner_name = "FC0012";
+		dev->tuner = TUNER_RTL2832_FC0012;
+		dev->tuner_name = "FC0012";
 		goto tuner_found;
 	}
 
 	/* check FC0013 ID register; reg=00 val=a3 */
 	ret = rtl28xxu_ctrl_msg(d, &req_fc0013);
 	if (ret == 0 && buf[0] == 0xa3) {
-		priv->tuner = TUNER_RTL2832_FC0013;
-		priv->tuner_name = "FC0013";
+		dev->tuner = TUNER_RTL2832_FC0013;
+		dev->tuner_name = "FC0013";
 		goto tuner_found;
 	}
 
 	/* check MT2266 ID register; reg=00 val=85 */
 	ret = rtl28xxu_ctrl_msg(d, &req_mt2266);
 	if (ret == 0 && buf[0] == 0x85) {
-		priv->tuner = TUNER_RTL2832_MT2266;
-		priv->tuner_name = "MT2266";
+		dev->tuner = TUNER_RTL2832_MT2266;
+		dev->tuner_name = "MT2266";
 		goto tuner_found;
 	}
 
 	/* check FC2580 ID register; reg=01 val=56 */
 	ret = rtl28xxu_ctrl_msg(d, &req_fc2580);
 	if (ret == 0 && buf[0] == 0x56) {
-		priv->tuner = TUNER_RTL2832_FC2580;
-		priv->tuner_name = "FC2580";
+		dev->tuner = TUNER_RTL2832_FC2580;
+		dev->tuner_name = "FC2580";
 		goto tuner_found;
 	}
 
 	/* check MT2063 ID register; reg=00 val=9e || 9c */
 	ret = rtl28xxu_ctrl_msg(d, &req_mt2063);
 	if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) {
-		priv->tuner = TUNER_RTL2832_MT2063;
-		priv->tuner_name = "MT2063";
+		dev->tuner = TUNER_RTL2832_MT2063;
+		dev->tuner_name = "MT2063";
 		goto tuner_found;
 	}
 
 	/* check MAX3543 ID register; reg=00 val=38 */
 	ret = rtl28xxu_ctrl_msg(d, &req_max3543);
 	if (ret == 0 && buf[0] == 0x38) {
-		priv->tuner = TUNER_RTL2832_MAX3543;
-		priv->tuner_name = "MAX3543";
+		dev->tuner = TUNER_RTL2832_MAX3543;
+		dev->tuner_name = "MAX3543";
 		goto tuner_found;
 	}
 
 	/* check TUA9001 ID register; reg=7e val=2328 */
 	ret = rtl28xxu_ctrl_msg(d, &req_tua9001);
 	if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) {
-		priv->tuner = TUNER_RTL2832_TUA9001;
-		priv->tuner_name = "TUA9001";
+		dev->tuner = TUNER_RTL2832_TUA9001;
+		dev->tuner_name = "TUA9001";
 		goto tuner_found;
 	}
 
 	/* check MXL5007R ID register; reg=d9 val=14 */
 	ret = rtl28xxu_ctrl_msg(d, &req_mxl5007t);
 	if (ret == 0 && buf[0] == 0x14) {
-		priv->tuner = TUNER_RTL2832_MXL5007T;
-		priv->tuner_name = "MXL5007T";
+		dev->tuner = TUNER_RTL2832_MXL5007T;
+		dev->tuner_name = "MXL5007T";
 		goto tuner_found;
 	}
 
 	/* check E4000 ID register; reg=02 val=40 */
 	ret = rtl28xxu_ctrl_msg(d, &req_e4000);
 	if (ret == 0 && buf[0] == 0x40) {
-		priv->tuner = TUNER_RTL2832_E4000;
-		priv->tuner_name = "E4000";
+		dev->tuner = TUNER_RTL2832_E4000;
+		dev->tuner_name = "E4000";
 		goto tuner_found;
 	}
 
 	/* check TDA18272 ID register; reg=00 val=c760  */
 	ret = rtl28xxu_ctrl_msg(d, &req_tda18272);
 	if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) {
-		priv->tuner = TUNER_RTL2832_TDA18272;
-		priv->tuner_name = "TDA18272";
+		dev->tuner = TUNER_RTL2832_TDA18272;
+		dev->tuner_name = "TDA18272";
 		goto tuner_found;
 	}
 
 	/* check R820T ID register; reg=00 val=69 */
 	ret = rtl28xxu_ctrl_msg(d, &req_r820t);
 	if (ret == 0 && buf[0] == 0x69) {
-		priv->tuner = TUNER_RTL2832_R820T;
-		priv->tuner_name = "R820T";
+		dev->tuner = TUNER_RTL2832_R820T;
+		dev->tuner_name = "R820T";
 		goto tuner_found;
 	}
 
 	/* check R828D ID register; reg=00 val=69 */
 	ret = rtl28xxu_ctrl_msg(d, &req_r828d);
 	if (ret == 0 && buf[0] == 0x69) {
-		priv->tuner = TUNER_RTL2832_R828D;
-		priv->tuner_name = "R828D";
+		dev->tuner = TUNER_RTL2832_R828D;
+		dev->tuner_name = "R828D";
 		goto tuner_found;
 	}
 
 tuner_found:
-	dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name);
+	dev_dbg(&d->intf->dev, "tuner=%s\n", dev->tuner_name);
 
 	/* probe slave demod */
-	if (priv->tuner == TUNER_RTL2832_R828D) {
+	if (dev->tuner == TUNER_RTL2832_R828D) {
 		/* power on MN88472 demod on GPIO0 */
-		ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x01, 0x01);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x01, 0x01);
 		if (ret)
 			goto err;
 
-		ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x01);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x01);
 		if (ret)
 			goto err;
 
-		ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x01, 0x01);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x01, 0x01);
 		if (ret)
 			goto err;
 
 		/* check MN88472 answers */
 		ret = rtl28xxu_ctrl_msg(d, &req_mn88472);
 		if (ret == 0 && buf[0] == 0x02) {
-			dev_dbg(&d->udev->dev, "%s: MN88472 found\n", __func__);
-			priv->slave_demod = SLAVE_DEMOD_MN88472;
+			dev_dbg(&d->intf->dev, "MN88472 found\n");
+			dev->slave_demod = SLAVE_DEMOD_MN88472;
 			goto demod_found;
 		}
 
 		ret = rtl28xxu_ctrl_msg(d, &req_mn88473);
 		if (ret == 0 && buf[0] == 0x03) {
-			dev_dbg(&d->udev->dev, "%s: MN88473 found\n", __func__);
-			priv->slave_demod = SLAVE_DEMOD_MN88473;
+			dev_dbg(&d->intf->dev, "MN88473 found\n");
+			dev->slave_demod = SLAVE_DEMOD_MN88473;
 			goto demod_found;
 		}
 	}
@@ -568,14 +519,51 @@
 
 	return 0;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
-static const struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
-	.i2c_addr = 0x10, /* 0x20 */
-	.xtal = 28800000,
-	.ts_mode = 0,
+static int rtl28xxu_read_config(struct dvb_usb_device *d)
+{
+	struct rtl28xxu_dev *dev = d_to_priv(d);
+
+	if (dev->chip_id == CHIP_ID_RTL2831U)
+		return rtl2831u_read_config(d);
+	else
+		return rtl2832u_read_config(d);
+}
+
+static int rtl28xxu_identify_state(struct dvb_usb_device *d, const char **name)
+{
+	struct rtl28xxu_dev *dev = d_to_priv(d);
+	int ret;
+	struct rtl28xxu_req req_demod_i2c = {0x0020, CMD_I2C_DA_RD, 0, NULL};
+
+	dev_dbg(&d->intf->dev, "\n");
+
+	/*
+	 * Detect chip type using I2C command that is not supported
+	 * by old RTL2831U.
+	 */
+	ret = rtl28xxu_ctrl_msg(d, &req_demod_i2c);
+	if (ret == -EPIPE) {
+		dev->chip_id = CHIP_ID_RTL2831U;
+	} else if (ret == 0) {
+		dev->chip_id = CHIP_ID_RTL2832U;
+	} else {
+		dev_err(&d->intf->dev, "chip type detection failed %d\n", ret);
+		goto err;
+	}
+	dev_dbg(&d->intf->dev, "chip_id=%u\n", dev->chip_id);
+
+	return WARM;
+err:
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static const struct rtl2830_platform_data rtl2830_mt2060_platform_data = {
+	.clk = 28800000,
 	.spec_inv = 1,
 	.vtop = 0x20,
 	.krf = 0x04,
@@ -583,20 +571,16 @@
 
 };
 
-static const struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
-	.i2c_addr = 0x10, /* 0x20 */
-	.xtal = 28800000,
-	.ts_mode = 0,
+static const struct rtl2830_platform_data rtl2830_qt1010_platform_data = {
+	.clk = 28800000,
 	.spec_inv = 1,
 	.vtop = 0x20,
 	.krf = 0x04,
 	.agc_targ_val = 0x2d,
 };
 
-static const struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
-	.i2c_addr = 0x10, /* 0x20 */
-	.xtal = 28800000,
-	.ts_mode = 0,
+static const struct rtl2830_platform_data rtl2830_mxl5005s_platform_data = {
+	.clk = 28800000,
 	.spec_inv = 0,
 	.vtop = 0x3f,
 	.krf = 0x04,
@@ -606,69 +590,81 @@
 static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
 {
 	struct dvb_usb_device *d = adap_to_d(adap);
-	struct rtl28xxu_priv *priv = d_to_priv(d);
-	const struct rtl2830_config *rtl2830_config;
+	struct rtl28xxu_dev *dev = d_to_priv(d);
+	struct rtl2830_platform_data *pdata = &dev->rtl2830_platform_data;
+	struct i2c_board_info board_info;
+	struct i2c_client *client;
 	int ret;
 
-	dev_dbg(&d->udev->dev, "%s:\n", __func__);
+	dev_dbg(&d->intf->dev, "\n");
 
-	switch (priv->tuner) {
+	switch (dev->tuner) {
 	case TUNER_RTL2830_QT1010:
-		rtl2830_config = &rtl28xxu_rtl2830_qt1010_config;
+		*pdata = rtl2830_qt1010_platform_data;
 		break;
 	case TUNER_RTL2830_MT2060:
-		rtl2830_config = &rtl28xxu_rtl2830_mt2060_config;
+		*pdata = rtl2830_mt2060_platform_data;
 		break;
 	case TUNER_RTL2830_MXL5005S:
-		rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config;
+		*pdata = rtl2830_mxl5005s_platform_data;
 		break;
 	default:
-		dev_err(&d->udev->dev, "%s: unknown tuner=%s\n",
-				KBUILD_MODNAME, priv->tuner_name);
+		dev_err(&d->intf->dev, "unknown tuner %s\n", dev->tuner_name);
 		ret = -ENODEV;
 		goto err;
 	}
 
 	/* attach demodulator */
-	adap->fe[0] = dvb_attach(rtl2830_attach, rtl2830_config, &d->i2c_adap);
-	if (!adap->fe[0]) {
+	memset(&board_info, 0, sizeof(board_info));
+	strlcpy(board_info.type, "rtl2830", I2C_NAME_SIZE);
+	board_info.addr = 0x10;
+	board_info.platform_data = pdata;
+	request_module("%s", board_info.type);
+	client = i2c_new_device(&d->i2c_adap, &board_info);
+	if (client == NULL || client->dev.driver == NULL) {
 		ret = -ENODEV;
 		goto err;
 	}
 
+	if (!try_module_get(client->dev.driver->owner)) {
+		i2c_unregister_device(client);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	adap->fe[0] = pdata->get_dvb_frontend(client);
+	dev->demod_i2c_adapter = pdata->get_i2c_adapter(client);
+
+	dev->i2c_client_demod = client;
+
 	return 0;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
-static const struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = {
-	.i2c_addr = 0x10, /* 0x20 */
-	.xtal = 28800000,
+static const struct rtl2832_platform_data rtl2832_fc0012_platform_data = {
+	.clk = 28800000,
 	.tuner = TUNER_RTL2832_FC0012
 };
 
-static const struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = {
-	.i2c_addr = 0x10, /* 0x20 */
-	.xtal = 28800000,
+static const struct rtl2832_platform_data rtl2832_fc0013_platform_data = {
+	.clk = 28800000,
 	.tuner = TUNER_RTL2832_FC0013
 };
 
-static const struct rtl2832_config rtl28xxu_rtl2832_tua9001_config = {
-	.i2c_addr = 0x10, /* 0x20 */
-	.xtal = 28800000,
+static const struct rtl2832_platform_data rtl2832_tua9001_platform_data = {
+	.clk = 28800000,
 	.tuner = TUNER_RTL2832_TUA9001,
 };
 
-static const struct rtl2832_config rtl28xxu_rtl2832_e4000_config = {
-	.i2c_addr = 0x10, /* 0x20 */
-	.xtal = 28800000,
+static const struct rtl2832_platform_data rtl2832_e4000_platform_data = {
+	.clk = 28800000,
 	.tuner = TUNER_RTL2832_E4000,
 };
 
-static const struct rtl2832_config rtl28xxu_rtl2832_r820t_config = {
-	.i2c_addr = 0x10,
-	.xtal = 28800000,
+static const struct rtl2832_platform_data rtl2832_r820t_platform_data = {
+	.clk = 28800000,
 	.tuner = TUNER_RTL2832_R820T,
 };
 
@@ -678,12 +674,12 @@
 	int ret;
 	u8 val;
 
-	dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg);
+	dev_dbg(&d->intf->dev, "cmd=%d arg=%d\n", cmd, arg);
 
 	switch (cmd) {
 	case FC_FE_CALLBACK_VHF_ENABLE:
 		/* set output values */
-		ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
+		ret = rtl28xxu_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
 		if (ret)
 			goto err;
 
@@ -693,7 +689,7 @@
 			val |= 0x40; /* set GPIO6 high */
 
 
-		ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
+		ret = rtl28xxu_wr_reg(d, SYS_GPIO_OUT_VAL, val);
 		if (ret)
 			goto err;
 		break;
@@ -703,7 +699,7 @@
 	}
 	return 0;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
@@ -713,7 +709,7 @@
 	int ret;
 	u8 val;
 
-	dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg);
+	dev_dbg(&d->intf->dev, "cmd=%d arg=%d\n", cmd, arg);
 
 	/*
 	 * CEN     always enabled by hardware wiring
@@ -728,7 +724,7 @@
 		else
 			val = (0 << 4);
 
-		ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x10);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x10);
 		if (ret)
 			goto err;
 		break;
@@ -738,7 +734,7 @@
 		else
 			val = (0 << 1);
 
-		ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x02);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x02);
 		if (ret)
 			goto err;
 		break;
@@ -746,40 +742,44 @@
 
 	return 0;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
-static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg)
-{
-	struct rtl28xxu_priv *priv = d->priv;
-
-	switch (priv->tuner) {
-	case TUNER_RTL2832_FC0012:
-		return rtl2832u_fc0012_tuner_callback(d, cmd, arg);
-	case TUNER_RTL2832_TUA9001:
-		return rtl2832u_tua9001_tuner_callback(d, cmd, arg);
-	default:
-		break;
-	}
-
-	return 0;
-}
-
 static int rtl2832u_frontend_callback(void *adapter_priv, int component,
 		int cmd, int arg)
 {
-	struct i2c_adapter *adap = adapter_priv;
-	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	struct i2c_adapter *adapter = adapter_priv;
+	struct device *parent = adapter->dev.parent;
+	struct i2c_adapter *parent_adapter;
+	struct dvb_usb_device *d;
+	struct rtl28xxu_dev *dev;
 
-	dev_dbg(&d->udev->dev, "%s: component=%d cmd=%d arg=%d\n",
-			__func__, component, cmd, arg);
+	/*
+	 * All tuners are connected to demod muxed I2C adapter. We have to
+	 * resolve its parent adapter in order to get handle for this driver
+	 * private data. That is a bit hackish solution, GPIO or direct driver
+	 * callback would be better...
+	 */
+	if (parent != NULL && parent->type == &i2c_adapter_type)
+		parent_adapter = to_i2c_adapter(parent);
+	else
+		return -EINVAL;
+
+	d = i2c_get_adapdata(parent_adapter);
+	dev = d->priv;
+
+	dev_dbg(&d->intf->dev, "component=%d cmd=%d arg=%d\n",
+		component, cmd, arg);
 
 	switch (component) {
 	case DVB_FRONTEND_COMPONENT_TUNER:
-		return rtl2832u_tuner_callback(d, cmd, arg);
-	default:
-		break;
+		switch (dev->tuner) {
+		case TUNER_RTL2832_FC0012:
+			return rtl2832u_fc0012_tuner_callback(d, cmd, arg);
+		case TUNER_RTL2832_TUA9001:
+			return rtl2832u_tua9001_tuner_callback(d, cmd, arg);
+		}
 	}
 
 	return 0;
@@ -787,57 +787,70 @@
 
 static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
 {
-	int ret;
 	struct dvb_usb_device *d = adap_to_d(adap);
-	struct rtl28xxu_priv *priv = d_to_priv(d);
-	const struct rtl2832_config *rtl2832_config;
+	struct rtl28xxu_dev *dev = d_to_priv(d);
+	struct rtl2832_platform_data *pdata = &dev->rtl2832_platform_data;
+	struct i2c_board_info board_info;
+	struct i2c_client *client;
+	int ret;
 
-	dev_dbg(&d->udev->dev, "%s:\n", __func__);
+	dev_dbg(&d->intf->dev, "\n");
 
-	switch (priv->tuner) {
+	switch (dev->tuner) {
 	case TUNER_RTL2832_FC0012:
-		rtl2832_config = &rtl28xxu_rtl2832_fc0012_config;
+		*pdata = rtl2832_fc0012_platform_data;
 		break;
 	case TUNER_RTL2832_FC0013:
-		rtl2832_config = &rtl28xxu_rtl2832_fc0013_config;
+		*pdata = rtl2832_fc0013_platform_data;
 		break;
 	case TUNER_RTL2832_FC2580:
 		/* FIXME: do not abuse fc0012 settings */
-		rtl2832_config = &rtl28xxu_rtl2832_fc0012_config;
+		*pdata = rtl2832_fc0012_platform_data;
 		break;
 	case TUNER_RTL2832_TUA9001:
-		rtl2832_config = &rtl28xxu_rtl2832_tua9001_config;
+		*pdata = rtl2832_tua9001_platform_data;
 		break;
 	case TUNER_RTL2832_E4000:
-		rtl2832_config = &rtl28xxu_rtl2832_e4000_config;
+		*pdata = rtl2832_e4000_platform_data;
 		break;
 	case TUNER_RTL2832_R820T:
 	case TUNER_RTL2832_R828D:
-		rtl2832_config = &rtl28xxu_rtl2832_r820t_config;
+		*pdata = rtl2832_r820t_platform_data;
 		break;
 	default:
-		dev_err(&d->udev->dev, "%s: unknown tuner=%s\n",
-				KBUILD_MODNAME, priv->tuner_name);
+		dev_err(&d->intf->dev, "unknown tuner %s\n", dev->tuner_name);
 		ret = -ENODEV;
 		goto err;
 	}
 
 	/* attach demodulator */
-	adap->fe[0] = dvb_attach(rtl2832_attach, rtl2832_config, &d->i2c_adap);
-	if (!adap->fe[0]) {
+	memset(&board_info, 0, sizeof(board_info));
+	strlcpy(board_info.type, "rtl2832", I2C_NAME_SIZE);
+	board_info.addr = 0x10;
+	board_info.platform_data = pdata;
+	request_module("%s", board_info.type);
+	client = i2c_new_device(&d->i2c_adap, &board_info);
+	if (client == NULL || client->dev.driver == NULL) {
 		ret = -ENODEV;
 		goto err;
 	}
 
-	/* RTL2832 I2C repeater */
-	priv->demod_i2c_adapter = rtl2832_get_i2c_adapter(adap->fe[0]);
+	if (!try_module_get(client->dev.driver->owner)) {
+		i2c_unregister_device(client);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	adap->fe[0] = pdata->get_dvb_frontend(client);
+	dev->demod_i2c_adapter = pdata->get_i2c_adapter(client);
+
+	dev->i2c_client_demod = client;
 
 	/* set fe callback */
 	adap->fe[0]->callback = rtl2832u_frontend_callback;
 
-	if (priv->slave_demod) {
+	if (dev->slave_demod) {
 		struct i2c_board_info info = {};
-		struct i2c_client *client;
 
 		/*
 		 * We continue on reduced mode, without DVB-T2/C, using master
@@ -846,28 +859,31 @@
 		ret = 0;
 
 		/* attach slave demodulator */
-		if (priv->slave_demod == SLAVE_DEMOD_MN88472) {
+		if (dev->slave_demod == SLAVE_DEMOD_MN88472) {
 			struct mn88472_config mn88472_config = {};
 
 			mn88472_config.fe = &adap->fe[1];
 			mn88472_config.i2c_wr_max = 22,
 			strlcpy(info.type, "mn88472", I2C_NAME_SIZE);
+			mn88472_config.xtal = 20500000;
+			mn88472_config.ts_mode = SERIAL_TS_MODE;
+			mn88472_config.ts_clock = VARIABLE_TS_CLOCK;
 			info.addr = 0x18;
 			info.platform_data = &mn88472_config;
 			request_module(info.type);
-			client = i2c_new_device(priv->demod_i2c_adapter, &info);
+			client = i2c_new_device(&d->i2c_adap, &info);
 			if (client == NULL || client->dev.driver == NULL) {
-				priv->slave_demod = SLAVE_DEMOD_NONE;
+				dev->slave_demod = SLAVE_DEMOD_NONE;
 				goto err_slave_demod_failed;
 			}
 
 			if (!try_module_get(client->dev.driver->owner)) {
 				i2c_unregister_device(client);
-				priv->slave_demod = SLAVE_DEMOD_NONE;
+				dev->slave_demod = SLAVE_DEMOD_NONE;
 				goto err_slave_demod_failed;
 			}
 
-			priv->i2c_client_slave_demod = client;
+			dev->i2c_client_slave_demod = client;
 		} else {
 			struct mn88473_config mn88473_config = {};
 
@@ -877,29 +893,64 @@
 			info.addr = 0x18;
 			info.platform_data = &mn88473_config;
 			request_module(info.type);
-			client = i2c_new_device(priv->demod_i2c_adapter, &info);
+			client = i2c_new_device(&d->i2c_adap, &info);
 			if (client == NULL || client->dev.driver == NULL) {
-				priv->slave_demod = SLAVE_DEMOD_NONE;
+				dev->slave_demod = SLAVE_DEMOD_NONE;
 				goto err_slave_demod_failed;
 			}
 
 			if (!try_module_get(client->dev.driver->owner)) {
 				i2c_unregister_device(client);
-				priv->slave_demod = SLAVE_DEMOD_NONE;
+				dev->slave_demod = SLAVE_DEMOD_NONE;
 				goto err_slave_demod_failed;
 			}
 
-			priv->i2c_client_slave_demod = client;
+			dev->i2c_client_slave_demod = client;
 		}
 	}
 
 	return 0;
 err_slave_demod_failed:
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
+static int rtl28xxu_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	struct rtl28xxu_dev *dev = adap_to_priv(adap);
+
+	if (dev->chip_id == CHIP_ID_RTL2831U)
+		return rtl2831u_frontend_attach(adap);
+	else
+		return rtl2832u_frontend_attach(adap);
+}
+
+static int rtl28xxu_frontend_detach(struct dvb_usb_adapter *adap)
+{
+	struct dvb_usb_device *d = adap_to_d(adap);
+	struct rtl28xxu_dev *dev = d_to_priv(d);
+	struct i2c_client *client;
+
+	dev_dbg(&d->intf->dev, "\n");
+
+	/* remove I2C slave demod */
+	client = dev->i2c_client_slave_demod;
+	if (client) {
+		module_put(client->dev.driver->owner);
+		i2c_unregister_device(client);
+	}
+
+	/* remove I2C demod */
+	client = dev->i2c_client_demod;
+	if (client) {
+		module_put(client->dev.driver->owner);
+		i2c_unregister_device(client);
+	}
+
+	return 0;
+}
+
 static struct qt1010_config rtl28xxu_qt1010_config = {
 	.i2c_address = 0x62, /* 0xc4 */
 };
@@ -930,33 +981,30 @@
 {
 	int ret;
 	struct dvb_usb_device *d = adap_to_d(adap);
-	struct rtl28xxu_priv *priv = d_to_priv(d);
-	struct i2c_adapter *rtl2830_tuner_i2c;
+	struct rtl28xxu_dev *dev = d_to_priv(d);
 	struct dvb_frontend *fe;
 
-	dev_dbg(&d->udev->dev, "%s:\n", __func__);
+	dev_dbg(&d->intf->dev, "\n");
 
-	/* use rtl2830 driver I2C adapter, for more info see rtl2830 driver */
-	rtl2830_tuner_i2c = rtl2830_get_tuner_i2c_adapter(adap->fe[0]);
-
-	switch (priv->tuner) {
+	switch (dev->tuner) {
 	case TUNER_RTL2830_QT1010:
 		fe = dvb_attach(qt1010_attach, adap->fe[0],
-				rtl2830_tuner_i2c, &rtl28xxu_qt1010_config);
+				dev->demod_i2c_adapter,
+				&rtl28xxu_qt1010_config);
 		break;
 	case TUNER_RTL2830_MT2060:
 		fe = dvb_attach(mt2060_attach, adap->fe[0],
-				rtl2830_tuner_i2c, &rtl28xxu_mt2060_config,
-				1220);
+				dev->demod_i2c_adapter,
+				&rtl28xxu_mt2060_config, 1220);
 		break;
 	case TUNER_RTL2830_MXL5005S:
 		fe = dvb_attach(mxl5005s_attach, adap->fe[0],
-				rtl2830_tuner_i2c, &rtl28xxu_mxl5005s_config);
+				dev->demod_i2c_adapter,
+				&rtl28xxu_mxl5005s_config);
 		break;
 	default:
 		fe = NULL;
-		dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME,
-				priv->tuner);
+		dev_err(&d->intf->dev, "unknown tuner %d\n", dev->tuner);
 	}
 
 	if (fe == NULL) {
@@ -966,7 +1014,7 @@
 
 	return 0;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
@@ -1002,45 +1050,38 @@
 {
 	int ret;
 	struct dvb_usb_device *d = adap_to_d(adap);
-	struct rtl28xxu_priv *priv = d_to_priv(d);
+	struct rtl28xxu_dev *dev = d_to_priv(d);
 	struct dvb_frontend *fe = NULL;
 	struct i2c_board_info info;
 	struct i2c_client *client;
+	struct v4l2_subdev *subdev = NULL;
+	struct platform_device *pdev;
+	struct rtl2832_sdr_platform_data pdata;
 
-	dev_dbg(&d->udev->dev, "%s:\n", __func__);
+	dev_dbg(&d->intf->dev, "\n");
 
 	memset(&info, 0, sizeof(struct i2c_board_info));
+	memset(&pdata, 0, sizeof(pdata));
 
-	switch (priv->tuner) {
+	switch (dev->tuner) {
 	case TUNER_RTL2832_FC0012:
 		fe = dvb_attach(fc0012_attach, adap->fe[0],
-			&d->i2c_adap, &rtl2832u_fc0012_config);
+			dev->demod_i2c_adapter, &rtl2832u_fc0012_config);
 
 		/* since fc0012 includs reading the signal strength delegate
 		 * that to the tuner driver */
 		adap->fe[0]->ops.read_signal_strength =
 				adap->fe[0]->ops.tuner_ops.get_rf_strength;
-
-		/* attach SDR */
-		dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
-				&rtl28xxu_rtl2832_fc0012_config, NULL);
 		break;
 	case TUNER_RTL2832_FC0013:
 		fe = dvb_attach(fc0013_attach, adap->fe[0],
-			&d->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ);
+			dev->demod_i2c_adapter, 0xc6>>1, 0, FC_XTAL_28_8_MHZ);
 
 		/* fc0013 also supports signal strength reading */
 		adap->fe[0]->ops.read_signal_strength =
 				adap->fe[0]->ops.tuner_ops.get_rf_strength;
-
-		/* attach SDR */
-		dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
-				&rtl28xxu_rtl2832_fc0013_config, NULL);
 		break;
 	case TUNER_RTL2832_E4000: {
-			struct v4l2_subdev *sd;
-			struct i2c_adapter *i2c_adap_internal =
-					rtl2832_get_private_i2c_adapter(adap->fe[0]);
 			struct e4000_config e4000_config = {
 				.fe = adap->fe[0],
 				.clock = 28800000,
@@ -1051,7 +1092,7 @@
 			info.platform_data = &e4000_config;
 
 			request_module(info.type);
-			client = i2c_new_device(priv->demod_i2c_adapter, &info);
+			client = i2c_new_device(dev->demod_i2c_adapter, &info);
 			if (client == NULL || client->dev.driver == NULL)
 				break;
 
@@ -1060,157 +1101,183 @@
 				break;
 			}
 
-			priv->i2c_client_tuner = client;
-			sd = i2c_get_clientdata(client);
-			i2c_set_adapdata(i2c_adap_internal, d);
-
-			/* attach SDR */
-			dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0],
-					i2c_adap_internal,
-					&rtl28xxu_rtl2832_e4000_config, sd);
+			dev->i2c_client_tuner = client;
+			subdev = i2c_get_clientdata(client);
 		}
 		break;
 	case TUNER_RTL2832_FC2580:
-		fe = dvb_attach(fc2580_attach, adap->fe[0], &d->i2c_adap,
+		fe = dvb_attach(fc2580_attach, adap->fe[0],
+				dev->demod_i2c_adapter,
 				&rtl2832u_fc2580_config);
 		break;
 	case TUNER_RTL2832_TUA9001:
 		/* enable GPIO1 and GPIO4 as output */
-		ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x12);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x12);
 		if (ret)
 			goto err;
 
-		ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x12, 0x12);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x12, 0x12);
 		if (ret)
 			goto err;
 
-		fe = dvb_attach(tua9001_attach, adap->fe[0], &d->i2c_adap,
+		fe = dvb_attach(tua9001_attach, adap->fe[0],
+				dev->demod_i2c_adapter,
 				&rtl2832u_tua9001_config);
 		break;
 	case TUNER_RTL2832_R820T:
-		fe = dvb_attach(r820t_attach, adap->fe[0], &d->i2c_adap,
+		fe = dvb_attach(r820t_attach, adap->fe[0],
+				dev->demod_i2c_adapter,
 				&rtl2832u_r820t_config);
 
 		/* Use tuner to get the signal strength */
 		adap->fe[0]->ops.read_signal_strength =
 				adap->fe[0]->ops.tuner_ops.get_rf_strength;
-
-		/* attach SDR */
-		dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
-				&rtl28xxu_rtl2832_r820t_config, NULL);
 		break;
 	case TUNER_RTL2832_R828D:
 		fe = dvb_attach(r820t_attach, adap->fe[0],
-				priv->demod_i2c_adapter,
+				dev->demod_i2c_adapter,
 				&rtl2832u_r828d_config);
 		adap->fe[0]->ops.read_signal_strength =
 				adap->fe[0]->ops.tuner_ops.get_rf_strength;
 
 		if (adap->fe[1]) {
 			fe = dvb_attach(r820t_attach, adap->fe[1],
-					priv->demod_i2c_adapter,
+					dev->demod_i2c_adapter,
 					&rtl2832u_r828d_config);
 			adap->fe[1]->ops.read_signal_strength =
 					adap->fe[1]->ops.tuner_ops.get_rf_strength;
 		}
-
-		/* attach SDR */
-		dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
-				&rtl28xxu_rtl2832_r820t_config, NULL);
 		break;
 	default:
-		dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME,
-				priv->tuner);
+		dev_err(&d->intf->dev, "unknown tuner %d\n", dev->tuner);
 	}
-
-	if (fe == NULL && priv->i2c_client_tuner == NULL) {
+	if (fe == NULL && dev->i2c_client_tuner == NULL) {
 		ret = -ENODEV;
 		goto err;
 	}
 
+	/* register SDR */
+	switch (dev->tuner) {
+	case TUNER_RTL2832_FC0012:
+	case TUNER_RTL2832_FC0013:
+	case TUNER_RTL2832_E4000:
+	case TUNER_RTL2832_R820T:
+	case TUNER_RTL2832_R828D:
+		pdata.clk = dev->rtl2832_platform_data.clk;
+		pdata.tuner = dev->tuner;
+		pdata.i2c_client = dev->i2c_client_demod;
+		pdata.bulk_read = dev->rtl2832_platform_data.bulk_read;
+		pdata.bulk_write = dev->rtl2832_platform_data.bulk_write;
+		pdata.update_bits = dev->rtl2832_platform_data.update_bits;
+		pdata.dvb_frontend = adap->fe[0];
+		pdata.dvb_usb_device = d;
+		pdata.v4l2_subdev = subdev;
+
+		request_module("%s", "rtl2832_sdr");
+		pdev = platform_device_register_data(&d->intf->dev,
+						     "rtl2832_sdr",
+						     PLATFORM_DEVID_AUTO,
+						     &pdata, sizeof(pdata));
+		if (pdev == NULL || pdev->dev.driver == NULL)
+			break;
+		dev->platform_device_sdr = pdev;
+		break;
+	default:
+		dev_dbg(&d->intf->dev, "no SDR for tuner=%d\n", dev->tuner);
+	}
+
 	return 0;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
+static int rtl28xxu_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	struct rtl28xxu_dev *dev = adap_to_priv(adap);
+
+	if (dev->chip_id == CHIP_ID_RTL2831U)
+		return rtl2831u_tuner_attach(adap);
+	else
+		return rtl2832u_tuner_attach(adap);
+}
+
+static int rtl28xxu_tuner_detach(struct dvb_usb_adapter *adap)
+{
+	struct dvb_usb_device *d = adap_to_d(adap);
+	struct rtl28xxu_dev *dev = d_to_priv(d);
+	struct i2c_client *client;
+	struct platform_device *pdev;
+
+	dev_dbg(&d->intf->dev, "\n");
+
+	/* remove platform SDR */
+	pdev = dev->platform_device_sdr;
+	if (pdev)
+		platform_device_unregister(pdev);
+
+	/* remove I2C tuner */
+	client = dev->i2c_client_tuner;
+	if (client) {
+		module_put(client->dev.driver->owner);
+		i2c_unregister_device(client);
+	}
+
+	return 0;
+}
+
 static int rtl28xxu_init(struct dvb_usb_device *d)
 {
 	int ret;
 	u8 val;
 
-	dev_dbg(&d->udev->dev, "%s:\n", __func__);
+	dev_dbg(&d->intf->dev, "\n");
 
 	/* init USB endpoints */
-	ret = rtl28xx_rd_reg(d, USB_SYSCTL_0, &val);
+	ret = rtl28xxu_rd_reg(d, USB_SYSCTL_0, &val);
 	if (ret)
 		goto err;
 
 	/* enable DMA and Full Packet Mode*/
 	val |= 0x09;
-	ret = rtl28xx_wr_reg(d, USB_SYSCTL_0, val);
+	ret = rtl28xxu_wr_reg(d, USB_SYSCTL_0, val);
 	if (ret)
 		goto err;
 
 	/* set EPA maximum packet size to 0x0200 */
-	ret = rtl28xx_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
+	ret = rtl28xxu_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
 	if (ret)
 		goto err;
 
 	/* change EPA FIFO length */
-	ret = rtl28xx_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
+	ret = rtl28xxu_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
 	if (ret)
 		goto err;
 
 	return ret;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
-static void rtl28xxu_exit(struct dvb_usb_device *d)
-{
-	struct rtl28xxu_priv *priv = d->priv;
-	struct i2c_client *client;
-
-	dev_dbg(&d->udev->dev, "%s:\n", __func__);
-
-	/* remove I2C tuner */
-	client = priv->i2c_client_tuner;
-	if (client) {
-		module_put(client->dev.driver->owner);
-		i2c_unregister_device(client);
-	}
-
-	/* remove I2C slave demod */
-	client = priv->i2c_client_slave_demod;
-	if (client) {
-		module_put(client->dev.driver->owner);
-		i2c_unregister_device(client);
-	}
-
-	return;
-}
-
 static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff)
 {
 	int ret;
 	u8 gpio, sys0, epa_ctl[2];
 
-	dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
+	dev_dbg(&d->intf->dev, "onoff=%d\n", onoff);
 
 	/* demod adc */
-	ret = rtl28xx_rd_reg(d, SYS_SYS0, &sys0);
+	ret = rtl28xxu_rd_reg(d, SYS_SYS0, &sys0);
 	if (ret)
 		goto err;
 
 	/* tuner power, read GPIOs */
-	ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
+	ret = rtl28xxu_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
 	if (ret)
 		goto err;
 
-	dev_dbg(&d->udev->dev, "%s: RD SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__,
-			sys0, gpio);
+	dev_dbg(&d->intf->dev, "RD SYS0=%02x GPIO_OUT_VAL=%02x\n", sys0, gpio);
 
 	if (onoff) {
 		gpio |= 0x01; /* GPIO0 = 1 */
@@ -1229,21 +1296,20 @@
 		epa_ctl[1] = 0x02; /* set reset */
 	}
 
-	dev_dbg(&d->udev->dev, "%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__,
-			sys0, gpio);
+	dev_dbg(&d->intf->dev, "WR SYS0=%02x GPIO_OUT_VAL=%02x\n", sys0, gpio);
 
 	/* demod adc */
-	ret = rtl28xx_wr_reg(d, SYS_SYS0, sys0);
+	ret = rtl28xxu_wr_reg(d, SYS_SYS0, sys0);
 	if (ret)
 		goto err;
 
 	/* tuner power, write GPIOs */
-	ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
+	ret = rtl28xxu_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
 	if (ret)
 		goto err;
 
 	/* streaming EP: stall & reset */
-	ret = rtl28xx_wr_regs(d, USB_EPA_CTL, epa_ctl, 2);
+	ret = rtl28xxu_wr_regs(d, USB_EPA_CTL, epa_ctl, 2);
 	if (ret)
 		goto err;
 
@@ -1252,7 +1318,7 @@
 
 	return ret;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
@@ -1260,31 +1326,31 @@
 {
 	int ret;
 
-	dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
+	dev_dbg(&d->intf->dev, "onoff=%d\n", onoff);
 
 	if (onoff) {
 		/* GPIO3=1, GPIO4=0 */
-		ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x08, 0x18);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x08, 0x18);
 		if (ret)
 			goto err;
 
 		/* suspend? */
-		ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL1, 0x00, 0x10);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL1, 0x00, 0x10);
 		if (ret)
 			goto err;
 
 		/* enable PLL */
-		ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x80, 0x80);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, 0x80, 0x80);
 		if (ret)
 			goto err;
 
 		/* disable reset */
-		ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x20, 0x20);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, 0x20, 0x20);
 		if (ret)
 			goto err;
 
 		/* streaming EP: clear stall & reset */
-		ret = rtl28xx_wr_regs(d, USB_EPA_CTL, "\x00\x00", 2);
+		ret = rtl28xxu_wr_regs(d, USB_EPA_CTL, "\x00\x00", 2);
 		if (ret)
 			goto err;
 
@@ -1293,35 +1359,49 @@
 			goto err;
 	} else {
 		/* GPIO4=1 */
-		ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x10, 0x10);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x10, 0x10);
 		if (ret)
 			goto err;
 
 		/* disable PLL */
-		ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x00, 0x80);
+		ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, 0x00, 0x80);
 		if (ret)
 			goto err;
 
 		/* streaming EP: set stall & reset */
-		ret = rtl28xx_wr_regs(d, USB_EPA_CTL, "\x10\x02", 2);
+		ret = rtl28xxu_wr_regs(d, USB_EPA_CTL, "\x10\x02", 2);
 		if (ret)
 			goto err;
 	}
 
 	return ret;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
-static int rtl2832u_frontend_ctrl(struct dvb_frontend *fe, int onoff)
+static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+	struct rtl28xxu_dev *dev = d_to_priv(d);
+
+	if (dev->chip_id == CHIP_ID_RTL2831U)
+		return rtl2831u_power_ctrl(d, onoff);
+	else
+		return rtl2832u_power_ctrl(d, onoff);
+}
+
+static int rtl28xxu_frontend_ctrl(struct dvb_frontend *fe, int onoff)
 {
 	struct dvb_usb_device *d = fe_to_d(fe);
-	struct dvb_usb_adapter *adap = fe_to_adap(fe);
+	struct rtl28xxu_dev *dev = fe_to_priv(fe);
+	struct rtl2832_platform_data *pdata = &dev->rtl2832_platform_data;
 	int ret;
 	u8 val;
 
-	dev_dbg(&d->udev->dev, "%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+	dev_dbg(&d->intf->dev, "fe=%d onoff=%d\n", fe->id, onoff);
+
+	if (dev->chip_id == CHIP_ID_RTL2831U)
+		return 0;
 
 	/* control internal demod ADC */
 	if (fe->id == 0 && onoff)
@@ -1329,20 +1409,20 @@
 	else
 		val = 0x00; /* disable ADC */
 
-	ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, val, 0x48);
+	ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, val, 0x48);
 	if (ret)
 		goto err;
 
 	/* bypass slave demod TS through master demod */
 	if (fe->id == 1 && onoff) {
-		ret = rtl2832_enable_external_ts_if(adap->fe[0]);
+		ret = pdata->enable_slave_ts(dev->i2c_client_demod);
 		if (ret)
 			goto err;
 	}
 
 	return 0;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
@@ -1350,7 +1430,7 @@
 static int rtl2831u_rc_query(struct dvb_usb_device *d)
 {
 	int ret, i;
-	struct rtl28xxu_priv *priv = d->priv;
+	struct rtl28xxu_dev *dev = d->priv;
 	u8 buf[5];
 	u32 rc_code;
 	struct rtl28xxu_reg_val rc_nec_tab[] = {
@@ -1371,17 +1451,17 @@
 	};
 
 	/* init remote controller */
-	if (!priv->rc_active) {
+	if (!dev->rc_active) {
 		for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
-			ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg,
+			ret = rtl28xxu_wr_reg(d, rc_nec_tab[i].reg,
 					rc_nec_tab[i].val);
 			if (ret)
 				goto err;
 		}
-		priv->rc_active = true;
+		dev->rc_active = true;
 	}
 
-	ret = rtl2831_rd_regs(d, SYS_IRRC_RP, buf, 5);
+	ret = rtl28xxu_rd_regs(d, SYS_IRRC_RP, buf, 5);
 	if (ret)
 		goto err;
 
@@ -1403,19 +1483,19 @@
 
 		rc_keydown(d->rc_dev, RC_TYPE_NEC, rc_code, 0);
 
-		ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1);
+		ret = rtl28xxu_wr_reg(d, SYS_IRRC_SR, 1);
 		if (ret)
 			goto err;
 
 		/* repeated intentionally to avoid extra keypress */
-		ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1);
+		ret = rtl28xxu_wr_reg(d, SYS_IRRC_SR, 1);
 		if (ret)
 			goto err;
 	}
 
 	return ret;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
@@ -1433,7 +1513,7 @@
 static int rtl2832u_rc_query(struct dvb_usb_device *d)
 {
 	int ret, i, len;
-	struct rtl28xxu_priv *priv = d->priv;
+	struct rtl28xxu_dev *dev = d->priv;
 	struct ir_raw_event ev;
 	u8 buf[128];
 	static const struct rtl28xxu_reg_val_mask refresh_tab[] = {
@@ -1443,7 +1523,7 @@
 	};
 
 	/* init remote controller */
-	if (!priv->rc_active) {
+	if (!dev->rc_active) {
 		static const struct rtl28xxu_reg_val_mask init_tab[] = {
 			{SYS_DEMOD_CTL1,         0x00, 0x04},
 			{SYS_DEMOD_CTL1,         0x00, 0x08},
@@ -1464,36 +1544,36 @@
 		};
 
 		for (i = 0; i < ARRAY_SIZE(init_tab); i++) {
-			ret = rtl28xx_wr_reg_mask(d, init_tab[i].reg,
+			ret = rtl28xxu_wr_reg_mask(d, init_tab[i].reg,
 					init_tab[i].val, init_tab[i].mask);
 			if (ret)
 				goto err;
 		}
 
-		priv->rc_active = true;
+		dev->rc_active = true;
 	}
 
-	ret = rtl28xx_rd_reg(d, IR_RX_IF, &buf[0]);
+	ret = rtl28xxu_rd_reg(d, IR_RX_IF, &buf[0]);
 	if (ret)
 		goto err;
 
 	if (buf[0] != 0x83)
 		goto exit;
 
-	ret = rtl28xx_rd_reg(d, IR_RX_BC, &buf[0]);
+	ret = rtl28xxu_rd_reg(d, IR_RX_BC, &buf[0]);
 	if (ret)
 		goto err;
 
 	len = buf[0];
 
 	/* read raw code from hw */
-	ret = rtl2831_rd_regs(d, IR_RX_BUF, buf, len);
+	ret = rtl28xxu_rd_regs(d, IR_RX_BUF, buf, len);
 	if (ret)
 		goto err;
 
 	/* let hw receive new code */
 	for (i = 0; i < ARRAY_SIZE(refresh_tab); i++) {
-		ret = rtl28xx_wr_reg_mask(d, refresh_tab[i].reg,
+		ret = rtl28xxu_wr_reg_mask(d, refresh_tab[i].reg,
 				refresh_tab[i].val, refresh_tab[i].mask);
 		if (ret)
 			goto err;
@@ -1514,7 +1594,7 @@
 exit:
 	return ret;
 err:
-	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&d->intf->dev, "failed=%d\n", ret);
 	return ret;
 }
 
@@ -1523,7 +1603,7 @@
 {
 	/* disable IR interrupts in order to avoid SDR sample loss */
 	if (rtl28xxu_disable_rc)
-		return rtl28xx_wr_reg(d, IR_RX_IE, 0x00);
+		return rtl28xxu_wr_reg(d, IR_RX_IE, 0x00);
 
 	/* load empty to enable rc */
 	if (!rc->map_name)
@@ -1531,56 +1611,84 @@
 	rc->allowed_protos = RC_BIT_ALL;
 	rc->driver_type = RC_DRIVER_IR_RAW;
 	rc->query = rtl2832u_rc_query;
-	rc->interval = 400;
+	rc->interval = 200;
 
 	return 0;
 }
+
+static int rtl28xxu_get_rc_config(struct dvb_usb_device *d,
+		struct dvb_usb_rc *rc)
+{
+	struct rtl28xxu_dev *dev = d_to_priv(d);
+
+	if (dev->chip_id == CHIP_ID_RTL2831U)
+		return rtl2831u_get_rc_config(d, rc);
+	else
+		return rtl2832u_get_rc_config(d, rc);
+}
 #else
-#define rtl2831u_get_rc_config NULL
-#define rtl2832u_get_rc_config NULL
+#define rtl28xxu_get_rc_config NULL
 #endif
 
-static const struct dvb_usb_device_properties rtl2831u_props = {
+static int rtl28xxu_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+	struct rtl28xxu_dev *dev = adap_to_priv(adap);
+
+	if (dev->chip_id == CHIP_ID_RTL2831U) {
+		struct rtl2830_platform_data *pdata = &dev->rtl2830_platform_data;
+
+		return pdata->pid_filter_ctrl(adap->fe[0], onoff);
+	} else {
+		struct rtl2832_platform_data *pdata = &dev->rtl2832_platform_data;
+
+		return pdata->pid_filter_ctrl(adap->fe[0], onoff);
+	}
+}
+
+static int rtl28xxu_pid_filter(struct dvb_usb_adapter *adap, int index,
+			       u16 pid, int onoff)
+{
+	struct rtl28xxu_dev *dev = adap_to_priv(adap);
+
+	if (dev->chip_id == CHIP_ID_RTL2831U) {
+		struct rtl2830_platform_data *pdata = &dev->rtl2830_platform_data;
+
+		return pdata->pid_filter(adap->fe[0], index, pid, onoff);
+	} else {
+		struct rtl2832_platform_data *pdata = &dev->rtl2832_platform_data;
+
+		return pdata->pid_filter(adap->fe[0], index, pid, onoff);
+	}
+}
+
+static const struct dvb_usb_device_properties rtl28xxu_props = {
 	.driver_name = KBUILD_MODNAME,
 	.owner = THIS_MODULE,
 	.adapter_nr = adapter_nr,
-	.size_of_priv = sizeof(struct rtl28xxu_priv),
+	.size_of_priv = sizeof(struct rtl28xxu_dev),
 
-	.power_ctrl = rtl2831u_power_ctrl,
+	.identify_state = rtl28xxu_identify_state,
+	.power_ctrl = rtl28xxu_power_ctrl,
+	.frontend_ctrl = rtl28xxu_frontend_ctrl,
 	.i2c_algo = &rtl28xxu_i2c_algo,
-	.read_config = rtl2831u_read_config,
-	.frontend_attach = rtl2831u_frontend_attach,
-	.tuner_attach = rtl2831u_tuner_attach,
+	.read_config = rtl28xxu_read_config,
+	.frontend_attach = rtl28xxu_frontend_attach,
+	.frontend_detach = rtl28xxu_frontend_detach,
+	.tuner_attach = rtl28xxu_tuner_attach,
+	.tuner_detach = rtl28xxu_tuner_detach,
 	.init = rtl28xxu_init,
-	.get_rc_config = rtl2831u_get_rc_config,
+	.get_rc_config = rtl28xxu_get_rc_config,
 
 	.num_adapters = 1,
 	.adapter = {
 		{
-			.stream = DVB_USB_STREAM_BULK(0x81, 6, 8 * 512),
-		},
-	},
-};
+			.caps = DVB_USB_ADAP_HAS_PID_FILTER |
+				DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 
-static const struct dvb_usb_device_properties rtl2832u_props = {
-	.driver_name = KBUILD_MODNAME,
-	.owner = THIS_MODULE,
-	.adapter_nr = adapter_nr,
-	.size_of_priv = sizeof(struct rtl28xxu_priv),
+			.pid_filter_count = 32,
+			.pid_filter_ctrl = rtl28xxu_pid_filter_ctrl,
+			.pid_filter = rtl28xxu_pid_filter,
 
-	.power_ctrl = rtl2832u_power_ctrl,
-	.frontend_ctrl = rtl2832u_frontend_ctrl,
-	.i2c_algo = &rtl28xxu_i2c_algo,
-	.read_config = rtl2832u_read_config,
-	.frontend_attach = rtl2832u_frontend_attach,
-	.tuner_attach = rtl2832u_tuner_attach,
-	.init = rtl28xxu_init,
-	.exit = rtl28xxu_exit,
-	.get_rc_config = rtl2832u_get_rc_config,
-
-	.num_adapters = 1,
-	.adapter = {
-		{
 			.stream = DVB_USB_STREAM_BULK(0x81, 6, 8 * 512),
 		},
 	},
@@ -1589,69 +1697,73 @@
 static const struct usb_device_id rtl28xxu_id_table[] = {
 	/* RTL2831U devices: */
 	{ DVB_USB_DEVICE(USB_VID_REALTEK, USB_PID_REALTEK_RTL2831U,
-		&rtl2831u_props, "Realtek RTL2831U reference design", NULL) },
+		&rtl28xxu_props, "Realtek RTL2831U reference design", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT,
-		&rtl2831u_props, "Freecom USB2.0 DVB-T", NULL) },
+		&rtl28xxu_props, "Freecom USB2.0 DVB-T", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2,
-		&rtl2831u_props, "Freecom USB2.0 DVB-T", NULL) },
+		&rtl28xxu_props, "Freecom USB2.0 DVB-T", NULL) },
 
 	/* RTL2832U devices: */
 	{ DVB_USB_DEVICE(USB_VID_REALTEK, 0x2832,
-		&rtl2832u_props, "Realtek RTL2832U reference design", NULL) },
+		&rtl28xxu_props, "Realtek RTL2832U reference design", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838,
-		&rtl2832u_props, "Realtek RTL2832U reference design", NULL) },
+		&rtl28xxu_props, "Realtek RTL2832U reference design", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1,
-		&rtl2832u_props, "TerraTec Cinergy T Stick Black", RC_MAP_TERRATEC_SLIM) },
+		&rtl28xxu_props, "TerraTec Cinergy T Stick Black", RC_MAP_TERRATEC_SLIM) },
 	{ DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT,
-		&rtl2832u_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) },
+		&rtl28xxu_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK,
-		&rtl2832u_props, "TerraTec NOXON DAB Stick", NULL) },
+		&rtl28xxu_props, "TerraTec NOXON DAB Stick", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK_REV2,
-		&rtl2832u_props, "TerraTec NOXON DAB Stick (rev 2)", NULL) },
+		&rtl28xxu_props, "TerraTec NOXON DAB Stick (rev 2)", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK_REV3,
-		&rtl2832u_props, "TerraTec NOXON DAB Stick (rev 3)", NULL) },
+		&rtl28xxu_props, "TerraTec NOXON DAB Stick (rev 3)", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TREKSTOR_TERRES_2_0,
-		&rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) },
+		&rtl28xxu_props, "Trekstor DVB-T Stick Terres 2.0", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1101,
-		&rtl2832u_props, "Dexatek DK DVB-T Dongle", NULL) },
+		&rtl28xxu_props, "Dexatek DK DVB-T Dongle", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6680,
-		&rtl2832u_props, "DigitalNow Quad DVB-T Receiver", NULL) },
+		&rtl28xxu_props, "DigitalNow Quad DVB-T Receiver", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_MINID,
-		&rtl2832u_props, "Leadtek Winfast DTV Dongle Mini D", NULL) },
+		&rtl28xxu_props, "Leadtek Winfast DTV Dongle Mini D", NULL) },
+	{ DVB_USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS_PLUS,
+		&rtl28xxu_props, "Leadtek WinFast DTV2000DS Plus", RC_MAP_LEADTEK_Y04G0051) },
 	{ DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d3,
-		&rtl2832u_props, "TerraTec Cinergy T Stick RC (Rev. 3)", NULL) },
+		&rtl28xxu_props, "TerraTec Cinergy T Stick RC (Rev. 3)", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1102,
-		&rtl2832u_props, "Dexatek DK mini DVB-T Dongle", NULL) },
+		&rtl28xxu_props, "Dexatek DK mini DVB-T Dongle", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d7,
-		&rtl2832u_props, "TerraTec Cinergy T Stick+", NULL) },
+		&rtl28xxu_props, "TerraTec Cinergy T Stick+", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd3a8,
-		&rtl2832u_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) },
+		&rtl28xxu_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd393,
-		&rtl2832u_props, "GIGABYTE U7300", NULL) },
+		&rtl28xxu_props, "GIGABYTE U7300", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1104,
-		&rtl2832u_props, "MSI DIGIVOX Micro HD", NULL) },
+		&rtl28xxu_props, "MSI DIGIVOX Micro HD", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_COMPRO, 0x0620,
-		&rtl2832u_props, "Compro VideoMate U620F", NULL) },
+		&rtl28xxu_props, "Compro VideoMate U620F", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd394,
-		&rtl2832u_props, "MaxMedia HU394-T", NULL) },
+		&rtl28xxu_props, "MaxMedia HU394-T", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a03,
-		&rtl2832u_props, "Leadtek WinFast DTV Dongle mini", NULL) },
+		&rtl28xxu_props, "Leadtek WinFast DTV Dongle mini", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_CPYTO_REDI_PC50A,
-		&rtl2832u_props, "Crypto ReDi PC 50 A", NULL) },
+		&rtl28xxu_props, "Crypto ReDi PC 50 A", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_KYE, 0x707f,
-		&rtl2832u_props, "Genius TVGo DVB-T03", NULL) },
+		&rtl28xxu_props, "Genius TVGo DVB-T03", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd395,
-		&rtl2832u_props, "Peak DVB-T USB", NULL) },
+		&rtl28xxu_props, "Peak DVB-T USB", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20_RTL2832U,
-		&rtl2832u_props, "Sveon STV20", NULL) },
+		&rtl28xxu_props, "Sveon STV20", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV21,
-		&rtl2832u_props, "Sveon STV21", NULL) },
+		&rtl28xxu_props, "Sveon STV21", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV27,
-		&rtl2832u_props, "Sveon STV27", NULL) },
+		&rtl28xxu_props, "Sveon STV27", NULL) },
+	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TURBOX_DTT_2000,
+		&rtl28xxu_props, "TURBO-X Pure TV Tuner DTT-2000", NULL) },
 
 	/* RTL2832P devices: */
 	{ DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131,
-		&rtl2832u_props, "Astrometa DVB-T2", NULL) },
+		&rtl28xxu_props, "Astrometa DVB-T2", NULL) },
 	{ }
 };
 MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table);
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
index 3e3ea9d..1b5d7ff 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
@@ -22,8 +22,26 @@
 #ifndef RTL28XXU_H
 #define RTL28XXU_H
 
+#include <linux/platform_device.h>
+
 #include "dvb_usb.h"
 
+#include "rtl2830.h"
+#include "rtl2832.h"
+#include "rtl2832_sdr.h"
+#include "mn88472.h"
+#include "mn88473.h"
+
+#include "qt1010.h"
+#include "mt2060.h"
+#include "mxl5005s.h"
+#include "fc0012.h"
+#include "fc0013.h"
+#include "e4000.h"
+#include "fc2580.h"
+#include "tua9001.h"
+#include "r820t.h"
+
 /*
  * USB commands
  * (usb_control_msg() index parameter)
@@ -50,19 +68,26 @@
 #define CMD_I2C_DA_WR    0x0610
 
 
-struct rtl28xxu_priv {
+struct rtl28xxu_dev {
+	u8 buf[28];
 	u8 chip_id;
 	u8 tuner;
 	char *tuner_name;
 	u8 page; /* integrated demod active register page */
 	struct i2c_adapter *demod_i2c_adapter;
 	bool rc_active;
+	struct i2c_client *i2c_client_demod;
 	struct i2c_client *i2c_client_tuner;
 	struct i2c_client *i2c_client_slave_demod;
+	struct platform_device *platform_device_sdr;
 	#define SLAVE_DEMOD_NONE           0
 	#define SLAVE_DEMOD_MN88472        1
 	#define SLAVE_DEMOD_MN88473        2
 	unsigned int slave_demod:2;
+	union {
+		struct rtl2830_platform_data rtl2830_platform_data;
+		struct rtl2832_platform_data rtl2832_platform_data;
+	};
 };
 
 enum rtl28xxu_chip_id {
diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig
index 1378eca..431c02e 100644
--- a/drivers/media/usb/dvb-usb/Kconfig
+++ b/drivers/media/usb/dvb-usb/Kconfig
@@ -298,9 +298,10 @@
 	select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
 	help
-	  Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0
-	  receivers.
+	  Say Y here to support the DvbWorld, TeVii, Prof, TechnoTrend
+	  DVB-S/S2 USB2.0 receivers.
 
 config DVB_USB_CINERGY_T2
 	tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver"
diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c
index 0f345b1..ffc3704 100644
--- a/drivers/media/usb/dvb-usb/cxusb.c
+++ b/drivers/media/usb/dvb-usb/cxusb.c
@@ -1516,28 +1516,95 @@
 	dvb_usb_device_exit(intf);
 }
 
-static struct usb_device_id cxusb_table [] = {
-	{ USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) },
-	{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) },
-	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) },
-	{ USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) },
-	{ USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) },
-	{ USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230) },
+enum cxusb_table_index {
+	MEDION_MD95700,
+	DVICO_BLUEBIRD_LG064F_COLD,
+	DVICO_BLUEBIRD_LG064F_WARM,
+	DVICO_BLUEBIRD_DUAL_1_COLD,
+	DVICO_BLUEBIRD_DUAL_1_WARM,
+	DVICO_BLUEBIRD_LGZ201_COLD,
+	DVICO_BLUEBIRD_LGZ201_WARM,
+	DVICO_BLUEBIRD_TH7579_COLD,
+	DVICO_BLUEBIRD_TH7579_WARM,
+	DIGITALNOW_BLUEBIRD_DUAL_1_COLD,
+	DIGITALNOW_BLUEBIRD_DUAL_1_WARM,
+	DVICO_BLUEBIRD_DUAL_2_COLD,
+	DVICO_BLUEBIRD_DUAL_2_WARM,
+	DVICO_BLUEBIRD_DUAL_4,
+	DVICO_BLUEBIRD_DVB_T_NANO_2,
+	DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM,
+	AVERMEDIA_VOLAR_A868R,
+	DVICO_BLUEBIRD_DUAL_4_REV_2,
+	CONEXANT_D680_DMB,
+	MYGICA_D689,
+	MYGICA_T230,
+	NR__cxusb_table_index
+};
+
+static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = {
+	[MEDION_MD95700] = {
+		USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700)
+	},
+	[DVICO_BLUEBIRD_LG064F_COLD] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD)
+	},
+	[DVICO_BLUEBIRD_LG064F_WARM] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM)
+	},
+	[DVICO_BLUEBIRD_DUAL_1_COLD] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD)
+	},
+	[DVICO_BLUEBIRD_DUAL_1_WARM] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM)
+	},
+	[DVICO_BLUEBIRD_LGZ201_COLD] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD)
+	},
+	[DVICO_BLUEBIRD_LGZ201_WARM] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM)
+	},
+	[DVICO_BLUEBIRD_TH7579_COLD] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD)
+	},
+	[DVICO_BLUEBIRD_TH7579_WARM] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM)
+	},
+	[DIGITALNOW_BLUEBIRD_DUAL_1_COLD] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD)
+	},
+	[DIGITALNOW_BLUEBIRD_DUAL_1_WARM] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM)
+	},
+	[DVICO_BLUEBIRD_DUAL_2_COLD] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD)
+	},
+	[DVICO_BLUEBIRD_DUAL_2_WARM] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM)
+	},
+	[DVICO_BLUEBIRD_DUAL_4] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4)
+	},
+	[DVICO_BLUEBIRD_DVB_T_NANO_2] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2)
+	},
+	[DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM)
+	},
+	[AVERMEDIA_VOLAR_A868R] = {
+		USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R)
+	},
+	[DVICO_BLUEBIRD_DUAL_4_REV_2] = {
+		USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2)
+	},
+	[CONEXANT_D680_DMB] = {
+		USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB)
+	},
+	[MYGICA_D689] = {
+		USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689)
+	},
+	[MYGICA_T230] = {
+		USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230)
+	},
 	{}		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -1581,7 +1648,7 @@
 	.devices = {
 		{   "Medion MD95700 (MDUSBTV-HYBRID)",
 			{ NULL },
-			{ &cxusb_table[0], NULL },
+			{ &cxusb_table[MEDION_MD95700], NULL },
 		},
 	}
 };
@@ -1637,8 +1704,8 @@
 	.num_device_descs = 1,
 	.devices = {
 		{   "DViCO FusionHDTV5 USB Gold",
-			{ &cxusb_table[1], NULL },
-			{ &cxusb_table[2], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_LG064F_COLD], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_LG064F_WARM], NULL },
 		},
 	}
 };
@@ -1693,16 +1760,16 @@
 	.num_device_descs = 3,
 	.devices = {
 		{   "DViCO FusionHDTV DVB-T Dual USB",
-			{ &cxusb_table[3], NULL },
-			{ &cxusb_table[4], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_DUAL_1_COLD], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_DUAL_1_WARM], NULL },
 		},
 		{   "DigitalNow DVB-T Dual USB",
-			{ &cxusb_table[9],  NULL },
-			{ &cxusb_table[10], NULL },
+			{ &cxusb_table[DIGITALNOW_BLUEBIRD_DUAL_1_COLD],  NULL },
+			{ &cxusb_table[DIGITALNOW_BLUEBIRD_DUAL_1_WARM], NULL },
 		},
 		{   "DViCO FusionHDTV DVB-T Dual Digital 2",
-			{ &cxusb_table[11], NULL },
-			{ &cxusb_table[12], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_DUAL_2_COLD], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_DUAL_2_WARM], NULL },
 		},
 	}
 };
@@ -1756,8 +1823,8 @@
 	.num_device_descs = 1,
 	.devices = {
 		{   "DViCO FusionHDTV DVB-T USB (LGZ201)",
-			{ &cxusb_table[5], NULL },
-			{ &cxusb_table[6], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_LGZ201_COLD], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_LGZ201_WARM], NULL },
 		},
 	}
 };
@@ -1812,8 +1879,8 @@
 	.num_device_descs = 1,
 	.devices = {
 		{   "DViCO FusionHDTV DVB-T USB (TH7579)",
-			{ &cxusb_table[7], NULL },
-			{ &cxusb_table[8], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_TH7579_COLD], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_TH7579_WARM], NULL },
 		},
 	}
 };
@@ -1865,7 +1932,7 @@
 	.devices = {
 		{   "DViCO FusionHDTV DVB-T Dual Digital 4",
 			{ NULL },
-			{ &cxusb_table[13], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_DUAL_4], NULL },
 		},
 	}
 };
@@ -1918,7 +1985,7 @@
 	.devices = {
 		{   "DViCO FusionHDTV DVB-T NANO2",
 			{ NULL },
-			{ &cxusb_table[14], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2], NULL },
 		},
 	}
 };
@@ -1972,8 +2039,8 @@
 	.num_device_descs = 1,
 	.devices = {
 		{   "DViCO FusionHDTV DVB-T NANO2 w/o firmware",
-			{ &cxusb_table[14], NULL },
-			{ &cxusb_table[15], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM], NULL },
 		},
 	}
 };
@@ -2017,7 +2084,7 @@
 	.devices = {
 		{   "AVerMedia AVerTVHD Volar (A868R)",
 			{ NULL },
-			{ &cxusb_table[16], NULL },
+			{ &cxusb_table[AVERMEDIA_VOLAR_A868R], NULL },
 		},
 	}
 };
@@ -2071,7 +2138,7 @@
 	.devices = {
 		{   "DViCO FusionHDTV DVB-T Dual Digital 4 (rev 2)",
 			{ NULL },
-			{ &cxusb_table[17], NULL },
+			{ &cxusb_table[DVICO_BLUEBIRD_DUAL_4_REV_2], NULL },
 		},
 	}
 };
@@ -2125,7 +2192,7 @@
 		{
 			"Conexant DMB-TH Stick",
 			{ NULL },
-			{ &cxusb_table[18], NULL },
+			{ &cxusb_table[CONEXANT_D680_DMB], NULL },
 		},
 	}
 };
@@ -2179,7 +2246,7 @@
 		{
 			"Mygica D689 DMB-TH",
 			{ NULL },
-			{ &cxusb_table[19], NULL },
+			{ &cxusb_table[MYGICA_D689], NULL },
 		},
 	}
 };
@@ -2232,7 +2299,7 @@
 		{
 			"Mygica T230 DVB-T/T2/C",
 			{ NULL },
-			{ &cxusb_table[22], NULL },
+			{ &cxusb_table[MYGICA_T230], NULL },
 		},
 	}
 };
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
index 50856db..2b40393 100644
--- a/drivers/media/usb/dvb-usb/dib0700_core.c
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -651,9 +651,6 @@
 	return ret;
 }
 
-/* Number of keypresses to ignore before start repeating */
-#define RC_REPEAT_DELAY_V1_20 10
-
 /* This is the structure of the RC response packet starting in firmware 1.20 */
 struct dib0700_rc_response {
 	u8 report_id;
diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c
index e1757b8..d7d55a2 100644
--- a/drivers/media/usb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/usb/dvb-usb/dib0700_devices.c
@@ -510,9 +510,6 @@
 
 static u8 rc_request[] = { REQUEST_POLL_RC, 0 };
 
-/* Number of keypresses to ignore before start repeating */
-#define RC_REPEAT_DELAY 6
-
 /*
  * This function is used only when firmware is < 1.20 version. Newer
  * firmwares use bulk mode, with functions implemented at dib0700_core,
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
index 719413b..4bf9443 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
@@ -84,14 +84,61 @@
 
 static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
 {
-	deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type);
-	return dvb_usb_ctrl_feed(dvbdmxfeed,1);
+	deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,
+	       dvbdmxfeed->type);
+	return dvb_usb_ctrl_feed(dvbdmxfeed, 1);
 }
 
 static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
 {
 	deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type);
-	return dvb_usb_ctrl_feed(dvbdmxfeed,0);
+	return dvb_usb_ctrl_feed(dvbdmxfeed, 0);
+}
+
+static void dvb_usb_media_device_register(struct dvb_usb_adapter *adap)
+{
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+	struct media_device *mdev;
+	struct dvb_usb_device *d = adap->dev;
+	struct usb_device *udev = d->udev;
+	int ret;
+
+	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+	if (!mdev)
+		return;
+
+	mdev->dev = &udev->dev;
+	strlcpy(mdev->model, d->desc->name, sizeof(mdev->model));
+	if (udev->serial)
+		strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
+	strcpy(mdev->bus_info, udev->devpath);
+	mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+	mdev->driver_version = LINUX_VERSION_CODE;
+
+	ret = media_device_register(mdev);
+	if (ret) {
+		dev_err(&d->udev->dev,
+			"Couldn't create a media device. Error: %d\n",
+			ret);
+		kfree(mdev);
+		return;
+	}
+	dvb_register_media_controller(&adap->dvb_adap, mdev);
+
+	dev_info(&d->udev->dev, "media controller created\n");
+#endif
+}
+
+static void dvb_usb_media_device_unregister(struct dvb_usb_adapter *adap)
+{
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+	if (!adap->dvb_adap.mdev)
+		return;
+
+	media_device_unregister(adap->dvb_adap.mdev);
+	kfree(adap->dvb_adap.mdev);
+	adap->dvb_adap.mdev = NULL;
+#endif
 }
 
 int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums)
@@ -107,9 +154,11 @@
 	}
 	adap->dvb_adap.priv = adap;
 
+	dvb_usb_media_device_register(adap);
+
 	if (adap->dev->props.read_mac_address) {
-		if (adap->dev->props.read_mac_address(adap->dev,adap->dvb_adap.proposed_mac) == 0)
-			info("MAC address: %pM",adap->dvb_adap.proposed_mac);
+		if (adap->dev->props.read_mac_address(adap->dev, adap->dvb_adap.proposed_mac) == 0)
+			info("MAC address: %pM", adap->dvb_adap.proposed_mac);
 		else
 			err("MAC address reading failed.");
 	}
@@ -128,7 +177,7 @@
 	adap->demux.stop_feed        = dvb_usb_stop_feed;
 	adap->demux.write_to_decoder = NULL;
 	if ((ret = dvb_dmx_init(&adap->demux)) < 0) {
-		err("dvb_dmx_init failed: error %d",ret);
+		err("dvb_dmx_init failed: error %d", ret);
 		goto err_dmx;
 	}
 
@@ -136,13 +185,13 @@
 	adap->dmxdev.demux           = &adap->demux.dmx;
 	adap->dmxdev.capabilities    = 0;
 	if ((ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap)) < 0) {
-		err("dvb_dmxdev_init failed: error %d",ret);
+		err("dvb_dmxdev_init failed: error %d", ret);
 		goto err_dmx_dev;
 	}
 
 	if ((ret = dvb_net_init(&adap->dvb_adap, &adap->dvb_net,
 						&adap->demux.dmx)) < 0) {
-		err("dvb_net_init failed: error %d",ret);
+		err("dvb_net_init failed: error %d", ret);
 		goto err_net_init;
 	}
 
@@ -154,6 +203,7 @@
 err_dmx_dev:
 	dvb_dmx_release(&adap->demux);
 err_dmx:
+	dvb_usb_media_device_unregister(adap);
 	dvb_unregister_adapter(&adap->dvb_adap);
 err:
 	return ret;
@@ -167,6 +217,7 @@
 		adap->demux.dmx.close(&adap->demux.dmx);
 		dvb_dmxdev_release(&adap->dmxdev);
 		dvb_dmx_release(&adap->demux);
+		dvb_usb_media_device_unregister(adap);
 		dvb_unregister_adapter(&adap->dvb_adap);
 		adap->state &= ~DVB_USB_ADAP_STATE_DVB;
 	}
@@ -268,6 +319,8 @@
 		adap->num_frontends_initialized++;
 	}
 
+	dvb_create_media_graph(&adap->dvb_adap);
+
 	return 0;
 }
 
diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c
index 1a3df10..f1f357f 100644
--- a/drivers/media/usb/dvb-usb/dw2102.c
+++ b/drivers/media/usb/dvb-usb/dw2102.c
@@ -2,7 +2,8 @@
  *	DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101,
  *	TeVii S600, S630, S650, S660, S480, S421, S632
  *	Prof 1100, 7500,
- *	Geniatech SU3000, T220 Cards
+ *	Geniatech SU3000, T220,
+ *	TechnoTrend S2-4600 Cards
  * Copyright (C) 2008-2012 Igor M. Liplianin (liplianin@me.by)
  *
  *	This program is free software; you can redistribute it and/or modify it
@@ -31,6 +32,8 @@
 #include "m88rs2000.h"
 #include "tda18271.h"
 #include "cxd2820r.h"
+#include "m88ds3103.h"
+#include "ts2020.h"
 
 /* Max transfer size done by I2C transfer functions */
 #define MAX_XFER_SIZE  64
@@ -112,11 +115,9 @@
 		"Please see linux/Documentation/dvb/ for more details " \
 		"on firmware-problems."
 
-struct su3000_state {
+struct dw2102_state {
 	u8 initialized;
-};
-
-struct s6x0_state {
+	struct i2c_client *i2c_client_tuner;
 	int (*old_set_voltage)(struct dvb_frontend *f, fe_sec_voltage_t v);
 };
 
@@ -887,7 +888,7 @@
 
 static int su3000_power_ctrl(struct dvb_usb_device *d, int i)
 {
-	struct su3000_state *state = (struct su3000_state *)d->priv;
+	struct dw2102_state *state = (struct dw2102_state *)d->priv;
 	u8 obuf[] = {0xde, 0};
 
 	info("%s: %d, initialized %d\n", __func__, i, state->initialized);
@@ -973,7 +974,7 @@
 {
 	struct dvb_usb_adapter *d =
 		(struct dvb_usb_adapter *)(fe->dvb->priv);
-	struct s6x0_state *st = (struct s6x0_state *)d->dev->priv;
+	struct dw2102_state *st = (struct dw2102_state *)d->dev->priv;
 
 	dw210x_set_voltage(fe, voltage);
 	if (st->old_set_voltage)
@@ -1117,6 +1118,22 @@
 	.gate = TDA18271_GATE_DIGITAL,
 };
 
+static const struct m88ds3103_config tt_s2_4600_m88ds3103_config = {
+	.i2c_addr = 0x68,
+	.clock = 27000000,
+	.i2c_wr_max = 33,
+	.ts_mode = M88DS3103_TS_CI,
+	.ts_clk = 16000,
+	.ts_clk_pol = 0,
+	.spec_inv = 0,
+	.agc_inv = 0,
+	.clock_out = M88DS3103_CLOCK_OUT_ENABLED,
+	.envelope_mode = 0,
+	.agc = 0x99,
+	.lnb_hv_pol = 1,
+	.lnb_en_pol = 0,
+};
+
 static u8 m88rs2000_inittab[] = {
 	DEMOD_WRITE, 0x9a, 0x30,
 	DEMOD_WRITE, 0x00, 0x01,
@@ -1295,7 +1312,7 @@
 
 static int ds3000_frontend_attach(struct dvb_usb_adapter *d)
 {
-	struct s6x0_state *st = (struct s6x0_state *)d->dev->priv;
+	struct dw2102_state *st = d->dev->priv;
 	u8 obuf[] = {7, 1};
 
 	d->fe_adap[0].fe = dvb_attach(ds3000_attach, &s660_ds3000_config,
@@ -1461,6 +1478,84 @@
 	return -EIO;
 }
 
+static int tt_s2_4600_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	struct dvb_usb_device *d = adap->dev;
+	struct dw2102_state *state = d->priv;
+	u8 obuf[3] = { 0xe, 0x80, 0 };
+	u8 ibuf[] = { 0 };
+	struct i2c_adapter *i2c_adapter;
+	struct i2c_client *client;
+	struct i2c_board_info info;
+	struct ts2020_config ts2020_config = {};
+
+	if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x0e transfer failed.");
+
+	obuf[0] = 0xe;
+	obuf[1] = 0x02;
+	obuf[2] = 1;
+
+	if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x0e transfer failed.");
+	msleep(300);
+
+	obuf[0] = 0xe;
+	obuf[1] = 0x83;
+	obuf[2] = 0;
+
+	if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x0e transfer failed.");
+
+	obuf[0] = 0xe;
+	obuf[1] = 0x83;
+	obuf[2] = 1;
+
+	if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x0e transfer failed.");
+
+	obuf[0] = 0x51;
+
+	if (dvb_usb_generic_rw(d, obuf, 1, ibuf, 1, 0) < 0)
+		err("command 0x51 transfer failed.");
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+
+	adap->fe_adap[0].fe = dvb_attach(m88ds3103_attach,
+					&tt_s2_4600_m88ds3103_config,
+					&d->i2c_adap,
+					&i2c_adapter);
+	if (adap->fe_adap[0].fe == NULL)
+		return -ENODEV;
+
+	/* attach tuner */
+	ts2020_config.fe = adap->fe_adap[0].fe;
+	strlcpy(info.type, "ts2022", I2C_NAME_SIZE);
+	info.addr = 0x60;
+	info.platform_data = &ts2020_config;
+	request_module("ts2020");
+	client = i2c_new_device(i2c_adapter, &info);
+
+	if (client == NULL || client->dev.driver == NULL) {
+		dvb_frontend_detach(adap->fe_adap[0].fe);
+		return -ENODEV;
+	}
+
+	if (!try_module_get(client->dev.driver->owner)) {
+		i2c_unregister_device(client);
+		dvb_frontend_detach(adap->fe_adap[0].fe);
+		return -ENODEV;
+	}
+
+	/* delegate signal strength measurement to tuner */
+	adap->fe_adap[0].fe->ops.read_signal_strength =
+			adap->fe_adap[0].fe->ops.tuner_ops.get_rf_strength;
+
+	state->i2c_client_tuner = client;
+
+	return 0;
+}
+
 static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
 {
 	dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60,
@@ -1561,6 +1656,7 @@
 	TERRATEC_CINERGY_S2_R2,
 	GOTVIEW_SAT_HD,
 	GENIATECH_T220,
+	TECHNOTREND_S2_4600,
 };
 
 static struct usb_device_id dw2102_table[] = {
@@ -1584,6 +1680,8 @@
 	[TERRATEC_CINERGY_S2_R2] = {USB_DEVICE(USB_VID_TERRATEC, 0x00b0)},
 	[GOTVIEW_SAT_HD] = {USB_DEVICE(0x1FE1, USB_PID_GOTVIEW_SAT_HD)},
 	[GENIATECH_T220] = {USB_DEVICE(0x1f4d, 0xD220)},
+	[TECHNOTREND_S2_4600] = {USB_DEVICE(USB_VID_TECHNOTREND,
+		USB_PID_TECHNOTREND_CONNECT_S2_4600)},
 	{ }
 };
 
@@ -1857,7 +1955,7 @@
 static struct dvb_usb_device_properties s6x0_properties = {
 	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
 	.usb_ctrl = DEVICE_SPECIFIC,
-	.size_of_priv = sizeof(struct s6x0_state),
+	.size_of_priv = sizeof(struct dw2102_state),
 	.firmware = S630_FIRMWARE,
 	.no_reconnect = 1,
 
@@ -1950,7 +2048,7 @@
 static struct dvb_usb_device_properties su3000_properties = {
 	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
 	.usb_ctrl = DEVICE_SPECIFIC,
-	.size_of_priv = sizeof(struct su3000_state),
+	.size_of_priv = sizeof(struct dw2102_state),
 	.power_ctrl = su3000_power_ctrl,
 	.num_adapters = 1,
 	.identify_state	= su3000_identify_state,
@@ -2015,7 +2113,7 @@
 static struct dvb_usb_device_properties t220_properties = {
 	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
 	.usb_ctrl = DEVICE_SPECIFIC,
-	.size_of_priv = sizeof(struct su3000_state),
+	.size_of_priv = sizeof(struct dw2102_state),
 	.power_ctrl = su3000_power_ctrl,
 	.num_adapters = 1,
 	.identify_state	= su3000_identify_state,
@@ -2061,6 +2159,55 @@
 	}
 };
 
+static struct dvb_usb_device_properties tt_s2_4600_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+	.usb_ctrl = DEVICE_SPECIFIC,
+	.size_of_priv = sizeof(struct dw2102_state),
+	.power_ctrl = su3000_power_ctrl,
+	.num_adapters = 1,
+	.identify_state	= su3000_identify_state,
+	.i2c_algo = &su3000_i2c_algo,
+
+	.rc.core = {
+		.rc_interval = 250,
+		.rc_codes = RC_MAP_TT_1500,
+		.module_name = "dw2102",
+		.allowed_protos   = RC_BIT_RC5,
+		.rc_query = su3000_rc_query,
+	},
+
+	.read_mac_address = su3000_read_mac_address,
+
+	.generic_bulk_ctrl_endpoint = 0x01,
+
+	.adapter = {
+		{
+		.num_frontends = 1,
+		.fe = {{
+			.streaming_ctrl   = su3000_streaming_ctrl,
+			.frontend_attach  = tt_s2_4600_frontend_attach,
+			.stream = {
+				.type = USB_BULK,
+				.count = 8,
+				.endpoint = 0x82,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			}
+		} },
+		}
+	},
+	.num_device_descs = 1,
+	.devices = {
+		{ "TechnoTrend TT-connect S2-4600",
+			{ &dw2102_table[TECHNOTREND_S2_4600], NULL },
+			{ NULL },
+		},
+	}
+};
+
 static int dw2102_probe(struct usb_interface *intf,
 		const struct usb_device_id *id)
 {
@@ -2135,16 +2282,34 @@
 	    0 == dvb_usb_device_init(intf, &su3000_properties,
 			 THIS_MODULE, NULL, adapter_nr) ||
 	    0 == dvb_usb_device_init(intf, &t220_properties,
+			 THIS_MODULE, NULL, adapter_nr) ||
+	    0 == dvb_usb_device_init(intf, &tt_s2_4600_properties,
 			 THIS_MODULE, NULL, adapter_nr))
 		return 0;
 
 	return -ENODEV;
 }
 
+static void dw2102_disconnect(struct usb_interface *intf)
+{
+	struct dvb_usb_device *d = usb_get_intfdata(intf);
+	struct dw2102_state *st = (struct dw2102_state *)d->priv;
+	struct i2c_client *client;
+
+	/* remove I2C client for tuner */
+	client = st->i2c_client_tuner;
+	if (client) {
+		module_put(client->dev.driver->owner);
+		i2c_unregister_device(client);
+	}
+
+	dvb_usb_device_exit(intf);
+}
+
 static struct usb_driver dw2102_driver = {
 	.name = "dw2102",
 	.probe = dw2102_probe,
-	.disconnect = dvb_usb_device_exit,
+	.disconnect = dw2102_disconnect,
 	.id_table = dw2102_table,
 };
 
@@ -2155,7 +2320,8 @@
 			" DVB-C 3101 USB2.0,"
 			" TeVii S600, S630, S650, S660, S480, S421, S632"
 			" Prof 1100, 7500 USB2.0,"
-			" Geniatech SU3000, T220 devices");
+			" Geniatech SU3000, T220,"
+			" TechnoTrend S2-4600 devices");
 MODULE_VERSION("0.1");
 MODULE_LICENSE("GPL");
 MODULE_FIRMWARE(DW2101_FIRMWARE);
diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c
index abf8ab2..eafc5c8 100644
--- a/drivers/media/usb/dvb-usb/m920x.c
+++ b/drivers/media/usb/dvb-usb/m920x.c
@@ -1269,8 +1269,3 @@
 MODULE_DESCRIPTION("DVB Driver for ULI M920x");
 MODULE_VERSION("0.1");
 MODULE_LICENSE("GPL");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig
index 82fbf57..121fbfc 100644
--- a/drivers/media/usb/em28xx/Kconfig
+++ b/drivers/media/usb/em28xx/Kconfig
@@ -60,7 +60,7 @@
 	select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
-	select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_DRX39XYJ if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c
index 44ae1e0..49a5f95 100644
--- a/drivers/media/usb/em28xx/em28xx-audio.c
+++ b/drivers/media/usb/em28xx/em28xx-audio.c
@@ -820,7 +820,7 @@
 	if (urb_size > ep_size * npackets)
 		npackets = DIV_ROUND_UP(urb_size, ep_size);
 
-	em28xx_info("Number of URBs: %d, with %d packets and %d size",
+	em28xx_info("Number of URBs: %d, with %d packets and %d size\n",
 		    num_urb, npackets, urb_size);
 
 	/* Estimate the bytes per period */
@@ -981,7 +981,7 @@
 		return 0;
 	}
 
-	em28xx_info("Closing audio extension");
+	em28xx_info("Closing audio extension\n");
 
 	if (dev->adev.sndcard) {
 		snd_card_disconnect(dev->adev.sndcard);
@@ -1005,7 +1005,7 @@
 	if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR)
 		return 0;
 
-	em28xx_info("Suspending audio extension");
+	em28xx_info("Suspending audio extension\n");
 	em28xx_deinit_isoc_audio(dev);
 	atomic_set(&dev->adev.stream_started, 0);
 	return 0;
@@ -1019,7 +1019,7 @@
 	if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR)
 		return 0;
 
-	em28xx_info("Resuming audio extension");
+	em28xx_info("Resuming audio extension\n");
 	/* Nothing to do other than schedule_work() ?? */
 	schedule_work(&dev->adev.wq_trigger);
 	return 0;
diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c
index 7be661f..a4b22c2 100644
--- a/drivers/media/usb/em28xx/em28xx-camera.c
+++ b/drivers/media/usb/em28xx/em28xx-camera.c
@@ -330,7 +330,7 @@
 
 	v4l2_clk_name_i2c(clk_name, sizeof(clk_name),
 			  i2c_adapter_id(adap), client->addr);
-	v4l2->clk = v4l2_clk_register_fixed(clk_name, "mclk", -EINVAL);
+	v4l2->clk = v4l2_clk_register_fixed(clk_name, -EINVAL);
 	if (IS_ERR(v4l2->clk))
 		return PTR_ERR(v4l2->clk);
 
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index d9704e6..3940046 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -1157,6 +1157,15 @@
 		.i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE |
 				EM28XX_I2C_FREQ_400_KHZ,
 	},
+	[EM2884_BOARD_ELGATO_EYETV_HYBRID_2008] = {
+		.name         = "Elgato EyeTV Hybrid 2008 INT",
+		.has_dvb      = 1,
+		.ir_codes     = RC_MAP_NEC_TERRATEC_CINERGY_XS,
+		.tuner_type   = TUNER_ABSENT,
+		.def_i2c_bus  = 1,
+		.i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE |
+				EM28XX_I2C_FREQ_400_KHZ,
+	},
 	[EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
 		.name         = "Hauppauge WinTV HVR 900",
 		.tda9887_conf = TDA9887_PRESENT,
@@ -2378,8 +2387,10 @@
 			.driver_info = EM2860_BOARD_TERRATEC_GRABBY },
 	{ USB_DEVICE(0x0ccd, 0x00b2),
 			.driver_info = EM2884_BOARD_CINERGY_HTC_STICK },
+	{ USB_DEVICE(0x0fd9, 0x0018),
+			.driver_info = EM2884_BOARD_ELGATO_EYETV_HYBRID_2008 },
 	{ USB_DEVICE(0x0fd9, 0x0033),
-			.driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE},
+			.driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE },
 	{ USB_DEVICE(0x185b, 0x2870),
 			.driver_info = EM2870_BOARD_COMPRO_VIDEOMATE },
 	{ USB_DEVICE(0x185b, 0x2041),
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index 86461a7..3745607 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -1125,7 +1125,7 @@
 {
 	const struct em28xx_ops *ops = NULL;
 
-	em28xx_info("Suspending extensions");
+	em28xx_info("Suspending extensions\n");
 	mutex_lock(&em28xx_devlist_mutex);
 	list_for_each_entry(ops, &em28xx_extension_devlist, next) {
 		if (ops->suspend)
@@ -1139,7 +1139,7 @@
 {
 	const struct em28xx_ops *ops = NULL;
 
-	em28xx_info("Resuming extensions");
+	em28xx_info("Resuming extensions\n");
 	mutex_lock(&em28xx_devlist_mutex);
 	list_for_each_entry(ops, &em28xx_extension_devlist, next) {
 		if (ops->resume)
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 9877b69..a5b22c5 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -54,7 +54,7 @@
 #include "qt1010.h"
 #include "mb86a20s.h"
 #include "m88ds3103.h"
-#include "m88ts2022.h"
+#include "ts2020.h"
 #include "si2168.h"
 #include "si2157.h"
 
@@ -1380,6 +1380,7 @@
 			}
 		}
 		break;
+	case EM2884_BOARD_ELGATO_EYETV_HYBRID_2008:
 	case EM2884_BOARD_CINERGY_HTC_STICK:
 		terratec_htc_stick_init(dev);
 
@@ -1491,8 +1492,7 @@
 			struct i2c_adapter *i2c_adapter;
 			struct i2c_client *client;
 			struct i2c_board_info info;
-			struct m88ts2022_config m88ts2022_config = {
-				.clock = 27000000,
+			struct ts2020_config ts2020_config = {
 			};
 			memset(&info, 0, sizeof(struct i2c_board_info));
 
@@ -1507,11 +1507,11 @@
 			}
 
 			/* attach tuner */
-			m88ts2022_config.fe = dvb->fe[0];
-			strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+			ts2020_config.fe = dvb->fe[0];
+			strlcpy(info.type, "ts2022", I2C_NAME_SIZE);
 			info.addr = 0x60;
-			info.platform_data = &m88ts2022_config;
-			request_module("m88ts2022");
+			info.platform_data = &ts2020_config;
+			request_module("ts2020");
 			client = i2c_new_device(i2c_adapter, &info);
 			if (client == NULL || client->dev.driver == NULL) {
 				dvb_frontend_detach(dvb->fe[0]);
@@ -1724,7 +1724,7 @@
 	if (!dev->dvb)
 		return 0;
 
-	em28xx_info("Closing DVB extension");
+	em28xx_info("Closing DVB extension\n");
 
 	dvb = dev->dvb;
 	client = dvb->i2c_client_tuner;
@@ -1775,17 +1775,17 @@
 	if (!dev->board.has_dvb)
 		return 0;
 
-	em28xx_info("Suspending DVB extension");
+	em28xx_info("Suspending DVB extension\n");
 	if (dev->dvb) {
 		struct em28xx_dvb *dvb = dev->dvb;
 
 		if (dvb->fe[0]) {
 			ret = dvb_frontend_suspend(dvb->fe[0]);
-			em28xx_info("fe0 suspend %d", ret);
+			em28xx_info("fe0 suspend %d\n", ret);
 		}
 		if (dvb->fe[1]) {
 			dvb_frontend_suspend(dvb->fe[1]);
-			em28xx_info("fe1 suspend %d", ret);
+			em28xx_info("fe1 suspend %d\n", ret);
 		}
 	}
 
@@ -1802,18 +1802,18 @@
 	if (!dev->board.has_dvb)
 		return 0;
 
-	em28xx_info("Resuming DVB extension");
+	em28xx_info("Resuming DVB extension\n");
 	if (dev->dvb) {
 		struct em28xx_dvb *dvb = dev->dvb;
 
 		if (dvb->fe[0]) {
 			ret = dvb_frontend_resume(dvb->fe[0]);
-			em28xx_info("fe0 resume %d", ret);
+			em28xx_info("fe0 resume %d\n", ret);
 		}
 
 		if (dvb->fe[1]) {
 			ret = dvb_frontend_resume(dvb->fe[1]);
-			em28xx_info("fe1 resume %d", ret);
+			em28xx_info("fe1 resume %d\n", ret);
 		}
 	}
 
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
index d8dc03a..4007356 100644
--- a/drivers/media/usb/em28xx/em28xx-input.c
+++ b/drivers/media/usb/em28xx/em28xx-input.c
@@ -654,8 +654,6 @@
 	if (dev->num_button_polling_addresses) {
 		memset(dev->button_polling_last_values, 0,
 		       EM28XX_NUM_BUTTON_ADDRESSES_MAX);
-		INIT_DELAYED_WORK(&dev->buttons_query_work,
-				  em28xx_query_buttons);
 		schedule_delayed_work(&dev->buttons_query_work,
 				      msecs_to_jiffies(dev->button_polling_interval));
 	}
@@ -689,6 +687,7 @@
 	}
 
 	kref_get(&dev->ref);
+	INIT_DELAYED_WORK(&dev->buttons_query_work, em28xx_query_buttons);
 
 	if (dev->board.buttons)
 		em28xx_init_buttons(dev);
@@ -833,7 +832,7 @@
 		return 0;
 	}
 
-	em28xx_info("Closing input extension");
+	em28xx_info("Closing input extension\n");
 
 	em28xx_shutdown_buttons(dev);
 
@@ -862,7 +861,7 @@
 	if (dev->is_audio_only)
 		return 0;
 
-	em28xx_info("Suspending input extension");
+	em28xx_info("Suspending input extension\n");
 	if (ir)
 		cancel_delayed_work_sync(&ir->work);
 	cancel_delayed_work_sync(&dev->buttons_query_work);
@@ -879,7 +878,7 @@
 	if (dev->is_audio_only)
 		return 0;
 
-	em28xx_info("Resuming input extension");
+	em28xx_info("Resuming input extension\n");
 	/* if suspend calls ir_raw_event_unregister(), the should call
 	   ir_raw_event_register() */
 	if (ir)
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index bf1be43..4c6fa67 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -1472,7 +1472,7 @@
 	    (EM28XX_VMUX_CABLE == INPUT(n)->type))
 		i->type = V4L2_INPUT_TYPE_TUNER;
 
-	i->std = dev->v4l2->vdev->tvnorms;
+	i->std = dev->v4l2->vdev.tvnorms;
 	/* webcams do not have the STD API */
 	if (dev->board.is_webcam)
 		i->capabilities = 0;
@@ -1730,9 +1730,9 @@
 
 	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS |
 		V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
-	if (v4l2->vbi_dev)
+	if (video_is_registered(&v4l2->vbi_dev))
 		cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
-	if (v4l2->radio_dev)
+	if (video_is_registered(&v4l2->radio_dev))
 		cap->capabilities |= V4L2_CAP_RADIO;
 	return 0;
 }
@@ -1958,7 +1958,7 @@
 	if (v4l2 == NULL)
 		return 0;
 
-	em28xx_info("Closing video extension");
+	em28xx_info("Closing video extension\n");
 
 	mutex_lock(&dev->lock);
 
@@ -1966,20 +1966,20 @@
 
 	em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE);
 
-	if (v4l2->radio_dev) {
+	if (video_is_registered(&v4l2->radio_dev)) {
 		em28xx_info("V4L2 device %s deregistered\n",
-			    video_device_node_name(v4l2->radio_dev));
-		video_unregister_device(v4l2->radio_dev);
+			    video_device_node_name(&v4l2->radio_dev));
+		video_unregister_device(&v4l2->radio_dev);
 	}
-	if (v4l2->vbi_dev) {
+	if (video_is_registered(&v4l2->vbi_dev)) {
 		em28xx_info("V4L2 device %s deregistered\n",
-			    video_device_node_name(v4l2->vbi_dev));
-		video_unregister_device(v4l2->vbi_dev);
+			    video_device_node_name(&v4l2->vbi_dev));
+		video_unregister_device(&v4l2->vbi_dev);
 	}
-	if (v4l2->vdev) {
+	if (video_is_registered(&v4l2->vdev)) {
 		em28xx_info("V4L2 device %s deregistered\n",
-			    video_device_node_name(v4l2->vdev));
-		video_unregister_device(v4l2->vdev);
+			    video_device_node_name(&v4l2->vdev));
+		video_unregister_device(&v4l2->vdev);
 	}
 
 	v4l2_ctrl_handler_free(&v4l2->ctrl_handler);
@@ -2007,7 +2007,7 @@
 	if (!dev->has_video)
 		return 0;
 
-	em28xx_info("Suspending video extension");
+	em28xx_info("Suspending video extension\n");
 	em28xx_stop_urbs(dev);
 	return 0;
 }
@@ -2020,7 +2020,7 @@
 	if (!dev->has_video)
 		return 0;
 
-	em28xx_info("Resuming video extension");
+	em28xx_info("Resuming video extension\n");
 	/* what do we do here */
 	return 0;
 }
@@ -2127,7 +2127,7 @@
 static const struct video_device em28xx_video_template = {
 	.fops		= &em28xx_v4l_fops,
 	.ioctl_ops	= &video_ioctl_ops,
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
 	.tvnorms	= V4L2_STD_ALL,
 };
 
@@ -2156,7 +2156,7 @@
 static struct video_device em28xx_radio_template = {
 	.fops		= &radio_fops,
 	.ioctl_ops	= &radio_ioctl_ops,
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
 };
 
 /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
@@ -2179,20 +2179,13 @@
 
 /******************************** usb interface ******************************/
 
-static struct video_device
-*em28xx_vdev_init(struct em28xx *dev,
-		  const struct video_device *template,
-		  const char *type_name)
+static void em28xx_vdev_init(struct em28xx *dev,
+			     struct video_device *vfd,
+			     const struct video_device *template,
+			     const char *type_name)
 {
-	struct video_device *vfd;
-
-	vfd = video_device_alloc();
-	if (NULL == vfd)
-		return NULL;
-
 	*vfd		= *template;
 	vfd->v4l2_dev	= &dev->v4l2->v4l2_dev;
-	vfd->debug	= video_debug;
 	vfd->lock	= &dev->lock;
 	if (dev->board.is_webcam)
 		vfd->tvnorms = 0;
@@ -2201,7 +2194,6 @@
 		 dev->name, type_name);
 
 	video_set_drvdata(vfd, dev);
-	return vfd;
 }
 
 static void em28xx_tuner_setup(struct em28xx *dev, unsigned short tuner_addr)
@@ -2492,38 +2484,33 @@
 		goto unregister_dev;
 
 	/* allocate and fill video video_device struct */
-	v4l2->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video");
-	if (!v4l2->vdev) {
-		em28xx_errdev("cannot allocate video_device.\n");
-		ret = -ENODEV;
-		goto unregister_dev;
-	}
+	em28xx_vdev_init(dev, &v4l2->vdev, &em28xx_video_template, "video");
 	mutex_init(&v4l2->vb_queue_lock);
 	mutex_init(&v4l2->vb_vbi_queue_lock);
-	v4l2->vdev->queue = &v4l2->vb_vidq;
-	v4l2->vdev->queue->lock = &v4l2->vb_queue_lock;
+	v4l2->vdev.queue = &v4l2->vb_vidq;
+	v4l2->vdev.queue->lock = &v4l2->vb_queue_lock;
 
 	/* disable inapplicable ioctls */
 	if (dev->board.is_webcam) {
-		v4l2_disable_ioctl(v4l2->vdev, VIDIOC_QUERYSTD);
-		v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_STD);
-		v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_STD);
+		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_QUERYSTD);
+		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_STD);
+		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_STD);
 	} else {
-		v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_PARM);
+		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_PARM);
 	}
 	if (dev->tuner_type == TUNER_ABSENT) {
-		v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_TUNER);
-		v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_TUNER);
-		v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_FREQUENCY);
-		v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_FREQUENCY);
+		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_TUNER);
+		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_TUNER);
+		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_FREQUENCY);
+		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_FREQUENCY);
 	}
 	if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) {
-		v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_AUDIO);
-		v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_AUDIO);
+		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_AUDIO);
+		v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_AUDIO);
 	}
 
 	/* register v4l2 video video_device */
-	ret = video_register_device(v4l2->vdev, VFL_TYPE_GRABBER,
+	ret = video_register_device(&v4l2->vdev, VFL_TYPE_GRABBER,
 				    video_nr[dev->devno]);
 	if (ret) {
 		em28xx_errdev("unable to register video device (error=%i).\n",
@@ -2533,27 +2520,27 @@
 
 	/* Allocate and fill vbi video_device struct */
 	if (em28xx_vbi_supported(dev) == 1) {
-		v4l2->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
-						"vbi");
+		em28xx_vdev_init(dev, &v4l2->vbi_dev, &em28xx_video_template,
+				"vbi");
 
-		v4l2->vbi_dev->queue = &v4l2->vb_vbiq;
-		v4l2->vbi_dev->queue->lock = &v4l2->vb_vbi_queue_lock;
+		v4l2->vbi_dev.queue = &v4l2->vb_vbiq;
+		v4l2->vbi_dev.queue->lock = &v4l2->vb_vbi_queue_lock;
 
 		/* disable inapplicable ioctls */
-		v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_PARM);
+		v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_PARM);
 		if (dev->tuner_type == TUNER_ABSENT) {
-			v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_TUNER);
-			v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_TUNER);
-			v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_FREQUENCY);
-			v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_FREQUENCY);
+			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_TUNER);
+			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_TUNER);
+			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_FREQUENCY);
+			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_FREQUENCY);
 		}
 		if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) {
-			v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_AUDIO);
-			v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_AUDIO);
+			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_AUDIO);
+			v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_AUDIO);
 		}
 
 		/* register v4l2 vbi video_device */
-		ret = video_register_device(v4l2->vbi_dev, VFL_TYPE_VBI,
+		ret = video_register_device(&v4l2->vbi_dev, VFL_TYPE_VBI,
 					    vbi_nr[dev->devno]);
 		if (ret < 0) {
 			em28xx_errdev("unable to register vbi device\n");
@@ -2562,29 +2549,24 @@
 	}
 
 	if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
-		v4l2->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
-						   "radio");
-		if (!v4l2->radio_dev) {
-			em28xx_errdev("cannot allocate video_device.\n");
-			ret = -ENODEV;
-			goto unregister_dev;
-		}
-		ret = video_register_device(v4l2->radio_dev, VFL_TYPE_RADIO,
+		em28xx_vdev_init(dev, &v4l2->radio_dev, &em28xx_radio_template,
+				   "radio");
+		ret = video_register_device(&v4l2->radio_dev, VFL_TYPE_RADIO,
 					    radio_nr[dev->devno]);
 		if (ret < 0) {
 			em28xx_errdev("can't register radio device\n");
 			goto unregister_dev;
 		}
 		em28xx_info("Registered radio device as %s\n",
-			    video_device_node_name(v4l2->radio_dev));
+			    video_device_node_name(&v4l2->radio_dev));
 	}
 
 	em28xx_info("V4L2 video device registered as %s\n",
-		    video_device_node_name(v4l2->vdev));
+		    video_device_node_name(&v4l2->vdev));
 
-	if (v4l2->vbi_dev)
+	if (video_is_registered(&v4l2->vbi_dev))
 		em28xx_info("V4L2 VBI device registered as %s\n",
-			    video_device_node_name(v4l2->vbi_dev));
+			    video_device_node_name(&v4l2->vbi_dev));
 
 	/* Save some power by putting tuner to sleep */
 	v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_power, 0);
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index 9c70753..e6559c6 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -143,6 +143,7 @@
 #define EM28178_BOARD_PCTV_292E                   94
 #define EM2861_BOARD_LEADTEK_VC100                95
 #define EM28178_BOARD_TERRATEC_T2_STICK_HD        96
+#define EM2884_BOARD_ELGATO_EYETV_HYBRID_2008     97
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
@@ -512,9 +513,9 @@
 	struct v4l2_ctrl_handler ctrl_handler;
 	struct v4l2_clk *clk;
 
-	struct video_device *vdev;
-	struct video_device *vbi_dev;
-	struct video_device *radio_dev;
+	struct video_device vdev;
+	struct video_device vbi_dev;
+	struct video_device radio_dev;
 
 	/* Videobuf2 */
 	struct vb2_queue vb_vidq;
diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig
index 918af60..934026e 100644
--- a/drivers/media/usb/gspca/Kconfig
+++ b/drivers/media/usb/gspca/Kconfig
@@ -2,6 +2,7 @@
 	tristate "GSPCA based webcams"
 	depends on m
 	depends on VIDEO_V4L2
+	depends on INPUT || INPUT=n
 	default m
 	---help---
 	  Say Y here if you want to enable selecting webcams based
@@ -437,6 +438,17 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called gspca_topro.
 
+config USB_GSPCA_TOUPTEK
+	tristate "Touptek USB Camera Driver"
+	depends on m
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the ToupTek UCMOS
+	  / AmScope MU series camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_touptek.
+
 config USB_GSPCA_TV8532
 	tristate "TV8532 USB Camera Driver"
 	depends on m
diff --git a/drivers/media/usb/gspca/Makefile b/drivers/media/usb/gspca/Makefile
index d199abf..d0e965c 100644
--- a/drivers/media/usb/gspca/Makefile
+++ b/drivers/media/usb/gspca/Makefile
@@ -39,6 +39,7 @@
 obj-$(CPTCFG_USB_GSPCA_STV0680)  += gspca_stv0680.o
 obj-$(CPTCFG_USB_GSPCA_T613)     += gspca_t613.o
 obj-$(CPTCFG_USB_GSPCA_TOPRO)    += gspca_topro.o
+obj-$(CPTCFG_USB_GSPCA_TOUPTEK)  += gspca_touptek.o
 obj-$(CPTCFG_USB_GSPCA_TV8532)   += gspca_tv8532.o
 obj-$(CPTCFG_USB_GSPCA_VC032X)   += gspca_vc032x.o
 obj-$(CPTCFG_USB_GSPCA_VICAM)    += gspca_vicam.o
@@ -86,6 +87,7 @@
 gspca_sunplus-objs  := sunplus.o
 gspca_t613-objs     := t613.o
 gspca_topro-objs    := topro.o
+gspca_touptek-objs  := touptek.o
 gspca_tv8532-objs   := tv8532.o
 gspca_vc032x-objs   := vc032x.o
 gspca_vicam-objs    := vicam.o
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
index 4e6ac0a..53c4063 100644
--- a/drivers/media/usb/gspca/gspca.c
+++ b/drivers/media/usb/gspca/gspca.c
@@ -1562,7 +1562,7 @@
 			struct v4l2_streamparm *parm)
 {
 	struct gspca_dev *gspca_dev = video_drvdata(filp);
-	int n;
+	unsigned int n;
 
 	n = parm->parm.capture.readbuffers;
 	if (n == 0 || n >= GSPCA_MAX_FRAMES)
diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c
index 90f0d63..146071b 100644
--- a/drivers/media/usb/gspca/ov534.c
+++ b/drivers/media/usb/gspca/ov534.c
@@ -12,6 +12,8 @@
  * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr
  * PS3 Eye camera - brightness, contrast, awb, agc, aec controls
  *                  added by Max Thrun <bear24rw@gmail.com>
+ * PS3 Eye camera - FPS range extended by Joseph Howse
+ *                  <josephhowse@nummist.com> http://nummist.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
@@ -116,7 +118,7 @@
 		.colorspace = V4L2_COLORSPACE_JPEG},
 };
 
-static const u8 qvga_rates[] = {125, 100, 75, 60, 50, 40, 30};
+static const u8 qvga_rates[] = {187, 150, 137, 125, 100, 75, 60, 50, 37, 30};
 static const u8 vga_rates[] = {60, 50, 40, 30, 15};
 
 static const struct framerates ov772x_framerates[] = {
@@ -769,12 +771,16 @@
 		{15, 0x03, 0x41, 0x04},
 	};
 	static const struct rate_s rate_1[] = {	/* 320x240 */
+/*		{205, 0x01, 0xc1, 0x02},  * 205 FPS: video is partly corrupt */
+		{187, 0x01, 0x81, 0x02}, /* 187 FPS or below: video is valid */
+		{150, 0x01, 0xc1, 0x04},
+		{137, 0x02, 0xc1, 0x02},
 		{125, 0x02, 0x81, 0x02},
 		{100, 0x02, 0xc1, 0x04},
 		{75, 0x03, 0xc1, 0x04},
 		{60, 0x04, 0xc1, 0x04},
 		{50, 0x02, 0x41, 0x04},
-		{40, 0x03, 0x41, 0x04},
+		{37, 0x03, 0x41, 0x04},
 		{30, 0x04, 0x41, 0x04},
 	};
 
@@ -810,21 +816,16 @@
 		s16 huesin;
 		s16 huecos;
 
-		/* fixp_sin and fixp_cos accept only positive values, while
-		 * our val is between -90 and 90
-		 */
-		val += 360;
-
 		/* According to the datasheet the registers expect HUESIN and
 		 * HUECOS to be the result of the trigonometric functions,
 		 * scaled by 0x80.
 		 *
-		 * The 0x100 here represents the maximun absolute value
+		 * The 0x7fff here represents the maximum absolute value
 		 * returned byt fixp_sin and fixp_cos, so the scaling will
 		 * consider the result like in the interval [-1.0, 1.0].
 		 */
-		huesin = fixp_sin(val) * 0x80 / 0x100;
-		huecos = fixp_cos(val) * 0x80 / 0x100;
+		huesin = fixp_sin16(val) * 0x80 / 0x7fff;
+		huecos = fixp_cos16(val) * 0x80 / 0x7fff;
 
 		if (huesin < 0) {
 			sccb_reg_write(gspca_dev, 0xab,
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx.c b/drivers/media/usb/gspca/stv06xx/stv06xx.c
index 49d209b..6ac93d8 100644
--- a/drivers/media/usb/gspca/stv06xx/stv06xx.c
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx.c
@@ -505,13 +505,13 @@
 {
 	int ret = -EINVAL;
 
-	if (len == 1 && data[0] == 0x80) {
+	if (len == 1 && (data[0] == 0x80 || data[0] == 0x10)) {
 		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
 		input_sync(gspca_dev->input_dev);
 		ret = 0;
 	}
 
-	if (len == 1 && data[0] == 0x88) {
+	if (len == 1 && (data[0] == 0x88 || data[0] == 0x11)) {
 		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
 		input_sync(gspca_dev->input_dev);
 		ret = 0;
diff --git a/drivers/media/usb/gspca/topro.c b/drivers/media/usb/gspca/topro.c
index 5fcd1ee..c70ff40 100644
--- a/drivers/media/usb/gspca/topro.c
+++ b/drivers/media/usb/gspca/topro.c
@@ -969,7 +969,9 @@
 {
 	int i, sc;
 
-	if (quality < 50)
+	if (quality <= 0)
+		sc = 5000;
+	else if (quality < 50)
 		sc = 5000 / quality;
 	else
 		sc = 200 - quality * 2;
diff --git a/drivers/media/usb/gspca/touptek.c b/drivers/media/usb/gspca/touptek.c
new file mode 100644
index 0000000..7bac6bc
--- /dev/null
+++ b/drivers/media/usb/gspca/touptek.c
@@ -0,0 +1,731 @@
+/*
+ * ToupTek UCMOS / AmScope MU series camera driver
+ * TODO: contrast with ScopeTek / AmScope MDC cameras
+ *
+ * Copyright (C) 2012-2014 John McMaster <JohnDMcMaster@gmail.com>
+ *
+ * Special thanks to Bushing for helping with the decrypt algorithm and
+ * Sean O'Sullivan / the Rensselaer Center for Open Source
+ * Software (RCOS) for helping me learn kernel development
+ *
+ * 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
+ * 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 "gspca.h"
+
+#define MODULE_NAME "touptek"
+
+MODULE_AUTHOR("John McMaster");
+MODULE_DESCRIPTION("ToupTek UCMOS / Amscope MU microscope camera driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Exposure reg is linear with exposure time
+ * Exposure (sec), E (reg)
+ * 0.000400, 0x0002
+ * 0.001000, 0x0005
+ * 0.005000, 0x0019
+ * 0.020000, 0x0064
+ * 0.080000, 0x0190
+ * 0.400000, 0x07D0
+ * 1.000000, 0x1388
+ * 2.000000, 0x2710
+ *
+ * Three gain stages
+ * 0x1000: master channel enable bit
+ * 0x007F: low gain bits
+ * 0x0080: medium gain bit
+ * 0x0100: high gain bit
+ * gain = enable * (1 + regH) * (1 + regM) * z * regL
+ *
+ * Gain implementation
+ * Want to do something similar to mt9v011.c's set_balance
+ *
+ * Gain does not vary with resolution (checked 640x480 vs 1600x1200)
+ *
+ * Constant derivation:
+ *
+ * Raw data:
+ * Gain,   GTOP,   B,	  R,	  GBOT
+ * 1.00,   0x105C, 0x1068, 0x10C8, 0x105C
+ * 1.20,   0x106E, 0x107E, 0x10D6, 0x106E
+ * 1.40,   0x10C0, 0x10CA, 0x10E5, 0x10C0
+ * 1.60,   0x10C9, 0x10D4, 0x10F3, 0x10C9
+ * 1.80,   0x10D2, 0x10DE, 0x11C1, 0x10D2
+ * 2.00,   0x10DC, 0x10E9, 0x11C8, 0x10DC
+ * 2.20,   0x10E5, 0x10F3, 0x11CF, 0x10E5
+ * 2.40,   0x10EE, 0x10FE, 0x11D7, 0x10EE
+ * 2.60,   0x10F7, 0x11C4, 0x11DE, 0x10F7
+ * 2.80,   0x11C0, 0x11CA, 0x11E5, 0x11C0
+ * 3.00,   0x11C5, 0x11CF, 0x11ED, 0x11C5
+ *
+ * zR = 0.0069605943152454778
+ *	about 3/431 = 0.0069605568445475635
+ * zB = 0.0095695970695970703
+ *	about 6/627 = 0.0095693779904306216
+ * zG = 0.010889328063241107
+ *	about 6/551 = 0.010889292196007259
+ * about 10 bits for constant + 7 bits for value => at least 17 bit
+ * intermediate with 32 bit ints should be fine for overflow etc
+ * Essentially gains are in range 0-0x001FF
+ *
+ * However, V4L expects a main gain channel + R and B balance
+ * To keep things simple for now saturate the values of balance is too high/low
+ * This isn't really ideal but easy way to fit the Linux model
+ *
+ * Converted using gain model turns out to be quite linear:
+ * Gain, GTOP, B, R, GBOT
+ * 1.00, 92, 104, 144, 92
+ * 1.20, 110, 126, 172, 110
+ * 1.40, 128, 148, 202, 128
+ * 1.60, 146, 168, 230, 146
+ * 1.80, 164, 188, 260, 164
+ * 2.00, 184, 210, 288, 184
+ * 2.20, 202, 230, 316, 202
+ * 2.40, 220, 252, 348, 220
+ * 2.60, 238, 272, 376, 238
+ * 2.80, 256, 296, 404, 256
+ * 3.00, 276, 316, 436, 276
+ *
+ * Maximum gain is 0x7FF * 2 * 2 => 0x1FFC (8188)
+ * or about 13 effective bits of gain
+ * The highest the commercial driver goes in my setup 436
+ * However, because could *maybe* damage circuits
+ * limit the gain until have a reason to go higher
+ * Solution: gain clipped and warning emitted
+ */
+#define GAIN_MAX		511
+
+/* Frame sync is a short read */
+#define BULK_SIZE		0x4000
+
+/* MT9E001 reg names to give a rough approximation */
+#define REG_COARSE_INTEGRATION_TIME_	0x3012
+#define REG_GROUPED_PARAMETER_HOLD_	0x3022
+#define REG_MODE_SELECT			0x0100
+#define REG_OP_SYS_CLK_DIV		0x030A
+#define REG_VT_SYS_CLK_DIV		0x0302
+#define REG_PRE_PLL_CLK_DIV		0x0304
+#define REG_VT_PIX_CLK_DIV		0x0300
+#define REG_OP_PIX_CLK_DIV		0x0308
+#define REG_PLL_MULTIPLIER		0x0306
+#define REG_COARSE_INTEGRATION_TIME_	0x3012
+#define REG_FRAME_LENGTH_LINES		0x0340
+#define REG_FRAME_LENGTH_LINES_		0x300A
+#define REG_GREEN1_GAIN			0x3056
+#define REG_GREEN2_GAIN			0x305C
+#define REG_GROUPED_PARAMETER_HOLD	0x0104
+#define REG_LINE_LENGTH_PCK_		0x300C
+#define REG_MODE_SELECT			0x0100
+#define REG_PLL_MULTIPLIER		0x0306
+#define REG_READ_MODE			0x3040
+#define REG_BLUE_GAIN			0x3058
+#define REG_RED_GAIN			0x305A
+#define REG_RESET_REGISTER		0x301A
+#define REG_SCALE_M			0x0404
+#define REG_SCALING_MODE		0x0400
+#define REG_SOFTWARE_RESET		0x0103
+#define REG_X_ADDR_END			0x0348
+#define REG_X_ADDR_START		0x0344
+#define REG_X_ADDR_START		0x0344
+#define REG_X_OUTPUT_SIZE		0x034C
+#define REG_Y_ADDR_END			0x034A
+#define REG_Y_ADDR_START		0x0346
+#define REG_Y_OUTPUT_SIZE		0x034E
+
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	/* How many bytes this frame */
+	unsigned int this_f;
+
+	/*
+	Device has separate gains for each Bayer quadrant
+	V4L supports master gain which is referenced to G1/G2 and supplies
+	individual balance controls for R/B
+	*/
+	struct v4l2_ctrl *blue;
+	struct v4l2_ctrl *red;
+};
+
+/* Used to simplify reg write error handling */
+struct cmd {
+	u16 value;
+	u16 index;
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+	{800, 600,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.bytesperline = 800,
+		.sizeimage = 800 * 600,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{1600, 1200,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.bytesperline = 1600,
+		.sizeimage = 1600 * 1200,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{3264, 2448,
+		V4L2_PIX_FMT_SGRBG8,
+		V4L2_FIELD_NONE,
+		.bytesperline = 3264,
+		.sizeimage = 3264 * 2448,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+/*
+ * As theres no known frame sync, the only way to keep synced is to try hard
+ * to never miss any packets
+ */
+#if MAX_NURBS < 4
+#error "Not enough URBs in the gspca table"
+#endif
+
+static int val_reply(struct gspca_dev *gspca_dev, const char *reply, int rc)
+{
+	if (rc < 0) {
+		PERR("reply has error %d", rc);
+		return -EIO;
+	}
+	if (rc != 1) {
+		PERR("Bad reply size %d", rc);
+		return -EIO;
+	}
+	if (reply[0] != 0x08) {
+		PERR("Bad reply 0x%02X", reply[0]);
+		return -EIO;
+	}
+	return 0;
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+	char buff[1];
+	int rc;
+
+	PDEBUG(D_USBO,
+		"reg_w bReq=0x0B, bReqT=0xC0, wVal=0x%04X, wInd=0x%04X\n",
+		value, index);
+	rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0),
+		0x0B, 0xC0, value, index, buff, 1, 500);
+	PDEBUG(D_USBO, "rc=%d, ret={0x%02X}", rc, buff[0]);
+	if (rc < 0) {
+		PERR("Failed reg_w(0x0B, 0xC0, 0x%04X, 0x%04X) w/ rc %d\n",
+			value, index, rc);
+		gspca_dev->usb_err = rc;
+		return;
+	}
+	if (val_reply(gspca_dev, buff, rc)) {
+		PERR("Bad reply to reg_w(0x0B, 0xC0, 0x%04X, 0x%04X\n",
+			value, index);
+		gspca_dev->usb_err = -EIO;
+	}
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+		const struct cmd *p, int l)
+{
+	do {
+		reg_w(gspca_dev, p->value, p->index);
+		p++;
+	} while (--l > 0);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+	u16 value;
+	unsigned int w = gspca_dev->pixfmt.width;
+
+	if (w == 800)
+		value = val * 5;
+	else if (w == 1600)
+		value = val * 3;
+	else if (w == 3264)
+		value = val * 3 / 2;
+	else {
+		PERR("Invalid width %u\n", w);
+		gspca_dev->usb_err = -EINVAL;
+		return;
+	}
+	PDEBUG(D_STREAM, "exposure: 0x%04X ms\n", value);
+	/* Wonder if theres a good reason for sending it twice */
+	/* probably not but leave it in because...why not */
+	reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_);
+	reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_);
+}
+
+static int gainify(int in)
+{
+	/*
+	 * TODO: check if there are any issues with corner cases
+	 * 0x000 (0):0x07F (127): regL
+	 * 0x080 (128) - 0x0FF (255): regM, regL
+	 * 0x100 (256) - max: regH, regM, regL
+	 */
+	if (in <= 0x7F)
+		return 0x1000 | in;
+	else if (in <= 0xFF)
+		return 0x1080 | in / 2;
+	else
+		return 0x1180 | in / 4;
+}
+
+static void setggain(struct gspca_dev *gspca_dev, u16 global_gain)
+{
+	u16 normalized;
+
+	normalized = gainify(global_gain);
+	PDEBUG(D_STREAM, "gain G1/G2 (0x%04X): 0x%04X (src 0x%04X)\n",
+		 REG_GREEN1_GAIN,
+		 normalized, global_gain);
+
+	reg_w(gspca_dev, normalized, REG_GREEN1_GAIN);
+	reg_w(gspca_dev, normalized, REG_GREEN2_GAIN);
+}
+
+static void setbgain(struct gspca_dev *gspca_dev,
+		u16 gain, u16 global_gain)
+{
+	u16 normalized;
+
+	normalized = global_gain +
+		((u32)global_gain) * gain / GAIN_MAX;
+	if (normalized > GAIN_MAX) {
+		PDEBUG(D_STREAM, "Truncating blue 0x%04X w/ value 0x%04X\n",
+			 GAIN_MAX, normalized);
+		normalized = GAIN_MAX;
+	}
+	normalized = gainify(normalized);
+	PDEBUG(D_STREAM, "gain B (0x%04X): 0x%04X w/ source 0x%04X\n",
+		 REG_BLUE_GAIN, normalized, gain);
+
+	reg_w(gspca_dev, normalized, REG_BLUE_GAIN);
+}
+
+static void setrgain(struct gspca_dev *gspca_dev,
+		u16 gain, u16 global_gain)
+{
+	u16 normalized;
+
+	normalized = global_gain +
+		((u32)global_gain) * gain / GAIN_MAX;
+	if (normalized > GAIN_MAX) {
+		PDEBUG(D_STREAM, "Truncating gain 0x%04X w/ value 0x%04X\n",
+			 GAIN_MAX, normalized);
+		normalized = GAIN_MAX;
+	}
+	normalized = gainify(normalized);
+	PDEBUG(D_STREAM, "gain R (0x%04X): 0x%04X w / source 0x%04X\n",
+		 REG_RED_GAIN, normalized, gain);
+
+	reg_w(gspca_dev, normalized, REG_RED_GAIN);
+}
+
+static void configure_wh(struct gspca_dev *gspca_dev)
+{
+	unsigned int w = gspca_dev->pixfmt.width;
+
+	PDEBUG(D_STREAM, "configure_wh\n");
+
+	if (w == 800) {
+		static const struct cmd reg_init_res[] = {
+			{0x0060, REG_X_ADDR_START},
+			{0x0CD9, REG_X_ADDR_END},
+			{0x0036, REG_Y_ADDR_START},
+			{0x098F, REG_Y_ADDR_END},
+			{0x07C7, REG_READ_MODE},
+		};
+
+		reg_w_buf(gspca_dev,
+			       reg_init_res, ARRAY_SIZE(reg_init_res));
+	} else if (w == 1600) {
+		static const struct cmd reg_init_res[] = {
+			{0x009C, REG_X_ADDR_START},
+			{0x0D19, REG_X_ADDR_END},
+			{0x0068, REG_Y_ADDR_START},
+			{0x09C5, REG_Y_ADDR_END},
+			{0x06C3, REG_READ_MODE},
+		};
+
+		reg_w_buf(gspca_dev,
+			       reg_init_res, ARRAY_SIZE(reg_init_res));
+	} else if (w == 3264) {
+		static const struct cmd reg_init_res[] = {
+			{0x00E8, REG_X_ADDR_START},
+			{0x0DA7, REG_X_ADDR_END},
+			{0x009E, REG_Y_ADDR_START},
+			{0x0A2D, REG_Y_ADDR_END},
+			{0x0241, REG_READ_MODE},
+		};
+
+		reg_w_buf(gspca_dev,
+			       reg_init_res, ARRAY_SIZE(reg_init_res));
+	} else {
+		PERR("bad width %u\n", w);
+		gspca_dev->usb_err = -EINVAL;
+		return;
+	}
+
+	reg_w(gspca_dev, 0x0000, REG_SCALING_MODE);
+	reg_w(gspca_dev, 0x0010, REG_SCALE_M);
+	reg_w(gspca_dev, w, REG_X_OUTPUT_SIZE);
+	reg_w(gspca_dev, gspca_dev->pixfmt.height, REG_Y_OUTPUT_SIZE);
+
+	if (w == 800) {
+		reg_w(gspca_dev, 0x0384, REG_FRAME_LENGTH_LINES_);
+		reg_w(gspca_dev, 0x0960, REG_LINE_LENGTH_PCK_);
+	} else if (w == 1600) {
+		reg_w(gspca_dev, 0x0640, REG_FRAME_LENGTH_LINES_);
+		reg_w(gspca_dev, 0x0FA0, REG_LINE_LENGTH_PCK_);
+	} else if (w == 3264) {
+		reg_w(gspca_dev, 0x0B4B, REG_FRAME_LENGTH_LINES_);
+		reg_w(gspca_dev, 0x1F40, REG_LINE_LENGTH_PCK_);
+	} else {
+		PERR("bad width %u\n", w);
+		gspca_dev->usb_err = -EINVAL;
+		return;
+	}
+}
+
+/* Packets that were encrypted, no idea if the grouping is significant */
+static void configure_encrypted(struct gspca_dev *gspca_dev)
+{
+	static const struct cmd reg_init_begin[] = {
+		{0x0100, REG_SOFTWARE_RESET},
+		{0x0000, REG_MODE_SELECT},
+		{0x0100, REG_GROUPED_PARAMETER_HOLD},
+		{0x0004, REG_VT_PIX_CLK_DIV},
+		{0x0001, REG_VT_SYS_CLK_DIV},
+		{0x0008, REG_OP_PIX_CLK_DIV},
+		{0x0001, REG_OP_SYS_CLK_DIV},
+		{0x0004, REG_PRE_PLL_CLK_DIV},
+		{0x0040, REG_PLL_MULTIPLIER},
+		{0x0000, REG_GROUPED_PARAMETER_HOLD},
+		{0x0100, REG_GROUPED_PARAMETER_HOLD},
+	};
+	static const struct cmd reg_init_end[] = {
+		{0x0000, REG_GROUPED_PARAMETER_HOLD},
+		{0x0301, 0x31AE},
+		{0x0805, 0x3064},
+		{0x0071, 0x3170},
+		{0x10DE, REG_RESET_REGISTER},
+		{0x0000, REG_MODE_SELECT},
+		{0x0010, REG_PLL_MULTIPLIER},
+		{0x0100, REG_MODE_SELECT},
+	};
+
+	PDEBUG(D_STREAM, "Encrypted begin, w = %u\n", gspca_dev->pixfmt.width);
+	reg_w_buf(gspca_dev, reg_init_begin, ARRAY_SIZE(reg_init_begin));
+	configure_wh(gspca_dev);
+	reg_w_buf(gspca_dev, reg_init_end, ARRAY_SIZE(reg_init_end));
+	reg_w(gspca_dev, 0x0100, REG_GROUPED_PARAMETER_HOLD);
+	reg_w(gspca_dev, 0x0000, REG_GROUPED_PARAMETER_HOLD);
+
+	PDEBUG(D_STREAM, "Encrypted end\n");
+}
+
+static int configure(struct gspca_dev *gspca_dev)
+{
+	int rc;
+	uint8_t buff[4];
+
+	PDEBUG(D_STREAM, "configure()\n");
+
+	/*
+	 * First driver sets a sort of encryption key
+	 * A number of futur requests of this type have wValue and wIndex
+	 * encrypted as follows:
+	 * -Compute key = this wValue rotate left by 4 bits
+	 *	(decrypt.py rotates right because we are decrypting)
+	 * -Later packets encrypt packets by XOR'ing with key
+	 *	XOR encrypt/decrypt is symmetrical
+	 *	wValue, and wIndex are encrypted
+	 *	bRequest is not and bRequestType is always 0xC0
+	 *		This allows resyncing if key is unknown?
+	 * By setting 0 we XOR with 0 and the shifting and XOR drops out
+	 */
+	rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0),
+			     0x16, 0xC0, 0x0000, 0x0000, buff, 2, 500);
+	if (val_reply(gspca_dev, buff, rc)) {
+		PERR("failed key req");
+		return -EIO;
+	}
+
+	/*
+	 * Next does some sort of 2 packet challenge / response
+	 * evidence suggests its an Atmel I2C crypto part but nobody cares to
+	 * look
+	 * (to make sure its not cloned hardware?)
+	 * Ignore: I want to work with their hardware, not clone it
+	 * 16 bytes out challenge, requestType: 0x40
+	 * 16 bytes in response, requestType: 0xC0
+	 */
+
+	rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
+			     0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500);
+	if (rc < 0) {
+		PERR("failed to replay packet 176 w/ rc %d\n", rc);
+		return rc;
+	}
+
+	rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
+			     0x01, 0x40, 0x0000, 0x000F, NULL, 0, 500);
+	if (rc < 0) {
+		PERR("failed to replay packet 178 w/ rc %d\n", rc);
+		return rc;
+	}
+
+	rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
+			     0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500);
+	if (rc < 0) {
+		PERR("failed to replay packet 180 w/ rc %d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * Serial number?  Doesn't seem to be required
+	 * cam1: \xE6\x0D\x00\x00, cam2: \x70\x19\x00\x00
+	 * rc = usb_control_msg(gspca_dev->dev,
+	 *			usb_rcvctrlpipe(gspca_dev->dev, 0),
+	 *			0x20, 0xC0, 0x0000, 0x0000, buff, 4, 500);
+	 */
+
+	/* Large (EEPROM?) read, skip it since no idea what to do with it */
+	gspca_dev->usb_err = 0;
+	configure_encrypted(gspca_dev);
+	if (gspca_dev->usb_err)
+		return gspca_dev->usb_err;
+
+	/* Omitted this by accident, does not work without it */
+	rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
+			     0x01, 0x40, 0x0003, 0x000F, NULL, 0, 500);
+	if (rc < 0) {
+		PERR("failed to replay final packet w/ rc %d\n", rc);
+		return rc;
+	}
+
+	PDEBUG(D_STREAM, "Configure complete\n");
+	return 0;
+}
+
+static int sd_config(struct gspca_dev *gspca_dev,
+		     const struct usb_device_id *id)
+{
+	gspca_dev->cam.cam_mode = vga_mode;
+	gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+
+	/* Yes we want URBs and we want them now! */
+	gspca_dev->cam.no_urb_create = 0;
+	gspca_dev->cam.bulk_nurbs = 4;
+	/* Largest size the windows driver uses */
+	gspca_dev->cam.bulk_size = BULK_SIZE;
+	/* Def need to use bulk transfers */
+	gspca_dev->cam.bulk = 1;
+
+	return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int rc;
+
+	sd->this_f = 0;
+
+	rc = configure(gspca_dev);
+	if (rc < 0) {
+		PERR("Failed configure");
+		return rc;
+	}
+	/* First two frames have messed up gains
+	Drop them to avoid special cases in user apps? */
+	return 0;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data,	/* isoc packet */
+			int len)	/* iso packet length */
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (len != BULK_SIZE) {
+		/* can we finish a frame? */
+		if (sd->this_f + len == gspca_dev->pixfmt.sizeimage) {
+			gspca_frame_add(gspca_dev, LAST_PACKET, data, len);
+			PDEBUG(D_FRAM, "finish frame sz %u/%u w/ len %u\n",
+				 sd->this_f, gspca_dev->pixfmt.sizeimage, len);
+		/* lost some data, discard the frame */
+		} else {
+			gspca_frame_add(gspca_dev, DISCARD_PACKET, NULL, 0);
+			PDEBUG(D_FRAM, "abort frame sz %u/%u w/ len %u\n",
+				 sd->this_f, gspca_dev->pixfmt.sizeimage, len);
+		}
+		sd->this_f = 0;
+	} else {
+		if (sd->this_f == 0)
+			gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		else
+			gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+		sd->this_f += len;
+	}
+}
+
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gspca_dev *gspca_dev =
+		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->usb_err = 0;
+
+	if (!gspca_dev->streaming)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		setexposure(gspca_dev, ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		/* gspca_dev->gain automatically updated */
+		setggain(gspca_dev, gspca_dev->gain->val);
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		sd->blue->val = ctrl->val;
+		setbgain(gspca_dev, sd->blue->val, gspca_dev->gain->val);
+		break;
+	case V4L2_CID_RED_BALANCE:
+		sd->red->val = ctrl->val;
+		setrgain(gspca_dev, sd->red->val, gspca_dev->gain->val);
+		break;
+	}
+	return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+	.s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+	gspca_dev->vdev.ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+
+	gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+	/* Mostly limited by URB timeouts */
+	/* XXX: make dynamic based on frame rate? */
+		V4L2_CID_EXPOSURE, 0, 800, 1, 350);
+	gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_GAIN, 0, 511, 1, 128);
+	sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_BLUE_BALANCE, 0, 1023, 1, 80);
+	sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+			V4L2_CID_RED_BALANCE, 0, 1023, 1, 295);
+
+	if (hdl->error) {
+		PERR("Could not initialize controls\n");
+		return hdl->error;
+	}
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.config = sd_config,
+	.init = sd_init,
+	.init_controls = sd_init_controls,
+	.start = sd_start,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+	/* Commented out devices should be related */
+	/* AS: AmScope, TT: ToupTek */
+	/* { USB_DEVICE(0x0547, 0x6035) },  TT UCMOS00350KPA */
+	/* { USB_DEVICE(0x0547, 0x6130) },  TT UCMOS01300KPA */
+	/* { USB_DEVICE(0x0547, 0x6200) },  TT UCMOS02000KPA */
+	/* { USB_DEVICE(0x0547, 0x6310) },  TT UCMOS03100KPA */
+	/* { USB_DEVICE(0x0547, 0x6510) },  TT UCMOS05100KPA */
+	/* { USB_DEVICE(0x0547, 0x6800) },  TT UCMOS08000KPA */
+	/* { USB_DEVICE(0x0547, 0x6801) },  TT UCMOS08000KPB */
+	{ USB_DEVICE(0x0547, 0x6801) }, /* TT UCMOS08000KPB, AS MU800 */
+	/* { USB_DEVICE(0x0547, 0x6900) },  TT UCMOS09000KPA */
+	/* { USB_DEVICE(0x0547, 0x6901) },  TT UCMOS09000KPB */
+	/* { USB_DEVICE(0x0547, 0x6010) },  TT UCMOS10000KPA */
+	/* { USB_DEVICE(0x0547, 0x6014) },  TT UCMOS14000KPA */
+	/* { USB_DEVICE(0x0547, 0x6131) },  TT UCMOS01300KMA */
+	/* { USB_DEVICE(0x0547, 0x6511) },  TT UCMOS05100KMA */
+	/* { USB_DEVICE(0x0547, 0x8080) },  TT UHCCD00800KPA */
+	/* { USB_DEVICE(0x0547, 0x8140) },  TT UHCCD01400KPA */
+	/* { USB_DEVICE(0x0547, 0x8141) },  TT EXCCD01400KPA */
+	/* { USB_DEVICE(0x0547, 0x8200) },  TT UHCCD02000KPA */
+	/* { USB_DEVICE(0x0547, 0x8201) },  TT UHCCD02000KPB */
+	/* { USB_DEVICE(0x0547, 0x8310) },  TT UHCCD03100KPA */
+	/* { USB_DEVICE(0x0547, 0x8500) },  TT UHCCD05000KPA */
+	/* { USB_DEVICE(0x0547, 0x8510) },  TT UHCCD05100KPA */
+	/* { USB_DEVICE(0x0547, 0x8600) },  TT UHCCD06000KPA */
+	/* { USB_DEVICE(0x0547, 0x8800) },  TT UHCCD08000KPA */
+	/* { USB_DEVICE(0x0547, 0x8315) },  TT UHCCD03150KPA */
+	/* { USB_DEVICE(0x0547, 0x7800) },  TT UHCCD00800KMA */
+	/* { USB_DEVICE(0x0547, 0x7140) },  TT UHCCD01400KMA */
+	/* { USB_DEVICE(0x0547, 0x7141) },  TT UHCCD01400KMB */
+	/* { USB_DEVICE(0x0547, 0x7200) },  TT UHCCD02000KMA */
+	/* { USB_DEVICE(0x0547, 0x7315) },  TT UHCCD03150KMA */
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static int sd_probe(struct usb_interface *intf,
+		    const struct usb_device_id *id)
+{
+	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+			     THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+	.name = MODULE_NAME,
+	.id_table = device_table,
+	.probe = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+#endif
+};
+
+static int __init sd_mod_init(void)
+{
+	int ret;
+
+	ret = usb_register(&sd_driver);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+	usb_deregister(&sd_driver);
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/usb/gspca/vc032x.c b/drivers/media/usb/gspca/vc032x.c
index c00ac57..b4efb2f 100644
--- a/drivers/media/usb/gspca/vc032x.c
+++ b/drivers/media/usb/gspca/vc032x.c
@@ -68,12 +68,12 @@
 
 static const struct v4l2_pix_format vc0321_mode[] = {
 	{320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
-		.bytesperline = 320,
+		.bytesperline = 320 * 2,
 		.sizeimage = 320 * 240 * 2,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 1},
 	{640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
-		.bytesperline = 640,
+		.bytesperline = 640 * 2,
 		.sizeimage = 640 * 480 * 2,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 0},
@@ -97,17 +97,17 @@
 };
 static const struct v4l2_pix_format bi_mode[] = {
 	{320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
-		.bytesperline = 320,
+		.bytesperline = 320 * 2,
 		.sizeimage = 320 * 240 * 2,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 2},
 	{640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
-		.bytesperline = 640,
+		.bytesperline = 640 * 2,
 		.sizeimage = 640 * 480 * 2,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 1},
 	{1280, 1024, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
-		.bytesperline = 1280,
+		.bytesperline = 1280 * 2,
 		.sizeimage = 1280 * 1024 * 2,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 0},
diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c
index 42b4cdf..3fc6419 100644
--- a/drivers/media/usb/hdpvr/hdpvr-core.c
+++ b/drivers/media/usb/hdpvr/hdpvr-core.c
@@ -69,10 +69,6 @@
 void hdpvr_delete(struct hdpvr_device *dev)
 {
 	hdpvr_free_buffers(dev);
-
-	if (dev->video_dev)
-		video_device_release(dev->video_dev);
-
 	usb_put_dev(dev->udev);
 }
 
@@ -397,7 +393,7 @@
 
 	/* let the user know what node this device is now attached to */
 	v4l2_info(&dev->v4l2_dev, "device now attached to %s\n",
-		  video_device_node_name(dev->video_dev));
+		  video_device_node_name(&dev->video_dev));
 	return 0;
 
 reg_fail:
@@ -420,7 +416,7 @@
 	struct hdpvr_device *dev = to_hdpvr_dev(usb_get_intfdata(interface));
 
 	v4l2_info(&dev->v4l2_dev, "device %s disconnected\n",
-		  video_device_node_name(dev->video_dev));
+		  video_device_node_name(&dev->video_dev));
 	/* prevent more I/O from starting and stop any ongoing */
 	mutex_lock(&dev->io_mutex);
 	dev->status = STATUS_DISCONNECTED;
@@ -436,7 +432,7 @@
 #if IS_ENABLED(CONFIG_I2C)
 	i2c_del_adapter(&dev->i2c_adapter);
 #endif
-	video_unregister_device(dev->video_dev);
+	video_unregister_device(&dev->video_dev);
 	atomic_dec(&dev_nr);
 }
 
diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c
index 59d15fd..d8d8c0f 100644
--- a/drivers/media/usb/hdpvr/hdpvr-video.c
+++ b/drivers/media/usb/hdpvr/hdpvr-video.c
@@ -797,7 +797,7 @@
 		 * Comment this out for now, but if the legacy mode can be
 		 * removed in the future, then this code should be enabled
 		 * again.
-		dev->video_dev->tvnorms =
+		dev->video_dev.tvnorms =
 			(index != HDPVR_COMPONENT) ? V4L2_STD_ALL : 0;
 		 */
 	}
@@ -1228,19 +1228,12 @@
 	}
 
 	/* setup and register video device */
-	dev->video_dev = video_device_alloc();
-	if (!dev->video_dev) {
-		v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n");
-		res = -ENOMEM;
-		goto error;
-	}
+	dev->video_dev = hdpvr_video_template;
+	strcpy(dev->video_dev.name, "Hauppauge HD PVR");
+	dev->video_dev.v4l2_dev = &dev->v4l2_dev;
+	video_set_drvdata(&dev->video_dev, dev);
 
-	*dev->video_dev = hdpvr_video_template;
-	strcpy(dev->video_dev->name, "Hauppauge HD PVR");
-	dev->video_dev->v4l2_dev = &dev->v4l2_dev;
-	video_set_drvdata(dev->video_dev, dev);
-
-	res = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum);
+	res = video_register_device(&dev->video_dev, VFL_TYPE_GRABBER, devnum);
 	if (res < 0) {
 		v4l2_err(&dev->v4l2_dev, "video_device registration failed\n");
 		goto error;
diff --git a/drivers/media/usb/hdpvr/hdpvr.h b/drivers/media/usb/hdpvr/hdpvr.h
index dc685d4..a319430 100644
--- a/drivers/media/usb/hdpvr/hdpvr.h
+++ b/drivers/media/usb/hdpvr/hdpvr.h
@@ -66,7 +66,7 @@
 /* Structure to hold all of our device specific stuff */
 struct hdpvr_device {
 	/* the v4l device for this device */
-	struct video_device	*video_dev;
+	struct video_device	video_dev;
 	/* the control handler for this device */
 	struct v4l2_ctrl_handler hdl;
 	/* the usb device for this device */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-audio.c b/drivers/media/usb/pvrusb2/pvrusb2-audio.c
index cc06d5e..45276c6 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-audio.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-audio.c
@@ -84,13 +84,3 @@
 			MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0);
 	}
 }
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-audio.h b/drivers/media/usb/pvrusb2/pvrusb2-audio.h
index e3e63d7..27cefb5 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-audio.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-audio.h
@@ -25,13 +25,3 @@
 #include "pvrusb2-hdw-internal.h"
 void pvr2_msp3400_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
 #endif /* __PVRUSB2_AUDIO_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-context.c b/drivers/media/usb/pvrusb2/pvrusb2-context.c
index c8761c7..924fc4c 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-context.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-context.c
@@ -418,14 +418,3 @@
 	pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
 	return cp;
 }
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-context.h b/drivers/media/usb/pvrusb2/pvrusb2-context.h
index d657e53..1c1d442 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-context.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-context.h
@@ -83,12 +83,3 @@
 void pvr2_context_global_done(void);
 
 #endif /* __PVRUSB2_CONTEXT_H */
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c
index 8832090..f82f0f0 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c
@@ -82,14 +82,3 @@
 		sd->ops->audio->s_routing(sd, input, 0, 0);
 	}
 }
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h
index 53ba548..86c17be 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h
@@ -36,13 +36,3 @@
 void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
 
 #endif /* __PVRUSB2_AUDIO_CS53L32A_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c
index 7d5a713..958db17 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c
@@ -596,14 +596,3 @@
 	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
 	return ret;
 }
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h
index 794ff90..c175571 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h
@@ -110,13 +110,3 @@
 			   unsigned int *len);
 
 #endif /* __PVRUSB2_CTRL_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
index c514d0b..1a81aa7 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -152,15 +152,3 @@
 		sd->ops->audio->s_routing(sd, (u32)aud_input, 0, 0);
 	}
 }
-
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h
index e35c232..2eed7b7 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h
@@ -40,13 +40,3 @@
 
 
 #endif /* __PVRUSB2_CX2584X_V4L_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debug.h b/drivers/media/usb/pvrusb2/pvrusb2-debug.h
index be79249..4ef2ebc 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-debug.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-debug.h
@@ -57,13 +57,3 @@
 
 
 #endif /* __PVRUSB2_HDW_INTERNAL_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c
index 4279ebb..e4022bc 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c
@@ -322,14 +322,3 @@
 
 	return 0;
 }
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h
index 2f8d467..a8dfc55 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h
@@ -40,13 +40,3 @@
 			const char *buf_ptr,unsigned int buf_size);
 
 #endif /* __PVRUSB2_DEBUGIFC_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c
index f8ddcfe..d03e904 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c
@@ -564,13 +564,3 @@
 MODULE_FIRMWARE(PVR2_FIRMWARE_24xxx);
 MODULE_FIRMWARE(PVR2_FIRMWARE_73xxx);
 MODULE_FIRMWARE(PVR2_FIRMWARE_75xxx);
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.h b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h
index 8b22319..ce7126b 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-devattr.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h
@@ -187,13 +187,3 @@
 extern struct usb_device_id pvr2_device_table[];
 
 #endif /* __PVRUSB2_HDW_INTERNAL_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c
index 9515f3a..e1907cd 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c
@@ -152,13 +152,3 @@
 
 	return 0;
 }
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h
index cca3216..f1e33c8 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h
@@ -27,13 +27,3 @@
 int pvr2_eeprom_analyze(struct pvr2_hdw *);
 
 #endif /* __PVRUSB2_EEPROM_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c
index f7702ae..593b3e9 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c
@@ -538,14 +538,3 @@
 
 	return status;
 }
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-encoder.h b/drivers/media/usb/pvrusb2/pvrusb2-encoder.h
index 232fefb..a2bfb48 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-encoder.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.h
@@ -30,13 +30,3 @@
 int pvr2_encoder_stop(struct pvr2_hdw *);
 
 #endif /* __PVRUSB2_ENCODER_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h
index 614755e..06a15a6 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h
@@ -60,13 +60,3 @@
 #define FX2CMD_ONAIR_DTV_POWER_OFF     0xa3u
 
 #endif /* _PVRUSB2_FX2_CMD_H_ */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h
index 036952f..1f9c028 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h
@@ -394,13 +394,3 @@
 void pvr2_hdw_status_poll(struct pvr2_hdw *);
 
 #endif /* __PVRUSB2_HDW_INTERNAL_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
index 2fd9b5e..930593d 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
@@ -2425,22 +2425,18 @@
 	}
 	if (!hdw) goto fail;
 
-	init_timer(&hdw->quiescent_timer);
-	hdw->quiescent_timer.data = (unsigned long)hdw;
-	hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout;
+	setup_timer(&hdw->quiescent_timer, pvr2_hdw_quiescent_timeout,
+		    (unsigned long)hdw);
 
-	init_timer(&hdw->decoder_stabilization_timer);
-	hdw->decoder_stabilization_timer.data = (unsigned long)hdw;
-	hdw->decoder_stabilization_timer.function =
-		pvr2_hdw_decoder_stabilization_timeout;
+	setup_timer(&hdw->decoder_stabilization_timer,
+		    pvr2_hdw_decoder_stabilization_timeout,
+		    (unsigned long)hdw);
 
-	init_timer(&hdw->encoder_wait_timer);
-	hdw->encoder_wait_timer.data = (unsigned long)hdw;
-	hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout;
+	setup_timer(&hdw->encoder_wait_timer, pvr2_hdw_encoder_wait_timeout,
+		    (unsigned long)hdw);
 
-	init_timer(&hdw->encoder_run_timer);
-	hdw->encoder_run_timer.data = (unsigned long)hdw;
-	hdw->encoder_run_timer.function = pvr2_hdw_encoder_run_timeout;
+	setup_timer(&hdw->encoder_run_timer, pvr2_hdw_encoder_run_timeout,
+		    (unsigned long)hdw);
 
 	hdw->master_state = PVR2_STATE_DEAD;
 
@@ -3680,10 +3676,8 @@
 	hdw->ctl_timeout_flag = 0;
 	hdw->ctl_write_pend_flag = 0;
 	hdw->ctl_read_pend_flag = 0;
-	init_timer(&timer);
+	setup_timer(&timer, pvr2_ctl_timeout, (unsigned long)hdw);
 	timer.expires = jiffies + timeout;
-	timer.data = (unsigned long)hdw;
-	timer.function = pvr2_ctl_timeout;
 
 	if (write_len) {
 		hdw->cmd_debug_state = 2;
@@ -4035,11 +4029,6 @@
 }
 
 
-int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw)
-{
-	return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_OFF);
-}
-
 
 int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw)
 {
@@ -4301,9 +4290,8 @@
 				   the encoder. */
 				if (!hdw->state_encoder_waitok) {
 					hdw->encoder_wait_timer.expires =
-						jiffies +
-						(HZ * TIME_MSEC_ENCODER_WAIT
-						 / 1000);
+						jiffies + msecs_to_jiffies(
+						TIME_MSEC_ENCODER_WAIT);
 					add_timer(&hdw->encoder_wait_timer);
 				}
 			}
@@ -4426,8 +4414,8 @@
 		if (pvr2_encoder_start(hdw) < 0) return !0;
 		hdw->state_encoder_run = !0;
 		if (!hdw->state_encoder_runok) {
-			hdw->encoder_run_timer.expires =
-				jiffies + (HZ * TIME_MSEC_ENCODER_OK / 1000);
+			hdw->encoder_run_timer.expires = jiffies +
+				 msecs_to_jiffies(TIME_MSEC_ENCODER_OK);
 			add_timer(&hdw->encoder_run_timer);
 		}
 	}
@@ -4518,9 +4506,8 @@
 				   but before we did the pending check. */
 				if (!hdw->state_decoder_quiescent) {
 					hdw->quiescent_timer.expires =
-						jiffies +
-						(HZ * TIME_MSEC_DECODER_WAIT
-						 / 1000);
+						jiffies + msecs_to_jiffies(
+						TIME_MSEC_DECODER_WAIT);
 					add_timer(&hdw->quiescent_timer);
 				}
 			}
@@ -4544,9 +4531,8 @@
 		hdw->state_decoder_run = !0;
 		if (hdw->decoder_client_id == PVR2_CLIENT_ID_SAA7115) {
 			hdw->decoder_stabilization_timer.expires =
-				jiffies +
-				(HZ * TIME_MSEC_DECODER_STABILIZATION_WAIT /
-				 1000);
+				jiffies + msecs_to_jiffies(
+				TIME_MSEC_DECODER_STABILIZATION_WAIT);
 			add_timer(&hdw->decoder_stabilization_timer);
 		} else {
 			hdw->state_decoder_ready = !0;
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h
index 4184707..a82a00d 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h
@@ -271,9 +271,6 @@
 /* Execute simple reset command */
 int pvr2_hdw_cmd_powerup(struct pvr2_hdw *);
 
-/* suspend */
-int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *);
-
 /* Order decoder to reset */
 int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *);
 
@@ -343,13 +340,3 @@
 int pvr2_upload_firmware2(struct pvr2_hdw *hdw);
 
 #endif /* __PVRUSB2_HDW_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
index b5e929f..4baa9d6 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
@@ -686,13 +686,3 @@
 		hdw->i2c_linked = 0;
 	}
 }
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h
index 6a75769..a10a3e8 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h
@@ -27,14 +27,3 @@
 
 
 #endif /* __PVRUSB2_I2C_ADAPTER_H */
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-io.c b/drivers/media/usb/pvrusb2/pvrusb2-io.c
index 1e35474..0c08f22 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-io.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-io.c
@@ -682,14 +682,3 @@
 {
 	return bp->id;
 }
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-io.h b/drivers/media/usb/pvrusb2/pvrusb2-io.h
index afb7e87..0c47c6a 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-io.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-io.h
@@ -90,13 +90,3 @@
 int pvr2_buffer_queue(struct pvr2_buffer *);
 
 #endif /* __PVRUSB2_IO_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ioread.c b/drivers/media/usb/pvrusb2/pvrusb2-ioread.c
index bba6115..cd995b5 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-ioread.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-ioread.c
@@ -499,14 +499,3 @@
 		   cp,req_cnt,ret);
 	return ret;
 }
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ioread.h b/drivers/media/usb/pvrusb2/pvrusb2-ioread.h
index 100e078..0b1f0fb 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-ioread.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-ioread.h
@@ -36,13 +36,3 @@
 int pvr2_ioread_avail(struct pvr2_ioread *);
 
 #endif /* __PVRUSB2_IOREAD_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-main.c b/drivers/media/usb/pvrusb2/pvrusb2-main.c
index 1b05e28..d83456b 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-main.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-main.c
@@ -169,14 +169,3 @@
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 MODULE_VERSION("0.9.1");
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.c b/drivers/media/usb/pvrusb2/pvrusb2-std.c
index 453627b..9a596a3 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-std.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-std.c
@@ -398,14 +398,3 @@
 {
 	return CSTD_ALL;
 }
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.h b/drivers/media/usb/pvrusb2/pvrusb2-std.h
index a35c53d..ed4ec04 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-std.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-std.h
@@ -47,13 +47,3 @@
 v4l2_std_id pvr2_std_get_usable(void);
 
 #endif /* __PVRUSB2_STD_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c
index 75bcc59..5b14abb 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c
@@ -848,14 +848,3 @@
 	return count;
 }
 #endif /* CPTCFG_VIDEO_PVRUSB2_DEBUGIFC */
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h
index 6d875bf..6f0579e 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h
@@ -34,13 +34,3 @@
 				     struct pvr2_sysfs_class *);
 
 #endif /* __PVRUSB2_SYSFS_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-util.h b/drivers/media/usb/pvrusb2/pvrusb2-util.h
index 92b7554..5465bf9 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-util.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-util.h
@@ -50,13 +50,3 @@
 
 
 #endif /* __PVRUSB2_UTIL_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
index 1b158f1..1c5f85b 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
@@ -21,7 +21,6 @@
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
-#include <linux/version.h>
 #include "pvrusb2-context.h"
 #include "pvrusb2-hdw.h"
 #include "pvrusb2.h"
@@ -32,6 +31,7 @@
 #include <linux/module.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 
@@ -50,14 +50,11 @@
 };
 
 struct pvr2_v4l2_fh {
+	struct v4l2_fh fh;
 	struct pvr2_channel channel;
 	struct pvr2_v4l2_dev *pdi;
-	enum v4l2_priority prio;
 	struct pvr2_ioread *rhp;
 	struct file *file;
-	struct pvr2_v4l2 *vhead;
-	struct pvr2_v4l2_fh *vnext;
-	struct pvr2_v4l2_fh *vprev;
 	wait_queue_head_t wait_data;
 	int fw_mode_flag;
 	/* Map contiguous ordinal value to input id */
@@ -67,10 +64,6 @@
 
 struct pvr2_v4l2 {
 	struct pvr2_channel channel;
-	struct pvr2_v4l2_fh *vfirst;
-	struct pvr2_v4l2_fh *vlast;
-
-	struct v4l2_prio_state prio;
 
 	/* streams - Note that these must be separately, individually,
 	 * allocated pointers.  This is because the v4l core is going to
@@ -89,16 +82,6 @@
 module_param_array(vbi_nr, int, NULL, 0444);
 MODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor");
 
-static struct v4l2_capability pvr_capability ={
-	.driver         = "pvrusb2",
-	.card           = "Hauppauge WinTV pvr-usb2",
-	.bus_info       = "usb",
-	.version        = LINUX_VERSION_CODE,
-	.capabilities   = (V4L2_CAP_VIDEO_CAPTURE |
-			   V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
-			   V4L2_CAP_READWRITE),
-};
-
 static struct v4l2_fmtdesc pvr_fmtdesc [] = {
 	{
 		.index          = 0,
@@ -160,30 +143,25 @@
 	struct pvr2_v4l2_fh *fh = file->private_data;
 	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
 
-	memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+	strlcpy(cap->driver, "pvrusb2", sizeof(cap->driver));
 	strlcpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw),
 			sizeof(cap->bus_info));
 	strlcpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card));
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+			    V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
+			    V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS;
+	switch (fh->pdi->devbase.vfl_type) {
+	case VFL_TYPE_GRABBER:
+		cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO;
+		break;
+	case VFL_TYPE_RADIO:
+		cap->device_caps = V4L2_CAP_RADIO;
+		break;
+	}
+	cap->device_caps |= V4L2_CAP_TUNER | V4L2_CAP_READWRITE;
 	return 0;
 }
 
-static int pvr2_g_priority(struct file *file, void *priv, enum v4l2_priority *p)
-{
-	struct pvr2_v4l2_fh *fh = file->private_data;
-	struct pvr2_v4l2 *vp = fh->vhead;
-
-	*p = v4l2_prio_max(&vp->prio);
-	return 0;
-}
-
-static int pvr2_s_priority(struct file *file, void *priv, enum v4l2_priority prio)
-{
-	struct pvr2_v4l2_fh *fh = file->private_data;
-	struct pvr2_v4l2 *vp = fh->vhead;
-
-	return v4l2_prio_change(&vp->prio, &fh->prio, prio);
-}
-
 static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std)
 {
 	struct pvr2_v4l2_fh *fh = file->private_data;
@@ -803,8 +781,6 @@
 
 static const struct v4l2_ioctl_ops pvr2_ioctl_ops = {
 	.vidioc_querycap		    = pvr2_querycap,
-	.vidioc_g_priority		    = pvr2_g_priority,
-	.vidioc_s_priority		    = pvr2_s_priority,
 	.vidioc_s_audio			    = pvr2_s_audio,
 	.vidioc_g_audio			    = pvr2_g_audio,
 	.vidioc_enumaudio		    = pvr2_enumaudio,
@@ -909,7 +885,9 @@
 	if (!vp->channel.mc_head->disconnect_flag) return;
 	pvr2_v4l2_dev_disassociate_parent(vp->dev_video);
 	pvr2_v4l2_dev_disassociate_parent(vp->dev_radio);
-	if (vp->vfirst) return;
+	if (!list_empty(&vp->dev_video->devbase.fh_list) ||
+	    !list_empty(&vp->dev_radio->devbase.fh_list))
+		return;
 	pvr2_v4l2_destroy_no_lock(vp);
 }
 
@@ -919,7 +897,6 @@
 {
 
 	struct pvr2_v4l2_fh *fh = file->private_data;
-	struct pvr2_v4l2 *vp = fh->vhead;
 	struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
 	long ret = -EINVAL;
 
@@ -932,18 +909,6 @@
 		return -EFAULT;
 	}
 
-	/* check priority */
-	switch (cmd) {
-	case VIDIOC_S_CTRL:
-	case VIDIOC_S_STD:
-	case VIDIOC_S_INPUT:
-	case VIDIOC_S_TUNER:
-	case VIDIOC_S_FREQUENCY:
-		ret = v4l2_prio_check(&vp->prio, fh->prio);
-		if (ret)
-			return ret;
-	}
-
 	ret = video_ioctl2(file, cmd, arg);
 
 	pvr2_hdw_commit_ctl(hdw);
@@ -968,7 +933,7 @@
 static int pvr2_v4l2_release(struct file *file)
 {
 	struct pvr2_v4l2_fh *fhp = file->private_data;
-	struct pvr2_v4l2 *vp = fhp->vhead;
+	struct pvr2_v4l2 *vp = fhp->pdi->v4lp;
 	struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw;
 
 	pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
@@ -982,22 +947,10 @@
 		fhp->rhp = NULL;
 	}
 
-	v4l2_prio_close(&vp->prio, fhp->prio);
+	v4l2_fh_del(&fhp->fh);
+	v4l2_fh_exit(&fhp->fh);
 	file->private_data = NULL;
 
-	if (fhp->vnext) {
-		fhp->vnext->vprev = fhp->vprev;
-	} else {
-		vp->vlast = fhp->vprev;
-	}
-	if (fhp->vprev) {
-		fhp->vprev->vnext = fhp->vnext;
-	} else {
-		vp->vfirst = fhp->vnext;
-	}
-	fhp->vnext = NULL;
-	fhp->vprev = NULL;
-	fhp->vhead = NULL;
 	pvr2_channel_done(&fhp->channel);
 	pvr2_trace(PVR2_TRACE_STRUCT,
 		   "Destroying pvr_v4l2_fh id=%p",fhp);
@@ -1006,7 +959,9 @@
 		fhp->input_map = NULL;
 	}
 	kfree(fhp);
-	if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
+	if (vp->channel.mc_head->disconnect_flag &&
+	    list_empty(&vp->dev_video->devbase.fh_list) &&
+	    list_empty(&vp->dev_radio->devbase.fh_list)) {
 		pvr2_v4l2_destroy_no_lock(vp);
 	}
 	return 0;
@@ -1041,6 +996,7 @@
 		return -ENOMEM;
 	}
 
+	v4l2_fh_init(&fhp->fh, &dip->devbase);
 	init_waitqueue_head(&fhp->wait_data);
 	fhp->pdi = dip;
 
@@ -1091,21 +1047,11 @@
 		fhp->input_map[input_cnt++] = idx;
 	}
 
-	fhp->vnext = NULL;
-	fhp->vprev = vp->vlast;
-	if (vp->vlast) {
-		vp->vlast->vnext = fhp;
-	} else {
-		vp->vfirst = fhp;
-	}
-	vp->vlast = fhp;
-	fhp->vhead = vp;
-
 	fhp->file = file;
 	file->private_data = fhp;
-	v4l2_prio_open(&vp->prio, &fhp->prio);
 
 	fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw);
+	v4l2_fh_add(&fhp->fh);
 
 	return 0;
 }
@@ -1245,7 +1191,7 @@
 	.open       = pvr2_v4l2_open,
 	.release    = pvr2_v4l2_release,
 	.read       = pvr2_v4l2_read,
-	.ioctl      = pvr2_v4l2_ioctl,
+	.unlocked_ioctl = pvr2_v4l2_ioctl,
 	.poll       = pvr2_v4l2_poll,
 };
 
@@ -1360,13 +1306,3 @@
 	pvr2_v4l2_destroy_no_lock(vp);
 	return NULL;
 }
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h
index 34c011a..e455c95 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h
@@ -27,13 +27,3 @@
 struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *);
 
 #endif /* __PVRUSB2_V4L2_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 75 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c
index 2e205c9..139b397 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c
@@ -101,14 +101,3 @@
 		sd->ops->video->s_routing(sd, input, 0, 0);
 	}
 }
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h
index 3b0bd5d..dacf3ec 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h
@@ -36,13 +36,3 @@
 void pvr2_saa7115_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
 
 #endif /* __PVRUSB2_VIDEO_V4L_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c
index 3ac8d75..f1df94a 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c
@@ -56,15 +56,3 @@
 		sd->ops->audio->s_routing(sd, input, 0, 0);
 	}
 }
-
-
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h
index 0577bc7..a4ee12e 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h
@@ -40,13 +40,3 @@
 
 
 #endif /* __PVRUSB2_WM8775_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2.h b/drivers/media/usb/pvrusb2/pvrusb2.h
index 240de9b..95f98a8 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2.h
@@ -30,13 +30,3 @@
 #define PVR_NUM 20
 
 #endif /* __PVRUSB2_H */
-
-/*
-  Stuff for Emacs to see, in order to encourage consistent editing style:
-  *** Local Variables: ***
-  *** mode: c ***
-  *** fill-column: 70 ***
-  *** tab-width: 8 ***
-  *** c-basic-offset: 8 ***
-  *** End: ***
-  */
diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
index cf289e8..934bc1f 100644
--- a/drivers/media/usb/pwc/pwc-if.c
+++ b/drivers/media/usb/pwc/pwc-if.c
@@ -508,7 +508,8 @@
 }
 
 /* Must be called with vb_queue_lock hold */
-static void pwc_cleanup_queued_bufs(struct pwc_device *pdev)
+static void pwc_cleanup_queued_bufs(struct pwc_device *pdev,
+				    enum vb2_buffer_state state)
 {
 	unsigned long flags = 0;
 
@@ -519,7 +520,7 @@
 		buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf,
 				 list);
 		list_del(&buf->list);
-		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+		vb2_buffer_done(&buf->vb, state);
 	}
 	spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
 }
@@ -674,7 +675,7 @@
 		pwc_set_leds(pdev, 0, 0);
 		pwc_camera_power(pdev, 0);
 		/* And cleanup any queued bufs!! */
-		pwc_cleanup_queued_bufs(pdev);
+		pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
 	}
 	mutex_unlock(&pdev->v4l2_lock);
 
@@ -692,7 +693,9 @@
 		pwc_isoc_cleanup(pdev);
 	}
 
-	pwc_cleanup_queued_bufs(pdev);
+	pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_ERROR);
+	if (pdev->fill_buf)
+		vb2_buffer_done(&pdev->fill_buf->vb, VB2_BUF_STATE_ERROR);
 	mutex_unlock(&pdev->v4l2_lock);
 }
 
@@ -1125,7 +1128,6 @@
 	if (pdev->vb_queue.streaming)
 		pwc_isoc_cleanup(pdev);
 	pdev->udev = NULL;
-	pwc_cleanup_queued_bufs(pdev);
 
 	v4l2_device_disconnect(&pdev->v4l2_dev);
 	video_unregister_device(&pdev->vdev);
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index de55e96..0f3c34d 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -2274,9 +2274,7 @@
 		dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
 		goto errorEP;
 	}
-	init_timer(&dev->timer);
-	dev->timer.function = s2255_timer;
-	dev->timer.data = (unsigned long)dev->fw_data;
+	setup_timer(&dev->timer, s2255_timer, (unsigned long)dev->fw_data);
 	init_waitqueue_head(&dev->fw_data->wait_fw);
 	for (i = 0; i < MAX_CHANNELS; i++) {
 		struct s2255_vc *vc = &dev->vc[i];
diff --git a/drivers/media/usb/siano/Kconfig b/drivers/media/usb/siano/Kconfig
index 6dbbf39..da2a261 100644
--- a/drivers/media/usb/siano/Kconfig
+++ b/drivers/media/usb/siano/Kconfig
@@ -6,7 +6,9 @@
 	tristate "Siano SMS1xxx based MDTV receiver"
 	depends on m
 	depends on DVB_CORE && HAS_DMA
+	depends on !RC_CORE || RC_CORE
 	select MEDIA_COMMON_OPTIONS
+	select SMS_SIANO_MDTV
 	---help---
 	  Choose if you would like to have Siano's support for USB interface
 
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
index 94e10b1..76f4df8 100644
--- a/drivers/media/usb/siano/smsusb.c
+++ b/drivers/media/usb/siano/smsusb.c
@@ -19,6 +19,8 @@
 
 ****************************************************************/
 
+#include "smscoreapi.h"
+
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/usb.h>
@@ -26,14 +28,9 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 
-#include "smscoreapi.h"
 #include "sms-cards.h"
 #include "smsendian.h"
 
-static int sms_dbg;
-module_param_named(debug, sms_dbg, int, 0644);
-MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
-
 #define USB1_BUFFER_SIZE		0x1000
 #define USB2_BUFFER_SIZE		0x2000
 
@@ -87,7 +84,7 @@
 	struct smsusb_device_t *dev = surb->dev;
 
 	if (urb->status == -ESHUTDOWN) {
-		sms_err("error, urb status %d (-ESHUTDOWN), %d bytes",
+		pr_err("error, urb status %d (-ESHUTDOWN), %d bytes\n",
 			urb->status, urb->actual_length);
 		return;
 	}
@@ -109,9 +106,7 @@
 				/* sanity check */
 				if (((int) phdr->msg_length +
 				     surb->cb->offset) > urb->actual_length) {
-					sms_err("invalid response "
-						"msglen %d offset %d "
-						"size %d",
+					pr_err("invalid response msglen %d offset %d size %d\n",
 						phdr->msg_length,
 						surb->cb->offset,
 						urb->actual_length);
@@ -125,7 +120,7 @@
 			} else
 				surb->cb->offset = 0;
 
-			sms_debug("received %s(%d) size: %d",
+			pr_debug("received %s(%d) size: %d\n",
 				  smscore_translate_msg(phdr->msg_type),
 				  phdr->msg_type, phdr->msg_length);
 
@@ -134,12 +129,11 @@
 			smscore_onresponse(dev->coredev, surb->cb);
 			surb->cb = NULL;
 		} else {
-			sms_err("invalid response "
-				"msglen %d actual %d",
+			pr_err("invalid response msglen %d actual %d\n",
 				phdr->msg_length, urb->actual_length);
 		}
 	} else
-		sms_err("error, urb status %d, %d bytes",
+		pr_err("error, urb status %d, %d bytes\n",
 			urb->status, urb->actual_length);
 
 
@@ -153,7 +147,7 @@
 	if (!surb->cb) {
 		surb->cb = smscore_getbuffer(dev->coredev);
 		if (!surb->cb) {
-			sms_err("smscore_getbuffer(...) returned NULL");
+			pr_err("smscore_getbuffer(...) returned NULL\n");
 			return -ENOMEM;
 		}
 	}
@@ -194,7 +188,7 @@
 	for (i = 0; i < MAX_URBS; i++) {
 		rc = smsusb_submit_urb(dev, &dev->surbs[i]);
 		if (rc < 0) {
-			sms_err("smsusb_submit_urb(...) failed");
+			pr_err("smsusb_submit_urb(...) failed\n");
 			smsusb_stop_streaming(dev);
 			break;
 		}
@@ -210,11 +204,11 @@
 	int dummy;
 
 	if (dev->state != SMSUSB_ACTIVE) {
-		sms_debug("Device not active yet");
+		pr_debug("Device not active yet\n");
 		return -ENOENT;
 	}
 
-	sms_debug("sending %s(%d) size: %d",
+	pr_debug("sending %s(%d) size: %d\n",
 		  smscore_translate_msg(phdr->msg_type), phdr->msg_type,
 		  phdr->msg_length);
 
@@ -249,7 +243,7 @@
 		id = sms_get_board(board_id)->default_mode;
 
 	if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) {
-		sms_err("invalid firmware id specified %d", id);
+		pr_err("invalid firmware id specified %d\n", id);
 		return -EINVAL;
 	}
 
@@ -257,13 +251,13 @@
 
 	rc = request_firmware(&fw, fw_filename, &udev->dev);
 	if (rc < 0) {
-		sms_warn("failed to open \"%s\" mode %d, "
-			 "trying again with default firmware", fw_filename, id);
+		pr_warn("failed to open '%s' mode %d, trying again with default firmware\n",
+			fw_filename, id);
 
 		fw_filename = smsusb1_fw_lkup[id];
 		rc = request_firmware(&fw, fw_filename, &udev->dev);
 		if (rc < 0) {
-			sms_warn("failed to open \"%s\" mode %d",
+			pr_warn("failed to open '%s' mode %d\n",
 				 fw_filename, id);
 
 			return rc;
@@ -277,14 +271,14 @@
 		rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2),
 				  fw_buffer, fw->size, &dummy, 1000);
 
-		sms_info("sent %zu(%d) bytes, rc %d", fw->size, dummy, rc);
+		pr_debug("sent %zu(%d) bytes, rc %d\n", fw->size, dummy, rc);
 
 		kfree(fw_buffer);
 	} else {
-		sms_err("failed to allocate firmware buffer");
+		pr_err("failed to allocate firmware buffer\n");
 		rc = -ENOMEM;
 	}
-	sms_info("read FW %s, size=%zu", fw_filename, fw->size);
+	pr_debug("read FW %s, size=%zu\n", fw_filename, fw->size);
 
 	release_firmware(fw);
 
@@ -300,7 +294,7 @@
 
 	if (!product_string) {
 		product_string = "none";
-		sms_err("product string not found");
+		pr_err("product string not found\n");
 	} else if (strstr(product_string, "DVBH"))
 		*mode = 1;
 	else if (strstr(product_string, "BDA"))
@@ -310,7 +304,7 @@
 	else if (strstr(product_string, "TDMB"))
 		*mode = 2;
 
-	sms_info("%d \"%s\"", *mode, product_string);
+	pr_debug("%d \"%s\"\n", *mode, product_string);
 }
 
 static int smsusb1_setmode(void *context, int mode)
@@ -319,7 +313,7 @@
 			     sizeof(struct sms_msg_hdr), 0 };
 
 	if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) {
-		sms_err("invalid firmware id specified %d", mode);
+		pr_err("invalid firmware id specified %d\n", mode);
 		return -EINVAL;
 	}
 
@@ -339,25 +333,61 @@
 		if (dev->coredev)
 			smscore_unregister_device(dev->coredev);
 
-		sms_info("device 0x%p destroyed", dev);
+		pr_debug("device 0x%p destroyed\n", dev);
 		kfree(dev);
 	}
 
 	usb_set_intfdata(intf, NULL);
 }
 
+static void *siano_media_device_register(struct smsusb_device_t *dev,
+					int board_id)
+{
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+	struct media_device *mdev;
+	struct usb_device *udev = dev->udev;
+	struct sms_board *board = sms_get_board(board_id);
+	int ret;
+
+	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+	if (!mdev)
+		return NULL;
+
+	mdev->dev = &udev->dev;
+	strlcpy(mdev->model, board->name, sizeof(mdev->model));
+	if (udev->serial)
+		strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
+	strcpy(mdev->bus_info, udev->devpath);
+	mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+	mdev->driver_version = LINUX_VERSION_CODE;
+
+	ret = media_device_register(mdev);
+	if (ret) {
+		pr_err("Couldn't create a media device. Error: %d\n",
+			ret);
+		kfree(mdev);
+		return NULL;
+	}
+
+	pr_info("media controller created\n");
+
+	return mdev;
+#else
+	return NULL;
+#endif
+}
+
 static int smsusb_init_device(struct usb_interface *intf, int board_id)
 {
 	struct smsdevice_params_t params;
 	struct smsusb_device_t *dev;
+	void *mdev;
 	int i, rc;
 
 	/* create device object */
 	dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL);
-	if (!dev) {
-		sms_err("kzalloc(sizeof(struct smsusb_device_t) failed");
+	if (!dev)
 		return -ENOMEM;
-	}
 
 	memset(&params, 0, sizeof(params));
 	usb_set_intfdata(intf, dev);
@@ -374,7 +404,7 @@
 		params.detectmode_handler = smsusb1_detectmode;
 		break;
 	case SMS_UNKNOWN_TYPE:
-		sms_err("Unspecified sms device type!");
+		pr_err("Unspecified sms device type!\n");
 		/* fall-thru */
 	default:
 		dev->buffer_size = USB2_BUFFER_SIZE;
@@ -393,7 +423,7 @@
 			dev->out_ep = intf->cur_altsetting->endpoint[i].desc.bEndpointAddress;
 	}
 
-	sms_info("in_ep = %02x, out_ep = %02x",
+	pr_debug("in_ep = %02x, out_ep = %02x\n",
 		dev->in_ep, dev->out_ep);
 
 	params.device = &dev->udev->dev;
@@ -403,11 +433,17 @@
 	params.context = dev;
 	usb_make_path(dev->udev, params.devpath, sizeof(params.devpath));
 
+	mdev = siano_media_device_register(dev, board_id);
+
 	/* register in smscore */
-	rc = smscore_register_device(&params, &dev->coredev);
+	rc = smscore_register_device(&params, &dev->coredev, mdev);
 	if (rc < 0) {
-		sms_err("smscore_register_device(...) failed, rc %d", rc);
+		pr_err("smscore_register_device(...) failed, rc %d\n", rc);
 		smsusb_term_device(intf);
+#ifdef CPTCFG_MEDIA_CONTROLLER_DVB
+		media_device_unregister(mdev);
+#endif
+		kfree(mdev);
 		return rc;
 	}
 
@@ -421,10 +457,10 @@
 		usb_init_urb(&dev->surbs[i].urb);
 	}
 
-	sms_info("smsusb_start_streaming(...).");
+	pr_debug("smsusb_start_streaming(...).\n");
 	rc = smsusb_start_streaming(dev);
 	if (rc < 0) {
-		sms_err("smsusb_start_streaming(...) failed");
+		pr_err("smsusb_start_streaming(...) failed\n");
 		smsusb_term_device(intf);
 		return rc;
 	}
@@ -433,12 +469,12 @@
 
 	rc = smscore_start_device(dev->coredev);
 	if (rc < 0) {
-		sms_err("smscore_start_device(...) failed");
+		pr_err("smscore_start_device(...) failed\n");
 		smsusb_term_device(intf);
 		return rc;
 	}
 
-	sms_info("device 0x%p created", dev);
+	pr_debug("device 0x%p created\n", dev);
 
 	return rc;
 }
@@ -450,13 +486,13 @@
 	char devpath[32];
 	int i, rc;
 
-	sms_info("board id=%lu, interface number %d",
+	pr_info("board id=%lu, interface number %d\n",
 		 id->driver_info,
 		 intf->cur_altsetting->desc.bInterfaceNumber);
 
 	if (sms_get_board(id->driver_info)->intf_num !=
 	    intf->cur_altsetting->desc.bInterfaceNumber) {
-		sms_debug("interface %d won't be used. Expecting interface %d to popup",
+		pr_debug("interface %d won't be used. Expecting interface %d to popup\n",
 			intf->cur_altsetting->desc.bInterfaceNumber,
 			sms_get_board(id->driver_info)->intf_num);
 		return -ENODEV;
@@ -467,15 +503,15 @@
 				       intf->cur_altsetting->desc.bInterfaceNumber,
 				       0);
 		if (rc < 0) {
-			sms_err("usb_set_interface failed, rc %d", rc);
+			pr_err("usb_set_interface failed, rc %d\n", rc);
 			return rc;
 		}
 	}
 
-	sms_info("smsusb_probe %d",
+	pr_debug("smsusb_probe %d\n",
 	       intf->cur_altsetting->desc.bInterfaceNumber);
 	for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
-		sms_info("endpoint %d %02x %02x %d", i,
+		pr_debug("endpoint %d %02x %02x %d\n", i,
 		       intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
 		       intf->cur_altsetting->endpoint[i].desc.bmAttributes,
 		       intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
@@ -489,7 +525,7 @@
 	}
 	if ((udev->actconfig->desc.bNumInterfaces == 2) &&
 	    (intf->cur_altsetting->desc.bInterfaceNumber == 0)) {
-		sms_debug("rom interface 0 is not used");
+		pr_debug("rom interface 0 is not used\n");
 		return -ENODEV;
 	}
 
@@ -498,23 +534,25 @@
 
 		snprintf(devpath, sizeof(devpath), "usb\\%d-%s",
 			 udev->bus->busnum, udev->devpath);
-		sms_info("stellar device in cold state was found at %s.", devpath);
+		pr_info("stellar device in cold state was found at %s.\n",
+			devpath);
 		rc = smsusb1_load_firmware(
 				udev, smscore_registry_getmode(devpath),
 				id->driver_info);
 
 		/* This device will reset and gain another USB ID */
 		if (!rc)
-			sms_info("stellar device now in warm state");
+			pr_info("stellar device now in warm state\n");
 		else
-			sms_err("Failed to put stellar in warm state. Error: %d", rc);
+			pr_err("Failed to put stellar in warm state. Error: %d\n",
+			       rc);
 
 		return rc;
 	} else {
 		rc = smsusb_init_device(intf, id->driver_info);
 	}
 
-	sms_info("Device initialized with return code %d", rc);
+	pr_info("Device initialized with return code %d\n", rc);
 	sms_board_load_modules(id->driver_info);
 	return rc;
 }
diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c
index 963eae2..9b53532 100644
--- a/drivers/media/usb/stk1160/stk1160-v4l.c
+++ b/drivers/media/usb/stk1160/stk1160-v4l.c
@@ -38,10 +38,6 @@
 #include "stk1160.h"
 #include "stk1160-reg.h"
 
-static unsigned int vidioc_debug;
-module_param(vidioc_debug, int, 0644);
-MODULE_PARM_DESC(vidioc_debug, "enable debug messages [vidioc]");
-
 static bool keep_buffers;
 module_param(keep_buffers, bool, 0644);
 MODULE_PARM_DESC(keep_buffers, "don't release buffers upon stop streaming");
@@ -244,6 +240,11 @@
 	if (mutex_lock_interruptible(&dev->v4l_lock))
 		return -ERESTARTSYS;
 
+	/*
+	 * Once URBs are cancelled, the URB complete handler
+	 * won't be running. This is required to safely release the
+	 * current buffer (dev->isoc_ctl.buf).
+	 */
 	stk1160_cancel_isoc(dev);
 
 	/*
@@ -624,8 +625,16 @@
 		stk1160_info("buffer [%p/%d] aborted\n",
 				buf, buf->vb.v4l2_buf.index);
 	}
-	/* It's important to clear current buffer */
-	dev->isoc_ctl.buf = NULL;
+
+	/* It's important to release the current buffer */
+	if (dev->isoc_ctl.buf) {
+		buf = dev->isoc_ctl.buf;
+		dev->isoc_ctl.buf = NULL;
+
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+		stk1160_info("buffer [%p/%d] aborted\n",
+				buf, buf->vb.v4l2_buf.index);
+	}
 	spin_unlock_irqrestore(&dev->buf_lock, flags);
 }
 
@@ -659,7 +668,6 @@
 
 	/* Initialize video_device with a template structure */
 	dev->vdev = v4l_template;
-	dev->vdev.debug = vidioc_debug;
 	dev->vdev.queue = &dev->vb_vidq;
 
 	/*
diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c
index 3588dc3..c21c4c0 100644
--- a/drivers/media/usb/stkwebcam/stk-webcam.c
+++ b/drivers/media/usb/stkwebcam/stk-webcam.c
@@ -556,10 +556,8 @@
 	nbufs = dev->n_sbufs;
 	dev->n_sbufs = 0;
 	spin_unlock_irqrestore(&dev->spinlock, flags);
-	for (i = 0; i < nbufs; i++) {
-		if (dev->sio_bufs[i].buffer != NULL)
-			vfree(dev->sio_bufs[i].buffer);
-	}
+	for (i = 0; i < nbufs; i++)
+		vfree(dev->sio_bufs[i].buffer);
 	kfree(dev->sio_bufs);
 	dev->sio_bufs = NULL;
 	return 0;
@@ -1262,7 +1260,6 @@
 
 	dev->vdev = stk_v4l_data;
 	dev->vdev.lock = &dev->lock;
-	dev->vdev.debug = debug;
 	dev->vdev.v4l2_dev = &dev->v4l2_dev;
 	video_set_drvdata(&dev->vdev, dev);
 	err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c
index 793577f..77ce9ef 100644
--- a/drivers/media/usb/tm6000/tm6000-video.c
+++ b/drivers/media/usb/tm6000/tm6000-video.c
@@ -941,7 +941,7 @@
 
 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
 	if (NULL == fmt) {
-		dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)"
+		dprintk(dev, 2, "Fourcc format (0x%08x)"
 				" invalid.\n", f->fmt.pix.pixelformat);
 		return -EINVAL;
 	}
@@ -1576,7 +1576,7 @@
 	.name		= "tm6000",
 	.fops           = &tm6000_fops,
 	.ioctl_ops      = &video_ioctl_ops,
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
 	.tvnorms        = TM6000_STD,
 };
 
@@ -1609,26 +1609,19 @@
  * ------------------------------------------------------------------
  */
 
-static struct video_device *vdev_init(struct tm6000_core *dev,
+static void vdev_init(struct tm6000_core *dev,
+		struct video_device *vfd,
 		const struct video_device
 		*template, const char *type_name)
 {
-	struct video_device *vfd;
-
-	vfd = video_device_alloc();
-	if (NULL == vfd)
-		return NULL;
-
 	*vfd = *template;
 	vfd->v4l2_dev = &dev->v4l2_dev;
-	vfd->release = video_device_release;
-	vfd->debug = tm6000_debug;
+	vfd->release = video_device_release_empty;
 	vfd->lock = &dev->lock;
 
 	snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
 
 	video_set_drvdata(vfd, dev);
-	return vfd;
 }
 
 int tm6000_v4l2_register(struct tm6000_core *dev)
@@ -1659,62 +1652,46 @@
 	if (ret)
 		goto free_ctrl;
 
-	dev->vfd = vdev_init(dev, &tm6000_template, "video");
+	vdev_init(dev, &dev->vfd, &tm6000_template, "video");
 
-	if (!dev->vfd) {
-		printk(KERN_INFO "%s: can't register video device\n",
-		       dev->name);
-		ret = -ENOMEM;
-		goto free_ctrl;
-	}
-	dev->vfd->ctrl_handler = &dev->ctrl_handler;
+	dev->vfd.ctrl_handler = &dev->ctrl_handler;
 
 	/* init video dma queues */
 	INIT_LIST_HEAD(&dev->vidq.active);
 	INIT_LIST_HEAD(&dev->vidq.queued);
 
-	ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr);
+	ret = video_register_device(&dev->vfd, VFL_TYPE_GRABBER, video_nr);
 
 	if (ret < 0) {
 		printk(KERN_INFO "%s: can't register video device\n",
 		       dev->name);
-		video_device_release(dev->vfd);
-		dev->vfd = NULL;
 		goto free_ctrl;
 	}
 
 	printk(KERN_INFO "%s: registered device %s\n",
-	       dev->name, video_device_node_name(dev->vfd));
+	       dev->name, video_device_node_name(&dev->vfd));
 
 	if (dev->caps.has_radio) {
-		dev->radio_dev = vdev_init(dev, &tm6000_radio_template,
+		vdev_init(dev, &dev->radio_dev, &tm6000_radio_template,
 							   "radio");
-		if (!dev->radio_dev) {
-			printk(KERN_INFO "%s: can't register radio device\n",
-			       dev->name);
-			ret = -ENXIO;
-			goto unreg_video;
-		}
-
-		dev->radio_dev->ctrl_handler = &dev->radio_ctrl_handler;
-		ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+		dev->radio_dev.ctrl_handler = &dev->radio_ctrl_handler;
+		ret = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO,
 					    radio_nr);
 		if (ret < 0) {
 			printk(KERN_INFO "%s: can't register radio device\n",
 			       dev->name);
-			video_device_release(dev->radio_dev);
 			goto unreg_video;
 		}
 
 		printk(KERN_INFO "%s: registered device %s\n",
-		       dev->name, video_device_node_name(dev->radio_dev));
+		       dev->name, video_device_node_name(&dev->radio_dev));
 	}
 
 	printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret);
 	return ret;
 
 unreg_video:
-	video_unregister_device(dev->vfd);
+	video_unregister_device(&dev->vfd);
 free_ctrl:
 	v4l2_ctrl_handler_free(&dev->ctrl_handler);
 	v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
@@ -1723,19 +1700,12 @@
 
 int tm6000_v4l2_unregister(struct tm6000_core *dev)
 {
-	video_unregister_device(dev->vfd);
+	video_unregister_device(&dev->vfd);
 
 	/* if URB buffers are still allocated free them now */
 	tm6000_free_urb_buffers(dev);
 
-	if (dev->radio_dev) {
-		if (video_is_registered(dev->radio_dev))
-			video_unregister_device(dev->radio_dev);
-		else
-			video_device_release(dev->radio_dev);
-		dev->radio_dev = NULL;
-	}
-
+	video_unregister_device(&dev->radio_dev);
 	return 0;
 }
 
diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h
index 08bd074..f212794 100644
--- a/drivers/media/usb/tm6000/tm6000.h
+++ b/drivers/media/usb/tm6000/tm6000.h
@@ -220,8 +220,8 @@
 	struct tm6000_fh		*resources;	/* Points to fh that is streaming */
 	bool				is_res_read;
 
-	struct video_device		*vfd;
-	struct video_device		*radio_dev;
+	struct video_device		vfd;
+	struct video_device		radio_dev;
 	struct tm6000_dmaqueue		vidq;
 	struct v4l2_device		v4l2_dev;
 	struct v4l2_ctrl_handler	ctrl_handler;
diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c
index 302aa07..44b0c28 100644
--- a/drivers/media/usb/usbvision/usbvision-core.c
+++ b/drivers/media/usb/usbvision/usbvision-core.c
@@ -2194,9 +2194,8 @@
 
 void usbvision_init_power_off_timer(struct usb_usbvision *usbvision)
 {
-	init_timer(&usbvision->power_off_timer);
-	usbvision->power_off_timer.data = (long)usbvision;
-	usbvision->power_off_timer.function = usbvision_power_off_timer;
+	setup_timer(&usbvision->power_off_timer, usbvision_power_off_timer,
+		    (unsigned long)usbvision);
 }
 
 void usbvision_set_power_off_timer(struct usb_usbvision *usbvision)
@@ -2502,11 +2501,3 @@
 	usbvision_set_audio(usbvision, audio[channel]);
 	return 0;
 }
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/usbvision/usbvision-i2c.c b/drivers/media/usb/usbvision/usbvision-i2c.c
index ba262a3..26dbcb1 100644
--- a/drivers/media/usb/usbvision/usbvision-i2c.c
+++ b/drivers/media/usb/usbvision/usbvision-i2c.c
@@ -445,11 +445,3 @@
 	.owner = THIS_MODULE,
 	.name              = "usbvision",
 };
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index 282f11c..45f4e77 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -471,7 +471,7 @@
 	/* NT100x has a 8-bit register space */
 	err_code = usbvision_read_reg(usbvision, reg->reg&0xff);
 	if (err_code < 0) {
-		dev_err(&usbvision->vdev->dev,
+		dev_err(&usbvision->vdev.dev,
 			"%s: VIDIOC_DBG_G_REGISTER failed: error %d\n",
 				__func__, err_code);
 		return err_code;
@@ -490,7 +490,7 @@
 	/* NT100x has a 8-bit register space */
 	err_code = usbvision_write_reg(usbvision, reg->reg & 0xff, reg->val);
 	if (err_code < 0) {
-		dev_err(&usbvision->vdev->dev,
+		dev_err(&usbvision->vdev.dev,
 			"%s: VIDIOC_DBG_S_REGISTER failed: error %d\n",
 				__func__, err_code);
 		return err_code;
@@ -1157,7 +1157,7 @@
 	if (mutex_lock_interruptible(&usbvision->v4l2_lock))
 		return -ERESTARTSYS;
 	if (usbvision->user) {
-		dev_err(&usbvision->rdev->dev,
+		dev_err(&usbvision->rdev.dev,
 			"%s: Someone tried to open an already opened USBVision Radio!\n",
 				__func__);
 		err_code = -EBUSY;
@@ -1280,7 +1280,7 @@
 	.fops		= &usbvision_fops,
 	.ioctl_ops	= &usbvision_ioctl_ops,
 	.name           = "usbvision-video",
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
 	.tvnorms        = USBVISION_NORMS,
 };
 
@@ -1312,58 +1312,46 @@
 static struct video_device usbvision_radio_template = {
 	.fops		= &usbvision_radio_fops,
 	.name		= "usbvision-radio",
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
 	.ioctl_ops	= &usbvision_radio_ioctl_ops,
 };
 
 
-static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision,
-					struct video_device *vdev_template,
-					char *name)
+static void usbvision_vdev_init(struct usb_usbvision *usbvision,
+				struct video_device *vdev,
+				const struct video_device *vdev_template,
+				const char *name)
 {
 	struct usb_device *usb_dev = usbvision->dev;
-	struct video_device *vdev;
 
 	if (usb_dev == NULL) {
 		dev_err(&usbvision->dev->dev,
 			"%s: usbvision->dev is not set\n", __func__);
-		return NULL;
+		return;
 	}
 
-	vdev = video_device_alloc();
-	if (NULL == vdev)
-		return NULL;
 	*vdev = *vdev_template;
 	vdev->lock = &usbvision->v4l2_lock;
 	vdev->v4l2_dev = &usbvision->v4l2_dev;
 	snprintf(vdev->name, sizeof(vdev->name), "%s", name);
 	video_set_drvdata(vdev, usbvision);
-	return vdev;
 }
 
 /* unregister video4linux devices */
 static void usbvision_unregister_video(struct usb_usbvision *usbvision)
 {
 	/* Radio Device: */
-	if (usbvision->rdev) {
+	if (video_is_registered(&usbvision->rdev)) {
 		PDEBUG(DBG_PROBE, "unregister %s [v4l2]",
-		       video_device_node_name(usbvision->rdev));
-		if (video_is_registered(usbvision->rdev))
-			video_unregister_device(usbvision->rdev);
-		else
-			video_device_release(usbvision->rdev);
-		usbvision->rdev = NULL;
+		       video_device_node_name(&usbvision->rdev));
+		video_unregister_device(&usbvision->rdev);
 	}
 
 	/* Video Device: */
-	if (usbvision->vdev) {
+	if (video_is_registered(&usbvision->vdev)) {
 		PDEBUG(DBG_PROBE, "unregister %s [v4l2]",
-		       video_device_node_name(usbvision->vdev));
-		if (video_is_registered(usbvision->vdev))
-			video_unregister_device(usbvision->vdev);
-		else
-			video_device_release(usbvision->vdev);
-		usbvision->vdev = NULL;
+		       video_device_node_name(&usbvision->vdev));
+		video_unregister_device(&usbvision->vdev);
 	}
 }
 
@@ -1371,28 +1359,22 @@
 static int usbvision_register_video(struct usb_usbvision *usbvision)
 {
 	/* Video Device: */
-	usbvision->vdev = usbvision_vdev_init(usbvision,
-					      &usbvision_video_template,
-					      "USBVision Video");
-	if (usbvision->vdev == NULL)
-		goto err_exit;
-	if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
+	usbvision_vdev_init(usbvision, &usbvision->vdev,
+			      &usbvision_video_template, "USBVision Video");
+	if (video_register_device(&usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
 		goto err_exit;
 	printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n",
-	       usbvision->nr, video_device_node_name(usbvision->vdev));
+	       usbvision->nr, video_device_node_name(&usbvision->vdev));
 
 	/* Radio Device: */
 	if (usbvision_device_data[usbvision->dev_model].radio) {
 		/* usbvision has radio */
-		usbvision->rdev = usbvision_vdev_init(usbvision,
-						      &usbvision_radio_template,
-						      "USBVision Radio");
-		if (usbvision->rdev == NULL)
-			goto err_exit;
-		if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr) < 0)
+		usbvision_vdev_init(usbvision, &usbvision->rdev,
+			      &usbvision_radio_template, "USBVision Radio");
+		if (video_register_device(&usbvision->rdev, VFL_TYPE_RADIO, radio_nr) < 0)
 			goto err_exit;
 		printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n",
-		       usbvision->nr, video_device_node_name(usbvision->rdev));
+		       usbvision->nr, video_device_node_name(&usbvision->rdev));
 	}
 	/* all done */
 	return 0;
@@ -1461,7 +1443,7 @@
 
 	usbvision->initialized = 0;
 
-	usbvision_remove_sysfs(usbvision->vdev);
+	usbvision_remove_sysfs(&usbvision->vdev);
 	usbvision_unregister_video(usbvision);
 	kfree(usbvision->alt_max_pkt_size);
 
@@ -1525,7 +1507,7 @@
 	const struct usb_host_interface *interface;
 	struct usb_usbvision *usbvision = NULL;
 	const struct usb_endpoint_descriptor *endpoint;
-	int model, i;
+	int model, i, ret;
 
 	PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u",
 				dev->descriptor.idVendor,
@@ -1534,7 +1516,8 @@
 	model = devid->driver_info;
 	if (model < 0 || model >= usbvision_device_data_size) {
 		PDEBUG(DBG_PROBE, "model out of bounds %d", model);
-		return -ENODEV;
+		ret = -ENODEV;
+		goto err_usb;
 	}
 	printk(KERN_INFO "%s: %s found\n", __func__,
 				usbvision_device_data[model].model_string);
@@ -1549,18 +1532,21 @@
 		    __func__, ifnum);
 		dev_err(&intf->dev, "%s: Endpoint attributes %d",
 		    __func__, endpoint->bmAttributes);
-		return -ENODEV;
+		ret = -ENODEV;
+		goto err_usb;
 	}
 	if (usb_endpoint_dir_out(endpoint)) {
 		dev_err(&intf->dev, "%s: interface %d. has ISO OUT endpoint!\n",
 		    __func__, ifnum);
-		return -ENODEV;
+		ret = -ENODEV;
+		goto err_usb;
 	}
 
 	usbvision = usbvision_alloc(dev, intf);
 	if (usbvision == NULL) {
 		dev_err(&intf->dev, "%s: couldn't allocate USBVision struct\n", __func__);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto err_usb;
 	}
 
 	if (dev->descriptor.bNumConfigurations > 1)
@@ -1579,8 +1565,8 @@
 	usbvision->alt_max_pkt_size = kmalloc(32 * usbvision->num_alt, GFP_KERNEL);
 	if (usbvision->alt_max_pkt_size == NULL) {
 		dev_err(&intf->dev, "usbvision: out of memory!\n");
-		usbvision_release(usbvision);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto err_pkt;
 	}
 
 	for (i = 0; i < usbvision->num_alt; i++) {
@@ -1611,10 +1597,16 @@
 	usbvision_configure_video(usbvision);
 	usbvision_register_video(usbvision);
 
-	usbvision_create_sysfs(usbvision->vdev);
+	usbvision_create_sysfs(&usbvision->vdev);
 
 	PDEBUG(DBG_PROBE, "success");
 	return 0;
+
+err_pkt:
+	usbvision_release(usbvision);
+err_usb:
+	usb_put_dev(dev);
+	return ret;
 }
 
 
@@ -1716,11 +1708,3 @@
 
 module_init(usbvision_init);
 module_exit(usbvision_exit);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h
index a0c73cf..140a1f6 100644
--- a/drivers/media/usb/usbvision/usbvision.h
+++ b/drivers/media/usb/usbvision/usbvision.h
@@ -357,8 +357,8 @@
 
 struct usb_usbvision {
 	struct v4l2_device v4l2_dev;
-	struct video_device *vdev;					/* Video Device */
-	struct video_device *rdev;					/* Radio Device */
+	struct video_device vdev;					/* Video Device */
+	struct video_device rdev;					/* Radio Device */
 
 	/* i2c Declaration Section*/
 	struct i2c_adapter i2c_adap;
@@ -517,11 +517,3 @@
 int usbvision_power_on(struct usb_usbvision *usbvision);
 
 #endif									/* __LINUX_USBVISION_H */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 40d6c83..d3c0005 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -138,6 +138,11 @@
 		.fcc		= V4L2_PIX_FMT_RGB565,
 	},
 	{
+		.name		= "BGR 8:8:8 (BGR3)",
+		.guid		= UVC_GUID_FORMAT_BGR3,
+		.fcc		= V4L2_PIX_FMT_BGR24,
+	},
+	{
 		.name		= "H.264",
 		.guid		= UVC_GUID_FORMAT_H264,
 		.fcc		= V4L2_PIX_FMT_H264,
@@ -1664,10 +1669,6 @@
 #ifdef CPTCFG_MEDIA_CONTROLLER
 		uvc_mc_cleanup_entity(entity);
 #endif
-		if (entity->vdev) {
-			video_device_release(entity->vdev);
-			entity->vdev = NULL;
-		}
 		kfree(entity);
 	}
 
@@ -1712,11 +1713,10 @@
 	atomic_inc(&dev->nstreams);
 
 	list_for_each_entry(stream, &dev->streams, list) {
-		if (stream->vdev == NULL)
+		if (!video_is_registered(&stream->vdev))
 			continue;
 
-		video_unregister_device(stream->vdev);
-		stream->vdev = NULL;
+		video_unregister_device(&stream->vdev);
 
 		uvc_debugfs_cleanup_stream(stream);
 	}
@@ -1731,7 +1731,7 @@
 static int uvc_register_video(struct uvc_device *dev,
 		struct uvc_streaming *stream)
 {
-	struct video_device *vdev;
+	struct video_device *vdev = &stream->vdev;
 	int ret;
 
 	/* Initialize the video buffers queue. */
@@ -1752,12 +1752,6 @@
 	uvc_debugfs_init_stream(stream);
 
 	/* Register the device with V4L. */
-	vdev = video_device_alloc();
-	if (vdev == NULL) {
-		uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",
-			   ret);
-		return -ENOMEM;
-	}
 
 	/* We already hold a reference to dev->udev. The video device will be
 	 * unregistered before the reference is released, so we don't need to
@@ -1775,15 +1769,12 @@
 	/* Set the driver data before calling video_register_device, otherwise
 	 * uvc_v4l2_open might race us.
 	 */
-	stream->vdev = vdev;
 	video_set_drvdata(vdev, stream);
 
 	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
 	if (ret < 0) {
 		uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
 			   ret);
-		stream->vdev = NULL;
-		video_device_release(vdev);
 		return ret;
 	}
 
@@ -1822,7 +1813,7 @@
 		if (ret < 0)
 			return ret;
 
-		term->vdev = stream->vdev;
+		term->vdev = &stream->vdev;
 	}
 
 	return 0;
@@ -2456,6 +2447,14 @@
 	  .bInterfaceProtocol	= 0,
 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX
 				| UVC_QUIRK_PROBE_EXTRAFIELDS },
+	/* Aveo Technology USB 2.0 Camera (Tasco USB Microscope) */
+	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
+				| USB_DEVICE_ID_MATCH_INT_INFO,
+	  .idVendor		= 0x1871,
+	  .idProduct		= 0x0516,
+	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0 },
 	/* Ecamm Pico iMage */
 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
 				| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index cc96072..87a19f3 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -143,20 +143,6 @@
 		uvc_video_clock_update(stream, &vb->v4l2_buf, buf);
 }
 
-static void uvc_wait_prepare(struct vb2_queue *vq)
-{
-	struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
-
-	mutex_unlock(&queue->mutex);
-}
-
-static void uvc_wait_finish(struct vb2_queue *vq)
-{
-	struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
-
-	mutex_lock(&queue->mutex);
-}
-
 static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
 	struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
@@ -195,8 +181,8 @@
 	.buf_prepare = uvc_buffer_prepare,
 	.buf_queue = uvc_buffer_queue,
 	.buf_finish = uvc_buffer_finish,
-	.wait_prepare = uvc_wait_prepare,
-	.wait_finish = uvc_wait_finish,
+	.wait_prepare = vb2_ops_wait_prepare,
+	.wait_finish = vb2_ops_wait_finish,
 	.start_streaming = uvc_start_streaming,
 	.stop_streaming = uvc_stop_streaming,
 };
@@ -214,6 +200,7 @@
 	queue->queue.mem_ops = &vb2_vmalloc_memops;
 	queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
 		| V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
+	queue->queue.lock = &queue->mutex;
 	ret = vb2_queue_init(&queue->queue);
 	if (ret)
 		return ret;
@@ -319,25 +306,14 @@
 
 int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
 {
-	int ret;
-
-	mutex_lock(&queue->mutex);
-	ret = vb2_mmap(&queue->queue, vma);
-	mutex_unlock(&queue->mutex);
-
-	return ret;
+	return vb2_mmap(&queue->queue, vma);
 }
 
 #ifndef CONFIG_MMU
 unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
 		unsigned long pgoff)
 {
-	unsigned long ret;
-
-	mutex_lock(&queue->mutex);
-	ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
-	mutex_unlock(&queue->mutex);
-	return ret;
+	return vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
 }
 #endif
 
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 9c5cbcf..c4b1ac6 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -13,7 +13,6 @@
 
 #include <linux/compat.h>
 #include <linux/kernel.h>
-#include <linux/version.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -512,7 +511,7 @@
 	stream->dev->users++;
 	mutex_unlock(&stream->dev->lock);
 
-	v4l2_fh_init(&handle->vfh, stream->vdev);
+	v4l2_fh_init(&handle->vfh, &stream->vdev);
 	v4l2_fh_add(&handle->vfh);
 	handle->chain = stream->chain;
 	handle->stream = stream;
@@ -883,6 +882,35 @@
 	return uvc_query_v4l2_ctrl(chain, qc);
 }
 
+static int uvc_ioctl_query_ext_ctrl(struct file *file, void *fh,
+				    struct v4l2_query_ext_ctrl *qec)
+{
+	struct uvc_fh *handle = fh;
+	struct uvc_video_chain *chain = handle->chain;
+	struct v4l2_queryctrl qc = { qec->id };
+	int ret;
+
+	ret = uvc_query_v4l2_ctrl(chain, &qc);
+	if (ret)
+		return ret;
+
+	qec->id = qc.id;
+	qec->type = qc.type;
+	strlcpy(qec->name, qc.name, sizeof(qec->name));
+	qec->minimum = qc.minimum;
+	qec->maximum = qc.maximum;
+	qec->step = qc.step;
+	qec->default_value = qc.default_value;
+	qec->flags = qc.flags;
+	qec->elem_size = 4;
+	qec->elems = 1;
+	qec->nr_of_dims = 0;
+	memset(qec->dims, 0, sizeof(qec->dims));
+	memset(qec->reserved, 0, sizeof(qec->reserved));
+
+	return 0;
+}
+
 static int uvc_ioctl_g_ctrl(struct file *file, void *fh,
 			    struct v4l2_control *ctrl)
 {
@@ -1019,26 +1047,37 @@
 	return uvc_query_v4l2_menu(chain, qm);
 }
 
-static int uvc_ioctl_cropcap(struct file *file, void *fh,
-			     struct v4l2_cropcap *ccap)
+static int uvc_ioctl_g_selection(struct file *file, void *fh,
+				 struct v4l2_selection *sel)
 {
 	struct uvc_fh *handle = fh;
 	struct uvc_streaming *stream = handle->stream;
 
-	if (ccap->type != stream->type)
+	if (sel->type != stream->type)
 		return -EINVAL;
 
-	ccap->bounds.left = 0;
-	ccap->bounds.top = 0;
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		if (stream->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		break;
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+		if (stream->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	sel->r.left = 0;
+	sel->r.top = 0;
 	mutex_lock(&stream->mutex);
-	ccap->bounds.width = stream->cur_frame->wWidth;
-	ccap->bounds.height = stream->cur_frame->wHeight;
+	sel->r.width = stream->cur_frame->wWidth;
+	sel->r.height = stream->cur_frame->wHeight;
 	mutex_unlock(&stream->mutex);
 
-	ccap->defrect = ccap->bounds;
-
-	ccap->pixelaspect.numerator = 1;
-	ccap->pixelaspect.denominator = 1;
 	return 0;
 }
 
@@ -1134,6 +1173,9 @@
 		uvc_simplify_fraction(&fival->discrete.numerator,
 			&fival->discrete.denominator, 8, 333);
 	} else {
+		if (fival->index)
+			return -EINVAL;
+
 		fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
 		fival->stepwise.min.numerator = frame->dwFrameInterval[0];
 		fival->stepwise.min.denominator = 10000000;
@@ -1444,13 +1486,14 @@
 	.vidioc_g_input = uvc_ioctl_g_input,
 	.vidioc_s_input = uvc_ioctl_s_input,
 	.vidioc_queryctrl = uvc_ioctl_queryctrl,
+	.vidioc_query_ext_ctrl = uvc_ioctl_query_ext_ctrl,
 	.vidioc_g_ctrl = uvc_ioctl_g_ctrl,
 	.vidioc_s_ctrl = uvc_ioctl_s_ctrl,
 	.vidioc_g_ext_ctrls = uvc_ioctl_g_ext_ctrls,
 	.vidioc_s_ext_ctrls = uvc_ioctl_s_ext_ctrls,
 	.vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls,
 	.vidioc_querymenu = uvc_ioctl_querymenu,
-	.vidioc_cropcap = uvc_ioctl_cropcap,
+	.vidioc_g_selection = uvc_ioctl_g_selection,
 	.vidioc_g_parm = uvc_ioctl_g_parm,
 	.vidioc_s_parm = uvc_ioctl_s_parm,
 	.vidioc_enum_framesizes = uvc_ioctl_enum_framesizes,
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 9637e8b..20ccc9d 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1734,13 +1734,13 @@
 
 	uvc_video_clock_reset(stream);
 
+	if (!uvc_queue_streaming(&stream->queue))
+		return 0;
+
 	ret = uvc_commit_video(stream, &stream->ctrl);
 	if (ret < 0)
 		return ret;
 
-	if (!uvc_queue_streaming(&stream->queue))
-		return 0;
-
 	return uvc_init_video(stream, GFP_NOIO);
 }
 
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 1683a9c..b0dc0ec 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -109,6 +109,9 @@
 #define UVC_GUID_FORMAT_RGBP \
 	{ 'R',  'G',  'B',  'P', 0x00, 0x00, 0x10, 0x00, \
 	 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_BGR3 \
+	{ 0x7d, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, \
+	 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}
 #define UVC_GUID_FORMAT_M420 \
 	{ 'M',  '4',  '2',  '0', 0x00, 0x00, 0x10, 0x00, \
 	 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
@@ -440,7 +443,7 @@
 struct uvc_streaming {
 	struct list_head list;
 	struct uvc_device *dev;
-	struct video_device *vdev;
+	struct video_device vdev;
 	struct uvc_video_chain *chain;
 	atomic_t active;
 
diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c
index 5c00627..ca85031 100644
--- a/drivers/media/usb/zr364xx/zr364xx.c
+++ b/drivers/media/usb/zr364xx/zr364xx.c
@@ -1454,8 +1454,6 @@
 	cam->vdev.v4l2_dev = &cam->v4l2_dev;
 	cam->vdev.ctrl_handler = &cam->ctrl_handler;
 	video_set_drvdata(&cam->vdev, cam);
-	if (debug)
-		cam->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
 
 	cam->udev = udev;
 
diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c
index f5d4f6f..34fbfc5 100644
--- a/drivers/media/v4l2-core/tuner-core.c
+++ b/drivers/media/v4l2-core/tuner-core.c
@@ -134,6 +134,9 @@
 	unsigned int        type; /* chip type id */
 	void                *config;
 	const char          *name;
+#if defined(CPTCFG_MEDIA_CONTROLLER)
+	struct media_pad	pad;
+#endif
 };
 
 /*
@@ -434,6 +437,10 @@
 		t->name = analog_ops->info.name;
 	}
 
+#ifdef CPTCFG_MEDIA_CONTROLLER
+	t->sd.entity.name = t->name;
+#endif
+
 	tuner_dbg("type set to %s\n", t->name);
 
 	t->mode_mask = new_mode_mask;
@@ -592,6 +599,9 @@
 	struct tuner *t;
 	struct tuner *radio;
 	struct tuner *tv;
+#ifdef CPTCFG_MEDIA_CONTROLLER
+	int ret;
+#endif
 
 	t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
 	if (NULL == t)
@@ -684,6 +694,18 @@
 
 	/* Should be just before return */
 register_client:
+#if defined(CPTCFG_MEDIA_CONTROLLER)
+	t->pad.flags = MEDIA_PAD_FL_SOURCE;
+	t->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_TUNER;
+	t->sd.entity.name = t->name;
+
+	ret = media_entity_init(&t->sd.entity, 1, &t->pad, 0);
+	if (ret < 0) {
+		tuner_err("failed to initialize media entity!\n");
+		kfree(t);
+		return -ENODEV;
+	}
+#endif
 	/* Sets a default mode */
 	if (t->mode_mask & T_ANALOG_TV)
 		t->mode = V4L2_TUNER_ANALOG_TV;
diff --git a/drivers/media/v4l2-core/v4l2-clk.c b/drivers/media/v4l2-core/v4l2-clk.c
index e18cc04..55cd50b 100644
--- a/drivers/media/v4l2-core/v4l2-clk.c
+++ b/drivers/media/v4l2-core/v4l2-clk.c
@@ -9,6 +9,7 @@
  */
 
 #include <linux/atomic.h>
+#include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/errno.h>
 #include <linux/list.h>
@@ -23,17 +24,13 @@
 static DEFINE_MUTEX(clk_lock);
 static LIST_HEAD(clk_list);
 
-static struct v4l2_clk *v4l2_clk_find(const char *dev_id, const char *id)
+static struct v4l2_clk *v4l2_clk_find(const char *dev_id)
 {
 	struct v4l2_clk *clk;
 
-	list_for_each_entry(clk, &clk_list, list) {
-		if (strcmp(dev_id, clk->dev_id))
-			continue;
-
-		if (!id || !clk->id || !strcmp(clk->id, id))
+	list_for_each_entry(clk, &clk_list, list)
+		if (!strcmp(dev_id, clk->dev_id))
 			return clk;
-	}
 
 	return ERR_PTR(-ENODEV);
 }
@@ -41,9 +38,26 @@
 struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id)
 {
 	struct v4l2_clk *clk;
+	struct clk *ccf_clk = clk_get(dev, id);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
+	if (PTR_ERR(ccf_clk) == -EPROBE_DEFER)
+		return ERR_PTR(-EPROBE_DEFER);
+#endif
+
+	if (!IS_ERR_OR_NULL(ccf_clk)) {
+		clk = kzalloc(sizeof(*clk), GFP_KERNEL);
+		if (!clk) {
+			clk_put(ccf_clk);
+			return ERR_PTR(-ENOMEM);
+		}
+		clk->clk = ccf_clk;
+
+		return clk;
+	}
 
 	mutex_lock(&clk_lock);
-	clk = v4l2_clk_find(dev_name(dev), id);
+	clk = v4l2_clk_find(dev_name(dev));
 
 	if (!IS_ERR(clk))
 		atomic_inc(&clk->use_count);
@@ -60,6 +74,12 @@
 	if (IS_ERR(clk))
 		return;
 
+	if (clk->clk) {
+		clk_put(clk->clk);
+		kfree(clk);
+		return;
+	}
+
 	mutex_lock(&clk_lock);
 
 	list_for_each_entry(tmp, &clk_list, list)
@@ -97,8 +117,12 @@
 
 int v4l2_clk_enable(struct v4l2_clk *clk)
 {
-	int ret = v4l2_clk_lock_driver(clk);
+	int ret;
 
+	if (clk->clk)
+		return clk_prepare_enable(clk->clk);
+
+	ret = v4l2_clk_lock_driver(clk);
 	if (ret < 0)
 		return ret;
 
@@ -124,11 +148,14 @@
 {
 	int enable;
 
+	if (clk->clk)
+		return clk_disable_unprepare(clk->clk);
+
 	mutex_lock(&clk->lock);
 
 	enable = --clk->enable;
-	if (WARN(enable < 0, "Unbalanced %s() on %s:%s!\n", __func__,
-		 clk->dev_id, clk->id))
+	if (WARN(enable < 0, "Unbalanced %s() on %s!\n", __func__,
+		 clk->dev_id))
 		clk->enable++;
 	else if (!enable && clk->ops->disable)
 		clk->ops->disable(clk);
@@ -141,8 +168,12 @@
 
 unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk)
 {
-	int ret = v4l2_clk_lock_driver(clk);
+	int ret;
 
+	if (clk->clk)
+		return clk_get_rate(clk->clk);
+
+	ret = v4l2_clk_lock_driver(clk);
 	if (ret < 0)
 		return ret;
 
@@ -161,7 +192,16 @@
 
 int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate)
 {
-	int ret = v4l2_clk_lock_driver(clk);
+	int ret;
+
+	if (clk->clk) {
+		long r = clk_round_rate(clk->clk, rate);
+		if (r < 0)
+			return r;
+		return clk_set_rate(clk->clk, r);
+	}
+
+	ret = v4l2_clk_lock_driver(clk);
 
 	if (ret < 0)
 		return ret;
@@ -181,7 +221,7 @@
 
 struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops,
 				   const char *dev_id,
-				   const char *id, void *priv)
+				   void *priv)
 {
 	struct v4l2_clk *clk;
 	int ret;
@@ -193,9 +233,8 @@
 	if (!clk)
 		return ERR_PTR(-ENOMEM);
 
-	clk->id = kstrdup(id, GFP_KERNEL);
 	clk->dev_id = kstrdup(dev_id, GFP_KERNEL);
-	if ((id && !clk->id) || !clk->dev_id) {
+	if (!clk->dev_id) {
 		ret = -ENOMEM;
 		goto ealloc;
 	}
@@ -205,7 +244,7 @@
 	mutex_init(&clk->lock);
 
 	mutex_lock(&clk_lock);
-	if (!IS_ERR(v4l2_clk_find(dev_id, id))) {
+	if (!IS_ERR(v4l2_clk_find(dev_id))) {
 		mutex_unlock(&clk_lock);
 		ret = -EEXIST;
 		goto eexist;
@@ -217,7 +256,6 @@
 
 eexist:
 ealloc:
-	kfree(clk->id);
 	kfree(clk->dev_id);
 	kfree(clk);
 	return ERR_PTR(ret);
@@ -227,15 +265,14 @@
 void v4l2_clk_unregister(struct v4l2_clk *clk)
 {
 	if (WARN(atomic_read(&clk->use_count),
-		 "%s(): Refusing to unregister ref-counted %s:%s clock!\n",
-		 __func__, clk->dev_id, clk->id))
+		 "%s(): Refusing to unregister ref-counted %s clock!\n",
+		 __func__, clk->dev_id))
 		return;
 
 	mutex_lock(&clk_lock);
 	list_del(&clk->list);
 	mutex_unlock(&clk_lock);
 
-	kfree(clk->id);
 	kfree(clk->dev_id);
 	kfree(clk);
 }
@@ -253,7 +290,7 @@
 }
 
 struct v4l2_clk *__v4l2_clk_register_fixed(const char *dev_id,
-		const char *id, unsigned long rate, struct module *owner)
+				unsigned long rate, struct module *owner)
 {
 	struct v4l2_clk *clk;
 	struct v4l2_clk_fixed *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -265,7 +302,7 @@
 	priv->ops.get_rate = fixed_get_rate;
 	priv->ops.owner = owner;
 
-	clk = v4l2_clk_register(&priv->ops, dev_id, id, priv);
+	clk = v4l2_clk_register(&priv->ops, dev_id, priv);
 	if (IS_ERR(clk))
 		kfree(priv);
 
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 45c5b47..e3a3468 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -991,7 +991,8 @@
 	case V4L2_CID_AUTO_FOCUS_START:
 	case V4L2_CID_AUTO_FOCUS_STOP:
 		*type = V4L2_CTRL_TYPE_BUTTON;
-		*flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+		*flags |= V4L2_CTRL_FLAG_WRITE_ONLY |
+			  V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
 		*min = *max = *step = *def = 0;
 		break;
 	case V4L2_CID_POWER_LINE_FREQUENCY:
@@ -1172,7 +1173,8 @@
 	case V4L2_CID_FOCUS_RELATIVE:
 	case V4L2_CID_IRIS_RELATIVE:
 	case V4L2_CID_ZOOM_RELATIVE:
-		*flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+		*flags |= V4L2_CTRL_FLAG_WRITE_ONLY |
+			  V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
 		break;
 	case V4L2_CID_FLASH_STROBE_STATUS:
 	case V4L2_CID_AUTO_FOCUS_STATUS:
@@ -1609,6 +1611,19 @@
 
 		if (ctrl == NULL)
 			continue;
+
+		if (ctrl->flags & V4L2_CTRL_FLAG_EXECUTE_ON_WRITE)
+			changed = ctrl_changed = true;
+
+		/*
+		 * Set has_changed to false to avoid generating
+		 * the event V4L2_EVENT_CTRL_CH_VALUE
+		 */
+		if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
+			ctrl->has_changed = false;
+			continue;
+		}
+
 		for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++)
 			ctrl_changed = !ctrl->type_ops->equal(ctrl, idx,
 				ctrl->p_cur, ctrl->p_new);
@@ -1974,7 +1989,8 @@
 
 	sz_extra = 0;
 	if (type == V4L2_CTRL_TYPE_BUTTON)
-		flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+		flags |= V4L2_CTRL_FLAG_WRITE_ONLY |
+			V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
 	else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
 		flags |= V4L2_CTRL_FLAG_READ_ONLY;
 	else if (type == V4L2_CTRL_TYPE_INTEGER64 ||
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 7385fc2..1511f70 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -47,15 +47,15 @@
 }
 static DEVICE_ATTR_RO(index);
 
-static ssize_t debug_show(struct device *cd,
+static ssize_t dev_debug_show(struct device *cd,
 			  struct device_attribute *attr, char *buf)
 {
 	struct video_device *vdev = to_video_device(cd);
 
-	return sprintf(buf, "%i\n", vdev->debug);
+	return sprintf(buf, "%i\n", vdev->dev_debug);
 }
 
-static ssize_t debug_store(struct device *cd, struct device_attribute *attr,
+static ssize_t dev_debug_store(struct device *cd, struct device_attribute *attr,
 			  const char *buf, size_t len)
 {
 	struct video_device *vdev = to_video_device(cd);
@@ -66,10 +66,10 @@
 	if (res)
 		return res;
 
-	vdev->debug = value;
+	vdev->dev_debug = value;
 	return len;
 }
-static DEVICE_ATTR_RW(debug);
+static DEVICE_ATTR_RW(dev_debug);
 
 static ssize_t name_show(struct device *cd,
 			 struct device_attribute *attr, char *buf)
@@ -82,7 +82,7 @@
 
 static struct attribute *video_device_attrs[] = {
 	&dev_attr_name.attr,
-	&dev_attr_debug.attr,
+	&dev_attr_dev_debug.attr,
 	&dev_attr_index.attr,
 	NULL,
 };
@@ -313,7 +313,8 @@
 		return -EINVAL;
 	if (video_is_registered(vdev))
 		ret = vdev->fops->read(filp, buf, sz, off);
-	if (vdev->debug)
+	if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) &&
+	    (vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING))
 		printk(KERN_DEBUG "%s: read: %zd (%d)\n",
 			video_device_node_name(vdev), sz, ret);
 	return ret;
@@ -329,7 +330,8 @@
 		return -EINVAL;
 	if (video_is_registered(vdev))
 		ret = vdev->fops->write(filp, buf, sz, off);
-	if (vdev->debug)
+	if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) &&
+	    (vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING))
 		printk(KERN_DEBUG "%s: write: %zd (%d)\n",
 			video_device_node_name(vdev), sz, ret);
 	return ret;
@@ -344,7 +346,7 @@
 		return DEFAULT_POLLMASK;
 	if (video_is_registered(vdev))
 		res = vdev->fops->poll(filp, poll);
-	if (vdev->debug > 2)
+	if (vdev->dev_debug & V4L2_DEV_DEBUG_POLL)
 		printk(KERN_DEBUG "%s: poll: %08x\n",
 			video_device_node_name(vdev), res);
 	return res;
@@ -364,34 +366,6 @@
 			ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
 		if (lock)
 			mutex_unlock(lock);
-	} else if (vdev->fops->ioctl) {
-		/* This code path is a replacement for the BKL. It is a major
-		 * hack but it will have to do for those drivers that are not
-		 * yet converted to use unlocked_ioctl.
-		 *
-		 * All drivers implement struct v4l2_device, so we use the
-		 * lock defined there to serialize the ioctls.
-		 *
-		 * However, if the driver sleeps, then it blocks all ioctls
-		 * since the lock is still held. This is very common for
-		 * VIDIOC_DQBUF since that normally waits for a frame to arrive.
-		 * As a result any other ioctl calls will proceed very, very
-		 * slowly since each call will have to wait for the VIDIOC_QBUF
-		 * to finish. Things that should take 0.01s may now take 10-20
-		 * seconds.
-		 *
-		 * The workaround is to *not* take the lock for VIDIOC_DQBUF.
-		 * This actually works OK for videobuf-based drivers, since
-		 * videobuf will take its own internal lock.
-		 */
-		struct mutex *m = &vdev->v4l2_dev->ioctl_lock;
-
-		if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m))
-			return -ERESTARTSYS;
-		if (video_is_registered(vdev))
-			ret = vdev->fops->ioctl(filp, cmd, arg);
-		if (cmd != VIDIOC_DQBUF)
-			mutex_unlock(m);
 	} else
 		ret = -ENOTTY;
 
@@ -413,7 +387,7 @@
 	if (!video_is_registered(vdev))
 		return -ENODEV;
 	ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
-	if (vdev->debug)
+	if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
 		printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n",
 			video_device_node_name(vdev), ret);
 	return ret;
@@ -429,7 +403,7 @@
 		return -ENODEV;
 	if (video_is_registered(vdev))
 		ret = vdev->fops->mmap(filp, vm);
-	if (vdev->debug)
+	if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
 		printk(KERN_DEBUG "%s: mmap (%d)\n",
 			video_device_node_name(vdev), ret);
 	return ret;
@@ -459,7 +433,7 @@
 			ret = -ENODEV;
 	}
 
-	if (vdev->debug)
+	if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
 		printk(KERN_DEBUG "%s: open (%d)\n",
 			video_device_node_name(vdev), ret);
 	/* decrease the refcount in case of an error */
@@ -476,7 +450,7 @@
 
 	if (vdev->fops->release)
 		ret = vdev->fops->release(filp);
-	if (vdev->debug)
+	if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
 		printk(KERN_DEBUG "%s: release\n",
 			video_device_node_name(vdev));
 
@@ -567,10 +541,9 @@
 	/* vfl_type and vfl_dir independent ioctls */
 
 	SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap);
-	if (ops->vidioc_g_priority)
-		set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls);
-	if (ops->vidioc_s_priority)
-		set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls);
+	set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls);
+	set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls);
+
 	/* Note: the control handler can also be passed through the filehandle,
 	   and that can't be tested here. If the bit for these control ioctls
 	   is set, then the ioctl is valid. But if it is 0, then it can still
@@ -647,6 +620,14 @@
 		SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd);
 		SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes);
 		SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals);
+		if (ops->vidioc_g_crop || ops->vidioc_g_selection)
+			set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls);
+		if (ops->vidioc_s_crop || ops->vidioc_s_selection)
+			set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
+		SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
+		SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
+		if (ops->vidioc_cropcap || ops->vidioc_g_selection)
+			set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
 	} else if (is_vbi) {
 		/* vbi specific ioctls */
 		if ((is_rx && (ops->vidioc_g_fmt_vbi_cap ||
@@ -715,14 +696,6 @@
 			SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout);
 			SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout);
 		}
-		if (ops->vidioc_g_crop || ops->vidioc_g_selection)
-			set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls);
-		if (ops->vidioc_s_crop || ops->vidioc_s_selection)
-			set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
-		SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
-		SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
-		if (ops->vidioc_cropcap || ops->vidioc_g_selection)
-			set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
 		if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER &&
 					ops->vidioc_g_std))
 			set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
@@ -950,8 +923,8 @@
 	    vdev->vfl_type != VFL_TYPE_SUBDEV) {
 		vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
 		vdev->entity.name = vdev->name;
-		vdev->entity.info.v4l.major = VIDEO_MAJOR;
-		vdev->entity.info.v4l.minor = vdev->minor;
+		vdev->entity.info.dev.major = VIDEO_MAJOR;
+		vdev->entity.info.dev.minor = vdev->minor;
 		ret = media_device_register_entity(vdev->v4l2_dev->mdev,
 			&vdev->entity);
 		if (ret < 0)
@@ -1043,10 +1016,3 @@
 MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);
-
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
index e59c301..fdc07ef 100644
--- a/drivers/media/v4l2-core/v4l2-device.c
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -37,7 +37,6 @@
 
 	INIT_LIST_HEAD(&v4l2_dev->subdevs);
 	spin_lock_init(&v4l2_dev->lock);
-	mutex_init(&v4l2_dev->ioctl_lock);
 	v4l2_prio_init(&v4l2_dev->prio);
 	kref_init(&v4l2_dev->ref);
 	get_device(dev);
@@ -248,8 +247,8 @@
 			goto clean_up;
 		}
 #if defined(CPTCFG_MEDIA_CONTROLLER)
-		sd->entity.info.v4l.major = VIDEO_MAJOR;
-		sd->entity.info.v4l.minor = vdev->minor;
+		sd->entity.info.dev.major = VIDEO_MAJOR;
+		sd->entity.info.dev.minor = vdev->minor;
 #endif
 		sd->devnode = vdev;
 	}
diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c
index b1d8dbb..c0e9638 100644
--- a/drivers/media/v4l2-core/v4l2-dv-timings.c
+++ b/drivers/media/v4l2-core/v4l2-dv-timings.c
@@ -282,7 +282,7 @@
 			(bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-",
 			bt->vsync, bt->vbackporch);
 	pr_info("%s: pixelclock: %llu\n", dev_prefix, bt->pixelclock);
-	pr_info("%s: flags (0x%x):%s%s%s%s\n", dev_prefix, bt->flags,
+	pr_info("%s: flags (0x%x):%s%s%s%s%s\n", dev_prefix, bt->flags,
 			(bt->flags & V4L2_DV_FL_REDUCED_BLANKING) ?
 			" REDUCED_BLANKING" : "",
 			(bt->flags & V4L2_DV_FL_CAN_REDUCE_FPS) ?
@@ -290,7 +290,9 @@
 			(bt->flags & V4L2_DV_FL_REDUCED_FPS) ?
 			" REDUCED_FPS" : "",
 			(bt->flags & V4L2_DV_FL_HALF_LINE) ?
-			" HALF_LINE" : "");
+			" HALF_LINE" : "",
+			(bt->flags & V4L2_DV_FL_IS_CE_VIDEO) ?
+			" CE_VIDEO" : "");
 	pr_info("%s: standards (0x%x):%s%s%s%s\n", dev_prefix, bt->standards,
 			(bt->standards & V4L2_DV_BT_STD_CEA861) ?  " CEA" : "",
 			(bt->standards & V4L2_DV_BT_STD_DMT) ?  " DMT" : "",
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index e20f1c9..969e7a2 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -257,7 +257,7 @@
 		pr_cont(", width=%u, height=%u, "
 			"pixelformat=%c%c%c%c, field=%s, "
 			"bytesperline=%u, sizeimage=%u, colorspace=%d, "
-			"flags %x, ycbcr_enc=%u, quantization=%u\n",
+			"flags=0x%x, ycbcr_enc=%u, quantization=%u\n",
 			pix->width, pix->height,
 			(pix->pixelformat & 0xff),
 			(pix->pixelformat >>  8) & 0xff,
@@ -273,7 +273,7 @@
 		mp = &p->fmt.pix_mp;
 		pr_cont(", width=%u, height=%u, "
 			"format=%c%c%c%c, field=%s, "
-			"colorspace=%d, num_planes=%u, flags=%x, "
+			"colorspace=%d, num_planes=%u, flags=0x%x, "
 			"ycbcr_enc=%u, quantization=%u\n",
 			mp->width, mp->height,
 			(mp->pixelformat & 0xff),
@@ -901,6 +901,8 @@
 	 */
 	if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
 		return 0;
+	if (c->ctrl_class == 0)
+		return 1;
 	/* Check that all controls are from the same control class. */
 	for (i = 0; i < c->count; i++) {
 		if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
@@ -1046,8 +1048,6 @@
 	struct video_device *vfd;
 	u32 *p = arg;
 
-	if (ops->vidioc_g_priority)
-		return ops->vidioc_g_priority(file, fh, arg);
 	vfd = video_devdata(file);
 	*p = v4l2_prio_max(vfd->prio);
 	return 0;
@@ -1060,9 +1060,9 @@
 	struct v4l2_fh *vfh;
 	u32 *p = arg;
 
-	if (ops->vidioc_s_priority)
-		return ops->vidioc_s_priority(file, fh, *p);
 	vfd = video_devdata(file);
+	if (!test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
+		return -ENOTTY;
 	vfh = file->private_data;
 	return v4l2_prio_change(vfd->prio, &vfh->prio, *p);
 }
@@ -2339,7 +2339,7 @@
 	const struct v4l2_ioctl_info *info;
 	void *fh = file->private_data;
 	struct v4l2_fh *vfh = NULL;
-	int debug = vfd->debug;
+	int dev_debug = vfd->dev_debug;
 	long ret = -ENOTTY;
 
 	if (ops == NULL) {
@@ -2388,11 +2388,15 @@
 	}
 
 done:
-	if (debug) {
+	if (dev_debug & (V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG)) {
+		if (!(dev_debug & V4L2_DEV_DEBUG_STREAMING) &&
+		    (cmd == VIDIOC_QBUF || cmd == VIDIOC_DQBUF))
+			return ret;
+
 		v4l_printk_ioctl(video_device_node_name(vfd), cmd);
 		if (ret < 0)
 			pr_cont(": error %ld", ret);
-		if (debug == V4L2_DEBUG_IOCTL)
+		if (!(dev_debug & V4L2_DEV_DEBUG_IOCTL_ARG))
 			pr_cont("\n");
 		else if (_IOC_DIR(cmd) == _IOC_NONE)
 			info->debug(arg, write_only);
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index 8e3580b..337e1ae 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -97,7 +97,7 @@
  */
 void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
 {
-	struct v4l2_m2m_buffer *b = NULL;
+	struct v4l2_m2m_buffer *b;
 	unsigned long flags;
 
 	spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
@@ -119,7 +119,7 @@
  */
 void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
 {
-	struct v4l2_m2m_buffer *b = NULL;
+	struct v4l2_m2m_buffer *b;
 	unsigned long flags;
 
 	spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c
index b4ed9a9..83143d3 100644
--- a/drivers/media/v4l2-core/v4l2-of.c
+++ b/drivers/media/v4l2-core/v4l2-of.c
@@ -19,11 +19,10 @@
 
 #include <media/v4l2-of.h>
 
-static void v4l2_of_parse_csi_bus(const struct device_node *node,
-				  struct v4l2_of_endpoint *endpoint)
+static int v4l2_of_parse_csi_bus(const struct device_node *node,
+				 struct v4l2_of_endpoint *endpoint)
 {
 	struct v4l2_of_bus_mipi_csi2 *bus = &endpoint->bus.mipi_csi2;
-	u32 data_lanes[ARRAY_SIZE(bus->data_lanes)];
 	struct property *prop;
 	bool have_clk_lane = false;
 	unsigned int flags = 0;
@@ -32,16 +31,34 @@
 	prop = of_find_property(node, "data-lanes", NULL);
 	if (prop) {
 		const __be32 *lane = NULL;
-		int i;
+		unsigned int i;
 
-		for (i = 0; i < ARRAY_SIZE(data_lanes); i++) {
-			lane = of_prop_next_u32(prop, lane, &data_lanes[i]);
+		for (i = 0; i < ARRAY_SIZE(bus->data_lanes); i++) {
+			lane = of_prop_next_u32(prop, lane, &v);
 			if (!lane)
 				break;
+			bus->data_lanes[i] = v;
 		}
 		bus->num_data_lanes = i;
-		while (i--)
-			bus->data_lanes[i] = data_lanes[i];
+	}
+
+	prop = of_find_property(node, "lane-polarities", NULL);
+	if (prop) {
+		const __be32 *polarity = NULL;
+		unsigned int i;
+
+		for (i = 0; i < ARRAY_SIZE(bus->lane_polarities); i++) {
+			polarity = of_prop_next_u32(prop, polarity, &v);
+			if (!polarity)
+				break;
+			bus->lane_polarities[i] = v;
+		}
+
+		if (i < 1 + bus->num_data_lanes /* clock + data */) {
+			pr_warn("%s: too few lane-polarities entries (need %u, got %u)\n",
+				node->full_name, 1 + bus->num_data_lanes, i);
+			return -EINVAL;
+		}
 	}
 
 	if (!of_property_read_u32(node, "clock-lanes", &v)) {
@@ -56,6 +73,8 @@
 
 	bus->flags = flags;
 	endpoint->bus_type = V4L2_MBUS_CSI2;
+
+	return 0;
 }
 
 static void v4l2_of_parse_parallel_bus(const struct device_node *node,
@@ -127,11 +146,15 @@
 int v4l2_of_parse_endpoint(const struct device_node *node,
 			   struct v4l2_of_endpoint *endpoint)
 {
+	int rval;
+
 	of_graph_parse_endpoint(node, &endpoint->base);
 	endpoint->bus_type = 0;
 	memset(&endpoint->bus, 0, sizeof(endpoint->bus));
 
-	v4l2_of_parse_csi_bus(node, endpoint);
+	rval = v4l2_of_parse_csi_bus(node, endpoint);
+	if (rval)
+		return rval;
 	/*
 	 * Parse the parallel video bus properties only if none
 	 * of the MIPI CSI-2 specific properties were found.
@@ -142,3 +165,64 @@
 	return 0;
 }
 EXPORT_SYMBOL(v4l2_of_parse_endpoint);
+
+/**
+ * v4l2_of_parse_link() - parse a link between two endpoints
+ * @node: pointer to the endpoint at the local end of the link
+ * @link: pointer to the V4L2 OF link data structure
+ *
+ * Fill the link structure with the local and remote nodes and port numbers.
+ * The local_node and remote_node fields are set to point to the local and
+ * remote port's parent nodes respectively (the port parent node being the
+ * parent node of the port node if that node isn't a 'ports' node, or the
+ * grand-parent node of the port node otherwise).
+ *
+ * A reference is taken to both the local and remote nodes, the caller must use
+ * v4l2_of_put_link() to drop the references when done with the link.
+ *
+ * Return: 0 on success, or -ENOLINK if the remote endpoint can't be found.
+ */
+int v4l2_of_parse_link(const struct device_node *node,
+		       struct v4l2_of_link *link)
+{
+	struct device_node *np;
+
+	memset(link, 0, sizeof(*link));
+
+	np = of_get_parent(node);
+	of_property_read_u32(np, "reg", &link->local_port);
+	np = of_get_next_parent(np);
+	if (of_node_cmp(np->name, "ports") == 0)
+		np = of_get_next_parent(np);
+	link->local_node = np;
+
+	np = of_parse_phandle(node, "remote-endpoint", 0);
+	if (!np) {
+		of_node_put(link->local_node);
+		return -ENOLINK;
+	}
+
+	np = of_get_parent(np);
+	of_property_read_u32(np, "reg", &link->remote_port);
+	np = of_get_next_parent(np);
+	if (of_node_cmp(np->name, "ports") == 0)
+		np = of_get_next_parent(np);
+	link->remote_node = np;
+
+	return 0;
+}
+EXPORT_SYMBOL(v4l2_of_parse_link);
+
+/**
+ * v4l2_of_put_link() - drop references to nodes in a link
+ * @link: pointer to the V4L2 OF link data structure
+ *
+ * Drop references to the local and remote nodes in the link. This function must
+ * be called on every link parsed with v4l2_of_parse_link().
+ */
+void v4l2_of_put_link(struct v4l2_of_link *link)
+{
+	of_node_put(link->local_node);
+	of_node_put(link->remote_node);
+}
+EXPORT_SYMBOL(v4l2_of_put_link);
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index f13b01e..9683e35 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -93,8 +93,7 @@
 
 err:
 #if defined(CPTCFG_MEDIA_CONTROLLER)
-	if (entity)
-		media_entity_put(entity);
+	media_entity_put(entity);
 #endif
 	v4l2_fh_del(&subdev_fh->vfh);
 	v4l2_fh_exit(&subdev_fh->vfh);
@@ -262,7 +261,7 @@
 		if (rval)
 			return rval;
 
-		return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format);
+		return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh->pad, format);
 	}
 
 	case VIDIOC_SUBDEV_S_FMT: {
@@ -272,7 +271,7 @@
 		if (rval)
 			return rval;
 
-		return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format);
+		return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->pad, format);
 	}
 
 	case VIDIOC_SUBDEV_G_CROP: {
@@ -283,17 +282,13 @@
 		if (rval)
 			return rval;
 
-		rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop);
-		if (rval != -ENOIOCTLCMD)
-			return rval;
-
 		memset(&sel, 0, sizeof(sel));
 		sel.which = crop->which;
 		sel.pad = crop->pad;
 		sel.target = V4L2_SEL_TGT_CROP;
 
 		rval = v4l2_subdev_call(
-			sd, pad, get_selection, subdev_fh, &sel);
+			sd, pad, get_selection, subdev_fh->pad, &sel);
 
 		crop->rect = sel.r;
 
@@ -308,10 +303,6 @@
 		if (rval)
 			return rval;
 
-		rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop);
-		if (rval != -ENOIOCTLCMD)
-			return rval;
-
 		memset(&sel, 0, sizeof(sel));
 		sel.which = crop->which;
 		sel.pad = crop->pad;
@@ -319,7 +310,7 @@
 		sel.r = crop->rect;
 
 		rval = v4l2_subdev_call(
-			sd, pad, set_selection, subdev_fh, &sel);
+			sd, pad, set_selection, subdev_fh->pad, &sel);
 
 		crop->rect = sel.r;
 
@@ -329,20 +320,28 @@
 	case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
 		struct v4l2_subdev_mbus_code_enum *code = arg;
 
+		if (code->which != V4L2_SUBDEV_FORMAT_TRY &&
+		    code->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+			return -EINVAL;
+
 		if (code->pad >= sd->entity.num_pads)
 			return -EINVAL;
 
-		return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh,
+		return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh->pad,
 					code);
 	}
 
 	case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
 		struct v4l2_subdev_frame_size_enum *fse = arg;
 
+		if (fse->which != V4L2_SUBDEV_FORMAT_TRY &&
+		    fse->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+			return -EINVAL;
+
 		if (fse->pad >= sd->entity.num_pads)
 			return -EINVAL;
 
-		return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh,
+		return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh->pad,
 					fse);
 	}
 
@@ -367,10 +366,14 @@
 	case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
 		struct v4l2_subdev_frame_interval_enum *fie = arg;
 
+		if (fie->which != V4L2_SUBDEV_FORMAT_TRY &&
+		    fie->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+			return -EINVAL;
+
 		if (fie->pad >= sd->entity.num_pads)
 			return -EINVAL;
 
-		return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh,
+		return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh->pad,
 					fie);
 	}
 
@@ -382,7 +385,7 @@
 			return rval;
 
 		return v4l2_subdev_call(
-			sd, pad, get_selection, subdev_fh, sel);
+			sd, pad, get_selection, subdev_fh->pad, sel);
 	}
 
 	case VIDIOC_SUBDEV_S_SELECTION: {
@@ -393,7 +396,7 @@
 			return rval;
 
 		return v4l2_subdev_call(
-			sd, pad, set_selection, subdev_fh, sel);
+			sd, pad, set_selection, subdev_fh->pad, sel);
 	}
 
 	case VIDIOC_G_EDID: {
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
index 3ff15f1..f669ced 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -145,12 +145,11 @@
 }
 EXPORT_SYMBOL_GPL(videobuf_to_dma);
 
-void videobuf_dma_init(struct videobuf_dmabuf *dma)
+static void videobuf_dma_init(struct videobuf_dmabuf *dma)
 {
 	memset(dma, 0, sizeof(*dma));
 	dma->magic = MAGIC_DMABUF;
 }
-EXPORT_SYMBOL_GPL(videobuf_dma_init);
 
 static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
 			int direction, unsigned long data, unsigned long size)
@@ -195,7 +194,7 @@
 	return 0;
 }
 
-int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
+static int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
 			   unsigned long data, unsigned long size)
 {
 	int ret;
@@ -206,9 +205,8 @@
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
 
-int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
+static int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
 			     int nr_pages)
 {
 	int i;
@@ -267,9 +265,8 @@
 	return -ENOMEM;
 
 }
-EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);
 
-int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction,
+static int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction,
 			      dma_addr_t addr, int nr_pages)
 {
 	dprintk(1, "init overlay [%d pages @ bus 0x%lx]\n",
@@ -284,9 +281,8 @@
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay);
 
-int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma)
+static int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma)
 {
 	MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
 	BUG_ON(0 == dma->nr_pages);
@@ -328,7 +324,6 @@
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(videobuf_dma_map);
 
 int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma)
 {
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index c20bdf7..30bebd7 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -1255,6 +1255,16 @@
 {
 	unsigned int plane;
 
+	if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+		if (WARN_ON_ONCE(b->bytesused == 0)) {
+			pr_warn_once("use of bytesused == 0 is deprecated and will be removed in the future,\n");
+			if (vb->vb2_queue->allow_zero_bytesused)
+				pr_warn_once("use VIDIOC_DECODER_CMD(V4L2_DEC_CMD_STOP) instead.\n");
+			else
+				pr_warn_once("use the actual size instead.\n");
+		}
+	}
+
 	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
 		if (b->memory == V4L2_MEMORY_USERPTR) {
 			for (plane = 0; plane < vb->num_planes; ++plane) {
@@ -1284,13 +1294,22 @@
 			 * userspace clearly never bothered to set it and
 			 * it's a safe assumption that they really meant to
 			 * use the full plane sizes.
+			 *
+			 * Some drivers, e.g. old codec drivers, use bytesused == 0
+			 * as a way to indicate that streaming is finished.
+			 * In that case, the driver should use the
+			 * allow_zero_bytesused flag to keep old userspace
+			 * applications working.
 			 */
 			for (plane = 0; plane < vb->num_planes; ++plane) {
 				struct v4l2_plane *pdst = &v4l2_planes[plane];
 				struct v4l2_plane *psrc = &b->m.planes[plane];
 
-				pdst->bytesused = psrc->bytesused ?
-					psrc->bytesused : pdst->length;
+				if (vb->vb2_queue->allow_zero_bytesused)
+					pdst->bytesused = psrc->bytesused;
+				else
+					pdst->bytesused = psrc->bytesused ?
+						psrc->bytesused : pdst->length;
 				pdst->data_offset = psrc->data_offset;
 			}
 		}
@@ -1303,6 +1322,11 @@
 		 *
 		 * If bytesused == 0 for the output buffer, then fall back
 		 * to the full buffer size as that's a sensible default.
+		 *
+		 * Some drivers, e.g. old codec drivers, use bytesused == 0 as
+		 * a way to indicate that streaming is finished. In that case,
+		 * the driver should use the allow_zero_bytesused flag to keep
+		 * old userspace applications working.
 		 */
 		if (b->memory == V4L2_MEMORY_USERPTR) {
 			v4l2_planes[0].m.userptr = b->m.userptr;
@@ -1314,10 +1338,13 @@
 			v4l2_planes[0].length = b->length;
 		}
 
-		if (V4L2_TYPE_IS_OUTPUT(b->type))
-			v4l2_planes[0].bytesused = b->bytesused ?
-				b->bytesused : v4l2_planes[0].length;
-		else
+		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+			if (vb->vb2_queue->allow_zero_bytesused)
+				v4l2_planes[0].bytesused = b->bytesused;
+			else
+				v4l2_planes[0].bytesused = b->bytesused ?
+					b->bytesused : v4l2_planes[0].length;
+		} else
 			v4l2_planes[0].bytesused = 0;
 
 	}
@@ -2778,7 +2805,8 @@
 	unsigned int initial_index;
 	unsigned int q_count;
 	unsigned int dq_count;
-	unsigned int flags;
+	unsigned read_once:1;
+	unsigned write_immediately:1;
 };
 
 /**
@@ -2816,14 +2844,16 @@
 	 */
 	count = 1;
 
-	dprintk(3, "setting up file io: mode %s, count %d, flags %08x\n",
-		(read) ? "read" : "write", count, q->io_flags);
+	dprintk(3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n",
+		(read) ? "read" : "write", count, q->fileio_read_once,
+		q->fileio_write_immediately);
 
 	fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL);
 	if (fileio == NULL)
 		return -ENOMEM;
 
-	fileio->flags = q->io_flags;
+	fileio->read_once = q->fileio_read_once;
+	fileio->write_immediately = q->fileio_write_immediately;
 
 	/*
 	 * Request buffers and use MMAP type to force driver
@@ -3046,13 +3076,11 @@
 	/*
 	 * Queue next buffer if required.
 	 */
-	if (buf->pos == buf->size ||
-	   (!read && (fileio->flags & VB2_FILEIO_WRITE_IMMEDIATELY))) {
+	if (buf->pos == buf->size || (!read && fileio->write_immediately)) {
 		/*
 		 * Check if this is the last buffer to read.
 		 */
-		if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) &&
-		    fileio->dq_count == 1) {
+		if (read && fileio->read_once && fileio->dq_count == 1) {
 			dprintk(3, "read limit reached\n");
 			return __vb2_cleanup_fileio(q);
 		}
@@ -3164,27 +3192,26 @@
 			prequeue--;
 		} else {
 			call_void_qop(q, wait_finish, q);
-			ret = vb2_internal_dqbuf(q, &fileio->b, 0);
+			if (!threadio->stop)
+				ret = vb2_internal_dqbuf(q, &fileio->b, 0);
 			call_void_qop(q, wait_prepare, q);
 			dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
 		}
-		if (threadio->stop)
-			break;
-		if (ret)
+		if (ret || threadio->stop)
 			break;
 		try_to_freeze();
 
 		vb = q->bufs[fileio->b.index];
 		if (!(fileio->b.flags & V4L2_BUF_FLAG_ERROR))
-			ret = threadio->fnc(vb, threadio->priv);
-		if (ret)
-			break;
+			if (threadio->fnc(vb, threadio->priv))
+				break;
 		call_void_qop(q, wait_finish, q);
 		if (set_timestamp)
 			v4l2_get_timestamp(&fileio->b.timestamp);
-		ret = vb2_internal_qbuf(q, &fileio->b);
+		if (!threadio->stop)
+			ret = vb2_internal_qbuf(q, &fileio->b);
 		call_void_qop(q, wait_prepare, q);
-		if (ret)
+		if (ret || threadio->stop)
 			break;
 	}
 
@@ -3244,23 +3271,17 @@
 int vb2_thread_stop(struct vb2_queue *q)
 {
 	struct vb2_threadio_data *threadio = q->threadio;
-	struct vb2_fileio_data *fileio = q->fileio;
 	int err;
 
 	if (threadio == NULL)
 		return 0;
-	call_void_qop(q, wait_finish, q);
 	threadio->stop = true;
-	vb2_internal_streamoff(q, q->type);
-	call_void_qop(q, wait_prepare, q);
-	q->fileio = NULL;
-	fileio->req.count = 0;
-	vb2_reqbufs(q, &fileio->req);
-	kfree(fileio);
+	/* Wake up all pending sleeps in the thread */
+	vb2_queue_error(q);
 	err = kthread_stop(threadio->thread);
+	__vb2_cleanup_fileio(q);
 	threadio->thread = NULL;
 	kfree(threadio);
-	q->fileio = NULL;
 	q->threadio = NULL;
 	return err;
 }
@@ -3437,6 +3458,8 @@
 	struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
 	int err = -EBUSY;
 
+	if (!(vdev->queue->io_modes & VB2_WRITE))
+		return -EINVAL;
 	if (lock && mutex_lock_interruptible(lock))
 		return -ERESTARTSYS;
 	if (vb2_queue_is_busy(vdev, file))
@@ -3459,6 +3482,8 @@
 	struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
 	int err = -EBUSY;
 
+	if (!(vdev->queue->io_modes & VB2_READ))
+		return -EINVAL;
 	if (lock && mutex_lock_interruptible(lock))
 		return -ERESTARTSYS;
 	if (vb2_queue_is_busy(vdev, file))
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index d5e28df..b9978dd 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -452,6 +452,12 @@
 {
 	struct vb2_dc_buf *buf = buf_priv;
 	struct dma_buf *dbuf;
+	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+	exp_info.ops = &vb2_dc_dmabuf_ops;
+	exp_info.size = buf->size;
+	exp_info.flags = flags;
+	exp_info.priv = buf;
 
 	if (!buf->sgt_base)
 		buf->sgt_base = vb2_dc_get_base_sgt(buf);
@@ -459,7 +465,7 @@
 	if (WARN_ON(!buf->sgt_base))
 		return NULL;
 
-	dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, flags, NULL);
+	dbuf = dma_buf_export(&exp_info);
 	if (IS_ERR(dbuf))
 		return NULL;
 
@@ -687,8 +693,7 @@
 	}
 
 	/* extract page list from userspace mapping */
-	ret = vb2_dc_get_user_pages(start, pages, n_pages, vma,
-				    dma_dir == DMA_FROM_DEVICE);
+	ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, dma_dir);
 	if (ret) {
 		unsigned long pfn;
 		if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) {
diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c
index 18f9f42..6413241 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c
@@ -594,11 +594,17 @@
 {
 	struct vb2_dma_sg_buf *buf = buf_priv;
 	struct dma_buf *dbuf;
+	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+	exp_info.ops = &vb2_dma_sg_dmabuf_ops;
+	exp_info.size = buf->size;
+	exp_info.flags = flags;
+	exp_info.priv = buf;
 
 	if (WARN_ON(!buf->dma_sgt))
 		return NULL;
 
-	dbuf = dma_buf_export(buf, &vb2_dma_sg_dmabuf_ops, buf->size, flags, NULL);
+	dbuf = dma_buf_export(&exp_info);
 	if (IS_ERR(dbuf))
 		return NULL;
 
diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c
index 272a83d..6657e39 100644
--- a/drivers/media/v4l2-core/videobuf2-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c
@@ -97,7 +97,7 @@
 		if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
 			goto fail_pages_array_alloc;
 		buf->vma = vma;
-		buf->vaddr = ioremap_nocache(physp, size);
+		buf->vaddr = (__force void *)ioremap_nocache(physp, size);
 		if (!buf->vaddr)
 			goto fail_pages_array_alloc;
 	} else {
@@ -157,7 +157,7 @@
 		kfree(buf->pages);
 	} else {
 		vb2_put_vma(buf->vma);
-		iounmap(buf->vaddr);
+		iounmap((__force void __iomem *)buf->vaddr);
 	}
 	kfree(buf);
 }
@@ -213,11 +213,12 @@
 	return 0;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+#ifdef CONFIG_HAS_DMA
 /*********************************************/
 /*         DMABUF ops for exporters          */
 /*********************************************/
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
 struct vb2_vmalloc_attachment {
 	struct sg_table sgt;
 	enum dma_data_direction dma_dir;
@@ -370,11 +371,17 @@
 {
 	struct vb2_vmalloc_buf *buf = buf_priv;
 	struct dma_buf *dbuf;
+	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+	exp_info.ops = &vb2_vmalloc_dmabuf_ops;
+	exp_info.size = buf->size;
+	exp_info.flags = flags;
+	exp_info.priv = buf;
 
 	if (WARN_ON(!buf->vaddr))
 		return NULL;
 
-	dbuf = dma_buf_export(buf, &vb2_vmalloc_dmabuf_ops, buf->size, flags, NULL);
+	dbuf = dma_buf_export(&exp_info);
 	if (IS_ERR(dbuf))
 		return NULL;
 
@@ -383,6 +390,8 @@
 
 	return dbuf;
 }
+#endif /* CONFIG_HAS_DMA */
+
 
 /*********************************************/
 /*       callbacks for DMABUF buffers        */
@@ -442,7 +451,9 @@
 	.get_userptr	= vb2_vmalloc_get_userptr,
 	.put_userptr	= vb2_vmalloc_put_userptr,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+#ifdef CONFIG_HAS_DMA
 	.get_dmabuf	= vb2_vmalloc_get_dmabuf,
+#endif
 	.map_dmabuf	= vb2_vmalloc_map_dmabuf,
 	.unmap_dmabuf	= vb2_vmalloc_unmap_dmabuf,
 	.attach_dmabuf	= vb2_vmalloc_attach_dmabuf,
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 61deb80..c9abd20 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -174,20 +174,6 @@
 #source "drivers/net/ethernet/renesas/Kconfig"
 #source "drivers/net/ethernet/rdc/Kconfig"
 #source "drivers/net/ethernet/rocker/Kconfig"
-
-config S6GMAC
-	depends on n
-	tristate "S6105 GMAC ethernet support"
-	depends on m
-	depends on XTENSA_VARIANT_S6000
-	depends on PHYLIB
-	---help---
-	  This driver supports the on chip ethernet device on the
-	  S6105 xtensa processor.
-
-	  To compile this driver as a module, choose M here. The module
-	  will be called s6gmac.
-
 #source "drivers/net/ethernet/samsung/Kconfig"
 #source "drivers/net/ethernet/seeq/Kconfig"
 #source "drivers/net/ethernet/silan/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index be78a41..a99a729 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -66,7 +66,6 @@
 #obj-$(CONFIG_SH_ETH) += renesas/
 #obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
 #obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/
-#obj-$(CPTCFG_S6GMAC) += s6gmac.o
 #obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/
 #obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
 #obj-$(CONFIG_NET_VENDOR_SILAN) += silan/
diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c
index 7758cf9..80344dd 100644
--- a/drivers/net/ethernet/atheros/alx/main.c
+++ b/drivers/net/ethernet/atheros/alx/main.c
@@ -184,15 +184,16 @@
 	schedule_work(&alx->reset_wk);
 }
 
-static bool alx_clean_rx_irq(struct alx_priv *alx, int budget)
+static int alx_clean_rx_irq(struct alx_priv *alx, int budget)
 {
 	struct alx_rx_queue *rxq = &alx->rxq;
 	struct alx_rrd *rrd;
 	struct alx_buffer *rxb;
 	struct sk_buff *skb;
 	u16 length, rfd_cleaned = 0;
+	int work = 0;
 
-	while (budget > 0) {
+	while (work < budget) {
 		rrd = &rxq->rrd[rxq->rrd_read_idx];
 		if (!(rrd->word3 & cpu_to_le32(1 << RRD_UPDATED_SHIFT)))
 			break;
@@ -203,7 +204,7 @@
 		    ALX_GET_FIELD(le32_to_cpu(rrd->word0),
 				  RRD_NOR) != 1) {
 			alx_schedule_reset(alx);
-			return 0;
+			return work;
 		}
 
 		rxb = &rxq->bufs[rxq->read_idx];
@@ -243,7 +244,7 @@
 		}
 
 		napi_gro_receive(&alx->napi, skb);
-		budget--;
+		work++;
 
 next_pkt:
 		if (++rxq->read_idx == alx->rx_ringsz)
@@ -258,21 +259,22 @@
 	if (rfd_cleaned)
 		alx_refill_rx_ring(alx, GFP_ATOMIC);
 
-	return budget > 0;
+	return work;
 }
 
 static int alx_poll(struct napi_struct *napi, int budget)
 {
 	struct alx_priv *alx = container_of(napi, struct alx_priv, napi);
 	struct alx_hw *hw = &alx->hw;
-	bool complete = true;
 	unsigned long flags;
+	bool tx_complete;
+	int work;
 
-	complete = alx_clean_tx_irq(alx) &&
-		   alx_clean_rx_irq(alx, budget);
+	tx_complete = alx_clean_tx_irq(alx);
+	work = alx_clean_rx_irq(alx, budget);
 
-	if (!complete)
-		return 1;
+	if (!tx_complete || work == budget)
+		return budget;
 
 	napi_complete(&alx->napi);
 
@@ -284,7 +286,7 @@
 
 	alx_post_write(hw);
 
-	return 0;
+	return work;
 }
 
 static irqreturn_t alx_intr_handle(struct alx_priv *alx, u32 intr)
@@ -1521,11 +1523,11 @@
 	rtnl_unlock();
 }
 
+static 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
-static const struct pci_error_handlers alx_err_handlers = {
-#else
-static struct pci_error_handlers alx_err_handlers = {
+const 
 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) */
+struct pci_error_handlers alx_err_handlers = {
 	.error_detected = alx_pci_error_detected,
 	.slot_reset     = alx_pci_error_slot_reset,
 	.resume         = alx_pci_error_resume,
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
index 52fdfe2..a8b80c5 100644
--- a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
@@ -307,7 +307,7 @@
 
 /*
  * atl1c_read_phy_core
- * core funtion to read register in PHY via MDIO control regsiter.
+ * core function to read register in PHY via MDIO control regsiter.
  * ext: extension register (see IEEE 802.3)
  * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
  * reg: reg to read
@@ -356,7 +356,7 @@
 
 /*
  * atl1c_write_phy_core
- * core funtion to write to register in PHY via MDIO control regsiter.
+ * core function to write to register in PHY via MDIO control register.
  * ext: extension register (see IEEE 802.3)
  * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
  * reg: reg to write
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
index 8ca9e3e..498e969 100644
--- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
@@ -752,7 +752,7 @@
 
 	if (hw->device_id == PCI_DEVICE_ID_ATHEROS_L2C_B2 &&
 	    hw->revision_id == L2CB_V21) {
-		/* config acess mode */
+		/* config access mode */
 		pci_write_config_dword(pdev, REG_PCIE_IND_ACC_ADDR,
 				       REG_PCIE_DEV_MISC_CTRL);
 		pci_read_config_dword(pdev, REG_PCIE_IND_ACC_DATA, &misc_ctrl);
@@ -2235,8 +2235,8 @@
 		return NETDEV_TX_OK;
 	}
 
-	if (unlikely(vlan_tx_tag_present(skb))) {
-		u16 vlan = vlan_tx_tag_get(skb);
+	if (unlikely(skb_vlan_tag_present(skb))) {
+		u16 vlan = skb_vlan_tag_get(skb);
 		__le16 tag;
 
 		vlan = cpu_to_le16(vlan);
@@ -2782,11 +2782,11 @@
 	netif_device_attach(netdev);
 }
 
+static 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
-static const struct pci_error_handlers atl1c_err_handler = {
-#else
-static struct pci_error_handlers atl1c_err_handler = {
+const 
 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) */
+struct pci_error_handlers atl1c_err_handler = {
 	.error_detected = atl1c_io_error_detected,
 	.slot_reset = atl1c_io_slot_reset,
 	.resume = atl1c_io_resume,
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
index 85dfc57..ef63797 100644
--- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
@@ -1892,8 +1892,8 @@
 
 	tpd = atl1e_get_tpd(adapter);
 
-	if (vlan_tx_tag_present(skb)) {
-		u16 vlan_tag = vlan_tx_tag_get(skb);
+	if (skb_vlan_tag_present(skb)) {
+		u16 vlan_tag = skb_vlan_tag_get(skb);
 		u16 atl1e_vlan_tag;
 
 		tpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT;
@@ -2373,9 +2373,8 @@
 
 	netif_napi_add(netdev, &adapter->napi, atl1e_clean, 64);
 
-	init_timer(&adapter->phy_config_timer);
-	adapter->phy_config_timer.function = atl1e_phy_config;
-	adapter->phy_config_timer.data = (unsigned long) adapter;
+	setup_timer(&adapter->phy_config_timer, atl1e_phy_config,
+		    (unsigned long)adapter);
 
 	/* get user settings */
 	atl1e_check_options(adapter);
@@ -2557,7 +2556,11 @@
 }
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
-static const struct pci_error_handlers atl1e_err_handler = {
+static 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+const 
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) */
+struct pci_error_handlers atl1e_err_handler = {
 #else
 static struct pci_error_handlers atl1e_err_handler = {
 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) */
diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c
index 2c8f398..eca1d11 100644
--- a/drivers/net/ethernet/atheros/atlx/atl1.c
+++ b/drivers/net/ethernet/atheros/atlx/atl1.c
@@ -2415,8 +2415,8 @@
 		(u16) atomic_read(&tpd_ring->next_to_use));
 	memset(ptpd, 0, sizeof(struct tx_packet_desc));
 
-	if (vlan_tx_tag_present(skb)) {
-		vlan_tag = vlan_tx_tag_get(skb);
+	if (skb_vlan_tag_present(skb)) {
+		vlan_tag = skb_vlan_tag_get(skb);
 		vlan_tag = (vlan_tag << 4) | (vlan_tag >> 13) |
 			((vlan_tag >> 9) & 0x8);
 		ptpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT;
diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c
index 84a09e8..46a5353 100644
--- a/drivers/net/ethernet/atheros/atlx/atl2.c
+++ b/drivers/net/ethernet/atheros/atlx/atl2.c
@@ -887,8 +887,8 @@
 		offset = ((u32)(skb->len-copy_len + 3) & ~3);
 	}
 #ifdef NETIF_F_HW_VLAN_CTAG_TX
-	if (vlan_tx_tag_present(skb)) {
-		u16 vlan_tag = vlan_tx_tag_get(skb);
+	if (skb_vlan_tag_present(skb)) {
+		u16 vlan_tag = skb_vlan_tag_get(skb);
 		vlan_tag = (vlan_tag << 4) |
 			(vlan_tag >> 13) |
 			((vlan_tag >> 9) & 0x8);
@@ -1436,13 +1436,11 @@
 
 	atl2_check_options(adapter);
 
-	init_timer(&adapter->watchdog_timer);
-	adapter->watchdog_timer.function = atl2_watchdog;
-	adapter->watchdog_timer.data = (unsigned long) adapter;
+	setup_timer(&adapter->watchdog_timer, atl2_watchdog,
+		    (unsigned long)adapter);
 
-	init_timer(&adapter->phy_config_timer);
-	adapter->phy_config_timer.function = atl2_phy_config;
-	adapter->phy_config_timer.data = (unsigned long) adapter;
+	setup_timer(&adapter->phy_config_timer, atl2_phy_config,
+		    (unsigned long)adapter);
 
 	INIT_WORK(&adapter->reset_task, atl2_reset_task);
 	INIT_WORK(&adapter->link_chg_task, atl2_link_chg_task);
diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig
index 9d17913..48c6b14 100644
--- a/drivers/net/ethernet/broadcom/Kconfig
+++ b/drivers/net/ethernet/broadcom/Kconfig
@@ -77,13 +77,13 @@
 
 config BNX2
 	depends on n
-	tristate "QLogic NetXtremeII support"
+	tristate "QLogic bnx2 support"
 	depends on m
 	depends on PCI
 	depends on CRC32
 	depends on FW_LOADER
 	---help---
-	  This driver supports QLogic NetXtremeII gigabit Ethernet cards.
+	  This driver supports QLogic bnx2 gigabit Ethernet cards.
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called bnx2.  This is recommended.
@@ -96,8 +96,8 @@
 	select BNX2
 	depends on UIO
 	---help---
-	  This driver supports offload features of QLogic NetXtremeII
-	  gigabit Ethernet cards.
+	  This driver supports offload features of QLogic bnx2 gigabit
+	  Ethernet cards.
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called cnic.  This is recommended.
@@ -159,7 +159,7 @@
 	depends on n
 	tristate "BCMA bus GBit core support"
 	depends on m
-	depends on BCMA_HOST_SOC && HAS_DMA && BCM47XX
+	depends on BCMA_HOST_SOC && HAS_DMA && (BCM47XX || ARCH_BCM_5301X)
 	depends on PHYLIB
 	---help---
 	  This driver supports GBit MAC and BCM4706 GBit MAC cores on BCMA bus.
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
index 290bf08..9de711a 100644
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -121,7 +121,7 @@
 
 static const struct ssb_device_id b44_ssb_tbl[] = {
 	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET, SSB_ANY_REV),
-	SSB_DEVTABLE_END
+	{},
 };
 MODULE_DEVICE_TABLE(ssb, b44_ssb_tbl);
 
@@ -400,7 +400,7 @@
 }
 
 #ifdef CONFIG_BCM47XX
-#include <bcm47xx_nvram.h>
+#include <linux/bcm47xx_nvram.h>
 static void b44_wap54g10_workaround(struct b44 *bp)
 {
 	char buf[20];
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 538d75f..d3be3f7 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -206,6 +206,17 @@
 	  To compile this driver as a module, choose M here. The module
 	  will be called ixgbe.
 
+config IXGBE_VXLAN
+	bool "Virtual eXtensible Local Area Network Support"
+	default n
+	depends on IXGBE && VXLAN && !(IXGBE=y && VXLAN=m)
+	---help---
+	  This allows one to create VXLAN virtual interfaces that provide
+	  Layer 2 Networks over Layer 3 Networks. VXLAN is often used
+	  to tunnel virtual network infrastructure in virtualized environments.
+	  Say Y here if you want to use Virtual eXtensible Local Area Network
+	  (VXLAN) in the driver.
+
 config IXGBE_HWMON
 	bool "Intel(R) 10GbE PCI Express adapters HWMON support"
 	default y
@@ -299,6 +310,17 @@
 
 	  If unsure, say N.
 
+config I40E_FCOE
+	bool "Fibre Channel over Ethernet (FCoE)"
+	default n
+	depends on I40E && DCB && FCOE
+	---help---
+	  Say Y here if you want to use Fibre Channel over Ethernet (FCoE)
+	  in the driver. This will create new netdev for exclusive FCoE
+	  use with XL710 FCoE offloads enabled.
+
+	  If unsure, say N.
+
 config I40EVF
 	depends on n
 	tristate "Intel(R) XL710 X710 Virtual Function Ethernet support"
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c
index 5a2f5d0..65b5870 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.c
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
@@ -1125,7 +1125,7 @@
 	u32 swmask = mask;
 	u32 fwmask = mask << 16;
 	s32 ret_val = 0;
-	s32 i = 0, timeout = 200; /* FIXME: find real value to use here */
+	s32 i = 0, timeout = 200;
 
 	while (i < timeout) {
 		if (igb_get_hw_semaphore(hw)) {
diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
index 9f13a86..86f1499 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -29,7 +29,7 @@
 #include "e1000_mac.h"
 #include "e1000_82575.h"
 
-#include <linux/clocksource.h>
+#include <linux/timecounter.h>
 #include <linux/net_tstamp.h>
 #include <linux/ptp_clock_kernel.h>
 #include <linux/bitops.h>
@@ -343,6 +343,9 @@
 	};
 #endif
 
+#define IGB_N_EXTTS	2
+#define IGB_N_PEROUT	2
+#define IGB_N_SDP	4
 #define IGB_RETA_SIZE	128
 
 /* board specific private data structure */
@@ -439,6 +442,14 @@
 	u32 tx_hwtstamp_timeouts;
 	u32 rx_hwtstamp_cleared;
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
+	struct ptp_pin_desc sdp_config[IGB_N_SDP];
+#endif
+	struct {
+		struct timespec start;
+		struct timespec period;
+	} perout[IGB_N_PEROUT];
+
 	char fw_version[32];
 #ifdef CPTCFG_IGB_HWMON
 	struct hwmon_buff *igb_hwmon_buff;
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index 813ab10..c684cc8 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -2458,13 +2458,14 @@
 	return 0;
 }
 
+static int igb_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
-static int igb_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
-			 u32 *rule_locs)
+			 ,
+			 u32 *rule_locs
 #else
-static int igb_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
-			 void *rule_locs)
+			 , void *rule_locs
 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) */
+)
 {
 	struct igb_adapter *adapter = netdev_priv(dev);
 	int ret = -EOPNOTSUPP;
@@ -2682,7 +2683,9 @@
 
 	return 0;
 }
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) */
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
 static int igb_set_eee(struct net_device *netdev,
 		       struct ethtool_eee *edata)
 {
@@ -2756,9 +2759,10 @@
 
 	return 0;
 }
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) */
-
+#endif 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+/* LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) */
+
 static int igb_get_module_info(struct net_device *netdev,
 			       struct ethtool_modinfo *modinfo)
 {
@@ -2800,7 +2804,9 @@
 
 	return 0;
 }
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) */
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
 static int igb_get_module_eeprom(struct net_device *netdev,
 				 struct ethtool_eeprom *ee, u8 *data)
 {
@@ -3087,10 +3093,14 @@
 	.set_rxnfc		= igb_set_rxnfc,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
 	.get_eee		= igb_get_eee,
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
 	.set_eee		= igb_set_eee,
 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) */
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
 	.get_module_info	= igb_get_module_info,
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
 	.get_module_eeprom	= igb_get_module_eeprom,
 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) */
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
diff --git a/drivers/net/ethernet/intel/igb/igb_hwmon.c b/drivers/net/ethernet/intel/igb/igb_hwmon.c
index ad08b1f..2eee9bb 100644
--- a/drivers/net/ethernet/intel/igb/igb_hwmon.c
+++ b/drivers/net/ethernet/intel/igb/igb_hwmon.c
@@ -173,9 +173,7 @@
 {
 	struct hwmon_buff *igb_hwmon;
 	struct i2c_client *client;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)
 	struct device *hwmon_dev;
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0) */
 	unsigned int i;
 	int rc = 0;
 
@@ -232,7 +230,6 @@
 	igb_hwmon->groups[0] = &igb_hwmon->group;
 	igb_hwmon->group.attrs = igb_hwmon->attrs;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)
 	hwmon_dev = devm_hwmon_device_register_with_groups(&adapter->pdev->dev,
 							   client->name,
 							   igb_hwmon,
@@ -245,7 +242,6 @@
 	goto exit;
 
 err:
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0) */
 	igb_sysfs_del_adapter(adapter);
 exit:
 	return rc;
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 84f0acb..b8d5643 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -177,11 +177,13 @@
 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 *, int
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
-static int igb_ndo_set_vf_bw(struct net_device *, int, int, int);
+	, int, int
 #else
-static int igb_ndo_set_vf_bw(struct net_device *, int, int);
-#endif
+	, int
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) */
+);
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
 static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf,
 				   bool setting);
@@ -236,11 +238,11 @@
 static pci_ers_result_t igb_io_slot_reset(struct pci_dev *);
 static void igb_io_resume(struct pci_dev *);
 
+static 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
-static const struct pci_error_handlers igb_err_handler = {
-#else
-static struct pci_error_handlers igb_err_handler = {
+const 
 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) */
+struct pci_error_handlers igb_err_handler = {
 	.error_detected = igb_io_error_detected,
 	.slot_reset = igb_io_slot_reset,
 	.resume = igb_io_resume,
@@ -1798,6 +1800,7 @@
 	wr32(E1000_RCTL, rctl & ~E1000_RCTL_EN);
 	/* flush and sleep below */
 
+	netif_carrier_off(netdev);
 	netif_tx_stop_all_queues(netdev);
 
 	/* disable transmits in the hardware */
@@ -1819,12 +1822,9 @@
 		}
 	}
 
-
 	del_timer_sync(&adapter->watchdog_timer);
 	del_timer_sync(&adapter->phy_info_timer);
 
-	netif_carrier_off(netdev);
-
 	/* record the stats before reset*/
 	spin_lock(&adapter->stats64_lock);
 	igb_update_stats(adapter, &adapter->stats64);
@@ -2112,8 +2112,8 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
 	.ndo_set_vf_rate	= igb_ndo_set_vf_bw,
 #else
-	.ndo_set_vf_tx_rate	= igb_ndo_set_vf_bw,
-#endif
+	.ndo_set_vf_tx_rate = igb_ndo_set_vf_bw,
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) */
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
 	.ndo_set_vf_spoofchk	= igb_ndo_set_vf_spoofchk,
 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) */
@@ -2123,6 +2123,9 @@
 #endif
 	.ndo_fix_features	= igb_fix_features,
 	.ndo_set_features	= igb_set_features,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
+	.ndo_features_check	= passthru_features_check,
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0) */
 };
 
 /**
@@ -5067,9 +5070,9 @@
 
 	skb_tx_timestamp(skb);
 
-	if (vlan_tx_tag_present(skb)) {
+	if (skb_vlan_tag_present(skb)) {
 		tx_flags |= IGB_TX_FLAGS_VLAN;
-		tx_flags |= (vlan_tx_tag_get(skb) << IGB_TX_FLAGS_VLAN_SHIFT);
+		tx_flags |= (skb_vlan_tag_get(skb) << IGB_TX_FLAGS_VLAN_SHIFT);
 	}
 
 	/* record initial flags and protocol */
@@ -5416,6 +5419,80 @@
 	}
 }
 
+static void igb_tsync_interrupt(struct igb_adapter *adapter)
+{
+	struct e1000_hw *hw = &adapter->hw;
+	struct ptp_clock_event event;
+	struct timespec ts;
+	u32 ack = 0, tsauxc, sec, nsec, tsicr = rd32(E1000_TSICR);
+
+	if (tsicr & TSINTR_SYS_WRAP) {
+		event.type = PTP_CLOCK_PPS;
+		if (adapter->ptp_caps.pps)
+			ptp_clock_event(adapter->ptp_clock, &event);
+		else
+			dev_err(&adapter->pdev->dev, "unexpected SYS WRAP");
+		ack |= TSINTR_SYS_WRAP;
+	}
+
+	if (tsicr & E1000_TSICR_TXTS) {
+		/* retrieve hardware timestamp */
+		schedule_work(&adapter->ptp_tx_work);
+		ack |= E1000_TSICR_TXTS;
+	}
+
+	if (tsicr & TSINTR_TT0) {
+		spin_lock(&adapter->tmreg_lock);
+		ts = timespec_add(adapter->perout[0].start,
+				  adapter->perout[0].period);
+		wr32(E1000_TRGTTIML0, ts.tv_nsec);
+		wr32(E1000_TRGTTIMH0, ts.tv_sec);
+		tsauxc = rd32(E1000_TSAUXC);
+		tsauxc |= TSAUXC_EN_TT0;
+		wr32(E1000_TSAUXC, tsauxc);
+		adapter->perout[0].start = ts;
+		spin_unlock(&adapter->tmreg_lock);
+		ack |= TSINTR_TT0;
+	}
+
+	if (tsicr & TSINTR_TT1) {
+		spin_lock(&adapter->tmreg_lock);
+		ts = timespec_add(adapter->perout[1].start,
+				  adapter->perout[1].period);
+		wr32(E1000_TRGTTIML1, ts.tv_nsec);
+		wr32(E1000_TRGTTIMH1, ts.tv_sec);
+		tsauxc = rd32(E1000_TSAUXC);
+		tsauxc |= TSAUXC_EN_TT1;
+		wr32(E1000_TSAUXC, tsauxc);
+		adapter->perout[1].start = ts;
+		spin_unlock(&adapter->tmreg_lock);
+		ack |= TSINTR_TT1;
+	}
+
+	if (tsicr & TSINTR_AUTT0) {
+		nsec = rd32(E1000_AUXSTMPL0);
+		sec  = rd32(E1000_AUXSTMPH0);
+		event.type = PTP_CLOCK_EXTTS;
+		event.index = 0;
+		event.timestamp = sec * 1000000000ULL + nsec;
+		ptp_clock_event(adapter->ptp_clock, &event);
+		ack |= TSINTR_AUTT0;
+	}
+
+	if (tsicr & TSINTR_AUTT1) {
+		nsec = rd32(E1000_AUXSTMPL1);
+		sec  = rd32(E1000_AUXSTMPH1);
+		event.type = PTP_CLOCK_EXTTS;
+		event.index = 1;
+		event.timestamp = sec * 1000000000ULL + nsec;
+		ptp_clock_event(adapter->ptp_clock, &event);
+		ack |= TSINTR_AUTT1;
+	}
+
+	/* acknowledge the interrupts */
+	wr32(E1000_TSICR, ack);
+}
+
 static irqreturn_t igb_msix_other(int irq, void *data)
 {
 	struct igb_adapter *adapter = data;
@@ -5447,16 +5524,8 @@
 			mod_timer(&adapter->watchdog_timer, jiffies + 1);
 	}
 
-	if (icr & E1000_ICR_TS) {
-		u32 tsicr = rd32(E1000_TSICR);
-
-		if (tsicr & E1000_TSICR_TXTS) {
-			/* acknowledge the interrupt */
-			wr32(E1000_TSICR, E1000_TSICR_TXTS);
-			/* retrieve hardware timestamp */
-			schedule_work(&adapter->ptp_tx_work);
-		}
-	}
+	if (icr & E1000_ICR_TS)
+		igb_tsync_interrupt(adapter);
 
 	wr32(E1000_EIMS, adapter->eims_other);
 
@@ -6043,8 +6112,12 @@
 	adapter->vf_data[vf].flags |= IGB_VF_FLAG_CTS;
 
 	/* reply to reset with ack and vf mac address */
-	msgbuf[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_ACK;
-	memcpy(addr, vf_mac, ETH_ALEN);
+	if (!is_zero_ether_addr(vf_mac)) {
+		msgbuf[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_ACK;
+		memcpy(addr, vf_mac, ETH_ALEN);
+	} else {
+		msgbuf[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_NACK;
+	}
 	igb_write_mbx(hw, msgbuf, 3, vf);
 }
 
@@ -6235,16 +6308,8 @@
 			mod_timer(&adapter->watchdog_timer, jiffies + 1);
 	}
 
-	if (icr & E1000_ICR_TS) {
-		u32 tsicr = rd32(E1000_TSICR);
-
-		if (tsicr & E1000_TSICR_TXTS) {
-			/* acknowledge the interrupt */
-			wr32(E1000_TSICR, E1000_TSICR_TXTS);
-			/* retrieve hardware timestamp */
-			schedule_work(&adapter->ptp_tx_work);
-		}
-	}
+	if (icr & E1000_ICR_TS)
+		igb_tsync_interrupt(adapter);
 
 	napi_schedule(&q_vector->napi);
 
@@ -6289,16 +6354,8 @@
 			mod_timer(&adapter->watchdog_timer, jiffies + 1);
 	}
 
-	if (icr & E1000_ICR_TS) {
-		u32 tsicr = rd32(E1000_TSICR);
-
-		if (tsicr & E1000_TSICR_TXTS) {
-			/* acknowledge the interrupt */
-			wr32(E1000_TSICR, E1000_TSICR_TXTS);
-			/* retrieve hardware timestamp */
-			schedule_work(&adapter->ptp_tx_work);
-		}
-	}
+	if (icr & E1000_ICR_TS)
+		igb_tsync_interrupt(adapter);
 
 	napi_schedule(&q_vector->napi);
 
@@ -6559,19 +6616,23 @@
 					 DMA_FROM_DEVICE);
 }
 
+static inline bool igb_page_is_reserved(struct page *page)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+	return (page_to_nid(page) != numa_mem_id()) || page->pfmemalloc;
+#else
+	return (page_to_nid(page) != numa_mem_id());
+#endif
+}
+
 static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer,
 				  struct page *page,
 				  unsigned int truesize)
 {
 	/* avoid re-using remote pages */
-	if (unlikely(page_to_nid(page) != numa_node_id()))
+	if (unlikely(igb_page_is_reserved(page)))
 		return false;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
-	if (unlikely(page->pfmemalloc))
-		return false;
-#endif
-
 #if (PAGE_SIZE < 8192)
 	/* if we are only owner of page we can reuse it */
 	if (unlikely(page_count(page) != 1))
@@ -6579,22 +6640,19 @@
 
 	/* flip page offset to other buffer */
 	rx_buffer->page_offset ^= IGB_RX_BUFSZ;
-
-	/* Even if we own the page, we are not allowed to use atomic_set()
-	 * This would break get_page_unless_zero() users.
-	 */
-	atomic_inc(&page->_count);
 #else
 	/* move offset up to the next cache line */
 	rx_buffer->page_offset += truesize;
 
 	if (rx_buffer->page_offset > (PAGE_SIZE - IGB_RX_BUFSZ))
 		return false;
-
-	/* bump ref count on page before it is given to the stack */
-	get_page(page);
 #endif
 
+	/* Even if we own the page, we are not allowed to use atomic_set()
+	 * This would break get_page_unless_zero() users.
+	 */
+	atomic_inc(&page->_count);
+
 	return true;
 }
 
@@ -6637,17 +6695,12 @@
 
 		memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long)));
 
-		/* we can reuse buffer as-is, just make sure it is local */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
-		if (likely((page_to_nid(page) == numa_node_id()) &&
-			   !page->pfmemalloc))
-#else
-		if (likely(page_to_nid(page) == numa_node_id()))
-#endif
+		/* page is not reserved, we can reuse buffer as-is */
+		if (likely(!igb_page_is_reserved(page)))
 			return true;
 
 		/* this page cannot be reused so discard it */
-		put_page(page);
+		__free_page(page);
 		return false;
 	}
 
@@ -6665,7 +6718,6 @@
 	struct page *page;
 
 	rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean];
-
 	page = rx_buffer->page;
 	prefetchw(page);
 
@@ -7080,8 +7132,8 @@
 			i -= rx_ring->count;
 		}
 
-		/* clear the hdr_addr for the next_to_use descriptor */
-		rx_desc->read.hdr_addr = 0;
+		/* clear the status bits for the next_to_use descriptor */
+		rx_desc->wb.upper.status_error = 0;
 
 		cleaned_count--;
 	} while (cleaned_count);
@@ -7866,12 +7918,14 @@
 	}
 }
 
+static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
-static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf,
-			     int min_tx_rate, int max_tx_rate)
+			     ,
+			     int min_tx_rate, int max_tx_rate
 #else
-static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
-#endif
+			     , int tx_rate
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) */
+)
 {
 	struct igb_adapter *adapter = netdev_priv(netdev);
 	struct e1000_hw *hw = &adapter->hw;
@@ -7904,7 +7958,7 @@
 	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);
-#endif
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) */
 	return 0;
 }
 
@@ -7950,7 +8004,7 @@
 	ivi->min_tx_rate = 0;
 #else
 	ivi->tx_rate = adapter->vf_data[vf].tx_rate;
-#endif
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) */
 	ivi->vlan = adapter->vf_data[vf].pf_vlan;
 	ivi->qos = adapter->vf_data[vf].pf_qos;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
index 794c139..96e2cc8 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -30,7 +30,7 @@
  *
  * Neither the 82576 nor the 82580 offer registers wide enough to hold
  * nanoseconds time values for very long. For the 82580, SYSTIM always
- * counts nanoseconds, but the upper 24 bits are not availible. The
+ * counts nanoseconds, but the upper 24 bits are not available. The
  * frequency is adjusted by changing the 32 bit fractional nanoseconds
  * register, TIMINCA.
  *
@@ -115,8 +115,10 @@
 	return val;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
 /* SYSTIM read access for I210/I211 */
-static void igb_ptp_read_i210(struct igb_adapter *adapter, struct timespec *ts)
+static void igb_ptp_read_i210(struct igb_adapter *adapter,
+			      struct timespec64 *ts)
 {
 	struct e1000_hw *hw = &adapter->hw;
 	u32 sec, nsec;
@@ -132,9 +134,10 @@
 	ts->tv_sec = sec;
 	ts->tv_nsec = nsec;
 }
+#endif
 
 static void igb_ptp_write_i210(struct igb_adapter *adapter,
-			       const struct timespec *ts)
+			       const struct timespec64 *ts)
 {
 	struct e1000_hw *hw = &adapter->hw;
 
@@ -256,45 +259,41 @@
 	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
 					       ptp_caps);
 	unsigned long flags;
-	s64 now;
 
 	spin_lock_irqsave(&igb->tmreg_lock, flags);
-
-	now = timecounter_read(&igb->tc);
-	now += delta;
-	timecounter_init(&igb->tc, &igb->cc, now);
-
+	timecounter_adjtime(&igb->tc, delta);
 	spin_unlock_irqrestore(&igb->tmreg_lock, flags);
 
 	return 0;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
 static int igb_ptp_adjtime_i210(struct ptp_clock_info *ptp, s64 delta)
 {
 	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
 					       ptp_caps);
 	unsigned long flags;
-	struct timespec now, then = ns_to_timespec(delta);
+	struct timespec64 now, then = ns_to_timespec64(delta);
 
 	spin_lock_irqsave(&igb->tmreg_lock, flags);
 
 	igb_ptp_read_i210(igb, &now);
-	now = timespec_add(now, then);
-	igb_ptp_write_i210(igb, (const struct timespec *)&now);
+	now = timespec64_add(now, then);
+	igb_ptp_write_i210(igb, (const struct timespec64 *)&now);
 
 	spin_unlock_irqrestore(&igb->tmreg_lock, flags);
 
 	return 0;
 }
+#endif
 
 static int igb_ptp_gettime_82576(struct ptp_clock_info *ptp,
-				 struct timespec *ts)
+				 struct timespec64 *ts)
 {
 	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
 					       ptp_caps);
 	unsigned long flags;
 	u64 ns;
-	u32 remainder;
 
 	spin_lock_irqsave(&igb->tmreg_lock, flags);
 
@@ -302,14 +301,14 @@
 
 	spin_unlock_irqrestore(&igb->tmreg_lock, flags);
 
-	ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
-	ts->tv_nsec = remainder;
+	*ts = ns_to_timespec64(ns);
 
 	return 0;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
 static int igb_ptp_gettime_i210(struct ptp_clock_info *ptp,
-				struct timespec *ts)
+				struct timespec64 *ts)
 {
 	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
 					       ptp_caps);
@@ -323,17 +322,17 @@
 
 	return 0;
 }
+#endif
 
 static int igb_ptp_settime_82576(struct ptp_clock_info *ptp,
-				 const struct timespec *ts)
+				 const struct timespec64 *ts)
 {
 	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
 					       ptp_caps);
 	unsigned long flags;
 	u64 ns;
 
-	ns = ts->tv_sec * 1000000000ULL;
-	ns += ts->tv_nsec;
+	ns = timespec64_to_ns(ts);
 
 	spin_lock_irqsave(&igb->tmreg_lock, flags);
 
@@ -344,8 +343,9 @@
 	return 0;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
 static int igb_ptp_settime_i210(struct ptp_clock_info *ptp,
-				const struct timespec *ts)
+				const struct timespec64 *ts)
 {
 	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
 					       ptp_caps);
@@ -360,12 +360,242 @@
 	return 0;
 }
 
+static void igb_pin_direction(int pin, int input, u32 *ctrl, u32 *ctrl_ext)
+{
+	u32 *ptr = pin < 2 ? ctrl : ctrl_ext;
+	static const u32 mask[IGB_N_SDP] = {
+		E1000_CTRL_SDP0_DIR,
+		E1000_CTRL_SDP1_DIR,
+		E1000_CTRL_EXT_SDP2_DIR,
+		E1000_CTRL_EXT_SDP3_DIR,
+	};
+
+	if (input)
+		*ptr &= ~mask[pin];
+	else
+		*ptr |= mask[pin];
+}
+
+static void igb_pin_extts(struct igb_adapter *igb, int chan, int pin)
+{
+	static const u32 aux0_sel_sdp[IGB_N_SDP] = {
+		AUX0_SEL_SDP0, AUX0_SEL_SDP1, AUX0_SEL_SDP2, AUX0_SEL_SDP3,
+	};
+	static const u32 aux1_sel_sdp[IGB_N_SDP] = {
+		AUX1_SEL_SDP0, AUX1_SEL_SDP1, AUX1_SEL_SDP2, AUX1_SEL_SDP3,
+	};
+	static const u32 ts_sdp_en[IGB_N_SDP] = {
+		TS_SDP0_EN, TS_SDP1_EN, TS_SDP2_EN, TS_SDP3_EN,
+	};
+	struct e1000_hw *hw = &igb->hw;
+	u32 ctrl, ctrl_ext, tssdp = 0;
+
+	ctrl = rd32(E1000_CTRL);
+	ctrl_ext = rd32(E1000_CTRL_EXT);
+	tssdp = rd32(E1000_TSSDP);
+
+	igb_pin_direction(pin, 1, &ctrl, &ctrl_ext);
+
+	/* Make sure this pin is not enabled as an output. */
+	tssdp &= ~ts_sdp_en[pin];
+
+	if (chan == 1) {
+		tssdp &= ~AUX1_SEL_SDP3;
+		tssdp |= aux1_sel_sdp[pin] | AUX1_TS_SDP_EN;
+	} else {
+		tssdp &= ~AUX0_SEL_SDP3;
+		tssdp |= aux0_sel_sdp[pin] | AUX0_TS_SDP_EN;
+	}
+
+	wr32(E1000_TSSDP, tssdp);
+	wr32(E1000_CTRL, ctrl);
+	wr32(E1000_CTRL_EXT, ctrl_ext);
+}
+
+static void igb_pin_perout(struct igb_adapter *igb, int chan, int pin)
+{
+	static const u32 aux0_sel_sdp[IGB_N_SDP] = {
+		AUX0_SEL_SDP0, AUX0_SEL_SDP1, AUX0_SEL_SDP2, AUX0_SEL_SDP3,
+	};
+	static const u32 aux1_sel_sdp[IGB_N_SDP] = {
+		AUX1_SEL_SDP0, AUX1_SEL_SDP1, AUX1_SEL_SDP2, AUX1_SEL_SDP3,
+	};
+	static const u32 ts_sdp_en[IGB_N_SDP] = {
+		TS_SDP0_EN, TS_SDP1_EN, TS_SDP2_EN, TS_SDP3_EN,
+	};
+	static const u32 ts_sdp_sel_tt0[IGB_N_SDP] = {
+		TS_SDP0_SEL_TT0, TS_SDP1_SEL_TT0,
+		TS_SDP2_SEL_TT0, TS_SDP3_SEL_TT0,
+	};
+	static const u32 ts_sdp_sel_tt1[IGB_N_SDP] = {
+		TS_SDP0_SEL_TT1, TS_SDP1_SEL_TT1,
+		TS_SDP2_SEL_TT1, TS_SDP3_SEL_TT1,
+	};
+	static const u32 ts_sdp_sel_clr[IGB_N_SDP] = {
+		TS_SDP0_SEL_FC1, TS_SDP1_SEL_FC1,
+		TS_SDP2_SEL_FC1, TS_SDP3_SEL_FC1,
+	};
+	struct e1000_hw *hw = &igb->hw;
+	u32 ctrl, ctrl_ext, tssdp = 0;
+
+	ctrl = rd32(E1000_CTRL);
+	ctrl_ext = rd32(E1000_CTRL_EXT);
+	tssdp = rd32(E1000_TSSDP);
+
+	igb_pin_direction(pin, 0, &ctrl, &ctrl_ext);
+
+	/* Make sure this pin is not enabled as an input. */
+	if ((tssdp & AUX0_SEL_SDP3) == aux0_sel_sdp[pin])
+		tssdp &= ~AUX0_TS_SDP_EN;
+
+	if ((tssdp & AUX1_SEL_SDP3) == aux1_sel_sdp[pin])
+		tssdp &= ~AUX1_TS_SDP_EN;
+
+	tssdp &= ~ts_sdp_sel_clr[pin];
+	if (chan == 1)
+		tssdp |= ts_sdp_sel_tt1[pin];
+	else
+		tssdp |= ts_sdp_sel_tt0[pin];
+
+	tssdp |= ts_sdp_en[pin];
+
+	wr32(E1000_TSSDP, tssdp);
+	wr32(E1000_CTRL, ctrl);
+	wr32(E1000_CTRL_EXT, ctrl_ext);
+}
+
+static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp,
+				       struct ptp_clock_request *rq, int on)
+{
+	struct igb_adapter *igb =
+		container_of(ptp, struct igb_adapter, ptp_caps);
+	struct e1000_hw *hw = &igb->hw;
+	u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh;
+	unsigned long flags;
+	struct timespec ts;
+	int pin = -1;
+	s64 ns;
+
+	switch (rq->type) {
+	case PTP_CLK_REQ_EXTTS:
+		if (on) {
+			pin = ptp_find_pin(igb->ptp_clock, PTP_PF_EXTTS,
+					   rq->extts.index);
+			if (pin < 0)
+				return -EBUSY;
+		}
+		if (rq->extts.index == 1) {
+			tsauxc_mask = TSAUXC_EN_TS1;
+			tsim_mask = TSINTR_AUTT1;
+		} else {
+			tsauxc_mask = TSAUXC_EN_TS0;
+			tsim_mask = TSINTR_AUTT0;
+		}
+		spin_lock_irqsave(&igb->tmreg_lock, flags);
+		tsauxc = rd32(E1000_TSAUXC);
+		tsim = rd32(E1000_TSIM);
+		if (on) {
+			igb_pin_extts(igb, rq->extts.index, pin);
+			tsauxc |= tsauxc_mask;
+			tsim |= tsim_mask;
+		} else {
+			tsauxc &= ~tsauxc_mask;
+			tsim &= ~tsim_mask;
+		}
+		wr32(E1000_TSAUXC, tsauxc);
+		wr32(E1000_TSIM, tsim);
+		spin_unlock_irqrestore(&igb->tmreg_lock, flags);
+		return 0;
+
+	case PTP_CLK_REQ_PEROUT:
+		if (on) {
+			pin = ptp_find_pin(igb->ptp_clock, PTP_PF_PEROUT,
+					   rq->perout.index);
+			if (pin < 0)
+				return -EBUSY;
+		}
+		ts.tv_sec = rq->perout.period.sec;
+		ts.tv_nsec = rq->perout.period.nsec;
+		ns = timespec_to_ns(&ts);
+		ns = ns >> 1;
+		if (on && ns < 500000LL) {
+			/* 2k interrupts per second is an awful lot. */
+			return -EINVAL;
+		}
+		ts = ns_to_timespec(ns);
+		if (rq->perout.index == 1) {
+			tsauxc_mask = TSAUXC_EN_TT1;
+			tsim_mask = TSINTR_TT1;
+			trgttiml = E1000_TRGTTIML1;
+			trgttimh = E1000_TRGTTIMH1;
+		} else {
+			tsauxc_mask = TSAUXC_EN_TT0;
+			tsim_mask = TSINTR_TT0;
+			trgttiml = E1000_TRGTTIML0;
+			trgttimh = E1000_TRGTTIMH0;
+		}
+		spin_lock_irqsave(&igb->tmreg_lock, flags);
+		tsauxc = rd32(E1000_TSAUXC);
+		tsim = rd32(E1000_TSIM);
+		if (on) {
+			int i = rq->perout.index;
+
+			igb_pin_perout(igb, i, pin);
+			igb->perout[i].start.tv_sec = rq->perout.start.sec;
+			igb->perout[i].start.tv_nsec = rq->perout.start.nsec;
+			igb->perout[i].period.tv_sec = ts.tv_sec;
+			igb->perout[i].period.tv_nsec = ts.tv_nsec;
+			wr32(trgttiml, rq->perout.start.sec);
+			wr32(trgttimh, rq->perout.start.nsec);
+			tsauxc |= tsauxc_mask;
+			tsim |= tsim_mask;
+		} else {
+			tsauxc &= ~tsauxc_mask;
+			tsim &= ~tsim_mask;
+		}
+		wr32(E1000_TSAUXC, tsauxc);
+		wr32(E1000_TSIM, tsim);
+		spin_unlock_irqrestore(&igb->tmreg_lock, flags);
+		return 0;
+
+	case PTP_CLK_REQ_PPS:
+		spin_lock_irqsave(&igb->tmreg_lock, flags);
+		tsim = rd32(E1000_TSIM);
+		if (on)
+			tsim |= TSINTR_SYS_WRAP;
+		else
+			tsim &= ~TSINTR_SYS_WRAP;
+		wr32(E1000_TSIM, tsim);
+		spin_unlock_irqrestore(&igb->tmreg_lock, flags);
+		return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+#endif
+
 static int igb_ptp_feature_enable(struct ptp_clock_info *ptp,
 				  struct ptp_clock_request *rq, int on)
 {
 	return -EOPNOTSUPP;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
+static int igb_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
+			      enum ptp_pin_function func, unsigned int chan)
+{
+	switch (func) {
+	case PTP_PF_NONE:
+	case PTP_PF_EXTTS:
+	case PTP_PF_PEROUT:
+		break;
+	case PTP_PF_PHYSYNC:
+		return -1;
+	}
+	return 0;
+}
+#endif
+
 /**
  * igb_ptp_tx_work
  * @work: pointer to work struct
@@ -405,11 +635,16 @@
 {
 	struct igb_adapter *igb =
 		container_of(work, struct igb_adapter, ptp_overflow_work.work);
-	struct timespec ts;
+	struct timespec64 ts;
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+	igb->ptp_caps.gettime64(&igb->ptp_caps, &ts);
+#else
 	igb->ptp_caps.gettime(&igb->ptp_caps, &ts);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) */
 
-	pr_debug("igb overflow check at %ld.%09lu\n", ts.tv_sec, ts.tv_nsec);
+	pr_debug("igb overflow check at %lld.%09lu\n",
+		 (long long) ts.tv_sec, ts.tv_nsec);
 
 	schedule_delayed_work(&igb->ptp_overflow_work,
 			      IGB_SYSTIM_OVERFLOW_PERIOD);
@@ -756,6 +991,9 @@
 {
 	struct e1000_hw *hw = &adapter->hw;
 	struct net_device *netdev = adapter->netdev;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
+	int i;
+#endif
 
 	switch (hw->mac.type) {
 	case e1000_82576:
@@ -766,11 +1004,19 @@
 		adapter->ptp_caps.pps = 0;
 		adapter->ptp_caps.adjfreq = igb_ptp_adjfreq_82576;
 		adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+		adapter->ptp_caps.gettime64 = igb_ptp_gettime_82576;
+#else
 		adapter->ptp_caps.gettime = igb_ptp_gettime_82576;
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+		adapter->ptp_caps.settime64 = igb_ptp_settime_82576;
+#else
 		adapter->ptp_caps.settime = igb_ptp_settime_82576;
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) */
 		adapter->ptp_caps.enable = igb_ptp_feature_enable;
 		adapter->cc.read = igb_ptp_read_82576;
-		adapter->cc.mask = CLOCKSOURCE_MASK(64);
+		adapter->cc.mask = CYCLECOUNTER_MASK(64);
 		adapter->cc.mult = 1;
 		adapter->cc.shift = IGB_82576_TSYNC_SHIFT;
 		/* Dial the nominal frequency. */
@@ -786,31 +1032,60 @@
 		adapter->ptp_caps.pps = 0;
 		adapter->ptp_caps.adjfreq = igb_ptp_adjfreq_82580;
 		adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+		adapter->ptp_caps.gettime64 = igb_ptp_gettime_82576;
+#else
 		adapter->ptp_caps.gettime = igb_ptp_gettime_82576;
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+		adapter->ptp_caps.settime64 = igb_ptp_settime_82576;
+#else
 		adapter->ptp_caps.settime = igb_ptp_settime_82576;
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) */
 		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.mask = CYCLECOUNTER_MASK(IGB_NBITS_82580);
 		adapter->cc.mult = 1;
 		adapter->cc.shift = 0;
 		/* Enable the timer functions by clearing bit 31. */
 		wr32(E1000_TSAUXC, 0x0);
 		break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
 	case e1000_i210:
 	case e1000_i211:
+		for (i = 0; i < IGB_N_SDP; i++) {
+			struct ptp_pin_desc *ppd = &adapter->sdp_config[i];
+
+			snprintf(ppd->name, sizeof(ppd->name), "SDP%d", i);
+			ppd->index = i;
+			ppd->func = PTP_PF_NONE;
+		}
 		snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);
 		adapter->ptp_caps.owner = THIS_MODULE;
 		adapter->ptp_caps.max_adj = 62499999;
-		adapter->ptp_caps.n_ext_ts = 0;
-		adapter->ptp_caps.pps = 0;
+		adapter->ptp_caps.n_ext_ts = IGB_N_EXTTS;
+		adapter->ptp_caps.n_per_out = IGB_N_PEROUT;
+		adapter->ptp_caps.n_pins = IGB_N_SDP;
+		adapter->ptp_caps.pps = 1;
+		adapter->ptp_caps.pin_config = adapter->sdp_config;
 		adapter->ptp_caps.adjfreq = igb_ptp_adjfreq_82580;
 		adapter->ptp_caps.adjtime = igb_ptp_adjtime_i210;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+		adapter->ptp_caps.gettime64 = igb_ptp_gettime_i210;
+#else
 		adapter->ptp_caps.gettime = igb_ptp_gettime_i210;
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+		adapter->ptp_caps.settime64 = igb_ptp_settime_i210;
+#else
 		adapter->ptp_caps.settime = igb_ptp_settime_i210;
-		adapter->ptp_caps.enable = igb_ptp_feature_enable;
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) */
+		adapter->ptp_caps.enable = igb_ptp_feature_enable_i210;
+		adapter->ptp_caps.verify = igb_ptp_verify_pin;
 		/* Enable the timer functions by clearing bit 31. */
 		wr32(E1000_TSAUXC, 0x0);
 		break;
+#endif
 	default:
 		adapter->ptp_clock = NULL;
 		return;
@@ -822,11 +1097,14 @@
 	INIT_WORK(&adapter->ptp_tx_work, igb_ptp_tx_work);
 
 	/* Initialize the clock and overflow work for devices that need it. */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
 	if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211)) {
-		struct timespec ts = ktime_to_timespec(ktime_get_real());
+		struct timespec64 ts = ktime_to_timespec64(ktime_get_real());
 
 		igb_ptp_settime_i210(&adapter->ptp_caps, &ts);
-	} else {
+	} else
+#endif
+	{
 		timecounter_init(&adapter->tc, &adapter->cc,
 				 ktime_to_ns(ktime_get_real()));
 
@@ -905,6 +1183,7 @@
 void igb_ptp_reset(struct igb_adapter *adapter)
 {
 	struct e1000_hw *hw = &adapter->hw;
+	unsigned long flags;
 
 	if (!(adapter->flags & IGB_FLAG_PTP))
 		return;
@@ -912,6 +1191,8 @@
 	/* reset the tstamp_config */
 	igb_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
 
+	spin_lock_irqsave(&adapter->tmreg_lock, flags);
+
 	switch (adapter->hw.mac.type) {
 	case e1000_82576:
 		/* Dial the nominal frequency. */
@@ -922,23 +1203,25 @@
 	case e1000_i350:
 	case e1000_i210:
 	case e1000_i211:
-		/* Enable the timer functions and interrupts. */
 		wr32(E1000_TSAUXC, 0x0);
+		wr32(E1000_TSSDP, 0x0);
 		wr32(E1000_TSIM, TSYNC_INTERRUPTS);
 		wr32(E1000_IMS, E1000_IMS_TS);
 		break;
 	default:
 		/* No work to do. */
-		return;
+		goto out;
 	}
 
 	/* Re-initialize the timer. */
 	if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211)) {
-		struct timespec ts = ktime_to_timespec(ktime_get_real());
+		struct timespec64 ts = ktime_to_timespec64(ktime_get_real());
 
-		igb_ptp_settime_i210(&adapter->ptp_caps, &ts);
+		igb_ptp_write_i210(adapter, &ts);
 	} else {
 		timecounter_init(&adapter->tc, &adapter->cc,
 				 ktime_to_ns(ktime_get_real()));
 	}
+out:
+	spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
 }
diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c
index 1c01356..3802665 100644
--- a/drivers/net/ieee802154/at86rf230.c
+++ b/drivers/net/ieee802154/at86rf230.c
@@ -19,11 +19,12 @@
  */
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/jiffies.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/gpio.h>
 #include <linux/delay.h>
-#include <linux/spinlock.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/at86rf230.h>
 #include <linux/regmap.h>
@@ -46,19 +47,27 @@
 	u16 t_off_to_tx_on;
 	u16 t_frame;
 	u16 t_p_ack;
-	/* completion timeout for tx in msecs */
-	u16 t_tx_timeout;
 	int rssi_base_val;
 
 	int (*set_channel)(struct at86rf230_local *, u8, u8);
 	int (*get_desense_steps)(struct at86rf230_local *, s32);
 };
 
-#define AT86RF2XX_MAX_BUF (127 + 3)
+#define AT86RF2XX_MAX_BUF		(127 + 3)
+/* tx retries to access the TX_ON state
+ * if it's above then force change will be started.
+ *
+ * We assume the max_frame_retries (7) value of 802.15.4 here.
+ */
+#define AT86RF2XX_MAX_TX_RETRIES	7
+/* We use the recommended 5 minutes timeout to recalibrate */
+#define AT86RF2XX_CAL_LOOP_TIMEOUT	(5 * 60 * HZ)
 
 struct at86rf230_state_change {
 	struct at86rf230_local *lp;
+	int irq;
 
+	struct hrtimer timer;
 	struct spi_message msg;
 	struct spi_transfer trx;
 	u8 buf[AT86RF2XX_MAX_BUF];
@@ -83,10 +92,10 @@
 	struct at86rf230_state_change irq;
 
 	bool tx_aret;
+	unsigned long cal_timeout;
 	s8 max_frame_retries;
 	bool is_tx;
-	/* spinlock for is_tx protection */
-	spinlock_t lock;
+	u8 tx_retry;
 	struct sk_buff *tx_skb;
 	struct at86rf230_state_change tx;
 };
@@ -313,7 +322,7 @@
 	int rc;
 
 	rc = __at86rf230_read(lp, addr, data);
-	if (rc > 0)
+	if (!rc)
 		*data = (*data & mask) >> shift;
 
 	return rc;
@@ -409,6 +418,8 @@
 	case RG_PHY_ED_LEVEL:
 	case RG_IRQ_STATUS:
 	case RG_VREG_CTRL:
+	case RG_PLL_CF:
+	case RG_PLL_DCU:
 		return true;
 	default:
 		return false;
@@ -427,7 +438,7 @@
 	}
 }
 
-static struct regmap_config at86rf230_regmap_spi_config = {
+static const struct regmap_config at86rf230_regmap_spi_config = {
 	.reg_bits = 8,
 	.val_bits = 8,
 	.write_flag_mask = CMD_REG | CMD_WRITE,
@@ -446,11 +457,12 @@
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_local *lp = ctx->lp;
 
+	lp->is_tx = 0;
 	at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL, false);
 	ieee802154_wake_queue(lp->hw);
 }
 
-static void
+static inline void
 at86rf230_async_error(struct at86rf230_local *lp,
 		      struct at86rf230_state_change *ctx, int rc)
 {
@@ -472,18 +484,25 @@
 	u8 *tx_buf = ctx->buf;
 
 	tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
-	ctx->trx.len = 2;
 	ctx->msg.complete = complete;
 	ctx->irq_enable = irq_enable;
 	rc = spi_async(lp->spi, &ctx->msg);
 	if (rc) {
 		if (irq_enable)
-			enable_irq(lp->spi->irq);
+			enable_irq(ctx->irq);
 
 		at86rf230_async_error(lp, ctx, rc);
 	}
 }
 
+static inline u8 at86rf230_state_to_force(u8 state)
+{
+	if (state == STATE_TX_ON)
+		return STATE_FORCE_TX_ON;
+	else
+		return STATE_FORCE_TRX_OFF;
+}
+
 static void
 at86rf230_async_state_assert(void *context)
 {
@@ -514,17 +533,27 @@
 			 * in STATE_BUSY_RX_AACK, we run a force state change
 			 * to STATE_TX_ON. This is a timeout handling, if the
 			 * transceiver stucks in STATE_BUSY_RX_AACK.
+			 *
+			 * Additional we do several retries to try to get into
+			 * TX_ON state without forcing. If the retries are
+			 * higher or equal than AT86RF2XX_MAX_TX_RETRIES we
+			 * will do a force change.
 			 */
-			if (ctx->to_state == STATE_TX_ON) {
-				at86rf230_async_state_change(lp, ctx,
-							     STATE_FORCE_TX_ON,
+			if (ctx->to_state == STATE_TX_ON ||
+			    ctx->to_state == STATE_TRX_OFF) {
+				u8 state = ctx->to_state;
+
+				if (lp->tx_retry >= AT86RF2XX_MAX_TX_RETRIES)
+					state = at86rf230_state_to_force(state);
+				lp->tx_retry++;
+
+				at86rf230_async_state_change(lp, ctx, state,
 							     ctx->complete,
 							     ctx->irq_enable);
 				return;
 			}
 		}
 
-
 		dev_warn(&lp->spi->dev, "unexcept state change from 0x%02x to 0x%02x. Actual state: 0x%02x\n",
 			 ctx->from_state, ctx->to_state, trx_state);
 	}
@@ -534,6 +563,19 @@
 		ctx->complete(context);
 }
 
+static enum hrtimer_restart at86rf230_async_state_timer(struct hrtimer *timer)
+{
+	struct at86rf230_state_change *ctx =
+		container_of(timer, struct at86rf230_state_change, timer);
+	struct at86rf230_local *lp = ctx->lp;
+
+	at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
+				 at86rf230_async_state_assert,
+				 ctx->irq_enable);
+
+	return HRTIMER_NORESTART;
+}
+
 /* Do state change timing delay. */
 static void
 at86rf230_async_state_delay(void *context)
@@ -542,6 +584,7 @@
 	struct at86rf230_local *lp = ctx->lp;
 	struct at86rf2xx_chip_data *c = lp->data;
 	bool force = false;
+	ktime_t tim;
 
 	/* The force state changes are will show as normal states in the
 	 * state status subregister. We change the to_state to the
@@ -565,11 +608,15 @@
 	case STATE_TRX_OFF:
 		switch (ctx->to_state) {
 		case STATE_RX_AACK_ON:
-			usleep_range(c->t_off_to_aack, c->t_off_to_aack + 10);
+			tim = ktime_set(0, c->t_off_to_aack * NSEC_PER_USEC);
 			goto change;
 		case STATE_TX_ON:
-			usleep_range(c->t_off_to_tx_on,
-				     c->t_off_to_tx_on + 10);
+			tim = ktime_set(0, c->t_off_to_tx_on * NSEC_PER_USEC);
+			/* state change from TRX_OFF to TX_ON to do a
+			 * calibration, we need to reset the timeout for the
+			 * next one.
+			 */
+			lp->cal_timeout = jiffies + AT86RF2XX_CAL_LOOP_TIMEOUT;
 			goto change;
 		default:
 			break;
@@ -577,14 +624,15 @@
 		break;
 	case STATE_BUSY_RX_AACK:
 		switch (ctx->to_state) {
+		case STATE_TRX_OFF:
 		case STATE_TX_ON:
 			/* Wait for worst case receiving time if we
 			 * didn't make a force change from BUSY_RX_AACK
-			 * to TX_ON.
+			 * to TX_ON or TRX_OFF.
 			 */
 			if (!force) {
-				usleep_range(c->t_frame + c->t_p_ack,
-					     c->t_frame + c->t_p_ack + 1000);
+				tim = ktime_set(0, (c->t_frame + c->t_p_ack) *
+						   NSEC_PER_USEC);
 				goto change;
 			}
 			break;
@@ -596,7 +644,7 @@
 	case STATE_P_ON:
 		switch (ctx->to_state) {
 		case STATE_TRX_OFF:
-			usleep_range(c->t_reset_to_off, c->t_reset_to_off + 10);
+			tim = ktime_set(0, c->t_reset_to_off * NSEC_PER_USEC);
 			goto change;
 		default:
 			break;
@@ -607,12 +655,10 @@
 	}
 
 	/* Default delay is 1us in the most cases */
-	udelay(1);
+	tim = ktime_set(0, NSEC_PER_USEC);
 
 change:
-	at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
-				 at86rf230_async_state_assert,
-				 ctx->irq_enable);
+	hrtimer_start(&ctx->timer, tim, HRTIMER_MODE_REL);
 }
 
 static void
@@ -648,14 +694,13 @@
 	 */
 	buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
 	buf[1] = ctx->to_state;
-	ctx->trx.len = 2;
 	ctx->msg.complete = at86rf230_async_state_delay;
 	rc = spi_async(lp->spi, &ctx->msg);
 	if (rc) {
 		if (ctx->irq_enable)
-			enable_irq(lp->spi->irq);
+			enable_irq(ctx->irq);
 
-		at86rf230_async_error(lp, &lp->state, rc);
+		at86rf230_async_error(lp, ctx, rc);
 	}
 }
 
@@ -690,7 +735,7 @@
 static int
 at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state)
 {
-	int rc;
+	unsigned long rc;
 
 	at86rf230_async_state_change(lp, &lp->state, state,
 				     at86rf230_sync_state_change_complete,
@@ -711,14 +756,10 @@
 {
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_local *lp = ctx->lp;
-	struct sk_buff *skb = lp->tx_skb;
 
-	enable_irq(lp->spi->irq);
+	enable_irq(ctx->irq);
 
-	if (lp->max_frame_retries <= 0)
-		ieee802154_xmit_complete(lp->hw, skb, true);
-	else
-		ieee802154_xmit_complete(lp->hw, skb, false);
+	ieee802154_xmit_complete(lp->hw, lp->tx_skb, !lp->tx_aret);
 }
 
 static void
@@ -727,7 +768,7 @@
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_local *lp = ctx->lp;
 
-	at86rf230_async_state_change(lp, &lp->irq, STATE_RX_AACK_ON,
+	at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON,
 				     at86rf230_tx_complete, true);
 }
 
@@ -753,16 +794,13 @@
 	 * to STATE_FORCE_TRX_OFF then STATE_TX_ON to recover the transceiver
 	 * state to TX_ON.
 	 */
-	if (trac) {
+	if (trac)
 		at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
 					     at86rf230_tx_trac_error, true);
-		return;
-	}
-
-	at86rf230_tx_on(context);
+	else
+		at86rf230_tx_on(context);
 }
 
-
 static void
 at86rf230_tx_trac_status(void *context)
 {
@@ -774,14 +812,25 @@
 }
 
 static void
-at86rf230_rx(struct at86rf230_local *lp,
-	     const u8 *data, const u8 len, const u8 lqi)
+at86rf230_rx_read_frame_complete(void *context)
 {
-	struct sk_buff *skb;
+	struct at86rf230_state_change *ctx = context;
+	struct at86rf230_local *lp = ctx->lp;
 	u8 rx_local_buf[AT86RF2XX_MAX_BUF];
+	const u8 *buf = ctx->buf;
+	struct sk_buff *skb;
+	u8 len, lqi;
 
-	memcpy(rx_local_buf, data, len);
-	enable_irq(lp->spi->irq);
+	len = buf[1];
+	if (!ieee802154_is_valid_psdu_len(len)) {
+		dev_vdbg(&lp->spi->dev, "corrupted frame received\n");
+		len = IEEE802154_MTU;
+	}
+	lqi = buf[2 + len];
+
+	memcpy(rx_local_buf, buf + 2, len);
+	ctx->trx.len = 2;
+	enable_irq(ctx->irq);
 
 	skb = dev_alloc_skb(IEEE802154_MTU);
 	if (!skb) {
@@ -794,60 +843,41 @@
 }
 
 static void
-at86rf230_rx_read_frame_complete(void *context)
+at86rf230_rx_read_frame(void *context)
 {
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_local *lp = ctx->lp;
-	const u8 *buf = lp->irq.buf;
-	u8 len = buf[1];
-
-	if (!ieee802154_is_valid_psdu_len(len)) {
-		dev_vdbg(&lp->spi->dev, "corrupted frame received\n");
-		len = IEEE802154_MTU;
-	}
-
-	at86rf230_rx(lp, buf + 2, len, buf[2 + len]);
-}
-
-static void
-at86rf230_rx_read_frame(struct at86rf230_local *lp)
-{
+	u8 *buf = ctx->buf;
 	int rc;
 
-	u8 *buf = lp->irq.buf;
-
 	buf[0] = CMD_FB;
-	lp->irq.trx.len = AT86RF2XX_MAX_BUF;
-	lp->irq.msg.complete = at86rf230_rx_read_frame_complete;
-	rc = spi_async(lp->spi, &lp->irq.msg);
+	ctx->trx.len = AT86RF2XX_MAX_BUF;
+	ctx->msg.complete = at86rf230_rx_read_frame_complete;
+	rc = spi_async(lp->spi, &ctx->msg);
 	if (rc) {
-		enable_irq(lp->spi->irq);
-		at86rf230_async_error(lp, &lp->irq, rc);
+		ctx->trx.len = 2;
+		enable_irq(ctx->irq);
+		at86rf230_async_error(lp, ctx, rc);
 	}
 }
 
 static void
 at86rf230_rx_trac_check(void *context)
 {
-	struct at86rf230_state_change *ctx = context;
-	struct at86rf230_local *lp = ctx->lp;
-
 	/* Possible check on trac status here. This could be useful to make
 	 * some stats why receive is failed. Not used at the moment, but it's
 	 * maybe timing relevant. Datasheet doesn't say anything about this.
 	 * The programming guide say do it so.
 	 */
 
-	at86rf230_rx_read_frame(lp);
+	at86rf230_rx_read_frame(context);
 }
 
 static void
 at86rf230_irq_trx_end(struct at86rf230_local *lp)
 {
-	spin_lock(&lp->lock);
 	if (lp->is_tx) {
 		lp->is_tx = 0;
-		spin_unlock(&lp->lock);
 
 		if (lp->tx_aret)
 			at86rf230_async_state_change(lp, &lp->irq,
@@ -860,7 +890,6 @@
 						     at86rf230_tx_complete,
 						     true);
 	} else {
-		spin_unlock(&lp->lock);
 		at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
 					 at86rf230_rx_trac_check, true);
 	}
@@ -871,13 +900,13 @@
 {
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_local *lp = ctx->lp;
-	const u8 *buf = lp->irq.buf;
+	const u8 *buf = ctx->buf;
 	const u8 irq = buf[1];
 
 	if (irq & IRQ_TRX_END) {
 		at86rf230_irq_trx_end(lp);
 	} else {
-		enable_irq(lp->spi->irq);
+		enable_irq(ctx->irq);
 		dev_err(&lp->spi->dev, "not supported irq %02x received\n",
 			irq);
 	}
@@ -893,7 +922,6 @@
 	disable_irq_nosync(irq);
 
 	buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
-	ctx->trx.len = 2;
 	ctx->msg.complete = at86rf230_irq_status;
 	rc = spi_async(lp->spi, &ctx->msg);
 	if (rc) {
@@ -928,21 +956,21 @@
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_local *lp = ctx->lp;
 	struct sk_buff *skb = lp->tx_skb;
-	u8 *buf = lp->tx.buf;
+	u8 *buf = ctx->buf;
 	int rc;
 
-	spin_lock(&lp->lock);
 	lp->is_tx = 1;
-	spin_unlock(&lp->lock);
 
 	buf[0] = CMD_FB | CMD_WRITE;
 	buf[1] = skb->len + 2;
 	memcpy(buf + 2, skb->data, skb->len);
-	lp->tx.trx.len = skb->len + 2;
-	lp->tx.msg.complete = at86rf230_write_frame_complete;
-	rc = spi_async(lp->spi, &lp->tx.msg);
-	if (rc)
+	ctx->trx.len = skb->len + 2;
+	ctx->msg.complete = at86rf230_write_frame_complete;
+	rc = spi_async(lp->spi, &ctx->msg);
+	if (rc) {
+		ctx->trx.len = 2;
 		at86rf230_async_error(lp, ctx, rc);
+	}
 }
 
 static void
@@ -955,24 +983,45 @@
 				     at86rf230_write_frame, false);
 }
 
-static int
-at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
+static void
+at86rf230_xmit_start(void *context)
 {
-	struct at86rf230_local *lp = hw->priv;
-	struct at86rf230_state_change *ctx = &lp->tx;
-
-	void (*tx_complete)(void *context) = at86rf230_write_frame;
-
-	lp->tx_skb = skb;
+	struct at86rf230_state_change *ctx = context;
+	struct at86rf230_local *lp = ctx->lp;
 
 	/* In ARET mode we need to go into STATE_TX_ARET_ON after we
 	 * are in STATE_TX_ON. The pfad differs here, so we change
 	 * the complete handler.
 	 */
 	if (lp->tx_aret)
-		tx_complete = at86rf230_xmit_tx_on;
+		at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
+					     at86rf230_xmit_tx_on, false);
+	else
+		at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
+					     at86rf230_write_frame, false);
+}
 
-	at86rf230_async_state_change(lp, ctx, STATE_TX_ON, tx_complete, false);
+static int
+at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
+{
+	struct at86rf230_local *lp = hw->priv;
+	struct at86rf230_state_change *ctx = &lp->tx;
+
+	lp->tx_skb = skb;
+	lp->tx_retry = 0;
+
+	/* After 5 minutes in PLL and the same frequency we run again the
+	 * calibration loops which is recommended by at86rf2xx datasheets.
+	 *
+	 * The calibration is initiate by a state change from TRX_OFF
+	 * to TX_ON, the lp->cal_timeout should be reinit by state_delay
+	 * function then to start in the next 5 minutes.
+	 */
+	if (time_is_before_jiffies(lp->cal_timeout))
+		at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF,
+					     at86rf230_xmit_start, false);
+	else
+		at86rf230_xmit_start(ctx);
 
 	return 0;
 }
@@ -988,6 +1037,9 @@
 static int
 at86rf230_start(struct ieee802154_hw *hw)
 {
+	struct at86rf230_local *lp = hw->priv;
+
+	lp->cal_timeout = jiffies + AT86RF2XX_CAL_LOOP_TIMEOUT;
 	return at86rf230_sync_state_change(hw->priv, STATE_RX_AACK_ON);
 }
 
@@ -1068,6 +1120,8 @@
 	/* Wait for PLL */
 	usleep_range(lp->data->t_channel_switch,
 		     lp->data->t_channel_switch + 10);
+
+	lp->cal_timeout = jiffies + AT86RF2XX_CAL_LOOP_TIMEOUT;
 	return rc;
 }
 
@@ -1082,7 +1136,7 @@
 		u16 addr = le16_to_cpu(filt->short_addr);
 
 		dev_vdbg(&lp->spi->dev,
-			"at86rf230_set_hw_addr_filt called for saddr\n");
+			 "at86rf230_set_hw_addr_filt called for saddr\n");
 		__at86rf230_write(lp, RG_SHORT_ADDR_0, addr);
 		__at86rf230_write(lp, RG_SHORT_ADDR_1, addr >> 8);
 	}
@@ -1091,7 +1145,7 @@
 		u16 pan = le16_to_cpu(filt->pan_id);
 
 		dev_vdbg(&lp->spi->dev,
-			"at86rf230_set_hw_addr_filt called for pan id\n");
+			 "at86rf230_set_hw_addr_filt called for pan id\n");
 		__at86rf230_write(lp, RG_PAN_ID_0, pan);
 		__at86rf230_write(lp, RG_PAN_ID_1, pan >> 8);
 	}
@@ -1101,14 +1155,14 @@
 
 		memcpy(addr, &filt->ieee_addr, 8);
 		dev_vdbg(&lp->spi->dev,
-			"at86rf230_set_hw_addr_filt called for IEEE addr\n");
+			 "at86rf230_set_hw_addr_filt called for IEEE addr\n");
 		for (i = 0; i < 8; i++)
 			__at86rf230_write(lp, RG_IEEE_ADDR_0 + i, addr[i]);
 	}
 
 	if (changed & IEEE802154_AFILT_PANC_CHANGED) {
 		dev_vdbg(&lp->spi->dev,
-			"at86rf230_set_hw_addr_filt called for panc change\n");
+			 "at86rf230_set_hw_addr_filt called for panc change\n");
 		if (filt->pan_coord)
 			at86rf230_write_subreg(lp, SR_AACK_I_AM_COORD, 1);
 		else
@@ -1119,7 +1173,7 @@
 }
 
 static int
-at86rf230_set_txpower(struct ieee802154_hw *hw, int db)
+at86rf230_set_txpower(struct ieee802154_hw *hw, s8 db)
 {
 	struct at86rf230_local *lp = hw->priv;
 
@@ -1146,11 +1200,37 @@
 }
 
 static int
-at86rf230_set_cca_mode(struct ieee802154_hw *hw, u8 mode)
+at86rf230_set_cca_mode(struct ieee802154_hw *hw,
+		       const struct wpan_phy_cca *cca)
 {
 	struct at86rf230_local *lp = hw->priv;
+	u8 val;
 
-	return at86rf230_write_subreg(lp, SR_CCA_MODE, mode);
+	/* mapping 802.15.4 to driver spec */
+	switch (cca->mode) {
+	case NL802154_CCA_ENERGY:
+		val = 1;
+		break;
+	case NL802154_CCA_CARRIER:
+		val = 2;
+		break;
+	case NL802154_CCA_ENERGY_CARRIER:
+		switch (cca->opt) {
+		case NL802154_CCA_OPT_ENERGY_CARRIER_AND:
+			val = 3;
+			break;
+		case NL802154_CCA_OPT_ENERGY_CARRIER_OR:
+			val = 0;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return at86rf230_write_subreg(lp, SR_CCA_MODE, val);
 }
 
 static int
@@ -1262,7 +1342,6 @@
 	.t_off_to_tx_on = 80,
 	.t_frame = 4096,
 	.t_p_ack = 545,
-	.t_tx_timeout = 2000,
 	.rssi_base_val = -91,
 	.set_channel = at86rf23x_set_channel,
 	.get_desense_steps = at86rf23x_get_desens_steps
@@ -1276,7 +1355,6 @@
 	.t_off_to_tx_on = 110,
 	.t_frame = 4096,
 	.t_p_ack = 545,
-	.t_tx_timeout = 2000,
 	.rssi_base_val = -91,
 	.set_channel = at86rf23x_set_channel,
 	.get_desense_steps = at86rf23x_get_desens_steps
@@ -1290,13 +1368,12 @@
 	.t_off_to_tx_on = 200,
 	.t_frame = 4096,
 	.t_p_ack = 545,
-	.t_tx_timeout = 2000,
 	.rssi_base_val = -100,
 	.set_channel = at86rf212_set_channel,
 	.get_desense_steps = at86rf212_get_desens_steps
 };
 
-static int at86rf230_hw_init(struct at86rf230_local *lp)
+static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim)
 {
 	int rc, irq_type, irq_pol = IRQ_ACTIVE_HIGH;
 	unsigned int dvdd;
@@ -1307,7 +1384,12 @@
 		return rc;
 
 	irq_type = irq_get_trigger_type(lp->spi->irq);
-	if (irq_type == IRQ_TYPE_EDGE_FALLING)
+	if (irq_type == IRQ_TYPE_EDGE_RISING ||
+	    irq_type == IRQ_TYPE_EDGE_FALLING)
+		dev_warn(&lp->spi->dev,
+			 "Using edge triggered irq's are not recommended!\n");
+	if (irq_type == IRQ_TYPE_EDGE_FALLING ||
+	    irq_type == IRQ_TYPE_LEVEL_LOW)
 		irq_pol = IRQ_ACTIVE_LOW;
 
 	rc = at86rf230_write_subreg(lp, SR_IRQ_POLARITY, irq_pol);
@@ -1322,6 +1404,11 @@
 	if (rc)
 		return rc;
 
+	/* reset values differs in at86rf231 and at86rf233 */
+	rc = at86rf230_write_subreg(lp, SR_IRQ_MASK_MODE, 0);
+	if (rc)
+		return rc;
+
 	get_random_bytes(csma_seed, ARRAY_SIZE(csma_seed));
 	rc = at86rf230_write_subreg(lp, SR_CSMA_SEED_0, csma_seed[0]);
 	if (rc)
@@ -1343,6 +1430,45 @@
 	usleep_range(lp->data->t_sleep_cycle,
 		     lp->data->t_sleep_cycle + 100);
 
+	/* xtal_trim value is calculated by:
+	 * CL = 0.5 * (CX + CTRIM + CPAR)
+	 *
+	 * whereas:
+	 * CL = capacitor of used crystal
+	 * CX = connected capacitors at xtal pins
+	 * CPAR = in all at86rf2xx datasheets this is a constant value 3 pF,
+	 *	  but this is different on each board setup. You need to fine
+	 *	  tuning this value via CTRIM.
+	 * CTRIM = variable capacitor setting. Resolution is 0.3 pF range is
+	 *	   0 pF upto 4.5 pF.
+	 *
+	 * Examples:
+	 * atben transceiver:
+	 *
+	 * CL = 8 pF
+	 * CX = 12 pF
+	 * CPAR = 3 pF (We assume the magic constant from datasheet)
+	 * CTRIM = 0.9 pF
+	 *
+	 * (12+0.9+3)/2 = 7.95 which is nearly at 8 pF
+	 *
+	 * xtal_trim = 0x3
+	 *
+	 * openlabs transceiver:
+	 *
+	 * CL = 16 pF
+	 * CX = 22 pF
+	 * CPAR = 3 pF (We assume the magic constant from datasheet)
+	 * CTRIM = 4.5 pF
+	 *
+	 * (22+4.5+3)/2 = 14.75 which is the nearest value to 16 pF
+	 *
+	 * xtal_trim = 0xf
+	 */
+	rc = at86rf230_write_subreg(lp, SR_XTAL_TRIM, xtal_trim);
+	if (rc)
+		return rc;
+
 	rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &dvdd);
 	if (rc)
 		return rc;
@@ -1358,24 +1484,30 @@
 	return at86rf230_write_subreg(lp, SR_SLOTTED_OPERATION, 0);
 }
 
-static struct at86rf230_platform_data *
-at86rf230_get_pdata(struct spi_device *spi)
+static int
+at86rf230_get_pdata(struct spi_device *spi, int *rstn, int *slp_tr,
+		    u8 *xtal_trim)
 {
-	struct at86rf230_platform_data *pdata;
+	struct at86rf230_platform_data *pdata = spi->dev.platform_data;
+	int ret;
 
-	if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node)
-		return spi->dev.platform_data;
+	if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node) {
+		if (!pdata)
+			return -ENOENT;
 
-	pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL);
-	if (!pdata)
-		goto done;
+		*rstn = pdata->rstn;
+		*slp_tr = pdata->slp_tr;
+		*xtal_trim = pdata->xtal_trim;
+		return 0;
+	}
 
-	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);
+	*rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0);
+	*slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0);
+	ret = of_property_read_u8(spi->dev.of_node, "xtal-trim", xtal_trim);
+	if (ret < 0 && ret != -EINVAL)
+		return ret;
 
-	spi->dev.platform_data = pdata;
-done:
-	return pdata;
+	return 0;
 }
 
 static int
@@ -1400,7 +1532,7 @@
 	if (rc)
 		return rc;
 
-	rc = __at86rf230_read(lp, RG_PART_NUM, &version);
+	rc = __at86rf230_read(lp, RG_VERSION_NUM, &version);
 	if (rc)
 		return rc;
 
@@ -1410,11 +1542,12 @@
 		return -EINVAL;
 	}
 
-	lp->hw->extra_tx_headroom = 0;
 	lp->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AACK |
 			IEEE802154_HW_TXPOWER | IEEE802154_HW_ARET |
 			IEEE802154_HW_AFILT | IEEE802154_HW_PROMISCUOUS;
 
+	lp->hw->phy->cca.mode = NL802154_CCA_ENERGY;
+
 	switch (part) {
 	case 2:
 		chip = "at86rf230";
@@ -1429,16 +1562,12 @@
 		break;
 	case 7:
 		chip = "at86rf212";
-		if (version == 1) {
-			lp->data = &at86rf212_data;
-			lp->hw->flags |= IEEE802154_HW_LBT;
-			lp->hw->phy->channels_supported[0] = 0x00007FF;
-			lp->hw->phy->channels_supported[2] = 0x00007FF;
-			lp->hw->phy->current_channel = 5;
-			lp->hw->phy->symbol_duration = 25;
-		} else {
-			rc = -ENOTSUPP;
-		}
+		lp->data = &at86rf212_data;
+		lp->hw->flags |= IEEE802154_HW_LBT;
+		lp->hw->phy->channels_supported[0] = 0x00007FF;
+		lp->hw->phy->channels_supported[2] = 0x00007FF;
+		lp->hw->phy->current_channel = 5;
+		lp->hw->phy->symbol_duration = 25;
 		break;
 	case 11:
 		chip = "at86rf233";
@@ -1448,7 +1577,7 @@
 		lp->hw->phy->symbol_duration = 16;
 		break;
 	default:
-		chip = "unkown";
+		chip = "unknown";
 		rc = -ENOTSUPP;
 		break;
 	}
@@ -1462,66 +1591,78 @@
 at86rf230_setup_spi_messages(struct at86rf230_local *lp)
 {
 	lp->state.lp = lp;
+	lp->state.irq = lp->spi->irq;
 	spi_message_init(&lp->state.msg);
 	lp->state.msg.context = &lp->state;
+	lp->state.trx.len = 2;
 	lp->state.trx.tx_buf = lp->state.buf;
 	lp->state.trx.rx_buf = lp->state.buf;
 	spi_message_add_tail(&lp->state.trx, &lp->state.msg);
+	hrtimer_init(&lp->state.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	lp->state.timer.function = at86rf230_async_state_timer;
 
 	lp->irq.lp = lp;
+	lp->irq.irq = lp->spi->irq;
 	spi_message_init(&lp->irq.msg);
 	lp->irq.msg.context = &lp->irq;
+	lp->irq.trx.len = 2;
 	lp->irq.trx.tx_buf = lp->irq.buf;
 	lp->irq.trx.rx_buf = lp->irq.buf;
 	spi_message_add_tail(&lp->irq.trx, &lp->irq.msg);
+	hrtimer_init(&lp->irq.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	lp->irq.timer.function = at86rf230_async_state_timer;
 
 	lp->tx.lp = lp;
+	lp->tx.irq = lp->spi->irq;
 	spi_message_init(&lp->tx.msg);
 	lp->tx.msg.context = &lp->tx;
+	lp->tx.trx.len = 2;
 	lp->tx.trx.tx_buf = lp->tx.buf;
 	lp->tx.trx.rx_buf = lp->tx.buf;
 	spi_message_add_tail(&lp->tx.trx, &lp->tx.msg);
+	hrtimer_init(&lp->tx.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	lp->tx.timer.function = at86rf230_async_state_timer;
 }
 
 static int at86rf230_probe(struct spi_device *spi)
 {
-	struct at86rf230_platform_data *pdata;
 	struct ieee802154_hw *hw;
 	struct at86rf230_local *lp;
 	unsigned int status;
-	int rc, irq_type;
+	int rc, irq_type, rstn, slp_tr;
+	u8 xtal_trim = 0;
 
 	if (!spi->irq) {
 		dev_err(&spi->dev, "no IRQ specified\n");
 		return -EINVAL;
 	}
 
-	pdata = at86rf230_get_pdata(spi);
-	if (!pdata) {
-		dev_err(&spi->dev, "no platform_data\n");
-		return -EINVAL;
+	rc = at86rf230_get_pdata(spi, &rstn, &slp_tr, &xtal_trim);
+	if (rc < 0) {
+		dev_err(&spi->dev, "failed to parse platform_data: %d\n", rc);
+		return rc;
 	}
 
-	if (gpio_is_valid(pdata->rstn)) {
-		rc = devm_gpio_request_one(&spi->dev, pdata->rstn,
+	if (gpio_is_valid(rstn)) {
+		rc = devm_gpio_request_one(&spi->dev, rstn,
 					   GPIOF_OUT_INIT_HIGH, "rstn");
 		if (rc)
 			return rc;
 	}
 
-	if (gpio_is_valid(pdata->slp_tr)) {
-		rc = devm_gpio_request_one(&spi->dev, pdata->slp_tr,
+	if (gpio_is_valid(slp_tr)) {
+		rc = devm_gpio_request_one(&spi->dev, slp_tr,
 					   GPIOF_OUT_INIT_LOW, "slp_tr");
 		if (rc)
 			return rc;
 	}
 
 	/* Reset */
-	if (gpio_is_valid(pdata->rstn)) {
+	if (gpio_is_valid(rstn)) {
 		udelay(1);
-		gpio_set_value(pdata->rstn, 0);
+		gpio_set_value(rstn, 0);
 		udelay(1);
-		gpio_set_value(pdata->rstn, 1);
+		gpio_set_value(rstn, 1);
 		usleep_range(120, 240);
 	}
 
@@ -1550,12 +1691,11 @@
 	if (rc < 0)
 		goto free_dev;
 
-	spin_lock_init(&lp->lock);
 	init_completion(&lp->state_complete);
 
 	spi_set_drvdata(spi, lp);
 
-	rc = at86rf230_hw_init(lp);
+	rc = at86rf230_hw_init(lp, xtal_trim);
 	if (rc)
 		goto free_dev;
 
diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c
index f9df9fa..f833b8b 100644
--- a/drivers/net/ieee802154/cc2520.c
+++ b/drivers/net/ieee802154/cc2520.c
@@ -19,7 +19,6 @@
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/skbuff.h>
-#include <linux/pinctrl/consumer.h>
 #include <linux/of_gpio.h>
 #include <linux/ieee802154.h>
 
@@ -45,9 +44,9 @@
 #define	CC2520_FREG_MASK	0x3F
 
 /* status byte values */
-#define	CC2520_STATUS_XOSC32M_STABLE	(1 << 7)
-#define	CC2520_STATUS_RSSI_VALID	(1 << 6)
-#define	CC2520_STATUS_TX_UNDERFLOW	(1 << 3)
+#define	CC2520_STATUS_XOSC32M_STABLE	BIT(7)
+#define	CC2520_STATUS_RSSI_VALID	BIT(6)
+#define	CC2520_STATUS_TX_UNDERFLOW	BIT(3)
 
 /* IEEE-802.15.4 defined constants (2.4 GHz logical channels) */
 #define	CC2520_MINCHANNEL		11
@@ -513,7 +512,6 @@
 	return rc;
 }
 
-
 static int cc2520_rx(struct cc2520_private *priv)
 {
 	u8 len = 0, lqi = 0, bytes = 1;
@@ -551,14 +549,14 @@
 	u8 rssi;
 	int ret;
 
-	ret = cc2520_read_register(priv , CC2520_RSSISTAT, &status);
+	ret = cc2520_read_register(priv, CC2520_RSSISTAT, &status);
 	if (ret)
 		return ret;
 
 	if (status != RSSI_VALID)
 		return -EINVAL;
 
-	ret = cc2520_read_register(priv , CC2520_RSSI, &rssi);
+	ret = cc2520_read_register(priv, CC2520_RSSI, &rssi);
 	if (ret)
 		return ret;
 
@@ -652,6 +650,7 @@
 	priv->hw->parent = &priv->spi->dev;
 	priv->hw->extra_tx_headroom = 0;
 	priv->hw->vif_data_size = sizeof(*priv);
+	ieee802154_random_extended_addr(&priv->hw->phy->perm_extended_addr);
 
 	/* We do support only 2.4 Ghz */
 	priv->hw->phy->channels_supported[0] = 0x7FFF800;
@@ -715,11 +714,45 @@
 	return IRQ_HANDLED;
 }
 
+static int cc2520_get_platform_data(struct spi_device *spi,
+				    struct cc2520_platform_data *pdata)
+{
+	struct device_node *np = spi->dev.of_node;
+	struct cc2520_private *priv = spi_get_drvdata(spi);
+
+	if (!np) {
+		struct cc2520_platform_data *spi_pdata = spi->dev.platform_data;
+		if (!spi_pdata)
+			return -ENOENT;
+		*pdata = *spi_pdata;
+		return 0;
+	}
+
+	pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0);
+	priv->fifo_pin = pdata->fifo;
+
+	pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0);
+
+	pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0);
+	pdata->cca = of_get_named_gpio(np, "cca-gpio", 0);
+	pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0);
+	pdata->reset = of_get_named_gpio(np, "reset-gpio", 0);
+
+	pdata->amplified = of_property_read_bool(np, "amplified");
+
+	return 0;
+}
+
 static int cc2520_hw_init(struct cc2520_private *priv)
 {
 	u8 status = 0, state = 0xff;
 	int ret;
 	int timeout = 100;
+	struct cc2520_platform_data pdata;
+
+	ret = cc2520_get_platform_data(priv->spi, &pdata);
+	if (ret)
+		goto err_ret;
 
 	ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state);
 	if (ret)
@@ -742,11 +775,47 @@
 
 	dev_vdbg(&priv->spi->dev, "oscillator brought up\n");
 
-	/* Registers default value: section 28.1 in Datasheet */
-	ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7);
-	if (ret)
-		goto err_ret;
+	/* If the CC2520 is connected to a CC2591 amplifier, we must both
+	 * configure GPIOs on the CC2520 to correctly configure the CC2591
+	 * and change a couple settings of the CC2520 to work with the
+	 * amplifier. See section 8 page 17 of TI application note AN065.
+	 * http://www.ti.com/lit/an/swra229a/swra229a.pdf
+	 */
+	if (pdata.amplified) {
+		ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF9);
+		if (ret)
+			goto err_ret;
 
+		ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x16);
+		if (ret)
+			goto err_ret;
+
+		ret = cc2520_write_register(priv, CC2520_GPIOCTRL0, 0x46);
+		if (ret)
+			goto err_ret;
+
+		ret = cc2520_write_register(priv, CC2520_GPIOCTRL5, 0x47);
+		if (ret)
+			goto err_ret;
+
+		ret = cc2520_write_register(priv, CC2520_GPIOPOLARITY, 0x1e);
+		if (ret)
+			goto err_ret;
+
+		ret = cc2520_write_register(priv, CC2520_TXCTRL, 0xc1);
+		if (ret)
+			goto err_ret;
+	} else {
+		ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7);
+		if (ret)
+			goto err_ret;
+
+		ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11);
+		if (ret)
+			goto err_ret;
+	}
+
+	/* Registers default value: section 28.1 in Datasheet */
 	ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A);
 	if (ret)
 		goto err_ret;
@@ -771,10 +840,6 @@
 	if (ret)
 		goto err_ret;
 
-	ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11);
-	if (ret)
-		goto err_ret;
-
 	ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10);
 	if (ret)
 		goto err_ret;
@@ -809,59 +874,20 @@
 	return ret;
 }
 
-static struct cc2520_platform_data *
-cc2520_get_platform_data(struct spi_device *spi)
-{
-	struct cc2520_platform_data *pdata;
-	struct device_node *np = spi->dev.of_node;
-	struct cc2520_private *priv = spi_get_drvdata(spi);
-
-	if (!np)
-		return spi->dev.platform_data;
-
-	pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL);
-	if (!pdata)
-		goto done;
-
-	pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0);
-	priv->fifo_pin = pdata->fifo;
-
-	pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0);
-
-	pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0);
-	pdata->cca = of_get_named_gpio(np, "cca-gpio", 0);
-	pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0);
-	pdata->reset = of_get_named_gpio(np, "reset-gpio", 0);
-
-	spi->dev.platform_data = pdata;
-
-done:
-	return pdata;
-}
-
 static int cc2520_probe(struct spi_device *spi)
 {
 	struct cc2520_private *priv;
-	struct pinctrl *pinctrl;
-	struct cc2520_platform_data *pdata;
+	struct cc2520_platform_data pdata;
 	int ret;
 
-	priv = devm_kzalloc(&spi->dev,
-			    sizeof(struct cc2520_private), GFP_KERNEL);
-	if (!priv) {
-		ret = -ENOMEM;
-		goto err_ret;
-	}
+	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
 
 	spi_set_drvdata(spi, priv);
 
-	pinctrl = devm_pinctrl_get_select_default(&spi->dev);
-	if (IS_ERR(pinctrl))
-		dev_warn(&spi->dev,
-			 "pinctrl pins are not configured\n");
-
-	pdata = cc2520_get_platform_data(spi);
-	if (!pdata) {
+	ret = cc2520_get_platform_data(spi, &pdata);
+	if (ret < 0) {
 		dev_err(&spi->dev, "no platform data\n");
 		return -EINVAL;
 	}
@@ -870,10 +896,8 @@
 
 	priv->buf = devm_kzalloc(&spi->dev,
 				 SPI_COMMAND_BUFFER, GFP_KERNEL);
-	if (!priv->buf) {
-		ret = -ENOMEM;
-		goto err_ret;
-	}
+	if (!priv->buf)
+		return -ENOMEM;
 
 	mutex_init(&priv->buffer_mutex);
 	INIT_WORK(&priv->fifop_irqwork, cc2520_fifop_irqwork);
@@ -881,77 +905,76 @@
 	init_completion(&priv->tx_complete);
 
 	/* Request all the gpio's */
-	if (!gpio_is_valid(pdata->fifo)) {
+	if (!gpio_is_valid(pdata.fifo)) {
 		dev_err(&spi->dev, "fifo gpio is not valid\n");
 		ret = -EINVAL;
 		goto err_hw_init;
 	}
 
-	ret = devm_gpio_request_one(&spi->dev, pdata->fifo,
+	ret = devm_gpio_request_one(&spi->dev, pdata.fifo,
 				    GPIOF_IN, "fifo");
 	if (ret)
 		goto err_hw_init;
 
-	if (!gpio_is_valid(pdata->cca)) {
+	if (!gpio_is_valid(pdata.cca)) {
 		dev_err(&spi->dev, "cca gpio is not valid\n");
 		ret = -EINVAL;
 		goto err_hw_init;
 	}
 
-	ret = devm_gpio_request_one(&spi->dev, pdata->cca,
+	ret = devm_gpio_request_one(&spi->dev, pdata.cca,
 				    GPIOF_IN, "cca");
 	if (ret)
 		goto err_hw_init;
 
-	if (!gpio_is_valid(pdata->fifop)) {
+	if (!gpio_is_valid(pdata.fifop)) {
 		dev_err(&spi->dev, "fifop gpio is not valid\n");
 		ret = -EINVAL;
 		goto err_hw_init;
 	}
 
-	ret = devm_gpio_request_one(&spi->dev, pdata->fifop,
+	ret = devm_gpio_request_one(&spi->dev, pdata.fifop,
 				    GPIOF_IN, "fifop");
 	if (ret)
 		goto err_hw_init;
 
-	if (!gpio_is_valid(pdata->sfd)) {
+	if (!gpio_is_valid(pdata.sfd)) {
 		dev_err(&spi->dev, "sfd gpio is not valid\n");
 		ret = -EINVAL;
 		goto err_hw_init;
 	}
 
-	ret = devm_gpio_request_one(&spi->dev, pdata->sfd,
+	ret = devm_gpio_request_one(&spi->dev, pdata.sfd,
 				    GPIOF_IN, "sfd");
 	if (ret)
 		goto err_hw_init;
 
-	if (!gpio_is_valid(pdata->reset)) {
+	if (!gpio_is_valid(pdata.reset)) {
 		dev_err(&spi->dev, "reset gpio is not valid\n");
 		ret = -EINVAL;
 		goto err_hw_init;
 	}
 
-	ret = devm_gpio_request_one(&spi->dev, pdata->reset,
+	ret = devm_gpio_request_one(&spi->dev, pdata.reset,
 				    GPIOF_OUT_INIT_LOW, "reset");
 	if (ret)
 		goto err_hw_init;
 
-	if (!gpio_is_valid(pdata->vreg)) {
+	if (!gpio_is_valid(pdata.vreg)) {
 		dev_err(&spi->dev, "vreg gpio is not valid\n");
 		ret = -EINVAL;
 		goto err_hw_init;
 	}
 
-	ret = devm_gpio_request_one(&spi->dev, pdata->vreg,
+	ret = devm_gpio_request_one(&spi->dev, pdata.vreg,
 				    GPIOF_OUT_INIT_LOW, "vreg");
 	if (ret)
 		goto err_hw_init;
 
-
-	gpio_set_value(pdata->vreg, HIGH);
+	gpio_set_value(pdata.vreg, HIGH);
 	usleep_range(100, 150);
 
-	gpio_set_value(pdata->reset, HIGH);
+	gpio_set_value(pdata.reset, HIGH);
 	usleep_range(200, 250);
 
 	ret = cc2520_hw_init(priv);
@@ -960,7 +983,7 @@
 
 	/* Set up fifop interrupt */
 	ret = devm_request_irq(&spi->dev,
-			       gpio_to_irq(pdata->fifop),
+			       gpio_to_irq(pdata.fifop),
 			       cc2520_fifop_isr,
 			       IRQF_TRIGGER_RISING,
 			       dev_name(&spi->dev),
@@ -972,7 +995,7 @@
 
 	/* Set up sfd interrupt */
 	ret = devm_request_irq(&spi->dev,
-			       gpio_to_irq(pdata->sfd),
+			       gpio_to_irq(pdata.sfd),
 			       cc2520_sfd_isr,
 			       IRQF_TRIGGER_FALLING,
 			       dev_name(&spi->dev),
@@ -991,8 +1014,6 @@
 err_hw_init:
 	mutex_destroy(&priv->buffer_mutex);
 	flush_work(&priv->fifop_irqwork);
-
-err_ret:
 	return ret;
 }
 
diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c
index a200fa1..fba2dfd 100644
--- a/drivers/net/ieee802154/mrf24j40.c
+++ b/drivers/net/ieee802154/mrf24j40.c
@@ -289,7 +289,7 @@
 		goto out;
 
 	/* Range check the RX FIFO length, accounting for the one-byte
-	 * length field at the begining. */
+	 * length field at the beginning. */
 	if (rx_len > RX_FIFO_SIZE-1) {
 		dev_err(printdev(devrec), "Invalid length read from device. Performing short read.\n");
 		rx_len = RX_FIFO_SIZE-1;
@@ -323,7 +323,7 @@
 
 #ifdef DEBUG
 	print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ",
-		DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0);
+		       DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0);
 	pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n",
 		 lqi_rssi[0], lqi_rssi[1]);
 #endif
@@ -521,7 +521,7 @@
 		 */
 
 		dev_dbg(printdev(devrec), "Set Pan Coord to %s\n",
-					filt->pan_coord ? "on" : "off");
+			filt->pan_coord ? "on" : "off");
 	}
 
 	return 0;
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index d6683dd..c7685ff 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -175,6 +175,7 @@
 	    * Linksys USB200M
 	    * Netgear FA120
 	    * Sitecom LN-029
+	    * Sitecom LN-028
 	    * Intellinet USB 2.0 Ethernet
 	    * ST Lab USB 2.0 Ethernet
 	    * TrendNet TU2-ET100
@@ -441,14 +442,14 @@
 	  not generally have permanently assigned Ethernet addresses.
 
 config USB_ALI_M5632
-	boolean "ALi M5632 based 'USB 2.0 Data Link' cables"
+	bool "ALi M5632 based 'USB 2.0 Data Link' cables"
 	depends on USB_NET_CDC_SUBSET
 	help
 	  Choose this option if you're using a host-to-host cable
 	  based on this design, which supports USB 2.0 high speed.
 
 config USB_AN2720
-	boolean "AnchorChips 2720 based cables (Xircom PGUNET, ...)"
+	bool "AnchorChips 2720 based cables (Xircom PGUNET, ...)"
 	depends on USB_NET_CDC_SUBSET
 	help
 	  Choose this option if you're using a host-to-host cable
@@ -456,7 +457,7 @@
 	  Cypress brand.
 
 config USB_BELKIN
-	boolean "eTEK based host-to-host cables (Advance, Belkin, ...)"
+	bool "eTEK based host-to-host cables (Advance, Belkin, ...)"
 	depends on USB_NET_CDC_SUBSET
 	default y
 	help
@@ -465,7 +466,7 @@
 	  microcontroller, with LEDs that indicate traffic.
 
 config USB_ARMLINUX
-	boolean "Embedded ARM Linux links (iPaq, ...)"
+	bool "Embedded ARM Linux links (iPaq, ...)"
 	depends on USB_NET_CDC_SUBSET
 	default y
 	help
@@ -482,14 +483,14 @@
 	  this simpler protocol by installing a different kernel.
 
 config USB_EPSON2888
-	boolean "Epson 2888 based firmware (DEVELOPMENT)"
+	bool "Epson 2888 based firmware (DEVELOPMENT)"
 	depends on USB_NET_CDC_SUBSET
 	help
 	  Choose this option to support the usb networking links used
 	  by some sample firmware from Epson.
 
 config USB_KC2190
-	boolean "KT Technology KC2190 based cables (InstaNet)"
+	bool "KT Technology KC2190 based cables (InstaNet)"
 	depends on USB_NET_CDC_SUBSET
 	help
 	  Choose this option if you're using a host-to-host cable
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 80a714b..8635713 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -522,6 +522,7 @@
 #define DELL_VENDOR_ID		0x413C
 #define REALTEK_VENDOR_ID	0x0bda
 #define SAMSUNG_VENDOR_ID	0x04e8
+#define LENOVO_VENDOR_ID	0x17ef
 
 static const struct usb_device_id	products[] = {
 /* BLACKLIST !!
@@ -702,6 +703,13 @@
 	.driver_info = 0,
 },
 
+/* Lenovo Thinkpad USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
+{
+	USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7205, USB_CLASS_COMM,
+			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+	.driver_info = 0,
+},
+
 /* WHITELIST!!!
  *
  * CDC Ether uses two interfaces, not necessarily consecutive.
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c
index e330332..3e60fab 100644
--- a/drivers/net/usb/cdc_mbim.c
+++ b/drivers/net/usb/cdc_mbim.c
@@ -414,7 +414,7 @@
 	skb_put(skb, ETH_HLEN);
 	skb_reset_mac_header(skb);
 	eth_hdr(skb)->h_proto = proto;
-	memset(eth_hdr(skb)->h_source, 0, ETH_ALEN);
+	eth_zero_addr(eth_hdr(skb)->h_source);
 	memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN);
 
 	/* add datagram */
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index b203436..06f5823 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -1174,17 +1174,17 @@
 
 	/* 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.
+	/* usbnet will count all the framing overhead by default.
 	 * 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;
+	usbnet_set_skb_tx_stats(skb_out, n,
+				ctx->tx_curr_frame_payload - skb_out->len);
 
 	return skb_out;
 
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index c138280..78768a4 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -56,6 +56,8 @@
 /* default ethernet address used by the modem */
 static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3};
 
+static const u8 buggy_fw_addr[ETH_ALEN] = {0x00, 0xa0, 0xc6, 0x00, 0x00, 0x00};
+
 /* Make up an ethernet header if the packet doesn't have one.
  *
  * A firmware bug common among several devices cause them to send raw
@@ -106,7 +108,7 @@
 	skb_push(skb, ETH_HLEN);
 	skb_reset_mac_header(skb);
 	eth_hdr(skb)->h_proto = proto;
-	memset(eth_hdr(skb)->h_source, 0, ETH_ALEN);
+	eth_zero_addr(eth_hdr(skb)->h_source);
 fix_dest:
 	memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN);
 	return 1;
@@ -332,10 +334,12 @@
 		usb_driver_release_interface(driver, info->data);
 	}
 
-	/* Never use the same address on both ends of the link, even
-	 * if the buggy firmware told us to.
+	/* Never use the same address on both ends of the link, even if the
+	 * buggy firmware told us to. Or, if device is assigned the well-known
+	 * buggy firmware MAC address, replace it with a random address,
 	 */
-	if (ether_addr_equal(dev->net->dev_addr, default_modem_addr))
+	if (ether_addr_equal(dev->net->dev_addr, default_modem_addr) ||
+	    ether_addr_equal(dev->net->dev_addr, buggy_fw_addr))
 		eth_hw_addr_random(dev->net);
 
 	/* make MAC addr easily distinguishable from an IP header */
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index a0dcb2f..772255c 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -160,20 +160,19 @@
 
 int usbnet_get_ethernet_addr(struct usbnet *dev, int iMACAddress)
 {
-	int 		tmp, i;
+	int 		tmp = -1, ret;
 	unsigned char	buf [13];
 
-	tmp = usb_string(dev->udev, iMACAddress, buf, sizeof buf);
-	if (tmp != 12) {
+	ret = usb_string(dev->udev, iMACAddress, buf, sizeof buf);
+	if (ret == 12)
+		tmp = hex2bin(dev->net->dev_addr, buf, 6);
+	if (tmp < 0) {
 		dev_dbg(&dev->udev->dev,
 			"bad MAC string %d fetch, %d\n", iMACAddress, tmp);
-		if (tmp >= 0)
-			tmp = -EINVAL;
-		return tmp;
+		if (ret >= 0)
+			ret = -EINVAL;
+		return ret;
 	}
-	for (i = tmp = 0; i < 6; i++, tmp += 2)
-		dev->net->dev_addr [i] =
-			(hex_to_bin(buf[tmp]) << 4) + hex_to_bin(buf[tmp + 1]);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(usbnet_get_ethernet_addr);
@@ -1023,9 +1022,9 @@
 	.get_drvinfo		= usbnet_get_drvinfo,
 	.get_msglevel		= usbnet_get_msglevel,
 	.set_msglevel		= usbnet_set_msglevel,
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0))
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
 	.get_ts_info		= ethtool_op_get_ts_info,
-#endif
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) */
 };
 
 /*-------------------------------------------------------------------------*/
@@ -1075,7 +1074,7 @@
  * especially now that control transfers can be queued.
  */
 static void
-kevent (struct work_struct *work)
+usbnet_deferred_kevent (struct work_struct *work)
 {
 	struct usbnet		*dev =
 		container_of(work, struct usbnet, kevent);
@@ -1191,8 +1190,7 @@
 	struct usbnet		*dev = entry->dev;
 
 	if (urb->status == 0) {
-		if (!(dev->driver_info->flags & FLAG_MULTI_PACKET))
-			dev->net->stats.tx_packets++;
+		dev->net->stats.tx_packets += entry->packets;
 		dev->net->stats.tx_bytes += entry->length;
 	} else {
 		dev->net->stats.tx_errors++;
@@ -1364,7 +1362,19 @@
 		} else
 			urb->transfer_flags |= URB_ZERO_PACKET;
 	}
-	entry->length = urb->transfer_buffer_length = length;
+	urb->transfer_buffer_length = length;
+
+	if (info->flags & FLAG_MULTI_PACKET) {
+		/* Driver has set number of packets and a length delta.
+		 * Calculate the complete length and ensure that it's
+		 * positive.
+		 */
+		entry->length += length;
+		if (WARN_ON_ONCE(entry->length <= 0))
+			entry->length = length;
+	} else {
+		usbnet_set_skb_tx_stats(skb, 1, length);
+	}
 
 	spin_lock_irqsave(&dev->txq.lock, flags);
 	retval = usb_autopm_get_interface_async(dev->intf);
@@ -1636,7 +1646,7 @@
 	skb_queue_head_init(&dev->rxq_pause);
 	dev->bh.func = usbnet_bh;
 	dev->bh.data = (unsigned long) dev;
-	INIT_WORK (&dev->kevent, kevent);
+	INIT_WORK (&dev->kevent, usbnet_deferred_kevent);
 	init_usb_anchor(&dev->deferred);
 	dev->delay.function = usbnet_bh;
 	dev->delay.data = (unsigned long) dev;
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index b9de346..2ff5602 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -56,7 +56,7 @@
 	  A driver for Marvell Libertas 8388 USB devices using thinfirm.
 
 config AIRO
-	depends on n
+	depends on !KERNEL_3_9
 	tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
 	depends on m
 	depends on CFG80211 && ISA_DMA_API && (PCI || BROKEN)
@@ -78,7 +78,6 @@
 	  The driver can be compiled as a module and will be named "airo".
 
 config ATMEL
-	depends on n
       tristate "Atmel at76c50x chipset  802.11b support"
       depends on m
       depends on CFG80211 && (PCI || PCMCIA)
@@ -98,7 +97,6 @@
         <http://www.thekelleys.org.uk/atmel>
 
 config PCI_ATMEL
-	depends on n
       tristate "Atmel at76c506 PCI cards"
       depends on m
       depends on ATMEL && PCI
@@ -107,7 +105,6 @@
         Atmel at76c506 chip.
 
 config PCMCIA_ATMEL
-	depends on n
 	tristate "Atmel at76c502/at76c504 PCMCIA cards"
 	depends on m
 	depends on ATMEL && PCMCIA
@@ -128,7 +125,7 @@
           at76c505 or at76c505a chips.
 
 config AIRO_CS
-	depends on n
+	depends on !KERNEL_3_9
 	tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
 	depends on m
 	depends on CFG80211 && PCMCIA && (BROKEN || !M32R)
@@ -186,7 +183,6 @@
 	  When built as module you get the module prism54
 
 config USB_ZD1201
-	depends on n
 	tristate "USB ZD1201 based Wireless device support"
 	depends on m
 	depends on CFG80211 && USB
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index a569a65..45c575c 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -6,13 +6,13 @@
 obj-$(CPTCFG_IPW2200) += ipw2x00/
 
 obj-$(CPTCFG_HERMES)		+= orinoco/
-#
-#obj-$(CPTCFG_AIRO)		+= airo.o
-#obj-$(CPTCFG_AIRO_CS)		+= airo_cs.o airo.o
-#
-#obj-$(CPTCFG_ATMEL)             += atmel.o
-#obj-$(CPTCFG_PCI_ATMEL)         += atmel_pci.o 
-#obj-$(CPTCFG_PCMCIA_ATMEL)      += atmel_cs.o
+
+obj-$(CPTCFG_AIRO)		+= airo.o
+obj-$(CPTCFG_AIRO_CS)		+= airo_cs.o airo.o
+
+obj-$(CPTCFG_ATMEL)             += atmel.o
+obj-$(CPTCFG_PCI_ATMEL)         += atmel_pci.o 
+obj-$(CPTCFG_PCMCIA_ATMEL)      += atmel_cs.o
 
 obj-$(CPTCFG_AT76C50X_USB)      += at76c50x-usb.o
 #
@@ -31,8 +31,8 @@
 #obj-$(CPTCFG_PCMCIA_WL3501)	+= wl3501_cs.o
 
 obj-$(CPTCFG_USB_NET_RNDIS_WLAN)	+= rndis_wlan.o
-#
-#obj-$(CPTCFG_USB_ZD1201)	+= zd1201.o
+
+obj-$(CPTCFG_USB_ZD1201)	+= zd1201.o
 obj-$(CPTCFG_LIBERTAS)		+= libertas/
 
 obj-$(CPTCFG_LIBERTAS_THINFIRM)	+= libertas_tf/
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
index 17fcaab..f07a618 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/adm8211.c
@@ -1837,6 +1837,7 @@
 	if (!priv->map) {
 		printk(KERN_ERR "%s (adm8211): Cannot map device memory\n",
 		       pci_name(pdev));
+		err = -ENOMEM;
 		goto err_free_dev;
 	}
 
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
new file mode 100644
index 0000000..d0c97c2
--- /dev/null
+++ b/drivers/net/wireless/airo.c
@@ -0,0 +1,8225 @@
+/*======================================================================
+
+    Aironet driver for 4500 and 4800 series cards
+
+    This code is released under both the GPL version 2 and BSD licenses.
+    Either license may be used.  The respective licenses are found at
+    the end of this file.
+
+    This code was developed by Benjamin Reed <breed@users.sourceforge.net>
+    including portions of which come from the Aironet PC4500
+    Developer's Reference Manual and used with permission.  Copyright
+    (C) 1999 Benjamin Reed.  All Rights Reserved.  Permission to use
+    code in the Developer's manual was granted for this driver by
+    Aironet.  Major code contributions were received from Javier Achirica
+    <achirica@users.sourceforge.net> and Jean Tourrilhes <jt@hpl.hp.com>.
+    Code was also integrated from the Cisco Aironet driver for Linux.
+    Support for MPI350 cards was added by Fabrice Bellet
+    <fabrice@bellet.info>.
+
+======================================================================*/
+
+#include <linux/err.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/bitops.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/io.h>
+#include <asm/unaligned.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <net/cfg80211.h>
+#include <net/iw_handler.h>
+
+#include "airo.h"
+
+#define DRV_NAME "airo"
+
+#ifdef CONFIG_PCI
+static const struct pci_device_id card_ids[] = {
+	{ 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, card_ids);
+
+static int airo_pci_probe(struct pci_dev *, const struct pci_device_id *);
+static void airo_pci_remove(struct pci_dev *);
+static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state);
+static int airo_pci_resume(struct pci_dev *pdev);
+
+static struct pci_driver airo_driver = {
+	.name     = DRV_NAME,
+	.id_table = card_ids,
+	.probe    = airo_pci_probe,
+	.remove   = airo_pci_remove,
+	.suspend  = airo_pci_suspend,
+	.resume   = airo_pci_resume,
+};
+#endif /* CONFIG_PCI */
+
+/* Include Wireless Extension definition and check version - Jean II */
+#include <linux/wireless.h>
+#define WIRELESS_SPY		/* enable iwspy support */
+
+#define CISCO_EXT		/* enable Cisco extensions */
+#ifdef CISCO_EXT
+#include <linux/delay.h>
+#endif
+
+/* Hack to do some power saving */
+#define POWER_ON_DOWN
+
+/* As you can see this list is HUGH!
+   I really don't know what a lot of these counts are about, but they
+   are all here for completeness.  If the IGNLABEL macro is put in
+   infront of the label, that statistic will not be included in the list
+   of statistics in the /proc filesystem */
+
+#define IGNLABEL(comment) NULL
+static const char *statsLabels[] = {
+	"RxOverrun",
+	IGNLABEL("RxPlcpCrcErr"),
+	IGNLABEL("RxPlcpFormatErr"),
+	IGNLABEL("RxPlcpLengthErr"),
+	"RxMacCrcErr",
+	"RxMacCrcOk",
+	"RxWepErr",
+	"RxWepOk",
+	"RetryLong",
+	"RetryShort",
+	"MaxRetries",
+	"NoAck",
+	"NoCts",
+	"RxAck",
+	"RxCts",
+	"TxAck",
+	"TxRts",
+	"TxCts",
+	"TxMc",
+	"TxBc",
+	"TxUcFrags",
+	"TxUcPackets",
+	"TxBeacon",
+	"RxBeacon",
+	"TxSinColl",
+	"TxMulColl",
+	"DefersNo",
+	"DefersProt",
+	"DefersEngy",
+	"DupFram",
+	"RxFragDisc",
+	"TxAged",
+	"RxAged",
+	"LostSync-MaxRetry",
+	"LostSync-MissedBeacons",
+	"LostSync-ArlExceeded",
+	"LostSync-Deauth",
+	"LostSync-Disassoced",
+	"LostSync-TsfTiming",
+	"HostTxMc",
+	"HostTxBc",
+	"HostTxUc",
+	"HostTxFail",
+	"HostRxMc",
+	"HostRxBc",
+	"HostRxUc",
+	"HostRxDiscard",
+	IGNLABEL("HmacTxMc"),
+	IGNLABEL("HmacTxBc"),
+	IGNLABEL("HmacTxUc"),
+	IGNLABEL("HmacTxFail"),
+	IGNLABEL("HmacRxMc"),
+	IGNLABEL("HmacRxBc"),
+	IGNLABEL("HmacRxUc"),
+	IGNLABEL("HmacRxDiscard"),
+	IGNLABEL("HmacRxAccepted"),
+	"SsidMismatch",
+	"ApMismatch",
+	"RatesMismatch",
+	"AuthReject",
+	"AuthTimeout",
+	"AssocReject",
+	"AssocTimeout",
+	IGNLABEL("ReasonOutsideTable"),
+	IGNLABEL("ReasonStatus1"),
+	IGNLABEL("ReasonStatus2"),
+	IGNLABEL("ReasonStatus3"),
+	IGNLABEL("ReasonStatus4"),
+	IGNLABEL("ReasonStatus5"),
+	IGNLABEL("ReasonStatus6"),
+	IGNLABEL("ReasonStatus7"),
+	IGNLABEL("ReasonStatus8"),
+	IGNLABEL("ReasonStatus9"),
+	IGNLABEL("ReasonStatus10"),
+	IGNLABEL("ReasonStatus11"),
+	IGNLABEL("ReasonStatus12"),
+	IGNLABEL("ReasonStatus13"),
+	IGNLABEL("ReasonStatus14"),
+	IGNLABEL("ReasonStatus15"),
+	IGNLABEL("ReasonStatus16"),
+	IGNLABEL("ReasonStatus17"),
+	IGNLABEL("ReasonStatus18"),
+	IGNLABEL("ReasonStatus19"),
+	"RxMan",
+	"TxMan",
+	"RxRefresh",
+	"TxRefresh",
+	"RxPoll",
+	"TxPoll",
+	"HostRetries",
+	"LostSync-HostReq",
+	"HostTxBytes",
+	"HostRxBytes",
+	"ElapsedUsec",
+	"ElapsedSec",
+	"LostSyncBetterAP",
+	"PrivacyMismatch",
+	"Jammed",
+	"DiscRxNotWepped",
+	"PhyEleMismatch",
+	(char*)-1 };
+#ifndef RUN_AT
+#define RUN_AT(x) (jiffies+(x))
+#endif
+
+
+/* These variables are for insmod, since it seems that the rates
+   can only be set in setup_card.  Rates should be a comma separated
+   (no spaces) list of rates (up to 8). */
+
+static int rates[8];
+static char *ssids[3];
+
+static int io[4];
+static int irq[4];
+
+static
+int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at.
+		       0 means no limit.  For old cards this was 4 */
+
+static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */
+static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read
+		    the bap, needed on some older cards and buses. */
+static int adhoc;
+
+static int probe = 1;
+
+static kuid_t proc_kuid;
+static int proc_uid /* = 0 */;
+
+static kgid_t proc_kgid;
+static int proc_gid /* = 0 */;
+
+static int airo_perm = 0555;
+
+static int proc_perm = 0644;
+
+MODULE_AUTHOR("Benjamin Reed");
+MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet cards.  "
+		   "Direct support for ISA/PCI/MPI cards and support for PCMCIA when used with airo_cs.");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350");
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(rates, int, NULL, 0);
+module_param_array(ssids, charp, NULL, 0);
+module_param(auto_wep, int, 0);
+MODULE_PARM_DESC(auto_wep,
+		 "If non-zero, the driver will keep looping through the authentication options until an association is made.  "
+		 "The value of auto_wep is number of the wep keys to check.  "
+		 "A value of 2 will try using the key at index 0 and index 1.");
+module_param(aux_bap, int, 0);
+MODULE_PARM_DESC(aux_bap,
+		 "If non-zero, the driver will switch into a mode that seems to work better for older cards with some older buses.  "
+		 "Before switching it checks that the switch is needed.");
+module_param(maxencrypt, int, 0);
+MODULE_PARM_DESC(maxencrypt,
+		 "The maximum speed that the card can do encryption.  "
+		 "Units are in 512kbs.  "
+		 "Zero (default) means there is no limit.  "
+		 "Older cards used to be limited to 2mbs (4).");
+module_param(adhoc, int, 0);
+MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode.");
+module_param(probe, int, 0);
+MODULE_PARM_DESC(probe, "If zero, the driver won't start the card.");
+
+module_param(proc_uid, int, 0);
+MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to.");
+module_param(proc_gid, int, 0);
+MODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to.");
+module_param(airo_perm, int, 0);
+MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet.");
+module_param(proc_perm, int, 0);
+MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc");
+
+/* This is a kind of sloppy hack to get this information to OUT4500 and
+   IN4500.  I would be extremely interested in the situation where this
+   doesn't work though!!! */
+static int do8bitIO /* = 0 */;
+
+/* Return codes */
+#define SUCCESS 0
+#define ERROR -1
+#define NO_PACKET -2
+
+/* Commands */
+#define NOP2		0x0000
+#define MAC_ENABLE	0x0001
+#define MAC_DISABLE	0x0002
+#define CMD_LOSE_SYNC	0x0003 /* Not sure what this does... */
+#define CMD_SOFTRESET	0x0004
+#define HOSTSLEEP	0x0005
+#define CMD_MAGIC_PKT	0x0006
+#define CMD_SETWAKEMASK	0x0007
+#define CMD_READCFG	0x0008
+#define CMD_SETMODE	0x0009
+#define CMD_ALLOCATETX	0x000a
+#define CMD_TRANSMIT	0x000b
+#define CMD_DEALLOCATETX 0x000c
+#define NOP		0x0010
+#define CMD_WORKAROUND	0x0011
+#define CMD_ALLOCATEAUX 0x0020
+#define CMD_ACCESS	0x0021
+#define CMD_PCIBAP	0x0022
+#define CMD_PCIAUX	0x0023
+#define CMD_ALLOCBUF	0x0028
+#define CMD_GETTLV	0x0029
+#define CMD_PUTTLV	0x002a
+#define CMD_DELTLV	0x002b
+#define CMD_FINDNEXTTLV	0x002c
+#define CMD_PSPNODES	0x0030
+#define CMD_SETCW	0x0031    
+#define CMD_SETPCF	0x0032    
+#define CMD_SETPHYREG	0x003e
+#define CMD_TXTEST	0x003f
+#define MAC_ENABLETX	0x0101
+#define CMD_LISTBSS	0x0103
+#define CMD_SAVECFG	0x0108
+#define CMD_ENABLEAUX	0x0111
+#define CMD_WRITERID	0x0121
+#define CMD_USEPSPNODES	0x0130
+#define MAC_ENABLERX	0x0201
+
+/* Command errors */
+#define ERROR_QUALIF 0x00
+#define ERROR_ILLCMD 0x01
+#define ERROR_ILLFMT 0x02
+#define ERROR_INVFID 0x03
+#define ERROR_INVRID 0x04
+#define ERROR_LARGE 0x05
+#define ERROR_NDISABL 0x06
+#define ERROR_ALLOCBSY 0x07
+#define ERROR_NORD 0x0B
+#define ERROR_NOWR 0x0C
+#define ERROR_INVFIDTX 0x0D
+#define ERROR_TESTACT 0x0E
+#define ERROR_TAGNFND 0x12
+#define ERROR_DECODE 0x20
+#define ERROR_DESCUNAV 0x21
+#define ERROR_BADLEN 0x22
+#define ERROR_MODE 0x80
+#define ERROR_HOP 0x81
+#define ERROR_BINTER 0x82
+#define ERROR_RXMODE 0x83
+#define ERROR_MACADDR 0x84
+#define ERROR_RATES 0x85
+#define ERROR_ORDER 0x86
+#define ERROR_SCAN 0x87
+#define ERROR_AUTH 0x88
+#define ERROR_PSMODE 0x89
+#define ERROR_RTYPE 0x8A
+#define ERROR_DIVER 0x8B
+#define ERROR_SSID 0x8C
+#define ERROR_APLIST 0x8D
+#define ERROR_AUTOWAKE 0x8E
+#define ERROR_LEAP 0x8F
+
+/* Registers */
+#define COMMAND 0x00
+#define PARAM0 0x02
+#define PARAM1 0x04
+#define PARAM2 0x06
+#define STATUS 0x08
+#define RESP0 0x0a
+#define RESP1 0x0c
+#define RESP2 0x0e
+#define LINKSTAT 0x10
+#define SELECT0 0x18
+#define OFFSET0 0x1c
+#define RXFID 0x20
+#define TXALLOCFID 0x22
+#define TXCOMPLFID 0x24
+#define DATA0 0x36
+#define EVSTAT 0x30
+#define EVINTEN 0x32
+#define EVACK 0x34
+#define SWS0 0x28
+#define SWS1 0x2a
+#define SWS2 0x2c
+#define SWS3 0x2e
+#define AUXPAGE 0x3A
+#define AUXOFF 0x3C
+#define AUXDATA 0x3E
+
+#define FID_TX 1
+#define FID_RX 2
+/* Offset into aux memory for descriptors */
+#define AUX_OFFSET 0x800
+/* Size of allocated packets */
+#define PKTSIZE 1840
+#define RIDSIZE 2048
+/* Size of the transmit queue */
+#define MAXTXQ 64
+
+/* BAP selectors */
+#define BAP0 0 /* Used for receiving packets */
+#define BAP1 2 /* Used for xmiting packets and working with RIDS */
+
+/* Flags */
+#define COMMAND_BUSY 0x8000
+
+#define BAP_BUSY 0x8000
+#define BAP_ERR 0x4000
+#define BAP_DONE 0x2000
+
+#define PROMISC 0xffff
+#define NOPROMISC 0x0000
+
+#define EV_CMD 0x10
+#define EV_CLEARCOMMANDBUSY 0x4000
+#define EV_RX 0x01
+#define EV_TX 0x02
+#define EV_TXEXC 0x04
+#define EV_ALLOC 0x08
+#define EV_LINK 0x80
+#define EV_AWAKE 0x100
+#define EV_TXCPY 0x400
+#define EV_UNKNOWN 0x800
+#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */
+#define EV_AWAKEN 0x2000
+#define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC)
+
+#ifdef CHECK_UNKNOWN_INTS
+#define IGNORE_INTS ( EV_CMD | EV_UNKNOWN)
+#else
+#define IGNORE_INTS (~STATUS_INTS)
+#endif
+
+/* RID TYPES */
+#define RID_RW 0x20
+
+/* The RIDs */
+#define RID_CAPABILITIES 0xFF00
+#define RID_APINFO     0xFF01
+#define RID_RADIOINFO  0xFF02
+#define RID_UNKNOWN3   0xFF03
+#define RID_RSSI       0xFF04
+#define RID_CONFIG     0xFF10
+#define RID_SSID       0xFF11
+#define RID_APLIST     0xFF12
+#define RID_DRVNAME    0xFF13
+#define RID_ETHERENCAP 0xFF14
+#define RID_WEP_TEMP   0xFF15
+#define RID_WEP_PERM   0xFF16
+#define RID_MODULATION 0xFF17
+#define RID_OPTIONS    0xFF18
+#define RID_ACTUALCONFIG 0xFF20 /*readonly*/
+#define RID_FACTORYCONFIG 0xFF21
+#define RID_UNKNOWN22  0xFF22
+#define RID_LEAPUSERNAME 0xFF23
+#define RID_LEAPPASSWORD 0xFF24
+#define RID_STATUS     0xFF50
+#define RID_BEACON_HST 0xFF51
+#define RID_BUSY_HST   0xFF52
+#define RID_RETRIES_HST 0xFF53
+#define RID_UNKNOWN54  0xFF54
+#define RID_UNKNOWN55  0xFF55
+#define RID_UNKNOWN56  0xFF56
+#define RID_MIC        0xFF57
+#define RID_STATS16    0xFF60
+#define RID_STATS16DELTA 0xFF61
+#define RID_STATS16DELTACLEAR 0xFF62
+#define RID_STATS      0xFF68
+#define RID_STATSDELTA 0xFF69
+#define RID_STATSDELTACLEAR 0xFF6A
+#define RID_ECHOTEST_RID 0xFF70
+#define RID_ECHOTEST_RESULTS 0xFF71
+#define RID_BSSLISTFIRST 0xFF72
+#define RID_BSSLISTNEXT  0xFF73
+#define RID_WPA_BSSLISTFIRST 0xFF74
+#define RID_WPA_BSSLISTNEXT  0xFF75
+
+typedef struct {
+	u16 cmd;
+	u16 parm0;
+	u16 parm1;
+	u16 parm2;
+} Cmd;
+
+typedef struct {
+	u16 status;
+	u16 rsp0;
+	u16 rsp1;
+	u16 rsp2;
+} Resp;
+
+/*
+ * Rids and endian-ness:  The Rids will always be in cpu endian, since
+ * this all the patches from the big-endian guys end up doing that.
+ * so all rid access should use the read/writeXXXRid routines.
+ */
+
+/* This structure came from an email sent to me from an engineer at
+   aironet for inclusion into this driver */
+typedef struct WepKeyRid WepKeyRid;
+struct WepKeyRid {
+	__le16 len;
+	__le16 kindex;
+	u8 mac[ETH_ALEN];
+	__le16 klen;
+	u8 key[16];
+} __packed;
+
+/* These structures are from the Aironet's PC4500 Developers Manual */
+typedef struct Ssid Ssid;
+struct Ssid {
+	__le16 len;
+	u8 ssid[32];
+} __packed;
+
+typedef struct SsidRid SsidRid;
+struct SsidRid {
+	__le16 len;
+	Ssid ssids[3];
+} __packed;
+
+typedef struct ModulationRid ModulationRid;
+struct ModulationRid {
+        __le16 len;
+        __le16 modulation;
+#define MOD_DEFAULT cpu_to_le16(0)
+#define MOD_CCK cpu_to_le16(1)
+#define MOD_MOK cpu_to_le16(2)
+} __packed;
+
+typedef struct ConfigRid ConfigRid;
+struct ConfigRid {
+	__le16 len; /* sizeof(ConfigRid) */
+	__le16 opmode; /* operating mode */
+#define MODE_STA_IBSS cpu_to_le16(0)
+#define MODE_STA_ESS cpu_to_le16(1)
+#define MODE_AP cpu_to_le16(2)
+#define MODE_AP_RPTR cpu_to_le16(3)
+#define MODE_CFG_MASK cpu_to_le16(0xff)
+#define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */
+#define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */
+#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extenstions */
+#define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */
+#define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */
+#define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */
+#define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */
+#define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */
+#define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */
+	__le16 rmode; /* receive mode */
+#define RXMODE_BC_MC_ADDR cpu_to_le16(0)
+#define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */
+#define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */
+#define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */
+#define RXMODE_RFMON_ANYBSS cpu_to_le16(4)
+#define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */
+#define RXMODE_MASK cpu_to_le16(255)
+#define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */
+#define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER)
+#define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */
+	__le16 fragThresh;
+	__le16 rtsThres;
+	u8 macAddr[ETH_ALEN];
+	u8 rates[8];
+	__le16 shortRetryLimit;
+	__le16 longRetryLimit;
+	__le16 txLifetime; /* in kusec */
+	__le16 rxLifetime; /* in kusec */
+	__le16 stationary;
+	__le16 ordering;
+	__le16 u16deviceType; /* for overriding device type */
+	__le16 cfpRate;
+	__le16 cfpDuration;
+	__le16 _reserved1[3];
+	/*---------- Scanning/Associating ----------*/
+	__le16 scanMode;
+#define SCANMODE_ACTIVE cpu_to_le16(0)
+#define SCANMODE_PASSIVE cpu_to_le16(1)
+#define SCANMODE_AIROSCAN cpu_to_le16(2)
+	__le16 probeDelay; /* in kusec */
+	__le16 probeEnergyTimeout; /* in kusec */
+        __le16 probeResponseTimeout;
+	__le16 beaconListenTimeout;
+	__le16 joinNetTimeout;
+	__le16 authTimeout;
+	__le16 authType;
+#define AUTH_OPEN cpu_to_le16(0x1)
+#define AUTH_ENCRYPT cpu_to_le16(0x101)
+#define AUTH_SHAREDKEY cpu_to_le16(0x102)
+#define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200)
+	__le16 associationTimeout;
+	__le16 specifiedApTimeout;
+	__le16 offlineScanInterval;
+	__le16 offlineScanDuration;
+	__le16 linkLossDelay;
+	__le16 maxBeaconLostTime;
+	__le16 refreshInterval;
+#define DISABLE_REFRESH cpu_to_le16(0xFFFF)
+	__le16 _reserved1a[1];
+	/*---------- Power save operation ----------*/
+	__le16 powerSaveMode;
+#define POWERSAVE_CAM cpu_to_le16(0)
+#define POWERSAVE_PSP cpu_to_le16(1)
+#define POWERSAVE_PSPCAM cpu_to_le16(2)
+	__le16 sleepForDtims;
+	__le16 listenInterval;
+	__le16 fastListenInterval;
+	__le16 listenDecay;
+	__le16 fastListenDelay;
+	__le16 _reserved2[2];
+	/*---------- Ap/Ibss config items ----------*/
+	__le16 beaconPeriod;
+	__le16 atimDuration;
+	__le16 hopPeriod;
+	__le16 channelSet;
+	__le16 channel;
+	__le16 dtimPeriod;
+	__le16 bridgeDistance;
+	__le16 radioID;
+	/*---------- Radio configuration ----------*/
+	__le16 radioType;
+#define RADIOTYPE_DEFAULT cpu_to_le16(0)
+#define RADIOTYPE_802_11 cpu_to_le16(1)
+#define RADIOTYPE_LEGACY cpu_to_le16(2)
+	u8 rxDiversity;
+	u8 txDiversity;
+	__le16 txPower;
+#define TXPOWER_DEFAULT 0
+	__le16 rssiThreshold;
+#define RSSI_DEFAULT 0
+        __le16 modulation;
+#define PREAMBLE_AUTO cpu_to_le16(0)
+#define PREAMBLE_LONG cpu_to_le16(1)
+#define PREAMBLE_SHORT cpu_to_le16(2)
+	__le16 preamble;
+	__le16 homeProduct;
+	__le16 radioSpecific;
+	/*---------- Aironet Extensions ----------*/
+	u8 nodeName[16];
+	__le16 arlThreshold;
+	__le16 arlDecay;
+	__le16 arlDelay;
+	__le16 _reserved4[1];
+	/*---------- Aironet Extensions ----------*/
+	u8 magicAction;
+#define MAGIC_ACTION_STSCHG 1
+#define MAGIC_ACTION_RESUME 2
+#define MAGIC_IGNORE_MCAST (1<<8)
+#define MAGIC_IGNORE_BCAST (1<<9)
+#define MAGIC_SWITCH_TO_PSP (0<<10)
+#define MAGIC_STAY_IN_CAM (1<<10)
+	u8 magicControl;
+	__le16 autoWake;
+} __packed;
+
+typedef struct StatusRid StatusRid;
+struct StatusRid {
+	__le16 len;
+	u8 mac[ETH_ALEN];
+	__le16 mode;
+	__le16 errorCode;
+	__le16 sigQuality;
+	__le16 SSIDlen;
+	char SSID[32];
+	char apName[16];
+	u8 bssid[4][ETH_ALEN];
+	__le16 beaconPeriod;
+	__le16 dimPeriod;
+	__le16 atimDuration;
+	__le16 hopPeriod;
+	__le16 channelSet;
+	__le16 channel;
+	__le16 hopsToBackbone;
+	__le16 apTotalLoad;
+	__le16 generatedLoad;
+	__le16 accumulatedArl;
+	__le16 signalQuality;
+	__le16 currentXmitRate;
+	__le16 apDevExtensions;
+	__le16 normalizedSignalStrength;
+	__le16 shortPreamble;
+	u8 apIP[4];
+	u8 noisePercent; /* Noise percent in last second */
+	u8 noisedBm; /* Noise dBm in last second */
+	u8 noiseAvePercent; /* Noise percent in last minute */
+	u8 noiseAvedBm; /* Noise dBm in last minute */
+	u8 noiseMaxPercent; /* Highest noise percent in last minute */
+	u8 noiseMaxdBm; /* Highest noise dbm in last minute */
+	__le16 load;
+	u8 carrier[4];
+	__le16 assocStatus;
+#define STAT_NOPACKETS 0
+#define STAT_NOCARRIERSET 10
+#define STAT_GOTCARRIERSET 11
+#define STAT_WRONGSSID 20
+#define STAT_BADCHANNEL 25
+#define STAT_BADBITRATES 30
+#define STAT_BADPRIVACY 35
+#define STAT_APFOUND 40
+#define STAT_APREJECTED 50
+#define STAT_AUTHENTICATING 60
+#define STAT_DEAUTHENTICATED 61
+#define STAT_AUTHTIMEOUT 62
+#define STAT_ASSOCIATING 70
+#define STAT_DEASSOCIATED 71
+#define STAT_ASSOCTIMEOUT 72
+#define STAT_NOTAIROAP 73
+#define STAT_ASSOCIATED 80
+#define STAT_LEAPING 90
+#define STAT_LEAPFAILED 91
+#define STAT_LEAPTIMEDOUT 92
+#define STAT_LEAPCOMPLETE 93
+} __packed;
+
+typedef struct StatsRid StatsRid;
+struct StatsRid {
+	__le16 len;
+	__le16 spacer;
+	__le32 vals[100];
+} __packed;
+
+typedef struct APListRid APListRid;
+struct APListRid {
+	__le16 len;
+	u8 ap[4][ETH_ALEN];
+} __packed;
+
+typedef struct CapabilityRid CapabilityRid;
+struct CapabilityRid {
+	__le16 len;
+	char oui[3];
+	char zero;
+	__le16 prodNum;
+	char manName[32];
+	char prodName[16];
+	char prodVer[8];
+	char factoryAddr[ETH_ALEN];
+	char aironetAddr[ETH_ALEN];
+	__le16 radioType;
+	__le16 country;
+	char callid[ETH_ALEN];
+	char supportedRates[8];
+	char rxDiversity;
+	char txDiversity;
+	__le16 txPowerLevels[8];
+	__le16 hardVer;
+	__le16 hardCap;
+	__le16 tempRange;
+	__le16 softVer;
+	__le16 softSubVer;
+	__le16 interfaceVer;
+	__le16 softCap;
+	__le16 bootBlockVer;
+	__le16 requiredHard;
+	__le16 extSoftCap;
+} __packed;
+
+/* Only present on firmware >= 5.30.17 */
+typedef struct BSSListRidExtra BSSListRidExtra;
+struct BSSListRidExtra {
+  __le16 unknown[4];
+  u8 fixed[12]; /* WLAN management frame */
+  u8 iep[624];
+} __packed;
+
+typedef struct BSSListRid BSSListRid;
+struct BSSListRid {
+  __le16 len;
+  __le16 index; /* First is 0 and 0xffff means end of list */
+#define RADIO_FH 1 /* Frequency hopping radio type */
+#define RADIO_DS 2 /* Direct sequence radio type */
+#define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */
+  __le16 radioType;
+  u8 bssid[ETH_ALEN]; /* Mac address of the BSS */
+  u8 zero;
+  u8 ssidLen;
+  u8 ssid[32];
+  __le16 dBm;
+#define CAP_ESS cpu_to_le16(1<<0)
+#define CAP_IBSS cpu_to_le16(1<<1)
+#define CAP_PRIVACY cpu_to_le16(1<<4)
+#define CAP_SHORTHDR cpu_to_le16(1<<5)
+  __le16 cap;
+  __le16 beaconInterval;
+  u8 rates[8]; /* Same as rates for config rid */
+  struct { /* For frequency hopping only */
+    __le16 dwell;
+    u8 hopSet;
+    u8 hopPattern;
+    u8 hopIndex;
+    u8 fill;
+  } fh;
+  __le16 dsChannel;
+  __le16 atimWindow;
+
+  /* Only present on firmware >= 5.30.17 */
+  BSSListRidExtra extra;
+} __packed;
+
+typedef struct {
+  BSSListRid bss;
+  struct list_head list;
+} BSSListElement;
+
+typedef struct tdsRssiEntry tdsRssiEntry;
+struct tdsRssiEntry {
+  u8 rssipct;
+  u8 rssidBm;
+} __packed;
+
+typedef struct tdsRssiRid tdsRssiRid;
+struct tdsRssiRid {
+  u16 len;
+  tdsRssiEntry x[256];
+} __packed;
+
+typedef struct MICRid MICRid;
+struct MICRid {
+	__le16 len;
+	__le16 state;
+	__le16 multicastValid;
+	u8  multicast[16];
+	__le16 unicastValid;
+	u8  unicast[16];
+} __packed;
+
+typedef struct MICBuffer MICBuffer;
+struct MICBuffer {
+	__be16 typelen;
+
+	union {
+	    u8 snap[8];
+	    struct {
+		u8 dsap;
+		u8 ssap;
+		u8 control;
+		u8 orgcode[3];
+		u8 fieldtype[2];
+	    } llc;
+	} u;
+	__be32 mic;
+	__be32 seq;
+} __packed;
+
+typedef struct {
+	u8 da[ETH_ALEN];
+	u8 sa[ETH_ALEN];
+} etherHead;
+
+#define TXCTL_TXOK (1<<1) /* report if tx is ok */
+#define TXCTL_TXEX (1<<2) /* report if tx fails */
+#define TXCTL_802_3 (0<<3) /* 802.3 packet */
+#define TXCTL_802_11 (1<<3) /* 802.11 mac packet */
+#define TXCTL_ETHERNET (0<<4) /* payload has ethertype */
+#define TXCTL_LLC (1<<4) /* payload is llc */
+#define TXCTL_RELEASE (0<<5) /* release after completion */
+#define TXCTL_NORELEASE (1<<5) /* on completion returns to host */
+
+#define BUSY_FID 0x10000
+
+#ifdef CISCO_EXT
+#define AIROMAGIC	0xa55a
+/* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */
+#ifdef SIOCIWFIRSTPRIV
+#ifdef SIOCDEVPRIVATE
+#define AIROOLDIOCTL	SIOCDEVPRIVATE
+#define AIROOLDIDIFC 	AIROOLDIOCTL + 1
+#endif /* SIOCDEVPRIVATE */
+#else /* SIOCIWFIRSTPRIV */
+#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
+#endif /* SIOCIWFIRSTPRIV */
+/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably
+ * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root
+ * only and don't return the modified struct ifreq to the application which
+ * is usually a problem. - Jean II */
+#define AIROIOCTL	SIOCIWFIRSTPRIV
+#define AIROIDIFC 	AIROIOCTL + 1
+
+/* Ioctl constants to be used in airo_ioctl.command */
+
+#define	AIROGCAP  		0	// Capability rid
+#define AIROGCFG		1       // USED A LOT
+#define AIROGSLIST		2	// System ID list
+#define AIROGVLIST		3       // List of specified AP's
+#define AIROGDRVNAM		4	//  NOTUSED
+#define AIROGEHTENC		5	// NOTUSED
+#define AIROGWEPKTMP		6
+#define AIROGWEPKNV		7
+#define AIROGSTAT		8
+#define AIROGSTATSC32		9
+#define AIROGSTATSD32		10
+#define AIROGMICRID		11
+#define AIROGMICSTATS		12
+#define AIROGFLAGS		13
+#define AIROGID			14
+#define AIRORRID		15
+#define AIRORSWVERSION		17
+
+/* Leave gap of 40 commands after AIROGSTATSD32 for future */
+
+#define AIROPCAP               	AIROGSTATSD32 + 40
+#define AIROPVLIST              AIROPCAP      + 1
+#define AIROPSLIST		AIROPVLIST    + 1
+#define AIROPCFG		AIROPSLIST    + 1
+#define AIROPSIDS		AIROPCFG      + 1
+#define AIROPAPLIST		AIROPSIDS     + 1
+#define AIROPMACON		AIROPAPLIST   + 1	/* Enable mac  */
+#define AIROPMACOFF		AIROPMACON    + 1 	/* Disable mac */
+#define AIROPSTCLR		AIROPMACOFF   + 1
+#define AIROPWEPKEY		AIROPSTCLR    + 1
+#define AIROPWEPKEYNV		AIROPWEPKEY   + 1
+#define AIROPLEAPPWD            AIROPWEPKEYNV + 1
+#define AIROPLEAPUSR            AIROPLEAPPWD  + 1
+
+/* Flash codes */
+
+#define AIROFLSHRST	       AIROPWEPKEYNV  + 40
+#define AIROFLSHGCHR           AIROFLSHRST    + 1
+#define AIROFLSHSTFL           AIROFLSHGCHR   + 1
+#define AIROFLSHPCHR           AIROFLSHSTFL   + 1
+#define AIROFLPUTBUF           AIROFLSHPCHR   + 1
+#define AIRORESTART            AIROFLPUTBUF   + 1
+
+#define FLASHSIZE	32768
+#define AUXMEMSIZE	(256 * 1024)
+
+typedef struct aironet_ioctl {
+	unsigned short command;		// What to do
+	unsigned short len;		// Len of data
+	unsigned short ridnum;		// rid number
+	unsigned char __user *data;	// d-data
+} aironet_ioctl;
+
+static const char swversion[] = "2.1";
+#endif /* CISCO_EXT */
+
+#define NUM_MODULES       2
+#define MIC_MSGLEN_MAX    2400
+#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX
+#define AIRO_DEF_MTU      2312
+
+typedef struct {
+	u32   size;            // size
+	u8    enabled;         // MIC enabled or not
+	u32   rxSuccess;       // successful packets received
+	u32   rxIncorrectMIC;  // pkts dropped due to incorrect MIC comparison
+	u32   rxNotMICed;      // pkts dropped due to not being MIC'd
+	u32   rxMICPlummed;    // pkts dropped due to not having a MIC plummed
+	u32   rxWrongSequence; // pkts dropped due to sequence number violation
+	u32   reserve[32];
+} mic_statistics;
+
+typedef struct {
+	u32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2];
+	u64 accum;	// accumulated mic, reduced to u32 in final()
+	int position;	// current position (byte offset) in message
+	union {
+		u8  d8[4];
+		__be32 d32;
+	} part;	// saves partial message word across update() calls
+} emmh32_context;
+
+typedef struct {
+	emmh32_context seed;	    // Context - the seed
+	u32		 rx;	    // Received sequence number
+	u32		 tx;	    // Tx sequence number
+	u32		 window;    // Start of window
+	u8		 valid;	    // Flag to say if context is valid or not
+	u8		 key[16];
+} miccntx;
+
+typedef struct {
+	miccntx mCtx;		// Multicast context
+	miccntx uCtx;		// Unicast context
+} mic_module;
+
+typedef struct {
+	unsigned int  rid: 16;
+	unsigned int  len: 15;
+	unsigned int  valid: 1;
+	dma_addr_t host_addr;
+} Rid;
+
+typedef struct {
+	unsigned int  offset: 15;
+	unsigned int  eoc: 1;
+	unsigned int  len: 15;
+	unsigned int  valid: 1;
+	dma_addr_t host_addr;
+} TxFid;
+
+struct rx_hdr {
+	__le16 status, len;
+	u8 rssi[2];
+	u8 rate;
+	u8 freq;
+	__le16 tmp[4];
+} __packed;
+
+typedef struct {
+	unsigned int  ctl: 15;
+	unsigned int  rdy: 1;
+	unsigned int  len: 15;
+	unsigned int  valid: 1;
+	dma_addr_t host_addr;
+} RxFid;
+
+/*
+ * Host receive descriptor
+ */
+typedef struct {
+	unsigned char __iomem *card_ram_off; /* offset into card memory of the
+						desc */
+	RxFid         rx_desc;		     /* card receive descriptor */
+	char          *virtual_host_addr;    /* virtual address of host receive
+					        buffer */
+	int           pending;
+} HostRxDesc;
+
+/*
+ * Host transmit descriptor
+ */
+typedef struct {
+	unsigned char __iomem *card_ram_off;	     /* offset into card memory of the
+						desc */
+	TxFid         tx_desc;		     /* card transmit descriptor */
+	char          *virtual_host_addr;    /* virtual address of host receive
+					        buffer */
+	int           pending;
+} HostTxDesc;
+
+/*
+ * Host RID descriptor
+ */
+typedef struct {
+	unsigned char __iomem *card_ram_off;      /* offset into card memory of the
+					     descriptor */
+	Rid           rid_desc;		  /* card RID descriptor */
+	char          *virtual_host_addr; /* virtual address of host receive
+					     buffer */
+} HostRidDesc;
+
+typedef struct {
+	u16 sw0;
+	u16 sw1;
+	u16 status;
+	u16 len;
+#define HOST_SET (1 << 0)
+#define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */
+#define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */
+#define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */
+#define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */
+#define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */
+#define HOST_CLR_AID (1 << 7) /* clear AID failure */
+#define HOST_RTS (1 << 9) /* Force RTS use */
+#define HOST_SHORT (1 << 10) /* Do short preamble */
+	u16 ctl;
+	u16 aid;
+	u16 retries;
+	u16 fill;
+} TxCtlHdr;
+
+typedef struct {
+        u16 ctl;
+        u16 duration;
+        char addr1[6];
+        char addr2[6];
+        char addr3[6];
+        u16 seq;
+        char addr4[6];
+} WifiHdr;
+
+
+typedef struct {
+	TxCtlHdr ctlhdr;
+	u16 fill1;
+	u16 fill2;
+	WifiHdr wifihdr;
+	u16 gaplen;
+	u16 status;
+} WifiCtlHdr;
+
+static WifiCtlHdr wifictlhdr8023 = {
+	.ctlhdr = {
+		.ctl	= HOST_DONT_RLSE,
+	}
+};
+
+// A few details needed for WEP (Wireless Equivalent Privacy)
+#define MAX_KEY_SIZE 13			// 128 (?) bits
+#define MIN_KEY_SIZE  5			// 40 bits RC4 - WEP
+typedef struct wep_key_t {
+	u16	len;
+	u8	key[16];	/* 40-bit and 104-bit keys */
+} wep_key_t;
+
+/* List of Wireless Handlers (new API) */
+static const struct iw_handler_def	airo_handler_def;
+
+static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)";
+
+struct airo_info;
+
+static int get_dec_u16( char *buffer, int *start, int limit );
+static void OUT4500( struct airo_info *, u16 register, u16 value );
+static unsigned short IN4500( struct airo_info *, u16 register );
+static u16 setup_card(struct airo_info*, u8 *mac, int lock);
+static int enable_MAC(struct airo_info *ai, int lock);
+static void disable_MAC(struct airo_info *ai, int lock);
+static void enable_interrupts(struct airo_info*);
+static void disable_interrupts(struct airo_info*);
+static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp);
+static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap);
+static int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
+			int whichbap);
+static int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
+			 int whichbap);
+static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen,
+		     int whichbap);
+static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd);
+static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock);
+static int PC4500_writerid(struct airo_info*, u16 rid, const void
+			   *pBuf, int len, int lock);
+static int do_writerid( struct airo_info*, u16 rid, const void *rid_data,
+			int len, int dummy );
+static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw);
+static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket);
+static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket);
+
+static int mpi_send_packet (struct net_device *dev);
+static void mpi_unmap_card(struct pci_dev *pci);
+static void mpi_receive_802_3(struct airo_info *ai);
+static void mpi_receive_802_11(struct airo_info *ai);
+static int waitbusy (struct airo_info *ai);
+
+static irqreturn_t airo_interrupt( int irq, void* dev_id);
+static int airo_thread(void *data);
+static void timer_func( struct net_device *dev );
+static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
+static void airo_read_wireless_stats (struct airo_info *local);
+#ifdef CISCO_EXT
+static int readrids(struct net_device *dev, aironet_ioctl *comp);
+static int writerids(struct net_device *dev, aironet_ioctl *comp);
+static int flashcard(struct net_device *dev, aironet_ioctl *comp);
+#endif /* CISCO_EXT */
+static void micinit(struct airo_info *ai);
+static int micsetup(struct airo_info *ai);
+static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len);
+static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen);
+
+static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi);
+static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm);
+
+static void airo_networks_free(struct airo_info *ai);
+
+struct airo_info {
+	struct net_device             *dev;
+	struct list_head              dev_list;
+	/* Note, we can have MAX_FIDS outstanding.  FIDs are 16-bits, so we
+	   use the high bit to mark whether it is in use. */
+#define MAX_FIDS 6
+#define MPI_MAX_FIDS 1
+	u32                           fids[MAX_FIDS];
+	ConfigRid config;
+	char keyindex; // Used with auto wep
+	char defindex; // Used with auto wep
+	struct proc_dir_entry *proc_entry;
+        spinlock_t aux_lock;
+#define FLAG_RADIO_OFF	0	/* User disabling of MAC */
+#define FLAG_RADIO_DOWN	1	/* ifup/ifdown disabling of MAC */
+#define FLAG_RADIO_MASK 0x03
+#define FLAG_ENABLED	2
+#define FLAG_ADHOC	3	/* Needed by MIC */
+#define FLAG_MIC_CAPABLE 4
+#define FLAG_UPDATE_MULTI 5
+#define FLAG_UPDATE_UNI 6
+#define FLAG_802_11	7
+#define FLAG_PROMISC	8	/* IFF_PROMISC 0x100 - include/linux/if.h */
+#define FLAG_PENDING_XMIT 9
+#define FLAG_PENDING_XMIT11 10
+#define FLAG_MPI	11
+#define FLAG_REGISTERED	12
+#define FLAG_COMMIT	13
+#define FLAG_RESET	14
+#define FLAG_FLASHING	15
+#define FLAG_WPA_CAPABLE	16
+	unsigned long flags;
+#define JOB_DIE	0
+#define JOB_XMIT	1
+#define JOB_XMIT11	2
+#define JOB_STATS	3
+#define JOB_PROMISC	4
+#define JOB_MIC	5
+#define JOB_EVENT	6
+#define JOB_AUTOWEP	7
+#define JOB_WSTATS	8
+#define JOB_SCAN_RESULTS  9
+	unsigned long jobs;
+	int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen,
+			int whichbap);
+	unsigned short *flash;
+	tdsRssiEntry *rssi;
+	struct task_struct *list_bss_task;
+	struct task_struct *airo_thread_task;
+	struct semaphore sem;
+	wait_queue_head_t thr_wait;
+	unsigned long expires;
+	struct {
+		struct sk_buff *skb;
+		int fid;
+	} xmit, xmit11;
+	struct net_device *wifidev;
+	struct iw_statistics	wstats;		// wireless stats
+	unsigned long		scan_timeout;	/* Time scan should be read */
+	struct iw_spy_data	spy_data;
+	struct iw_public_data	wireless_data;
+	/* MIC stuff */
+	struct crypto_cipher	*tfm;
+	mic_module		mod[2];
+	mic_statistics		micstats;
+	HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors
+	HostTxDesc txfids[MPI_MAX_FIDS];
+	HostRidDesc config_desc;
+	unsigned long ridbus; // phys addr of config_desc
+	struct sk_buff_head txq;// tx queue used by mpi350 code
+	struct pci_dev          *pci;
+	unsigned char		__iomem *pcimem;
+	unsigned char		__iomem *pciaux;
+	unsigned char		*shared;
+	dma_addr_t		shared_dma;
+	pm_message_t		power;
+	SsidRid			*SSID;
+	APListRid		*APList;
+#define	PCI_SHARED_LEN		2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
+	char			proc_name[IFNAMSIZ];
+
+	int			wep_capable;
+	int			max_wep_idx;
+
+	/* WPA-related stuff */
+	unsigned int bssListFirst;
+	unsigned int bssListNext;
+	unsigned int bssListRidLen;
+
+	struct list_head network_list;
+	struct list_head network_free_list;
+	BSSListElement *networks;
+};
+
+static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen,
+			   int whichbap)
+{
+	return ai->bap_read(ai, pu16Dst, bytelen, whichbap);
+}
+
+static int setup_proc_entry( struct net_device *dev,
+			     struct airo_info *apriv );
+static int takedown_proc_entry( struct net_device *dev,
+				struct airo_info *apriv );
+
+static int cmdreset(struct airo_info *ai);
+static int setflashmode (struct airo_info *ai);
+static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
+static int flashputbuf(struct airo_info *ai);
+static int flashrestart(struct airo_info *ai,struct net_device *dev);
+
+#define airo_print(type, name, fmt, args...) \
+	printk(type DRV_NAME "(%s): " fmt "\n", name, ##args)
+
+#define airo_print_info(name, fmt, args...) \
+	airo_print(KERN_INFO, name, fmt, ##args)
+
+#define airo_print_dbg(name, fmt, args...) \
+	airo_print(KERN_DEBUG, name, fmt, ##args)
+
+#define airo_print_warn(name, fmt, args...) \
+	airo_print(KERN_WARNING, name, fmt, ##args)
+
+#define airo_print_err(name, fmt, args...) \
+	airo_print(KERN_ERR, name, fmt, ##args)
+
+#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash)
+
+/***********************************************************************
+ *                              MIC ROUTINES                           *
+ ***********************************************************************
+ */
+
+static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq);
+static void MoveWindow(miccntx *context, u32 micSeq);
+static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
+			   struct crypto_cipher *tfm);
+static void emmh32_init(emmh32_context *context);
+static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
+static void emmh32_final(emmh32_context *context, u8 digest[4]);
+static int flashpchar(struct airo_info *ai,int byte,int dwelltime);
+
+static void age_mic_context(miccntx *cur, miccntx *old, u8 *key, int key_len,
+			    struct crypto_cipher *tfm)
+{
+	/* If the current MIC context is valid and its key is the same as
+	 * the MIC register, there's nothing to do.
+	 */
+	if (cur->valid && (memcmp(cur->key, key, key_len) == 0))
+		return;
+
+	/* Age current mic Context */
+	memcpy(old, cur, sizeof(*cur));
+
+	/* Initialize new context */
+	memcpy(cur->key, key, key_len);
+	cur->window  = 33; /* Window always points to the middle */
+	cur->rx      = 0;  /* Rx Sequence numbers */
+	cur->tx      = 0;  /* Tx sequence numbers */
+	cur->valid   = 1;  /* Key is now valid */
+
+	/* Give key to mic seed */
+	emmh32_setseed(&cur->seed, key, key_len, tfm);
+}
+
+/* micinit - Initialize mic seed */
+
+static void micinit(struct airo_info *ai)
+{
+	MICRid mic_rid;
+
+	clear_bit(JOB_MIC, &ai->jobs);
+	PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
+	up(&ai->sem);
+
+	ai->micstats.enabled = (le16_to_cpu(mic_rid.state) & 0x00FF) ? 1 : 0;
+	if (!ai->micstats.enabled) {
+		/* So next time we have a valid key and mic is enabled, we will
+		 * update the sequence number if the key is the same as before.
+		 */
+		ai->mod[0].uCtx.valid = 0;
+		ai->mod[0].mCtx.valid = 0;
+		return;
+	}
+
+	if (mic_rid.multicastValid) {
+		age_mic_context(&ai->mod[0].mCtx, &ai->mod[1].mCtx,
+		                mic_rid.multicast, sizeof(mic_rid.multicast),
+		                ai->tfm);
+	}
+
+	if (mic_rid.unicastValid) {
+		age_mic_context(&ai->mod[0].uCtx, &ai->mod[1].uCtx,
+				mic_rid.unicast, sizeof(mic_rid.unicast),
+				ai->tfm);
+	}
+}
+
+/* micsetup - Get ready for business */
+
+static int micsetup(struct airo_info *ai) {
+	int i;
+
+	if (ai->tfm == NULL)
+	        ai->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
+
+        if (IS_ERR(ai->tfm)) {
+                airo_print_err(ai->dev->name, "failed to load transform for AES");
+                ai->tfm = NULL;
+                return ERROR;
+        }
+
+	for (i=0; i < NUM_MODULES; i++) {
+		memset(&ai->mod[i].mCtx,0,sizeof(miccntx));
+		memset(&ai->mod[i].uCtx,0,sizeof(miccntx));
+	}
+	return SUCCESS;
+}
+
+static const u8 micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
+
+/*===========================================================================
+ * Description: Mic a packet
+ *    
+ *      Inputs: etherHead * pointer to an 802.3 frame
+ *    
+ *     Returns: BOOLEAN if successful, otherwise false.
+ *             PacketTxLen will be updated with the mic'd packets size.
+ *
+ *    Caveats: It is assumed that the frame buffer will already
+ *             be big enough to hold the largets mic message possible.
+ *            (No memory allocation is done here).
+ *  
+ *    Author: sbraneky (10/15/01)
+ *    Merciless hacks by rwilcher (1/14/02)
+ */
+
+static int encapsulate(struct airo_info *ai ,etherHead *frame, MICBuffer *mic, int payLen)
+{
+	miccntx   *context;
+
+	// Determine correct context
+	// If not adhoc, always use unicast key
+
+	if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1))
+		context = &ai->mod[0].mCtx;
+	else
+		context = &ai->mod[0].uCtx;
+  
+	if (!context->valid)
+		return ERROR;
+
+	mic->typelen = htons(payLen + 16); //Length of Mic'd packet
+
+	memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap
+
+	// Add Tx sequence
+	mic->seq = htonl(context->tx);
+	context->tx += 2;
+
+	emmh32_init(&context->seed); // Mic the packet
+	emmh32_update(&context->seed,frame->da,ETH_ALEN * 2); // DA,SA
+	emmh32_update(&context->seed,(u8*)&mic->typelen,10); // Type/Length and Snap
+	emmh32_update(&context->seed,(u8*)&mic->seq,sizeof(mic->seq)); //SEQ
+	emmh32_update(&context->seed,(u8*)(frame + 1),payLen); //payload
+	emmh32_final(&context->seed, (u8*)&mic->mic);
+
+	/*    New Type/length ?????????? */
+	mic->typelen = 0; //Let NIC know it could be an oversized packet
+	return SUCCESS;
+}
+
+typedef enum {
+    NONE,
+    NOMIC,
+    NOMICPLUMMED,
+    SEQUENCE,
+    INCORRECTMIC,
+} mic_error;
+
+/*===========================================================================
+ *  Description: Decapsulates a MIC'd packet and returns the 802.3 packet
+ *               (removes the MIC stuff) if packet is a valid packet.
+ *      
+ *       Inputs: etherHead  pointer to the 802.3 packet             
+ *     
+ *      Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE
+ *     
+ *      Author: sbraneky (10/15/01)
+ *    Merciless hacks by rwilcher (1/14/02)
+ *---------------------------------------------------------------------------
+ */
+
+static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen)
+{
+	int      i;
+	u32      micSEQ;
+	miccntx  *context;
+	u8       digest[4];
+	mic_error micError = NONE;
+
+	// Check if the packet is a Mic'd packet
+
+	if (!ai->micstats.enabled) {
+		//No Mic set or Mic OFF but we received a MIC'd packet.
+		if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) {
+			ai->micstats.rxMICPlummed++;
+			return ERROR;
+		}
+		return SUCCESS;
+	}
+
+	if (ntohs(mic->typelen) == 0x888E)
+		return SUCCESS;
+
+	if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) {
+	    // Mic enabled but packet isn't Mic'd
+		ai->micstats.rxMICPlummed++;
+	    	return ERROR;
+	}
+
+	micSEQ = ntohl(mic->seq);            //store SEQ as CPU order
+
+	//At this point we a have a mic'd packet and mic is enabled
+	//Now do the mic error checking.
+
+	//Receive seq must be odd
+	if ( (micSEQ & 1) == 0 ) {
+		ai->micstats.rxWrongSequence++;
+		return ERROR;
+	}
+
+	for (i = 0; i < NUM_MODULES; i++) {
+		int mcast = eth->da[0] & 1;
+		//Determine proper context 
+		context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx;
+	
+		//Make sure context is valid
+		if (!context->valid) {
+			if (i == 0)
+				micError = NOMICPLUMMED;
+			continue;                
+		}
+	       	//DeMic it 
+
+		if (!mic->typelen)
+			mic->typelen = htons(payLen + sizeof(MICBuffer) - 2);
+	
+		emmh32_init(&context->seed);
+		emmh32_update(&context->seed, eth->da, ETH_ALEN*2); 
+		emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap)); 
+		emmh32_update(&context->seed, (u8 *)&mic->seq,sizeof(mic->seq));	
+		emmh32_update(&context->seed, (u8 *)(eth + 1),payLen);	
+		//Calculate MIC
+		emmh32_final(&context->seed, digest);
+	
+		if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match
+		  //Invalid Mic
+			if (i == 0)
+				micError = INCORRECTMIC;
+			continue;
+		}
+
+		//Check Sequence number if mics pass
+		if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) {
+			ai->micstats.rxSuccess++;
+			return SUCCESS;
+		}
+		if (i == 0)
+			micError = SEQUENCE;
+	}
+
+	// Update statistics
+	switch (micError) {
+		case NOMICPLUMMED: ai->micstats.rxMICPlummed++;   break;
+		case SEQUENCE:    ai->micstats.rxWrongSequence++; break;
+		case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break;
+		case NONE:  break;
+		case NOMIC: break;
+	}
+	return ERROR;
+}
+
+/*===========================================================================
+ * Description:  Checks the Rx Seq number to make sure it is valid
+ *               and hasn't already been received
+ *   
+ *     Inputs: miccntx - mic context to check seq against
+ *             micSeq  - the Mic seq number
+ *   
+ *    Returns: TRUE if valid otherwise FALSE. 
+ *
+ *    Author: sbraneky (10/15/01)
+ *    Merciless hacks by rwilcher (1/14/02)
+ *---------------------------------------------------------------------------
+ */
+
+static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq)
+{
+	u32 seq,index;
+
+	//Allow for the ap being rebooted - if it is then use the next 
+	//sequence number of the current sequence number - might go backwards
+
+	if (mcast) {
+		if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) {
+			clear_bit (FLAG_UPDATE_MULTI, &ai->flags);
+			context->window = (micSeq > 33) ? micSeq : 33;
+			context->rx     = 0;        // Reset rx
+		}
+	} else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) {
+		clear_bit (FLAG_UPDATE_UNI, &ai->flags);
+		context->window = (micSeq > 33) ? micSeq : 33; // Move window
+		context->rx     = 0;        // Reset rx
+	}
+
+	//Make sequence number relative to START of window
+	seq = micSeq - (context->window - 33);
+
+	//Too old of a SEQ number to check.
+	if ((s32)seq < 0)
+		return ERROR;
+    
+	if ( seq > 64 ) {
+		//Window is infinite forward
+		MoveWindow(context,micSeq);
+		return SUCCESS;
+	}
+
+	// We are in the window. Now check the context rx bit to see if it was already sent
+	seq >>= 1;         //divide by 2 because we only have odd numbers
+	index = 1 << seq;  //Get an index number
+
+	if (!(context->rx & index)) {
+		//micSEQ falls inside the window.
+		//Add seqence number to the list of received numbers.
+		context->rx |= index;
+
+		MoveWindow(context,micSeq);
+
+		return SUCCESS;
+	}
+	return ERROR;
+}
+
+static void MoveWindow(miccntx *context, u32 micSeq)
+{
+	u32 shift;
+
+	//Move window if seq greater than the middle of the window
+	if (micSeq > context->window) {
+		shift = (micSeq - context->window) >> 1;
+    
+		    //Shift out old
+		if (shift < 32)
+			context->rx >>= shift;
+		else
+			context->rx = 0;
+
+		context->window = micSeq;      //Move window
+	}
+}
+
+/*==============================================*/
+/*========== EMMH ROUTINES  ====================*/
+/*==============================================*/
+
+/* mic accumulate */
+#define MIC_ACCUM(val)	\
+	context->accum += (u64)(val) * context->coeff[coeff_position++];
+
+static unsigned char aes_counter[16];
+
+/* expand the key to fill the MMH coefficient array */
+static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
+			   struct crypto_cipher *tfm)
+{
+  /* take the keying material, expand if necessary, truncate at 16-bytes */
+  /* run through AES counter mode to generate context->coeff[] */
+  
+	int i,j;
+	u32 counter;
+	u8 *cipher, plain[16];
+
+	crypto_cipher_setkey(tfm, pkey, 16);
+	counter = 0;
+	for (i = 0; i < ARRAY_SIZE(context->coeff); ) {
+		aes_counter[15] = (u8)(counter >> 0);
+		aes_counter[14] = (u8)(counter >> 8);
+		aes_counter[13] = (u8)(counter >> 16);
+		aes_counter[12] = (u8)(counter >> 24);
+		counter++;
+		memcpy (plain, aes_counter, 16);
+		crypto_cipher_encrypt_one(tfm, plain, plain);
+		cipher = plain;
+		for (j = 0; (j < 16) && (i < ARRAY_SIZE(context->coeff)); ) {
+			context->coeff[i++] = ntohl(*(__be32 *)&cipher[j]);
+			j += 4;
+		}
+	}
+}
+
+/* prepare for calculation of a new mic */
+static void emmh32_init(emmh32_context *context)
+{
+	/* prepare for new mic calculation */
+	context->accum = 0;
+	context->position = 0;
+}
+
+/* add some bytes to the mic calculation */
+static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
+{
+	int	coeff_position, byte_position;
+  
+	if (len == 0) return;
+  
+	coeff_position = context->position >> 2;
+  
+	/* deal with partial 32-bit word left over from last update */
+	byte_position = context->position & 3;
+	if (byte_position) {
+		/* have a partial word in part to deal with */
+		do {
+			if (len == 0) return;
+			context->part.d8[byte_position++] = *pOctets++;
+			context->position++;
+			len--;
+		} while (byte_position < 4);
+		MIC_ACCUM(ntohl(context->part.d32));
+	}
+
+	/* deal with full 32-bit words */
+	while (len >= 4) {
+		MIC_ACCUM(ntohl(*(__be32 *)pOctets));
+		context->position += 4;
+		pOctets += 4;
+		len -= 4;
+	}
+
+	/* deal with partial 32-bit word that will be left over from this update */
+	byte_position = 0;
+	while (len > 0) {
+		context->part.d8[byte_position++] = *pOctets++;
+		context->position++;
+		len--;
+	}
+}
+
+/* mask used to zero empty bytes for final partial word */
+static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
+
+/* calculate the mic */
+static void emmh32_final(emmh32_context *context, u8 digest[4])
+{
+	int	coeff_position, byte_position;
+	u32	val;
+  
+	u64 sum, utmp;
+	s64 stmp;
+
+	coeff_position = context->position >> 2;
+  
+	/* deal with partial 32-bit word left over from last update */
+	byte_position = context->position & 3;
+	if (byte_position) {
+		/* have a partial word in part to deal with */
+		val = ntohl(context->part.d32);
+		MIC_ACCUM(val & mask32[byte_position]);	/* zero empty bytes */
+	}
+
+	/* reduce the accumulated u64 to a 32-bit MIC */
+	sum = context->accum;
+	stmp = (sum  & 0xffffffffLL) - ((sum >> 32)  * 15);
+	utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15);
+	sum = utmp & 0xffffffffLL;
+	if (utmp > 0x10000000fLL)
+		sum -= 15;
+
+	val = (u32)sum;
+	digest[0] = (val>>24) & 0xFF;
+	digest[1] = (val>>16) & 0xFF;
+	digest[2] = (val>>8) & 0xFF;
+	digest[3] = val & 0xFF;
+}
+
+static int readBSSListRid(struct airo_info *ai, int first,
+		      BSSListRid *list)
+{
+	Cmd cmd;
+	Resp rsp;
+
+	if (first == 1) {
+		if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.cmd=CMD_LISTBSS;
+		if (down_interruptible(&ai->sem))
+			return -ERESTARTSYS;
+		ai->list_bss_task = current;
+		issuecommand(ai, &cmd, &rsp);
+		up(&ai->sem);
+		/* Let the command take effect */
+		schedule_timeout_uninterruptible(3 * HZ);
+		ai->list_bss_task = NULL;
+	}
+	return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext,
+			    list, ai->bssListRidLen, 1);
+}
+
+static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock)
+{
+	return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM,
+				wkr, sizeof(*wkr), lock);
+}
+
+static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock)
+{
+	int rc;
+	rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock);
+	if (rc!=SUCCESS)
+		airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc);
+	if (perm) {
+		rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock);
+		if (rc!=SUCCESS)
+			airo_print_err(ai->dev->name, "WEP_PERM set %x", rc);
+	}
+	return rc;
+}
+
+static int readSsidRid(struct airo_info*ai, SsidRid *ssidr)
+{
+	return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1);
+}
+
+static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock)
+{
+	return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock);
+}
+
+static int readConfigRid(struct airo_info *ai, int lock)
+{
+	int rc;
+	ConfigRid cfg;
+
+	if (ai->config.len)
+		return SUCCESS;
+
+	rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock);
+	if (rc != SUCCESS)
+		return rc;
+
+	ai->config = cfg;
+	return SUCCESS;
+}
+
+static inline void checkThrottle(struct airo_info *ai)
+{
+	int i;
+/* Old hardware had a limit on encryption speed */
+	if (ai->config.authType != AUTH_OPEN && maxencrypt) {
+		for(i=0; i<8; i++) {
+			if (ai->config.rates[i] > maxencrypt) {
+				ai->config.rates[i] = 0;
+			}
+		}
+	}
+}
+
+static int writeConfigRid(struct airo_info *ai, int lock)
+{
+	ConfigRid cfgr;
+
+	if (!test_bit (FLAG_COMMIT, &ai->flags))
+		return SUCCESS;
+
+	clear_bit (FLAG_COMMIT, &ai->flags);
+	clear_bit (FLAG_RESET, &ai->flags);
+	checkThrottle(ai);
+	cfgr = ai->config;
+
+	if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
+		set_bit(FLAG_ADHOC, &ai->flags);
+	else
+		clear_bit(FLAG_ADHOC, &ai->flags);
+
+	return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock);
+}
+
+static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock)
+{
+	return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock);
+}
+
+static int readAPListRid(struct airo_info *ai, APListRid *aplr)
+{
+	return PC4500_readrid(ai, RID_APLIST, aplr, sizeof(*aplr), 1);
+}
+
+static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock)
+{
+	return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock);
+}
+
+static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock)
+{
+	return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock);
+}
+
+static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock)
+{
+	return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock);
+}
+
+static void try_auto_wep(struct airo_info *ai)
+{
+	if (auto_wep && !test_bit(FLAG_RADIO_DOWN, &ai->flags)) {
+		ai->expires = RUN_AT(3*HZ);
+		wake_up_interruptible(&ai->thr_wait);
+	}
+}
+
+static int airo_open(struct net_device *dev) {
+	struct airo_info *ai = dev->ml_priv;
+	int rc = 0;
+
+	if (test_bit(FLAG_FLASHING, &ai->flags))
+		return -EIO;
+
+	/* Make sure the card is configured.
+	 * Wireless Extensions may postpone config changes until the card
+	 * is open (to pipeline changes and speed-up card setup). If
+	 * those changes are not yet committed, do it now - Jean II */
+	if (test_bit(FLAG_COMMIT, &ai->flags)) {
+		disable_MAC(ai, 1);
+		writeConfigRid(ai, 1);
+	}
+
+	if (ai->wifidev != dev) {
+		clear_bit(JOB_DIE, &ai->jobs);
+		ai->airo_thread_task = kthread_run(airo_thread, dev, "%s",
+						   dev->name);
+		if (IS_ERR(ai->airo_thread_task))
+			return (int)PTR_ERR(ai->airo_thread_task);
+
+		rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED,
+			dev->name, dev);
+		if (rc) {
+			airo_print_err(dev->name,
+				"register interrupt %d failed, rc %d",
+				dev->irq, rc);
+			set_bit(JOB_DIE, &ai->jobs);
+			kthread_stop(ai->airo_thread_task);
+			return rc;
+		}
+
+		/* Power on the MAC controller (which may have been disabled) */
+		clear_bit(FLAG_RADIO_DOWN, &ai->flags);
+		enable_interrupts(ai);
+
+		try_auto_wep(ai);
+	}
+	enable_MAC(ai, 1);
+
+	netif_start_queue(dev);
+	return 0;
+}
+
+static netdev_tx_t mpi_start_xmit(struct sk_buff *skb,
+					struct net_device *dev)
+{
+	int npacks, pending;
+	unsigned long flags;
+	struct airo_info *ai = dev->ml_priv;
+
+	if (!skb) {
+		airo_print_err(dev->name, "%s: skb == NULL!",__func__);
+		return NETDEV_TX_OK;
+	}
+	npacks = skb_queue_len (&ai->txq);
+
+	if (npacks >= MAXTXQ - 1) {
+		netif_stop_queue (dev);
+		if (npacks > MAXTXQ) {
+			dev->stats.tx_fifo_errors++;
+			return NETDEV_TX_BUSY;
+		}
+		skb_queue_tail (&ai->txq, skb);
+		return NETDEV_TX_OK;
+	}
+
+	spin_lock_irqsave(&ai->aux_lock, flags);
+	skb_queue_tail (&ai->txq, skb);
+	pending = test_bit(FLAG_PENDING_XMIT, &ai->flags);
+	spin_unlock_irqrestore(&ai->aux_lock,flags);
+	netif_wake_queue (dev);
+
+	if (pending == 0) {
+		set_bit(FLAG_PENDING_XMIT, &ai->flags);
+		mpi_send_packet (dev);
+	}
+	return NETDEV_TX_OK;
+}
+
+/*
+ * @mpi_send_packet
+ *
+ * Attempt to transmit a packet. Can be called from interrupt
+ * or transmit . return number of packets we tried to send
+ */
+
+static int mpi_send_packet (struct net_device *dev)
+{
+	struct sk_buff *skb;
+	unsigned char *buffer;
+	s16 len;
+	__le16 *payloadLen;
+	struct airo_info *ai = dev->ml_priv;
+	u8 *sendbuf;
+
+	/* get a packet to send */
+
+	if ((skb = skb_dequeue(&ai->txq)) == NULL) {
+		airo_print_err(dev->name,
+			"%s: Dequeue'd zero in send_packet()",
+			__func__);
+		return 0;
+	}
+
+	/* check min length*/
+	len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	buffer = skb->data;
+
+	ai->txfids[0].tx_desc.offset = 0;
+	ai->txfids[0].tx_desc.valid = 1;
+	ai->txfids[0].tx_desc.eoc = 1;
+	ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr);
+
+/*
+ * Magic, the cards firmware needs a length count (2 bytes) in the host buffer
+ * right after  TXFID_HDR.The TXFID_HDR contains the status short so payloadlen
+ * is immediately after it. ------------------------------------------------
+ *                         |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA|
+ *                         ------------------------------------------------
+ */
+
+	memcpy(ai->txfids[0].virtual_host_addr,
+		(char *)&wifictlhdr8023, sizeof(wifictlhdr8023));
+
+	payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr +
+		sizeof(wifictlhdr8023));
+	sendbuf = ai->txfids[0].virtual_host_addr +
+		sizeof(wifictlhdr8023) + 2 ;
+
+	/*
+	 * Firmware automatically puts 802 header on so
+	 * we don't need to account for it in the length
+	 */
+	if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
+		(ntohs(((__be16 *)buffer)[6]) != 0x888E)) {
+		MICBuffer pMic;
+
+		if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS)
+			return ERROR;
+
+		*payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic));
+		ai->txfids[0].tx_desc.len += sizeof(pMic);
+		/* copy data into airo dma buffer */
+		memcpy (sendbuf, buffer, sizeof(etherHead));
+		buffer += sizeof(etherHead);
+		sendbuf += sizeof(etherHead);
+		memcpy (sendbuf, &pMic, sizeof(pMic));
+		sendbuf += sizeof(pMic);
+		memcpy (sendbuf, buffer, len - sizeof(etherHead));
+	} else {
+		*payloadLen = cpu_to_le16(len - sizeof(etherHead));
+
+		dev->trans_start = jiffies;
+
+		/* copy data into airo dma buffer */
+		memcpy(sendbuf, buffer, len);
+	}
+
+	memcpy_toio(ai->txfids[0].card_ram_off,
+		&ai->txfids[0].tx_desc, sizeof(TxFid));
+
+	OUT4500(ai, EVACK, 8);
+
+	dev_kfree_skb_any(skb);
+	return 1;
+}
+
+static void get_tx_error(struct airo_info *ai, s32 fid)
+{
+	__le16 status;
+
+	if (fid < 0)
+		status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status;
+	else {
+		if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS)
+			return;
+		bap_read(ai, &status, 2, BAP0);
+	}
+	if (le16_to_cpu(status) & 2) /* Too many retries */
+		ai->dev->stats.tx_aborted_errors++;
+	if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */
+		ai->dev->stats.tx_heartbeat_errors++;
+	if (le16_to_cpu(status) & 8) /* Aid fail */
+		{ }
+	if (le16_to_cpu(status) & 0x10) /* MAC disabled */
+		ai->dev->stats.tx_carrier_errors++;
+	if (le16_to_cpu(status) & 0x20) /* Association lost */
+		{ }
+	/* We produce a TXDROP event only for retry or lifetime
+	 * exceeded, because that's the only status that really mean
+	 * that this particular node went away.
+	 * Other errors means that *we* screwed up. - Jean II */
+	if ((le16_to_cpu(status) & 2) ||
+	     (le16_to_cpu(status) & 4)) {
+		union iwreq_data	wrqu;
+		char junk[0x18];
+
+		/* Faster to skip over useless data than to do
+		 * another bap_setup(). We are at offset 0x6 and
+		 * need to go to 0x18 and read 6 bytes - Jean II */
+		bap_read(ai, (__le16 *) junk, 0x18, BAP0);
+
+		/* Copy 802.11 dest address.
+		 * We use the 802.11 header because the frame may
+		 * not be 802.3 or may be mangled...
+		 * In Ad-Hoc mode, it will be the node address.
+		 * In managed mode, it will be most likely the AP addr
+		 * User space will figure out how to convert it to
+		 * whatever it needs (IP address or else).
+		 * - Jean II */
+		memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN);
+		wrqu.addr.sa_family = ARPHRD_ETHER;
+
+		/* Send event to user space */
+		wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL);
+	}
+}
+
+static void airo_end_xmit(struct net_device *dev) {
+	u16 status;
+	int i;
+	struct airo_info *priv = dev->ml_priv;
+	struct sk_buff *skb = priv->xmit.skb;
+	int fid = priv->xmit.fid;
+	u32 *fids = priv->fids;
+
+	clear_bit(JOB_XMIT, &priv->jobs);
+	clear_bit(FLAG_PENDING_XMIT, &priv->flags);
+	status = transmit_802_3_packet (priv, fids[fid], skb->data);
+	up(&priv->sem);
+
+	i = 0;
+	if ( status == SUCCESS ) {
+		dev->trans_start = jiffies;
+		for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++);
+	} else {
+		priv->fids[fid] &= 0xffff;
+		dev->stats.tx_window_errors++;
+	}
+	if (i < MAX_FIDS / 2)
+		netif_wake_queue(dev);
+	dev_kfree_skb(skb);
+}
+
+static netdev_tx_t airo_start_xmit(struct sk_buff *skb,
+					 struct net_device *dev)
+{
+	s16 len;
+	int i, j;
+	struct airo_info *priv = dev->ml_priv;
+	u32 *fids = priv->fids;
+
+	if ( skb == NULL ) {
+		airo_print_err(dev->name, "%s: skb == NULL!", __func__);
+		return NETDEV_TX_OK;
+	}
+
+	/* Find a vacant FID */
+	for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ );
+	for( j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++ );
+
+	if ( j >= MAX_FIDS / 2 ) {
+		netif_stop_queue(dev);
+
+		if (i == MAX_FIDS / 2) {
+			dev->stats.tx_fifo_errors++;
+			return NETDEV_TX_BUSY;
+		}
+	}
+	/* check min length*/
+	len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+        /* Mark fid as used & save length for later */
+	fids[i] |= (len << 16);
+	priv->xmit.skb = skb;
+	priv->xmit.fid = i;
+	if (down_trylock(&priv->sem) != 0) {
+		set_bit(FLAG_PENDING_XMIT, &priv->flags);
+		netif_stop_queue(dev);
+		set_bit(JOB_XMIT, &priv->jobs);
+		wake_up_interruptible(&priv->thr_wait);
+	} else
+		airo_end_xmit(dev);
+	return NETDEV_TX_OK;
+}
+
+static void airo_end_xmit11(struct net_device *dev) {
+	u16 status;
+	int i;
+	struct airo_info *priv = dev->ml_priv;
+	struct sk_buff *skb = priv->xmit11.skb;
+	int fid = priv->xmit11.fid;
+	u32 *fids = priv->fids;
+
+	clear_bit(JOB_XMIT11, &priv->jobs);
+	clear_bit(FLAG_PENDING_XMIT11, &priv->flags);
+	status = transmit_802_11_packet (priv, fids[fid], skb->data);
+	up(&priv->sem);
+
+	i = MAX_FIDS / 2;
+	if ( status == SUCCESS ) {
+		dev->trans_start = jiffies;
+		for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++);
+	} else {
+		priv->fids[fid] &= 0xffff;
+		dev->stats.tx_window_errors++;
+	}
+	if (i < MAX_FIDS)
+		netif_wake_queue(dev);
+	dev_kfree_skb(skb);
+}
+
+static netdev_tx_t airo_start_xmit11(struct sk_buff *skb,
+					   struct net_device *dev)
+{
+	s16 len;
+	int i, j;
+	struct airo_info *priv = dev->ml_priv;
+	u32 *fids = priv->fids;
+
+	if (test_bit(FLAG_MPI, &priv->flags)) {
+		/* Not implemented yet for MPI350 */
+		netif_stop_queue(dev);
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
+	}
+
+	if ( skb == NULL ) {
+		airo_print_err(dev->name, "%s: skb == NULL!", __func__);
+		return NETDEV_TX_OK;
+	}
+
+	/* Find a vacant FID */
+	for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ );
+	for( j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++ );
+
+	if ( j >= MAX_FIDS ) {
+		netif_stop_queue(dev);
+
+		if (i == MAX_FIDS) {
+			dev->stats.tx_fifo_errors++;
+			return NETDEV_TX_BUSY;
+		}
+	}
+	/* check min length*/
+	len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+        /* Mark fid as used & save length for later */
+	fids[i] |= (len << 16);
+	priv->xmit11.skb = skb;
+	priv->xmit11.fid = i;
+	if (down_trylock(&priv->sem) != 0) {
+		set_bit(FLAG_PENDING_XMIT11, &priv->flags);
+		netif_stop_queue(dev);
+		set_bit(JOB_XMIT11, &priv->jobs);
+		wake_up_interruptible(&priv->thr_wait);
+	} else
+		airo_end_xmit11(dev);
+	return NETDEV_TX_OK;
+}
+
+static void airo_read_stats(struct net_device *dev)
+{
+	struct airo_info *ai = dev->ml_priv;
+	StatsRid stats_rid;
+	__le32 *vals = stats_rid.vals;
+
+	clear_bit(JOB_STATS, &ai->jobs);
+	if (ai->power.event) {
+		up(&ai->sem);
+		return;
+	}
+	readStatsRid(ai, &stats_rid, RID_STATS, 0);
+	up(&ai->sem);
+
+	dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) +
+			       le32_to_cpu(vals[45]);
+	dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) +
+			       le32_to_cpu(vals[41]);
+	dev->stats.rx_bytes = le32_to_cpu(vals[92]);
+	dev->stats.tx_bytes = le32_to_cpu(vals[91]);
+	dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) +
+			      le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]);
+	dev->stats.tx_errors = le32_to_cpu(vals[42]) +
+			      dev->stats.tx_fifo_errors;
+	dev->stats.multicast = le32_to_cpu(vals[43]);
+	dev->stats.collisions = le32_to_cpu(vals[89]);
+
+	/* detailed rx_errors: */
+	dev->stats.rx_length_errors = le32_to_cpu(vals[3]);
+	dev->stats.rx_crc_errors = le32_to_cpu(vals[4]);
+	dev->stats.rx_frame_errors = le32_to_cpu(vals[2]);
+	dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]);
+}
+
+static struct net_device_stats *airo_get_stats(struct net_device *dev)
+{
+	struct airo_info *local =  dev->ml_priv;
+
+	if (!test_bit(JOB_STATS, &local->jobs)) {
+		/* Get stats out of the card if available */
+		if (down_trylock(&local->sem) != 0) {
+			set_bit(JOB_STATS, &local->jobs);
+			wake_up_interruptible(&local->thr_wait);
+		} else
+			airo_read_stats(dev);
+	}
+
+	return &dev->stats;
+}
+
+static void airo_set_promisc(struct airo_info *ai) {
+	Cmd cmd;
+	Resp rsp;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd=CMD_SETMODE;
+	clear_bit(JOB_PROMISC, &ai->jobs);
+	cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC;
+	issuecommand(ai, &cmd, &rsp);
+	up(&ai->sem);
+}
+
+static void airo_set_multicast_list(struct net_device *dev) {
+	struct airo_info *ai = dev->ml_priv;
+
+	if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
+		change_bit(FLAG_PROMISC, &ai->flags);
+		if (down_trylock(&ai->sem) != 0) {
+			set_bit(JOB_PROMISC, &ai->jobs);
+			wake_up_interruptible(&ai->thr_wait);
+		} else
+			airo_set_promisc(ai);
+	}
+
+	if ((dev->flags&IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
+		/* Turn on multicast.  (Should be already setup...) */
+	}
+}
+
+static int airo_set_mac_address(struct net_device *dev, void *p)
+{
+	struct airo_info *ai = dev->ml_priv;
+	struct sockaddr *addr = p;
+
+	readConfigRid(ai, 1);
+	memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len);
+	set_bit (FLAG_COMMIT, &ai->flags);
+	disable_MAC(ai, 1);
+	writeConfigRid (ai, 1);
+	enable_MAC(ai, 1);
+	memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len);
+	if (ai->wifidev)
+		memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len);
+	return 0;
+}
+
+static int airo_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if ((new_mtu < 68) || (new_mtu > 2400))
+		return -EINVAL;
+	dev->mtu = new_mtu;
+	return 0;
+}
+
+static LIST_HEAD(airo_devices);
+
+static void add_airo_dev(struct airo_info *ai)
+{
+	/* Upper layers already keep track of PCI devices,
+	 * so we only need to remember our non-PCI cards. */
+	if (!ai->pci)
+		list_add_tail(&ai->dev_list, &airo_devices);
+}
+
+static void del_airo_dev(struct airo_info *ai)
+{
+	if (!ai->pci)
+		list_del(&ai->dev_list);
+}
+
+static int airo_close(struct net_device *dev) {
+	struct airo_info *ai = dev->ml_priv;
+
+	netif_stop_queue(dev);
+
+	if (ai->wifidev != dev) {
+#ifdef POWER_ON_DOWN
+		/* Shut power to the card. The idea is that the user can save
+		 * power when he doesn't need the card with "ifconfig down".
+		 * That's the method that is most friendly towards the network
+		 * stack (i.e. the network stack won't try to broadcast
+		 * anything on the interface and routes are gone. Jean II */
+		set_bit(FLAG_RADIO_DOWN, &ai->flags);
+		disable_MAC(ai, 1);
+#endif
+		disable_interrupts( ai );
+
+		free_irq(dev->irq, dev);
+
+		set_bit(JOB_DIE, &ai->jobs);
+		kthread_stop(ai->airo_thread_task);
+	}
+	return 0;
+}
+
+void stop_airo_card( struct net_device *dev, int freeres )
+{
+	struct airo_info *ai = dev->ml_priv;
+
+	set_bit(FLAG_RADIO_DOWN, &ai->flags);
+	disable_MAC(ai, 1);
+	disable_interrupts(ai);
+	takedown_proc_entry( dev, ai );
+	if (test_bit(FLAG_REGISTERED, &ai->flags)) {
+		unregister_netdev( dev );
+		if (ai->wifidev) {
+			unregister_netdev(ai->wifidev);
+			free_netdev(ai->wifidev);
+			ai->wifidev = NULL;
+		}
+		clear_bit(FLAG_REGISTERED, &ai->flags);
+	}
+	/*
+	 * Clean out tx queue
+	 */
+	if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) {
+		struct sk_buff *skb = NULL;
+		for (;(skb = skb_dequeue(&ai->txq));)
+			dev_kfree_skb(skb);
+	}
+
+	airo_networks_free (ai);
+
+	kfree(ai->flash);
+	kfree(ai->rssi);
+	kfree(ai->APList);
+	kfree(ai->SSID);
+	if (freeres) {
+		/* PCMCIA frees this stuff, so only for PCI and ISA */
+	        release_region( dev->base_addr, 64 );
+		if (test_bit(FLAG_MPI, &ai->flags)) {
+			if (ai->pci)
+				mpi_unmap_card(ai->pci);
+			if (ai->pcimem)
+				iounmap(ai->pcimem);
+			if (ai->pciaux)
+				iounmap(ai->pciaux);
+			pci_free_consistent(ai->pci, PCI_SHARED_LEN,
+				ai->shared, ai->shared_dma);
+		}
+        }
+	crypto_free_cipher(ai->tfm);
+	del_airo_dev(ai);
+	free_netdev( dev );
+}
+
+EXPORT_SYMBOL(stop_airo_card);
+
+static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+	memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
+	return ETH_ALEN;
+}
+
+static void mpi_unmap_card(struct pci_dev *pci)
+{
+	unsigned long mem_start = pci_resource_start(pci, 1);
+	unsigned long mem_len = pci_resource_len(pci, 1);
+	unsigned long aux_start = pci_resource_start(pci, 2);
+	unsigned long aux_len = AUXMEMSIZE;
+
+	release_mem_region(aux_start, aux_len);
+	release_mem_region(mem_start, mem_len);
+}
+
+/*************************************************************
+ *  This routine assumes that descriptors have been setup .
+ *  Run at insmod time or after reset  when the decriptors
+ *  have been initialized . Returns 0 if all is well nz
+ *  otherwise . Does not allocate memory but sets up card
+ *  using previously allocated descriptors.
+ */
+static int mpi_init_descriptors (struct airo_info *ai)
+{
+	Cmd cmd;
+	Resp rsp;
+	int i;
+	int rc = SUCCESS;
+
+	/* Alloc  card RX descriptors */
+	netif_stop_queue(ai->dev);
+
+	memset(&rsp,0,sizeof(rsp));
+	memset(&cmd,0,sizeof(cmd));
+
+	cmd.cmd = CMD_ALLOCATEAUX;
+	cmd.parm0 = FID_RX;
+	cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux);
+	cmd.parm2 = MPI_MAX_FIDS;
+	rc=issuecommand(ai, &cmd, &rsp);
+	if (rc != SUCCESS) {
+		airo_print_err(ai->dev->name, "Couldn't allocate RX FID");
+		return rc;
+	}
+
+	for (i=0; i<MPI_MAX_FIDS; i++) {
+		memcpy_toio(ai->rxfids[i].card_ram_off,
+			&ai->rxfids[i].rx_desc, sizeof(RxFid));
+	}
+
+	/* Alloc card TX descriptors */
+
+	memset(&rsp,0,sizeof(rsp));
+	memset(&cmd,0,sizeof(cmd));
+
+	cmd.cmd = CMD_ALLOCATEAUX;
+	cmd.parm0 = FID_TX;
+	cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux);
+	cmd.parm2 = MPI_MAX_FIDS;
+
+	for (i=0; i<MPI_MAX_FIDS; i++) {
+		ai->txfids[i].tx_desc.valid = 1;
+		memcpy_toio(ai->txfids[i].card_ram_off,
+			&ai->txfids[i].tx_desc, sizeof(TxFid));
+	}
+	ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
+
+	rc=issuecommand(ai, &cmd, &rsp);
+	if (rc != SUCCESS) {
+		airo_print_err(ai->dev->name, "Couldn't allocate TX FID");
+		return rc;
+	}
+
+	/* Alloc card Rid descriptor */
+	memset(&rsp,0,sizeof(rsp));
+	memset(&cmd,0,sizeof(cmd));
+
+	cmd.cmd = CMD_ALLOCATEAUX;
+	cmd.parm0 = RID_RW;
+	cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux);
+	cmd.parm2 = 1; /* Magic number... */
+	rc=issuecommand(ai, &cmd, &rsp);
+	if (rc != SUCCESS) {
+		airo_print_err(ai->dev->name, "Couldn't allocate RID");
+		return rc;
+	}
+
+	memcpy_toio(ai->config_desc.card_ram_off,
+		&ai->config_desc.rid_desc, sizeof(Rid));
+
+	return rc;
+}
+
+/*
+ * We are setting up three things here:
+ * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid.
+ * 2) Map PCI memory for issuing commands.
+ * 3) Allocate memory (shared) to send and receive ethernet frames.
+ */
+static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci)
+{
+	unsigned long mem_start, mem_len, aux_start, aux_len;
+	int rc = -1;
+	int i;
+	dma_addr_t busaddroff;
+	unsigned char *vpackoff;
+	unsigned char __iomem *pciaddroff;
+
+	mem_start = pci_resource_start(pci, 1);
+	mem_len = pci_resource_len(pci, 1);
+	aux_start = pci_resource_start(pci, 2);
+	aux_len = AUXMEMSIZE;
+
+	if (!request_mem_region(mem_start, mem_len, DRV_NAME)) {
+		airo_print_err("", "Couldn't get region %x[%x]",
+			(int)mem_start, (int)mem_len);
+		goto out;
+	}
+	if (!request_mem_region(aux_start, aux_len, DRV_NAME)) {
+		airo_print_err("", "Couldn't get region %x[%x]",
+			(int)aux_start, (int)aux_len);
+		goto free_region1;
+	}
+
+	ai->pcimem = ioremap(mem_start, mem_len);
+	if (!ai->pcimem) {
+		airo_print_err("", "Couldn't map region %x[%x]",
+			(int)mem_start, (int)mem_len);
+		goto free_region2;
+	}
+	ai->pciaux = ioremap(aux_start, aux_len);
+	if (!ai->pciaux) {
+		airo_print_err("", "Couldn't map region %x[%x]",
+			(int)aux_start, (int)aux_len);
+		goto free_memmap;
+	}
+
+	/* Reserve PKTSIZE for each fid and 2K for the Rids */
+	ai->shared = pci_alloc_consistent(pci, PCI_SHARED_LEN, &ai->shared_dma);
+	if (!ai->shared) {
+		airo_print_err("", "Couldn't alloc_consistent %d",
+			PCI_SHARED_LEN);
+		goto free_auxmap;
+	}
+
+	/*
+	 * Setup descriptor RX, TX, CONFIG
+	 */
+	busaddroff = ai->shared_dma;
+	pciaddroff = ai->pciaux + AUX_OFFSET;
+	vpackoff   = ai->shared;
+
+	/* RX descriptor setup */
+	for(i = 0; i < MPI_MAX_FIDS; i++) {
+		ai->rxfids[i].pending = 0;
+		ai->rxfids[i].card_ram_off = pciaddroff;
+		ai->rxfids[i].virtual_host_addr = vpackoff;
+		ai->rxfids[i].rx_desc.host_addr = busaddroff;
+		ai->rxfids[i].rx_desc.valid = 1;
+		ai->rxfids[i].rx_desc.len = PKTSIZE;
+		ai->rxfids[i].rx_desc.rdy = 0;
+
+		pciaddroff += sizeof(RxFid);
+		busaddroff += PKTSIZE;
+		vpackoff   += PKTSIZE;
+	}
+
+	/* TX descriptor setup */
+	for(i = 0; i < MPI_MAX_FIDS; i++) {
+		ai->txfids[i].card_ram_off = pciaddroff;
+		ai->txfids[i].virtual_host_addr = vpackoff;
+		ai->txfids[i].tx_desc.valid = 1;
+		ai->txfids[i].tx_desc.host_addr = busaddroff;
+		memcpy(ai->txfids[i].virtual_host_addr,
+			&wifictlhdr8023, sizeof(wifictlhdr8023));
+
+		pciaddroff += sizeof(TxFid);
+		busaddroff += PKTSIZE;
+		vpackoff   += PKTSIZE;
+	}
+	ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
+
+	/* Rid descriptor setup */
+	ai->config_desc.card_ram_off = pciaddroff;
+	ai->config_desc.virtual_host_addr = vpackoff;
+	ai->config_desc.rid_desc.host_addr = busaddroff;
+	ai->ridbus = busaddroff;
+	ai->config_desc.rid_desc.rid = 0;
+	ai->config_desc.rid_desc.len = RIDSIZE;
+	ai->config_desc.rid_desc.valid = 1;
+	pciaddroff += sizeof(Rid);
+	busaddroff += RIDSIZE;
+	vpackoff   += RIDSIZE;
+
+	/* Tell card about descriptors */
+	if (mpi_init_descriptors (ai) != SUCCESS)
+		goto free_shared;
+
+	return 0;
+ free_shared:
+	pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
+ free_auxmap:
+	iounmap(ai->pciaux);
+ free_memmap:
+	iounmap(ai->pcimem);
+ free_region2:
+	release_mem_region(aux_start, aux_len);
+ free_region1:
+	release_mem_region(mem_start, mem_len);
+ out:
+	return rc;
+}
+
+static const struct header_ops airo_header_ops = {
+	.parse = wll_header_parse,
+};
+
+static const struct net_device_ops airo11_netdev_ops = {
+	.ndo_open 		= airo_open,
+	.ndo_stop 		= airo_close,
+	.ndo_start_xmit 	= airo_start_xmit11,
+	.ndo_get_stats 		= airo_get_stats,
+	.ndo_set_mac_address	= airo_set_mac_address,
+	.ndo_do_ioctl		= airo_ioctl,
+	.ndo_change_mtu		= airo_change_mtu,
+};
+
+static void wifi_setup(struct net_device *dev)
+{
+	dev->netdev_ops = &airo11_netdev_ops;
+	dev->header_ops = &airo_header_ops;
+	dev->wireless_handlers = &airo_handler_def;
+
+	dev->type               = ARPHRD_IEEE80211;
+	dev->hard_header_len    = ETH_HLEN;
+	dev->mtu                = AIRO_DEF_MTU;
+	dev->addr_len           = ETH_ALEN;
+	dev->tx_queue_len       = 100; 
+
+	eth_broadcast_addr(dev->broadcast);
+
+	dev->flags              = IFF_BROADCAST|IFF_MULTICAST;
+}
+
+static struct net_device *init_wifidev(struct airo_info *ai,
+					struct net_device *ethdev)
+{
+	int err;
+	struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN,
+					      wifi_setup);
+	if (!dev)
+		return NULL;
+	dev->ml_priv = ethdev->ml_priv;
+	dev->irq = ethdev->irq;
+	dev->base_addr = ethdev->base_addr;
+	dev->wireless_data = ethdev->wireless_data;
+	SET_NETDEV_DEV(dev, ethdev->dev.parent);
+	eth_hw_addr_inherit(dev, ethdev);
+	err = register_netdev(dev);
+	if (err<0) {
+		free_netdev(dev);
+		return NULL;
+	}
+	return dev;
+}
+
+static int reset_card( struct net_device *dev , int lock) {
+	struct airo_info *ai = dev->ml_priv;
+
+	if (lock && down_interruptible(&ai->sem))
+		return -1;
+	waitbusy (ai);
+	OUT4500(ai,COMMAND,CMD_SOFTRESET);
+	msleep(200);
+	waitbusy (ai);
+	msleep(200);
+	if (lock)
+		up(&ai->sem);
+	return 0;
+}
+
+#define AIRO_MAX_NETWORK_COUNT	64
+static int airo_networks_allocate(struct airo_info *ai)
+{
+	if (ai->networks)
+		return 0;
+
+	ai->networks = kcalloc(AIRO_MAX_NETWORK_COUNT, sizeof(BSSListElement),
+			       GFP_KERNEL);
+	if (!ai->networks) {
+		airo_print_warn("", "Out of memory allocating beacons");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void airo_networks_free(struct airo_info *ai)
+{
+	kfree(ai->networks);
+	ai->networks = NULL;
+}
+
+static void airo_networks_initialize(struct airo_info *ai)
+{
+	int i;
+
+	INIT_LIST_HEAD(&ai->network_free_list);
+	INIT_LIST_HEAD(&ai->network_list);
+	for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++)
+		list_add_tail(&ai->networks[i].list,
+			      &ai->network_free_list);
+}
+
+static const struct net_device_ops airo_netdev_ops = {
+	.ndo_open		= airo_open,
+	.ndo_stop		= airo_close,
+	.ndo_start_xmit		= airo_start_xmit,
+	.ndo_get_stats		= airo_get_stats,
+	.ndo_set_rx_mode	= airo_set_multicast_list,
+	.ndo_set_mac_address	= airo_set_mac_address,
+	.ndo_do_ioctl		= airo_ioctl,
+	.ndo_change_mtu		= airo_change_mtu,
+	.ndo_validate_addr	= eth_validate_addr,
+};
+
+static const struct net_device_ops mpi_netdev_ops = {
+	.ndo_open		= airo_open,
+	.ndo_stop		= airo_close,
+	.ndo_start_xmit		= mpi_start_xmit,
+	.ndo_get_stats		= airo_get_stats,
+	.ndo_set_rx_mode	= airo_set_multicast_list,
+	.ndo_set_mac_address	= airo_set_mac_address,
+	.ndo_do_ioctl		= airo_ioctl,
+	.ndo_change_mtu		= airo_change_mtu,
+	.ndo_validate_addr	= eth_validate_addr,
+};
+
+
+static struct net_device *_init_airo_card( unsigned short irq, int port,
+					   int is_pcmcia, struct pci_dev *pci,
+					   struct device *dmdev )
+{
+	struct net_device *dev;
+	struct airo_info *ai;
+	int i, rc;
+	CapabilityRid cap_rid;
+
+	/* Create the network device object. */
+	dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup);
+	if (!dev) {
+		airo_print_err("", "Couldn't alloc_etherdev");
+		return NULL;
+	}
+
+	ai = dev->ml_priv = netdev_priv(dev);
+	ai->wifidev = NULL;
+	ai->flags = 1 << FLAG_RADIO_DOWN;
+	ai->jobs = 0;
+	ai->dev = dev;
+	if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
+		airo_print_dbg("", "Found an MPI350 card");
+		set_bit(FLAG_MPI, &ai->flags);
+	}
+	spin_lock_init(&ai->aux_lock);
+	sema_init(&ai->sem, 1);
+	ai->config.len = 0;
+	ai->pci = pci;
+	init_waitqueue_head (&ai->thr_wait);
+	ai->tfm = NULL;
+	add_airo_dev(ai);
+
+	if (airo_networks_allocate (ai))
+		goto err_out_free;
+	airo_networks_initialize (ai);
+
+	skb_queue_head_init (&ai->txq);
+
+	/* The Airo-specific entries in the device structure. */
+	if (test_bit(FLAG_MPI,&ai->flags))
+		dev->netdev_ops = &mpi_netdev_ops;
+	else
+		dev->netdev_ops = &airo_netdev_ops;
+	dev->wireless_handlers = &airo_handler_def;
+	ai->wireless_data.spy_data = &ai->spy_data;
+	dev->wireless_data = &ai->wireless_data;
+	dev->irq = irq;
+	dev->base_addr = port;
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+
+	SET_NETDEV_DEV(dev, dmdev);
+
+	reset_card (dev, 1);
+	msleep(400);
+
+	if (!is_pcmcia) {
+		if (!request_region(dev->base_addr, 64, DRV_NAME)) {
+			rc = -EBUSY;
+			airo_print_err(dev->name, "Couldn't request region");
+			goto err_out_nets;
+		}
+	}
+
+	if (test_bit(FLAG_MPI,&ai->flags)) {
+		if (mpi_map_card(ai, pci)) {
+			airo_print_err("", "Could not map memory");
+			goto err_out_res;
+		}
+	}
+
+	if (probe) {
+		if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) {
+			airo_print_err(dev->name, "MAC could not be enabled" );
+			rc = -EIO;
+			goto err_out_map;
+		}
+	} else if (!test_bit(FLAG_MPI,&ai->flags)) {
+		ai->bap_read = fast_bap_read;
+		set_bit(FLAG_FLASHING, &ai->flags);
+	}
+
+	strcpy(dev->name, "eth%d");
+	rc = register_netdev(dev);
+	if (rc) {
+		airo_print_err(dev->name, "Couldn't register_netdev");
+		goto err_out_map;
+	}
+	ai->wifidev = init_wifidev(ai, dev);
+	if (!ai->wifidev)
+		goto err_out_reg;
+
+	rc = readCapabilityRid(ai, &cap_rid, 1);
+	if (rc != SUCCESS) {
+		rc = -EIO;
+		goto err_out_wifi;
+	}
+	/* WEP capability discovery */
+	ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0;
+	ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0;
+
+	airo_print_info(dev->name, "Firmware version %x.%x.%02d",
+	                ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF),
+	                (le16_to_cpu(cap_rid.softVer) & 0xFF),
+	                le16_to_cpu(cap_rid.softSubVer));
+
+	/* Test for WPA support */
+	/* Only firmware versions 5.30.17 or better can do WPA */
+	if (le16_to_cpu(cap_rid.softVer) > 0x530
+	 || (le16_to_cpu(cap_rid.softVer) == 0x530
+	      && le16_to_cpu(cap_rid.softSubVer) >= 17)) {
+		airo_print_info(ai->dev->name, "WPA supported.");
+
+		set_bit(FLAG_WPA_CAPABLE, &ai->flags);
+		ai->bssListFirst = RID_WPA_BSSLISTFIRST;
+		ai->bssListNext = RID_WPA_BSSLISTNEXT;
+		ai->bssListRidLen = sizeof(BSSListRid);
+	} else {
+		airo_print_info(ai->dev->name, "WPA unsupported with firmware "
+			"versions older than 5.30.17.");
+
+		ai->bssListFirst = RID_BSSLISTFIRST;
+		ai->bssListNext = RID_BSSLISTNEXT;
+		ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra);
+	}
+
+	set_bit(FLAG_REGISTERED,&ai->flags);
+	airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
+
+	/* Allocate the transmit buffers */
+	if (probe && !test_bit(FLAG_MPI,&ai->flags))
+		for( i = 0; i < MAX_FIDS; i++ )
+			ai->fids[i] = transmit_allocate(ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
+
+	if (setup_proc_entry(dev, dev->ml_priv) < 0)
+		goto err_out_wifi;
+
+	return dev;
+
+err_out_wifi:
+	unregister_netdev(ai->wifidev);
+	free_netdev(ai->wifidev);
+err_out_reg:
+	unregister_netdev(dev);
+err_out_map:
+	if (test_bit(FLAG_MPI,&ai->flags) && pci) {
+		pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
+		iounmap(ai->pciaux);
+		iounmap(ai->pcimem);
+		mpi_unmap_card(ai->pci);
+	}
+err_out_res:
+	if (!is_pcmcia)
+	        release_region( dev->base_addr, 64 );
+err_out_nets:
+	airo_networks_free(ai);
+err_out_free:
+	del_airo_dev(ai);
+	free_netdev(dev);
+	return NULL;
+}
+
+struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia,
+				  struct device *dmdev)
+{
+	return _init_airo_card ( irq, port, is_pcmcia, NULL, dmdev);
+}
+
+EXPORT_SYMBOL(init_airo_card);
+
+static int waitbusy (struct airo_info *ai) {
+	int delay = 0;
+	while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) {
+		udelay (10);
+		if ((++delay % 20) == 0)
+			OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
+	}
+	return delay < 10000;
+}
+
+int reset_airo_card( struct net_device *dev )
+{
+	int i;
+	struct airo_info *ai = dev->ml_priv;
+
+	if (reset_card (dev, 1))
+		return -1;
+
+	if ( setup_card(ai, dev->dev_addr, 1 ) != SUCCESS ) {
+		airo_print_err(dev->name, "MAC could not be enabled");
+		return -1;
+	}
+	airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
+	/* Allocate the transmit buffers if needed */
+	if (!test_bit(FLAG_MPI,&ai->flags))
+		for( i = 0; i < MAX_FIDS; i++ )
+			ai->fids[i] = transmit_allocate (ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
+
+	enable_interrupts( ai );
+	netif_wake_queue(dev);
+	return 0;
+}
+
+EXPORT_SYMBOL(reset_airo_card);
+
+static void airo_send_event(struct net_device *dev) {
+	struct airo_info *ai = dev->ml_priv;
+	union iwreq_data wrqu;
+	StatusRid status_rid;
+
+	clear_bit(JOB_EVENT, &ai->jobs);
+	PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
+	up(&ai->sem);
+	wrqu.data.length = 0;
+	wrqu.data.flags = 0;
+	memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+	/* Send event to user space */
+	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+static void airo_process_scan_results (struct airo_info *ai) {
+	union iwreq_data	wrqu;
+	BSSListRid bss;
+	int rc;
+	BSSListElement * loop_net;
+	BSSListElement * tmp_net;
+
+	/* Blow away current list of scan results */
+	list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) {
+		list_move_tail (&loop_net->list, &ai->network_free_list);
+		/* Don't blow away ->list, just BSS data */
+		memset (loop_net, 0, sizeof (loop_net->bss));
+	}
+
+	/* Try to read the first entry of the scan result */
+	rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0);
+	if((rc) || (bss.index == cpu_to_le16(0xffff))) {
+		/* No scan results */
+		goto out;
+	}
+
+	/* Read and parse all entries */
+	tmp_net = NULL;
+	while((!rc) && (bss.index != cpu_to_le16(0xffff))) {
+		/* Grab a network off the free list */
+		if (!list_empty(&ai->network_free_list)) {
+			tmp_net = list_entry(ai->network_free_list.next,
+					    BSSListElement, list);
+			list_del(ai->network_free_list.next);
+		}
+
+		if (tmp_net != NULL) {
+			memcpy(tmp_net, &bss, sizeof(tmp_net->bss));
+			list_add_tail(&tmp_net->list, &ai->network_list);
+			tmp_net = NULL;
+		}
+
+		/* Read next entry */
+		rc = PC4500_readrid(ai, ai->bssListNext,
+				    &bss, ai->bssListRidLen, 0);
+	}
+
+out:
+	ai->scan_timeout = 0;
+	clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
+	up(&ai->sem);
+
+	/* Send an empty event to user space.
+	 * We don't send the received data on
+	 * the event because it would require
+	 * us to do complex transcoding, and
+	 * we want to minimise the work done in
+	 * the irq handler. Use a request to
+	 * extract the data - Jean II */
+	wrqu.data.length = 0;
+	wrqu.data.flags = 0;
+	wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL);
+}
+
+static int airo_thread(void *data) {
+	struct net_device *dev = data;
+	struct airo_info *ai = dev->ml_priv;
+	int locked;
+
+	set_freezable();
+	while(1) {
+		/* make swsusp happy with our thread */
+		try_to_freeze();
+
+		if (test_bit(JOB_DIE, &ai->jobs))
+			break;
+
+		if (ai->jobs) {
+			locked = down_interruptible(&ai->sem);
+		} else {
+			wait_queue_t wait;
+
+			init_waitqueue_entry(&wait, current);
+			add_wait_queue(&ai->thr_wait, &wait);
+			for (;;) {
+				set_current_state(TASK_INTERRUPTIBLE);
+				if (ai->jobs)
+					break;
+				if (ai->expires || ai->scan_timeout) {
+					if (ai->scan_timeout &&
+							time_after_eq(jiffies,ai->scan_timeout)){
+						set_bit(JOB_SCAN_RESULTS, &ai->jobs);
+						break;
+					} else if (ai->expires &&
+							time_after_eq(jiffies,ai->expires)){
+						set_bit(JOB_AUTOWEP, &ai->jobs);
+						break;
+					}
+					if (!kthread_should_stop() &&
+					    !freezing(current)) {
+						unsigned long wake_at;
+						if (!ai->expires || !ai->scan_timeout) {
+							wake_at = max(ai->expires,
+								ai->scan_timeout);
+						} else {
+							wake_at = min(ai->expires,
+								ai->scan_timeout);
+						}
+						schedule_timeout(wake_at - jiffies);
+						continue;
+					}
+				} else if (!kthread_should_stop() &&
+					   !freezing(current)) {
+					schedule();
+					continue;
+				}
+				break;
+			}
+			current->state = TASK_RUNNING;
+			remove_wait_queue(&ai->thr_wait, &wait);
+			locked = 1;
+		}
+
+		if (locked)
+			continue;
+
+		if (test_bit(JOB_DIE, &ai->jobs)) {
+			up(&ai->sem);
+			break;
+		}
+
+		if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) {
+			up(&ai->sem);
+			continue;
+		}
+
+		if (test_bit(JOB_XMIT, &ai->jobs))
+			airo_end_xmit(dev);
+		else if (test_bit(JOB_XMIT11, &ai->jobs))
+			airo_end_xmit11(dev);
+		else if (test_bit(JOB_STATS, &ai->jobs))
+			airo_read_stats(dev);
+		else if (test_bit(JOB_WSTATS, &ai->jobs))
+			airo_read_wireless_stats(ai);
+		else if (test_bit(JOB_PROMISC, &ai->jobs))
+			airo_set_promisc(ai);
+		else if (test_bit(JOB_MIC, &ai->jobs))
+			micinit(ai);
+		else if (test_bit(JOB_EVENT, &ai->jobs))
+			airo_send_event(dev);
+		else if (test_bit(JOB_AUTOWEP, &ai->jobs))
+			timer_func(dev);
+		else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs))
+			airo_process_scan_results(ai);
+		else  /* Shouldn't get here, but we make sure to unlock */
+			up(&ai->sem);
+	}
+
+	return 0;
+}
+
+static int header_len(__le16 ctl)
+{
+	u16 fc = le16_to_cpu(ctl);
+	switch (fc & 0xc) {
+	case 4:
+		if ((fc & 0xe0) == 0xc0)
+			return 10;	/* one-address control packet */
+		return 16;	/* two-address control packet */
+	case 8:
+		if ((fc & 0x300) == 0x300)
+			return 30;	/* WDS packet */
+	}
+	return 24;
+}
+
+static void airo_handle_cisco_mic(struct airo_info *ai)
+{
+	if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) {
+		set_bit(JOB_MIC, &ai->jobs);
+		wake_up_interruptible(&ai->thr_wait);
+	}
+}
+
+/* Airo Status codes */
+#define STAT_NOBEACON	0x8000 /* Loss of sync - missed beacons */
+#define STAT_MAXRETRIES	0x8001 /* Loss of sync - max retries */
+#define STAT_MAXARL	0x8002 /* Loss of sync - average retry level exceeded*/
+#define STAT_FORCELOSS	0x8003 /* Loss of sync - host request */
+#define STAT_TSFSYNC	0x8004 /* Loss of sync - TSF synchronization */
+#define STAT_DEAUTH	0x8100 /* low byte is 802.11 reason code */
+#define STAT_DISASSOC	0x8200 /* low byte is 802.11 reason code */
+#define STAT_ASSOC_FAIL	0x8400 /* low byte is 802.11 reason code */
+#define STAT_AUTH_FAIL	0x0300 /* low byte is 802.11 reason code */
+#define STAT_ASSOC	0x0400 /* Associated */
+#define STAT_REASSOC    0x0600 /* Reassociated?  Only on firmware >= 5.30.17 */
+
+static void airo_print_status(const char *devname, u16 status)
+{
+	u8 reason = status & 0xFF;
+
+	switch (status & 0xFF00) {
+	case STAT_NOBEACON:
+		switch (status) {
+		case STAT_NOBEACON:
+			airo_print_dbg(devname, "link lost (missed beacons)");
+			break;
+		case STAT_MAXRETRIES:
+		case STAT_MAXARL:
+			airo_print_dbg(devname, "link lost (max retries)");
+			break;
+		case STAT_FORCELOSS:
+			airo_print_dbg(devname, "link lost (local choice)");
+			break;
+		case STAT_TSFSYNC:
+			airo_print_dbg(devname, "link lost (TSF sync lost)");
+			break;
+		default:
+			airo_print_dbg(devname, "unknown status %x\n", status);
+			break;
+		}
+		break;
+	case STAT_DEAUTH:
+		airo_print_dbg(devname, "deauthenticated (reason: %d)", reason);
+		break;
+	case STAT_DISASSOC:
+		airo_print_dbg(devname, "disassociated (reason: %d)", reason);
+		break;
+	case STAT_ASSOC_FAIL:
+		airo_print_dbg(devname, "association failed (reason: %d)",
+			       reason);
+		break;
+	case STAT_AUTH_FAIL:
+		airo_print_dbg(devname, "authentication failed (reason: %d)",
+			       reason);
+		break;
+	case STAT_ASSOC:
+	case STAT_REASSOC:
+		break;
+	default:
+		airo_print_dbg(devname, "unknown status %x\n", status);
+		break;
+	}
+}
+
+static void airo_handle_link(struct airo_info *ai)
+{
+	union iwreq_data wrqu;
+	int scan_forceloss = 0;
+	u16 status;
+
+	/* Get new status and acknowledge the link change */
+	status = le16_to_cpu(IN4500(ai, LINKSTAT));
+	OUT4500(ai, EVACK, EV_LINK);
+
+	if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0))
+		scan_forceloss = 1;
+
+	airo_print_status(ai->dev->name, status);
+
+	if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) {
+		if (auto_wep)
+			ai->expires = 0;
+		if (ai->list_bss_task)
+			wake_up_process(ai->list_bss_task);
+		set_bit(FLAG_UPDATE_UNI, &ai->flags);
+		set_bit(FLAG_UPDATE_MULTI, &ai->flags);
+
+		if (down_trylock(&ai->sem) != 0) {
+			set_bit(JOB_EVENT, &ai->jobs);
+			wake_up_interruptible(&ai->thr_wait);
+		} else
+			airo_send_event(ai->dev);
+	} else if (!scan_forceloss) {
+		if (auto_wep && !ai->expires) {
+			ai->expires = RUN_AT(3*HZ);
+			wake_up_interruptible(&ai->thr_wait);
+		}
+
+		/* Send event to user space */
+		eth_zero_addr(wrqu.ap_addr.sa_data);
+		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+		wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL);
+	}
+}
+
+static void airo_handle_rx(struct airo_info *ai)
+{
+	struct sk_buff *skb = NULL;
+	__le16 fc, v, *buffer, tmpbuf[4];
+	u16 len, hdrlen = 0, gap, fid;
+	struct rx_hdr hdr;
+	int success = 0;
+
+	if (test_bit(FLAG_MPI, &ai->flags)) {
+		if (test_bit(FLAG_802_11, &ai->flags))
+			mpi_receive_802_11(ai);
+		else
+			mpi_receive_802_3(ai);
+		OUT4500(ai, EVACK, EV_RX);
+		return;
+	}
+
+	fid = IN4500(ai, RXFID);
+
+	/* Get the packet length */
+	if (test_bit(FLAG_802_11, &ai->flags)) {
+		bap_setup (ai, fid, 4, BAP0);
+		bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0);
+		/* Bad CRC. Ignore packet */
+		if (le16_to_cpu(hdr.status) & 2)
+			hdr.len = 0;
+		if (ai->wifidev == NULL)
+			hdr.len = 0;
+	} else {
+		bap_setup(ai, fid, 0x36, BAP0);
+		bap_read(ai, &hdr.len, 2, BAP0);
+	}
+	len = le16_to_cpu(hdr.len);
+
+	if (len > AIRO_DEF_MTU) {
+		airo_print_err(ai->dev->name, "Bad size %d", len);
+		goto done;
+	}
+	if (len == 0)
+		goto done;
+
+	if (test_bit(FLAG_802_11, &ai->flags)) {
+		bap_read(ai, &fc, sizeof (fc), BAP0);
+		hdrlen = header_len(fc);
+	} else
+		hdrlen = ETH_ALEN * 2;
+
+	skb = dev_alloc_skb(len + hdrlen + 2 + 2);
+	if (!skb) {
+		ai->dev->stats.rx_dropped++;
+		goto done;
+	}
+
+	skb_reserve(skb, 2); /* This way the IP header is aligned */
+	buffer = (__le16 *) skb_put(skb, len + hdrlen);
+	if (test_bit(FLAG_802_11, &ai->flags)) {
+		buffer[0] = fc;
+		bap_read(ai, buffer + 1, hdrlen - 2, BAP0);
+		if (hdrlen == 24)
+			bap_read(ai, tmpbuf, 6, BAP0);
+
+		bap_read(ai, &v, sizeof(v), BAP0);
+		gap = le16_to_cpu(v);
+		if (gap) {
+			if (gap <= 8) {
+				bap_read(ai, tmpbuf, gap, BAP0);
+			} else {
+				airo_print_err(ai->dev->name, "gaplen too "
+					"big. Problems will follow...");
+			}
+		}
+		bap_read(ai, buffer + hdrlen/2, len, BAP0);
+	} else {
+		MICBuffer micbuf;
+
+		bap_read(ai, buffer, ETH_ALEN * 2, BAP0);
+		if (ai->micstats.enabled) {
+			bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0);
+			if (ntohs(micbuf.typelen) > 0x05DC)
+				bap_setup(ai, fid, 0x44, BAP0);
+			else {
+				if (len <= sizeof (micbuf)) {
+					dev_kfree_skb_irq(skb);
+					goto done;
+				}
+
+				len -= sizeof(micbuf);
+				skb_trim(skb, len + hdrlen);
+			}
+		}
+
+		bap_read(ai, buffer + ETH_ALEN, len, BAP0);
+		if (decapsulate(ai, &micbuf, (etherHead*) buffer, len))
+			dev_kfree_skb_irq (skb);
+		else
+			success = 1;
+	}
+
+#ifdef WIRELESS_SPY
+	if (success && (ai->spy_data.spy_number > 0)) {
+		char *sa;
+		struct iw_quality wstats;
+
+		/* Prepare spy data : addr + qual */
+		if (!test_bit(FLAG_802_11, &ai->flags)) {
+			sa = (char *) buffer + 6;
+			bap_setup(ai, fid, 8, BAP0);
+			bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0);
+		} else
+			sa = (char *) buffer + 10;
+		wstats.qual = hdr.rssi[0];
+		if (ai->rssi)
+			wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
+		else
+			wstats.level = (hdr.rssi[1] + 321) / 2;
+		wstats.noise = ai->wstats.qual.noise;
+		wstats.updated =  IW_QUAL_LEVEL_UPDATED
+				| IW_QUAL_QUAL_UPDATED
+				| IW_QUAL_DBM;
+		/* Update spy records */
+		wireless_spy_update(ai->dev, sa, &wstats);
+	}
+#endif /* WIRELESS_SPY */
+
+done:
+	OUT4500(ai, EVACK, EV_RX);
+
+	if (success) {
+		if (test_bit(FLAG_802_11, &ai->flags)) {
+			skb_reset_mac_header(skb);
+			skb->pkt_type = PACKET_OTHERHOST;
+			skb->dev = ai->wifidev;
+			skb->protocol = htons(ETH_P_802_2);
+		} else
+			skb->protocol = eth_type_trans(skb, ai->dev);
+		skb->ip_summed = CHECKSUM_NONE;
+
+		netif_rx(skb);
+	}
+}
+
+static void airo_handle_tx(struct airo_info *ai, u16 status)
+{
+	int i, len = 0, index = -1;
+	u16 fid;
+
+	if (test_bit(FLAG_MPI, &ai->flags)) {
+		unsigned long flags;
+
+		if (status & EV_TXEXC)
+			get_tx_error(ai, -1);
+
+		spin_lock_irqsave(&ai->aux_lock, flags);
+		if (!skb_queue_empty(&ai->txq)) {
+			spin_unlock_irqrestore(&ai->aux_lock,flags);
+			mpi_send_packet(ai->dev);
+		} else {
+			clear_bit(FLAG_PENDING_XMIT, &ai->flags);
+			spin_unlock_irqrestore(&ai->aux_lock,flags);
+			netif_wake_queue(ai->dev);
+		}
+		OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
+		return;
+	}
+
+	fid = IN4500(ai, TXCOMPLFID);
+
+	for(i = 0; i < MAX_FIDS; i++) {
+		if ((ai->fids[i] & 0xffff) == fid) {
+			len = ai->fids[i] >> 16;
+			index = i;
+		}
+	}
+
+	if (index != -1) {
+		if (status & EV_TXEXC)
+			get_tx_error(ai, index);
+
+		OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC));
+
+		/* Set up to be used again */
+		ai->fids[index] &= 0xffff;
+		if (index < MAX_FIDS / 2) {
+			if (!test_bit(FLAG_PENDING_XMIT, &ai->flags))
+				netif_wake_queue(ai->dev);
+		} else {
+			if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags))
+				netif_wake_queue(ai->wifidev);
+		}
+	} else {
+		OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
+		airo_print_err(ai->dev->name, "Unallocated FID was used to xmit");
+	}
+}
+
+static irqreturn_t airo_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	u16 status, savedInterrupts = 0;
+	struct airo_info *ai = dev->ml_priv;
+	int handled = 0;
+
+	if (!netif_device_present(dev))
+		return IRQ_NONE;
+
+	for (;;) {
+		status = IN4500(ai, EVSTAT);
+		if (!(status & STATUS_INTS) || (status == 0xffff))
+			break;
+
+		handled = 1;
+
+		if (status & EV_AWAKE) {
+			OUT4500(ai, EVACK, EV_AWAKE);
+			OUT4500(ai, EVACK, EV_AWAKE);
+		}
+
+		if (!savedInterrupts) {
+			savedInterrupts = IN4500(ai, EVINTEN);
+			OUT4500(ai, EVINTEN, 0);
+		}
+
+		if (status & EV_MIC) {
+			OUT4500(ai, EVACK, EV_MIC);
+			airo_handle_cisco_mic(ai);
+		}
+
+		if (status & EV_LINK) {
+			/* Link status changed */
+			airo_handle_link(ai);
+		}
+
+		/* Check to see if there is something to receive */
+		if (status & EV_RX)
+			airo_handle_rx(ai);
+
+		/* Check to see if a packet has been transmitted */
+		if (status & (EV_TX | EV_TXCPY | EV_TXEXC))
+			airo_handle_tx(ai, status);
+
+		if ( status & ~STATUS_INTS & ~IGNORE_INTS ) {
+			airo_print_warn(ai->dev->name, "Got weird status %x",
+				status & ~STATUS_INTS & ~IGNORE_INTS );
+		}
+	}
+
+	if (savedInterrupts)
+		OUT4500(ai, EVINTEN, savedInterrupts);
+
+	return IRQ_RETVAL(handled);
+}
+
+/*
+ *  Routines to talk to the card
+ */
+
+/*
+ *  This was originally written for the 4500, hence the name
+ *  NOTE:  If use with 8bit mode and SMP bad things will happen!
+ *         Why would some one do 8 bit IO in an SMP machine?!?
+ */
+static void OUT4500( struct airo_info *ai, u16 reg, u16 val ) {
+	if (test_bit(FLAG_MPI,&ai->flags))
+		reg <<= 1;
+	if ( !do8bitIO )
+		outw( val, ai->dev->base_addr + reg );
+	else {
+		outb( val & 0xff, ai->dev->base_addr + reg );
+		outb( val >> 8, ai->dev->base_addr + reg + 1 );
+	}
+}
+
+static u16 IN4500( struct airo_info *ai, u16 reg ) {
+	unsigned short rc;
+
+	if (test_bit(FLAG_MPI,&ai->flags))
+		reg <<= 1;
+	if ( !do8bitIO )
+		rc = inw( ai->dev->base_addr + reg );
+	else {
+		rc = inb( ai->dev->base_addr + reg );
+		rc += ((int)inb( ai->dev->base_addr + reg + 1 )) << 8;
+	}
+	return rc;
+}
+
+static int enable_MAC(struct airo_info *ai, int lock)
+{
+	int rc;
+	Cmd cmd;
+	Resp rsp;
+
+	/* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions
+	 * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down"
+	 * Note : we could try to use !netif_running(dev) in enable_MAC()
+	 * instead of this flag, but I don't trust it *within* the
+	 * open/close functions, and testing both flags together is
+	 * "cheaper" - Jean II */
+	if (ai->flags & FLAG_RADIO_MASK) return SUCCESS;
+
+	if (lock && down_interruptible(&ai->sem))
+		return -ERESTARTSYS;
+
+	if (!test_bit(FLAG_ENABLED, &ai->flags)) {
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.cmd = MAC_ENABLE;
+		rc = issuecommand(ai, &cmd, &rsp);
+		if (rc == SUCCESS)
+			set_bit(FLAG_ENABLED, &ai->flags);
+	} else
+		rc = SUCCESS;
+
+	if (lock)
+	    up(&ai->sem);
+
+	if (rc)
+		airo_print_err(ai->dev->name, "Cannot enable MAC");
+	else if ((rsp.status & 0xFF00) != 0) {
+		airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, "
+			"rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2);
+		rc = ERROR;
+	}
+	return rc;
+}
+
+static void disable_MAC( struct airo_info *ai, int lock ) {
+        Cmd cmd;
+	Resp rsp;
+
+	if (lock && down_interruptible(&ai->sem))
+		return;
+
+	if (test_bit(FLAG_ENABLED, &ai->flags)) {
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.cmd = MAC_DISABLE; // disable in case already enabled
+		issuecommand(ai, &cmd, &rsp);
+		clear_bit(FLAG_ENABLED, &ai->flags);
+	}
+	if (lock)
+		up(&ai->sem);
+}
+
+static void enable_interrupts( struct airo_info *ai ) {
+	/* Enable the interrupts */
+	OUT4500( ai, EVINTEN, STATUS_INTS );
+}
+
+static void disable_interrupts( struct airo_info *ai ) {
+	OUT4500( ai, EVINTEN, 0 );
+}
+
+static void mpi_receive_802_3(struct airo_info *ai)
+{
+	RxFid rxd;
+	int len = 0;
+	struct sk_buff *skb;
+	char *buffer;
+	int off = 0;
+	MICBuffer micbuf;
+
+	memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
+	/* Make sure we got something */
+	if (rxd.rdy && rxd.valid == 0) {
+		len = rxd.len + 12;
+		if (len < 12 || len > 2048)
+			goto badrx;
+
+		skb = dev_alloc_skb(len);
+		if (!skb) {
+			ai->dev->stats.rx_dropped++;
+			goto badrx;
+		}
+		buffer = skb_put(skb,len);
+		memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2);
+		if (ai->micstats.enabled) {
+			memcpy(&micbuf,
+				ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2,
+				sizeof(micbuf));
+			if (ntohs(micbuf.typelen) <= 0x05DC) {
+				if (len <= sizeof(micbuf) + ETH_ALEN * 2)
+					goto badmic;
+
+				off = sizeof(micbuf);
+				skb_trim (skb, len - off);
+			}
+		}
+		memcpy(buffer + ETH_ALEN * 2,
+			ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off,
+			len - ETH_ALEN * 2 - off);
+		if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) {
+badmic:
+			dev_kfree_skb_irq (skb);
+			goto badrx;
+		}
+#ifdef WIRELESS_SPY
+		if (ai->spy_data.spy_number > 0) {
+			char *sa;
+			struct iw_quality wstats;
+			/* Prepare spy data : addr + qual */
+			sa = buffer + ETH_ALEN;
+			wstats.qual = 0; /* XXX Where do I get that info from ??? */
+			wstats.level = 0;
+			wstats.updated = 0;
+			/* Update spy records */
+			wireless_spy_update(ai->dev, sa, &wstats);
+		}
+#endif /* WIRELESS_SPY */
+
+		skb->ip_summed = CHECKSUM_NONE;
+		skb->protocol = eth_type_trans(skb, ai->dev);
+		netif_rx(skb);
+	}
+badrx:
+	if (rxd.valid == 0) {
+		rxd.valid = 1;
+		rxd.rdy = 0;
+		rxd.len = PKTSIZE;
+		memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
+	}
+}
+
+static void mpi_receive_802_11(struct airo_info *ai)
+{
+	RxFid rxd;
+	struct sk_buff *skb = NULL;
+	u16 len, hdrlen = 0;
+	__le16 fc;
+	struct rx_hdr hdr;
+	u16 gap;
+	u16 *buffer;
+	char *ptr = ai->rxfids[0].virtual_host_addr + 4;
+
+	memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
+	memcpy ((char *)&hdr, ptr, sizeof(hdr));
+	ptr += sizeof(hdr);
+	/* Bad CRC. Ignore packet */
+	if (le16_to_cpu(hdr.status) & 2)
+		hdr.len = 0;
+	if (ai->wifidev == NULL)
+		hdr.len = 0;
+	len = le16_to_cpu(hdr.len);
+	if (len > AIRO_DEF_MTU) {
+		airo_print_err(ai->dev->name, "Bad size %d", len);
+		goto badrx;
+	}
+	if (len == 0)
+		goto badrx;
+
+	fc = get_unaligned((__le16 *)ptr);
+	hdrlen = header_len(fc);
+
+	skb = dev_alloc_skb( len + hdrlen + 2 );
+	if ( !skb ) {
+		ai->dev->stats.rx_dropped++;
+		goto badrx;
+	}
+	buffer = (u16*)skb_put (skb, len + hdrlen);
+	memcpy ((char *)buffer, ptr, hdrlen);
+	ptr += hdrlen;
+	if (hdrlen == 24)
+		ptr += 6;
+	gap = get_unaligned_le16(ptr);
+	ptr += sizeof(__le16);
+	if (gap) {
+		if (gap <= 8)
+			ptr += gap;
+		else
+			airo_print_err(ai->dev->name,
+			    "gaplen too big. Problems will follow...");
+	}
+	memcpy ((char *)buffer + hdrlen, ptr, len);
+	ptr += len;
+#ifdef IW_WIRELESS_SPY	  /* defined in iw_handler.h */
+	if (ai->spy_data.spy_number > 0) {
+		char *sa;
+		struct iw_quality wstats;
+		/* Prepare spy data : addr + qual */
+		sa = (char*)buffer + 10;
+		wstats.qual = hdr.rssi[0];
+		if (ai->rssi)
+			wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
+		else
+			wstats.level = (hdr.rssi[1] + 321) / 2;
+		wstats.noise = ai->wstats.qual.noise;
+		wstats.updated = IW_QUAL_QUAL_UPDATED
+			| IW_QUAL_LEVEL_UPDATED
+			| IW_QUAL_DBM;
+		/* Update spy records */
+		wireless_spy_update(ai->dev, sa, &wstats);
+	}
+#endif /* IW_WIRELESS_SPY */
+	skb_reset_mac_header(skb);
+	skb->pkt_type = PACKET_OTHERHOST;
+	skb->dev = ai->wifidev;
+	skb->protocol = htons(ETH_P_802_2);
+	skb->ip_summed = CHECKSUM_NONE;
+	netif_rx( skb );
+
+badrx:
+	if (rxd.valid == 0) {
+		rxd.valid = 1;
+		rxd.rdy = 0;
+		rxd.len = PKTSIZE;
+		memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
+	}
+}
+
+static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
+{
+	Cmd cmd;
+	Resp rsp;
+	int status;
+	SsidRid mySsid;
+	__le16 lastindex;
+	WepKeyRid wkr;
+	int rc;
+
+	memset( &mySsid, 0, sizeof( mySsid ) );
+	kfree (ai->flash);
+	ai->flash = NULL;
+
+	/* The NOP is the first step in getting the card going */
+	cmd.cmd = NOP;
+	cmd.parm0 = cmd.parm1 = cmd.parm2 = 0;
+	if (lock && down_interruptible(&ai->sem))
+		return ERROR;
+	if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) {
+		if (lock)
+			up(&ai->sem);
+		return ERROR;
+	}
+	disable_MAC( ai, 0);
+
+	// Let's figure out if we need to use the AUX port
+	if (!test_bit(FLAG_MPI,&ai->flags)) {
+		cmd.cmd = CMD_ENABLEAUX;
+		if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
+			if (lock)
+				up(&ai->sem);
+			airo_print_err(ai->dev->name, "Error checking for AUX port");
+			return ERROR;
+		}
+		if (!aux_bap || rsp.status & 0xff00) {
+			ai->bap_read = fast_bap_read;
+			airo_print_dbg(ai->dev->name, "Doing fast bap_reads");
+		} else {
+			ai->bap_read = aux_bap_read;
+			airo_print_dbg(ai->dev->name, "Doing AUX bap_reads");
+		}
+	}
+	if (lock)
+		up(&ai->sem);
+	if (ai->config.len == 0) {
+		int i;
+		tdsRssiRid rssi_rid;
+		CapabilityRid cap_rid;
+
+		kfree(ai->APList);
+		ai->APList = NULL;
+		kfree(ai->SSID);
+		ai->SSID = NULL;
+		// general configuration (read/modify/write)
+		status = readConfigRid(ai, lock);
+		if ( status != SUCCESS ) return ERROR;
+
+		status = readCapabilityRid(ai, &cap_rid, lock);
+		if ( status != SUCCESS ) return ERROR;
+
+		status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),lock);
+		if ( status == SUCCESS ) {
+			if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
+				memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */
+		}
+		else {
+			kfree(ai->rssi);
+			ai->rssi = NULL;
+			if (cap_rid.softCap & cpu_to_le16(8))
+				ai->config.rmode |= RXMODE_NORMALIZED_RSSI;
+			else
+				airo_print_warn(ai->dev->name, "unknown received signal "
+						"level scale");
+		}
+		ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
+		ai->config.authType = AUTH_OPEN;
+		ai->config.modulation = MOD_CCK;
+
+		if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) &&
+		    (cap_rid.extSoftCap & cpu_to_le16(1)) &&
+		    micsetup(ai) == SUCCESS) {
+			ai->config.opmode |= MODE_MIC;
+			set_bit(FLAG_MIC_CAPABLE, &ai->flags);
+		}
+
+		/* Save off the MAC */
+		for( i = 0; i < ETH_ALEN; i++ ) {
+			mac[i] = ai->config.macAddr[i];
+		}
+
+		/* Check to see if there are any insmod configured
+		   rates to add */
+		if ( rates[0] ) {
+			memset(ai->config.rates,0,sizeof(ai->config.rates));
+			for( i = 0; i < 8 && rates[i]; i++ ) {
+				ai->config.rates[i] = rates[i];
+			}
+		}
+		set_bit (FLAG_COMMIT, &ai->flags);
+	}
+
+	/* Setup the SSIDs if present */
+	if ( ssids[0] ) {
+		int i;
+		for( i = 0; i < 3 && ssids[i]; i++ ) {
+			size_t len = strlen(ssids[i]);
+			if (len > 32)
+				len = 32;
+			mySsid.ssids[i].len = cpu_to_le16(len);
+			memcpy(mySsid.ssids[i].ssid, ssids[i], len);
+		}
+		mySsid.len = cpu_to_le16(sizeof(mySsid));
+	}
+
+	status = writeConfigRid(ai, lock);
+	if ( status != SUCCESS ) return ERROR;
+
+	/* Set up the SSID list */
+	if ( ssids[0] ) {
+		status = writeSsidRid(ai, &mySsid, lock);
+		if ( status != SUCCESS ) return ERROR;
+	}
+
+	status = enable_MAC(ai, lock);
+	if (status != SUCCESS)
+		return ERROR;
+
+	/* Grab the initial wep key, we gotta save it for auto_wep */
+	rc = readWepKeyRid(ai, &wkr, 1, lock);
+	if (rc == SUCCESS) do {
+		lastindex = wkr.kindex;
+		if (wkr.kindex == cpu_to_le16(0xffff)) {
+			ai->defindex = wkr.mac[0];
+		}
+		rc = readWepKeyRid(ai, &wkr, 0, lock);
+	} while(lastindex != wkr.kindex);
+
+	try_auto_wep(ai);
+
+	return SUCCESS;
+}
+
+static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) {
+        // Im really paranoid about letting it run forever!
+	int max_tries = 600000;
+
+	if (IN4500(ai, EVSTAT) & EV_CMD)
+		OUT4500(ai, EVACK, EV_CMD);
+
+	OUT4500(ai, PARAM0, pCmd->parm0);
+	OUT4500(ai, PARAM1, pCmd->parm1);
+	OUT4500(ai, PARAM2, pCmd->parm2);
+	OUT4500(ai, COMMAND, pCmd->cmd);
+
+	while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) {
+		if ((IN4500(ai, COMMAND)) == pCmd->cmd)
+			// PC4500 didn't notice command, try again
+			OUT4500(ai, COMMAND, pCmd->cmd);
+		if (!in_atomic() && (max_tries & 255) == 0)
+			schedule();
+	}
+
+	if ( max_tries == -1 ) {
+		airo_print_err(ai->dev->name,
+			"Max tries exceeded when issuing command");
+		if (IN4500(ai, COMMAND) & COMMAND_BUSY)
+			OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
+		return ERROR;
+	}
+
+	// command completed
+	pRsp->status = IN4500(ai, STATUS);
+	pRsp->rsp0 = IN4500(ai, RESP0);
+	pRsp->rsp1 = IN4500(ai, RESP1);
+	pRsp->rsp2 = IN4500(ai, RESP2);
+	if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET)
+		airo_print_err(ai->dev->name,
+			"cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x",
+			pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1,
+			pRsp->rsp2);
+
+	// clear stuck command busy if necessary
+	if (IN4500(ai, COMMAND) & COMMAND_BUSY) {
+		OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
+	}
+	// acknowledge processing the status/response
+	OUT4500(ai, EVACK, EV_CMD);
+
+	return SUCCESS;
+}
+
+/* Sets up the bap to start exchange data.  whichbap should
+ * be one of the BAP0 or BAP1 defines.  Locks should be held before
+ * calling! */
+static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap )
+{
+	int timeout = 50;
+	int max_tries = 3;
+
+	OUT4500(ai, SELECT0+whichbap, rid);
+	OUT4500(ai, OFFSET0+whichbap, offset);
+	while (1) {
+		int status = IN4500(ai, OFFSET0+whichbap);
+		if (status & BAP_BUSY) {
+                        /* This isn't really a timeout, but its kinda
+			   close */
+			if (timeout--) {
+				continue;
+			}
+		} else if ( status & BAP_ERR ) {
+			/* invalid rid or offset */
+			airo_print_err(ai->dev->name, "BAP error %x %d",
+				status, whichbap );
+			return ERROR;
+		} else if (status & BAP_DONE) { // success
+			return SUCCESS;
+		}
+		if ( !(max_tries--) ) {
+			airo_print_err(ai->dev->name,
+				"BAP setup error too many retries\n");
+			return ERROR;
+		}
+		// -- PC4500 missed it, try again
+		OUT4500(ai, SELECT0+whichbap, rid);
+		OUT4500(ai, OFFSET0+whichbap, offset);
+		timeout = 50;
+	}
+}
+
+/* should only be called by aux_bap_read.  This aux function and the
+   following use concepts not documented in the developers guide.  I
+   got them from a patch given to my by Aironet */
+static u16 aux_setup(struct airo_info *ai, u16 page,
+		     u16 offset, u16 *len)
+{
+	u16 next;
+
+	OUT4500(ai, AUXPAGE, page);
+	OUT4500(ai, AUXOFF, 0);
+	next = IN4500(ai, AUXDATA);
+	*len = IN4500(ai, AUXDATA)&0xff;
+	if (offset != 4) OUT4500(ai, AUXOFF, offset);
+	return next;
+}
+
+/* requires call to bap_setup() first */
+static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst,
+			int bytelen, int whichbap)
+{
+	u16 len;
+	u16 page;
+	u16 offset;
+	u16 next;
+	int words;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ai->aux_lock, flags);
+	page = IN4500(ai, SWS0+whichbap);
+	offset = IN4500(ai, SWS2+whichbap);
+	next = aux_setup(ai, page, offset, &len);
+	words = (bytelen+1)>>1;
+
+	for (i=0; i<words;) {
+		int count;
+		count = (len>>1) < (words-i) ? (len>>1) : (words-i);
+		if ( !do8bitIO )
+			insw( ai->dev->base_addr+DATA0+whichbap,
+			      pu16Dst+i,count );
+		else
+			insb( ai->dev->base_addr+DATA0+whichbap,
+			      pu16Dst+i, count << 1 );
+		i += count;
+		if (i<words) {
+			next = aux_setup(ai, next, 4, &len);
+		}
+	}
+	spin_unlock_irqrestore(&ai->aux_lock, flags);
+	return SUCCESS;
+}
+
+
+/* requires call to bap_setup() first */
+static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst,
+			 int bytelen, int whichbap)
+{
+	bytelen = (bytelen + 1) & (~1); // round up to even value
+	if ( !do8bitIO )
+		insw( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1 );
+	else
+		insb( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen );
+	return SUCCESS;
+}
+
+/* requires call to bap_setup() first */
+static int bap_write(struct airo_info *ai, const __le16 *pu16Src,
+		     int bytelen, int whichbap)
+{
+	bytelen = (bytelen + 1) & (~1); // round up to even value
+	if ( !do8bitIO )
+		outsw( ai->dev->base_addr+DATA0+whichbap,
+		       pu16Src, bytelen>>1 );
+	else
+		outsb( ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen );
+	return SUCCESS;
+}
+
+static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd)
+{
+	Cmd cmd; /* for issuing commands */
+	Resp rsp; /* response from commands */
+	u16 status;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd = accmd;
+	cmd.parm0 = rid;
+	status = issuecommand(ai, &cmd, &rsp);
+	if (status != 0) return status;
+	if ( (rsp.status & 0x7F00) != 0) {
+		return (accmd << 8) + (rsp.rsp0 & 0xFF);
+	}
+	return 0;
+}
+
+/*  Note, that we are using BAP1 which is also used by transmit, so
+ *  we must get a lock. */
+static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock)
+{
+	u16 status;
+        int rc = SUCCESS;
+
+	if (lock) {
+		if (down_interruptible(&ai->sem))
+			return ERROR;
+	}
+	if (test_bit(FLAG_MPI,&ai->flags)) {
+		Cmd cmd;
+		Resp rsp;
+
+		memset(&cmd, 0, sizeof(cmd));
+		memset(&rsp, 0, sizeof(rsp));
+		ai->config_desc.rid_desc.valid = 1;
+		ai->config_desc.rid_desc.len = RIDSIZE;
+		ai->config_desc.rid_desc.rid = 0;
+		ai->config_desc.rid_desc.host_addr = ai->ridbus;
+
+		cmd.cmd = CMD_ACCESS;
+		cmd.parm0 = rid;
+
+		memcpy_toio(ai->config_desc.card_ram_off,
+			&ai->config_desc.rid_desc, sizeof(Rid));
+
+		rc = issuecommand(ai, &cmd, &rsp);
+
+		if (rsp.status & 0x7f00)
+			rc = rsp.rsp0;
+		if (!rc)
+			memcpy(pBuf, ai->config_desc.virtual_host_addr, len);
+		goto done;
+	} else {
+		if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) {
+	                rc = status;
+	                goto done;
+	        }
+		if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
+			rc = ERROR;
+	                goto done;
+	        }
+		// read the rid length field
+		bap_read(ai, pBuf, 2, BAP1);
+		// length for remaining part of rid
+		len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2;
+
+		if ( len <= 2 ) {
+			airo_print_err(ai->dev->name,
+				"Rid %x has a length of %d which is too short",
+				(int)rid, (int)len );
+			rc = ERROR;
+	                goto done;
+		}
+		// read remainder of the rid
+		rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1);
+	}
+done:
+	if (lock)
+		up(&ai->sem);
+	return rc;
+}
+
+/*  Note, that we are using BAP1 which is also used by transmit, so
+ *  make sure this isn't called when a transmit is happening */
+static int PC4500_writerid(struct airo_info *ai, u16 rid,
+			   const void *pBuf, int len, int lock)
+{
+	u16 status;
+	int rc = SUCCESS;
+
+	*(__le16*)pBuf = cpu_to_le16((u16)len);
+
+	if (lock) {
+		if (down_interruptible(&ai->sem))
+			return ERROR;
+	}
+	if (test_bit(FLAG_MPI,&ai->flags)) {
+		Cmd cmd;
+		Resp rsp;
+
+		if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid))
+			airo_print_err(ai->dev->name,
+				"%s: MAC should be disabled (rid=%04x)",
+				__func__, rid);
+		memset(&cmd, 0, sizeof(cmd));
+		memset(&rsp, 0, sizeof(rsp));
+
+		ai->config_desc.rid_desc.valid = 1;
+		ai->config_desc.rid_desc.len = *((u16 *)pBuf);
+		ai->config_desc.rid_desc.rid = 0;
+
+		cmd.cmd = CMD_WRITERID;
+		cmd.parm0 = rid;
+
+		memcpy_toio(ai->config_desc.card_ram_off,
+			&ai->config_desc.rid_desc, sizeof(Rid));
+
+		if (len < 4 || len > 2047) {
+			airo_print_err(ai->dev->name, "%s: len=%d", __func__, len);
+			rc = -1;
+		} else {
+			memcpy(ai->config_desc.virtual_host_addr,
+				pBuf, len);
+
+			rc = issuecommand(ai, &cmd, &rsp);
+			if ((rc & 0xff00) != 0) {
+				airo_print_err(ai->dev->name, "%s: Write rid Error %d",
+						__func__, rc);
+				airo_print_err(ai->dev->name, "%s: Cmd=%04x",
+						__func__, cmd.cmd);
+			}
+
+			if ((rsp.status & 0x7f00))
+				rc = rsp.rsp0;
+		}
+	} else {
+		// --- first access so that we can write the rid data
+		if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) {
+	                rc = status;
+	                goto done;
+	        }
+		// --- now write the rid data
+		if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
+	                rc = ERROR;
+	                goto done;
+	        }
+		bap_write(ai, pBuf, len, BAP1);
+		// ---now commit the rid data
+		rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS);
+	}
+done:
+	if (lock)
+		up(&ai->sem);
+        return rc;
+}
+
+/* Allocates a FID to be used for transmitting packets.  We only use
+   one for now. */
+static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw)
+{
+	unsigned int loop = 3000;
+	Cmd cmd;
+	Resp rsp;
+	u16 txFid;
+	__le16 txControl;
+
+	cmd.cmd = CMD_ALLOCATETX;
+	cmd.parm0 = lenPayload;
+	if (down_interruptible(&ai->sem))
+		return ERROR;
+	if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
+		txFid = ERROR;
+		goto done;
+	}
+	if ( (rsp.status & 0xFF00) != 0) {
+		txFid = ERROR;
+		goto done;
+	}
+	/* wait for the allocate event/indication
+	 * It makes me kind of nervous that this can just sit here and spin,
+	 * but in practice it only loops like four times. */
+	while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop);
+	if (!loop) {
+		txFid = ERROR;
+		goto done;
+	}
+
+	// get the allocated fid and acknowledge
+	txFid = IN4500(ai, TXALLOCFID);
+	OUT4500(ai, EVACK, EV_ALLOC);
+
+	/*  The CARD is pretty cool since it converts the ethernet packet
+	 *  into 802.11.  Also note that we don't release the FID since we
+	 *  will be using the same one over and over again. */
+	/*  We only have to setup the control once since we are not
+	 *  releasing the fid. */
+	if (raw)
+		txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11
+			| TXCTL_ETHERNET | TXCTL_NORELEASE);
+	else
+		txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3
+			| TXCTL_ETHERNET | TXCTL_NORELEASE);
+	if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS)
+		txFid = ERROR;
+	else
+		bap_write(ai, &txControl, sizeof(txControl), BAP1);
+
+done:
+	up(&ai->sem);
+
+	return txFid;
+}
+
+/* In general BAP1 is dedicated to transmiting packets.  However,
+   since we need a BAP when accessing RIDs, we also use BAP1 for that.
+   Make sure the BAP1 spinlock is held when this is called. */
+static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket)
+{
+	__le16 payloadLen;
+	Cmd cmd;
+	Resp rsp;
+	int miclen = 0;
+	u16 txFid = len;
+	MICBuffer pMic;
+
+	len >>= 16;
+
+	if (len <= ETH_ALEN * 2) {
+		airo_print_warn(ai->dev->name, "Short packet %d", len);
+		return ERROR;
+	}
+	len -= ETH_ALEN * 2;
+
+	if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled && 
+	    (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) {
+		if (encapsulate(ai,(etherHead *)pPacket,&pMic,len) != SUCCESS)
+			return ERROR;
+		miclen = sizeof(pMic);
+	}
+	// packet is destination[6], source[6], payload[len-12]
+	// write the payload length and dst/src/payload
+	if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR;
+	/* The hardware addresses aren't counted as part of the payload, so
+	 * we have to subtract the 12 bytes for the addresses off */
+	payloadLen = cpu_to_le16(len + miclen);
+	bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
+	bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1);
+	if (miclen)
+		bap_write(ai, (__le16*)&pMic, miclen, BAP1);
+	bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1);
+	// issue the transmit command
+	memset( &cmd, 0, sizeof( cmd ) );
+	cmd.cmd = CMD_TRANSMIT;
+	cmd.parm0 = txFid;
+	if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
+	if ( (rsp.status & 0xFF00) != 0) return ERROR;
+	return SUCCESS;
+}
+
+static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket)
+{
+	__le16 fc, payloadLen;
+	Cmd cmd;
+	Resp rsp;
+	int hdrlen;
+	static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6};
+	/* padding of header to full size + le16 gaplen (6) + gaplen bytes */
+	u16 txFid = len;
+	len >>= 16;
+
+	fc = *(__le16*)pPacket;
+	hdrlen = header_len(fc);
+
+	if (len < hdrlen) {
+		airo_print_warn(ai->dev->name, "Short packet %d", len);
+		return ERROR;
+	}
+
+	/* packet is 802.11 header +  payload
+	 * write the payload length and dst/src/payload */
+	if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR;
+	/* The 802.11 header aren't counted as part of the payload, so
+	 * we have to subtract the header bytes off */
+	payloadLen = cpu_to_le16(len-hdrlen);
+	bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
+	if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR;
+	bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1);
+	bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1);
+
+	bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1);
+	// issue the transmit command
+	memset( &cmd, 0, sizeof( cmd ) );
+	cmd.cmd = CMD_TRANSMIT;
+	cmd.parm0 = txFid;
+	if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
+	if ( (rsp.status & 0xFF00) != 0) return ERROR;
+	return SUCCESS;
+}
+
+/*
+ *  This is the proc_fs routines.  It is a bit messier than I would
+ *  like!  Feel free to clean it up!
+ */
+
+static ssize_t proc_read( struct file *file,
+			  char __user *buffer,
+			  size_t len,
+			  loff_t *offset);
+
+static ssize_t proc_write( struct file *file,
+			   const char __user *buffer,
+			   size_t len,
+			   loff_t *offset );
+static int proc_close( struct inode *inode, struct file *file );
+
+static int proc_stats_open( struct inode *inode, struct file *file );
+static int proc_statsdelta_open( struct inode *inode, struct file *file );
+static int proc_status_open( struct inode *inode, struct file *file );
+static int proc_SSID_open( struct inode *inode, struct file *file );
+static int proc_APList_open( struct inode *inode, struct file *file );
+static int proc_BSSList_open( struct inode *inode, struct file *file );
+static int proc_config_open( struct inode *inode, struct file *file );
+static int proc_wepkey_open( struct inode *inode, struct file *file );
+
+static const struct file_operations proc_statsdelta_ops = {
+	.owner		= THIS_MODULE,
+	.read		= proc_read,
+	.open		= proc_statsdelta_open,
+	.release	= proc_close,
+	.llseek		= default_llseek,
+};
+
+static const struct file_operations proc_stats_ops = {
+	.owner		= THIS_MODULE,
+	.read		= proc_read,
+	.open		= proc_stats_open,
+	.release	= proc_close,
+	.llseek		= default_llseek,
+};
+
+static const struct file_operations proc_status_ops = {
+	.owner		= THIS_MODULE,
+	.read		= proc_read,
+	.open		= proc_status_open,
+	.release	= proc_close,
+	.llseek		= default_llseek,
+};
+
+static const struct file_operations proc_SSID_ops = {
+	.owner		= THIS_MODULE,
+	.read		= proc_read,
+	.write		= proc_write,
+	.open		= proc_SSID_open,
+	.release	= proc_close,
+	.llseek		= default_llseek,
+};
+
+static const struct file_operations proc_BSSList_ops = {
+	.owner		= THIS_MODULE,
+	.read		= proc_read,
+	.write		= proc_write,
+	.open		= proc_BSSList_open,
+	.release	= proc_close,
+	.llseek		= default_llseek,
+};
+
+static const struct file_operations proc_APList_ops = {
+	.owner		= THIS_MODULE,
+	.read		= proc_read,
+	.write		= proc_write,
+	.open		= proc_APList_open,
+	.release	= proc_close,
+	.llseek		= default_llseek,
+};
+
+static const struct file_operations proc_config_ops = {
+	.owner		= THIS_MODULE,
+	.read		= proc_read,
+	.write		= proc_write,
+	.open		= proc_config_open,
+	.release	= proc_close,
+	.llseek		= default_llseek,
+};
+
+static const struct file_operations proc_wepkey_ops = {
+	.owner		= THIS_MODULE,
+	.read		= proc_read,
+	.write		= proc_write,
+	.open		= proc_wepkey_open,
+	.release	= proc_close,
+	.llseek		= default_llseek,
+};
+
+static struct proc_dir_entry *airo_entry;
+
+struct proc_data {
+	int release_buffer;
+	int readlen;
+	char *rbuffer;
+	int writelen;
+	int maxwritelen;
+	char *wbuffer;
+	void (*on_close) (struct inode *, struct file *);
+};
+
+static int setup_proc_entry( struct net_device *dev,
+			     struct airo_info *apriv ) {
+	struct proc_dir_entry *entry;
+
+	/* First setup the device directory */
+	strcpy(apriv->proc_name,dev->name);
+	apriv->proc_entry = proc_mkdir_mode(apriv->proc_name, airo_perm,
+					    airo_entry);
+	if (!apriv->proc_entry)
+		return -ENOMEM;
+	proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid);
+
+	/* Setup the StatsDelta */
+	entry = proc_create_data("StatsDelta", S_IRUGO & proc_perm,
+				 apriv->proc_entry, &proc_statsdelta_ops, dev);
+	if (!entry)
+		goto fail;
+	proc_set_user(entry, proc_kuid, proc_kgid);
+
+	/* Setup the Stats */
+	entry = proc_create_data("Stats", S_IRUGO & proc_perm,
+				 apriv->proc_entry, &proc_stats_ops, dev);
+	if (!entry)
+		goto fail;
+	proc_set_user(entry, proc_kuid, proc_kgid);
+
+	/* Setup the Status */
+	entry = proc_create_data("Status", S_IRUGO & proc_perm,
+				 apriv->proc_entry, &proc_status_ops, dev);
+	if (!entry)
+		goto fail;
+	proc_set_user(entry, proc_kuid, proc_kgid);
+
+	/* Setup the Config */
+	entry = proc_create_data("Config", proc_perm,
+				 apriv->proc_entry, &proc_config_ops, dev);
+	if (!entry)
+		goto fail;
+	proc_set_user(entry, proc_kuid, proc_kgid);
+
+	/* Setup the SSID */
+	entry = proc_create_data("SSID", proc_perm,
+				 apriv->proc_entry, &proc_SSID_ops, dev);
+	if (!entry)
+		goto fail;
+	proc_set_user(entry, proc_kuid, proc_kgid);
+
+	/* Setup the APList */
+	entry = proc_create_data("APList", proc_perm,
+				 apriv->proc_entry, &proc_APList_ops, dev);
+	if (!entry)
+		goto fail;
+	proc_set_user(entry, proc_kuid, proc_kgid);
+
+	/* Setup the BSSList */
+	entry = proc_create_data("BSSList", proc_perm,
+				 apriv->proc_entry, &proc_BSSList_ops, dev);
+	if (!entry)
+		goto fail;
+	proc_set_user(entry, proc_kuid, proc_kgid);
+
+	/* Setup the WepKey */
+	entry = proc_create_data("WepKey", proc_perm,
+				 apriv->proc_entry, &proc_wepkey_ops, dev);
+	if (!entry)
+		goto fail;
+	proc_set_user(entry, proc_kuid, proc_kgid);
+	return 0;
+
+fail:
+	remove_proc_subtree(apriv->proc_name, airo_entry);
+	return -ENOMEM;
+}
+
+static int takedown_proc_entry( struct net_device *dev,
+				struct airo_info *apriv )
+{
+	remove_proc_subtree(apriv->proc_name, airo_entry);
+	return 0;
+}
+
+/*
+ *  What we want from the proc_fs is to be able to efficiently read
+ *  and write the configuration.  To do this, we want to read the
+ *  configuration when the file is opened and write it when the file is
+ *  closed.  So basically we allocate a read buffer at open and fill it
+ *  with data, and allocate a write buffer and read it at close.
+ */
+
+/*
+ *  The read routine is generic, it relies on the preallocated rbuffer
+ *  to supply the data.
+ */
+static ssize_t proc_read( struct file *file,
+			  char __user *buffer,
+			  size_t len,
+			  loff_t *offset )
+{
+	struct proc_data *priv = file->private_data;
+
+	if (!priv->rbuffer)
+		return -EINVAL;
+
+	return simple_read_from_buffer(buffer, len, offset, priv->rbuffer,
+					priv->readlen);
+}
+
+/*
+ *  The write routine is generic, it fills in a preallocated rbuffer
+ *  to supply the data.
+ */
+static ssize_t proc_write( struct file *file,
+			   const char __user *buffer,
+			   size_t len,
+			   loff_t *offset )
+{
+	ssize_t ret;
+	struct proc_data *priv = file->private_data;
+
+	if (!priv->wbuffer)
+		return -EINVAL;
+
+	ret = simple_write_to_buffer(priv->wbuffer, priv->maxwritelen, offset,
+					buffer, len);
+	if (ret > 0)
+		priv->writelen = max_t(int, priv->writelen, *offset);
+
+	return ret;
+}
+
+static int proc_status_open(struct inode *inode, struct file *file)
+{
+	struct proc_data *data;
+	struct net_device *dev = PDE_DATA(inode);
+	struct airo_info *apriv = dev->ml_priv;
+	CapabilityRid cap_rid;
+	StatusRid status_rid;
+	u16 mode;
+	int i;
+
+	if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	data = file->private_data;
+	if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
+		kfree (file->private_data);
+		return -ENOMEM;
+	}
+
+	readStatusRid(apriv, &status_rid, 1);
+	readCapabilityRid(apriv, &cap_rid, 1);
+
+	mode = le16_to_cpu(status_rid.mode);
+
+        i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n",
+                    mode & 1 ? "CFG ": "",
+                    mode & 2 ? "ACT ": "",
+                    mode & 0x10 ? "SYN ": "",
+                    mode & 0x20 ? "LNK ": "",
+                    mode & 0x40 ? "LEAP ": "",
+                    mode & 0x80 ? "PRIV ": "",
+                    mode & 0x100 ? "KEY ": "",
+                    mode & 0x200 ? "WEP ": "",
+                    mode & 0x8000 ? "ERR ": "");
+	sprintf( data->rbuffer+i, "Mode: %x\n"
+		 "Signal Strength: %d\n"
+		 "Signal Quality: %d\n"
+		 "SSID: %-.*s\n"
+		 "AP: %-.16s\n"
+		 "Freq: %d\n"
+		 "BitRate: %dmbs\n"
+		 "Driver Version: %s\n"
+		 "Device: %s\nManufacturer: %s\nFirmware Version: %s\n"
+		 "Radio type: %x\nCountry: %x\nHardware Version: %x\n"
+		 "Software Version: %x\nSoftware Subversion: %x\n"
+		 "Boot block version: %x\n",
+		 le16_to_cpu(status_rid.mode),
+		 le16_to_cpu(status_rid.normalizedSignalStrength),
+		 le16_to_cpu(status_rid.signalQuality),
+		 le16_to_cpu(status_rid.SSIDlen),
+		 status_rid.SSID,
+		 status_rid.apName,
+		 le16_to_cpu(status_rid.channel),
+		 le16_to_cpu(status_rid.currentXmitRate) / 2,
+		 version,
+		 cap_rid.prodName,
+		 cap_rid.manName,
+		 cap_rid.prodVer,
+		 le16_to_cpu(cap_rid.radioType),
+		 le16_to_cpu(cap_rid.country),
+		 le16_to_cpu(cap_rid.hardVer),
+		 le16_to_cpu(cap_rid.softVer),
+		 le16_to_cpu(cap_rid.softSubVer),
+		 le16_to_cpu(cap_rid.bootBlockVer));
+	data->readlen = strlen( data->rbuffer );
+	return 0;
+}
+
+static int proc_stats_rid_open(struct inode*, struct file*, u16);
+static int proc_statsdelta_open( struct inode *inode,
+				 struct file *file ) {
+	if (file->f_mode&FMODE_WRITE) {
+		return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR);
+	}
+	return proc_stats_rid_open(inode, file, RID_STATSDELTA);
+}
+
+static int proc_stats_open( struct inode *inode, struct file *file ) {
+	return proc_stats_rid_open(inode, file, RID_STATS);
+}
+
+static int proc_stats_rid_open( struct inode *inode,
+				struct file *file,
+				u16 rid )
+{
+	struct proc_data *data;
+	struct net_device *dev = PDE_DATA(inode);
+	struct airo_info *apriv = dev->ml_priv;
+	StatsRid stats;
+	int i, j;
+	__le32 *vals = stats.vals;
+	int len;
+
+	if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	data = file->private_data;
+	if ((data->rbuffer = kmalloc( 4096, GFP_KERNEL )) == NULL) {
+		kfree (file->private_data);
+		return -ENOMEM;
+	}
+
+	readStatsRid(apriv, &stats, rid, 1);
+	len = le16_to_cpu(stats.len);
+
+        j = 0;
+	for(i=0; statsLabels[i]!=(char *)-1 && i*4<len; i++) {
+		if (!statsLabels[i]) continue;
+		if (j+strlen(statsLabels[i])+16>4096) {
+			airo_print_warn(apriv->dev->name,
+			       "Potentially disastrous buffer overflow averted!");
+			break;
+		}
+		j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i],
+				le32_to_cpu(vals[i]));
+	}
+	if (i*4 >= len) {
+		airo_print_warn(apriv->dev->name, "Got a short rid");
+	}
+	data->readlen = j;
+	return 0;
+}
+
+static int get_dec_u16( char *buffer, int *start, int limit ) {
+	u16 value;
+	int valid = 0;
+	for (value = 0; *start < limit && buffer[*start] >= '0' &&
+			buffer[*start] <= '9'; (*start)++) {
+		valid = 1;
+		value *= 10;
+		value += buffer[*start] - '0';
+	}
+	if ( !valid ) return -1;
+	return value;
+}
+
+static int airo_config_commit(struct net_device *dev,
+			      struct iw_request_info *info, void *zwrq,
+			      char *extra);
+
+static inline int sniffing_mode(struct airo_info *ai)
+{
+	return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >=
+		le16_to_cpu(RXMODE_RFMON);
+}
+
+static void proc_config_on_close(struct inode *inode, struct file *file)
+{
+	struct proc_data *data = file->private_data;
+	struct net_device *dev = PDE_DATA(inode);
+	struct airo_info *ai = dev->ml_priv;
+	char *line;
+
+	if ( !data->writelen ) return;
+
+	readConfigRid(ai, 1);
+	set_bit (FLAG_COMMIT, &ai->flags);
+
+	line = data->wbuffer;
+	while( line[0] ) {
+/*** Mode processing */
+		if ( !strncmp( line, "Mode: ", 6 ) ) {
+			line += 6;
+			if (sniffing_mode(ai))
+				set_bit (FLAG_RESET, &ai->flags);
+			ai->config.rmode &= ~RXMODE_FULL_MASK;
+			clear_bit (FLAG_802_11, &ai->flags);
+			ai->config.opmode &= ~MODE_CFG_MASK;
+			ai->config.scanMode = SCANMODE_ACTIVE;
+			if ( line[0] == 'a' ) {
+				ai->config.opmode |= MODE_STA_IBSS;
+			} else {
+				ai->config.opmode |= MODE_STA_ESS;
+				if ( line[0] == 'r' ) {
+					ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
+					ai->config.scanMode = SCANMODE_PASSIVE;
+					set_bit (FLAG_802_11, &ai->flags);
+				} else if ( line[0] == 'y' ) {
+					ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER;
+					ai->config.scanMode = SCANMODE_PASSIVE;
+					set_bit (FLAG_802_11, &ai->flags);
+				} else if ( line[0] == 'l' )
+					ai->config.rmode |= RXMODE_LANMON;
+			}
+			set_bit (FLAG_COMMIT, &ai->flags);
+		}
+
+/*** Radio status */
+		else if (!strncmp(line,"Radio: ", 7)) {
+			line += 7;
+			if (!strncmp(line,"off",3)) {
+				set_bit (FLAG_RADIO_OFF, &ai->flags);
+			} else {
+				clear_bit (FLAG_RADIO_OFF, &ai->flags);
+			}
+		}
+/*** NodeName processing */
+		else if ( !strncmp( line, "NodeName: ", 10 ) ) {
+			int j;
+
+			line += 10;
+			memset( ai->config.nodeName, 0, 16 );
+/* Do the name, assume a space between the mode and node name */
+			for( j = 0; j < 16 && line[j] != '\n'; j++ ) {
+				ai->config.nodeName[j] = line[j];
+			}
+			set_bit (FLAG_COMMIT, &ai->flags);
+		}
+
+/*** PowerMode processing */
+		else if ( !strncmp( line, "PowerMode: ", 11 ) ) {
+			line += 11;
+			if ( !strncmp( line, "PSPCAM", 6 ) ) {
+				ai->config.powerSaveMode = POWERSAVE_PSPCAM;
+				set_bit (FLAG_COMMIT, &ai->flags);
+			} else if ( !strncmp( line, "PSP", 3 ) ) {
+				ai->config.powerSaveMode = POWERSAVE_PSP;
+				set_bit (FLAG_COMMIT, &ai->flags);
+			} else {
+				ai->config.powerSaveMode = POWERSAVE_CAM;
+				set_bit (FLAG_COMMIT, &ai->flags);
+			}
+		} else if ( !strncmp( line, "DataRates: ", 11 ) ) {
+			int v, i = 0, k = 0; /* i is index into line,
+						k is index to rates */
+
+			line += 11;
+			while((v = get_dec_u16(line, &i, 3))!=-1) {
+				ai->config.rates[k++] = (u8)v;
+				line += i + 1;
+				i = 0;
+			}
+			set_bit (FLAG_COMMIT, &ai->flags);
+		} else if ( !strncmp( line, "Channel: ", 9 ) ) {
+			int v, i = 0;
+			line += 9;
+			v = get_dec_u16(line, &i, i+3);
+			if ( v != -1 ) {
+				ai->config.channelSet = cpu_to_le16(v);
+				set_bit (FLAG_COMMIT, &ai->flags);
+			}
+		} else if ( !strncmp( line, "XmitPower: ", 11 ) ) {
+			int v, i = 0;
+			line += 11;
+			v = get_dec_u16(line, &i, i+3);
+			if ( v != -1 ) {
+				ai->config.txPower = cpu_to_le16(v);
+				set_bit (FLAG_COMMIT, &ai->flags);
+			}
+		} else if ( !strncmp( line, "WEP: ", 5 ) ) {
+			line += 5;
+			switch( line[0] ) {
+			case 's':
+				ai->config.authType = AUTH_SHAREDKEY;
+				break;
+			case 'e':
+				ai->config.authType = AUTH_ENCRYPT;
+				break;
+			default:
+				ai->config.authType = AUTH_OPEN;
+				break;
+			}
+			set_bit (FLAG_COMMIT, &ai->flags);
+		} else if ( !strncmp( line, "LongRetryLimit: ", 16 ) ) {
+			int v, i = 0;
+
+			line += 16;
+			v = get_dec_u16(line, &i, 3);
+			v = (v<0) ? 0 : ((v>255) ? 255 : v);
+			ai->config.longRetryLimit = cpu_to_le16(v);
+			set_bit (FLAG_COMMIT, &ai->flags);
+		} else if ( !strncmp( line, "ShortRetryLimit: ", 17 ) ) {
+			int v, i = 0;
+
+			line += 17;
+			v = get_dec_u16(line, &i, 3);
+			v = (v<0) ? 0 : ((v>255) ? 255 : v);
+			ai->config.shortRetryLimit = cpu_to_le16(v);
+			set_bit (FLAG_COMMIT, &ai->flags);
+		} else if ( !strncmp( line, "RTSThreshold: ", 14 ) ) {
+			int v, i = 0;
+
+			line += 14;
+			v = get_dec_u16(line, &i, 4);
+			v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
+			ai->config.rtsThres = cpu_to_le16(v);
+			set_bit (FLAG_COMMIT, &ai->flags);
+		} else if ( !strncmp( line, "TXMSDULifetime: ", 16 ) ) {
+			int v, i = 0;
+
+			line += 16;
+			v = get_dec_u16(line, &i, 5);
+			v = (v<0) ? 0 : v;
+			ai->config.txLifetime = cpu_to_le16(v);
+			set_bit (FLAG_COMMIT, &ai->flags);
+		} else if ( !strncmp( line, "RXMSDULifetime: ", 16 ) ) {
+			int v, i = 0;
+
+			line += 16;
+			v = get_dec_u16(line, &i, 5);
+			v = (v<0) ? 0 : v;
+			ai->config.rxLifetime = cpu_to_le16(v);
+			set_bit (FLAG_COMMIT, &ai->flags);
+		} else if ( !strncmp( line, "TXDiversity: ", 13 ) ) {
+			ai->config.txDiversity =
+				(line[13]=='l') ? 1 :
+				((line[13]=='r')? 2: 3);
+			set_bit (FLAG_COMMIT, &ai->flags);
+		} else if ( !strncmp( line, "RXDiversity: ", 13 ) ) {
+			ai->config.rxDiversity =
+				(line[13]=='l') ? 1 :
+				((line[13]=='r')? 2: 3);
+			set_bit (FLAG_COMMIT, &ai->flags);
+		} else if ( !strncmp( line, "FragThreshold: ", 15 ) ) {
+			int v, i = 0;
+
+			line += 15;
+			v = get_dec_u16(line, &i, 4);
+			v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
+			v = v & 0xfffe; /* Make sure its even */
+			ai->config.fragThresh = cpu_to_le16(v);
+			set_bit (FLAG_COMMIT, &ai->flags);
+		} else if (!strncmp(line, "Modulation: ", 12)) {
+			line += 12;
+			switch(*line) {
+			case 'd':  ai->config.modulation=MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break;
+			case 'c':  ai->config.modulation=MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break;
+			case 'm':  ai->config.modulation=MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break;
+			default: airo_print_warn(ai->dev->name, "Unknown modulation");
+			}
+		} else if (!strncmp(line, "Preamble: ", 10)) {
+			line += 10;
+			switch(*line) {
+			case 'a': ai->config.preamble=PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break;
+			case 'l': ai->config.preamble=PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break;
+			case 's': ai->config.preamble=PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break;
+			default: airo_print_warn(ai->dev->name, "Unknown preamble");
+			}
+		} else {
+			airo_print_warn(ai->dev->name, "Couldn't figure out %s", line);
+		}
+		while( line[0] && line[0] != '\n' ) line++;
+		if ( line[0] ) line++;
+	}
+	airo_config_commit(dev, NULL, NULL, NULL);
+}
+
+static const char *get_rmode(__le16 mode)
+{
+        switch(mode & RXMODE_MASK) {
+        case RXMODE_RFMON:  return "rfmon";
+        case RXMODE_RFMON_ANYBSS:  return "yna (any) bss rfmon";
+        case RXMODE_LANMON:  return "lanmon";
+        }
+        return "ESS";
+}
+
+static int proc_config_open(struct inode *inode, struct file *file)
+{
+	struct proc_data *data;
+	struct net_device *dev = PDE_DATA(inode);
+	struct airo_info *ai = dev->ml_priv;
+	int i;
+	__le16 mode;
+
+	if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	data = file->private_data;
+	if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
+		kfree (file->private_data);
+		return -ENOMEM;
+	}
+	if ((data->wbuffer = kzalloc( 2048, GFP_KERNEL )) == NULL) {
+		kfree (data->rbuffer);
+		kfree (file->private_data);
+		return -ENOMEM;
+	}
+	data->maxwritelen = 2048;
+	data->on_close = proc_config_on_close;
+
+	readConfigRid(ai, 1);
+
+	mode = ai->config.opmode & MODE_CFG_MASK;
+	i = sprintf( data->rbuffer,
+		     "Mode: %s\n"
+		     "Radio: %s\n"
+		     "NodeName: %-16s\n"
+		     "PowerMode: %s\n"
+		     "DataRates: %d %d %d %d %d %d %d %d\n"
+		     "Channel: %d\n"
+		     "XmitPower: %d\n",
+		     mode == MODE_STA_IBSS ? "adhoc" :
+		     mode == MODE_STA_ESS ? get_rmode(ai->config.rmode):
+		     mode == MODE_AP ? "AP" :
+		     mode == MODE_AP_RPTR ? "AP RPTR" : "Error",
+		     test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on",
+		     ai->config.nodeName,
+		     ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" :
+		     ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" :
+		     ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" :
+		     "Error",
+		     (int)ai->config.rates[0],
+		     (int)ai->config.rates[1],
+		     (int)ai->config.rates[2],
+		     (int)ai->config.rates[3],
+		     (int)ai->config.rates[4],
+		     (int)ai->config.rates[5],
+		     (int)ai->config.rates[6],
+		     (int)ai->config.rates[7],
+		     le16_to_cpu(ai->config.channelSet),
+		     le16_to_cpu(ai->config.txPower)
+		);
+	sprintf( data->rbuffer + i,
+		 "LongRetryLimit: %d\n"
+		 "ShortRetryLimit: %d\n"
+		 "RTSThreshold: %d\n"
+		 "TXMSDULifetime: %d\n"
+		 "RXMSDULifetime: %d\n"
+		 "TXDiversity: %s\n"
+		 "RXDiversity: %s\n"
+		 "FragThreshold: %d\n"
+		 "WEP: %s\n"
+		 "Modulation: %s\n"
+		 "Preamble: %s\n",
+		 le16_to_cpu(ai->config.longRetryLimit),
+		 le16_to_cpu(ai->config.shortRetryLimit),
+		 le16_to_cpu(ai->config.rtsThres),
+		 le16_to_cpu(ai->config.txLifetime),
+		 le16_to_cpu(ai->config.rxLifetime),
+		 ai->config.txDiversity == 1 ? "left" :
+		 ai->config.txDiversity == 2 ? "right" : "both",
+		 ai->config.rxDiversity == 1 ? "left" :
+		 ai->config.rxDiversity == 2 ? "right" : "both",
+		 le16_to_cpu(ai->config.fragThresh),
+		 ai->config.authType == AUTH_ENCRYPT ? "encrypt" :
+		 ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open",
+		 ai->config.modulation == MOD_DEFAULT ? "default" :
+		 ai->config.modulation == MOD_CCK ? "cck" :
+		 ai->config.modulation == MOD_MOK ? "mok" : "error",
+		 ai->config.preamble == PREAMBLE_AUTO ? "auto" :
+		 ai->config.preamble == PREAMBLE_LONG ? "long" :
+		 ai->config.preamble == PREAMBLE_SHORT ? "short" : "error"
+		);
+	data->readlen = strlen( data->rbuffer );
+	return 0;
+}
+
+static void proc_SSID_on_close(struct inode *inode, struct file *file)
+{
+	struct proc_data *data = file->private_data;
+	struct net_device *dev = PDE_DATA(inode);
+	struct airo_info *ai = dev->ml_priv;
+	SsidRid SSID_rid;
+	int i;
+	char *p = data->wbuffer;
+	char *end = p + data->writelen;
+
+	if (!data->writelen)
+		return;
+
+	*end = '\n'; /* sentinel; we have space for it */
+
+	memset(&SSID_rid, 0, sizeof(SSID_rid));
+
+	for (i = 0; i < 3 && p < end; i++) {
+		int j = 0;
+		/* copy up to 32 characters from this line */
+		while (*p != '\n' && j < 32)
+			SSID_rid.ssids[i].ssid[j++] = *p++;
+		if (j == 0)
+			break;
+		SSID_rid.ssids[i].len = cpu_to_le16(j);
+		/* skip to the beginning of the next line */
+		while (*p++ != '\n')
+			;
+	}
+	if (i)
+		SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
+	disable_MAC(ai, 1);
+	writeSsidRid(ai, &SSID_rid, 1);
+	enable_MAC(ai, 1);
+}
+
+static void proc_APList_on_close( struct inode *inode, struct file *file ) {
+	struct proc_data *data = file->private_data;
+	struct net_device *dev = PDE_DATA(inode);
+	struct airo_info *ai = dev->ml_priv;
+	APListRid APList_rid;
+	int i;
+
+	if ( !data->writelen ) return;
+
+	memset( &APList_rid, 0, sizeof(APList_rid) );
+	APList_rid.len = cpu_to_le16(sizeof(APList_rid));
+
+	for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) {
+		int j;
+		for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) {
+			switch(j%3) {
+			case 0:
+				APList_rid.ap[i][j/3]=
+					hex_to_bin(data->wbuffer[j+i*6*3])<<4;
+				break;
+			case 1:
+				APList_rid.ap[i][j/3]|=
+					hex_to_bin(data->wbuffer[j+i*6*3]);
+				break;
+			}
+		}
+	}
+	disable_MAC(ai, 1);
+	writeAPListRid(ai, &APList_rid, 1);
+	enable_MAC(ai, 1);
+}
+
+/* This function wraps PC4500_writerid with a MAC disable */
+static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data,
+			int len, int dummy ) {
+	int rc;
+
+	disable_MAC(ai, 1);
+	rc = PC4500_writerid(ai, rid, rid_data, len, 1);
+	enable_MAC(ai, 1);
+	return rc;
+}
+
+/* Returns the WEP key at the specified index, or -1 if that key does
+ * not exist.  The buffer is assumed to be at least 16 bytes in length.
+ */
+static int get_wep_key(struct airo_info *ai, u16 index, char *buf, u16 buflen)
+{
+	WepKeyRid wkr;
+	int rc;
+	__le16 lastindex;
+
+	rc = readWepKeyRid(ai, &wkr, 1, 1);
+	if (rc != SUCCESS)
+		return -1;
+	do {
+		lastindex = wkr.kindex;
+		if (le16_to_cpu(wkr.kindex) == index) {
+			int klen = min_t(int, buflen, le16_to_cpu(wkr.klen));
+			memcpy(buf, wkr.key, klen);
+			return klen;
+		}
+		rc = readWepKeyRid(ai, &wkr, 0, 1);
+		if (rc != SUCCESS)
+			return -1;
+	} while (lastindex != wkr.kindex);
+	return -1;
+}
+
+static int get_wep_tx_idx(struct airo_info *ai)
+{
+	WepKeyRid wkr;
+	int rc;
+	__le16 lastindex;
+
+	rc = readWepKeyRid(ai, &wkr, 1, 1);
+	if (rc != SUCCESS)
+		return -1;
+	do {
+		lastindex = wkr.kindex;
+		if (wkr.kindex == cpu_to_le16(0xffff))
+			return wkr.mac[0];
+		rc = readWepKeyRid(ai, &wkr, 0, 1);
+		if (rc != SUCCESS)
+			return -1;
+	} while (lastindex != wkr.kindex);
+	return -1;
+}
+
+static int set_wep_key(struct airo_info *ai, u16 index, const char *key,
+		       u16 keylen, int perm, int lock)
+{
+	static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
+	WepKeyRid wkr;
+	int rc;
+
+	if (WARN_ON(keylen == 0))
+		return -1;
+
+	memset(&wkr, 0, sizeof(wkr));
+	wkr.len = cpu_to_le16(sizeof(wkr));
+	wkr.kindex = cpu_to_le16(index);
+	wkr.klen = cpu_to_le16(keylen);
+	memcpy(wkr.key, key, keylen);
+	memcpy(wkr.mac, macaddr, ETH_ALEN);
+
+	if (perm) disable_MAC(ai, lock);
+	rc = writeWepKeyRid(ai, &wkr, perm, lock);
+	if (perm) enable_MAC(ai, lock);
+	return rc;
+}
+
+static int set_wep_tx_idx(struct airo_info *ai, u16 index, int perm, int lock)
+{
+	WepKeyRid wkr;
+	int rc;
+
+	memset(&wkr, 0, sizeof(wkr));
+	wkr.len = cpu_to_le16(sizeof(wkr));
+	wkr.kindex = cpu_to_le16(0xffff);
+	wkr.mac[0] = (char)index;
+
+	if (perm) {
+		ai->defindex = (char)index;
+		disable_MAC(ai, lock);
+	}
+
+	rc = writeWepKeyRid(ai, &wkr, perm, lock);
+
+	if (perm)
+		enable_MAC(ai, lock);
+	return rc;
+}
+
+static void proc_wepkey_on_close( struct inode *inode, struct file *file ) {
+	struct proc_data *data;
+	struct net_device *dev = PDE_DATA(inode);
+	struct airo_info *ai = dev->ml_priv;
+	int i, rc;
+	char key[16];
+	u16 index = 0;
+	int j = 0;
+
+	memset(key, 0, sizeof(key));
+
+	data = file->private_data;
+	if ( !data->writelen ) return;
+
+	if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' &&
+	    (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) {
+		index = data->wbuffer[0] - '0';
+		if (data->wbuffer[1] == '\n') {
+			rc = set_wep_tx_idx(ai, index, 1, 1);
+			if (rc < 0) {
+				airo_print_err(ai->dev->name, "failed to set "
+				               "WEP transmit index to %d: %d.",
+				               index, rc);
+			}
+			return;
+		}
+		j = 2;
+	} else {
+		airo_print_err(ai->dev->name, "WepKey passed invalid key index");
+		return;
+	}
+
+	for( i = 0; i < 16*3 && data->wbuffer[i+j]; i++ ) {
+		switch(i%3) {
+		case 0:
+			key[i/3] = hex_to_bin(data->wbuffer[i+j])<<4;
+			break;
+		case 1:
+			key[i/3] |= hex_to_bin(data->wbuffer[i+j]);
+			break;
+		}
+	}
+
+	rc = set_wep_key(ai, index, key, i/3, 1, 1);
+	if (rc < 0) {
+		airo_print_err(ai->dev->name, "failed to set WEP key at index "
+		               "%d: %d.", index, rc);
+	}
+}
+
+static int proc_wepkey_open( struct inode *inode, struct file *file )
+{
+	struct proc_data *data;
+	struct net_device *dev = PDE_DATA(inode);
+	struct airo_info *ai = dev->ml_priv;
+	char *ptr;
+	WepKeyRid wkr;
+	__le16 lastindex;
+	int j=0;
+	int rc;
+
+	if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	memset(&wkr, 0, sizeof(wkr));
+	data = file->private_data;
+	if ((data->rbuffer = kzalloc( 180, GFP_KERNEL )) == NULL) {
+		kfree (file->private_data);
+		return -ENOMEM;
+	}
+	data->writelen = 0;
+	data->maxwritelen = 80;
+	if ((data->wbuffer = kzalloc( 80, GFP_KERNEL )) == NULL) {
+		kfree (data->rbuffer);
+		kfree (file->private_data);
+		return -ENOMEM;
+	}
+	data->on_close = proc_wepkey_on_close;
+
+	ptr = data->rbuffer;
+	strcpy(ptr, "No wep keys\n");
+	rc = readWepKeyRid(ai, &wkr, 1, 1);
+	if (rc == SUCCESS) do {
+		lastindex = wkr.kindex;
+		if (wkr.kindex == cpu_to_le16(0xffff)) {
+			j += sprintf(ptr+j, "Tx key = %d\n",
+				     (int)wkr.mac[0]);
+		} else {
+			j += sprintf(ptr+j, "Key %d set with length = %d\n",
+				     le16_to_cpu(wkr.kindex),
+				     le16_to_cpu(wkr.klen));
+		}
+		readWepKeyRid(ai, &wkr, 0, 1);
+	} while((lastindex != wkr.kindex) && (j < 180-30));
+
+	data->readlen = strlen( data->rbuffer );
+	return 0;
+}
+
+static int proc_SSID_open(struct inode *inode, struct file *file)
+{
+	struct proc_data *data;
+	struct net_device *dev = PDE_DATA(inode);
+	struct airo_info *ai = dev->ml_priv;
+	int i;
+	char *ptr;
+	SsidRid SSID_rid;
+
+	if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	data = file->private_data;
+	if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
+		kfree (file->private_data);
+		return -ENOMEM;
+	}
+	data->writelen = 0;
+	data->maxwritelen = 33*3;
+	/* allocate maxwritelen + 1; we'll want a sentinel */
+	if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) {
+		kfree (data->rbuffer);
+		kfree (file->private_data);
+		return -ENOMEM;
+	}
+	data->on_close = proc_SSID_on_close;
+
+	readSsidRid(ai, &SSID_rid);
+	ptr = data->rbuffer;
+	for (i = 0; i < 3; i++) {
+		int j;
+		size_t len = le16_to_cpu(SSID_rid.ssids[i].len);
+		if (!len)
+			break;
+		if (len > 32)
+			len = 32;
+		for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++)
+			*ptr++ = SSID_rid.ssids[i].ssid[j];
+		*ptr++ = '\n';
+	}
+	*ptr = '\0';
+	data->readlen = strlen( data->rbuffer );
+	return 0;
+}
+
+static int proc_APList_open( struct inode *inode, struct file *file ) {
+	struct proc_data *data;
+	struct net_device *dev = PDE_DATA(inode);
+	struct airo_info *ai = dev->ml_priv;
+	int i;
+	char *ptr;
+	APListRid APList_rid;
+
+	if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	data = file->private_data;
+	if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
+		kfree (file->private_data);
+		return -ENOMEM;
+	}
+	data->writelen = 0;
+	data->maxwritelen = 4*6*3;
+	if ((data->wbuffer = kzalloc( data->maxwritelen, GFP_KERNEL )) == NULL) {
+		kfree (data->rbuffer);
+		kfree (file->private_data);
+		return -ENOMEM;
+	}
+	data->on_close = proc_APList_on_close;
+
+	readAPListRid(ai, &APList_rid);
+	ptr = data->rbuffer;
+	for( i = 0; i < 4; i++ ) {
+// We end when we find a zero MAC
+		if ( !*(int*)APList_rid.ap[i] &&
+		     !*(int*)&APList_rid.ap[i][2]) break;
+		ptr += sprintf(ptr, "%pM\n", APList_rid.ap[i]);
+	}
+	if (i==0) ptr += sprintf(ptr, "Not using specific APs\n");
+
+	*ptr = '\0';
+	data->readlen = strlen( data->rbuffer );
+	return 0;
+}
+
+static int proc_BSSList_open( struct inode *inode, struct file *file ) {
+	struct proc_data *data;
+	struct net_device *dev = PDE_DATA(inode);
+	struct airo_info *ai = dev->ml_priv;
+	char *ptr;
+	BSSListRid BSSList_rid;
+	int rc;
+	/* If doLoseSync is not 1, we won't do a Lose Sync */
+	int doLoseSync = -1;
+
+	if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	data = file->private_data;
+	if ((data->rbuffer = kmalloc( 1024, GFP_KERNEL )) == NULL) {
+		kfree (file->private_data);
+		return -ENOMEM;
+	}
+	data->writelen = 0;
+	data->maxwritelen = 0;
+	data->wbuffer = NULL;
+	data->on_close = NULL;
+
+	if (file->f_mode & FMODE_WRITE) {
+		if (!(file->f_mode & FMODE_READ)) {
+			Cmd cmd;
+			Resp rsp;
+
+			if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
+			memset(&cmd, 0, sizeof(cmd));
+			cmd.cmd=CMD_LISTBSS;
+			if (down_interruptible(&ai->sem))
+				return -ERESTARTSYS;
+			issuecommand(ai, &cmd, &rsp);
+			up(&ai->sem);
+			data->readlen = 0;
+			return 0;
+		}
+		doLoseSync = 1;
+	}
+	ptr = data->rbuffer;
+	/* There is a race condition here if there are concurrent opens.
+           Since it is a rare condition, we'll just live with it, otherwise
+           we have to add a spin lock... */
+	rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
+	while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
+		ptr += sprintf(ptr, "%pM %*s rssi = %d",
+			       BSSList_rid.bssid,
+				(int)BSSList_rid.ssidLen,
+				BSSList_rid.ssid,
+				le16_to_cpu(BSSList_rid.dBm));
+		ptr += sprintf(ptr, " channel = %d %s %s %s %s\n",
+				le16_to_cpu(BSSList_rid.dsChannel),
+				BSSList_rid.cap & CAP_ESS ? "ESS" : "",
+				BSSList_rid.cap & CAP_IBSS ? "adhoc" : "",
+				BSSList_rid.cap & CAP_PRIVACY ? "wep" : "",
+				BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : "");
+		rc = readBSSListRid(ai, 0, &BSSList_rid);
+	}
+	*ptr = '\0';
+	data->readlen = strlen( data->rbuffer );
+	return 0;
+}
+
+static int proc_close( struct inode *inode, struct file *file )
+{
+	struct proc_data *data = file->private_data;
+
+	if (data->on_close != NULL)
+		data->on_close(inode, file);
+	kfree(data->rbuffer);
+	kfree(data->wbuffer);
+	kfree(data);
+	return 0;
+}
+
+/* Since the card doesn't automatically switch to the right WEP mode,
+   we will make it do it.  If the card isn't associated, every secs we
+   will switch WEP modes to see if that will help.  If the card is
+   associated we will check every minute to see if anything has
+   changed. */
+static void timer_func( struct net_device *dev ) {
+	struct airo_info *apriv = dev->ml_priv;
+
+/* We don't have a link so try changing the authtype */
+	readConfigRid(apriv, 0);
+	disable_MAC(apriv, 0);
+	switch(apriv->config.authType) {
+		case AUTH_ENCRYPT:
+/* So drop to OPEN */
+			apriv->config.authType = AUTH_OPEN;
+			break;
+		case AUTH_SHAREDKEY:
+			if (apriv->keyindex < auto_wep) {
+				set_wep_tx_idx(apriv, apriv->keyindex, 0, 0);
+				apriv->config.authType = AUTH_SHAREDKEY;
+				apriv->keyindex++;
+			} else {
+			        /* Drop to ENCRYPT */
+				apriv->keyindex = 0;
+				set_wep_tx_idx(apriv, apriv->defindex, 0, 0);
+				apriv->config.authType = AUTH_ENCRYPT;
+			}
+			break;
+		default:  /* We'll escalate to SHAREDKEY */
+			apriv->config.authType = AUTH_SHAREDKEY;
+	}
+	set_bit (FLAG_COMMIT, &apriv->flags);
+	writeConfigRid(apriv, 0);
+	enable_MAC(apriv, 0);
+	up(&apriv->sem);
+
+/* Schedule check to see if the change worked */
+	clear_bit(JOB_AUTOWEP, &apriv->jobs);
+	apriv->expires = RUN_AT(HZ*3);
+}
+
+#ifdef CONFIG_PCI
+static int airo_pci_probe(struct pci_dev *pdev,
+				    const struct pci_device_id *pent)
+{
+	struct net_device *dev;
+
+	if (pci_enable_device(pdev))
+		return -ENODEV;
+	pci_set_master(pdev);
+
+	if (pdev->device == 0x5000 || pdev->device == 0xa504)
+			dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev);
+	else
+			dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev);
+	if (!dev) {
+		pci_disable_device(pdev);
+		return -ENODEV;
+	}
+
+	pci_set_drvdata(pdev, dev);
+	return 0;
+}
+
+static void airo_pci_remove(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+
+	airo_print_info(dev->name, "Unregistering...");
+	stop_airo_card(dev, 1);
+	pci_disable_device(pdev);
+}
+
+static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct airo_info *ai = dev->ml_priv;
+	Cmd cmd;
+	Resp rsp;
+
+	if (!ai->APList)
+		ai->APList = kmalloc(sizeof(APListRid), GFP_KERNEL);
+	if (!ai->APList)
+		return -ENOMEM;
+	if (!ai->SSID)
+		ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL);
+	if (!ai->SSID)
+		return -ENOMEM;
+	readAPListRid(ai, ai->APList);
+	readSsidRid(ai, ai->SSID);
+	memset(&cmd, 0, sizeof(cmd));
+	/* the lock will be released at the end of the resume callback */
+	if (down_interruptible(&ai->sem))
+		return -EAGAIN;
+	disable_MAC(ai, 0);
+	netif_device_detach(dev);
+	ai->power = state;
+	cmd.cmd = HOSTSLEEP;
+	issuecommand(ai, &cmd, &rsp);
+
+	pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
+	pci_save_state(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	return 0;
+}
+
+static int airo_pci_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct airo_info *ai = dev->ml_priv;
+	pci_power_t prev_state = pdev->current_state;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	pci_enable_wake(pdev, PCI_D0, 0);
+
+	if (prev_state != PCI_D1) {
+		reset_card(dev, 0);
+		mpi_init_descriptors(ai);
+		setup_card(ai, dev->dev_addr, 0);
+		clear_bit(FLAG_RADIO_OFF, &ai->flags);
+		clear_bit(FLAG_PENDING_XMIT, &ai->flags);
+	} else {
+		OUT4500(ai, EVACK, EV_AWAKEN);
+		OUT4500(ai, EVACK, EV_AWAKEN);
+		msleep(100);
+	}
+
+	set_bit(FLAG_COMMIT, &ai->flags);
+	disable_MAC(ai, 0);
+        msleep(200);
+	if (ai->SSID) {
+		writeSsidRid(ai, ai->SSID, 0);
+		kfree(ai->SSID);
+		ai->SSID = NULL;
+	}
+	if (ai->APList) {
+		writeAPListRid(ai, ai->APList, 0);
+		kfree(ai->APList);
+		ai->APList = NULL;
+	}
+	writeConfigRid(ai, 0);
+	enable_MAC(ai, 0);
+	ai->power = PMSG_ON;
+	netif_device_attach(dev);
+	netif_wake_queue(dev);
+	enable_interrupts(ai);
+	up(&ai->sem);
+	return 0;
+}
+#endif
+
+static int __init airo_init_module( void )
+{
+	int i;
+
+	proc_kuid = make_kuid(&init_user_ns, proc_uid);
+	proc_kgid = make_kgid(&init_user_ns, proc_gid);
+	if (!uid_valid(proc_kuid) || !gid_valid(proc_kgid))
+		return -EINVAL;
+
+	airo_entry = proc_mkdir_mode("driver/aironet", airo_perm, NULL);
+
+	if (airo_entry)
+		proc_set_user(airo_entry, proc_kuid, proc_kgid);
+
+	for (i = 0; i < 4 && io[i] && irq[i]; i++) {
+		airo_print_info("", "Trying to configure ISA adapter at irq=%d "
+			"io=0x%x", irq[i], io[i] );
+		if (init_airo_card( irq[i], io[i], 0, NULL ))
+			/* do nothing */ ;
+	}
+
+#ifdef CONFIG_PCI
+	airo_print_info("", "Probing for PCI adapters");
+	i = pci_register_driver(&airo_driver);
+	airo_print_info("", "Finished probing for PCI adapters");
+
+	if (i) {
+		remove_proc_entry("driver/aironet", NULL);
+		return i;
+	}
+#endif
+
+	/* Always exit with success, as we are a library module
+	 * as well as a driver module
+	 */
+	return 0;
+}
+
+static void __exit airo_cleanup_module( void )
+{
+	struct airo_info *ai;
+	while(!list_empty(&airo_devices)) {
+		ai = list_entry(airo_devices.next, struct airo_info, dev_list);
+		airo_print_info(ai->dev->name, "Unregistering...");
+		stop_airo_card(ai->dev, 1);
+	}
+#ifdef CONFIG_PCI
+	pci_unregister_driver(&airo_driver);
+#endif
+	remove_proc_entry("driver/aironet", NULL);
+}
+
+/*
+ * Initial Wireless Extension code for Aironet driver by :
+ *	Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00
+ * Conversion to new driver API by :
+ *	Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02
+ * Javier also did a good amount of work here, adding some new extensions
+ * and fixing my code. Let's just say that without him this code just
+ * would not work at all... - Jean II
+ */
+
+static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi)
+{
+	if (!rssi_rid)
+		return 0;
+
+	return (0x100 - rssi_rid[rssi].rssidBm);
+}
+
+static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm)
+{
+	int i;
+
+	if (!rssi_rid)
+		return 0;
+
+	for (i = 0; i < 256; i++)
+		if (rssi_rid[i].rssidBm == dbm)
+			return rssi_rid[i].rssipct;
+
+	return 0;
+}
+
+
+static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid)
+{
+	int quality = 0;
+	u16 sq;
+
+	if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f))
+		return 0;
+
+	if (!(cap_rid->hardCap & cpu_to_le16(8)))
+		return 0;
+
+	sq = le16_to_cpu(status_rid->signalQuality);
+	if (memcmp(cap_rid->prodName, "350", 3))
+		if (sq > 0x20)
+			quality = 0;
+		else
+			quality = 0x20 - sq;
+	else
+		if (sq > 0xb0)
+			quality = 0;
+		else if (sq < 0x10)
+			quality = 0xa0;
+		else
+			quality = 0xb0 - sq;
+	return quality;
+}
+
+#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0)
+#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50);
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get protocol name
+ */
+static int airo_get_name(struct net_device *dev,
+			 struct iw_request_info *info,
+			 char *cwrq,
+			 char *extra)
+{
+	strcpy(cwrq, "IEEE 802.11-DS");
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set frequency
+ */
+static int airo_set_freq(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_freq *fwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	int rc = -EINPROGRESS;		/* Call commit handler */
+
+	/* If setting by frequency, convert to a channel */
+	if(fwrq->e == 1) {
+		int f = fwrq->m / 100000;
+
+		/* Hack to fall through... */
+		fwrq->e = 0;
+		fwrq->m = ieee80211_frequency_to_channel(f);
+	}
+	/* Setting by channel number */
+	if((fwrq->m > 1000) || (fwrq->e > 0))
+		rc = -EOPNOTSUPP;
+	else {
+		int channel = fwrq->m;
+		/* We should do a better check than that,
+		 * based on the card capability !!! */
+		if((channel < 1) || (channel > 14)) {
+			airo_print_dbg(dev->name, "New channel value of %d is invalid!",
+				fwrq->m);
+			rc = -EINVAL;
+		} else {
+			readConfigRid(local, 1);
+			/* Yes ! We can set it !!! */
+			local->config.channelSet = cpu_to_le16(channel);
+			set_bit (FLAG_COMMIT, &local->flags);
+		}
+	}
+	return rc;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get frequency
+ */
+static int airo_get_freq(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_freq *fwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	StatusRid status_rid;		/* Card status info */
+	int ch;
+
+	readConfigRid(local, 1);
+	if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS)
+		status_rid.channel = local->config.channelSet;
+	else
+		readStatusRid(local, &status_rid, 1);
+
+	ch = le16_to_cpu(status_rid.channel);
+	if((ch > 0) && (ch < 15)) {
+		fwrq->m = 100000 *
+			ieee80211_channel_to_frequency(ch, IEEE80211_BAND_2GHZ);
+		fwrq->e = 1;
+	} else {
+		fwrq->m = ch;
+		fwrq->e = 0;
+	}
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set ESSID
+ */
+static int airo_set_essid(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_point *dwrq,
+			  char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	SsidRid SSID_rid;		/* SSIDs */
+
+	/* Reload the list of current SSID */
+	readSsidRid(local, &SSID_rid);
+
+	/* Check if we asked for `any' */
+	if (dwrq->flags == 0) {
+		/* Just send an empty SSID list */
+		memset(&SSID_rid, 0, sizeof(SSID_rid));
+	} else {
+		unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+		/* Check the size of the string */
+		if (dwrq->length > IW_ESSID_MAX_SIZE)
+			return -E2BIG ;
+
+		/* Check if index is valid */
+		if (index >= ARRAY_SIZE(SSID_rid.ssids))
+			return -EINVAL;
+
+		/* Set the SSID */
+		memset(SSID_rid.ssids[index].ssid, 0,
+		       sizeof(SSID_rid.ssids[index].ssid));
+		memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length);
+		SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length);
+	}
+	SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
+	/* Write it to the card */
+	disable_MAC(local, 1);
+	writeSsidRid(local, &SSID_rid, 1);
+	enable_MAC(local, 1);
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get ESSID
+ */
+static int airo_get_essid(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_point *dwrq,
+			  char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	StatusRid status_rid;		/* Card status info */
+
+	readStatusRid(local, &status_rid, 1);
+
+	/* Note : if dwrq->flags != 0, we should
+	 * get the relevant SSID from the SSID list... */
+
+	/* Get the current SSID */
+	memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen));
+	/* If none, we may want to get the one that was set */
+
+	/* Push it out ! */
+	dwrq->length = le16_to_cpu(status_rid.SSIDlen);
+	dwrq->flags = 1; /* active */
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set AP address
+ */
+static int airo_set_wap(struct net_device *dev,
+			struct iw_request_info *info,
+			struct sockaddr *awrq,
+			char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	Cmd cmd;
+	Resp rsp;
+	APListRid APList_rid;
+
+	if (awrq->sa_family != ARPHRD_ETHER)
+		return -EINVAL;
+	else if (is_broadcast_ether_addr(awrq->sa_data) ||
+		 is_zero_ether_addr(awrq->sa_data)) {
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.cmd=CMD_LOSE_SYNC;
+		if (down_interruptible(&local->sem))
+			return -ERESTARTSYS;
+		issuecommand(local, &cmd, &rsp);
+		up(&local->sem);
+	} else {
+		memset(&APList_rid, 0, sizeof(APList_rid));
+		APList_rid.len = cpu_to_le16(sizeof(APList_rid));
+		memcpy(APList_rid.ap[0], awrq->sa_data, ETH_ALEN);
+		disable_MAC(local, 1);
+		writeAPListRid(local, &APList_rid, 1);
+		enable_MAC(local, 1);
+	}
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get AP address
+ */
+static int airo_get_wap(struct net_device *dev,
+			struct iw_request_info *info,
+			struct sockaddr *awrq,
+			char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	StatusRid status_rid;		/* Card status info */
+
+	readStatusRid(local, &status_rid, 1);
+
+	/* Tentative. This seems to work, wow, I'm lucky !!! */
+	memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN);
+	awrq->sa_family = ARPHRD_ETHER;
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Nickname
+ */
+static int airo_set_nick(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_point *dwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+
+	/* Check the size of the string */
+	if(dwrq->length > 16) {
+		return -E2BIG;
+	}
+	readConfigRid(local, 1);
+	memset(local->config.nodeName, 0, sizeof(local->config.nodeName));
+	memcpy(local->config.nodeName, extra, dwrq->length);
+	set_bit (FLAG_COMMIT, &local->flags);
+
+	return -EINPROGRESS;		/* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Nickname
+ */
+static int airo_get_nick(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_point *dwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+
+	readConfigRid(local, 1);
+	strncpy(extra, local->config.nodeName, 16);
+	extra[16] = '\0';
+	dwrq->length = strlen(extra);
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Bit-Rate
+ */
+static int airo_set_rate(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *vwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	CapabilityRid cap_rid;		/* Card capability info */
+	u8	brate = 0;
+	int	i;
+
+	/* First : get a valid bit rate value */
+	readCapabilityRid(local, &cap_rid, 1);
+
+	/* Which type of value ? */
+	if((vwrq->value < 8) && (vwrq->value >= 0)) {
+		/* Setting by rate index */
+		/* Find value in the magic rate table */
+		brate = cap_rid.supportedRates[vwrq->value];
+	} else {
+		/* Setting by frequency value */
+		u8	normvalue = (u8) (vwrq->value/500000);
+
+		/* Check if rate is valid */
+		for(i = 0 ; i < 8 ; i++) {
+			if(normvalue == cap_rid.supportedRates[i]) {
+				brate = normvalue;
+				break;
+			}
+		}
+	}
+	/* -1 designed the max rate (mostly auto mode) */
+	if(vwrq->value == -1) {
+		/* Get the highest available rate */
+		for(i = 0 ; i < 8 ; i++) {
+			if(cap_rid.supportedRates[i] == 0)
+				break;
+		}
+		if(i != 0)
+			brate = cap_rid.supportedRates[i - 1];
+	}
+	/* Check that it is valid */
+	if(brate == 0) {
+		return -EINVAL;
+	}
+
+	readConfigRid(local, 1);
+	/* Now, check if we want a fixed or auto value */
+	if(vwrq->fixed == 0) {
+		/* Fill all the rates up to this max rate */
+		memset(local->config.rates, 0, 8);
+		for(i = 0 ; i < 8 ; i++) {
+			local->config.rates[i] = cap_rid.supportedRates[i];
+			if(local->config.rates[i] == brate)
+				break;
+		}
+	} else {
+		/* Fixed mode */
+		/* One rate, fixed */
+		memset(local->config.rates, 0, 8);
+		local->config.rates[0] = brate;
+	}
+	set_bit (FLAG_COMMIT, &local->flags);
+
+	return -EINPROGRESS;		/* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Bit-Rate
+ */
+static int airo_get_rate(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *vwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	StatusRid status_rid;		/* Card status info */
+
+	readStatusRid(local, &status_rid, 1);
+
+	vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000;
+	/* If more than one rate, set auto */
+	readConfigRid(local, 1);
+	vwrq->fixed = (local->config.rates[1] == 0);
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set RTS threshold
+ */
+static int airo_set_rts(struct net_device *dev,
+			struct iw_request_info *info,
+			struct iw_param *vwrq,
+			char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	int rthr = vwrq->value;
+
+	if(vwrq->disabled)
+		rthr = AIRO_DEF_MTU;
+	if((rthr < 0) || (rthr > AIRO_DEF_MTU)) {
+		return -EINVAL;
+	}
+	readConfigRid(local, 1);
+	local->config.rtsThres = cpu_to_le16(rthr);
+	set_bit (FLAG_COMMIT, &local->flags);
+
+	return -EINPROGRESS;		/* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get RTS threshold
+ */
+static int airo_get_rts(struct net_device *dev,
+			struct iw_request_info *info,
+			struct iw_param *vwrq,
+			char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+
+	readConfigRid(local, 1);
+	vwrq->value = le16_to_cpu(local->config.rtsThres);
+	vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
+	vwrq->fixed = 1;
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Fragmentation threshold
+ */
+static int airo_set_frag(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *vwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	int fthr = vwrq->value;
+
+	if(vwrq->disabled)
+		fthr = AIRO_DEF_MTU;
+	if((fthr < 256) || (fthr > AIRO_DEF_MTU)) {
+		return -EINVAL;
+	}
+	fthr &= ~0x1;	/* Get an even value - is it really needed ??? */
+	readConfigRid(local, 1);
+	local->config.fragThresh = cpu_to_le16(fthr);
+	set_bit (FLAG_COMMIT, &local->flags);
+
+	return -EINPROGRESS;		/* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Fragmentation threshold
+ */
+static int airo_get_frag(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *vwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+
+	readConfigRid(local, 1);
+	vwrq->value = le16_to_cpu(local->config.fragThresh);
+	vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
+	vwrq->fixed = 1;
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Mode of Operation
+ */
+static int airo_set_mode(struct net_device *dev,
+			 struct iw_request_info *info,
+			 __u32 *uwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	int reset = 0;
+
+	readConfigRid(local, 1);
+	if (sniffing_mode(local))
+		reset = 1;
+
+	switch(*uwrq) {
+		case IW_MODE_ADHOC:
+			local->config.opmode &= ~MODE_CFG_MASK;
+			local->config.opmode |= MODE_STA_IBSS;
+			local->config.rmode &= ~RXMODE_FULL_MASK;
+			local->config.scanMode = SCANMODE_ACTIVE;
+			clear_bit (FLAG_802_11, &local->flags);
+			break;
+		case IW_MODE_INFRA:
+			local->config.opmode &= ~MODE_CFG_MASK;
+			local->config.opmode |= MODE_STA_ESS;
+			local->config.rmode &= ~RXMODE_FULL_MASK;
+			local->config.scanMode = SCANMODE_ACTIVE;
+			clear_bit (FLAG_802_11, &local->flags);
+			break;
+		case IW_MODE_MASTER:
+			local->config.opmode &= ~MODE_CFG_MASK;
+			local->config.opmode |= MODE_AP;
+			local->config.rmode &= ~RXMODE_FULL_MASK;
+			local->config.scanMode = SCANMODE_ACTIVE;
+			clear_bit (FLAG_802_11, &local->flags);
+			break;
+		case IW_MODE_REPEAT:
+			local->config.opmode &= ~MODE_CFG_MASK;
+			local->config.opmode |= MODE_AP_RPTR;
+			local->config.rmode &= ~RXMODE_FULL_MASK;
+			local->config.scanMode = SCANMODE_ACTIVE;
+			clear_bit (FLAG_802_11, &local->flags);
+			break;
+		case IW_MODE_MONITOR:
+			local->config.opmode &= ~MODE_CFG_MASK;
+			local->config.opmode |= MODE_STA_ESS;
+			local->config.rmode &= ~RXMODE_FULL_MASK;
+			local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
+			local->config.scanMode = SCANMODE_PASSIVE;
+			set_bit (FLAG_802_11, &local->flags);
+			break;
+		default:
+			return -EINVAL;
+	}
+	if (reset)
+		set_bit (FLAG_RESET, &local->flags);
+	set_bit (FLAG_COMMIT, &local->flags);
+
+	return -EINPROGRESS;		/* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Mode of Operation
+ */
+static int airo_get_mode(struct net_device *dev,
+			 struct iw_request_info *info,
+			 __u32 *uwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+
+	readConfigRid(local, 1);
+	/* If not managed, assume it's ad-hoc */
+	switch (local->config.opmode & MODE_CFG_MASK) {
+		case MODE_STA_ESS:
+			*uwrq = IW_MODE_INFRA;
+			break;
+		case MODE_AP:
+			*uwrq = IW_MODE_MASTER;
+			break;
+		case MODE_AP_RPTR:
+			*uwrq = IW_MODE_REPEAT;
+			break;
+		default:
+			*uwrq = IW_MODE_ADHOC;
+	}
+
+	return 0;
+}
+
+static inline int valid_index(struct airo_info *ai, int index)
+{
+	return (index >= 0) && (index <= ai->max_wep_idx);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Encryption Key
+ */
+static int airo_set_encode(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_point *dwrq,
+			   char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1);
+	__le16 currentAuthType = local->config.authType;
+	int rc = 0;
+
+	if (!local->wep_capable)
+		return -EOPNOTSUPP;
+
+	readConfigRid(local, 1);
+
+	/* Basic checking: do we have a key to set ?
+	 * Note : with the new API, it's impossible to get a NULL pointer.
+	 * Therefore, we need to check a key size == 0 instead.
+	 * New version of iwconfig properly set the IW_ENCODE_NOKEY flag
+	 * when no key is present (only change flags), but older versions
+	 * don't do it. - Jean II */
+	if (dwrq->length > 0) {
+		wep_key_t key;
+		int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+		int current_index;
+
+		/* Check the size of the key */
+		if (dwrq->length > MAX_KEY_SIZE) {
+			return -EINVAL;
+		}
+
+		current_index = get_wep_tx_idx(local);
+		if (current_index < 0)
+			current_index = 0;
+
+		/* Check the index (none -> use current) */
+		if (!valid_index(local, index))
+			index = current_index;
+
+		/* Set the length */
+		if (dwrq->length > MIN_KEY_SIZE)
+			key.len = MAX_KEY_SIZE;
+		else
+			key.len = MIN_KEY_SIZE;
+		/* Check if the key is not marked as invalid */
+		if(!(dwrq->flags & IW_ENCODE_NOKEY)) {
+			/* Cleanup */
+			memset(key.key, 0, MAX_KEY_SIZE);
+			/* Copy the key in the driver */
+			memcpy(key.key, extra, dwrq->length);
+			/* Send the key to the card */
+			rc = set_wep_key(local, index, key.key, key.len, perm, 1);
+			if (rc < 0) {
+				airo_print_err(local->dev->name, "failed to set"
+				               " WEP key at index %d: %d.",
+				               index, rc);
+				return rc;
+			}
+		}
+		/* WE specify that if a valid key is set, encryption
+		 * should be enabled (user may turn it off later)
+		 * This is also how "iwconfig ethX key on" works */
+		if((index == current_index) && (key.len > 0) &&
+		   (local->config.authType == AUTH_OPEN)) {
+			local->config.authType = AUTH_ENCRYPT;
+		}
+	} else {
+		/* Do we want to just set the transmit key index ? */
+		int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+		if (valid_index(local, index)) {
+			rc = set_wep_tx_idx(local, index, perm, 1);
+			if (rc < 0) {
+				airo_print_err(local->dev->name, "failed to set"
+				               " WEP transmit index to %d: %d.",
+				               index, rc);
+				return rc;
+			}
+		} else {
+			/* Don't complain if only change the mode */
+			if (!(dwrq->flags & IW_ENCODE_MODE))
+				return -EINVAL;
+		}
+	}
+	/* Read the flags */
+	if(dwrq->flags & IW_ENCODE_DISABLED)
+		local->config.authType = AUTH_OPEN;	// disable encryption
+	if(dwrq->flags & IW_ENCODE_RESTRICTED)
+		local->config.authType = AUTH_SHAREDKEY;	// Only Both
+	if(dwrq->flags & IW_ENCODE_OPEN)
+		local->config.authType = AUTH_ENCRYPT;	// Only Wep
+	/* Commit the changes to flags if needed */
+	if (local->config.authType != currentAuthType)
+		set_bit (FLAG_COMMIT, &local->flags);
+	return -EINPROGRESS;		/* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Encryption Key
+ */
+static int airo_get_encode(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_point *dwrq,
+			   char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+	int wep_key_len;
+	u8 buf[16];
+
+	if (!local->wep_capable)
+		return -EOPNOTSUPP;
+
+	readConfigRid(local, 1);
+
+	/* Check encryption mode */
+	switch(local->config.authType)	{
+		case AUTH_ENCRYPT:
+			dwrq->flags = IW_ENCODE_OPEN;
+			break;
+		case AUTH_SHAREDKEY:
+			dwrq->flags = IW_ENCODE_RESTRICTED;
+			break;
+		default:
+		case AUTH_OPEN:
+			dwrq->flags = IW_ENCODE_DISABLED;
+			break;
+	}
+	/* We can't return the key, so set the proper flag and return zero */
+	dwrq->flags |= IW_ENCODE_NOKEY;
+	memset(extra, 0, 16);
+
+	/* Which key do we want ? -1 -> tx index */
+	if (!valid_index(local, index)) {
+		index = get_wep_tx_idx(local);
+		if (index < 0)
+			index = 0;
+	}
+	dwrq->flags |= index + 1;
+
+	/* Copy the key to the user buffer */
+	wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf));
+	if (wep_key_len < 0) {
+		dwrq->length = 0;
+	} else {
+		dwrq->length = wep_key_len;
+		memcpy(extra, buf, dwrq->length);
+	}
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set extended Encryption parameters
+ */
+static int airo_set_encodeext(struct net_device *dev,
+			   struct iw_request_info *info,
+			    union iwreq_data *wrqu,
+			    char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	struct iw_point *encoding = &wrqu->encoding;
+	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+	int perm = ( encoding->flags & IW_ENCODE_TEMP ? 0 : 1 );
+	__le16 currentAuthType = local->config.authType;
+	int idx, key_len, alg = ext->alg, set_key = 1, rc;
+	wep_key_t key;
+
+	if (!local->wep_capable)
+		return -EOPNOTSUPP;
+
+	readConfigRid(local, 1);
+
+	/* Determine and validate the key index */
+	idx = encoding->flags & IW_ENCODE_INDEX;
+	if (idx) {
+		if (!valid_index(local, idx - 1))
+			return -EINVAL;
+		idx--;
+	} else {
+		idx = get_wep_tx_idx(local);
+		if (idx < 0)
+			idx = 0;
+	}
+
+	if (encoding->flags & IW_ENCODE_DISABLED)
+		alg = IW_ENCODE_ALG_NONE;
+
+	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+		/* Only set transmit key index here, actual
+		 * key is set below if needed.
+		 */
+		rc = set_wep_tx_idx(local, idx, perm, 1);
+		if (rc < 0) {
+			airo_print_err(local->dev->name, "failed to set "
+			               "WEP transmit index to %d: %d.",
+			               idx, rc);
+			return rc;
+		}
+		set_key = ext->key_len > 0 ? 1 : 0;
+	}
+
+	if (set_key) {
+		/* Set the requested key first */
+		memset(key.key, 0, MAX_KEY_SIZE);
+		switch (alg) {
+		case IW_ENCODE_ALG_NONE:
+			key.len = 0;
+			break;
+		case IW_ENCODE_ALG_WEP:
+			if (ext->key_len > MIN_KEY_SIZE) {
+				key.len = MAX_KEY_SIZE;
+			} else if (ext->key_len > 0) {
+				key.len = MIN_KEY_SIZE;
+			} else {
+				return -EINVAL;
+			}
+			key_len = min (ext->key_len, key.len);
+			memcpy(key.key, ext->key, key_len);
+			break;
+		default:
+			return -EINVAL;
+		}
+		if (key.len == 0) {
+			rc = set_wep_tx_idx(local, idx, perm, 1);
+			if (rc < 0) {
+				airo_print_err(local->dev->name,
+					       "failed to set WEP transmit index to %d: %d.",
+					       idx, rc);
+				return rc;
+			}
+		} else {
+			rc = set_wep_key(local, idx, key.key, key.len, perm, 1);
+			if (rc < 0) {
+				airo_print_err(local->dev->name,
+					       "failed to set WEP key at index %d: %d.",
+					       idx, rc);
+				return rc;
+			}
+		}
+	}
+
+	/* Read the flags */
+	if(encoding->flags & IW_ENCODE_DISABLED)
+		local->config.authType = AUTH_OPEN;	// disable encryption
+	if(encoding->flags & IW_ENCODE_RESTRICTED)
+		local->config.authType = AUTH_SHAREDKEY;	// Only Both
+	if(encoding->flags & IW_ENCODE_OPEN)
+		local->config.authType = AUTH_ENCRYPT;	// Only Wep
+	/* Commit the changes to flags if needed */
+	if (local->config.authType != currentAuthType)
+		set_bit (FLAG_COMMIT, &local->flags);
+
+	return -EINPROGRESS;
+}
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get extended Encryption parameters
+ */
+static int airo_get_encodeext(struct net_device *dev,
+			    struct iw_request_info *info,
+			    union iwreq_data *wrqu,
+			    char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	struct iw_point *encoding = &wrqu->encoding;
+	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+	int idx, max_key_len, wep_key_len;
+	u8 buf[16];
+
+	if (!local->wep_capable)
+		return -EOPNOTSUPP;
+
+	readConfigRid(local, 1);
+
+	max_key_len = encoding->length - sizeof(*ext);
+	if (max_key_len < 0)
+		return -EINVAL;
+
+	idx = encoding->flags & IW_ENCODE_INDEX;
+	if (idx) {
+		if (!valid_index(local, idx - 1))
+			return -EINVAL;
+		idx--;
+	} else {
+		idx = get_wep_tx_idx(local);
+		if (idx < 0)
+			idx = 0;
+	}
+
+	encoding->flags = idx + 1;
+	memset(ext, 0, sizeof(*ext));
+
+	/* Check encryption mode */
+	switch(local->config.authType) {
+		case AUTH_ENCRYPT:
+			encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
+			break;
+		case AUTH_SHAREDKEY:
+			encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
+			break;
+		default:
+		case AUTH_OPEN:
+			encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED;
+			break;
+	}
+	/* We can't return the key, so set the proper flag and return zero */
+	encoding->flags |= IW_ENCODE_NOKEY;
+	memset(extra, 0, 16);
+	
+	/* Copy the key to the user buffer */
+	wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf));
+	if (wep_key_len < 0) {
+		ext->key_len = 0;
+	} else {
+		ext->key_len = wep_key_len;
+		memcpy(extra, buf, ext->key_len);
+	}
+
+	return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set extended authentication parameters
+ */
+static int airo_set_auth(struct net_device *dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *wrqu, char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	struct iw_param *param = &wrqu->param;
+	__le16 currentAuthType = local->config.authType;
+
+	switch (param->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_WPA_VERSION:
+	case IW_AUTH_CIPHER_PAIRWISE:
+	case IW_AUTH_CIPHER_GROUP:
+	case IW_AUTH_KEY_MGMT:
+	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+	case IW_AUTH_PRIVACY_INVOKED:
+		/*
+		 * airo does not use these parameters
+		 */
+		break;
+
+	case IW_AUTH_DROP_UNENCRYPTED:
+		if (param->value) {
+			/* Only change auth type if unencrypted */
+			if (currentAuthType == AUTH_OPEN)
+				local->config.authType = AUTH_ENCRYPT;
+		} else {
+			local->config.authType = AUTH_OPEN;
+		}
+
+		/* Commit the changes to flags if needed */
+		if (local->config.authType != currentAuthType)
+			set_bit (FLAG_COMMIT, &local->flags);
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG: {
+			/* FIXME: What about AUTH_OPEN?  This API seems to
+			 * disallow setting our auth to AUTH_OPEN.
+			 */
+			if (param->value & IW_AUTH_ALG_SHARED_KEY) {
+				local->config.authType = AUTH_SHAREDKEY;
+			} else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
+				local->config.authType = AUTH_ENCRYPT;
+			} else
+				return -EINVAL;
+
+			/* Commit the changes to flags if needed */
+			if (local->config.authType != currentAuthType)
+				set_bit (FLAG_COMMIT, &local->flags);
+			break;
+		}
+
+	case IW_AUTH_WPA_ENABLED:
+		/* Silently accept disable of WPA */
+		if (param->value > 0)
+			return -EOPNOTSUPP;
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+	return -EINPROGRESS;
+}
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get extended authentication parameters
+ */
+static int airo_get_auth(struct net_device *dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *wrqu, char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	struct iw_param *param = &wrqu->param;
+	__le16 currentAuthType = local->config.authType;
+
+	switch (param->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_DROP_UNENCRYPTED:
+		switch (currentAuthType) {
+		case AUTH_SHAREDKEY:
+		case AUTH_ENCRYPT:
+			param->value = 1;
+			break;
+		default:
+			param->value = 0;
+			break;
+		}
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG:
+		switch (currentAuthType) {
+		case AUTH_SHAREDKEY:
+			param->value = IW_AUTH_ALG_SHARED_KEY;
+			break;
+		case AUTH_ENCRYPT:
+		default:
+			param->value = IW_AUTH_ALG_OPEN_SYSTEM;
+			break;
+		}
+		break;
+
+	case IW_AUTH_WPA_ENABLED:
+		param->value = 0;
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Tx-Power
+ */
+static int airo_set_txpow(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *vwrq,
+			  char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	CapabilityRid cap_rid;		/* Card capability info */
+	int i;
+	int rc = -EINVAL;
+	__le16 v = cpu_to_le16(vwrq->value);
+
+	readCapabilityRid(local, &cap_rid, 1);
+
+	if (vwrq->disabled) {
+		set_bit (FLAG_RADIO_OFF, &local->flags);
+		set_bit (FLAG_COMMIT, &local->flags);
+		return -EINPROGRESS;		/* Call commit handler */
+	}
+	if (vwrq->flags != IW_TXPOW_MWATT) {
+		return -EINVAL;
+	}
+	clear_bit (FLAG_RADIO_OFF, &local->flags);
+	for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++)
+		if (v == cap_rid.txPowerLevels[i]) {
+			readConfigRid(local, 1);
+			local->config.txPower = v;
+			set_bit (FLAG_COMMIT, &local->flags);
+			rc = -EINPROGRESS;	/* Call commit handler */
+			break;
+		}
+	return rc;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Tx-Power
+ */
+static int airo_get_txpow(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *vwrq,
+			  char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+
+	readConfigRid(local, 1);
+	vwrq->value = le16_to_cpu(local->config.txPower);
+	vwrq->fixed = 1;	/* No power control */
+	vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags);
+	vwrq->flags = IW_TXPOW_MWATT;
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Retry limits
+ */
+static int airo_set_retry(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *vwrq,
+			  char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	int rc = -EINVAL;
+
+	if(vwrq->disabled) {
+		return -EINVAL;
+	}
+	readConfigRid(local, 1);
+	if(vwrq->flags & IW_RETRY_LIMIT) {
+		__le16 v = cpu_to_le16(vwrq->value);
+		if(vwrq->flags & IW_RETRY_LONG)
+			local->config.longRetryLimit = v;
+		else if (vwrq->flags & IW_RETRY_SHORT)
+			local->config.shortRetryLimit = v;
+		else {
+			/* No modifier : set both */
+			local->config.longRetryLimit = v;
+			local->config.shortRetryLimit = v;
+		}
+		set_bit (FLAG_COMMIT, &local->flags);
+		rc = -EINPROGRESS;		/* Call commit handler */
+	}
+	if(vwrq->flags & IW_RETRY_LIFETIME) {
+		local->config.txLifetime = cpu_to_le16(vwrq->value / 1024);
+		set_bit (FLAG_COMMIT, &local->flags);
+		rc = -EINPROGRESS;		/* Call commit handler */
+	}
+	return rc;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Retry limits
+ */
+static int airo_get_retry(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *vwrq,
+			  char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+
+	vwrq->disabled = 0;      /* Can't be disabled */
+
+	readConfigRid(local, 1);
+	/* Note : by default, display the min retry number */
+	if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
+		vwrq->flags = IW_RETRY_LIFETIME;
+		vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024;
+	} else if((vwrq->flags & IW_RETRY_LONG)) {
+		vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
+		vwrq->value = le16_to_cpu(local->config.longRetryLimit);
+	} else {
+		vwrq->flags = IW_RETRY_LIMIT;
+		vwrq->value = le16_to_cpu(local->config.shortRetryLimit);
+		if(local->config.shortRetryLimit != local->config.longRetryLimit)
+			vwrq->flags |= IW_RETRY_SHORT;
+	}
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get range info
+ */
+static int airo_get_range(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_point *dwrq,
+			  char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	struct iw_range *range = (struct iw_range *) extra;
+	CapabilityRid cap_rid;		/* Card capability info */
+	int		i;
+	int		k;
+
+	readCapabilityRid(local, &cap_rid, 1);
+
+	dwrq->length = sizeof(struct iw_range);
+	memset(range, 0, sizeof(*range));
+	range->min_nwid = 0x0000;
+	range->max_nwid = 0x0000;
+	range->num_channels = 14;
+	/* Should be based on cap_rid.country to give only
+	 * what the current card support */
+	k = 0;
+	for(i = 0; i < 14; i++) {
+		range->freq[k].i = i + 1; /* List index */
+		range->freq[k].m = 100000 *
+		     ieee80211_channel_to_frequency(i + 1, IEEE80211_BAND_2GHZ);
+		range->freq[k++].e = 1;	/* Values in MHz -> * 10^5 * 10 */
+	}
+	range->num_frequency = k;
+
+	range->sensitivity = 65535;
+
+	/* Hum... Should put the right values there */
+	if (local->rssi)
+		range->max_qual.qual = 100;	/* % */
+	else
+		range->max_qual.qual = airo_get_max_quality(&cap_rid);
+	range->max_qual.level = 0x100 - 120;	/* -120 dBm */
+	range->max_qual.noise = 0x100 - 120;	/* -120 dBm */
+
+	/* Experimental measurements - boundary 11/5.5 Mb/s */
+	/* Note : with or without the (local->rssi), results
+	 * are somewhat different. - Jean II */
+	if (local->rssi) {
+		range->avg_qual.qual = 50;		/* % */
+		range->avg_qual.level = 0x100 - 70;	/* -70 dBm */
+	} else {
+		range->avg_qual.qual = airo_get_avg_quality(&cap_rid);
+		range->avg_qual.level = 0x100 - 80;	/* -80 dBm */
+	}
+	range->avg_qual.noise = 0x100 - 85;		/* -85 dBm */
+
+	for(i = 0 ; i < 8 ; i++) {
+		range->bitrate[i] = cap_rid.supportedRates[i] * 500000;
+		if(range->bitrate[i] == 0)
+			break;
+	}
+	range->num_bitrates = i;
+
+	/* Set an indication of the max TCP throughput
+	 * in bit/s that we can expect using this interface.
+	 * May be use for QoS stuff... Jean II */
+	if(i > 2)
+		range->throughput = 5000 * 1000;
+	else
+		range->throughput = 1500 * 1000;
+
+	range->min_rts = 0;
+	range->max_rts = AIRO_DEF_MTU;
+	range->min_frag = 256;
+	range->max_frag = AIRO_DEF_MTU;
+
+	if(cap_rid.softCap & cpu_to_le16(2)) {
+		// WEP: RC4 40 bits
+		range->encoding_size[0] = 5;
+		// RC4 ~128 bits
+		if (cap_rid.softCap & cpu_to_le16(0x100)) {
+			range->encoding_size[1] = 13;
+			range->num_encoding_sizes = 2;
+		} else
+			range->num_encoding_sizes = 1;
+		range->max_encoding_tokens =
+			cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1;
+	} else {
+		range->num_encoding_sizes = 0;
+		range->max_encoding_tokens = 0;
+	}
+	range->min_pmp = 0;
+	range->max_pmp = 5000000;	/* 5 secs */
+	range->min_pmt = 0;
+	range->max_pmt = 65535 * 1024;	/* ??? */
+	range->pmp_flags = IW_POWER_PERIOD;
+	range->pmt_flags = IW_POWER_TIMEOUT;
+	range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
+
+	/* Transmit Power - values are in mW */
+	for(i = 0 ; i < 8 ; i++) {
+		range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]);
+		if(range->txpower[i] == 0)
+			break;
+	}
+	range->num_txpower = i;
+	range->txpower_capa = IW_TXPOW_MWATT;
+	range->we_version_source = 19;
+	range->we_version_compiled = WIRELESS_EXT;
+	range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
+	range->retry_flags = IW_RETRY_LIMIT;
+	range->r_time_flags = IW_RETRY_LIFETIME;
+	range->min_retry = 1;
+	range->max_retry = 65535;
+	range->min_r_time = 1024;
+	range->max_r_time = 65535 * 1024;
+
+	/* Event capability (kernel + driver) */
+	range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+				IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
+				IW_EVENT_CAPA_MASK(SIOCGIWAP) |
+				IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
+	range->event_capa[1] = IW_EVENT_CAPA_K_1;
+	range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP);
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Power Management
+ */
+static int airo_set_power(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *vwrq,
+			  char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+
+	readConfigRid(local, 1);
+	if (vwrq->disabled) {
+		if (sniffing_mode(local))
+			return -EINVAL;
+		local->config.powerSaveMode = POWERSAVE_CAM;
+		local->config.rmode &= ~RXMODE_MASK;
+		local->config.rmode |= RXMODE_BC_MC_ADDR;
+		set_bit (FLAG_COMMIT, &local->flags);
+		return -EINPROGRESS;		/* Call commit handler */
+	}
+	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+		local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024);
+		local->config.powerSaveMode = POWERSAVE_PSPCAM;
+		set_bit (FLAG_COMMIT, &local->flags);
+	} else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
+		local->config.fastListenInterval =
+		local->config.listenInterval =
+			cpu_to_le16((vwrq->value + 500) / 1024);
+		local->config.powerSaveMode = POWERSAVE_PSPCAM;
+		set_bit (FLAG_COMMIT, &local->flags);
+	}
+	switch (vwrq->flags & IW_POWER_MODE) {
+		case IW_POWER_UNICAST_R:
+			if (sniffing_mode(local))
+				return -EINVAL;
+			local->config.rmode &= ~RXMODE_MASK;
+			local->config.rmode |= RXMODE_ADDR;
+			set_bit (FLAG_COMMIT, &local->flags);
+			break;
+		case IW_POWER_ALL_R:
+			if (sniffing_mode(local))
+				return -EINVAL;
+			local->config.rmode &= ~RXMODE_MASK;
+			local->config.rmode |= RXMODE_BC_MC_ADDR;
+			set_bit (FLAG_COMMIT, &local->flags);
+		case IW_POWER_ON:
+			/* This is broken, fixme ;-) */
+			break;
+		default:
+			return -EINVAL;
+	}
+	// Note : we may want to factor local->need_commit here
+	// Note2 : may also want to factor RXMODE_RFMON test
+	return -EINPROGRESS;		/* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Power Management
+ */
+static int airo_get_power(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *vwrq,
+			  char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	__le16 mode;
+
+	readConfigRid(local, 1);
+	mode = local->config.powerSaveMode;
+	if ((vwrq->disabled = (mode == POWERSAVE_CAM)))
+		return 0;
+	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+		vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024;
+		vwrq->flags = IW_POWER_TIMEOUT;
+	} else {
+		vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024;
+		vwrq->flags = IW_POWER_PERIOD;
+	}
+	if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR)
+		vwrq->flags |= IW_POWER_UNICAST_R;
+	else
+		vwrq->flags |= IW_POWER_ALL_R;
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Sensitivity
+ */
+static int airo_set_sens(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *vwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+
+	readConfigRid(local, 1);
+	local->config.rssiThreshold =
+		cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value);
+	set_bit (FLAG_COMMIT, &local->flags);
+
+	return -EINPROGRESS;		/* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Sensitivity
+ */
+static int airo_get_sens(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *vwrq,
+			 char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+
+	readConfigRid(local, 1);
+	vwrq->value = le16_to_cpu(local->config.rssiThreshold);
+	vwrq->disabled = (vwrq->value == 0);
+	vwrq->fixed = 1;
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get AP List
+ * Note : this is deprecated in favor of IWSCAN
+ */
+static int airo_get_aplist(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_point *dwrq,
+			   char *extra)
+{
+	struct airo_info *local = dev->ml_priv;
+	struct sockaddr *address = (struct sockaddr *) extra;
+	struct iw_quality *qual;
+	BSSListRid BSSList;
+	int i;
+	int loseSync = capable(CAP_NET_ADMIN) ? 1: -1;
+
+	qual = kmalloc(IW_MAX_AP * sizeof(*qual), GFP_KERNEL);
+	if (!qual)
+		return -ENOMEM;
+
+	for (i = 0; i < IW_MAX_AP; i++) {
+		u16 dBm;
+		if (readBSSListRid(local, loseSync, &BSSList))
+			break;
+		loseSync = 0;
+		memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN);
+		address[i].sa_family = ARPHRD_ETHER;
+		dBm = le16_to_cpu(BSSList.dBm);
+		if (local->rssi) {
+			qual[i].level = 0x100 - dBm;
+			qual[i].qual = airo_dbm_to_pct(local->rssi, dBm);
+			qual[i].updated = IW_QUAL_QUAL_UPDATED
+					| IW_QUAL_LEVEL_UPDATED
+					| IW_QUAL_DBM;
+		} else {
+			qual[i].level = (dBm + 321) / 2;
+			qual[i].qual = 0;
+			qual[i].updated = IW_QUAL_QUAL_INVALID
+					| IW_QUAL_LEVEL_UPDATED
+					| IW_QUAL_DBM;
+		}
+		qual[i].noise = local->wstats.qual.noise;
+		if (BSSList.index == cpu_to_le16(0xffff))
+			break;
+	}
+	if (!i) {
+		StatusRid status_rid;		/* Card status info */
+		readStatusRid(local, &status_rid, 1);
+		for (i = 0;
+		     i < min(IW_MAX_AP, 4) &&
+			     (status_rid.bssid[i][0]
+			      & status_rid.bssid[i][1]
+			      & status_rid.bssid[i][2]
+			      & status_rid.bssid[i][3]
+			      & status_rid.bssid[i][4]
+			      & status_rid.bssid[i][5])!=0xff &&
+			     (status_rid.bssid[i][0]
+			      | status_rid.bssid[i][1]
+			      | status_rid.bssid[i][2]
+			      | status_rid.bssid[i][3]
+			      | status_rid.bssid[i][4]
+			      | status_rid.bssid[i][5]);
+		     i++) {
+			memcpy(address[i].sa_data,
+			       status_rid.bssid[i], ETH_ALEN);
+			address[i].sa_family = ARPHRD_ETHER;
+		}
+	} else {
+		dwrq->flags = 1; /* Should be define'd */
+		memcpy(extra + sizeof(struct sockaddr) * i, qual,
+		       sizeof(struct iw_quality) * i);
+	}
+	dwrq->length = i;
+
+	kfree(qual);
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : Initiate Scan
+ */
+static int airo_set_scan(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_point *dwrq,
+			 char *extra)
+{
+	struct airo_info *ai = dev->ml_priv;
+	Cmd cmd;
+	Resp rsp;
+	int wake = 0;
+
+	/* Note : you may have realised that, as this is a SET operation,
+	 * this is privileged and therefore a normal user can't
+	 * perform scanning.
+	 * This is not an error, while the device perform scanning,
+	 * traffic doesn't flow, so it's a perfect DoS...
+	 * Jean II */
+	if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
+
+	if (down_interruptible(&ai->sem))
+		return -ERESTARTSYS;
+
+	/* If there's already a scan in progress, don't
+	 * trigger another one. */
+	if (ai->scan_timeout > 0)
+		goto out;
+
+	/* Initiate a scan command */
+	ai->scan_timeout = RUN_AT(3*HZ);
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd=CMD_LISTBSS;
+	issuecommand(ai, &cmd, &rsp);
+	wake = 1;
+
+out:
+	up(&ai->sem);
+	if (wake)
+		wake_up_interruptible(&ai->thr_wait);
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Translate scan data returned from the card to a card independent
+ * format that the Wireless Tools will understand - Jean II
+ */
+static inline char *airo_translate_scan(struct net_device *dev,
+					struct iw_request_info *info,
+					char *current_ev,
+					char *end_buf,
+					BSSListRid *bss)
+{
+	struct airo_info *ai = dev->ml_priv;
+	struct iw_event		iwe;		/* Temporary buffer */
+	__le16			capabilities;
+	char *			current_val;	/* For rates */
+	int			i;
+	char *		buf;
+	u16 dBm;
+
+	/* First entry *MUST* be the AP MAC address */
+	iwe.cmd = SIOCGIWAP;
+	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+	memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+	current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+					  &iwe, IW_EV_ADDR_LEN);
+
+	/* Other entries will be displayed in the order we give them */
+
+	/* Add the ESSID */
+	iwe.u.data.length = bss->ssidLen;
+	if(iwe.u.data.length > 32)
+		iwe.u.data.length = 32;
+	iwe.cmd = SIOCGIWESSID;
+	iwe.u.data.flags = 1;
+	current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+					  &iwe, bss->ssid);
+
+	/* Add mode */
+	iwe.cmd = SIOCGIWMODE;
+	capabilities = bss->cap;
+	if(capabilities & (CAP_ESS | CAP_IBSS)) {
+		if(capabilities & CAP_ESS)
+			iwe.u.mode = IW_MODE_MASTER;
+		else
+			iwe.u.mode = IW_MODE_ADHOC;
+		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+						  &iwe, IW_EV_UINT_LEN);
+	}
+
+	/* Add frequency */
+	iwe.cmd = SIOCGIWFREQ;
+	iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
+	iwe.u.freq.m = 100000 *
+	      ieee80211_channel_to_frequency(iwe.u.freq.m, IEEE80211_BAND_2GHZ);
+	iwe.u.freq.e = 1;
+	current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+					  &iwe, IW_EV_FREQ_LEN);
+
+	dBm = le16_to_cpu(bss->dBm);
+
+	/* Add quality statistics */
+	iwe.cmd = IWEVQUAL;
+	if (ai->rssi) {
+		iwe.u.qual.level = 0x100 - dBm;
+		iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm);
+		iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
+				| IW_QUAL_LEVEL_UPDATED
+				| IW_QUAL_DBM;
+	} else {
+		iwe.u.qual.level = (dBm + 321) / 2;
+		iwe.u.qual.qual = 0;
+		iwe.u.qual.updated = IW_QUAL_QUAL_INVALID
+				| IW_QUAL_LEVEL_UPDATED
+				| IW_QUAL_DBM;
+	}
+	iwe.u.qual.noise = ai->wstats.qual.noise;
+	current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+					  &iwe, IW_EV_QUAL_LEN);
+
+	/* Add encryption capability */
+	iwe.cmd = SIOCGIWENCODE;
+	if(capabilities & CAP_PRIVACY)
+		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+	else
+		iwe.u.data.flags = IW_ENCODE_DISABLED;
+	iwe.u.data.length = 0;
+	current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+					  &iwe, bss->ssid);
+
+	/* Rate : stuffing multiple values in a single event require a bit
+	 * more of magic - Jean II */
+	current_val = current_ev + iwe_stream_lcp_len(info);
+
+	iwe.cmd = SIOCGIWRATE;
+	/* Those two flags are ignored... */
+	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+	/* Max 8 values */
+	for(i = 0 ; i < 8 ; i++) {
+		/* NULL terminated */
+		if(bss->rates[i] == 0)
+			break;
+		/* Bit rate given in 500 kb/s units (+ 0x80) */
+		iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000);
+		/* Add new value to event */
+		current_val = iwe_stream_add_value(info, current_ev,
+						   current_val, end_buf,
+						   &iwe, IW_EV_PARAM_LEN);
+	}
+	/* Check if we added any event */
+	if ((current_val - current_ev) > iwe_stream_lcp_len(info))
+		current_ev = current_val;
+
+	/* Beacon interval */
+	buf = kmalloc(30, GFP_KERNEL);
+	if (buf) {
+		iwe.cmd = IWEVCUSTOM;
+		sprintf(buf, "bcn_int=%d", bss->beaconInterval);
+		iwe.u.data.length = strlen(buf);
+		current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+						  &iwe, buf);
+		kfree(buf);
+	}
+
+	/* Put WPA/RSN Information Elements into the event stream */
+	if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
+		unsigned int num_null_ies = 0;
+		u16 length = sizeof (bss->extra.iep);
+		u8 *ie = (void *)&bss->extra.iep;
+
+		while ((length >= 2) && (num_null_ies < 2)) {
+			if (2 + ie[1] > length) {
+				/* Invalid element, don't continue parsing IE */
+				break;
+			}
+
+			switch (ie[0]) {
+			case WLAN_EID_SSID:
+				/* Two zero-length SSID elements
+				 * mean we're done parsing elements */
+				if (!ie[1])
+					num_null_ies++;
+				break;
+
+			case WLAN_EID_VENDOR_SPECIFIC:
+				if (ie[1] >= 4 &&
+				    ie[2] == 0x00 &&
+				    ie[3] == 0x50 &&
+				    ie[4] == 0xf2 &&
+				    ie[5] == 0x01) {
+					iwe.cmd = IWEVGENIE;
+					/* 64 is an arbitrary cut-off */
+					iwe.u.data.length = min(ie[1] + 2,
+								64);
+					current_ev = iwe_stream_add_point(
+							info, current_ev,
+							end_buf, &iwe, ie);
+				}
+				break;
+
+			case WLAN_EID_RSN:
+				iwe.cmd = IWEVGENIE;
+				/* 64 is an arbitrary cut-off */
+				iwe.u.data.length = min(ie[1] + 2, 64);
+				current_ev = iwe_stream_add_point(
+					info, current_ev, end_buf,
+					&iwe, ie);
+				break;
+
+			default:
+				break;
+			}
+
+			length -= 2 + ie[1];
+			ie += 2 + ie[1];
+		}
+	}
+	return current_ev;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : Read Scan Results
+ */
+static int airo_get_scan(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_point *dwrq,
+			 char *extra)
+{
+	struct airo_info *ai = dev->ml_priv;
+	BSSListElement *net;
+	int err = 0;
+	char *current_ev = extra;
+
+	/* If a scan is in-progress, return -EAGAIN */
+	if (ai->scan_timeout > 0)
+		return -EAGAIN;
+
+	if (down_interruptible(&ai->sem))
+		return -EAGAIN;
+
+	list_for_each_entry (net, &ai->network_list, list) {
+		/* Translate to WE format this entry */
+		current_ev = airo_translate_scan(dev, info, current_ev,
+						 extra + dwrq->length,
+						 &net->bss);
+
+		/* Check if there is space for one more entry */
+		if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
+			/* Ask user space to try again with a bigger buffer */
+			err = -E2BIG;
+			goto out;
+		}
+	}
+
+	/* Length of data */
+	dwrq->length = (current_ev - extra);
+	dwrq->flags = 0;	/* todo */
+
+out:
+	up(&ai->sem);
+	return err;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Commit handler : called after a bunch of SET operations
+ */
+static int airo_config_commit(struct net_device *dev,
+			      struct iw_request_info *info,	/* NULL */
+			      void *zwrq,			/* NULL */
+			      char *extra)			/* NULL */
+{
+	struct airo_info *local = dev->ml_priv;
+
+	if (!test_bit (FLAG_COMMIT, &local->flags))
+		return 0;
+
+	/* Some of the "SET" function may have modified some of the
+	 * parameters. It's now time to commit them in the card */
+	disable_MAC(local, 1);
+	if (test_bit (FLAG_RESET, &local->flags)) {
+		APListRid APList_rid;
+		SsidRid SSID_rid;
+
+		readAPListRid(local, &APList_rid);
+		readSsidRid(local, &SSID_rid);
+		if (test_bit(FLAG_MPI,&local->flags))
+			setup_card(local, dev->dev_addr, 1 );
+		else
+			reset_airo_card(dev);
+		disable_MAC(local, 1);
+		writeSsidRid(local, &SSID_rid, 1);
+		writeAPListRid(local, &APList_rid, 1);
+	}
+	if (down_interruptible(&local->sem))
+		return -ERESTARTSYS;
+	writeConfigRid(local, 0);
+	enable_MAC(local, 0);
+	if (test_bit (FLAG_RESET, &local->flags))
+		airo_set_promisc(local);
+	else
+		up(&local->sem);
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Structures to export the Wireless Handlers
+ */
+
+static const struct iw_priv_args airo_private_args[] = {
+/*{ cmd,         set_args,                            get_args, name } */
+  { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
+    IW_PRIV_TYPE_BYTE | 2047, "airoioctl" },
+  { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
+    IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" },
+};
+
+static const iw_handler		airo_handler[] =
+{
+	(iw_handler) airo_config_commit,	/* SIOCSIWCOMMIT */
+	(iw_handler) airo_get_name,		/* SIOCGIWNAME */
+	(iw_handler) NULL,			/* SIOCSIWNWID */
+	(iw_handler) NULL,			/* SIOCGIWNWID */
+	(iw_handler) airo_set_freq,		/* SIOCSIWFREQ */
+	(iw_handler) airo_get_freq,		/* SIOCGIWFREQ */
+	(iw_handler) airo_set_mode,		/* SIOCSIWMODE */
+	(iw_handler) airo_get_mode,		/* SIOCGIWMODE */
+	(iw_handler) airo_set_sens,		/* SIOCSIWSENS */
+	(iw_handler) airo_get_sens,		/* SIOCGIWSENS */
+	(iw_handler) NULL,			/* SIOCSIWRANGE */
+	(iw_handler) airo_get_range,		/* SIOCGIWRANGE */
+	(iw_handler) NULL,			/* SIOCSIWPRIV */
+	(iw_handler) NULL,			/* SIOCGIWPRIV */
+	(iw_handler) NULL,			/* SIOCSIWSTATS */
+	(iw_handler) NULL,			/* SIOCGIWSTATS */
+	iw_handler_set_spy,			/* SIOCSIWSPY */
+	iw_handler_get_spy,			/* SIOCGIWSPY */
+	iw_handler_set_thrspy,			/* SIOCSIWTHRSPY */
+	iw_handler_get_thrspy,			/* SIOCGIWTHRSPY */
+	(iw_handler) airo_set_wap,		/* SIOCSIWAP */
+	(iw_handler) airo_get_wap,		/* SIOCGIWAP */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) airo_get_aplist,		/* SIOCGIWAPLIST */
+	(iw_handler) airo_set_scan,		/* SIOCSIWSCAN */
+	(iw_handler) airo_get_scan,		/* SIOCGIWSCAN */
+	(iw_handler) airo_set_essid,		/* SIOCSIWESSID */
+	(iw_handler) airo_get_essid,		/* SIOCGIWESSID */
+	(iw_handler) airo_set_nick,		/* SIOCSIWNICKN */
+	(iw_handler) airo_get_nick,		/* SIOCGIWNICKN */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) airo_set_rate,		/* SIOCSIWRATE */
+	(iw_handler) airo_get_rate,		/* SIOCGIWRATE */
+	(iw_handler) airo_set_rts,		/* SIOCSIWRTS */
+	(iw_handler) airo_get_rts,		/* SIOCGIWRTS */
+	(iw_handler) airo_set_frag,		/* SIOCSIWFRAG */
+	(iw_handler) airo_get_frag,		/* SIOCGIWFRAG */
+	(iw_handler) airo_set_txpow,		/* SIOCSIWTXPOW */
+	(iw_handler) airo_get_txpow,		/* SIOCGIWTXPOW */
+	(iw_handler) airo_set_retry,		/* SIOCSIWRETRY */
+	(iw_handler) airo_get_retry,		/* SIOCGIWRETRY */
+	(iw_handler) airo_set_encode,		/* SIOCSIWENCODE */
+	(iw_handler) airo_get_encode,		/* SIOCGIWENCODE */
+	(iw_handler) airo_set_power,		/* SIOCSIWPOWER */
+	(iw_handler) airo_get_power,		/* SIOCGIWPOWER */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* SIOCSIWGENIE */
+	(iw_handler) NULL,			/* SIOCGIWGENIE */
+	(iw_handler) airo_set_auth,		/* SIOCSIWAUTH */
+	(iw_handler) airo_get_auth,		/* SIOCGIWAUTH */
+	(iw_handler) airo_set_encodeext,	/* SIOCSIWENCODEEXT */
+	(iw_handler) airo_get_encodeext,	/* SIOCGIWENCODEEXT */
+	(iw_handler) NULL,			/* SIOCSIWPMKSA */
+};
+
+/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here.
+ * We want to force the use of the ioctl code, because those can't be
+ * won't work the iw_handler code (because they simultaneously read
+ * and write data and iw_handler can't do that).
+ * Note that it's perfectly legal to read/write on a single ioctl command,
+ * you just can't use iwpriv and need to force it via the ioctl handler.
+ * Jean II */
+static const iw_handler		airo_private_handler[] =
+{
+	NULL,				/* SIOCIWFIRSTPRIV */
+};
+
+static const struct iw_handler_def	airo_handler_def =
+{
+	.num_standard	= ARRAY_SIZE(airo_handler),
+	.num_private	= ARRAY_SIZE(airo_private_handler),
+	.num_private_args = ARRAY_SIZE(airo_private_args),
+	.standard	= airo_handler,
+	.private	= airo_private_handler,
+	.private_args	= airo_private_args,
+	.get_wireless_stats = airo_get_wireless_stats,
+};
+
+/*
+ * This defines the configuration part of the Wireless Extensions
+ * Note : irq and spinlock protection will occur in the subroutines
+ *
+ * TODO :
+ *	o Check input value more carefully and fill correct values in range
+ *	o Test and shakeout the bugs (if any)
+ *
+ * Jean II
+ *
+ * Javier Achirica did a great job of merging code from the unnamed CISCO
+ * developer that added support for flashing the card.
+ */
+static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+	struct airo_info *ai = dev->ml_priv;
+
+	if (ai->power.event)
+		return 0;
+
+	switch (cmd) {
+#ifdef CISCO_EXT
+	case AIROIDIFC:
+#ifdef AIROOLDIDIFC
+	case AIROOLDIDIFC:
+#endif
+	{
+		int val = AIROMAGIC;
+		aironet_ioctl com;
+		if (copy_from_user(&com,rq->ifr_data,sizeof(com)))
+			rc = -EFAULT;
+		else if (copy_to_user(com.data,(char *)&val,sizeof(val)))
+			rc = -EFAULT;
+	}
+	break;
+
+	case AIROIOCTL:
+#ifdef AIROOLDIOCTL
+	case AIROOLDIOCTL:
+#endif
+		/* Get the command struct and hand it off for evaluation by
+		 * the proper subfunction
+		 */
+	{
+		aironet_ioctl com;
+		if (copy_from_user(&com,rq->ifr_data,sizeof(com))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		/* Separate R/W functions bracket legality here
+		 */
+		if ( com.command == AIRORSWVERSION ) {
+			if (copy_to_user(com.data, swversion, sizeof(swversion)))
+				rc = -EFAULT;
+			else
+				rc = 0;
+		}
+		else if ( com.command <= AIRORRID)
+			rc = readrids(dev,&com);
+		else if ( com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2) )
+			rc = writerids(dev,&com);
+		else if ( com.command >= AIROFLSHRST && com.command <= AIRORESTART )
+			rc = flashcard(dev,&com);
+		else
+			rc = -EINVAL;      /* Bad command in ioctl */
+	}
+	break;
+#endif /* CISCO_EXT */
+
+	// All other calls are currently unsupported
+	default:
+		rc = -EOPNOTSUPP;
+	}
+	return rc;
+}
+
+/*
+ * Get the Wireless stats out of the driver
+ * Note : irq and spinlock protection will occur in the subroutines
+ *
+ * TODO :
+ *	o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs)
+ *
+ * Jean
+ */
+static void airo_read_wireless_stats(struct airo_info *local)
+{
+	StatusRid status_rid;
+	StatsRid stats_rid;
+	CapabilityRid cap_rid;
+	__le32 *vals = stats_rid.vals;
+
+	/* Get stats out of the card */
+	clear_bit(JOB_WSTATS, &local->jobs);
+	if (local->power.event) {
+		up(&local->sem);
+		return;
+	}
+	readCapabilityRid(local, &cap_rid, 0);
+	readStatusRid(local, &status_rid, 0);
+	readStatsRid(local, &stats_rid, RID_STATS, 0);
+	up(&local->sem);
+
+	/* The status */
+	local->wstats.status = le16_to_cpu(status_rid.mode);
+
+	/* Signal quality and co */
+	if (local->rssi) {
+		local->wstats.qual.level =
+			airo_rssi_to_dbm(local->rssi,
+					 le16_to_cpu(status_rid.sigQuality));
+		/* normalizedSignalStrength appears to be a percentage */
+		local->wstats.qual.qual =
+			le16_to_cpu(status_rid.normalizedSignalStrength);
+	} else {
+		local->wstats.qual.level =
+			(le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2;
+		local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid);
+	}
+	if (le16_to_cpu(status_rid.len) >= 124) {
+		local->wstats.qual.noise = 0x100 - status_rid.noisedBm;
+		local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+	} else {
+		local->wstats.qual.noise = 0;
+		local->wstats.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID | IW_QUAL_DBM;
+	}
+
+	/* Packets discarded in the wireless adapter due to wireless
+	 * specific problems */
+	local->wstats.discard.nwid = le32_to_cpu(vals[56]) +
+				     le32_to_cpu(vals[57]) +
+				     le32_to_cpu(vals[58]); /* SSID Mismatch */
+	local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */
+	local->wstats.discard.fragment = le32_to_cpu(vals[30]);
+	local->wstats.discard.retries = le32_to_cpu(vals[10]);
+	local->wstats.discard.misc = le32_to_cpu(vals[1]) +
+				     le32_to_cpu(vals[32]);
+	local->wstats.miss.beacon = le32_to_cpu(vals[34]);
+}
+
+static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
+{
+	struct airo_info *local =  dev->ml_priv;
+
+	if (!test_bit(JOB_WSTATS, &local->jobs)) {
+		/* Get stats out of the card if available */
+		if (down_trylock(&local->sem) != 0) {
+			set_bit(JOB_WSTATS, &local->jobs);
+			wake_up_interruptible(&local->thr_wait);
+		} else
+			airo_read_wireless_stats(local);
+	}
+
+	return &local->wstats;
+}
+
+#ifdef CISCO_EXT
+/*
+ * This just translates from driver IOCTL codes to the command codes to
+ * feed to the radio's host interface. Things can be added/deleted
+ * as needed.  This represents the READ side of control I/O to
+ * the card
+ */
+static int readrids(struct net_device *dev, aironet_ioctl *comp) {
+	unsigned short ridcode;
+	unsigned char *iobuf;
+	int len;
+	struct airo_info *ai = dev->ml_priv;
+
+	if (test_bit(FLAG_FLASHING, &ai->flags))
+		return -EIO;
+
+	switch(comp->command)
+	{
+	case AIROGCAP:      ridcode = RID_CAPABILITIES; break;
+	case AIROGCFG:      ridcode = RID_CONFIG;
+		if (test_bit(FLAG_COMMIT, &ai->flags)) {
+			disable_MAC (ai, 1);
+			writeConfigRid (ai, 1);
+			enable_MAC(ai, 1);
+		}
+		break;
+	case AIROGSLIST:    ridcode = RID_SSID;         break;
+	case AIROGVLIST:    ridcode = RID_APLIST;       break;
+	case AIROGDRVNAM:   ridcode = RID_DRVNAME;      break;
+	case AIROGEHTENC:   ridcode = RID_ETHERENCAP;   break;
+	case AIROGWEPKTMP:  ridcode = RID_WEP_TEMP;
+		/* Only super-user can read WEP keys */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		break;
+	case AIROGWEPKNV:   ridcode = RID_WEP_PERM;
+		/* Only super-user can read WEP keys */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		break;
+	case AIROGSTAT:     ridcode = RID_STATUS;       break;
+	case AIROGSTATSD32: ridcode = RID_STATSDELTA;   break;
+	case AIROGSTATSC32: ridcode = RID_STATS;        break;
+	case AIROGMICSTATS:
+		if (copy_to_user(comp->data, &ai->micstats,
+				 min((int)comp->len,(int)sizeof(ai->micstats))))
+			return -EFAULT;
+		return 0;
+	case AIRORRID:      ridcode = comp->ridnum;     break;
+	default:
+		return -EINVAL;
+	}
+
+	if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	PC4500_readrid(ai,ridcode,iobuf,RIDSIZE, 1);
+	/* get the count of bytes in the rid  docs say 1st 2 bytes is it.
+	 * then return it to the user
+	 * 9/22/2000 Honor user given length
+	 */
+	len = comp->len;
+
+	if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) {
+		kfree (iobuf);
+		return -EFAULT;
+	}
+	kfree (iobuf);
+	return 0;
+}
+
+/*
+ * Danger Will Robinson write the rids here
+ */
+
+static int writerids(struct net_device *dev, aironet_ioctl *comp) {
+	struct airo_info *ai = dev->ml_priv;
+	int  ridcode;
+        int  enabled;
+	static int (* writer)(struct airo_info *, u16 rid, const void *, int, int);
+	unsigned char *iobuf;
+
+	/* Only super-user can write RIDs */
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	if (test_bit(FLAG_FLASHING, &ai->flags))
+		return -EIO;
+
+	ridcode = 0;
+	writer = do_writerid;
+
+	switch(comp->command)
+	{
+	case AIROPSIDS:     ridcode = RID_SSID;         break;
+	case AIROPCAP:      ridcode = RID_CAPABILITIES; break;
+	case AIROPAPLIST:   ridcode = RID_APLIST;       break;
+	case AIROPCFG: ai->config.len = 0;
+			    clear_bit(FLAG_COMMIT, &ai->flags);
+			    ridcode = RID_CONFIG;       break;
+	case AIROPWEPKEYNV: ridcode = RID_WEP_PERM;     break;
+	case AIROPLEAPUSR:  ridcode = RID_LEAPUSERNAME; break;
+	case AIROPLEAPPWD:  ridcode = RID_LEAPPASSWORD; break;
+	case AIROPWEPKEY:   ridcode = RID_WEP_TEMP; writer = PC4500_writerid;
+		break;
+	case AIROPLEAPUSR+1: ridcode = 0xFF2A;          break;
+	case AIROPLEAPUSR+2: ridcode = 0xFF2B;          break;
+
+		/* this is not really a rid but a command given to the card
+		 * same with MAC off
+		 */
+	case AIROPMACON:
+		if (enable_MAC(ai, 1) != 0)
+			return -EIO;
+		return 0;
+
+		/*
+		 * Evidently this code in the airo driver does not get a symbol
+		 * as disable_MAC. it's probably so short the compiler does not gen one.
+		 */
+	case AIROPMACOFF:
+		disable_MAC(ai, 1);
+		return 0;
+
+		/* This command merely clears the counts does not actually store any data
+		 * only reads rid. But as it changes the cards state, I put it in the
+		 * writerid routines.
+		 */
+	case AIROPSTCLR:
+		if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+
+		PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDSIZE, 1);
+
+		enabled = ai->micstats.enabled;
+		memset(&ai->micstats,0,sizeof(ai->micstats));
+		ai->micstats.enabled = enabled;
+
+		if (copy_to_user(comp->data, iobuf,
+				 min((int)comp->len, (int)RIDSIZE))) {
+			kfree (iobuf);
+			return -EFAULT;
+		}
+		kfree (iobuf);
+		return 0;
+
+	default:
+		return -EOPNOTSUPP;	/* Blarg! */
+	}
+	if(comp->len > RIDSIZE)
+		return -EINVAL;
+
+	if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	if (copy_from_user(iobuf,comp->data,comp->len)) {
+		kfree (iobuf);
+		return -EFAULT;
+	}
+
+	if (comp->command == AIROPCFG) {
+		ConfigRid *cfg = (ConfigRid *)iobuf;
+
+		if (test_bit(FLAG_MIC_CAPABLE, &ai->flags))
+			cfg->opmode |= MODE_MIC;
+
+		if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
+			set_bit (FLAG_ADHOC, &ai->flags);
+		else
+			clear_bit (FLAG_ADHOC, &ai->flags);
+	}
+
+	if((*writer)(ai, ridcode, iobuf,comp->len,1)) {
+		kfree (iobuf);
+		return -EIO;
+	}
+	kfree (iobuf);
+	return 0;
+}
+
+/*****************************************************************************
+ * Ancillary flash / mod functions much black magic lurkes here              *
+ *****************************************************************************
+ */
+
+/*
+ * Flash command switch table
+ */
+
+static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
+	int z;
+
+	/* Only super-user can modify flash */
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	switch(comp->command)
+	{
+	case AIROFLSHRST:
+		return cmdreset((struct airo_info *)dev->ml_priv);
+
+	case AIROFLSHSTFL:
+		if (!AIRO_FLASH(dev) &&
+		    (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+		return setflashmode((struct airo_info *)dev->ml_priv);
+
+	case AIROFLSHGCHR: /* Get char from aux */
+		if(comp->len != sizeof(int))
+			return -EINVAL;
+		if (copy_from_user(&z,comp->data,comp->len))
+			return -EFAULT;
+		return flashgchar((struct airo_info *)dev->ml_priv, z, 8000);
+
+	case AIROFLSHPCHR: /* Send char to card. */
+		if(comp->len != sizeof(int))
+			return -EINVAL;
+		if (copy_from_user(&z,comp->data,comp->len))
+			return -EFAULT;
+		return flashpchar((struct airo_info *)dev->ml_priv, z, 8000);
+
+	case AIROFLPUTBUF: /* Send 32k to card */
+		if (!AIRO_FLASH(dev))
+			return -ENOMEM;
+		if(comp->len > FLASHSIZE)
+			return -EINVAL;
+		if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len))
+			return -EFAULT;
+
+		flashputbuf((struct airo_info *)dev->ml_priv);
+		return 0;
+
+	case AIRORESTART:
+		if (flashrestart((struct airo_info *)dev->ml_priv, dev))
+			return -EIO;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+#define FLASH_COMMAND  0x7e7e
+
+/*
+ * STEP 1)
+ * Disable MAC and do soft reset on
+ * card.
+ */
+
+static int cmdreset(struct airo_info *ai) {
+	disable_MAC(ai, 1);
+
+	if(!waitbusy (ai)){
+		airo_print_info(ai->dev->name, "Waitbusy hang before RESET");
+		return -EBUSY;
+	}
+
+	OUT4500(ai,COMMAND,CMD_SOFTRESET);
+
+	ssleep(1);			/* WAS 600 12/7/00 */
+
+	if(!waitbusy (ai)){
+		airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET");
+		return -EBUSY;
+	}
+	return 0;
+}
+
+/* STEP 2)
+ * Put the card in legendary flash
+ * mode
+ */
+
+static int setflashmode (struct airo_info *ai) {
+	set_bit (FLAG_FLASHING, &ai->flags);
+
+	OUT4500(ai, SWS0, FLASH_COMMAND);
+	OUT4500(ai, SWS1, FLASH_COMMAND);
+	if (probe) {
+		OUT4500(ai, SWS0, FLASH_COMMAND);
+		OUT4500(ai, COMMAND,0x10);
+	} else {
+		OUT4500(ai, SWS2, FLASH_COMMAND);
+		OUT4500(ai, SWS3, FLASH_COMMAND);
+		OUT4500(ai, COMMAND,0);
+	}
+	msleep(500);		/* 500ms delay */
+
+	if(!waitbusy(ai)) {
+		clear_bit (FLAG_FLASHING, &ai->flags);
+		airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode");
+		return -EIO;
+	}
+	return 0;
+}
+
+/* Put character to SWS0 wait for dwelltime
+ * x 50us for  echo .
+ */
+
+static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
+	int echo;
+	int waittime;
+
+	byte |= 0x8000;
+
+	if(dwelltime == 0 )
+		dwelltime = 200;
+
+	waittime=dwelltime;
+
+	/* Wait for busy bit d15 to go false indicating buffer empty */
+	while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) {
+		udelay (50);
+		waittime -= 50;
+	}
+
+	/* timeout for busy clear wait */
+	if(waittime <= 0 ){
+		airo_print_info(ai->dev->name, "flash putchar busywait timeout!");
+		return -EBUSY;
+	}
+
+	/* Port is clear now write byte and wait for it to echo back */
+	do {
+		OUT4500(ai,SWS0,byte);
+		udelay(50);
+		dwelltime -= 50;
+		echo = IN4500(ai,SWS1);
+	} while (dwelltime >= 0 && echo != byte);
+
+	OUT4500(ai,SWS1,0);
+
+	return (echo == byte) ? 0 : -EIO;
+}
+
+/*
+ * Get a character from the card matching matchbyte
+ * Step 3)
+ */
+static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
+	int           rchar;
+	unsigned char rbyte=0;
+
+	do {
+		rchar = IN4500(ai,SWS1);
+
+		if(dwelltime && !(0x8000 & rchar)){
+			dwelltime -= 10;
+			mdelay(10);
+			continue;
+		}
+		rbyte = 0xff & rchar;
+
+		if( (rbyte == matchbyte) && (0x8000 & rchar) ){
+			OUT4500(ai,SWS1,0);
+			return 0;
+		}
+		if( rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar)
+			break;
+		OUT4500(ai,SWS1,0);
+
+	}while(dwelltime > 0);
+	return -EIO;
+}
+
+/*
+ * Transfer 32k of firmware data from user buffer to our buffer and
+ * send to the card
+ */
+
+static int flashputbuf(struct airo_info *ai){
+	int            nwords;
+
+	/* Write stuff */
+	if (test_bit(FLAG_MPI,&ai->flags))
+		memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE);
+	else {
+		OUT4500(ai,AUXPAGE,0x100);
+		OUT4500(ai,AUXOFF,0);
+
+		for(nwords=0;nwords != FLASHSIZE / 2;nwords++){
+			OUT4500(ai,AUXDATA,ai->flash[nwords] & 0xffff);
+		}
+	}
+	OUT4500(ai,SWS0,0x8000);
+
+	return 0;
+}
+
+/*
+ *
+ */
+static int flashrestart(struct airo_info *ai,struct net_device *dev){
+	int    i,status;
+
+	ssleep(1);			/* Added 12/7/00 */
+	clear_bit (FLAG_FLASHING, &ai->flags);
+	if (test_bit(FLAG_MPI, &ai->flags)) {
+		status = mpi_init_descriptors(ai);
+		if (status != SUCCESS)
+			return status;
+	}
+	status = setup_card(ai, dev->dev_addr, 1);
+
+	if (!test_bit(FLAG_MPI,&ai->flags))
+		for( i = 0; i < MAX_FIDS; i++ ) {
+			ai->fids[i] = transmit_allocate
+				( ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2 );
+		}
+
+	ssleep(1);			/* Added 12/7/00 */
+	return status;
+}
+#endif /* CISCO_EXT */
+
+/*
+    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.
+
+    In addition:
+
+    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. The name of the author may not be used to endorse or promote
+       products derived from this software without specific prior written
+       permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+*/
+
+module_init(airo_init_module);
+module_exit(airo_cleanup_module);
diff --git a/drivers/net/wireless/airo.h b/drivers/net/wireless/airo.h
new file mode 100644
index 0000000..e480adf
--- /dev/null
+++ b/drivers/net/wireless/airo.h
@@ -0,0 +1,9 @@
+#ifndef _AIRO_H_
+#define _AIRO_H_
+
+struct net_device *init_airo_card(unsigned short irq, int port, int is_pcmcia,
+				  struct device *dmdev);
+int reset_airo_card(struct net_device *dev);
+void stop_airo_card(struct net_device *dev, int freeres);
+
+#endif  /*  _AIRO_H_  */
diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c
new file mode 100644
index 0000000..d9ed22b
--- /dev/null
+++ b/drivers/net/wireless/airo_cs.c
@@ -0,0 +1,222 @@
+/*======================================================================
+
+    Aironet driver for 4500 and 4800 series cards
+
+    This code is released under both the GPL version 2 and BSD licenses.
+    Either license may be used.  The respective licenses are found at
+    the end of this file.
+
+    This code was developed by Benjamin Reed <breed@users.sourceforge.net>
+    including portions of which come from the Aironet PC4500
+    Developer's Reference Manual and used with permission.  Copyright
+    (C) 1999 Benjamin Reed.  All Rights Reserved.  Permission to use
+    code in the Developer's manual was granted for this driver by
+    Aironet.
+
+    In addition this module was derived from dummy_cs.
+    The initial developer of dummy_cs is David A. Hinds
+    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+======================================================================*/
+
+#ifdef __IN_PCMCIA_PACKAGE__
+#include <pcmcia/k_compat.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/netdevice.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include <linux/io.h>
+
+#include "airo.h"
+
+
+/*====================================================================*/
+
+MODULE_AUTHOR("Benjamin Reed");
+MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet "
+		   "cards.  This is the module that links the PCMCIA card "
+		   "with the airo module.");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340 PCMCIA cards");
+
+/*====================================================================*/
+
+static int airo_config(struct pcmcia_device *link);
+static void airo_release(struct pcmcia_device *link);
+
+static void airo_detach(struct pcmcia_device *p_dev);
+
+struct local_info {
+	struct net_device *eth_dev;
+};
+
+static int airo_probe(struct pcmcia_device *p_dev)
+{
+	struct local_info *local;
+
+	dev_dbg(&p_dev->dev, "airo_attach()\n");
+
+	/* Allocate space for private device-specific data */
+	local = kzalloc(sizeof(*local), GFP_KERNEL);
+	if (!local)
+		return -ENOMEM;
+
+	p_dev->priv = local;
+
+	return airo_config(p_dev);
+} /* airo_attach */
+
+static void airo_detach(struct pcmcia_device *link)
+{
+	dev_dbg(&link->dev, "airo_detach\n");
+
+	airo_release(link);
+
+	if (((struct local_info *)link->priv)->eth_dev) {
+		stop_airo_card(((struct local_info *)link->priv)->eth_dev,
+			       0);
+	}
+	((struct local_info *)link->priv)->eth_dev = NULL;
+
+	kfree(link->priv);
+} /* airo_detach */
+
+static int airo_cs_config_check(struct pcmcia_device *p_dev, void *priv_data)
+{
+	if (p_dev->config_index == 0)
+		return -EINVAL;
+
+	return pcmcia_request_io(p_dev);
+}
+
+
+static int airo_config(struct pcmcia_device *link)
+{
+	struct local_info *dev;
+	int ret;
+
+	dev = link->priv;
+
+	dev_dbg(&link->dev, "airo_config\n");
+
+	link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
+		CONF_AUTO_AUDIO | CONF_AUTO_SET_IO;
+
+	ret = pcmcia_loop_config(link, airo_cs_config_check, NULL);
+	if (ret)
+		goto failed;
+
+	if (!link->irq)
+		goto failed;
+
+	ret = pcmcia_enable_device(link);
+	if (ret)
+		goto failed;
+	((struct local_info *)link->priv)->eth_dev =
+		init_airo_card(link->irq,
+			       link->resource[0]->start, 1, &link->dev);
+	if (!((struct local_info *)link->priv)->eth_dev)
+		goto failed;
+
+	return 0;
+
+ failed:
+	airo_release(link);
+	return -ENODEV;
+} /* airo_config */
+
+static void airo_release(struct pcmcia_device *link)
+{
+	dev_dbg(&link->dev, "airo_release\n");
+	pcmcia_disable_device(link);
+}
+
+static int airo_suspend(struct pcmcia_device *link)
+{
+	struct local_info *local = link->priv;
+
+	netif_device_detach(local->eth_dev);
+
+	return 0;
+}
+
+static int airo_resume(struct pcmcia_device *link)
+{
+	struct local_info *local = link->priv;
+
+	if (link->open) {
+		reset_airo_card(local->eth_dev);
+		netif_device_attach(local->eth_dev);
+	}
+
+	return 0;
+}
+
+static const struct pcmcia_device_id airo_ids[] = {
+	PCMCIA_DEVICE_MANF_CARD(0x015f, 0x000a),
+	PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0005),
+	PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0007),
+	PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0007),
+	PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, airo_ids);
+
+static struct pcmcia_driver airo_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "airo_cs",
+	.probe		= airo_probe,
+	.remove		= airo_detach,
+	.id_table       = airo_ids,
+	.suspend	= airo_suspend,
+	.resume		= airo_resume,
+};
+module_pcmcia_driver(airo_driver);
+
+/*
+    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.
+
+    In addition:
+
+    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. The name of the author may not be used to endorse or promote
+       products derived from this software without specific prior written
+       permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+*/
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index 395b4c0..4fbfa46 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -1166,7 +1166,7 @@
 	int ret;
 
 	memset(&scan, 0, sizeof(struct at76_req_scan));
-	memset(scan.bssid, 0xff, ETH_ALEN);
+	eth_broadcast_addr(scan.bssid);
 
 	scan.channel = priv->channel;
 	scan.scan_type = SCAN_TYPE_PASSIVE;
@@ -1427,7 +1427,7 @@
 	at76_wait_completion(priv, CMD_STARTUP);
 
 	/* remove BSSID from previous run */
-	memset(priv->bssid, 0, ETH_ALEN);
+	eth_zero_addr(priv->bssid);
 
 	priv->scanning = false;
 
@@ -1973,7 +1973,7 @@
 	ieee80211_stop_queues(hw);
 
 	memset(&scan, 0, sizeof(struct at76_req_scan));
-	memset(scan.bssid, 0xFF, ETH_ALEN);
+	eth_broadcast_addr(scan.bssid);
 
 	if (req->n_ssids) {
 		scan.scan_type = SCAN_TYPE_ACTIVE;
diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c
index f920506..5147ebe 100644
--- a/drivers/net/wireless/ath/ar5523/ar5523.c
+++ b/drivers/net/wireless/ath/ar5523/ar5523.c
@@ -779,8 +779,6 @@
 		ieee80211_stop_queues(hw);
 	}
 
-	data->skb = skb;
-
 	spin_lock_irqsave(&ar->tx_data_list_lock, flags);
 	list_add_tail(&data->list, &ar->tx_queue_pending);
 	spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
@@ -817,10 +815,13 @@
 		if (!data)
 			break;
 
-		skb = data->skb;
+		txi = container_of((void *)data, struct ieee80211_tx_info,
+				   driver_data);
 		txqid = 0;
-		txi = IEEE80211_SKB_CB(skb);
+
+		skb = container_of((void *)txi, struct sk_buff, cb);
 		paylen = skb->len;
+
 		urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (!urb) {
 			ar5523_err(ar, "Failed to allocate TX urb\n");
diff --git a/drivers/net/wireless/ath/ar5523/ar5523.h b/drivers/net/wireless/ath/ar5523/ar5523.h
index 00c6fd3..9a322a6 100644
--- a/drivers/net/wireless/ath/ar5523/ar5523.h
+++ b/drivers/net/wireless/ath/ar5523/ar5523.h
@@ -74,7 +74,6 @@
 struct ar5523_tx_data {
 	struct list_head	list;
 	struct ar5523		*ar;
-	struct sk_buff		*skb;
 	struct urb		*urb;
 };
 
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index 6d58a3e..9fd9890 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -64,6 +64,7 @@
 	ATH_OP_HW_RESET,
 	ATH_OP_SCANNING,
 	ATH_OP_MULTI_CHANNEL,
+	ATH_OP_WOW_ENABLED,
 };
 
 enum ath_bus_type {
@@ -130,6 +131,9 @@
 	void (*enable_write_buffer)(void *);
 	void (*write_flush) (void *);
 	u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr);
+	void (*enable_rmw_buffer)(void *);
+	void (*rmw_flush) (void *);
+
 };
 
 struct ath_common;
diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
index 49fab91..9f7225f 100644
--- a/drivers/net/wireless/ath/ath10k/Makefile
+++ b/drivers/net/wireless/ath/ath10k/Makefile
@@ -8,11 +8,15 @@
 		 htt_tx.o \
 		 txrx.o \
 		 wmi.o \
-		 bmi.o
+		 wmi-tlv.o \
+		 bmi.o \
+		 hw.o
 
 ath10k_core-$(CPTCFG_ATH10K_DEBUGFS) += spectral.o
 ath10k_core-$(CPTCFG_NL80211_TESTMODE) += testmode.o
 ath10k_core-$(CPTCFG_ATH10K_TRACING) += trace.o
+ath10k_core-$(CONFIG_THERMAL) += thermal.o
+ath10k_core-$(CPTCFG_MAC80211_DEBUGFS) += debugfs_sta.o
 
 obj-$(CPTCFG_ATH10K_PCI) += ath10k_pci.o
 ath10k_pci-y += pci.o \
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index a156e6e..e508c65 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -803,7 +803,7 @@
 	int ce_id;
 
 	for (ce_id = 0; ce_id < CE_COUNT; ce_id++) {
-		u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+		u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
 
 		ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
 		ath10k_ce_error_intr_disable(ar, ctrl_addr);
@@ -832,7 +832,7 @@
 	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 *src_ring = ce_state->src_ring;
-	u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id);
+	u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id);
 
 	nentries = roundup_pow_of_two(attr->src_nentries);
 
@@ -869,7 +869,7 @@
 	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);
+	u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id);
 
 	nentries = roundup_pow_of_two(attr->dest_nentries);
 
@@ -1051,7 +1051,7 @@
 
 static void ath10k_ce_deinit_src_ring(struct ath10k *ar, unsigned int ce_id)
 {
-	u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+	u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
 
 	ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, 0);
 	ath10k_ce_src_ring_size_set(ar, ctrl_addr, 0);
@@ -1061,7 +1061,7 @@
 
 static void ath10k_ce_deinit_dest_ring(struct ath10k *ar, unsigned int ce_id)
 {
-	u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+	u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
 
 	ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, 0);
 	ath10k_ce_dest_ring_size_set(ar, ctrl_addr, 0);
@@ -1093,10 +1093,12 @@
 		     (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
 	BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC >
 		     (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
+	BUILD_BUG_ON(2*TARGET_TLV_NUM_MSDU_DESC >
+		     (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
 
 	ce_state->ar = ar;
 	ce_state->id = ce_id;
-	ce_state->ctrl_addr = ath10k_ce_base_address(ce_id);
+	ce_state->ctrl_addr = ath10k_ce_base_address(ar, ce_id);
 	ce_state->attr_flags = attr->flags;
 	ce_state->src_sz_max = attr->src_sz_max;
 
diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
index 617a151..0eddb20 100644
--- a/drivers/net/wireless/ath/ath10k/ce.h
+++ b/drivers/net/wireless/ath/ath10k/ce.h
@@ -39,7 +39,7 @@
 #define CE_DESC_FLAGS_GATHER         (1 << 0)
 #define CE_DESC_FLAGS_BYTE_SWAP      (1 << 1)
 #define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC
-#define CE_DESC_FLAGS_META_DATA_LSB  3
+#define CE_DESC_FLAGS_META_DATA_LSB  2
 
 struct ce_desc {
 	__le32 addr;
@@ -394,7 +394,7 @@
 #define DST_WATERMARK_HIGH_RESET		0
 #define DST_WATERMARK_ADDRESS			0x0050
 
-static inline u32 ath10k_ce_base_address(unsigned int ce_id)
+static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
 {
 	return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
 }
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 7762061..c0e454b 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -17,6 +17,7 @@
 
 #include <linux/module.h>
 #include <linux/firmware.h>
+#include <linux/of.h>
 
 #include "core.h"
 #include "mac.h"
@@ -27,20 +28,18 @@
 #include "debug.h"
 #include "htt.h"
 #include "testmode.h"
+#include "wmi-ops.h"
 
 unsigned int ath10k_debug_mask;
 static bool uart_print;
-static unsigned int ath10k_p2p;
 static bool skip_otp;
 
 module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
 module_param(uart_print, bool, 0644);
-module_param_named(p2p, ath10k_p2p, uint, 0644);
 module_param(skip_otp, bool, 0644);
 
 MODULE_PARM_DESC(debug_mask, "Debugging mask");
 MODULE_PARM_DESC(uart_print, "Uart target debugging");
-MODULE_PARM_DESC(p2p, "Enable ath10k P2P support");
 MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode");
 
 static const struct ath10k_hw_params ath10k_hw_params_list[] = {
@@ -48,11 +47,57 @@
 		.id = QCA988X_HW_2_0_VERSION,
 		.name = "qca988x hw2.0",
 		.patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
+		.uart_pin = 7,
 		.fw = {
 			.dir = QCA988X_HW_2_0_FW_DIR,
 			.fw = QCA988X_HW_2_0_FW_FILE,
 			.otp = QCA988X_HW_2_0_OTP_FILE,
 			.board = QCA988X_HW_2_0_BOARD_DATA_FILE,
+			.board_size = QCA988X_BOARD_DATA_SZ,
+			.board_ext_size = QCA988X_BOARD_EXT_DATA_SZ,
+		},
+	},
+	{
+		.id = QCA6174_HW_2_1_VERSION,
+		.name = "qca6174 hw2.1",
+		.patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR,
+		.uart_pin = 6,
+		.fw = {
+			.dir = QCA6174_HW_2_1_FW_DIR,
+			.fw = QCA6174_HW_2_1_FW_FILE,
+			.otp = QCA6174_HW_2_1_OTP_FILE,
+			.board = QCA6174_HW_2_1_BOARD_DATA_FILE,
+			.board_size = QCA6174_BOARD_DATA_SZ,
+			.board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
+		},
+	},
+	{
+		.id = QCA6174_HW_3_0_VERSION,
+		.name = "qca6174 hw3.0",
+		.patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
+		.uart_pin = 6,
+		.fw = {
+			.dir = QCA6174_HW_3_0_FW_DIR,
+			.fw = QCA6174_HW_3_0_FW_FILE,
+			.otp = QCA6174_HW_3_0_OTP_FILE,
+			.board = QCA6174_HW_3_0_BOARD_DATA_FILE,
+			.board_size = QCA6174_BOARD_DATA_SZ,
+			.board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
+		},
+	},
+	{
+		.id = QCA6174_HW_3_2_VERSION,
+		.name = "qca6174 hw3.2",
+		.patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
+		.uart_pin = 6,
+		.fw = {
+			/* uses same binaries as hw3.0 */
+			.dir = QCA6174_HW_3_0_FW_DIR,
+			.fw = QCA6174_HW_3_0_FW_FILE,
+			.otp = QCA6174_HW_3_0_OTP_FILE,
+			.board = QCA6174_HW_3_0_BOARD_DATA_FILE,
+			.board_size = QCA6174_BOARD_DATA_SZ,
+			.board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
 		},
 	},
 };
@@ -146,8 +191,8 @@
 static int ath10k_push_board_ext_data(struct ath10k *ar, const void *data,
 				      size_t data_len)
 {
-	u32 board_data_size = QCA988X_BOARD_DATA_SZ;
-	u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ;
+	u32 board_data_size = ar->hw_params.fw.board_size;
+	u32 board_ext_data_size = ar->hw_params.fw.board_ext_size;
 	u32 board_ext_data_addr;
 	int ret;
 
@@ -193,7 +238,7 @@
 static int ath10k_download_board_data(struct ath10k *ar, const void *data,
 				      size_t data_len)
 {
-	u32 board_data_size = QCA988X_BOARD_DATA_SZ;
+	u32 board_data_size = ar->hw_params.fw.board_size;
 	u32 address;
 	int ret;
 
@@ -249,6 +294,63 @@
 	return 0;
 }
 
+static int ath10k_download_cal_dt(struct ath10k *ar)
+{
+	struct device_node *node;
+	int data_len;
+	void *data;
+	int ret;
+
+	node = ar->dev->of_node;
+	if (!node)
+		/* Device Tree is optional, don't print any warnings if
+		 * there's no node for ath10k.
+		 */
+		return -ENOENT;
+
+	if (!of_get_property(node, "qcom,ath10k-calibration-data",
+			     &data_len)) {
+		/* The calibration data node is optional */
+		return -ENOENT;
+	}
+
+	if (data_len != QCA988X_CAL_DATA_LEN) {
+		ath10k_warn(ar, "invalid calibration data length in DT: %d\n",
+			    data_len);
+		ret = -EMSGSIZE;
+		goto out;
+	}
+
+	data = kmalloc(data_len, GFP_KERNEL);
+	if (!data) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = of_property_read_u8_array(node, "qcom,ath10k-calibration-data",
+					data, data_len);
+	if (ret) {
+		ath10k_warn(ar, "failed to read calibration data from DT: %d\n",
+			    ret);
+		goto out_free;
+	}
+
+	ret = ath10k_download_board_data(ar, data, data_len);
+	if (ret) {
+		ath10k_warn(ar, "failed to download calibration data from Device Tree: %d\n",
+			    ret);
+		goto out_free;
+	}
+
+	ret = 0;
+
+out_free:
+	kfree(data);
+
+out:
+	return ret;
+}
+
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
 	u32 result, address = ar->hw_params.patch_load_addr;
@@ -334,16 +436,16 @@
 
 static void ath10k_core_free_firmware_files(struct ath10k *ar)
 {
-	if (ar->board && !IS_ERR(ar->board))
+	if (!IS_ERR(ar->board))
 		release_firmware(ar->board);
 
-	if (ar->otp && !IS_ERR(ar->otp))
+	if (!IS_ERR(ar->otp))
 		release_firmware(ar->otp);
 
-	if (ar->firmware && !IS_ERR(ar->firmware))
+	if (!IS_ERR(ar->firmware))
 		release_firmware(ar->firmware);
 
-	if (ar->cal_file && !IS_ERR(ar->cal_file))
+	if (!IS_ERR(ar->cal_file))
 		release_firmware(ar->cal_file);
 
 	ar->board = NULL;
@@ -447,7 +549,7 @@
 	int ie_id, i, index, bit, ret;
 	struct ath10k_fw_ie *hdr;
 	const u8 *data;
-	__le32 *timestamp;
+	__le32 *timestamp, *version;
 
 	/* first fetch the firmware file (firmware-*.bin) */
 	ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name);
@@ -562,6 +664,17 @@
 			ar->otp_len = ie_len;
 
 			break;
+		case ATH10K_FW_IE_WMI_OP_VERSION:
+			if (ie_len != sizeof(u32))
+				break;
+
+			version = (__le32 *)data;
+
+			ar->wmi.op_version = le32_to_cpup(version);
+
+			ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw ie wmi op version %d\n",
+				   ar->wmi.op_version);
+			break;
 		default:
 			ath10k_warn(ar, "Unknown FW IE: %u\n",
 				    le32_to_cpu(hdr->id));
@@ -582,13 +695,6 @@
 		goto err;
 	}
 
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features) &&
-	    !test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
-		ath10k_err(ar, "feature bits corrupted: 10.2 feature requires 10.x feature to be set as well");
-		ret = -EINVAL;
-		goto err;
-	}
-
 	/* now fetch the board file */
 	if (ar->hw_params.fw.board == NULL) {
 		ath10k_err(ar, "board data file not defined");
@@ -624,6 +730,13 @@
 	/* calibration file is optional, don't check for any errors */
 	ath10k_fetch_cal_file(ar);
 
+	ar->fw_api = 4;
+	ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
+
+	ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API4_FILE);
+	if (ret == 0)
+		goto success;
+
 	ar->fw_api = 3;
 	ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
 
@@ -662,7 +775,17 @@
 	}
 
 	ath10k_dbg(ar, ATH10K_DBG_BOOT,
-		   "boot did not find a calibration file, try OTP next: %d\n",
+		   "boot did not find a calibration file, try DT next: %d\n",
+		   ret);
+
+	ret = ath10k_download_cal_dt(ar);
+	if (ret == 0) {
+		ar->cal_mode = ATH10K_CAL_MODE_DT;
+		goto done;
+	}
+
+	ath10k_dbg(ar, ATH10K_DBG_BOOT,
+		   "boot did not find DT entry, try OTP next: %d\n",
 		   ret);
 
 	ret = ath10k_download_and_run_otp(ar);
@@ -696,7 +819,7 @@
 	if (!uart_print)
 		return 0;
 
-	ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7);
+	ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, ar->hw_params.uart_pin);
 	if (ret) {
 		ath10k_warn(ar, "could not enable UART prints (%d)\n", ret);
 		return ret;
@@ -764,6 +887,7 @@
 	complete_all(&ar->offchan_tx_completed);
 	complete_all(&ar->install_key_done);
 	complete_all(&ar->vdev_setup_done);
+	complete_all(&ar->thermal.wmi_sync);
 	wake_up(&ar->htt.empty_tx_wq);
 	wake_up(&ar->wmi.tx_credits_wq);
 	wake_up(&ar->peer_mapping_wq);
@@ -799,15 +923,63 @@
 	mutex_unlock(&ar->conf_mutex);
 }
 
-static void ath10k_core_init_max_sta_count(struct ath10k *ar)
+static int ath10k_core_init_firmware_features(struct ath10k *ar)
 {
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
-		ar->max_num_peers = TARGET_10X_NUM_PEERS;
-		ar->max_num_stations = TARGET_10X_NUM_STATIONS;
-	} else {
+	if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features) &&
+	    !test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+		ath10k_err(ar, "feature bits corrupted: 10.2 feature requires 10.x feature to be set as well");
+		return -EINVAL;
+	}
+
+	if (ar->wmi.op_version >= ATH10K_FW_WMI_OP_VERSION_MAX) {
+		ath10k_err(ar, "unsupported WMI OP version (max %d): %d\n",
+			   ATH10K_FW_WMI_OP_VERSION_MAX, ar->wmi.op_version);
+		return -EINVAL;
+	}
+
+	/* Backwards compatibility for firmwares without
+	 * ATH10K_FW_IE_WMI_OP_VERSION.
+	 */
+	if (ar->wmi.op_version == ATH10K_FW_WMI_OP_VERSION_UNSET) {
+		if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+			if (test_bit(ATH10K_FW_FEATURE_WMI_10_2,
+				     ar->fw_features))
+				ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_2;
+			else
+				ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
+		} else {
+			ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_MAIN;
+		}
+	}
+
+	switch (ar->wmi.op_version) {
+	case ATH10K_FW_WMI_OP_VERSION_MAIN:
 		ar->max_num_peers = TARGET_NUM_PEERS;
 		ar->max_num_stations = TARGET_NUM_STATIONS;
+		ar->max_num_vdevs = TARGET_NUM_VDEVS;
+		ar->htt.max_num_pending_tx = TARGET_NUM_MSDU_DESC;
+		break;
+	case ATH10K_FW_WMI_OP_VERSION_10_1:
+	case ATH10K_FW_WMI_OP_VERSION_10_2:
+	case ATH10K_FW_WMI_OP_VERSION_10_2_4:
+		ar->max_num_peers = TARGET_10X_NUM_PEERS;
+		ar->max_num_stations = TARGET_10X_NUM_STATIONS;
+		ar->max_num_vdevs = TARGET_10X_NUM_VDEVS;
+		ar->htt.max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC;
+		break;
+	case ATH10K_FW_WMI_OP_VERSION_TLV:
+		ar->max_num_peers = TARGET_TLV_NUM_PEERS;
+		ar->max_num_stations = TARGET_TLV_NUM_STATIONS;
+		ar->max_num_vdevs = TARGET_TLV_NUM_VDEVS;
+		ar->htt.max_num_pending_tx = TARGET_TLV_NUM_MSDU_DESC;
+		break;
+	case ATH10K_FW_WMI_OP_VERSION_UNSET:
+	case ATH10K_FW_WMI_OP_VERSION_MAX:
+		WARN_ON(1);
+		return -EINVAL;
 	}
+
+	return 0;
 }
 
 int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
@@ -932,6 +1104,18 @@
 		goto err_hif_stop;
 	}
 
+	/* If firmware indicates Full Rx Reorder support it must be used in a
+	 * slightly different manner. Let HTT code know.
+	 */
+	ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER,
+						ar->wmi.svc_map));
+
+	status = ath10k_htt_rx_ring_refill(ar);
+	if (status) {
+		ath10k_err(ar, "failed to refill htt rx ring: %d\n", status);
+		goto err_hif_stop;
+	}
+
 	/* we don't care about HTT in UTF mode */
 	if (mode == ATH10K_FIRMWARE_MODE_NORMAL) {
 		status = ath10k_htt_setup(&ar->htt);
@@ -945,10 +1129,7 @@
 	if (status)
 		goto err_hif_stop;
 
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
-		ar->free_vdev_map = (1LL << TARGET_10X_NUM_VDEVS) - 1;
-	else
-		ar->free_vdev_map = (1LL << TARGET_NUM_VDEVS) - 1;
+	ar->free_vdev_map = (1LL << ar->max_num_vdevs) - 1;
 
 	INIT_LIST_HEAD(&ar->arvifs);
 
@@ -1025,8 +1206,7 @@
 	ret = ath10k_bmi_get_target_info(ar, &target_info);
 	if (ret) {
 		ath10k_err(ar, "could not get target info (%d)\n", ret);
-		ath10k_hif_power_down(ar);
-		return ret;
+		goto err_power_down;
 	}
 
 	ar->target_version = target_info.version;
@@ -1035,28 +1215,28 @@
 	ret = ath10k_init_hw_params(ar);
 	if (ret) {
 		ath10k_err(ar, "could not get hw params (%d)\n", ret);
-		ath10k_hif_power_down(ar);
-		return ret;
+		goto err_power_down;
 	}
 
 	ret = ath10k_core_fetch_firmware_files(ar);
 	if (ret) {
 		ath10k_err(ar, "could not fetch firmware files (%d)\n", ret);
-		ath10k_hif_power_down(ar);
-		return ret;
+		goto err_power_down;
 	}
 
-	ath10k_core_init_max_sta_count(ar);
+	ret = ath10k_core_init_firmware_features(ar);
+	if (ret) {
+		ath10k_err(ar, "fatal problem with firmware features: %d\n",
+			   ret);
+		goto err_free_firmware_files;
+	}
 
 	mutex_lock(&ar->conf_mutex);
 
 	ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL);
 	if (ret) {
 		ath10k_err(ar, "could not init core (%d)\n", ret);
-		ath10k_core_free_firmware_files(ar);
-		ath10k_hif_power_down(ar);
-		mutex_unlock(&ar->conf_mutex);
-		return ret;
+		goto err_unlock;
 	}
 
 	ath10k_print_driver_info(ar);
@@ -1066,34 +1246,17 @@
 
 	ath10k_hif_power_down(ar);
 	return 0;
-}
 
-static int ath10k_core_check_chip_id(struct ath10k *ar)
-{
-	u32 hw_revision = MS(ar->chip_id, SOC_CHIP_ID_REV);
+err_unlock:
+	mutex_unlock(&ar->conf_mutex);
 
-	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip_id 0x%08x hw_revision 0x%x\n",
-		   ar->chip_id, hw_revision);
+err_free_firmware_files:
+	ath10k_core_free_firmware_files(ar);
 
-	/* Check that we are not using hw1.0 (some of them have same pci id
-	 * as hw2.0) before doing anything else as ath10k crashes horribly
-	 * due to missing hw1.0 workarounds. */
-	switch (hw_revision) {
-	case QCA988X_HW_1_0_CHIP_ID_REV:
-		ath10k_err(ar, "ERROR: qca988x hw1.0 is not supported\n");
-		return -EOPNOTSUPP;
+err_power_down:
+	ath10k_hif_power_down(ar);
 
-	case QCA988X_HW_2_0_CHIP_ID_REV:
-		/* known hardware revision, continue normally */
-		return 0;
-
-	default:
-		ath10k_warn(ar, "Warning: hardware revision unknown (0x%x), expect problems\n",
-			    ar->chip_id);
-		return 0;
-	}
-
-	return 0;
+	return ret;
 }
 
 static void ath10k_core_register_work(struct work_struct *work)
@@ -1125,9 +1288,18 @@
 		goto err_debug_destroy;
 	}
 
+	status = ath10k_thermal_register(ar);
+	if (status) {
+		ath10k_err(ar, "could not register thermal device: %d\n",
+			   status);
+		goto err_spectral_destroy;
+	}
+
 	set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags);
 	return;
 
+err_spectral_destroy:
+	ath10k_spectral_destroy(ar);
 err_debug_destroy:
 	ath10k_debug_destroy(ar);
 err_unregister_mac:
@@ -1143,16 +1315,7 @@
 
 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(ar, "Unsupported chip id 0x%08x\n", ar->chip_id);
-		return status;
-	}
-
 	queue_work(ar->workqueue, &ar->register_work);
 
 	return 0;
@@ -1166,6 +1329,7 @@
 	if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags))
 		return;
 
+	ath10k_thermal_unregister(ar);
 	/* Stop spectral before unregistering from mac80211 to remove the
 	 * relayfs debugfs file cleanly. Otherwise the parent debugfs tree
 	 * would be already be free'd recursively, leading to a double free.
@@ -1187,6 +1351,7 @@
 
 struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
 				  enum ath10k_bus bus,
+				  enum ath10k_hw_rev hw_rev,
 				  const struct ath10k_hif_ops *hif_ops)
 {
 	struct ath10k *ar;
@@ -1198,13 +1363,25 @@
 
 	ar->ath_common.priv = ar;
 	ar->ath_common.hw = ar->hw;
-
-	ar->p2p = !!ath10k_p2p;
 	ar->dev = dev;
-
+	ar->hw_rev = hw_rev;
 	ar->hif.ops = hif_ops;
 	ar->hif.bus = bus;
 
+	switch (hw_rev) {
+	case ATH10K_HW_QCA988X:
+		ar->regs = &qca988x_regs;
+		break;
+	case ATH10K_HW_QCA6174:
+		ar->regs = &qca6174_regs;
+		break;
+	default:
+		ath10k_err(ar, "unsupported core hardware revision %d\n",
+			   hw_rev);
+		ret = -ENOTSUPP;
+		goto err_free_mac;
+	}
+
 	init_completion(&ar->scan.started);
 	init_completion(&ar->scan.completed);
 	init_completion(&ar->scan.on_channel);
@@ -1212,6 +1389,7 @@
 
 	init_completion(&ar->install_key_done);
 	init_completion(&ar->vdev_setup_done);
+	init_completion(&ar->thermal.wmi_sync);
 
 	INIT_DELAYED_WORK(&ar->scan.timeout, ath10k_scan_timeout_work);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 1a17ae9..bacdfe4 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -34,6 +34,7 @@
 #include "../regd.h"
 #include "../dfs_pattern_detector.h"
 #include "spectral.h"
+#include "thermal.h"
 
 #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
 #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
@@ -96,6 +97,11 @@
 	} bcn;
 } __packed;
 
+struct ath10k_skb_rxcb {
+	dma_addr_t paddr;
+	struct hlist_node hlist;
+};
+
 static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
 {
 	BUILD_BUG_ON(sizeof(struct ath10k_skb_cb) >
@@ -103,6 +109,15 @@
 	return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data;
 }
 
+static inline struct ath10k_skb_rxcb *ATH10K_SKB_RXCB(struct sk_buff *skb)
+{
+	BUILD_BUG_ON(sizeof(struct ath10k_skb_rxcb) > sizeof(skb->cb));
+	return (struct ath10k_skb_rxcb *)skb->cb;
+}
+
+#define ATH10K_RXCB_SKB(rxcb) \
+		container_of((void *)rxcb, struct sk_buff, cb)
+
 static inline u32 host_interest_item_address(u32 item_offset)
 {
 	return QCA988X_HOST_INTEREST_ADDRESS + item_offset;
@@ -120,6 +135,7 @@
 };
 
 struct ath10k_wmi {
+	enum ath10k_fw_wmi_op_version op_version;
 	enum ath10k_htc_ep_id eid;
 	struct completion service_ready;
 	struct completion unified_ready;
@@ -128,6 +144,7 @@
 	struct wmi_cmd_map *cmd;
 	struct wmi_vdev_param_map *vdev_param;
 	struct wmi_pdev_param_map *pdev_param;
+	const struct wmi_ops *ops;
 
 	u32 num_mem_chunks;
 	struct ath10k_mem_chunk mem_chunks[WMI_MAX_MEM_REQS];
@@ -142,6 +159,25 @@
 	u32 peer_rx_rate; /* 10x only */
 };
 
+struct ath10k_fw_stats_vdev {
+	struct list_head list;
+
+	u32 vdev_id;
+	u32 beacon_snr;
+	u32 data_snr;
+	u32 num_tx_frames[4];
+	u32 num_rx_frames;
+	u32 num_tx_frames_retries[4];
+	u32 num_tx_frames_failures[4];
+	u32 num_rts_fail;
+	u32 num_rts_success;
+	u32 num_rx_err;
+	u32 num_rx_discard;
+	u32 num_tx_not_acked;
+	u32 tx_rate_history[10];
+	u32 beacon_rssi_history[10];
+};
+
 struct ath10k_fw_stats_pdev {
 	struct list_head list;
 
@@ -203,6 +239,7 @@
 
 struct ath10k_fw_stats {
 	struct list_head pdevs;
+	struct list_head vdevs;
 	struct list_head peers;
 };
 
@@ -236,10 +273,21 @@
 	u32 smps;
 
 	struct work_struct update_wk;
+
+#ifdef CPTCFG_MAC80211_DEBUGFS
+	/* protected by conf_mutex */
+	bool aggr_mode;
+#endif
 };
 
 #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
 
+enum ath10k_beacon_state {
+	ATH10K_BEACON_SCHEDULED = 0,
+	ATH10K_BEACON_SENDING,
+	ATH10K_BEACON_SENT,
+};
+
 struct ath10k_vif {
 	struct list_head list;
 
@@ -250,7 +298,7 @@
 	u32 dtim_period;
 	struct sk_buff *beacon;
 	/* protected by data_lock */
-	bool beacon_sent;
+	enum ath10k_beacon_state beacon_state;
 	void *beacon_buf;
 	dma_addr_t beacon_paddr;
 
@@ -260,13 +308,12 @@
 	bool is_started;
 	bool is_up;
 	bool spectral_enabled;
+	bool ps;
 	u32 aid;
 	u8 bssid[ETH_ALEN];
 
-	struct work_struct wep_key_work;
 	struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
-	u8 def_wep_key_idx;
-	u8 def_wep_key_newidx;
+	s8 def_wep_key_idx;
 
 	u16 tx_seq_no;
 
@@ -293,6 +340,7 @@
 	bool use_cts_prot;
 	int num_legacy_stations;
 	int txpower;
+	struct wmi_wmm_params_all_arg wmm_params;
 };
 
 struct ath10k_vif_iter {
@@ -323,8 +371,10 @@
 
 	/* protected by conf_mutex */
 	u32 fw_dbglog_mask;
+	u32 fw_dbglog_level;
 	u32 pktlog_filter;
 	u32 reg_addr;
+	u32 nf_cal_period;
 
 	u8 htt_max_amsdu;
 	u8 htt_max_ampdu;
@@ -369,7 +419,7 @@
 	/* wmi_mgmt_rx_hdr contains extra RSSI information */
 	ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX = 0,
 
-	/* firmware from 10X branch */
+	/* Firmware from 10X branch. Deprecated, don't use in new code. */
 	ATH10K_FW_FEATURE_WMI_10X = 1,
 
 	/* firmware support tx frame management over WMI, otherwise it's HTT */
@@ -378,11 +428,18 @@
 	/* Firmware does not support P2P */
 	ATH10K_FW_FEATURE_NO_P2P = 3,
 
-	/* Firmware 10.2 feature bit. The ATH10K_FW_FEATURE_WMI_10X feature bit
-	 * is required to be set as well.
+	/* Firmware 10.2 feature bit. The ATH10K_FW_FEATURE_WMI_10X feature
+	 * bit is required to be set as well. Deprecated, don't use in new
+	 * code.
 	 */
 	ATH10K_FW_FEATURE_WMI_10_2 = 4,
 
+	/* Some firmware revisions lack proper multi-interface client powersave
+	 * implementation. Enabling PS could result in connection drops,
+	 * traffic stalls, etc.
+	 */
+	ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT = 5,
+
 	/* keep last */
 	ATH10K_FW_FEATURE_COUNT,
 };
@@ -401,6 +458,7 @@
 enum ath10k_cal_mode {
 	ATH10K_CAL_MODE_FILE,
 	ATH10K_CAL_MODE_OTP,
+	ATH10K_CAL_MODE_DT,
 };
 
 static inline const char *ath10k_cal_mode_str(enum ath10k_cal_mode mode)
@@ -410,6 +468,8 @@
 		return "file";
 	case ATH10K_CAL_MODE_OTP:
 		return "otp";
+	case ATH10K_CAL_MODE_DT:
+		return "dt";
 	}
 
 	return "unknown";
@@ -444,6 +504,7 @@
 	struct device *dev;
 	u8 mac_addr[ETH_ALEN];
 
+	enum ath10k_hw_rev hw_rev;
 	u32 chip_id;
 	u32 target_version;
 	u8 fw_version_major;
@@ -459,9 +520,6 @@
 
 	DECLARE_BITMAP(fw_features, ATH10K_FW_FEATURE_COUNT);
 
-	struct targetdef *targetdef;
-	struct hostdef *hostdef;
-
 	bool p2p;
 
 	struct {
@@ -471,6 +529,7 @@
 
 	struct completion target_suspend;
 
+	const struct ath10k_hw_regs *regs;
 	struct ath10k_bmi bmi;
 	struct ath10k_wmi wmi;
 	struct ath10k_htc htc;
@@ -480,12 +539,15 @@
 		u32 id;
 		const char *name;
 		u32 patch_load_addr;
+		int uart_pin;
 
 		struct ath10k_hw_params_fw {
 			const char *dir;
 			const char *fw;
 			const char *otp;
 			const char *board;
+			size_t board_size;
+			size_t board_ext_size;
 		} fw;
 	} hw_params;
 
@@ -548,7 +610,6 @@
 	u8 cfg_tx_chainmask;
 	u8 cfg_rx_chainmask;
 
-	struct wmi_pdev_set_wmm_params_arg wmm_params;
 	struct completion install_key_done;
 
 	struct completion vdev_setup_done;
@@ -571,6 +632,7 @@
 
 	int max_num_peers;
 	int max_num_stations;
+	int max_num_vdevs;
 
 	struct work_struct offchan_tx_work;
 	struct sk_buff_head offchan_tx_queue;
@@ -610,6 +672,7 @@
 		/* protected by conf_mutex */
 		const struct firmware *utf;
 		DECLARE_BITMAP(orig_fw_features, ATH10K_FW_FEATURE_COUNT);
+		enum ath10k_fw_wmi_op_version orig_wmi_op_version;
 
 		/* protected by data_lock */
 		bool utf_monitor;
@@ -622,12 +685,15 @@
 		u32 fw_cold_reset_counter;
 	} stats;
 
+	struct ath10k_thermal thermal;
+
 	/* must be last */
 	u8 drv_priv[0] __aligned(sizeof(void *));
 };
 
 struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
 				  enum ath10k_bus bus,
+				  enum ath10k_hw_rev hw_rev,
 				  const struct ath10k_hif_ops *hif_ops);
 void ath10k_core_destroy(struct ath10k *ar);
 
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 1bc46ae..38519cc 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -23,6 +23,7 @@
 #include "core.h"
 #include "debug.h"
 #include "hif.h"
+#include "wmi-ops.h"
 
 /* ms */
 #define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000
@@ -123,7 +124,7 @@
 
 void ath10k_print_driver_info(struct ath10k *ar)
 {
-	ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d.%d.%d.%d cal %s max_sta %d\n",
+	ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d cal %s max_sta %d\n",
 		    ar->hw_params.name,
 		    ar->target_version,
 		    ar->chip_id,
@@ -131,10 +132,7 @@
 		    ar->fw_api,
 		    ar->htt.target_version_major,
 		    ar->htt.target_version_minor,
-		    ar->fw_version_major,
-		    ar->fw_version_minor,
-		    ar->fw_version_release,
-		    ar->fw_version_build,
+		    ar->wmi.op_version,
 		    ath10k_cal_mode_str(ar->cal_mode),
 		    ar->max_num_stations);
 	ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n",
@@ -245,6 +243,16 @@
 	}
 }
 
+static void ath10k_debug_fw_stats_vdevs_free(struct list_head *head)
+{
+	struct ath10k_fw_stats_vdev *i, *tmp;
+
+	list_for_each_entry_safe(i, tmp, head, list) {
+		list_del(&i->list);
+		kfree(i);
+	}
+}
+
 static void ath10k_debug_fw_stats_peers_free(struct list_head *head)
 {
 	struct ath10k_fw_stats_peer *i, *tmp;
@@ -260,6 +268,7 @@
 	spin_lock_bh(&ar->data_lock);
 	ar->debug.fw_stats_done = false;
 	ath10k_debug_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
+	ath10k_debug_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
 	ath10k_debug_fw_stats_peers_free(&ar->debug.fw_stats.peers);
 	spin_unlock_bh(&ar->data_lock);
 }
@@ -275,14 +284,27 @@
 	return num;
 }
 
+static size_t ath10k_debug_fw_stats_num_vdevs(struct list_head *head)
+{
+	struct ath10k_fw_stats_vdev *i;
+	size_t num = 0;
+
+	list_for_each_entry(i, head, list)
+		++num;
+
+	return num;
+}
+
 void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
 {
 	struct ath10k_fw_stats stats = {};
 	bool is_start, is_started, is_end;
 	size_t num_peers;
+	size_t num_vdevs;
 	int ret;
 
 	INIT_LIST_HEAD(&stats.pdevs);
+	INIT_LIST_HEAD(&stats.vdevs);
 	INIT_LIST_HEAD(&stats.peers);
 
 	spin_lock_bh(&ar->data_lock);
@@ -310,6 +332,7 @@
 	}
 
 	num_peers = ath10k_debug_fw_stats_num_peers(&ar->debug.fw_stats.peers);
+	num_vdevs = ath10k_debug_fw_stats_num_vdevs(&ar->debug.fw_stats.vdevs);
 	is_start = (list_empty(&ar->debug.fw_stats.pdevs) &&
 		    !list_empty(&stats.pdevs));
 	is_end = (!list_empty(&ar->debug.fw_stats.pdevs) &&
@@ -332,7 +355,13 @@
 			goto free;
 		}
 
+		if (num_vdevs >= BITS_PER_LONG) {
+			ath10k_warn(ar, "dropping fw vdev stats\n");
+			goto free;
+		}
+
 		list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);
+		list_splice_tail_init(&stats.vdevs, &ar->debug.fw_stats.vdevs);
 	}
 
 	complete(&ar->debug.fw_stats_complete);
@@ -342,6 +371,7 @@
 	 * resources if that is not the case.
 	 */
 	ath10k_debug_fw_stats_pdevs_free(&stats.pdevs);
+	ath10k_debug_fw_stats_vdevs_free(&stats.vdevs);
 	ath10k_debug_fw_stats_peers_free(&stats.peers);
 
 unlock:
@@ -365,7 +395,10 @@
 
 		reinit_completion(&ar->debug.fw_stats_complete);
 
-		ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
+		ret = ath10k_wmi_request_stats(ar,
+					       WMI_STAT_PDEV |
+					       WMI_STAT_VDEV |
+					       WMI_STAT_PEER);
 		if (ret) {
 			ath10k_warn(ar, "could not request stats (%d)\n", ret);
 			return ret;
@@ -373,7 +406,7 @@
 
 		ret = wait_for_completion_timeout(&ar->debug.fw_stats_complete,
 						  1*HZ);
-		if (ret <= 0)
+		if (ret == 0)
 			return -ETIMEDOUT;
 
 		spin_lock_bh(&ar->data_lock);
@@ -397,8 +430,11 @@
 	unsigned int len = 0;
 	unsigned int buf_len = ATH10K_FW_STATS_BUF_SIZE;
 	const struct ath10k_fw_stats_pdev *pdev;
+	const struct ath10k_fw_stats_vdev *vdev;
 	const struct ath10k_fw_stats_peer *peer;
 	size_t num_peers;
+	size_t num_vdevs;
+	int i;
 
 	spin_lock_bh(&ar->data_lock);
 
@@ -410,6 +446,7 @@
 	}
 
 	num_peers = ath10k_debug_fw_stats_num_peers(&fw_stats->peers);
+	num_vdevs = ath10k_debug_fw_stats_num_vdevs(&fw_stats->vdevs);
 
 	len += scnprintf(buf + len, buf_len - len, "\n");
 	len += scnprintf(buf + len, buf_len - len, "%30s\n",
@@ -533,6 +570,65 @@
 
 	len += scnprintf(buf + len, buf_len - len, "\n");
 	len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+			 "ath10k VDEV stats", num_vdevs);
+	len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+				 "=================");
+
+	list_for_each_entry(vdev, &fw_stats->vdevs, list) {
+		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+				 "vdev id", vdev->vdev_id);
+		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+				 "beacon snr", vdev->beacon_snr);
+		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+				 "data snr", vdev->data_snr);
+		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+				 "num rx frames", vdev->num_rx_frames);
+		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+				 "num rts fail", vdev->num_rts_fail);
+		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+				 "num rts success", vdev->num_rts_success);
+		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+				 "num rx err", vdev->num_rx_err);
+		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+				 "num rx discard", vdev->num_rx_discard);
+		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+				 "num tx not acked", vdev->num_tx_not_acked);
+
+		for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames); i++)
+			len += scnprintf(buf + len, buf_len - len,
+					"%25s [%02d] %u\n",
+					 "num tx frames", i,
+					 vdev->num_tx_frames[i]);
+
+		for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_retries); i++)
+			len += scnprintf(buf + len, buf_len - len,
+					"%25s [%02d] %u\n",
+					 "num tx frames retries", i,
+					 vdev->num_tx_frames_retries[i]);
+
+		for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_failures); i++)
+			len += scnprintf(buf + len, buf_len - len,
+					"%25s [%02d] %u\n",
+					 "num tx frames failures", i,
+					 vdev->num_tx_frames_failures[i]);
+
+		for (i = 0 ; i < ARRAY_SIZE(vdev->tx_rate_history); i++)
+			len += scnprintf(buf + len, buf_len - len,
+					"%25s [%02d] 0x%08x\n",
+					 "tx rate history", i,
+					 vdev->tx_rate_history[i]);
+
+		for (i = 0 ; i < ARRAY_SIZE(vdev->beacon_rssi_history); i++)
+			len += scnprintf(buf + len, buf_len - len,
+					"%25s [%02d] %u\n",
+					 "beacon rssi history", i,
+					 vdev->beacon_rssi_history[i]);
+
+		len += scnprintf(buf + len, buf_len - len, "\n");
+	}
+
+	len += scnprintf(buf + len, buf_len - len, "\n");
+	len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
 			 "ath10k PEER stats", num_peers);
 	len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
 				 "=================");
@@ -1320,10 +1416,10 @@
 {
 	struct ath10k *ar = file->private_data;
 	unsigned int len;
-	char buf[32];
+	char buf[64];
 
-	len = scnprintf(buf, sizeof(buf), "0x%08x\n",
-			ar->debug.fw_dbglog_mask);
+	len = scnprintf(buf, sizeof(buf), "0x%08x %u\n",
+			ar->debug.fw_dbglog_mask, ar->debug.fw_dbglog_level);
 
 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
@@ -1333,19 +1429,32 @@
 				      size_t count, loff_t *ppos)
 {
 	struct ath10k *ar = file->private_data;
-	unsigned long mask;
 	int ret;
+	char buf[64];
+	unsigned int log_level, mask;
 
-	ret = kstrtoul_from_user(user_buf, count, 0, &mask);
-	if (ret)
-		return ret;
+	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+
+	/* make sure that buf is null terminated */
+	buf[sizeof(buf) - 1] = 0;
+
+	ret = sscanf(buf, "%x %u", &mask, &log_level);
+
+	if (!ret)
+		return -EINVAL;
+
+	if (ret == 1)
+		/* default if user did not specify */
+		log_level = ATH10K_DBGLOG_LEVEL_WARN;
 
 	mutex_lock(&ar->conf_mutex);
 
 	ar->debug.fw_dbglog_mask = mask;
+	ar->debug.fw_dbglog_level = log_level;
 
 	if (ar->state == ATH10K_STATE_ON) {
-		ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask);
+		ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,
+					    ar->debug.fw_dbglog_level);
 		if (ret) {
 			ath10k_warn(ar, "dbglog cfg failed from debugfs: %d\n",
 				    ret);
@@ -1607,6 +1716,73 @@
 	.llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_nf_cal_period(struct file *file,
+					 char __user *user_buf,
+					 size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	unsigned int len;
+	char buf[32];
+
+	len = scnprintf(buf, sizeof(buf), "%d\n",
+			ar->debug.nf_cal_period);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_write_nf_cal_period(struct file *file,
+					  const char __user *user_buf,
+					  size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	unsigned long period;
+	int ret;
+
+	ret = kstrtoul_from_user(user_buf, count, 0, &period);
+	if (ret)
+		return ret;
+
+	if (period > WMI_PDEV_PARAM_CAL_PERIOD_MAX)
+		return -EINVAL;
+
+	/* there's no way to switch back to the firmware default */
+	if (period == 0)
+		return -EINVAL;
+
+	mutex_lock(&ar->conf_mutex);
+
+	ar->debug.nf_cal_period = period;
+
+	if (ar->state != ATH10K_STATE_ON) {
+		/* firmware is not running, nothing else to do */
+		ret = count;
+		goto exit;
+	}
+
+	ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->cal_period,
+					ar->debug.nf_cal_period);
+	if (ret) {
+		ath10k_warn(ar, "cal period cfg failed from debugfs: %d\n",
+			    ret);
+		goto exit;
+	}
+
+	ret = count;
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+
+	return ret;
+}
+
+static const struct file_operations fops_nf_cal_period = {
+	.read = ath10k_read_nf_cal_period,
+	.write = ath10k_write_nf_cal_period,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 int ath10k_debug_start(struct ath10k *ar)
 {
 	int ret;
@@ -1620,7 +1796,8 @@
 			    ret);
 
 	if (ar->debug.fw_dbglog_mask) {
-		ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask);
+		ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,
+					    ATH10K_DBGLOG_LEVEL_WARN);
 		if (ret)
 			/* not serious */
 			ath10k_warn(ar, "failed to enable dbglog during start: %d",
@@ -1642,6 +1819,16 @@
 			ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);
 	}
 
+	if (ar->debug.nf_cal_period) {
+		ret = ath10k_wmi_pdev_set_param(ar,
+						ar->wmi.pdev_param->cal_period,
+						ar->debug.nf_cal_period);
+		if (ret)
+			/* not serious */
+			ath10k_warn(ar, "cal period cfg failed from debug start: %d\n",
+				    ret);
+	}
+
 	return ret;
 }
 
@@ -1811,6 +1998,7 @@
 		return -ENOMEM;
 
 	INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
+	INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
 	INIT_LIST_HEAD(&ar->debug.fw_stats.peers);
 
 	return 0;
@@ -1880,6 +2068,9 @@
 	debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy,
 			    ar, &fops_cal_data);
 
+	debugfs_create_file("nf_cal_period", S_IRUSR | S_IWUSR,
+			    ar->debug.debugfs_phy, ar, &fops_nf_cal_period);
+
 	if (config_enabled(CPTCFG_ATH10K_DFS_CERTIFIED)) {
 		debugfs_create_file("dfs_simulate_radar", S_IWUSR,
 				    ar->debug.debugfs_phy, ar,
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index b59bd24..5a2180e 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -48,6 +48,12 @@
 	ATH10K_PKTLOG_ANY        = 0x00000001f,
 };
 
+enum ath10k_dbg_aggr_mode {
+	ATH10K_DBG_AGGR_MODE_AUTO,
+	ATH10K_DBG_AGGR_MODE_MANUAL,
+	ATH10K_DBG_AGGR_MODE_MAX,
+};
+
 extern unsigned int ath10k_debug_mask;
 
 __printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...);
@@ -77,7 +83,6 @@
 void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
 			       struct ieee80211_vif *vif,
 			       struct ethtool_stats *stats, u64 *data);
-
 #else
 static inline int ath10k_debug_start(struct ath10k *ar)
 {
@@ -129,6 +134,10 @@
 #define ath10k_debug_get_et_stats NULL
 
 #endif /* CPTCFG_ATH10K_DEBUGFS */
+#ifdef CPTCFG_MAC80211_DEBUGFS
+void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+			    struct ieee80211_sta *sta, struct dentry *dir);
+#endif /* CPTCFG_MAC80211_DEBUGFS */
 
 #ifdef CPTCFG_ATH10K_DEBUG
 __printf(3, 4) void ath10k_dbg(struct ath10k *ar,
diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c
new file mode 100644
index 0000000..95b5c49
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "wmi-ops.h"
+#include "debug.h"
+
+static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file,
+					     char __user *user_buf,
+					     size_t count, loff_t *ppos)
+{
+	struct ieee80211_sta *sta = file->private_data;
+	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+	struct ath10k *ar = arsta->arvif->ar;
+	char buf[32];
+	int len = 0;
+
+	mutex_lock(&ar->conf_mutex);
+	len = scnprintf(buf, sizeof(buf) - len, "aggregation mode: %s\n",
+			(arsta->aggr_mode == ATH10K_DBG_AGGR_MODE_AUTO) ?
+			"auto" : "manual");
+	mutex_unlock(&ar->conf_mutex);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_dbg_sta_write_aggr_mode(struct file *file,
+					      const char __user *user_buf,
+					      size_t count, loff_t *ppos)
+{
+	struct ieee80211_sta *sta = file->private_data;
+	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+	struct ath10k *ar = arsta->arvif->ar;
+	u32 aggr_mode;
+	int ret;
+
+	if (kstrtouint_from_user(user_buf, count, 0, &aggr_mode))
+		return -EINVAL;
+
+	if (aggr_mode >= ATH10K_DBG_AGGR_MODE_MAX)
+		return -EINVAL;
+
+	mutex_lock(&ar->conf_mutex);
+	if ((ar->state != ATH10K_STATE_ON) ||
+	    (aggr_mode == arsta->aggr_mode)) {
+		ret = count;
+		goto out;
+	}
+
+	ret = ath10k_wmi_addba_clear_resp(ar, arsta->arvif->vdev_id, sta->addr);
+	if (ret) {
+		ath10k_warn(ar, "failed to clear addba session ret: %d\n", ret);
+		goto out;
+	}
+
+	arsta->aggr_mode = aggr_mode;
+out:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static const struct file_operations fops_aggr_mode = {
+	.read = ath10k_dbg_sta_read_aggr_mode,
+	.write = ath10k_dbg_sta_write_aggr_mode,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static ssize_t ath10k_dbg_sta_write_addba(struct file *file,
+					  const char __user *user_buf,
+					  size_t count, loff_t *ppos)
+{
+	struct ieee80211_sta *sta = file->private_data;
+	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+	struct ath10k *ar = arsta->arvif->ar;
+	u32 tid, buf_size;
+	int ret;
+	char buf[64];
+
+	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+
+	/* make sure that buf is null terminated */
+	buf[sizeof(buf) - 1] = '\0';
+
+	ret = sscanf(buf, "%u %u", &tid, &buf_size);
+	if (ret != 2)
+		return -EINVAL;
+
+	/* Valid TID values are 0 through 15 */
+	if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
+		return -EINVAL;
+
+	mutex_lock(&ar->conf_mutex);
+	if ((ar->state != ATH10K_STATE_ON) ||
+	    (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
+		ret = count;
+		goto out;
+	}
+
+	ret = ath10k_wmi_addba_send(ar, arsta->arvif->vdev_id, sta->addr,
+				    tid, buf_size);
+	if (ret) {
+		ath10k_warn(ar, "failed to send addba request: vdev_id %u peer %pM tid %u buf_size %u\n",
+			    arsta->arvif->vdev_id, sta->addr, tid, buf_size);
+	}
+
+	ret = count;
+out:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static const struct file_operations fops_addba = {
+	.write = ath10k_dbg_sta_write_addba,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static ssize_t ath10k_dbg_sta_write_addba_resp(struct file *file,
+					       const char __user *user_buf,
+					       size_t count, loff_t *ppos)
+{
+	struct ieee80211_sta *sta = file->private_data;
+	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+	struct ath10k *ar = arsta->arvif->ar;
+	u32 tid, status;
+	int ret;
+	char buf[64];
+
+	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+
+	/* make sure that buf is null terminated */
+	buf[sizeof(buf) - 1] = '\0';
+
+	ret = sscanf(buf, "%u %u", &tid, &status);
+	if (ret != 2)
+		return -EINVAL;
+
+	/* Valid TID values are 0 through 15 */
+	if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
+		return -EINVAL;
+
+	mutex_lock(&ar->conf_mutex);
+	if ((ar->state != ATH10K_STATE_ON) ||
+	    (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
+		ret = count;
+		goto out;
+	}
+
+	ret = ath10k_wmi_addba_set_resp(ar, arsta->arvif->vdev_id, sta->addr,
+					tid, status);
+	if (ret) {
+		ath10k_warn(ar, "failed to send addba response: vdev_id %u peer %pM tid %u status%u\n",
+			    arsta->arvif->vdev_id, sta->addr, tid, status);
+	}
+	ret = count;
+out:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static const struct file_operations fops_addba_resp = {
+	.write = ath10k_dbg_sta_write_addba_resp,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static ssize_t ath10k_dbg_sta_write_delba(struct file *file,
+					  const char __user *user_buf,
+					  size_t count, loff_t *ppos)
+{
+	struct ieee80211_sta *sta = file->private_data;
+	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+	struct ath10k *ar = arsta->arvif->ar;
+	u32 tid, initiator, reason;
+	int ret;
+	char buf[64];
+
+	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+
+	/* make sure that buf is null terminated */
+	buf[sizeof(buf) - 1] = '\0';
+
+	ret = sscanf(buf, "%u %u %u", &tid, &initiator, &reason);
+	if (ret != 3)
+		return -EINVAL;
+
+	/* Valid TID values are 0 through 15 */
+	if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
+		return -EINVAL;
+
+	mutex_lock(&ar->conf_mutex);
+	if ((ar->state != ATH10K_STATE_ON) ||
+	    (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
+		ret = count;
+		goto out;
+	}
+
+	ret = ath10k_wmi_delba_send(ar, arsta->arvif->vdev_id, sta->addr,
+				    tid, initiator, reason);
+	if (ret) {
+		ath10k_warn(ar, "failed to send delba: vdev_id %u peer %pM tid %u initiator %u reason %u\n",
+			    arsta->arvif->vdev_id, sta->addr, tid, initiator,
+			    reason);
+	}
+	ret = count;
+out:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static const struct file_operations fops_delba = {
+	.write = ath10k_dbg_sta_write_delba,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+			    struct ieee80211_sta *sta, struct dentry *dir)
+{
+	debugfs_create_file("aggr_mode", S_IRUGO | S_IWUSR, dir, sta,
+			    &fops_aggr_mode);
+	debugfs_create_file("addba", S_IWUSR, dir, sta, &fops_addba);
+	debugfs_create_file("addba_resp", S_IWUSR, dir, sta, &fops_addba_resp);
+	debugfs_create_file("delba", S_IWUSR, dir, sta, &fops_delba);
+}
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index f1946a6..2fd9e18 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -703,11 +703,9 @@
 	/* wait for response */
 	status = wait_for_completion_timeout(&htc->ctl_resp,
 					     ATH10K_HTC_CONN_SVC_TIMEOUT_HZ);
-	if (status <= 0) {
-		if (status == 0)
-			status = -ETIMEDOUT;
+	if (status == 0) {
 		ath10k_err(ar, "Service connect timeout: %d\n", status);
-		return status;
+		return -ETIMEDOUT;
 	}
 
 	/* we controlled the buffer creation, it's aligned */
diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c
index 56cb4ac..4f59ab9 100644
--- a/drivers/net/wireless/ath/ath10k/htt.c
+++ b/drivers/net/wireless/ath/ath10k/htt.c
@@ -53,7 +53,6 @@
 	struct ath10k_htt *htt = &ar->htt;
 
 	htt->ar = ar;
-	htt->max_throughput_mbps = 800;
 
 	/*
 	 * Prefetch enough data to satisfy target
@@ -102,7 +101,7 @@
 
 	status = wait_for_completion_timeout(&htt->target_version_received,
 					     HTT_TARGET_VERSION_TIMEOUT_HZ);
-	if (status <= 0) {
+	if (status == 0) {
 		ath10k_warn(ar, "htt version request timed out\n");
 		return -ETIMEDOUT;
 	}
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 1bd5545..6d9f361 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -19,8 +19,10 @@
 #define _HTT_H_
 
 #include <linux/bug.h>
+#include <linux/idr.h>
 #include <linux/interrupt.h>
 #include <linux/dmapool.h>
+#include <linux/hashtable.h>
 #include <net/mac80211.h>
 
 #include "htc.h"
@@ -286,7 +288,19 @@
 	HTT_T2H_MSG_TYPE_RC_UPDATE_IND		= 0xc,
 	HTT_T2H_MSG_TYPE_TX_INSPECT_IND		= 0xd,
 	HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION	= 0xe,
+	HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND	= 0xf,
+	HTT_T2H_MSG_TYPE_RX_PN_IND		= 0x10,
+	HTT_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND = 0x11,
+	HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND	= 0x12,
+	/* 0x13 reservd */
+	HTT_T2H_MSG_TYPE_WDI_IPA_OP_RESPONSE	= 0x14,
+
+	/* FIXME: Do not depend on this event id. Numbering of this event id is
+	 * broken across different firmware revisions and HTT version fails to
+	 * indicate this.
+	 */
 	HTT_T2H_MSG_TYPE_TEST,
+
 	/* keep this last */
 	HTT_T2H_NUM_MSGS
 };
@@ -655,6 +669,53 @@
 #define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_MASK   0x00000FC0
 #define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_LSB    6
 
+struct htt_rx_pn_ind {
+	__le16 peer_id;
+	u8 tid;
+	u8 seqno_start;
+	u8 seqno_end;
+	u8 pn_ie_count;
+	u8 reserved;
+	u8 pn_ies[0];
+} __packed;
+
+struct htt_rx_offload_msdu {
+	__le16 msdu_len;
+	__le16 peer_id;
+	u8 vdev_id;
+	u8 tid;
+	u8 fw_desc;
+	u8 payload[0];
+} __packed;
+
+struct htt_rx_offload_ind {
+	u8 reserved;
+	__le16 msdu_count;
+} __packed;
+
+struct htt_rx_in_ord_msdu_desc {
+	__le32 msdu_paddr;
+	__le16 msdu_len;
+	u8 fw_desc;
+	u8 reserved;
+} __packed;
+
+struct htt_rx_in_ord_ind {
+	u8 info;
+	__le16 peer_id;
+	u8 vdev_id;
+	u8 reserved;
+	__le16 msdu_count;
+	struct htt_rx_in_ord_msdu_desc msdu_descs[0];
+} __packed;
+
+#define HTT_RX_IN_ORD_IND_INFO_TID_MASK		0x0000001f
+#define HTT_RX_IN_ORD_IND_INFO_TID_LSB		0
+#define HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK	0x00000020
+#define HTT_RX_IN_ORD_IND_INFO_OFFLOAD_LSB	5
+#define HTT_RX_IN_ORD_IND_INFO_FRAG_MASK	0x00000040
+#define HTT_RX_IN_ORD_IND_INFO_FRAG_LSB		6
+
 /*
  * target -> host test message definition
  *
@@ -1150,6 +1211,9 @@
 		struct htt_rx_test rx_test;
 		struct htt_pktlog_msg pktlog_msg;
 		struct htt_stats_conf stats_conf;
+		struct htt_rx_pn_ind rx_pn_ind;
+		struct htt_rx_offload_ind rx_offload_ind;
+		struct htt_rx_in_ord_ind rx_in_ord_ind;
 	};
 } __packed;
 
@@ -1182,7 +1246,6 @@
 	struct ath10k *ar;
 	enum ath10k_htc_ep_id eid;
 
-	int max_throughput_mbps;
 	u8 target_version_major;
 	u8 target_version_minor;
 	struct completion target_version_received;
@@ -1198,6 +1261,20 @@
 		 * filled.
 		 */
 		struct sk_buff **netbufs_ring;
+
+		/* This is used only with firmware supporting IN_ORD_IND.
+		 *
+		 * With Full Rx Reorder the HTT Rx Ring is more of a temporary
+		 * buffer ring from which buffer addresses are copied by the
+		 * firmware to MAC Rx ring. Firmware then delivers IN_ORD_IND
+		 * pointing to specific (re-ordered) buffers.
+		 *
+		 * FIXME: With kernel generic hashing functions there's a lot
+		 * of hash collisions for sk_buffs.
+		 */
+		bool in_ord_rx;
+		DECLARE_HASHTABLE(skb_table, 4);
+
 		/*
 		 * Ring of buffer addresses -
 		 * This ring holds the "physical" device address of the
@@ -1252,12 +1329,11 @@
 
 	unsigned int prefetch_len;
 
-	/* Protects access to %pending_tx, %used_msdu_ids */
+	/* Protects access to pending_tx, num_pending_tx */
 	spinlock_t tx_lock;
 	int max_num_pending_tx;
 	int num_pending_tx;
-	struct sk_buff **pending_tx;
-	unsigned long *used_msdu_ids; /* bitmap */
+	struct idr pending_tx;
 	wait_queue_head_t empty_tx_wq;
 	struct dma_pool *tx_pool;
 
@@ -1271,6 +1347,7 @@
 	struct tasklet_struct txrx_compl_task;
 	struct sk_buff_head tx_compl_q;
 	struct sk_buff_head rx_compl_q;
+	struct sk_buff_head rx_in_ord_compl_q;
 
 	/* rx_status template */
 	struct ieee80211_rx_status rx_status;
@@ -1334,6 +1411,7 @@
 void ath10k_htt_tx_free(struct ath10k_htt *htt);
 
 int ath10k_htt_rx_alloc(struct ath10k_htt *htt);
+int ath10k_htt_rx_ring_refill(struct ath10k *ar);
 void ath10k_htt_rx_free(struct ath10k_htt *htt);
 
 void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb);
@@ -1346,7 +1424,7 @@
 				u8 max_subfrms_amsdu);
 
 void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
-int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt);
+int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb);
 void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
 int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
 int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *);
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 9c782a4..01a2b38 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -25,8 +25,8 @@
 
 #include <linux/log2.h>
 
-#define HTT_RX_RING_SIZE 1024
-#define HTT_RX_RING_FILL_LEVEL 1000
+#define HTT_RX_RING_SIZE HTT_RX_RING_SIZE_MAX
+#define HTT_RX_RING_FILL_LEVEL (((HTT_RX_RING_SIZE) / 2) - 1)
 
 /* when under memory pressure rx ring refill may fail and needs a retry */
 #define HTT_RX_RING_REFILL_RETRY_MS 50
@@ -34,31 +34,70 @@
 static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
 static void ath10k_htt_txrx_compl_task(unsigned long ptr);
 
+static struct sk_buff *
+ath10k_htt_rx_find_skb_paddr(struct ath10k *ar, u32 paddr)
+{
+	struct ath10k_skb_rxcb *rxcb;
+
+	hash_for_each_possible(ar->htt.rx_ring.skb_table, rxcb, hlist, paddr)
+		if (rxcb->paddr == paddr)
+			return ATH10K_RXCB_SKB(rxcb);
+
+	WARN_ON_ONCE(1);
+	return NULL;
+}
+
 static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt)
 {
 	struct sk_buff *skb;
-	struct ath10k_skb_cb *cb;
+	struct ath10k_skb_rxcb *rxcb;
+	struct hlist_node *n;
 	int i;
 
-	for (i = 0; i < htt->rx_ring.fill_cnt; i++) {
-		skb = htt->rx_ring.netbufs_ring[i];
-		cb = ATH10K_SKB_CB(skb);
-		dma_unmap_single(htt->ar->dev, cb->paddr,
-				 skb->len + skb_tailroom(skb),
-				 DMA_FROM_DEVICE);
-		dev_kfree_skb_any(skb);
+	if (htt->rx_ring.in_ord_rx) {
+		hash_for_each_safe(htt->rx_ring.skb_table, i, n, rxcb, hlist) {
+			skb = ATH10K_RXCB_SKB(rxcb);
+			dma_unmap_single(htt->ar->dev, rxcb->paddr,
+					 skb->len + skb_tailroom(skb),
+					 DMA_FROM_DEVICE);
+			hash_del(&rxcb->hlist);
+			dev_kfree_skb_any(skb);
+		}
+	} else {
+		for (i = 0; i < htt->rx_ring.size; i++) {
+			skb = htt->rx_ring.netbufs_ring[i];
+			if (!skb)
+				continue;
+
+			rxcb = ATH10K_SKB_RXCB(skb);
+			dma_unmap_single(htt->ar->dev, rxcb->paddr,
+					 skb->len + skb_tailroom(skb),
+					 DMA_FROM_DEVICE);
+			dev_kfree_skb_any(skb);
+		}
 	}
 
 	htt->rx_ring.fill_cnt = 0;
+	hash_init(htt->rx_ring.skb_table);
+	memset(htt->rx_ring.netbufs_ring, 0,
+	       htt->rx_ring.size * sizeof(htt->rx_ring.netbufs_ring[0]));
 }
 
 static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
 {
 	struct htt_rx_desc *rx_desc;
+	struct ath10k_skb_rxcb *rxcb;
 	struct sk_buff *skb;
 	dma_addr_t paddr;
 	int ret = 0, idx;
 
+	/* The Full Rx Reorder firmware has no way of telling the host
+	 * implicitly when it copied HTT Rx Ring buffers to MAC Rx Ring.
+	 * To keep things simple make sure ring is always half empty. This
+	 * guarantees there'll be no replenishment overruns possible.
+	 */
+	BUILD_BUG_ON(HTT_RX_RING_FILL_LEVEL >= HTT_RX_RING_SIZE / 2);
+
 	idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr);
 	while (num > 0) {
 		skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN);
@@ -86,17 +125,29 @@
 			goto fail;
 		}
 
-		ATH10K_SKB_CB(skb)->paddr = paddr;
+		rxcb = ATH10K_SKB_RXCB(skb);
+		rxcb->paddr = paddr;
 		htt->rx_ring.netbufs_ring[idx] = skb;
 		htt->rx_ring.paddrs_ring[idx] = __cpu_to_le32(paddr);
 		htt->rx_ring.fill_cnt++;
 
+		if (htt->rx_ring.in_ord_rx) {
+			hash_add(htt->rx_ring.skb_table,
+				 &ATH10K_SKB_RXCB(skb)->hlist,
+				 (u32)paddr);
+		}
+
 		num--;
 		idx++;
 		idx &= htt->rx_ring.size_mask;
 	}
 
 fail:
+	/*
+	 * Make sure the rx buffer is updated before available buffer
+	 * index to avoid any potential rx ring corruption.
+	 */
+	mb();
 	*htt->rx_ring.alloc_idx.vaddr = __cpu_to_le32(idx);
 	return ret;
 }
@@ -125,7 +176,7 @@
 	 * automatically balances load wrt to CPU power.
 	 *
 	 * This probably comes at a cost of lower maximum throughput but
-	 * improves the avarage and stability. */
+	 * improves the average and stability. */
 	spin_lock_bh(&htt->rx_ring.lock);
 	num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
 	num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit);
@@ -153,22 +204,20 @@
 	ath10k_htt_rx_msdu_buff_replenish(htt);
 }
 
-static void ath10k_htt_rx_ring_clean_up(struct ath10k_htt *htt)
+int ath10k_htt_rx_ring_refill(struct ath10k *ar)
 {
-	struct sk_buff *skb;
-	int i;
+	struct ath10k_htt *htt = &ar->htt;
+	int ret;
 
-	for (i = 0; i < htt->rx_ring.size; i++) {
-		skb = htt->rx_ring.netbufs_ring[i];
-		if (!skb)
-			continue;
+	spin_lock_bh(&htt->rx_ring.lock);
+	ret = ath10k_htt_rx_ring_fill_n(htt, (htt->rx_ring.fill_level -
+					      htt->rx_ring.fill_cnt));
+	spin_unlock_bh(&htt->rx_ring.lock);
 
-		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;
-	}
+	if (ret)
+		ath10k_htt_rx_ring_free(htt);
+
+	return ret;
 }
 
 void ath10k_htt_rx_free(struct ath10k_htt *htt)
@@ -179,8 +228,9 @@
 
 	skb_queue_purge(&htt->tx_compl_q);
 	skb_queue_purge(&htt->rx_compl_q);
+	skb_queue_purge(&htt->rx_in_ord_compl_q);
 
-	ath10k_htt_rx_ring_clean_up(htt);
+	ath10k_htt_rx_ring_free(htt);
 
 	dma_free_coherent(htt->ar->dev,
 			  (htt->rx_ring.size *
@@ -212,6 +262,7 @@
 	idx = htt->rx_ring.sw_rd_idx.msdu_payld;
 	msdu = htt->rx_ring.netbufs_ring[idx];
 	htt->rx_ring.netbufs_ring[idx] = NULL;
+	htt->rx_ring.paddrs_ring[idx] = 0;
 
 	idx++;
 	idx &= htt->rx_ring.size_mask;
@@ -219,7 +270,7 @@
 	htt->rx_ring.fill_cnt--;
 
 	dma_unmap_single(htt->ar->dev,
-			 ATH10K_SKB_CB(msdu)->paddr,
+			 ATH10K_SKB_RXCB(msdu)->paddr,
 			 msdu->len + skb_tailroom(msdu),
 			 DMA_FROM_DEVICE);
 	ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ",
@@ -379,6 +430,82 @@
 	ath10k_htt_rx_msdu_buff_replenish(htt);
 }
 
+static struct sk_buff *ath10k_htt_rx_pop_paddr(struct ath10k_htt *htt,
+					       u32 paddr)
+{
+	struct ath10k *ar = htt->ar;
+	struct ath10k_skb_rxcb *rxcb;
+	struct sk_buff *msdu;
+
+	lockdep_assert_held(&htt->rx_ring.lock);
+
+	msdu = ath10k_htt_rx_find_skb_paddr(ar, paddr);
+	if (!msdu)
+		return NULL;
+
+	rxcb = ATH10K_SKB_RXCB(msdu);
+	hash_del(&rxcb->hlist);
+	htt->rx_ring.fill_cnt--;
+
+	dma_unmap_single(htt->ar->dev, rxcb->paddr,
+			 msdu->len + skb_tailroom(msdu),
+			 DMA_FROM_DEVICE);
+	ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ",
+			msdu->data, msdu->len + skb_tailroom(msdu));
+
+	return msdu;
+}
+
+static int ath10k_htt_rx_pop_paddr_list(struct ath10k_htt *htt,
+					struct htt_rx_in_ord_ind *ev,
+					struct sk_buff_head *list)
+{
+	struct ath10k *ar = htt->ar;
+	struct htt_rx_in_ord_msdu_desc *msdu_desc = ev->msdu_descs;
+	struct htt_rx_desc *rxd;
+	struct sk_buff *msdu;
+	int msdu_count;
+	bool is_offload;
+	u32 paddr;
+
+	lockdep_assert_held(&htt->rx_ring.lock);
+
+	msdu_count = __le16_to_cpu(ev->msdu_count);
+	is_offload = !!(ev->info & HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK);
+
+	while (msdu_count--) {
+		paddr = __le32_to_cpu(msdu_desc->msdu_paddr);
+
+		msdu = ath10k_htt_rx_pop_paddr(htt, paddr);
+		if (!msdu) {
+			__skb_queue_purge(list);
+			return -ENOENT;
+		}
+
+		__skb_queue_tail(list, msdu);
+
+		if (!is_offload) {
+			rxd = (void *)msdu->data;
+
+			trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd));
+
+			skb_put(msdu, sizeof(*rxd));
+			skb_pull(msdu, sizeof(*rxd));
+			skb_put(msdu, __le16_to_cpu(msdu_desc->msdu_len));
+
+			if (!(__le32_to_cpu(rxd->attention.flags) &
+			      RX_ATTENTION_FLAGS_MSDU_DONE)) {
+				ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n");
+				return -EIO;
+			}
+		}
+
+		msdu_desc++;
+	}
+
+	return 0;
+}
+
 int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
 {
 	struct ath10k *ar = htt->ar;
@@ -424,7 +551,7 @@
 
 	htt->rx_ring.alloc_idx.vaddr = vaddr;
 	htt->rx_ring.alloc_idx.paddr = paddr;
-	htt->rx_ring.sw_rd_idx.msdu_payld = 0;
+	htt->rx_ring.sw_rd_idx.msdu_payld = htt->rx_ring.size_mask;
 	*htt->rx_ring.alloc_idx.vaddr = 0;
 
 	/* Initialize the Rx refill retry timer */
@@ -433,14 +560,15 @@
 	spin_lock_init(&htt->rx_ring.lock);
 
 	htt->rx_ring.fill_cnt = 0;
-	if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level))
-		goto err_fill_ring;
+	htt->rx_ring.sw_rd_idx.msdu_payld = 0;
+	hash_init(htt->rx_ring.skb_table);
 
 	tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task,
 		     (unsigned long)htt);
 
 	skb_queue_head_init(&htt->tx_compl_q);
 	skb_queue_head_init(&htt->rx_compl_q);
+	skb_queue_head_init(&htt->rx_in_ord_compl_q);
 
 	tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
 		     (unsigned long)htt);
@@ -449,12 +577,6 @@
 		   htt->rx_ring.size, htt->rx_ring.fill_level);
 	return 0;
 
-err_fill_ring:
-	ath10k_htt_rx_ring_free(htt);
-	dma_free_coherent(htt->ar->dev,
-			  sizeof(*htt->rx_ring.alloc_idx.vaddr),
-			  htt->rx_ring.alloc_idx.vaddr,
-			  htt->rx_ring.alloc_idx.paddr);
 err_dma_idx:
 	dma_free_coherent(htt->ar->dev,
 			  (htt->rx_ring.size *
@@ -691,7 +813,7 @@
 	 *
 	 * FIXME: Can we get/compute 64bit TSF?
 	 */
-	status->mactime = __le32_to_cpu(rxd->ppdu_end.tsf_timestamp);
+	status->mactime = __le32_to_cpu(rxd->ppdu_end.common.tsf_timestamp);
 	status->flag |= RX_FLAG_MACTIME_END;
 }
 
@@ -1578,6 +1700,194 @@
 	spin_unlock_bh(&ar->data_lock);
 }
 
+static int ath10k_htt_rx_extract_amsdu(struct sk_buff_head *list,
+				       struct sk_buff_head *amsdu)
+{
+	struct sk_buff *msdu;
+	struct htt_rx_desc *rxd;
+
+	if (skb_queue_empty(list))
+		return -ENOBUFS;
+
+	if (WARN_ON(!skb_queue_empty(amsdu)))
+		return -EINVAL;
+
+	while ((msdu = __skb_dequeue(list))) {
+		__skb_queue_tail(amsdu, msdu);
+
+		rxd = (void *)msdu->data - sizeof(*rxd);
+		if (rxd->msdu_end.info0 &
+		    __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU))
+			break;
+	}
+
+	msdu = skb_peek_tail(amsdu);
+	rxd = (void *)msdu->data - sizeof(*rxd);
+	if (!(rxd->msdu_end.info0 &
+	      __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU))) {
+		skb_queue_splice_init(amsdu, list);
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+static void ath10k_htt_rx_h_rx_offload_prot(struct ieee80211_rx_status *status,
+					    struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+	if (!ieee80211_has_protected(hdr->frame_control))
+		return;
+
+	/* Offloaded frames are already decrypted but firmware insists they are
+	 * protected in the 802.11 header. Strip the flag.  Otherwise mac80211
+	 * will drop the frame.
+	 */
+
+	hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+	status->flag |= RX_FLAG_DECRYPTED |
+			RX_FLAG_IV_STRIPPED |
+			RX_FLAG_MMIC_STRIPPED;
+}
+
+static void ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
+				       struct sk_buff_head *list)
+{
+	struct ath10k_htt *htt = &ar->htt;
+	struct ieee80211_rx_status *status = &htt->rx_status;
+	struct htt_rx_offload_msdu *rx;
+	struct sk_buff *msdu;
+	size_t offset;
+
+	while ((msdu = __skb_dequeue(list))) {
+		/* Offloaded frames don't have Rx descriptor. Instead they have
+		 * a short meta information header.
+		 */
+
+		rx = (void *)msdu->data;
+
+		skb_put(msdu, sizeof(*rx));
+		skb_pull(msdu, sizeof(*rx));
+
+		if (skb_tailroom(msdu) < __le16_to_cpu(rx->msdu_len)) {
+			ath10k_warn(ar, "dropping frame: offloaded rx msdu is too long!\n");
+			dev_kfree_skb_any(msdu);
+			continue;
+		}
+
+		skb_put(msdu, __le16_to_cpu(rx->msdu_len));
+
+		/* Offloaded rx header length isn't multiple of 2 nor 4 so the
+		 * actual payload is unaligned. Align the frame.  Otherwise
+		 * mac80211 complains.  This shouldn't reduce performance much
+		 * because these offloaded frames are rare.
+		 */
+		offset = 4 - ((unsigned long)msdu->data & 3);
+		skb_put(msdu, offset);
+		memmove(msdu->data + offset, msdu->data, msdu->len);
+		skb_pull(msdu, offset);
+
+		/* FIXME: The frame is NWifi. Re-construct QoS Control
+		 * if possible later.
+		 */
+
+		memset(status, 0, sizeof(*status));
+		status->flag |= RX_FLAG_NO_SIGNAL_VAL;
+
+		ath10k_htt_rx_h_rx_offload_prot(status, msdu);
+		ath10k_htt_rx_h_channel(ar, status);
+		ath10k_process_rx(ar, status, msdu);
+	}
+}
+
+static void ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct ath10k_htt *htt = &ar->htt;
+	struct htt_resp *resp = (void *)skb->data;
+	struct ieee80211_rx_status *status = &htt->rx_status;
+	struct sk_buff_head list;
+	struct sk_buff_head amsdu;
+	u16 peer_id;
+	u16 msdu_count;
+	u8 vdev_id;
+	u8 tid;
+	bool offload;
+	bool frag;
+	int ret;
+
+	lockdep_assert_held(&htt->rx_ring.lock);
+
+	if (htt->rx_confused)
+		return;
+
+	skb_pull(skb, sizeof(resp->hdr));
+	skb_pull(skb, sizeof(resp->rx_in_ord_ind));
+
+	peer_id = __le16_to_cpu(resp->rx_in_ord_ind.peer_id);
+	msdu_count = __le16_to_cpu(resp->rx_in_ord_ind.msdu_count);
+	vdev_id = resp->rx_in_ord_ind.vdev_id;
+	tid = SM(resp->rx_in_ord_ind.info, HTT_RX_IN_ORD_IND_INFO_TID);
+	offload = !!(resp->rx_in_ord_ind.info &
+			HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK);
+	frag = !!(resp->rx_in_ord_ind.info & HTT_RX_IN_ORD_IND_INFO_FRAG_MASK);
+
+	ath10k_dbg(ar, ATH10K_DBG_HTT,
+		   "htt rx in ord vdev %i peer %i tid %i offload %i frag %i msdu count %i\n",
+		   vdev_id, peer_id, tid, offload, frag, msdu_count);
+
+	if (skb->len < msdu_count * sizeof(*resp->rx_in_ord_ind.msdu_descs)) {
+		ath10k_warn(ar, "dropping invalid in order rx indication\n");
+		return;
+	}
+
+	/* The event can deliver more than 1 A-MSDU. Each A-MSDU is later
+	 * extracted and processed.
+	 */
+	__skb_queue_head_init(&list);
+	ret = ath10k_htt_rx_pop_paddr_list(htt, &resp->rx_in_ord_ind, &list);
+	if (ret < 0) {
+		ath10k_warn(ar, "failed to pop paddr list: %d\n", ret);
+		htt->rx_confused = true;
+		return;
+	}
+
+	/* Offloaded frames are very different and need to be handled
+	 * separately.
+	 */
+	if (offload)
+		ath10k_htt_rx_h_rx_offload(ar, &list);
+
+	while (!skb_queue_empty(&list)) {
+		__skb_queue_head_init(&amsdu);
+		ret = ath10k_htt_rx_extract_amsdu(&list, &amsdu);
+		switch (ret) {
+		case 0:
+			/* Note: The in-order indication may report interleaved
+			 * frames from different PPDUs meaning reported rx rate
+			 * to mac80211 isn't accurate/reliable. It's still
+			 * better to report something than nothing though. This
+			 * should still give an idea about rx rate to the user.
+			 */
+			ath10k_htt_rx_h_ppdu(ar, &amsdu, status);
+			ath10k_htt_rx_h_filter(ar, &amsdu, status);
+			ath10k_htt_rx_h_mpdu(ar, &amsdu, status);
+			ath10k_htt_rx_h_deliver(ar, &amsdu, status);
+			break;
+		case -EAGAIN:
+			/* fall through */
+		default:
+			/* Should not happen. */
+			ath10k_warn(ar, "failed to extract amsdu: %d\n", ret);
+			htt->rx_confused = true;
+			__skb_queue_purge(&list);
+			return;
+		}
+	}
+
+	tasklet_schedule(&htt->rx_replenish_task);
+}
+
 void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
 {
 	struct ath10k_htt *htt = &ar->htt;
@@ -1700,6 +2010,20 @@
 		 */
 		break;
 	}
+	case HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND: {
+		spin_lock_bh(&htt->rx_ring.lock);
+		__skb_queue_tail(&htt->rx_in_ord_compl_q, skb);
+		spin_unlock_bh(&htt->rx_ring.lock);
+		tasklet_schedule(&htt->txrx_compl_task);
+		return;
+	}
+	case HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND:
+		/* FIXME: This WMI-TLV event is overlapping with 10.2
+		 * CHAN_CHANGE - both being 0xF. Neither is being used in
+		 * practice so no immediate action is necessary. Nevertheless
+		 * HTT may need an abstraction layer like WMI has one day.
+		 */
+		break;
 	default:
 		ath10k_warn(ar, "htt event (%d) not handled\n",
 			    resp->hdr.msg_type);
@@ -1715,6 +2039,7 @@
 static void ath10k_htt_txrx_compl_task(unsigned long ptr)
 {
 	struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
+	struct ath10k *ar = htt->ar;
 	struct htt_resp *resp;
 	struct sk_buff *skb;
 
@@ -1731,5 +2056,10 @@
 		ath10k_htt_rx_handler(htt, &resp->rx_ind);
 		dev_kfree_skb_any(skb);
 	}
+
+	while ((skb = __skb_dequeue(&htt->rx_in_ord_compl_q))) {
+		ath10k_htt_rx_in_ord_ind(ar, skb);
+		dev_kfree_skb_any(skb);
+	}
 	spin_unlock_bh(&htt->rx_ring.lock);
 }
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 4bc51d8..cbd2bc9 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -56,21 +56,18 @@
 	return ret;
 }
 
-int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt)
+int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb)
 {
 	struct ath10k *ar = htt->ar;
-	int msdu_id;
+	int ret;
 
 	lockdep_assert_held(&htt->tx_lock);
 
-	msdu_id = find_first_zero_bit(htt->used_msdu_ids,
-				      htt->max_num_pending_tx);
-	if (msdu_id == htt->max_num_pending_tx)
-		return -ENOBUFS;
+	ret = idr_alloc(&htt->pending_tx, skb, 0, 0x10000, GFP_ATOMIC);
 
-	ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id);
-	__set_bit(msdu_id, htt->used_msdu_ids);
-	return msdu_id;
+	ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", ret);
+
+	return ret;
 }
 
 void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id)
@@ -79,79 +76,53 @@
 
 	lockdep_assert_held(&htt->tx_lock);
 
-	if (!test_bit(msdu_id, htt->used_msdu_ids))
-		ath10k_warn(ar, "trying to free unallocated msdu_id %d\n",
-			    msdu_id);
-
 	ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id);
-	__clear_bit(msdu_id, htt->used_msdu_ids);
+
+	idr_remove(&htt->pending_tx, msdu_id);
 }
 
 int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
 {
 	struct ath10k *ar = htt->ar;
 
-	spin_lock_init(&htt->tx_lock);
-
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, htt->ar->fw_features))
-		htt->max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC;
-	else
-		htt->max_num_pending_tx = TARGET_NUM_MSDU_DESC;
-
 	ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n",
 		   htt->max_num_pending_tx);
 
-	htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) *
-				  htt->max_num_pending_tx, GFP_KERNEL);
-	if (!htt->pending_tx)
-		return -ENOMEM;
-
-	htt->used_msdu_ids = kzalloc(sizeof(unsigned long) *
-				     BITS_TO_LONGS(htt->max_num_pending_tx),
-				     GFP_KERNEL);
-	if (!htt->used_msdu_ids) {
-		kfree(htt->pending_tx);
-		return -ENOMEM;
-	}
+	spin_lock_init(&htt->tx_lock);
+	idr_init(&htt->pending_tx);
 
 	htt->tx_pool = dma_pool_create("ath10k htt tx pool", htt->ar->dev,
 				       sizeof(struct ath10k_htt_txbuf), 4, 0);
 	if (!htt->tx_pool) {
-		kfree(htt->used_msdu_ids);
-		kfree(htt->pending_tx);
+		idr_destroy(&htt->pending_tx);
 		return -ENOMEM;
 	}
 
 	return 0;
 }
 
-static void ath10k_htt_tx_free_pending(struct ath10k_htt *htt)
+static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx)
 {
-	struct ath10k *ar = htt->ar;
+	struct ath10k *ar = ctx;
+	struct ath10k_htt *htt = &ar->htt;
 	struct htt_tx_done tx_done = {0};
-	int msdu_id;
+
+	ath10k_dbg(ar, ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", msdu_id);
+
+	tx_done.discard = 1;
+	tx_done.msdu_id = msdu_id;
 
 	spin_lock_bh(&htt->tx_lock);
-	for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) {
-		if (!test_bit(msdu_id, htt->used_msdu_ids))
-			continue;
-
-		ath10k_dbg(ar, ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n",
-			   msdu_id);
-
-		tx_done.discard = 1;
-		tx_done.msdu_id = msdu_id;
-
-		ath10k_txrx_tx_unref(htt, &tx_done);
-	}
+	ath10k_txrx_tx_unref(htt, &tx_done);
 	spin_unlock_bh(&htt->tx_lock);
+
+	return 0;
 }
 
 void ath10k_htt_tx_free(struct ath10k_htt *htt)
 {
-	ath10k_htt_tx_free_pending(htt);
-	kfree(htt->pending_tx);
-	kfree(htt->used_msdu_ids);
+	idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar);
+	idr_destroy(&htt->pending_tx);
 	dma_pool_destroy(htt->tx_pool);
 }
 
@@ -383,13 +354,12 @@
 	len += sizeof(cmd->mgmt_tx);
 
 	spin_lock_bh(&htt->tx_lock);
-	res = ath10k_htt_tx_alloc_msdu_id(htt);
+	res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
 	if (res < 0) {
 		spin_unlock_bh(&htt->tx_lock);
 		goto err_tx_dec;
 	}
 	msdu_id = res;
-	htt->pending_tx[msdu_id] = msdu;
 	spin_unlock_bh(&htt->tx_lock);
 
 	txdesc = ath10k_htc_alloc_skb(ar, len);
@@ -428,7 +398,6 @@
 	dev_kfree_skb_any(txdesc);
 err_free_msdu_id:
 	spin_lock_bh(&htt->tx_lock);
-	htt->pending_tx[msdu_id] = NULL;
 	ath10k_htt_tx_free_msdu_id(htt, msdu_id);
 	spin_unlock_bh(&htt->tx_lock);
 err_tx_dec:
@@ -460,13 +429,12 @@
 		goto err;
 
 	spin_lock_bh(&htt->tx_lock);
-	res = ath10k_htt_tx_alloc_msdu_id(htt);
+	res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
 	if (res < 0) {
 		spin_unlock_bh(&htt->tx_lock);
 		goto err_tx_dec;
 	}
 	msdu_id = res;
-	htt->pending_tx[msdu_id] = msdu;
 	spin_unlock_bh(&htt->tx_lock);
 
 	prefetch_len = min(htt->prefetch_len, msdu->len);
@@ -480,10 +448,18 @@
 
 	skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC,
 					   &paddr);
-	if (!skb_cb->htt.txbuf)
+	if (!skb_cb->htt.txbuf) {
+		res = -ENOMEM;
 		goto err_free_msdu_id;
+	}
 	skb_cb->htt.txbuf_paddr = paddr;
 
+	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))
+		skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
+
 	skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len,
 				       DMA_TO_DEVICE);
 	res = dma_mapping_error(dev, skb_cb->paddr);
@@ -539,8 +515,10 @@
 
 	flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
 	flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
-	flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
-	flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
+	if (msdu->ip_summed == CHECKSUM_PARTIAL) {
+		flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
+		flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
+	}
 
 	/* Prevent firmware from sending up tx inspection requests. There's
 	 * nothing ath10k can do with frames requested for inspection so force
@@ -598,7 +576,6 @@
 		      skb_cb->htt.txbuf_paddr);
 err_free_msdu_id:
 	spin_lock_bh(&htt->tx_lock);
-	htt->pending_tx[msdu_id] = NULL;
 	ath10k_htt_tx_free_msdu_id(htt, msdu_id);
 	spin_unlock_bh(&htt->tx_lock);
 err_tx_dec:
diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c
new file mode 100644
index 0000000..839a879
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/hw.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2014-2015 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/types.h>
+#include "hw.h"
+
+const struct ath10k_hw_regs qca988x_regs = {
+	.rtc_state_cold_reset_mask	= 0x00000400,
+	.rtc_soc_base_address		= 0x00004000,
+	.rtc_wmac_base_address		= 0x00005000,
+	.soc_core_base_address		= 0x00009000,
+	.ce_wrapper_base_address	= 0x00057000,
+	.ce0_base_address		= 0x00057400,
+	.ce1_base_address		= 0x00057800,
+	.ce2_base_address		= 0x00057c00,
+	.ce3_base_address		= 0x00058000,
+	.ce4_base_address		= 0x00058400,
+	.ce5_base_address		= 0x00058800,
+	.ce6_base_address		= 0x00058c00,
+	.ce7_base_address		= 0x00059000,
+	.soc_reset_control_si0_rst_mask	= 0x00000001,
+	.soc_reset_control_ce_rst_mask	= 0x00040000,
+	.soc_chip_id_address		= 0x00ec,
+	.scratch_3_address		= 0x0030,
+};
+
+const struct ath10k_hw_regs qca6174_regs = {
+	.rtc_state_cold_reset_mask		= 0x00002000,
+	.rtc_soc_base_address			= 0x00000800,
+	.rtc_wmac_base_address			= 0x00001000,
+	.soc_core_base_address			= 0x0003a000,
+	.ce_wrapper_base_address		= 0x00034000,
+	.ce0_base_address			= 0x00034400,
+	.ce1_base_address			= 0x00034800,
+	.ce2_base_address			= 0x00034c00,
+	.ce3_base_address			= 0x00035000,
+	.ce4_base_address			= 0x00035400,
+	.ce5_base_address			= 0x00035800,
+	.ce6_base_address			= 0x00035c00,
+	.ce7_base_address			= 0x00036000,
+	.soc_reset_control_si0_rst_mask		= 0x00000000,
+	.soc_reset_control_ce_rst_mask		= 0x00000001,
+	.soc_chip_id_address			= 0x000f0,
+	.scratch_3_address			= 0x0028,
+};
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index dfedfd0..460771f 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -34,9 +34,50 @@
 #define QCA988X_HW_2_0_BOARD_DATA_FILE	"board.bin"
 #define QCA988X_HW_2_0_PATCH_LOAD_ADDR	0x1234
 
+/* QCA6174 target BMI version signatures */
+#define QCA6174_HW_1_0_VERSION		0x05000000
+#define QCA6174_HW_1_1_VERSION		0x05000001
+#define QCA6174_HW_1_3_VERSION		0x05000003
+#define QCA6174_HW_2_1_VERSION		0x05010000
+#define QCA6174_HW_3_0_VERSION		0x05020000
+#define QCA6174_HW_3_2_VERSION		0x05030000
+
+enum qca6174_pci_rev {
+	QCA6174_PCI_REV_1_1 = 0x11,
+	QCA6174_PCI_REV_1_3 = 0x13,
+	QCA6174_PCI_REV_2_0 = 0x20,
+	QCA6174_PCI_REV_3_0 = 0x30,
+};
+
+enum qca6174_chip_id_rev {
+	QCA6174_HW_1_0_CHIP_ID_REV = 0,
+	QCA6174_HW_1_1_CHIP_ID_REV = 1,
+	QCA6174_HW_1_3_CHIP_ID_REV = 2,
+	QCA6174_HW_2_1_CHIP_ID_REV = 4,
+	QCA6174_HW_2_2_CHIP_ID_REV = 5,
+	QCA6174_HW_3_0_CHIP_ID_REV = 8,
+	QCA6174_HW_3_1_CHIP_ID_REV = 9,
+	QCA6174_HW_3_2_CHIP_ID_REV = 10,
+};
+
+#define QCA6174_HW_2_1_FW_DIR		"ath10k/QCA6174/hw2.1"
+#define QCA6174_HW_2_1_FW_FILE		"firmware.bin"
+#define QCA6174_HW_2_1_OTP_FILE		"otp.bin"
+#define QCA6174_HW_2_1_BOARD_DATA_FILE	"board.bin"
+#define QCA6174_HW_2_1_PATCH_LOAD_ADDR	0x1234
+
+#define QCA6174_HW_3_0_FW_DIR		"ath10k/QCA6174/hw3.0"
+#define QCA6174_HW_3_0_FW_FILE		"firmware.bin"
+#define QCA6174_HW_3_0_OTP_FILE		"otp.bin"
+#define QCA6174_HW_3_0_BOARD_DATA_FILE	"board.bin"
+#define QCA6174_HW_3_0_PATCH_LOAD_ADDR	0x1234
+
 #define ATH10K_FW_API2_FILE		"firmware-2.bin"
 #define ATH10K_FW_API3_FILE		"firmware-3.bin"
 
+/* added support for ATH10K_FW_IE_WMI_OP_VERSION */
+#define ATH10K_FW_API4_FILE		"firmware-4.bin"
+
 #define ATH10K_FW_UTF_FILE		"utf.bin"
 
 /* includes also the null byte */
@@ -58,8 +99,57 @@
 	ATH10K_FW_IE_FEATURES = 2,
 	ATH10K_FW_IE_FW_IMAGE = 3,
 	ATH10K_FW_IE_OTP_IMAGE = 4,
+
+	/* WMI "operations" interface version, 32 bit value. Supported from
+	 * FW API 4 and above.
+	 */
+	ATH10K_FW_IE_WMI_OP_VERSION = 5,
 };
 
+enum ath10k_fw_wmi_op_version {
+	ATH10K_FW_WMI_OP_VERSION_UNSET = 0,
+
+	ATH10K_FW_WMI_OP_VERSION_MAIN = 1,
+	ATH10K_FW_WMI_OP_VERSION_10_1 = 2,
+	ATH10K_FW_WMI_OP_VERSION_10_2 = 3,
+	ATH10K_FW_WMI_OP_VERSION_TLV = 4,
+	ATH10K_FW_WMI_OP_VERSION_10_2_4 = 5,
+
+	/* keep last */
+	ATH10K_FW_WMI_OP_VERSION_MAX,
+};
+
+enum ath10k_hw_rev {
+	ATH10K_HW_QCA988X,
+	ATH10K_HW_QCA6174,
+};
+
+struct ath10k_hw_regs {
+	u32 rtc_state_cold_reset_mask;
+	u32 rtc_soc_base_address;
+	u32 rtc_wmac_base_address;
+	u32 soc_core_base_address;
+	u32 ce_wrapper_base_address;
+	u32 ce0_base_address;
+	u32 ce1_base_address;
+	u32 ce2_base_address;
+	u32 ce3_base_address;
+	u32 ce4_base_address;
+	u32 ce5_base_address;
+	u32 ce6_base_address;
+	u32 ce7_base_address;
+	u32 soc_reset_control_si0_rst_mask;
+	u32 soc_reset_control_ce_rst_mask;
+	u32 soc_chip_id_address;
+	u32 scratch_3_address;
+};
+
+extern const struct ath10k_hw_regs qca988x_regs;
+extern const struct ath10k_hw_regs qca6174_regs;
+
+#define QCA_REV_988X(ar) ((ar)->hw_rev == ATH10K_HW_QCA988X)
+#define QCA_REV_6174(ar) ((ar)->hw_rev == ATH10K_HW_QCA6174)
+
 /* Known pecularities:
  *  - current FW doesn't support raw rx mode (last tested v599)
  *  - current FW dumps upon raw tx mode (last tested v599)
@@ -162,6 +252,18 @@
 #define TARGET_10X_NUM_MSDU_DESC		(1024 + 400)
 #define TARGET_10X_MAX_FRAG_ENTRIES		0
 
+/* 10.2 parameters */
+#define TARGET_10_2_DMA_BURST_SIZE		1
+
+/* Target specific defines for WMI-TLV firmware */
+#define TARGET_TLV_NUM_VDEVS			3
+#define TARGET_TLV_NUM_STATIONS			32
+#define TARGET_TLV_NUM_PEERS			((TARGET_TLV_NUM_STATIONS) + \
+						 (TARGET_TLV_NUM_VDEVS) + \
+						 2)
+#define TARGET_TLV_NUM_TIDS			((TARGET_TLV_NUM_PEERS) * 2)
+#define TARGET_TLV_NUM_MSDU_DESC		(1024 + 32)
+
 /* Number of Copy Engines supported */
 #define CE_COUNT 8
 
@@ -192,7 +294,7 @@
 /* as of IP3.7.1 */
 #define RTC_STATE_V_ON				3
 
-#define RTC_STATE_COLD_RESET_MASK		0x00000400
+#define RTC_STATE_COLD_RESET_MASK		ar->regs->rtc_state_cold_reset_mask
 #define RTC_STATE_V_LSB				0
 #define RTC_STATE_V_MASK			0x00000007
 #define RTC_STATE_ADDRESS			0x0000
@@ -201,12 +303,12 @@
 #define PCIE_SOC_WAKE_RESET			0x00000000
 #define SOC_GLOBAL_RESET_ADDRESS		0x0008
 
-#define RTC_SOC_BASE_ADDRESS			0x00004000
-#define RTC_WMAC_BASE_ADDRESS			0x00005000
+#define RTC_SOC_BASE_ADDRESS			ar->regs->rtc_soc_base_address
+#define RTC_WMAC_BASE_ADDRESS			ar->regs->rtc_wmac_base_address
 #define MAC_COEX_BASE_ADDRESS			0x00006000
 #define BT_COEX_BASE_ADDRESS			0x00007000
 #define SOC_PCIE_BASE_ADDRESS			0x00008000
-#define SOC_CORE_BASE_ADDRESS			0x00009000
+#define SOC_CORE_BASE_ADDRESS			ar->regs->soc_core_base_address
 #define WLAN_UART_BASE_ADDRESS			0x0000c000
 #define WLAN_SI_BASE_ADDRESS			0x00010000
 #define WLAN_GPIO_BASE_ADDRESS			0x00014000
@@ -215,23 +317,23 @@
 #define EFUSE_BASE_ADDRESS			0x00030000
 #define FPGA_REG_BASE_ADDRESS			0x00039000
 #define WLAN_UART2_BASE_ADDRESS			0x00054c00
-#define CE_WRAPPER_BASE_ADDRESS			0x00057000
-#define CE0_BASE_ADDRESS			0x00057400
-#define CE1_BASE_ADDRESS			0x00057800
-#define CE2_BASE_ADDRESS			0x00057c00
-#define CE3_BASE_ADDRESS			0x00058000
-#define CE4_BASE_ADDRESS			0x00058400
-#define CE5_BASE_ADDRESS			0x00058800
-#define CE6_BASE_ADDRESS			0x00058c00
-#define CE7_BASE_ADDRESS			0x00059000
+#define CE_WRAPPER_BASE_ADDRESS			ar->regs->ce_wrapper_base_address
+#define CE0_BASE_ADDRESS			ar->regs->ce0_base_address
+#define CE1_BASE_ADDRESS			ar->regs->ce1_base_address
+#define CE2_BASE_ADDRESS			ar->regs->ce2_base_address
+#define CE3_BASE_ADDRESS			ar->regs->ce3_base_address
+#define CE4_BASE_ADDRESS			ar->regs->ce4_base_address
+#define CE5_BASE_ADDRESS			ar->regs->ce5_base_address
+#define CE6_BASE_ADDRESS			ar->regs->ce6_base_address
+#define CE7_BASE_ADDRESS			ar->regs->ce7_base_address
 #define DBI_BASE_ADDRESS			0x00060000
 #define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS	0x0006c000
 #define PCIE_LOCAL_BASE_ADDRESS			0x00080000
 
 #define SOC_RESET_CONTROL_ADDRESS		0x00000000
 #define SOC_RESET_CONTROL_OFFSET		0x00000000
-#define SOC_RESET_CONTROL_SI0_RST_MASK		0x00000001
-#define SOC_RESET_CONTROL_CE_RST_MASK		0x00040000
+#define SOC_RESET_CONTROL_SI0_RST_MASK		ar->regs->soc_reset_control_si0_rst_mask
+#define SOC_RESET_CONTROL_CE_RST_MASK		ar->regs->soc_reset_control_ce_rst_mask
 #define SOC_RESET_CONTROL_CPU_WARM_RST_MASK	0x00000040
 #define SOC_CPU_CLOCK_OFFSET			0x00000020
 #define SOC_CPU_CLOCK_STANDARD_LSB		0
@@ -245,7 +347,7 @@
 #define SOC_LF_TIMER_CONTROL0_ADDRESS		0x00000050
 #define SOC_LF_TIMER_CONTROL0_ENABLE_MASK	0x00000004
 
-#define SOC_CHIP_ID_ADDRESS			0x000000ec
+#define SOC_CHIP_ID_ADDRESS			ar->regs->soc_chip_id_address
 #define SOC_CHIP_ID_REV_LSB			8
 #define SOC_CHIP_ID_REV_MASK			0x00000f00
 
@@ -301,7 +403,7 @@
 #define PCIE_INTR_ENABLE_ADDRESS		0x0008
 #define PCIE_INTR_CAUSE_ADDRESS			0x000c
 #define PCIE_INTR_CLR_ADDRESS			0x0014
-#define SCRATCH_3_ADDRESS			0x0030
+#define SCRATCH_3_ADDRESS			ar->regs->scratch_3_address
 #define CPU_INTR_ADDRESS			0x0010
 
 /* Firmware indications to the Host via SCRATCH_3 register. */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 6369d4b..eb14cb2 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -27,6 +27,8 @@
 #include "htt.h"
 #include "txrx.h"
 #include "testmode.h"
+#include "wmi.h"
+#include "wmi-ops.h"
 
 /**********/
 /* Crypto */
@@ -35,7 +37,7 @@
 static int ath10k_send_key(struct ath10k_vif *arvif,
 			   struct ieee80211_key_conf *key,
 			   enum set_key_cmd cmd,
-			   const u8 *macaddr)
+			   const u8 *macaddr, bool def_idx)
 {
 	struct ath10k *ar = arvif->ar;
 	struct wmi_vdev_install_key_arg arg = {
@@ -56,10 +58,7 @@
 	switch (key->cipher) {
 	case WLAN_CIPHER_SUITE_CCMP:
 		arg.key_cipher = WMI_CIPHER_AES_CCM;
-		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;
+		key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
 		break;
 	case WLAN_CIPHER_SUITE_TKIP:
 		arg.key_cipher = WMI_CIPHER_TKIP;
@@ -73,7 +72,13 @@
 		 * Otherwise pairwise key must be set */
 		if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN))
 			arg.key_flags = WMI_KEY_PAIRWISE;
+
+		if (def_idx)
+			arg.key_flags |= WMI_KEY_TX_USAGE;
 		break;
+	case WLAN_CIPHER_SUITE_AES_CMAC:
+		/* this one needs to be done in software */
+		return 1;
 	default:
 		ath10k_warn(ar, "cipher %d is not supported\n", key->cipher);
 		return -EOPNOTSUPP;
@@ -90,7 +95,7 @@
 static int ath10k_install_key(struct ath10k_vif *arvif,
 			      struct ieee80211_key_conf *key,
 			      enum set_key_cmd cmd,
-			      const u8 *macaddr)
+			      const u8 *macaddr, bool def_idx)
 {
 	struct ath10k *ar = arvif->ar;
 	int ret;
@@ -99,7 +104,7 @@
 
 	reinit_completion(&ar->install_key_done);
 
-	ret = ath10k_send_key(arvif, key, cmd, macaddr);
+	ret = ath10k_send_key(arvif, key, cmd, macaddr, def_idx);
 	if (ret)
 		return ret;
 
@@ -117,6 +122,7 @@
 	struct ath10k_peer *peer;
 	int ret;
 	int i;
+	bool def_idx;
 
 	lockdep_assert_held(&ar->conf_mutex);
 
@@ -130,9 +136,14 @@
 	for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
 		if (arvif->wep_keys[i] == NULL)
 			continue;
+		/* set TX_USAGE flag for default key id */
+		if (arvif->def_wep_key_idx == i)
+			def_idx = true;
+		else
+			def_idx = false;
 
 		ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY,
-					 addr);
+					 addr, def_idx);
 		if (ret)
 			return ret;
 
@@ -166,8 +177,9 @@
 		if (peer->keys[i] == NULL)
 			continue;
 
+		/* key flags are not required to delete the key */
 		ret = ath10k_install_key(arvif, peer->keys[i],
-					 DISABLE_KEY, addr);
+					 DISABLE_KEY, addr, false);
 		if (ret && first_errno == 0)
 			first_errno = ret;
 
@@ -241,8 +253,8 @@
 
 		if (i == ARRAY_SIZE(peer->keys))
 			break;
-
-		ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr);
+		/* key flags are not required to delete the key */
+		ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr, false);
 		if (ret && first_errno == 0)
 			first_errno = ret;
 
@@ -267,7 +279,10 @@
 	case IEEE80211_BAND_2GHZ:
 		switch (chandef->width) {
 		case NL80211_CHAN_WIDTH_20_NOHT:
-			phymode = MODE_11G;
+			if (chandef->chan->flags & IEEE80211_CHAN_NO_OFDM)
+				phymode = MODE_11B;
+			else
+				phymode = MODE_11G;
 			break;
 		case NL80211_CHAN_WIDTH_20:
 			phymode = MODE_11NG_HT20;
@@ -519,10 +534,14 @@
 		dma_unmap_single(ar->dev, ATH10K_SKB_CB(arvif->beacon)->paddr,
 				 arvif->beacon->len, DMA_TO_DEVICE);
 
+	if (WARN_ON(arvif->beacon_state != ATH10K_BEACON_SCHEDULED &&
+		    arvif->beacon_state != ATH10K_BEACON_SENT))
+		return;
+
 	dev_kfree_skb_any(arvif->beacon);
 
 	arvif->beacon = NULL;
-	arvif->beacon_sent = false;
+	arvif->beacon_state = ATH10K_BEACON_SCHEDULED;
 }
 
 static void ath10k_mac_vif_beacon_cleanup(struct ath10k_vif *arvif)
@@ -592,7 +611,7 @@
 
 	ret = ath10k_vdev_setup_sync(ar);
 	if (ret) {
-		ath10k_warn(ar, "failed to synchronize setup for monitor vdev %i: %d\n",
+		ath10k_warn(ar, "failed to synchronize setup for monitor vdev %i start: %d\n",
 			    vdev_id, ret);
 		return ret;
 	}
@@ -639,7 +658,7 @@
 
 	ret = ath10k_vdev_setup_sync(ar);
 	if (ret)
-		ath10k_warn(ar, "failed to synchronise monitor vdev %i: %d\n",
+		ath10k_warn(ar, "failed to synchronize monitor vdev %i stop: %d\n",
 			    ar->monitor_vdev_id, ret);
 
 	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n",
@@ -908,8 +927,9 @@
 
 	ret = ath10k_vdev_setup_sync(ar);
 	if (ret) {
-		ath10k_warn(ar, "failed to synchronise setup for vdev %i: %d\n",
-			    arg.vdev_id, ret);
+		ath10k_warn(ar,
+			    "failed to synchronize setup for vdev %i restart %d: %d\n",
+			    arg.vdev_id, restart, ret);
 		return ret;
 	}
 
@@ -947,7 +967,7 @@
 
 	ret = ath10k_vdev_setup_sync(ar);
 	if (ret) {
-		ath10k_warn(ar, "failed to syncronise setup for vdev %i: %d\n",
+		ath10k_warn(ar, "failed to synchronize setup for vdev %i stop: %d\n",
 			    arvif->vdev_id, ret);
 		return ret;
 	}
@@ -962,6 +982,143 @@
 	return ret;
 }
 
+static int ath10k_mac_setup_bcn_p2p_ie(struct ath10k_vif *arvif,
+				       struct sk_buff *bcn)
+{
+	struct ath10k *ar = arvif->ar;
+	struct ieee80211_mgmt *mgmt;
+	const u8 *p2p_ie;
+	int ret;
+
+	if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
+		return 0;
+
+	if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+		return 0;
+
+	mgmt = (void *)bcn->data;
+	p2p_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
+					 mgmt->u.beacon.variable,
+					 bcn->len - (mgmt->u.beacon.variable -
+						     bcn->data));
+	if (!p2p_ie)
+		return -ENOENT;
+
+	ret = ath10k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie);
+	if (ret) {
+		ath10k_warn(ar, "failed to submit p2p go bcn ie for vdev %i: %d\n",
+			    arvif->vdev_id, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ath10k_mac_remove_vendor_ie(struct sk_buff *skb, unsigned int oui,
+				       u8 oui_type, size_t ie_offset)
+{
+	size_t len;
+	const u8 *next;
+	const u8 *end;
+	u8 *ie;
+
+	if (WARN_ON(skb->len < ie_offset))
+		return -EINVAL;
+
+	ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type,
+					   skb->data + ie_offset,
+					   skb->len - ie_offset);
+	if (!ie)
+		return -ENOENT;
+
+	len = ie[1] + 2;
+	end = skb->data + skb->len;
+	next = ie + len;
+
+	if (WARN_ON(next > end))
+		return -EINVAL;
+
+	memmove(ie, next, end - next);
+	skb_trim(skb, skb->len - len);
+
+	return 0;
+}
+
+static int ath10k_mac_setup_bcn_tmpl(struct ath10k_vif *arvif)
+{
+	struct ath10k *ar = arvif->ar;
+	struct ieee80211_hw *hw = ar->hw;
+	struct ieee80211_vif *vif = arvif->vif;
+	struct ieee80211_mutable_offsets offs = {};
+	struct sk_buff *bcn;
+	int ret;
+
+	if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
+		return 0;
+
+	bcn = ieee80211_beacon_get_template(hw, vif, &offs);
+	if (!bcn) {
+		ath10k_warn(ar, "failed to get beacon template from mac80211\n");
+		return -EPERM;
+	}
+
+	ret = ath10k_mac_setup_bcn_p2p_ie(arvif, bcn);
+	if (ret) {
+		ath10k_warn(ar, "failed to setup p2p go bcn ie: %d\n", ret);
+		kfree_skb(bcn);
+		return ret;
+	}
+
+	/* P2P IE is inserted by firmware automatically (as configured above)
+	 * so remove it from the base beacon template to avoid duplicate P2P
+	 * IEs in beacon frames.
+	 */
+	ath10k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
+				    offsetof(struct ieee80211_mgmt,
+					     u.beacon.variable));
+
+	ret = ath10k_wmi_bcn_tmpl(ar, arvif->vdev_id, offs.tim_offset, bcn, 0,
+				  0, NULL, 0);
+	kfree_skb(bcn);
+
+	if (ret) {
+		ath10k_warn(ar, "failed to submit beacon template command: %d\n",
+			    ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ath10k_mac_setup_prb_tmpl(struct ath10k_vif *arvif)
+{
+	struct ath10k *ar = arvif->ar;
+	struct ieee80211_hw *hw = ar->hw;
+	struct ieee80211_vif *vif = arvif->vif;
+	struct sk_buff *prb;
+	int ret;
+
+	if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
+		return 0;
+
+	prb = ieee80211_proberesp_get(hw, vif);
+	if (!prb) {
+		ath10k_warn(ar, "failed to get probe resp template from mac80211\n");
+		return -EPERM;
+	}
+
+	ret = ath10k_wmi_prb_tmpl(ar, arvif->vdev_id, prb);
+	kfree_skb(prb);
+
+	if (ret) {
+		ath10k_warn(ar, "failed to submit probe resp template command: %d\n",
+			    ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 static void ath10k_control_beaconing(struct ath10k_vif *arvif,
 				     struct ieee80211_bss_conf *info)
 {
@@ -1026,7 +1183,7 @@
 		if (is_zero_ether_addr(arvif->bssid))
 			return;
 
-		memset(arvif->bssid, 0, ETH_ALEN);
+		eth_zero_addr(arvif->bssid);
 
 		return;
 	}
@@ -1046,28 +1203,110 @@
 			    arvif->vdev_id, ret);
 }
 
-/*
- * Review this when mac80211 gains per-interface powersave support.
- */
+static int ath10k_mac_vif_recalc_ps_wake_threshold(struct ath10k_vif *arvif)
+{
+	struct ath10k *ar = arvif->ar;
+	u32 param;
+	u32 value;
+	int ret;
+
+	lockdep_assert_held(&arvif->ar->conf_mutex);
+
+	if (arvif->u.sta.uapsd)
+		value = WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER;
+	else
+		value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
+
+	param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
+	ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value);
+	if (ret) {
+		ath10k_warn(ar, "failed to submit ps wake threshold %u on vdev %i: %d\n",
+			    value, arvif->vdev_id, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif)
+{
+	struct ath10k *ar = arvif->ar;
+	u32 param;
+	u32 value;
+	int ret;
+
+	lockdep_assert_held(&arvif->ar->conf_mutex);
+
+	if (arvif->u.sta.uapsd)
+		value = WMI_STA_PS_PSPOLL_COUNT_UAPSD;
+	else
+		value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
+
+	param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
+	ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+					  param, value);
+	if (ret) {
+		ath10k_warn(ar, "failed to submit ps poll count %u on vdev %i: %d\n",
+			    value, arvif->vdev_id, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ath10k_mac_ps_vif_count(struct ath10k *ar)
+{
+	struct ath10k_vif *arvif;
+	int num = 0;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	list_for_each_entry(arvif, &ar->arvifs, list)
+		if (arvif->ps)
+			num++;
+
+	return num;
+}
+
 static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
 {
 	struct ath10k *ar = arvif->ar;
+	struct ieee80211_vif *vif = arvif->vif;
 	struct ieee80211_conf *conf = &ar->hw->conf;
 	enum wmi_sta_powersave_param param;
 	enum wmi_sta_ps_mode psmode;
 	int ret;
+	int ps_timeout;
+	bool enable_ps;
 
 	lockdep_assert_held(&arvif->ar->conf_mutex);
 
 	if (arvif->vif->type != NL80211_IFTYPE_STATION)
 		return 0;
 
-	if (conf->flags & IEEE80211_CONF_PS) {
+	enable_ps = arvif->ps;
+
+	if (enable_ps && ath10k_mac_ps_vif_count(ar) > 1 &&
+	    !test_bit(ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT,
+		      ar->fw_features)) {
+		ath10k_warn(ar, "refusing to enable ps on vdev %i: not supported by fw\n",
+			    arvif->vdev_id);
+		enable_ps = false;
+	}
+
+	if (enable_ps) {
 		psmode = WMI_STA_PS_MODE_ENABLED;
 		param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
 
+		ps_timeout = conf->dynamic_ps_timeout;
+		if (ps_timeout == 0) {
+			/* Firmware doesn't like 0 */
+			ps_timeout = ieee80211_tu_to_usec(
+				vif->bss_conf.beacon_int) / 1000;
+		}
+
 		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
-						  conf->dynamic_ps_timeout);
+						  ps_timeout);
 		if (ret) {
 			ath10k_warn(ar, "failed to set inactivity time for vdev %d: %i\n",
 				    arvif->vdev_id, ret);
@@ -1090,6 +1329,38 @@
 	return 0;
 }
 
+static int ath10k_mac_vif_disable_keepalive(struct ath10k_vif *arvif)
+{
+	struct ath10k *ar = arvif->ar;
+	struct wmi_sta_keepalive_arg arg = {};
+	int ret;
+
+	lockdep_assert_held(&arvif->ar->conf_mutex);
+
+	if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
+		return 0;
+
+	if (!test_bit(WMI_SERVICE_STA_KEEP_ALIVE, ar->wmi.svc_map))
+		return 0;
+
+	/* Some firmware revisions have a bug and ignore the `enabled` field.
+	 * Instead use the interval to disable the keepalive.
+	 */
+	arg.vdev_id = arvif->vdev_id;
+	arg.enabled = 1;
+	arg.method = WMI_STA_KEEPALIVE_METHOD_NULL_FRAME;
+	arg.interval = WMI_STA_KEEPALIVE_INTERVAL_DISABLE;
+
+	ret = ath10k_wmi_sta_keepalive(ar, &arg);
+	if (ret) {
+		ath10k_warn(ar, "failed to submit keepalive on vdev %i: %d\n",
+			    arvif->vdev_id, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 /**********************/
 /* Station management */
 /**********************/
@@ -1141,7 +1412,8 @@
 	lockdep_assert_held(&ar->conf_mutex);
 
 	bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
-			       info->bssid, NULL, 0, 0, 0);
+			       info->bssid, NULL, 0, IEEE80211_BSS_TYPE_ANY,
+			       IEEE80211_PRIVACY_ANY);
 	if (bss) {
 		const struct cfg80211_bss_ies *ies;
 
@@ -1358,6 +1630,10 @@
 		return;
 
 	arg->peer_flags |= WMI_PEER_VHT;
+
+	if (ar->hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ)
+		arg->peer_flags |= WMI_PEER_VHT_2G;
+
 	arg->peer_vht_caps = vht_cap->cap;
 
 	ampdu_factor = (vht_cap->cap &
@@ -1409,9 +1685,22 @@
 		if (vif->bss_conf.qos)
 			arg->peer_flags |= WMI_PEER_QOS;
 		break;
+	case WMI_VDEV_TYPE_IBSS:
+		if (sta->wme)
+			arg->peer_flags |= WMI_PEER_QOS;
+		break;
 	default:
 		break;
 	}
+
+	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM qos %d\n",
+		   sta->addr, !!(arg->peer_flags & WMI_PEER_QOS));
+}
+
+static bool ath10k_mac_sta_has_11g_rates(struct ieee80211_sta *sta)
+{
+	/* First 4 rates in ath10k_rates are CCK (11b) rates. */
+	return sta->supp_rates[IEEE80211_BAND_2GHZ] >> 4;
 }
 
 static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
@@ -1423,13 +1712,20 @@
 
 	switch (ar->hw->conf.chandef.chan->band) {
 	case IEEE80211_BAND_2GHZ:
-		if (sta->ht_cap.ht_supported) {
+		if (sta->vht_cap.vht_supported) {
+			if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+				phymode = MODE_11AC_VHT40;
+			else
+				phymode = MODE_11AC_VHT20;
+		} else if (sta->ht_cap.ht_supported) {
 			if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
 				phymode = MODE_11NG_HT40;
 			else
 				phymode = MODE_11NG_HT20;
-		} else {
+		} else if (ath10k_mac_sta_has_11g_rates(sta)) {
 			phymode = MODE_11G;
+		} else {
+			phymode = MODE_11B;
 		}
 
 		break;
@@ -1512,6 +1808,68 @@
 					 ath10k_smps_map[smps]);
 }
 
+static int ath10k_mac_vif_recalc_txbf(struct ath10k *ar,
+				      struct ieee80211_vif *vif,
+				      struct ieee80211_sta_vht_cap vht_cap)
+{
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	int ret;
+	u32 param;
+	u32 value;
+
+	if (!(ar->vht_cap_info &
+	      (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+	       IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
+	       IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+	       IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)))
+		return 0;
+
+	param = ar->wmi.vdev_param->txbf;
+	value = 0;
+
+	if (WARN_ON(param == WMI_VDEV_PARAM_UNSUPPORTED))
+		return 0;
+
+	/* The following logic is correct. If a remote STA advertises support
+	 * for being a beamformer then we should enable us being a beamformee.
+	 */
+
+	if (ar->vht_cap_info &
+	    (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+	     IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
+		if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
+			value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE;
+
+		if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
+			value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFEE;
+	}
+
+	if (ar->vht_cap_info &
+	    (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+	     IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) {
+		if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)
+			value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER;
+
+		if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)
+			value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFER;
+	}
+
+	if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFEE)
+		value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE;
+
+	if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFER)
+		value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER;
+
+	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, value);
+	if (ret) {
+		ath10k_warn(ar, "failed to submit vdev param txbf 0x%x: %d\n",
+			    value, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 /* can be called only in mac80211 callbacks due to `key_count` usage */
 static void ath10k_bss_assoc(struct ieee80211_hw *hw,
 			     struct ieee80211_vif *vif,
@@ -1520,6 +1878,7 @@
 	struct ath10k *ar = hw->priv;
 	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
 	struct ieee80211_sta_ht_cap ht_cap;
+	struct ieee80211_sta_vht_cap vht_cap;
 	struct wmi_peer_assoc_complete_arg peer_arg;
 	struct ieee80211_sta *ap_sta;
 	int ret;
@@ -1542,6 +1901,7 @@
 	/* ap_sta must be accessed only within rcu section which must be left
 	 * before calling ath10k_setup_peer_smps() which might sleep. */
 	ht_cap = ap_sta->ht_cap;
+	vht_cap = ap_sta->vht_cap;
 
 	ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg);
 	if (ret) {
@@ -1567,6 +1927,13 @@
 		return;
 	}
 
+	ret = ath10k_mac_vif_recalc_txbf(ar, vif, vht_cap);
+	if (ret) {
+		ath10k_warn(ar, "failed to recalc txbf for vdev %i on bss %pM: %d\n",
+			    arvif->vdev_id, bss_conf->bssid, ret);
+		return;
+	}
+
 	ath10k_dbg(ar, ATH10K_DBG_MAC,
 		   "mac vdev %d up (associated) bssid %pM aid %d\n",
 		   arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
@@ -1584,6 +1951,18 @@
 	}
 
 	arvif->is_up = true;
+
+	/* Workaround: Some firmware revisions (tested with qca6174
+	 * WLAN.RM.2.0-00073) have buggy powersave state machine and must be
+	 * poked with peer param command.
+	 */
+	ret = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, arvif->bssid,
+					WMI_PEER_DUMMY_VAR, 1);
+	if (ret) {
+		ath10k_warn(ar, "failed to poke peer %pM param for ps workaround on vdev %i: %d\n",
+			    arvif->bssid, arvif->vdev_id, ret);
+		return;
+	}
 }
 
 static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
@@ -1591,6 +1970,7 @@
 {
 	struct ath10k *ar = hw->priv;
 	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	struct ieee80211_sta_vht_cap vht_cap = {};
 	int ret;
 
 	lockdep_assert_held(&ar->conf_mutex);
@@ -1603,7 +1983,15 @@
 		ath10k_warn(ar, "faield to down vdev %i: %d\n",
 			    arvif->vdev_id, ret);
 
-	arvif->def_wep_key_idx = 0;
+	arvif->def_wep_key_idx = -1;
+
+	ret = ath10k_mac_vif_recalc_txbf(ar, vif, vht_cap);
+	if (ret) {
+		ath10k_warn(ar, "failed to recalc txbf for vdev %i: %d\n",
+			    arvif->vdev_id, ret);
+		return;
+	}
+
 	arvif->is_up = false;
 }
 
@@ -1662,11 +2050,14 @@
 			}
 		}
 
-		ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
-		if (ret) {
-			ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
-				    arvif->vdev_id, ret);
-			return ret;
+		/* Plumb cached keys only for static WEP */
+		if (arvif->def_wep_key_idx != -1) {
+			ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
+			if (ret) {
+				ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
+					    arvif->vdev_id, ret);
+				return ret;
+			}
 		}
 	}
 
@@ -1931,75 +2322,13 @@
 	 * used only for CQM purposes (e.g. hostapd station keepalive ping) so
 	 * it is safe to downgrade to NullFunc.
 	 */
+	hdr = (void *)skb->data;
 	if (ieee80211_is_qos_nullfunc(hdr->frame_control)) {
 		hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
 		cb->htt.tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
 	}
 }
 
-static void ath10k_tx_wep_key_work(struct work_struct *work)
-{
-	struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
-						wep_key_work);
-	struct ath10k *ar = arvif->ar;
-	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)
-		goto unlock;
-
-	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
-		   arvif->vdev_id, keyidx);
-
-	ret = ath10k_wmi_vdev_set_param(arvif->ar,
-					arvif->vdev_id,
-					arvif->ar->wmi.vdev_param->def_keyid,
-					keyidx);
-	if (ret) {
-		ath10k_warn(ar, "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 ieee80211_vif *vif,
-				       struct ieee80211_key_conf *key,
-				       struct sk_buff *skb)
-{
-	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
-	struct ath10k *ar = arvif->ar;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
-	if (!ieee80211_has_protected(hdr->frame_control))
-		return;
-
-	if (!key)
-		return;
-
-	if (key->cipher != WLAN_CIPHER_SUITE_WEP40 &&
-	    key->cipher != WLAN_CIPHER_SUITE_WEP104)
-		return;
-
-	if (key->keyidx == arvif->def_wep_key_idx)
-		return;
-
-	/* FIXME: Most likely a few frames will be TXed with an old key. Simply
-	 * queueing frames until key index is updated is not an option because
-	 * sk_buff may need more processing to be done, e.g. offchannel */
-	arvif->def_wep_key_newidx = key->keyidx;
-	ieee80211_queue_work(ar->hw, &arvif->wep_key_work);
-}
-
 static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
 				       struct ieee80211_vif *vif,
 				       struct sk_buff *skb)
@@ -2151,7 +2480,7 @@
 
 		ret = wait_for_completion_timeout(&ar->offchan_tx_completed,
 						  3 * HZ);
-		if (ret <= 0)
+		if (ret == 0)
 			ath10k_warn(ar, "timed out waiting for offchannel skb %p\n",
 				    skb);
 
@@ -2213,6 +2542,7 @@
 	case ATH10K_SCAN_RUNNING:
 		if (ar->scan.is_roc)
 			ieee80211_remain_on_channel_expired(ar->hw);
+		/* fall through */
 	case ATH10K_SCAN_ABORTING:
 		if (!ar->scan.is_roc)
 			ieee80211_scan_completed(ar->hw,
@@ -2342,6 +2672,17 @@
 		return -ETIMEDOUT;
 	}
 
+	/* If we failed to start the scan, return error code at
+	 * this point.  This is probably due to some issue in the
+	 * firmware, but no need to wedge the driver due to that...
+	 */
+	spin_lock_bh(&ar->data_lock);
+	if (ar->scan.state == ATH10K_SCAN_IDLE) {
+		spin_unlock_bh(&ar->data_lock);
+		return -EINVAL;
+	}
+	spin_unlock_bh(&ar->data_lock);
+
 	/* Add a 200ms margin to account for event/command processing */
 	ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
 				     msecs_to_jiffies(arg->max_scan_time+200));
@@ -2359,7 +2700,6 @@
 	struct ath10k *ar = hw->priv;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_vif *vif = info->control.vif;
-	struct ieee80211_key_conf *key = info->control.hw_key;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
 	/* We should disable CCK RATE due to P2P */
@@ -2373,7 +2713,6 @@
 	/* it makes no sense to process injected frames like that */
 	if (vif && vif->type != NL80211_IFTYPE_MONITOR) {
 		ath10k_tx_h_nwifi(hw, skb);
-		ath10k_tx_h_update_wep_key(vif, key, skb);
 		ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
 		ath10k_tx_h_seq_no(vif, skb);
 	}
@@ -2871,6 +3210,8 @@
 	int bit;
 	u32 vdev_param;
 
+	vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
+
 	mutex_lock(&ar->conf_mutex);
 
 	memset(arvif, 0, sizeof(*arvif));
@@ -2878,7 +3219,6 @@
 	arvif->ar = ar;
 	arvif->vif = vif;
 
-	INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
 	INIT_LIST_HEAD(&arvif->list);
 
 	if (ar->free_vdev_map == 0) {
@@ -2894,10 +3234,11 @@
 	arvif->vdev_id = bit;
 	arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
 
-	if (ar->p2p)
-		arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
-
 	switch (vif->type) {
+	case NL80211_IFTYPE_P2P_DEVICE:
+		arvif->vdev_type = WMI_VDEV_TYPE_STA;
+		arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
+		break;
 	case NL80211_IFTYPE_UNSPECIFIED:
 	case NL80211_IFTYPE_STATION:
 		arvif->vdev_type = WMI_VDEV_TYPE_STA;
@@ -2966,15 +3307,18 @@
 	ar->free_vdev_map &= ~(1LL << arvif->vdev_id);
 	list_add(&arvif->list, &ar->arvifs);
 
-	vdev_param = ar->wmi.vdev_param->def_keyid;
-	ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
-					arvif->def_wep_key_idx);
+	/* It makes no sense to have firmware do keepalives. mac80211 already
+	 * takes care of this with idle connection polling.
+	 */
+	ret = ath10k_mac_vif_disable_keepalive(arvif);
 	if (ret) {
-		ath10k_warn(ar, "failed to set vdev %i default key id: %d\n",
+		ath10k_warn(ar, "failed to disable keepalive on vdev %i: %d\n",
 			    arvif->vdev_id, ret);
 		goto err_vdev_delete;
 	}
 
+	arvif->def_wep_key_idx = -1;
+
 	vdev_param = ar->wmi.vdev_param->tx_encap_type;
 	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
 					ATH10K_HW_TXRX_NATIVE_WIFI);
@@ -3026,22 +3370,16 @@
 			goto err_peer_delete;
 		}
 
-		param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
-		value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
-		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
-						  param, value);
+		ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif);
 		if (ret) {
-			ath10k_warn(ar, "failed to set vdev %i TX wake thresh: %d\n",
+			ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n",
 				    arvif->vdev_id, ret);
 			goto err_peer_delete;
 		}
 
-		param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
-		value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
-		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
-						  param, value);
+		ret = ath10k_mac_vif_recalc_ps_poll_count(arvif);
 		if (ret) {
-			ath10k_warn(ar, "failed to set vdev %i PSPOLL count: %d\n",
+			ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n",
 				    arvif->vdev_id, ret);
 			goto err_peer_delete;
 		}
@@ -3099,8 +3437,6 @@
 	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
 	int ret;
 
-	cancel_work_sync(&arvif->wep_key_work);
-
 	mutex_lock(&ar->conf_mutex);
 
 	spin_lock_bh(&ar->data_lock);
@@ -3116,9 +3452,10 @@
 	list_del(&arvif->list);
 
 	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
-		ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
+		ret = ath10k_wmi_peer_delete(arvif->ar, arvif->vdev_id,
+					     vif->addr);
 		if (ret)
-			ath10k_warn(ar, "failed to remove peer for AP vdev %i: %d\n",
+			ath10k_warn(ar, "failed to submit AP self-peer removal on vdev %i: %d\n",
 				    arvif->vdev_id, ret);
 
 		kfree(arvif->u.ap.noa_data);
@@ -3132,6 +3469,21 @@
 		ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n",
 			    arvif->vdev_id, ret);
 
+	/* Some firmware revisions don't notify host about self-peer removal
+	 * until after associated vdev is deleted.
+	 */
+	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+		ret = ath10k_wait_for_peer_deleted(ar, arvif->vdev_id,
+						   vif->addr);
+		if (ret)
+			ath10k_warn(ar, "failed to remove AP self-peer on vdev %i: %d\n",
+				    arvif->vdev_id, ret);
+
+		spin_lock_bh(&ar->data_lock);
+		ar->num_peers--;
+		spin_unlock_bh(&ar->data_lock);
+	}
+
 	ath10k_peer_cleanup(ar, arvif->vdev_id);
 
 	mutex_unlock(&ar->conf_mutex);
@@ -3211,9 +3563,21 @@
 		if (ret)
 			ath10k_warn(ar, "failed to set beacon mode for vdev %d: %i\n",
 				    arvif->vdev_id, ret);
+
+		ret = ath10k_mac_setup_bcn_tmpl(arvif);
+		if (ret)
+			ath10k_warn(ar, "failed to update beacon template: %d\n",
+				    ret);
 	}
 
-	if (changed & BSS_CHANGED_BEACON_INFO) {
+	if (changed & BSS_CHANGED_AP_PROBE_RESP) {
+		ret = ath10k_mac_setup_prb_tmpl(arvif);
+		if (ret)
+			ath10k_warn(ar, "failed to setup probe resp template on vdev %i: %d\n",
+				    arvif->vdev_id, ret);
+	}
+
+	if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) {
 		arvif->dtim_period = info->dtim_period;
 
 		ath10k_dbg(ar, ATH10K_DBG_MAC,
@@ -3314,6 +3678,15 @@
 			ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
 	}
 
+	if (changed & BSS_CHANGED_PS) {
+		arvif->ps = vif->bss_conf.ps;
+
+		ret = ath10k_config_ps(ar);
+		if (ret)
+			ath10k_warn(ar, "failed to setup ps on vdev %i: %d\n",
+				    arvif->vdev_id, ret);
+	}
+
 	mutex_unlock(&ar->conf_mutex);
 }
 
@@ -3453,6 +3826,7 @@
 	const u8 *peer_addr;
 	bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
 		      key->cipher == WLAN_CIPHER_SUITE_WEP104;
+	bool def_idx = false;
 	int ret = 0;
 
 	if (key->keyidx > WMI_MAX_KEY_INDEX)
@@ -3498,7 +3872,14 @@
 			ath10k_clear_vdev_key(arvif, key);
 	}
 
-	ret = ath10k_install_key(arvif, key, cmd, peer_addr);
+	/* set TX_USAGE flag for all the keys incase of dot1x-WEP. For
+	 * static WEP, do not set this flag for the keys whose key id
+	 * is  greater than default key id.
+	 */
+	if (arvif->def_wep_key_idx == -1)
+		def_idx = true;
+
+	ret = ath10k_install_key(arvif, key, cmd, peer_addr, def_idx);
 	if (ret) {
 		ath10k_warn(ar, "failed to install key for vdev %i peer %pM: %d\n",
 			    arvif->vdev_id, peer_addr, ret);
@@ -3523,6 +3904,39 @@
 	return ret;
 }
 
+static void ath10k_set_default_unicast_key(struct ieee80211_hw *hw,
+					   struct ieee80211_vif *vif,
+					   int keyidx)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	int ret;
+
+	mutex_lock(&arvif->ar->conf_mutex);
+
+	if (arvif->ar->state != ATH10K_STATE_ON)
+		goto unlock;
+
+	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
+		   arvif->vdev_id, keyidx);
+
+	ret = ath10k_wmi_vdev_set_param(arvif->ar,
+					arvif->vdev_id,
+					arvif->ar->wmi.vdev_param->def_keyid,
+					keyidx);
+
+	if (ret) {
+		ath10k_warn(ar, "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_sta_rc_update_wk(struct work_struct *wk)
 {
 	struct ath10k *ar;
@@ -3583,8 +3997,9 @@
 				    sta->addr, smps, err);
 	}
 
-	if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
-		ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates\n",
+	if (changed & IEEE80211_RC_SUPP_RATES_CHANGED ||
+	    changed & IEEE80211_RC_NSS_CHANGED) {
+		ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates/nss\n",
 			   sta->addr);
 
 		err = ath10k_station_assoc(ar, arvif->vif, sta, true);
@@ -3757,6 +4172,8 @@
 				u16 ac, bool enable)
 {
 	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	struct wmi_sta_uapsd_auto_trig_arg arg = {};
+	u32 prio = 0, acc = 0;
 	u32 value = 0;
 	int ret = 0;
 
@@ -3769,18 +4186,26 @@
 	case IEEE80211_AC_VO:
 		value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN |
 			WMI_STA_PS_UAPSD_AC3_TRIGGER_EN;
+		prio = 7;
+		acc = 3;
 		break;
 	case IEEE80211_AC_VI:
 		value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN |
 			WMI_STA_PS_UAPSD_AC2_TRIGGER_EN;
+		prio = 5;
+		acc = 2;
 		break;
 	case IEEE80211_AC_BE:
 		value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN |
 			WMI_STA_PS_UAPSD_AC1_TRIGGER_EN;
+		prio = 2;
+		acc = 1;
 		break;
 	case IEEE80211_AC_BK:
 		value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN |
 			WMI_STA_PS_UAPSD_AC0_TRIGGER_EN;
+		prio = 0;
+		acc = 0;
 		break;
 	}
 
@@ -3808,6 +4233,43 @@
 	if (ret)
 		ath10k_warn(ar, "failed to set rx wake param: %d\n", ret);
 
+	ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif);
+	if (ret) {
+		ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n",
+			    arvif->vdev_id, ret);
+		return ret;
+	}
+
+	ret = ath10k_mac_vif_recalc_ps_poll_count(arvif);
+	if (ret) {
+		ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n",
+			    arvif->vdev_id, ret);
+		return ret;
+	}
+
+	if (test_bit(WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, ar->wmi.svc_map) ||
+	    test_bit(WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, ar->wmi.svc_map)) {
+		/* Only userspace can make an educated decision when to send
+		 * trigger frame. The following effectively disables u-UAPSD
+		 * autotrigger in firmware (which is enabled by default
+		 * provided the autotrigger service is available).
+		 */
+
+		arg.wmm_ac = acc;
+		arg.user_priority = prio;
+		arg.service_interval = 0;
+		arg.suspend_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC;
+		arg.delay_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC;
+
+		ret = ath10k_wmi_vdev_sta_uapsd(ar, arvif->vdev_id,
+						arvif->bssid, &arg, 1);
+		if (ret) {
+			ath10k_warn(ar, "failed to set uapsd auto trigger %d\n",
+				    ret);
+			return ret;
+		}
+	}
+
 exit:
 	return ret;
 }
@@ -3817,6 +4279,7 @@
 			  const struct ieee80211_tx_queue_params *params)
 {
 	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
 	struct wmi_wmm_params_arg *p = NULL;
 	int ret;
 
@@ -3824,16 +4287,16 @@
 
 	switch (ac) {
 	case IEEE80211_AC_VO:
-		p = &ar->wmm_params.ac_vo;
+		p = &arvif->wmm_params.ac_vo;
 		break;
 	case IEEE80211_AC_VI:
-		p = &ar->wmm_params.ac_vi;
+		p = &arvif->wmm_params.ac_vi;
 		break;
 	case IEEE80211_AC_BE:
-		p = &ar->wmm_params.ac_be;
+		p = &arvif->wmm_params.ac_be;
 		break;
 	case IEEE80211_AC_BK:
-		p = &ar->wmm_params.ac_bk;
+		p = &arvif->wmm_params.ac_bk;
 		break;
 	}
 
@@ -3853,11 +4316,23 @@
 	 */
 	p->txop = params->txop * 32;
 
-	/* FIXME: FW accepts wmm params per hw, not per vif */
-	ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params);
-	if (ret) {
-		ath10k_warn(ar, "failed to set wmm params: %d\n", ret);
-		goto exit;
+	if (ar->wmi.ops->gen_vdev_wmm_conf) {
+		ret = ath10k_wmi_vdev_wmm_conf(ar, arvif->vdev_id,
+					       &arvif->wmm_params);
+		if (ret) {
+			ath10k_warn(ar, "failed to set vdev wmm params on vdev %i: %d\n",
+				    arvif->vdev_id, ret);
+			goto exit;
+		}
+	} else {
+		/* This won't work well with multi-interface cases but it's
+		 * better than nothing.
+		 */
+		ret = ath10k_wmi_pdev_set_wmm_params(ar, &arvif->wmm_params);
+		if (ret) {
+			ath10k_warn(ar, "failed to set wmm params: %d\n", ret);
+			goto exit;
+		}
 	}
 
 	ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
@@ -3989,29 +4464,6 @@
 	return ret;
 }
 
-static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
-{
-	struct ath10k *ar = hw->priv;
-	struct ath10k_vif *arvif;
-	int ret = 0;
-
-	mutex_lock(&ar->conf_mutex);
-	list_for_each_entry(arvif, &ar->arvifs, list) {
-		ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n",
-			   arvif->vdev_id, value);
-
-		ret = ath10k_mac_set_frag(arvif, value);
-		if (ret) {
-			ath10k_warn(ar, "failed to set fragmentation threshold for vdev %d: %d\n",
-				    arvif->vdev_id, ret);
-			break;
-		}
-	}
-	mutex_unlock(&ar->conf_mutex);
-
-	return ret;
-}
-
 static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 			 u32 queues, bool drop)
 {
@@ -4552,7 +5004,7 @@
 			bw = WMI_PEER_CHWIDTH_80MHZ;
 			break;
 		case IEEE80211_STA_RX_BW_160:
-			ath10k_warn(ar, "Invalid bandwith %d in rc update for %pM\n",
+			ath10k_warn(ar, "Invalid bandwidth %d in rc update for %pM\n",
 				    sta->bandwidth, sta->addr);
 			bw = WMI_PEER_CHWIDTH_20MHZ;
 			break;
@@ -4650,12 +5102,12 @@
 	.hw_scan			= ath10k_hw_scan,
 	.cancel_hw_scan			= ath10k_cancel_hw_scan,
 	.set_key			= ath10k_set_key,
+	.set_default_unicast_key        = ath10k_set_default_unicast_key,
 	.sta_state			= ath10k_sta_state,
 	.conf_tx			= ath10k_conf_tx,
 	.remain_on_channel		= ath10k_remain_on_channel,
 	.cancel_remain_on_channel	= ath10k_cancel_remain_on_channel,
 	.set_rts_threshold		= ath10k_set_rts_threshold,
-	.set_frag_threshold		= ath10k_set_frag_threshold,
 	.flush				= ath10k_flush,
 	.tx_last_beacon			= ath10k_tx_last_beacon,
 	.set_antenna			= ath10k_set_antenna,
@@ -4676,6 +5128,9 @@
 	.suspend			= ath10k_suspend,
 	.resume				= ath10k_resume,
 #endif
+#ifdef CPTCFG_MAC80211_DEBUGFS
+	.sta_add_debugfs		= ath10k_sta_add_debugfs,
+#endif
 };
 
 #define RATETAB_ENT(_rate, _rateid, _flags) { \
@@ -4746,6 +5201,9 @@
 	CHAN5G(165, 5825, 0),
 };
 
+/* Note: Be careful if you re-order these. There is code which depends on this
+ * ordering.
+ */
 static struct ieee80211_rate ath10k_rates[] = {
 	/* CCK */
 	RATETAB_ENT(10,  0x82, 0),
@@ -4799,6 +5257,10 @@
 	.types	= BIT(NL80211_IFTYPE_P2P_GO)
 	},
 	{
+	.max	= 1,
+	.types	= BIT(NL80211_IFTYPE_P2P_DEVICE)
+	},
+	{
 	.max	= 7,
 	.types	= BIT(NL80211_IFTYPE_AP)
 	},
@@ -4956,6 +5418,13 @@
 
 int ath10k_mac_register(struct ath10k *ar)
 {
+	static const u32 cipher_suites[] = {
+		WLAN_CIPHER_SUITE_WEP40,
+		WLAN_CIPHER_SUITE_WEP104,
+		WLAN_CIPHER_SUITE_TKIP,
+		WLAN_CIPHER_SUITE_CCMP,
+		WLAN_CIPHER_SUITE_AES_CMAC,
+	};
 	struct ieee80211_supported_band *band;
 	struct ieee80211_sta_vht_cap vht_cap;
 	struct ieee80211_sta_ht_cap ht_cap;
@@ -4985,7 +5454,8 @@
 		band->bitrates = ath10k_g_rates;
 		band->ht_cap = ht_cap;
 
-		/* vht is not supported in 2.4 GHz */
+		/* Enable the VHT support at 2.4 GHz */
+		band->vht_cap = vht_cap;
 
 		ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band;
 	}
@@ -5018,18 +5488,19 @@
 
 	if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features))
 		ar->hw->wiphy->interface_modes |=
+			BIT(NL80211_IFTYPE_P2P_DEVICE) |
 			BIT(NL80211_IFTYPE_P2P_CLIENT) |
 			BIT(NL80211_IFTYPE_P2P_GO);
 
 	ar->hw->flags = IEEE80211_HW_SIGNAL_DBM |
 			IEEE80211_HW_SUPPORTS_PS |
 			IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
-			IEEE80211_HW_SUPPORTS_UAPSD |
 			IEEE80211_HW_MFP_CAPABLE |
 			IEEE80211_HW_REPORTS_TX_ACK_STATUS |
 			IEEE80211_HW_HAS_RATE_CONTROL |
 			IEEE80211_HW_AP_LINK_PS |
-			IEEE80211_HW_SPECTRUM_MGMT;
+			IEEE80211_HW_SPECTRUM_MGMT |
+			IEEE80211_HW_SW_CRYPTO_CONTROL;
 
 	ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
 
@@ -5049,6 +5520,19 @@
 
 	ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
 
+	if (test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) {
+		ar->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
+
+		/* Firmware delivers WPS/P2P Probe Requests frames to driver so
+		 * that userspace (e.g. wpa_supplicant/hostapd) can generate
+		 * correct Probe Responses. This is more of a hack advert..
+		 */
+		ar->hw->wiphy->probe_resp_offload |=
+			NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
+			NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
+			NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
+	}
+
 	ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 	ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
 	ar->hw->wiphy->max_remain_on_channel_duration = 5000;
@@ -5062,16 +5546,26 @@
 	 */
 	ar->hw->queues = 4;
 
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
-		ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
-		ar->hw->wiphy->n_iface_combinations =
-			ARRAY_SIZE(ath10k_10x_if_comb);
-	} else {
+	switch (ar->wmi.op_version) {
+	case ATH10K_FW_WMI_OP_VERSION_MAIN:
+	case ATH10K_FW_WMI_OP_VERSION_TLV:
 		ar->hw->wiphy->iface_combinations = ath10k_if_comb;
 		ar->hw->wiphy->n_iface_combinations =
 			ARRAY_SIZE(ath10k_if_comb);
-
 		ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
+		break;
+	case ATH10K_FW_WMI_OP_VERSION_10_1:
+	case ATH10K_FW_WMI_OP_VERSION_10_2:
+	case ATH10K_FW_WMI_OP_VERSION_10_2_4:
+		ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
+		ar->hw->wiphy->n_iface_combinations =
+			ARRAY_SIZE(ath10k_10x_if_comb);
+		break;
+	case ATH10K_FW_WMI_OP_VERSION_UNSET:
+	case ATH10K_FW_WMI_OP_VERSION_MAX:
+		WARN_ON(1);
+		ret = -EINVAL;
+		goto err_free;
 	}
 
 	ar->hw->netdev_features = NETIF_F_HW_CSUM;
@@ -5093,6 +5587,9 @@
 		goto err_free;
 	}
 
+	ar->hw->wiphy->cipher_suites = cipher_suites;
+	ar->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
 	ret = ieee80211_register_hw(ar->hw);
 	if (ret) {
 		ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 7abb836..7681237 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -58,12 +58,27 @@
 #define ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS 3
 
 #define QCA988X_2_0_DEVICE_ID	(0x003c)
+#define QCA6174_2_1_DEVICE_ID	(0x003e)
 
 static const struct pci_device_id ath10k_pci_id_table[] = {
 	{ PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */
+	{ PCI_VDEVICE(ATHEROS, QCA6174_2_1_DEVICE_ID) }, /* PCI-E QCA6174 V2.1 */
 	{0}
 };
 
+static const struct ath10k_pci_supp_chip ath10k_pci_supp_chips[] = {
+	/* QCA988X pre 2.0 chips are not supported because they need some nasty
+	 * hacks. ath10k doesn't have them and these devices crash horribly
+	 * because of that.
+	 */
+	{ QCA988X_2_0_DEVICE_ID, QCA988X_HW_2_0_CHIP_ID_REV },
+	{ QCA6174_2_1_DEVICE_ID, QCA6174_HW_2_1_CHIP_ID_REV },
+	{ QCA6174_2_1_DEVICE_ID, QCA6174_HW_2_2_CHIP_ID_REV },
+	{ QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_0_CHIP_ID_REV },
+	{ QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_1_CHIP_ID_REV },
+	{ QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_2_CHIP_ID_REV },
+};
+
 static void ath10k_pci_buffer_cleanup(struct ath10k *ar);
 static int ath10k_pci_cold_reset(struct ath10k *ar);
 static int ath10k_pci_warm_reset(struct ath10k *ar);
@@ -89,7 +104,7 @@
 	{
 		.flags = CE_ATTR_FLAGS,
 		.src_nentries = 0,
-		.src_sz_max = 512,
+		.src_sz_max = 2048,
 		.dest_nentries = 512,
 	},
 
@@ -159,7 +174,7 @@
 		.pipenum = __cpu_to_le32(1),
 		.pipedir = __cpu_to_le32(PIPEDIR_IN),
 		.nentries = __cpu_to_le32(32),
-		.nbytes_max = __cpu_to_le32(512),
+		.nbytes_max = __cpu_to_le32(2048),
 		.flags = __cpu_to_le32(CE_ATTR_FLAGS),
 		.reserved = __cpu_to_le32(0),
 	},
@@ -395,7 +410,7 @@
 		return -EIO;
 	}
 
-	ATH10K_SKB_CB(skb)->paddr = paddr;
+	ATH10K_SKB_RXCB(skb)->paddr = paddr;
 
 	ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr);
 	if (ret) {
@@ -864,7 +879,7 @@
 					     &flags) == 0) {
 		skb = transfer_context;
 		max_nbytes = skb->len + skb_tailroom(skb);
-		dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
+		dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
 				 max_nbytes, DMA_FROM_DEVICE);
 
 		if (unlikely(max_nbytes < nbytes)) {
@@ -1230,7 +1245,7 @@
 
 		ce_ring->per_transfer_context[i] = NULL;
 
-		dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
+		dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
 				 skb->len + skb_tailroom(skb),
 				 DMA_FROM_DEVICE);
 		dev_kfree_skb_any(skb);
@@ -1498,6 +1513,35 @@
 	return 0;
 }
 
+static int ath10k_pci_get_num_banks(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	switch (ar_pci->pdev->device) {
+	case QCA988X_2_0_DEVICE_ID:
+		return 1;
+	case QCA6174_2_1_DEVICE_ID:
+		switch (MS(ar->chip_id, SOC_CHIP_ID_REV)) {
+		case QCA6174_HW_1_0_CHIP_ID_REV:
+		case QCA6174_HW_1_1_CHIP_ID_REV:
+			return 3;
+		case QCA6174_HW_1_3_CHIP_ID_REV:
+			return 2;
+		case QCA6174_HW_2_1_CHIP_ID_REV:
+		case QCA6174_HW_2_2_CHIP_ID_REV:
+			return 6;
+		case QCA6174_HW_3_0_CHIP_ID_REV:
+		case QCA6174_HW_3_1_CHIP_ID_REV:
+		case QCA6174_HW_3_2_CHIP_ID_REV:
+			return 9;
+		}
+		break;
+	}
+
+	ath10k_warn(ar, "unknown number of banks, assuming 1\n");
+	return 1;
+}
+
 static int ath10k_pci_init_config(struct ath10k *ar)
 {
 	u32 interconnect_targ_addr;
@@ -1608,7 +1652,8 @@
 	/* first bank is switched to IRAM */
 	ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) &
 			 HI_EARLY_ALLOC_MAGIC_MASK);
-	ealloc_value |= ((1 << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
+	ealloc_value |= ((ath10k_pci_get_num_banks(ar) <<
+			  HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
 			 HI_EARLY_ALLOC_IRAM_BANKS_MASK);
 
 	ret = ath10k_pci_diag_write32(ar, ealloc_targ_addr, ealloc_value);
@@ -1804,12 +1849,12 @@
 	return 0;
 }
 
-static int ath10k_pci_chip_reset(struct ath10k *ar)
+static int ath10k_pci_qca988x_chip_reset(struct ath10k *ar)
 {
 	int i, ret;
 	u32 val;
 
-	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset\n");
+	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot 988x chip reset\n");
 
 	/* Some hardware revisions (e.g. CUS223v2) has issues with cold reset.
 	 * It is thus preferred to use warm reset which is safer but may not be
@@ -1873,11 +1918,53 @@
 		return ret;
 	}
 
-	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset complete (cold)\n");
+	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca988x chip reset complete (cold)\n");
 
 	return 0;
 }
 
+static int ath10k_pci_qca6174_chip_reset(struct ath10k *ar)
+{
+	int ret;
+
+	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca6174 chip reset\n");
+
+	/* FIXME: QCA6174 requires cold + warm reset to work. */
+
+	ret = ath10k_pci_cold_reset(ar);
+	if (ret) {
+		ath10k_warn(ar, "failed to cold reset: %d\n", ret);
+		return ret;
+	}
+
+	ret = ath10k_pci_wait_for_target_init(ar);
+	if (ret) {
+		ath10k_warn(ar, "failed to wait for target after cold reset: %d\n",
+				ret);
+		return ret;
+	}
+
+	ret = ath10k_pci_warm_reset(ar);
+	if (ret) {
+		ath10k_warn(ar, "failed to warm reset: %d\n", ret);
+		return ret;
+	}
+
+	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca6174 chip reset complete (cold)\n");
+
+	return 0;
+}
+
+static int ath10k_pci_chip_reset(struct ath10k *ar)
+{
+	if (QCA_REV_988X(ar))
+		return ath10k_pci_qca988x_chip_reset(ar);
+	else if (QCA_REV_6174(ar))
+		return ath10k_pci_qca6174_chip_reset(ar);
+	else
+		return -ENOTSUPP;
+}
+
 static int ath10k_pci_hif_power_up(struct ath10k *ar)
 {
 	int ret;
@@ -1902,6 +1989,12 @@
 	 */
 	ret = ath10k_pci_chip_reset(ar);
 	if (ret) {
+		if (ath10k_pci_has_fw_crashed(ar)) {
+			ath10k_warn(ar, "firmware crashed during chip reset\n");
+			ath10k_pci_fw_crashed_clear(ar);
+			ath10k_pci_fw_crashed_dump(ar);
+		}
+
 		ath10k_err(ar, "failed to reset chip: %d\n", ret);
 		goto err_sleep;
 	}
@@ -2033,6 +2126,7 @@
 		return;
 	}
 
+	ath10k_pci_irq_disable(ar);
 	ath10k_pci_fw_crashed_clear(ar);
 	ath10k_pci_fw_crashed_dump(ar);
 }
@@ -2102,6 +2196,7 @@
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
 	if (ath10k_pci_has_fw_crashed(ar)) {
+		ath10k_pci_irq_disable(ar);
 		ath10k_pci_fw_crashed_clear(ar);
 		ath10k_pci_fw_crashed_dump(ar);
 		return;
@@ -2344,8 +2439,6 @@
 
 	if (val & FW_IND_EVENT_PENDING) {
 		ath10k_warn(ar, "device has crashed during init\n");
-		ath10k_pci_fw_crashed_clear(ar);
-		ath10k_pci_fw_crashed_dump(ar);
 		return -ECOMM;
 	}
 
@@ -2476,17 +2569,46 @@
 	pci_disable_device(pdev);
 }
 
+static bool ath10k_pci_chip_is_supported(u32 dev_id, u32 chip_id)
+{
+	const struct ath10k_pci_supp_chip *supp_chip;
+	int i;
+	u32 rev_id = MS(chip_id, SOC_CHIP_ID_REV);
+
+	for (i = 0; i < ARRAY_SIZE(ath10k_pci_supp_chips); i++) {
+		supp_chip = &ath10k_pci_supp_chips[i];
+
+		if (supp_chip->dev_id == dev_id &&
+		    supp_chip->rev_id == rev_id)
+			return true;
+	}
+
+	return false;
+}
+
 static int ath10k_pci_probe(struct pci_dev *pdev,
 			    const struct pci_device_id *pci_dev)
 {
 	int ret = 0;
 	struct ath10k *ar;
 	struct ath10k_pci *ar_pci;
+	enum ath10k_hw_rev hw_rev;
 	u32 chip_id;
 
-	ar = ath10k_core_create(sizeof(*ar_pci), &pdev->dev,
-				ATH10K_BUS_PCI,
-				&ath10k_pci_hif_ops);
+	switch (pci_dev->device) {
+	case QCA988X_2_0_DEVICE_ID:
+		hw_rev = ATH10K_HW_QCA988X;
+		break;
+	case QCA6174_2_1_DEVICE_ID:
+		hw_rev = ATH10K_HW_QCA6174;
+		break;
+	default:
+		WARN_ON(1);
+		return -ENOTSUPP;
+	}
+
+	ar = ath10k_core_create(sizeof(*ar_pci), &pdev->dev, ATH10K_BUS_PCI,
+				hw_rev, &ath10k_pci_hif_ops);
 	if (!ar) {
 		dev_err(&pdev->dev, "failed to allocate core\n");
 		return -ENOMEM;
@@ -2515,12 +2637,6 @@
 		goto err_release;
 	}
 
-	chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS);
-	if (chip_id == 0xffffffff) {
-		ath10k_err(ar, "failed to get chip id\n");
-		goto err_sleep;
-	}
-
 	ret = ath10k_pci_alloc_pipes(ar);
 	if (ret) {
 		ath10k_err(ar, "failed to allocate copy engine pipes: %d\n",
@@ -2547,6 +2663,24 @@
 		goto err_deinit_irq;
 	}
 
+	ret = ath10k_pci_chip_reset(ar);
+	if (ret) {
+		ath10k_err(ar, "failed to reset chip: %d\n", ret);
+		goto err_free_irq;
+	}
+
+	chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS);
+	if (chip_id == 0xffffffff) {
+		ath10k_err(ar, "failed to get chip id\n");
+		goto err_free_irq;
+	}
+
+	if (!ath10k_pci_chip_is_supported(pdev->device, chip_id)) {
+		ath10k_err(ar, "device %04x with chip_id %08x isn't supported\n",
+			   pdev->device, chip_id);
+		goto err_sleep;
+	}
+
 	ath10k_pci_sleep(ar);
 
 	ret = ath10k_core_register(ar, chip_id);
diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h
index cf36511..bddf543 100644
--- a/drivers/net/wireless/ath/ath10k/pci.h
+++ b/drivers/net/wireless/ath/ath10k/pci.h
@@ -152,6 +152,11 @@
 	struct tasklet_struct intr;
 };
 
+struct ath10k_pci_supp_chip {
+	u32 dev_id;
+	u32 rev_id;
+};
+
 struct ath10k_pci {
 	struct pci_dev *pdev;
 	struct device *dev;
@@ -189,7 +194,7 @@
 
 #define ATH10K_PCI_RX_POST_RETRY_MS 50
 #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
-#define PCIE_WAKE_TIMEOUT 5000	/* 5ms */
+#define PCIE_WAKE_TIMEOUT 10000	/* 10ms */
 
 #define BAR_NUM 0
 
diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h
index e1ffdd5..e9cc778 100644
--- a/drivers/net/wireless/ath/ath10k/rx_desc.h
+++ b/drivers/net/wireless/ath/ath10k/rx_desc.h
@@ -850,7 +850,7 @@
 
 #define RX_PPDU_END_INFO1_PPDU_DONE (1 << 15)
 
-struct rx_ppdu_end {
+struct rx_ppdu_end_common {
 	__le32 evm_p0;
 	__le32 evm_p1;
 	__le32 evm_p2;
@@ -873,10 +873,33 @@
 	u8 phy_err_code;
 	__le16 flags; /* %RX_PPDU_END_FLAGS_ */
 	__le32 info0; /* %RX_PPDU_END_INFO0_ */
+} __packed;
+
+struct rx_ppdu_end_qca988x {
 	__le16 bb_length;
 	__le16 info1; /* %RX_PPDU_END_INFO1_ */
 } __packed;
 
+#define RX_PPDU_END_RTT_CORRELATION_VALUE_MASK 0x00ffffff
+#define RX_PPDU_END_RTT_CORRELATION_VALUE_LSB  0
+#define RX_PPDU_END_RTT_UNUSED_MASK            0x7f000000
+#define RX_PPDU_END_RTT_UNUSED_LSB             24
+#define RX_PPDU_END_RTT_NORMAL_MODE            BIT(31)
+
+struct rx_ppdu_end_qca6174 {
+	__le32 rtt; /* %RX_PPDU_END_RTT_ */
+	__le16 bb_length;
+	__le16 info1; /* %RX_PPDU_END_INFO1_ */
+} __packed;
+
+struct rx_ppdu_end {
+	struct rx_ppdu_end_common common;
+	union {
+		struct rx_ppdu_end_qca988x qca988x;
+		struct rx_ppdu_end_qca6174 qca6174;
+	} __packed;
+} __packed;
+
 /*
  * evm_p0
  *		EVM for pilot 0.  Contain EVM for streams: 0, 1, 2 and 3.
diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c
index 127c8df..8732673 100644
--- a/drivers/net/wireless/ath/ath10k/spectral.c
+++ b/drivers/net/wireless/ath/ath10k/spectral.c
@@ -17,6 +17,7 @@
 #include <linux/relay.h>
 #include "core.h"
 #include "debug.h"
+#include "wmi-ops.h"
 
 static void send_fft_sample(struct ath10k *ar,
 			    const struct fft_sample_tlv *fft_sample_tlv)
diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h
index 9d0ae30..a417aae 100644
--- a/drivers/net/wireless/ath/ath10k/targaddrs.h
+++ b/drivers/net/wireless/ath/ath10k/targaddrs.h
@@ -18,6 +18,8 @@
 #ifndef __TARGADDRS_H__
 #define __TARGADDRS_H__
 
+#include "hw.h"
+
 /*
  * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the
  * host_interest structure.  It must match the address of the _host_interest
@@ -445,4 +447,7 @@
 #define QCA988X_BOARD_DATA_SZ     7168
 #define QCA988X_BOARD_EXT_DATA_SZ 0
 
+#define QCA6174_BOARD_DATA_SZ     8192
+#define QCA6174_BOARD_EXT_DATA_SZ 0
+
 #endif /* __TARGADDRS_H__ */
diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c
index 483db9c..b084f88 100644
--- a/drivers/net/wireless/ath/ath10k/testmode.c
+++ b/drivers/net/wireless/ath/ath10k/testmode.c
@@ -187,13 +187,14 @@
 
 	memcpy(ar->testmode.orig_fw_features, ar->fw_features,
 	       sizeof(ar->fw_features));
+	ar->testmode.orig_wmi_op_version = ar->wmi.op_version;
 
 	/* utf.bin firmware image does not advertise firmware features. Do
 	 * an ugly hack where we force the firmware features so that wmi.c
 	 * will use the correct WMI interface.
 	 */
 	memset(ar->fw_features, 0, sizeof(ar->fw_features));
-	__set_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features);
+	ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
 
 	ret = ath10k_hif_power_up(ar);
 	if (ret) {
@@ -224,6 +225,7 @@
 	/* return the original firmware features */
 	memcpy(ar->fw_features, ar->testmode.orig_fw_features,
 	       sizeof(ar->fw_features));
+	ar->wmi.op_version = ar->testmode.orig_wmi_op_version;
 
 	release_firmware(ar->testmode.utf);
 	ar->testmode.utf = NULL;
@@ -250,6 +252,7 @@
 	/* return the original firmware features */
 	memcpy(ar->fw_features, ar->testmode.orig_fw_features,
 	       sizeof(ar->fw_features));
+	ar->wmi.op_version = ar->testmode.orig_wmi_op_version;
 
 	release_firmware(ar->testmode.utf);
 	ar->testmode.utf = NULL;
diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c
new file mode 100644
index 0000000..aede750
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/thermal.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include "core.h"
+#include "debug.h"
+#include "wmi-ops.h"
+
+static int ath10k_thermal_get_active_vifs(struct ath10k *ar,
+					  enum wmi_vdev_type type)
+{
+	struct ath10k_vif *arvif;
+	int count = 0;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	list_for_each_entry(arvif, &ar->arvifs, list) {
+		if (!arvif->is_started)
+			continue;
+
+		if (!arvif->is_up)
+			continue;
+
+		if (arvif->vdev_type != type)
+			continue;
+
+		count++;
+	}
+	return count;
+}
+
+static int ath10k_thermal_get_max_dutycycle(struct thermal_cooling_device *cdev,
+					    unsigned long *state)
+{
+	*state = ATH10K_QUIET_DUTY_CYCLE_MAX;
+
+	return 0;
+}
+
+static int ath10k_thermal_get_cur_dutycycle(struct thermal_cooling_device *cdev,
+					    unsigned long *state)
+{
+	struct ath10k *ar = cdev->devdata;
+
+	mutex_lock(&ar->conf_mutex);
+	*state = ar->thermal.duty_cycle;
+	mutex_unlock(&ar->conf_mutex);
+
+	return 0;
+}
+
+static int ath10k_thermal_set_cur_dutycycle(struct thermal_cooling_device *cdev,
+					    unsigned long duty_cycle)
+{
+	struct ath10k *ar = cdev->devdata;
+	u32 period, duration, enabled;
+	int num_bss, ret = 0;
+
+	mutex_lock(&ar->conf_mutex);
+	if (ar->state != ATH10K_STATE_ON) {
+		ret = -ENETDOWN;
+		goto out;
+	}
+
+	if (duty_cycle > ATH10K_QUIET_DUTY_CYCLE_MAX) {
+		ath10k_warn(ar, "duty cycle %ld is exceeding the limit %d\n",
+			    duty_cycle, ATH10K_QUIET_DUTY_CYCLE_MAX);
+		ret = -EINVAL;
+		goto out;
+	}
+	/* TODO: Right now, thermal mitigation is handled only for single/multi
+	 * vif AP mode. Since quiet param is not validated in STA mode, it needs
+	 * to be investigated further to handle multi STA and multi-vif (AP+STA)
+	 * mode properly.
+	 */
+	num_bss = ath10k_thermal_get_active_vifs(ar, WMI_VDEV_TYPE_AP);
+	if (!num_bss) {
+		ath10k_warn(ar, "no active AP interfaces\n");
+		ret = -ENETDOWN;
+		goto out;
+	}
+	period = max(ATH10K_QUIET_PERIOD_MIN,
+		     (ATH10K_QUIET_PERIOD_DEFAULT / num_bss));
+	duration = (period * duty_cycle) / 100;
+	enabled = duration ? 1 : 0;
+
+	ret = ath10k_wmi_pdev_set_quiet_mode(ar, period, duration,
+					     ATH10K_QUIET_START_OFFSET,
+					     enabled);
+	if (ret) {
+		ath10k_warn(ar, "failed to set quiet mode period %u duarion %u enabled %u ret %d\n",
+			    period, duration, enabled, ret);
+		goto out;
+	}
+	ar->thermal.duty_cycle = duty_cycle;
+out:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static struct thermal_cooling_device_ops ath10k_thermal_ops = {
+	.get_max_state = ath10k_thermal_get_max_dutycycle,
+	.get_cur_state = ath10k_thermal_get_cur_dutycycle,
+	.set_cur_state = ath10k_thermal_set_cur_dutycycle,
+};
+
+static ssize_t ath10k_thermal_show_temp(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct ath10k *ar = dev_get_drvdata(dev);
+	int ret, temperature;
+
+	mutex_lock(&ar->conf_mutex);
+
+	/* Can't get temperature when the card is off */
+	if (ar->state != ATH10K_STATE_ON) {
+		ret = -ENETDOWN;
+		goto out;
+	}
+
+	reinit_completion(&ar->thermal.wmi_sync);
+	ret = ath10k_wmi_pdev_get_temperature(ar);
+	if (ret) {
+		ath10k_warn(ar, "failed to read temperature %d\n", ret);
+		goto out;
+	}
+
+	if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
+		ret = -ESHUTDOWN;
+		goto out;
+	}
+
+	ret = wait_for_completion_timeout(&ar->thermal.wmi_sync,
+					  ATH10K_THERMAL_SYNC_TIMEOUT_HZ);
+	if (ret == 0) {
+		ath10k_warn(ar, "failed to synchronize thermal read\n");
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	spin_lock_bh(&ar->data_lock);
+	temperature = ar->thermal.temperature;
+	spin_unlock_bh(&ar->data_lock);
+
+	/* display in millidegree celcius */
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000);
+out:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature)
+{
+	spin_lock_bh(&ar->data_lock);
+	ar->thermal.temperature = temperature;
+	spin_unlock_bh(&ar->data_lock);
+	complete(&ar->thermal.wmi_sync);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ath10k_thermal_show_temp,
+			  NULL, 0);
+
+static struct attribute *ath10k_hwmon_attrs[] = {
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(ath10k_hwmon);
+
+int ath10k_thermal_register(struct ath10k *ar)
+{
+	struct thermal_cooling_device *cdev;
+	struct device *hwmon_dev;
+	int ret;
+
+	cdev = thermal_cooling_device_register("ath10k_thermal", ar,
+					       &ath10k_thermal_ops);
+
+	if (IS_ERR(cdev)) {
+		ath10k_err(ar, "failed to setup thermal device result: %ld\n",
+			   PTR_ERR(cdev));
+		return -EINVAL;
+	}
+
+	ret = sysfs_create_link(&ar->dev->kobj, &cdev->device.kobj,
+				"cooling_device");
+	if (ret) {
+		ath10k_err(ar, "failed to create thermal symlink\n");
+		goto err_cooling_destroy;
+	}
+
+	ar->thermal.cdev = cdev;
+
+	/* Do not register hwmon device when temperature reading is not
+	 * supported by firmware
+	 */
+	if (ar->wmi.op_version != ATH10K_FW_WMI_OP_VERSION_10_2_4)
+		return 0;
+
+	/* Avoid linking error on devm_hwmon_device_register_with_groups, I
+	 * guess linux/hwmon.h is missing proper stubs. */
+	if (!config_enabled(CONFIG_HWMON))
+		return 0;
+
+	hwmon_dev = devm_hwmon_device_register_with_groups(ar->dev,
+							   "ath10k_hwmon", ar,
+							   ath10k_hwmon_groups);
+	if (IS_ERR(hwmon_dev)) {
+		ath10k_err(ar, "failed to register hwmon device: %ld\n",
+			   PTR_ERR(hwmon_dev));
+		ret = -EINVAL;
+		goto err_remove_link;
+	}
+	return 0;
+
+err_remove_link:
+	sysfs_remove_link(&ar->dev->kobj, "thermal_sensor");
+err_cooling_destroy:
+	thermal_cooling_device_unregister(cdev);
+	return ret;
+}
+
+void ath10k_thermal_unregister(struct ath10k *ar)
+{
+	thermal_cooling_device_unregister(ar->thermal.cdev);
+	sysfs_remove_link(&ar->dev->kobj, "cooling_device");
+}
diff --git a/drivers/net/wireless/ath/ath10k/thermal.h b/drivers/net/wireless/ath/ath10k/thermal.h
new file mode 100644
index 0000000..bccc17a
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/thermal.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _THERMAL_
+#define _THERMAL_
+
+#define ATH10K_QUIET_PERIOD_DEFAULT     100
+#define ATH10K_QUIET_PERIOD_MIN         25
+#define ATH10K_QUIET_START_OFFSET       10
+#define ATH10K_QUIET_DUTY_CYCLE_MAX     70
+#define ATH10K_HWMON_NAME_LEN           15
+#define ATH10K_THERMAL_SYNC_TIMEOUT_HZ (5*HZ)
+
+struct ath10k_thermal {
+	struct thermal_cooling_device *cdev;
+	struct completion wmi_sync;
+
+	/* protected by conf_mutex */
+	u32 duty_cycle;
+	/* temperature value in Celcius degree
+	 * protected by data_lock
+	 */
+	int temperature;
+};
+
+#ifdef CONFIG_THERMAL
+int ath10k_thermal_register(struct ath10k *ar);
+void ath10k_thermal_unregister(struct ath10k *ar);
+void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature);
+#else
+static inline int ath10k_thermal_register(struct ath10k *ar)
+{
+	return 0;
+}
+
+static inline void ath10k_thermal_unregister(struct ath10k *ar)
+{
+}
+
+static inline void ath10k_thermal_event_temperature(struct ath10k *ar,
+						    int temperature)
+{
+}
+
+#endif
+#endif /* _THERMAL_ */
diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h
index b3bba04..d3575a6 100644
--- a/drivers/net/wireless/ath/ath10k/trace.h
+++ b/drivers/net/wireless/ath/ath10k/trace.h
@@ -453,6 +453,74 @@
 	 )
 );
 
+TRACE_EVENT(ath10k_wmi_diag_container,
+	    TP_PROTO(struct ath10k *ar,
+		     u8 type,
+		     u32 timestamp,
+		     u32 code,
+		     u16 len,
+		     const void *data),
+
+	TP_ARGS(ar, type, timestamp, code, len, data),
+
+	TP_STRUCT__entry(
+		__string(device, dev_name(ar->dev))
+		__string(driver, dev_driver_string(ar->dev))
+		__field(u8, type)
+		__field(u32, timestamp)
+		__field(u32, code)
+		__field(u16, len)
+		__dynamic_array(u8, data, len)
+	),
+
+	TP_fast_assign(
+		__assign_str(device, dev_name(ar->dev));
+		__assign_str(driver, dev_driver_string(ar->dev));
+		__entry->type = type;
+		__entry->timestamp = timestamp;
+		__entry->code = code;
+		__entry->len = len;
+		memcpy(__get_dynamic_array(data), data, len);
+	),
+
+	TP_printk(
+		"%s %s diag container type %hhu timestamp %u code %u len %d",
+		__get_str(driver),
+		__get_str(device),
+		__entry->type,
+		__entry->timestamp,
+		__entry->code,
+		__entry->len
+	)
+);
+
+TRACE_EVENT(ath10k_wmi_diag,
+	    TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+
+	TP_ARGS(ar, data, len),
+
+	TP_STRUCT__entry(
+		__string(device, dev_name(ar->dev))
+		__string(driver, dev_driver_string(ar->dev))
+		__field(u16, len)
+		__dynamic_array(u8, data, len)
+	),
+
+	TP_fast_assign(
+		__assign_str(device, dev_name(ar->dev));
+		__assign_str(driver, dev_driver_string(ar->dev));
+		__entry->len = len;
+		memcpy(__get_dynamic_array(data), data, len);
+	),
+
+	TP_printk(
+		"%s %s tlv diag len %d",
+		__get_str(driver),
+		__get_str(device),
+		__entry->len
+	)
+);
+
 #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
 
 /* we don't want to use include/trace/events */
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 7579de8..3f00cec 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -64,7 +64,13 @@
 		return;
 	}
 
-	msdu = htt->pending_tx[tx_done->msdu_id];
+	msdu = idr_find(&htt->pending_tx, tx_done->msdu_id);
+	if (!msdu) {
+		ath10k_warn(ar, "received tx completion for invalid msdu_id: %d\n",
+			    tx_done->msdu_id);
+		return;
+	}
+
 	skb_cb = ATH10K_SKB_CB(msdu);
 
 	dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
@@ -95,7 +101,6 @@
 	/* we do not own the msdu anymore */
 
 exit:
-	htt->pending_tx[tx_done->msdu_id] = NULL;
 	ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
 	__ath10k_htt_tx_dec_pending(htt);
 	if (htt->num_pending_tx == 0)
diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h
new file mode 100644
index 0000000..c8b64e7
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h
@@ -0,0 +1,1063 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2014 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WMI_OPS_H_
+#define _WMI_OPS_H_
+
+struct ath10k;
+struct sk_buff;
+
+struct wmi_ops {
+	void (*rx)(struct ath10k *ar, struct sk_buff *skb);
+	void (*map_svc)(const __le32 *in, unsigned long *out, size_t len);
+
+	int (*pull_scan)(struct ath10k *ar, struct sk_buff *skb,
+			 struct wmi_scan_ev_arg *arg);
+	int (*pull_mgmt_rx)(struct ath10k *ar, struct sk_buff *skb,
+			    struct wmi_mgmt_rx_ev_arg *arg);
+	int (*pull_ch_info)(struct ath10k *ar, struct sk_buff *skb,
+			    struct wmi_ch_info_ev_arg *arg);
+	int (*pull_vdev_start)(struct ath10k *ar, struct sk_buff *skb,
+			       struct wmi_vdev_start_ev_arg *arg);
+	int (*pull_peer_kick)(struct ath10k *ar, struct sk_buff *skb,
+			      struct wmi_peer_kick_ev_arg *arg);
+	int (*pull_swba)(struct ath10k *ar, struct sk_buff *skb,
+			 struct wmi_swba_ev_arg *arg);
+	int (*pull_phyerr)(struct ath10k *ar, struct sk_buff *skb,
+			   struct wmi_phyerr_ev_arg *arg);
+	int (*pull_svc_rdy)(struct ath10k *ar, struct sk_buff *skb,
+			    struct wmi_svc_rdy_ev_arg *arg);
+	int (*pull_rdy)(struct ath10k *ar, struct sk_buff *skb,
+			struct wmi_rdy_ev_arg *arg);
+	int (*pull_fw_stats)(struct ath10k *ar, struct sk_buff *skb,
+			     struct ath10k_fw_stats *stats);
+
+	struct sk_buff *(*gen_pdev_suspend)(struct ath10k *ar, u32 suspend_opt);
+	struct sk_buff *(*gen_pdev_resume)(struct ath10k *ar);
+	struct sk_buff *(*gen_pdev_set_rd)(struct ath10k *ar, u16 rd, u16 rd2g,
+					   u16 rd5g, u16 ctl2g, u16 ctl5g,
+					   enum wmi_dfs_region dfs_reg);
+	struct sk_buff *(*gen_pdev_set_param)(struct ath10k *ar, u32 id,
+					      u32 value);
+	struct sk_buff *(*gen_init)(struct ath10k *ar);
+	struct sk_buff *(*gen_start_scan)(struct ath10k *ar,
+					  const struct wmi_start_scan_arg *arg);
+	struct sk_buff *(*gen_stop_scan)(struct ath10k *ar,
+					 const struct wmi_stop_scan_arg *arg);
+	struct sk_buff *(*gen_vdev_create)(struct ath10k *ar, u32 vdev_id,
+					   enum wmi_vdev_type type,
+					   enum wmi_vdev_subtype subtype,
+					   const u8 macaddr[ETH_ALEN]);
+	struct sk_buff *(*gen_vdev_delete)(struct ath10k *ar, u32 vdev_id);
+	struct sk_buff *(*gen_vdev_start)(struct ath10k *ar,
+					  const struct wmi_vdev_start_request_arg *arg,
+					  bool restart);
+	struct sk_buff *(*gen_vdev_stop)(struct ath10k *ar, u32 vdev_id);
+	struct sk_buff *(*gen_vdev_up)(struct ath10k *ar, u32 vdev_id, u32 aid,
+				       const u8 *bssid);
+	struct sk_buff *(*gen_vdev_down)(struct ath10k *ar, u32 vdev_id);
+	struct sk_buff *(*gen_vdev_set_param)(struct ath10k *ar, u32 vdev_id,
+					      u32 param_id, u32 param_value);
+	struct sk_buff *(*gen_vdev_install_key)(struct ath10k *ar,
+						const struct wmi_vdev_install_key_arg *arg);
+	struct sk_buff *(*gen_vdev_spectral_conf)(struct ath10k *ar,
+						  const struct wmi_vdev_spectral_conf_arg *arg);
+	struct sk_buff *(*gen_vdev_spectral_enable)(struct ath10k *ar, u32 vdev_id,
+						    u32 trigger, u32 enable);
+	struct sk_buff *(*gen_vdev_wmm_conf)(struct ath10k *ar, u32 vdev_id,
+					     const struct wmi_wmm_params_all_arg *arg);
+	struct sk_buff *(*gen_peer_create)(struct ath10k *ar, u32 vdev_id,
+					   const u8 peer_addr[ETH_ALEN]);
+	struct sk_buff *(*gen_peer_delete)(struct ath10k *ar, u32 vdev_id,
+					   const u8 peer_addr[ETH_ALEN]);
+	struct sk_buff *(*gen_peer_flush)(struct ath10k *ar, u32 vdev_id,
+					  const u8 peer_addr[ETH_ALEN],
+					  u32 tid_bitmap);
+	struct sk_buff *(*gen_peer_set_param)(struct ath10k *ar, u32 vdev_id,
+					      const u8 *peer_addr,
+					      enum wmi_peer_param param_id,
+					      u32 param_value);
+	struct sk_buff *(*gen_peer_assoc)(struct ath10k *ar,
+					  const struct wmi_peer_assoc_complete_arg *arg);
+	struct sk_buff *(*gen_set_psmode)(struct ath10k *ar, u32 vdev_id,
+					  enum wmi_sta_ps_mode psmode);
+	struct sk_buff *(*gen_set_sta_ps)(struct ath10k *ar, u32 vdev_id,
+					  enum wmi_sta_powersave_param param_id,
+					  u32 value);
+	struct sk_buff *(*gen_set_ap_ps)(struct ath10k *ar, u32 vdev_id,
+					 const u8 *mac,
+					 enum wmi_ap_ps_peer_param param_id,
+					 u32 value);
+	struct sk_buff *(*gen_scan_chan_list)(struct ath10k *ar,
+					      const struct wmi_scan_chan_list_arg *arg);
+	struct sk_buff *(*gen_beacon_dma)(struct ath10k *ar, u32 vdev_id,
+					  const void *bcn, size_t bcn_len,
+					  u32 bcn_paddr, bool dtim_zero,
+					  bool deliver_cab);
+	struct sk_buff *(*gen_pdev_set_wmm)(struct ath10k *ar,
+					    const struct wmi_wmm_params_all_arg *arg);
+	struct sk_buff *(*gen_request_stats)(struct ath10k *ar, u32 stats_mask);
+	struct sk_buff *(*gen_force_fw_hang)(struct ath10k *ar,
+					     enum wmi_force_fw_hang_type type,
+					     u32 delay_ms);
+	struct sk_buff *(*gen_mgmt_tx)(struct ath10k *ar, struct sk_buff *skb);
+	struct sk_buff *(*gen_dbglog_cfg)(struct ath10k *ar, u32 module_enable,
+					  u32 log_level);
+	struct sk_buff *(*gen_pktlog_enable)(struct ath10k *ar, u32 filter);
+	struct sk_buff *(*gen_pktlog_disable)(struct ath10k *ar);
+	struct sk_buff *(*gen_pdev_set_quiet_mode)(struct ath10k *ar,
+						   u32 period, u32 duration,
+						   u32 next_offset,
+						   u32 enabled);
+	struct sk_buff *(*gen_pdev_get_temperature)(struct ath10k *ar);
+	struct sk_buff *(*gen_addba_clear_resp)(struct ath10k *ar, u32 vdev_id,
+						const u8 *mac);
+	struct sk_buff *(*gen_addba_send)(struct ath10k *ar, u32 vdev_id,
+					  const u8 *mac, u32 tid, u32 buf_size);
+	struct sk_buff *(*gen_addba_set_resp)(struct ath10k *ar, u32 vdev_id,
+					      const u8 *mac, u32 tid,
+					      u32 status);
+	struct sk_buff *(*gen_delba_send)(struct ath10k *ar, u32 vdev_id,
+					  const u8 *mac, u32 tid, u32 initiator,
+					  u32 reason);
+	struct sk_buff *(*gen_bcn_tmpl)(struct ath10k *ar, u32 vdev_id,
+					u32 tim_ie_offset, struct sk_buff *bcn,
+					u32 prb_caps, u32 prb_erp,
+					void *prb_ies, size_t prb_ies_len);
+	struct sk_buff *(*gen_prb_tmpl)(struct ath10k *ar, u32 vdev_id,
+					struct sk_buff *bcn);
+	struct sk_buff *(*gen_p2p_go_bcn_ie)(struct ath10k *ar, u32 vdev_id,
+					     const u8 *p2p_ie);
+	struct sk_buff *(*gen_vdev_sta_uapsd)(struct ath10k *ar, u32 vdev_id,
+					      const u8 peer_addr[ETH_ALEN],
+					      const struct wmi_sta_uapsd_auto_trig_arg *args,
+					      u32 num_ac);
+	struct sk_buff *(*gen_sta_keepalive)(struct ath10k *ar,
+					     const struct wmi_sta_keepalive_arg *arg);
+};
+
+int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
+
+static inline int
+ath10k_wmi_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+	if (WARN_ON_ONCE(!ar->wmi.ops->rx))
+		return -EOPNOTSUPP;
+
+	ar->wmi.ops->rx(ar, skb);
+	return 0;
+}
+
+static inline int
+ath10k_wmi_map_svc(struct ath10k *ar, const __le32 *in, unsigned long *out,
+		   size_t len)
+{
+	if (!ar->wmi.ops->map_svc)
+		return -EOPNOTSUPP;
+
+	ar->wmi.ops->map_svc(in, out, len);
+	return 0;
+}
+
+static inline int
+ath10k_wmi_pull_scan(struct ath10k *ar, struct sk_buff *skb,
+		     struct wmi_scan_ev_arg *arg)
+{
+	if (!ar->wmi.ops->pull_scan)
+		return -EOPNOTSUPP;
+
+	return ar->wmi.ops->pull_scan(ar, skb, arg);
+}
+
+static inline int
+ath10k_wmi_pull_mgmt_rx(struct ath10k *ar, struct sk_buff *skb,
+			struct wmi_mgmt_rx_ev_arg *arg)
+{
+	if (!ar->wmi.ops->pull_mgmt_rx)
+		return -EOPNOTSUPP;
+
+	return ar->wmi.ops->pull_mgmt_rx(ar, skb, arg);
+}
+
+static inline int
+ath10k_wmi_pull_ch_info(struct ath10k *ar, struct sk_buff *skb,
+			struct wmi_ch_info_ev_arg *arg)
+{
+	if (!ar->wmi.ops->pull_ch_info)
+		return -EOPNOTSUPP;
+
+	return ar->wmi.ops->pull_ch_info(ar, skb, arg);
+}
+
+static inline int
+ath10k_wmi_pull_vdev_start(struct ath10k *ar, struct sk_buff *skb,
+			   struct wmi_vdev_start_ev_arg *arg)
+{
+	if (!ar->wmi.ops->pull_vdev_start)
+		return -EOPNOTSUPP;
+
+	return ar->wmi.ops->pull_vdev_start(ar, skb, arg);
+}
+
+static inline int
+ath10k_wmi_pull_peer_kick(struct ath10k *ar, struct sk_buff *skb,
+			  struct wmi_peer_kick_ev_arg *arg)
+{
+	if (!ar->wmi.ops->pull_peer_kick)
+		return -EOPNOTSUPP;
+
+	return ar->wmi.ops->pull_peer_kick(ar, skb, arg);
+}
+
+static inline int
+ath10k_wmi_pull_swba(struct ath10k *ar, struct sk_buff *skb,
+		     struct wmi_swba_ev_arg *arg)
+{
+	if (!ar->wmi.ops->pull_swba)
+		return -EOPNOTSUPP;
+
+	return ar->wmi.ops->pull_swba(ar, skb, arg);
+}
+
+static inline int
+ath10k_wmi_pull_phyerr(struct ath10k *ar, struct sk_buff *skb,
+		       struct wmi_phyerr_ev_arg *arg)
+{
+	if (!ar->wmi.ops->pull_phyerr)
+		return -EOPNOTSUPP;
+
+	return ar->wmi.ops->pull_phyerr(ar, skb, arg);
+}
+
+static inline int
+ath10k_wmi_pull_svc_rdy(struct ath10k *ar, struct sk_buff *skb,
+			struct wmi_svc_rdy_ev_arg *arg)
+{
+	if (!ar->wmi.ops->pull_svc_rdy)
+		return -EOPNOTSUPP;
+
+	return ar->wmi.ops->pull_svc_rdy(ar, skb, arg);
+}
+
+static inline int
+ath10k_wmi_pull_rdy(struct ath10k *ar, struct sk_buff *skb,
+		    struct wmi_rdy_ev_arg *arg)
+{
+	if (!ar->wmi.ops->pull_rdy)
+		return -EOPNOTSUPP;
+
+	return ar->wmi.ops->pull_rdy(ar, skb, arg);
+}
+
+static inline int
+ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
+			 struct ath10k_fw_stats *stats)
+{
+	if (!ar->wmi.ops->pull_fw_stats)
+		return -EOPNOTSUPP;
+
+	return ar->wmi.ops->pull_fw_stats(ar, skb, stats);
+}
+
+static inline int
+ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu);
+	struct sk_buff *skb;
+	int ret;
+
+	if (!ar->wmi.ops->gen_mgmt_tx)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_mgmt_tx(ar, msdu);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	ret = ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->mgmt_tx_cmdid);
+	if (ret)
+		return ret;
+
+	/* FIXME There's no ACK event for Management Tx. This probably
+	 * shouldn't be called here either. */
+	info->flags |= IEEE80211_TX_STAT_ACK;
+	ieee80211_tx_status_irqsafe(ar->hw, msdu);
+
+	return 0;
+}
+
+static inline 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)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_pdev_set_rd)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_pdev_set_rd(ar, rd, rd2g, rd5g, ctl2g, ctl5g,
+					   dfs_reg);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->pdev_set_regdomain_cmdid);
+}
+
+static inline int
+ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_pdev_suspend)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_pdev_suspend(ar, suspend_opt);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid);
+}
+
+static inline int
+ath10k_wmi_pdev_resume_target(struct ath10k *ar)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_pdev_resume)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_pdev_resume(ar);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid);
+}
+
+static inline int
+ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_pdev_set_param)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_pdev_set_param(ar, id, value);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid);
+}
+
+static inline int
+ath10k_wmi_cmd_init(struct ath10k *ar)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_init)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_init(ar);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->init_cmdid);
+}
+
+static inline int
+ath10k_wmi_start_scan(struct ath10k *ar,
+		      const struct wmi_start_scan_arg *arg)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_start_scan)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_start_scan(ar, arg);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid);
+}
+
+static inline int
+ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_stop_scan)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_stop_scan(ar, arg);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid);
+}
+
+static inline int
+ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
+		       enum wmi_vdev_type type,
+		       enum wmi_vdev_subtype subtype,
+		       const u8 macaddr[ETH_ALEN])
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_vdev_create)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_vdev_create(ar, vdev_id, type, subtype, macaddr);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid);
+}
+
+static inline int
+ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_vdev_delete)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_vdev_delete(ar, vdev_id);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid);
+}
+
+static inline int
+ath10k_wmi_vdev_start(struct ath10k *ar,
+		      const struct wmi_vdev_start_request_arg *arg)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_vdev_start)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_vdev_start(ar, arg, false);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->vdev_start_request_cmdid);
+}
+
+static inline int
+ath10k_wmi_vdev_restart(struct ath10k *ar,
+			const struct wmi_vdev_start_request_arg *arg)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_vdev_start)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_vdev_start(ar, arg, true);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->vdev_restart_request_cmdid);
+}
+
+static inline int
+ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_vdev_stop)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_vdev_stop(ar, vdev_id);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid);
+}
+
+static inline int
+ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_vdev_up)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_vdev_up(ar, vdev_id, aid, bssid);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid);
+}
+
+static inline int
+ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_vdev_down)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_vdev_down(ar, vdev_id);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid);
+}
+
+static inline int
+ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, u32 param_id,
+			  u32 param_value)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_vdev_set_param)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_vdev_set_param(ar, vdev_id, param_id,
+					      param_value);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid);
+}
+
+static inline int
+ath10k_wmi_vdev_install_key(struct ath10k *ar,
+			    const struct wmi_vdev_install_key_arg *arg)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_vdev_install_key)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_vdev_install_key(ar, arg);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->vdev_install_key_cmdid);
+}
+
+static inline int
+ath10k_wmi_vdev_spectral_conf(struct ath10k *ar,
+			      const struct wmi_vdev_spectral_conf_arg *arg)
+{
+	struct sk_buff *skb;
+	u32 cmd_id;
+
+	skb = ar->wmi.ops->gen_vdev_spectral_conf(ar, arg);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	cmd_id = ar->wmi.cmd->vdev_spectral_scan_configure_cmdid;
+	return ath10k_wmi_cmd_send(ar, skb, cmd_id);
+}
+
+static inline int
+ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger,
+				u32 enable)
+{
+	struct sk_buff *skb;
+	u32 cmd_id;
+
+	skb = ar->wmi.ops->gen_vdev_spectral_enable(ar, vdev_id, trigger,
+						    enable);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	cmd_id = ar->wmi.cmd->vdev_spectral_scan_enable_cmdid;
+	return ath10k_wmi_cmd_send(ar, skb, cmd_id);
+}
+
+static inline int
+ath10k_wmi_vdev_sta_uapsd(struct ath10k *ar, u32 vdev_id,
+			  const u8 peer_addr[ETH_ALEN],
+			  const struct wmi_sta_uapsd_auto_trig_arg *args,
+			  u32 num_ac)
+{
+	struct sk_buff *skb;
+	u32 cmd_id;
+
+	if (!ar->wmi.ops->gen_vdev_sta_uapsd)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_vdev_sta_uapsd(ar, vdev_id, peer_addr, args,
+					      num_ac);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	cmd_id = ar->wmi.cmd->sta_uapsd_auto_trig_cmdid;
+	return ath10k_wmi_cmd_send(ar, skb, cmd_id);
+}
+
+static inline int
+ath10k_wmi_vdev_wmm_conf(struct ath10k *ar, u32 vdev_id,
+			 const struct wmi_wmm_params_all_arg *arg)
+{
+	struct sk_buff *skb;
+	u32 cmd_id;
+
+	skb = ar->wmi.ops->gen_vdev_wmm_conf(ar, vdev_id, arg);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	cmd_id = ar->wmi.cmd->vdev_set_wmm_params_cmdid;
+	return ath10k_wmi_cmd_send(ar, skb, cmd_id);
+}
+
+static inline int
+ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
+		       const u8 peer_addr[ETH_ALEN])
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_peer_create)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_peer_create(ar, vdev_id, peer_addr);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid);
+}
+
+static inline int
+ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
+		       const u8 peer_addr[ETH_ALEN])
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_peer_delete)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_peer_delete(ar, vdev_id, peer_addr);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid);
+}
+
+static inline int
+ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
+		      const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_peer_flush)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_peer_flush(ar, vdev_id, peer_addr, tid_bitmap);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid);
+}
+
+static inline int
+ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, const u8 *peer_addr,
+			  enum wmi_peer_param param_id, u32 param_value)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_peer_set_param)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_peer_set_param(ar, vdev_id, peer_addr, param_id,
+					      param_value);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid);
+}
+
+static inline int
+ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
+		      enum wmi_sta_ps_mode psmode)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_set_psmode)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_set_psmode(ar, vdev_id, psmode);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->sta_powersave_mode_cmdid);
+}
+
+static inline int
+ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
+			    enum wmi_sta_powersave_param param_id, u32 value)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_set_sta_ps)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_set_sta_ps(ar, vdev_id, param_id, value);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->sta_powersave_param_cmdid);
+}
+
+static inline int
+ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+			   enum wmi_ap_ps_peer_param param_id, u32 value)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_set_ap_ps)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_set_ap_ps(ar, vdev_id, mac, param_id, value);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->ap_ps_peer_param_cmdid);
+}
+
+static inline int
+ath10k_wmi_scan_chan_list(struct ath10k *ar,
+			  const struct wmi_scan_chan_list_arg *arg)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_scan_chan_list)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_scan_chan_list(ar, arg);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid);
+}
+
+static inline int
+ath10k_wmi_peer_assoc(struct ath10k *ar,
+		      const struct wmi_peer_assoc_complete_arg *arg)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_peer_assoc)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_peer_assoc(ar, arg);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid);
+}
+
+static inline int
+ath10k_wmi_beacon_send_ref_nowait(struct ath10k *ar, u32 vdev_id,
+				  const void *bcn, size_t bcn_len,
+				  u32 bcn_paddr, bool dtim_zero,
+				  bool deliver_cab)
+{
+	struct sk_buff *skb;
+	int ret;
+
+	if (!ar->wmi.ops->gen_beacon_dma)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_beacon_dma(ar, vdev_id, bcn, bcn_len, bcn_paddr,
+					  dtim_zero, deliver_cab);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	ret = ath10k_wmi_cmd_send_nowait(ar, skb,
+					 ar->wmi.cmd->pdev_send_bcn_cmdid);
+	if (ret) {
+		dev_kfree_skb(skb);
+		return ret;
+	}
+
+	return 0;
+}
+
+static inline int
+ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
+			       const struct wmi_wmm_params_all_arg *arg)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_pdev_set_wmm)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_pdev_set_wmm(ar, arg);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->pdev_set_wmm_params_cmdid);
+}
+
+static inline int
+ath10k_wmi_request_stats(struct ath10k *ar, u32 stats_mask)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_request_stats)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_request_stats(ar, stats_mask);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid);
+}
+
+static inline int
+ath10k_wmi_force_fw_hang(struct ath10k *ar,
+			 enum wmi_force_fw_hang_type type, u32 delay_ms)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_force_fw_hang)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_force_fw_hang(ar, type, delay_ms);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid);
+}
+
+static inline int
+ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable, u32 log_level)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_dbglog_cfg)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_dbglog_cfg(ar, module_enable, log_level);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid);
+}
+
+static inline int
+ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 filter)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_pktlog_enable)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_pktlog_enable(ar, filter);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_pktlog_enable_cmdid);
+}
+
+static inline int
+ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_pktlog_disable)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_pktlog_disable(ar);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->pdev_pktlog_disable_cmdid);
+}
+
+static inline int
+ath10k_wmi_pdev_set_quiet_mode(struct ath10k *ar, u32 period, u32 duration,
+			       u32 next_offset, u32 enabled)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_pdev_set_quiet_mode)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_pdev_set_quiet_mode(ar, period, duration,
+						   next_offset, enabled);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->pdev_set_quiet_mode_cmdid);
+}
+
+static inline int
+ath10k_wmi_pdev_get_temperature(struct ath10k *ar)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_pdev_get_temperature)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_pdev_get_temperature(ar);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->pdev_get_temperature_cmdid);
+}
+
+static inline int
+ath10k_wmi_addba_clear_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_addba_clear_resp)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_addba_clear_resp(ar, vdev_id, mac);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->addba_clear_resp_cmdid);
+}
+
+static inline int
+ath10k_wmi_addba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+		      u32 tid, u32 buf_size)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_addba_send)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_addba_send(ar, vdev_id, mac, tid, buf_size);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->addba_send_cmdid);
+}
+
+static inline int
+ath10k_wmi_addba_set_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+			  u32 tid, u32 status)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_addba_set_resp)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_addba_set_resp(ar, vdev_id, mac, tid, status);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->addba_set_resp_cmdid);
+}
+
+static inline int
+ath10k_wmi_delba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+		      u32 tid, u32 initiator, u32 reason)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_delba_send)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_delba_send(ar, vdev_id, mac, tid, initiator,
+					  reason);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb,
+				   ar->wmi.cmd->delba_send_cmdid);
+}
+
+static inline int
+ath10k_wmi_bcn_tmpl(struct ath10k *ar, u32 vdev_id, u32 tim_ie_offset,
+		    struct sk_buff *bcn, u32 prb_caps, u32 prb_erp,
+		    void *prb_ies, size_t prb_ies_len)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_bcn_tmpl)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_bcn_tmpl(ar, vdev_id, tim_ie_offset, bcn,
+					prb_caps, prb_erp, prb_ies,
+					prb_ies_len);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->bcn_tmpl_cmdid);
+}
+
+static inline int
+ath10k_wmi_prb_tmpl(struct ath10k *ar, u32 vdev_id, struct sk_buff *prb)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_prb_tmpl)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_prb_tmpl(ar, vdev_id, prb);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->prb_tmpl_cmdid);
+}
+
+static inline int
+ath10k_wmi_p2p_go_bcn_ie(struct ath10k *ar, u32 vdev_id, const u8 *p2p_ie)
+{
+	struct sk_buff *skb;
+
+	if (!ar->wmi.ops->gen_p2p_go_bcn_ie)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_p2p_go_bcn_ie(ar, vdev_id, p2p_ie);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->p2p_go_set_beacon_ie);
+}
+
+static inline int
+ath10k_wmi_sta_keepalive(struct ath10k *ar,
+			 const struct wmi_sta_keepalive_arg *arg)
+{
+	struct sk_buff *skb;
+	u32 cmd_id;
+
+	if (!ar->wmi.ops->gen_sta_keepalive)
+		return -EOPNOTSUPP;
+
+	skb = ar->wmi.ops->gen_sta_keepalive(ar, arg);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	cmd_id = ar->wmi.cmd->sta_keepalive_cmd;
+	return ath10k_wmi_cmd_send(ar, skb, cmd_id);
+}
+
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
new file mode 100644
index 0000000..ee0c5f6
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -0,0 +1,2796 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2014 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "core.h"
+#include "debug.h"
+#include "hw.h"
+#include "wmi.h"
+#include "wmi-ops.h"
+#include "wmi-tlv.h"
+
+/***************/
+/* TLV helpers */
+/**************/
+
+struct wmi_tlv_policy {
+	size_t min_len;
+};
+
+static const struct wmi_tlv_policy wmi_tlv_policies[] = {
+	[WMI_TLV_TAG_ARRAY_BYTE]
+		= { .min_len = sizeof(u8) },
+	[WMI_TLV_TAG_ARRAY_UINT32]
+		= { .min_len = sizeof(u32) },
+	[WMI_TLV_TAG_STRUCT_SCAN_EVENT]
+		= { .min_len = sizeof(struct wmi_scan_event) },
+	[WMI_TLV_TAG_STRUCT_MGMT_RX_HDR]
+		= { .min_len = sizeof(struct wmi_tlv_mgmt_rx_ev) },
+	[WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT]
+		= { .min_len = sizeof(struct wmi_chan_info_event) },
+	[WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT]
+		= { .min_len = sizeof(struct wmi_vdev_start_response_event) },
+	[WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT]
+		= { .min_len = sizeof(struct wmi_peer_sta_kickout_event) },
+	[WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT]
+		= { .min_len = sizeof(struct wmi_host_swba_event) },
+	[WMI_TLV_TAG_STRUCT_TIM_INFO]
+		= { .min_len = sizeof(struct wmi_tim_info) },
+	[WMI_TLV_TAG_STRUCT_P2P_NOA_INFO]
+		= { .min_len = sizeof(struct wmi_p2p_noa_info) },
+	[WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT]
+		= { .min_len = sizeof(struct wmi_tlv_svc_rdy_ev) },
+	[WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES]
+		= { .min_len = sizeof(struct hal_reg_capabilities) },
+	[WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ]
+		= { .min_len = sizeof(struct wlan_host_mem_req) },
+	[WMI_TLV_TAG_STRUCT_READY_EVENT]
+		= { .min_len = sizeof(struct wmi_tlv_rdy_ev) },
+	[WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT]
+		= { .min_len = sizeof(struct wmi_tlv_bcn_tx_status_ev) },
+	[WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT]
+		= { .min_len = sizeof(struct wmi_tlv_diag_data_ev) },
+};
+
+static int
+ath10k_wmi_tlv_iter(struct ath10k *ar, const void *ptr, size_t len,
+		    int (*iter)(struct ath10k *ar, u16 tag, u16 len,
+				const void *ptr, void *data),
+		    void *data)
+{
+	const void *begin = ptr;
+	const struct wmi_tlv *tlv;
+	u16 tlv_tag, tlv_len;
+	int ret;
+
+	while (len > 0) {
+		if (len < sizeof(*tlv)) {
+			ath10k_dbg(ar, ATH10K_DBG_WMI,
+				   "wmi tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n",
+				   ptr - begin, len, sizeof(*tlv));
+			return -EINVAL;
+		}
+
+		tlv = ptr;
+		tlv_tag = __le16_to_cpu(tlv->tag);
+		tlv_len = __le16_to_cpu(tlv->len);
+		ptr += sizeof(*tlv);
+		len -= sizeof(*tlv);
+
+		if (tlv_len > len) {
+			ath10k_dbg(ar, ATH10K_DBG_WMI,
+				   "wmi tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n",
+				   tlv_tag, ptr - begin, len, tlv_len);
+			return -EINVAL;
+		}
+
+		if (tlv_tag < ARRAY_SIZE(wmi_tlv_policies) &&
+		    wmi_tlv_policies[tlv_tag].min_len &&
+		    wmi_tlv_policies[tlv_tag].min_len > tlv_len) {
+			ath10k_dbg(ar, ATH10K_DBG_WMI,
+				   "wmi tlv parse failure of tag %hhu at byte %zd (%hhu bytes is less than min length %zu)\n",
+				   tlv_tag, ptr - begin, tlv_len,
+				   wmi_tlv_policies[tlv_tag].min_len);
+			return -EINVAL;
+		}
+
+		ret = iter(ar, tlv_tag, tlv_len, ptr, data);
+		if (ret)
+			return ret;
+
+		ptr += tlv_len;
+		len -= tlv_len;
+	}
+
+	return 0;
+}
+
+static int ath10k_wmi_tlv_iter_parse(struct ath10k *ar, u16 tag, u16 len,
+				     const void *ptr, void *data)
+{
+	const void **tb = data;
+
+	if (tag < WMI_TLV_TAG_MAX)
+		tb[tag] = ptr;
+
+	return 0;
+}
+
+static int ath10k_wmi_tlv_parse(struct ath10k *ar, const void **tb,
+				const void *ptr, size_t len)
+{
+	return ath10k_wmi_tlv_iter(ar, ptr, len, ath10k_wmi_tlv_iter_parse,
+				   (void *)tb);
+}
+
+static const void **
+ath10k_wmi_tlv_parse_alloc(struct ath10k *ar, const void *ptr,
+			   size_t len, gfp_t gfp)
+{
+	const void **tb;
+	int ret;
+
+	tb = kzalloc(sizeof(*tb) * WMI_TLV_TAG_MAX, gfp);
+	if (!tb)
+		return ERR_PTR(-ENOMEM);
+
+	ret = ath10k_wmi_tlv_parse(ar, tb, ptr, len);
+	if (ret) {
+		kfree(tb);
+		return ERR_PTR(ret);
+	}
+
+	return tb;
+}
+
+static u16 ath10k_wmi_tlv_len(const void *ptr)
+{
+	return __le16_to_cpu((((const struct wmi_tlv *)ptr) - 1)->len);
+}
+
+/**************/
+/* TLV events */
+/**************/
+static int ath10k_wmi_tlv_event_bcn_tx_status(struct ath10k *ar,
+					      struct sk_buff *skb)
+{
+	const void **tb;
+	const struct wmi_tlv_bcn_tx_status_ev *ev;
+	u32 vdev_id, tx_status;
+	int ret;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT];
+	if (!ev) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	tx_status = __le32_to_cpu(ev->tx_status);
+	vdev_id = __le32_to_cpu(ev->vdev_id);
+
+	switch (tx_status) {
+	case WMI_TLV_BCN_TX_STATUS_OK:
+		break;
+	case WMI_TLV_BCN_TX_STATUS_XRETRY:
+	case WMI_TLV_BCN_TX_STATUS_DROP:
+	case WMI_TLV_BCN_TX_STATUS_FILTERED:
+		/* FIXME: It's probably worth telling mac80211 to stop the
+		 * interface as it is crippled.
+		 */
+		ath10k_warn(ar, "received bcn tmpl tx status on vdev %i: %d",
+			    vdev_id, tx_status);
+		break;
+	}
+
+	kfree(tb);
+	return 0;
+}
+
+static int ath10k_wmi_tlv_event_diag_data(struct ath10k *ar,
+					  struct sk_buff *skb)
+{
+	const void **tb;
+	const struct wmi_tlv_diag_data_ev *ev;
+	const struct wmi_tlv_diag_item *item;
+	const void *data;
+	int ret, num_items, len;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT];
+	data = tb[WMI_TLV_TAG_ARRAY_BYTE];
+	if (!ev || !data) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	num_items = __le32_to_cpu(ev->num_items);
+	len = ath10k_wmi_tlv_len(data);
+
+	while (num_items--) {
+		if (len == 0)
+			break;
+		if (len < sizeof(*item)) {
+			ath10k_warn(ar, "failed to parse diag data: can't fit item header\n");
+			break;
+		}
+
+		item = data;
+
+		if (len < sizeof(*item) + __le16_to_cpu(item->len)) {
+			ath10k_warn(ar, "failed to parse diag data: item is too long\n");
+			break;
+		}
+
+		trace_ath10k_wmi_diag_container(ar,
+						item->type,
+						__le32_to_cpu(item->timestamp),
+						__le32_to_cpu(item->code),
+						__le16_to_cpu(item->len),
+						item->payload);
+
+		len -= sizeof(*item);
+		len -= roundup(__le16_to_cpu(item->len), 4);
+
+		data += sizeof(*item);
+		data += roundup(__le16_to_cpu(item->len), 4);
+	}
+
+	if (num_items != -1 || len != 0)
+		ath10k_warn(ar, "failed to parse diag data event: num_items %d len %d\n",
+			    num_items, len);
+
+	kfree(tb);
+	return 0;
+}
+
+static int ath10k_wmi_tlv_event_diag(struct ath10k *ar,
+				     struct sk_buff *skb)
+{
+	const void **tb;
+	const void *data;
+	int ret, len;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	data = tb[WMI_TLV_TAG_ARRAY_BYTE];
+	if (!data) {
+		kfree(tb);
+		return -EPROTO;
+	}
+	len = ath10k_wmi_tlv_len(data);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv diag event len %d\n", len);
+	trace_ath10k_wmi_diag(ar, data, len);
+
+	kfree(tb);
+	return 0;
+}
+
+/***********/
+/* TLV ops */
+/***********/
+
+static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct wmi_cmd_hdr *cmd_hdr;
+	enum wmi_tlv_event_id id;
+
+	cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+	id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+
+	if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
+		return;
+
+	trace_ath10k_wmi_event(ar, id, skb->data, skb->len);
+
+	switch (id) {
+	case WMI_TLV_MGMT_RX_EVENTID:
+		ath10k_wmi_event_mgmt_rx(ar, skb);
+		/* mgmt_rx() owns the skb now! */
+		return;
+	case WMI_TLV_SCAN_EVENTID:
+		ath10k_wmi_event_scan(ar, skb);
+		break;
+	case WMI_TLV_CHAN_INFO_EVENTID:
+		ath10k_wmi_event_chan_info(ar, skb);
+		break;
+	case WMI_TLV_ECHO_EVENTID:
+		ath10k_wmi_event_echo(ar, skb);
+		break;
+	case WMI_TLV_DEBUG_MESG_EVENTID:
+		ath10k_wmi_event_debug_mesg(ar, skb);
+		break;
+	case WMI_TLV_UPDATE_STATS_EVENTID:
+		ath10k_wmi_event_update_stats(ar, skb);
+		break;
+	case WMI_TLV_VDEV_START_RESP_EVENTID:
+		ath10k_wmi_event_vdev_start_resp(ar, skb);
+		break;
+	case WMI_TLV_VDEV_STOPPED_EVENTID:
+		ath10k_wmi_event_vdev_stopped(ar, skb);
+		break;
+	case WMI_TLV_PEER_STA_KICKOUT_EVENTID:
+		ath10k_wmi_event_peer_sta_kickout(ar, skb);
+		break;
+	case WMI_TLV_HOST_SWBA_EVENTID:
+		ath10k_wmi_event_host_swba(ar, skb);
+		break;
+	case WMI_TLV_TBTTOFFSET_UPDATE_EVENTID:
+		ath10k_wmi_event_tbttoffset_update(ar, skb);
+		break;
+	case WMI_TLV_PHYERR_EVENTID:
+		ath10k_wmi_event_phyerr(ar, skb);
+		break;
+	case WMI_TLV_ROAM_EVENTID:
+		ath10k_wmi_event_roam(ar, skb);
+		break;
+	case WMI_TLV_PROFILE_MATCH:
+		ath10k_wmi_event_profile_match(ar, skb);
+		break;
+	case WMI_TLV_DEBUG_PRINT_EVENTID:
+		ath10k_wmi_event_debug_print(ar, skb);
+		break;
+	case WMI_TLV_PDEV_QVIT_EVENTID:
+		ath10k_wmi_event_pdev_qvit(ar, skb);
+		break;
+	case WMI_TLV_WLAN_PROFILE_DATA_EVENTID:
+		ath10k_wmi_event_wlan_profile_data(ar, skb);
+		break;
+	case WMI_TLV_RTT_MEASUREMENT_REPORT_EVENTID:
+		ath10k_wmi_event_rtt_measurement_report(ar, skb);
+		break;
+	case WMI_TLV_TSF_MEASUREMENT_REPORT_EVENTID:
+		ath10k_wmi_event_tsf_measurement_report(ar, skb);
+		break;
+	case WMI_TLV_RTT_ERROR_REPORT_EVENTID:
+		ath10k_wmi_event_rtt_error_report(ar, skb);
+		break;
+	case WMI_TLV_WOW_WAKEUP_HOST_EVENTID:
+		ath10k_wmi_event_wow_wakeup_host(ar, skb);
+		break;
+	case WMI_TLV_DCS_INTERFERENCE_EVENTID:
+		ath10k_wmi_event_dcs_interference(ar, skb);
+		break;
+	case WMI_TLV_PDEV_TPC_CONFIG_EVENTID:
+		ath10k_wmi_event_pdev_tpc_config(ar, skb);
+		break;
+	case WMI_TLV_PDEV_FTM_INTG_EVENTID:
+		ath10k_wmi_event_pdev_ftm_intg(ar, skb);
+		break;
+	case WMI_TLV_GTK_OFFLOAD_STATUS_EVENTID:
+		ath10k_wmi_event_gtk_offload_status(ar, skb);
+		break;
+	case WMI_TLV_GTK_REKEY_FAIL_EVENTID:
+		ath10k_wmi_event_gtk_rekey_fail(ar, skb);
+		break;
+	case WMI_TLV_TX_DELBA_COMPLETE_EVENTID:
+		ath10k_wmi_event_delba_complete(ar, skb);
+		break;
+	case WMI_TLV_TX_ADDBA_COMPLETE_EVENTID:
+		ath10k_wmi_event_addba_complete(ar, skb);
+		break;
+	case WMI_TLV_VDEV_INSTALL_KEY_COMPLETE_EVENTID:
+		ath10k_wmi_event_vdev_install_key_complete(ar, skb);
+		break;
+	case WMI_TLV_SERVICE_READY_EVENTID:
+		ath10k_wmi_event_service_ready(ar, skb);
+		break;
+	case WMI_TLV_READY_EVENTID:
+		ath10k_wmi_event_ready(ar, skb);
+		break;
+	case WMI_TLV_OFFLOAD_BCN_TX_STATUS_EVENTID:
+		ath10k_wmi_tlv_event_bcn_tx_status(ar, skb);
+		break;
+	case WMI_TLV_DIAG_DATA_CONTAINER_EVENTID:
+		ath10k_wmi_tlv_event_diag_data(ar, skb);
+		break;
+	case WMI_TLV_DIAG_EVENTID:
+		ath10k_wmi_tlv_event_diag(ar, skb);
+		break;
+	default:
+		ath10k_warn(ar, "Unknown eventid: %d\n", id);
+		break;
+	}
+
+	dev_kfree_skb(skb);
+}
+
+static int ath10k_wmi_tlv_op_pull_scan_ev(struct ath10k *ar,
+					  struct sk_buff *skb,
+					  struct wmi_scan_ev_arg *arg)
+{
+	const void **tb;
+	const struct wmi_scan_event *ev;
+	int ret;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TLV_TAG_STRUCT_SCAN_EVENT];
+	if (!ev) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	arg->event_type = ev->event_type;
+	arg->reason = ev->reason;
+	arg->channel_freq = ev->channel_freq;
+	arg->scan_req_id = ev->scan_req_id;
+	arg->scan_id = ev->scan_id;
+	arg->vdev_id = ev->vdev_id;
+
+	kfree(tb);
+	return 0;
+}
+
+static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar,
+					     struct sk_buff *skb,
+					     struct wmi_mgmt_rx_ev_arg *arg)
+{
+	const void **tb;
+	const struct wmi_tlv_mgmt_rx_ev *ev;
+	const u8 *frame;
+	u32 msdu_len;
+	int ret;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TLV_TAG_STRUCT_MGMT_RX_HDR];
+	frame = tb[WMI_TLV_TAG_ARRAY_BYTE];
+
+	if (!ev || !frame) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	arg->channel = ev->channel;
+	arg->buf_len = ev->buf_len;
+	arg->status = ev->status;
+	arg->snr = ev->snr;
+	arg->phy_mode = ev->phy_mode;
+	arg->rate = ev->rate;
+
+	msdu_len = __le32_to_cpu(arg->buf_len);
+
+	if (skb->len < (frame - skb->data) + msdu_len) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	/* shift the sk_buff to point to `frame` */
+	skb_trim(skb, 0);
+	skb_put(skb, frame - skb->data);
+	skb_pull(skb, frame - skb->data);
+	skb_put(skb, msdu_len);
+
+	kfree(tb);
+	return 0;
+}
+
+static int ath10k_wmi_tlv_op_pull_ch_info_ev(struct ath10k *ar,
+					     struct sk_buff *skb,
+					     struct wmi_ch_info_ev_arg *arg)
+{
+	const void **tb;
+	const struct wmi_chan_info_event *ev;
+	int ret;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT];
+	if (!ev) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	arg->err_code = ev->err_code;
+	arg->freq = ev->freq;
+	arg->cmd_flags = ev->cmd_flags;
+	arg->noise_floor = ev->noise_floor;
+	arg->rx_clear_count = ev->rx_clear_count;
+	arg->cycle_count = ev->cycle_count;
+
+	kfree(tb);
+	return 0;
+}
+
+static int
+ath10k_wmi_tlv_op_pull_vdev_start_ev(struct ath10k *ar, struct sk_buff *skb,
+				     struct wmi_vdev_start_ev_arg *arg)
+{
+	const void **tb;
+	const struct wmi_vdev_start_response_event *ev;
+	int ret;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT];
+	if (!ev) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	skb_pull(skb, sizeof(*ev));
+	arg->vdev_id = ev->vdev_id;
+	arg->req_id = ev->req_id;
+	arg->resp_type = ev->resp_type;
+	arg->status = ev->status;
+
+	kfree(tb);
+	return 0;
+}
+
+static int ath10k_wmi_tlv_op_pull_peer_kick_ev(struct ath10k *ar,
+					       struct sk_buff *skb,
+					       struct wmi_peer_kick_ev_arg *arg)
+{
+	const void **tb;
+	const struct wmi_peer_sta_kickout_event *ev;
+	int ret;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT];
+	if (!ev) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	arg->mac_addr = ev->peer_macaddr.addr;
+
+	kfree(tb);
+	return 0;
+}
+
+struct wmi_tlv_swba_parse {
+	const struct wmi_host_swba_event *ev;
+	bool tim_done;
+	bool noa_done;
+	size_t n_tim;
+	size_t n_noa;
+	struct wmi_swba_ev_arg *arg;
+};
+
+static int ath10k_wmi_tlv_swba_tim_parse(struct ath10k *ar, u16 tag, u16 len,
+					 const void *ptr, void *data)
+{
+	struct wmi_tlv_swba_parse *swba = data;
+
+	if (tag != WMI_TLV_TAG_STRUCT_TIM_INFO)
+		return -EPROTO;
+
+	if (swba->n_tim >= ARRAY_SIZE(swba->arg->tim_info))
+		return -ENOBUFS;
+
+	swba->arg->tim_info[swba->n_tim++] = ptr;
+	return 0;
+}
+
+static int ath10k_wmi_tlv_swba_noa_parse(struct ath10k *ar, u16 tag, u16 len,
+					 const void *ptr, void *data)
+{
+	struct wmi_tlv_swba_parse *swba = data;
+
+	if (tag != WMI_TLV_TAG_STRUCT_P2P_NOA_INFO)
+		return -EPROTO;
+
+	if (swba->n_noa >= ARRAY_SIZE(swba->arg->noa_info))
+		return -ENOBUFS;
+
+	swba->arg->noa_info[swba->n_noa++] = ptr;
+	return 0;
+}
+
+static int ath10k_wmi_tlv_swba_parse(struct ath10k *ar, u16 tag, u16 len,
+				     const void *ptr, void *data)
+{
+	struct wmi_tlv_swba_parse *swba = data;
+	int ret;
+
+	switch (tag) {
+	case WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT:
+		swba->ev = ptr;
+		break;
+	case WMI_TLV_TAG_ARRAY_STRUCT:
+		if (!swba->tim_done) {
+			swba->tim_done = true;
+			ret = ath10k_wmi_tlv_iter(ar, ptr, len,
+						  ath10k_wmi_tlv_swba_tim_parse,
+						  swba);
+			if (ret)
+				return ret;
+		} else if (!swba->noa_done) {
+			swba->noa_done = true;
+			ret = ath10k_wmi_tlv_iter(ar, ptr, len,
+						  ath10k_wmi_tlv_swba_noa_parse,
+						  swba);
+			if (ret)
+				return ret;
+		}
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int ath10k_wmi_tlv_op_pull_swba_ev(struct ath10k *ar,
+					  struct sk_buff *skb,
+					  struct wmi_swba_ev_arg *arg)
+{
+	struct wmi_tlv_swba_parse swba = { .arg = arg };
+	u32 map;
+	size_t n_vdevs;
+	int ret;
+
+	ret = ath10k_wmi_tlv_iter(ar, skb->data, skb->len,
+				  ath10k_wmi_tlv_swba_parse, &swba);
+	if (ret) {
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	if (!swba.ev)
+		return -EPROTO;
+
+	arg->vdev_map = swba.ev->vdev_map;
+
+	for (map = __le32_to_cpu(arg->vdev_map), n_vdevs = 0; map; map >>= 1)
+		if (map & BIT(0))
+			n_vdevs++;
+
+	if (n_vdevs != swba.n_tim ||
+	    n_vdevs != swba.n_noa)
+		return -EPROTO;
+
+	return 0;
+}
+
+static int ath10k_wmi_tlv_op_pull_phyerr_ev(struct ath10k *ar,
+					    struct sk_buff *skb,
+					    struct wmi_phyerr_ev_arg *arg)
+{
+	const void **tb;
+	const struct wmi_tlv_phyerr_ev *ev;
+	const void *phyerrs;
+	int ret;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TLV_TAG_STRUCT_COMB_PHYERR_RX_HDR];
+	phyerrs = tb[WMI_TLV_TAG_ARRAY_BYTE];
+
+	if (!ev || !phyerrs) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	arg->num_phyerrs  = ev->num_phyerrs;
+	arg->tsf_l32 = ev->tsf_l32;
+	arg->tsf_u32 = ev->tsf_u32;
+	arg->buf_len = ev->buf_len;
+	arg->phyerrs = phyerrs;
+
+	kfree(tb);
+	return 0;
+}
+
+#define WMI_TLV_ABI_VER_NS0 0x5F414351
+#define WMI_TLV_ABI_VER_NS1 0x00004C4D
+#define WMI_TLV_ABI_VER_NS2 0x00000000
+#define WMI_TLV_ABI_VER_NS3 0x00000000
+
+#define WMI_TLV_ABI_VER0_MAJOR 1
+#define WMI_TLV_ABI_VER0_MINOR 0
+#define WMI_TLV_ABI_VER0 ((((WMI_TLV_ABI_VER0_MAJOR) << 24) & 0xFF000000) | \
+			  (((WMI_TLV_ABI_VER0_MINOR) <<  0) & 0x00FFFFFF))
+#define WMI_TLV_ABI_VER1 53
+
+static int
+ath10k_wmi_tlv_parse_mem_reqs(struct ath10k *ar, u16 tag, u16 len,
+			      const void *ptr, void *data)
+{
+	struct wmi_svc_rdy_ev_arg *arg = data;
+	int i;
+
+	if (tag != WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ)
+		return -EPROTO;
+
+	for (i = 0; i < ARRAY_SIZE(arg->mem_reqs); i++) {
+		if (!arg->mem_reqs[i]) {
+			arg->mem_reqs[i] = ptr;
+			return 0;
+		}
+	}
+
+	return -ENOMEM;
+}
+
+static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar,
+					     struct sk_buff *skb,
+					     struct wmi_svc_rdy_ev_arg *arg)
+{
+	const void **tb;
+	const struct hal_reg_capabilities *reg;
+	const struct wmi_tlv_svc_rdy_ev *ev;
+	const __le32 *svc_bmap;
+	const struct wlan_host_mem_req *mem_reqs;
+	int ret;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT];
+	reg = tb[WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES];
+	svc_bmap = tb[WMI_TLV_TAG_ARRAY_UINT32];
+	mem_reqs = tb[WMI_TLV_TAG_ARRAY_STRUCT];
+
+	if (!ev || !reg || !svc_bmap || !mem_reqs) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	/* This is an internal ABI compatibility check for WMI TLV so check it
+	 * here instead of the generic WMI code.
+	 */
+	ath10k_dbg(ar, ATH10K_DBG_WMI,
+		   "wmi tlv abi 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x\n",
+		   __le32_to_cpu(ev->abi.abi_ver0), WMI_TLV_ABI_VER0,
+		   __le32_to_cpu(ev->abi.abi_ver_ns0), WMI_TLV_ABI_VER_NS0,
+		   __le32_to_cpu(ev->abi.abi_ver_ns1), WMI_TLV_ABI_VER_NS1,
+		   __le32_to_cpu(ev->abi.abi_ver_ns2), WMI_TLV_ABI_VER_NS2,
+		   __le32_to_cpu(ev->abi.abi_ver_ns3), WMI_TLV_ABI_VER_NS3);
+
+	if (__le32_to_cpu(ev->abi.abi_ver0) != WMI_TLV_ABI_VER0 ||
+	    __le32_to_cpu(ev->abi.abi_ver_ns0) != WMI_TLV_ABI_VER_NS0 ||
+	    __le32_to_cpu(ev->abi.abi_ver_ns1) != WMI_TLV_ABI_VER_NS1 ||
+	    __le32_to_cpu(ev->abi.abi_ver_ns2) != WMI_TLV_ABI_VER_NS2 ||
+	    __le32_to_cpu(ev->abi.abi_ver_ns3) != WMI_TLV_ABI_VER_NS3) {
+		kfree(tb);
+		return -ENOTSUPP;
+	}
+
+	arg->min_tx_power = ev->hw_min_tx_power;
+	arg->max_tx_power = ev->hw_max_tx_power;
+	arg->ht_cap = ev->ht_cap_info;
+	arg->vht_cap = ev->vht_cap_info;
+	arg->sw_ver0 = ev->abi.abi_ver0;
+	arg->sw_ver1 = ev->abi.abi_ver1;
+	arg->fw_build = ev->fw_build_vers;
+	arg->phy_capab = ev->phy_capability;
+	arg->num_rf_chains = ev->num_rf_chains;
+	arg->eeprom_rd = reg->eeprom_rd;
+	arg->num_mem_reqs = ev->num_mem_reqs;
+	arg->service_map = svc_bmap;
+	arg->service_map_len = ath10k_wmi_tlv_len(svc_bmap);
+
+	ret = ath10k_wmi_tlv_iter(ar, mem_reqs, ath10k_wmi_tlv_len(mem_reqs),
+				  ath10k_wmi_tlv_parse_mem_reqs, arg);
+	if (ret) {
+		kfree(tb);
+		ath10k_warn(ar, "failed to parse mem_reqs tlv: %d\n", ret);
+		return ret;
+	}
+
+	kfree(tb);
+	return 0;
+}
+
+static int ath10k_wmi_tlv_op_pull_rdy_ev(struct ath10k *ar,
+					 struct sk_buff *skb,
+					 struct wmi_rdy_ev_arg *arg)
+{
+	const void **tb;
+	const struct wmi_tlv_rdy_ev *ev;
+	int ret;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TLV_TAG_STRUCT_READY_EVENT];
+	if (!ev) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	arg->sw_version = ev->abi.abi_ver0;
+	arg->abi_version = ev->abi.abi_ver1;
+	arg->status = ev->status;
+	arg->mac_addr = ev->mac_addr.addr;
+
+	kfree(tb);
+	return 0;
+}
+
+static void ath10k_wmi_tlv_pull_vdev_stats(const struct wmi_tlv_vdev_stats *src,
+					   struct ath10k_fw_stats_vdev *dst)
+{
+	int i;
+
+	dst->vdev_id = __le32_to_cpu(src->vdev_id);
+	dst->beacon_snr = __le32_to_cpu(src->beacon_snr);
+	dst->data_snr = __le32_to_cpu(src->data_snr);
+	dst->num_rx_frames = __le32_to_cpu(src->num_rx_frames);
+	dst->num_rts_fail = __le32_to_cpu(src->num_rts_fail);
+	dst->num_rts_success = __le32_to_cpu(src->num_rts_success);
+	dst->num_rx_err = __le32_to_cpu(src->num_rx_err);
+	dst->num_rx_discard = __le32_to_cpu(src->num_rx_discard);
+	dst->num_tx_not_acked = __le32_to_cpu(src->num_tx_not_acked);
+
+	for (i = 0; i < ARRAY_SIZE(src->num_tx_frames); i++)
+		dst->num_tx_frames[i] =
+			__le32_to_cpu(src->num_tx_frames[i]);
+
+	for (i = 0; i < ARRAY_SIZE(src->num_tx_frames_retries); i++)
+		dst->num_tx_frames_retries[i] =
+			__le32_to_cpu(src->num_tx_frames_retries[i]);
+
+	for (i = 0; i < ARRAY_SIZE(src->num_tx_frames_failures); i++)
+		dst->num_tx_frames_failures[i] =
+			__le32_to_cpu(src->num_tx_frames_failures[i]);
+
+	for (i = 0; i < ARRAY_SIZE(src->tx_rate_history); i++)
+		dst->tx_rate_history[i] =
+			__le32_to_cpu(src->tx_rate_history[i]);
+
+	for (i = 0; i < ARRAY_SIZE(src->beacon_rssi_history); i++)
+		dst->beacon_rssi_history[i] =
+			__le32_to_cpu(src->beacon_rssi_history[i]);
+}
+
+static int ath10k_wmi_tlv_op_pull_fw_stats(struct ath10k *ar,
+					   struct sk_buff *skb,
+					   struct ath10k_fw_stats *stats)
+{
+	const void **tb;
+	const struct wmi_tlv_stats_ev *ev;
+	const void *data;
+	u32 num_pdev_stats;
+	u32 num_vdev_stats;
+	u32 num_peer_stats;
+	u32 num_bcnflt_stats;
+	u32 num_chan_stats;
+	size_t data_len;
+	int ret;
+	int i;
+
+	tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ret = PTR_ERR(tb);
+		ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+		return ret;
+	}
+
+	ev = tb[WMI_TLV_TAG_STRUCT_STATS_EVENT];
+	data = tb[WMI_TLV_TAG_ARRAY_BYTE];
+
+	if (!ev || !data) {
+		kfree(tb);
+		return -EPROTO;
+	}
+
+	data_len = ath10k_wmi_tlv_len(data);
+	num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
+	num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
+	num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
+	num_bcnflt_stats = __le32_to_cpu(ev->num_bcnflt_stats);
+	num_chan_stats = __le32_to_cpu(ev->num_chan_stats);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI,
+		   "wmi tlv stats update pdev %i vdev %i peer %i bcnflt %i chan %i\n",
+		   num_pdev_stats, num_vdev_stats, num_peer_stats,
+		   num_bcnflt_stats, num_chan_stats);
+
+	for (i = 0; i < num_pdev_stats; i++) {
+		const struct wmi_pdev_stats *src;
+		struct ath10k_fw_stats_pdev *dst;
+
+		src = data;
+		if (data_len < sizeof(*src))
+			return -EPROTO;
+
+		data += sizeof(*src);
+		data_len -= sizeof(*src);
+
+		dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+		if (!dst)
+			continue;
+
+		ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
+		ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
+		ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
+		list_add_tail(&dst->list, &stats->pdevs);
+	}
+
+	for (i = 0; i < num_vdev_stats; i++) {
+		const struct wmi_tlv_vdev_stats *src;
+		struct ath10k_fw_stats_vdev *dst;
+
+		src = data;
+		if (data_len < sizeof(*src))
+			return -EPROTO;
+
+		data += sizeof(*src);
+		data_len -= sizeof(*src);
+
+		dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+		if (!dst)
+			continue;
+
+		ath10k_wmi_tlv_pull_vdev_stats(src, dst);
+		list_add_tail(&dst->list, &stats->vdevs);
+	}
+
+	for (i = 0; i < num_peer_stats; i++) {
+		const struct wmi_10x_peer_stats *src;
+		struct ath10k_fw_stats_peer *dst;
+
+		src = data;
+		if (data_len < sizeof(*src))
+			return -EPROTO;
+
+		data += sizeof(*src);
+		data_len -= sizeof(*src);
+
+		dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+		if (!dst)
+			continue;
+
+		ath10k_wmi_pull_peer_stats(&src->old, dst);
+		dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
+		list_add_tail(&dst->list, &stats->peers);
+	}
+
+	kfree(tb);
+	return 0;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_pdev_suspend(struct ath10k *ar, u32 opt)
+{
+	struct wmi_tlv_pdev_suspend *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SUSPEND_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->opt = __cpu_to_le32(opt);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev suspend\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_pdev_resume(struct ath10k *ar)
+{
+	struct wmi_tlv_resume_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_RESUME_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->reserved = __cpu_to_le32(0);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev resume\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_pdev_set_rd(struct ath10k *ar,
+				  u16 rd, u16 rd2g, u16 rd5g,
+				  u16 ctl2g, u16 ctl5g,
+				  enum wmi_dfs_region dfs_reg)
+{
+	struct wmi_tlv_pdev_set_rd_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_REGDOMAIN_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->regd = __cpu_to_le32(rd);
+	cmd->regd_2ghz = __cpu_to_le32(rd2g);
+	cmd->regd_5ghz = __cpu_to_le32(rd5g);
+	cmd->conform_limit_2ghz = __cpu_to_le32(rd2g);
+	cmd->conform_limit_5ghz = __cpu_to_le32(rd5g);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set rd\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_pdev_set_param(struct ath10k *ar, u32 param_id,
+				     u32 param_value)
+{
+	struct wmi_tlv_pdev_set_param_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_PARAM_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->param_id = __cpu_to_le32(param_id);
+	cmd->param_value = __cpu_to_le32(param_value);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set param\n");
+	return skb;
+}
+
+static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar)
+{
+	struct sk_buff *skb;
+	struct wmi_tlv *tlv;
+	struct wmi_tlv_init_cmd *cmd;
+	struct wmi_tlv_resource_config *cfg;
+	struct wmi_host_mem_chunks *chunks;
+	size_t len, chunks_len;
+	void *ptr;
+
+	chunks_len = ar->wmi.num_mem_chunks * sizeof(struct host_memory_chunk);
+	len = (sizeof(*tlv) + sizeof(*cmd)) +
+	      (sizeof(*tlv) + sizeof(*cfg)) +
+	      (sizeof(*tlv) + chunks_len);
+
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = skb->data;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_INIT_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_RESOURCE_CONFIG);
+	tlv->len = __cpu_to_le16(sizeof(*cfg));
+	cfg = (void *)tlv->value;
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cfg);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
+	tlv->len = __cpu_to_le16(chunks_len);
+	chunks = (void *)tlv->value;
+
+	ptr += sizeof(*tlv);
+	ptr += chunks_len;
+
+	cmd->abi.abi_ver0 = __cpu_to_le32(WMI_TLV_ABI_VER0);
+	cmd->abi.abi_ver1 = __cpu_to_le32(WMI_TLV_ABI_VER1);
+	cmd->abi.abi_ver_ns0 = __cpu_to_le32(WMI_TLV_ABI_VER_NS0);
+	cmd->abi.abi_ver_ns1 = __cpu_to_le32(WMI_TLV_ABI_VER_NS1);
+	cmd->abi.abi_ver_ns2 = __cpu_to_le32(WMI_TLV_ABI_VER_NS2);
+	cmd->abi.abi_ver_ns3 = __cpu_to_le32(WMI_TLV_ABI_VER_NS3);
+	cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
+
+	cfg->num_vdevs = __cpu_to_le32(TARGET_TLV_NUM_VDEVS);
+	cfg->num_peers = __cpu_to_le32(TARGET_TLV_NUM_PEERS);
+
+	if (test_bit(WMI_SERVICE_RX_FULL_REORDER, ar->wmi.svc_map)) {
+		cfg->num_offload_peers = __cpu_to_le32(3);
+		cfg->num_offload_reorder_bufs = __cpu_to_le32(3);
+	} else {
+		cfg->num_offload_peers = __cpu_to_le32(0);
+		cfg->num_offload_reorder_bufs = __cpu_to_le32(0);
+	}
+
+	cfg->num_peer_keys = __cpu_to_le32(2);
+	cfg->num_tids = __cpu_to_le32(TARGET_TLV_NUM_TIDS);
+	cfg->ast_skid_limit = __cpu_to_le32(0x10);
+	cfg->tx_chain_mask = __cpu_to_le32(0x7);
+	cfg->rx_chain_mask = __cpu_to_le32(0x7);
+	cfg->rx_timeout_pri[0] = __cpu_to_le32(0x64);
+	cfg->rx_timeout_pri[1] = __cpu_to_le32(0x64);
+	cfg->rx_timeout_pri[2] = __cpu_to_le32(0x64);
+	cfg->rx_timeout_pri[3] = __cpu_to_le32(0x28);
+	cfg->rx_decap_mode = __cpu_to_le32(1);
+	cfg->scan_max_pending_reqs = __cpu_to_le32(4);
+	cfg->bmiss_offload_max_vdev = __cpu_to_le32(3);
+	cfg->roam_offload_max_vdev = __cpu_to_le32(3);
+	cfg->roam_offload_max_ap_profiles = __cpu_to_le32(8);
+	cfg->num_mcast_groups = __cpu_to_le32(0);
+	cfg->num_mcast_table_elems = __cpu_to_le32(0);
+	cfg->mcast2ucast_mode = __cpu_to_le32(0);
+	cfg->tx_dbg_log_size = __cpu_to_le32(0x400);
+	cfg->num_wds_entries = __cpu_to_le32(0x20);
+	cfg->dma_burst_size = __cpu_to_le32(0);
+	cfg->mac_aggr_delim = __cpu_to_le32(0);
+	cfg->rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(0);
+	cfg->vow_config = __cpu_to_le32(0);
+	cfg->gtk_offload_max_vdev = __cpu_to_le32(2);
+	cfg->num_msdu_desc = __cpu_to_le32(TARGET_TLV_NUM_MSDU_DESC);
+	cfg->max_frag_entries = __cpu_to_le32(2);
+	cfg->num_tdls_vdevs = __cpu_to_le32(1);
+	cfg->num_tdls_conn_table_entries = __cpu_to_le32(0x20);
+	cfg->beacon_tx_offload_max_vdev = __cpu_to_le32(2);
+	cfg->num_multicast_filter_entries = __cpu_to_le32(5);
+	cfg->num_wow_filters = __cpu_to_le32(0x16);
+	cfg->num_keep_alive_pattern = __cpu_to_le32(6);
+	cfg->keep_alive_pattern_size = __cpu_to_le32(0);
+	cfg->max_tdls_concurrent_sleep_sta = __cpu_to_le32(1);
+	cfg->max_tdls_concurrent_buffer_sta = __cpu_to_le32(1);
+
+	ath10k_wmi_put_host_mem_chunks(ar, chunks);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv init\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_start_scan(struct ath10k *ar,
+				 const struct wmi_start_scan_arg *arg)
+{
+	struct wmi_tlv_start_scan_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	size_t len, chan_len, ssid_len, bssid_len, ie_len;
+	__le32 *chans;
+	struct wmi_ssid *ssids;
+	struct wmi_mac_addr *addrs;
+	void *ptr;
+	int i, ret;
+
+	ret = ath10k_wmi_start_scan_verify(arg);
+	if (ret)
+		return ERR_PTR(ret);
+
+	chan_len = arg->n_channels * sizeof(__le32);
+	ssid_len = arg->n_ssids * sizeof(struct wmi_ssid);
+	bssid_len = arg->n_bssids * sizeof(struct wmi_mac_addr);
+	ie_len = roundup(arg->ie_len, 4);
+	len = (sizeof(*tlv) + sizeof(*cmd)) +
+	      (arg->n_channels ? sizeof(*tlv) + chan_len : 0) +
+	      (arg->n_ssids ? sizeof(*tlv) + ssid_len : 0) +
+	      (arg->n_bssids ? sizeof(*tlv) + bssid_len : 0) +
+	      (arg->ie_len ? sizeof(*tlv) + ie_len : 0);
+
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_START_SCAN_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+
+	ath10k_wmi_put_start_scan_common(&cmd->common, arg);
+	cmd->burst_duration_ms = __cpu_to_le32(0);
+	cmd->num_channels = __cpu_to_le32(arg->n_channels);
+	cmd->num_ssids = __cpu_to_le32(arg->n_ssids);
+	cmd->num_bssids = __cpu_to_le32(arg->n_bssids);
+	cmd->ie_len = __cpu_to_le32(arg->ie_len);
+	cmd->num_probes = __cpu_to_le32(3);
+
+	/* FIXME: There are some scan flag inconsistencies across firmwares,
+	 * e.g. WMI-TLV inverts the logic behind the following flag.
+	 */
+	cmd->common.scan_ctrl_flags ^= __cpu_to_le32(WMI_SCAN_FILTER_PROBE_REQ);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_UINT32);
+	tlv->len = __cpu_to_le16(chan_len);
+	chans = (void *)tlv->value;
+	for (i = 0; i < arg->n_channels; i++)
+		chans[i] = __cpu_to_le32(arg->channels[i]);
+
+	ptr += sizeof(*tlv);
+	ptr += chan_len;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_FIXED_STRUCT);
+	tlv->len = __cpu_to_le16(ssid_len);
+	ssids = (void *)tlv->value;
+	for (i = 0; i < arg->n_ssids; i++) {
+		ssids[i].ssid_len = __cpu_to_le32(arg->ssids[i].len);
+		memcpy(ssids[i].ssid, arg->ssids[i].ssid, arg->ssids[i].len);
+	}
+
+	ptr += sizeof(*tlv);
+	ptr += ssid_len;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_FIXED_STRUCT);
+	tlv->len = __cpu_to_le16(bssid_len);
+	addrs = (void *)tlv->value;
+	for (i = 0; i < arg->n_bssids; i++)
+		ether_addr_copy(addrs[i].addr, arg->bssids[i].bssid);
+
+	ptr += sizeof(*tlv);
+	ptr += bssid_len;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
+	tlv->len = __cpu_to_le16(ie_len);
+	memcpy(tlv->value, arg->ie, arg->ie_len);
+
+	ptr += sizeof(*tlv);
+	ptr += ie_len;
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv start scan\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_stop_scan(struct ath10k *ar,
+				const struct wmi_stop_scan_arg *arg)
+{
+	struct wmi_stop_scan_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	u32 scan_id;
+	u32 req_id;
+
+	if (arg->req_id > 0xFFF)
+		return ERR_PTR(-EINVAL);
+	if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF)
+		return ERR_PTR(-EINVAL);
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	scan_id = arg->u.scan_id;
+	scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX;
+
+	req_id = arg->req_id;
+	req_id |= WMI_HOST_SCAN_REQUESTOR_ID_PREFIX;
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STOP_SCAN_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->req_type = __cpu_to_le32(arg->req_type);
+	cmd->vdev_id = __cpu_to_le32(arg->u.vdev_id);
+	cmd->scan_id = __cpu_to_le32(scan_id);
+	cmd->scan_req_id = __cpu_to_le32(req_id);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv stop scan\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_vdev_create(struct ath10k *ar,
+				  u32 vdev_id,
+				  enum wmi_vdev_type vdev_type,
+				  enum wmi_vdev_subtype vdev_subtype,
+				  const u8 mac_addr[ETH_ALEN])
+{
+	struct wmi_vdev_create_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_CREATE_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->vdev_type = __cpu_to_le32(vdev_type);
+	cmd->vdev_subtype = __cpu_to_le32(vdev_subtype);
+	ether_addr_copy(cmd->vdev_macaddr.addr, mac_addr);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev create\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_vdev_delete(struct ath10k *ar, u32 vdev_id)
+{
+	struct wmi_vdev_delete_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_DELETE_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev delete\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_vdev_start(struct ath10k *ar,
+				 const struct wmi_vdev_start_request_arg *arg,
+				 bool restart)
+{
+	struct wmi_tlv_vdev_start_cmd *cmd;
+	struct wmi_channel *ch;
+	struct wmi_p2p_noa_descriptor *noa;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	size_t len;
+	void *ptr;
+	u32 flags = 0;
+
+	if (WARN_ON(arg->ssid && arg->ssid_len == 0))
+		return ERR_PTR(-EINVAL);
+	if (WARN_ON(arg->hidden_ssid && !arg->ssid))
+		return ERR_PTR(-EINVAL);
+	if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid)))
+		return ERR_PTR(-EINVAL);
+
+	len = (sizeof(*tlv) + sizeof(*cmd)) +
+	      (sizeof(*tlv) + sizeof(*ch)) +
+	      (sizeof(*tlv) + 0);
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	if (arg->hidden_ssid)
+		flags |= WMI_VDEV_START_HIDDEN_SSID;
+	if (arg->pmf_enabled)
+		flags |= WMI_VDEV_START_PMF_ENABLED;
+
+	ptr = (void *)skb->data;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_START_REQUEST_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
+	cmd->bcn_intval = __cpu_to_le32(arg->bcn_intval);
+	cmd->dtim_period = __cpu_to_le32(arg->dtim_period);
+	cmd->flags = __cpu_to_le32(flags);
+	cmd->bcn_tx_rate = __cpu_to_le32(arg->bcn_tx_rate);
+	cmd->bcn_tx_power = __cpu_to_le32(arg->bcn_tx_power);
+	cmd->disable_hw_ack = __cpu_to_le32(arg->disable_hw_ack);
+
+	if (arg->ssid) {
+		cmd->ssid.ssid_len = __cpu_to_le32(arg->ssid_len);
+		memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len);
+	}
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_CHANNEL);
+	tlv->len = __cpu_to_le16(sizeof(*ch));
+	ch = (void *)tlv->value;
+	ath10k_wmi_put_wmi_channel(ch, &arg->channel);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*ch);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
+	tlv->len = 0;
+	noa = (void *)tlv->value;
+
+	/* Note: This is a nested TLV containing:
+	 * [wmi_tlv][wmi_p2p_noa_descriptor][wmi_tlv]..
+	 */
+
+	ptr += sizeof(*tlv);
+	ptr += 0;
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev start\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_vdev_stop(struct ath10k *ar, u32 vdev_id)
+{
+	struct wmi_vdev_stop_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_STOP_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev stop\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
+			      const u8 *bssid)
+
+{
+	struct wmi_vdev_up_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_UP_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->vdev_assoc_id = __cpu_to_le32(aid);
+	ether_addr_copy(cmd->vdev_bssid.addr, bssid);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev up\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_vdev_down(struct ath10k *ar, u32 vdev_id)
+{
+	struct wmi_vdev_down_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_DOWN_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev down\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_vdev_set_param(struct ath10k *ar, u32 vdev_id,
+				     u32 param_id, u32 param_value)
+{
+	struct wmi_vdev_set_param_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_SET_PARAM_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->param_id = __cpu_to_le32(param_id);
+	cmd->param_value = __cpu_to_le32(param_value);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev set param\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_vdev_install_key(struct ath10k *ar,
+				       const struct wmi_vdev_install_key_arg *arg)
+{
+	struct wmi_vdev_install_key_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	size_t len;
+	void *ptr;
+
+	if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL)
+		return ERR_PTR(-EINVAL);
+	if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL)
+		return ERR_PTR(-EINVAL);
+
+	len = sizeof(*tlv) + sizeof(*cmd) +
+	      sizeof(*tlv) + roundup(arg->key_len, sizeof(__le32));
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
+	cmd->key_idx = __cpu_to_le32(arg->key_idx);
+	cmd->key_flags = __cpu_to_le32(arg->key_flags);
+	cmd->key_cipher = __cpu_to_le32(arg->key_cipher);
+	cmd->key_len = __cpu_to_le32(arg->key_len);
+	cmd->key_txmic_len = __cpu_to_le32(arg->key_txmic_len);
+	cmd->key_rxmic_len = __cpu_to_le32(arg->key_rxmic_len);
+
+	if (arg->macaddr)
+		ether_addr_copy(cmd->peer_macaddr.addr, arg->macaddr);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
+	tlv->len = __cpu_to_le16(roundup(arg->key_len, sizeof(__le32)));
+	if (arg->key_data)
+		memcpy(tlv->value, arg->key_data, arg->key_len);
+
+	ptr += sizeof(*tlv);
+	ptr += roundup(arg->key_len, sizeof(__le32));
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev install key\n");
+	return skb;
+}
+
+static void *ath10k_wmi_tlv_put_uapsd_ac(struct ath10k *ar, void *ptr,
+					 const struct wmi_sta_uapsd_auto_trig_arg *arg)
+{
+	struct wmi_sta_uapsd_auto_trig_param *ac;
+	struct wmi_tlv *tlv;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_PARAM);
+	tlv->len = __cpu_to_le16(sizeof(*ac));
+	ac = (void *)tlv->value;
+
+	ac->wmm_ac = __cpu_to_le32(arg->wmm_ac);
+	ac->user_priority = __cpu_to_le32(arg->user_priority);
+	ac->service_interval = __cpu_to_le32(arg->service_interval);
+	ac->suspend_interval = __cpu_to_le32(arg->suspend_interval);
+	ac->delay_interval = __cpu_to_le32(arg->delay_interval);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI,
+		   "wmi tlv vdev sta uapsd auto trigger ac %d prio %d svc int %d susp int %d delay int %d\n",
+		   ac->wmm_ac, ac->user_priority, ac->service_interval,
+		   ac->suspend_interval, ac->delay_interval);
+
+	return ptr + sizeof(*tlv) + sizeof(*ac);
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_vdev_sta_uapsd(struct ath10k *ar, u32 vdev_id,
+				     const u8 peer_addr[ETH_ALEN],
+				     const struct wmi_sta_uapsd_auto_trig_arg *args,
+				     u32 num_ac)
+{
+	struct wmi_sta_uapsd_auto_trig_cmd_fixed_param *cmd;
+	struct wmi_sta_uapsd_auto_trig_param *ac;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	size_t len;
+	size_t ac_tlv_len;
+	void *ptr;
+	int i;
+
+	ac_tlv_len = num_ac * (sizeof(*tlv) + sizeof(*ac));
+	len = sizeof(*tlv) + sizeof(*cmd) +
+	      sizeof(*tlv) + ac_tlv_len;
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->num_ac = __cpu_to_le32(num_ac);
+	ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
+	tlv->len = __cpu_to_le16(ac_tlv_len);
+	ac = (void *)tlv->value;
+
+	ptr += sizeof(*tlv);
+	for (i = 0; i < num_ac; i++)
+		ptr = ath10k_wmi_tlv_put_uapsd_ac(ar, ptr, &args[i]);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev sta uapsd auto trigger\n");
+	return skb;
+}
+
+static void *ath10k_wmi_tlv_put_wmm(void *ptr,
+				    const struct wmi_wmm_params_arg *arg)
+{
+	struct wmi_wmm_params *wmm;
+	struct wmi_tlv *tlv;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_WMM_PARAMS);
+	tlv->len = __cpu_to_le16(sizeof(*wmm));
+	wmm = (void *)tlv->value;
+	ath10k_wmi_set_wmm_param(wmm, arg);
+
+	return ptr + sizeof(*tlv) + sizeof(*wmm);
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_vdev_wmm_conf(struct ath10k *ar, u32 vdev_id,
+				    const struct wmi_wmm_params_all_arg *arg)
+{
+	struct wmi_tlv_vdev_set_wmm_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	size_t len;
+	void *ptr;
+
+	len = sizeof(*tlv) + sizeof(*cmd);
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_SET_WMM_PARAMS_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+	ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[0].params, &arg->ac_be);
+	ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[1].params, &arg->ac_bk);
+	ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[2].params, &arg->ac_vi);
+	ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[3].params, &arg->ac_vo);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev wmm conf\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_sta_keepalive(struct ath10k *ar,
+				    const struct wmi_sta_keepalive_arg *arg)
+{
+	struct wmi_tlv_sta_keepalive_cmd *cmd;
+	struct wmi_sta_keepalive_arp_resp *arp;
+	struct sk_buff *skb;
+	struct wmi_tlv *tlv;
+	void *ptr;
+	size_t len;
+
+	len = sizeof(*tlv) + sizeof(*cmd) +
+	      sizeof(*tlv) + sizeof(*arp);
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_KEEPALIVE_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
+	cmd->enabled = __cpu_to_le32(arg->enabled);
+	cmd->method = __cpu_to_le32(arg->method);
+	cmd->interval = __cpu_to_le32(arg->interval);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_KEEPALVE_ARP_RESPONSE);
+	tlv->len = __cpu_to_le16(sizeof(*arp));
+	arp = (void *)tlv->value;
+
+	arp->src_ip4_addr = arg->src_ip4_addr;
+	arp->dest_ip4_addr = arg->dest_ip4_addr;
+	ether_addr_copy(arp->dest_mac_addr.addr, arg->dest_mac_addr);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv sta keepalive vdev %d enabled %d method %d inverval %d\n",
+		   arg->vdev_id, arg->enabled, arg->method, arg->interval);
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_peer_create(struct ath10k *ar, u32 vdev_id,
+				  const u8 peer_addr[ETH_ALEN])
+{
+	struct wmi_tlv_peer_create_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_CREATE_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->peer_type = __cpu_to_le32(WMI_TLV_PEER_TYPE_DEFAULT); /* FIXME */
+	ether_addr_copy(cmd->peer_addr.addr, peer_addr);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer create\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_peer_delete(struct ath10k *ar, u32 vdev_id,
+				  const u8 peer_addr[ETH_ALEN])
+{
+	struct wmi_peer_delete_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_DELETE_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer delete\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_peer_flush(struct ath10k *ar, u32 vdev_id,
+				 const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
+{
+	struct wmi_peer_flush_tids_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_FLUSH_TIDS_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->peer_tid_bitmap = __cpu_to_le32(tid_bitmap);
+	ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer flush\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id,
+				     const u8 *peer_addr,
+				     enum wmi_peer_param param_id,
+				     u32 param_value)
+{
+	struct wmi_peer_set_param_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_SET_PARAM_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->param_id = __cpu_to_le32(param_id);
+	cmd->param_value = __cpu_to_le32(param_value);
+	ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer set param\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_peer_assoc(struct ath10k *ar,
+				 const struct wmi_peer_assoc_complete_arg *arg)
+{
+	struct wmi_tlv_peer_assoc_cmd *cmd;
+	struct wmi_vht_rate_set *vht_rate;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	size_t len, legacy_rate_len, ht_rate_len;
+	void *ptr;
+
+	if (arg->peer_mpdu_density > 16)
+		return ERR_PTR(-EINVAL);
+	if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES)
+		return ERR_PTR(-EINVAL);
+	if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES)
+		return ERR_PTR(-EINVAL);
+
+	legacy_rate_len = roundup(arg->peer_legacy_rates.num_rates,
+				  sizeof(__le32));
+	ht_rate_len = roundup(arg->peer_ht_rates.num_rates, sizeof(__le32));
+	len = (sizeof(*tlv) + sizeof(*cmd)) +
+	      (sizeof(*tlv) + legacy_rate_len) +
+	      (sizeof(*tlv) + ht_rate_len) +
+	      (sizeof(*tlv) + sizeof(*vht_rate));
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_ASSOC_COMPLETE_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+
+	cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
+	cmd->new_assoc = __cpu_to_le32(arg->peer_reassoc ? 0 : 1);
+	cmd->assoc_id = __cpu_to_le32(arg->peer_aid);
+	cmd->flags = __cpu_to_le32(arg->peer_flags);
+	cmd->caps = __cpu_to_le32(arg->peer_caps);
+	cmd->listen_intval = __cpu_to_le32(arg->peer_listen_intval);
+	cmd->ht_caps = __cpu_to_le32(arg->peer_ht_caps);
+	cmd->max_mpdu = __cpu_to_le32(arg->peer_max_mpdu);
+	cmd->mpdu_density = __cpu_to_le32(arg->peer_mpdu_density);
+	cmd->rate_caps = __cpu_to_le32(arg->peer_rate_caps);
+	cmd->nss = __cpu_to_le32(arg->peer_num_spatial_streams);
+	cmd->vht_caps = __cpu_to_le32(arg->peer_vht_caps);
+	cmd->phy_mode = __cpu_to_le32(arg->peer_phymode);
+	cmd->num_legacy_rates = __cpu_to_le32(arg->peer_legacy_rates.num_rates);
+	cmd->num_ht_rates = __cpu_to_le32(arg->peer_ht_rates.num_rates);
+	ether_addr_copy(cmd->mac_addr.addr, arg->addr);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
+	tlv->len = __cpu_to_le16(legacy_rate_len);
+	memcpy(tlv->value, arg->peer_legacy_rates.rates,
+	       arg->peer_legacy_rates.num_rates);
+
+	ptr += sizeof(*tlv);
+	ptr += legacy_rate_len;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
+	tlv->len = __cpu_to_le16(ht_rate_len);
+	memcpy(tlv->value, arg->peer_ht_rates.rates,
+	       arg->peer_ht_rates.num_rates);
+
+	ptr += sizeof(*tlv);
+	ptr += ht_rate_len;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VHT_RATE_SET);
+	tlv->len = __cpu_to_le16(sizeof(*vht_rate));
+	vht_rate = (void *)tlv->value;
+
+	vht_rate->rx_max_rate = __cpu_to_le32(arg->peer_vht_rates.rx_max_rate);
+	vht_rate->rx_mcs_set = __cpu_to_le32(arg->peer_vht_rates.rx_mcs_set);
+	vht_rate->tx_max_rate = __cpu_to_le32(arg->peer_vht_rates.tx_max_rate);
+	vht_rate->tx_mcs_set = __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*vht_rate);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer assoc\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id,
+				 enum wmi_sta_ps_mode psmode)
+{
+	struct wmi_sta_powersave_mode_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_POWERSAVE_MODE_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->sta_ps_mode = __cpu_to_le32(psmode);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv set psmode\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_set_sta_ps(struct ath10k *ar, u32 vdev_id,
+				 enum wmi_sta_powersave_param param_id,
+				 u32 param_value)
+{
+	struct wmi_sta_powersave_param_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_POWERSAVE_PARAM_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->param_id = __cpu_to_le32(param_id);
+	cmd->param_value = __cpu_to_le32(param_value);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv set sta ps\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_set_ap_ps(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+				enum wmi_ap_ps_peer_param param_id, u32 value)
+{
+	struct wmi_ap_ps_peer_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	if (!mac)
+		return ERR_PTR(-EINVAL);
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_AP_PS_PEER_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->param_id = __cpu_to_le32(param_id);
+	cmd->param_value = __cpu_to_le32(value);
+	ether_addr_copy(cmd->peer_macaddr.addr, mac);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv ap ps param\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_scan_chan_list(struct ath10k *ar,
+				     const struct wmi_scan_chan_list_arg *arg)
+{
+	struct wmi_tlv_scan_chan_list_cmd *cmd;
+	struct wmi_channel *ci;
+	struct wmi_channel_arg *ch;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	size_t chans_len, len;
+	int i;
+	void *ptr, *chans;
+
+	chans_len = arg->n_channels * (sizeof(*tlv) + sizeof(*ci));
+	len = (sizeof(*tlv) + sizeof(*cmd)) +
+	      (sizeof(*tlv) + chans_len);
+
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_SCAN_CHAN_LIST_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->num_scan_chans = __cpu_to_le32(arg->n_channels);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
+	tlv->len = __cpu_to_le16(chans_len);
+	chans = (void *)tlv->value;
+
+	for (i = 0; i < arg->n_channels; i++) {
+		ch = &arg->channels[i];
+
+		tlv = chans;
+		tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_CHANNEL);
+		tlv->len = __cpu_to_le16(sizeof(*ci));
+		ci = (void *)tlv->value;
+
+		ath10k_wmi_put_wmi_channel(ci, ch);
+
+		chans += sizeof(*tlv);
+		chans += sizeof(*ci);
+	}
+
+	ptr += sizeof(*tlv);
+	ptr += chans_len;
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv scan chan list\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_beacon_dma(struct ath10k *ar, u32 vdev_id,
+				 const void *bcn, size_t bcn_len,
+				 u32 bcn_paddr, bool dtim_zero,
+				 bool deliver_cab)
+
+{
+	struct wmi_bcn_tx_ref_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	hdr = (struct ieee80211_hdr *)bcn;
+	fc = le16_to_cpu(hdr->frame_control);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_SEND_FROM_HOST_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->data_len = __cpu_to_le32(bcn_len);
+	cmd->data_ptr = __cpu_to_le32(bcn_paddr);
+	cmd->msdu_id = 0;
+	cmd->frame_control = __cpu_to_le32(fc);
+	cmd->flags = 0;
+
+	if (dtim_zero)
+		cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO);
+
+	if (deliver_cab)
+		cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv beacon dma\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_pdev_set_wmm(struct ath10k *ar,
+				   const struct wmi_wmm_params_all_arg *arg)
+{
+	struct wmi_tlv_pdev_set_wmm_cmd *cmd;
+	struct wmi_wmm_params *wmm;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	size_t len;
+	void *ptr;
+
+	len = (sizeof(*tlv) + sizeof(*cmd)) +
+	      (4 * (sizeof(*tlv) + sizeof(*wmm)));
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_WMM_PARAMS_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+
+	/* nothing to set here */
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_be);
+	ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_bk);
+	ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vi);
+	ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vo);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set wmm\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar, u32 stats_mask)
+{
+	struct wmi_request_stats_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_REQUEST_STATS_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->stats_id = __cpu_to_le32(stats_mask);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv request stats\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_force_fw_hang(struct ath10k *ar,
+				    enum wmi_force_fw_hang_type type,
+				    u32 delay_ms)
+{
+	struct wmi_force_fw_hang_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	tlv = (void *)skb->data;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_FORCE_FW_HANG_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->type = __cpu_to_le32(type);
+	cmd->delay_ms = __cpu_to_le32(delay_ms);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv force fw hang\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable,
+				 u32 log_level) {
+	struct wmi_tlv_dbglog_cmd *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	size_t len, bmap_len;
+	u32 value;
+	void *ptr;
+
+	if (module_enable) {
+		value = WMI_TLV_DBGLOG_LOG_LEVEL_VALUE(
+				module_enable,
+				WMI_TLV_DBGLOG_LOG_LEVEL_VERBOSE);
+	} else {
+		value = WMI_TLV_DBGLOG_LOG_LEVEL_VALUE(
+				WMI_TLV_DBGLOG_ALL_MODULES,
+				WMI_TLV_DBGLOG_LOG_LEVEL_WARN);
+	}
+
+	bmap_len = 0;
+	len = sizeof(*tlv) + sizeof(*cmd) + sizeof(*tlv) + bmap_len;
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_DEBUG_LOG_CONFIG_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->param = __cpu_to_le32(WMI_TLV_DBGLOG_PARAM_LOG_LEVEL);
+	cmd->value = __cpu_to_le32(value);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_UINT32);
+	tlv->len = __cpu_to_le16(bmap_len);
+
+	/* nothing to do here */
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(bmap_len);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv dbglog value 0x%08x\n", value);
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_pktlog_enable(struct ath10k *ar, u32 filter)
+{
+	struct wmi_tlv_pktlog_enable *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	void *ptr;
+	size_t len;
+
+	len = sizeof(*tlv) + sizeof(*cmd);
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_ENABLE_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->filter = __cpu_to_le32(filter);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pktlog enable filter 0x%08x\n",
+		   filter);
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_pktlog_disable(struct ath10k *ar)
+{
+	struct wmi_tlv_pktlog_disable *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	void *ptr;
+	size_t len;
+
+	len = sizeof(*tlv) + sizeof(*cmd);
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_DISABLE_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pktlog disable\n");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_bcn_tmpl(struct ath10k *ar, u32 vdev_id,
+			       u32 tim_ie_offset, struct sk_buff *bcn,
+			       u32 prb_caps, u32 prb_erp, void *prb_ies,
+			       size_t prb_ies_len)
+{
+	struct wmi_tlv_bcn_tmpl_cmd *cmd;
+	struct wmi_tlv_bcn_prb_info *info;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	void *ptr;
+	size_t len;
+
+	if (WARN_ON(prb_ies_len > 0 && !prb_ies))
+		return ERR_PTR(-EINVAL);
+
+	len = sizeof(*tlv) + sizeof(*cmd) +
+	      sizeof(*tlv) + sizeof(*info) + prb_ies_len +
+	      sizeof(*tlv) + roundup(bcn->len, 4);
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_TMPL_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->tim_ie_offset = __cpu_to_le32(tim_ie_offset);
+	cmd->buf_len = __cpu_to_le32(bcn->len);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	/* FIXME: prb_ies_len should be probably aligned to 4byte boundary but
+	 * then it is then impossible to pass original ie len.
+	 * This chunk is not used yet so if setting probe resp template yields
+	 * problems with beaconing or crashes firmware look here.
+	 */
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_PRB_INFO);
+	tlv->len = __cpu_to_le16(sizeof(*info) + prb_ies_len);
+	info = (void *)tlv->value;
+	info->caps = __cpu_to_le32(prb_caps);
+	info->erp = __cpu_to_le32(prb_erp);
+	memcpy(info->ies, prb_ies, prb_ies_len);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*info);
+	ptr += prb_ies_len;
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
+	tlv->len = __cpu_to_le16(roundup(bcn->len, 4));
+	memcpy(tlv->value, bcn->data, bcn->len);
+
+	/* FIXME: Adjust TSF? */
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv bcn tmpl vdev_id %i\n",
+		   vdev_id);
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_prb_tmpl(struct ath10k *ar, u32 vdev_id,
+			       struct sk_buff *prb)
+{
+	struct wmi_tlv_prb_tmpl_cmd *cmd;
+	struct wmi_tlv_bcn_prb_info *info;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	void *ptr;
+	size_t len;
+
+	len = sizeof(*tlv) + sizeof(*cmd) +
+	      sizeof(*tlv) + sizeof(*info) +
+	      sizeof(*tlv) + roundup(prb->len, 4);
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PRB_TMPL_CMD);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->buf_len = __cpu_to_le32(prb->len);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_PRB_INFO);
+	tlv->len = __cpu_to_le16(sizeof(*info));
+	info = (void *)tlv->value;
+	info->caps = 0;
+	info->erp = 0;
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*info);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
+	tlv->len = __cpu_to_le16(roundup(prb->len, 4));
+	memcpy(tlv->value, prb->data, prb->len);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv prb tmpl vdev_id %i\n",
+		   vdev_id);
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_p2p_go_bcn_ie(struct ath10k *ar, u32 vdev_id,
+				    const u8 *p2p_ie)
+{
+	struct wmi_tlv_p2p_go_bcn_ie *cmd;
+	struct wmi_tlv *tlv;
+	struct sk_buff *skb;
+	void *ptr;
+	size_t len;
+
+	len = sizeof(*tlv) + sizeof(*cmd) +
+	      sizeof(*tlv) + roundup(p2p_ie[1] + 2, 4);
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ptr = (void *)skb->data;
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_P2P_GO_SET_BEACON_IE);
+	tlv->len = __cpu_to_le16(sizeof(*cmd));
+	cmd = (void *)tlv->value;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->ie_len = __cpu_to_le32(p2p_ie[1] + 2);
+
+	ptr += sizeof(*tlv);
+	ptr += sizeof(*cmd);
+
+	tlv = ptr;
+	tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
+	tlv->len = __cpu_to_le16(roundup(p2p_ie[1] + 2, 4));
+	memcpy(tlv->value, p2p_ie, p2p_ie[1] + 2);
+
+	ptr += sizeof(*tlv);
+	ptr += roundup(p2p_ie[1] + 2, 4);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv p2p go bcn ie for vdev %i\n",
+		   vdev_id);
+	return skb;
+}
+
+/****************/
+/* TLV mappings */
+/****************/
+
+static struct wmi_cmd_map wmi_tlv_cmd_map = {
+	.init_cmdid = WMI_TLV_INIT_CMDID,
+	.start_scan_cmdid = WMI_TLV_START_SCAN_CMDID,
+	.stop_scan_cmdid = WMI_TLV_STOP_SCAN_CMDID,
+	.scan_chan_list_cmdid = WMI_TLV_SCAN_CHAN_LIST_CMDID,
+	.scan_sch_prio_tbl_cmdid = WMI_TLV_SCAN_SCH_PRIO_TBL_CMDID,
+	.pdev_set_regdomain_cmdid = WMI_TLV_PDEV_SET_REGDOMAIN_CMDID,
+	.pdev_set_channel_cmdid = WMI_TLV_PDEV_SET_CHANNEL_CMDID,
+	.pdev_set_param_cmdid = WMI_TLV_PDEV_SET_PARAM_CMDID,
+	.pdev_pktlog_enable_cmdid = WMI_TLV_PDEV_PKTLOG_ENABLE_CMDID,
+	.pdev_pktlog_disable_cmdid = WMI_TLV_PDEV_PKTLOG_DISABLE_CMDID,
+	.pdev_set_wmm_params_cmdid = WMI_TLV_PDEV_SET_WMM_PARAMS_CMDID,
+	.pdev_set_ht_cap_ie_cmdid = WMI_TLV_PDEV_SET_HT_CAP_IE_CMDID,
+	.pdev_set_vht_cap_ie_cmdid = WMI_TLV_PDEV_SET_VHT_CAP_IE_CMDID,
+	.pdev_set_dscp_tid_map_cmdid = WMI_TLV_PDEV_SET_DSCP_TID_MAP_CMDID,
+	.pdev_set_quiet_mode_cmdid = WMI_TLV_PDEV_SET_QUIET_MODE_CMDID,
+	.pdev_green_ap_ps_enable_cmdid = WMI_TLV_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+	.pdev_get_tpc_config_cmdid = WMI_TLV_PDEV_GET_TPC_CONFIG_CMDID,
+	.pdev_set_base_macaddr_cmdid = WMI_TLV_PDEV_SET_BASE_MACADDR_CMDID,
+	.vdev_create_cmdid = WMI_TLV_VDEV_CREATE_CMDID,
+	.vdev_delete_cmdid = WMI_TLV_VDEV_DELETE_CMDID,
+	.vdev_start_request_cmdid = WMI_TLV_VDEV_START_REQUEST_CMDID,
+	.vdev_restart_request_cmdid = WMI_TLV_VDEV_RESTART_REQUEST_CMDID,
+	.vdev_up_cmdid = WMI_TLV_VDEV_UP_CMDID,
+	.vdev_stop_cmdid = WMI_TLV_VDEV_STOP_CMDID,
+	.vdev_down_cmdid = WMI_TLV_VDEV_DOWN_CMDID,
+	.vdev_set_param_cmdid = WMI_TLV_VDEV_SET_PARAM_CMDID,
+	.vdev_install_key_cmdid = WMI_TLV_VDEV_INSTALL_KEY_CMDID,
+	.peer_create_cmdid = WMI_TLV_PEER_CREATE_CMDID,
+	.peer_delete_cmdid = WMI_TLV_PEER_DELETE_CMDID,
+	.peer_flush_tids_cmdid = WMI_TLV_PEER_FLUSH_TIDS_CMDID,
+	.peer_set_param_cmdid = WMI_TLV_PEER_SET_PARAM_CMDID,
+	.peer_assoc_cmdid = WMI_TLV_PEER_ASSOC_CMDID,
+	.peer_add_wds_entry_cmdid = WMI_TLV_PEER_ADD_WDS_ENTRY_CMDID,
+	.peer_remove_wds_entry_cmdid = WMI_TLV_PEER_REMOVE_WDS_ENTRY_CMDID,
+	.peer_mcast_group_cmdid = WMI_TLV_PEER_MCAST_GROUP_CMDID,
+	.bcn_tx_cmdid = WMI_TLV_BCN_TX_CMDID,
+	.pdev_send_bcn_cmdid = WMI_TLV_PDEV_SEND_BCN_CMDID,
+	.bcn_tmpl_cmdid = WMI_TLV_BCN_TMPL_CMDID,
+	.bcn_filter_rx_cmdid = WMI_TLV_BCN_FILTER_RX_CMDID,
+	.prb_req_filter_rx_cmdid = WMI_TLV_PRB_REQ_FILTER_RX_CMDID,
+	.mgmt_tx_cmdid = WMI_TLV_MGMT_TX_CMDID,
+	.prb_tmpl_cmdid = WMI_TLV_PRB_TMPL_CMDID,
+	.addba_clear_resp_cmdid = WMI_TLV_ADDBA_CLEAR_RESP_CMDID,
+	.addba_send_cmdid = WMI_TLV_ADDBA_SEND_CMDID,
+	.addba_status_cmdid = WMI_TLV_ADDBA_STATUS_CMDID,
+	.delba_send_cmdid = WMI_TLV_DELBA_SEND_CMDID,
+	.addba_set_resp_cmdid = WMI_TLV_ADDBA_SET_RESP_CMDID,
+	.send_singleamsdu_cmdid = WMI_TLV_SEND_SINGLEAMSDU_CMDID,
+	.sta_powersave_mode_cmdid = WMI_TLV_STA_POWERSAVE_MODE_CMDID,
+	.sta_powersave_param_cmdid = WMI_TLV_STA_POWERSAVE_PARAM_CMDID,
+	.sta_mimo_ps_mode_cmdid = WMI_TLV_STA_MIMO_PS_MODE_CMDID,
+	.pdev_dfs_enable_cmdid = WMI_TLV_PDEV_DFS_ENABLE_CMDID,
+	.pdev_dfs_disable_cmdid = WMI_TLV_PDEV_DFS_DISABLE_CMDID,
+	.roam_scan_mode = WMI_TLV_ROAM_SCAN_MODE,
+	.roam_scan_rssi_threshold = WMI_TLV_ROAM_SCAN_RSSI_THRESHOLD,
+	.roam_scan_period = WMI_TLV_ROAM_SCAN_PERIOD,
+	.roam_scan_rssi_change_threshold =
+				WMI_TLV_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+	.roam_ap_profile = WMI_TLV_ROAM_AP_PROFILE,
+	.ofl_scan_add_ap_profile = WMI_TLV_ROAM_AP_PROFILE,
+	.ofl_scan_remove_ap_profile = WMI_TLV_OFL_SCAN_REMOVE_AP_PROFILE,
+	.ofl_scan_period = WMI_TLV_OFL_SCAN_PERIOD,
+	.p2p_dev_set_device_info = WMI_TLV_P2P_DEV_SET_DEVICE_INFO,
+	.p2p_dev_set_discoverability = WMI_TLV_P2P_DEV_SET_DISCOVERABILITY,
+	.p2p_go_set_beacon_ie = WMI_TLV_P2P_GO_SET_BEACON_IE,
+	.p2p_go_set_probe_resp_ie = WMI_TLV_P2P_GO_SET_PROBE_RESP_IE,
+	.p2p_set_vendor_ie_data_cmdid = WMI_TLV_P2P_SET_VENDOR_IE_DATA_CMDID,
+	.ap_ps_peer_param_cmdid = WMI_TLV_AP_PS_PEER_PARAM_CMDID,
+	.ap_ps_peer_uapsd_coex_cmdid = WMI_TLV_AP_PS_PEER_UAPSD_COEX_CMDID,
+	.peer_rate_retry_sched_cmdid = WMI_TLV_PEER_RATE_RETRY_SCHED_CMDID,
+	.wlan_profile_trigger_cmdid = WMI_TLV_WLAN_PROFILE_TRIGGER_CMDID,
+	.wlan_profile_set_hist_intvl_cmdid =
+				WMI_TLV_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+	.wlan_profile_get_profile_data_cmdid =
+				WMI_TLV_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+	.wlan_profile_enable_profile_id_cmdid =
+				WMI_TLV_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+	.wlan_profile_list_profile_id_cmdid =
+				WMI_TLV_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+	.pdev_suspend_cmdid = WMI_TLV_PDEV_SUSPEND_CMDID,
+	.pdev_resume_cmdid = WMI_TLV_PDEV_RESUME_CMDID,
+	.add_bcn_filter_cmdid = WMI_TLV_ADD_BCN_FILTER_CMDID,
+	.rmv_bcn_filter_cmdid = WMI_TLV_RMV_BCN_FILTER_CMDID,
+	.wow_add_wake_pattern_cmdid = WMI_TLV_WOW_ADD_WAKE_PATTERN_CMDID,
+	.wow_del_wake_pattern_cmdid = WMI_TLV_WOW_DEL_WAKE_PATTERN_CMDID,
+	.wow_enable_disable_wake_event_cmdid =
+				WMI_TLV_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+	.wow_enable_cmdid = WMI_TLV_WOW_ENABLE_CMDID,
+	.wow_hostwakeup_from_sleep_cmdid =
+				WMI_TLV_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+	.rtt_measreq_cmdid = WMI_TLV_RTT_MEASREQ_CMDID,
+	.rtt_tsf_cmdid = WMI_TLV_RTT_TSF_CMDID,
+	.vdev_spectral_scan_configure_cmdid = WMI_TLV_SPECTRAL_SCAN_CONF_CMDID,
+	.vdev_spectral_scan_enable_cmdid = WMI_TLV_SPECTRAL_SCAN_ENABLE_CMDID,
+	.request_stats_cmdid = WMI_TLV_REQUEST_STATS_CMDID,
+	.set_arp_ns_offload_cmdid = WMI_TLV_SET_ARP_NS_OFFLOAD_CMDID,
+	.network_list_offload_config_cmdid =
+				WMI_TLV_NETWORK_LIST_OFFLOAD_CONFIG_CMDID,
+	.gtk_offload_cmdid = WMI_TLV_GTK_OFFLOAD_CMDID,
+	.csa_offload_enable_cmdid = WMI_TLV_CSA_OFFLOAD_ENABLE_CMDID,
+	.csa_offload_chanswitch_cmdid = WMI_TLV_CSA_OFFLOAD_CHANSWITCH_CMDID,
+	.chatter_set_mode_cmdid = WMI_TLV_CHATTER_SET_MODE_CMDID,
+	.peer_tid_addba_cmdid = WMI_TLV_PEER_TID_ADDBA_CMDID,
+	.peer_tid_delba_cmdid = WMI_TLV_PEER_TID_DELBA_CMDID,
+	.sta_dtim_ps_method_cmdid = WMI_TLV_STA_DTIM_PS_METHOD_CMDID,
+	.sta_uapsd_auto_trig_cmdid = WMI_TLV_STA_UAPSD_AUTO_TRIG_CMDID,
+	.sta_keepalive_cmd = WMI_TLV_STA_KEEPALIVE_CMDID,
+	.echo_cmdid = WMI_TLV_ECHO_CMDID,
+	.pdev_utf_cmdid = WMI_TLV_PDEV_UTF_CMDID,
+	.dbglog_cfg_cmdid = WMI_TLV_DBGLOG_CFG_CMDID,
+	.pdev_qvit_cmdid = WMI_TLV_PDEV_QVIT_CMDID,
+	.pdev_ftm_intg_cmdid = WMI_TLV_PDEV_FTM_INTG_CMDID,
+	.vdev_set_keepalive_cmdid = WMI_TLV_VDEV_SET_KEEPALIVE_CMDID,
+	.vdev_get_keepalive_cmdid = WMI_TLV_VDEV_GET_KEEPALIVE_CMDID,
+	.force_fw_hang_cmdid = WMI_TLV_FORCE_FW_HANG_CMDID,
+	.gpio_config_cmdid = WMI_TLV_GPIO_CONFIG_CMDID,
+	.gpio_output_cmdid = WMI_TLV_GPIO_OUTPUT_CMDID,
+	.pdev_get_temperature_cmdid = WMI_TLV_CMD_UNSUPPORTED,
+	.vdev_set_wmm_params_cmdid = WMI_TLV_VDEV_SET_WMM_PARAMS_CMDID,
+};
+
+static struct wmi_pdev_param_map wmi_tlv_pdev_param_map = {
+	.tx_chain_mask = WMI_TLV_PDEV_PARAM_TX_CHAIN_MASK,
+	.rx_chain_mask = WMI_TLV_PDEV_PARAM_RX_CHAIN_MASK,
+	.txpower_limit2g = WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT2G,
+	.txpower_limit5g = WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT5G,
+	.txpower_scale = WMI_TLV_PDEV_PARAM_TXPOWER_SCALE,
+	.beacon_gen_mode = WMI_TLV_PDEV_PARAM_BEACON_GEN_MODE,
+	.beacon_tx_mode = WMI_TLV_PDEV_PARAM_BEACON_TX_MODE,
+	.resmgr_offchan_mode = WMI_TLV_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+	.protection_mode = WMI_TLV_PDEV_PARAM_PROTECTION_MODE,
+	.dynamic_bw = WMI_TLV_PDEV_PARAM_DYNAMIC_BW,
+	.non_agg_sw_retry_th = WMI_TLV_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+	.agg_sw_retry_th = WMI_TLV_PDEV_PARAM_AGG_SW_RETRY_TH,
+	.sta_kickout_th = WMI_TLV_PDEV_PARAM_STA_KICKOUT_TH,
+	.ac_aggrsize_scaling = WMI_TLV_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+	.ltr_enable = WMI_TLV_PDEV_PARAM_LTR_ENABLE,
+	.ltr_ac_latency_be = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BE,
+	.ltr_ac_latency_bk = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BK,
+	.ltr_ac_latency_vi = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VI,
+	.ltr_ac_latency_vo = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VO,
+	.ltr_ac_latency_timeout = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+	.ltr_sleep_override = WMI_TLV_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+	.ltr_rx_override = WMI_TLV_PDEV_PARAM_LTR_RX_OVERRIDE,
+	.ltr_tx_activity_timeout = WMI_TLV_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+	.l1ss_enable = WMI_TLV_PDEV_PARAM_L1SS_ENABLE,
+	.dsleep_enable = WMI_TLV_PDEV_PARAM_DSLEEP_ENABLE,
+	.pcielp_txbuf_flush = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_FLUSH,
+	.pcielp_txbuf_watermark = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+	.pcielp_txbuf_tmo_en = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+	.pcielp_txbuf_tmo_value = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE,
+	.pdev_stats_update_period = WMI_TLV_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+	.vdev_stats_update_period = WMI_TLV_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+	.peer_stats_update_period = WMI_TLV_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+	.bcnflt_stats_update_period =
+				WMI_TLV_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+	.pmf_qos = WMI_TLV_PDEV_PARAM_PMF_QOS,
+	.arp_ac_override = WMI_TLV_PDEV_PARAM_ARP_AC_OVERRIDE,
+	.dcs = WMI_TLV_PDEV_PARAM_DCS,
+	.ani_enable = WMI_TLV_PDEV_PARAM_ANI_ENABLE,
+	.ani_poll_period = WMI_TLV_PDEV_PARAM_ANI_POLL_PERIOD,
+	.ani_listen_period = WMI_TLV_PDEV_PARAM_ANI_LISTEN_PERIOD,
+	.ani_ofdm_level = WMI_TLV_PDEV_PARAM_ANI_OFDM_LEVEL,
+	.ani_cck_level = WMI_TLV_PDEV_PARAM_ANI_CCK_LEVEL,
+	.dyntxchain = WMI_TLV_PDEV_PARAM_DYNTXCHAIN,
+	.proxy_sta = WMI_TLV_PDEV_PARAM_PROXY_STA,
+	.idle_ps_config = WMI_TLV_PDEV_PARAM_IDLE_PS_CONFIG,
+	.power_gating_sleep = WMI_TLV_PDEV_PARAM_POWER_GATING_SLEEP,
+	.fast_channel_reset = WMI_TLV_PDEV_PARAM_UNSUPPORTED,
+	.burst_dur = WMI_TLV_PDEV_PARAM_BURST_DUR,
+	.burst_enable = WMI_TLV_PDEV_PARAM_BURST_ENABLE,
+	.cal_period = WMI_PDEV_PARAM_UNSUPPORTED,
+};
+
+static struct wmi_vdev_param_map wmi_tlv_vdev_param_map = {
+	.rts_threshold = WMI_TLV_VDEV_PARAM_RTS_THRESHOLD,
+	.fragmentation_threshold = WMI_TLV_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+	.beacon_interval = WMI_TLV_VDEV_PARAM_BEACON_INTERVAL,
+	.listen_interval = WMI_TLV_VDEV_PARAM_LISTEN_INTERVAL,
+	.multicast_rate = WMI_TLV_VDEV_PARAM_MULTICAST_RATE,
+	.mgmt_tx_rate = WMI_TLV_VDEV_PARAM_MGMT_TX_RATE,
+	.slot_time = WMI_TLV_VDEV_PARAM_SLOT_TIME,
+	.preamble = WMI_TLV_VDEV_PARAM_PREAMBLE,
+	.swba_time = WMI_TLV_VDEV_PARAM_SWBA_TIME,
+	.wmi_vdev_stats_update_period = WMI_TLV_VDEV_STATS_UPDATE_PERIOD,
+	.wmi_vdev_pwrsave_ageout_time = WMI_TLV_VDEV_PWRSAVE_AGEOUT_TIME,
+	.wmi_vdev_host_swba_interval = WMI_TLV_VDEV_HOST_SWBA_INTERVAL,
+	.dtim_period = WMI_TLV_VDEV_PARAM_DTIM_PERIOD,
+	.wmi_vdev_oc_scheduler_air_time_limit =
+				WMI_TLV_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+	.wds = WMI_TLV_VDEV_PARAM_WDS,
+	.atim_window = WMI_TLV_VDEV_PARAM_ATIM_WINDOW,
+	.bmiss_count_max = WMI_TLV_VDEV_PARAM_BMISS_COUNT_MAX,
+	.bmiss_first_bcnt = WMI_TLV_VDEV_PARAM_BMISS_FIRST_BCNT,
+	.bmiss_final_bcnt = WMI_TLV_VDEV_PARAM_BMISS_FINAL_BCNT,
+	.feature_wmm = WMI_TLV_VDEV_PARAM_FEATURE_WMM,
+	.chwidth = WMI_TLV_VDEV_PARAM_CHWIDTH,
+	.chextoffset = WMI_TLV_VDEV_PARAM_CHEXTOFFSET,
+	.disable_htprotection =	WMI_TLV_VDEV_PARAM_DISABLE_HTPROTECTION,
+	.sta_quickkickout = WMI_TLV_VDEV_PARAM_STA_QUICKKICKOUT,
+	.mgmt_rate = WMI_TLV_VDEV_PARAM_MGMT_RATE,
+	.protection_mode = WMI_TLV_VDEV_PARAM_PROTECTION_MODE,
+	.fixed_rate = WMI_TLV_VDEV_PARAM_FIXED_RATE,
+	.sgi = WMI_TLV_VDEV_PARAM_SGI,
+	.ldpc = WMI_TLV_VDEV_PARAM_LDPC,
+	.tx_stbc = WMI_TLV_VDEV_PARAM_TX_STBC,
+	.rx_stbc = WMI_TLV_VDEV_PARAM_RX_STBC,
+	.intra_bss_fwd = WMI_TLV_VDEV_PARAM_INTRA_BSS_FWD,
+	.def_keyid = WMI_TLV_VDEV_PARAM_DEF_KEYID,
+	.nss = WMI_TLV_VDEV_PARAM_NSS,
+	.bcast_data_rate = WMI_TLV_VDEV_PARAM_BCAST_DATA_RATE,
+	.mcast_data_rate = WMI_TLV_VDEV_PARAM_MCAST_DATA_RATE,
+	.mcast_indicate = WMI_TLV_VDEV_PARAM_MCAST_INDICATE,
+	.dhcp_indicate = WMI_TLV_VDEV_PARAM_DHCP_INDICATE,
+	.unknown_dest_indicate = WMI_TLV_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+	.ap_keepalive_min_idle_inactive_time_secs =
+		WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+	.ap_keepalive_max_idle_inactive_time_secs =
+		WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+	.ap_keepalive_max_unresponsive_time_secs =
+		WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+	.ap_enable_nawds = WMI_TLV_VDEV_PARAM_AP_ENABLE_NAWDS,
+	.mcast2ucast_set = WMI_TLV_VDEV_PARAM_UNSUPPORTED,
+	.enable_rtscts = WMI_TLV_VDEV_PARAM_ENABLE_RTSCTS,
+	.txbf = WMI_TLV_VDEV_PARAM_TXBF,
+	.packet_powersave = WMI_TLV_VDEV_PARAM_PACKET_POWERSAVE,
+	.drop_unencry = WMI_TLV_VDEV_PARAM_DROP_UNENCRY,
+	.tx_encap_type = WMI_TLV_VDEV_PARAM_TX_ENCAP_TYPE,
+	.ap_detect_out_of_sync_sleeping_sta_time_secs =
+					WMI_TLV_VDEV_PARAM_UNSUPPORTED,
+};
+
+static const struct wmi_ops wmi_tlv_ops = {
+	.rx = ath10k_wmi_tlv_op_rx,
+	.map_svc = wmi_tlv_svc_map,
+
+	.pull_scan = ath10k_wmi_tlv_op_pull_scan_ev,
+	.pull_mgmt_rx = ath10k_wmi_tlv_op_pull_mgmt_rx_ev,
+	.pull_ch_info = ath10k_wmi_tlv_op_pull_ch_info_ev,
+	.pull_vdev_start = ath10k_wmi_tlv_op_pull_vdev_start_ev,
+	.pull_peer_kick = ath10k_wmi_tlv_op_pull_peer_kick_ev,
+	.pull_swba = ath10k_wmi_tlv_op_pull_swba_ev,
+	.pull_phyerr = ath10k_wmi_tlv_op_pull_phyerr_ev,
+	.pull_svc_rdy = ath10k_wmi_tlv_op_pull_svc_rdy_ev,
+	.pull_rdy = ath10k_wmi_tlv_op_pull_rdy_ev,
+	.pull_fw_stats = ath10k_wmi_tlv_op_pull_fw_stats,
+
+	.gen_pdev_suspend = ath10k_wmi_tlv_op_gen_pdev_suspend,
+	.gen_pdev_resume = ath10k_wmi_tlv_op_gen_pdev_resume,
+	.gen_pdev_set_rd = ath10k_wmi_tlv_op_gen_pdev_set_rd,
+	.gen_pdev_set_param = ath10k_wmi_tlv_op_gen_pdev_set_param,
+	.gen_init = ath10k_wmi_tlv_op_gen_init,
+	.gen_start_scan = ath10k_wmi_tlv_op_gen_start_scan,
+	.gen_stop_scan = ath10k_wmi_tlv_op_gen_stop_scan,
+	.gen_vdev_create = ath10k_wmi_tlv_op_gen_vdev_create,
+	.gen_vdev_delete = ath10k_wmi_tlv_op_gen_vdev_delete,
+	.gen_vdev_start = ath10k_wmi_tlv_op_gen_vdev_start,
+	.gen_vdev_stop = ath10k_wmi_tlv_op_gen_vdev_stop,
+	.gen_vdev_up = ath10k_wmi_tlv_op_gen_vdev_up,
+	.gen_vdev_down = ath10k_wmi_tlv_op_gen_vdev_down,
+	.gen_vdev_set_param = ath10k_wmi_tlv_op_gen_vdev_set_param,
+	.gen_vdev_install_key = ath10k_wmi_tlv_op_gen_vdev_install_key,
+	.gen_vdev_wmm_conf = ath10k_wmi_tlv_op_gen_vdev_wmm_conf,
+	.gen_peer_create = ath10k_wmi_tlv_op_gen_peer_create,
+	.gen_peer_delete = ath10k_wmi_tlv_op_gen_peer_delete,
+	.gen_peer_flush = ath10k_wmi_tlv_op_gen_peer_flush,
+	.gen_peer_set_param = ath10k_wmi_tlv_op_gen_peer_set_param,
+	.gen_peer_assoc = ath10k_wmi_tlv_op_gen_peer_assoc,
+	.gen_set_psmode = ath10k_wmi_tlv_op_gen_set_psmode,
+	.gen_set_sta_ps = ath10k_wmi_tlv_op_gen_set_sta_ps,
+	.gen_set_ap_ps = ath10k_wmi_tlv_op_gen_set_ap_ps,
+	.gen_scan_chan_list = ath10k_wmi_tlv_op_gen_scan_chan_list,
+	.gen_beacon_dma = ath10k_wmi_tlv_op_gen_beacon_dma,
+	.gen_pdev_set_wmm = ath10k_wmi_tlv_op_gen_pdev_set_wmm,
+	.gen_request_stats = ath10k_wmi_tlv_op_gen_request_stats,
+	.gen_force_fw_hang = ath10k_wmi_tlv_op_gen_force_fw_hang,
+	/* .gen_mgmt_tx = not implemented; HTT is used */
+	.gen_dbglog_cfg = ath10k_wmi_tlv_op_gen_dbglog_cfg,
+	.gen_pktlog_enable = ath10k_wmi_tlv_op_gen_pktlog_enable,
+	.gen_pktlog_disable = ath10k_wmi_tlv_op_gen_pktlog_disable,
+	/* .gen_pdev_set_quiet_mode not implemented */
+	/* .gen_pdev_get_temperature not implemented */
+	/* .gen_addba_clear_resp not implemented */
+	/* .gen_addba_send not implemented */
+	/* .gen_addba_set_resp not implemented */
+	/* .gen_delba_send not implemented */
+	.gen_bcn_tmpl = ath10k_wmi_tlv_op_gen_bcn_tmpl,
+	.gen_prb_tmpl = ath10k_wmi_tlv_op_gen_prb_tmpl,
+	.gen_p2p_go_bcn_ie = ath10k_wmi_tlv_op_gen_p2p_go_bcn_ie,
+	.gen_vdev_sta_uapsd = ath10k_wmi_tlv_op_gen_vdev_sta_uapsd,
+	.gen_sta_keepalive = ath10k_wmi_tlv_op_gen_sta_keepalive,
+};
+
+/************/
+/* TLV init */
+/************/
+
+void ath10k_wmi_tlv_attach(struct ath10k *ar)
+{
+	ar->wmi.cmd = &wmi_tlv_cmd_map;
+	ar->wmi.vdev_param = &wmi_tlv_vdev_param_map;
+	ar->wmi.pdev_param = &wmi_tlv_pdev_param_map;
+	ar->wmi.ops = &wmi_tlv_ops;
+}
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
new file mode 100644
index 0000000..a6c8280
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
@@ -0,0 +1,1459 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2014 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _WMI_TLV_H
+#define _WMI_TLV_H
+
+#define WMI_TLV_CMD(grp_id) (((grp_id) << 12) | 0x1)
+#define WMI_TLV_EV(grp_id) (((grp_id) << 12) | 0x1)
+#define WMI_TLV_CMD_UNSUPPORTED 0
+#define WMI_TLV_PDEV_PARAM_UNSUPPORTED 0
+#define WMI_TLV_VDEV_PARAM_UNSUPPORTED 0
+
+enum wmi_tlv_grp_id {
+	WMI_TLV_GRP_START = 0x3,
+	WMI_TLV_GRP_SCAN = WMI_TLV_GRP_START,
+	WMI_TLV_GRP_PDEV,
+	WMI_TLV_GRP_VDEV,
+	WMI_TLV_GRP_PEER,
+	WMI_TLV_GRP_MGMT,
+	WMI_TLV_GRP_BA_NEG,
+	WMI_TLV_GRP_STA_PS,
+	WMI_TLV_GRP_DFS,
+	WMI_TLV_GRP_ROAM,
+	WMI_TLV_GRP_OFL_SCAN,
+	WMI_TLV_GRP_P2P,
+	WMI_TLV_GRP_AP_PS,
+	WMI_TLV_GRP_RATECTL,
+	WMI_TLV_GRP_PROFILE,
+	WMI_TLV_GRP_SUSPEND,
+	WMI_TLV_GRP_BCN_FILTER,
+	WMI_TLV_GRP_WOW,
+	WMI_TLV_GRP_RTT,
+	WMI_TLV_GRP_SPECTRAL,
+	WMI_TLV_GRP_STATS,
+	WMI_TLV_GRP_ARP_NS_OFL,
+	WMI_TLV_GRP_NLO_OFL,
+	WMI_TLV_GRP_GTK_OFL,
+	WMI_TLV_GRP_CSA_OFL,
+	WMI_TLV_GRP_CHATTER,
+	WMI_TLV_GRP_TID_ADDBA,
+	WMI_TLV_GRP_MISC,
+	WMI_TLV_GRP_GPIO,
+	WMI_TLV_GRP_FWTEST,
+	WMI_TLV_GRP_TDLS,
+	WMI_TLV_GRP_RESMGR,
+	WMI_TLV_GRP_STA_SMPS,
+	WMI_TLV_GRP_WLAN_HB,
+	WMI_TLV_GRP_RMC,
+	WMI_TLV_GRP_MHF_OFL,
+	WMI_TLV_GRP_LOCATION_SCAN,
+	WMI_TLV_GRP_OEM,
+	WMI_TLV_GRP_NAN,
+	WMI_TLV_GRP_COEX,
+	WMI_TLV_GRP_OBSS_OFL,
+	WMI_TLV_GRP_LPI,
+	WMI_TLV_GRP_EXTSCAN,
+	WMI_TLV_GRP_DHCP_OFL,
+	WMI_TLV_GRP_IPA,
+	WMI_TLV_GRP_MDNS_OFL,
+	WMI_TLV_GRP_SAP_OFL,
+};
+
+enum wmi_tlv_cmd_id {
+	WMI_TLV_INIT_CMDID = 0x1,
+	WMI_TLV_START_SCAN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SCAN),
+	WMI_TLV_STOP_SCAN_CMDID,
+	WMI_TLV_SCAN_CHAN_LIST_CMDID,
+	WMI_TLV_SCAN_SCH_PRIO_TBL_CMDID,
+	WMI_TLV_SCAN_UPDATE_REQUEST_CMDID,
+	WMI_TLV_SCAN_PROB_REQ_OUI_CMDID,
+	WMI_TLV_PDEV_SET_REGDOMAIN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PDEV),
+	WMI_TLV_PDEV_SET_CHANNEL_CMDID,
+	WMI_TLV_PDEV_SET_PARAM_CMDID,
+	WMI_TLV_PDEV_PKTLOG_ENABLE_CMDID,
+	WMI_TLV_PDEV_PKTLOG_DISABLE_CMDID,
+	WMI_TLV_PDEV_SET_WMM_PARAMS_CMDID,
+	WMI_TLV_PDEV_SET_HT_CAP_IE_CMDID,
+	WMI_TLV_PDEV_SET_VHT_CAP_IE_CMDID,
+	WMI_TLV_PDEV_SET_DSCP_TID_MAP_CMDID,
+	WMI_TLV_PDEV_SET_QUIET_MODE_CMDID,
+	WMI_TLV_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+	WMI_TLV_PDEV_GET_TPC_CONFIG_CMDID,
+	WMI_TLV_PDEV_SET_BASE_MACADDR_CMDID,
+	WMI_TLV_PDEV_DUMP_CMDID,
+	WMI_TLV_PDEV_SET_LED_CONFIG_CMDID,
+	WMI_TLV_PDEV_GET_TEMPERATURE_CMDID,
+	WMI_TLV_PDEV_SET_LED_FLASHING_CMDID,
+	WMI_TLV_VDEV_CREATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_VDEV),
+	WMI_TLV_VDEV_DELETE_CMDID,
+	WMI_TLV_VDEV_START_REQUEST_CMDID,
+	WMI_TLV_VDEV_RESTART_REQUEST_CMDID,
+	WMI_TLV_VDEV_UP_CMDID,
+	WMI_TLV_VDEV_STOP_CMDID,
+	WMI_TLV_VDEV_DOWN_CMDID,
+	WMI_TLV_VDEV_SET_PARAM_CMDID,
+	WMI_TLV_VDEV_INSTALL_KEY_CMDID,
+	WMI_TLV_VDEV_WNM_SLEEPMODE_CMDID,
+	WMI_TLV_VDEV_WMM_ADDTS_CMDID,
+	WMI_TLV_VDEV_WMM_DELTS_CMDID,
+	WMI_TLV_VDEV_SET_WMM_PARAMS_CMDID,
+	WMI_TLV_VDEV_SET_GTX_PARAMS_CMDID,
+	WMI_TLV_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMDID,
+	WMI_TLV_VDEV_PLMREQ_START_CMDID,
+	WMI_TLV_VDEV_PLMREQ_STOP_CMDID,
+	WMI_TLV_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PEER),
+	WMI_TLV_PEER_DELETE_CMDID,
+	WMI_TLV_PEER_FLUSH_TIDS_CMDID,
+	WMI_TLV_PEER_SET_PARAM_CMDID,
+	WMI_TLV_PEER_ASSOC_CMDID,
+	WMI_TLV_PEER_ADD_WDS_ENTRY_CMDID,
+	WMI_TLV_PEER_REMOVE_WDS_ENTRY_CMDID,
+	WMI_TLV_PEER_MCAST_GROUP_CMDID,
+	WMI_TLV_PEER_INFO_REQ_CMDID,
+	WMI_TLV_PEER_GET_ESTIMATED_LINKSPEED_CMDID,
+	WMI_TLV_BCN_TX_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MGMT),
+	WMI_TLV_PDEV_SEND_BCN_CMDID,
+	WMI_TLV_BCN_TMPL_CMDID,
+	WMI_TLV_BCN_FILTER_RX_CMDID,
+	WMI_TLV_PRB_REQ_FILTER_RX_CMDID,
+	WMI_TLV_MGMT_TX_CMDID,
+	WMI_TLV_PRB_TMPL_CMDID,
+	WMI_TLV_ADDBA_CLEAR_RESP_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_BA_NEG),
+	WMI_TLV_ADDBA_SEND_CMDID,
+	WMI_TLV_ADDBA_STATUS_CMDID,
+	WMI_TLV_DELBA_SEND_CMDID,
+	WMI_TLV_ADDBA_SET_RESP_CMDID,
+	WMI_TLV_SEND_SINGLEAMSDU_CMDID,
+	WMI_TLV_STA_POWERSAVE_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STA_PS),
+	WMI_TLV_STA_POWERSAVE_PARAM_CMDID,
+	WMI_TLV_STA_MIMO_PS_MODE_CMDID,
+	WMI_TLV_PDEV_DFS_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_DFS),
+	WMI_TLV_PDEV_DFS_DISABLE_CMDID,
+	WMI_TLV_DFS_PHYERR_FILTER_ENA_CMDID,
+	WMI_TLV_DFS_PHYERR_FILTER_DIS_CMDID,
+	WMI_TLV_ROAM_SCAN_MODE = WMI_TLV_CMD(WMI_TLV_GRP_ROAM),
+	WMI_TLV_ROAM_SCAN_RSSI_THRESHOLD,
+	WMI_TLV_ROAM_SCAN_PERIOD,
+	WMI_TLV_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+	WMI_TLV_ROAM_AP_PROFILE,
+	WMI_TLV_ROAM_CHAN_LIST,
+	WMI_TLV_ROAM_SCAN_CMD,
+	WMI_TLV_ROAM_SYNCH_COMPLETE,
+	WMI_TLV_ROAM_SET_RIC_REQUEST_CMDID,
+	WMI_TLV_ROAM_INVOKE_CMDID,
+	WMI_TLV_OFL_SCAN_ADD_AP_PROFILE = WMI_TLV_CMD(WMI_TLV_GRP_OFL_SCAN),
+	WMI_TLV_OFL_SCAN_REMOVE_AP_PROFILE,
+	WMI_TLV_OFL_SCAN_PERIOD,
+	WMI_TLV_P2P_DEV_SET_DEVICE_INFO = WMI_TLV_CMD(WMI_TLV_GRP_P2P),
+	WMI_TLV_P2P_DEV_SET_DISCOVERABILITY,
+	WMI_TLV_P2P_GO_SET_BEACON_IE,
+	WMI_TLV_P2P_GO_SET_PROBE_RESP_IE,
+	WMI_TLV_P2P_SET_VENDOR_IE_DATA_CMDID,
+	WMI_TLV_P2P_DISC_OFFLOAD_CONFIG_CMDID,
+	WMI_TLV_P2P_DISC_OFFLOAD_APPIE_CMDID,
+	WMI_TLV_P2P_DISC_OFFLOAD_PATTERN_CMDID,
+	WMI_TLV_P2P_SET_OPPPS_PARAM_CMDID,
+	WMI_TLV_AP_PS_PEER_PARAM_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_AP_PS),
+	WMI_TLV_AP_PS_PEER_UAPSD_COEX_CMDID,
+	WMI_TLV_PEER_RATE_RETRY_SCHED_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RATECTL),
+	WMI_TLV_WLAN_PROFILE_TRIGGER_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PROFILE),
+	WMI_TLV_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+	WMI_TLV_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+	WMI_TLV_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+	WMI_TLV_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+	WMI_TLV_PDEV_SUSPEND_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SUSPEND),
+	WMI_TLV_PDEV_RESUME_CMDID,
+	WMI_TLV_ADD_BCN_FILTER_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_BCN_FILTER),
+	WMI_TLV_RMV_BCN_FILTER_CMDID,
+	WMI_TLV_WOW_ADD_WAKE_PATTERN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_WOW),
+	WMI_TLV_WOW_DEL_WAKE_PATTERN_CMDID,
+	WMI_TLV_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+	WMI_TLV_WOW_ENABLE_CMDID,
+	WMI_TLV_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+	WMI_TLV_WOW_ACER_IOAC_ADD_KEEPALIVE_CMDID,
+	WMI_TLV_WOW_ACER_IOAC_DEL_KEEPALIVE_CMDID,
+	WMI_TLV_WOW_ACER_IOAC_ADD_WAKE_PATTERN_CMDID,
+	WMI_TLV_WOW_ACER_IOAC_DEL_WAKE_PATTERN_CMDID,
+	WMI_TLV_D0_WOW_ENABLE_DISABLE_CMDID,
+	WMI_TLV_EXTWOW_ENABLE_CMDID,
+	WMI_TLV_EXTWOW_SET_APP_TYPE1_PARAMS_CMDID,
+	WMI_TLV_EXTWOW_SET_APP_TYPE2_PARAMS_CMDID,
+	WMI_TLV_RTT_MEASREQ_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RTT),
+	WMI_TLV_RTT_TSF_CMDID,
+	WMI_TLV_SPECTRAL_SCAN_CONF_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SPECTRAL),
+	WMI_TLV_SPECTRAL_SCAN_ENABLE_CMDID,
+	WMI_TLV_REQUEST_STATS_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STATS),
+	WMI_TLV_MCC_SCHED_TRAFFIC_STATS_CMDID,
+	WMI_TLV_REQUEST_STATS_EXT_CMDID,
+	WMI_TLV_REQUEST_LINK_STATS_CMDID,
+	WMI_TLV_START_LINK_STATS_CMDID,
+	WMI_TLV_CLEAR_LINK_STATS_CMDID,
+	WMI_TLV_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_ARP_NS_OFL),
+	WMI_TLV_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID,
+	WMI_TLV_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID,
+	WMI_TLV_NETWORK_LIST_OFFLOAD_CONFIG_CMDID =
+			WMI_TLV_CMD(WMI_TLV_GRP_NLO_OFL),
+	WMI_TLV_APFIND_CMDID,
+	WMI_TLV_GTK_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_GTK_OFL),
+	WMI_TLV_CSA_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_CSA_OFL),
+	WMI_TLV_CSA_OFFLOAD_CHANSWITCH_CMDID,
+	WMI_TLV_CHATTER_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_CHATTER),
+	WMI_TLV_CHATTER_ADD_COALESCING_FILTER_CMDID,
+	WMI_TLV_CHATTER_DELETE_COALESCING_FILTER_CMDID,
+	WMI_TLV_CHATTER_COALESCING_QUERY_CMDID,
+	WMI_TLV_PEER_TID_ADDBA_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_TID_ADDBA),
+	WMI_TLV_PEER_TID_DELBA_CMDID,
+	WMI_TLV_STA_DTIM_PS_METHOD_CMDID,
+	WMI_TLV_STA_UAPSD_AUTO_TRIG_CMDID,
+	WMI_TLV_STA_KEEPALIVE_CMDID,
+	WMI_TLV_BA_REQ_SSN_CMDID,
+	WMI_TLV_ECHO_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MISC),
+	WMI_TLV_PDEV_UTF_CMDID,
+	WMI_TLV_DBGLOG_CFG_CMDID,
+	WMI_TLV_PDEV_QVIT_CMDID,
+	WMI_TLV_PDEV_FTM_INTG_CMDID,
+	WMI_TLV_VDEV_SET_KEEPALIVE_CMDID,
+	WMI_TLV_VDEV_GET_KEEPALIVE_CMDID,
+	WMI_TLV_FORCE_FW_HANG_CMDID,
+	WMI_TLV_SET_MCASTBCAST_FILTER_CMDID,
+	WMI_TLV_THERMAL_MGMT_CMDID,
+	WMI_TLV_HOST_AUTO_SHUTDOWN_CFG_CMDID,
+	WMI_TLV_TPC_CHAINMASK_CONFIG_CMDID,
+	WMI_TLV_SET_ANTENNA_DIVERSITY_CMDID,
+	WMI_TLV_GPIO_CONFIG_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_GPIO),
+	WMI_TLV_GPIO_OUTPUT_CMDID,
+	WMI_TLV_TXBF_CMDID,
+	WMI_TLV_FWTEST_VDEV_MCC_SET_TBTT_MODE_CMDID =
+			WMI_TLV_CMD(WMI_TLV_GRP_FWTEST),
+	WMI_TLV_FWTEST_P2P_SET_NOA_PARAM_CMDID,
+	WMI_TLV_UNIT_TEST_CMDID,
+	WMI_TLV_TDLS_SET_STATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_TDLS),
+	WMI_TLV_TDLS_PEER_UPDATE_CMDID,
+	WMI_TLV_TDLS_SET_OFFCHAN_MODE_CMDID,
+	WMI_TLV_RESMGR_ADAPTIVE_OCS_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RESMGR),
+	WMI_TLV_RESMGR_SET_CHAN_TIME_QUOTA_CMDID,
+	WMI_TLV_RESMGR_SET_CHAN_LATENCY_CMDID,
+	WMI_TLV_STA_SMPS_FORCE_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STA_SMPS),
+	WMI_TLV_STA_SMPS_PARAM_CMDID,
+	WMI_TLV_HB_SET_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_WLAN_HB),
+	WMI_TLV_HB_SET_TCP_PARAMS_CMDID,
+	WMI_TLV_HB_SET_TCP_PKT_FILTER_CMDID,
+	WMI_TLV_HB_SET_UDP_PARAMS_CMDID,
+	WMI_TLV_HB_SET_UDP_PKT_FILTER_CMDID,
+	WMI_TLV_RMC_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RMC),
+	WMI_TLV_RMC_SET_ACTION_PERIOD_CMDID,
+	WMI_TLV_RMC_CONFIG_CMDID,
+	WMI_TLV_MHF_OFFLOAD_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MHF_OFL),
+	WMI_TLV_MHF_OFFLOAD_PLUMB_ROUTING_TBL_CMDID,
+	WMI_TLV_BATCH_SCAN_ENABLE_CMDID =
+			WMI_TLV_CMD(WMI_TLV_GRP_LOCATION_SCAN),
+	WMI_TLV_BATCH_SCAN_DISABLE_CMDID,
+	WMI_TLV_BATCH_SCAN_TRIGGER_RESULT_CMDID,
+	WMI_TLV_OEM_REQ_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_OEM),
+	WMI_TLV_NAN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_NAN),
+	WMI_TLV_MODEM_POWER_STATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_COEX),
+	WMI_TLV_CHAN_AVOID_UPDATE_CMDID,
+	WMI_TLV_OBSS_SCAN_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_OBSS_OFL),
+	WMI_TLV_OBSS_SCAN_DISABLE_CMDID,
+	WMI_TLV_LPI_MGMT_SNOOPING_CONFIG_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_LPI),
+	WMI_TLV_LPI_START_SCAN_CMDID,
+	WMI_TLV_LPI_STOP_SCAN_CMDID,
+	WMI_TLV_EXTSCAN_START_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_EXTSCAN),
+	WMI_TLV_EXTSCAN_STOP_CMDID,
+	WMI_TLV_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMDID,
+	WMI_TLV_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMDID,
+	WMI_TLV_EXTSCAN_GET_CACHED_RESULTS_CMDID,
+	WMI_TLV_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMDID,
+	WMI_TLV_EXTSCAN_SET_CAPABILITIES_CMDID,
+	WMI_TLV_EXTSCAN_GET_CAPABILITIES_CMDID,
+	WMI_TLV_SET_DHCP_SERVER_OFFLOAD_CMDID =
+			WMI_TLV_CMD(WMI_TLV_GRP_DHCP_OFL),
+	WMI_TLV_IPA_OFFLOAD_ENABLE_DISABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_IPA),
+	WMI_TLV_MDNS_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MDNS_OFL),
+	WMI_TLV_MDNS_SET_FQDN_CMDID,
+	WMI_TLV_MDNS_SET_RESPONSE_CMDID,
+	WMI_TLV_MDNS_GET_STATS_CMDID,
+	WMI_TLV_SAP_OFL_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SAP_OFL),
+};
+
+enum wmi_tlv_event_id {
+	WMI_TLV_SERVICE_READY_EVENTID = 0x1,
+	WMI_TLV_READY_EVENTID,
+	WMI_TLV_SCAN_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SCAN),
+	WMI_TLV_PDEV_TPC_CONFIG_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PDEV),
+	WMI_TLV_CHAN_INFO_EVENTID,
+	WMI_TLV_PHYERR_EVENTID,
+	WMI_TLV_PDEV_DUMP_EVENTID,
+	WMI_TLV_TX_PAUSE_EVENTID,
+	WMI_TLV_DFS_RADAR_EVENTID,
+	WMI_TLV_PDEV_L1SS_TRACK_EVENTID,
+	WMI_TLV_PDEV_TEMPERATURE_EVENTID,
+	WMI_TLV_VDEV_START_RESP_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_VDEV),
+	WMI_TLV_VDEV_STOPPED_EVENTID,
+	WMI_TLV_VDEV_INSTALL_KEY_COMPLETE_EVENTID,
+	WMI_TLV_VDEV_MCC_BCN_INTERVAL_CHANGE_REQ_EVENTID,
+	WMI_TLV_PEER_STA_KICKOUT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PEER),
+	WMI_TLV_PEER_INFO_EVENTID,
+	WMI_TLV_PEER_TX_FAIL_CNT_THR_EVENTID,
+	WMI_TLV_PEER_ESTIMATED_LINKSPEED_EVENTID,
+	WMI_TLV_PEER_STATE_EVENTID,
+	WMI_TLV_MGMT_RX_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MGMT),
+	WMI_TLV_HOST_SWBA_EVENTID,
+	WMI_TLV_TBTTOFFSET_UPDATE_EVENTID,
+	WMI_TLV_OFFLOAD_BCN_TX_STATUS_EVENTID,
+	WMI_TLV_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID,
+	WMI_TLV_TX_DELBA_COMPLETE_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_BA_NEG),
+	WMI_TLV_TX_ADDBA_COMPLETE_EVENTID,
+	WMI_TLV_BA_RSP_SSN_EVENTID,
+	WMI_TLV_AGGR_STATE_TRIG_EVENTID,
+	WMI_TLV_ROAM_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_ROAM),
+	WMI_TLV_PROFILE_MATCH,
+	WMI_TLV_ROAM_SYNCH_EVENTID,
+	WMI_TLV_P2P_DISC_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_P2P),
+	WMI_TLV_P2P_NOA_EVENTID,
+	WMI_TLV_PDEV_RESUME_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SUSPEND),
+	WMI_TLV_WOW_WAKEUP_HOST_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_WOW),
+	WMI_TLV_D0_WOW_DISABLE_ACK_EVENTID,
+	WMI_TLV_RTT_MEASUREMENT_REPORT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_RTT),
+	WMI_TLV_TSF_MEASUREMENT_REPORT_EVENTID,
+	WMI_TLV_RTT_ERROR_REPORT_EVENTID,
+	WMI_TLV_STATS_EXT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_STATS),
+	WMI_TLV_IFACE_LINK_STATS_EVENTID,
+	WMI_TLV_PEER_LINK_STATS_EVENTID,
+	WMI_TLV_RADIO_LINK_STATS_EVENTID,
+	WMI_TLV_NLO_MATCH_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_NLO_OFL),
+	WMI_TLV_NLO_SCAN_COMPLETE_EVENTID,
+	WMI_TLV_APFIND_EVENTID,
+	WMI_TLV_GTK_OFFLOAD_STATUS_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_GTK_OFL),
+	WMI_TLV_GTK_REKEY_FAIL_EVENTID,
+	WMI_TLV_CSA_HANDLING_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_CSA_OFL),
+	WMI_TLV_CHATTER_PC_QUERY_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_CHATTER),
+	WMI_TLV_ECHO_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MISC),
+	WMI_TLV_PDEV_UTF_EVENTID,
+	WMI_TLV_DEBUG_MESG_EVENTID,
+	WMI_TLV_UPDATE_STATS_EVENTID,
+	WMI_TLV_DEBUG_PRINT_EVENTID,
+	WMI_TLV_DCS_INTERFERENCE_EVENTID,
+	WMI_TLV_PDEV_QVIT_EVENTID,
+	WMI_TLV_WLAN_PROFILE_DATA_EVENTID,
+	WMI_TLV_PDEV_FTM_INTG_EVENTID,
+	WMI_TLV_WLAN_FREQ_AVOID_EVENTID,
+	WMI_TLV_VDEV_GET_KEEPALIVE_EVENTID,
+	WMI_TLV_THERMAL_MGMT_EVENTID,
+	WMI_TLV_DIAG_DATA_CONTAINER_EVENTID,
+	WMI_TLV_HOST_AUTO_SHUTDOWN_EVENTID,
+	WMI_TLV_UPDATE_WHAL_MIB_STATS_EVENTID,
+	WMI_TLV_UPDATE_VDEV_RATE_STATS_EVENTID,
+	WMI_TLV_DIAG_EVENTID,
+	WMI_TLV_GPIO_INPUT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_GPIO),
+	WMI_TLV_UPLOADH_EVENTID,
+	WMI_TLV_CAPTUREH_EVENTID,
+	WMI_TLV_RFKILL_STATE_CHANGE_EVENTID,
+	WMI_TLV_TDLS_PEER_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_TDLS),
+	WMI_TLV_BATCH_SCAN_ENABLED_EVENTID =
+			WMI_TLV_EV(WMI_TLV_GRP_LOCATION_SCAN),
+	WMI_TLV_BATCH_SCAN_RESULT_EVENTID,
+	WMI_TLV_OEM_CAPABILITY_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_OEM),
+	WMI_TLV_OEM_MEASUREMENT_REPORT_EVENTID,
+	WMI_TLV_OEM_ERROR_REPORT_EVENTID,
+	WMI_TLV_NAN_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_NAN),
+	WMI_TLV_LPI_RESULT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_LPI),
+	WMI_TLV_LPI_STATUS_EVENTID,
+	WMI_TLV_LPI_HANDOFF_EVENTID,
+	WMI_TLV_EXTSCAN_START_STOP_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_EXTSCAN),
+	WMI_TLV_EXTSCAN_OPERATION_EVENTID,
+	WMI_TLV_EXTSCAN_TABLE_USAGE_EVENTID,
+	WMI_TLV_EXTSCAN_CACHED_RESULTS_EVENTID,
+	WMI_TLV_EXTSCAN_WLAN_CHANGE_RESULTS_EVENTID,
+	WMI_TLV_EXTSCAN_HOTLIST_MATCH_EVENTID,
+	WMI_TLV_EXTSCAN_CAPABILITIES_EVENTID,
+	WMI_TLV_MDNS_STATS_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MDNS_OFL),
+	WMI_TLV_SAP_OFL_ADD_STA_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SAP_OFL),
+	WMI_TLV_SAP_OFL_DEL_STA_EVENTID,
+};
+
+enum wmi_tlv_pdev_param {
+	WMI_TLV_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
+	WMI_TLV_PDEV_PARAM_RX_CHAIN_MASK,
+	WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT2G,
+	WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT5G,
+	WMI_TLV_PDEV_PARAM_TXPOWER_SCALE,
+	WMI_TLV_PDEV_PARAM_BEACON_GEN_MODE,
+	WMI_TLV_PDEV_PARAM_BEACON_TX_MODE,
+	WMI_TLV_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+	WMI_TLV_PDEV_PARAM_PROTECTION_MODE,
+	WMI_TLV_PDEV_PARAM_DYNAMIC_BW,
+	WMI_TLV_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+	WMI_TLV_PDEV_PARAM_AGG_SW_RETRY_TH,
+	WMI_TLV_PDEV_PARAM_STA_KICKOUT_TH,
+	WMI_TLV_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+	WMI_TLV_PDEV_PARAM_LTR_ENABLE,
+	WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BE,
+	WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BK,
+	WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VI,
+	WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VO,
+	WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+	WMI_TLV_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+	WMI_TLV_PDEV_PARAM_LTR_RX_OVERRIDE,
+	WMI_TLV_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+	WMI_TLV_PDEV_PARAM_L1SS_ENABLE,
+	WMI_TLV_PDEV_PARAM_DSLEEP_ENABLE,
+	WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_FLUSH,
+	WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_WATERMARK,
+	WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+	WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE,
+	WMI_TLV_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+	WMI_TLV_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+	WMI_TLV_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+	WMI_TLV_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+	WMI_TLV_PDEV_PARAM_PMF_QOS,
+	WMI_TLV_PDEV_PARAM_ARP_AC_OVERRIDE,
+	WMI_TLV_PDEV_PARAM_DCS,
+	WMI_TLV_PDEV_PARAM_ANI_ENABLE,
+	WMI_TLV_PDEV_PARAM_ANI_POLL_PERIOD,
+	WMI_TLV_PDEV_PARAM_ANI_LISTEN_PERIOD,
+	WMI_TLV_PDEV_PARAM_ANI_OFDM_LEVEL,
+	WMI_TLV_PDEV_PARAM_ANI_CCK_LEVEL,
+	WMI_TLV_PDEV_PARAM_DYNTXCHAIN,
+	WMI_TLV_PDEV_PARAM_PROXY_STA,
+	WMI_TLV_PDEV_PARAM_IDLE_PS_CONFIG,
+	WMI_TLV_PDEV_PARAM_POWER_GATING_SLEEP,
+	WMI_TLV_PDEV_PARAM_RFKILL_ENABLE,
+	WMI_TLV_PDEV_PARAM_BURST_DUR,
+	WMI_TLV_PDEV_PARAM_BURST_ENABLE,
+	WMI_TLV_PDEV_PARAM_HW_RFKILL_CONFIG,
+	WMI_TLV_PDEV_PARAM_LOW_POWER_RF_ENABLE,
+	WMI_TLV_PDEV_PARAM_L1SS_TRACK,
+	WMI_TLV_PDEV_PARAM_HYST_EN,
+	WMI_TLV_PDEV_PARAM_POWER_COLLAPSE_ENABLE,
+	WMI_TLV_PDEV_PARAM_LED_SYS_STATE,
+	WMI_TLV_PDEV_PARAM_LED_ENABLE,
+	WMI_TLV_PDEV_PARAM_AUDIO_OVER_WLAN_LATENCY,
+	WMI_TLV_PDEV_PARAM_AUDIO_OVER_WLAN_ENABLE,
+	WMI_TLV_PDEV_PARAM_WHAL_MIB_STATS_UPDATE_ENABLE,
+	WMI_TLV_PDEV_PARAM_VDEV_RATE_STATS_UPDATE_PERIOD,
+	WMI_TLV_PDEV_PARAM_TXPOWER_REASON_NONE,
+	WMI_TLV_PDEV_PARAM_TXPOWER_REASON_SAR,
+	WMI_TLV_PDEV_PARAM_TXPOWER_REASON_MAX,
+};
+
+enum wmi_tlv_vdev_param {
+	WMI_TLV_VDEV_PARAM_RTS_THRESHOLD = 0x1,
+	WMI_TLV_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+	WMI_TLV_VDEV_PARAM_BEACON_INTERVAL,
+	WMI_TLV_VDEV_PARAM_LISTEN_INTERVAL,
+	WMI_TLV_VDEV_PARAM_MULTICAST_RATE,
+	WMI_TLV_VDEV_PARAM_MGMT_TX_RATE,
+	WMI_TLV_VDEV_PARAM_SLOT_TIME,
+	WMI_TLV_VDEV_PARAM_PREAMBLE,
+	WMI_TLV_VDEV_PARAM_SWBA_TIME,
+	WMI_TLV_VDEV_STATS_UPDATE_PERIOD,
+	WMI_TLV_VDEV_PWRSAVE_AGEOUT_TIME,
+	WMI_TLV_VDEV_HOST_SWBA_INTERVAL,
+	WMI_TLV_VDEV_PARAM_DTIM_PERIOD,
+	WMI_TLV_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+	WMI_TLV_VDEV_PARAM_WDS,
+	WMI_TLV_VDEV_PARAM_ATIM_WINDOW,
+	WMI_TLV_VDEV_PARAM_BMISS_COUNT_MAX,
+	WMI_TLV_VDEV_PARAM_BMISS_FIRST_BCNT,
+	WMI_TLV_VDEV_PARAM_BMISS_FINAL_BCNT,
+	WMI_TLV_VDEV_PARAM_FEATURE_WMM,
+	WMI_TLV_VDEV_PARAM_CHWIDTH,
+	WMI_TLV_VDEV_PARAM_CHEXTOFFSET,
+	WMI_TLV_VDEV_PARAM_DISABLE_HTPROTECTION,
+	WMI_TLV_VDEV_PARAM_STA_QUICKKICKOUT,
+	WMI_TLV_VDEV_PARAM_MGMT_RATE,
+	WMI_TLV_VDEV_PARAM_PROTECTION_MODE,
+	WMI_TLV_VDEV_PARAM_FIXED_RATE,
+	WMI_TLV_VDEV_PARAM_SGI,
+	WMI_TLV_VDEV_PARAM_LDPC,
+	WMI_TLV_VDEV_PARAM_TX_STBC,
+	WMI_TLV_VDEV_PARAM_RX_STBC,
+	WMI_TLV_VDEV_PARAM_INTRA_BSS_FWD,
+	WMI_TLV_VDEV_PARAM_DEF_KEYID,
+	WMI_TLV_VDEV_PARAM_NSS,
+	WMI_TLV_VDEV_PARAM_BCAST_DATA_RATE,
+	WMI_TLV_VDEV_PARAM_MCAST_DATA_RATE,
+	WMI_TLV_VDEV_PARAM_MCAST_INDICATE,
+	WMI_TLV_VDEV_PARAM_DHCP_INDICATE,
+	WMI_TLV_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+	WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+	WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+	WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+	WMI_TLV_VDEV_PARAM_AP_ENABLE_NAWDS,
+	WMI_TLV_VDEV_PARAM_ENABLE_RTSCTS,
+	WMI_TLV_VDEV_PARAM_TXBF,
+	WMI_TLV_VDEV_PARAM_PACKET_POWERSAVE,
+	WMI_TLV_VDEV_PARAM_DROP_UNENCRY,
+	WMI_TLV_VDEV_PARAM_TX_ENCAP_TYPE,
+	WMI_TLV_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
+	WMI_TLV_VDEV_PARAM_EARLY_RX_ADJUST_ENABLE,
+	WMI_TLV_VDEV_PARAM_EARLY_RX_TGT_BMISS_NUM,
+	WMI_TLV_VDEV_PARAM_EARLY_RX_BMISS_SAMPLE_CYCLE,
+	WMI_TLV_VDEV_PARAM_EARLY_RX_SLOP_STEP,
+	WMI_TLV_VDEV_PARAM_EARLY_RX_INIT_SLOP,
+	WMI_TLV_VDEV_PARAM_EARLY_RX_ADJUST_PAUSE,
+	WMI_TLV_VDEV_PARAM_TX_PWRLIMIT,
+	WMI_TLV_VDEV_PARAM_SNR_NUM_FOR_CAL,
+	WMI_TLV_VDEV_PARAM_ROAM_FW_OFFLOAD,
+	WMI_TLV_VDEV_PARAM_ENABLE_RMC,
+	WMI_TLV_VDEV_PARAM_IBSS_MAX_BCN_LOST_MS,
+	WMI_TLV_VDEV_PARAM_MAX_RATE,
+	WMI_TLV_VDEV_PARAM_EARLY_RX_DRIFT_SAMPLE,
+	WMI_TLV_VDEV_PARAM_SET_IBSS_TX_FAIL_CNT_THR,
+	WMI_TLV_VDEV_PARAM_EBT_RESYNC_TIMEOUT,
+	WMI_TLV_VDEV_PARAM_AGGR_TRIG_EVENT_ENABLE,
+	WMI_TLV_VDEV_PARAM_IS_IBSS_POWER_SAVE_ALLOWED,
+	WMI_TLV_VDEV_PARAM_IS_POWER_COLLAPSE_ALLOWED,
+	WMI_TLV_VDEV_PARAM_IS_AWAKE_ON_TXRX_ENABLED,
+	WMI_TLV_VDEV_PARAM_INACTIVITY_CNT,
+	WMI_TLV_VDEV_PARAM_TXSP_END_INACTIVITY_TIME_MS,
+	WMI_TLV_VDEV_PARAM_DTIM_POLICY,
+	WMI_TLV_VDEV_PARAM_IBSS_PS_WARMUP_TIME_SECS,
+	WMI_TLV_VDEV_PARAM_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE,
+};
+
+enum wmi_tlv_tag {
+	WMI_TLV_TAG_LAST_RESERVED = 15,
+
+	WMI_TLV_TAG_FIRST_ARRAY_ENUM,
+	WMI_TLV_TAG_ARRAY_UINT32 = WMI_TLV_TAG_FIRST_ARRAY_ENUM,
+	WMI_TLV_TAG_ARRAY_BYTE,
+	WMI_TLV_TAG_ARRAY_STRUCT,
+	WMI_TLV_TAG_ARRAY_FIXED_STRUCT,
+	WMI_TLV_TAG_LAST_ARRAY_ENUM = 31,
+
+	WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT,
+	WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES,
+	WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ,
+	WMI_TLV_TAG_STRUCT_READY_EVENT,
+	WMI_TLV_TAG_STRUCT_SCAN_EVENT,
+	WMI_TLV_TAG_STRUCT_PDEV_TPC_CONFIG_EVENT,
+	WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT,
+	WMI_TLV_TAG_STRUCT_COMB_PHYERR_RX_HDR,
+	WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT,
+	WMI_TLV_TAG_STRUCT_VDEV_STOPPED_EVENT,
+	WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_COMPLETE_EVENT,
+	WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT,
+	WMI_TLV_TAG_STRUCT_MGMT_RX_HDR,
+	WMI_TLV_TAG_STRUCT_TBTT_OFFSET_EVENT,
+	WMI_TLV_TAG_STRUCT_TX_DELBA_COMPLETE_EVENT,
+	WMI_TLV_TAG_STRUCT_TX_ADDBA_COMPLETE_EVENT,
+	WMI_TLV_TAG_STRUCT_ROAM_EVENT,
+	WMI_TLV_TAG_STRUCT_WOW_EVENT_INFO,
+	WMI_TLV_TAG_STRUCT_WOW_EVENT_INFO_SECTION_BITMAP,
+	WMI_TLV_TAG_STRUCT_RTT_EVENT_HEADER,
+	WMI_TLV_TAG_STRUCT_RTT_ERROR_REPORT_EVENT,
+	WMI_TLV_TAG_STRUCT_RTT_MEAS_EVENT,
+	WMI_TLV_TAG_STRUCT_ECHO_EVENT,
+	WMI_TLV_TAG_STRUCT_FTM_INTG_EVENT,
+	WMI_TLV_TAG_STRUCT_VDEV_GET_KEEPALIVE_EVENT,
+	WMI_TLV_TAG_STRUCT_GPIO_INPUT_EVENT,
+	WMI_TLV_TAG_STRUCT_CSA_EVENT,
+	WMI_TLV_TAG_STRUCT_GTK_OFFLOAD_STATUS_EVENT,
+	WMI_TLV_TAG_STRUCT_IGTK_INFO,
+	WMI_TLV_TAG_STRUCT_DCS_INTERFERENCE_EVENT,
+	WMI_TLV_TAG_STRUCT_ATH_DCS_CW_INT,
+	WMI_TLV_TAG_STRUCT_ATH_DCS_WLAN_INT_STAT,
+	WMI_TLV_TAG_STRUCT_WLAN_PROFILE_CTX_T,
+	WMI_TLV_TAG_STRUCT_WLAN_PROFILE_T,
+	WMI_TLV_TAG_STRUCT_PDEV_QVIT_EVENT,
+	WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT,
+	WMI_TLV_TAG_STRUCT_TIM_INFO,
+	WMI_TLV_TAG_STRUCT_P2P_NOA_INFO,
+	WMI_TLV_TAG_STRUCT_STATS_EVENT,
+	WMI_TLV_TAG_STRUCT_AVOID_FREQ_RANGES_EVENT,
+	WMI_TLV_TAG_STRUCT_AVOID_FREQ_RANGE_DESC,
+	WMI_TLV_TAG_STRUCT_GTK_REKEY_FAIL_EVENT,
+	WMI_TLV_TAG_STRUCT_INIT_CMD,
+	WMI_TLV_TAG_STRUCT_RESOURCE_CONFIG,
+	WMI_TLV_TAG_STRUCT_WLAN_HOST_MEMORY_CHUNK,
+	WMI_TLV_TAG_STRUCT_START_SCAN_CMD,
+	WMI_TLV_TAG_STRUCT_STOP_SCAN_CMD,
+	WMI_TLV_TAG_STRUCT_SCAN_CHAN_LIST_CMD,
+	WMI_TLV_TAG_STRUCT_CHANNEL,
+	WMI_TLV_TAG_STRUCT_PDEV_SET_REGDOMAIN_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_SET_PARAM_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_SET_WMM_PARAMS_CMD,
+	WMI_TLV_TAG_STRUCT_WMM_PARAMS,
+	WMI_TLV_TAG_STRUCT_PDEV_SET_QUIET_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_CREATE_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_DELETE_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_START_REQUEST_CMD,
+	WMI_TLV_TAG_STRUCT_P2P_NOA_DESCRIPTOR,
+	WMI_TLV_TAG_STRUCT_P2P_GO_SET_BEACON_IE,
+	WMI_TLV_TAG_STRUCT_GTK_OFFLOAD_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_UP_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_STOP_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_DOWN_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_SET_PARAM_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_CREATE_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_DELETE_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_FLUSH_TIDS_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_SET_PARAM_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_ASSOC_COMPLETE_CMD,
+	WMI_TLV_TAG_STRUCT_VHT_RATE_SET,
+	WMI_TLV_TAG_STRUCT_BCN_TMPL_CMD,
+	WMI_TLV_TAG_STRUCT_PRB_TMPL_CMD,
+	WMI_TLV_TAG_STRUCT_BCN_PRB_INFO,
+	WMI_TLV_TAG_STRUCT_PEER_TID_ADDBA_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_TID_DELBA_CMD,
+	WMI_TLV_TAG_STRUCT_STA_POWERSAVE_MODE_CMD,
+	WMI_TLV_TAG_STRUCT_STA_POWERSAVE_PARAM_CMD,
+	WMI_TLV_TAG_STRUCT_STA_DTIM_PS_METHOD_CMD,
+	WMI_TLV_TAG_STRUCT_ROAM_SCAN_MODE,
+	WMI_TLV_TAG_STRUCT_ROAM_SCAN_RSSI_THRESHOLD,
+	WMI_TLV_TAG_STRUCT_ROAM_SCAN_PERIOD,
+	WMI_TLV_TAG_STRUCT_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+	WMI_TLV_TAG_STRUCT_PDEV_SUSPEND_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_RESUME_CMD,
+	WMI_TLV_TAG_STRUCT_ADD_BCN_FILTER_CMD,
+	WMI_TLV_TAG_STRUCT_RMV_BCN_FILTER_CMD,
+	WMI_TLV_TAG_STRUCT_WOW_ENABLE_CMD,
+	WMI_TLV_TAG_STRUCT_WOW_HOSTWAKEUP_FROM_SLEEP_CMD,
+	WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_CMD,
+	WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_PARAM,
+	WMI_TLV_TAG_STRUCT_SET_ARP_NS_OFFLOAD_CMD,
+	WMI_TLV_TAG_STRUCT_ARP_OFFLOAD_TUPLE,
+	WMI_TLV_TAG_STRUCT_NS_OFFLOAD_TUPLE,
+	WMI_TLV_TAG_STRUCT_FTM_INTG_CMD,
+	WMI_TLV_TAG_STRUCT_STA_KEEPALIVE_CMD,
+	WMI_TLV_TAG_STRUCT_STA_KEEPALVE_ARP_RESPONSE,
+	WMI_TLV_TAG_STRUCT_P2P_SET_VENDOR_IE_DATA_CMD,
+	WMI_TLV_TAG_STRUCT_AP_PS_PEER_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_RATE_RETRY_SCHED_CMD,
+	WMI_TLV_TAG_STRUCT_WLAN_PROFILE_TRIGGER_CMD,
+	WMI_TLV_TAG_STRUCT_WLAN_PROFILE_SET_HIST_INTVL_CMD,
+	WMI_TLV_TAG_STRUCT_WLAN_PROFILE_GET_PROF_DATA_CMD,
+	WMI_TLV_TAG_STRUCT_WLAN_PROFILE_ENABLE_PROFILE_ID_CMD,
+	WMI_TLV_TAG_STRUCT_WOW_DEL_PATTERN_CMD,
+	WMI_TLV_TAG_STRUCT_WOW_ADD_DEL_EVT_CMD,
+	WMI_TLV_TAG_STRUCT_RTT_MEASREQ_HEAD,
+	WMI_TLV_TAG_STRUCT_RTT_MEASREQ_BODY,
+	WMI_TLV_TAG_STRUCT_RTT_TSF_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_SPECTRAL_CONFIGURE_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_SPECTRAL_ENABLE_CMD,
+	WMI_TLV_TAG_STRUCT_REQUEST_STATS_CMD,
+	WMI_TLV_TAG_STRUCT_NLO_CONFIG_CMD,
+	WMI_TLV_TAG_STRUCT_NLO_CONFIGURED_PARAMETERS,
+	WMI_TLV_TAG_STRUCT_CSA_OFFLOAD_ENABLE_CMD,
+	WMI_TLV_TAG_STRUCT_CSA_OFFLOAD_CHANSWITCH_CMD,
+	WMI_TLV_TAG_STRUCT_CHATTER_SET_MODE_CMD,
+	WMI_TLV_TAG_STRUCT_ECHO_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_SET_KEEPALIVE_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_GET_KEEPALIVE_CMD,
+	WMI_TLV_TAG_STRUCT_FORCE_FW_HANG_CMD,
+	WMI_TLV_TAG_STRUCT_GPIO_CONFIG_CMD,
+	WMI_TLV_TAG_STRUCT_GPIO_OUTPUT_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_ADD_WDS_ENTRY_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_REMOVE_WDS_ENTRY_CMD,
+	WMI_TLV_TAG_STRUCT_BCN_TX_HDR,
+	WMI_TLV_TAG_STRUCT_BCN_SEND_FROM_HOST_CMD,
+	WMI_TLV_TAG_STRUCT_MGMT_TX_HDR,
+	WMI_TLV_TAG_STRUCT_ADDBA_CLEAR_RESP_CMD,
+	WMI_TLV_TAG_STRUCT_ADDBA_SEND_CMD,
+	WMI_TLV_TAG_STRUCT_DELBA_SEND_CMD,
+	WMI_TLV_TAG_STRUCT_ADDBA_SETRESPONSE_CMD,
+	WMI_TLV_TAG_STRUCT_SEND_SINGLEAMSDU_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_ENABLE_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_DISABLE_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_SET_HT_IE_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_SET_VHT_IE_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_SET_DSCP_TID_MAP_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_GREEN_AP_PS_ENABLE_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_GET_TPC_CONFIG_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_SET_BASE_MACADDR_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_MCAST_GROUP_CMD,
+	WMI_TLV_TAG_STRUCT_ROAM_AP_PROFILE,
+	WMI_TLV_TAG_STRUCT_AP_PROFILE,
+	WMI_TLV_TAG_STRUCT_SCAN_SCH_PRIORITY_TABLE_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_DFS_ENABLE_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_DFS_DISABLE_CMD,
+	WMI_TLV_TAG_STRUCT_WOW_ADD_PATTERN_CMD,
+	WMI_TLV_TAG_STRUCT_WOW_BITMAP_PATTERN_T,
+	WMI_TLV_TAG_STRUCT_WOW_IPV4_SYNC_PATTERN_T,
+	WMI_TLV_TAG_STRUCT_WOW_IPV6_SYNC_PATTERN_T,
+	WMI_TLV_TAG_STRUCT_WOW_MAGIC_PATTERN_CMD,
+	WMI_TLV_TAG_STRUCT_SCAN_UPDATE_REQUEST_CMD,
+	WMI_TLV_TAG_STRUCT_CHATTER_PKT_COALESCING_FILTER,
+	WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_ADD_FILTER_CMD,
+	WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_DELETE_FILTER_CMD,
+	WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_QUERY_CMD,
+	WMI_TLV_TAG_STRUCT_TXBF_CMD,
+	WMI_TLV_TAG_STRUCT_DEBUG_LOG_CONFIG_CMD,
+	WMI_TLV_TAG_STRUCT_NLO_EVENT,
+	WMI_TLV_TAG_STRUCT_CHATTER_QUERY_REPLY_EVENT,
+	WMI_TLV_TAG_STRUCT_UPLOAD_H_HDR,
+	WMI_TLV_TAG_STRUCT_CAPTURE_H_EVENT_HDR,
+	WMI_TLV_TAG_STRUCT_VDEV_WNM_SLEEPMODE_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_WMM_ADDTS_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_WMM_DELTS_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_SET_WMM_PARAMS_CMD,
+	WMI_TLV_TAG_STRUCT_TDLS_SET_STATE_CMD,
+	WMI_TLV_TAG_STRUCT_TDLS_PEER_UPDATE_CMD,
+	WMI_TLV_TAG_STRUCT_TDLS_PEER_EVENT,
+	WMI_TLV_TAG_STRUCT_TDLS_PEER_CAPABILITIES,
+	WMI_TLV_TAG_STRUCT_VDEV_MCC_SET_TBTT_MODE_CMD,
+	WMI_TLV_TAG_STRUCT_ROAM_CHAN_LIST,
+	WMI_TLV_TAG_STRUCT_VDEV_MCC_BCN_INTVL_CHANGE_EVENT,
+	WMI_TLV_TAG_STRUCT_RESMGR_ADAPTIVE_OCS_CMD,
+	WMI_TLV_TAG_STRUCT_RESMGR_SET_CHAN_TIME_QUOTA_CMD,
+	WMI_TLV_TAG_STRUCT_RESMGR_SET_CHAN_LATENCY_CMD,
+	WMI_TLV_TAG_STRUCT_BA_REQ_SSN_CMD,
+	WMI_TLV_TAG_STRUCT_BA_RSP_SSN_EVENT,
+	WMI_TLV_TAG_STRUCT_STA_SMPS_FORCE_MODE_CMD,
+	WMI_TLV_TAG_STRUCT_SET_MCASTBCAST_FILTER_CMD,
+	WMI_TLV_TAG_STRUCT_P2P_SET_OPPPS_CMD,
+	WMI_TLV_TAG_STRUCT_P2P_SET_NOA_CMD,
+	WMI_TLV_TAG_STRUCT_BA_REQ_SSN_CMD_SUB_STRUCT_PARAM,
+	WMI_TLV_TAG_STRUCT_BA_REQ_SSN_EVENT_SUB_STRUCT_PARAM,
+	WMI_TLV_TAG_STRUCT_STA_SMPS_PARAM_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_SET_GTX_PARAMS_CMD,
+	WMI_TLV_TAG_STRUCT_MCC_SCHED_TRAFFIC_STATS_CMD,
+	WMI_TLV_TAG_STRUCT_MCC_SCHED_STA_TRAFFIC_STATS,
+	WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT,
+	WMI_TLV_TAG_STRUCT_P2P_NOA_EVENT,
+	WMI_TLV_TAG_STRUCT_HB_SET_ENABLE_CMD,
+	WMI_TLV_TAG_STRUCT_HB_SET_TCP_PARAMS_CMD,
+	WMI_TLV_TAG_STRUCT_HB_SET_TCP_PKT_FILTER_CMD,
+	WMI_TLV_TAG_STRUCT_HB_SET_UDP_PARAMS_CMD,
+	WMI_TLV_TAG_STRUCT_HB_SET_UDP_PKT_FILTER_CMD,
+	WMI_TLV_TAG_STRUCT_HB_IND_EVENT,
+	WMI_TLV_TAG_STRUCT_TX_PAUSE_EVENT,
+	WMI_TLV_TAG_STRUCT_RFKILL_EVENT,
+	WMI_TLV_TAG_STRUCT_DFS_RADAR_EVENT,
+	WMI_TLV_TAG_STRUCT_DFS_PHYERR_FILTER_ENA_CMD,
+	WMI_TLV_TAG_STRUCT_DFS_PHYERR_FILTER_DIS_CMD,
+	WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_SCAN_LIST,
+	WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_NETWORK_INFO,
+	WMI_TLV_TAG_STRUCT_BATCH_SCAN_ENABLE_CMD,
+	WMI_TLV_TAG_STRUCT_BATCH_SCAN_DISABLE_CMD,
+	WMI_TLV_TAG_STRUCT_BATCH_SCAN_TRIGGER_RESULT_CMD,
+	WMI_TLV_TAG_STRUCT_BATCH_SCAN_ENABLED_EVENT,
+	WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_EVENT,
+	WMI_TLV_TAG_STRUCT_VDEV_PLMREQ_START_CMD,
+	WMI_TLV_TAG_STRUCT_VDEV_PLMREQ_STOP_CMD,
+	WMI_TLV_TAG_STRUCT_THERMAL_MGMT_CMD,
+	WMI_TLV_TAG_STRUCT_THERMAL_MGMT_EVENT,
+	WMI_TLV_TAG_STRUCT_PEER_INFO_REQ_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_INFO_EVENT,
+	WMI_TLV_TAG_STRUCT_PEER_INFO,
+	WMI_TLV_TAG_STRUCT_PEER_TX_FAIL_CNT_THR_EVENT,
+	WMI_TLV_TAG_STRUCT_RMC_SET_MODE_CMD,
+	WMI_TLV_TAG_STRUCT_RMC_SET_ACTION_PERIOD_CMD,
+	WMI_TLV_TAG_STRUCT_RMC_CONFIG_CMD,
+	WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_SET_MODE_CMD,
+	WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_PLUMB_ROUTING_TABLE_CMD,
+	WMI_TLV_TAG_STRUCT_ADD_PROACTIVE_ARP_RSP_PATTERN_CMD,
+	WMI_TLV_TAG_STRUCT_DEL_PROACTIVE_ARP_RSP_PATTERN_CMD,
+	WMI_TLV_TAG_STRUCT_NAN_CMD_PARAM,
+	WMI_TLV_TAG_STRUCT_NAN_EVENT_HDR,
+	WMI_TLV_TAG_STRUCT_PDEV_L1SS_TRACK_EVENT,
+	WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT,
+	WMI_TLV_TAG_STRUCT_MODEM_POWER_STATE_CMD_PARAM,
+	WMI_TLV_TAG_STRUCT_PEER_GET_ESTIMATED_LINKSPEED_CMD,
+	WMI_TLV_TAG_STRUCT_PEER_ESTIMATED_LINKSPEED_EVENT,
+	WMI_TLV_TAG_STRUCT_AGGR_STATE_TRIG_EVENT,
+	WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_ROUTING_TABLE_ENTRY,
+	WMI_TLV_TAG_STRUCT_ROAM_SCAN_CMD,
+	WMI_TLV_TAG_STRUCT_REQ_STATS_EXT_CMD,
+	WMI_TLV_TAG_STRUCT_STATS_EXT_EVENT,
+	WMI_TLV_TAG_STRUCT_OBSS_SCAN_ENABLE_CMD,
+	WMI_TLV_TAG_STRUCT_OBSS_SCAN_DISABLE_CMD,
+	WMI_TLV_TAG_STRUCT_OFFLOAD_PRB_RSP_TX_STATUS_EVENT,
+	WMI_TLV_TAG_STRUCT_PDEV_SET_LED_CONFIG_CMD,
+	WMI_TLV_TAG_STRUCT_HOST_AUTO_SHUTDOWN_CFG_CMD,
+	WMI_TLV_TAG_STRUCT_HOST_AUTO_SHUTDOWN_EVENT,
+	WMI_TLV_TAG_STRUCT_UPDATE_WHAL_MIB_STATS_EVENT,
+	WMI_TLV_TAG_STRUCT_CHAN_AVOID_UPDATE_CMD_PARAM,
+	WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_PKT_PATTERN_T,
+	WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_TMR_PATTERN_T,
+	WMI_TLV_TAG_STRUCT_WOW_IOAC_ADD_KEEPALIVE_CMD,
+	WMI_TLV_TAG_STRUCT_WOW_IOAC_DEL_KEEPALIVE_CMD,
+	WMI_TLV_TAG_STRUCT_WOW_IOAC_KEEPALIVE_T,
+	WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_ADD_PATTERN_CMD,
+	WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_DEL_PATTERN_CMD,
+	WMI_TLV_TAG_STRUCT_START_LINK_STATS_CMD,
+	WMI_TLV_TAG_STRUCT_CLEAR_LINK_STATS_CMD,
+	WMI_TLV_TAG_STRUCT_REQUEST_LINK_STATS_CMD,
+	WMI_TLV_TAG_STRUCT_IFACE_LINK_STATS_EVENT,
+	WMI_TLV_TAG_STRUCT_RADIO_LINK_STATS_EVENT,
+	WMI_TLV_TAG_STRUCT_PEER_STATS_EVENT,
+	WMI_TLV_TAG_STRUCT_CHANNEL_STATS,
+	WMI_TLV_TAG_STRUCT_RADIO_LINK_STATS,
+	WMI_TLV_TAG_STRUCT_RATE_STATS,
+	WMI_TLV_TAG_STRUCT_PEER_LINK_STATS,
+	WMI_TLV_TAG_STRUCT_WMM_AC_STATS,
+	WMI_TLV_TAG_STRUCT_IFACE_LINK_STATS,
+	WMI_TLV_TAG_STRUCT_LPI_MGMT_SNOOPING_CONFIG_CMD,
+	WMI_TLV_TAG_STRUCT_LPI_START_SCAN_CMD,
+	WMI_TLV_TAG_STRUCT_LPI_STOP_SCAN_CMD,
+	WMI_TLV_TAG_STRUCT_LPI_RESULT_EVENT,
+	WMI_TLV_TAG_STRUCT_PEER_STATE_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_BUCKET_CMD,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_BUCKET_CHANNEL_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_START_CMD,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_STOP_CMD,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMD,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_BSSID_PARAM_CMD,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMD,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_GET_CACHED_RESULTS_CMD,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMD,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_SET_CAPABILITIES_CMD,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_GET_CAPABILITIES_CMD,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_OPERATION_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_START_STOP_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_TABLE_USAGE_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_DESCRIPTOR_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_RSSI_INFO_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_CACHED_RESULTS_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_RESULTS_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_RESULT_BSSID_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_HOTLIST_MATCH_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_CAPABILITIES_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_CACHE_CAPABILITIES_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_MONITOR_CAPABILITIES_EVENT,
+	WMI_TLV_TAG_STRUCT_EXTSCAN_HOTLIST_MONITOR_CAPABILITIES_EVENT,
+	WMI_TLV_TAG_STRUCT_D0_WOW_ENABLE_DISABLE_CMD,
+	WMI_TLV_TAG_STRUCT_D0_WOW_DISABLE_ACK_EVENT,
+	WMI_TLV_TAG_STRUCT_UNIT_TEST_CMD,
+	WMI_TLV_TAG_STRUCT_ROAM_OFFLOAD_TLV_PARAM,
+	WMI_TLV_TAG_STRUCT_ROAM_11I_OFFLOAD_TLV_PARAM,
+	WMI_TLV_TAG_STRUCT_ROAM_11R_OFFLOAD_TLV_PARAM,
+	WMI_TLV_TAG_STRUCT_ROAM_ESE_OFFLOAD_TLV_PARAM,
+	WMI_TLV_TAG_STRUCT_ROAM_SYNCH_EVENT,
+	WMI_TLV_TAG_STRUCT_ROAM_SYNCH_COMPLETE,
+	WMI_TLV_TAG_STRUCT_EXTWOW_ENABLE_CMD,
+	WMI_TLV_TAG_STRUCT_EXTWOW_SET_APP_TYPE1_PARAMS_CMD,
+	WMI_TLV_TAG_STRUCT_EXTWOW_SET_APP_TYPE2_PARAMS_CMD,
+	WMI_TLV_TAG_STRUCT_LPI_STATUS_EVENT,
+	WMI_TLV_TAG_STRUCT_LPI_HANDOFF_EVENT,
+	WMI_TLV_TAG_STRUCT_VDEV_RATE_STATS_EVENT,
+	WMI_TLV_TAG_STRUCT_VDEV_RATE_HT_INFO,
+	WMI_TLV_TAG_STRUCT_RIC_REQUEST,
+	WMI_TLV_TAG_STRUCT_PDEV_GET_TEMPERATURE_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_TEMPERATURE_EVENT,
+	WMI_TLV_TAG_STRUCT_SET_DHCP_SERVER_OFFLOAD_CMD,
+	WMI_TLV_TAG_STRUCT_TPC_CHAINMASK_CONFIG_CMD,
+	WMI_TLV_TAG_STRUCT_RIC_TSPEC,
+	WMI_TLV_TAG_STRUCT_TPC_CHAINMASK_CONFIG,
+	WMI_TLV_TAG_STRUCT_IPA_OFFLOAD_CMD,
+	WMI_TLV_TAG_STRUCT_SCAN_PROB_REQ_OUI_CMD,
+	WMI_TLV_TAG_STRUCT_KEY_MATERIAL,
+	WMI_TLV_TAG_STRUCT_TDLS_SET_OFFCHAN_MODE_CMD,
+	WMI_TLV_TAG_STRUCT_SET_LED_FLASHING_CMD,
+	WMI_TLV_TAG_STRUCT_MDNS_OFFLOAD_CMD,
+	WMI_TLV_TAG_STRUCT_MDNS_SET_FQDN_CMD,
+	WMI_TLV_TAG_STRUCT_MDNS_SET_RESP_CMD,
+	WMI_TLV_TAG_STRUCT_MDNS_GET_STATS_CMD,
+	WMI_TLV_TAG_STRUCT_MDNS_STATS_EVENT,
+	WMI_TLV_TAG_STRUCT_ROAM_INVOKE_CMD,
+	WMI_TLV_TAG_STRUCT_PDEV_RESUME_EVENT,
+	WMI_TLV_TAG_STRUCT_PDEV_SET_ANTENNA_DIVERSITY_CMD,
+	WMI_TLV_TAG_STRUCT_SAP_OFL_ENABLE_CMD,
+	WMI_TLV_TAG_STRUCT_SAP_OFL_ADD_STA_EVENT,
+	WMI_TLV_TAG_STRUCT_SAP_OFL_DEL_STA_EVENT,
+	WMI_TLV_TAG_STRUCT_APFIND_CMD_PARAM,
+	WMI_TLV_TAG_STRUCT_APFIND_EVENT_HDR,
+
+	WMI_TLV_TAG_MAX
+};
+
+enum wmi_tlv_service {
+	WMI_TLV_SERVICE_BEACON_OFFLOAD = 0,
+	WMI_TLV_SERVICE_SCAN_OFFLOAD,
+	WMI_TLV_SERVICE_ROAM_SCAN_OFFLOAD,
+	WMI_TLV_SERVICE_BCN_MISS_OFFLOAD,
+	WMI_TLV_SERVICE_STA_PWRSAVE,
+	WMI_TLV_SERVICE_STA_ADVANCED_PWRSAVE,
+	WMI_TLV_SERVICE_AP_UAPSD,
+	WMI_TLV_SERVICE_AP_DFS,
+	WMI_TLV_SERVICE_11AC,
+	WMI_TLV_SERVICE_BLOCKACK,
+	WMI_TLV_SERVICE_PHYERR,
+	WMI_TLV_SERVICE_BCN_FILTER,
+	WMI_TLV_SERVICE_RTT,
+	WMI_TLV_SERVICE_WOW,
+	WMI_TLV_SERVICE_RATECTRL_CACHE,
+	WMI_TLV_SERVICE_IRAM_TIDS,
+	WMI_TLV_SERVICE_ARPNS_OFFLOAD,
+	WMI_TLV_SERVICE_NLO,
+	WMI_TLV_SERVICE_GTK_OFFLOAD,
+	WMI_TLV_SERVICE_SCAN_SCH,
+	WMI_TLV_SERVICE_CSA_OFFLOAD,
+	WMI_TLV_SERVICE_CHATTER,
+	WMI_TLV_SERVICE_COEX_FREQAVOID,
+	WMI_TLV_SERVICE_PACKET_POWER_SAVE,
+	WMI_TLV_SERVICE_FORCE_FW_HANG,
+	WMI_TLV_SERVICE_GPIO,
+	WMI_TLV_SERVICE_STA_DTIM_PS_MODULATED_DTIM,
+	WMI_TLV_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG,
+	WMI_TLV_SERVICE_STA_UAPSD_VAR_AUTO_TRIG,
+	WMI_TLV_SERVICE_STA_KEEP_ALIVE,
+	WMI_TLV_SERVICE_TX_ENCAP,
+	WMI_TLV_SERVICE_AP_PS_DETECT_OUT_OF_SYNC,
+	WMI_TLV_SERVICE_EARLY_RX,
+	WMI_TLV_SERVICE_STA_SMPS,
+	WMI_TLV_SERVICE_FWTEST,
+	WMI_TLV_SERVICE_STA_WMMAC,
+	WMI_TLV_SERVICE_TDLS,
+	WMI_TLV_SERVICE_BURST,
+	WMI_TLV_SERVICE_MCC_BCN_INTERVAL_CHANGE,
+	WMI_TLV_SERVICE_ADAPTIVE_OCS,
+	WMI_TLV_SERVICE_BA_SSN_SUPPORT,
+	WMI_TLV_SERVICE_FILTER_IPSEC_NATKEEPALIVE,
+	WMI_TLV_SERVICE_WLAN_HB,
+	WMI_TLV_SERVICE_LTE_ANT_SHARE_SUPPORT,
+	WMI_TLV_SERVICE_BATCH_SCAN,
+	WMI_TLV_SERVICE_QPOWER,
+	WMI_TLV_SERVICE_PLMREQ,
+	WMI_TLV_SERVICE_THERMAL_MGMT,
+	WMI_TLV_SERVICE_RMC,
+	WMI_TLV_SERVICE_MHF_OFFLOAD,
+	WMI_TLV_SERVICE_COEX_SAR,
+	WMI_TLV_SERVICE_BCN_TXRATE_OVERRIDE,
+	WMI_TLV_SERVICE_NAN,
+	WMI_TLV_SERVICE_L1SS_STAT,
+	WMI_TLV_SERVICE_ESTIMATE_LINKSPEED,
+	WMI_TLV_SERVICE_OBSS_SCAN,
+	WMI_TLV_SERVICE_TDLS_OFFCHAN,
+	WMI_TLV_SERVICE_TDLS_UAPSD_BUFFER_STA,
+	WMI_TLV_SERVICE_TDLS_UAPSD_SLEEP_STA,
+	WMI_TLV_SERVICE_IBSS_PWRSAVE,
+	WMI_TLV_SERVICE_LPASS,
+	WMI_TLV_SERVICE_EXTSCAN,
+	WMI_TLV_SERVICE_D0WOW,
+	WMI_TLV_SERVICE_HSOFFLOAD,
+	WMI_TLV_SERVICE_ROAM_HO_OFFLOAD,
+	WMI_TLV_SERVICE_RX_FULL_REORDER,
+	WMI_TLV_SERVICE_DHCP_OFFLOAD,
+	WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT,
+	WMI_TLV_SERVICE_MDNS_OFFLOAD,
+	WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD,
+};
+
+#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \
+	((svc_id) < (len) && \
+	 __le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
+	 BIT((svc_id)%(sizeof(u32))))
+
+#define SVCMAP(x, y, len) \
+	do { \
+		if (WMI_SERVICE_IS_ENABLED((in), (x), (len))) \
+			__set_bit(y, out); \
+	} while (0)
+
+static inline void
+wmi_tlv_svc_map(const __le32 *in, unsigned long *out, size_t len)
+{
+	SVCMAP(WMI_TLV_SERVICE_BEACON_OFFLOAD,
+	       WMI_SERVICE_BEACON_OFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_SCAN_OFFLOAD,
+	       WMI_SERVICE_SCAN_OFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_ROAM_SCAN_OFFLOAD,
+	       WMI_SERVICE_ROAM_SCAN_OFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_BCN_MISS_OFFLOAD,
+	       WMI_SERVICE_BCN_MISS_OFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_STA_PWRSAVE,
+	       WMI_SERVICE_STA_PWRSAVE, len);
+	SVCMAP(WMI_TLV_SERVICE_STA_ADVANCED_PWRSAVE,
+	       WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
+	SVCMAP(WMI_TLV_SERVICE_AP_UAPSD,
+	       WMI_SERVICE_AP_UAPSD, len);
+	SVCMAP(WMI_TLV_SERVICE_AP_DFS,
+	       WMI_SERVICE_AP_DFS, len);
+	SVCMAP(WMI_TLV_SERVICE_11AC,
+	       WMI_SERVICE_11AC, len);
+	SVCMAP(WMI_TLV_SERVICE_BLOCKACK,
+	       WMI_SERVICE_BLOCKACK, len);
+	SVCMAP(WMI_TLV_SERVICE_PHYERR,
+	       WMI_SERVICE_PHYERR, len);
+	SVCMAP(WMI_TLV_SERVICE_BCN_FILTER,
+	       WMI_SERVICE_BCN_FILTER, len);
+	SVCMAP(WMI_TLV_SERVICE_RTT,
+	       WMI_SERVICE_RTT, len);
+	SVCMAP(WMI_TLV_SERVICE_WOW,
+	       WMI_SERVICE_WOW, len);
+	SVCMAP(WMI_TLV_SERVICE_RATECTRL_CACHE,
+	       WMI_SERVICE_RATECTRL_CACHE, len);
+	SVCMAP(WMI_TLV_SERVICE_IRAM_TIDS,
+	       WMI_SERVICE_IRAM_TIDS, len);
+	SVCMAP(WMI_TLV_SERVICE_ARPNS_OFFLOAD,
+	       WMI_SERVICE_ARPNS_OFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_NLO,
+	       WMI_SERVICE_NLO, len);
+	SVCMAP(WMI_TLV_SERVICE_GTK_OFFLOAD,
+	       WMI_SERVICE_GTK_OFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_SCAN_SCH,
+	       WMI_SERVICE_SCAN_SCH, len);
+	SVCMAP(WMI_TLV_SERVICE_CSA_OFFLOAD,
+	       WMI_SERVICE_CSA_OFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_CHATTER,
+	       WMI_SERVICE_CHATTER, len);
+	SVCMAP(WMI_TLV_SERVICE_COEX_FREQAVOID,
+	       WMI_SERVICE_COEX_FREQAVOID, len);
+	SVCMAP(WMI_TLV_SERVICE_PACKET_POWER_SAVE,
+	       WMI_SERVICE_PACKET_POWER_SAVE, len);
+	SVCMAP(WMI_TLV_SERVICE_FORCE_FW_HANG,
+	       WMI_SERVICE_FORCE_FW_HANG, len);
+	SVCMAP(WMI_TLV_SERVICE_GPIO,
+	       WMI_SERVICE_GPIO, len);
+	SVCMAP(WMI_TLV_SERVICE_STA_DTIM_PS_MODULATED_DTIM,
+	       WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, len);
+	SVCMAP(WMI_TLV_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG,
+	       WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, len);
+	SVCMAP(WMI_TLV_SERVICE_STA_UAPSD_VAR_AUTO_TRIG,
+	       WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, len);
+	SVCMAP(WMI_TLV_SERVICE_STA_KEEP_ALIVE,
+	       WMI_SERVICE_STA_KEEP_ALIVE, len);
+	SVCMAP(WMI_TLV_SERVICE_TX_ENCAP,
+	       WMI_SERVICE_TX_ENCAP, len);
+	SVCMAP(WMI_TLV_SERVICE_AP_PS_DETECT_OUT_OF_SYNC,
+	       WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC, len);
+	SVCMAP(WMI_TLV_SERVICE_EARLY_RX,
+	       WMI_SERVICE_EARLY_RX, len);
+	SVCMAP(WMI_TLV_SERVICE_STA_SMPS,
+	       WMI_SERVICE_STA_SMPS, len);
+	SVCMAP(WMI_TLV_SERVICE_FWTEST,
+	       WMI_SERVICE_FWTEST, len);
+	SVCMAP(WMI_TLV_SERVICE_STA_WMMAC,
+	       WMI_SERVICE_STA_WMMAC, len);
+	SVCMAP(WMI_TLV_SERVICE_TDLS,
+	       WMI_SERVICE_TDLS, len);
+	SVCMAP(WMI_TLV_SERVICE_BURST,
+	       WMI_SERVICE_BURST, len);
+	SVCMAP(WMI_TLV_SERVICE_MCC_BCN_INTERVAL_CHANGE,
+	       WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE, len);
+	SVCMAP(WMI_TLV_SERVICE_ADAPTIVE_OCS,
+	       WMI_SERVICE_ADAPTIVE_OCS, len);
+	SVCMAP(WMI_TLV_SERVICE_BA_SSN_SUPPORT,
+	       WMI_SERVICE_BA_SSN_SUPPORT, len);
+	SVCMAP(WMI_TLV_SERVICE_FILTER_IPSEC_NATKEEPALIVE,
+	       WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE, len);
+	SVCMAP(WMI_TLV_SERVICE_WLAN_HB,
+	       WMI_SERVICE_WLAN_HB, len);
+	SVCMAP(WMI_TLV_SERVICE_LTE_ANT_SHARE_SUPPORT,
+	       WMI_SERVICE_LTE_ANT_SHARE_SUPPORT, len);
+	SVCMAP(WMI_TLV_SERVICE_BATCH_SCAN,
+	       WMI_SERVICE_BATCH_SCAN, len);
+	SVCMAP(WMI_TLV_SERVICE_QPOWER,
+	       WMI_SERVICE_QPOWER, len);
+	SVCMAP(WMI_TLV_SERVICE_PLMREQ,
+	       WMI_SERVICE_PLMREQ, len);
+	SVCMAP(WMI_TLV_SERVICE_THERMAL_MGMT,
+	       WMI_SERVICE_THERMAL_MGMT, len);
+	SVCMAP(WMI_TLV_SERVICE_RMC,
+	       WMI_SERVICE_RMC, len);
+	SVCMAP(WMI_TLV_SERVICE_MHF_OFFLOAD,
+	       WMI_SERVICE_MHF_OFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_COEX_SAR,
+	       WMI_SERVICE_COEX_SAR, len);
+	SVCMAP(WMI_TLV_SERVICE_BCN_TXRATE_OVERRIDE,
+	       WMI_SERVICE_BCN_TXRATE_OVERRIDE, len);
+	SVCMAP(WMI_TLV_SERVICE_NAN,
+	       WMI_SERVICE_NAN, len);
+	SVCMAP(WMI_TLV_SERVICE_L1SS_STAT,
+	       WMI_SERVICE_L1SS_STAT, len);
+	SVCMAP(WMI_TLV_SERVICE_ESTIMATE_LINKSPEED,
+	       WMI_SERVICE_ESTIMATE_LINKSPEED, len);
+	SVCMAP(WMI_TLV_SERVICE_OBSS_SCAN,
+	       WMI_SERVICE_OBSS_SCAN, len);
+	SVCMAP(WMI_TLV_SERVICE_TDLS_OFFCHAN,
+	       WMI_SERVICE_TDLS_OFFCHAN, len);
+	SVCMAP(WMI_TLV_SERVICE_TDLS_UAPSD_BUFFER_STA,
+	       WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, len);
+	SVCMAP(WMI_TLV_SERVICE_TDLS_UAPSD_SLEEP_STA,
+	       WMI_SERVICE_TDLS_UAPSD_SLEEP_STA, len);
+	SVCMAP(WMI_TLV_SERVICE_IBSS_PWRSAVE,
+	       WMI_SERVICE_IBSS_PWRSAVE, len);
+	SVCMAP(WMI_TLV_SERVICE_LPASS,
+	       WMI_SERVICE_LPASS, len);
+	SVCMAP(WMI_TLV_SERVICE_EXTSCAN,
+	       WMI_SERVICE_EXTSCAN, len);
+	SVCMAP(WMI_TLV_SERVICE_D0WOW,
+	       WMI_SERVICE_D0WOW, len);
+	SVCMAP(WMI_TLV_SERVICE_HSOFFLOAD,
+	       WMI_SERVICE_HSOFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_ROAM_HO_OFFLOAD,
+	       WMI_SERVICE_ROAM_HO_OFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_RX_FULL_REORDER,
+	       WMI_SERVICE_RX_FULL_REORDER, len);
+	SVCMAP(WMI_TLV_SERVICE_DHCP_OFFLOAD,
+	       WMI_SERVICE_DHCP_OFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT,
+	       WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT, len);
+	SVCMAP(WMI_TLV_SERVICE_MDNS_OFFLOAD,
+	       WMI_SERVICE_MDNS_OFFLOAD, len);
+	SVCMAP(WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD,
+	       WMI_SERVICE_SAP_AUTH_OFFLOAD, len);
+}
+
+#undef SVCMAP
+
+struct wmi_tlv {
+	__le16 len;
+	__le16 tag;
+	u8 value[0];
+} __packed;
+
+#define WMI_TLV_MGMT_RX_NUM_RSSI 4
+
+struct wmi_tlv_mgmt_rx_ev {
+	__le32 channel;
+	__le32 snr;
+	__le32 rate;
+	__le32 phy_mode;
+	__le32 buf_len;
+	__le32 status;
+	__le32 rssi[WMI_TLV_MGMT_RX_NUM_RSSI];
+} __packed;
+
+struct wmi_tlv_abi_version {
+	__le32 abi_ver0;
+	__le32 abi_ver1;
+	__le32 abi_ver_ns0;
+	__le32 abi_ver_ns1;
+	__le32 abi_ver_ns2;
+	__le32 abi_ver_ns3;
+} __packed;
+
+enum wmi_tlv_hw_bd_id {
+	WMI_TLV_HW_BD_LEGACY = 0,
+	WMI_TLV_HW_BD_QCA6174 = 1,
+	WMI_TLV_HW_BD_QCA2582 = 2,
+};
+
+struct wmi_tlv_hw_bd_info {
+	u8 rev;
+	u8 project_id;
+	u8 custom_id;
+	u8 reference_design_id;
+} __packed;
+
+struct wmi_tlv_svc_rdy_ev {
+	__le32 fw_build_vers;
+	struct wmi_tlv_abi_version abi;
+	__le32 phy_capability;
+	__le32 max_frag_entry;
+	__le32 num_rf_chains;
+	__le32 ht_cap_info;
+	__le32 vht_cap_info;
+	__le32 vht_supp_mcs;
+	__le32 hw_min_tx_power;
+	__le32 hw_max_tx_power;
+	__le32 sys_cap_info;
+	__le32 min_pkt_size_enable;
+	__le32 max_bcn_ie_size;
+	__le32 num_mem_reqs;
+	__le32 max_num_scan_chans;
+	__le32 hw_bd_id; /* 0 means hw_bd_info is invalid */
+	struct wmi_tlv_hw_bd_info hw_bd_info[5];
+} __packed;
+
+struct wmi_tlv_rdy_ev {
+	struct wmi_tlv_abi_version abi;
+	struct wmi_mac_addr mac_addr;
+	__le32 status;
+} __packed;
+
+struct wmi_tlv_resource_config {
+	__le32 num_vdevs;
+	__le32 num_peers;
+	__le32 num_offload_peers;
+	__le32 num_offload_reorder_bufs;
+	__le32 num_peer_keys;
+	__le32 num_tids;
+	__le32 ast_skid_limit;
+	__le32 tx_chain_mask;
+	__le32 rx_chain_mask;
+	__le32 rx_timeout_pri[4];
+	__le32 rx_decap_mode;
+	__le32 scan_max_pending_reqs;
+	__le32 bmiss_offload_max_vdev;
+	__le32 roam_offload_max_vdev;
+	__le32 roam_offload_max_ap_profiles;
+	__le32 num_mcast_groups;
+	__le32 num_mcast_table_elems;
+	__le32 mcast2ucast_mode;
+	__le32 tx_dbg_log_size;
+	__le32 num_wds_entries;
+	__le32 dma_burst_size;
+	__le32 mac_aggr_delim;
+	__le32 rx_skip_defrag_timeout_dup_detection_check;
+	__le32 vow_config;
+	__le32 gtk_offload_max_vdev;
+	__le32 num_msdu_desc;
+	__le32 max_frag_entries;
+	__le32 num_tdls_vdevs;
+	__le32 num_tdls_conn_table_entries;
+	__le32 beacon_tx_offload_max_vdev;
+	__le32 num_multicast_filter_entries;
+	__le32 num_wow_filters;
+	__le32 num_keep_alive_pattern;
+	__le32 keep_alive_pattern_size;
+	__le32 max_tdls_concurrent_sleep_sta;
+	__le32 max_tdls_concurrent_buffer_sta;
+} __packed;
+
+struct wmi_tlv_init_cmd {
+	struct wmi_tlv_abi_version abi;
+	__le32 num_host_mem_chunks;
+} __packed;
+
+struct wmi_tlv_pdev_set_param_cmd {
+	__le32 pdev_id; /* not used yet */
+	__le32 param_id;
+	__le32 param_value;
+} __packed;
+
+struct wmi_tlv_pdev_set_rd_cmd {
+	__le32 pdev_id; /* not used yet */
+	__le32 regd;
+	__le32 regd_2ghz;
+	__le32 regd_5ghz;
+	__le32 conform_limit_2ghz;
+	__le32 conform_limit_5ghz;
+} __packed;
+
+struct wmi_tlv_scan_chan_list_cmd {
+	__le32 num_scan_chans;
+} __packed;
+
+struct wmi_tlv_start_scan_cmd {
+	struct wmi_start_scan_common common;
+	__le32 burst_duration_ms;
+	__le32 num_channels;
+	__le32 num_bssids;
+	__le32 num_ssids;
+	__le32 ie_len;
+	__le32 num_probes;
+} __packed;
+
+struct wmi_tlv_vdev_start_cmd {
+	__le32 vdev_id;
+	__le32 requestor_id;
+	__le32 bcn_intval;
+	__le32 dtim_period;
+	__le32 flags;
+	struct wmi_ssid ssid;
+	__le32 bcn_tx_rate;
+	__le32 bcn_tx_power;
+	__le32 num_noa_descr;
+	__le32 disable_hw_ack;
+} __packed;
+
+enum {
+	WMI_TLV_PEER_TYPE_DEFAULT = 0, /* generic / non-BSS / self-peer */
+	WMI_TLV_PEER_TYPE_BSS = 1,
+	WMI_TLV_PEER_TYPE_TDLS = 2,
+	WMI_TLV_PEER_TYPE_HOST_MAX = 127,
+	WMI_TLV_PEER_TYPE_ROAMOFFLOAD_TMP = 128,
+};
+
+struct wmi_tlv_peer_create_cmd {
+	__le32 vdev_id;
+	struct wmi_mac_addr peer_addr;
+	__le32 peer_type;
+} __packed;
+
+struct wmi_tlv_peer_assoc_cmd {
+	struct wmi_mac_addr mac_addr;
+	__le32 vdev_id;
+	__le32 new_assoc;
+	__le32 assoc_id;
+	__le32 flags;
+	__le32 caps;
+	__le32 listen_intval;
+	__le32 ht_caps;
+	__le32 max_mpdu;
+	__le32 mpdu_density;
+	__le32 rate_caps;
+	__le32 nss;
+	__le32 vht_caps;
+	__le32 phy_mode;
+	__le32 ht_info[2];
+	__le32 num_legacy_rates;
+	__le32 num_ht_rates;
+} __packed;
+
+struct wmi_tlv_pdev_suspend {
+	__le32 pdev_id; /* not used yet */
+	__le32 opt;
+} __packed;
+
+struct wmi_tlv_pdev_set_wmm_cmd {
+	__le32 pdev_id; /* not used yet */
+	__le32 dg_type; /* no idea.. */
+} __packed;
+
+struct wmi_tlv_vdev_wmm_params {
+	__le32 dummy;
+	struct wmi_wmm_params params;
+} __packed;
+
+struct wmi_tlv_vdev_set_wmm_cmd {
+	__le32 vdev_id;
+	struct wmi_tlv_vdev_wmm_params vdev_wmm_params[4];
+} __packed;
+
+struct wmi_tlv_phyerr_ev {
+	__le32 num_phyerrs;
+	__le32 tsf_l32;
+	__le32 tsf_u32;
+	__le32 buf_len;
+} __packed;
+
+enum wmi_tlv_dbglog_param {
+	WMI_TLV_DBGLOG_PARAM_LOG_LEVEL = 1,
+	WMI_TLV_DBGLOG_PARAM_VDEV_ENABLE,
+	WMI_TLV_DBGLOG_PARAM_VDEV_DISABLE,
+	WMI_TLV_DBGLOG_PARAM_VDEV_ENABLE_BITMAP,
+	WMI_TLV_DBGLOG_PARAM_VDEV_DISABLE_BITMAP,
+};
+
+enum wmi_tlv_dbglog_log_level {
+	WMI_TLV_DBGLOG_LOG_LEVEL_VERBOSE = 0,
+	WMI_TLV_DBGLOG_LOG_LEVEL_INFO,
+	WMI_TLV_DBGLOG_LOG_LEVEL_INFO_LVL_1,
+	WMI_TLV_DBGLOG_LOG_LEVEL_INFO_LVL_2,
+	WMI_TLV_DBGLOG_LOG_LEVEL_WARN,
+	WMI_TLV_DBGLOG_LOG_LEVEL_ERR,
+};
+
+#define WMI_TLV_DBGLOG_BITMAP_MAX_IDS 512
+#define WMI_TLV_DBGLOG_BITMAP_MAX_WORDS (WMI_TLV_DBGLOG_BITMAP_MAX_IDS / \
+					 sizeof(__le32))
+#define WMI_TLV_DBGLOG_ALL_MODULES 0xffff
+#define WMI_TLV_DBGLOG_LOG_LEVEL_VALUE(module_id, log_level) \
+		(((module_id << 16) & 0xffff0000) | \
+		 ((log_level <<  0) & 0x000000ff))
+
+struct wmi_tlv_dbglog_cmd {
+	__le32 param;
+	__le32 value;
+} __packed;
+
+struct wmi_tlv_resume_cmd {
+	__le32 reserved;
+} __packed;
+
+struct wmi_tlv_req_stats_cmd {
+	__le32 stats_id; /* wmi_stats_id */
+	__le32 vdev_id;
+	struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+struct wmi_tlv_vdev_stats {
+	__le32 vdev_id;
+	__le32 beacon_snr;
+	__le32 data_snr;
+	__le32 num_tx_frames[4]; /* per-AC */
+	__le32 num_rx_frames;
+	__le32 num_tx_frames_retries[4];
+	__le32 num_tx_frames_failures[4];
+	__le32 num_rts_fail;
+	__le32 num_rts_success;
+	__le32 num_rx_err;
+	__le32 num_rx_discard;
+	__le32 num_tx_not_acked;
+	__le32 tx_rate_history[10];
+	__le32 beacon_rssi_history[10];
+} __packed;
+
+struct wmi_tlv_pktlog_enable {
+	__le32 reserved;
+	__le32 filter;
+} __packed;
+
+struct wmi_tlv_pktlog_disable {
+	__le32 reserved;
+} __packed;
+
+enum wmi_tlv_bcn_tx_status {
+	WMI_TLV_BCN_TX_STATUS_OK,
+	WMI_TLV_BCN_TX_STATUS_XRETRY,
+	WMI_TLV_BCN_TX_STATUS_DROP,
+	WMI_TLV_BCN_TX_STATUS_FILTERED,
+};
+
+struct wmi_tlv_bcn_tx_status_ev {
+	__le32 vdev_id;
+	__le32 tx_status;
+} __packed;
+
+struct wmi_tlv_bcn_prb_info {
+	__le32 caps;
+	__le32 erp;
+	u8 ies[0];
+} __packed;
+
+struct wmi_tlv_bcn_tmpl_cmd {
+	__le32 vdev_id;
+	__le32 tim_ie_offset;
+	__le32 buf_len;
+} __packed;
+
+struct wmi_tlv_prb_tmpl_cmd {
+	__le32 vdev_id;
+	__le32 buf_len;
+} __packed;
+
+struct wmi_tlv_p2p_go_bcn_ie {
+	__le32 vdev_id;
+	__le32 ie_len;
+} __packed;
+
+enum wmi_tlv_diag_item_type {
+	WMI_TLV_DIAG_ITEM_TYPE_FW_EVENT,
+	WMI_TLV_DIAG_ITEM_TYPE_FW_LOG,
+	WMI_TLV_DIAG_ITEM_TYPE_FW_DEBUG_MSG,
+};
+
+struct wmi_tlv_diag_item {
+	u8 type;
+	u8 reserved;
+	__le16 len;
+	__le32 timestamp;
+	__le32 code;
+	u8 payload[0];
+} __packed;
+
+struct wmi_tlv_diag_data_ev {
+	__le32 num_items;
+} __packed;
+
+struct wmi_tlv_sta_keepalive_cmd {
+	__le32 vdev_id;
+	__le32 enabled;
+	__le32 method; /* WMI_STA_KEEPALIVE_METHOD_ */
+	__le32 interval; /* in seconds */
+} __packed;
+
+struct wmi_tlv_stats_ev {
+	__le32 stats_id; /* WMI_STAT_ */
+	__le32 num_pdev_stats;
+	__le32 num_vdev_stats;
+	__le32 num_peer_stats;
+	__le32 num_bcnflt_stats;
+	__le32 num_chan_stats;
+} __packed;
+
+void ath10k_wmi_tlv_attach(struct ath10k *ar);
+
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 45e311c..c77f77d 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -22,8 +22,10 @@
 #include "htc.h"
 #include "debug.h"
 #include "wmi.h"
+#include "wmi-tlv.h"
 #include "mac.h"
 #include "testmode.h"
+#include "wmi-ops.h"
 
 /* MAIN WMI cmd track */
 static struct wmi_cmd_map wmi_cmd_map = {
@@ -143,6 +145,7 @@
 	.force_fw_hang_cmdid = WMI_FORCE_FW_HANG_CMDID,
 	.gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID,
 	.gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID,
+	.pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
 };
 
 /* 10.X WMI cmd track */
@@ -265,6 +268,129 @@
 	.force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
 	.gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID,
 	.gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID,
+	.pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
+};
+
+/* 10.2.4 WMI cmd track */
+static struct wmi_cmd_map wmi_10_2_4_cmd_map = {
+	.init_cmdid = WMI_10_2_INIT_CMDID,
+	.start_scan_cmdid = WMI_10_2_START_SCAN_CMDID,
+	.stop_scan_cmdid = WMI_10_2_STOP_SCAN_CMDID,
+	.scan_chan_list_cmdid = WMI_10_2_SCAN_CHAN_LIST_CMDID,
+	.scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED,
+	.pdev_set_regdomain_cmdid = WMI_10_2_PDEV_SET_REGDOMAIN_CMDID,
+	.pdev_set_channel_cmdid = WMI_10_2_PDEV_SET_CHANNEL_CMDID,
+	.pdev_set_param_cmdid = WMI_10_2_PDEV_SET_PARAM_CMDID,
+	.pdev_pktlog_enable_cmdid = WMI_10_2_PDEV_PKTLOG_ENABLE_CMDID,
+	.pdev_pktlog_disable_cmdid = WMI_10_2_PDEV_PKTLOG_DISABLE_CMDID,
+	.pdev_set_wmm_params_cmdid = WMI_10_2_PDEV_SET_WMM_PARAMS_CMDID,
+	.pdev_set_ht_cap_ie_cmdid = WMI_10_2_PDEV_SET_HT_CAP_IE_CMDID,
+	.pdev_set_vht_cap_ie_cmdid = WMI_10_2_PDEV_SET_VHT_CAP_IE_CMDID,
+	.pdev_set_quiet_mode_cmdid = WMI_10_2_PDEV_SET_QUIET_MODE_CMDID,
+	.pdev_green_ap_ps_enable_cmdid = WMI_10_2_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+	.pdev_get_tpc_config_cmdid = WMI_10_2_PDEV_GET_TPC_CONFIG_CMDID,
+	.pdev_set_base_macaddr_cmdid = WMI_10_2_PDEV_SET_BASE_MACADDR_CMDID,
+	.vdev_create_cmdid = WMI_10_2_VDEV_CREATE_CMDID,
+	.vdev_delete_cmdid = WMI_10_2_VDEV_DELETE_CMDID,
+	.vdev_start_request_cmdid = WMI_10_2_VDEV_START_REQUEST_CMDID,
+	.vdev_restart_request_cmdid = WMI_10_2_VDEV_RESTART_REQUEST_CMDID,
+	.vdev_up_cmdid = WMI_10_2_VDEV_UP_CMDID,
+	.vdev_stop_cmdid = WMI_10_2_VDEV_STOP_CMDID,
+	.vdev_down_cmdid = WMI_10_2_VDEV_DOWN_CMDID,
+	.vdev_set_param_cmdid = WMI_10_2_VDEV_SET_PARAM_CMDID,
+	.vdev_install_key_cmdid = WMI_10_2_VDEV_INSTALL_KEY_CMDID,
+	.peer_create_cmdid = WMI_10_2_PEER_CREATE_CMDID,
+	.peer_delete_cmdid = WMI_10_2_PEER_DELETE_CMDID,
+	.peer_flush_tids_cmdid = WMI_10_2_PEER_FLUSH_TIDS_CMDID,
+	.peer_set_param_cmdid = WMI_10_2_PEER_SET_PARAM_CMDID,
+	.peer_assoc_cmdid = WMI_10_2_PEER_ASSOC_CMDID,
+	.peer_add_wds_entry_cmdid = WMI_10_2_PEER_ADD_WDS_ENTRY_CMDID,
+	.peer_remove_wds_entry_cmdid = WMI_10_2_PEER_REMOVE_WDS_ENTRY_CMDID,
+	.peer_mcast_group_cmdid = WMI_10_2_PEER_MCAST_GROUP_CMDID,
+	.bcn_tx_cmdid = WMI_10_2_BCN_TX_CMDID,
+	.pdev_send_bcn_cmdid = WMI_10_2_PDEV_SEND_BCN_CMDID,
+	.bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
+	.bcn_filter_rx_cmdid = WMI_10_2_BCN_FILTER_RX_CMDID,
+	.prb_req_filter_rx_cmdid = WMI_10_2_PRB_REQ_FILTER_RX_CMDID,
+	.mgmt_tx_cmdid = WMI_10_2_MGMT_TX_CMDID,
+	.prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
+	.addba_clear_resp_cmdid = WMI_10_2_ADDBA_CLEAR_RESP_CMDID,
+	.addba_send_cmdid = WMI_10_2_ADDBA_SEND_CMDID,
+	.addba_status_cmdid = WMI_10_2_ADDBA_STATUS_CMDID,
+	.delba_send_cmdid = WMI_10_2_DELBA_SEND_CMDID,
+	.addba_set_resp_cmdid = WMI_10_2_ADDBA_SET_RESP_CMDID,
+	.send_singleamsdu_cmdid = WMI_10_2_SEND_SINGLEAMSDU_CMDID,
+	.sta_powersave_mode_cmdid = WMI_10_2_STA_POWERSAVE_MODE_CMDID,
+	.sta_powersave_param_cmdid = WMI_10_2_STA_POWERSAVE_PARAM_CMDID,
+	.sta_mimo_ps_mode_cmdid = WMI_10_2_STA_MIMO_PS_MODE_CMDID,
+	.pdev_dfs_enable_cmdid = WMI_10_2_PDEV_DFS_ENABLE_CMDID,
+	.pdev_dfs_disable_cmdid = WMI_10_2_PDEV_DFS_DISABLE_CMDID,
+	.roam_scan_mode = WMI_10_2_ROAM_SCAN_MODE,
+	.roam_scan_rssi_threshold = WMI_10_2_ROAM_SCAN_RSSI_THRESHOLD,
+	.roam_scan_period = WMI_10_2_ROAM_SCAN_PERIOD,
+	.roam_scan_rssi_change_threshold =
+				WMI_10_2_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+	.roam_ap_profile = WMI_10_2_ROAM_AP_PROFILE,
+	.ofl_scan_add_ap_profile = WMI_10_2_OFL_SCAN_ADD_AP_PROFILE,
+	.ofl_scan_remove_ap_profile = WMI_10_2_OFL_SCAN_REMOVE_AP_PROFILE,
+	.ofl_scan_period = WMI_10_2_OFL_SCAN_PERIOD,
+	.p2p_dev_set_device_info = WMI_10_2_P2P_DEV_SET_DEVICE_INFO,
+	.p2p_dev_set_discoverability = WMI_10_2_P2P_DEV_SET_DISCOVERABILITY,
+	.p2p_go_set_beacon_ie = WMI_10_2_P2P_GO_SET_BEACON_IE,
+	.p2p_go_set_probe_resp_ie = WMI_10_2_P2P_GO_SET_PROBE_RESP_IE,
+	.p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED,
+	.ap_ps_peer_param_cmdid = WMI_10_2_AP_PS_PEER_PARAM_CMDID,
+	.ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED,
+	.peer_rate_retry_sched_cmdid = WMI_10_2_PEER_RATE_RETRY_SCHED_CMDID,
+	.wlan_profile_trigger_cmdid = WMI_10_2_WLAN_PROFILE_TRIGGER_CMDID,
+	.wlan_profile_set_hist_intvl_cmdid =
+				WMI_10_2_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+	.wlan_profile_get_profile_data_cmdid =
+				WMI_10_2_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+	.wlan_profile_enable_profile_id_cmdid =
+				WMI_10_2_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+	.wlan_profile_list_profile_id_cmdid =
+				WMI_10_2_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+	.pdev_suspend_cmdid = WMI_10_2_PDEV_SUSPEND_CMDID,
+	.pdev_resume_cmdid = WMI_10_2_PDEV_RESUME_CMDID,
+	.add_bcn_filter_cmdid = WMI_10_2_ADD_BCN_FILTER_CMDID,
+	.rmv_bcn_filter_cmdid = WMI_10_2_RMV_BCN_FILTER_CMDID,
+	.wow_add_wake_pattern_cmdid = WMI_10_2_WOW_ADD_WAKE_PATTERN_CMDID,
+	.wow_del_wake_pattern_cmdid = WMI_10_2_WOW_DEL_WAKE_PATTERN_CMDID,
+	.wow_enable_disable_wake_event_cmdid =
+				WMI_10_2_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+	.wow_enable_cmdid = WMI_10_2_WOW_ENABLE_CMDID,
+	.wow_hostwakeup_from_sleep_cmdid =
+				WMI_10_2_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+	.rtt_measreq_cmdid = WMI_10_2_RTT_MEASREQ_CMDID,
+	.rtt_tsf_cmdid = WMI_10_2_RTT_TSF_CMDID,
+	.vdev_spectral_scan_configure_cmdid =
+				WMI_10_2_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
+	.vdev_spectral_scan_enable_cmdid =
+				WMI_10_2_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+	.request_stats_cmdid = WMI_10_2_REQUEST_STATS_CMDID,
+	.set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED,
+	.network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED,
+	.gtk_offload_cmdid = WMI_CMD_UNSUPPORTED,
+	.csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED,
+	.csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED,
+	.chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED,
+	.peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED,
+	.peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED,
+	.sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED,
+	.sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED,
+	.sta_keepalive_cmd = WMI_CMD_UNSUPPORTED,
+	.echo_cmdid = WMI_10_2_ECHO_CMDID,
+	.pdev_utf_cmdid = WMI_10_2_PDEV_UTF_CMDID,
+	.dbglog_cfg_cmdid = WMI_10_2_DBGLOG_CFG_CMDID,
+	.pdev_qvit_cmdid = WMI_10_2_PDEV_QVIT_CMDID,
+	.pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED,
+	.vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
+	.vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
+	.force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
+	.gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID,
+	.gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID,
+	.pdev_get_temperature_cmdid = WMI_10_2_PDEV_GET_TEMPERATURE_CMDID,
 };
 
 /* MAIN WMI VDEV param map */
@@ -385,6 +511,64 @@
 		WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
 };
 
+static struct wmi_vdev_param_map wmi_10_2_4_vdev_param_map = {
+	.rts_threshold = WMI_10X_VDEV_PARAM_RTS_THRESHOLD,
+	.fragmentation_threshold = WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+	.beacon_interval = WMI_10X_VDEV_PARAM_BEACON_INTERVAL,
+	.listen_interval = WMI_10X_VDEV_PARAM_LISTEN_INTERVAL,
+	.multicast_rate = WMI_10X_VDEV_PARAM_MULTICAST_RATE,
+	.mgmt_tx_rate = WMI_10X_VDEV_PARAM_MGMT_TX_RATE,
+	.slot_time = WMI_10X_VDEV_PARAM_SLOT_TIME,
+	.preamble = WMI_10X_VDEV_PARAM_PREAMBLE,
+	.swba_time = WMI_10X_VDEV_PARAM_SWBA_TIME,
+	.wmi_vdev_stats_update_period = WMI_10X_VDEV_STATS_UPDATE_PERIOD,
+	.wmi_vdev_pwrsave_ageout_time = WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME,
+	.wmi_vdev_host_swba_interval = WMI_10X_VDEV_HOST_SWBA_INTERVAL,
+	.dtim_period = WMI_10X_VDEV_PARAM_DTIM_PERIOD,
+	.wmi_vdev_oc_scheduler_air_time_limit =
+				WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+	.wds = WMI_10X_VDEV_PARAM_WDS,
+	.atim_window = WMI_10X_VDEV_PARAM_ATIM_WINDOW,
+	.bmiss_count_max = WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX,
+	.bmiss_first_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
+	.bmiss_final_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
+	.feature_wmm = WMI_10X_VDEV_PARAM_FEATURE_WMM,
+	.chwidth = WMI_10X_VDEV_PARAM_CHWIDTH,
+	.chextoffset = WMI_10X_VDEV_PARAM_CHEXTOFFSET,
+	.disable_htprotection = WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION,
+	.sta_quickkickout = WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT,
+	.mgmt_rate = WMI_10X_VDEV_PARAM_MGMT_RATE,
+	.protection_mode = WMI_10X_VDEV_PARAM_PROTECTION_MODE,
+	.fixed_rate = WMI_10X_VDEV_PARAM_FIXED_RATE,
+	.sgi = WMI_10X_VDEV_PARAM_SGI,
+	.ldpc = WMI_10X_VDEV_PARAM_LDPC,
+	.tx_stbc = WMI_10X_VDEV_PARAM_TX_STBC,
+	.rx_stbc = WMI_10X_VDEV_PARAM_RX_STBC,
+	.intra_bss_fwd = WMI_10X_VDEV_PARAM_INTRA_BSS_FWD,
+	.def_keyid = WMI_10X_VDEV_PARAM_DEF_KEYID,
+	.nss = WMI_10X_VDEV_PARAM_NSS,
+	.bcast_data_rate = WMI_10X_VDEV_PARAM_BCAST_DATA_RATE,
+	.mcast_data_rate = WMI_10X_VDEV_PARAM_MCAST_DATA_RATE,
+	.mcast_indicate = WMI_10X_VDEV_PARAM_MCAST_INDICATE,
+	.dhcp_indicate = WMI_10X_VDEV_PARAM_DHCP_INDICATE,
+	.unknown_dest_indicate = WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+	.ap_keepalive_min_idle_inactive_time_secs =
+		WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+	.ap_keepalive_max_idle_inactive_time_secs =
+		WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+	.ap_keepalive_max_unresponsive_time_secs =
+		WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+	.ap_enable_nawds = WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS,
+	.mcast2ucast_set = WMI_10X_VDEV_PARAM_MCAST2UCAST_SET,
+	.enable_rtscts = WMI_10X_VDEV_PARAM_ENABLE_RTSCTS,
+	.txbf = WMI_VDEV_PARAM_UNSUPPORTED,
+	.packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED,
+	.drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED,
+	.tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED,
+	.ap_detect_out_of_sync_sleeping_sta_time_secs =
+		WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
+};
+
 static struct wmi_pdev_param_map wmi_pdev_param_map = {
 	.tx_chain_mask = WMI_PDEV_PARAM_TX_CHAIN_MASK,
 	.rx_chain_mask = WMI_PDEV_PARAM_RX_CHAIN_MASK,
@@ -434,6 +618,7 @@
 	.fast_channel_reset = WMI_PDEV_PARAM_UNSUPPORTED,
 	.burst_dur = WMI_PDEV_PARAM_UNSUPPORTED,
 	.burst_enable = WMI_PDEV_PARAM_UNSUPPORTED,
+	.cal_period = WMI_PDEV_PARAM_UNSUPPORTED,
 };
 
 static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
@@ -486,6 +671,60 @@
 	.fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
 	.burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR,
 	.burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE,
+	.cal_period = WMI_10X_PDEV_PARAM_CAL_PERIOD,
+};
+
+static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
+	.tx_chain_mask = WMI_10X_PDEV_PARAM_TX_CHAIN_MASK,
+	.rx_chain_mask = WMI_10X_PDEV_PARAM_RX_CHAIN_MASK,
+	.txpower_limit2g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G,
+	.txpower_limit5g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G,
+	.txpower_scale = WMI_10X_PDEV_PARAM_TXPOWER_SCALE,
+	.beacon_gen_mode = WMI_10X_PDEV_PARAM_BEACON_GEN_MODE,
+	.beacon_tx_mode = WMI_10X_PDEV_PARAM_BEACON_TX_MODE,
+	.resmgr_offchan_mode = WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+	.protection_mode = WMI_10X_PDEV_PARAM_PROTECTION_MODE,
+	.dynamic_bw = WMI_10X_PDEV_PARAM_DYNAMIC_BW,
+	.non_agg_sw_retry_th = WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+	.agg_sw_retry_th = WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH,
+	.sta_kickout_th = WMI_10X_PDEV_PARAM_STA_KICKOUT_TH,
+	.ac_aggrsize_scaling = WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+	.ltr_enable = WMI_10X_PDEV_PARAM_LTR_ENABLE,
+	.ltr_ac_latency_be = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE,
+	.ltr_ac_latency_bk = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK,
+	.ltr_ac_latency_vi = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI,
+	.ltr_ac_latency_vo = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO,
+	.ltr_ac_latency_timeout = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+	.ltr_sleep_override = WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+	.ltr_rx_override = WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE,
+	.ltr_tx_activity_timeout = WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+	.l1ss_enable = WMI_10X_PDEV_PARAM_L1SS_ENABLE,
+	.dsleep_enable = WMI_10X_PDEV_PARAM_DSLEEP_ENABLE,
+	.pcielp_txbuf_flush = WMI_PDEV_PARAM_UNSUPPORTED,
+	.pcielp_txbuf_watermark = WMI_PDEV_PARAM_UNSUPPORTED,
+	.pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_UNSUPPORTED,
+	.pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_UNSUPPORTED,
+	.pdev_stats_update_period = WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+	.vdev_stats_update_period = WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+	.peer_stats_update_period = WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+	.bcnflt_stats_update_period =
+				WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+	.pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS,
+	.arp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
+	.dcs = WMI_10X_PDEV_PARAM_DCS,
+	.ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE,
+	.ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD,
+	.ani_listen_period = WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD,
+	.ani_ofdm_level = WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL,
+	.ani_cck_level = WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL,
+	.dyntxchain = WMI_10X_PDEV_PARAM_DYNTXCHAIN,
+	.proxy_sta = WMI_PDEV_PARAM_UNSUPPORTED,
+	.idle_ps_config = WMI_PDEV_PARAM_UNSUPPORTED,
+	.power_gating_sleep = WMI_PDEV_PARAM_UNSUPPORTED,
+	.fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
+	.burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR,
+	.burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE,
+	.cal_period = WMI_10X_PDEV_PARAM_CAL_PERIOD,
 };
 
 /* firmware 10.2 specific mappings */
@@ -607,11 +846,11 @@
 	.force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
 	.gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID,
 	.gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID,
+	.pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
 };
 
-static void
-ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
-			   const struct wmi_channel_arg *arg)
+void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
+				const struct wmi_channel_arg *arg)
 {
 	u32 flags = 0;
 
@@ -685,8 +924,8 @@
 	dev_kfree_skb(skb);
 }
 
-static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
-				      u32 cmd_id)
+int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
+			       u32 cmd_id)
 {
 	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
 	struct wmi_cmd_hdr *cmd_hdr;
@@ -717,23 +956,45 @@
 
 static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
 {
+	struct ath10k *ar = arvif->ar;
+	struct ath10k_skb_cb *cb;
+	struct sk_buff *bcn;
 	int ret;
 
-	lockdep_assert_held(&arvif->ar->data_lock);
+	spin_lock_bh(&ar->data_lock);
 
-	if (arvif->beacon == NULL)
-		return;
+	bcn = arvif->beacon;
 
-	if (arvif->beacon_sent)
-		return;
+	if (!bcn)
+		goto unlock;
 
-	ret = ath10k_wmi_beacon_send_ref_nowait(arvif);
-	if (ret)
-		return;
+	cb = ATH10K_SKB_CB(bcn);
 
-	/* We need to retain the arvif->beacon reference for DMA unmapping and
-	 * freeing the skbuff later. */
-	arvif->beacon_sent = true;
+	switch (arvif->beacon_state) {
+	case ATH10K_BEACON_SENDING:
+	case ATH10K_BEACON_SENT:
+		break;
+	case ATH10K_BEACON_SCHEDULED:
+		arvif->beacon_state = ATH10K_BEACON_SENDING;
+		spin_unlock_bh(&ar->data_lock);
+
+		ret = ath10k_wmi_beacon_send_ref_nowait(arvif->ar,
+							arvif->vdev_id,
+							bcn->data, bcn->len,
+							cb->paddr,
+							cb->bcn.dtim_zero,
+							cb->bcn.deliver_cab);
+
+		spin_lock_bh(&ar->data_lock);
+
+		if (ret == 0)
+			arvif->beacon_state = ATH10K_BEACON_SENT;
+		else
+			arvif->beacon_state = ATH10K_BEACON_SCHEDULED;
+	}
+
+unlock:
+	spin_unlock_bh(&ar->data_lock);
 }
 
 static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac,
@@ -746,12 +1007,10 @@
 
 static void ath10k_wmi_tx_beacons_nowait(struct ath10k *ar)
 {
-	spin_lock_bh(&ar->data_lock);
 	ieee80211_iterate_active_interfaces_atomic(ar->hw,
 						   IEEE80211_IFACE_ITER_NORMAL,
 						   ath10k_wmi_tx_beacons_iter,
 						   NULL);
-	spin_unlock_bh(&ar->data_lock);
 }
 
 static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar)
@@ -792,24 +1051,23 @@
 	return ret;
 }
 
-int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
+static struct sk_buff *
+ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
 {
-	int ret = 0;
 	struct wmi_mgmt_tx_cmd *cmd;
 	struct ieee80211_hdr *hdr;
-	struct sk_buff *wmi_skb;
-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct sk_buff *skb;
 	int len;
-	u32 buf_len = skb->len;
+	u32 buf_len = msdu->len;
 	u16 fc;
 
-	hdr = (struct ieee80211_hdr *)skb->data;
+	hdr = (struct ieee80211_hdr *)msdu->data;
 	fc = le16_to_cpu(hdr->frame_control);
 
 	if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control)))
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
-	len = sizeof(cmd->hdr) + skb->len;
+	len = sizeof(cmd->hdr) + msdu->len;
 
 	if ((ieee80211_is_action(hdr->frame_control) ||
 	     ieee80211_is_deauth(hdr->frame_control) ||
@@ -821,36 +1079,27 @@
 
 	len = round_up(len, 4);
 
-	wmi_skb = ath10k_wmi_alloc_skb(ar, len);
-	if (!wmi_skb)
-		return -ENOMEM;
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
 
-	cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data;
+	cmd = (struct wmi_mgmt_tx_cmd *)skb->data;
 
-	cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id);
+	cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(msdu)->vdev_id);
 	cmd->hdr.tx_rate = 0;
 	cmd->hdr.tx_power = 0;
 	cmd->hdr.buf_len = __cpu_to_le32(buf_len);
 
 	ether_addr_copy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr));
-	memcpy(cmd->buf, skb->data, skb->len);
+	memcpy(cmd->buf, msdu->data, msdu->len);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n",
-		   wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE,
+		   msdu, skb->len, fc & IEEE80211_FCTL_FTYPE,
 		   fc & IEEE80211_FCTL_STYPE);
 	trace_ath10k_tx_hdr(ar, skb->data, skb->len);
 	trace_ath10k_tx_payload(ar, skb->data, skb->len);
 
-	/* Send the management frame buffer to the target */
-	ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid);
-	if (ret)
-		return ret;
-
-	/* TODO: report tx status to mac80211 - temporary just ACK */
-	info->flags |= IEEE80211_TX_STAT_ACK;
-	ieee80211_tx_status_irqsafe(ar->hw, skb);
-
-	return ret;
+	return skb;
 }
 
 static void ath10k_wmi_event_scan_started(struct ath10k *ar)
@@ -876,6 +1125,25 @@
 	}
 }
 
+static void ath10k_wmi_event_scan_start_failed(struct ath10k *ar)
+{
+	lockdep_assert_held(&ar->data_lock);
+
+	switch (ar->scan.state) {
+	case ATH10K_SCAN_IDLE:
+	case ATH10K_SCAN_RUNNING:
+	case ATH10K_SCAN_ABORTING:
+		ath10k_warn(ar, "received scan start failed event in an invalid scan state: %s (%d)\n",
+			    ath10k_scan_state_str(ar->scan.state),
+			    ar->scan.state);
+		break;
+	case ATH10K_SCAN_STARTING:
+		complete(&ar->scan.started);
+		__ath10k_scan_finish(ar);
+		break;
+	}
+}
+
 static void ath10k_wmi_event_scan_completed(struct ath10k *ar)
 {
 	lockdep_assert_held(&ar->data_lock);
@@ -977,22 +1245,48 @@
 	}
 }
 
-static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
+static int ath10k_wmi_op_pull_scan_ev(struct ath10k *ar, struct sk_buff *skb,
+				      struct wmi_scan_ev_arg *arg)
 {
-	struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data;
+	struct wmi_scan_event *ev = (void *)skb->data;
+
+	if (skb->len < sizeof(*ev))
+		return -EPROTO;
+
+	skb_pull(skb, sizeof(*ev));
+	arg->event_type = ev->event_type;
+	arg->reason = ev->reason;
+	arg->channel_freq = ev->channel_freq;
+	arg->scan_req_id = ev->scan_req_id;
+	arg->scan_id = ev->scan_id;
+	arg->vdev_id = ev->vdev_id;
+
+	return 0;
+}
+
+int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct wmi_scan_ev_arg arg = {};
 	enum wmi_scan_event_type event_type;
 	enum wmi_scan_completion_reason reason;
 	u32 freq;
 	u32 req_id;
 	u32 scan_id;
 	u32 vdev_id;
+	int ret;
 
-	event_type = __le32_to_cpu(event->event_type);
-	reason     = __le32_to_cpu(event->reason);
-	freq       = __le32_to_cpu(event->channel_freq);
-	req_id     = __le32_to_cpu(event->scan_req_id);
-	scan_id    = __le32_to_cpu(event->scan_id);
-	vdev_id    = __le32_to_cpu(event->vdev_id);
+	ret = ath10k_wmi_pull_scan(ar, skb, &arg);
+	if (ret) {
+		ath10k_warn(ar, "failed to parse scan event: %d\n", ret);
+		return ret;
+	}
+
+	event_type = __le32_to_cpu(arg.event_type);
+	reason = __le32_to_cpu(arg.reason);
+	freq = __le32_to_cpu(arg.channel_freq);
+	req_id = __le32_to_cpu(arg.scan_req_id);
+	scan_id = __le32_to_cpu(arg.scan_id);
+	vdev_id = __le32_to_cpu(arg.vdev_id);
 
 	spin_lock_bh(&ar->data_lock);
 
@@ -1017,6 +1311,7 @@
 		break;
 	case WMI_SCAN_EVENT_START_FAILED:
 		ath10k_warn(ar, "received scan start failure event\n");
+		ath10k_wmi_event_scan_start_failed(ar);
 		break;
 	case WMI_SCAN_EVENT_DEQUEUED:
 	case WMI_SCAN_EVENT_PREEMPTED:
@@ -1147,21 +1442,14 @@
 	}
 }
 
-static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
+static int ath10k_wmi_op_pull_mgmt_rx_ev(struct ath10k *ar, struct sk_buff *skb,
+					 struct wmi_mgmt_rx_ev_arg *arg)
 {
 	struct wmi_mgmt_rx_event_v1 *ev_v1;
 	struct wmi_mgmt_rx_event_v2 *ev_v2;
 	struct wmi_mgmt_rx_hdr_v1 *ev_hdr;
-	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-	struct ieee80211_hdr *hdr;
-	u32 rx_status;
-	u32 channel;
-	u32 phy_mode;
-	u32 snr;
-	u32 rate;
-	u32 buf_len;
-	u16 fc;
-	int pull_len;
+	size_t pull_len;
+	u32 msdu_len;
 
 	if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) {
 		ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data;
@@ -1173,12 +1461,55 @@
 		pull_len = sizeof(*ev_v1);
 	}
 
-	channel   = __le32_to_cpu(ev_hdr->channel);
-	buf_len   = __le32_to_cpu(ev_hdr->buf_len);
-	rx_status = __le32_to_cpu(ev_hdr->status);
-	snr       = __le32_to_cpu(ev_hdr->snr);
-	phy_mode  = __le32_to_cpu(ev_hdr->phy_mode);
-	rate	  = __le32_to_cpu(ev_hdr->rate);
+	if (skb->len < pull_len)
+		return -EPROTO;
+
+	skb_pull(skb, pull_len);
+	arg->channel = ev_hdr->channel;
+	arg->buf_len = ev_hdr->buf_len;
+	arg->status = ev_hdr->status;
+	arg->snr = ev_hdr->snr;
+	arg->phy_mode = ev_hdr->phy_mode;
+	arg->rate = ev_hdr->rate;
+
+	msdu_len = __le32_to_cpu(arg->buf_len);
+	if (skb->len < msdu_len)
+		return -EPROTO;
+
+	/* the WMI buffer might've ended up being padded to 4 bytes due to HTC
+	 * trailer with credit update. Trim the excess garbage.
+	 */
+	skb_trim(skb, msdu_len);
+
+	return 0;
+}
+
+int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct wmi_mgmt_rx_ev_arg arg = {};
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+	struct ieee80211_hdr *hdr;
+	u32 rx_status;
+	u32 channel;
+	u32 phy_mode;
+	u32 snr;
+	u32 rate;
+	u32 buf_len;
+	u16 fc;
+	int ret;
+
+	ret = ath10k_wmi_pull_mgmt_rx(ar, skb, &arg);
+	if (ret) {
+		ath10k_warn(ar, "failed to parse mgmt rx event: %d\n", ret);
+		return ret;
+	}
+
+	channel = __le32_to_cpu(arg.channel);
+	buf_len = __le32_to_cpu(arg.buf_len);
+	rx_status = __le32_to_cpu(arg.status);
+	snr = __le32_to_cpu(arg.snr);
+	phy_mode = __le32_to_cpu(arg.phy_mode);
+	rate = __le32_to_cpu(arg.rate);
 
 	memset(status, 0, sizeof(*status));
 
@@ -1232,8 +1563,6 @@
 	status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
 	status->rate_idx = get_rate_idx(rate, status->band);
 
-	skb_pull(skb, pull_len);
-
 	hdr = (struct ieee80211_hdr *)skb->data;
 	fc = le16_to_cpu(hdr->frame_control);
 
@@ -1266,12 +1595,6 @@
 		   status->freq, status->band, status->signal,
 		   status->rate_idx);
 
-	/*
-	 * packets from HTC come aligned to 4byte boundaries
-	 * because they can originally come in along with a trailer
-	 */
-	skb_trim(skb, buf_len);
-
 	ieee80211_rx(ar->hw, skb);
 	return 0;
 }
@@ -1295,21 +1618,44 @@
 	return idx;
 }
 
-static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
+static int ath10k_wmi_op_pull_ch_info_ev(struct ath10k *ar, struct sk_buff *skb,
+					 struct wmi_ch_info_ev_arg *arg)
 {
-	struct wmi_chan_info_event *ev;
+	struct wmi_chan_info_event *ev = (void *)skb->data;
+
+	if (skb->len < sizeof(*ev))
+		return -EPROTO;
+
+	skb_pull(skb, sizeof(*ev));
+	arg->err_code = ev->err_code;
+	arg->freq = ev->freq;
+	arg->cmd_flags = ev->cmd_flags;
+	arg->noise_floor = ev->noise_floor;
+	arg->rx_clear_count = ev->rx_clear_count;
+	arg->cycle_count = ev->cycle_count;
+
+	return 0;
+}
+
+void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct wmi_ch_info_ev_arg arg = {};
 	struct survey_info *survey;
 	u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count;
-	int idx;
+	int idx, ret;
 
-	ev = (struct wmi_chan_info_event *)skb->data;
+	ret = ath10k_wmi_pull_ch_info(ar, skb, &arg);
+	if (ret) {
+		ath10k_warn(ar, "failed to parse chan info event: %d\n", ret);
+		return;
+	}
 
-	err_code = __le32_to_cpu(ev->err_code);
-	freq = __le32_to_cpu(ev->freq);
-	cmd_flags = __le32_to_cpu(ev->cmd_flags);
-	noise_floor = __le32_to_cpu(ev->noise_floor);
-	rx_clear_count = __le32_to_cpu(ev->rx_clear_count);
-	cycle_count = __le32_to_cpu(ev->cycle_count);
+	err_code = __le32_to_cpu(arg.err_code);
+	freq = __le32_to_cpu(arg.freq);
+	cmd_flags = __le32_to_cpu(arg.cmd_flags);
+	noise_floor = __le32_to_cpu(arg.noise_floor);
+	rx_clear_count = __le32_to_cpu(arg.rx_clear_count);
+	cycle_count = __le32_to_cpu(arg.cycle_count);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n",
@@ -1344,11 +1690,11 @@
 		rx_clear_count -= ar->survey_last_rx_clear_count;
 
 		survey = &ar->survey[idx];
-		survey->channel_time = WMI_CHAN_INFO_MSEC(cycle_count);
-		survey->channel_time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count);
+		survey->time = WMI_CHAN_INFO_MSEC(cycle_count);
+		survey->time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count);
 		survey->noise = noise_floor;
-		survey->filled = SURVEY_INFO_CHANNEL_TIME |
-				 SURVEY_INFO_CHANNEL_TIME_RX |
+		survey->filled = SURVEY_INFO_TIME |
+				 SURVEY_INFO_TIME_RX |
 				 SURVEY_INFO_NOISE_DBM;
 	}
 
@@ -1359,12 +1705,12 @@
 	spin_unlock_bh(&ar->data_lock);
 }
 
-static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
+void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n");
 }
 
-static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
+int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event debug mesg len %d\n",
 		   skb->len);
@@ -1374,12 +1720,9 @@
 	return 0;
 }
 
-static void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src,
-				       struct ath10k_fw_stats_pdev *dst)
+void ath10k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src,
+				     struct ath10k_fw_stats_pdev *dst)
 {
-	const struct wal_dbg_tx_stats *tx = &src->wal.tx;
-	const struct wal_dbg_rx_stats *rx = &src->wal.rx;
-
 	dst->ch_noise_floor = __le32_to_cpu(src->chan_nf);
 	dst->tx_frame_count = __le32_to_cpu(src->tx_frame_count);
 	dst->rx_frame_count = __le32_to_cpu(src->rx_frame_count);
@@ -1387,57 +1730,76 @@
 	dst->cycle_count = __le32_to_cpu(src->cycle_count);
 	dst->phy_err_count = __le32_to_cpu(src->phy_err_count);
 	dst->chan_tx_power = __le32_to_cpu(src->chan_tx_pwr);
-
-	dst->comp_queued = __le32_to_cpu(tx->comp_queued);
-	dst->comp_delivered = __le32_to_cpu(tx->comp_delivered);
-	dst->msdu_enqued = __le32_to_cpu(tx->msdu_enqued);
-	dst->mpdu_enqued = __le32_to_cpu(tx->mpdu_enqued);
-	dst->wmm_drop = __le32_to_cpu(tx->wmm_drop);
-	dst->local_enqued = __le32_to_cpu(tx->local_enqued);
-	dst->local_freed = __le32_to_cpu(tx->local_freed);
-	dst->hw_queued = __le32_to_cpu(tx->hw_queued);
-	dst->hw_reaped = __le32_to_cpu(tx->hw_reaped);
-	dst->underrun = __le32_to_cpu(tx->underrun);
-	dst->tx_abort = __le32_to_cpu(tx->tx_abort);
-	dst->mpdus_requed = __le32_to_cpu(tx->mpdus_requed);
-	dst->tx_ko = __le32_to_cpu(tx->tx_ko);
-	dst->data_rc = __le32_to_cpu(tx->data_rc);
-	dst->self_triggers = __le32_to_cpu(tx->self_triggers);
-	dst->sw_retry_failure = __le32_to_cpu(tx->sw_retry_failure);
-	dst->illgl_rate_phy_err = __le32_to_cpu(tx->illgl_rate_phy_err);
-	dst->pdev_cont_xretry = __le32_to_cpu(tx->pdev_cont_xretry);
-	dst->pdev_tx_timeout = __le32_to_cpu(tx->pdev_tx_timeout);
-	dst->pdev_resets = __le32_to_cpu(tx->pdev_resets);
-	dst->phy_underrun = __le32_to_cpu(tx->phy_underrun);
-	dst->txop_ovf = __le32_to_cpu(tx->txop_ovf);
-
-	dst->mid_ppdu_route_change = __le32_to_cpu(rx->mid_ppdu_route_change);
-	dst->status_rcvd = __le32_to_cpu(rx->status_rcvd);
-	dst->r0_frags = __le32_to_cpu(rx->r0_frags);
-	dst->r1_frags = __le32_to_cpu(rx->r1_frags);
-	dst->r2_frags = __le32_to_cpu(rx->r2_frags);
-	dst->r3_frags = __le32_to_cpu(rx->r3_frags);
-	dst->htt_msdus = __le32_to_cpu(rx->htt_msdus);
-	dst->htt_mpdus = __le32_to_cpu(rx->htt_mpdus);
-	dst->loc_msdus = __le32_to_cpu(rx->loc_msdus);
-	dst->loc_mpdus = __le32_to_cpu(rx->loc_mpdus);
-	dst->oversize_amsdu = __le32_to_cpu(rx->oversize_amsdu);
-	dst->phy_errs = __le32_to_cpu(rx->phy_errs);
-	dst->phy_err_drop = __le32_to_cpu(rx->phy_err_drop);
-	dst->mpdu_errs = __le32_to_cpu(rx->mpdu_errs);
 }
 
-static void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
-				       struct ath10k_fw_stats_peer *dst)
+void ath10k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src,
+				   struct ath10k_fw_stats_pdev *dst)
+{
+	dst->comp_queued = __le32_to_cpu(src->comp_queued);
+	dst->comp_delivered = __le32_to_cpu(src->comp_delivered);
+	dst->msdu_enqued = __le32_to_cpu(src->msdu_enqued);
+	dst->mpdu_enqued = __le32_to_cpu(src->mpdu_enqued);
+	dst->wmm_drop = __le32_to_cpu(src->wmm_drop);
+	dst->local_enqued = __le32_to_cpu(src->local_enqued);
+	dst->local_freed = __le32_to_cpu(src->local_freed);
+	dst->hw_queued = __le32_to_cpu(src->hw_queued);
+	dst->hw_reaped = __le32_to_cpu(src->hw_reaped);
+	dst->underrun = __le32_to_cpu(src->underrun);
+	dst->tx_abort = __le32_to_cpu(src->tx_abort);
+	dst->mpdus_requed = __le32_to_cpu(src->mpdus_requed);
+	dst->tx_ko = __le32_to_cpu(src->tx_ko);
+	dst->data_rc = __le32_to_cpu(src->data_rc);
+	dst->self_triggers = __le32_to_cpu(src->self_triggers);
+	dst->sw_retry_failure = __le32_to_cpu(src->sw_retry_failure);
+	dst->illgl_rate_phy_err = __le32_to_cpu(src->illgl_rate_phy_err);
+	dst->pdev_cont_xretry = __le32_to_cpu(src->pdev_cont_xretry);
+	dst->pdev_tx_timeout = __le32_to_cpu(src->pdev_tx_timeout);
+	dst->pdev_resets = __le32_to_cpu(src->pdev_resets);
+	dst->phy_underrun = __le32_to_cpu(src->phy_underrun);
+	dst->txop_ovf = __le32_to_cpu(src->txop_ovf);
+}
+
+void ath10k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src,
+				   struct ath10k_fw_stats_pdev *dst)
+{
+	dst->mid_ppdu_route_change = __le32_to_cpu(src->mid_ppdu_route_change);
+	dst->status_rcvd = __le32_to_cpu(src->status_rcvd);
+	dst->r0_frags = __le32_to_cpu(src->r0_frags);
+	dst->r1_frags = __le32_to_cpu(src->r1_frags);
+	dst->r2_frags = __le32_to_cpu(src->r2_frags);
+	dst->r3_frags = __le32_to_cpu(src->r3_frags);
+	dst->htt_msdus = __le32_to_cpu(src->htt_msdus);
+	dst->htt_mpdus = __le32_to_cpu(src->htt_mpdus);
+	dst->loc_msdus = __le32_to_cpu(src->loc_msdus);
+	dst->loc_mpdus = __le32_to_cpu(src->loc_mpdus);
+	dst->oversize_amsdu = __le32_to_cpu(src->oversize_amsdu);
+	dst->phy_errs = __le32_to_cpu(src->phy_errs);
+	dst->phy_err_drop = __le32_to_cpu(src->phy_err_drop);
+	dst->mpdu_errs = __le32_to_cpu(src->mpdu_errs);
+}
+
+void ath10k_wmi_pull_pdev_stats_extra(const struct wmi_pdev_stats_extra *src,
+				      struct ath10k_fw_stats_pdev *dst)
+{
+	dst->ack_rx_bad = __le32_to_cpu(src->ack_rx_bad);
+	dst->rts_bad = __le32_to_cpu(src->rts_bad);
+	dst->rts_good = __le32_to_cpu(src->rts_good);
+	dst->fcs_bad = __le32_to_cpu(src->fcs_bad);
+	dst->no_beacons = __le32_to_cpu(src->no_beacons);
+	dst->mib_int_count = __le32_to_cpu(src->mib_int_count);
+}
+
+void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
+				struct ath10k_fw_stats_peer *dst)
 {
 	ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
 	dst->peer_rssi = __le32_to_cpu(src->peer_rssi);
 	dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate);
 }
 
-static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar,
-					 struct sk_buff *skb,
-					 struct ath10k_fw_stats *stats)
+static int ath10k_wmi_main_op_pull_fw_stats(struct ath10k *ar,
+					    struct sk_buff *skb,
+					    struct ath10k_fw_stats *stats)
 {
 	const struct wmi_stats_event *ev = (void *)skb->data;
 	u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
@@ -1462,7 +1824,10 @@
 		if (!dst)
 			continue;
 
-		ath10k_wmi_pull_pdev_stats(src, dst);
+		ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
+		ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
+		ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
+
 		list_add_tail(&dst->list, &stats->pdevs);
 	}
 
@@ -1487,9 +1852,9 @@
 	return 0;
 }
 
-static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar,
-					struct sk_buff *skb,
-					struct ath10k_fw_stats *stats)
+static int ath10k_wmi_10x_op_pull_fw_stats(struct ath10k *ar,
+					   struct sk_buff *skb,
+					   struct ath10k_fw_stats *stats)
 {
 	const struct wmi_stats_event *ev = (void *)skb->data;
 	u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
@@ -1514,14 +1879,10 @@
 		if (!dst)
 			continue;
 
-		ath10k_wmi_pull_pdev_stats(&src->old, dst);
-
-		dst->ack_rx_bad = __le32_to_cpu(src->ack_rx_bad);
-		dst->rts_bad = __le32_to_cpu(src->rts_bad);
-		dst->rts_good = __le32_to_cpu(src->rts_good);
-		dst->fcs_bad = __le32_to_cpu(src->fcs_bad);
-		dst->no_beacons = __le32_to_cpu(src->no_beacons);
-		dst->mib_int_count = __le32_to_cpu(src->mib_int_count);
+		ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
+		ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
+		ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
+		ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst);
 
 		list_add_tail(&dst->list, &stats->pdevs);
 	}
@@ -1550,61 +1911,250 @@
 	return 0;
 }
 
-int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
-			     struct ath10k_fw_stats *stats)
+static int ath10k_wmi_10_2_op_pull_fw_stats(struct ath10k *ar,
+					    struct sk_buff *skb,
+					    struct ath10k_fw_stats *stats)
 {
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
-		return ath10k_wmi_10x_pull_fw_stats(ar, skb, stats);
-	else
-		return ath10k_wmi_main_pull_fw_stats(ar, skb, stats);
+	const struct wmi_10_2_stats_event *ev = (void *)skb->data;
+	u32 num_pdev_stats;
+	u32 num_pdev_ext_stats;
+	u32 num_vdev_stats;
+	u32 num_peer_stats;
+	int i;
+
+	if (!skb_pull(skb, sizeof(*ev)))
+		return -EPROTO;
+
+	num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
+	num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats);
+	num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
+	num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
+
+	for (i = 0; i < num_pdev_stats; i++) {
+		const struct wmi_10_2_pdev_stats *src;
+		struct ath10k_fw_stats_pdev *dst;
+
+		src = (void *)skb->data;
+		if (!skb_pull(skb, sizeof(*src)))
+			return -EPROTO;
+
+		dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+		if (!dst)
+			continue;
+
+		ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
+		ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
+		ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
+		ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst);
+		/* FIXME: expose 10.2 specific values */
+
+		list_add_tail(&dst->list, &stats->pdevs);
+	}
+
+	for (i = 0; i < num_pdev_ext_stats; i++) {
+		const struct wmi_10_2_pdev_ext_stats *src;
+
+		src = (void *)skb->data;
+		if (!skb_pull(skb, sizeof(*src)))
+			return -EPROTO;
+
+		/* FIXME: expose values to userspace
+		 *
+		 * Note: Even though this loop seems to do nothing it is
+		 * required to parse following sub-structures properly.
+		 */
+	}
+
+	/* fw doesn't implement vdev stats */
+
+	for (i = 0; i < num_peer_stats; i++) {
+		const struct wmi_10_2_peer_stats *src;
+		struct ath10k_fw_stats_peer *dst;
+
+		src = (void *)skb->data;
+		if (!skb_pull(skb, sizeof(*src)))
+			return -EPROTO;
+
+		dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+		if (!dst)
+			continue;
+
+		ath10k_wmi_pull_peer_stats(&src->old, dst);
+
+		dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
+		/* FIXME: expose 10.2 specific values */
+
+		list_add_tail(&dst->list, &stats->peers);
+	}
+
+	return 0;
 }
 
-static void ath10k_wmi_event_update_stats(struct ath10k *ar,
-					  struct sk_buff *skb)
+static int ath10k_wmi_10_2_4_op_pull_fw_stats(struct ath10k *ar,
+					      struct sk_buff *skb,
+					      struct ath10k_fw_stats *stats)
+{
+	const struct wmi_10_2_stats_event *ev = (void *)skb->data;
+	u32 num_pdev_stats;
+	u32 num_pdev_ext_stats;
+	u32 num_vdev_stats;
+	u32 num_peer_stats;
+	int i;
+
+	if (!skb_pull(skb, sizeof(*ev)))
+		return -EPROTO;
+
+	num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
+	num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats);
+	num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
+	num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
+
+	for (i = 0; i < num_pdev_stats; i++) {
+		const struct wmi_10_2_pdev_stats *src;
+		struct ath10k_fw_stats_pdev *dst;
+
+		src = (void *)skb->data;
+		if (!skb_pull(skb, sizeof(*src)))
+			return -EPROTO;
+
+		dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+		if (!dst)
+			continue;
+
+		ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
+		ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
+		ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
+		ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst);
+		/* FIXME: expose 10.2 specific values */
+
+		list_add_tail(&dst->list, &stats->pdevs);
+	}
+
+	for (i = 0; i < num_pdev_ext_stats; i++) {
+		const struct wmi_10_2_pdev_ext_stats *src;
+
+		src = (void *)skb->data;
+		if (!skb_pull(skb, sizeof(*src)))
+			return -EPROTO;
+
+		/* FIXME: expose values to userspace
+		 *
+		 * Note: Even though this loop seems to do nothing it is
+		 * required to parse following sub-structures properly.
+		 */
+	}
+
+	/* fw doesn't implement vdev stats */
+
+	for (i = 0; i < num_peer_stats; i++) {
+		const struct wmi_10_2_4_peer_stats *src;
+		struct ath10k_fw_stats_peer *dst;
+
+		src = (void *)skb->data;
+		if (!skb_pull(skb, sizeof(*src)))
+			return -EPROTO;
+
+		dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+		if (!dst)
+			continue;
+
+		ath10k_wmi_pull_peer_stats(&src->common.old, dst);
+
+		dst->peer_rx_rate = __le32_to_cpu(src->common.peer_rx_rate);
+		/* FIXME: expose 10.2 specific values */
+
+		list_add_tail(&dst->list, &stats->peers);
+	}
+
+	return 0;
+}
+
+void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
 	ath10k_debug_fw_stats_process(ar, skb);
 }
 
-static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar,
-					     struct sk_buff *skb)
+static int
+ath10k_wmi_op_pull_vdev_start_ev(struct ath10k *ar, struct sk_buff *skb,
+				 struct wmi_vdev_start_ev_arg *arg)
 {
-	struct wmi_vdev_start_response_event *ev;
+	struct wmi_vdev_start_response_event *ev = (void *)skb->data;
+
+	if (skb->len < sizeof(*ev))
+		return -EPROTO;
+
+	skb_pull(skb, sizeof(*ev));
+	arg->vdev_id = ev->vdev_id;
+	arg->req_id = ev->req_id;
+	arg->resp_type = ev->resp_type;
+	arg->status = ev->status;
+
+	return 0;
+}
+
+void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct wmi_vdev_start_ev_arg arg = {};
+	int ret;
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n");
 
-	ev = (struct wmi_vdev_start_response_event *)skb->data;
+	ret = ath10k_wmi_pull_vdev_start(ar, skb, &arg);
+	if (ret) {
+		ath10k_warn(ar, "failed to parse vdev start event: %d\n", ret);
+		return;
+	}
 
-	if (WARN_ON(__le32_to_cpu(ev->status)))
+	if (WARN_ON(__le32_to_cpu(arg.status)))
 		return;
 
 	complete(&ar->vdev_setup_done);
 }
 
-static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar,
-					  struct sk_buff *skb)
+void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n");
 	complete(&ar->vdev_setup_done);
 }
 
-static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar,
-					      struct sk_buff *skb)
+static int
+ath10k_wmi_op_pull_peer_kick_ev(struct ath10k *ar, struct sk_buff *skb,
+				struct wmi_peer_kick_ev_arg *arg)
 {
-	struct wmi_peer_sta_kickout_event *ev;
-	struct ieee80211_sta *sta;
+	struct wmi_peer_sta_kickout_event *ev = (void *)skb->data;
 
-	ev = (struct wmi_peer_sta_kickout_event *)skb->data;
+	if (skb->len < sizeof(*ev))
+		return -EPROTO;
+
+	skb_pull(skb, sizeof(*ev));
+	arg->mac_addr = ev->peer_macaddr.addr;
+
+	return 0;
+}
+
+void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct wmi_peer_kick_ev_arg arg = {};
+	struct ieee80211_sta *sta;
+	int ret;
+
+	ret = ath10k_wmi_pull_peer_kick(ar, skb, &arg);
+	if (ret) {
+		ath10k_warn(ar, "failed to parse peer kickout event: %d\n",
+			    ret);
+		return;
+	}
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n",
-		   ev->peer_macaddr.addr);
+		   arg.mac_addr);
 
 	rcu_read_lock();
 
-	sta = ieee80211_find_sta_by_ifaddr(ar->hw, ev->peer_macaddr.addr, NULL);
+	sta = ieee80211_find_sta_by_ifaddr(ar->hw, arg.mac_addr, NULL);
 	if (!sta) {
 		ath10k_warn(ar, "Spurious quick kickout for STA %pM\n",
-			    ev->peer_macaddr.addr);
+			    arg.mac_addr);
 		goto exit;
 	}
 
@@ -1641,7 +2191,7 @@
 static void ath10k_wmi_update_tim(struct ath10k *ar,
 				  struct ath10k_vif *arvif,
 				  struct sk_buff *bcn,
-				  struct wmi_bcn_info *bcn_info)
+				  const struct wmi_tim_info *tim_info)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)bcn->data;
 	struct ieee80211_tim_ie *tim;
@@ -1652,14 +2202,14 @@
 
 	/* if next SWBA has no tim_changed the tim_bitmap is garbage.
 	 * we must copy the bitmap upon change and reuse it later */
-	if (__le32_to_cpu(bcn_info->tim_info.tim_changed)) {
+	if (__le32_to_cpu(tim_info->tim_changed)) {
 		int i;
 
 		BUILD_BUG_ON(sizeof(arvif->u.ap.tim_bitmap) !=
-			     sizeof(bcn_info->tim_info.tim_bitmap));
+			     sizeof(tim_info->tim_bitmap));
 
 		for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) {
-			t = bcn_info->tim_info.tim_bitmap[i / 4];
+			t = tim_info->tim_bitmap[i / 4];
 			v = __le32_to_cpu(t);
 			arvif->u.ap.tim_bitmap[i] = (v >> ((i % 4) * 8)) & 0xFF;
 		}
@@ -1711,13 +2261,13 @@
 		return;
 	}
 
-	tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast);
+	tim->bitmap_ctrl = !!__le32_to_cpu(tim_info->tim_mcast);
 	memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
 
 	if (tim->dtim_count == 0) {
 		ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true;
 
-		if (__le32_to_cpu(bcn_info->tim_info.tim_mcast) == 1)
+		if (__le32_to_cpu(tim_info->tim_mcast) == 1)
 			ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true;
 	}
 
@@ -1727,7 +2277,7 @@
 }
 
 static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len,
-				   struct wmi_p2p_noa_info *noa)
+				   const struct wmi_p2p_noa_info *noa)
 {
 	struct ieee80211_p2p_noa_attr *noa_attr;
 	u8  ctwindow_oppps = noa->ctwindow_oppps;
@@ -1769,7 +2319,7 @@
 	*noa_attr_len = __cpu_to_le16(attr_len);
 }
 
-static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa)
+static u32 ath10k_p2p_calc_noa_ie_len(const struct wmi_p2p_noa_info *noa)
 {
 	u32 len = 0;
 	u8 noa_descriptors = noa->num_descriptors;
@@ -1789,9 +2339,8 @@
 
 static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif,
 				  struct sk_buff *bcn,
-				  struct wmi_bcn_info *bcn_info)
+				  const struct wmi_p2p_noa_info *noa)
 {
-	struct wmi_p2p_noa_info *noa = &bcn_info->p2p_noa_info;
 	u8 *new_data, *old_data = arvif->u.ap.noa_data;
 	u32 new_len;
 
@@ -1832,22 +2381,59 @@
 	kfree(old_data);
 }
 
-static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
+static int ath10k_wmi_op_pull_swba_ev(struct ath10k *ar, struct sk_buff *skb,
+				      struct wmi_swba_ev_arg *arg)
 {
-	struct wmi_host_swba_event *ev;
+	struct wmi_host_swba_event *ev = (void *)skb->data;
+	u32 map;
+	size_t i;
+
+	if (skb->len < sizeof(*ev))
+		return -EPROTO;
+
+	skb_pull(skb, sizeof(*ev));
+	arg->vdev_map = ev->vdev_map;
+
+	for (i = 0, map = __le32_to_cpu(ev->vdev_map); map; map >>= 1) {
+		if (!(map & BIT(0)))
+			continue;
+
+		/* If this happens there were some changes in firmware and
+		 * ath10k should update the max size of tim_info array.
+		 */
+		if (WARN_ON_ONCE(i == ARRAY_SIZE(arg->tim_info)))
+			break;
+
+		arg->tim_info[i] = &ev->bcn_info[i].tim_info;
+		arg->noa_info[i] = &ev->bcn_info[i].p2p_noa_info;
+		i++;
+	}
+
+	return 0;
+}
+
+void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct wmi_swba_ev_arg arg = {};
 	u32 map;
 	int i = -1;
-	struct wmi_bcn_info *bcn_info;
+	const struct wmi_tim_info *tim_info;
+	const struct wmi_p2p_noa_info *noa_info;
 	struct ath10k_vif *arvif;
 	struct sk_buff *bcn;
 	dma_addr_t paddr;
 	int ret, vdev_id = 0;
 
-	ev = (struct wmi_host_swba_event *)skb->data;
-	map = __le32_to_cpu(ev->vdev_map);
+	ret = ath10k_wmi_pull_swba(ar, skb, &arg);
+	if (ret) {
+		ath10k_warn(ar, "failed to parse swba event: %d\n", ret);
+		return;
+	}
+
+	map = __le32_to_cpu(arg.vdev_map);
 
 	ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt swba vdev_map 0x%x\n",
-		   ev->vdev_map);
+		   map);
 
 	for (; map; map >>= 1, vdev_id++) {
 		if (!(map & 0x1))
@@ -1860,19 +2446,20 @@
 			break;
 		}
 
-		bcn_info = &ev->bcn_info[i];
+		tim_info = arg.tim_info[i];
+		noa_info = arg.noa_info[i];
 
 		ath10k_dbg(ar, ATH10K_DBG_MGMT,
 			   "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),
-			   __le32_to_cpu(bcn_info->tim_info.tim_changed),
-			   __le32_to_cpu(bcn_info->tim_info.tim_num_ps_pending),
-			   __le32_to_cpu(bcn_info->tim_info.tim_bitmap[3]),
-			   __le32_to_cpu(bcn_info->tim_info.tim_bitmap[2]),
-			   __le32_to_cpu(bcn_info->tim_info.tim_bitmap[1]),
-			   __le32_to_cpu(bcn_info->tim_info.tim_bitmap[0]));
+			   __le32_to_cpu(tim_info->tim_len),
+			   __le32_to_cpu(tim_info->tim_mcast),
+			   __le32_to_cpu(tim_info->tim_changed),
+			   __le32_to_cpu(tim_info->tim_num_ps_pending),
+			   __le32_to_cpu(tim_info->tim_bitmap[3]),
+			   __le32_to_cpu(tim_info->tim_bitmap[2]),
+			   __le32_to_cpu(tim_info->tim_bitmap[1]),
+			   __le32_to_cpu(tim_info->tim_bitmap[0]));
 
 		arvif = ath10k_get_arvif(ar, vdev_id);
 		if (arvif == NULL) {
@@ -1899,15 +2486,25 @@
 		}
 
 		ath10k_tx_h_seq_no(arvif->vif, bcn);
-		ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info);
-		ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info);
+		ath10k_wmi_update_tim(ar, arvif, bcn, tim_info);
+		ath10k_wmi_update_noa(ar, arvif, bcn, noa_info);
 
 		spin_lock_bh(&ar->data_lock);
 
 		if (arvif->beacon) {
-			if (!arvif->beacon_sent)
-				ath10k_warn(ar, "SWBA overrun on vdev %d\n",
+			switch (arvif->beacon_state) {
+			case ATH10K_BEACON_SENT:
+				break;
+			case ATH10K_BEACON_SCHEDULED:
+				ath10k_warn(ar, "SWBA overrun on vdev %d, skipped old beacon\n",
 					    arvif->vdev_id);
+				break;
+			case ATH10K_BEACON_SENDING:
+				ath10k_warn(ar, "SWBA overrun on vdev %d, skipped new beacon\n",
+					    arvif->vdev_id);
+				dev_kfree_skb(bcn);
+				goto skip;
+			}
 
 			ath10k_mac_vif_beacon_free(arvif);
 		}
@@ -1935,19 +2532,19 @@
 		}
 
 		arvif->beacon = bcn;
-		arvif->beacon_sent = false;
+		arvif->beacon_state = ATH10K_BEACON_SCHEDULED;
 
 		trace_ath10k_tx_hdr(ar, bcn->data, bcn->len);
 		trace_ath10k_tx_payload(ar, bcn->data, bcn->len);
 
-		ath10k_wmi_tx_beacon_nowait(arvif);
 skip:
 		spin_unlock_bh(&ar->data_lock);
 	}
+
+	ath10k_wmi_tx_beacons_nowait(ar);
 }
 
-static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar,
-					       struct sk_buff *skb)
+void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n");
 }
@@ -2068,9 +2665,9 @@
 	return 0;
 }
 
-static void ath10k_wmi_event_dfs(struct ath10k *ar,
-				 const struct wmi_phyerr *phyerr,
-				 u64 tsf)
+void ath10k_wmi_event_dfs(struct ath10k *ar,
+			  const struct wmi_phyerr *phyerr,
+			  u64 tsf)
 {
 	int buf_len, tlv_len, res, i = 0;
 	const struct phyerr_tlv *tlv;
@@ -2133,10 +2730,9 @@
 	}
 }
 
-static void
-ath10k_wmi_event_spectral_scan(struct ath10k *ar,
-			       const struct wmi_phyerr *phyerr,
-			       u64 tsf)
+void ath10k_wmi_event_spectral_scan(struct ath10k *ar,
+				    const struct wmi_phyerr *phyerr,
+				    u64 tsf)
 {
 	int buf_len, tlv_len, res, i = 0;
 	struct phyerr_tlv *tlv;
@@ -2188,37 +2784,53 @@
 	}
 }
 
-static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
+static int ath10k_wmi_op_pull_phyerr_ev(struct ath10k *ar, struct sk_buff *skb,
+					struct wmi_phyerr_ev_arg *arg)
 {
-	const struct wmi_phyerr_event *ev;
+	struct wmi_phyerr_event *ev = (void *)skb->data;
+
+	if (skb->len < sizeof(*ev))
+		return -EPROTO;
+
+	arg->num_phyerrs = ev->num_phyerrs;
+	arg->tsf_l32 = ev->tsf_l32;
+	arg->tsf_u32 = ev->tsf_u32;
+	arg->buf_len = __cpu_to_le32(skb->len - sizeof(*ev));
+	arg->phyerrs = ev->phyerrs;
+
+	return 0;
+}
+
+void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct wmi_phyerr_ev_arg arg = {};
 	const struct wmi_phyerr *phyerr;
 	u32 count, i, buf_len, phy_err_code;
 	u64 tsf;
-	int left_len = skb->len;
+	int left_len, ret;
 
 	ATH10K_DFS_STAT_INC(ar, phy_errors);
 
-	/* Check if combined event available */
-	if (left_len < sizeof(*ev)) {
-		ath10k_warn(ar, "wmi phyerr combined event wrong len\n");
+	ret = ath10k_wmi_pull_phyerr(ar, skb, &arg);
+	if (ret) {
+		ath10k_warn(ar, "failed to parse phyerr event: %d\n", ret);
 		return;
 	}
 
-	left_len -= sizeof(*ev);
+	left_len = __le32_to_cpu(arg.buf_len);
 
 	/* Check number of included events */
-	ev = (const struct wmi_phyerr_event *)skb->data;
-	count = __le32_to_cpu(ev->num_phyerrs);
+	count = __le32_to_cpu(arg.num_phyerrs);
 
-	tsf = __le32_to_cpu(ev->tsf_u32);
+	tsf = __le32_to_cpu(arg.tsf_u32);
 	tsf <<= 32;
-	tsf |= __le32_to_cpu(ev->tsf_l32);
+	tsf |= __le32_to_cpu(arg.tsf_l32);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi event phyerr count %d tsf64 0x%llX\n",
 		   count, tsf);
 
-	phyerr = ev->phyerrs;
+	phyerr = arg.phyerrs;
 	for (i = 0; i < count; i++) {
 		/* Check if we can read event header */
 		if (left_len < sizeof(*phyerr)) {
@@ -2258,19 +2870,17 @@
 	}
 }
 
-static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
+void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n");
 }
 
-static void ath10k_wmi_event_profile_match(struct ath10k *ar,
-					   struct sk_buff *skb)
+void ath10k_wmi_event_profile_match(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n");
 }
 
-static void ath10k_wmi_event_debug_print(struct ath10k *ar,
-					 struct sk_buff *skb)
+void ath10k_wmi_event_debug_print(struct ath10k *ar, struct sk_buff *skb)
 {
 	char buf[101], c;
 	int i;
@@ -2303,103 +2913,90 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI_PRINT, "wmi print '%s'\n", buf);
 }
 
-static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
+void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n");
 }
 
-static void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar,
-					       struct sk_buff *skb)
+void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n");
 }
 
-static void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar,
-						    struct sk_buff *skb)
+void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar,
+					     struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n");
 }
 
-static void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar,
-						    struct sk_buff *skb)
+void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar,
+					     struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n");
 }
 
-static void ath10k_wmi_event_rtt_error_report(struct ath10k *ar,
-					      struct sk_buff *skb)
+void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n");
 }
 
-static void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar,
-					     struct sk_buff *skb)
+void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n");
 }
 
-static void ath10k_wmi_event_dcs_interference(struct ath10k *ar,
-					      struct sk_buff *skb)
+void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n");
 }
 
-static void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar,
-					     struct sk_buff *skb)
+void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n");
 }
 
-static void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar,
-					   struct sk_buff *skb)
+void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n");
 }
 
-static void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar,
-						struct sk_buff *skb)
+void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n");
 }
 
-static void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar,
-					    struct sk_buff *skb)
+void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n");
 }
 
-static void ath10k_wmi_event_delba_complete(struct ath10k *ar,
-					    struct sk_buff *skb)
+void ath10k_wmi_event_delba_complete(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n");
 }
 
-static void ath10k_wmi_event_addba_complete(struct ath10k *ar,
-					    struct sk_buff *skb)
+void ath10k_wmi_event_addba_complete(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n");
 }
 
-static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
-						       struct sk_buff *skb)
+void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
+						struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n");
 }
 
-static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar,
-					     struct sk_buff *skb)
+void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n");
 }
 
-static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar,
-					      struct sk_buff *skb)
+void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n");
 }
 
-static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar,
-					     struct sk_buff *skb)
+void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb)
 {
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n");
 }
@@ -2435,8 +3032,9 @@
 	return 0;
 }
 
-static int ath10k_wmi_main_pull_svc_rdy_ev(struct sk_buff *skb,
-					   struct wmi_svc_rdy_ev_arg *arg)
+static int
+ath10k_wmi_main_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb,
+				   struct wmi_svc_rdy_ev_arg *arg)
 {
 	struct wmi_service_ready_event *ev;
 	size_t i, n;
@@ -2471,8 +3069,9 @@
 	return 0;
 }
 
-static int ath10k_wmi_10x_pull_svc_rdy_ev(struct sk_buff *skb,
-					  struct wmi_svc_rdy_ev_arg *arg)
+static int
+ath10k_wmi_10x_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb,
+				  struct wmi_svc_rdy_ev_arg *arg)
 {
 	struct wmi_10x_service_ready_event *ev;
 	int i, n;
@@ -2506,30 +3105,22 @@
 	return 0;
 }
 
-static void ath10k_wmi_event_service_ready(struct ath10k *ar,
-					   struct sk_buff *skb)
+void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb)
 {
 	struct wmi_svc_rdy_ev_arg arg = {};
 	u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
 	int ret;
 
-	memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map));
-
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
-		ret = ath10k_wmi_10x_pull_svc_rdy_ev(skb, &arg);
-		wmi_10x_svc_map(arg.service_map, ar->wmi.svc_map,
-				arg.service_map_len);
-	} else {
-		ret = ath10k_wmi_main_pull_svc_rdy_ev(skb, &arg);
-		wmi_main_svc_map(arg.service_map, ar->wmi.svc_map,
-				 arg.service_map_len);
-	}
-
+	ret = ath10k_wmi_pull_svc_rdy(ar, skb, &arg);
 	if (ret) {
 		ath10k_warn(ar, "failed to parse service ready: %d\n", ret);
 		return;
 	}
 
+	memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map));
+	ath10k_wmi_map_svc(ar, arg.service_map, ar->wmi.svc_map,
+			   arg.service_map_len);
+
 	ar->hw_min_tx_power = __le32_to_cpu(arg.min_tx_power);
 	ar->hw_max_tx_power = __le32_to_cpu(arg.max_tx_power);
 	ar->ht_cap_info = __le32_to_cpu(arg.ht_cap);
@@ -2607,13 +3198,14 @@
 	}
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
-		   "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n",
+		   "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x fw_build 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n",
 		   __le32_to_cpu(arg.min_tx_power),
 		   __le32_to_cpu(arg.max_tx_power),
 		   __le32_to_cpu(arg.ht_cap),
 		   __le32_to_cpu(arg.vht_cap),
 		   __le32_to_cpu(arg.sw_ver0),
 		   __le32_to_cpu(arg.sw_ver1),
+		   __le32_to_cpu(arg.fw_build),
 		   __le32_to_cpu(arg.phy_capab),
 		   __le32_to_cpu(arg.num_rf_chains),
 		   __le32_to_cpu(arg.eeprom_rd),
@@ -2622,27 +3214,59 @@
 	complete(&ar->wmi.service_ready);
 }
 
-static int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb)
+static int ath10k_wmi_op_pull_rdy_ev(struct ath10k *ar, struct sk_buff *skb,
+				     struct wmi_rdy_ev_arg *arg)
 {
-	struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data;
+	struct wmi_ready_event *ev = (void *)skb->data;
 
-	if (WARN_ON(skb->len < sizeof(*ev)))
-		return -EINVAL;
+	if (skb->len < sizeof(*ev))
+		return -EPROTO;
 
-	ether_addr_copy(ar->mac_addr, ev->mac_addr.addr);
+	skb_pull(skb, sizeof(*ev));
+	arg->sw_version = ev->sw_version;
+	arg->abi_version = ev->abi_version;
+	arg->status = ev->status;
+	arg->mac_addr = ev->mac_addr.addr;
+
+	return 0;
+}
+
+int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct wmi_rdy_ev_arg arg = {};
+	int ret;
+
+	ret = ath10k_wmi_pull_rdy(ar, skb, &arg);
+	if (ret) {
+		ath10k_warn(ar, "failed to parse ready event: %d\n", ret);
+		return ret;
+	}
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
-		   "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d skb->len %i ev-sz %zu\n",
-		   __le32_to_cpu(ev->sw_version),
-		   __le32_to_cpu(ev->abi_version),
-		   ev->mac_addr.addr,
-		   __le32_to_cpu(ev->status), skb->len, sizeof(*ev));
+		   "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n",
+		   __le32_to_cpu(arg.sw_version),
+		   __le32_to_cpu(arg.abi_version),
+		   arg.mac_addr,
+		   __le32_to_cpu(arg.status));
 
+	ether_addr_copy(ar->mac_addr, arg.mac_addr);
 	complete(&ar->wmi.unified_ready);
 	return 0;
 }
 
-static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb)
+static int ath10k_wmi_event_temperature(struct ath10k *ar, struct sk_buff *skb)
+{
+	const struct wmi_pdev_temperature_event *ev;
+
+	ev = (struct wmi_pdev_temperature_event *)skb->data;
+	if (WARN_ON(skb->len < sizeof(*ev)))
+		return -EPROTO;
+
+	ath10k_thermal_event_temperature(ar, __le32_to_cpu(ev->temperature));
+	return 0;
+}
+
+static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb)
 {
 	struct wmi_cmd_hdr *cmd_hdr;
 	enum wmi_event_id id;
@@ -2758,7 +3382,7 @@
 	dev_kfree_skb(skb);
 }
 
-static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb)
+static void ath10k_wmi_10_1_op_rx(struct ath10k *ar, struct sk_buff *skb)
 {
 	struct wmi_cmd_hdr *cmd_hdr;
 	enum wmi_10x_event_id id;
@@ -2882,7 +3506,7 @@
 	dev_kfree_skb(skb);
 }
 
-static void ath10k_wmi_10_2_process_rx(struct ath10k *ar, struct sk_buff *skb)
+static void ath10k_wmi_10_2_op_rx(struct ath10k *ar, struct sk_buff *skb)
 {
 	struct wmi_cmd_hdr *cmd_hdr;
 	enum wmi_10_2_event_id id;
@@ -2981,6 +3605,9 @@
 	case WMI_10_2_READY_EVENTID:
 		ath10k_wmi_event_ready(ar, skb);
 		break;
+	case WMI_10_2_PDEV_TEMPERATURE_EVENTID:
+		ath10k_wmi_event_temperature(ar, skb);
+		break;
 	case WMI_10_2_RTT_KEEPALIVE_EVENTID:
 	case WMI_10_2_GPIO_INPUT_EVENTID:
 	case WMI_10_2_PEER_RATECODE_LIST_EVENTID:
@@ -3001,14 +3628,11 @@
 
 static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
 {
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
-		if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
-			ath10k_wmi_10_2_process_rx(ar, skb);
-		else
-			ath10k_wmi_10x_process_rx(ar, skb);
-	} else {
-		ath10k_wmi_main_process_rx(ar, skb);
-	}
+	int ret;
+
+	ret = ath10k_wmi_rx(ar, skb);
+	if (ret)
+		ath10k_warn(ar, "failed to process wmi rx: %d\n", ret);
 }
 
 int ath10k_wmi_connect(struct ath10k *ar)
@@ -3039,16 +3663,17 @@
 	return 0;
 }
 
-static int ath10k_wmi_main_pdev_set_regdomain(struct ath10k *ar, u16 rd,
-					      u16 rd2g, u16 rd5g, u16 ctl2g,
-					      u16 ctl5g)
+static struct sk_buff *
+ath10k_wmi_op_gen_pdev_set_rd(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 *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_pdev_set_regdomain_cmd *)skb->data;
 	cmd->reg_domain = __cpu_to_le32(rd);
@@ -3060,22 +3685,20 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n",
 		   rd, rd2g, rd5g, ctl2g, ctl5g);
-
-	return ath10k_wmi_cmd_send(ar, skb,
-				   ar->wmi.cmd->pdev_set_regdomain_cmdid);
+	return skb;
 }
 
-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)
+static struct sk_buff *
+ath10k_wmi_10x_op_gen_pdev_set_rd(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(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_pdev_set_regdomain_cmd_10x *)skb->data;
 	cmd->reg_domain = __cpu_to_le32(rd);
@@ -3088,50 +3711,39 @@
 	ath10k_dbg(ar, 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);
+	return skb;
 }
 
-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_suspend_target(struct ath10k *ar, u32 suspend_opt)
+static struct sk_buff *
+ath10k_wmi_op_gen_pdev_suspend(struct ath10k *ar, u32 suspend_opt)
 {
 	struct wmi_pdev_suspend_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_pdev_suspend_cmd *)skb->data;
 	cmd->suspend_opt = __cpu_to_le32(suspend_opt);
 
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_pdev_resume_target(struct ath10k *ar)
+static struct sk_buff *
+ath10k_wmi_op_gen_pdev_resume(struct ath10k *ar)
 {
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, 0);
-	if (skb == NULL)
-		return -ENOMEM;
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
 
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
+static struct sk_buff *
+ath10k_wmi_op_gen_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
 {
 	struct wmi_pdev_set_param_cmd *cmd;
 	struct sk_buff *skb;
@@ -3139,12 +3751,12 @@
 	if (id == WMI_PDEV_PARAM_UNSUPPORTED) {
 		ath10k_warn(ar, "pdev param %d not supported by firmware\n",
 			    id);
-		return -EOPNOTSUPP;
+		return ERR_PTR(-EOPNOTSUPP);
 	}
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_pdev_set_param_cmd *)skb->data;
 	cmd->param_id    = __cpu_to_le32(id);
@@ -3152,11 +3764,11 @@
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n",
 		   id, value);
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid);
+	return skb;
 }
 
-static void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar,
-					   struct wmi_host_mem_chunks *chunks)
+void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar,
+				    struct wmi_host_mem_chunks *chunks)
 {
 	struct host_memory_chunk *chunk;
 	int i;
@@ -3177,7 +3789,7 @@
 	}
 }
 
-static int ath10k_wmi_main_cmd_init(struct ath10k *ar)
+static struct sk_buff *ath10k_wmi_op_gen_init(struct ath10k *ar)
 {
 	struct wmi_init_cmd *cmd;
 	struct sk_buff *buf;
@@ -3240,7 +3852,7 @@
 
 	buf = ath10k_wmi_alloc_skb(ar, len);
 	if (!buf)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_init_cmd *)buf->data;
 
@@ -3248,10 +3860,10 @@
 	ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init\n");
-	return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
+	return buf;
 }
 
-static int ath10k_wmi_10x_cmd_init(struct ath10k *ar)
+static struct sk_buff *ath10k_wmi_10_1_op_gen_init(struct ath10k *ar)
 {
 	struct wmi_init_cmd_10x *cmd;
 	struct sk_buff *buf;
@@ -3306,7 +3918,7 @@
 
 	buf = ath10k_wmi_alloc_skb(ar, len);
 	if (!buf)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_init_cmd_10x *)buf->data;
 
@@ -3314,15 +3926,15 @@
 	ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10x\n");
-	return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
+	return buf;
 }
 
-static int ath10k_wmi_10_2_cmd_init(struct ath10k *ar)
+static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar)
 {
 	struct wmi_init_cmd_10_2 *cmd;
 	struct sk_buff *buf;
 	struct wmi_resource_config_10x config = {};
-	u32 len, val;
+	u32 len, val, features;
 
 	config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
 	config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
@@ -3356,7 +3968,7 @@
 	config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE);
 	config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE);
 	config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES);
-	config.dma_burst_size = __cpu_to_le32(TARGET_10X_DMA_BURST_SIZE);
+	config.dma_burst_size = __cpu_to_le32(TARGET_10_2_DMA_BURST_SIZE);
 	config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM);
 
 	val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK;
@@ -3372,34 +3984,21 @@
 
 	buf = ath10k_wmi_alloc_skb(ar, len);
 	if (!buf)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_init_cmd_10_2 *)buf->data;
 
+	features = WMI_10_2_RX_BATCH_MODE;
+	cmd->resource_config.feature_mask = __cpu_to_le32(features);
+
 	memcpy(&cmd->resource_config.common, &config, sizeof(config));
 	ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10.2\n");
-	return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
+	return buf;
 }
 
-int ath10k_wmi_cmd_init(struct ath10k *ar)
-{
-	int ret;
-
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
-		if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
-			ret = ath10k_wmi_10_2_cmd_init(ar);
-		else
-			ret = ath10k_wmi_10x_cmd_init(ar);
-	} else {
-		ret = ath10k_wmi_main_cmd_init(ar);
-	}
-
-	return ret;
-}
-
-static int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg)
+int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg)
 {
 	if (arg->ie_len && !arg->ie)
 		return -EINVAL;
@@ -3450,9 +4049,8 @@
 	return len;
 }
 
-static void
-ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn,
-				 const struct wmi_start_scan_arg *arg)
+void ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn,
+				      const struct wmi_start_scan_arg *arg)
 {
 	u32 scan_id;
 	u32 scan_req_id;
@@ -3546,46 +4144,60 @@
 	}
 }
 
-int ath10k_wmi_start_scan(struct ath10k *ar,
-			  const struct wmi_start_scan_arg *arg)
+static struct sk_buff *
+ath10k_wmi_op_gen_start_scan(struct ath10k *ar,
+			     const struct wmi_start_scan_arg *arg)
 {
+	struct wmi_start_scan_cmd *cmd;
 	struct sk_buff *skb;
 	size_t len;
 	int ret;
 
 	ret = ath10k_wmi_start_scan_verify(arg);
 	if (ret)
-		return ret;
+		return ERR_PTR(ret);
 
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
-		len = sizeof(struct wmi_10x_start_scan_cmd) +
-		      ath10k_wmi_start_scan_tlvs_len(arg);
-	else
-		len = sizeof(struct wmi_start_scan_cmd) +
-		      ath10k_wmi_start_scan_tlvs_len(arg);
-
+	len = sizeof(*cmd) + ath10k_wmi_start_scan_tlvs_len(arg);
 	skb = ath10k_wmi_alloc_skb(ar, len);
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
-		struct wmi_10x_start_scan_cmd *cmd;
+	cmd = (struct wmi_start_scan_cmd *)skb->data;
 
-		cmd = (struct wmi_10x_start_scan_cmd *)skb->data;
-		ath10k_wmi_put_start_scan_common(&cmd->common, arg);
-		ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
-	} else {
-		struct wmi_start_scan_cmd *cmd;
+	ath10k_wmi_put_start_scan_common(&cmd->common, arg);
+	ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
 
-		cmd = (struct wmi_start_scan_cmd *)skb->data;
-		cmd->burst_duration_ms = __cpu_to_le32(0);
-
-		ath10k_wmi_put_start_scan_common(&cmd->common, arg);
-		ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
-	}
+	cmd->burst_duration_ms = __cpu_to_le32(0);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi start scan\n");
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid);
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_10x_op_gen_start_scan(struct ath10k *ar,
+				 const struct wmi_start_scan_arg *arg)
+{
+	struct wmi_10x_start_scan_cmd *cmd;
+	struct sk_buff *skb;
+	size_t len;
+	int ret;
+
+	ret = ath10k_wmi_start_scan_verify(arg);
+	if (ret)
+		return ERR_PTR(ret);
+
+	len = sizeof(*cmd) + ath10k_wmi_start_scan_tlvs_len(arg);
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	cmd = (struct wmi_10x_start_scan_cmd *)skb->data;
+
+	ath10k_wmi_put_start_scan_common(&cmd->common, arg);
+	ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi 10x start scan\n");
+	return skb;
 }
 
 void ath10k_wmi_start_scan_init(struct ath10k *ar,
@@ -3614,7 +4226,9 @@
 	arg->bssids[0].bssid = "\xFF\xFF\xFF\xFF\xFF\xFF";
 }
 
-int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg)
+static struct sk_buff *
+ath10k_wmi_op_gen_stop_scan(struct ath10k *ar,
+			    const struct wmi_stop_scan_arg *arg)
 {
 	struct wmi_stop_scan_cmd *cmd;
 	struct sk_buff *skb;
@@ -3622,13 +4236,13 @@
 	u32 req_id;
 
 	if (arg->req_id > 0xFFF)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 	if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	scan_id = arg->u.scan_id;
 	scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX;
@@ -3645,20 +4259,21 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n",
 		   arg->req_id, arg->req_type, arg->u.scan_id);
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
-			   enum wmi_vdev_type type,
-			   enum wmi_vdev_subtype subtype,
-			   const u8 macaddr[ETH_ALEN])
+static struct sk_buff *
+ath10k_wmi_op_gen_vdev_create(struct ath10k *ar, u32 vdev_id,
+			      enum wmi_vdev_type type,
+			      enum wmi_vdev_subtype subtype,
+			      const u8 macaddr[ETH_ALEN])
 {
 	struct wmi_vdev_create_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_vdev_create_cmd *)skb->data;
 	cmd->vdev_id      = __cpu_to_le32(vdev_id);
@@ -3669,58 +4284,52 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "WMI vdev create: id %d type %d subtype %d macaddr %pM\n",
 		   vdev_id, type, subtype, macaddr);
-
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
+static struct sk_buff *
+ath10k_wmi_op_gen_vdev_delete(struct ath10k *ar, u32 vdev_id)
 {
 	struct wmi_vdev_delete_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_vdev_delete_cmd *)skb->data;
 	cmd->vdev_id = __cpu_to_le32(vdev_id);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "WMI vdev delete id %d\n", vdev_id);
-
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid);
+	return skb;
 }
 
-static int
-ath10k_wmi_vdev_start_restart(struct ath10k *ar,
-			      const struct wmi_vdev_start_request_arg *arg,
-			      u32 cmd_id)
+static struct sk_buff *
+ath10k_wmi_op_gen_vdev_start(struct ath10k *ar,
+			     const struct wmi_vdev_start_request_arg *arg,
+			     bool restart)
 {
 	struct wmi_vdev_start_request_cmd *cmd;
 	struct sk_buff *skb;
 	const char *cmdname;
 	u32 flags = 0;
 
-	if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
-	    cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid)
-		return -EINVAL;
 	if (WARN_ON(arg->ssid && arg->ssid_len == 0))
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 	if (WARN_ON(arg->hidden_ssid && !arg->ssid))
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 	if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid)))
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
-	if (cmd_id == ar->wmi.cmd->vdev_start_request_cmdid)
-		cmdname = "start";
-	else if (cmd_id == ar->wmi.cmd->vdev_restart_request_cmdid)
+	if (restart)
 		cmdname = "restart";
 	else
-		return -EINVAL; /* should not happen, we already check cmd_id */
+		cmdname = "start";
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	if (arg->hidden_ssid)
 		flags |= WMI_VDEV_START_HIDDEN_SSID;
@@ -3749,50 +4358,36 @@
 		   flags, arg->channel.freq, arg->channel.mode,
 		   cmd->chan.flags, arg->channel.max_power);
 
-	return ath10k_wmi_cmd_send(ar, skb, cmd_id);
+	return skb;
 }
 
-int ath10k_wmi_vdev_start(struct ath10k *ar,
-			  const struct wmi_vdev_start_request_arg *arg)
-{
-	u32 cmd_id = ar->wmi.cmd->vdev_start_request_cmdid;
-
-	return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
-}
-
-int ath10k_wmi_vdev_restart(struct ath10k *ar,
-			    const struct wmi_vdev_start_request_arg *arg)
-{
-	u32 cmd_id = ar->wmi.cmd->vdev_restart_request_cmdid;
-
-	return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
-}
-
-int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
+static struct sk_buff *
+ath10k_wmi_op_gen_vdev_stop(struct ath10k *ar, u32 vdev_id)
 {
 	struct wmi_vdev_stop_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_vdev_stop_cmd *)skb->data;
 	cmd->vdev_id = __cpu_to_le32(vdev_id);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id);
-
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
+static struct sk_buff *
+ath10k_wmi_op_gen_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
+			  const u8 *bssid)
 {
 	struct wmi_vdev_up_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_vdev_up_cmd *)skb->data;
 	cmd->vdev_id       = __cpu_to_le32(vdev_id);
@@ -3802,30 +4397,30 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n",
 		   vdev_id, aid, bssid);
-
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
+static struct sk_buff *
+ath10k_wmi_op_gen_vdev_down(struct ath10k *ar, u32 vdev_id)
 {
 	struct wmi_vdev_down_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_vdev_down_cmd *)skb->data;
 	cmd->vdev_id = __cpu_to_le32(vdev_id);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi mgmt vdev down id 0x%x\n", vdev_id);
-
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
-			      u32 param_id, u32 param_value)
+static struct sk_buff *
+ath10k_wmi_op_gen_vdev_set_param(struct ath10k *ar, u32 vdev_id,
+				 u32 param_id, u32 param_value)
 {
 	struct wmi_vdev_set_param_cmd *cmd;
 	struct sk_buff *skb;
@@ -3834,12 +4429,12 @@
 		ath10k_dbg(ar, ATH10K_DBG_WMI,
 			   "vdev param %d not supported by firmware\n",
 			    param_id);
-		return -EOPNOTSUPP;
+		return ERR_PTR(-EOPNOTSUPP);
 	}
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_vdev_set_param_cmd *)skb->data;
 	cmd->vdev_id     = __cpu_to_le32(vdev_id);
@@ -3849,24 +4444,24 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi vdev id 0x%x set param %d value %d\n",
 		   vdev_id, param_id, param_value);
-
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_vdev_install_key(struct ath10k *ar,
-				const struct wmi_vdev_install_key_arg *arg)
+static struct sk_buff *
+ath10k_wmi_op_gen_vdev_install_key(struct ath10k *ar,
+				   const struct wmi_vdev_install_key_arg *arg)
 {
 	struct wmi_vdev_install_key_cmd *cmd;
 	struct sk_buff *skb;
 
 	if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 	if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd) + arg->key_len);
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_vdev_install_key_cmd *)skb->data;
 	cmd->vdev_id       = __cpu_to_le32(arg->vdev_id);
@@ -3885,20 +4480,19 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi vdev install key idx %d cipher %d len %d\n",
 		   arg->key_idx, arg->key_cipher, arg->key_len);
-	return ath10k_wmi_cmd_send(ar, skb,
-				   ar->wmi.cmd->vdev_install_key_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_vdev_spectral_conf(struct ath10k *ar,
-				  const struct wmi_vdev_spectral_conf_arg *arg)
+static struct sk_buff *
+ath10k_wmi_op_gen_vdev_spectral_conf(struct ath10k *ar,
+				     const struct wmi_vdev_spectral_conf_arg *arg)
 {
 	struct wmi_vdev_spectral_conf_cmd *cmd;
 	struct sk_buff *skb;
-	u32 cmdid;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_vdev_spectral_conf_cmd *)skb->data;
 	cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
@@ -3921,39 +4515,38 @@
 	cmd->scan_dbm_adj = __cpu_to_le32(arg->scan_dbm_adj);
 	cmd->scan_chn_mask = __cpu_to_le32(arg->scan_chn_mask);
 
-	cmdid = ar->wmi.cmd->vdev_spectral_scan_configure_cmdid;
-	return ath10k_wmi_cmd_send(ar, skb, cmdid);
+	return skb;
 }
 
-int ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger,
-				    u32 enable)
+static struct sk_buff *
+ath10k_wmi_op_gen_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id,
+				       u32 trigger, u32 enable)
 {
 	struct wmi_vdev_spectral_enable_cmd *cmd;
 	struct sk_buff *skb;
-	u32 cmdid;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_vdev_spectral_enable_cmd *)skb->data;
 	cmd->vdev_id = __cpu_to_le32(vdev_id);
 	cmd->trigger_cmd = __cpu_to_le32(trigger);
 	cmd->enable_cmd = __cpu_to_le32(enable);
 
-	cmdid = ar->wmi.cmd->vdev_spectral_scan_enable_cmdid;
-	return ath10k_wmi_cmd_send(ar, skb, cmdid);
+	return skb;
 }
 
-int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
-			   const u8 peer_addr[ETH_ALEN])
+static struct sk_buff *
+ath10k_wmi_op_gen_peer_create(struct ath10k *ar, u32 vdev_id,
+			      const u8 peer_addr[ETH_ALEN])
 {
 	struct wmi_peer_create_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_peer_create_cmd *)skb->data;
 	cmd->vdev_id = __cpu_to_le32(vdev_id);
@@ -3962,18 +4555,19 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi peer create vdev_id %d peer_addr %pM\n",
 		   vdev_id, peer_addr);
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
-			   const u8 peer_addr[ETH_ALEN])
+static struct sk_buff *
+ath10k_wmi_op_gen_peer_delete(struct ath10k *ar, u32 vdev_id,
+			      const u8 peer_addr[ETH_ALEN])
 {
 	struct wmi_peer_delete_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_peer_delete_cmd *)skb->data;
 	cmd->vdev_id = __cpu_to_le32(vdev_id);
@@ -3982,18 +4576,19 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi peer delete vdev_id %d peer_addr %pM\n",
 		   vdev_id, peer_addr);
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
-			  const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
+static struct sk_buff *
+ath10k_wmi_op_gen_peer_flush(struct ath10k *ar, u32 vdev_id,
+			     const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
 {
 	struct wmi_peer_flush_tids_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_peer_flush_tids_cmd *)skb->data;
 	cmd->vdev_id         = __cpu_to_le32(vdev_id);
@@ -4003,19 +4598,21 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n",
 		   vdev_id, peer_addr, tid_bitmap);
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
-			      const u8 *peer_addr, enum wmi_peer_param param_id,
-			      u32 param_value)
+static struct sk_buff *
+ath10k_wmi_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id,
+				 const u8 *peer_addr,
+				 enum wmi_peer_param param_id,
+				 u32 param_value)
 {
 	struct wmi_peer_set_param_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_peer_set_param_cmd *)skb->data;
 	cmd->vdev_id     = __cpu_to_le32(vdev_id);
@@ -4026,19 +4623,19 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi vdev %d peer 0x%pM set param %d value %d\n",
 		   vdev_id, peer_addr, param_id, param_value);
-
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
-			  enum wmi_sta_ps_mode psmode)
+static struct sk_buff *
+ath10k_wmi_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id,
+			     enum wmi_sta_ps_mode psmode)
 {
 	struct wmi_sta_powersave_mode_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_sta_powersave_mode_cmd *)skb->data;
 	cmd->vdev_id     = __cpu_to_le32(vdev_id);
@@ -4047,21 +4644,20 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi set powersave id 0x%x mode %d\n",
 		   vdev_id, psmode);
-
-	return ath10k_wmi_cmd_send(ar, skb,
-				   ar->wmi.cmd->sta_powersave_mode_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
-				enum wmi_sta_powersave_param param_id,
-				u32 value)
+static struct sk_buff *
+ath10k_wmi_op_gen_set_sta_ps(struct ath10k *ar, u32 vdev_id,
+			     enum wmi_sta_powersave_param param_id,
+			     u32 value)
 {
 	struct wmi_sta_powersave_param_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_sta_powersave_param_cmd *)skb->data;
 	cmd->vdev_id     = __cpu_to_le32(vdev_id);
@@ -4071,22 +4667,22 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi sta ps param vdev_id 0x%x param %d value %d\n",
 		   vdev_id, param_id, value);
-	return ath10k_wmi_cmd_send(ar, skb,
-				   ar->wmi.cmd->sta_powersave_param_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
-			       enum wmi_ap_ps_peer_param param_id, u32 value)
+static struct sk_buff *
+ath10k_wmi_op_gen_set_ap_ps(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+			    enum wmi_ap_ps_peer_param param_id, u32 value)
 {
 	struct wmi_ap_ps_peer_cmd *cmd;
 	struct sk_buff *skb;
 
 	if (!mac)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_ap_ps_peer_cmd *)skb->data;
 	cmd->vdev_id = __cpu_to_le32(vdev_id);
@@ -4097,13 +4693,12 @@
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n",
 		   vdev_id, param_id, value, mac);
-
-	return ath10k_wmi_cmd_send(ar, skb,
-				   ar->wmi.cmd->ap_ps_peer_param_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_scan_chan_list(struct ath10k *ar,
-			      const struct wmi_scan_chan_list_arg *arg)
+static struct sk_buff *
+ath10k_wmi_op_gen_scan_chan_list(struct ath10k *ar,
+				 const struct wmi_scan_chan_list_arg *arg)
 {
 	struct wmi_scan_chan_list_cmd *cmd;
 	struct sk_buff *skb;
@@ -4116,7 +4711,7 @@
 
 	skb = ath10k_wmi_alloc_skb(ar, len);
 	if (!skb)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	cmd = (struct wmi_scan_chan_list_cmd *)skb->data;
 	cmd->num_scan_chans = __cpu_to_le32(arg->n_channels);
@@ -4128,7 +4723,7 @@
 		ath10k_wmi_put_wmi_channel(ci, ch);
 	}
 
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid);
+	return skb;
 }
 
 static void
@@ -4209,12 +4804,9 @@
 	cmd->info0 = __cpu_to_le32(info0);
 }
 
-int ath10k_wmi_peer_assoc(struct ath10k *ar,
-			  const struct wmi_peer_assoc_complete_arg *arg)
+static int
+ath10k_wmi_peer_assoc_check_arg(const struct wmi_peer_assoc_complete_arg *arg)
 {
-	struct sk_buff *skb;
-	int len;
-
 	if (arg->peer_mpdu_density > 16)
 		return -EINVAL;
 	if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES)
@@ -4222,79 +4814,135 @@
 	if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES)
 		return -EINVAL;
 
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
-		if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
-			len = sizeof(struct wmi_10_2_peer_assoc_complete_cmd);
-		else
-			len = sizeof(struct wmi_10_1_peer_assoc_complete_cmd);
-	} else {
-		len = sizeof(struct wmi_main_peer_assoc_complete_cmd);
-	}
+	return 0;
+}
+
+static struct sk_buff *
+ath10k_wmi_op_gen_peer_assoc(struct ath10k *ar,
+			     const struct wmi_peer_assoc_complete_arg *arg)
+{
+	size_t len = sizeof(struct wmi_main_peer_assoc_complete_cmd);
+	struct sk_buff *skb;
+	int ret;
+
+	ret = ath10k_wmi_peer_assoc_check_arg(arg);
+	if (ret)
+		return ERR_PTR(ret);
 
 	skb = ath10k_wmi_alloc_skb(ar, len);
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
-		if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
-			ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg);
-		else
-			ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg);
-	} else {
-		ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg);
-	}
+	ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI,
 		   "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);
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_10_1_op_gen_peer_assoc(struct ath10k *ar,
+				  const struct wmi_peer_assoc_complete_arg *arg)
+{
+	size_t len = sizeof(struct wmi_10_1_peer_assoc_complete_cmd);
+	struct sk_buff *skb;
+	int ret;
+
+	ret = ath10k_wmi_peer_assoc_check_arg(arg);
+	if (ret)
+		return ERR_PTR(ret);
+
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI,
+		   "wmi peer assoc vdev %d addr %pM (%s)\n",
+		   arg->vdev_id, arg->addr,
+		   arg->peer_reassoc ? "reassociate" : "new");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_10_2_op_gen_peer_assoc(struct ath10k *ar,
+				  const struct wmi_peer_assoc_complete_arg *arg)
+{
+	size_t len = sizeof(struct wmi_10_2_peer_assoc_complete_cmd);
+	struct sk_buff *skb;
+	int ret;
+
+	ret = ath10k_wmi_peer_assoc_check_arg(arg);
+	if (ret)
+		return ERR_PTR(ret);
+
+	skb = ath10k_wmi_alloc_skb(ar, len);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI,
+		   "wmi peer assoc vdev %d addr %pM (%s)\n",
+		   arg->vdev_id, arg->addr,
+		   arg->peer_reassoc ? "reassociate" : "new");
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_10_2_op_gen_pdev_get_temperature(struct ath10k *ar)
+{
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, 0);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev get temperature\n");
+	return skb;
 }
 
 /* This function assumes the beacon is already DMA mapped */
-int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif)
+static struct sk_buff *
+ath10k_wmi_op_gen_beacon_dma(struct ath10k *ar, u32 vdev_id, const void *bcn,
+			     size_t bcn_len, u32 bcn_paddr, bool dtim_zero,
+			     bool deliver_cab)
 {
 	struct wmi_bcn_tx_ref_cmd *cmd;
 	struct sk_buff *skb;
-	struct sk_buff *beacon = arvif->beacon;
-	struct ath10k *ar = arvif->ar;
 	struct ieee80211_hdr *hdr;
-	int ret;
 	u16 fc;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
-	hdr = (struct ieee80211_hdr *)beacon->data;
+	hdr = (struct ieee80211_hdr *)bcn;
 	fc = le16_to_cpu(hdr->frame_control);
 
 	cmd = (struct wmi_bcn_tx_ref_cmd *)skb->data;
-	cmd->vdev_id = __cpu_to_le32(arvif->vdev_id);
-	cmd->data_len = __cpu_to_le32(beacon->len);
-	cmd->data_ptr = __cpu_to_le32(ATH10K_SKB_CB(beacon)->paddr);
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	cmd->data_len = __cpu_to_le32(bcn_len);
+	cmd->data_ptr = __cpu_to_le32(bcn_paddr);
 	cmd->msdu_id = 0;
 	cmd->frame_control = __cpu_to_le32(fc);
 	cmd->flags = 0;
 	cmd->antenna_mask = __cpu_to_le32(WMI_BCN_TX_REF_DEF_ANTENNA);
 
-	if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero)
+	if (dtim_zero)
 		cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO);
 
-	if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab)
+	if (deliver_cab)
 		cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB);
 
-	ret = ath10k_wmi_cmd_send_nowait(ar, skb,
-					 ar->wmi.cmd->pdev_send_bcn_cmdid);
-
-	if (ret)
-		dev_kfree_skb(skb);
-
-	return ret;
+	return skb;
 }
 
-static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
-					  const struct wmi_wmm_params_arg *arg)
+void ath10k_wmi_set_wmm_param(struct wmi_wmm_params *params,
+			      const struct wmi_wmm_params_arg *arg)
 {
 	params->cwmin  = __cpu_to_le32(arg->cwmin);
 	params->cwmax  = __cpu_to_le32(arg->cwmax);
@@ -4304,52 +4952,55 @@
 	params->no_ack = __cpu_to_le32(arg->no_ack);
 }
 
-int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
-				   const struct wmi_pdev_set_wmm_params_arg *arg)
+static struct sk_buff *
+ath10k_wmi_op_gen_pdev_set_wmm(struct ath10k *ar,
+			       const struct wmi_wmm_params_all_arg *arg)
 {
 	struct wmi_pdev_set_wmm_params *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_pdev_set_wmm_params *)skb->data;
-	ath10k_wmi_pdev_set_wmm_param(&cmd->ac_be, &arg->ac_be);
-	ath10k_wmi_pdev_set_wmm_param(&cmd->ac_bk, &arg->ac_bk);
-	ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vi, &arg->ac_vi);
-	ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
+	ath10k_wmi_set_wmm_param(&cmd->ac_be, &arg->ac_be);
+	ath10k_wmi_set_wmm_param(&cmd->ac_bk, &arg->ac_bk);
+	ath10k_wmi_set_wmm_param(&cmd->ac_vi, &arg->ac_vi);
+	ath10k_wmi_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set wmm params\n");
-	return ath10k_wmi_cmd_send(ar, skb,
-				   ar->wmi.cmd->pdev_set_wmm_params_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
+static struct sk_buff *
+ath10k_wmi_op_gen_request_stats(struct ath10k *ar, u32 stats_mask)
 {
 	struct wmi_request_stats_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_request_stats_cmd *)skb->data;
-	cmd->stats_id = __cpu_to_le32(stats_id);
+	cmd->stats_id = __cpu_to_le32(stats_mask);
 
-	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid);
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi request stats 0x%08x\n",
+		   stats_mask);
+	return skb;
 }
 
-int ath10k_wmi_force_fw_hang(struct ath10k *ar,
-			     enum wmi_force_fw_hang_type type, u32 delay_ms)
+static struct sk_buff *
+ath10k_wmi_op_gen_force_fw_hang(struct ath10k *ar,
+				enum wmi_force_fw_hang_type type, u32 delay_ms)
 {
 	struct wmi_force_fw_hang_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_force_fw_hang_cmd *)skb->data;
 	cmd->type = __cpu_to_le32(type);
@@ -4357,10 +5008,12 @@
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
 		   type, delay_ms);
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable)
+static struct sk_buff *
+ath10k_wmi_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable,
+			     u32 log_level)
 {
 	struct wmi_dbglog_cfg_cmd *cmd;
 	struct sk_buff *skb;
@@ -4368,12 +5021,12 @@
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	cmd = (struct wmi_dbglog_cfg_cmd *)skb->data;
 
 	if (module_enable) {
-		cfg = SM(ATH10K_DBGLOG_LEVEL_VERBOSE,
+		cfg = SM(log_level,
 			 ATH10K_DBGLOG_CFG_LOG_LVL);
 	} else {
 		/* set back defaults, all modules with WARN level */
@@ -4393,57 +5046,449 @@
 		   __le32_to_cpu(cmd->module_valid),
 		   __le32_to_cpu(cmd->config_enable),
 		   __le32_to_cpu(cmd->config_valid));
-
-	return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid);
+	return skb;
 }
 
-int ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 ev_bitmap)
+static struct sk_buff *
+ath10k_wmi_op_gen_pktlog_enable(struct ath10k *ar, u32 ev_bitmap)
 {
 	struct wmi_pdev_pktlog_enable_cmd *cmd;
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	ev_bitmap &= ATH10K_PKTLOG_ANY;
-	ath10k_dbg(ar, ATH10K_DBG_WMI,
-		   "wmi enable pktlog filter:%x\n", ev_bitmap);
 
 	cmd = (struct wmi_pdev_pktlog_enable_cmd *)skb->data;
 	cmd->ev_bitmap = __cpu_to_le32(ev_bitmap);
-	return ath10k_wmi_cmd_send(ar, skb,
-				   ar->wmi.cmd->pdev_pktlog_enable_cmdid);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi enable pktlog filter 0x%08x\n",
+		   ev_bitmap);
+	return skb;
 }
 
-int ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar)
+static struct sk_buff *
+ath10k_wmi_op_gen_pktlog_disable(struct ath10k *ar)
 {
 	struct sk_buff *skb;
 
 	skb = ath10k_wmi_alloc_skb(ar, 0);
 	if (!skb)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi disable pktlog\n");
-
-	return ath10k_wmi_cmd_send(ar, skb,
-				   ar->wmi.cmd->pdev_pktlog_disable_cmdid);
+	return skb;
 }
 
+static struct sk_buff *
+ath10k_wmi_op_gen_pdev_set_quiet_mode(struct ath10k *ar, u32 period,
+				      u32 duration, u32 next_offset,
+				      u32 enabled)
+{
+	struct wmi_pdev_set_quiet_cmd *cmd;
+	struct sk_buff *skb;
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	cmd = (struct wmi_pdev_set_quiet_cmd *)skb->data;
+	cmd->period = __cpu_to_le32(period);
+	cmd->duration = __cpu_to_le32(duration);
+	cmd->next_start = __cpu_to_le32(next_offset);
+	cmd->enabled = __cpu_to_le32(enabled);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI,
+		   "wmi quiet param: period %u duration %u enabled %d\n",
+		   period, duration, enabled);
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_op_gen_addba_clear_resp(struct ath10k *ar, u32 vdev_id,
+				   const u8 *mac)
+{
+	struct wmi_addba_clear_resp_cmd *cmd;
+	struct sk_buff *skb;
+
+	if (!mac)
+		return ERR_PTR(-EINVAL);
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	cmd = (struct wmi_addba_clear_resp_cmd *)skb->data;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	ether_addr_copy(cmd->peer_macaddr.addr, mac);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI,
+		   "wmi addba clear resp vdev_id 0x%X mac_addr %pM\n",
+		   vdev_id, mac);
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_op_gen_addba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+			     u32 tid, u32 buf_size)
+{
+	struct wmi_addba_send_cmd *cmd;
+	struct sk_buff *skb;
+
+	if (!mac)
+		return ERR_PTR(-EINVAL);
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	cmd = (struct wmi_addba_send_cmd *)skb->data;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	ether_addr_copy(cmd->peer_macaddr.addr, mac);
+	cmd->tid = __cpu_to_le32(tid);
+	cmd->buffersize = __cpu_to_le32(buf_size);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI,
+		   "wmi addba send vdev_id 0x%X mac_addr %pM tid %u bufsize %u\n",
+		   vdev_id, mac, tid, buf_size);
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_op_gen_addba_set_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+				 u32 tid, u32 status)
+{
+	struct wmi_addba_setresponse_cmd *cmd;
+	struct sk_buff *skb;
+
+	if (!mac)
+		return ERR_PTR(-EINVAL);
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	cmd = (struct wmi_addba_setresponse_cmd *)skb->data;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	ether_addr_copy(cmd->peer_macaddr.addr, mac);
+	cmd->tid = __cpu_to_le32(tid);
+	cmd->statuscode = __cpu_to_le32(status);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI,
+		   "wmi addba set resp vdev_id 0x%X mac_addr %pM tid %u status %u\n",
+		   vdev_id, mac, tid, status);
+	return skb;
+}
+
+static struct sk_buff *
+ath10k_wmi_op_gen_delba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+			     u32 tid, u32 initiator, u32 reason)
+{
+	struct wmi_delba_send_cmd *cmd;
+	struct sk_buff *skb;
+
+	if (!mac)
+		return ERR_PTR(-EINVAL);
+
+	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	cmd = (struct wmi_delba_send_cmd *)skb->data;
+	cmd->vdev_id = __cpu_to_le32(vdev_id);
+	ether_addr_copy(cmd->peer_macaddr.addr, mac);
+	cmd->tid = __cpu_to_le32(tid);
+	cmd->initiator = __cpu_to_le32(initiator);
+	cmd->reasoncode = __cpu_to_le32(reason);
+
+	ath10k_dbg(ar, ATH10K_DBG_WMI,
+		   "wmi delba send vdev_id 0x%X mac_addr %pM tid %u initiator %u reason %u\n",
+		   vdev_id, mac, tid, initiator, reason);
+	return skb;
+}
+
+static const struct wmi_ops wmi_ops = {
+	.rx = ath10k_wmi_op_rx,
+	.map_svc = wmi_main_svc_map,
+
+	.pull_scan = ath10k_wmi_op_pull_scan_ev,
+	.pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
+	.pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
+	.pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
+	.pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
+	.pull_swba = ath10k_wmi_op_pull_swba_ev,
+	.pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
+	.pull_svc_rdy = ath10k_wmi_main_op_pull_svc_rdy_ev,
+	.pull_rdy = ath10k_wmi_op_pull_rdy_ev,
+	.pull_fw_stats = ath10k_wmi_main_op_pull_fw_stats,
+
+	.gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
+	.gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
+	.gen_pdev_set_rd = ath10k_wmi_op_gen_pdev_set_rd,
+	.gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
+	.gen_init = ath10k_wmi_op_gen_init,
+	.gen_start_scan = ath10k_wmi_op_gen_start_scan,
+	.gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
+	.gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
+	.gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
+	.gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
+	.gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
+	.gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
+	.gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
+	.gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
+	.gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
+	.gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
+	.gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
+	/* .gen_vdev_wmm_conf not implemented */
+	.gen_peer_create = ath10k_wmi_op_gen_peer_create,
+	.gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
+	.gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
+	.gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
+	.gen_peer_assoc = ath10k_wmi_op_gen_peer_assoc,
+	.gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
+	.gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
+	.gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
+	.gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
+	.gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
+	.gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
+	.gen_request_stats = ath10k_wmi_op_gen_request_stats,
+	.gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
+	.gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
+	.gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
+	.gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
+	.gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
+	.gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
+	/* .gen_pdev_get_temperature not implemented */
+	.gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
+	.gen_addba_send = ath10k_wmi_op_gen_addba_send,
+	.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
+	.gen_delba_send = ath10k_wmi_op_gen_delba_send,
+	/* .gen_bcn_tmpl not implemented */
+	/* .gen_prb_tmpl not implemented */
+	/* .gen_p2p_go_bcn_ie not implemented */
+};
+
+static const struct wmi_ops wmi_10_1_ops = {
+	.rx = ath10k_wmi_10_1_op_rx,
+	.map_svc = wmi_10x_svc_map,
+	.pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev,
+	.pull_fw_stats = ath10k_wmi_10x_op_pull_fw_stats,
+	.gen_init = ath10k_wmi_10_1_op_gen_init,
+	.gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd,
+	.gen_start_scan = ath10k_wmi_10x_op_gen_start_scan,
+	.gen_peer_assoc = ath10k_wmi_10_1_op_gen_peer_assoc,
+	/* .gen_pdev_get_temperature not implemented */
+
+	/* shared with main branch */
+	.pull_scan = ath10k_wmi_op_pull_scan_ev,
+	.pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
+	.pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
+	.pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
+	.pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
+	.pull_swba = ath10k_wmi_op_pull_swba_ev,
+	.pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
+	.pull_rdy = ath10k_wmi_op_pull_rdy_ev,
+
+	.gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
+	.gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
+	.gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
+	.gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
+	.gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
+	.gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
+	.gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
+	.gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
+	.gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
+	.gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
+	.gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
+	.gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
+	.gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
+	.gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
+	/* .gen_vdev_wmm_conf not implemented */
+	.gen_peer_create = ath10k_wmi_op_gen_peer_create,
+	.gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
+	.gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
+	.gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
+	.gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
+	.gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
+	.gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
+	.gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
+	.gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
+	.gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
+	.gen_request_stats = ath10k_wmi_op_gen_request_stats,
+	.gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
+	.gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
+	.gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
+	.gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
+	.gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
+	.gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
+	.gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
+	.gen_addba_send = ath10k_wmi_op_gen_addba_send,
+	.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
+	.gen_delba_send = ath10k_wmi_op_gen_delba_send,
+	/* .gen_bcn_tmpl not implemented */
+	/* .gen_prb_tmpl not implemented */
+	/* .gen_p2p_go_bcn_ie not implemented */
+};
+
+static const struct wmi_ops wmi_10_2_ops = {
+	.rx = ath10k_wmi_10_2_op_rx,
+	.pull_fw_stats = ath10k_wmi_10_2_op_pull_fw_stats,
+	.gen_init = ath10k_wmi_10_2_op_gen_init,
+	.gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc,
+	/* .gen_pdev_get_temperature not implemented */
+
+	/* shared with 10.1 */
+	.map_svc = wmi_10x_svc_map,
+	.pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev,
+	.gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd,
+	.gen_start_scan = ath10k_wmi_10x_op_gen_start_scan,
+
+	.pull_scan = ath10k_wmi_op_pull_scan_ev,
+	.pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
+	.pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
+	.pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
+	.pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
+	.pull_swba = ath10k_wmi_op_pull_swba_ev,
+	.pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
+	.pull_rdy = ath10k_wmi_op_pull_rdy_ev,
+
+	.gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
+	.gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
+	.gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
+	.gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
+	.gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
+	.gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
+	.gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
+	.gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
+	.gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
+	.gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
+	.gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
+	.gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
+	.gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
+	.gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
+	/* .gen_vdev_wmm_conf not implemented */
+	.gen_peer_create = ath10k_wmi_op_gen_peer_create,
+	.gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
+	.gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
+	.gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
+	.gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
+	.gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
+	.gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
+	.gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
+	.gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
+	.gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
+	.gen_request_stats = ath10k_wmi_op_gen_request_stats,
+	.gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
+	.gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
+	.gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
+	.gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
+	.gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
+	.gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
+	.gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
+	.gen_addba_send = ath10k_wmi_op_gen_addba_send,
+	.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
+	.gen_delba_send = ath10k_wmi_op_gen_delba_send,
+};
+
+static const struct wmi_ops wmi_10_2_4_ops = {
+	.rx = ath10k_wmi_10_2_op_rx,
+	.pull_fw_stats = ath10k_wmi_10_2_4_op_pull_fw_stats,
+	.gen_init = ath10k_wmi_10_2_op_gen_init,
+	.gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc,
+	.gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature,
+
+	/* shared with 10.1 */
+	.map_svc = wmi_10x_svc_map,
+	.pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev,
+	.gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd,
+	.gen_start_scan = ath10k_wmi_10x_op_gen_start_scan,
+
+	.pull_scan = ath10k_wmi_op_pull_scan_ev,
+	.pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
+	.pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
+	.pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
+	.pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
+	.pull_swba = ath10k_wmi_op_pull_swba_ev,
+	.pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
+	.pull_rdy = ath10k_wmi_op_pull_rdy_ev,
+
+	.gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
+	.gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
+	.gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
+	.gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
+	.gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
+	.gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
+	.gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
+	.gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
+	.gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
+	.gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
+	.gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
+	.gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
+	.gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
+	.gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
+	.gen_peer_create = ath10k_wmi_op_gen_peer_create,
+	.gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
+	.gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
+	.gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
+	.gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
+	.gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
+	.gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
+	.gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
+	.gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
+	.gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
+	.gen_request_stats = ath10k_wmi_op_gen_request_stats,
+	.gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
+	.gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
+	.gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
+	.gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
+	.gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
+	.gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
+	.gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
+	.gen_addba_send = ath10k_wmi_op_gen_addba_send,
+	.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
+	.gen_delba_send = ath10k_wmi_op_gen_delba_send,
+	/* .gen_bcn_tmpl not implemented */
+	/* .gen_prb_tmpl not implemented */
+	/* .gen_p2p_go_bcn_ie not implemented */
+};
+
 int ath10k_wmi_attach(struct ath10k *ar)
 {
-	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
-		if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
-			ar->wmi.cmd = &wmi_10_2_cmd_map;
-		else
-			ar->wmi.cmd = &wmi_10x_cmd_map;
-
+	switch (ar->wmi.op_version) {
+	case ATH10K_FW_WMI_OP_VERSION_10_2_4:
+		ar->wmi.cmd = &wmi_10_2_4_cmd_map;
+		ar->wmi.ops = &wmi_10_2_4_ops;
+		ar->wmi.vdev_param = &wmi_10_2_4_vdev_param_map;
+		ar->wmi.pdev_param = &wmi_10_2_4_pdev_param_map;
+		break;
+	case ATH10K_FW_WMI_OP_VERSION_10_2:
+		ar->wmi.cmd = &wmi_10_2_cmd_map;
+		ar->wmi.ops = &wmi_10_2_ops;
 		ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
 		ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
-	} else {
+		break;
+	case ATH10K_FW_WMI_OP_VERSION_10_1:
+		ar->wmi.cmd = &wmi_10x_cmd_map;
+		ar->wmi.ops = &wmi_10_1_ops;
+		ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
+		ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
+		break;
+	case ATH10K_FW_WMI_OP_VERSION_MAIN:
 		ar->wmi.cmd = &wmi_cmd_map;
+		ar->wmi.ops = &wmi_ops;
 		ar->wmi.vdev_param = &wmi_vdev_param_map;
 		ar->wmi.pdev_param = &wmi_pdev_param_map;
+		break;
+	case ATH10K_FW_WMI_OP_VERSION_TLV:
+		ath10k_wmi_tlv_attach(ar);
+		break;
+	case ATH10K_FW_WMI_OP_VERSION_UNSET:
+	case ATH10K_FW_WMI_OP_VERSION_MAX:
+		ath10k_err(ar, "unsupported WMI op version: %d\n",
+			   ar->wmi.op_version);
+		return -EINVAL;
 	}
 
 	init_completion(&ar->wmi.service_ready);
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 2139192..adf935b 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -109,6 +109,45 @@
 	WMI_SERVICE_BURST,
 	WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT,
 	WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT,
+	WMI_SERVICE_ROAM_SCAN_OFFLOAD,
+	WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC,
+	WMI_SERVICE_EARLY_RX,
+	WMI_SERVICE_STA_SMPS,
+	WMI_SERVICE_FWTEST,
+	WMI_SERVICE_STA_WMMAC,
+	WMI_SERVICE_TDLS,
+	WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE,
+	WMI_SERVICE_ADAPTIVE_OCS,
+	WMI_SERVICE_BA_SSN_SUPPORT,
+	WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE,
+	WMI_SERVICE_WLAN_HB,
+	WMI_SERVICE_LTE_ANT_SHARE_SUPPORT,
+	WMI_SERVICE_BATCH_SCAN,
+	WMI_SERVICE_QPOWER,
+	WMI_SERVICE_PLMREQ,
+	WMI_SERVICE_THERMAL_MGMT,
+	WMI_SERVICE_RMC,
+	WMI_SERVICE_MHF_OFFLOAD,
+	WMI_SERVICE_COEX_SAR,
+	WMI_SERVICE_BCN_TXRATE_OVERRIDE,
+	WMI_SERVICE_NAN,
+	WMI_SERVICE_L1SS_STAT,
+	WMI_SERVICE_ESTIMATE_LINKSPEED,
+	WMI_SERVICE_OBSS_SCAN,
+	WMI_SERVICE_TDLS_OFFCHAN,
+	WMI_SERVICE_TDLS_UAPSD_BUFFER_STA,
+	WMI_SERVICE_TDLS_UAPSD_SLEEP_STA,
+	WMI_SERVICE_IBSS_PWRSAVE,
+	WMI_SERVICE_LPASS,
+	WMI_SERVICE_EXTSCAN,
+	WMI_SERVICE_D0WOW,
+	WMI_SERVICE_HSOFFLOAD,
+	WMI_SERVICE_ROAM_HO_OFFLOAD,
+	WMI_SERVICE_RX_FULL_REORDER,
+	WMI_SERVICE_DHCP_OFFLOAD,
+	WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT,
+	WMI_SERVICE_MDNS_OFFLOAD,
+	WMI_SERVICE_SAP_AUTH_OFFLOAD,
 
 	/* keep last */
 	WMI_SERVICE_MAX,
@@ -215,6 +254,45 @@
 	SVCSTR(WMI_SERVICE_BURST);
 	SVCSTR(WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT);
 	SVCSTR(WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT);
+	SVCSTR(WMI_SERVICE_ROAM_SCAN_OFFLOAD);
+	SVCSTR(WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC);
+	SVCSTR(WMI_SERVICE_EARLY_RX);
+	SVCSTR(WMI_SERVICE_STA_SMPS);
+	SVCSTR(WMI_SERVICE_FWTEST);
+	SVCSTR(WMI_SERVICE_STA_WMMAC);
+	SVCSTR(WMI_SERVICE_TDLS);
+	SVCSTR(WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE);
+	SVCSTR(WMI_SERVICE_ADAPTIVE_OCS);
+	SVCSTR(WMI_SERVICE_BA_SSN_SUPPORT);
+	SVCSTR(WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE);
+	SVCSTR(WMI_SERVICE_WLAN_HB);
+	SVCSTR(WMI_SERVICE_LTE_ANT_SHARE_SUPPORT);
+	SVCSTR(WMI_SERVICE_BATCH_SCAN);
+	SVCSTR(WMI_SERVICE_QPOWER);
+	SVCSTR(WMI_SERVICE_PLMREQ);
+	SVCSTR(WMI_SERVICE_THERMAL_MGMT);
+	SVCSTR(WMI_SERVICE_RMC);
+	SVCSTR(WMI_SERVICE_MHF_OFFLOAD);
+	SVCSTR(WMI_SERVICE_COEX_SAR);
+	SVCSTR(WMI_SERVICE_BCN_TXRATE_OVERRIDE);
+	SVCSTR(WMI_SERVICE_NAN);
+	SVCSTR(WMI_SERVICE_L1SS_STAT);
+	SVCSTR(WMI_SERVICE_ESTIMATE_LINKSPEED);
+	SVCSTR(WMI_SERVICE_OBSS_SCAN);
+	SVCSTR(WMI_SERVICE_TDLS_OFFCHAN);
+	SVCSTR(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA);
+	SVCSTR(WMI_SERVICE_TDLS_UAPSD_SLEEP_STA);
+	SVCSTR(WMI_SERVICE_IBSS_PWRSAVE);
+	SVCSTR(WMI_SERVICE_LPASS);
+	SVCSTR(WMI_SERVICE_EXTSCAN);
+	SVCSTR(WMI_SERVICE_D0WOW);
+	SVCSTR(WMI_SERVICE_HSOFFLOAD);
+	SVCSTR(WMI_SERVICE_ROAM_HO_OFFLOAD);
+	SVCSTR(WMI_SERVICE_RX_FULL_REORDER);
+	SVCSTR(WMI_SERVICE_DHCP_OFFLOAD);
+	SVCSTR(WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT);
+	SVCSTR(WMI_SERVICE_MDNS_OFFLOAD);
+	SVCSTR(WMI_SERVICE_SAP_AUTH_OFFLOAD);
 	default:
 		return NULL;
 	}
@@ -472,6 +550,8 @@
 	u32 force_fw_hang_cmdid;
 	u32 gpio_config_cmdid;
 	u32 gpio_output_cmdid;
+	u32 pdev_get_temperature_cmdid;
+	u32 vdev_set_wmm_params_cmdid;
 };
 
 /*
@@ -1076,6 +1156,11 @@
 	WMI_10_2_PDEV_SET_MIMOGAIN_TABLE_CMDID,
 	WMI_10_2_PDEV_RATEPWR_TABLE_CMDID,
 	WMI_10_2_PDEV_RATEPWR_CHAINMSK_TABLE_CMDID,
+	WMI_10_2_PDEV_GET_INFO,
+	WMI_10_2_VDEV_GET_INFO,
+	WMI_10_2_VDEV_ATF_REQUEST_CMDID,
+	WMI_10_2_PEER_ATF_REQUEST_CMDID,
+	WMI_10_2_PDEV_GET_TEMPERATURE_CMDID,
 	WMI_10_2_PDEV_UTF_CMDID = WMI_10_2_END_CMDID - 1,
 };
 
@@ -1117,6 +1202,8 @@
 	WMI_10_2_MCAST_BUF_RELEASE_EVENTID,
 	WMI_10_2_MCAST_LIST_AGEOUT_EVENTID,
 	WMI_10_2_WDS_PEER_EVENTID,
+	WMI_10_2_PEER_STA_PS_STATECHG_EVENTID,
+	WMI_10_2_PDEV_TEMPERATURE_EVENTID,
 	WMI_10_2_PDEV_UTF_EVENTID = WMI_10_2_END_EVENTID - 1,
 };
 
@@ -1862,6 +1949,11 @@
 	__le32 max_frag_entries;
 } __packed;
 
+enum wmi_10_2_feature_mask {
+	WMI_10_2_RX_BATCH_MODE = BIT(0),
+	WMI_10_2_ATF_CONFIG    = BIT(1),
+};
+
 struct wmi_resource_config_10_2 {
 	struct wmi_resource_config_10x common;
 	__le32 max_peer_ext_stats;
@@ -1870,7 +1962,7 @@
 	__le32 be_min_free;
 	__le32 vi_min_free;
 	__le32 vo_min_free;
-	__le32 rx_batchmode; /* 0-disable, 1-enable */
+	__le32 feature_mask;
 } __packed;
 
 #define NUM_UNITS_IS_NUM_VDEVS   0x1
@@ -2505,6 +2597,7 @@
 	u32 fast_channel_reset;
 	u32 burst_dur;
 	u32 burst_enable;
+	u32 cal_period;
 };
 
 #define WMI_PDEV_PARAM_UNSUPPORTED 0
@@ -2715,6 +2808,9 @@
 	WMI_10X_PDEV_PARAM_SET_MCAST2UCAST_MODE,
 	WMI_10X_PDEV_PARAM_SET_MCAST2UCAST_BUFFER,
 	WMI_10X_PDEV_PARAM_REMOVE_MCAST2UCAST_BUFFER,
+	WMI_10X_PDEV_PARAM_PEER_STA_PS_STATECHG_ENABLE,
+	WMI_10X_PDEV_PARAM_RTS_FIXED_RATE,
+	WMI_10X_PDEV_PARAM_CAL_PERIOD
 };
 
 struct wmi_pdev_set_param_cmd {
@@ -2722,6 +2818,9 @@
 	__le32 param_value;
 } __packed;
 
+/* valid period is 1 ~ 60000ms, unit in millisecond */
+#define WMI_PDEV_PARAM_CAL_PERIOD_MAX 60000
+
 struct wmi_pdev_get_tpc_config_cmd {
 	/* parameter   */
 	__le32 param;
@@ -2841,14 +2940,14 @@
 	u32 no_ack;
 };
 
-struct wmi_pdev_set_wmm_params_arg {
+struct wmi_wmm_params_all_arg {
 	struct wmi_wmm_params_arg ac_be;
 	struct wmi_wmm_params_arg ac_bk;
 	struct wmi_wmm_params_arg ac_vi;
 	struct wmi_wmm_params_arg ac_vo;
 };
 
-struct wal_dbg_tx_stats {
+struct wmi_pdev_stats_tx {
 	/* Num HTT cookies queued to dispatch list */
 	__le32 comp_queued;
 
@@ -2918,7 +3017,7 @@
 	__le32 txop_ovf;
 } __packed;
 
-struct wal_dbg_rx_stats {
+struct wmi_pdev_stats_rx {
 	/* Cnts any change in ring routing mid-ppdu */
 	__le32 mid_ppdu_route_change;
 
@@ -2952,20 +3051,18 @@
 	__le32 mpdu_errs;
 } __packed;
 
-struct wal_dbg_peer_stats {
+struct wmi_pdev_stats_peer {
 	/* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */
 	__le32 dummy;
 } __packed;
 
-struct wal_dbg_stats {
-	struct wal_dbg_tx_stats tx;
-	struct wal_dbg_rx_stats rx;
-	struct wal_dbg_peer_stats peer;
-} __packed;
-
 enum wmi_stats_id {
-	WMI_REQUEST_PEER_STAT	= 0x01,
-	WMI_REQUEST_AP_STAT	= 0x02
+	WMI_STAT_PEER = BIT(0),
+	WMI_STAT_AP = BIT(1),
+	WMI_STAT_PDEV = BIT(2),
+	WMI_STAT_VDEV = BIT(3),
+	WMI_STAT_BCNFLT = BIT(4),
+	WMI_STAT_VDEV_RATE = BIT(5),
 };
 
 struct wlan_inst_rssi_args {
@@ -3000,7 +3097,7 @@
 } __packed;
 
 struct wmi_stats_event {
-	__le32 stats_id; /* %WMI_REQUEST_ */
+	__le32 stats_id; /* WMI_STAT_ */
 	/*
 	 * number of pdev stats event structures
 	 * (wmi_pdev_stats) 0 or 1
@@ -3029,23 +3126,38 @@
 	u8 data[0];
 } __packed;
 
+struct wmi_10_2_stats_event {
+	__le32 stats_id; /* %WMI_REQUEST_ */
+	__le32 num_pdev_stats;
+	__le32 num_pdev_ext_stats;
+	__le32 num_vdev_stats;
+	__le32 num_peer_stats;
+	__le32 num_bcnflt_stats;
+	u8 data[0];
+} __packed;
+
 /*
  * PDEV statistics
  * TODO: add all PDEV stats here
  */
-struct wmi_pdev_stats {
-	__le32 chan_nf;        /* Channel noise floor */
-	__le32 tx_frame_count; /* TX frame count */
-	__le32 rx_frame_count; /* RX frame count */
-	__le32 rx_clear_count; /* rx clear count */
-	__le32 cycle_count;    /* cycle count */
-	__le32 phy_err_count;  /* Phy error count */
-	__le32 chan_tx_pwr;    /* channel tx power */
-	struct wal_dbg_stats wal; /* WAL dbg stats */
+struct wmi_pdev_stats_base {
+	__le32 chan_nf;
+	__le32 tx_frame_count;
+	__le32 rx_frame_count;
+	__le32 rx_clear_count;
+	__le32 cycle_count;
+	__le32 phy_err_count;
+	__le32 chan_tx_pwr;
 } __packed;
 
-struct wmi_10x_pdev_stats {
-	struct wmi_pdev_stats old;
+struct wmi_pdev_stats {
+	struct wmi_pdev_stats_base base;
+	struct wmi_pdev_stats_tx tx;
+	struct wmi_pdev_stats_rx rx;
+	struct wmi_pdev_stats_peer peer;
+} __packed;
+
+struct wmi_pdev_stats_extra {
 	__le32 ack_rx_bad;
 	__le32 rts_bad;
 	__le32 rts_good;
@@ -3054,6 +3166,30 @@
 	__le32 mib_int_count;
 } __packed;
 
+struct wmi_10x_pdev_stats {
+	struct wmi_pdev_stats_base base;
+	struct wmi_pdev_stats_tx tx;
+	struct wmi_pdev_stats_rx rx;
+	struct wmi_pdev_stats_peer peer;
+	struct wmi_pdev_stats_extra extra;
+} __packed;
+
+struct wmi_pdev_stats_mem {
+	__le32 dram_free;
+	__le32 iram_free;
+} __packed;
+
+struct wmi_10_2_pdev_stats {
+	struct wmi_pdev_stats_base base;
+	struct wmi_pdev_stats_tx tx;
+	__le32 mc_drop;
+	struct wmi_pdev_stats_rx rx;
+	__le32 pdev_rx_timeout;
+	struct wmi_pdev_stats_mem mem;
+	struct wmi_pdev_stats_peer peer;
+	struct wmi_pdev_stats_extra extra;
+} __packed;
+
 /*
  * VDEV statistics
  * TODO: add all VDEV stats here
@@ -3077,6 +3213,32 @@
 	__le32 peer_rx_rate;
 } __packed;
 
+struct wmi_10_2_peer_stats {
+	struct wmi_peer_stats old;
+	__le32 peer_rx_rate;
+	__le32 current_per;
+	__le32 retries;
+	__le32 tx_rate_count;
+	__le32 max_4ms_frame_len;
+	__le32 total_sub_frames;
+	__le32 tx_bytes;
+	__le32 num_pkt_loss_overflow[4];
+	__le32 num_pkt_loss_excess_retry[4];
+} __packed;
+
+struct wmi_10_2_4_peer_stats {
+	struct wmi_10_2_peer_stats common;
+	__le32 unknown_value; /* FIXME: what is this word? */
+} __packed;
+
+struct wmi_10_2_pdev_ext_stats {
+	__le32 rx_rssi_comb;
+	__le32 rx_rssi[4];
+	__le32 rx_mcs[10];
+	__le32 tx_mcs[10];
+	__le32 ack_rssi;
+} __packed;
+
 struct wmi_vdev_create_cmd {
 	__le32 vdev_id;
 	__le32 vdev_type;
@@ -3587,6 +3749,11 @@
 	WMI_10X_VDEV_PARAM_VHT80_RATEMASK,
 };
 
+#define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0)
+#define WMI_VDEV_PARAM_TXBF_MU_TX_BFEE BIT(1)
+#define WMI_VDEV_PARAM_TXBF_SU_TX_BFER BIT(2)
+#define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3)
+
 /* slot time long */
 #define WMI_VDEV_SLOT_TIME_LONG		0x1
 /* slot time short */
@@ -3930,6 +4097,13 @@
 	 * Values greater than 0 indicate the maximum numer of PS-Poll frames
 	 * FW will send before waking up.
 	 */
+
+	/* When u-APSD is enabled the firmware will be very reluctant to exit
+	 * STA PS. This could result in very poor Rx performance with STA doing
+	 * PS-Poll for each and every buffered frame. This value is a bit
+	 * arbitrary.
+	 */
+	WMI_STA_PS_PSPOLL_COUNT_UAPSD = 3,
 };
 
 /*
@@ -3955,6 +4129,30 @@
 	WMI_STA_PS_UAPSD_AC3_TRIGGER_EN  = (1 << 7),
 };
 
+#define WMI_STA_UAPSD_MAX_INTERVAL_MSEC UINT_MAX
+
+struct wmi_sta_uapsd_auto_trig_param {
+	__le32 wmm_ac;
+	__le32 user_priority;
+	__le32 service_interval;
+	__le32 suspend_interval;
+	__le32 delay_interval;
+};
+
+struct wmi_sta_uapsd_auto_trig_cmd_fixed_param {
+	__le32 vdev_id;
+	struct wmi_mac_addr peer_macaddr;
+	__le32 num_ac;
+};
+
+struct wmi_sta_uapsd_auto_trig_arg {
+	u32 wmm_ac;
+	u32 user_priority;
+	u32 service_interval;
+	u32 suspend_interval;
+	u32 delay_interval;
+};
+
 enum wmi_sta_powersave_param {
 	/*
 	 * Controls how frames are retrievd from AP while STA is sleeping
@@ -4120,7 +4318,7 @@
 
 struct wmi_host_swba_event {
 	__le32 vdev_map;
-	struct wmi_bcn_info bcn_info[1];
+	struct wmi_bcn_info bcn_info[0];
 } __packed;
 
 #define WMI_MAX_AP_VDEV 16
@@ -4247,7 +4445,8 @@
 	WMI_PEER_AUTHORIZE  = 0x3,
 	WMI_PEER_CHAN_WIDTH = 0x4,
 	WMI_PEER_NSS        = 0x5,
-	WMI_PEER_USE_4ADDR  = 0x6
+	WMI_PEER_USE_4ADDR  = 0x6,
+	WMI_PEER_DUMMY_VAR  = 0xff, /* dummy parameter for STA PS workaround */
 };
 
 struct wmi_peer_set_param_cmd {
@@ -4325,7 +4524,7 @@
 #define WMI_PEER_SPATIAL_MUX    0x00200000
 #define WMI_PEER_VHT            0x02000000
 #define WMI_PEER_80MHZ          0x04000000
-#define WMI_PEER_PMF            0x08000000
+#define WMI_PEER_VHT_2G         0x08000000
 
 /*
  * Peer rate capabilities.
@@ -4476,6 +4675,11 @@
 	WMI_STA_KEEPALIVE_METHOD_UNSOLICITATED_ARP_RESPONSE = 2,
 };
 
+#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0
+
+/* Firmware crashes if keepalive interval exceeds this limit */
+#define WMI_STA_KEEPALIVE_INTERVAL_MAX_SECONDS 0xffff
+
 /* note: ip4 addresses are in network byte order, i.e. big endian */
 struct wmi_sta_keepalive_arp_resp {
 	__be32 src_ip4_addr;
@@ -4491,6 +4695,16 @@
 	struct wmi_sta_keepalive_arp_resp arp_resp;
 } __packed;
 
+struct wmi_sta_keepalive_arg {
+	u32 vdev_id;
+	u32 enabled;
+	u32 method;
+	u32 interval;
+	__be32 src_ip4_addr;
+	__be32 dest_ip4_addr;
+	const u8 dest_mac_addr[ETH_ALEN];
+};
+
 enum wmi_force_fw_hang_type {
 	WMI_FORCE_FW_HANG_ASSERT = 1,
 	WMI_FORCE_FW_HANG_NO_DETECT,
@@ -4567,6 +4781,58 @@
 
 #define WMI_MAX_MEM_REQS 16
 
+struct wmi_scan_ev_arg {
+	__le32 event_type; /* %WMI_SCAN_EVENT_ */
+	__le32 reason; /* %WMI_SCAN_REASON_ */
+	__le32 channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */
+	__le32 scan_req_id;
+	__le32 scan_id;
+	__le32 vdev_id;
+};
+
+struct wmi_mgmt_rx_ev_arg {
+	__le32 channel;
+	__le32 snr;
+	__le32 rate;
+	__le32 phy_mode;
+	__le32 buf_len;
+	__le32 status; /* %WMI_RX_STATUS_ */
+};
+
+struct wmi_ch_info_ev_arg {
+	__le32 err_code;
+	__le32 freq;
+	__le32 cmd_flags;
+	__le32 noise_floor;
+	__le32 rx_clear_count;
+	__le32 cycle_count;
+};
+
+struct wmi_vdev_start_ev_arg {
+	__le32 vdev_id;
+	__le32 req_id;
+	__le32 resp_type; /* %WMI_VDEV_RESP_ */
+	__le32 status;
+};
+
+struct wmi_peer_kick_ev_arg {
+	const u8 *mac_addr;
+};
+
+struct wmi_swba_ev_arg {
+	__le32 vdev_map;
+	const struct wmi_tim_info *tim_info[WMI_MAX_AP_VDEV];
+	const struct wmi_p2p_noa_info *noa_info[WMI_MAX_AP_VDEV];
+};
+
+struct wmi_phyerr_ev_arg {
+	__le32 num_phyerrs;
+	__le32 tsf_l32;
+	__le32 tsf_u32;
+	__le32 buf_len;
+	const struct wmi_phyerr *phyerrs;
+};
+
 struct wmi_svc_rdy_ev_arg {
 	__le32 min_tx_power;
 	__le32 max_tx_power;
@@ -4574,6 +4840,7 @@
 	__le32 vht_cap;
 	__le32 sw_ver0;
 	__le32 sw_ver1;
+	__le32 fw_build;
 	__le32 phy_capab;
 	__le32 num_rf_chains;
 	__le32 eeprom_rd;
@@ -4583,83 +4850,99 @@
 	const struct wlan_host_mem_req *mem_reqs[WMI_MAX_MEM_REQS];
 };
 
+struct wmi_rdy_ev_arg {
+	__le32 sw_version;
+	__le32 abi_version;
+	__le32 status;
+	const u8 *mac_addr;
+};
+
+struct wmi_pdev_temperature_event {
+	/* temperature value in Celcius degree */
+	__le32 temperature;
+} __packed;
+
 struct ath10k;
 struct ath10k_vif;
-struct ath10k_fw_stats;
+struct ath10k_fw_stats_pdev;
+struct ath10k_fw_stats_peer;
 
 int ath10k_wmi_attach(struct ath10k *ar);
 void ath10k_wmi_detach(struct ath10k *ar);
 int ath10k_wmi_wait_for_service_ready(struct ath10k *ar);
 int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar);
 
+struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len);
 int ath10k_wmi_connect(struct ath10k *ar);
 
 struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len);
 int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
-
-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,
-				  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 *);
+int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
+			       u32 cmd_id);
 void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *);
-int ath10k_wmi_stop_scan(struct ath10k *ar,
-			 const struct wmi_stop_scan_arg *arg);
-int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
-			   enum wmi_vdev_type type,
-			   enum wmi_vdev_subtype subtype,
-			   const u8 macaddr[ETH_ALEN]);
-int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id);
-int ath10k_wmi_vdev_start(struct ath10k *ar,
-			  const struct wmi_vdev_start_request_arg *);
-int ath10k_wmi_vdev_restart(struct ath10k *ar,
-			    const struct wmi_vdev_start_request_arg *);
-int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id);
-int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
-		       const u8 *bssid);
-int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id);
-int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
-			      u32 param_id, u32 param_value);
-int ath10k_wmi_vdev_install_key(struct ath10k *ar,
-				const struct wmi_vdev_install_key_arg *arg);
-int ath10k_wmi_vdev_spectral_conf(struct ath10k *ar,
-				  const struct wmi_vdev_spectral_conf_arg *arg);
-int ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger,
-				    u32 enable);
-int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
-			   const u8 peer_addr[ETH_ALEN]);
-int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
-			   const u8 peer_addr[ETH_ALEN]);
-int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
-			  const u8 peer_addr[ETH_ALEN], u32 tid_bitmap);
-int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
-			      const u8 *peer_addr,
-			      enum wmi_peer_param param_id, u32 param_value);
-int ath10k_wmi_peer_assoc(struct ath10k *ar,
-			  const struct wmi_peer_assoc_complete_arg *arg);
-int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
-			  enum wmi_sta_ps_mode psmode);
-int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
-				enum wmi_sta_powersave_param param_id,
-				u32 value);
-int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
-			       enum wmi_ap_ps_peer_param param_id, u32 value);
-int ath10k_wmi_scan_chan_list(struct ath10k *ar,
-			      const struct wmi_scan_chan_list_arg *arg);
-int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif);
-int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
-				   const struct wmi_pdev_set_wmm_params_arg *arg);
-int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
-int ath10k_wmi_force_fw_hang(struct ath10k *ar,
-			     enum wmi_force_fw_hang_type type, u32 delay_ms);
-int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb);
-int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable);
-int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
-			     struct ath10k_fw_stats *stats);
-int ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 ev_list);
-int ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar);
+
+void ath10k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src,
+				     struct ath10k_fw_stats_pdev *dst);
+void ath10k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src,
+				   struct ath10k_fw_stats_pdev *dst);
+void ath10k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src,
+				   struct ath10k_fw_stats_pdev *dst);
+void ath10k_wmi_pull_pdev_stats_extra(const struct wmi_pdev_stats_extra *src,
+				      struct ath10k_fw_stats_pdev *dst);
+void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
+				struct ath10k_fw_stats_peer *dst);
+void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar,
+				    struct wmi_host_mem_chunks *chunks);
+void ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn,
+				      const struct wmi_start_scan_arg *arg);
+void ath10k_wmi_set_wmm_param(struct wmi_wmm_params *params,
+			      const struct wmi_wmm_params_arg *arg);
+void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
+				const struct wmi_channel_arg *arg);
+int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg);
+
+int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_dfs(struct ath10k *ar,
+			  const struct wmi_phyerr *phyerr, u64 tsf);
+void ath10k_wmi_event_spectral_scan(struct ath10k *ar,
+				    const struct wmi_phyerr *phyerr,
+				    u64 tsf);
+void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_profile_match(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_debug_print(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar,
+					     struct sk_buff *skb);
+void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar,
+					     struct sk_buff *skb);
+void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar,
+					 struct sk_buff *skb);
+void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_delba_complete(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_addba_complete(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
+						struct sk_buff *skb);
+void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb);
 
 #endif /* _WMI_H_ */
diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c
index 8f387cf..2ca88b5 100644
--- a/drivers/net/wireless/ath/ath5k/ahb.c
+++ b/drivers/net/wireless/ath/ath5k/ahb.c
@@ -227,7 +227,6 @@
 	.remove     = ath_ahb_remove,
 	.driver		= {
 		.name	= "ar231x-wmac",
-		.owner	= THIS_MODULE,
 	},
 };
 
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index cc701f1..d290c20 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -1283,6 +1283,7 @@
 #define ATH_STAT_PROMISC	1
 #define ATH_STAT_LEDSOFT	2		/* enable LED gpio status */
 #define ATH_STAT_STARTED	3		/* opened & irqs enabled */
+#define ATH_STAT_RESET		4		/* hw reset */
 
 	unsigned int		filter_flags;	/* HW flags, AR5K_RX_FILTER_* */
 	unsigned int		fif_filter_flags; /* Current FIF_* filter flags */
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 65cc4b3..f924d5b 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -528,7 +528,7 @@
 	 * together with the BSSID mask when matching addresses.
 	 */
 	iter_data.hw_macaddr = common->macaddr;
-	memset(&iter_data.mask, 0xff, ETH_ALEN);
+	eth_broadcast_addr(iter_data.mask);
 	iter_data.found_active = false;
 	iter_data.need_set_hw_addr = true;
 	iter_data.opmode = NL80211_IFTYPE_UNSPECIFIED;
@@ -1523,6 +1523,9 @@
 	enum ath5k_int imask;
 	unsigned long flags;
 
+	if (test_bit(ATH_STAT_RESET, ah->status))
+		return;
+
 	spin_lock_irqsave(&ah->irqlock, flags);
 	imask = ah->imask;
 	if (ah->rx_pending)
@@ -2858,10 +2861,12 @@
 {
 	struct ath_common *common = ath5k_hw_common(ah);
 	int ret, ani_mode;
-	bool fast;
+	bool fast = chan && modparam_fastchanswitch ? 1 : 0;
 
 	ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n");
 
+	__set_bit(ATH_STAT_RESET, ah->status);
+
 	ath5k_hw_set_imr(ah, 0);
 	synchronize_irq(ah->irq);
 	ath5k_stop_tasklets(ah);
@@ -2876,11 +2881,29 @@
 	 * so we should also free any remaining
 	 * tx buffers */
 	ath5k_drain_tx_buffs(ah);
+
+	/* Stop PCU */
+	ath5k_hw_stop_rx_pcu(ah);
+
+	/* Stop DMA
+	 *
+	 * Note: If DMA didn't stop continue
+	 * since only a reset will fix it.
+	 */
+	ret = ath5k_hw_dma_stop(ah);
+
+	/* RF Bus grant won't work if we have pending
+	 * frames
+	 */
+	if (ret && fast) {
+		ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+			  "DMA didn't stop, falling back to normal reset\n");
+		fast = false;
+	}
+
 	if (chan)
 		ah->curchan = chan;
 
-	fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0;
-
 	ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu);
 	if (ret) {
 		ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret);
@@ -2934,6 +2957,8 @@
 	 */
 /*	ath5k_chan_change(ah, c); */
 
+	__clear_bit(ATH_STAT_RESET, ah->status);
+
 	ath5k_beacon_config(ah);
 	/* intrs are enabled by ath5k_beacon_config */
 
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index 19eab2a..3b4a646 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -672,10 +672,10 @@
 	spin_lock_bh(&common->cc_lock);
 	ath_hw_cycle_counters_update(common);
 	if (cc->cycles > 0) {
-		ah->survey.channel_time += cc->cycles / div;
-		ah->survey.channel_time_busy += cc->rx_busy / div;
-		ah->survey.channel_time_rx += cc->rx_frame / div;
-		ah->survey.channel_time_tx += cc->tx_frame / div;
+		ah->survey.time += cc->cycles / div;
+		ah->survey.time_busy += cc->rx_busy / div;
+		ah->survey.time_rx += cc->rx_frame / div;
+		ah->survey.time_tx += cc->tx_frame / div;
 	}
 	memset(cc, 0, sizeof(*cc));
 	spin_unlock_bh(&common->cc_lock);
@@ -686,10 +686,10 @@
 	survey->noise = ah->ah_noise_floor;
 	survey->filled = SURVEY_INFO_NOISE_DBM |
 			SURVEY_INFO_IN_USE |
-			SURVEY_INFO_CHANNEL_TIME |
-			SURVEY_INFO_CHANNEL_TIME_BUSY |
-			SURVEY_INFO_CHANNEL_TIME_RX |
-			SURVEY_INFO_CHANNEL_TIME_TX;
+			SURVEY_INFO_TIME |
+			SURVEY_INFO_TIME_BUSY |
+			SURVEY_INFO_TIME_RX |
+			SURVEY_INFO_TIME_TX;
 
 	return 0;
 }
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c
index c60d36a..bf29da5 100644
--- a/drivers/net/wireless/ath/ath5k/pcu.c
+++ b/drivers/net/wireless/ath/ath5k/pcu.c
@@ -912,6 +912,7 @@
 		pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE
 			| (ah->ah_version == AR5K_AR5210 ?
 				AR5K_STA_ID1_PWR_SV : 0);
+		/* fall through */
 	case NL80211_IFTYPE_MONITOR:
 		pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE
 			| (ah->ah_version == AR5K_AR5210 ?
diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c
index a3399c4..99e62f9 100644
--- a/drivers/net/wireless/ath/ath5k/reset.c
+++ b/drivers/net/wireless/ath/ath5k/reset.c
@@ -478,7 +478,7 @@
 	regval = ioread32(reg);
 	iowrite32(regval | val, reg);
 	regval = ioread32(reg);
-	usleep_range(100, 150);
+	udelay(100);	/* NB: should be atomic */
 
 	/* Bring BB/MAC out of reset */
 	iowrite32(regval & ~val, reg);
@@ -1169,30 +1169,6 @@
 	if (ah->ah_version == AR5K_AR5212)
 		ath5k_hw_set_sleep_clock(ah, false);
 
-	/*
-	 * Stop PCU
-	 */
-	ath5k_hw_stop_rx_pcu(ah);
-
-	/*
-	 * Stop DMA
-	 *
-	 * Note: If DMA didn't stop continue
-	 * since only a reset will fix it.
-	 */
-	ret = ath5k_hw_dma_stop(ah);
-
-	/* RF Bus grant won't work if we have pending
-	 * frames */
-	if (ret && fast) {
-		ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
-			"DMA didn't stop, falling back to normal reset\n");
-		fast = false;
-		/* Non fatal, just continue with
-		 * normal reset */
-		ret = 0;
-	}
-
 	mode = channel->hw_value;
 	switch (mode) {
 	case AR5K_MODE_11A:
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 03799d2..183df9a 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -686,20 +686,21 @@
 {
 	struct ath6kl *ar = vif->ar;
 	struct cfg80211_bss *bss;
-	u16 cap_mask, cap_val;
+	u16 cap_val;
+	enum ieee80211_bss_type bss_type;
 	u8 *ie;
 
 	if (nw_type & ADHOC_NETWORK) {
-		cap_mask = WLAN_CAPABILITY_IBSS;
 		cap_val = WLAN_CAPABILITY_IBSS;
+		bss_type = IEEE80211_BSS_TYPE_IBSS;
 	} else {
-		cap_mask = WLAN_CAPABILITY_ESS;
 		cap_val = WLAN_CAPABILITY_ESS;
+		bss_type = IEEE80211_BSS_TYPE_ESS;
 	}
 
 	bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
 			       vif->ssid, vif->ssid_len,
-			       cap_mask, cap_val);
+			       bss_type, IEEE80211_PRIVACY_ANY);
 	if (bss == NULL) {
 		/*
 		 * Since cfg80211 may not yet know about the BSS,
@@ -1495,6 +1496,7 @@
 
 static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
 						      const char *name,
+						      unsigned char name_assign_type,
 						      enum nl80211_iftype type,
 						      u32 *flags,
 						      struct vif_params *params)
@@ -1513,7 +1515,7 @@
 		return ERR_PTR(-EINVAL);
 	}
 
-	wdev = ath6kl_interface_add(ar, name, type, if_idx, nw_type);
+	wdev = ath6kl_interface_add(ar, name, name_assign_type, type, if_idx, nw_type);
 	if (!wdev)
 		return ERR_PTR(-ENOMEM);
 
@@ -1799,20 +1801,20 @@
 
 	if (vif->target_stats.rx_byte) {
 		sinfo->rx_bytes = vif->target_stats.rx_byte;
-		sinfo->filled |= STATION_INFO_RX_BYTES64;
+		sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64);
 		sinfo->rx_packets = vif->target_stats.rx_pkt;
-		sinfo->filled |= STATION_INFO_RX_PACKETS;
+		sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
 	}
 
 	if (vif->target_stats.tx_byte) {
 		sinfo->tx_bytes = vif->target_stats.tx_byte;
-		sinfo->filled |= STATION_INFO_TX_BYTES64;
+		sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES64);
 		sinfo->tx_packets = vif->target_stats.tx_pkt;
-		sinfo->filled |= STATION_INFO_TX_PACKETS;
+		sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
 	}
 
 	sinfo->signal = vif->target_stats.cs_rssi;
-	sinfo->filled |= STATION_INFO_SIGNAL;
+	sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
 
 	rate = vif->target_stats.tx_ucast_rate;
 
@@ -1827,6 +1829,7 @@
 		}
 
 		sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+		sinfo->txrate.bw = RATE_INFO_BW_20;
 	} else if (is_rate_ht40(rate, &mcs, &sgi)) {
 		if (sgi) {
 			sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
@@ -1835,7 +1838,7 @@
 			sinfo->txrate.mcs = mcs;
 		}
 
-		sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+		sinfo->txrate.bw = RATE_INFO_BW_40;
 		sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
 	} else {
 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
@@ -1844,12 +1847,12 @@
 		return 0;
 	}
 
-	sinfo->filled |= STATION_INFO_TX_BITRATE;
+	sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
 
 	if (test_bit(CONNECTED, &vif->flags) &&
 	    test_bit(DTIM_PERIOD_AVAIL, &vif->flags) &&
 	    vif->nw_type == INFRA_NETWORK) {
-		sinfo->filled |= STATION_INFO_BSS_PARAM;
+		sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
 		sinfo->bss_param.flags = 0;
 		sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period;
 		sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int;
@@ -2032,7 +2035,7 @@
 	int ret;
 
 	/* Setup unicast pkt pattern */
-	memset(mac_mask, 0xff, ETH_ALEN);
+	eth_broadcast_addr(mac_mask);
 	ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
 				vif->fw_vif_idx, WOW_LIST_ID,
 				ETH_ALEN, 0, ndev->dev_addr,
@@ -3632,13 +3635,14 @@
 }
 
 struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
+					  unsigned char name_assign_type,
 					  enum nl80211_iftype type,
 					  u8 fw_vif_idx, u8 nw_type)
 {
 	struct net_device *ndev;
 	struct ath6kl_vif *vif;
 
-	ndev = alloc_netdev(sizeof(*vif), name, NET_NAME_UNKNOWN, ether_setup);
+	ndev = alloc_netdev(sizeof(*vif), name, name_assign_type, ether_setup);
 	if (!ndev)
 		return NULL;
 
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h
index b59becd..5aa57a7 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.h
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h
@@ -25,6 +25,7 @@
 };
 
 struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
+					  unsigned char name_assign_type,
 					  enum nl80211_iftype type,
 					  u8 fw_vif_idx, u8 nw_type);
 void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c
index 0df74b2..4ec02ce 100644
--- a/drivers/net/wireless/ath/ath6kl/core.c
+++ b/drivers/net/wireless/ath/ath6kl/core.c
@@ -211,8 +211,8 @@
 	rtnl_lock();
 
 	/* Add an initial station interface */
-	wdev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0,
-				    INFRA_NETWORK);
+	wdev = ath6kl_interface_add(ar, "wlan%d", NET_NAME_ENUM,
+				    NL80211_IFTYPE_STATION, 0, INFRA_NETWORK);
 
 	rtnl_unlock();
 
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
index 933aef0..1af3fed 100644
--- a/drivers/net/wireless/ath/ath6kl/main.c
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -105,7 +105,7 @@
 
 	memset(&ar->ap_stats.sta[sta->aid - 1], 0,
 	       sizeof(struct wmi_per_sta_stat));
-	memset(sta->mac, 0, ETH_ALEN);
+	eth_zero_addr(sta->mac);
 	memset(sta->wpa_ie, 0, ATH6KL_MAX_IE);
 	sta->aid = 0;
 	sta->sta_flags = 0;
@@ -488,7 +488,6 @@
 
 	sinfo.assoc_req_ies = ies;
 	sinfo.assoc_req_ies_len = ies_len;
-	sinfo.filled |= STATION_INFO_ASSOC_REQ_IES;
 
 	cfg80211_new_sta(vif->ndev, mac_addr, &sinfo, GFP_KERNEL);
 
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 18e21a8..2e1bdda 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -46,7 +46,8 @@
 ath9k_hw-$(CPTCFG_ATH9K_WOW) += ar9003_wow.o
 
 ath9k_hw-$(CPTCFG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \
-					   ar9003_mci.o
+					   ar9003_mci.o \
+					   ar9003_aic.o
 
 ath9k_hw-$(CPTCFG_ATH9K_PCOEM) += ar9003_rtt.o
 
diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c
index e000c4c..bd4a1a6 100644
--- a/drivers/net/wireless/ath/ath9k/ahb.c
+++ b/drivers/net/wireless/ath/ath9k/ahb.c
@@ -43,6 +43,10 @@
 		.name = "qca953x_wmac",
 		.driver_data = AR9300_DEVID_AR953X,
 	},
+	{
+		.name = "qca956x_wmac",
+		.driver_data = AR9300_DEVID_QCA956X,
+	},
 	{},
 };
 
diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c
index ba502a2..25e45e4 100644
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -107,11 +107,21 @@
 static void ath9k_hw_update_mibstats(struct ath_hw *ah,
 				     struct ath9k_mib_stats *stats)
 {
-	stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL);
-	stats->rts_bad += REG_READ(ah, AR_RTS_FAIL);
-	stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL);
-	stats->rts_good += REG_READ(ah, AR_RTS_OK);
-	stats->beacons += REG_READ(ah, AR_BEACON_CNT);
+	u32 addr[5] = {AR_RTS_OK, AR_RTS_FAIL, AR_ACK_FAIL,
+		       AR_FCS_FAIL, AR_BEACON_CNT};
+	u32 data[5];
+
+	REG_READ_MULTI(ah, &addr[0], &data[0], 5);
+	/* AR_RTS_OK */
+	stats->rts_good += data[0];
+	/* AR_RTS_FAIL */
+	stats->rts_bad += data[1];
+	/* AR_ACK_FAIL */
+	stats->ackrcv_bad += data[2];
+	/* AR_FCS_FAIL */
+	stats->fcs_bad += data[3];
+	/* AR_BEACON_CNT */
+	stats->beacons += data[4];
 }
 
 static void ath9k_ani_restart(struct ath_hw *ah)
@@ -259,7 +269,8 @@
 				     entry_cck->fir_step_level);
 
 	/* Skip MRC CCK for pre AR9003 families */
-	if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah))
+	if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9485(ah) ||
+	    AR_SREV_9565(ah) || AR_SREV_9561(ah))
 		return;
 
 	if (aniState->mrcCCK != entry_cck->mrc_cck_on)
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
index 5829074..6c23d27 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -22,6 +22,21 @@
 
 /* All code below is for AR5008, AR9001, AR9002 */
 
+#define AR5008_OFDM_RATES		8
+#define AR5008_HT_SS_RATES		8
+#define AR5008_HT_DS_RATES		8
+
+#define AR5008_HT20_SHIFT		16
+#define AR5008_HT40_SHIFT		24
+
+#define AR5008_11NA_OFDM_SHIFT		0
+#define AR5008_11NA_HT_SS_SHIFT		8
+#define AR5008_11NA_HT_DS_SHIFT		16
+
+#define AR5008_11NG_OFDM_SHIFT		4
+#define AR5008_11NG_HT_SS_SHIFT		12
+#define AR5008_11NG_HT_DS_SHIFT		20
+
 static const int firstep_table[] =
 /* level:  0   1   2   3   4   5   6   7   8  */
 	{ -4, -2,  0,  2,  4,  6,  8, 10, 12 }; /* lvl 0-8, default 2 */
@@ -666,12 +681,13 @@
 			phymode |= AR_PHY_FC_DYN2040_PRI_CH;
 
 	}
+	ENABLE_REGWRITE_BUFFER(ah);
 	REG_WRITE(ah, AR_PHY_TURBO, phymode);
 
+	/* This function do only REG_WRITE, so
+	 * we can include it to REGWRITE_BUFFER. */
 	ath9k_hw_set11nmac2040(ah, chan);
 
-	ENABLE_REGWRITE_BUFFER(ah);
-
 	REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
 	REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S);
 
@@ -1235,6 +1251,71 @@
 	conf->radar_inband = 8;
 }
 
+static void ar5008_hw_init_txpower_cck(struct ath_hw *ah, int16_t *rate_array)
+{
+#define CCK_DELTA(x) ((OLC_FOR_AR9280_20_LATER) ? max((x) - 2, 0) : (x))
+	ah->tx_power[0] = CCK_DELTA(rate_array[rate1l]);
+	ah->tx_power[1] = CCK_DELTA(min(rate_array[rate2l],
+					rate_array[rate2s]));
+	ah->tx_power[2] = CCK_DELTA(min(rate_array[rate5_5l],
+					rate_array[rate5_5s]));
+	ah->tx_power[3] = CCK_DELTA(min(rate_array[rate11l],
+					rate_array[rate11s]));
+#undef CCK_DELTA
+}
+
+static void ar5008_hw_init_txpower_ofdm(struct ath_hw *ah, int16_t *rate_array,
+					int offset)
+{
+	int i, idx = 0;
+
+	for (i = offset; i < offset + AR5008_OFDM_RATES; i++) {
+		ah->tx_power[i] = rate_array[idx];
+		idx++;
+	}
+}
+
+static void ar5008_hw_init_txpower_ht(struct ath_hw *ah, int16_t *rate_array,
+				      int ss_offset, int ds_offset,
+				      bool is_40, int ht40_delta)
+{
+	int i, mcs_idx = (is_40) ? AR5008_HT40_SHIFT : AR5008_HT20_SHIFT;
+
+	for (i = ss_offset; i < ss_offset + AR5008_HT_SS_RATES; i++) {
+		ah->tx_power[i] = rate_array[mcs_idx] + ht40_delta;
+		mcs_idx++;
+	}
+	memcpy(&ah->tx_power[ds_offset], &ah->tx_power[ss_offset],
+	       AR5008_HT_SS_RATES);
+}
+
+void ar5008_hw_init_rate_txpower(struct ath_hw *ah, int16_t *rate_array,
+				 struct ath9k_channel *chan, int ht40_delta)
+{
+	if (IS_CHAN_5GHZ(chan)) {
+		ar5008_hw_init_txpower_ofdm(ah, rate_array,
+					    AR5008_11NA_OFDM_SHIFT);
+		if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+			ar5008_hw_init_txpower_ht(ah, rate_array,
+						  AR5008_11NA_HT_SS_SHIFT,
+						  AR5008_11NA_HT_DS_SHIFT,
+						  IS_CHAN_HT40(chan),
+						  ht40_delta);
+		}
+	} else {
+		ar5008_hw_init_txpower_cck(ah, rate_array);
+		ar5008_hw_init_txpower_ofdm(ah, rate_array,
+					    AR5008_11NG_OFDM_SHIFT);
+		if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+			ar5008_hw_init_txpower_ht(ah, rate_array,
+						  AR5008_11NG_HT_SS_SHIFT,
+						  AR5008_11NG_HT_DS_SHIFT,
+						  IS_CHAN_HT40(chan),
+						  ht40_delta);
+		}
+	}
+}
+
 int ar5008_hw_attach_phy_ops(struct ath_hw *ah)
 {
 	struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
index 42190b6..50fcd34 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
@@ -430,46 +430,43 @@
 	u32 regVal;
 	unsigned int i;
 	u32 regList[][2] = {
-		{ 0x786c, 0 },
-		{ 0x7854, 0 },
-		{ 0x7820, 0 },
-		{ 0x7824, 0 },
-		{ 0x7868, 0 },
-		{ 0x783c, 0 },
-		{ 0x7838, 0 } ,
-		{ 0x7828, 0 } ,
+		{ AR9285_AN_TOP3, 0 },
+		{ AR9285_AN_RXTXBB1, 0 },
+		{ AR9285_AN_RF2G1, 0 },
+		{ AR9285_AN_RF2G2, 0 },
+		{ AR9285_AN_TOP2, 0 },
+		{ AR9285_AN_RF2G8, 0 },
+		{ AR9285_AN_RF2G7, 0 },
+		{ AR9285_AN_RF2G3, 0 },
 	};
 
-	for (i = 0; i < ARRAY_SIZE(regList); i++)
-		regList[i][1] = REG_READ(ah, regList[i][0]);
+	REG_READ_ARRAY(ah, regList, ARRAY_SIZE(regList));
 
-	regVal = REG_READ(ah, 0x7834);
-	regVal &= (~(0x1));
-	REG_WRITE(ah, 0x7834, regVal);
-	regVal = REG_READ(ah, 0x9808);
-	regVal |= (0x1 << 27);
-	REG_WRITE(ah, 0x9808, regVal);
-
+	ENABLE_REG_RMW_BUFFER(ah);
+	/* 7834, b1=0 */
+	REG_CLR_BIT(ah, AR9285_AN_RF2G6, 1 << 0);
+	/* 9808, b27=1 */
+	REG_SET_BIT(ah, 0x9808, 1 << 27);
 	/* 786c,b23,1, pwddac=1 */
-	REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1);
+	REG_SET_BIT(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC);
 	/* 7854, b5,1, pdrxtxbb=1 */
-	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1);
+	REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1);
 	/* 7854, b7,1, pdv2i=1 */
-	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1);
+	REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I);
 	/* 7854, b8,1, pddacinterface=1 */
-	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1);
+	REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF);
 	/* 7824,b12,0, offcal=0 */
-	REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0);
+	REG_CLR_BIT(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL);
 	/* 7838, b1,0, pwddb=0 */
-	REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0);
+	REG_CLR_BIT(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB);
 	/* 7820,b11,0, enpacal=0 */
-	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0);
+	REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL);
 	/* 7820,b25,1, pdpadrv1=0 */
-	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0);
+	REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1);
 	/* 7820,b24,0, pdpadrv2=0 */
-	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0);
+	REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2);
 	/* 7820,b23,0, pdpaout=0 */
-	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0);
+	REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT);
 	/* 783c,b14-16,7, padrvgn2tab_0=7 */
 	REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7);
 	/*
@@ -477,8 +474,9 @@
 	 * does not matter since we turn it off
 	 */
 	REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0);
-
+	/* 7828, b0-11, ccom=fff */
 	REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_CCOMP, 0xfff);
+	REG_RMW_BUFFER_FLUSH(ah);
 
 	/* Set:
 	 * localmode=1,bmode=1,bmoderxtx=1,synthon=1,
@@ -490,15 +488,16 @@
 
 	/* find off_6_1; */
 	for (i = 6; i > 0; i--) {
-		regVal = REG_READ(ah, 0x7834);
+		regVal = REG_READ(ah, AR9285_AN_RF2G6);
 		regVal |= (1 << (20 + i));
-		REG_WRITE(ah, 0x7834, regVal);
+		REG_WRITE(ah, AR9285_AN_RF2G6, regVal);
 		udelay(1);
 		/* regVal = REG_READ(ah, 0x7834); */
 		regVal &= (~(0x1 << (20 + i)));
-		regVal |= (MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9)
+		regVal |= (MS(REG_READ(ah, AR9285_AN_RF2G9),
+			      AR9285_AN_RXTXBB1_SPARE9)
 			    << (20 + i));
-		REG_WRITE(ah, 0x7834, regVal);
+		REG_WRITE(ah, AR9285_AN_RF2G6, regVal);
 	}
 
 	regVal = (regVal >> 20) & 0x7f;
@@ -515,15 +514,15 @@
 		ah->pacal_info.prev_offset = regVal;
 	}
 
+
+	ENABLE_REG_RMW_BUFFER(ah);
+	/* 7834, b1=1 */
+	REG_SET_BIT(ah, AR9285_AN_RF2G6, 1 << 0);
+	/* 9808, b27=0 */
+	REG_CLR_BIT(ah, 0x9808, 1 << 27);
+	REG_RMW_BUFFER_FLUSH(ah);
+
 	ENABLE_REGWRITE_BUFFER(ah);
-
-	regVal = REG_READ(ah, 0x7834);
-	regVal |= 0x1;
-	REG_WRITE(ah, 0x7834, regVal);
-	regVal = REG_READ(ah, 0x9808);
-	regVal &= (~(0x1 << 27));
-	REG_WRITE(ah, 0x9808, regVal);
-
 	for (i = 0; i < ARRAY_SIZE(regList); i++)
 		REG_WRITE(ah, regList[i][0], regList[i][1]);
 
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_aic.c b/drivers/net/wireless/ath/ath9k/ar9003_aic.c
new file mode 100644
index 0000000..1db119d
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar9003_aic.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 2015 Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "hw.h"
+#include "hw-ops.h"
+#include "ar9003_mci.h"
+#include "ar9003_aic.h"
+#include "ar9003_phy.h"
+#include "reg_aic.h"
+
+static const u8 com_att_db_table[ATH_AIC_MAX_COM_ATT_DB_TABLE] = {
+	0, 3, 9, 15, 21, 27
+};
+
+static const u16 aic_lin_table[ATH_AIC_MAX_AIC_LIN_TABLE] = {
+	8191, 7300, 6506, 5799, 5168, 4606, 4105, 3659,
+	3261, 2906, 2590, 2309, 2057, 1834, 1634, 1457,
+	1298, 1157, 1031, 919,	819,  730,  651,  580,
+	517,  461,  411,  366,	326,  291,  259,  231,
+	206,  183,  163,  146,	130,  116,  103,  92,
+	82,   73,   65,	  58,	52,   46,   41,	  37,
+	33,   29,   26,	  23,	21,   18,   16,	  15,
+	13,   12,   10,	  9,	8,    7,    7,	  6,
+	5,    5,    4,	  4,	3
+};
+
+static bool ar9003_hw_is_aic_enabled(struct ath_hw *ah)
+{
+	struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci;
+
+	/*
+	 * Disable AIC for now, until we have all the
+	 * HW code and the driver-layer support ready.
+	 */
+	return false;
+
+	if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_AIC)
+		return false;
+
+	return true;
+}
+
+static int16_t ar9003_aic_find_valid(struct ath_aic_sram_info *cal_sram,
+				     bool dir, u8 index)
+{
+	int16_t i;
+
+	if (dir) {
+		for (i = index + 1; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+			if (cal_sram[i].valid)
+				break;
+		}
+	} else {
+		for (i = index - 1; i >= 0; i--) {
+			if (cal_sram[i].valid)
+				break;
+		}
+	}
+
+	if ((i >= ATH_AIC_MAX_BT_CHANNEL) || (i < 0))
+		i = -1;
+
+	return i;
+}
+
+/*
+ * type 0: aic_lin_table, 1: com_att_db_table
+ */
+static int16_t ar9003_aic_find_index(u8 type, int16_t value)
+{
+	int16_t i = -1;
+
+	if (type == 0) {
+		for (i = ATH_AIC_MAX_AIC_LIN_TABLE - 1; i >= 0; i--) {
+			if (aic_lin_table[i] >= value)
+				break;
+		}
+	} else if (type == 1) {
+		for (i = 0; i < ATH_AIC_MAX_COM_ATT_DB_TABLE; i++) {
+			if (com_att_db_table[i] > value) {
+				i--;
+				break;
+			}
+		}
+
+		if (i >= ATH_AIC_MAX_COM_ATT_DB_TABLE)
+			i = -1;
+	}
+
+	return i;
+}
+
+static void ar9003_aic_gain_table(struct ath_hw *ah)
+{
+	u32 aic_atten_word[19], i;
+
+	/* Config LNA gain difference */
+	REG_WRITE(ah, AR_PHY_BT_COEX_4, 0x2c200a00);
+	REG_WRITE(ah, AR_PHY_BT_COEX_5, 0x5c4e4438);
+
+	/* Program gain table */
+	aic_atten_word[0] = (0x1 & 0xf) << 14 | (0x1f & 0x1f) << 9 | (0x0 & 0xf) << 5 |
+		(0x1f & 0x1f); /* -01 dB: 4'd1, 5'd31,  00 dB: 4'd0, 5'd31 */
+	aic_atten_word[1] = (0x3 & 0xf) << 14 | (0x1f & 0x1f) << 9 | (0x2 & 0xf) << 5 |
+		(0x1f & 0x1f); /* -03 dB: 4'd3, 5'd31, -02 dB: 4'd2, 5'd31 */
+	aic_atten_word[2] = (0x5 & 0xf) << 14 | (0x1f & 0x1f) << 9 | (0x4 & 0xf) << 5 |
+		(0x1f & 0x1f); /* -05 dB: 4'd5, 5'd31, -04 dB: 4'd4, 5'd31 */
+	aic_atten_word[3] = (0x1 & 0xf) << 14 | (0x1e & 0x1f) << 9 | (0x0 & 0xf) << 5 |
+		(0x1e & 0x1f); /* -07 dB: 4'd1, 5'd30, -06 dB: 4'd0, 5'd30 */
+	aic_atten_word[4] = (0x3 & 0xf) << 14 | (0x1e & 0x1f) << 9 | (0x2 & 0xf) << 5 |
+		(0x1e & 0x1f); /* -09 dB: 4'd3, 5'd30, -08 dB: 4'd2, 5'd30 */
+	aic_atten_word[5] = (0x5 & 0xf) << 14 | (0x1e & 0x1f) << 9 | (0x4 & 0xf) << 5 |
+		(0x1e & 0x1f); /* -11 dB: 4'd5, 5'd30, -10 dB: 4'd4, 5'd30 */
+	aic_atten_word[6] = (0x1 & 0xf) << 14 | (0xf & 0x1f) << 9  | (0x0 & 0xf) << 5 |
+		(0xf & 0x1f);  /* -13 dB: 4'd1, 5'd15, -12 dB: 4'd0, 5'd15 */
+	aic_atten_word[7] = (0x3 & 0xf) << 14 | (0xf & 0x1f) << 9  | (0x2 & 0xf) << 5 |
+		(0xf & 0x1f);  /* -15 dB: 4'd3, 5'd15, -14 dB: 4'd2, 5'd15 */
+	aic_atten_word[8] = (0x5 & 0xf) << 14 | (0xf & 0x1f) << 9  | (0x4 & 0xf) << 5 |
+		(0xf & 0x1f);  /* -17 dB: 4'd5, 5'd15, -16 dB: 4'd4, 5'd15 */
+	aic_atten_word[9] = (0x1 & 0xf) << 14 | (0x7 & 0x1f) << 9  | (0x0 & 0xf) << 5 |
+		(0x7 & 0x1f);  /* -19 dB: 4'd1, 5'd07, -18 dB: 4'd0, 5'd07 */
+	aic_atten_word[10] = (0x3 & 0xf) << 14 | (0x7 & 0x1f) << 9  | (0x2 & 0xf) << 5 |
+		(0x7 & 0x1f);  /* -21 dB: 4'd3, 5'd07, -20 dB: 4'd2, 5'd07 */
+	aic_atten_word[11] = (0x5 & 0xf) << 14 | (0x7 & 0x1f) << 9  | (0x4 & 0xf) << 5 |
+		(0x7 & 0x1f);  /* -23 dB: 4'd5, 5'd07, -22 dB: 4'd4, 5'd07 */
+	aic_atten_word[12] = (0x7 & 0xf) << 14 | (0x7 & 0x1f) << 9  | (0x6 & 0xf) << 5 |
+		(0x7 & 0x1f);  /* -25 dB: 4'd7, 5'd07, -24 dB: 4'd6, 5'd07 */
+	aic_atten_word[13] = (0x3 & 0xf) << 14 | (0x3 & 0x1f) << 9  | (0x2 & 0xf) << 5 |
+		(0x3 & 0x1f);  /* -27 dB: 4'd3, 5'd03, -26 dB: 4'd2, 5'd03 */
+	aic_atten_word[14] = (0x5 & 0xf) << 14 | (0x3 & 0x1f) << 9  | (0x4 & 0xf) << 5 |
+		(0x3 & 0x1f);  /* -29 dB: 4'd5, 5'd03, -28 dB: 4'd4, 5'd03 */
+	aic_atten_word[15] = (0x1 & 0xf) << 14 | (0x1 & 0x1f) << 9  | (0x0 & 0xf) << 5 |
+		(0x1 & 0x1f);  /* -31 dB: 4'd1, 5'd01, -30 dB: 4'd0, 5'd01 */
+	aic_atten_word[16] = (0x3 & 0xf) << 14 | (0x1 & 0x1f) << 9  | (0x2 & 0xf) << 5 |
+		(0x1 & 0x1f);  /* -33 dB: 4'd3, 5'd01, -32 dB: 4'd2, 5'd01 */
+	aic_atten_word[17] = (0x5 & 0xf) << 14 | (0x1 & 0x1f) << 9  | (0x4 & 0xf) << 5 |
+		(0x1 & 0x1f);  /* -35 dB: 4'd5, 5'd01, -34 dB: 4'd4, 5'd01 */
+	aic_atten_word[18] = (0x7 & 0xf) << 14 | (0x1 & 0x1f) << 9  | (0x6 & 0xf) << 5 |
+		(0x1 & 0x1f);  /* -37 dB: 4'd7, 5'd01, -36 dB: 4'd6, 5'd01 */
+
+	/* Write to Gain table with auto increment enabled. */
+	REG_WRITE(ah, (AR_PHY_AIC_SRAM_ADDR_B0 + 0x3000),
+		  (ATH_AIC_SRAM_AUTO_INCREMENT |
+		   ATH_AIC_SRAM_GAIN_TABLE_OFFSET));
+
+	for (i = 0; i < 19; i++) {
+		REG_WRITE(ah, (AR_PHY_AIC_SRAM_DATA_B0 + 0x3000),
+			  aic_atten_word[i]);
+	}
+}
+
+static u8 ar9003_aic_cal_start(struct ath_hw *ah, u8 min_valid_count)
+{
+	struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+	int i;
+
+	/* Write to Gain table with auto increment enabled. */
+	REG_WRITE(ah, (AR_PHY_AIC_SRAM_ADDR_B0 + 0x3000),
+		  (ATH_AIC_SRAM_AUTO_INCREMENT |
+		   ATH_AIC_SRAM_CAL_OFFSET));
+
+	for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+		REG_WRITE(ah, (AR_PHY_AIC_SRAM_DATA_B0 + 0x3000), 0);
+		aic->aic_sram[i] = 0;
+	}
+
+	REG_WRITE(ah, AR_PHY_AIC_CTRL_0_B0,
+		  (SM(0, AR_PHY_AIC_MON_ENABLE) |
+		   SM(127, AR_PHY_AIC_CAL_MAX_HOP_COUNT) |
+		   SM(min_valid_count, AR_PHY_AIC_CAL_MIN_VALID_COUNT) |
+		   SM(37, AR_PHY_AIC_F_WLAN) |
+		   SM(1, AR_PHY_AIC_CAL_CH_VALID_RESET) |
+		   SM(0, AR_PHY_AIC_CAL_ENABLE) |
+		   SM(0x40, AR_PHY_AIC_BTTX_PWR_THR) |
+		   SM(0, AR_PHY_AIC_ENABLE)));
+
+	REG_WRITE(ah, AR_PHY_AIC_CTRL_0_B1,
+		  (SM(0, AR_PHY_AIC_MON_ENABLE) |
+		   SM(1, AR_PHY_AIC_CAL_CH_VALID_RESET) |
+		   SM(0, AR_PHY_AIC_CAL_ENABLE) |
+		   SM(0x40, AR_PHY_AIC_BTTX_PWR_THR) |
+		   SM(0, AR_PHY_AIC_ENABLE)));
+
+	REG_WRITE(ah, AR_PHY_AIC_CTRL_1_B0,
+		  (SM(8, AR_PHY_AIC_CAL_BT_REF_DELAY) |
+		   SM(0, AR_PHY_AIC_BT_IDLE_CFG) |
+		   SM(1, AR_PHY_AIC_STDBY_COND) |
+		   SM(37, AR_PHY_AIC_STDBY_ROT_ATT_DB) |
+		   SM(5, AR_PHY_AIC_STDBY_COM_ATT_DB) |
+		   SM(15, AR_PHY_AIC_RSSI_MAX) |
+		   SM(0, AR_PHY_AIC_RSSI_MIN)));
+
+	REG_WRITE(ah, AR_PHY_AIC_CTRL_1_B1,
+		  (SM(15, AR_PHY_AIC_RSSI_MAX) |
+		   SM(0, AR_PHY_AIC_RSSI_MIN)));
+
+	REG_WRITE(ah, AR_PHY_AIC_CTRL_2_B0,
+		  (SM(44, AR_PHY_AIC_RADIO_DELAY) |
+		   SM(8, AR_PHY_AIC_CAL_STEP_SIZE_CORR) |
+		   SM(12, AR_PHY_AIC_CAL_ROT_IDX_CORR) |
+		   SM(2, AR_PHY_AIC_CAL_CONV_CHECK_FACTOR) |
+		   SM(5, AR_PHY_AIC_ROT_IDX_COUNT_MAX) |
+		   SM(0, AR_PHY_AIC_CAL_SYNTH_TOGGLE) |
+		   SM(0, AR_PHY_AIC_CAL_SYNTH_AFTER_BTRX) |
+		   SM(200, AR_PHY_AIC_CAL_SYNTH_SETTLING)));
+
+	REG_WRITE(ah, AR_PHY_AIC_CTRL_3_B0,
+		  (SM(2, AR_PHY_AIC_MON_MAX_HOP_COUNT) |
+		   SM(1, AR_PHY_AIC_MON_MIN_STALE_COUNT) |
+		   SM(1, AR_PHY_AIC_MON_PWR_EST_LONG) |
+		   SM(2, AR_PHY_AIC_MON_PD_TALLY_SCALING) |
+		   SM(10, AR_PHY_AIC_MON_PERF_THR) |
+		   SM(2, AR_PHY_AIC_CAL_TARGET_MAG_SETTING) |
+		   SM(1, AR_PHY_AIC_CAL_PERF_CHECK_FACTOR) |
+		   SM(1, AR_PHY_AIC_CAL_PWR_EST_LONG)));
+
+	REG_WRITE(ah, AR_PHY_AIC_CTRL_4_B0,
+		  (SM(2, AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO) |
+		   SM(3, AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO) |
+		   SM(0, AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING) |
+		   SM(2, AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF) |
+		   SM(1, AR_PHY_AIC_CAL_COM_ATT_DB_FIXED)));
+
+	REG_WRITE(ah, AR_PHY_AIC_CTRL_4_B1,
+		  (SM(2, AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO) |
+		   SM(3, AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO) |
+		   SM(0, AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING) |
+		   SM(2, AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF) |
+		   SM(1, AR_PHY_AIC_CAL_COM_ATT_DB_FIXED)));
+
+	ar9003_aic_gain_table(ah);
+
+	/* Need to enable AIC reference signal in BT modem. */
+	REG_WRITE(ah, ATH_AIC_BT_JUPITER_CTRL,
+		  (REG_READ(ah, ATH_AIC_BT_JUPITER_CTRL) |
+		   ATH_AIC_BT_AIC_ENABLE));
+
+	aic->aic_cal_start_time = REG_READ(ah, AR_TSF_L32);
+
+	/* Start calibration */
+	REG_CLR_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE);
+	REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_CH_VALID_RESET);
+	REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE);
+
+	aic->aic_caled_chan = 0;
+	aic->aic_cal_state = AIC_CAL_STATE_STARTED;
+
+	return aic->aic_cal_state;
+}
+
+static bool ar9003_aic_cal_post_process(struct ath_hw *ah)
+{
+	struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+	struct ath_aic_sram_info cal_sram[ATH_AIC_MAX_BT_CHANNEL];
+	struct ath_aic_out_info aic_sram[ATH_AIC_MAX_BT_CHANNEL];
+	u32 dir_path_gain_idx, quad_path_gain_idx, value;
+	u32 fixed_com_att_db;
+	int8_t dir_path_sign, quad_path_sign;
+	int16_t i;
+	bool ret = true;
+
+	memset(&cal_sram, 0, sizeof(cal_sram));
+	memset(&aic_sram, 0, sizeof(aic_sram));
+
+	for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+		value = aic->aic_sram[i];
+
+		cal_sram[i].valid =
+			MS(value, AR_PHY_AIC_SRAM_VALID);
+		cal_sram[i].rot_quad_att_db =
+			MS(value, AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB);
+		cal_sram[i].vga_quad_sign =
+			MS(value, AR_PHY_AIC_SRAM_VGA_QUAD_SIGN);
+		cal_sram[i].rot_dir_att_db =
+			MS(value, AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB);
+		cal_sram[i].vga_dir_sign =
+			MS(value, AR_PHY_AIC_SRAM_VGA_DIR_SIGN);
+		cal_sram[i].com_att_6db =
+			MS(value, AR_PHY_AIC_SRAM_COM_ATT_6DB);
+
+		if (cal_sram[i].valid) {
+			dir_path_gain_idx = cal_sram[i].rot_dir_att_db +
+				com_att_db_table[cal_sram[i].com_att_6db];
+			quad_path_gain_idx = cal_sram[i].rot_quad_att_db +
+				com_att_db_table[cal_sram[i].com_att_6db];
+
+			dir_path_sign = (cal_sram[i].vga_dir_sign) ? 1 : -1;
+			quad_path_sign = (cal_sram[i].vga_quad_sign) ? 1 : -1;
+
+			aic_sram[i].dir_path_gain_lin = dir_path_sign *
+				aic_lin_table[dir_path_gain_idx];
+			aic_sram[i].quad_path_gain_lin = quad_path_sign *
+				aic_lin_table[quad_path_gain_idx];
+		}
+	}
+
+	for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+		int16_t start_idx, end_idx;
+
+		if (cal_sram[i].valid)
+			continue;
+
+		start_idx = ar9003_aic_find_valid(cal_sram, 0, i);
+		end_idx = ar9003_aic_find_valid(cal_sram, 1, i);
+
+		if (start_idx < 0) {
+			/* extrapolation */
+			start_idx = end_idx;
+			end_idx = ar9003_aic_find_valid(cal_sram, 1, start_idx);
+
+			if (end_idx < 0) {
+				ret = false;
+				break;
+			}
+
+			aic_sram[i].dir_path_gain_lin =
+				((aic_sram[start_idx].dir_path_gain_lin -
+				  aic_sram[end_idx].dir_path_gain_lin) *
+				 (start_idx - i) + ((end_idx - i) >> 1)) /
+				(end_idx - i) +
+				aic_sram[start_idx].dir_path_gain_lin;
+			aic_sram[i].quad_path_gain_lin =
+				((aic_sram[start_idx].quad_path_gain_lin -
+				  aic_sram[end_idx].quad_path_gain_lin) *
+				 (start_idx - i) + ((end_idx - i) >> 1)) /
+				(end_idx - i) +
+				aic_sram[start_idx].quad_path_gain_lin;
+		}
+
+		if (end_idx < 0) {
+			/* extrapolation */
+			end_idx = ar9003_aic_find_valid(cal_sram, 0, start_idx);
+
+			if (end_idx < 0) {
+				ret = false;
+				break;
+			}
+
+			aic_sram[i].dir_path_gain_lin =
+				((aic_sram[start_idx].dir_path_gain_lin -
+				  aic_sram[end_idx].dir_path_gain_lin) *
+				 (i - start_idx) + ((start_idx - end_idx) >> 1)) /
+				(start_idx - end_idx) +
+				aic_sram[start_idx].dir_path_gain_lin;
+			aic_sram[i].quad_path_gain_lin =
+				((aic_sram[start_idx].quad_path_gain_lin -
+				  aic_sram[end_idx].quad_path_gain_lin) *
+				 (i - start_idx) + ((start_idx - end_idx) >> 1)) /
+				(start_idx - end_idx) +
+				aic_sram[start_idx].quad_path_gain_lin;
+
+		} else if (start_idx >= 0){
+			/* interpolation */
+			aic_sram[i].dir_path_gain_lin =
+				(((end_idx - i) * aic_sram[start_idx].dir_path_gain_lin) +
+				 ((i - start_idx) * aic_sram[end_idx].dir_path_gain_lin) +
+				 ((end_idx - start_idx) >> 1)) /
+				(end_idx - start_idx);
+			aic_sram[i].quad_path_gain_lin =
+				(((end_idx - i) * aic_sram[start_idx].quad_path_gain_lin) +
+				 ((i - start_idx) * aic_sram[end_idx].quad_path_gain_lin) +
+				 ((end_idx - start_idx) >> 1))/
+				(end_idx - start_idx);
+		}
+	}
+
+	/* From dir/quad_path_gain_lin to sram. */
+	i = ar9003_aic_find_valid(cal_sram, 1, 0);
+	if (i < 0) {
+		i = 0;
+		ret = false;
+	}
+	fixed_com_att_db = com_att_db_table[cal_sram[i].com_att_6db];
+
+	for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+		int16_t rot_dir_path_att_db, rot_quad_path_att_db;
+
+		aic_sram[i].sram.vga_dir_sign =
+			(aic_sram[i].dir_path_gain_lin >= 0) ? 1 : 0;
+		aic_sram[i].sram.vga_quad_sign=
+			(aic_sram[i].quad_path_gain_lin >= 0) ? 1 : 0;
+
+		rot_dir_path_att_db =
+			ar9003_aic_find_index(0, abs(aic_sram[i].dir_path_gain_lin)) -
+			fixed_com_att_db;
+		rot_quad_path_att_db =
+			ar9003_aic_find_index(0, abs(aic_sram[i].quad_path_gain_lin)) -
+			fixed_com_att_db;
+
+		aic_sram[i].sram.com_att_6db =
+			ar9003_aic_find_index(1, fixed_com_att_db);
+
+		aic_sram[i].sram.valid = 1;
+
+		aic_sram[i].sram.rot_dir_att_db =
+			min(max(rot_dir_path_att_db,
+				(int16_t)ATH_AIC_MIN_ROT_DIR_ATT_DB),
+			    ATH_AIC_MAX_ROT_DIR_ATT_DB);
+		aic_sram[i].sram.rot_quad_att_db =
+			min(max(rot_quad_path_att_db,
+				(int16_t)ATH_AIC_MIN_ROT_QUAD_ATT_DB),
+			    ATH_AIC_MAX_ROT_QUAD_ATT_DB);
+	}
+
+	for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+		aic->aic_sram[i] = (SM(aic_sram[i].sram.vga_dir_sign,
+				       AR_PHY_AIC_SRAM_VGA_DIR_SIGN) |
+				    SM(aic_sram[i].sram.vga_quad_sign,
+				       AR_PHY_AIC_SRAM_VGA_QUAD_SIGN) |
+				    SM(aic_sram[i].sram.com_att_6db,
+				       AR_PHY_AIC_SRAM_COM_ATT_6DB) |
+				    SM(aic_sram[i].sram.valid,
+				       AR_PHY_AIC_SRAM_VALID) |
+				    SM(aic_sram[i].sram.rot_dir_att_db,
+				       AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB) |
+				    SM(aic_sram[i].sram.rot_quad_att_db,
+				       AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB));
+	}
+
+	return ret;
+}
+
+static void ar9003_aic_cal_done(struct ath_hw *ah)
+{
+	struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+
+	/* Disable AIC reference signal in BT modem. */
+	REG_WRITE(ah, ATH_AIC_BT_JUPITER_CTRL,
+		  (REG_READ(ah, ATH_AIC_BT_JUPITER_CTRL) &
+		   ~ATH_AIC_BT_AIC_ENABLE));
+
+	if (ar9003_aic_cal_post_process(ah))
+		aic->aic_cal_state = AIC_CAL_STATE_DONE;
+	else
+		aic->aic_cal_state = AIC_CAL_STATE_ERROR;
+}
+
+static u8 ar9003_aic_cal_continue(struct ath_hw *ah, bool cal_once)
+{
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci;
+	struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+	int i, num_chan;
+
+	num_chan = MS(mci_hw->config, ATH_MCI_CONFIG_AIC_CAL_NUM_CHAN);
+
+	if (!num_chan) {
+		aic->aic_cal_state = AIC_CAL_STATE_ERROR;
+		return aic->aic_cal_state;
+	}
+
+	if (cal_once) {
+		for (i = 0; i < 10000; i++) {
+			if ((REG_READ(ah, AR_PHY_AIC_CTRL_0_B1) &
+			     AR_PHY_AIC_CAL_ENABLE) == 0)
+				break;
+
+			udelay(100);
+		}
+	}
+
+	/*
+	 * Use AR_PHY_AIC_CAL_ENABLE bit instead of AR_PHY_AIC_CAL_DONE.
+	 * Sometimes CAL_DONE bit is not asserted.
+	 */
+	if ((REG_READ(ah, AR_PHY_AIC_CTRL_0_B1) &
+	     AR_PHY_AIC_CAL_ENABLE) != 0) {
+		ath_dbg(common, MCI, "AIC cal is not done after 40ms");
+		goto exit;
+	}
+
+	REG_WRITE(ah, AR_PHY_AIC_SRAM_ADDR_B1,
+		  (ATH_AIC_SRAM_CAL_OFFSET | ATH_AIC_SRAM_AUTO_INCREMENT));
+
+	for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+		u32 value;
+
+		value = REG_READ(ah, AR_PHY_AIC_SRAM_DATA_B1);
+
+		if (value & 0x01) {
+			if (aic->aic_sram[i] == 0)
+				aic->aic_caled_chan++;
+
+			aic->aic_sram[i] = value;
+
+			if (!cal_once)
+				break;
+		}
+	}
+
+	if ((aic->aic_caled_chan >= num_chan) || cal_once) {
+		ar9003_aic_cal_done(ah);
+	} else {
+		/* Start calibration */
+		REG_CLR_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE);
+		REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1,
+			    AR_PHY_AIC_CAL_CH_VALID_RESET);
+		REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE);
+	}
+exit:
+	return aic->aic_cal_state;
+
+}
+
+u8 ar9003_aic_calibration(struct ath_hw *ah)
+{
+	struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+	u8 cal_ret = AIC_CAL_STATE_ERROR;
+
+	switch (aic->aic_cal_state) {
+	case AIC_CAL_STATE_IDLE:
+		cal_ret = ar9003_aic_cal_start(ah, 1);
+		break;
+	case AIC_CAL_STATE_STARTED:
+		cal_ret = ar9003_aic_cal_continue(ah, false);
+		break;
+	case AIC_CAL_STATE_DONE:
+		cal_ret = AIC_CAL_STATE_DONE;
+		break;
+	default:
+		break;
+	}
+
+	return cal_ret;
+}
+
+u8 ar9003_aic_start_normal(struct ath_hw *ah)
+{
+	struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+	int16_t i;
+
+	if (aic->aic_cal_state != AIC_CAL_STATE_DONE)
+		return 1;
+
+	ar9003_aic_gain_table(ah);
+
+	REG_WRITE(ah, AR_PHY_AIC_SRAM_ADDR_B1, ATH_AIC_SRAM_AUTO_INCREMENT);
+
+	for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+		REG_WRITE(ah, AR_PHY_AIC_SRAM_DATA_B1, aic->aic_sram[i]);
+	}
+
+	/* FIXME: Replace these with proper register names */
+	REG_WRITE(ah, 0xa6b0, 0x80);
+	REG_WRITE(ah, 0xa6b4, 0x5b2df0);
+	REG_WRITE(ah, 0xa6b8, 0x10762cc8);
+	REG_WRITE(ah, 0xa6bc, 0x1219a4b);
+	REG_WRITE(ah, 0xa6c0, 0x1e01);
+	REG_WRITE(ah, 0xb6b4, 0xf0);
+	REG_WRITE(ah, 0xb6c0, 0x1e01);
+	REG_WRITE(ah, 0xb6b0, 0x81);
+	REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX4, 0x40000000);
+
+	aic->aic_enabled = true;
+
+	return 0;
+}
+
+u8 ar9003_aic_cal_reset(struct ath_hw *ah)
+{
+	struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+
+	aic->aic_cal_state = AIC_CAL_STATE_IDLE;
+	return aic->aic_cal_state;
+}
+
+u8 ar9003_aic_calibration_single(struct ath_hw *ah)
+{
+	struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci;
+	u8 cal_ret;
+	int num_chan;
+
+	num_chan = MS(mci_hw->config, ATH_MCI_CONFIG_AIC_CAL_NUM_CHAN);
+
+	(void) ar9003_aic_cal_start(ah, num_chan);
+	cal_ret = ar9003_aic_cal_continue(ah, true);
+
+	return cal_ret;
+}
+
+void ar9003_hw_attach_aic_ops(struct ath_hw *ah)
+{
+	struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
+
+	priv_ops->is_aic_enabled = ar9003_hw_is_aic_enabled;
+}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_aic.h b/drivers/net/wireless/ath/ath9k/ar9003_aic.h
new file mode 100644
index 0000000..86f4064
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar9003_aic.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015 Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef AR9003_AIC_H
+#define AR9003_AIC_H
+
+#define ATH_AIC_MAX_COM_ATT_DB_TABLE    6
+#define ATH_AIC_MAX_AIC_LIN_TABLE       69
+#define ATH_AIC_MIN_ROT_DIR_ATT_DB      0
+#define ATH_AIC_MIN_ROT_QUAD_ATT_DB     0
+#define ATH_AIC_MAX_ROT_DIR_ATT_DB      37
+#define ATH_AIC_MAX_ROT_QUAD_ATT_DB     37
+#define ATH_AIC_SRAM_AUTO_INCREMENT     0x80000000
+#define ATH_AIC_SRAM_GAIN_TABLE_OFFSET  0x280
+#define ATH_AIC_SRAM_CAL_OFFSET         0x140
+#define ATH_AIC_SRAM_OFFSET             0x00
+#define ATH_AIC_MEAS_MAG_THRESH         20
+#define ATH_AIC_BT_JUPITER_CTRL         0x66820
+#define ATH_AIC_BT_AIC_ENABLE           0x02
+
+enum aic_cal_state {
+	AIC_CAL_STATE_IDLE = 0,
+	AIC_CAL_STATE_STARTED,
+	AIC_CAL_STATE_DONE,
+	AIC_CAL_STATE_ERROR
+};
+
+struct ath_aic_sram_info {
+	bool valid:1;
+	bool vga_quad_sign:1;
+	bool vga_dir_sign:1;
+	u8 rot_quad_att_db;
+	u8 rot_dir_att_db;
+	u8 com_att_6db;
+};
+
+struct ath_aic_out_info {
+	int16_t dir_path_gain_lin;
+	int16_t quad_path_gain_lin;
+	struct ath_aic_sram_info sram;
+};
+
+u8 ar9003_aic_calibration(struct ath_hw *ah);
+u8 ar9003_aic_start_normal(struct ath_hw *ah);
+u8 ar9003_aic_cal_reset(struct ath_hw *ah);
+u8 ar9003_aic_calibration_single(struct ath_hw *ah);
+
+#endif /* AR9003_AIC_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
index 06ab71d..174442b 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
@@ -1203,24 +1203,41 @@
 static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g)
 {
 	int offset[8] = {0}, total = 0, test;
-	int agc_out, i;
+	int agc_out, i, peak_detect_threshold;
 
+	if (AR_SREV_9550(ah) || AR_SREV_9531(ah))
+		peak_detect_threshold = 8;
+	else
+		peak_detect_threshold = 0;
+
+	/*
+	 * Turn off LNA/SW.
+	 */
 	REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
 		      AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0x1);
 	REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
 		      AR_PHY_65NM_RXRF_GAINSTAGES_LNAON_CALDC, 0x0);
-	if (is_2g)
-		REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
-			      AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR, 0x0);
-	else
-		REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
-			      AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR, 0x0);
 
+	if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9330_11(ah)) {
+		if (is_2g)
+			REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
+				      AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR, 0x0);
+		else
+			REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
+				      AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR, 0x0);
+	}
+
+	/*
+	 * Turn off RXON.
+	 */
 	REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain),
 		      AR_PHY_65NM_RXTX2_RXON_OVR, 0x1);
 	REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain),
 		      AR_PHY_65NM_RXTX2_RXON, 0x0);
 
+	/*
+	 * Turn on AGC for cal.
+	 */
 	REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
 		      AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1);
 	REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
@@ -1228,16 +1245,19 @@
 	REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
 		      AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0x1);
 
-	if (AR_SREV_9330_11(ah)) {
+	if (AR_SREV_9330_11(ah))
 		REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
 			      AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0);
-	} else {
+
+	if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
 		if (is_2g)
 			REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
-				      AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, 0x0);
+				      AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR,
+				      peak_detect_threshold);
 		else
 			REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
-				      AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR, 0x0);
+				      AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR,
+				      peak_detect_threshold);
 	}
 
 	for (i = 6; i > 0; i--) {
@@ -1266,10 +1286,19 @@
 		REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
 			      AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, total);
 
+	/*
+	 * Turn on LNA.
+	 */
 	REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
 		      AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0);
+	/*
+	 * Turn off RXON.
+	 */
 	REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain),
 		      AR_PHY_65NM_RXTX2_RXON_OVR, 0);
+	/*
+	 * Turn off peak detect calibration.
+	 */
 	REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
 		      AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0);
 }
@@ -1611,8 +1640,14 @@
 
 skip_tx_iqcal:
 	if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) {
-		if (AR_SREV_9330_11(ah))
-			ar9003_hw_manual_peak_cal(ah, 0, IS_CHAN_2GHZ(chan));
+		if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) {
+			for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+				if (!(ah->rxchainmask & (1 << i)))
+					continue;
+				ar9003_hw_manual_peak_cal(ah, i,
+							  IS_CHAN_2GHZ(chan));
+			}
+		}
 
 		/*
 		 * For non-AR9550 chips, we just trigger AGC calibration
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 278afac..922042a 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -3536,7 +3536,7 @@
 	int bias = ar9003_modal_header(ah, is2ghz)->xpaBiasLvl;
 
 	if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah) ||
-	    AR_SREV_9531(ah))
+	    AR_SREV_9531(ah) || AR_SREV_9561(ah))
 		REG_RMW_FIELD(ah, AR_CH0_TOP2, AR_CH0_TOP2_XPABIASLVL, bias);
 	else if (AR_SREV_9462(ah) || AR_SREV_9550(ah) || AR_SREV_9565(ah))
 		REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias);
@@ -3599,7 +3599,7 @@
 	if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
 		REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM,
 				AR_SWITCH_TABLE_COM_AR9462_ALL, value);
-	} else if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
+	} else if (AR_SREV_9550(ah) || AR_SREV_9531(ah) || AR_SREV_9561(ah)) {
 		REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM,
 				AR_SWITCH_TABLE_COM_AR9550_ALL, value);
 	} else
@@ -3929,9 +3929,13 @@
 			REG_WRITE(ah, AR_PHY_PMU2, reg_pmu_set);
 			if (!is_pmu_set(ah, AR_PHY_PMU2, reg_pmu_set))
 				return;
-		} else if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
+		} else if (AR_SREV_9462(ah) || AR_SREV_9565(ah) ||
+			   AR_SREV_9561(ah)) {
 			reg_val = le32_to_cpu(pBase->swreg);
 			REG_WRITE(ah, AR_PHY_PMU1, reg_val);
+
+			if (AR_SREV_9561(ah))
+				REG_WRITE(ah, AR_PHY_PMU2, 0x10200000);
 		} else {
 			/* Internal regulator is ON. Write swreg register. */
 			reg_val = le32_to_cpu(pBase->swreg);
@@ -4034,7 +4038,8 @@
 	if (!AR_SREV_9300(ah) &&
 	    !AR_SREV_9340(ah) &&
 	    !AR_SREV_9580(ah) &&
-	    !AR_SREV_9531(ah))
+	    !AR_SREV_9531(ah) &&
+	    !AR_SREV_9561(ah))
 		return;
 
 	xpa_ctl = ar9003_modal_header(ah, is2ghz)->txFrameToXpaOn;
@@ -4812,7 +4817,7 @@
 	}
 
 tempslope:
-	if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
+	if (AR_SREV_9550(ah) || AR_SREV_9531(ah) || AR_SREV_9561(ah)) {
 		u8 txmask = (eep->baseEepHeader.txrxMask & 0xf0) >> 4;
 
 		/*
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
index 06ad217..79fd3b2 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -29,6 +29,7 @@
 #include "ar9565_1p0_initvals.h"
 #include "ar9565_1p1_initvals.h"
 #include "ar953x_initvals.h"
+#include "ar956x_initvals.h"
 
 /* General hardware code for the AR9003 hadware family */
 
@@ -194,16 +195,16 @@
 		INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
 			       ar9485_1_1_baseband_core_txfir_coeff_japan_2484);
 
-		if (ah->config.no_pll_pwrsave) {
+		if (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) {
 			INIT_INI_ARRAY(&ah->iniPcieSerdes,
-				       ar9485_1_1_pcie_phy_clkreq_disable_L1);
+				       ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
 			INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
-				       ar9485_1_1_pcie_phy_clkreq_disable_L1);
+				       ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
 		} else {
 			INIT_INI_ARRAY(&ah->iniPcieSerdes,
-				       ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
+				       ar9485_1_1_pcie_phy_clkreq_disable_L1);
 			INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
-				       ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
+				       ar9485_1_1_pcie_phy_clkreq_disable_L1);
 		}
 	} else if (AR_SREV_9462_21(ah)) {
 		INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
@@ -230,10 +231,20 @@
 			       ar9462_2p1_modes_fast_clock);
 		INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
 			       ar9462_2p1_baseband_core_txfir_coeff_japan_2484);
-		INIT_INI_ARRAY(&ah->iniPcieSerdes,
-			       ar9462_2p1_pciephy_clkreq_disable_L1);
-		INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
-			       ar9462_2p1_pciephy_clkreq_disable_L1);
+
+		/* Awake -> Sleep Setting */
+		if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+		    (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) {
+			INIT_INI_ARRAY(&ah->iniPcieSerdes,
+				       ar9462_2p1_pciephy_clkreq_disable_L1);
+		}
+
+		/* Sleep -> Awake Setting */
+		if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+		    (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) {
+			INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+				       ar9462_2p1_pciephy_clkreq_disable_L1);
+		}
 	} else if (AR_SREV_9462_20(ah)) {
 
 		INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9462_2p0_mac_core);
@@ -261,11 +272,18 @@
 				ar9462_2p0_common_rx_gain);
 
 		/* Awake -> Sleep Setting */
-		INIT_INI_ARRAY(&ah->iniPcieSerdes,
-			       ar9462_2p0_pciephy_clkreq_disable_L1);
+		if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+		    (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) {
+			INIT_INI_ARRAY(&ah->iniPcieSerdes,
+				       ar9462_2p0_pciephy_clkreq_disable_L1);
+		}
+
 		/* Sleep -> Awake Setting */
-		INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
-			       ar9462_2p0_pciephy_clkreq_disable_L1);
+		if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+		    (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) {
+			INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+				       ar9462_2p0_pciephy_clkreq_disable_L1);
+		}
 
 		/* Fast clock modal settings */
 		INIT_INI_ARRAY(&ah->iniModesFastClock,
@@ -358,6 +376,40 @@
 
 		INIT_INI_ARRAY(&ah->iniModesFastClock,
 			       qca953x_1p0_modes_fast_clock);
+	} else if (AR_SREV_9561(ah)) {
+		INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
+			       qca956x_1p0_mac_core);
+		INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST],
+			       qca956x_1p0_mac_postamble);
+
+		INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+			       qca956x_1p0_baseband_core);
+		INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+			       qca956x_1p0_baseband_postamble);
+
+		INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE],
+			       qca956x_1p0_radio_core);
+		INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST],
+			       qca956x_1p0_radio_postamble);
+
+		INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE],
+			       qca956x_1p0_soc_preamble);
+		INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST],
+			       qca956x_1p0_soc_postamble);
+
+		INIT_INI_ARRAY(&ah->iniModesRxGain,
+			       qca956x_1p0_common_wo_xlna_rx_gain_table);
+		INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+			       qca956x_1p0_common_wo_xlna_rx_gain_bounds);
+		INIT_INI_ARRAY(&ah->iniModesTxGain,
+			       qca956x_1p0_modes_no_xpa_tx_gain_table);
+
+		INIT_INI_ARRAY(&ah->ini_dfs,
+			       qca956x_1p0_baseband_postamble_dfs_channel);
+		INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
+			       qca956x_1p0_baseband_core_txfir_coeff_japan_2484);
+		INIT_INI_ARRAY(&ah->iniModesFastClock,
+			       qca956x_1p0_modes_fast_clock);
 	} else if (AR_SREV_9580(ah)) {
 		/* mac */
 		INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
@@ -421,10 +473,19 @@
 		INIT_INI_ARRAY(&ah->iniModesTxGain,
 			       ar9565_1p1_Modes_lowest_ob_db_tx_gain_table);
 
-		INIT_INI_ARRAY(&ah->iniPcieSerdes,
-			       ar9565_1p1_pciephy_clkreq_disable_L1);
-		INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
-			       ar9565_1p1_pciephy_clkreq_disable_L1);
+		/* Awake -> Sleep Setting */
+		if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+		    (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) {
+			INIT_INI_ARRAY(&ah->iniPcieSerdes,
+				       ar9565_1p1_pciephy_clkreq_disable_L1);
+		}
+
+		/* Sleep -> Awake Setting */
+		if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+		    (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) {
+			INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+				       ar9565_1p1_pciephy_clkreq_disable_L1);
+		}
 
 		INIT_INI_ARRAY(&ah->iniModesFastClock,
 				ar9565_1p1_modes_fast_clock);
@@ -456,10 +517,19 @@
 		INIT_INI_ARRAY(&ah->iniModesTxGain,
 			       ar9565_1p0_Modes_lowest_ob_db_tx_gain_table);
 
-		INIT_INI_ARRAY(&ah->iniPcieSerdes,
-			       ar9565_1p0_pciephy_clkreq_disable_L1);
-		INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
-			       ar9565_1p0_pciephy_clkreq_disable_L1);
+		/* Awake -> Sleep Setting */
+		if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+		    (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) {
+			INIT_INI_ARRAY(&ah->iniPcieSerdes,
+				       ar9565_1p0_pciephy_clkreq_disable_L1);
+		}
+
+		/* Sleep -> Awake Setting */
+		if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+		    (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) {
+			INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+				       ar9565_1p0_pciephy_clkreq_disable_L1);
+		}
 
 		INIT_INI_ARRAY(&ah->iniModesFastClock,
 				ar9565_1p0_modes_fast_clock);
@@ -544,6 +614,9 @@
 	else if (AR_SREV_9531_20(ah))
 		INIT_INI_ARRAY(&ah->iniModesTxGain,
 			       qca953x_2p0_modes_xpa_tx_gain_table);
+	else if (AR_SREV_9561(ah))
+		INIT_INI_ARRAY(&ah->iniModesTxGain,
+			       qca956x_1p0_modes_xpa_tx_gain_table);
 	else if (AR_SREV_9580(ah))
 		INIT_INI_ARRAY(&ah->iniModesTxGain,
 			ar9580_1p0_lowest_ob_db_tx_gain_table);
@@ -594,7 +667,10 @@
 		else
 			INIT_INI_ARRAY(&ah->iniModesTxGain,
 				       qca953x_1p0_modes_no_xpa_tx_gain_table);
-	} else if (AR_SREV_9462_21(ah))
+	} else if (AR_SREV_9561(ah))
+		INIT_INI_ARRAY(&ah->iniModesTxGain,
+			       qca956x_1p0_modes_no_xpa_tx_gain_table);
+	else if (AR_SREV_9462_21(ah))
 		INIT_INI_ARRAY(&ah->iniModesTxGain,
 			ar9462_2p1_modes_high_ob_db_tx_gain);
 	else if (AR_SREV_9462_20(ah))
@@ -628,6 +704,9 @@
 	else if (AR_SREV_9580(ah))
 		INIT_INI_ARRAY(&ah->iniModesTxGain,
 			ar9580_1p0_low_ob_db_tx_gain_table);
+	else if (AR_SREV_9561(ah))
+		INIT_INI_ARRAY(&ah->iniModesTxGain,
+			       qca956x_1p0_modes_no_xpa_low_ob_db_tx_gain_table);
 	else if (AR_SREV_9565_11(ah))
 		INIT_INI_ARRAY(&ah->iniModesTxGain,
 			       ar9565_1p1_modes_low_ob_db_tx_gain_table);
@@ -699,6 +778,9 @@
 	else if (AR_SREV_9580(ah))
 		INIT_INI_ARRAY(&ah->iniModesTxGain,
 			ar9580_1p0_type5_tx_gain_table);
+	else if (AR_SREV_9561(ah))
+		INIT_INI_ARRAY(&ah->iniModesTxGain,
+			       qca956x_1p0_modes_no_xpa_green_tx_gain_table);
 	else if (AR_SREV_9300_22(ah))
 		INIT_INI_ARRAY(&ah->iniModesTxGain,
 			ar9300Modes_type5_tx_gain_table_2p2);
@@ -770,6 +852,13 @@
 			       qca953x_1p0_common_rx_gain_table);
 		INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
 			       qca953x_1p0_common_rx_gain_bounds);
+	} else if (AR_SREV_9561(ah)) {
+		INIT_INI_ARRAY(&ah->iniModesRxGain,
+			       qca956x_1p0_common_rx_gain_table);
+		INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+			       qca956x_1p0_common_rx_gain_bounds);
+		INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+			       qca956x_1p0_xlna_only);
 	} else if (AR_SREV_9580(ah))
 		INIT_INI_ARRAY(&ah->iniModesRxGain,
 				ar9580_1p0_rx_gain_table);
@@ -825,6 +914,11 @@
 			       qca953x_2p0_common_wo_xlna_rx_gain_table);
 		INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
 			       qca953x_2p0_common_wo_xlna_rx_gain_bounds);
+	} else if (AR_SREV_9561(ah)) {
+		INIT_INI_ARRAY(&ah->iniModesRxGain,
+			       qca956x_1p0_common_wo_xlna_rx_gain_table);
+		INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+			       qca956x_1p0_common_wo_xlna_rx_gain_bounds);
 	} else if (AR_SREV_9580(ah))
 		INIT_INI_ARRAY(&ah->iniModesRxGain,
 			ar9580_1p0_wo_xlna_rx_gain_table);
@@ -1071,6 +1165,12 @@
 	struct ath_hw_ops *ops = ath9k_hw_ops(ah);
 
 	ar9003_hw_init_mode_regs(ah);
+
+	if (AR_SREV_9003_PCOEM(ah)) {
+		WARN_ON(!ah->iniPcieSerdes.ia_array);
+		WARN_ON(!ah->iniPcieSerdesLowPower.ia_array);
+	}
+
 	priv_ops->init_mode_gain_regs = ar9003_hw_init_mode_gain_regs;
 	priv_ops->init_hang_checks = ar9003_hw_init_hang_checks;
 	priv_ops->detect_mac_hang = ar9003_hw_detect_mac_hang;
@@ -1080,4 +1180,5 @@
 	ar9003_hw_attach_phy_ops(ah);
 	ar9003_hw_attach_calib_ops(ah);
 	ar9003_hw_attach_mac_ops(ah);
+	ar9003_hw_attach_aic_ops(ah);
 }
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c
index 7b94a6c..af5ee41 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c
@@ -19,6 +19,7 @@
 #include "hw-ops.h"
 #include "ar9003_phy.h"
 #include "ar9003_mci.h"
+#include "ar9003_aic.h"
 
 static void ar9003_mci_reset_req_wakeup(struct ath_hw *ah)
 {
@@ -284,12 +285,12 @@
 		  AR_MCI_INTERRUPT_RX_MSG_CONT_RST);
 	REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_BT_PRI);
 
-	if (mci->is_2g) {
+	if (mci->is_2g && MCI_ANT_ARCH_PA_LNA_SHARED(mci)) {
 		ar9003_mci_send_lna_transfer(ah, true);
 		udelay(5);
 	}
 
-	if ((mci->is_2g && !mci->update_2g5g)) {
+	if (mci->is_2g && !mci->update_2g5g && MCI_ANT_ARCH_PA_LNA_SHARED(mci)) {
 		if (ar9003_mci_wait_for_interrupt(ah,
 					AR_MCI_INTERRUPT_RX_MSG_RAW,
 					AR_MCI_INTERRUPT_RX_MSG_LNA_INFO,
@@ -593,7 +594,7 @@
 		if (!time_out)
 			break;
 
-		offset = ar9003_mci_get_next_gpm_offset(ah, false, &more_data);
+		offset = ar9003_mci_get_next_gpm_offset(ah, &more_data);
 
 		if (offset == MCI_GPM_INVALID)
 			continue;
@@ -657,7 +658,7 @@
 		time_out = 0;
 
 	while (more_data == MCI_GPM_MORE) {
-		offset = ar9003_mci_get_next_gpm_offset(ah, false, &more_data);
+		offset = ar9003_mci_get_next_gpm_offset(ah, &more_data);
 		if (offset == MCI_GPM_INVALID)
 			break;
 
@@ -771,8 +772,14 @@
 
 static void ar9003_mci_mute_bt(struct ath_hw *ah)
 {
+	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
+
 	/* disable all MCI messages */
 	REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, 0xffff0000);
+	REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS0, 0xffffffff);
+	REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS1, 0xffffffff);
+	REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS2, 0xffffffff);
+	REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS3, 0xffffffff);
 	REG_SET_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
 
 	/* wait pending HW messages to flush out */
@@ -783,9 +790,10 @@
 	 * 1. reset not after resuming from full sleep
 	 * 2. before reset MCI RX, to quiet BT and avoid MCI RX misalignment
 	 */
-	ar9003_mci_send_lna_take(ah, true);
-
-	udelay(5);
+	if (MCI_ANT_ARCH_PA_LNA_SHARED(mci)) {
+		ar9003_mci_send_lna_take(ah, true);
+		udelay(5);
+	}
 
 	ar9003_mci_send_sys_sleeping(ah, true);
 }
@@ -821,6 +829,80 @@
 		      AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN, 1);
 }
 
+static void ar9003_mci_stat_setup(struct ath_hw *ah)
+{
+	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
+
+	if (!AR_SREV_9565(ah))
+		return;
+
+	if (mci->config & ATH_MCI_CONFIG_MCI_STAT_DBG) {
+		REG_RMW_FIELD(ah, AR_MCI_DBG_CNT_CTRL,
+			      AR_MCI_DBG_CNT_CTRL_ENABLE, 1);
+		REG_RMW_FIELD(ah, AR_MCI_DBG_CNT_CTRL,
+			      AR_MCI_DBG_CNT_CTRL_BT_LINKID,
+			      MCI_STAT_ALL_BT_LINKID);
+	} else {
+		REG_RMW_FIELD(ah, AR_MCI_DBG_CNT_CTRL,
+			      AR_MCI_DBG_CNT_CTRL_ENABLE, 0);
+	}
+}
+
+static void ar9003_mci_set_btcoex_ctrl_9565_1ANT(struct ath_hw *ah)
+{
+	u32 regval;
+
+	regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) |
+		 SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) |
+		 SM(1, AR_BTCOEX_CTRL_PA_SHARED) |
+		 SM(1, AR_BTCOEX_CTRL_LNA_SHARED) |
+		 SM(1, AR_BTCOEX_CTRL_NUM_ANTENNAS) |
+		 SM(1, AR_BTCOEX_CTRL_RX_CHAIN_MASK) |
+		 SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) |
+		 SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) |
+		 SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
+
+	REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2,
+		      AR_BTCOEX_CTRL2_TX_CHAIN_MASK, 0x1);
+	REG_WRITE(ah, AR_BTCOEX_CTRL, regval);
+}
+
+static void ar9003_mci_set_btcoex_ctrl_9565_2ANT(struct ath_hw *ah)
+{
+	u32 regval;
+
+	regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) |
+		 SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) |
+		 SM(0, AR_BTCOEX_CTRL_PA_SHARED) |
+		 SM(0, AR_BTCOEX_CTRL_LNA_SHARED) |
+		 SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) |
+		 SM(1, AR_BTCOEX_CTRL_RX_CHAIN_MASK) |
+		 SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) |
+		 SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) |
+		 SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
+
+	REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2,
+		      AR_BTCOEX_CTRL2_TX_CHAIN_MASK, 0x0);
+	REG_WRITE(ah, AR_BTCOEX_CTRL, regval);
+}
+
+static void ar9003_mci_set_btcoex_ctrl_9462(struct ath_hw *ah)
+{
+	u32 regval;
+
+        regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) |
+		 SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) |
+		 SM(1, AR_BTCOEX_CTRL_PA_SHARED) |
+		 SM(1, AR_BTCOEX_CTRL_LNA_SHARED) |
+		 SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) |
+		 SM(3, AR_BTCOEX_CTRL_RX_CHAIN_MASK) |
+		 SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) |
+		 SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) |
+		 SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
+
+	REG_WRITE(ah, AR_BTCOEX_CTRL, regval);
+}
+
 int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g,
 		     bool is_full_sleep)
 {
@@ -831,11 +913,6 @@
 	ath_dbg(common, MCI, "MCI Reset (full_sleep = %d, is_2g = %d)\n",
 		is_full_sleep, is_2g);
 
-	if (!mci->gpm_addr && !mci->sched_addr) {
-		ath_err(common, "MCI GPM and schedule buffers are not allocated\n");
-		return -ENOMEM;
-	}
-
 	if (REG_READ(ah, AR_BTCOEX_CTRL) == 0xdeadbeef) {
 		ath_err(common, "BTCOEX control register is dead\n");
 		return -EINVAL;
@@ -850,25 +927,16 @@
 	* To avoid MCI state machine be affected by incoming remote MCI msgs,
 	* MCI mode will be enabled later, right before reset the MCI TX and RX.
 	*/
-
-	regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) |
-		 SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) |
-		 SM(1, AR_BTCOEX_CTRL_PA_SHARED) |
-		 SM(1, AR_BTCOEX_CTRL_LNA_SHARED) |
-		 SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) |
-		 SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) |
-		 SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
 	if (AR_SREV_9565(ah)) {
-		regval |= SM(1, AR_BTCOEX_CTRL_NUM_ANTENNAS) |
-			  SM(1, AR_BTCOEX_CTRL_RX_CHAIN_MASK);
-		REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2,
-			      AR_BTCOEX_CTRL2_TX_CHAIN_MASK, 0x1);
-	} else {
-		regval |= SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) |
-			  SM(3, AR_BTCOEX_CTRL_RX_CHAIN_MASK);
-	}
+		u8 ant = MS(mci->config, ATH_MCI_CONFIG_ANT_ARCH);
 
-	REG_WRITE(ah, AR_BTCOEX_CTRL, regval);
+		if (ant == ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_SHARED)
+			ar9003_mci_set_btcoex_ctrl_9565_1ANT(ah);
+		else
+			ar9003_mci_set_btcoex_ctrl_9565_2ANT(ah);
+	} else {
+		ar9003_mci_set_btcoex_ctrl_9462(ah);
+	}
 
 	if (is_2g && !(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA))
 		ar9003_mci_osla_setup(ah, true);
@@ -926,26 +994,32 @@
 	regval &= ~SM(1, AR_MCI_COMMAND2_RESET_RX);
 	REG_WRITE(ah, AR_MCI_COMMAND2, regval);
 
-	ar9003_mci_get_next_gpm_offset(ah, true, NULL);
+	/* Init GPM offset after MCI Reset Rx */
+	ar9003_mci_state(ah, MCI_STATE_INIT_GPM_OFFSET);
 
 	REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE,
 		  (SM(0xe801, AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR) |
 		   SM(0x0000, AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM)));
 
-	REG_CLR_BIT(ah, AR_MCI_TX_CTRL,
-		    AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
+	if (MCI_ANT_ARCH_PA_LNA_SHARED(mci))
+		REG_CLR_BIT(ah, AR_MCI_TX_CTRL,
+			    AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
+	else
+		REG_SET_BIT(ah, AR_MCI_TX_CTRL,
+			    AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
 
 	ar9003_mci_observation_set_up(ah);
 
 	mci->ready = true;
 	ar9003_mci_prep_interface(ah);
+	ar9003_mci_stat_setup(ah);
 
-	if (AR_SREV_9565(ah))
-		REG_RMW_FIELD(ah, AR_MCI_DBG_CNT_CTRL,
-			      AR_MCI_DBG_CNT_CTRL_ENABLE, 0);
 	if (en_int)
 		ar9003_mci_enable_interrupt(ah);
 
+	if (ath9k_hw_is_aic_enabled(ah))
+		ar9003_aic_start_normal(ah);
+
 	return 0;
 }
 
@@ -1218,6 +1292,14 @@
 		}
 		value &= AR_BTCOEX_CTRL_MCI_MODE_EN;
 		break;
+	case MCI_STATE_INIT_GPM_OFFSET:
+		value = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR);
+
+		if (value < mci->gpm_len)
+			mci->gpm_idx = value;
+		else
+			mci->gpm_idx = 0;
+		break;
 	case MCI_STATE_LAST_SCHD_MSG_OFFSET:
 		value = MS(REG_READ(ah, AR_MCI_RX_STATUS),
 				    AR_MCI_RX_LAST_SCHD_MSG_INDEX);
@@ -1284,6 +1366,22 @@
 		value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0;
 		mci->need_flush_btinfo = false;
 		break;
+	case MCI_STATE_AIC_CAL:
+		if (ath9k_hw_is_aic_enabled(ah))
+			value = ar9003_aic_calibration(ah);
+		break;
+	case MCI_STATE_AIC_START:
+		if (ath9k_hw_is_aic_enabled(ah))
+			ar9003_aic_start_normal(ah);
+		break;
+	case MCI_STATE_AIC_CAL_RESET:
+		if (ath9k_hw_is_aic_enabled(ah))
+			value = ar9003_aic_cal_reset(ah);
+		break;
+	case MCI_STATE_AIC_CAL_SINGLE:
+		if (ath9k_hw_is_aic_enabled(ah))
+			value = ar9003_aic_calibration_single(ah);
+		break;
 	default:
 		break;
 	}
@@ -1364,21 +1462,11 @@
 	mci->gpm_idx = 0;
 }
 
-u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, bool first, u32 *more)
+u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, u32 *more)
 {
 	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
 	u32 offset, more_gpm = 0, gpm_ptr;
 
-	if (first) {
-		gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR);
-
-		if (gpm_ptr >= mci->gpm_len)
-			gpm_ptr = 0;
-
-		mci->gpm_idx = gpm_ptr;
-		return gpm_ptr;
-	}
-
 	/*
 	 * This could be useful to avoid new GPM message interrupt which
 	 * may lead to spurious interrupt after power sleep, or multiple
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h
index 2d28278..640017a 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h
@@ -92,14 +92,36 @@
 #define ATH_MCI_CONFIG_CLK_DIV              0x00003000
 #define ATH_MCI_CONFIG_CLK_DIV_S            12
 #define ATH_MCI_CONFIG_DISABLE_TUNING       0x00004000
+#define ATH_MCI_CONFIG_DISABLE_AIC          0x00008000
+#define ATH_MCI_CONFIG_AIC_CAL_NUM_CHAN     0x007f0000
+#define ATH_MCI_CONFIG_AIC_CAL_NUM_CHAN_S   16
+#define ATH_MCI_CONFIG_NO_QUIET_ACK         0x00800000
+#define ATH_MCI_CONFIG_NO_QUIET_ACK_S       23
+#define ATH_MCI_CONFIG_ANT_ARCH             0x07000000
+#define ATH_MCI_CONFIG_ANT_ARCH_S           24
+#define ATH_MCI_CONFIG_FORCE_QUIET_ACK      0x08000000
+#define ATH_MCI_CONFIG_FORCE_QUIET_ACK_S    27
+#define ATH_MCI_CONFIG_FORCE_2CHAIN_ACK     0x10000000
+#define ATH_MCI_CONFIG_MCI_STAT_DBG         0x20000000
 #define ATH_MCI_CONFIG_MCI_WEIGHT_DBG       0x40000000
 #define ATH_MCI_CONFIG_DISABLE_MCI          0x80000000
 
 #define ATH_MCI_CONFIG_MCI_OBS_MASK     (ATH_MCI_CONFIG_MCI_OBS_MCI  | \
 					 ATH_MCI_CONFIG_MCI_OBS_TXRX | \
 					 ATH_MCI_CONFIG_MCI_OBS_BT)
+
 #define ATH_MCI_CONFIG_MCI_OBS_GPIO     0x0000002F
 
+#define ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_NON_SHARED 0x00
+#define ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_SHARED     0x01
+#define ATH_MCI_ANT_ARCH_2_ANT_PA_LNA_NON_SHARED 0x02
+#define ATH_MCI_ANT_ARCH_2_ANT_PA_LNA_SHARED     0x03
+#define ATH_MCI_ANT_ARCH_3_ANT                   0x04
+
+#define MCI_ANT_ARCH_PA_LNA_SHARED(mci)					\
+	((MS(mci->config, ATH_MCI_CONFIG_ANT_ARCH) == ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_SHARED) || \
+	 (MS(mci->config, ATH_MCI_CONFIG_ANT_ARCH) == ATH_MCI_ANT_ARCH_2_ANT_PA_LNA_SHARED))
+
 enum mci_message_header {		/* length of payload */
 	MCI_LNA_CTRL     = 0x10,        /* len = 0 */
 	MCI_CONT_NACK    = 0x20,        /* len = 0 */
@@ -188,20 +210,55 @@
 	MCI_BT_CAL
 };
 
+enum mci_ps_state {
+	MCI_PS_DISABLE,
+	MCI_PS_ENABLE,
+	MCI_PS_ENABLE_OFF,
+	MCI_PS_ENABLE_ON
+};
+
 /* Type of state query */
 enum mci_state_type {
 	MCI_STATE_ENABLE,
+	MCI_STATE_INIT_GPM_OFFSET,
+	MCI_STATE_CHECK_GPM_OFFSET,
+	MCI_STATE_NEXT_GPM_OFFSET,
+	MCI_STATE_LAST_GPM_OFFSET,
+	MCI_STATE_BT,
+	MCI_STATE_SET_BT_SLEEP,
 	MCI_STATE_SET_BT_AWAKE,
+	MCI_STATE_SET_BT_CAL_START,
+	MCI_STATE_SET_BT_CAL,
 	MCI_STATE_LAST_SCHD_MSG_OFFSET,
 	MCI_STATE_REMOTE_SLEEP,
+	MCI_STATE_CONT_STATUS,
 	MCI_STATE_RESET_REQ_WAKE,
 	MCI_STATE_SEND_WLAN_COEX_VERSION,
+	MCI_STATE_SET_BT_COEX_VERSION,
+	MCI_STATE_SEND_WLAN_CHANNELS,
 	MCI_STATE_SEND_VERSION_QUERY,
 	MCI_STATE_SEND_STATUS_QUERY,
+	MCI_STATE_NEED_FLUSH_BT_INFO,
+	MCI_STATE_SET_CONCUR_TX_PRI,
 	MCI_STATE_RECOVER_RX,
 	MCI_STATE_NEED_FTP_STOMP,
+	MCI_STATE_NEED_TUNING,
+	MCI_STATE_NEED_STAT_DEBUG,
+	MCI_STATE_SHARED_CHAIN_CONCUR_TX,
+	MCI_STATE_AIC_CAL,
+	MCI_STATE_AIC_START,
+	MCI_STATE_AIC_CAL_RESET,
+	MCI_STATE_AIC_CAL_SINGLE,
+	MCI_STATE_IS_AR9462,
+	MCI_STATE_IS_AR9565_1ANT,
+	MCI_STATE_IS_AR9565_2ANT,
+	MCI_STATE_WLAN_WEAK_SIGNAL,
+	MCI_STATE_SET_WLAN_PS_STATE,
+	MCI_STATE_GET_WLAN_PS_STATE,
 	MCI_STATE_DEBUG,
-	MCI_STATE_NEED_FLUSH_BT_INFO,
+	MCI_STATE_STAT_DEBUG,
+	MCI_STATE_ALLOW_FCS,
+	MCI_STATE_SET_2G_CONTENTION,
 	MCI_STATE_MAX
 };
 
@@ -255,7 +312,7 @@
 void ar9003_mci_cleanup(struct ath_hw *ah);
 void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr,
 			      u32 *rx_msg_intr);
-u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, bool first, u32 *more);
+u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, u32 *more);
 void ar9003_mci_set_bt_version(struct ath_hw *ah, u8 major, u8 minor);
 void ar9003_mci_send_wlan_channels(struct ath_hw *ah);
 /*
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index d20a39e..c8494e9 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -183,7 +183,8 @@
 			} else {
 				channelSel = CHANSEL_2G(freq) >> 1;
 			}
-		} else if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
+		} else if (AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
+			   AR_SREV_9561(ah)) {
 			if (ah->is_clk_25mhz)
 				div = 75;
 			else
@@ -198,7 +199,8 @@
 		/* Set to 2G mode */
 		bMode = 1;
 	} else {
-		if ((AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) &&
+		if ((AR_SREV_9340(ah) || AR_SREV_9550(ah) ||
+		     AR_SREV_9531(ah) || AR_SREV_9561(ah)) &&
 		    ah->is_clk_25mhz) {
 			channelSel = freq / 75;
 			chan_frac = ((freq % 75) * 0x20000) / 75;
@@ -265,7 +267,7 @@
 	 */
 
 	if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah) ||
-	    AR_SREV_9550(ah)) {
+	    AR_SREV_9550(ah) || AR_SREV_9561(ah)) {
 		if (spur_fbin_ptr[0] == 0) /* No spur */
 			return;
 		max_spur_cnts = 5;
@@ -292,7 +294,7 @@
 
 		negative = 0;
 		if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah) ||
-		    AR_SREV_9550(ah))
+		    AR_SREV_9550(ah) || AR_SREV_9561(ah))
 			cur_bb_spur = ath9k_hw_fbin2freq(spur_fbin_ptr[i],
 							 IS_CHAN_2GHZ(chan));
 		else
@@ -641,8 +643,10 @@
 		(REG_READ(ah, AR_PHY_GEN_CTRL) & AR_PHY_GC_ENABLE_DAC_FIFO);
 
 	/* Enable 11n HT, 20 MHz */
-	phymode = AR_PHY_GC_HT_EN | AR_PHY_GC_SINGLE_HT_LTF1 |
-		  AR_PHY_GC_SHORT_GI_40 | enableDacFifo;
+	phymode = AR_PHY_GC_HT_EN | AR_PHY_GC_SHORT_GI_40 | enableDacFifo;
+
+	if (!AR_SREV_9561(ah))
+		phymode |= AR_PHY_GC_SINGLE_HT_LTF1;
 
 	/* Configure baseband for dynamic 20/40 operation */
 	if (IS_CHAN_HT40(chan)) {
@@ -745,7 +749,8 @@
 	else
 		ah->enabled_cals &= ~TX_CL_CAL;
 
-	if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) {
+	if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah) ||
+	    AR_SREV_9561(ah)) {
 		if (ah->is_clk_25mhz) {
 			REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
 			REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
@@ -812,6 +817,19 @@
 	return ret;
 }
 
+static int ar9561_hw_get_modes_txgain_index(struct ath_hw *ah,
+					    struct ath9k_channel *chan)
+{
+	if (IS_CHAN_2GHZ(chan)) {
+		if (IS_CHAN_HT40(chan))
+			return 1;
+		else
+			return 2;
+	}
+
+	return 0;
+}
+
 static void ar9003_doubler_fix(struct ath_hw *ah)
 {
 	if (AR_SREV_9300(ah) || AR_SREV_9580(ah) || AR_SREV_9550(ah)) {
@@ -911,21 +929,29 @@
 			REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
 					modesIndex, regWrites);
 		}
+
+		if (AR_SREV_9561(ah) && (ar9003_hw_get_rx_gain_idx(ah) == 0))
+			REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+					modesIndex, regWrites);
 	}
 
-	if (AR_SREV_9550(ah))
+	if (AR_SREV_9550(ah) || AR_SREV_9561(ah))
 		REG_WRITE_ARRAY(&ah->ini_modes_rx_gain_bounds, modesIndex,
 				regWrites);
 
 	/*
 	 * TXGAIN initvals.
 	 */
-	if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
+	if (AR_SREV_9550(ah) || AR_SREV_9531(ah) || AR_SREV_9561(ah)) {
 		int modes_txgain_index = 1;
 
 		if (AR_SREV_9550(ah))
 			modes_txgain_index = ar9550_hw_get_modes_txgain_index(ah, chan);
 
+		if (AR_SREV_9561(ah))
+			modes_txgain_index =
+				ar9561_hw_get_modes_txgain_index(ah, chan);
+
 		if (modes_txgain_index < 0)
 			return -EINVAL;
 
@@ -1989,7 +2015,8 @@
 	priv_ops->rf_set_freq = ar9003_hw_set_channel;
 	priv_ops->spur_mitigate_freq = ar9003_hw_spur_mitigate;
 
-	if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah))
+	if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
+	    AR_SREV_9561(ah))
 		priv_ops->compute_pll_control = ar9003_hw_compute_pll_control_soc;
 	else
 		priv_ops->compute_pll_control = ar9003_hw_compute_pll_control;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
index fd090b1..fc595b9 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
@@ -454,7 +454,7 @@
 #define AR_PHY_GEN_CTRL          (AR_SM_BASE + 0x4)
 #define AR_PHY_MODE              (AR_SM_BASE + 0x8)
 #define AR_PHY_ACTIVE            (AR_SM_BASE + 0xc)
-#define AR_PHY_SPUR_MASK_A       (AR_SM_BASE + 0x20)
+#define AR_PHY_SPUR_MASK_A       (AR_SM_BASE + (AR_SREV_9561(ah) ? 0x18 : 0x20))
 #define AR_PHY_SPUR_MASK_B       (AR_SM_BASE + 0x24)
 #define AR_PHY_SPECTRAL_SCAN     (AR_SM_BASE + 0x28)
 #define AR_PHY_RADAR_BW_FILTER   (AR_SM_BASE + 0x2c)
@@ -506,7 +506,7 @@
 #define AR_PHY_TEST_CHAIN_SEL      0xC0000000
 #define AR_PHY_TEST_CHAIN_SEL_S    30
 
-#define AR_PHY_TEST_CTL_STATUS   (AR_SM_BASE + 0x164)
+#define AR_PHY_TEST_CTL_STATUS   (AR_SM_BASE + (AR_SREV_9561(ah) ? 0x160 : 0x164))
 #define AR_PHY_TEST_CTL_TSTDAC_EN         0x1
 #define AR_PHY_TEST_CTL_TSTDAC_EN_S       0
 #define AR_PHY_TEST_CTL_TX_OBS_SEL        0x1C
@@ -525,7 +525,7 @@
 
 #define AR_PHY_CHAN_STATUS       (AR_SM_BASE + 0x16c)
 
-#define AR_PHY_CHAN_INFO_MEMORY				(AR_SM_BASE + 0x170)
+#define AR_PHY_CHAN_INFO_MEMORY (AR_SM_BASE + (AR_SREV_9561(ah) ? 0x16c : 0x170))
 #define AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ	0x00000008
 #define AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ_S	3
 
@@ -536,7 +536,7 @@
 #define AR_PHY_SCRAMBLER_SEED    (AR_SM_BASE + 0x190)
 #define AR_PHY_CCK_TX_CTRL       (AR_SM_BASE + 0x194)
 
-#define AR_PHY_HEAVYCLIP_CTL     (AR_SM_BASE + 0x1a4)
+#define AR_PHY_HEAVYCLIP_CTL     (AR_SM_BASE + (AR_SREV_9561(ah) ? 0x198 : 0x1a4))
 #define AR_PHY_HEAVYCLIP_20      (AR_SM_BASE + 0x1a8)
 #define AR_PHY_HEAVYCLIP_40      (AR_SM_BASE + 0x1ac)
 #define AR_PHY_ILLEGAL_TXRATE    (AR_SM_BASE + 0x1b0)
@@ -640,16 +640,6 @@
 #define AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE		0x0000ff00
 #define AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE_S	8
 
-/* AIC Registers */
-#define AR_PHY_AIC_CTRL_0_B0	(AR_SM_BASE + 0x4b0)
-#define AR_PHY_AIC_CTRL_1_B0	(AR_SM_BASE + 0x4b4)
-#define AR_PHY_AIC_CTRL_2_B0	(AR_SM_BASE + 0x4b8)
-#define AR_PHY_AIC_CTRL_3_B0	(AR_SM_BASE + 0x4bc)
-#define AR_PHY_AIC_STAT_0_B0	(AR_SM_BASE + 0x4c4))
-#define AR_PHY_AIC_STAT_1_B0	(AR_SM_BASE + 0x4c8))
-#define AR_PHY_AIC_CTRL_4_B0	(AR_SM_BASE + 0x4c0)
-#define AR_PHY_AIC_STAT_2_B0	(AR_SM_BASE + 0x4cc)
-
 #define AR_PHY_65NM_CH0_TXRF3       0x16048
 #define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G		0x0000001e
 #define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G_S	1
@@ -726,21 +716,24 @@
 
 #define AR_CH0_TOP2		(AR_SREV_9300(ah) ? 0x1628c : \
 					(AR_SREV_9462(ah) ? 0x16290 : 0x16284))
-#define AR_CH0_TOP2_XPABIASLVL		0xf000
+#define AR_CH0_TOP2_XPABIASLVL		(AR_SREV_9561(ah) ? 0x1e00 : 0xf000)
 #define AR_CH0_TOP2_XPABIASLVL_S	12
 
 #define AR_CH0_XTAL		(AR_SREV_9300(ah) ? 0x16294 : \
-				 ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16298 : 0x16290))
+				 ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16298 : \
+				  (AR_SREV_9561(ah) ? 0x162c0 : 0x16290)))
 #define AR_CH0_XTAL_CAPINDAC	0x7f000000
 #define AR_CH0_XTAL_CAPINDAC_S	24
 #define AR_CH0_XTAL_CAPOUTDAC	0x00fe0000
 #define AR_CH0_XTAL_CAPOUTDAC_S	17
 
-#define AR_PHY_PMU1		((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16340 : 0x16c40)
+#define AR_PHY_PMU1		((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16340 : \
+				 (AR_SREV_9561(ah) ? 0x16cc0 : 0x16c40))
 #define AR_PHY_PMU1_PWD		0x1
 #define AR_PHY_PMU1_PWD_S	0
 
-#define AR_PHY_PMU2		((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16344 : 0x16c44)
+#define AR_PHY_PMU2		((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16344 : \
+				 (AR_SREV_9561(ah) ? 0x16cc4 : 0x16c44))
 #define AR_PHY_PMU2_PGM		0x00200000
 #define AR_PHY_PMU2_PGM_S	21
 
@@ -986,21 +979,6 @@
 #define AR_PHY_TX_IQCAL_STATUS_B1   (AR_SM1_BASE + 0x48c)
 #define AR_PHY_TX_IQCAL_CORR_COEFF_B1(_i)    (AR_SM1_BASE + 0x450 + ((_i) << 2))
 
-/* SM 1 AIC Registers */
-
-#define AR_PHY_AIC_CTRL_0_B1	(AR_SM1_BASE + 0x4b0)
-#define AR_PHY_AIC_CTRL_1_B1	(AR_SM1_BASE + 0x4b4)
-#define AR_PHY_AIC_CTRL_2_B1	(AR_SM1_BASE + 0x4b8)
-#define AR_PHY_AIC_STAT_0_B1	(AR_SM1_BASE + (AR_SREV_9462_10(ah) ? \
-					0x4c0 : 0x4c4))
-#define AR_PHY_AIC_STAT_1_B1	(AR_SM1_BASE + (AR_SREV_9462_10(ah) ? \
-					0x4c4 : 0x4c8))
-#define AR_PHY_AIC_CTRL_4_B1	(AR_SM1_BASE + 0x4c0)
-#define AR_PHY_AIC_STAT_2_B1	(AR_SM1_BASE + 0x4cc)
-
-#define AR_PHY_AIC_SRAM_ADDR_B1	(AR_SM1_BASE + 0x5f0)
-#define AR_PHY_AIC_SRAM_DATA_B1	(AR_SM1_BASE + 0x5f4)
-
 #define AR_PHY_RTT_TABLE_SW_INTF_B(i)	(0x384 + ((i) ? \
 					AR_SM1_BASE : AR_SM_BASE))
 #define AR_PHY_RTT_TABLE_SW_INTF_1_B(i)	(0x388 + ((i) ? \
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c
index 9344188..e4d11fa 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c
@@ -106,7 +106,7 @@
 	int chain, i;
 
 	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
-		if (!(ah->rxchainmask & (1 << chain)))
+		if (!(ah->caps.rx_chainmask & (1 << chain)))
 			continue;
 		for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) {
 			ar9003_hw_rtt_load_hist_entry(ah, chain, i,
@@ -171,7 +171,7 @@
 	int chain, i;
 
 	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
-		if (!(ah->rxchainmask & (1 << chain)))
+		if (!(ah->caps.rx_chainmask & (1 << chain)))
 			continue;
 		for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) {
 			ah->caldata->rtt_table[chain][i] =
@@ -193,7 +193,7 @@
 	int chain, i;
 
 	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
-		if (!(ah->rxchainmask & (1 << chain)))
+		if (!(ah->caps.rx_chainmask & (1 << chain)))
 			continue;
 		for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++)
 			ar9003_hw_rtt_load_hist_entry(ah, chain, i, 0);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_wow.c b/drivers/net/wireless/ath/ath9k/ar9003_wow.c
index 81c88dd..bea41df 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_wow.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_wow.c
@@ -17,28 +17,28 @@
 #include <linux/export.h>
 #include "ath9k.h"
 #include "reg.h"
+#include "reg_wow.h"
 #include "hw-ops.h"
 
-const char *ath9k_hw_wow_event_to_string(u32 wow_event)
+static void ath9k_hw_set_sta_powersave(struct ath_hw *ah)
 {
-	if (wow_event & AH_WOW_MAGIC_PATTERN_EN)
-		return "Magic pattern";
-	if (wow_event & AH_WOW_USER_PATTERN_EN)
-		return "User pattern";
-	if (wow_event & AH_WOW_LINK_CHANGE)
-		return "Link change";
-	if (wow_event & AH_WOW_BEACON_MISS)
-		return "Beacon miss";
-
-	return  "unknown reason";
+	if (!ath9k_hw_mci_is_enabled(ah))
+		goto set;
+	/*
+	 * If MCI is being used, set PWR_SAV only when MCI's
+	 * PS state is disabled.
+	 */
+	if (ar9003_mci_state(ah, MCI_STATE_GET_WLAN_PS_STATE) != MCI_PS_DISABLE)
+		return;
+set:
+	REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
 }
-EXPORT_SYMBOL(ath9k_hw_wow_event_to_string);
 
 static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
 {
 	struct ath_common *common = ath9k_hw_common(ah);
 
-	REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
+	ath9k_hw_set_sta_powersave(ah);
 
 	/* set rx disable bit */
 	REG_WRITE(ah, AR_CR, AR_CR_RXD);
@@ -49,6 +49,18 @@
 		return;
 	}
 
+	if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
+		if (!REG_READ(ah, AR_MAC_PCU_GEN_TIMER_TSF_SEL))
+			REG_CLR_BIT(ah, AR_DIRECT_CONNECT, AR_DC_TSF2_ENABLE);
+	} else if (AR_SREV_9485(ah)){
+		if (!(REG_READ(ah, AR_NDP2_TIMER_MODE) &
+		      AR_GEN_TIMERS2_MODE_ENABLE_MASK))
+			REG_CLR_BIT(ah, AR_DIRECT_CONNECT, AR_DC_TSF2_ENABLE);
+	}
+
+	if (ath9k_hw_mci_is_enabled(ah))
+		REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
+
 	REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
 }
 
@@ -67,16 +79,18 @@
 	/* set the transmit buffer */
 	ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16));
 	ctl[1] = 0;
-	ctl[3] = 0xb;	/* OFDM_6M hardware value for this rate */
 	ctl[4] = 0;
 	ctl[7] = (ah->txchainmask) << 2;
 	ctl[2] = 0xf << 16; /* tx_tries 0 */
 
+	if (IS_CHAN_2GHZ(ah->curchan))
+		ctl[3] = 0x1b;	/* CCK_1M */
+	else
+		ctl[3] = 0xb;	/* OFDM_6M */
+
 	for (i = 0; i < KAL_NUM_DESC_WORDS; i++)
 		REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
 
-	REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
-
 	data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) |
 		       (KAL_TO_DS << 8) | (KAL_DURATION_ID << 16);
 	data_word[1] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) |
@@ -89,9 +103,11 @@
 		       (ap_mac_addr[1] << 8) | (ap_mac_addr[0]);
 	data_word[5] = (ap_mac_addr[5] << 8) | (ap_mac_addr[4]);
 
-	if (AR_SREV_9462_20(ah)) {
-		/* AR9462 2.0 has an extra descriptor word (time based
-		 * discard) compared to other chips */
+	if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565(ah)) {
+		/*
+		 * AR9462 2.0 and AR9565 have an extra descriptor word
+		 * (time based discard) compared to other chips.
+		 */
 		REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + (12 * 4)), 0);
 		wow_ka_data_word0 = AR_WOW_TXBUF(13);
 	} else {
@@ -100,24 +116,24 @@
 
 	for (i = 0; i < KAL_NUM_DATA_WORDS; i++)
 		REG_WRITE(ah, (wow_ka_data_word0 + i*4), data_word[i]);
-
 }
 
-void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
-				u8 *user_mask, int pattern_count,
-				int pattern_len)
+int ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
+			       u8 *user_mask, int pattern_count,
+			       int pattern_len)
 {
 	int i;
 	u32 pattern_val, mask_val;
 	u32 set, clr;
 
-	/* FIXME: should check count by querying the hardware capability */
-	if (pattern_count >= MAX_NUM_PATTERN)
-		return;
+	if (pattern_count >= ah->wow.max_patterns)
+		return -ENOSPC;
 
-	REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count));
+	if (pattern_count < MAX_NUM_PATTERN_LEGACY)
+		REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count));
+	else
+		REG_SET_BIT(ah, AR_MAC_PCU_WOW4, BIT(pattern_count - 8));
 
-	/* set the registers for pattern */
 	for (i = 0; i < MAX_PATTERN_SIZE; i += 4) {
 		memcpy(&pattern_val, user_pattern, 4);
 		REG_WRITE(ah, (AR_WOW_TB_PATTERN(pattern_count) + i),
@@ -125,49 +141,42 @@
 		user_pattern += 4;
 	}
 
-	/* set the registers for mask */
 	for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) {
 		memcpy(&mask_val, user_mask, 4);
 		REG_WRITE(ah, (AR_WOW_TB_MASK(pattern_count) + i), mask_val);
 		user_mask += 4;
 	}
 
-	/* set the pattern length to be matched
-	 *
-	 * AR_WOW_LENGTH1_REG1
-	 * bit 31:24 pattern 0 length
-	 * bit 23:16 pattern 1 length
-	 * bit 15:8 pattern 2 length
-	 * bit 7:0 pattern 3 length
-	 *
-	 * AR_WOW_LENGTH1_REG2
-	 * bit 31:24 pattern 4 length
-	 * bit 23:16 pattern 5 length
-	 * bit 15:8 pattern 6 length
-	 * bit 7:0 pattern 7 length
-	 *
-	 * the below logic writes out the new
-	 * pattern length for the corresponding
-	 * pattern_count, while masking out the
-	 * other fields
-	 */
-
-	ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT);
+	if (pattern_count < MAX_NUM_PATTERN_LEGACY)
+		ah->wow.wow_event_mask |=
+			BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT);
+	else
+		ah->wow.wow_event_mask2 |=
+			BIT((pattern_count - 8) + AR_WOW_PAT_FOUND_SHIFT);
 
 	if (pattern_count < 4) {
-		/* Pattern 0-3 uses AR_WOW_LENGTH1 register */
 		set = (pattern_len & AR_WOW_LENGTH_MAX) <<
 		       AR_WOW_LEN1_SHIFT(pattern_count);
 		clr = AR_WOW_LENGTH1_MASK(pattern_count);
 		REG_RMW(ah, AR_WOW_LENGTH1, set, clr);
-	} else {
-		/* Pattern 4-7 uses AR_WOW_LENGTH2 register */
+	} else if (pattern_count < 8) {
 		set = (pattern_len & AR_WOW_LENGTH_MAX) <<
 		       AR_WOW_LEN2_SHIFT(pattern_count);
 		clr = AR_WOW_LENGTH2_MASK(pattern_count);
 		REG_RMW(ah, AR_WOW_LENGTH2, set, clr);
+	} else if (pattern_count < 12) {
+		set = (pattern_len & AR_WOW_LENGTH_MAX) <<
+		       AR_WOW_LEN3_SHIFT(pattern_count);
+		clr = AR_WOW_LENGTH3_MASK(pattern_count);
+		REG_RMW(ah, AR_WOW_LENGTH3, set, clr);
+	} else if (pattern_count < MAX_NUM_PATTERN) {
+		set = (pattern_len & AR_WOW_LENGTH_MAX) <<
+		       AR_WOW_LEN4_SHIFT(pattern_count);
+		clr = AR_WOW_LENGTH4_MASK(pattern_count);
+		REG_RMW(ah, AR_WOW_LENGTH4, set, clr);
 	}
 
+	return 0;
 }
 EXPORT_SYMBOL(ath9k_hw_wow_apply_pattern);
 
@@ -177,19 +186,18 @@
 	u32 val = 0, rval;
 
 	/*
-	 * read the WoW status register to know
-	 * the wakeup reason
+	 * Read the WoW status register to know
+	 * the wakeup reason.
 	 */
 	rval = REG_READ(ah, AR_WOW_PATTERN);
 	val = AR_WOW_STATUS(rval);
 
 	/*
-	 * mask only the WoW events that we have enabled. Sometimes
+	 * Mask only the WoW events that we have enabled. Sometimes
 	 * we have spurious WoW events from the AR_WOW_PATTERN
 	 * register. This mask will clean it up.
 	 */
-
-	val &= ah->wow_event_mask;
+	val &= ah->wow.wow_event_mask;
 
 	if (val) {
 		if (val & AR_WOW_MAGIC_PAT_FOUND)
@@ -202,6 +210,15 @@
 			wow_status |= AH_WOW_BEACON_MISS;
 	}
 
+	rval = REG_READ(ah, AR_MAC_PCU_WOW4);
+	val = AR_WOW_STATUS2(rval);
+	val &= ah->wow.wow_event_mask2;
+
+	if (val) {
+		if (AR_WOW2_PATTERN_FOUND(val))
+			wow_status |= AH_WOW_USER_PATTERN_EN;
+	}
+
 	/*
 	 * set and clear WOW_PME_CLEAR registers for the chip to
 	 * generate next wow signal.
@@ -213,10 +230,12 @@
 		AR_PMCTRL_PWR_STATE_D1D3);
 
 	/*
-	 * clear all events
+	 * Clear all events.
 	 */
 	REG_WRITE(ah, AR_WOW_PATTERN,
 		  AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN)));
+	REG_WRITE(ah, AR_MAC_PCU_WOW4,
+		  AR_WOW_CLEAR_EVENTS2(REG_READ(ah, AR_MAC_PCU_WOW4)));
 
 	/*
 	 * restore the beacon threshold to init value
@@ -233,190 +252,203 @@
 	if (ah->is_pciexpress)
 		ath9k_hw_configpcipowersave(ah, false);
 
-	ah->wow_event_mask = 0;
+	if (AR_SREV_9462(ah) || AR_SREV_9565(ah) || AR_SREV_9485(ah)) {
+		u32 dc = REG_READ(ah, AR_DIRECT_CONNECT);
+
+		if (!(dc & AR_DC_TSF2_ENABLE))
+			ath9k_hw_gen_timer_start_tsf2(ah);
+	}
+
+	ah->wow.wow_event_mask = 0;
+	ah->wow.wow_event_mask2 = 0;
 
 	return wow_status;
 }
 EXPORT_SYMBOL(ath9k_hw_wow_wakeup);
 
+static void ath9k_hw_wow_set_arwr_reg(struct ath_hw *ah)
+{
+	u32 wa_reg;
+
+	if (!ah->is_pciexpress)
+		return;
+
+	/*
+	 * We need to untie the internal POR (power-on-reset)
+	 * to the external PCI-E reset. We also need to tie
+	 * the PCI-E Phy reset to the PCI-E reset.
+	 */
+	wa_reg = REG_READ(ah, AR_WA);
+	wa_reg &= ~AR_WA_UNTIE_RESET_EN;
+	wa_reg |= AR_WA_RESET_EN;
+	wa_reg |= AR_WA_POR_SHORT;
+
+	REG_WRITE(ah, AR_WA, wa_reg);
+}
+
 void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
 {
 	u32 wow_event_mask;
-	u32 set, clr;
+	u32 keep_alive, magic_pattern, host_pm_ctrl;
+
+	wow_event_mask = ah->wow.wow_event_mask;
 
 	/*
-	 * wow_event_mask is a mask to the AR_WOW_PATTERN register to
-	 * indicate which WoW events we have enabled. The WoW events
-	 * are from the 'pattern_enable' in this function and
-	 * 'pattern_count' of ath9k_hw_wow_apply_pattern()
-	 */
-	wow_event_mask = ah->wow_event_mask;
-
-	/*
-	 * Untie Power-on-Reset from the PCI-E-Reset. When we are in
-	 * WOW sleep, we do want the Reset from the PCI-E to disturb
-	 * our hw state
-	 */
-	if (ah->is_pciexpress) {
-		/*
-		 * we need to untie the internal POR (power-on-reset)
-		 * to the external PCI-E reset. We also need to tie
-		 * the PCI-E Phy reset to the PCI-E reset.
-		 */
-		set = AR_WA_RESET_EN | AR_WA_POR_SHORT;
-		clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
-		REG_RMW(ah, AR_WA, set, clr);
-	}
-
-	/*
-	 * set the power states appropriately and enable PME
-	 */
-	set = AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA |
-	      AR_PMCTRL_AUX_PWR_DET | AR_PMCTRL_WOW_PME_CLR;
-
-	/*
-	 * set and clear WOW_PME_CLEAR registers for the chip
+	 * AR_PMCTRL_HOST_PME_EN - Override PME enable in configuration
+	 *                         space and allow MAC to generate WoW anyway.
+	 *
+	 * AR_PMCTRL_PWR_PM_CTRL_ENA - ???
+	 *
+	 * AR_PMCTRL_AUX_PWR_DET - PCI core SYS_AUX_PWR_DET signal,
+	 *                         needs to be set for WoW in PCI mode.
+	 *
+	 * AR_PMCTRL_WOW_PME_CLR - WoW Clear Signal going to the MAC.
+	 *
+	 * Set the power states appropriately and enable PME.
+	 *
+	 * Set and clear WOW_PME_CLEAR for the chip
 	 * to generate next wow signal.
 	 */
-	REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
-	clr = AR_PMCTRL_WOW_PME_CLR;
-	REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
+	REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_HOST_PME_EN |
+		    			 AR_PMCTRL_PWR_PM_CTRL_ENA |
+		    			 AR_PMCTRL_AUX_PWR_DET |
+		    			 AR_PMCTRL_WOW_PME_CLR);
+	REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_WOW_PME_CLR);
 
 	/*
-	 * Setup for:
-	 *	- beacon misses
-	 *	- magic pattern
-	 *	- keep alive timeout
-	 *	- pattern matching
+	 * Random Backoff.
+	 *
+	 * 31:28 in AR_WOW_PATTERN : Indicates the number of bits used in the
+	 *                           contention window. For value N,
+	 *                           the random backoff will be selected between
+	 *                           0 and (2 ^ N) - 1.
 	 */
+	REG_SET_BIT(ah, AR_WOW_PATTERN,
+		    AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF));
 
 	/*
-	 * Program default values for pattern backoff, aifs/slot/KAL count,
-	 * beacon miss timeout, KAL timeout, etc.
+	 * AIFS time, Slot time, Keep Alive count.
 	 */
-	set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF);
-	REG_SET_BIT(ah, AR_WOW_PATTERN, set);
-
-	set = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) |
-	      AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) |
-	      AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT);
-	REG_SET_BIT(ah, AR_WOW_COUNT, set);
-
+	REG_SET_BIT(ah, AR_WOW_COUNT, AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) |
+		    		      AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) |
+		    		      AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT));
+	/*
+	 * Beacon timeout.
+	 */
 	if (pattern_enable & AH_WOW_BEACON_MISS)
-		set = AR_WOW_BEACON_TIMO;
-	/* We are not using beacon miss, program a large value */
+		REG_WRITE(ah, AR_WOW_BCN_TIMO, AR_WOW_BEACON_TIMO);
 	else
-		set = AR_WOW_BEACON_TIMO_MAX;
-
-	REG_WRITE(ah, AR_WOW_BCN_TIMO, set);
+		REG_WRITE(ah, AR_WOW_BCN_TIMO, AR_WOW_BEACON_TIMO_MAX);
 
 	/*
-	 * Keep alive timo in ms except AR9280
+	 * Keep alive timeout in ms.
 	 */
 	if (!pattern_enable)
-		set = AR_WOW_KEEP_ALIVE_NEVER;
+		REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, AR_WOW_KEEP_ALIVE_NEVER);
 	else
-		set = KAL_TIMEOUT * 32;
-
-	REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, set);
+		REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, KAL_TIMEOUT * 32);
 
 	/*
-	 * Keep alive delay in us. based on 'power on clock',
-	 * therefore in usec
+	 * Keep alive delay in us.
 	 */
-	set = KAL_DELAY * 1000;
-	REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, set);
+	REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, KAL_DELAY * 1000);
 
 	/*
-	 * Create keep alive pattern to respond to beacons
+	 * Create keep alive pattern to respond to beacons.
 	 */
 	ath9k_wow_create_keep_alive_pattern(ah);
 
 	/*
-	 * Configure MAC WoW Registers
+	 * Configure keep alive register.
 	 */
-	set = 0;
+	keep_alive = REG_READ(ah, AR_WOW_KEEP_ALIVE);
+
 	/* Send keep alive timeouts anyway */
-	clr = AR_WOW_KEEP_ALIVE_AUTO_DIS;
+	keep_alive &= ~AR_WOW_KEEP_ALIVE_AUTO_DIS;
 
-	if (pattern_enable & AH_WOW_LINK_CHANGE)
+	if (pattern_enable & AH_WOW_LINK_CHANGE) {
+		keep_alive &= ~AR_WOW_KEEP_ALIVE_FAIL_DIS;
 		wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL;
-	else
-		set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
+	} else {
+		keep_alive |= AR_WOW_KEEP_ALIVE_FAIL_DIS;
+	}
 
-	set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
-	REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr);
+	REG_WRITE(ah, AR_WOW_KEEP_ALIVE, keep_alive);
 
 	/*
-	 * we are relying on a bmiss failure. ensure we have
-	 * enough threshold to prevent false positives
+	 * We are relying on a bmiss failure, ensure we have
+	 * enough threshold to prevent false positives.
 	 */
 	REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR,
 		      AR_WOW_BMISSTHRESHOLD);
 
-	set = 0;
-	clr = 0;
-
 	if (pattern_enable & AH_WOW_BEACON_MISS) {
-		set = AR_WOW_BEACON_FAIL_EN;
 		wow_event_mask |= AR_WOW_BEACON_FAIL;
+		REG_SET_BIT(ah, AR_WOW_BCN_EN, AR_WOW_BEACON_FAIL_EN);
 	} else {
-		clr = AR_WOW_BEACON_FAIL_EN;
+		REG_CLR_BIT(ah, AR_WOW_BCN_EN, AR_WOW_BEACON_FAIL_EN);
 	}
 
-	REG_RMW(ah, AR_WOW_BCN_EN, set, clr);
-
-	set = 0;
-	clr = 0;
 	/*
-	 * Enable the magic packet registers
+	 * Enable the magic packet registers.
 	 */
+	magic_pattern = REG_READ(ah, AR_WOW_PATTERN);
+	magic_pattern |= AR_WOW_MAC_INTR_EN;
+
 	if (pattern_enable & AH_WOW_MAGIC_PATTERN_EN) {
-		set = AR_WOW_MAGIC_EN;
+		magic_pattern |= AR_WOW_MAGIC_EN;
 		wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND;
 	} else {
-		clr = AR_WOW_MAGIC_EN;
+		magic_pattern &= ~AR_WOW_MAGIC_EN;
 	}
-	set |= AR_WOW_MAC_INTR_EN;
-	REG_RMW(ah, AR_WOW_PATTERN, set, clr);
 
+	REG_WRITE(ah, AR_WOW_PATTERN, magic_pattern);
+
+	/*
+	 * Enable pattern matching for packets which are less
+	 * than 256 bytes.
+	 */
 	REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
 		  AR_WOW_PATTERN_SUPPORTED);
 
 	/*
-	 * Set the power states appropriately and enable PME
+	 * Set the power states appropriately and enable PME.
 	 */
-	clr = 0;
-	set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN |
-	      AR_PMCTRL_PWR_PM_CTRL_ENA;
+	host_pm_ctrl = REG_READ(ah, AR_PCIE_PM_CTRL);
+	host_pm_ctrl |= AR_PMCTRL_PWR_STATE_D1D3 |
+			AR_PMCTRL_HOST_PME_EN |
+			AR_PMCTRL_PWR_PM_CTRL_ENA;
+	host_pm_ctrl &= ~AR_PCIE_PM_CTRL_ENA;
 
-	clr = AR_PCIE_PM_CTRL_ENA;
-	REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr);
+	if (AR_SREV_9462(ah)) {
+		/*
+		 * This is needed to prevent the chip waking up
+		 * the host within 3-4 seconds with certain
+		 * platform/BIOS.
+		 */
+		host_pm_ctrl &= ~AR_PMCTRL_PWR_STATE_D1D3;
+		host_pm_ctrl |= AR_PMCTRL_PWR_STATE_D1D3_REAL;
+	}
+
+	REG_WRITE(ah, AR_PCIE_PM_CTRL, host_pm_ctrl);
 
 	/*
-	 * this is needed to prevent the chip waking up
-	 * the host within 3-4 seconds with certain
-	 * platform/BIOS. The fix is to enable
-	 * D1 & D3 to match original definition and
-	 * also match the OTP value. Anyway this
-	 * is more related to SW WOW.
+	 * Enable sequence number generation when asleep.
 	 */
-	clr = AR_PMCTRL_PWR_STATE_D1D3;
-	REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
-
-	set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
-	REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
-
 	REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM);
 
-	/* to bring down WOW power low margin */
-	set = BIT(13);
-	REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set);
+	/* To bring down WOW power low margin */
+	REG_SET_BIT(ah, AR_PCIE_PHY_REG3, BIT(13));
+
+	ath9k_hw_wow_set_arwr_reg(ah);
+
+	if (ath9k_hw_mci_is_enabled(ah))
+		REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
+
 	/* HW WoW */
-	clr = BIT(5);
-	REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
+	REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, BIT(5));
 
 	ath9k_hw_set_powermode_wow_sleep(ah);
-	ah->wow_event_mask = wow_event_mask;
+	ah->wow.wow_event_mask = wow_event_mask;
 }
 EXPORT_SYMBOL(ath9k_hw_wow_enable);
diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
index 159cc6f..6fc0d07 100644
--- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
@@ -358,7 +358,7 @@
 	{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
 	{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
 	{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
-	{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10822, 0xcfa10822},
+	{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10820, 0xcfa10820},
 	{0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
 	{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
 	{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@@ -378,7 +378,7 @@
 	{0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
 	{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
 	{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
-	{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+	{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18},
 	{0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33},
 	{0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982},
 	{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
diff --git a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h
index fd6a84c..148562a 100644
--- a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h
@@ -63,7 +63,7 @@
 	{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
 	{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
 	{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
-	{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10822, 0xcfa10822},
+	{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10820, 0xcfa10820},
 	{0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
 	{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
 	{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@@ -83,7 +83,7 @@
 	{0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
 	{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
 	{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
-	{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+	{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18},
 	{0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33},
 	{0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982},
 	{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
diff --git a/drivers/net/wireless/ath/ath9k/ar956x_initvals.h b/drivers/net/wireless/ath/ath9k/ar956x_initvals.h
new file mode 100644
index 0000000..c3a47ea
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar956x_initvals.h
@@ -0,0 +1,1046 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INITVALS_956X_H
+#define INITVALS_956X_H
+
+#define qca956x_1p0_mac_core ar955x_1p0_mac_core
+
+#define qca956x_1p0_mac_postamble ar9331_1p1_mac_postamble
+
+#define qca956x_1p0_soc_preamble ar955x_1p0_soc_preamble
+
+#define qca956x_1p0_soc_postamble ar9300_2p2_soc_postamble
+
+#define qca956x_1p0_common_wo_xlna_rx_gain_table ar9300Common_wo_xlna_rx_gain_table_2p2
+
+#define qca956x_1p0_baseband_postamble_dfs_channel ar9300_2p2_baseband_postamble_dfs_channel
+
+#define qca956x_1p0_common_wo_xlna_rx_gain_bounds ar955x_1p0_common_wo_xlna_rx_gain_bounds
+
+#define qca956x_1p0_common_rx_gain_bounds ar955x_1p0_common_rx_gain_bounds
+
+#define qca956x_1p0_modes_fast_clock ar9462_2p0_modes_fast_clock
+
+static const u32 qca956x_1p0_baseband_core[][2] = {
+	/* Addr      allmodes  */
+	{0x00009800, 0xafe68e30},
+	{0x00009804, 0xfd14e000},
+	{0x00009808, 0x9c0a9f6b},
+	{0x0000980c, 0x04900000},
+	{0x00009814, 0x0280c00a},
+	{0x00009818, 0x00000000},
+	{0x0000981c, 0x00020028},
+	{0x00009834, 0x6400a190},
+	{0x00009838, 0x0108ecff},
+	{0x0000983c, 0x14000600},
+	{0x00009880, 0x201fff00},
+	{0x00009884, 0x00001042},
+	{0x000098a4, 0x00200400},
+	{0x000098b0, 0x32840cbf},
+	{0x000098bc, 0x00000002},
+	{0x000098d0, 0x004b6a8e},
+	{0x000098d4, 0x00000820},
+	{0x000098dc, 0x00000000},
+	{0x000098f0, 0x00000000},
+	{0x000098f4, 0x00000000},
+	{0x00009c04, 0xff55ff55},
+	{0x00009c08, 0x0320ff55},
+	{0x00009c0c, 0x00000000},
+	{0x00009c10, 0x00000000},
+	{0x00009c14, 0x00046384},
+	{0x00009c18, 0x05b6b440},
+	{0x00009c1c, 0x00b6b440},
+	{0x00009d00, 0xc080a333},
+	{0x00009d04, 0x40206c10},
+	{0x00009d08, 0x009c4060},
+	{0x00009d0c, 0x9883800a},
+	{0x00009d10, 0x01834061},
+	{0x00009d14, 0x00c0040b},
+	{0x00009d18, 0x00000000},
+	{0x00009e08, 0x0038230c},
+	{0x00009e24, 0x990bb514},
+	{0x00009e28, 0x0c6f0000},
+	{0x00009e30, 0x06336f77},
+	{0x00009e34, 0x6af6532f},
+	{0x00009e38, 0x0cc80c00},
+	{0x00009e40, 0x0d261820},
+	{0x00009e4c, 0x00001004},
+	{0x00009e50, 0x00ff03f1},
+	{0x00009fc0, 0x813e4789},
+	{0x00009fc4, 0x0001efb5},
+	{0x00009fcc, 0x40000014},
+	{0x00009fd0, 0x02993b93},
+	{0x0000a20c, 0x00000000},
+	{0x0000a218, 0x00000000},
+	{0x0000a21c, 0x00000000},
+	{0x0000a228, 0x10002310},
+	{0x0000a23c, 0x00000000},
+	{0x0000a244, 0x0c000000},
+	{0x0000a248, 0x00000140},
+	{0x0000a2a0, 0x00000007},
+	{0x0000a2c0, 0x00000007},
+	{0x0000a2c8, 0x00000000},
+	{0x0000a2d4, 0x00000000},
+	{0x0000a2ec, 0x00000000},
+	{0x0000a2f0, 0x00000000},
+	{0x0000a2f4, 0x00000000},
+	{0x0000a2f8, 0x00000000},
+	{0x0000a344, 0x00000000},
+	{0x0000a34c, 0x00000000},
+	{0x0000a350, 0x0000a000},
+	{0x0000a360, 0x00000000},
+	{0x0000a36c, 0x00000000},
+	{0x0000a384, 0x00000001},
+	{0x0000a388, 0x00000444},
+	{0x0000a38c, 0x00000000},
+	{0x0000a390, 0x210d0401},
+	{0x0000a394, 0xab9a7144},
+	{0x0000a398, 0x00000201},
+	{0x0000a39c, 0x42424848},
+	{0x0000a3a0, 0x3c466478},
+	{0x0000a3a4, 0x3a363600},
+	{0x0000a3a8, 0x0000003a},
+	{0x0000a3ac, 0x00000000},
+	{0x0000a3b0, 0x009011fe},
+	{0x0000a3b4, 0x00000034},
+	{0x0000a3b8, 0x00b3ec0a},
+	{0x0000a3bc, 0x00000036},
+	{0x0000a3c0, 0x20202020},
+	{0x0000a3c4, 0x22222220},
+	{0x0000a3c8, 0x20200020},
+	{0x0000a3cc, 0x20202020},
+	{0x0000a3d0, 0x20202020},
+	{0x0000a3d4, 0x20202020},
+	{0x0000a3d8, 0x20202020},
+	{0x0000a3dc, 0x20202020},
+	{0x0000a3e0, 0x20202020},
+	{0x0000a3e4, 0x20202020},
+	{0x0000a3e8, 0x20202020},
+	{0x0000a3ec, 0x20202020},
+	{0x0000a3f0, 0x00000000},
+	{0x0000a3f4, 0x00000000},
+	{0x0000a3f8, 0x0c9bd380},
+	{0x0000a3fc, 0x000f0f01},
+	{0x0000a400, 0x8fa91f01},
+	{0x0000a404, 0x00000000},
+	{0x0000a408, 0x0e79e5c6},
+	{0x0000a40c, 0x00820820},
+	{0x0000a414, 0x1ce739ce},
+	{0x0000a418, 0x2d0019ce},
+	{0x0000a41c, 0x1ce739ce},
+	{0x0000a420, 0x000001ce},
+	{0x0000a424, 0x1ce739ce},
+	{0x0000a428, 0x000001ce},
+	{0x0000a42c, 0x1ce739ce},
+	{0x0000a430, 0x1ce739ce},
+	{0x0000a434, 0x00000000},
+	{0x0000a438, 0x00001801},
+	{0x0000a43c, 0x00100000},
+	{0x0000a444, 0x00000000},
+	{0x0000a448, 0x05000080},
+	{0x0000a44c, 0x00000001},
+	{0x0000a450, 0x00010000},
+	{0x0000a454, 0x05000000},
+	{0x0000a458, 0x00000000},
+	{0x0000a644, 0xbfad9fee},
+	{0x0000a648, 0x0048660d},
+	{0x0000a64c, 0x00003c37},
+	{0x0000a670, 0x03020100},
+	{0x0000a674, 0x21200504},
+	{0x0000a678, 0x61602322},
+	{0x0000a67c, 0x65646362},
+	{0x0000a680, 0x6b6a6968},
+	{0x0000a684, 0xe2706d6c},
+	{0x0000a688, 0x000000e3},
+	{0x0000a690, 0x00000838},
+	{0x0000a7cc, 0x00000000},
+	{0x0000a7d0, 0x00000000},
+	{0x0000a7d4, 0x00000004},
+	{0x0000a7dc, 0x00000000},
+	{0x0000a8d0, 0x004b6a8e},
+	{0x0000a8d4, 0x00000820},
+	{0x0000a8dc, 0x00000000},
+	{0x0000a8f0, 0x00000000},
+	{0x0000a8f4, 0x00000000},
+	{0x0000b2d0, 0x00000080},
+	{0x0000b2d4, 0x00000000},
+	{0x0000b2ec, 0x00000000},
+	{0x0000b2f0, 0x00000000},
+	{0x0000b2f4, 0x00000000},
+	{0x0000b2f8, 0x00000000},
+	{0x0000b408, 0x0e79e5c0},
+	{0x0000b40c, 0x00820820},
+	{0x0000b420, 0x00000000},
+	{0x0000b8d0, 0x004b6a8e},
+	{0x0000b8d4, 0x00000820},
+	{0x0000b8dc, 0x00000000},
+	{0x0000b8f0, 0x00000000},
+	{0x0000b8f4, 0x00000000},
+	{0x0000c2d0, 0x00000080},
+	{0x0000c2d4, 0x00000000},
+	{0x0000c2ec, 0x00000000},
+	{0x0000c2f0, 0x00000000},
+	{0x0000c2f4, 0x00000000},
+	{0x0000c2f8, 0x00000000},
+	{0x0000c408, 0x0e79e5c0},
+	{0x0000c40c, 0x00820820},
+	{0x0000c420, 0x00000000},
+};
+
+static const u32 qca956x_1p0_baseband_postamble[][5] = {
+	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+	{0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011},
+	{0x00009820, 0x206a022e, 0x206a022e, 0x206a01ae, 0x206a01ae},
+	{0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac621f1, 0x5ac621f1},
+	{0x00009828, 0x06903081, 0x06903081, 0x07d43881, 0x07d43881},
+	{0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+	{0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c},
+	{0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4},
+	{0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0},
+	{0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020},
+	{0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000de, 0x6c4000de},
+	{0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec86d2e},
+	{0x00009e14, 0x37b95d5e, 0x37b9605e, 0x337d605e, 0x337d5d5e},
+	{0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+	{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
+	{0x00009e20, 0x000003b5, 0x000003b5, 0x000003a6, 0x000003a6},
+	{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
+	{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcf946222, 0xcf946222},
+	{0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
+	{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
+	{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
+	{0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x045c0cc4, 0x045c0cc0},
+	{0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
+	{0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f},
+	{0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b},
+	{0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff},
+	{0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018},
+	{0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+	{0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+	{0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
+	{0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01010e0e, 0x01010e0e},
+	{0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501},
+	{0x0000a264, 0x00000e0e, 0x00000e0e, 0x01000e0e, 0x01000e0e},
+	{0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
+	{0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
+	{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
+	{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
+	{0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
+	{0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33},
+	{0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982},
+	{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
+	{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+	{0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+	{0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000},
+	{0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+	{0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+	{0x0000ae20, 0x000001b5, 0x000001b5, 0x000001a6, 0x000001a6},
+	{0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
+	{0x0000b830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+	{0x0000be04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000},
+	{0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+	{0x0000be1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+	{0x0000be20, 0x000001b5, 0x000001b5, 0x000001a6, 0x000001a6},
+	{0x0000c284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
+};
+
+static const u32 qca956x_1p0_radio_core[][2] = {
+	/* Addr      allmodes  */
+	{0x00016000, 0x36db6db6},
+	{0x00016004, 0x6db6db40},
+	{0x00016008, 0x73f00000},
+	{0x0001600c, 0x00000000},
+	{0x00016040, 0x3f80fff8},
+	{0x0001604c, 0x000f0278},
+	{0x00016050, 0x8036db6c},
+	{0x00016054, 0x6db60000},
+	{0x00016080, 0x00080000},
+	{0x00016084, 0x0e48048c},
+	{0x00016088, 0x14214514},
+	{0x0001608c, 0x119f080a},
+	{0x00016090, 0x24926490},
+	{0x00016094, 0x00000000},
+	{0x000160a0, 0xc2108ffe},
+	{0x000160a4, 0x812fc370},
+	{0x000160a8, 0x423c8000},
+	{0x000160b4, 0x92480000},
+	{0x000160c0, 0x006db6d8},
+	{0x000160c4, 0x24b6db6c},
+	{0x000160c8, 0x6db6db6c},
+	{0x000160cc, 0x6db6fb7c},
+	{0x000160d0, 0x6db6da44},
+	{0x00016100, 0x07ff8001},
+	{0x00016108, 0x00080010},
+	{0x00016144, 0x01884080},
+	{0x00016148, 0x00008058},
+	{0x00016288, 0x001c6000},
+	{0x0001628c, 0x50000000},
+	{0x000162c0, 0x4b962100},
+	{0x000162c4, 0x00000480},
+	{0x000162c8, 0x04000144},
+	{0x00016380, 0x00000000},
+	{0x00016384, 0x00000000},
+	{0x00016388, 0x00800700},
+	{0x0001638c, 0x00800700},
+	{0x00016390, 0x00800700},
+	{0x00016394, 0x00000000},
+	{0x00016398, 0x00000000},
+	{0x0001639c, 0x00000000},
+	{0x000163a0, 0x00000001},
+	{0x000163a4, 0x00000001},
+	{0x000163a8, 0x00000000},
+	{0x000163ac, 0x00000000},
+	{0x000163b0, 0x00000000},
+	{0x000163b4, 0x00000000},
+	{0x000163b8, 0x00000000},
+	{0x000163bc, 0x00000000},
+	{0x000163c0, 0x000000a0},
+	{0x000163c4, 0x000c0000},
+	{0x000163c8, 0x14021402},
+	{0x000163cc, 0x00001402},
+	{0x000163d0, 0x00000000},
+	{0x000163d4, 0x00000000},
+	{0x00016400, 0x36db6db6},
+	{0x00016404, 0x6db6db40},
+	{0x00016408, 0x73f00000},
+	{0x0001640c, 0x00000000},
+	{0x00016440, 0x3f80fff8},
+	{0x0001644c, 0x000f0278},
+	{0x00016450, 0x8036db6c},
+	{0x00016454, 0x6db60000},
+	{0x00016500, 0x07ff8001},
+	{0x00016508, 0x00080010},
+	{0x00016544, 0x01884080},
+	{0x00016548, 0x00008058},
+	{0x00016780, 0x00000000},
+	{0x00016784, 0x00000000},
+	{0x00016788, 0x00800700},
+	{0x0001678c, 0x00800700},
+	{0x00016790, 0x00800700},
+	{0x00016794, 0x00000000},
+	{0x00016798, 0x00000000},
+	{0x0001679c, 0x00000000},
+	{0x000167a0, 0x00000001},
+	{0x000167a4, 0x00000001},
+	{0x000167a8, 0x00000000},
+	{0x000167ac, 0x00000000},
+	{0x000167b0, 0x00000000},
+	{0x000167b4, 0x00000000},
+	{0x000167b8, 0x00000000},
+	{0x000167bc, 0x00000000},
+	{0x000167c0, 0x000000a0},
+	{0x000167c4, 0x000c0000},
+	{0x000167c8, 0x14021402},
+	{0x000167cc, 0x00001402},
+	{0x000167d0, 0x00000000},
+	{0x000167d4, 0x00000000},
+	{0x00016800, 0x36db6db6},
+	{0x00016804, 0x6db6db40},
+	{0x00016808, 0x73f00000},
+	{0x0001680c, 0x00000000},
+	{0x00016840, 0x3f80fff8},
+	{0x0001684c, 0x000f0278},
+	{0x00016850, 0x8036db6c},
+	{0x00016854, 0x6db60000},
+	{0x00016900, 0x07ff8001},
+	{0x00016908, 0x00080010},
+	{0x00016944, 0x01884080},
+	{0x00016948, 0x00008058},
+	{0x00016b80, 0x00000000},
+	{0x00016b84, 0x00000000},
+	{0x00016b88, 0x00800700},
+	{0x00016b8c, 0x00800700},
+	{0x00016b90, 0x00800700},
+	{0x00016b94, 0x00000000},
+	{0x00016b98, 0x00000000},
+	{0x00016b9c, 0x00000000},
+	{0x00016ba0, 0x00000001},
+	{0x00016ba4, 0x00000001},
+	{0x00016ba8, 0x00000000},
+	{0x00016bac, 0x00000000},
+	{0x00016bb0, 0x00000000},
+	{0x00016bb4, 0x00000000},
+	{0x00016bb8, 0x00000000},
+	{0x00016bbc, 0x00000000},
+	{0x00016bc0, 0x000000a0},
+	{0x00016bc4, 0x000c0000},
+	{0x00016bc8, 0x14021402},
+	{0x00016bcc, 0x00001402},
+	{0x00016bd0, 0x00000000},
+	{0x00016bd4, 0x00000000},
+};
+
+static const u32 qca956x_1p0_radio_postamble[][5] = {
+	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+	{0x00016098, 0xd2dd5554, 0xd2dd5554, 0xc4128f5c, 0xc4128f5c},
+	{0x0001609c, 0x0a566f3a, 0x0a566f3a, 0x0fd08f25, 0x0fd08f25},
+	{0x000160ac, 0xa4647c00, 0xa4647c00, 0x24646800, 0x24646800},
+	{0x000160b0, 0x01885f52, 0x01885f52, 0x00fe7f46, 0x00fe7f46},
+	{0x00016104, 0xb7a00000, 0xb7a00000, 0xfff80001, 0xfff80001},
+	{0x0001610c, 0xc0000000, 0xc0000000, 0x00000000, 0x00000000},
+	{0x00016140, 0x10804008, 0x10804008, 0x50804000, 0x50804000},
+	{0x00016504, 0xb7a00000, 0xb7a00000, 0xfff80001, 0xfff80001},
+	{0x0001650c, 0xc0000000, 0xc0000000, 0x00000000, 0x00000000},
+	{0x00016540, 0x10804008, 0x10804008, 0x50804000, 0x50804000},
+	{0x00016904, 0xb7a00000, 0xb7a00000, 0xfff80001, 0xfff80001},
+	{0x0001690c, 0xc0000000, 0xc0000000, 0x00000000, 0x00000000},
+	{0x00016940, 0x10804008, 0x10804008, 0x50804000, 0x50804000},
+};
+
+static const u32 qca956x_1p0_baseband_core_txfir_coeff_japan_2484[][2] = {
+	/* Addr      allmodes  */
+	{0x0000a38c, 0x00000000},
+	{0x0000a390, 0x6f7f0301},
+	{0x0000a394, 0xca9228ee},
+};
+
+static const u32 qca956x_1p0_modes_no_xpa_tx_gain_table[][3] = {
+	/* Addr      5G          2G        */
+	{0x0000a2dc, 0xffa9ac94, 0xffa9ac94},
+	{0x0000a2e0, 0xff323118, 0xff323118},
+	{0x0000a2e4, 0xff3ffe00, 0xff3ffe00},
+	{0x0000a2e8, 0xffc00000, 0xffc00000},
+	{0x0000a39c, 0x42424242, 0x42424242},
+	{0x0000a3a4, 0x3a3e3e00, 0x3a3e3e00},
+	{0x0000a3b0, 0x00a01404, 0x00a01404},
+	{0x0000a3b4, 0x00000034, 0x00000034},
+	{0x0000a3b8, 0x00800408, 0x00800408},
+	{0x0000a3bc, 0x00000036, 0x00000036},
+	{0x0000a410, 0x000050dc, 0x000050dc},
+	{0x0000a500, 0x09000040, 0x09000040},
+	{0x0000a504, 0x0b000041, 0x0b000041},
+	{0x0000a508, 0x0d000042, 0x0d000042},
+	{0x0000a50c, 0x11000044, 0x11000044},
+	{0x0000a510, 0x15000046, 0x15000046},
+	{0x0000a514, 0x1d000440, 0x1d000440},
+	{0x0000a518, 0x1f000441, 0x1f000441},
+	{0x0000a51c, 0x23000443, 0x23000443},
+	{0x0000a520, 0x25000444, 0x25000444},
+	{0x0000a524, 0x280004e0, 0x280004e0},
+	{0x0000a528, 0x2c0004e2, 0x2c0004e2},
+	{0x0000a52c, 0x2e0004e3, 0x2e0004e3},
+	{0x0000a530, 0x300004e4, 0x300004e4},
+	{0x0000a534, 0x340004e6, 0x340004e6},
+	{0x0000a538, 0x37000ce0, 0x37000ce0},
+	{0x0000a53c, 0x3b000ce2, 0x3b000ce2},
+	{0x0000a540, 0x3d000ce3, 0x3d000ce3},
+	{0x0000a544, 0x3f000ce4, 0x3f000ce4},
+	{0x0000a548, 0x45001ee0, 0x45001ee0},
+	{0x0000a54c, 0x49001ee2, 0x49001ee2},
+	{0x0000a550, 0x4d001ee4, 0x4d001ee4},
+	{0x0000a554, 0x51001ee6, 0x51001ee6},
+	{0x0000a558, 0x55001eea, 0x55001eea},
+	{0x0000a55c, 0x59001eec, 0x59001eec},
+	{0x0000a560, 0x5d001ef0, 0x5d001ef0},
+	{0x0000a564, 0x5f001ef1, 0x5f001ef1},
+	{0x0000a568, 0x60001ef2, 0x60001ef2},
+	{0x0000a56c, 0x61001ef3, 0x61001ef3},
+	{0x0000a570, 0x62001ef4, 0x62001ef4},
+	{0x0000a574, 0x63001ef5, 0x63001ef5},
+	{0x0000a578, 0x64001ffc, 0x64001ffc},
+	{0x0000a57c, 0x64001ffc, 0x64001ffc},
+	{0x0000a600, 0x00000000, 0x00000000},
+	{0x0000a604, 0x00000000, 0x00000000},
+	{0x0000a608, 0x00000000, 0x00000000},
+	{0x0000a60c, 0x00000000, 0x00000000},
+	{0x0000a610, 0x00804000, 0x00804000},
+	{0x0000a614, 0x00804201, 0x00804201},
+	{0x0000a618, 0x00804201, 0x00804201},
+	{0x0000a61c, 0x00804201, 0x00804201},
+	{0x0000a620, 0x00804201, 0x00804201},
+	{0x0000a624, 0x00804201, 0x00804201},
+	{0x0000a628, 0x00804201, 0x00804201},
+	{0x0000a62c, 0x02808a02, 0x02808a02},
+	{0x0000a630, 0x0340cd03, 0x0340cd03},
+	{0x0000a634, 0x0340cd03, 0x0340cd03},
+	{0x0000a638, 0x0340cd03, 0x0340cd03},
+	{0x0000a63c, 0x05011404, 0x05011404},
+	{0x0000b2dc, 0xffa9ac94, 0xffa9ac94},
+	{0x0000b2e0, 0xff323118, 0xff323118},
+	{0x0000b2e4, 0xff3ffe00, 0xff3ffe00},
+	{0x0000b2e8, 0xffc00000, 0xffc00000},
+	{0x0000c2dc, 0xffa9ac94, 0xffa9ac94},
+	{0x0000c2e0, 0xff323118, 0xff323118},
+	{0x0000c2e4, 0xff3ffe00, 0xff3ffe00},
+	{0x0000c2e8, 0xffc00000, 0xffc00000},
+	{0x00016044, 0x049242db, 0x049242db},
+	{0x00016048, 0x64925a70, 0x64925a70},
+	{0x00016148, 0x00008050, 0x00008050},
+	{0x00016280, 0x41110005, 0x41110005},
+	{0x00016284, 0x453a6000, 0x453a6000},
+	{0x00016444, 0x049242db, 0x049242db},
+	{0x00016448, 0x6c925a70, 0x6c925a70},
+	{0x00016548, 0x00008050, 0x00008050},
+	{0x00016844, 0x049242db, 0x049242db},
+	{0x00016848, 0x6c925a70, 0x6c925a70},
+	{0x00016948, 0x00008050, 0x00008050},
+};
+
+static const u32 qca956x_1p0_modes_xpa_tx_gain_table[][3] = {
+	/* Addr      5G          2G        */
+	{0x0000a2dc, 0xcc69ac94, 0xcc69ac94},
+	{0x0000a2e0, 0xf0b23118, 0xf0b23118},
+	{0x0000a2e4, 0xffffc000, 0xffffc000},
+	{0x0000a2e8, 0xc0000000, 0xc0000000},
+	{0x0000a410, 0x000050d2, 0x000050d2},
+	{0x0000a500, 0x0a000040, 0x0a000040},
+	{0x0000a504, 0x0c000041, 0x0c000041},
+	{0x0000a508, 0x0e000042, 0x0e000042},
+	{0x0000a50c, 0x12000044, 0x12000044},
+	{0x0000a510, 0x16000046, 0x16000046},
+	{0x0000a514, 0x1d000440, 0x1d000440},
+	{0x0000a518, 0x1f000441, 0x1f000441},
+	{0x0000a51c, 0x23000443, 0x23000443},
+	{0x0000a520, 0x25000444, 0x25000444},
+	{0x0000a524, 0x29000a40, 0x29000a40},
+	{0x0000a528, 0x2d000a42, 0x2d000a42},
+	{0x0000a52c, 0x2f000a43, 0x2f000a43},
+	{0x0000a530, 0x31000a44, 0x31000a44},
+	{0x0000a534, 0x35000a46, 0x35000a46},
+	{0x0000a538, 0x38000ce0, 0x38000ce0},
+	{0x0000a53c, 0x3c000ce2, 0x3c000ce2},
+	{0x0000a540, 0x3e000ce3, 0x3e000ce3},
+	{0x0000a544, 0x40000ce4, 0x40000ce4},
+	{0x0000a548, 0x46001ee0, 0x46001ee0},
+	{0x0000a54c, 0x4a001ee2, 0x4a001ee2},
+	{0x0000a550, 0x4e001ee4, 0x4e001ee4},
+	{0x0000a554, 0x52001ee6, 0x52001ee6},
+	{0x0000a558, 0x56001eea, 0x56001eea},
+	{0x0000a55c, 0x5a001eec, 0x5a001eec},
+	{0x0000a560, 0x5e001ef0, 0x5e001ef0},
+	{0x0000a564, 0x60001ef1, 0x60001ef1},
+	{0x0000a568, 0x61001ef2, 0x61001ef2},
+	{0x0000a56c, 0x62001ef3, 0x62001ef3},
+	{0x0000a570, 0x63001ef4, 0x63001ef4},
+	{0x0000a574, 0x64001ef5, 0x64001ef5},
+	{0x0000a578, 0x65001ffc, 0x65001ffc},
+	{0x0000a57c, 0x65001ffc, 0x65001ffc},
+	{0x0000a600, 0x00000000, 0x00000000},
+	{0x0000a604, 0x00000000, 0x00000000},
+	{0x0000a608, 0x00000000, 0x00000000},
+	{0x0000a60c, 0x00000000, 0x00000000},
+	{0x0000a610, 0x00000000, 0x00000000},
+	{0x0000a614, 0x00000000, 0x00000000},
+	{0x0000a618, 0x00000000, 0x00000000},
+	{0x0000a61c, 0x00804201, 0x00804201},
+	{0x0000a620, 0x00804201, 0x00804201},
+	{0x0000a624, 0x00804201, 0x00804201},
+	{0x0000a628, 0x00804201, 0x00804201},
+	{0x0000a62c, 0x02808a02, 0x02808a02},
+	{0x0000a630, 0x0340cd03, 0x0340cd03},
+	{0x0000a634, 0x0340cd03, 0x0340cd03},
+	{0x0000a638, 0x0340cd03, 0x0340cd03},
+	{0x0000a63c, 0x05011404, 0x05011404},
+	{0x0000b2dc, 0xcc69ac94, 0xcc69ac94},
+	{0x0000b2e0, 0xf0b23118, 0xf0b23118},
+	{0x0000b2e4, 0xffffc000, 0xffffc000},
+	{0x0000b2e8, 0xc0000000, 0xc0000000},
+	{0x0000c2dc, 0xcc69ac94, 0xcc69ac94},
+	{0x0000c2e0, 0xf0b23118, 0xf0b23118},
+	{0x0000c2e4, 0xffffc000, 0xffffc000},
+	{0x0000c2e8, 0xc0000000, 0xc0000000},
+	{0x00016044, 0x012492db, 0x012492db},
+	{0x00016048, 0x6c927a70, 0x6c927a70},
+	{0x00016050, 0x8036d36c, 0x8036d36c},
+	{0x00016280, 0x41110005, 0x41110005},
+	{0x00016284, 0x453a7e00, 0x453a7e00},
+	{0x00016444, 0x012492db, 0x012492db},
+	{0x00016448, 0x6c927a70, 0x6c927a70},
+	{0x00016450, 0x8036d36c, 0x8036d36c},
+	{0x00016844, 0x012492db, 0x012492db},
+	{0x00016848, 0x6c927a70, 0x6c927a70},
+	{0x00016850, 0x8036d36c, 0x8036d36c},
+};
+
+static const u32 qca956x_1p0_modes_no_xpa_low_ob_db_tx_gain_table[][3] = {
+	/* Addr      5G          2G        */
+	{0x0000a2dc, 0xffa9ac94, 0xffa9ac94},
+	{0x0000a2e0, 0xff323118, 0xff323118},
+	{0x0000a2e4, 0xff3ffe00, 0xff3ffe00},
+	{0x0000a2e8, 0xffc00000, 0xffc00000},
+	{0x0000a39c, 0x42424242, 0x42424242},
+	{0x0000a3a4, 0x3a3e3e00, 0x3a3e3e00},
+	{0x0000a3b0, 0x00a01404, 0x00a01404},
+	{0x0000a3b4, 0x00000034, 0x00000034},
+	{0x0000a3b8, 0x00800408, 0x00800408},
+	{0x0000a3bc, 0x00000036, 0x00000036},
+	{0x0000a410, 0x000050dc, 0x000050dc},
+	{0x0000a414, 0x16b739ce, 0x16b739ce},
+	{0x0000a418, 0x2d00198b, 0x2d00198b},
+	{0x0000a41c, 0x16b5adce, 0x16b5adce},
+	{0x0000a420, 0x0000014a, 0x0000014a},
+	{0x0000a424, 0x14a525cc, 0x14a525cc},
+	{0x0000a428, 0x0000012a, 0x0000012a},
+	{0x0000a42c, 0x14a5294a, 0x14a5294a},
+	{0x0000a430, 0x1294a929, 0x1294a929},
+	{0x0000a500, 0x09000040, 0x09000040},
+	{0x0000a504, 0x0b000041, 0x0b000041},
+	{0x0000a508, 0x0d000042, 0x0d000042},
+	{0x0000a50c, 0x11000044, 0x11000044},
+	{0x0000a510, 0x15000046, 0x15000046},
+	{0x0000a514, 0x1d000440, 0x1d000440},
+	{0x0000a518, 0x1f000441, 0x1f000441},
+	{0x0000a51c, 0x23000443, 0x23000443},
+	{0x0000a520, 0x25000444, 0x25000444},
+	{0x0000a524, 0x280004e0, 0x280004e0},
+	{0x0000a528, 0x2c0004e2, 0x2c0004e2},
+	{0x0000a52c, 0x2e0004e3, 0x2e0004e3},
+	{0x0000a530, 0x300004e4, 0x300004e4},
+	{0x0000a534, 0x340004e6, 0x340004e6},
+	{0x0000a538, 0x37000ce0, 0x37000ce0},
+	{0x0000a53c, 0x3b000ce2, 0x3b000ce2},
+	{0x0000a540, 0x3d000ce3, 0x3d000ce3},
+	{0x0000a544, 0x3f000ce4, 0x3f000ce4},
+	{0x0000a548, 0x45001ee0, 0x45001ee0},
+	{0x0000a54c, 0x49001ee2, 0x49001ee2},
+	{0x0000a550, 0x4d001ee4, 0x4d001ee4},
+	{0x0000a554, 0x51001ee6, 0x51001ee6},
+	{0x0000a558, 0x55001eea, 0x55001eea},
+	{0x0000a55c, 0x59001eec, 0x59001eec},
+	{0x0000a560, 0x5d001ef0, 0x5d001ef0},
+	{0x0000a564, 0x5f001ef1, 0x5f001ef1},
+	{0x0000a568, 0x60001ef2, 0x60001ef2},
+	{0x0000a56c, 0x61001ef3, 0x61001ef3},
+	{0x0000a570, 0x62001ef4, 0x62001ef4},
+	{0x0000a574, 0x63001ef5, 0x63001ef5},
+	{0x0000a578, 0x64001ffc, 0x64001ffc},
+	{0x0000a57c, 0x64001ffc, 0x64001ffc},
+	{0x0000a600, 0x00000000, 0x00000000},
+	{0x0000a604, 0x00000000, 0x00000000},
+	{0x0000a608, 0x00000000, 0x00000000},
+	{0x0000a60c, 0x00000000, 0x00000000},
+	{0x0000a610, 0x00804000, 0x00804000},
+	{0x0000a614, 0x00804201, 0x00804201},
+	{0x0000a618, 0x00804201, 0x00804201},
+	{0x0000a61c, 0x00804201, 0x00804201},
+	{0x0000a620, 0x00804201, 0x00804201},
+	{0x0000a624, 0x00804201, 0x00804201},
+	{0x0000a628, 0x00804201, 0x00804201},
+	{0x0000a62c, 0x02808a02, 0x02808a02},
+	{0x0000a630, 0x0340cd03, 0x0340cd03},
+	{0x0000a634, 0x0340cd03, 0x0340cd03},
+	{0x0000a638, 0x0340cd03, 0x0340cd03},
+	{0x0000a63c, 0x05011404, 0x05011404},
+	{0x0000b2dc, 0xffa9ac94, 0xffa9ac94},
+	{0x0000b2e0, 0xff323118, 0xff323118},
+	{0x0000b2e4, 0xff3ffe00, 0xff3ffe00},
+	{0x0000b2e8, 0xffc00000, 0xffc00000},
+	{0x0000c2dc, 0xffa9ac94, 0xffa9ac94},
+	{0x0000c2e0, 0xff323118, 0xff323118},
+	{0x0000c2e4, 0xff3ffe00, 0xff3ffe00},
+	{0x0000c2e8, 0xffc00000, 0xffc00000},
+	{0x00016044, 0x046e42db, 0x046e42db},
+	{0x00016048, 0x64925a70, 0x64925a70},
+	{0x00016148, 0x00008050, 0x00008050},
+	{0x00016280, 0x41110005, 0x41110005},
+	{0x00016284, 0x453a6000, 0x453a6000},
+	{0x00016444, 0x046e42db, 0x046e42db},
+	{0x00016448, 0x6c925a70, 0x6c925a70},
+	{0x00016548, 0x00008050, 0x00008050},
+	{0x00016844, 0x046e42db, 0x046e42db},
+	{0x00016848, 0x6c925a70, 0x6c925a70},
+	{0x00016948, 0x00008050, 0x00008050},
+};
+
+static const u32 qca956x_1p0_modes_no_xpa_green_tx_gain_table[][3] = {
+	/* Addr      5G          2G        */
+	{0x000098bc, 0x00000001, 0x00000001},
+	{0x0000a2dc, 0xd3555284, 0xd3555284},
+	{0x0000a2e0, 0x1c666318, 0x1c666318},
+	{0x0000a2e4, 0xe07bbc00, 0xe07bbc00},
+	{0x0000a2e8, 0xff800000, 0xff800000},
+	{0x0000a3a4, 0x3a3e3e00, 0x3a3e3e00},
+	{0x0000a410, 0x000050dc, 0x000050dc},
+	{0x0000a500, 0x02000040, 0x02000040},
+	{0x0000a504, 0x04000041, 0x04000041},
+	{0x0000a508, 0x06000042, 0x06000042},
+	{0x0000a50c, 0x0a000044, 0x0a000044},
+	{0x0000a510, 0x0c000045, 0x0c000045},
+	{0x0000a514, 0x13000440, 0x13000440},
+	{0x0000a518, 0x15000441, 0x15000441},
+	{0x0000a51c, 0x19000443, 0x19000443},
+	{0x0000a520, 0x1b000444, 0x1b000444},
+	{0x0000a524, 0x1e0004e0, 0x1e0004e0},
+	{0x0000a528, 0x220004e2, 0x220004e2},
+	{0x0000a52c, 0x240004e3, 0x240004e3},
+	{0x0000a530, 0x260004e4, 0x260004e4},
+	{0x0000a534, 0x2a0004e6, 0x2a0004e6},
+	{0x0000a538, 0x32000ce0, 0x32000ce0},
+	{0x0000a53c, 0x36000ce2, 0x36000ce2},
+	{0x0000a540, 0x3a000ce4, 0x3a000ce4},
+	{0x0000a544, 0x3e000ce6, 0x3e000ce6},
+	{0x0000a548, 0x45001ee0, 0x45001ee0},
+	{0x0000a54c, 0x49001ee2, 0x49001ee2},
+	{0x0000a550, 0x4d001ee4, 0x4d001ee4},
+	{0x0000a554, 0x51001ee6, 0x51001ee6},
+	{0x0000a558, 0x55001eea, 0x55001eea},
+	{0x0000a55c, 0x59001eec, 0x59001eec},
+	{0x0000a560, 0x5d001ef0, 0x5d001ef0},
+	{0x0000a564, 0x5f001ef1, 0x5f001ef1},
+	{0x0000a568, 0x60001ef2, 0x60001ef2},
+	{0x0000a56c, 0x61001ef3, 0x61001ef3},
+	{0x0000a570, 0x62001ef4, 0x62001ef4},
+	{0x0000a574, 0x63001ff5, 0x63001ff5},
+	{0x0000a578, 0x64001ffc, 0x64001ffc},
+	{0x0000a57c, 0x64001ffc, 0x64001ffc},
+	{0x0000a600, 0x00000000, 0x00000000},
+	{0x0000a604, 0x00000000, 0x00000000},
+	{0x0000a608, 0x00000000, 0x00000000},
+	{0x0000a60c, 0x00000000, 0x00000000},
+	{0x0000a610, 0x00804000, 0x00804000},
+	{0x0000a614, 0x00804201, 0x00804201},
+	{0x0000a618, 0x00804201, 0x00804201},
+	{0x0000a61c, 0x00804201, 0x00804201},
+	{0x0000a620, 0x00804201, 0x00804201},
+	{0x0000a624, 0x00804201, 0x00804201},
+	{0x0000a628, 0x00804201, 0x00804201},
+	{0x0000a62c, 0x02808a02, 0x02808a02},
+	{0x0000a630, 0x0340cd03, 0x0340cd03},
+	{0x0000a634, 0x0340cd03, 0x0340cd03},
+	{0x0000a638, 0x0340cd03, 0x0340cd03},
+	{0x0000a63c, 0x05011404, 0x05011404},
+	{0x0000b2dc, 0xd3555284, 0xd3555284},
+	{0x0000b2e0, 0x1c666318, 0x1c666318},
+	{0x0000b2e4, 0xe07bbc00, 0xe07bbc00},
+	{0x0000b2e8, 0xff800000, 0xff800000},
+	{0x0000c2dc, 0xd3555284, 0xd3555284},
+	{0x0000c2e0, 0x1c666318, 0x1c666318},
+	{0x0000c2e4, 0xe07bbc00, 0xe07bbc00},
+	{0x0000c2e8, 0xff800000, 0xff800000},
+	{0x00016044, 0x849242db, 0x849242db},
+	{0x00016048, 0x64925a70, 0x64925a70},
+	{0x00016280, 0x41110005, 0x41110005},
+	{0x00016284, 0x453a6000, 0x453a6000},
+	{0x00016444, 0x849242db, 0x849242db},
+	{0x00016448, 0x6c925a70, 0x6c925a70},
+	{0x00016844, 0x849242db, 0x849242db},
+	{0x00016848, 0x6c925a70, 0x6c925a70},
+	{0x0000a7f0, 0x800002cc, 0x800002cc},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000018, 0x00000018},
+	{0x0000a7f4, 0x00000028, 0x00000028},
+	{0x0000a7f4, 0x00000028, 0x00000028},
+	{0x0000a7f4, 0x00000028, 0x00000028},
+	{0x0000a7f4, 0x00000028, 0x00000028},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+	{0x0000a7f4, 0x00000048, 0x00000048},
+};
+
+static const u32 qca956x_1p0_common_rx_gain_table[][2] = {
+	/* Addr      allmodes  */
+	{0x0000a000, 0x00010000},
+	{0x0000a004, 0x00030002},
+	{0x0000a008, 0x00050004},
+	{0x0000a00c, 0x00810080},
+	{0x0000a010, 0x00830082},
+	{0x0000a014, 0x01810180},
+	{0x0000a018, 0x01830182},
+	{0x0000a01c, 0x01850184},
+	{0x0000a020, 0x01890188},
+	{0x0000a024, 0x018b018a},
+	{0x0000a028, 0x018d018c},
+	{0x0000a02c, 0x01910190},
+	{0x0000a030, 0x01930192},
+	{0x0000a034, 0x01950194},
+	{0x0000a038, 0x038a0196},
+	{0x0000a03c, 0x038c038b},
+	{0x0000a040, 0x0390038d},
+	{0x0000a044, 0x03920391},
+	{0x0000a048, 0x03940393},
+	{0x0000a04c, 0x03960395},
+	{0x0000a050, 0x00000000},
+	{0x0000a054, 0x00000000},
+	{0x0000a058, 0x00000000},
+	{0x0000a05c, 0x00000000},
+	{0x0000a060, 0x00000000},
+	{0x0000a064, 0x00000000},
+	{0x0000a068, 0x00000000},
+	{0x0000a06c, 0x00000000},
+	{0x0000a070, 0x00000000},
+	{0x0000a074, 0x00000000},
+	{0x0000a078, 0x00000000},
+	{0x0000a07c, 0x00000000},
+	{0x0000a080, 0x22222222},
+	{0x0000a084, 0x1d1d1d1d},
+	{0x0000a088, 0x1d1d1d1d},
+	{0x0000a08c, 0x1d1d1d1d},
+	{0x0000a090, 0x17171717},
+	{0x0000a094, 0x11111717},
+	{0x0000a098, 0x00030311},
+	{0x0000a09c, 0x00000000},
+	{0x0000a0a0, 0x00000000},
+	{0x0000a0a4, 0x00000000},
+	{0x0000a0a8, 0x00000000},
+	{0x0000a0ac, 0x00000000},
+	{0x0000a0b0, 0x00000000},
+	{0x0000a0b4, 0x00000000},
+	{0x0000a0b8, 0x00000000},
+	{0x0000a0bc, 0x00000000},
+	{0x0000a0c0, 0x001f0000},
+	{0x0000a0c4, 0x01000101},
+	{0x0000a0c8, 0x011e011f},
+	{0x0000a0cc, 0x011c011d},
+	{0x0000a0d0, 0x02030204},
+	{0x0000a0d4, 0x02010202},
+	{0x0000a0d8, 0x021f0200},
+	{0x0000a0dc, 0x0302021e},
+	{0x0000a0e0, 0x03000301},
+	{0x0000a0e4, 0x031e031f},
+	{0x0000a0e8, 0x0402031d},
+	{0x0000a0ec, 0x04000401},
+	{0x0000a0f0, 0x041e041f},
+	{0x0000a0f4, 0x0502041d},
+	{0x0000a0f8, 0x05000501},
+	{0x0000a0fc, 0x051e051f},
+	{0x0000a100, 0x06010602},
+	{0x0000a104, 0x061f0600},
+	{0x0000a108, 0x061d061e},
+	{0x0000a10c, 0x07020703},
+	{0x0000a110, 0x07000701},
+	{0x0000a114, 0x00000000},
+	{0x0000a118, 0x00000000},
+	{0x0000a11c, 0x00000000},
+	{0x0000a120, 0x00000000},
+	{0x0000a124, 0x00000000},
+	{0x0000a128, 0x00000000},
+	{0x0000a12c, 0x00000000},
+	{0x0000a130, 0x00000000},
+	{0x0000a134, 0x00000000},
+	{0x0000a138, 0x00000000},
+	{0x0000a13c, 0x00000000},
+	{0x0000a140, 0x001f0000},
+	{0x0000a144, 0x01000101},
+	{0x0000a148, 0x011e011f},
+	{0x0000a14c, 0x011c011d},
+	{0x0000a150, 0x02030204},
+	{0x0000a154, 0x02010202},
+	{0x0000a158, 0x021f0200},
+	{0x0000a15c, 0x0302021e},
+	{0x0000a160, 0x03000301},
+	{0x0000a164, 0x031e031f},
+	{0x0000a168, 0x0402031d},
+	{0x0000a16c, 0x04000401},
+	{0x0000a170, 0x041e041f},
+	{0x0000a174, 0x0502041d},
+	{0x0000a178, 0x05000501},
+	{0x0000a17c, 0x051e051f},
+	{0x0000a180, 0x06010602},
+	{0x0000a184, 0x061f0600},
+	{0x0000a188, 0x061d061e},
+	{0x0000a18c, 0x07020703},
+	{0x0000a190, 0x07000701},
+	{0x0000a194, 0x00000000},
+	{0x0000a198, 0x00000000},
+	{0x0000a19c, 0x00000000},
+	{0x0000a1a0, 0x00000000},
+	{0x0000a1a4, 0x00000000},
+	{0x0000a1a8, 0x00000000},
+	{0x0000a1ac, 0x00000000},
+	{0x0000a1b0, 0x00000000},
+	{0x0000a1b4, 0x00000000},
+	{0x0000a1b8, 0x00000000},
+	{0x0000a1bc, 0x00000000},
+	{0x0000a1c0, 0x00000000},
+	{0x0000a1c4, 0x00000000},
+	{0x0000a1c8, 0x00000000},
+	{0x0000a1cc, 0x00000000},
+	{0x0000a1d0, 0x00000000},
+	{0x0000a1d4, 0x00000000},
+	{0x0000a1d8, 0x00000000},
+	{0x0000a1dc, 0x00000000},
+	{0x0000a1e0, 0x00000000},
+	{0x0000a1e4, 0x00000000},
+	{0x0000a1e8, 0x00000000},
+	{0x0000a1ec, 0x00000000},
+	{0x0000a1f0, 0x00000396},
+	{0x0000a1f4, 0x00000396},
+	{0x0000a1f8, 0x00000396},
+	{0x0000a1fc, 0x00000196},
+	{0x0000b000, 0x00010000},
+	{0x0000b004, 0x00030002},
+	{0x0000b008, 0x00050004},
+	{0x0000b00c, 0x00810080},
+	{0x0000b010, 0x00830082},
+	{0x0000b014, 0x01810180},
+	{0x0000b018, 0x01830182},
+	{0x0000b01c, 0x01850184},
+	{0x0000b020, 0x02810280},
+	{0x0000b024, 0x02830282},
+	{0x0000b028, 0x02850284},
+	{0x0000b02c, 0x02890288},
+	{0x0000b030, 0x028b028a},
+	{0x0000b034, 0x0388028c},
+	{0x0000b038, 0x038a0389},
+	{0x0000b03c, 0x038c038b},
+	{0x0000b040, 0x0390038d},
+	{0x0000b044, 0x03920391},
+	{0x0000b048, 0x03940393},
+	{0x0000b04c, 0x03960395},
+	{0x0000b050, 0x00000000},
+	{0x0000b054, 0x00000000},
+	{0x0000b058, 0x00000000},
+	{0x0000b05c, 0x00000000},
+	{0x0000b060, 0x00000000},
+	{0x0000b064, 0x00000000},
+	{0x0000b068, 0x00000000},
+	{0x0000b06c, 0x00000000},
+	{0x0000b070, 0x00000000},
+	{0x0000b074, 0x00000000},
+	{0x0000b078, 0x00000000},
+	{0x0000b07c, 0x00000000},
+	{0x0000b080, 0x23232323},
+	{0x0000b084, 0x21232323},
+	{0x0000b088, 0x19191c1e},
+	{0x0000b08c, 0x12141417},
+	{0x0000b090, 0x07070e0e},
+	{0x0000b094, 0x03030305},
+	{0x0000b098, 0x00000003},
+	{0x0000b09c, 0x00000000},
+	{0x0000b0a0, 0x00000000},
+	{0x0000b0a4, 0x00000000},
+	{0x0000b0a8, 0x00000000},
+	{0x0000b0ac, 0x00000000},
+	{0x0000b0b0, 0x00000000},
+	{0x0000b0b4, 0x00000000},
+	{0x0000b0b8, 0x00000000},
+	{0x0000b0bc, 0x00000000},
+	{0x0000b0c0, 0x003f0020},
+	{0x0000b0c4, 0x00400041},
+	{0x0000b0c8, 0x0140005f},
+	{0x0000b0cc, 0x0160015f},
+	{0x0000b0d0, 0x017e017f},
+	{0x0000b0d4, 0x02410242},
+	{0x0000b0d8, 0x025f0240},
+	{0x0000b0dc, 0x027f0260},
+	{0x0000b0e0, 0x0341027e},
+	{0x0000b0e4, 0x035f0340},
+	{0x0000b0e8, 0x037f0360},
+	{0x0000b0ec, 0x04400441},
+	{0x0000b0f0, 0x0460045f},
+	{0x0000b0f4, 0x0541047f},
+	{0x0000b0f8, 0x055f0540},
+	{0x0000b0fc, 0x057f0560},
+	{0x0000b100, 0x06400641},
+	{0x0000b104, 0x0660065f},
+	{0x0000b108, 0x067e067f},
+	{0x0000b10c, 0x07410742},
+	{0x0000b110, 0x075f0740},
+	{0x0000b114, 0x077f0760},
+	{0x0000b118, 0x07800781},
+	{0x0000b11c, 0x07a0079f},
+	{0x0000b120, 0x07c107bf},
+	{0x0000b124, 0x000007c0},
+	{0x0000b128, 0x00000000},
+	{0x0000b12c, 0x00000000},
+	{0x0000b130, 0x00000000},
+	{0x0000b134, 0x00000000},
+	{0x0000b138, 0x00000000},
+	{0x0000b13c, 0x00000000},
+	{0x0000b140, 0x003f0020},
+	{0x0000b144, 0x00400041},
+	{0x0000b148, 0x0140005f},
+	{0x0000b14c, 0x0160015f},
+	{0x0000b150, 0x017e017f},
+	{0x0000b154, 0x02410242},
+	{0x0000b158, 0x025f0240},
+	{0x0000b15c, 0x027f0260},
+	{0x0000b160, 0x0341027e},
+	{0x0000b164, 0x035f0340},
+	{0x0000b168, 0x037f0360},
+	{0x0000b16c, 0x04400441},
+	{0x0000b170, 0x0460045f},
+	{0x0000b174, 0x0541047f},
+	{0x0000b178, 0x055f0540},
+	{0x0000b17c, 0x057f0560},
+	{0x0000b180, 0x06400641},
+	{0x0000b184, 0x0660065f},
+	{0x0000b188, 0x067e067f},
+	{0x0000b18c, 0x07410742},
+	{0x0000b190, 0x075f0740},
+	{0x0000b194, 0x077f0760},
+	{0x0000b198, 0x07800781},
+	{0x0000b19c, 0x07a0079f},
+	{0x0000b1a0, 0x07c107bf},
+	{0x0000b1a4, 0x000007c0},
+	{0x0000b1a8, 0x00000000},
+	{0x0000b1ac, 0x00000000},
+	{0x0000b1b0, 0x00000000},
+	{0x0000b1b4, 0x00000000},
+	{0x0000b1b8, 0x00000000},
+	{0x0000b1bc, 0x00000000},
+	{0x0000b1c0, 0x00000000},
+	{0x0000b1c4, 0x00000000},
+	{0x0000b1c8, 0x00000000},
+	{0x0000b1cc, 0x00000000},
+	{0x0000b1d0, 0x00000000},
+	{0x0000b1d4, 0x00000000},
+	{0x0000b1d8, 0x00000000},
+	{0x0000b1dc, 0x00000000},
+	{0x0000b1e0, 0x00000000},
+	{0x0000b1e4, 0x00000000},
+	{0x0000b1e8, 0x00000000},
+	{0x0000b1ec, 0x00000000},
+	{0x0000b1f0, 0x00000396},
+	{0x0000b1f4, 0x00000396},
+	{0x0000b1f8, 0x00000396},
+	{0x0000b1fc, 0x00000196},
+};
+
+static const u32 qca956x_1p0_xlna_only[][5] = {
+	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+	{0x00009820, 0x206a022e, 0x206a022e, 0x206a01ae, 0x206a01ae},
+	{0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac621f1, 0x5ac621f1},
+	{0x00009828, 0x06903081, 0x06903081, 0x07d43881, 0x07d43881},
+	{0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x03721720},
+	{0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000de, 0x6c4000da},
+	{0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec86d2e, 0x7ec8ad2e},
+	{0x00009e14, 0x37b95d5e, 0x37b9605e, 0x317a6062, 0x317a5ae2},
+	{0x00009e18, 0x00000000, 0x00000000, 0x03c00000, 0x03c00000},
+	{0x00009e20, 0x000003b5, 0x000003b5, 0x000003b2, 0x000003b2},
+	{0x00009fc0, 0x813e4788, 0x813e4788, 0x813e4789, 0x813e4789},
+	{0x0000ae18, 0x00000000, 0x00000000, 0x03c00000, 0x03c00000},
+	{0x0000ae20, 0x000001b5, 0x000001b5, 0x000001b2, 0x000001b2},
+	{0x0000be18, 0x00000000, 0x00000000, 0x03c00000, 0x03c00000},
+	{0x0000be20, 0x000001b5, 0x000001b5, 0x000001b2, 0x000001b2},
+};
+
+#endif /* INITVALS_956X_H */
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index e008569..cbd64ef 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -34,7 +34,7 @@
 
 extern struct ieee80211_ops ath9k_ops;
 extern int ath9k_modparam_nohwcrypt;
-extern int led_blink;
+extern int ath9k_led_blink;
 extern bool is_ath9k_unloaded;
 extern int ath9k_use_chanctx;
 
@@ -184,12 +184,12 @@
 	struct ath_buf *bf;
 	u16 framelen;
 	s8 txq;
-	enum ath9k_key_type keytype;
 	u8 keyix;
 	u8 rtscts_rate;
 	u8 retries : 7;
 	u8 baw_tracked : 1;
 	u8 tx_power;
+	enum ath9k_key_type keytype:2;
 };
 
 struct ath_rxbuf {
@@ -645,6 +645,7 @@
 			       struct ath9k_vif_iter_data *iter_data);
 void ath9k_calculate_summary_state(struct ath_softc *sc,
 				   struct ath_chanctx *ctx);
+void ath9k_set_txpower(struct ath_softc *sc, struct ieee80211_vif *vif);
 
 /*******************/
 /* Beacon Handling */
@@ -830,14 +831,9 @@
 /* Wake on Wireless LAN */
 /************************/
 
-struct ath9k_wow_pattern {
-	u8 pattern_bytes[MAX_PATTERN_SIZE];
-	u8 mask_bytes[MAX_PATTERN_SIZE];
-	u32 pattern_len;
-};
-
 #ifdef CPTCFG_ATH9K_WOW
 void ath9k_init_wow(struct ieee80211_hw *hw);
+void ath9k_deinit_wow(struct ieee80211_hw *hw);
 int ath9k_suspend(struct ieee80211_hw *hw,
 		  struct cfg80211_wowlan *wowlan);
 int ath9k_resume(struct ieee80211_hw *hw);
@@ -846,6 +842,9 @@
 static inline void ath9k_init_wow(struct ieee80211_hw *hw)
 {
 }
+static inline void ath9k_deinit_wow(struct ieee80211_hw *hw)
+{
+}
 static inline int ath9k_suspend(struct ieee80211_hw *hw,
 				struct cfg80211_wowlan *wowlan)
 {
@@ -1039,9 +1038,8 @@
 	s16 tx99_power;
 
 #ifdef CPTCFG_ATH9K_WOW
-	atomic_t wow_got_bmiss_intr;
-	atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */
 	u32 wow_intr_before_sleep;
+	bool force_wow;
 #endif
 };
 
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index cb366ad..f50a6bc 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -219,12 +219,15 @@
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	struct ath_vif *avp = (void *)vif->drv_priv;
 	struct ath_buf *bf = avp->av_bcbuf;
+	struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
 
 	ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n",
 		avp->av_bslot);
 
 	tasklet_disable(&sc->bcon_tasklet);
 
+	cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
+
 	if (bf && bf->bf_mpdu) {
 		struct sk_buff *skb = bf->bf_mpdu;
 		dma_unmap_single(sc->dev, bf->bf_buf_addr,
@@ -521,8 +524,7 @@
 	}
 
 	if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
-		if ((vif->type != NL80211_IFTYPE_AP) ||
-		    (sc->nbcnvifs > 1)) {
+		if (vif->type != NL80211_IFTYPE_AP) {
 			ath_dbg(common, CONFIG,
 				"An AP interface is already present !\n");
 			return false;
@@ -616,12 +618,14 @@
 	 * enabling/disabling SWBA.
 	 */
 	if (changed & BSS_CHANGED_BEACON_ENABLED) {
-		if (!bss_conf->enable_beacon &&
-		    (sc->nbcnvifs <= 1)) {
-			cur_conf->enable_beacon = false;
-		} else if (bss_conf->enable_beacon) {
-			cur_conf->enable_beacon = true;
-			ath9k_cache_beacon_config(sc, ctx, bss_conf);
+		bool enabled = cur_conf->enable_beacon;
+
+		if (!bss_conf->enable_beacon) {
+			cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
+		} else {
+			cur_conf->enable_beacon |= BIT(avp->av_bslot);
+			if (!enabled)
+				ath9k_cache_beacon_config(sc, ctx, bss_conf);
 		}
 	}
 
diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c
index 3dfc2c7..5a084d9 100644
--- a/drivers/net/wireless/ath/ath9k/btcoex.c
+++ b/drivers/net/wireless/ath/ath9k/btcoex.c
@@ -103,7 +103,9 @@
 		return;
 	}
 
-	if (AR_SREV_9300_20_OR_LATER(ah)) {
+	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) {
+		btcoex_hw->scheme = ATH_BTCOEX_CFG_MCI;
+	} else if (AR_SREV_9300_20_OR_LATER(ah)) {
 		btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE;
 		btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9300;
 		btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9300;
@@ -307,6 +309,18 @@
 	btcoex->enabled = true;
 }
 
+static void ath9k_hw_btcoex_disable_mci(struct ath_hw *ah)
+{
+	struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
+	int i;
+
+	ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE);
+
+	for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++)
+		REG_WRITE(ah, AR_MCI_COEX_WL_WEIGHTS(i),
+			  btcoex_hw->wlan_weight[i]);
+}
+
 void ath9k_hw_btcoex_enable(struct ath_hw *ah)
 {
 	struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
@@ -318,17 +332,18 @@
 		ath9k_hw_btcoex_enable_2wire(ah);
 		break;
 	case ATH_BTCOEX_CFG_3WIRE:
-		if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
-			ath9k_hw_btcoex_enable_mci(ah);
-			return;
-		}
 		ath9k_hw_btcoex_enable_3wire(ah);
 		break;
+	case ATH_BTCOEX_CFG_MCI:
+		ath9k_hw_btcoex_enable_mci(ah);
+		break;
 	}
 
-	REG_RMW(ah, AR_GPIO_PDPU,
-		(0x2 << (btcoex_hw->btactive_gpio * 2)),
-		(0x3 << (btcoex_hw->btactive_gpio * 2)));
+	if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI) {
+		REG_RMW(ah, AR_GPIO_PDPU,
+			(0x2 << (btcoex_hw->btactive_gpio * 2)),
+			(0x3 << (btcoex_hw->btactive_gpio * 2)));
+	}
 
 	ah->btcoex_hw.enabled = true;
 }
@@ -340,14 +355,14 @@
 	int i;
 
 	btcoex_hw->enabled = false;
-	if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
-		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE);
-		for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++)
-			REG_WRITE(ah, AR_MCI_COEX_WL_WEIGHTS(i),
-				  btcoex_hw->wlan_weight[i]);
+
+	if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_MCI) {
+		ath9k_hw_btcoex_disable_mci(ah);
 		return;
 	}
-	ath9k_hw_set_gpio(ah, btcoex_hw->wlanactive_gpio, 0);
+
+	if (!AR_SREV_9300_20_OR_LATER(ah))
+		ath9k_hw_set_gpio(ah, btcoex_hw->wlanactive_gpio, 0);
 
 	ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio,
 			AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h
index 6de26ea..cd2f0a2 100644
--- a/drivers/net/wireless/ath/ath9k/btcoex.h
+++ b/drivers/net/wireless/ath/ath9k/btcoex.h
@@ -44,6 +44,9 @@
 
 #define AR9300_NUM_BT_WEIGHTS   4
 #define AR9300_NUM_WLAN_WEIGHTS 4
+
+#define ATH_AIC_MAX_BT_CHANNEL  79
+
 /* Defines the BT AR_BT_COEX_WGHT used */
 enum ath_stomp_type {
 	ATH_BTCOEX_STOMP_ALL,
@@ -58,6 +61,7 @@
 	ATH_BTCOEX_CFG_NONE,
 	ATH_BTCOEX_CFG_2WIRE,
 	ATH_BTCOEX_CFG_3WIRE,
+	ATH_BTCOEX_CFG_MCI,
 };
 
 struct ath9k_hw_mci {
@@ -92,9 +96,18 @@
 	u32 last_recovery;
 };
 
+struct ath9k_hw_aic {
+	bool aic_enabled;
+	u8 aic_cal_state;
+	u8 aic_caled_chan;
+	u32 aic_sram[ATH_AIC_MAX_BT_CHANNEL];
+	u32 aic_cal_start_time;
+};
+
 struct ath_btcoex_hw {
 	enum ath_btcoex_scheme scheme;
 	struct ath9k_hw_mci mci;
+	struct ath9k_hw_aic aic;
 	bool enabled;
 	u8 wlanactive_gpio;
 	u8 btactive_gpio;
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index e200a6e..3e2e24e 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -238,7 +238,6 @@
 {
 	struct ath9k_nfcal_hist *h = NULL;
 	unsigned i, j;
-	int32_t val;
 	u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
 	struct ath_common *common = ath9k_hw_common(ah);
 	s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
@@ -246,6 +245,7 @@
 	if (ah->caldata)
 		h = ah->caldata->nfCalHist;
 
+	ENABLE_REG_RMW_BUFFER(ah);
 	for (i = 0; i < NUM_NF_READINGS; i++) {
 		if (chainmask & (1 << i)) {
 			s16 nfval;
@@ -258,10 +258,8 @@
 			else
 				nfval = default_nf;
 
-			val = REG_READ(ah, ah->nf_regs[i]);
-			val &= 0xFFFFFE00;
-			val |= (((u32) nfval << 1) & 0x1ff);
-			REG_WRITE(ah, ah->nf_regs[i], val);
+			REG_RMW(ah, ah->nf_regs[i],
+				(((u32) nfval << 1) & 0x1ff), 0x1ff);
 		}
 	}
 
@@ -274,6 +272,7 @@
 	REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
 		    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
 	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+	REG_RMW_BUFFER_FLUSH(ah);
 
 	/*
 	 * Wait for load to complete, should be fast, a few 10s of us.
@@ -309,19 +308,17 @@
 	 * by the median we just loaded.  This will be initial (and max) value
 	 * of next noise floor calibration the baseband does.
 	 */
-	ENABLE_REGWRITE_BUFFER(ah);
+	ENABLE_REG_RMW_BUFFER(ah);
 	for (i = 0; i < NUM_NF_READINGS; i++) {
 		if (chainmask & (1 << i)) {
 			if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
 				continue;
 
-			val = REG_READ(ah, ah->nf_regs[i]);
-			val &= 0xFFFFFE00;
-			val |= (((u32) (-50) << 1) & 0x1ff);
-			REG_WRITE(ah, ah->nf_regs[i], val);
+			REG_RMW(ah, ah->nf_regs[i],
+					(((u32) (-50) << 1) & 0x1ff), 0x1ff);
 		}
 	}
-	REGWRITE_BUFFER_FLUSH(ah);
+	REG_RMW_BUFFER_FLUSH(ah);
 
 	return 0;
 }
diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c
index 5253136..eb84f9b 100644
--- a/drivers/net/wireless/ath/ath9k/common-spectral.c
+++ b/drivers/net/wireless/ath/ath9k/common-spectral.c
@@ -586,7 +586,7 @@
 
 void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv)
 {
-	if (config_enabled(CPTCFG_ATH9K_DEBUGFS) && spec_priv->rfs_chan_spec_scan) {
+	if (config_enabled(CPTCFG_ATH9K_DEBUGFS)) {
 		relay_close(spec_priv->rfs_chan_spec_scan);
 		spec_priv->rfs_chan_spec_scan = NULL;
 	}
diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h
index 2b79a56..d237373 100644
--- a/drivers/net/wireless/ath/ath9k/common.h
+++ b/drivers/net/wireless/ath/ath9k/common.h
@@ -54,7 +54,7 @@
 	u16 dtim_period;
 	u16 bmiss_timeout;
 	u8 dtim_count;
-	bool enable_beacon;
+	u8 enable_beacon;
 	bool ibss_creator;
 	u32 nexttbtt;
 	u32 intval;
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 99ce0db..3109f51 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -403,7 +403,8 @@
 
 static int read_file_dma(struct seq_file *file, void *data)
 {
-	struct ath_softc *sc = file->private;
+	struct ieee80211_hw *hw = dev_get_drvdata(file->private);
+	struct ath_softc *sc = hw->priv;
 	struct ath_hw *ah = sc->sc_ah;
 	u32 val[ATH9K_NUM_DMA_DEBUG_REGS];
 	int i, qcuOffset = 0, dcuOffset = 0;
@@ -470,20 +471,6 @@
 	return 0;
 }
 
-static int open_file_dma(struct inode *inode, struct file *f)
-{
-	return single_open(f, read_file_dma, inode->i_private);
-}
-
-static const struct file_operations fops_dma = {
-	.open = open_file_dma,
-	.read = seq_read,
-	.owner = THIS_MODULE,
-	.llseek = seq_lseek,
-	.release = single_release,
-};
-
-
 void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status)
 {
 	if (status)
@@ -539,7 +526,8 @@
 
 static int read_file_interrupt(struct seq_file *file, void *data)
 {
-	struct ath_softc *sc = file->private;
+	struct ieee80211_hw *hw = dev_get_drvdata(file->private);
+	struct ath_softc *sc = hw->priv;
 
 #define PR_IS(a, s)						\
 	do {							\
@@ -600,22 +588,10 @@
 	return 0;
 }
 
-static int open_file_interrupt(struct inode *inode, struct file *f)
-{
-	return single_open(f, read_file_interrupt, inode->i_private);
-}
-
-static const struct file_operations fops_interrupt = {
-	.read = seq_read,
-	.open = open_file_interrupt,
-	.owner = THIS_MODULE,
-	.llseek = seq_lseek,
-	.release = single_release,
-};
-
 static int read_file_xmit(struct seq_file *file, void *data)
 {
-	struct ath_softc *sc = file->private;
+	struct ieee80211_hw *hw = dev_get_drvdata(file->private);
+	struct ath_softc *sc = hw->priv;
 
 	seq_printf(file, "%30s %10s%10s%10s\n\n", "BE", "BK", "VI", "VO");
 
@@ -661,7 +637,8 @@
 
 static int read_file_queues(struct seq_file *file, void *data)
 {
-	struct ath_softc *sc = file->private;
+	struct ieee80211_hw *hw = dev_get_drvdata(file->private);
+	struct ath_softc *sc = hw->priv;
 	struct ath_txq *txq;
 	int i;
 	static const char *qname[4] = {
@@ -682,7 +659,8 @@
 
 static int read_file_misc(struct seq_file *file, void *data)
 {
-	struct ath_softc *sc = file->private;
+	struct ieee80211_hw *hw = dev_get_drvdata(file->private);
+	struct ath_softc *sc = hw->priv;
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	struct ath9k_vif_iter_data iter_data;
 	struct ath_chanctx *ctx;
@@ -773,7 +751,8 @@
 
 static int read_file_reset(struct seq_file *file, void *data)
 {
-	struct ath_softc *sc = file->private;
+	struct ieee80211_hw *hw = dev_get_drvdata(file->private);
+	struct ath_softc *sc = hw->priv;
 	static const char * const reset_cause[__RESET_TYPE_MAX] = {
 		[RESET_TYPE_BB_HANG] = "Baseband Hang",
 		[RESET_TYPE_BB_WATCHDOG] = "Baseband Watchdog",
@@ -837,58 +816,6 @@
 		TX_STAT_INC(qnum, delim_underrun);
 }
 
-static int open_file_xmit(struct inode *inode, struct file *f)
-{
-	return single_open(f, read_file_xmit, inode->i_private);
-}
-
-static const struct file_operations fops_xmit = {
-	.read = seq_read,
-	.open = open_file_xmit,
-	.owner = THIS_MODULE,
-	.llseek = seq_lseek,
-	.release = single_release,
-};
-
-static int open_file_queues(struct inode *inode, struct file *f)
-{
-	return single_open(f, read_file_queues, inode->i_private);
-}
-
-static const struct file_operations fops_queues = {
-	.read = seq_read,
-	.open = open_file_queues,
-	.owner = THIS_MODULE,
-	.llseek = seq_lseek,
-	.release = single_release,
-};
-
-static int open_file_misc(struct inode *inode, struct file *f)
-{
-	return single_open(f, read_file_misc, inode->i_private);
-}
-
-static const struct file_operations fops_misc = {
-	.read = seq_read,
-	.open = open_file_misc,
-	.owner = THIS_MODULE,
-	.llseek = seq_lseek,
-	.release = single_release,
-};
-
-static int open_file_reset(struct inode *inode, struct file *f)
-{
-	return single_open(f, read_file_reset, inode->i_private);
-}
-
-static const struct file_operations fops_reset = {
-	.read = seq_read,
-	.open = open_file_reset,
-	.owner = THIS_MODULE,
-	.llseek = seq_lseek,
-	.release = single_release,
-};
-
 void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
 {
 	ath9k_cmn_debug_stat_rx(&sc->debug.stats.rxstats, rs);
@@ -1018,7 +945,8 @@
 
 static int read_file_dump_nfcal(struct seq_file *file, void *data)
 {
-	struct ath_softc *sc = file->private;
+	struct ieee80211_hw *hw = dev_get_drvdata(file->private);
+	struct ath_softc *sc = hw->priv;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath9k_nfcal_hist *h = sc->cur_chan->caldata.nfCalHist;
 	struct ath_common *common = ath9k_hw_common(ah);
@@ -1115,6 +1043,136 @@
 };
 #endif
 
+#ifdef CPTCFG_ATH9K_WOW
+
+static ssize_t read_file_wow(struct file *file, char __user *user_buf,
+			     size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	unsigned int len = 0, size = 32;
+	ssize_t retval;
+	char *buf;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len += scnprintf(buf + len, size - len, "WOW: %s\n",
+			 sc->force_wow ? "ENABLED" : "DISABLED");
+
+	if (len > size)
+		len = size;
+
+	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+
+	return retval;
+}
+
+static ssize_t write_file_wow(struct file *file, const char __user *user_buf,
+			      size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	unsigned long val;
+	char buf[32];
+	ssize_t len;
+
+	len = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+	if (kstrtoul(buf, 0, &val))
+		return -EINVAL;
+
+	if (val != 1)
+		return -EINVAL;
+
+	if (!sc->force_wow) {
+		sc->force_wow = true;
+		ath9k_init_wow(sc->hw);
+	}
+
+	return count;
+}
+
+static const struct file_operations fops_wow = {
+	.read = read_file_wow,
+	.write = write_file_wow,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+#endif
+
+static ssize_t read_file_tpc(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;
+	unsigned int len = 0, size = 32;
+	ssize_t retval;
+	char *buf;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len += scnprintf(buf + len, size - len, "%s\n",
+			 ah->tpc_enabled ? "ENABLED" : "DISABLED");
+
+	if (len > size)
+		len = size;
+
+	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+
+	return retval;
+}
+
+static ssize_t write_file_tpc(struct file *file, const char __user *user_buf,
+			      size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	struct ath_hw *ah = sc->sc_ah;
+	unsigned long val;
+	char buf[32];
+	ssize_t len;
+	bool tpc_enabled;
+
+	len = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+	if (kstrtoul(buf, 0, &val))
+		return -EINVAL;
+
+	if (val < 0 || val > 1)
+		return -EINVAL;
+
+	tpc_enabled = !!val;
+
+	if (tpc_enabled != ah->tpc_enabled) {
+		ah->tpc_enabled = tpc_enabled;
+
+		mutex_lock(&sc->mutex);
+		ath9k_set_txpower(sc, NULL);
+		mutex_unlock(&sc->mutex);
+	}
+
+	return count;
+}
+
+static const struct file_operations fops_tpc = {
+	.read = read_file_tpc,
+	.write = write_file_tpc,
+	.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"
@@ -1260,14 +1318,14 @@
 	ath9k_tx99_init_debug(sc);
 	ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy);
 
-	debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc,
-			    &fops_dma);
-	debugfs_create_file("interrupt", S_IRUSR, sc->debug.debugfs_phy, sc,
-			    &fops_interrupt);
-	debugfs_create_file("xmit", S_IRUSR, sc->debug.debugfs_phy, sc,
-			    &fops_xmit);
-	debugfs_create_file("queues", S_IRUSR, sc->debug.debugfs_phy, sc,
-			    &fops_queues);
+	debugfs_create_devm_seqfile(sc->dev, "dma", sc->debug.debugfs_phy,
+				    read_file_dma);
+	debugfs_create_devm_seqfile(sc->dev, "interrupt", sc->debug.debugfs_phy,
+				    read_file_interrupt);
+	debugfs_create_devm_seqfile(sc->dev, "xmit", sc->debug.debugfs_phy,
+				    read_file_xmit);
+	debugfs_create_devm_seqfile(sc->dev, "queues", sc->debug.debugfs_phy,
+				    read_file_queues);
 	debugfs_create_u32("qlen_bk", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
 			   &sc->tx.txq_max_pending[IEEE80211_AC_BK]);
 	debugfs_create_u32("qlen_be", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
@@ -1276,10 +1334,10 @@
 			   &sc->tx.txq_max_pending[IEEE80211_AC_VI]);
 	debugfs_create_u32("qlen_vo", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
 			   &sc->tx.txq_max_pending[IEEE80211_AC_VO]);
-	debugfs_create_file("misc", S_IRUSR, sc->debug.debugfs_phy, sc,
-			    &fops_misc);
-	debugfs_create_file("reset", S_IRUSR, sc->debug.debugfs_phy, sc,
-			    &fops_reset);
+	debugfs_create_devm_seqfile(sc->dev, "misc", sc->debug.debugfs_phy,
+				    read_file_misc);
+	debugfs_create_devm_seqfile(sc->dev, "reset", sc->debug.debugfs_phy,
+				    read_file_reset);
 
 	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);
@@ -1301,8 +1359,9 @@
 			    &ah->config.cwm_ignore_extcca);
 	debugfs_create_file("regdump", S_IRUSR, sc->debug.debugfs_phy, sc,
 			    &fops_regdump);
-	debugfs_create_file("dump_nfcal", S_IRUSR, sc->debug.debugfs_phy, sc,
-			    &fops_dump_nfcal);
+	debugfs_create_devm_seqfile(sc->dev, "dump_nfcal",
+				    sc->debug.debugfs_phy,
+				    read_file_dump_nfcal);
 
 	ath9k_cmn_debug_base_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
 	ath9k_cmn_debug_modal_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
@@ -1320,10 +1379,17 @@
 			    &fops_btcoex);
 #endif
 
+#ifdef CPTCFG_ATH9K_WOW
+	debugfs_create_file("wow", S_IRUSR | S_IWUSR,
+			    sc->debug.debugfs_phy, sc, &fops_wow);
+#endif
+
 #ifdef CPTCFG_ATH9K_DYNACK
 	debugfs_create_file("ack_to", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
 			    sc, &fops_ackto);
 #endif
+	debugfs_create_file("tpc", S_IRUSR | S_IWUSR,
+			    sc->debug.debugfs_phy, sc, &fops_tpc);
 
 	return 0;
 }
diff --git a/drivers/net/wireless/ath/ath9k/dfs.c b/drivers/net/wireless/ath/ath9k/dfs.c
index 726271c..e98a9ea 100644
--- a/drivers/net/wireless/ath/ath9k/dfs.c
+++ b/drivers/net/wireless/ath/ath9k/dfs.c
@@ -126,8 +126,19 @@
 	DFS_STAT_INC(sc, pulses_detected);
 	return true;
 }
-#undef PRI_CH_RADAR_FOUND
-#undef EXT_CH_RADAR_FOUND
+
+static void
+ath9k_dfs_process_radar_pulse(struct ath_softc *sc, struct pulse_event *pe)
+{
+	struct dfs_pattern_detector *pd = sc->dfs_detector;
+	DFS_STAT_INC(sc, pulses_processed);
+	if (pd == NULL)
+		return;
+	if (!pd->add_pulse(pd, pe))
+		return;
+	DFS_STAT_INC(sc, radar_detected);
+	ieee80211_radar_detected(sc->hw);
+}
 
 /*
  * DFS: check PHY-error for radar pulse and feed the detector
@@ -176,18 +187,21 @@
 	ard.pulse_length_pri = vdata_end[-3];
 	pe.freq = ah->curchan->channel;
 	pe.ts = mactime;
-	if (ath9k_postprocess_radar_event(sc, &ard, &pe)) {
-		struct dfs_pattern_detector *pd = sc->dfs_detector;
-		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 - 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);
-			ieee80211_radar_detected(sc->hw);
-		}
+	if (!ath9k_postprocess_radar_event(sc, &ard, &pe))
+		return;
+
+	ath_dbg(common, DFS,
+		"ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, "
+		"width=%d, rssi=%d, delta_ts=%llu\n",
+		ard.pulse_bw_info, pe.freq, pe.ts, pe.width, pe.rssi,
+		pe.ts - sc->dfs_prev_pulse_ts);
+	sc->dfs_prev_pulse_ts = pe.ts;
+	if (ard.pulse_bw_info & PRI_CH_RADAR_FOUND)
+		ath9k_dfs_process_radar_pulse(sc, &pe);
+	if (ard.pulse_bw_info & EXT_CH_RADAR_FOUND) {
+		pe.freq += IS_CHAN_HT40PLUS(ah->curchan) ? 20 : -20;
+		ath9k_dfs_process_radar_pulse(sc, &pe);
 	}
 }
+#undef PRI_CH_RADAR_FOUND
+#undef EXT_CH_RADAR_FOUND
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index 971d770..cc81482 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -27,12 +27,7 @@
 void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask,
 			       u32 shift, u32 val)
 {
-	u32 regVal;
-
-	regVal = REG_READ(ah, reg) & ~mask;
-	regVal |= (val << shift) & mask;
-
-	REG_WRITE(ah, reg, regVal);
+	REG_RMW(ah, reg, ((val << shift) & mask), mask);
 
 	if (ah->config.analog_shiftreg)
 		udelay(100);
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
index e3e619e..f890612 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
@@ -389,6 +389,7 @@
 		}
 	}
 
+	ENABLE_REG_RMW_BUFFER(ah);
 	REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,
 		      (numXpdGain - 1) & 0x3);
 	REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1,
@@ -396,6 +397,7 @@
 	REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2,
 		      xpdGainValues[1]);
 	REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, 0);
+	REG_RMW_BUFFER_FLUSH(ah);
 
 	for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) {
 		regChainOffset = i * 0x1000;
@@ -748,6 +750,20 @@
 			  | ATH9K_POW_SM(ratesArray[rateDupCck], 0));
 	}
 
+	/* TPC initializations */
+	if (ah->tpc_enabled) {
+		int ht40_delta;
+
+		ht40_delta = (IS_CHAN_HT40(chan)) ? ht40PowerIncForPdadc : 0;
+		ar5008_hw_init_rate_txpower(ah, ratesArray, chan, ht40_delta);
+		/* Enable TPC */
+		REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX,
+			MAX_RATE_POWER | AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE);
+	} else {
+		/* Disable TPC */
+		REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER);
+	}
+
 	REGWRITE_BUFFER_FLUSH(ah);
 }
 
@@ -756,15 +772,14 @@
 				 struct ar5416_eeprom_4k *eep,
 				 u8 txRxAttenLocal)
 {
-	REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0,
-		  pModal->antCtrlChain[0]);
+	ENABLE_REG_RMW_BUFFER(ah);
+	REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0,
+		pModal->antCtrlChain[0], 0);
 
-	REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0),
-		  (REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) &
-		   ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
-		     AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
-		  SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
-		  SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
+	REG_RMW(ah, AR_PHY_TIMING_CTRL4(0),
+		SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
+		SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF),
+		AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF);
 
 	if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
 	    AR5416_EEP_MINOR_VER_3) {
@@ -803,6 +818,7 @@
 		      AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
 	REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000,
 		      AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
+	REG_RMW_BUFFER_FLUSH(ah);
 }
 
 /*
@@ -914,6 +930,7 @@
 		}
 	}
 
+	ENABLE_REG_RMW_BUFFER(ah);
 	if (AR_SREV_9271(ah)) {
 		ath9k_hw_analog_shift_rmw(ah,
 					  AR9285_AN_RF2G3,
@@ -1018,18 +1035,19 @@
 					  AR9285_AN_RF2G4_DB2_4_S,
 					  db2[4]);
 	}
+	REG_RMW_BUFFER_FLUSH(ah);
 
-
+	ENABLE_REG_RMW_BUFFER(ah);
 	REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH,
 		      pModal->switchSettling);
 	REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC,
 		      pModal->adcDesiredSize);
 
-	REG_WRITE(ah, AR_PHY_RF_CTL4,
-		  SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) |
-		  SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) |
-		  SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON)  |
-		  SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON));
+	REG_RMW(ah, AR_PHY_RF_CTL4,
+		SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) |
+		SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) |
+		SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON)  |
+		SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON), 0);
 
 	REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON,
 		      pModal->txEndToRxOn);
@@ -1058,6 +1076,8 @@
 				      pModal->swSettleHt40);
 	}
 
+	REG_RMW_BUFFER_FLUSH(ah);
+
 	bb_desired_scale = (pModal->bb_scale_smrt_antenna &
 			EEP_4K_BB_DESIRED_SCALE_MASK);
 	if ((pBase->txGainType == 0) && (bb_desired_scale != 0)) {
@@ -1066,6 +1086,7 @@
 		mask = BIT(0)|BIT(5)|BIT(10)|BIT(15)|BIT(20)|BIT(25);
 		pwrctrl = mask * bb_desired_scale;
 		clr = mask * 0x1f;
+		ENABLE_REG_RMW_BUFFER(ah);
 		REG_RMW(ah, AR_PHY_TX_PWRCTRL8, pwrctrl, clr);
 		REG_RMW(ah, AR_PHY_TX_PWRCTRL10, pwrctrl, clr);
 		REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL12, pwrctrl, clr);
@@ -1080,6 +1101,7 @@
 		clr = mask * 0x1f;
 		REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL11, pwrctrl, clr);
 		REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL13, pwrctrl, clr);
+		REG_RMW_BUFFER_FLUSH(ah);
 	}
 }
 
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
index e2d41e2..4849c6b 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
@@ -886,6 +886,21 @@
 			  | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8)
 			  | ATH9K_POW_SM(ratesArray[rateDupCck], 0));
 	}
+
+	/* TPC initializations */
+	if (ah->tpc_enabled) {
+		int ht40_delta;
+
+		ht40_delta = (IS_CHAN_HT40(chan)) ? ht40PowerIncForPdadc : 0;
+		ar5008_hw_init_rate_txpower(ah, ratesArray, chan, ht40_delta);
+		/* Enable TPC */
+		REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX,
+			MAX_RATE_POWER | AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE);
+	} else {
+		/* Disable TPC */
+		REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER);
+	}
+
 	REGWRITE_BUFFER_FLUSH(ah);
 }
 
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index 303aa35..9c844d8 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -466,6 +466,7 @@
 				  struct ar5416_eeprom_def *eep,
 				  u8 txRxAttenLocal, int regChainOffset, int i)
 {
+	ENABLE_REG_RMW_BUFFER(ah);
 	if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) {
 		txRxAttenLocal = pModal->txRxAttenCh[i];
 
@@ -483,16 +484,12 @@
 			      AR_PHY_GAIN_2GHZ_XATTEN2_DB,
 			      pModal->xatten2Db[i]);
 		} else {
-			REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
-			  (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
-			   ~AR_PHY_GAIN_2GHZ_BSW_MARGIN)
-			  | SM(pModal-> bswMargin[i],
-			       AR_PHY_GAIN_2GHZ_BSW_MARGIN));
-			REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
-			  (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
-			   ~AR_PHY_GAIN_2GHZ_BSW_ATTEN)
-			  | SM(pModal->bswAtten[i],
-			       AR_PHY_GAIN_2GHZ_BSW_ATTEN));
+			REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+				SM(pModal-> bswMargin[i], AR_PHY_GAIN_2GHZ_BSW_MARGIN),
+				AR_PHY_GAIN_2GHZ_BSW_MARGIN);
+			REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+				SM(pModal->bswAtten[i], AR_PHY_GAIN_2GHZ_BSW_ATTEN),
+				AR_PHY_GAIN_2GHZ_BSW_ATTEN);
 		}
 	}
 
@@ -504,17 +501,14 @@
 		      AR_PHY_RXGAIN + regChainOffset,
 		      AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]);
 	} else {
-		REG_WRITE(ah,
-			  AR_PHY_RXGAIN + regChainOffset,
-			  (REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) &
-			   ~AR_PHY_RXGAIN_TXRX_ATTEN)
-			  | SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN));
-		REG_WRITE(ah,
-			  AR_PHY_GAIN_2GHZ + regChainOffset,
-			  (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
-			   ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) |
-			  SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN));
+		REG_RMW(ah, AR_PHY_RXGAIN + regChainOffset,
+			SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN),
+			AR_PHY_RXGAIN_TXRX_ATTEN);
+		REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+			SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN),
+			AR_PHY_GAIN_2GHZ_RXTX_MARGIN);
 	}
+	REG_RMW_BUFFER_FLUSH(ah);
 }
 
 static void ath9k_hw_def_set_board_values(struct ath_hw *ah,
@@ -1332,6 +1326,20 @@
 		  ATH9K_POW_SM(pModal->pwrDecreaseFor3Chain, 6)
 		  | ATH9K_POW_SM(pModal->pwrDecreaseFor2Chain, 0));
 
+	/* TPC initializations */
+	if (ah->tpc_enabled) {
+		int ht40_delta;
+
+		ht40_delta = (IS_CHAN_HT40(chan)) ? ht40PowerIncForPdadc : 0;
+		ar5008_hw_init_rate_txpower(ah, ratesArray, chan, ht40_delta);
+		/* Enable TPC */
+		REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX,
+			MAX_RATE_POWER | AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE);
+	} else {
+		/* Disable TPC */
+		REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER);
+	}
+
 	REGWRITE_BUFFER_FLUSH(ah);
 }
 
diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index 48ee417..8e9ce56 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -49,7 +49,7 @@
 	if (AR_SREV_9100(sc->sc_ah))
 		return;
 
-	if (!led_blink)
+	if (!ath9k_led_blink)
 		sc->led_cdev.default_trigger =
 			ieee80211_get_radio_led_name(sc->hw);
 
@@ -69,9 +69,15 @@
 {
 	struct ath_hw *ah = sc->sc_ah;
 
-	if (AR_SREV_9100(ah) || (ah->led_pin >= 0))
+	if (AR_SREV_9100(ah))
 		return;
 
+	if (ah->led_pin >= 0) {
+		if (!((1 << ah->led_pin) & AR_GPIO_OE_OUT_MASK))
+			ath9k_hw_request_gpio(ah, ah->led_pin, "ath9k-led");
+		return;
+	}
+
 	if (AR_SREV_9287(ah))
 		ah->led_pin = ATH_LED_PIN_9287;
 	else if (AR_SREV_9485(sc->sc_ah))
@@ -202,18 +208,17 @@
 	}
 	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
-	ath9k_mci_update_rssi(sc);
-
 	ath9k_ps_wakeup(sc);
+	spin_lock_bh(&btcoex->btcoex_lock);
+
+	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) {
+		ath9k_mci_update_rssi(sc);
+		ath_mci_ftp_adjust(sc);
+	}
 
 	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
 		ath_detect_bt_priority(sc);
 
-	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
-		ath_mci_ftp_adjust(sc);
-
-	spin_lock_bh(&btcoex->btcoex_lock);
-
 	stomp_type = btcoex->bt_stomp_type;
 	timer_period = btcoex->btcoex_no_stomp;
 
@@ -252,9 +257,6 @@
 	struct ath_softc *sc = (struct ath_softc *)arg;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_btcoex *btcoex = &sc->btcoex;
-	struct ath_common *common = ath9k_hw_common(ah);
-
-	ath_dbg(common, BTCOEX, "no stomp timer running\n");
 
 	ath9k_ps_wakeup(sc);
 	spin_lock_bh(&btcoex->btcoex_lock);
@@ -271,7 +273,7 @@
 	ath9k_ps_restore(sc);
 }
 
-static int ath_init_btcoex_timer(struct ath_softc *sc)
+static void ath_init_btcoex_timer(struct ath_softc *sc)
 {
 	struct ath_btcoex *btcoex = &sc->btcoex;
 
@@ -280,6 +282,7 @@
 		btcoex->btcoex_period / 100;
 	btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
 				   btcoex->btcoex_period / 100;
+	btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
 
 	setup_timer(&btcoex->period_timer, ath_btcoex_period_timer,
 			(unsigned long) sc);
@@ -287,8 +290,6 @@
 			(unsigned long) sc);
 
 	spin_lock_init(&btcoex->btcoex_lock);
-
-	return 0;
 }
 
 /*
@@ -299,6 +300,10 @@
 	struct ath_btcoex *btcoex = &sc->btcoex;
 	struct ath_hw *ah = sc->sc_ah;
 
+	if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_3WIRE &&
+	    ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI)
+		return;
+
 	ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n");
 
 	/* make sure duty cycle timer is also stopped when resuming */
@@ -312,13 +317,19 @@
 	mod_timer(&btcoex->period_timer, jiffies);
 }
 
-
 /*
  * Pause btcoex timer and bt duty cycle timer
  */
 void ath9k_btcoex_timer_pause(struct ath_softc *sc)
 {
 	struct ath_btcoex *btcoex = &sc->btcoex;
+	struct ath_hw *ah = sc->sc_ah;
+
+	if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_3WIRE &&
+	    ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI)
+		return;
+
+	ath_dbg(ath9k_hw_common(ah), BTCOEX, "Stopping btcoex timers\n");
 
 	del_timer_sync(&btcoex->period_timer);
 	del_timer_sync(&btcoex->no_stomp_timer);
@@ -356,33 +367,33 @@
 {
 	struct ath_hw *ah = sc->sc_ah;
 
-	if ((ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) &&
-	    !ah->btcoex_hw.enabled) {
-		if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
-			ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
-						   AR_STOMP_LOW_WLAN_WGHT, 0);
-		else
-			ath9k_hw_btcoex_set_weight(ah, 0, 0,
-						   ATH_BTCOEX_STOMP_NONE);
-		ath9k_hw_btcoex_enable(ah);
+	if (ah->btcoex_hw.enabled ||
+	    ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_NONE)
+		return;
 
-		if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE)
-			ath9k_btcoex_timer_resume(sc);
-	}
+	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
+		ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
+					   AR_STOMP_LOW_WLAN_WGHT, 0);
+	else
+		ath9k_hw_btcoex_set_weight(ah, 0, 0,
+					   ATH_BTCOEX_STOMP_NONE);
+	ath9k_hw_btcoex_enable(ah);
+	ath9k_btcoex_timer_resume(sc);
 }
 
 void ath9k_stop_btcoex(struct ath_softc *sc)
 {
 	struct ath_hw *ah = sc->sc_ah;
 
-	if (ah->btcoex_hw.enabled &&
-	    ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) {
-		if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE)
-			ath9k_btcoex_timer_pause(sc);
-		ath9k_hw_btcoex_disable(ah);
-		if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
-			ath_mci_flush_profile(&sc->btcoex.mci);
-	}
+	if (!ah->btcoex_hw.enabled ||
+	    ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_NONE)
+		return;
+
+	ath9k_btcoex_timer_pause(sc);
+	ath9k_hw_btcoex_disable(ah);
+
+	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
+		ath_mci_flush_profile(&sc->btcoex.mci);
 }
 
 void ath9k_deinit_btcoex(struct ath_softc *sc)
@@ -409,22 +420,20 @@
 		break;
 	case ATH_BTCOEX_CFG_3WIRE:
 		ath9k_hw_btcoex_init_3wire(sc->sc_ah);
-		r = ath_init_btcoex_timer(sc);
-		if (r)
-			return -1;
+		ath_init_btcoex_timer(sc);
 		txq = sc->tx.txq_map[IEEE80211_AC_BE];
 		ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum);
-		sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
-		if (ath9k_hw_mci_is_enabled(ah)) {
-			sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE;
-			INIT_LIST_HEAD(&sc->btcoex.mci.info);
+		break;
+	case ATH_BTCOEX_CFG_MCI:
+		ath_init_btcoex_timer(sc);
 
-			r = ath_mci_setup(sc);
-			if (r)
-				return r;
+		sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE;
+		INIT_LIST_HEAD(&sc->btcoex.mci.info);
+		ath9k_hw_btcoex_init_mci(ah);
 
-			ath9k_hw_btcoex_init_mci(ah);
-		}
+		r = ath_mci_setup(sc);
+		if (r)
+			return r;
 
 		break;
 	default:
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index f60b39b..91fc605 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -40,6 +40,7 @@
 	{ USB_DEVICE(0x0cf3, 0xb003) }, /* Ubiquiti WifiStation Ext */
 	{ USB_DEVICE(0x0cf3, 0xb002) }, /* Ubiquiti WifiStation */
 	{ USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */
+	{ USB_DEVICE(0x0471, 0x209e) }, /* Philips (or NXP) PTA01 */
 
 	{ USB_DEVICE(0x0cf3, 0x7015),
 	  .driver_info = AR9287_USB },  /* Atheros */
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index de6d475..d07aaeb 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -44,6 +44,9 @@
 
 extern struct ieee80211_ops ath9k_htc_ops;
 extern int htc_modparam_nohwcrypt;
+#ifdef CPTCFG_MAC80211_LEDS
+extern int ath9k_htc_led_blink;
+#endif
 
 enum htc_phymode {
 	HTC_MODE_11NA		= 0,
@@ -441,6 +444,10 @@
 #define OP_BT_SCAN                 BIT(4)
 #define OP_TSF_RESET               BIT(6)
 
+enum htc_op_flags {
+	HTC_FWFLAG_NO_RMW,
+};
+
 struct ath9k_htc_priv {
 	struct device *dev;
 	struct ieee80211_hw *hw;
@@ -479,6 +486,7 @@
 	bool reconfig_beacon;
 	unsigned int rxfilter;
 	unsigned long op_flags;
+	unsigned long fw_flags;
 
 	struct ath9k_hw_cal_data caldata;
 	struct ath_spec_scan_priv spec_priv;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
index 8cef1ed..dc79afd 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
@@ -291,26 +291,15 @@
 {
 	struct ath9k_htc_priv *priv = file->private_data;
 	char buf[512];
-	unsigned int len = 0;
+	unsigned int len;
 
 	spin_lock_bh(&priv->tx.tx_lock);
-
-	len += scnprintf(buf + len, sizeof(buf) - len, "TX slot bitmap : ");
-
-	len += bitmap_scnprintf(buf + len, sizeof(buf) - len,
-			       priv->tx.tx_slot, MAX_TX_BUF_NUM);
-
-	len += scnprintf(buf + len, sizeof(buf) - len, "\n");
-
-	len += scnprintf(buf + len, sizeof(buf) - len,
-			 "Used slots     : %d\n",
-			 bitmap_weight(priv->tx.tx_slot, MAX_TX_BUF_NUM));
-
+	len = scnprintf(buf, sizeof(buf),
+			"TX slot bitmap : %*pb\n"
+			"Used slots     : %d\n",
+			MAX_TX_BUF_NUM, priv->tx.tx_slot,
+			bitmap_weight(priv->tx.tx_slot, MAX_TX_BUF_NUM));
 	spin_unlock_bh(&priv->tx.tx_lock);
-
-	if (len > sizeof(buf))
-		len = sizeof(buf);
-
 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
index d4188c5..73b9e4b 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
@@ -279,6 +279,10 @@
 	else
 		priv->ah->led_pin = ATH_LED_PIN_DEF;
 
+	if (!ath9k_htc_led_blink)
+		priv->led_cdev.default_trigger =
+			ieee80211_get_radio_led_name(priv->hw);
+
 	ath9k_configure_leds(priv);
 
 	snprintf(priv->led_name, sizeof(priv->led_name),
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index 33d9103..97428d4 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -39,6 +39,10 @@
 MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
 
 #ifdef CPTCFG_MAC80211_LEDS
+int ath9k_htc_led_blink = 1;
+module_param_named(blink, ath9k_htc_led_blink, int, 0444);
+MODULE_PARM_DESC(blink, "Enable LED blink on activity");
+
 static const struct ieee80211_tpt_blink ath9k_htc_tpt_blink[] = {
 	{ .throughput = 0 * 1024, .blink_time = 334 },
 	{ .throughput = 1 * 1024, .blink_time = 260 },
@@ -372,15 +376,137 @@
 	mutex_unlock(&priv->wmi->multi_write_mutex);
 }
 
+static void ath9k_reg_rmw_buffer(void *hw_priv,
+				 u32 reg_offset, u32 set, u32 clr)
+{
+	struct ath_hw *ah = (struct ath_hw *) hw_priv;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+	u32 rsp_status;
+	int r;
+
+	mutex_lock(&priv->wmi->multi_rmw_mutex);
+
+	/* Store the register/value */
+	priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].reg =
+		cpu_to_be32(reg_offset);
+	priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].set =
+		cpu_to_be32(set);
+	priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].clr =
+		cpu_to_be32(clr);
+
+	priv->wmi->multi_rmw_idx++;
+
+	/* If the buffer is full, send it out. */
+	if (priv->wmi->multi_rmw_idx == MAX_RMW_CMD_NUMBER) {
+		r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
+			  (u8 *) &priv->wmi->multi_rmw,
+			  sizeof(struct register_write) * priv->wmi->multi_rmw_idx,
+			  (u8 *) &rsp_status, sizeof(rsp_status),
+			  100);
+		if (unlikely(r)) {
+			ath_dbg(common, WMI,
+				"REGISTER RMW FAILED, multi len: %d\n",
+				priv->wmi->multi_rmw_idx);
+		}
+		priv->wmi->multi_rmw_idx = 0;
+	}
+
+	mutex_unlock(&priv->wmi->multi_rmw_mutex);
+}
+
+static void ath9k_reg_rmw_flush(void *hw_priv)
+{
+	struct ath_hw *ah = (struct ath_hw *) hw_priv;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+	u32 rsp_status;
+	int r;
+
+	if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags))
+		return;
+
+	atomic_dec(&priv->wmi->m_rmw_cnt);
+
+	mutex_lock(&priv->wmi->multi_rmw_mutex);
+
+	if (priv->wmi->multi_rmw_idx) {
+		r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
+			  (u8 *) &priv->wmi->multi_rmw,
+			  sizeof(struct register_rmw) * priv->wmi->multi_rmw_idx,
+			  (u8 *) &rsp_status, sizeof(rsp_status),
+			  100);
+		if (unlikely(r)) {
+			ath_dbg(common, WMI,
+				"REGISTER RMW FAILED, multi len: %d\n",
+				priv->wmi->multi_rmw_idx);
+		}
+		priv->wmi->multi_rmw_idx = 0;
+	}
+
+	mutex_unlock(&priv->wmi->multi_rmw_mutex);
+}
+
+static void ath9k_enable_rmw_buffer(void *hw_priv)
+{
+	struct ath_hw *ah = (struct ath_hw *) hw_priv;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+
+	if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags))
+		return;
+
+	atomic_inc(&priv->wmi->m_rmw_cnt);
+}
+
+static u32 ath9k_reg_rmw_single(void *hw_priv,
+				 u32 reg_offset, u32 set, u32 clr)
+{
+	struct ath_hw *ah = (struct ath_hw *) hw_priv;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+	struct register_rmw buf, buf_ret;
+	int ret;
+	u32 val = 0;
+
+	buf.reg = cpu_to_be32(reg_offset);
+	buf.set = cpu_to_be32(set);
+	buf.clr = cpu_to_be32(clr);
+
+	ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
+			  (u8 *) &buf, sizeof(buf),
+			  (u8 *) &buf_ret, sizeof(buf_ret),
+			  100);
+	if (unlikely(ret)) {
+		ath_dbg(common, WMI, "REGISTER RMW FAILED:(0x%04x, %d)\n",
+			reg_offset, ret);
+	}
+	return val;
+}
+
 static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
 {
-	u32 val;
+	struct ath_hw *ah = (struct ath_hw *) hw_priv;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
 
-	val = ath9k_regread(hw_priv, reg_offset);
-	val &= ~clr;
-	val |= set;
-	ath9k_regwrite(hw_priv, val, reg_offset);
-	return val;
+	if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) {
+		u32 val;
+
+		val = REG_READ(ah, reg_offset);
+		val &= ~clr;
+		val |= set;
+		REG_WRITE(ah, reg_offset, val);
+
+		return 0;
+	}
+
+	if (atomic_read(&priv->wmi->m_rmw_cnt))
+		ath9k_reg_rmw_buffer(hw_priv, reg_offset, set, clr);
+	else
+		ath9k_reg_rmw_single(hw_priv, reg_offset, set, clr);
+
+	return 0;
 }
 
 static void ath_usb_read_cachesize(struct ath_common *common, int *csz)
@@ -497,6 +623,8 @@
 	ah->reg_ops.write = ath9k_regwrite;
 	ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer;
 	ah->reg_ops.write_flush = ath9k_regwrite_flush;
+	ah->reg_ops.enable_rmw_buffer = ath9k_enable_rmw_buffer;
+	ah->reg_ops.rmw_flush = ath9k_reg_rmw_flush;
 	ah->reg_ops.rmw = ath9k_reg_rmw;
 	priv->ah = ah;
 
@@ -682,6 +810,12 @@
 		return -EINVAL;
 	}
 
+	if (priv->fw_version_major == 1 && priv->fw_version_minor < 4)
+		set_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags);
+
+	dev_info(priv->dev, "FW RMW support: %s\n",
+		test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags) ? "Off" : "On");
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index ac9563e..d163224 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -149,7 +149,7 @@
 	 * when matching addresses.
 	 */
 	iter_data.hw_macaddr = NULL;
-	memset(&iter_data.mask, 0xff, ETH_ALEN);
+	eth_broadcast_addr(iter_data.mask);
 
 	if (vif)
 		ath9k_htc_bssid_iter(&iter_data, vif->addr, vif);
diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c
index a0ff5b6..d2408da 100644
--- a/drivers/net/wireless/ath/ath9k/htc_hst.c
+++ b/drivers/net/wireless/ath/ath9k/htc_hst.c
@@ -351,11 +351,7 @@
 
 	return;
 ret:
-	/* HTC-generated packets are freed here. */
-	if (htc_hdr && htc_hdr->endpoint_id != ENDPOINT0)
-		dev_kfree_skb_any(skb);
-	else
-		kfree_skb(skb);
+	kfree_skb(skb);
 }
 
 static void ath9k_htc_fw_panic_report(struct htc_target *htc_handle,
diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
index 6dd44d4..174d716 100644
--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
+++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
@@ -108,6 +108,14 @@
 		ath9k_hw_ops(ah)->set_bt_ant_diversity(ah, enable);
 }
 
+static inline bool ath9k_hw_is_aic_enabled(struct ath_hw *ah)
+{
+	if (ath9k_hw_private_ops(ah)->is_aic_enabled)
+		return ath9k_hw_private_ops(ah)->is_aic_enabled(ah);
+
+	return false;
+}
+
 #endif
 
 /* Private hardware call ops */
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 82c7afa..de3b8f5 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -20,6 +20,7 @@
 #include <linux/time.h>
 #include <linux/bitops.h>
 #include <linux/etherdevice.h>
+#include <linux/gpio.h>
 #include <asm/unaligned.h>
 
 #include "hw.h"
@@ -121,6 +122,36 @@
 	REGWRITE_BUFFER_FLUSH(ah);
 }
 
+void ath9k_hw_read_array(struct ath_hw *ah, u32 array[][2], int size)
+{
+	u32 *tmp_reg_list, *tmp_data;
+	int i;
+
+	tmp_reg_list = kmalloc(size * sizeof(u32), GFP_KERNEL);
+	if (!tmp_reg_list) {
+		dev_err(ah->dev, "%s: tmp_reg_list: alloc filed\n", __func__);
+		return;
+	}
+
+	tmp_data = kmalloc(size * sizeof(u32), GFP_KERNEL);
+	if (!tmp_data) {
+		dev_err(ah->dev, "%s tmp_data: alloc filed\n", __func__);
+		goto error_tmp_data;
+	}
+
+	for (i = 0; i < size; i++)
+		tmp_reg_list[i] = array[i][0];
+
+	REG_READ_MULTI(ah, tmp_reg_list, tmp_data, size);
+
+	for (i = 0; i < size; i++)
+		array[i][1] = tmp_data[i];
+
+	kfree(tmp_data);
+error_tmp_data:
+	kfree(tmp_reg_list);
+}
+
 u32 ath9k_hw_reverse_bits(u32 val, u32 n)
 {
 	u32 retval;
@@ -246,6 +277,8 @@
 	case AR9300_DEVID_AR953X:
 		ah->hw_version.macVersion = AR_SREV_VERSION_9531;
 		return;
+	case AR9300_DEVID_QCA956X:
+		ah->hw_version.macVersion = AR_SREV_VERSION_9561;
 	}
 
 	val = REG_READ(ah, AR_SREV) & AR_SREV_ID;
@@ -364,6 +397,9 @@
 		ah->config.rimt_first = 700;
 	}
 
+	if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
+		ah->config.pll_pwrsave = 7;
+
 	/*
 	 * We need this for PCI devices only (Cardbus, PCI, miniPCI)
 	 * _and_ if on non-uniprocessor systems (Multiprocessor/HT).
@@ -422,6 +458,8 @@
 	ah->power_mode = ATH9K_PM_UNDEFINED;
 	ah->htc_reset_init = true;
 
+	ah->tpc_enabled = false;
+
 	ah->ani_function = ATH9K_ANI_ALL;
 	if (!AR_SREV_9300_20_OR_LATER(ah))
 		ah->ani_function &= ~ATH9K_ANI_MRC_CCK;
@@ -536,6 +574,7 @@
 	case AR_SREV_VERSION_9550:
 	case AR_SREV_VERSION_9565:
 	case AR_SREV_VERSION_9531:
+	case AR_SREV_VERSION_9561:
 		break;
 	default:
 		ath_err(common,
@@ -636,6 +675,7 @@
 	case AR9485_DEVID_AR1111:
 	case AR9300_DEVID_AR9565:
 	case AR9300_DEVID_AR953X:
+	case AR9300_DEVID_QCA956X:
 		break;
 	default:
 		if (common->bus_ops->ath_bus_type == ATH_USB)
@@ -776,7 +816,8 @@
 		/* program BB PLL phase_shift */
 		REG_RMW_FIELD(ah, AR_CH0_BB_DPLL3,
 			      AR_CH0_BB_DPLL3_PHASE_SHIFT, 0x1);
-	} else if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
+	} else if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
+		   AR_SREV_9561(ah)) {
 		u32 regval, pll2_divint, pll2_divfrac, refdiv;
 
 		REG_WRITE(ah, AR_RTC_PLL_CONTROL,
@@ -787,7 +828,7 @@
 		udelay(100);
 
 		if (ah->is_clk_25mhz) {
-			if (AR_SREV_9531(ah)) {
+			if (AR_SREV_9531(ah) || AR_SREV_9561(ah)) {
 				pll2_divint = 0x1c;
 				pll2_divfrac = 0xa3d2;
 				refdiv = 1;
@@ -803,14 +844,15 @@
 				refdiv = 5;
 			} else {
 				pll2_divint = 0x11;
-				pll2_divfrac =
-					AR_SREV_9531(ah) ? 0x26665 : 0x26666;
+				pll2_divfrac = (AR_SREV_9531(ah) ||
+						AR_SREV_9561(ah)) ?
+						0x26665 : 0x26666;
 				refdiv = 1;
 			}
 		}
 
 		regval = REG_READ(ah, AR_PHY_PLL_MODE);
-		if (AR_SREV_9531(ah))
+		if (AR_SREV_9531(ah) || AR_SREV_9561(ah))
 			regval |= (0x1 << 22);
 		else
 			regval |= (0x1 << 16);
@@ -828,14 +870,16 @@
 				(0x1 << 13) |
 				(0x4 << 26) |
 				(0x18 << 19);
-		else if (AR_SREV_9531(ah))
+		else if (AR_SREV_9531(ah) || AR_SREV_9561(ah)) {
 			regval = (regval & 0x01c00fff) |
 				(0x1 << 31) |
 				(0x2 << 29) |
 				(0xa << 25) |
-				(0x1 << 19) |
-				(0x6 << 12);
-		else
+				(0x1 << 19);
+
+			if (AR_SREV_9531(ah))
+				regval |= (0x6 << 12);
+		} else
 			regval = (regval & 0x80071fff) |
 				(0x3 << 30) |
 				(0x1 << 13) |
@@ -843,7 +887,7 @@
 				(0x60 << 19);
 		REG_WRITE(ah, AR_PHY_PLL_MODE, regval);
 
-		if (AR_SREV_9531(ah))
+		if (AR_SREV_9531(ah) || AR_SREV_9561(ah))
 			REG_WRITE(ah, AR_PHY_PLL_MODE,
 				  REG_READ(ah, AR_PHY_PLL_MODE) & 0xffbfffff);
 		else
@@ -882,7 +926,8 @@
 		AR_IMR_RXORN |
 		AR_IMR_BCNMISC;
 
-	if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah))
+	if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
+	    AR_SREV_9561(ah))
 		sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;
 
 	if (AR_SREV_9300_20_OR_LATER(ah)) {
@@ -1186,6 +1231,7 @@
 	u32 mask = AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC;
 	u32 set = AR_STA_ID1_KSRCH_MODE;
 
+	ENABLE_REG_RMW_BUFFER(ah);
 	switch (opmode) {
 	case NL80211_IFTYPE_ADHOC:
 		if (!AR_SREV_9340_13(ah)) {
@@ -1207,6 +1253,7 @@
 		break;
 	}
 	REG_RMW(ah, AR_STA_ID1, set, mask);
+	REG_RMW_BUFFER_FLUSH(ah);
 }
 
 void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled,
@@ -1671,7 +1718,8 @@
 		}
 #ifdef __BIG_ENDIAN
 		else if (AR_SREV_9330(ah) || AR_SREV_9340(ah) ||
-			 AR_SREV_9550(ah) || AR_SREV_9531(ah))
+			 AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
+			 AR_SREV_9561(ah))
 			REG_RMW(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB, 0);
 		else
 			REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD);
@@ -1918,6 +1966,7 @@
 	if (!ath9k_hw_mci_is_enabled(ah))
 		REG_WRITE(ah, AR_OBS, 8);
 
+	ENABLE_REG_RMW_BUFFER(ah);
 	if (ah->config.rx_intr_mitigation) {
 		REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, ah->config.rimt_last);
 		REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, ah->config.rimt_first);
@@ -1927,6 +1976,7 @@
 		REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, 300);
 		REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, 750);
 	}
+	REG_RMW_BUFFER_FLUSH(ah);
 
 	ath9k_hw_init_bb(ah, chan);
 
@@ -2459,7 +2509,8 @@
 
 	if (AR_SREV_9300_20_OR_LATER(ah)) {
 		pCap->hw_caps |= ATH9K_HW_CAP_EDMA | ATH9K_HW_CAP_FASTCLOCK;
-		if (!AR_SREV_9330(ah) && !AR_SREV_9485(ah) && !AR_SREV_9565(ah))
+		if (!AR_SREV_9330(ah) && !AR_SREV_9485(ah) &&
+		    !AR_SREV_9561(ah) && !AR_SREV_9565(ah))
 			pCap->hw_caps |= ATH9K_HW_CAP_LDPC;
 
 		pCap->rx_hp_qdepth = ATH9K_HW_RX_HP_QDEPTH;
@@ -2476,7 +2527,9 @@
 	if (AR_SREV_9300_20_OR_LATER(ah))
 		pCap->hw_caps |= ATH9K_HW_CAP_RAC_SUPPORTED;
 
-	if (AR_SREV_9300_20_OR_LATER(ah))
+	if (AR_SREV_9561(ah))
+		ah->ent_mode = 0x3BDA000;
+	else if (AR_SREV_9300_20_OR_LATER(ah))
 		ah->ent_mode = REG_READ(ah, AR_ENT_OTP);
 
 	if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah))
@@ -2529,13 +2582,17 @@
 			pCap->hw_caps |= ATH9K_HW_CAP_RTT;
 	}
 
-	if (AR_SREV_9462(ah))
-		pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE;
-
 	if (AR_SREV_9300_20_OR_LATER(ah) &&
 	    ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
 			pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
 
+#ifdef CPTCFG_ATH9K_WOW
+	if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah))
+		ah->wow.max_patterns = MAX_NUM_PATTERN;
+	else
+		ah->wow.max_patterns = MAX_NUM_PATTERN_LEGACY;
+#endif
+
 	return 0;
 }
 
@@ -2655,11 +2712,23 @@
 	if (AR_SREV_9271(ah))
 		val = ~val;
 
-	REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
-		AR_GPIO_BIT(gpio));
+	if ((1 << gpio) & AR_GPIO_OE_OUT_MASK)
+		REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
+			AR_GPIO_BIT(gpio));
+	else
+		gpio_set_value(gpio, val & 1);
 }
 EXPORT_SYMBOL(ath9k_hw_set_gpio);
 
+void ath9k_hw_request_gpio(struct ath_hw *ah, u32 gpio, const char *label)
+{
+	if (gpio >= ah->caps.num_gpio_pins)
+		return;
+
+	gpio_request_one(gpio, GPIOF_DIR_OUT | GPIOF_INIT_LOW, label);
+}
+EXPORT_SYMBOL(ath9k_hw_request_gpio);
+
 void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna)
 {
 	REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7));
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index fda6cb5..97070f5 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -27,6 +27,7 @@
 #include "eeprom.h"
 #include "calib.h"
 #include "reg.h"
+#include "reg_mci.h"
 #include "phy.h"
 #include "btcoex.h"
 #include "dynack.h"
@@ -54,6 +55,7 @@
 #define AR9485_DEVID_AR1111	0x0037
 #define AR9300_DEVID_AR9565     0x0036
 #define AR9300_DEVID_AR953X     0x003d
+#define AR9300_DEVID_QCA956X    0x003f
 
 #define AR5416_AR9100_DEVID	0x000b
 
@@ -98,6 +100,18 @@
 			(_ah)->reg_ops.write_flush((_ah));	\
 	} while (0)
 
+#define ENABLE_REG_RMW_BUFFER(_ah)					\
+	do {								\
+		if ((_ah)->reg_ops.enable_rmw_buffer)	\
+			(_ah)->reg_ops.enable_rmw_buffer((_ah)); \
+	} while (0)
+
+#define REG_RMW_BUFFER_FLUSH(_ah)					\
+	do {								\
+		if ((_ah)->reg_ops.rmw_flush)		\
+			(_ah)->reg_ops.rmw_flush((_ah));	\
+	} while (0)
+
 #define PR_EEP(_s, _val)						\
 	do {								\
 		len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
@@ -124,6 +138,8 @@
 
 #define REG_WRITE_ARRAY(iniarray, column, regWr) \
 	ath9k_hw_write_array(ah, iniarray, column, &(regWr))
+#define REG_READ_ARRAY(ah, array, size) \
+	ath9k_hw_read_array(ah, array, size)
 
 #define AR_GPIO_OUTPUT_MUX_AS_OUTPUT             0
 #define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1
@@ -198,12 +214,13 @@
 #define KAL_NUM_DESC_WORDS	12
 #define KAL_ANTENNA_MODE	1
 #define KAL_TO_DS		1
-#define KAL_DELAY		4	/*delay of 4ms between 2 KAL frames */
+#define KAL_DELAY		4	/* delay of 4ms between 2 KAL frames */
 #define KAL_TIMEOUT		900
 
 #define MAX_PATTERN_SIZE		256
 #define MAX_PATTERN_MASK_SIZE		32
-#define MAX_NUM_PATTERN			8
+#define MAX_NUM_PATTERN			16
+#define MAX_NUM_PATTERN_LEGACY		8
 #define MAX_NUM_USER_PATTERN		6 /*  deducting the disassociate and
 					      deauthenticate packets */
 
@@ -247,12 +264,10 @@
 #ifdef CPTCFG_ATH9K_PCOEM
 	ATH9K_HW_CAP_RTT			= BIT(14),
 	ATH9K_HW_CAP_MCI			= BIT(15),
-	ATH9K_HW_WOW_DEVICE_CAPABLE		= BIT(16),
 	ATH9K_HW_CAP_BT_ANT_DIV			= BIT(17),
 #else
 	ATH9K_HW_CAP_RTT			= 0,
 	ATH9K_HW_CAP_MCI			= 0,
-	ATH9K_HW_WOW_DEVICE_CAPABLE		= 0,
 	ATH9K_HW_CAP_BT_ANT_DIV			= 0,
 #endif
 	ATH9K_HW_CAP_DFS			= BIT(18),
@@ -271,6 +286,12 @@
  * of those types.
  */
 
+struct ath9k_hw_wow {
+	u32 wow_event_mask;
+	u32 wow_event_mask2;
+	u8 max_patterns;
+};
+
 struct ath9k_hw_capabilities {
 	u32 hw_caps; /* ATH9K_HW_CAP_* from ath9k_hw_caps */
 	u16 rts_aggr_limit;
@@ -302,6 +323,12 @@
 	HW_MAC_HANG               = BIT(5),
 };
 
+#define AR_PCIE_PLL_PWRSAVE_CONTROL BIT(0)
+#define AR_PCIE_PLL_PWRSAVE_ON_D3   BIT(1)
+#define AR_PCIE_PLL_PWRSAVE_ON_D0   BIT(2)
+#define AR_PCIE_CDR_PWRSAVE_ON_D3   BIT(3)
+#define AR_PCIE_CDR_PWRSAVE_ON_D0   BIT(4)
+
 struct ath9k_ops_config {
 	int dma_beacon_response_time;
 	int sw_beacon_response_time;
@@ -328,7 +355,7 @@
 	u32 ant_ctrl_comm2g_switch_enable;
 	bool xatten_margin_cfg;
 	bool alt_mingainidx;
-	bool no_pll_pwrsave;
+	u8 pll_pwrsave;
 	bool tx_gain_buffalo;
 	bool led_active_high;
 };
@@ -640,6 +667,10 @@
 
 	/* ANI */
 	void (*ani_cache_ini_regs)(struct ath_hw *ah);
+
+#ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
+	bool (*is_aic_enabled)(struct ath_hw *ah);
+#endif /* CPTCFG_ATH9K_BTCOEX_SUPPORT */
 };
 
 /**
@@ -929,7 +960,7 @@
 	u32 ent_mode;
 
 #ifdef CPTCFG_ATH9K_WOW
-	u32 wow_event_mask;
+	struct ath9k_hw_wow wow;
 #endif
 	bool is_clk_25mhz;
 	int (*get_mac_revision)(void);
@@ -993,6 +1024,7 @@
 void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio,
 			 u32 ah_signal_type);
 void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val);
+void ath9k_hw_request_gpio(struct ath_hw *ah, u32 gpio, const char *label);
 void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna);
 
 /* General Operation */
@@ -1001,6 +1033,7 @@
 bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout);
 void ath9k_hw_write_array(struct ath_hw *ah, const struct ar5416IniArray *array,
 			  int column, unsigned int *writecnt);
+void ath9k_hw_read_array(struct ath_hw *ah, u32 array[][2], int size);
 u32 ath9k_hw_reverse_bits(u32 val, u32 n);
 u16 ath9k_hw_computetxtime(struct ath_hw *ah,
 			   u8 phy, int kbps,
@@ -1086,6 +1119,8 @@
 void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
 void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
 				 struct ath9k_channel *chan);
+void ar5008_hw_init_rate_txpower(struct ath_hw *ah, int16_t *rate_array,
+				 struct ath9k_channel *chan, int ht40_delta);
 
 /* Hardware family op attach helpers */
 int ar5008_hw_attach_phy_ops(struct ath_hw *ah);
@@ -1108,6 +1143,7 @@
 void ath9k_hw_setslottime(struct ath_hw *ah, u32 us);
 
 #ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
+void ar9003_hw_attach_aic_ops(struct ath_hw *ah);
 static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah)
 {
 	return ah->btcoex_hw.enabled;
@@ -1125,6 +1161,9 @@
 	return ah->btcoex_hw.scheme;
 }
 #else
+static inline void ar9003_hw_attach_aic_ops(struct ath_hw *ah)
+{
+}
 static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah)
 {
 	return false;
@@ -1145,23 +1184,19 @@
 
 
 #ifdef CPTCFG_ATH9K_WOW
-const char *ath9k_hw_wow_event_to_string(u32 wow_event);
-void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
-				u8 *user_mask, int pattern_count,
-				int pattern_len);
+int ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
+			       u8 *user_mask, int pattern_count,
+			       int pattern_len);
 u32 ath9k_hw_wow_wakeup(struct ath_hw *ah);
 void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable);
 #else
-static inline const char *ath9k_hw_wow_event_to_string(u32 wow_event)
+static inline int ath9k_hw_wow_apply_pattern(struct ath_hw *ah,
+					     u8 *user_pattern,
+					     u8 *user_mask,
+					     int pattern_count,
+					     int pattern_len)
 {
-	return NULL;
-}
-static inline void ath9k_hw_wow_apply_pattern(struct ath_hw *ah,
-					      u8 *user_pattern,
-					      u8 *user_mask,
-					      int pattern_count,
-					      int pattern_len)
-{
+	return 0;
 }
 static inline u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
 {
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 97dc209..d833f54 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -45,8 +45,8 @@
 module_param_named(nohwcrypt, ath9k_modparam_nohwcrypt, int, 0444);
 MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
 
-int led_blink;
-module_param_named(blink, led_blink, int, 0444);
+int ath9k_led_blink;
+module_param_named(blink, ath9k_led_blink, int, 0444);
 MODULE_PARM_DESC(blink, "Enable LED blink on activity");
 
 static int ath9k_btcoex_enable;
@@ -141,6 +141,16 @@
 	return val;
 }
 
+static void ath9k_multi_ioread32(void *hw_priv, u32 *addr,
+                                u32 *val, u16 count)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+		val[i] = ath9k_ioread32(hw_priv, addr[i]);
+}
+
+
 static unsigned int __ath9k_reg_rmw(struct ath_softc *sc, u32 reg_offset,
 				    u32 set, u32 clr)
 {
@@ -437,8 +447,15 @@
 		ath_info(common, "Enable WAR for ASPM D3/L1\n");
 	}
 
+	/*
+	 * The default value of pll_pwrsave is 1.
+	 * For certain AR9485 cards, it is set to 0.
+	 * For AR9462, AR9565 it's set to 7.
+	 */
+	ah->config.pll_pwrsave = 1;
+
 	if (sc->driver_data & ATH9K_PCI_NO_PLL_PWRSAVE) {
-		ah->config.no_pll_pwrsave = true;
+		ah->config.pll_pwrsave = 0;
 		ath_info(common, "Disable PLL PowerSave\n");
 	}
 
@@ -530,6 +547,7 @@
 	ah->hw = sc->hw;
 	ah->hw_version.devid = devid;
 	ah->reg_ops.read = ath9k_ioread32;
+	ah->reg_ops.multi_read = ath9k_multi_ioread32;
 	ah->reg_ops.write = ath9k_iowrite32;
 	ah->reg_ops.rmw = ath9k_reg_rmw;
 	pCap = &ah->caps;
@@ -763,7 +781,8 @@
 		.num_different_channels = 1,
 		.beacon_int_infra_match = true,
 		.radar_detect_widths =	BIT(NL80211_CHAN_WIDTH_20_NOHT) |
-					BIT(NL80211_CHAN_WIDTH_20),
+					BIT(NL80211_CHAN_WIDTH_20) |
+					BIT(NL80211_CHAN_WIDTH_40),
 	}
 #endif
 };
@@ -996,6 +1015,7 @@
 	ath9k_ps_restore(sc);
 
 	ath9k_deinit_debug(sc);
+	ath9k_deinit_wow(hw);
 	ieee80211_unregister_hw(hw);
 	ath_rx_cleanup(sc);
 	ath9k_deinit_softc(sc);
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index b829263..90631d7 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -516,14 +516,14 @@
 		ath_hw_cycle_counters_update(common);
 
 	if (cc->cycles > 0) {
-		survey->filled |= SURVEY_INFO_CHANNEL_TIME |
-			SURVEY_INFO_CHANNEL_TIME_BUSY |
-			SURVEY_INFO_CHANNEL_TIME_RX |
-			SURVEY_INFO_CHANNEL_TIME_TX;
-		survey->channel_time += cc->cycles / div;
-		survey->channel_time_busy += cc->rx_busy / div;
-		survey->channel_time_rx += cc->rx_frame / div;
-		survey->channel_time_tx += cc->tx_frame / div;
+		survey->filled |= SURVEY_INFO_TIME |
+			SURVEY_INFO_TIME_BUSY |
+			SURVEY_INFO_TIME_RX |
+			SURVEY_INFO_TIME_TX;
+		survey->time += cc->cycles / div;
+		survey->time_busy += cc->rx_busy / div;
+		survey->time_rx += cc->rx_frame / div;
+		survey->time_tx += cc->tx_frame / div;
 	}
 
 	if (cc->cycles < div)
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 3e58bfa..bba85d1 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -820,7 +820,8 @@
 		return;
 	}
 
-	if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah))
+	if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
+	    AR_SREV_9561(ah))
 		sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;
 
 	async_mask = AR_INTR_MAC_IRQ;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 4aeddd7..cca41f5 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -285,6 +285,7 @@
 
 	__ath_cancel_work(sc);
 
+	disable_irq(sc->irq);
 	tasklet_disable(&sc->intr_tq);
 	tasklet_disable(&sc->bcon_tasklet);
 	spin_lock_bh(&sc->sc_pcu_lock);
@@ -331,6 +332,7 @@
 		r = -EIO;
 
 out:
+	enable_irq(sc->irq);
 	spin_unlock_bh(&sc->sc_pcu_lock);
 	tasklet_enable(&sc->bcon_tasklet);
 	tasklet_enable(&sc->intr_tq);
@@ -512,9 +514,6 @@
 	if (!ah || test_bit(ATH_OP_INVALID, &common->op_flags))
 		return IRQ_NONE;
 
-	if (!AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags))
-		return IRQ_NONE;
-
 	/* shared irq, not for us */
 	if (!ath9k_hw_intrpend(ah))
 		return IRQ_NONE;
@@ -529,7 +528,7 @@
 	ath9k_debug_sync_cause(sc, sync_cause);
 	status &= ah->imask;	/* discard unasked-for bits */
 
-	if (AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags))
+	if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
 		return IRQ_HANDLED;
 
 	/*
@@ -556,15 +555,6 @@
 	    (status & ATH9K_INT_BB_WATCHDOG))
 		goto chip_reset;
 
-#ifdef CPTCFG_ATH9K_WOW
-	if (status & ATH9K_INT_BMISS) {
-		if (atomic_read(&sc->wow_sleep_proc_intr) == 0) {
-			atomic_inc(&sc->wow_got_bmiss_intr);
-			atomic_dec(&sc->wow_sleep_proc_intr);
-		}
-	}
-#endif
-
 	if (status & ATH9K_INT_SWBA)
 		tasklet_schedule(&sc->bcon_tasklet);
 
@@ -1004,7 +994,7 @@
 	 * BSSID mask when matching addresses.
 	 */
 	memset(iter_data, 0, sizeof(*iter_data));
-	memset(&iter_data->mask, 0xff, ETH_ALEN);
+	eth_broadcast_addr(iter_data->mask);
 	iter_data->slottime = ATH9K_SLOT_TIME_9;
 
 	list_for_each_entry(avp, &ctx->vifs, list)
@@ -1149,7 +1139,7 @@
 			ctx->primary_sta = iter_data.primary_sta;
 		} else {
 			ctx->primary_sta = NULL;
-			memset(common->curbssid, 0, ETH_ALEN);
+			eth_zero_addr(common->curbssid);
 			common->curaid = 0;
 			ath9k_hw_write_associd(sc->sc_ah);
 			if (ath9k_hw_mci_is_enabled(sc->sc_ah))
@@ -1182,6 +1172,38 @@
 	ath9k_ps_restore(sc);
 }
 
+static void ath9k_tpc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+	int *power = (int *)data;
+
+	if (*power < vif->bss_conf.txpower)
+		*power = vif->bss_conf.txpower;
+}
+
+/* Called with sc->mutex held. */
+void ath9k_set_txpower(struct ath_softc *sc, struct ieee80211_vif *vif)
+{
+	int power;
+	struct ath_hw *ah = sc->sc_ah;
+	struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
+
+	ath9k_ps_wakeup(sc);
+	if (ah->tpc_enabled) {
+		power = (vif) ? vif->bss_conf.txpower : -1;
+		ieee80211_iterate_active_interfaces_atomic(
+				sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+				ath9k_tpc_vif_iter, &power);
+		if (power == -1)
+			power = sc->hw->conf.power_level;
+	} else {
+		power = sc->hw->conf.power_level;
+	}
+	sc->cur_chan->txpower = 2 * power;
+	ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
+	sc->cur_chan->cur_txpower = reg->max_power_level;
+	ath9k_ps_restore(sc);
+}
+
 static void ath9k_assign_hw_queues(struct ieee80211_hw *hw,
 				   struct ieee80211_vif *vif)
 {
@@ -1235,6 +1257,8 @@
 
 	ath9k_assign_hw_queues(hw, vif);
 
+	ath9k_set_txpower(sc, vif);
+
 	an->sc = sc;
 	an->sta = NULL;
 	an->vif = vif;
@@ -1275,6 +1299,8 @@
 	ath9k_assign_hw_queues(hw, vif);
 	ath9k_calculate_summary_state(sc, avp->chanctx);
 
+	ath9k_set_txpower(sc, vif);
+
 	mutex_unlock(&sc->mutex);
 	return 0;
 }
@@ -1304,6 +1330,8 @@
 
 	ath9k_calculate_summary_state(sc, avp->chanctx);
 
+	ath9k_set_txpower(sc, NULL);
+
 	mutex_unlock(&sc->mutex);
 }
 
@@ -1407,14 +1435,6 @@
 		ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef);
 	}
 
-	if (changed & IEEE80211_CONF_CHANGE_POWER) {
-		ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level);
-		sc->cur_chan->txpower = 2 * conf->power_level;
-		ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
-				       sc->cur_chan->txpower,
-				       &sc->cur_chan->cur_txpower);
-	}
-
 	mutex_unlock(&sc->mutex);
 	ath9k_ps_restore(sc);
 
@@ -1774,6 +1794,12 @@
 	if (changed & CHECK_ANI)
 		ath_check_ani(sc);
 
+	if (changed & BSS_CHANGED_TXPOWER) {
+		ath_dbg(common, CONFIG, "vif %pM power %d dbm power_type %d\n",
+			vif->addr, bss_conf->txpower, bss_conf->txpower_type);
+		ath9k_set_txpower(sc, vif);
+	}
+
 	mutex_unlock(&sc->mutex);
 	ath9k_ps_restore(sc);
 
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c
index 3f7a11e..66596b9 100644
--- a/drivers/net/wireless/ath/ath9k/mci.c
+++ b/drivers/net/wireless/ath/ath9k/mci.c
@@ -495,7 +495,7 @@
 	ar9003_mci_get_interrupt(sc->sc_ah, &mci_int, &mci_int_rxmsg);
 
 	if (ar9003_mci_state(ah, MCI_STATE_ENABLE) == 0) {
-		ar9003_mci_get_next_gpm_offset(ah, true, NULL);
+		ar9003_mci_state(ah, MCI_STATE_INIT_GPM_OFFSET);
 		return;
 	}
 
@@ -559,8 +559,7 @@
 				return;
 
 			pgpm = mci->gpm_buf.bf_addr;
-			offset = ar9003_mci_get_next_gpm_offset(ah, false,
-								&more_data);
+			offset = ar9003_mci_get_next_gpm_offset(ah, &more_data);
 
 			if (offset == MCI_GPM_INVALID)
 				break;
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 6bc24fb..78cb23b 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -427,6 +427,11 @@
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
 			 0x0036,
 			 0x11AD, /* LITEON */
+			 0x1842),
+	  .driver_data = ATH9K_PCI_AR9565_1ANT },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
+			 0x11AD, /* LITEON */
 			 0x6671),
 	  .driver_data = ATH9K_PCI_AR9565_1ANT },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
@@ -446,9 +451,19 @@
 	  .driver_data = ATH9K_PCI_AR9565_1ANT },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
 			 0x0036,
+			 0x1B9A, /* XAVI */
+			 0x28A3),
+	  .driver_data = ATH9K_PCI_AR9565_1ANT },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
 			 PCI_VENDOR_ID_AZWAVE,
 			 0x218A),
 	  .driver_data = ATH9K_PCI_AR9565_1ANT },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
+			 PCI_VENDOR_ID_AZWAVE,
+			 0x2F8A),
+	  .driver_data = ATH9K_PCI_AR9565_1ANT },
 
 	/* WB335 1-ANT / Antenna Diversity */
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
@@ -508,6 +523,11 @@
 	  .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
 			 0x0036,
+			 PCI_VENDOR_ID_AZWAVE,
+			 0x213C),
+	  .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
 			 PCI_VENDOR_ID_HP,
 			 0x18E3),
 	  .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
@@ -555,6 +575,16 @@
 	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
 			 0x0036,
+			 PCI_VENDOR_ID_SAMSUNG,
+			 0x4129),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
+			 PCI_VENDOR_ID_SAMSUNG,
+			 0x412A),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
 			 PCI_VENDOR_ID_ATHEROS,
 			 0x3027),
 	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
@@ -586,10 +616,25 @@
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
 			 0x0036,
 			 0x11AD, /* LITEON */
+			 0x1832),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
+			 0x11AD, /* LITEON */
 			 0x0692),
 	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
 			 0x0036,
+			 0x11AD, /* LITEON */
+			 0x0803),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
+			 0x11AD, /* LITEON */
+			 0x0813),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
 			 PCI_VENDOR_ID_AZWAVE,
 			 0x2130),
 	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
@@ -605,6 +650,21 @@
 	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
 			 0x0036,
+			 PCI_VENDOR_ID_AZWAVE,
+			 0x218B),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
+			 PCI_VENDOR_ID_AZWAVE,
+			 0x218C),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
+			 PCI_VENDOR_ID_AZWAVE,
+			 0x2F82),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
 			 0x144F, /* ASKEY */
 			 0x7202),
 	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
@@ -616,10 +676,20 @@
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
 			 0x0036,
 			 0x1B9A, /* XAVI */
+			 0x2813),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
+			 0x1B9A, /* XAVI */
 			 0x28A2),
 	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
 			 0x0036,
+			 0x1B9A, /* XAVI */
+			 0x28A4),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
 			 0x185F, /* WNC */
 			 0x3027),
 	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
@@ -636,10 +706,25 @@
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
 			 0x0036,
 			 PCI_VENDOR_ID_FOXCONN,
+			 0xE08F),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
+			 PCI_VENDOR_ID_FOXCONN,
 			 0xE081),
 	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
 			 0x0036,
+			 PCI_VENDOR_ID_FOXCONN,
+			 0xE091),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
+			 PCI_VENDOR_ID_FOXCONN,
+			 0xE099),
+	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+			 0x0036,
 			 PCI_VENDOR_ID_LENOVO,
 			 0x3026),
 	  .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
@@ -913,9 +998,12 @@
 	struct pci_dev *pdev = to_pci_dev(device);
 	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
 	struct ath_softc *sc = hw->priv;
+	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 
-	if (sc->wow_enabled)
+	if (test_bit(ATH_OP_WOW_ENABLED, &common->op_flags)) {
+		dev_info(&pdev->dev, "WOW is enabled, bypassing PCI suspend\n");
 		return 0;
+	}
 
 	/* The device has to be moved to FULLSLEEP forcibly.
 	 * Otherwise the chip never moved to full sleep,
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 04da61b..8708af1 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -425,7 +425,8 @@
 		rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
 	}
 
-	if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah))
+	if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah) ||
+	    AR_SREV_9561(sc->sc_ah))
 		rfilt |= ATH9K_RX_FILTER_4ADDRESS;
 
 	if (ath9k_is_chanctx_enabled() &&
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index 7743b38..f3d91f0 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -814,6 +814,7 @@
 #define AR_SREV_REVISION_9531_10        0
 #define AR_SREV_REVISION_9531_11        1
 #define AR_SREV_REVISION_9531_20        2
+#define AR_SREV_VERSION_9561            0x600
 
 #define AR_SREV_5416(_ah) \
 	(((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \
@@ -899,10 +900,13 @@
 	(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485))
 #define AR_SREV_9565(_ah) \
 	(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565))
+#define AR_SREV_9003_PCOEM(_ah) \
+	(AR_SREV_9462(_ah) || AR_SREV_9485(_ah) || AR_SREV_9565(_ah))
 #else
 #define AR_SREV_9462(_ah) 0
 #define AR_SREV_9485(_ah) 0
 #define AR_SREV_9565(_ah) 0
+#define AR_SREV_9003_PCOEM(_ah) 0
 #endif
 
 #define AR_SREV_9485_11_OR_LATER(_ah) \
@@ -954,6 +958,8 @@
 
 #define AR_SREV_9550(_ah) \
 	(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9550))
+#define AR_SREV_9550_OR_LATER(_ah) \
+	(((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9550))
 
 #define AR_SREV_9580(_ah) \
 	(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9580) && \
@@ -974,6 +980,9 @@
 	(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \
 	 ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_20))
 
+#define AR_SREV_9561(_ah) \
+	(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9561))
+
 /* NOTE: When adding chips newer than Peacock, add chip check here */
 #define AR_SREV_9580_10_OR_LATER(_ah) \
 	(AR_SREV_9580(_ah))
@@ -1121,6 +1130,8 @@
 
 #define AR_GPIO_OE_OUT                           (AR_SREV_9340(ah) ? 0x4030 : \
 						  (AR_SREV_9300_20_OR_LATER(ah) ? 0x4050 : 0x404c))
+#define AR_GPIO_OE_OUT_MASK			 (AR_SREV_9550_OR_LATER(ah) ? \
+						  0x0000000F : 0xFFFFFFFF)
 #define AR_GPIO_OE_OUT_DRV                       0x3
 #define AR_GPIO_OE_OUT_DRV_NO                    0x0
 #define AR_GPIO_OE_OUT_DRV_LOW                   0x1
@@ -1876,6 +1887,7 @@
 #define AR_FIRST_NDP_TIMER                  7
 #define AR_NDP2_PERIOD                      0x81a0
 #define AR_NDP2_TIMER_MODE                  0x81c0
+#define AR_GEN_TIMERS2_MODE_ENABLE_MASK     0x000000FF
 
 #define AR_GEN_TIMERS(_i)                   (0x8200 + ((_i) << 2))
 #define AR_NEXT_TBTT_TIMER                  AR_GEN_TIMERS(0)
@@ -1971,6 +1983,7 @@
 
 #define AR_DIRECT_CONNECT                              0x83a0
 #define AR_DC_AP_STA_EN                                0x00000001
+#define AR_DC_TSF2_ENABLE                              0x00000001
 
 #define AR_AES_MUTE_MASK0       0x805c
 #define AR_AES_MUTE_MASK0_FC    0x0000FFFF
@@ -2003,126 +2016,6 @@
 
 #define AR_WOW_BEACON_TIMO_MAX		0xffffffff
 
-/*
- * MAC WoW Registers
- */
-
-#define AR_WOW_PATTERN			0x825C
-#define AR_WOW_COUNT			0x8260
-#define AR_WOW_BCN_EN			0x8270
-#define AR_WOW_BCN_TIMO			0x8274
-#define AR_WOW_KEEP_ALIVE_TIMO		0x8278
-#define AR_WOW_KEEP_ALIVE		0x827c
-#define AR_WOW_US_SCALAR		0x8284
-#define AR_WOW_KEEP_ALIVE_DELAY		0x8288
-#define AR_WOW_PATTERN_MATCH		0x828c
-#define AR_WOW_PATTERN_OFF1		0x8290	/* pattern bytes 0 -> 3 */
-#define AR_WOW_PATTERN_OFF2		0x8294	/* pattern bytes 4 -> 7 */
-
-/* for AR9285 or later version of chips */
-#define AR_WOW_EXACT			0x829c
-#define AR_WOW_LENGTH1			0x8360
-#define AR_WOW_LENGTH2			0X8364
-/* register to enable match for less than 256 bytes packets */
-#define AR_WOW_PATTERN_MATCH_LT_256B	0x8368
-
-#define AR_SW_WOW_CONTROL		0x20018
-#define AR_SW_WOW_ENABLE		0x1
-#define AR_SWITCH_TO_REFCLK		0x2
-#define AR_RESET_CONTROL		0x4
-#define AR_RESET_VALUE_MASK		0x8
-#define AR_HW_WOW_DISABLE		0x10
-#define AR_CLR_MAC_INTERRUPT		0x20
-#define AR_CLR_KA_INTERRUPT		0x40
-
-/* AR_WOW_PATTERN register values */
-#define AR_WOW_BACK_OFF_SHIFT(x)	((x & 0xf) << 28) /* in usecs */
-#define AR_WOW_MAC_INTR_EN		0x00040000
-#define AR_WOW_MAGIC_EN			0x00010000
-#define AR_WOW_PATTERN_EN(x)		(x & 0xff)
-#define AR_WOW_PAT_FOUND_SHIFT	8
-#define AR_WOW_PATTERN_FOUND(x)		(x & (0xff << AR_WOW_PAT_FOUND_SHIFT))
-#define AR_WOW_PATTERN_FOUND_MASK	((0xff) << AR_WOW_PAT_FOUND_SHIFT)
-#define AR_WOW_MAGIC_PAT_FOUND		0x00020000
-#define AR_WOW_MAC_INTR			0x00080000
-#define AR_WOW_KEEP_ALIVE_FAIL		0x00100000
-#define AR_WOW_BEACON_FAIL		0x00200000
-
-#define AR_WOW_STATUS(x)		(x & (AR_WOW_PATTERN_FOUND_MASK | \
-					      AR_WOW_MAGIC_PAT_FOUND	| \
-					      AR_WOW_KEEP_ALIVE_FAIL	| \
-					      AR_WOW_BEACON_FAIL))
-#define AR_WOW_CLEAR_EVENTS(x)		(x & ~(AR_WOW_PATTERN_EN(0xff) | \
-					       AR_WOW_MAGIC_EN | \
-					       AR_WOW_MAC_INTR_EN | \
-					       AR_WOW_BEACON_FAIL | \
-					       AR_WOW_KEEP_ALIVE_FAIL))
-
-/* AR_WOW_COUNT register values */
-#define AR_WOW_AIFS_CNT(x)		(x & 0xff)
-#define AR_WOW_SLOT_CNT(x)		((x & 0xff) << 8)
-#define AR_WOW_KEEP_ALIVE_CNT(x)	((x & 0xff) << 16)
-
-/* AR_WOW_BCN_EN register */
-#define AR_WOW_BEACON_FAIL_EN		0x00000001
-
-/* AR_WOW_BCN_TIMO rgister */
-#define AR_WOW_BEACON_TIMO		0x40000000 /* valid if BCN_EN is set */
-
-/* AR_WOW_KEEP_ALIVE_TIMO register */
-#define AR_WOW_KEEP_ALIVE_TIMO_VALUE
-#define AR_WOW_KEEP_ALIVE_NEVER		0xffffffff
-
-/* AR_WOW_KEEP_ALIVE register  */
-#define AR_WOW_KEEP_ALIVE_AUTO_DIS	0x00000001
-#define AR_WOW_KEEP_ALIVE_FAIL_DIS	0x00000002
-
-/* AR_WOW_KEEP_ALIVE_DELAY register */
-#define AR_WOW_KEEP_ALIVE_DELAY_VALUE	0x000003e8 /* 1 msec */
-
-
-/*
- * keep it long for beacon workaround - ensure no false alarm
- */
-#define AR_WOW_BMISSTHRESHOLD		0x20
-
-/* AR_WOW_PATTERN_MATCH register */
-#define AR_WOW_PAT_END_OF_PKT(x)	(x & 0xf)
-#define AR_WOW_PAT_OFF_MATCH(x)		((x & 0xf) << 8)
-
-/*
- * default values for Wow Configuration for backoff, aifs, slot, keep-alive
- * to be programmed into various registers.
- */
-#define AR_WOW_PAT_BACKOFF	0x00000004 /* AR_WOW_PATTERN_REG */
-#define AR_WOW_CNT_AIFS_CNT	0x00000022 /* AR_WOW_COUNT_REG */
-#define AR_WOW_CNT_SLOT_CNT	0x00000009 /* AR_WOW_COUNT_REG */
-/*
- * Keepalive count applicable for AR9280 2.0 and above.
- */
-#define AR_WOW_CNT_KA_CNT 0x00000008    /* AR_WOW_COUNT register */
-
-/* WoW - Transmit buffer for keep alive frames */
-#define AR_WOW_TRANSMIT_BUFFER	0xe000 /* E000 - EFFC */
-
-#define AR_WOW_TXBUF(i)		(AR_WOW_TRANSMIT_BUFFER + ((i) << 2))
-
-#define AR_WOW_KA_DESC_WORD2	0xe000
-
-#define AR_WOW_KA_DATA_WORD0	0xe030
-
-/* WoW Transmit Buffer for patterns */
-#define AR_WOW_TB_PATTERN(i)	(0xe100 + (i << 8))
-#define AR_WOW_TB_MASK(i)	(0xec00 + (i << 5))
-
-/* Currently Pattern 0-7 are supported - so bit 0-7 are set */
-#define AR_WOW_PATTERN_SUPPORTED	0xff
-#define AR_WOW_LENGTH_MAX		0xff
-#define AR_WOW_LEN1_SHIFT(_i)	((0x3 - ((_i) & 0x3)) << 0x3)
-#define AR_WOW_LENGTH1_MASK(_i)	(AR_WOW_LENGTH_MAX << AR_WOW_LEN1_SHIFT(_i))
-#define AR_WOW_LEN2_SHIFT(_i)	((0x7 - ((_i) & 0x7)) << 0x3)
-#define AR_WOW_LENGTH2_MASK(_i)	(AR_WOW_LENGTH_MAX << AR_WOW_LEN2_SHIFT(_i))
-
 #define AR9271_CORE_CLOCK	117   /* clock to 117Mhz */
 #define AR9271_TARGET_BAUD_RATE	19200 /* 115200 */
 
@@ -2155,279 +2048,4 @@
 #define AR_PHY_AGC_CONTROL_YCOK_MAX		0x000003c0
 #define AR_PHY_AGC_CONTROL_YCOK_MAX_S		6
 
-/* MCI Registers */
-
-#define AR_MCI_COMMAND0				0x1800
-#define AR_MCI_COMMAND0_HEADER			0xFF
-#define AR_MCI_COMMAND0_HEADER_S		0
-#define AR_MCI_COMMAND0_LEN			0x1f00
-#define AR_MCI_COMMAND0_LEN_S			8
-#define AR_MCI_COMMAND0_DISABLE_TIMESTAMP	0x2000
-#define AR_MCI_COMMAND0_DISABLE_TIMESTAMP_S	13
-
-#define AR_MCI_COMMAND1				0x1804
-
-#define AR_MCI_COMMAND2				0x1808
-#define AR_MCI_COMMAND2_RESET_TX		0x01
-#define AR_MCI_COMMAND2_RESET_TX_S		0
-#define AR_MCI_COMMAND2_RESET_RX		0x02
-#define AR_MCI_COMMAND2_RESET_RX_S		1
-#define AR_MCI_COMMAND2_RESET_RX_NUM_CYCLES     0x3FC
-#define AR_MCI_COMMAND2_RESET_RX_NUM_CYCLES_S   2
-#define AR_MCI_COMMAND2_RESET_REQ_WAKEUP        0x400
-#define AR_MCI_COMMAND2_RESET_REQ_WAKEUP_S      10
-
-#define AR_MCI_RX_CTRL				0x180c
-
-#define AR_MCI_TX_CTRL				0x1810
-/* 0 = no division, 1 = divide by 2, 2 = divide by 4, 3 = divide by 8 */
-#define AR_MCI_TX_CTRL_CLK_DIV			0x03
-#define AR_MCI_TX_CTRL_CLK_DIV_S		0
-#define AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE	0x04
-#define AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE_S	2
-#define AR_MCI_TX_CTRL_GAIN_UPDATE_FREQ		0xFFFFF8
-#define AR_MCI_TX_CTRL_GAIN_UPDATE_FREQ_S	3
-#define AR_MCI_TX_CTRL_GAIN_UPDATE_NUM		0xF000000
-#define AR_MCI_TX_CTRL_GAIN_UPDATE_NUM_S	24
-
-#define AR_MCI_MSG_ATTRIBUTES_TABLE			0x1814
-#define AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM		0xFFFF
-#define AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM_S		0
-#define AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR		0xFFFF0000
-#define AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR_S	16
-
-#define AR_MCI_SCHD_TABLE_0				0x1818
-#define AR_MCI_SCHD_TABLE_1				0x181c
-#define AR_MCI_GPM_0					0x1820
-#define AR_MCI_GPM_1					0x1824
-#define AR_MCI_GPM_WRITE_PTR				0xFFFF0000
-#define AR_MCI_GPM_WRITE_PTR_S				16
-#define AR_MCI_GPM_BUF_LEN				0x0000FFFF
-#define AR_MCI_GPM_BUF_LEN_S				0
-
-#define AR_MCI_INTERRUPT_RAW				0x1828
-#define AR_MCI_INTERRUPT_EN				0x182c
-#define AR_MCI_INTERRUPT_SW_MSG_DONE			0x00000001
-#define AR_MCI_INTERRUPT_SW_MSG_DONE_S			0
-#define AR_MCI_INTERRUPT_CPU_INT_MSG			0x00000002
-#define AR_MCI_INTERRUPT_CPU_INT_MSG_S			1
-#define AR_MCI_INTERRUPT_RX_CKSUM_FAIL			0x00000004
-#define AR_MCI_INTERRUPT_RX_CKSUM_FAIL_S		2
-#define AR_MCI_INTERRUPT_RX_INVALID_HDR			0x00000008
-#define AR_MCI_INTERRUPT_RX_INVALID_HDR_S		3
-#define AR_MCI_INTERRUPT_RX_HW_MSG_FAIL			0x00000010
-#define AR_MCI_INTERRUPT_RX_HW_MSG_FAIL_S		4
-#define AR_MCI_INTERRUPT_RX_SW_MSG_FAIL			0x00000020
-#define AR_MCI_INTERRUPT_RX_SW_MSG_FAIL_S		5
-#define AR_MCI_INTERRUPT_TX_HW_MSG_FAIL			0x00000080
-#define AR_MCI_INTERRUPT_TX_HW_MSG_FAIL_S		7
-#define AR_MCI_INTERRUPT_TX_SW_MSG_FAIL			0x00000100
-#define AR_MCI_INTERRUPT_TX_SW_MSG_FAIL_S		8
-#define AR_MCI_INTERRUPT_RX_MSG				0x00000200
-#define AR_MCI_INTERRUPT_RX_MSG_S			9
-#define AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE		0x00000400
-#define AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE_S		10
-#define AR_MCI_INTERRUPT_BT_PRI				0x07fff800
-#define AR_MCI_INTERRUPT_BT_PRI_S			11
-#define AR_MCI_INTERRUPT_BT_PRI_THRESH			0x08000000
-#define AR_MCI_INTERRUPT_BT_PRI_THRESH_S		27
-#define AR_MCI_INTERRUPT_BT_FREQ			0x10000000
-#define AR_MCI_INTERRUPT_BT_FREQ_S			28
-#define AR_MCI_INTERRUPT_BT_STOMP			0x20000000
-#define AR_MCI_INTERRUPT_BT_STOMP_S			29
-#define AR_MCI_INTERRUPT_BB_AIC_IRQ			0x40000000
-#define AR_MCI_INTERRUPT_BB_AIC_IRQ_S			30
-#define AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT		0x80000000
-#define AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT_S		31
-
-#define AR_MCI_INTERRUPT_DEFAULT    (AR_MCI_INTERRUPT_SW_MSG_DONE	  | \
-				     AR_MCI_INTERRUPT_RX_INVALID_HDR	  | \
-				     AR_MCI_INTERRUPT_RX_HW_MSG_FAIL	  | \
-				     AR_MCI_INTERRUPT_RX_SW_MSG_FAIL	  | \
-				     AR_MCI_INTERRUPT_TX_HW_MSG_FAIL	  | \
-				     AR_MCI_INTERRUPT_TX_SW_MSG_FAIL	  | \
-				     AR_MCI_INTERRUPT_RX_MSG		  | \
-				     AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE | \
-				     AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)
-
-#define AR_MCI_INTERRUPT_MSG_FAIL_MASK (AR_MCI_INTERRUPT_RX_HW_MSG_FAIL | \
-					AR_MCI_INTERRUPT_RX_SW_MSG_FAIL | \
-					AR_MCI_INTERRUPT_TX_HW_MSG_FAIL | \
-					AR_MCI_INTERRUPT_TX_SW_MSG_FAIL)
-
-#define AR_MCI_REMOTE_CPU_INT				0x1830
-#define AR_MCI_REMOTE_CPU_INT_EN			0x1834
-#define AR_MCI_INTERRUPT_RX_MSG_RAW			0x1838
-#define AR_MCI_INTERRUPT_RX_MSG_EN			0x183c
-#define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET		0x00000001
-#define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET_S		0
-#define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL		0x00000002
-#define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL_S		1
-#define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK		0x00000004
-#define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK_S		2
-#define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO		0x00000008
-#define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO_S		3
-#define AR_MCI_INTERRUPT_RX_MSG_CONT_RST		0x00000010
-#define AR_MCI_INTERRUPT_RX_MSG_CONT_RST_S		4
-#define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO		0x00000020
-#define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO_S		5
-#define AR_MCI_INTERRUPT_RX_MSG_CPU_INT			0x00000040
-#define AR_MCI_INTERRUPT_RX_MSG_CPU_INT_S		6
-#define AR_MCI_INTERRUPT_RX_MSG_GPM			0x00000100
-#define AR_MCI_INTERRUPT_RX_MSG_GPM_S			8
-#define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO		0x00000200
-#define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO_S		9
-#define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING		0x00000400
-#define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING_S		10
-#define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING		0x00000800
-#define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING_S		11
-#define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE		0x00001000
-#define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE_S		12
-#define AR_MCI_INTERRUPT_RX_HW_MSG_MASK	 (AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO  | \
-					  AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL| \
-					  AR_MCI_INTERRUPT_RX_MSG_LNA_INFO   | \
-					  AR_MCI_INTERRUPT_RX_MSG_CONT_NACK  | \
-					  AR_MCI_INTERRUPT_RX_MSG_CONT_INFO  | \
-					  AR_MCI_INTERRUPT_RX_MSG_CONT_RST)
-
-#define AR_MCI_INTERRUPT_RX_MSG_DEFAULT (AR_MCI_INTERRUPT_RX_MSG_GPM	 | \
-					 AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET| \
-					 AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING  | \
-					 AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING| \
-					 AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)
-
-#define AR_MCI_CPU_INT					0x1840
-
-#define AR_MCI_RX_STATUS			0x1844
-#define AR_MCI_RX_LAST_SCHD_MSG_INDEX		0x00000F00
-#define AR_MCI_RX_LAST_SCHD_MSG_INDEX_S		8
-#define AR_MCI_RX_REMOTE_SLEEP			0x00001000
-#define AR_MCI_RX_REMOTE_SLEEP_S		12
-#define AR_MCI_RX_MCI_CLK_REQ			0x00002000
-#define AR_MCI_RX_MCI_CLK_REQ_S			13
-
-#define AR_MCI_CONT_STATUS			0x1848
-#define AR_MCI_CONT_RSSI_POWER			0x000000FF
-#define AR_MCI_CONT_RSSI_POWER_S		0
-#define AR_MCI_CONT_PRIORITY			0x0000FF00
-#define AR_MCI_CONT_PRIORITY_S			8
-#define AR_MCI_CONT_TXRX			0x00010000
-#define AR_MCI_CONT_TXRX_S			16
-
-#define AR_MCI_BT_PRI0				0x184c
-#define AR_MCI_BT_PRI1				0x1850
-#define AR_MCI_BT_PRI2				0x1854
-#define AR_MCI_BT_PRI3				0x1858
-#define AR_MCI_BT_PRI				0x185c
-#define AR_MCI_WL_FREQ0				0x1860
-#define AR_MCI_WL_FREQ1				0x1864
-#define AR_MCI_WL_FREQ2				0x1868
-#define AR_MCI_GAIN				0x186c
-#define AR_MCI_WBTIMER1				0x1870
-#define AR_MCI_WBTIMER2				0x1874
-#define AR_MCI_WBTIMER3				0x1878
-#define AR_MCI_WBTIMER4				0x187c
-#define AR_MCI_MAXGAIN				0x1880
-#define AR_MCI_HW_SCHD_TBL_CTL			0x1884
-#define AR_MCI_HW_SCHD_TBL_D0			0x1888
-#define AR_MCI_HW_SCHD_TBL_D1			0x188c
-#define AR_MCI_HW_SCHD_TBL_D2			0x1890
-#define AR_MCI_HW_SCHD_TBL_D3			0x1894
-#define AR_MCI_TX_PAYLOAD0			0x1898
-#define AR_MCI_TX_PAYLOAD1			0x189c
-#define AR_MCI_TX_PAYLOAD2			0x18a0
-#define AR_MCI_TX_PAYLOAD3			0x18a4
-#define AR_BTCOEX_WBTIMER			0x18a8
-
-#define AR_BTCOEX_CTRL					0x18ac
-#define AR_BTCOEX_CTRL_AR9462_MODE			0x00000001
-#define AR_BTCOEX_CTRL_AR9462_MODE_S			0
-#define AR_BTCOEX_CTRL_WBTIMER_EN			0x00000002
-#define AR_BTCOEX_CTRL_WBTIMER_EN_S			1
-#define AR_BTCOEX_CTRL_MCI_MODE_EN			0x00000004
-#define AR_BTCOEX_CTRL_MCI_MODE_EN_S			2
-#define AR_BTCOEX_CTRL_LNA_SHARED			0x00000008
-#define AR_BTCOEX_CTRL_LNA_SHARED_S			3
-#define AR_BTCOEX_CTRL_PA_SHARED			0x00000010
-#define AR_BTCOEX_CTRL_PA_SHARED_S			4
-#define AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN		0x00000020
-#define AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN_S		5
-#define AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN	0x00000040
-#define AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN_S	6
-#define AR_BTCOEX_CTRL_NUM_ANTENNAS			0x00000180
-#define AR_BTCOEX_CTRL_NUM_ANTENNAS_S			7
-#define AR_BTCOEX_CTRL_RX_CHAIN_MASK			0x00000E00
-#define AR_BTCOEX_CTRL_RX_CHAIN_MASK_S			9
-#define AR_BTCOEX_CTRL_AGGR_THRESH			0x00007000
-#define AR_BTCOEX_CTRL_AGGR_THRESH_S			12
-#define AR_BTCOEX_CTRL_1_CHAIN_BCN			0x00080000
-#define AR_BTCOEX_CTRL_1_CHAIN_BCN_S			19
-#define AR_BTCOEX_CTRL_1_CHAIN_ACK			0x00100000
-#define AR_BTCOEX_CTRL_1_CHAIN_ACK_S			20
-#define AR_BTCOEX_CTRL_WAIT_BA_MARGIN			0x1FE00000
-#define AR_BTCOEX_CTRL_WAIT_BA_MARGIN_S			28
-#define AR_BTCOEX_CTRL_REDUCE_TXPWR			0x20000000
-#define AR_BTCOEX_CTRL_REDUCE_TXPWR_S			29
-#define AR_BTCOEX_CTRL_SPDT_ENABLE_10			0x40000000
-#define AR_BTCOEX_CTRL_SPDT_ENABLE_10_S			30
-#define AR_BTCOEX_CTRL_SPDT_POLARITY			0x80000000
-#define AR_BTCOEX_CTRL_SPDT_POLARITY_S			31
-
-#define AR_BTCOEX_MAX_TXPWR(_x)				(0x18c0 + ((_x) << 2))
-#define AR_BTCOEX_WL_LNA				0x1940
-#define AR_BTCOEX_RFGAIN_CTRL				0x1944
-#define AR_BTCOEX_WL_LNA_TIMEOUT			0x003FFFFF
-#define AR_BTCOEX_WL_LNA_TIMEOUT_S			0
-
-#define AR_BTCOEX_CTRL2					0x1948
-#define AR_BTCOEX_CTRL2_TXPWR_THRESH			0x0007F800
-#define AR_BTCOEX_CTRL2_TXPWR_THRESH_S			11
-#define AR_BTCOEX_CTRL2_TX_CHAIN_MASK			0x00380000
-#define AR_BTCOEX_CTRL2_TX_CHAIN_MASK_S			19
-#define AR_BTCOEX_CTRL2_RX_DEWEIGHT			0x00400000
-#define AR_BTCOEX_CTRL2_RX_DEWEIGHT_S			22
-#define AR_BTCOEX_CTRL2_GPIO_OBS_SEL			0x00800000
-#define AR_BTCOEX_CTRL2_GPIO_OBS_SEL_S			23
-#define AR_BTCOEX_CTRL2_MAC_BB_OBS_SEL			0x01000000
-#define AR_BTCOEX_CTRL2_MAC_BB_OBS_SEL_S		24
-#define AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE		0x02000000
-#define AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE_S	25
-
-#define AR_BTCOEX_CTRL_SPDT_ENABLE          0x00000001
-#define AR_BTCOEX_CTRL_SPDT_ENABLE_S        0
-#define AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL     0x00000002
-#define AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL_S   1
-#define AR_BTCOEX_CTRL_USE_LATCHED_BT_ANT   0x00000004
-#define AR_BTCOEX_CTRL_USE_LATCHED_BT_ANT_S 2
-#define AR_GLB_WLAN_UART_INTF_EN            0x00020000
-#define AR_GLB_WLAN_UART_INTF_EN_S          17
-#define AR_GLB_DS_JTAG_DISABLE              0x00040000
-#define AR_GLB_DS_JTAG_DISABLE_S            18
-
-#define AR_BTCOEX_RC                    0x194c
-#define AR_BTCOEX_MAX_RFGAIN(_x)        (0x1950 + ((_x) << 2))
-#define AR_BTCOEX_DBG                   0x1a50
-#define AR_MCI_LAST_HW_MSG_HDR          0x1a54
-#define AR_MCI_LAST_HW_MSG_BDY          0x1a58
-
-#define AR_MCI_SCHD_TABLE_2             0x1a5c
-#define AR_MCI_SCHD_TABLE_2_MEM_BASED   0x00000001
-#define AR_MCI_SCHD_TABLE_2_MEM_BASED_S 0
-#define AR_MCI_SCHD_TABLE_2_HW_BASED    0x00000002
-#define AR_MCI_SCHD_TABLE_2_HW_BASED_S  1
-
-#define AR_BTCOEX_CTRL3               0x1a60
-#define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT	0x00000fff
-#define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT_S	0
-
-#define AR_GLB_SWREG_DISCONT_MODE         0x2002c
-#define AR_GLB_SWREG_DISCONT_EN_BT_WLAN   0x3
-
-#define AR_MCI_MISC                    0x1a74
-#define AR_MCI_MISC_HW_FIX_EN          0x00000001
-#define AR_MCI_MISC_HW_FIX_EN_S        0
-#define AR_MCI_DBG_CNT_CTRL            0x1a78
-#define AR_MCI_DBG_CNT_CTRL_ENABLE     0x00000001
-#define AR_MCI_DBG_CNT_CTRL_ENABLE_S   0
-
 #endif
diff --git a/drivers/net/wireless/ath/ath9k/reg_aic.h b/drivers/net/wireless/ath/ath9k/reg_aic.h
new file mode 100644
index 0000000..955147a
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/reg_aic.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2015 Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef REG_AIC_H
+#define REG_AIC_H
+
+#define AR_SM_BASE                              0xa200
+#define AR_SM1_BASE                             0xb200
+#define AR_AGC_BASE                             0x9e00
+
+#define AR_PHY_AIC_CTRL_0_B0                    (AR_SM_BASE + 0x4b0)
+#define AR_PHY_AIC_CTRL_1_B0                    (AR_SM_BASE + 0x4b4)
+#define AR_PHY_AIC_CTRL_2_B0                    (AR_SM_BASE + 0x4b8)
+#define AR_PHY_AIC_CTRL_3_B0                    (AR_SM_BASE + 0x4bc)
+#define AR_PHY_AIC_CTRL_4_B0                    (AR_SM_BASE + 0x4c0)
+
+#define AR_PHY_AIC_STAT_0_B0                    (AR_SM_BASE + 0x4c4)
+#define AR_PHY_AIC_STAT_1_B0                    (AR_SM_BASE + 0x4c8)
+#define AR_PHY_AIC_STAT_2_B0                    (AR_SM_BASE + 0x4cc)
+
+#define AR_PHY_AIC_CTRL_0_B1                    (AR_SM1_BASE + 0x4b0)
+#define AR_PHY_AIC_CTRL_1_B1                    (AR_SM1_BASE + 0x4b4)
+#define AR_PHY_AIC_CTRL_4_B1                    (AR_SM1_BASE + 0x4c0)
+
+#define AR_PHY_AIC_STAT_0_B1                    (AR_SM1_BASE + 0x4c4)
+#define AR_PHY_AIC_STAT_1_B1                    (AR_SM1_BASE + 0x4c8)
+#define AR_PHY_AIC_STAT_2_B1                    (AR_SM1_BASE + 0x4cc)
+
+#define AR_PHY_AIC_SRAM_ADDR_B0                 (AR_SM_BASE + 0x5f0)
+#define AR_PHY_AIC_SRAM_DATA_B0                 (AR_SM_BASE + 0x5f4)
+
+#define AR_PHY_AIC_SRAM_ADDR_B1                 (AR_SM1_BASE + 0x5f0)
+#define AR_PHY_AIC_SRAM_DATA_B1                 (AR_SM1_BASE + 0x5f4)
+
+#define AR_PHY_BT_COEX_4                        (AR_AGC_BASE + 0x60)
+#define AR_PHY_BT_COEX_5                        (AR_AGC_BASE + 0x64)
+
+/* AIC fields */
+#define AR_PHY_AIC_MON_ENABLE                   0x80000000
+#define AR_PHY_AIC_MON_ENABLE_S                 31
+#define AR_PHY_AIC_CAL_MAX_HOP_COUNT            0x7F000000
+#define AR_PHY_AIC_CAL_MAX_HOP_COUNT_S          24
+#define AR_PHY_AIC_CAL_MIN_VALID_COUNT          0x00FE0000
+#define AR_PHY_AIC_CAL_MIN_VALID_COUNT_S        17
+#define AR_PHY_AIC_F_WLAN                       0x0001FC00
+#define AR_PHY_AIC_F_WLAN_S                     10
+#define AR_PHY_AIC_CAL_CH_VALID_RESET           0x00000200
+#define AR_PHY_AIC_CAL_CH_VALID_RESET_S         9
+#define AR_PHY_AIC_CAL_ENABLE                   0x00000100
+#define AR_PHY_AIC_CAL_ENABLE_S                 8
+#define AR_PHY_AIC_BTTX_PWR_THR                 0x000000FE
+#define AR_PHY_AIC_BTTX_PWR_THR_S               1
+#define AR_PHY_AIC_ENABLE                       0x00000001
+#define AR_PHY_AIC_ENABLE_S                     0
+#define AR_PHY_AIC_CAL_BT_REF_DELAY             0x00F00000
+#define AR_PHY_AIC_CAL_BT_REF_DELAY_S           20
+#define AR_PHY_AIC_BT_IDLE_CFG                  0x00080000
+#define AR_PHY_AIC_BT_IDLE_CFG_S                19
+#define AR_PHY_AIC_STDBY_COND                   0x00060000
+#define AR_PHY_AIC_STDBY_COND_S                 17
+#define AR_PHY_AIC_STDBY_ROT_ATT_DB             0x0001F800
+#define AR_PHY_AIC_STDBY_ROT_ATT_DB_S           11
+#define AR_PHY_AIC_STDBY_COM_ATT_DB             0x00000700
+#define AR_PHY_AIC_STDBY_COM_ATT_DB_S           8
+#define AR_PHY_AIC_RSSI_MAX                     0x000000F0
+#define AR_PHY_AIC_RSSI_MAX_S                   4
+#define AR_PHY_AIC_RSSI_MIN                     0x0000000F
+#define AR_PHY_AIC_RSSI_MIN_S                   0
+#define AR_PHY_AIC_RADIO_DELAY                  0x7F000000
+#define AR_PHY_AIC_RADIO_DELAY_S                24
+#define AR_PHY_AIC_CAL_STEP_SIZE_CORR           0x00F00000
+#define AR_PHY_AIC_CAL_STEP_SIZE_CORR_S         20
+#define AR_PHY_AIC_CAL_ROT_IDX_CORR             0x000F8000
+#define AR_PHY_AIC_CAL_ROT_IDX_CORR_S           15
+#define AR_PHY_AIC_CAL_CONV_CHECK_FACTOR        0x00006000
+#define AR_PHY_AIC_CAL_CONV_CHECK_FACTOR_S      13
+#define AR_PHY_AIC_ROT_IDX_COUNT_MAX            0x00001C00
+#define AR_PHY_AIC_ROT_IDX_COUNT_MAX_S          10
+#define AR_PHY_AIC_CAL_SYNTH_TOGGLE             0x00000200
+#define AR_PHY_AIC_CAL_SYNTH_TOGGLE_S           9
+#define AR_PHY_AIC_CAL_SYNTH_AFTER_BTRX         0x00000100
+#define AR_PHY_AIC_CAL_SYNTH_AFTER_BTRX_S       8
+#define AR_PHY_AIC_CAL_SYNTH_SETTLING           0x000000FF
+#define AR_PHY_AIC_CAL_SYNTH_SETTLING_S         0
+#define AR_PHY_AIC_MON_MAX_HOP_COUNT            0x07F00000
+#define AR_PHY_AIC_MON_MAX_HOP_COUNT_S          20
+#define AR_PHY_AIC_MON_MIN_STALE_COUNT          0x000FE000
+#define AR_PHY_AIC_MON_MIN_STALE_COUNT_S        13
+#define AR_PHY_AIC_MON_PWR_EST_LONG             0x00001000
+#define AR_PHY_AIC_MON_PWR_EST_LONG_S           12
+#define AR_PHY_AIC_MON_PD_TALLY_SCALING         0x00000C00
+#define AR_PHY_AIC_MON_PD_TALLY_SCALING_S       10
+#define AR_PHY_AIC_MON_PERF_THR                 0x000003E0
+#define AR_PHY_AIC_MON_PERF_THR_S               5
+#define AR_PHY_AIC_CAL_TARGET_MAG_SETTING       0x00000018
+#define AR_PHY_AIC_CAL_TARGET_MAG_SETTING_S     3
+#define AR_PHY_AIC_CAL_PERF_CHECK_FACTOR        0x00000006
+#define AR_PHY_AIC_CAL_PERF_CHECK_FACTOR_S      1
+#define AR_PHY_AIC_CAL_PWR_EST_LONG             0x00000001
+#define AR_PHY_AIC_CAL_PWR_EST_LONG_S           0
+#define AR_PHY_AIC_MON_DONE                     0x80000000
+#define AR_PHY_AIC_MON_DONE_S                   31
+#define AR_PHY_AIC_MON_ACTIVE                   0x40000000
+#define AR_PHY_AIC_MON_ACTIVE_S                 30
+#define AR_PHY_AIC_MEAS_COUNT                   0x3F000000
+#define AR_PHY_AIC_MEAS_COUNT_S                 24
+#define AR_PHY_AIC_CAL_ANT_ISO_EST              0x00FC0000
+#define AR_PHY_AIC_CAL_ANT_ISO_EST_S            18
+#define AR_PHY_AIC_CAL_HOP_COUNT                0x0003F800
+#define AR_PHY_AIC_CAL_HOP_COUNT_S              11
+#define AR_PHY_AIC_CAL_VALID_COUNT              0x000007F0
+#define AR_PHY_AIC_CAL_VALID_COUNT_S            4
+#define AR_PHY_AIC_CAL_BT_TOO_WEAK_ERR          0x00000008
+#define AR_PHY_AIC_CAL_BT_TOO_WEAK_ERR_S        3
+#define AR_PHY_AIC_CAL_BT_TOO_STRONG_ERR        0x00000004
+#define AR_PHY_AIC_CAL_BT_TOO_STRONG_ERR_S      2
+#define AR_PHY_AIC_CAL_DONE                     0x00000002
+#define AR_PHY_AIC_CAL_DONE_S                   1
+#define AR_PHY_AIC_CAL_ACTIVE                   0x00000001
+#define AR_PHY_AIC_CAL_ACTIVE_S                 0
+
+#define AR_PHY_AIC_MEAS_MAG_MIN                 0xFFC00000
+#define AR_PHY_AIC_MEAS_MAG_MIN_S               22
+#define AR_PHY_AIC_MON_STALE_COUNT              0x003F8000
+#define AR_PHY_AIC_MON_STALE_COUNT_S            15
+#define AR_PHY_AIC_MON_HOP_COUNT                0x00007F00
+#define AR_PHY_AIC_MON_HOP_COUNT_S              8
+#define AR_PHY_AIC_CAL_AIC_SM                   0x000000F8
+#define AR_PHY_AIC_CAL_AIC_SM_S                 3
+#define AR_PHY_AIC_SM                           0x00000007
+#define AR_PHY_AIC_SM_S                         0
+#define AR_PHY_AIC_SRAM_VALID                   0x00000001
+#define AR_PHY_AIC_SRAM_VALID_S                 0
+#define AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB         0x0000007E
+#define AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB_S       1
+#define AR_PHY_AIC_SRAM_VGA_QUAD_SIGN           0x00000080
+#define AR_PHY_AIC_SRAM_VGA_QUAD_SIGN_S         7
+#define AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB          0x00003F00
+#define AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB_S        8
+#define AR_PHY_AIC_SRAM_VGA_DIR_SIGN            0x00004000
+#define AR_PHY_AIC_SRAM_VGA_DIR_SIGN_S          14
+#define AR_PHY_AIC_SRAM_COM_ATT_6DB             0x00038000
+#define AR_PHY_AIC_SRAM_COM_ATT_6DB_S           15
+#define AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO       0x0000E000
+#define AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO_S     13
+#define AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO       0x00001E00
+#define AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO_S     9
+#define AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING     0x000001F8
+#define AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING_S   3
+#define AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF       0x00000006
+#define AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF_S     1
+#define AR_PHY_AIC_CAL_COM_ATT_DB_FIXED         0x00000001
+#define AR_PHY_AIC_CAL_COM_ATT_DB_FIXED_S       0
+
+#endif /* REG_AIC_H */
diff --git a/drivers/net/wireless/ath/ath9k/reg_mci.h b/drivers/net/wireless/ath/ath9k/reg_mci.h
new file mode 100644
index 0000000..6251310
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/reg_mci.h
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2015 Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef REG_MCI_H
+#define REG_MCI_H
+
+#define AR_MCI_COMMAND0                                 0x1800
+#define AR_MCI_COMMAND0_HEADER                          0xFF
+#define AR_MCI_COMMAND0_HEADER_S                        0
+#define AR_MCI_COMMAND0_LEN                             0x1f00
+#define AR_MCI_COMMAND0_LEN_S                           8
+#define AR_MCI_COMMAND0_DISABLE_TIMESTAMP               0x2000
+#define AR_MCI_COMMAND0_DISABLE_TIMESTAMP_S             13
+
+#define AR_MCI_COMMAND1                                 0x1804
+
+#define AR_MCI_COMMAND2                                 0x1808
+#define AR_MCI_COMMAND2_RESET_TX                        0x01
+#define AR_MCI_COMMAND2_RESET_TX_S                      0
+#define AR_MCI_COMMAND2_RESET_RX                        0x02
+#define AR_MCI_COMMAND2_RESET_RX_S                      1
+#define AR_MCI_COMMAND2_RESET_RX_NUM_CYCLES             0x3FC
+#define AR_MCI_COMMAND2_RESET_RX_NUM_CYCLES_S           2
+#define AR_MCI_COMMAND2_RESET_REQ_WAKEUP                0x400
+#define AR_MCI_COMMAND2_RESET_REQ_WAKEUP_S              10
+
+#define AR_MCI_RX_CTRL                                  0x180c
+
+#define AR_MCI_TX_CTRL                                  0x1810
+/*
+ * 0 = no division,
+ * 1 = divide by 2,
+ * 2 = divide by 4,
+ * 3 = divide by 8
+ */
+#define AR_MCI_TX_CTRL_CLK_DIV                          0x03
+#define AR_MCI_TX_CTRL_CLK_DIV_S                        0
+#define AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE               0x04
+#define AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE_S             2
+#define AR_MCI_TX_CTRL_GAIN_UPDATE_FREQ                 0xFFFFF8
+#define AR_MCI_TX_CTRL_GAIN_UPDATE_FREQ_S               3
+#define AR_MCI_TX_CTRL_GAIN_UPDATE_NUM                  0xF000000
+#define AR_MCI_TX_CTRL_GAIN_UPDATE_NUM_S                24
+
+#define AR_MCI_MSG_ATTRIBUTES_TABLE                     0x1814
+#define AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM            0xFFFF
+#define AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM_S          0
+#define AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR         0xFFFF0000
+#define AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR_S       16
+
+#define AR_MCI_SCHD_TABLE_0                             0x1818
+#define AR_MCI_SCHD_TABLE_1                             0x181c
+#define AR_MCI_GPM_0                                    0x1820
+#define AR_MCI_GPM_1                                    0x1824
+#define AR_MCI_GPM_WRITE_PTR                            0xFFFF0000
+#define AR_MCI_GPM_WRITE_PTR_S                          16
+#define AR_MCI_GPM_BUF_LEN                              0x0000FFFF
+#define AR_MCI_GPM_BUF_LEN_S                            0
+
+#define AR_MCI_INTERRUPT_RAW                            0x1828
+
+#define AR_MCI_INTERRUPT_EN                             0x182c
+#define AR_MCI_INTERRUPT_SW_MSG_DONE                    0x00000001
+#define AR_MCI_INTERRUPT_SW_MSG_DONE_S                  0
+#define AR_MCI_INTERRUPT_CPU_INT_MSG                    0x00000002
+#define AR_MCI_INTERRUPT_CPU_INT_MSG_S                  1
+#define AR_MCI_INTERRUPT_RX_CKSUM_FAIL                  0x00000004
+#define AR_MCI_INTERRUPT_RX_CKSUM_FAIL_S                2
+#define AR_MCI_INTERRUPT_RX_INVALID_HDR                 0x00000008
+#define AR_MCI_INTERRUPT_RX_INVALID_HDR_S               3
+#define AR_MCI_INTERRUPT_RX_HW_MSG_FAIL                 0x00000010
+#define AR_MCI_INTERRUPT_RX_HW_MSG_FAIL_S               4
+#define AR_MCI_INTERRUPT_RX_SW_MSG_FAIL                 0x00000020
+#define AR_MCI_INTERRUPT_RX_SW_MSG_FAIL_S               5
+#define AR_MCI_INTERRUPT_TX_HW_MSG_FAIL                 0x00000080
+#define AR_MCI_INTERRUPT_TX_HW_MSG_FAIL_S               7
+#define AR_MCI_INTERRUPT_TX_SW_MSG_FAIL                 0x00000100
+#define AR_MCI_INTERRUPT_TX_SW_MSG_FAIL_S               8
+#define AR_MCI_INTERRUPT_RX_MSG                         0x00000200
+#define AR_MCI_INTERRUPT_RX_MSG_S                       9
+#define AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE            0x00000400
+#define AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE_S          10
+#define AR_MCI_INTERRUPT_BT_PRI                         0x07fff800
+#define AR_MCI_INTERRUPT_BT_PRI_S                       11
+#define AR_MCI_INTERRUPT_BT_PRI_THRESH                  0x08000000
+#define AR_MCI_INTERRUPT_BT_PRI_THRESH_S                27
+#define AR_MCI_INTERRUPT_BT_FREQ                        0x10000000
+#define AR_MCI_INTERRUPT_BT_FREQ_S                      28
+#define AR_MCI_INTERRUPT_BT_STOMP                       0x20000000
+#define AR_MCI_INTERRUPT_BT_STOMP_S                     29
+#define AR_MCI_INTERRUPT_BB_AIC_IRQ                     0x40000000
+#define AR_MCI_INTERRUPT_BB_AIC_IRQ_S                   30
+#define AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT              0x80000000
+#define AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT_S            31
+
+#define AR_MCI_REMOTE_CPU_INT                           0x1830
+#define AR_MCI_REMOTE_CPU_INT_EN                        0x1834
+#define AR_MCI_INTERRUPT_RX_MSG_RAW                     0x1838
+#define AR_MCI_INTERRUPT_RX_MSG_EN                      0x183c
+#define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET            0x00000001
+#define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET_S          0
+#define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL             0x00000002
+#define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL_S           1
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK               0x00000004
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK_S             2
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO               0x00000008
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO_S             3
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_RST                0x00000010
+#define AR_MCI_INTERRUPT_RX_MSG_CONT_RST_S              4
+#define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO               0x00000020
+#define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO_S             5
+#define AR_MCI_INTERRUPT_RX_MSG_CPU_INT                 0x00000040
+#define AR_MCI_INTERRUPT_RX_MSG_CPU_INT_S               6
+#define AR_MCI_INTERRUPT_RX_MSG_GPM                     0x00000100
+#define AR_MCI_INTERRUPT_RX_MSG_GPM_S                   8
+#define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO                0x00000200
+#define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO_S              9
+#define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING            0x00000400
+#define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING_S          10
+#define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING              0x00000800
+#define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING_S            11
+#define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE                0x00001000
+#define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE_S              12
+
+#define AR_MCI_CPU_INT                                  0x1840
+
+#define AR_MCI_RX_STATUS                                0x1844
+#define AR_MCI_RX_LAST_SCHD_MSG_INDEX                   0x00000F00
+#define AR_MCI_RX_LAST_SCHD_MSG_INDEX_S                 8
+#define AR_MCI_RX_REMOTE_SLEEP                          0x00001000
+#define AR_MCI_RX_REMOTE_SLEEP_S                        12
+#define AR_MCI_RX_MCI_CLK_REQ                           0x00002000
+#define AR_MCI_RX_MCI_CLK_REQ_S                         13
+
+#define AR_MCI_CONT_STATUS                              0x1848
+#define AR_MCI_CONT_RSSI_POWER                          0x000000FF
+#define AR_MCI_CONT_RSSI_POWER_S                        0
+#define AR_MCI_CONT_PRIORITY                            0x0000FF00
+#define AR_MCI_CONT_PRIORITY_S                          8
+#define AR_MCI_CONT_TXRX                                0x00010000
+#define AR_MCI_CONT_TXRX_S                              16
+
+#define AR_MCI_BT_PRI0                                  0x184c
+#define AR_MCI_BT_PRI1                                  0x1850
+#define AR_MCI_BT_PRI2                                  0x1854
+#define AR_MCI_BT_PRI3                                  0x1858
+#define AR_MCI_BT_PRI                                   0x185c
+#define AR_MCI_WL_FREQ0                                 0x1860
+#define AR_MCI_WL_FREQ1                                 0x1864
+#define AR_MCI_WL_FREQ2                                 0x1868
+#define AR_MCI_GAIN                                     0x186c
+#define AR_MCI_WBTIMER1                                 0x1870
+#define AR_MCI_WBTIMER2                                 0x1874
+#define AR_MCI_WBTIMER3                                 0x1878
+#define AR_MCI_WBTIMER4                                 0x187c
+#define AR_MCI_MAXGAIN                                  0x1880
+#define AR_MCI_HW_SCHD_TBL_CTL                          0x1884
+#define AR_MCI_HW_SCHD_TBL_D0                           0x1888
+#define AR_MCI_HW_SCHD_TBL_D1                           0x188c
+#define AR_MCI_HW_SCHD_TBL_D2                           0x1890
+#define AR_MCI_HW_SCHD_TBL_D3                           0x1894
+#define AR_MCI_TX_PAYLOAD0                              0x1898
+#define AR_MCI_TX_PAYLOAD1                              0x189c
+#define AR_MCI_TX_PAYLOAD2                              0x18a0
+#define AR_MCI_TX_PAYLOAD3                              0x18a4
+#define AR_BTCOEX_WBTIMER                               0x18a8
+
+#define AR_BTCOEX_CTRL                                  0x18ac
+#define AR_BTCOEX_CTRL_AR9462_MODE                      0x00000001
+#define AR_BTCOEX_CTRL_AR9462_MODE_S                    0
+#define AR_BTCOEX_CTRL_WBTIMER_EN                       0x00000002
+#define AR_BTCOEX_CTRL_WBTIMER_EN_S                     1
+#define AR_BTCOEX_CTRL_MCI_MODE_EN                      0x00000004
+#define AR_BTCOEX_CTRL_MCI_MODE_EN_S                    2
+#define AR_BTCOEX_CTRL_LNA_SHARED                       0x00000008
+#define AR_BTCOEX_CTRL_LNA_SHARED_S                     3
+#define AR_BTCOEX_CTRL_PA_SHARED                        0x00000010
+#define AR_BTCOEX_CTRL_PA_SHARED_S                      4
+#define AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN           0x00000020
+#define AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN_S         5
+#define AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN        0x00000040
+#define AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN_S      6
+#define AR_BTCOEX_CTRL_NUM_ANTENNAS                     0x00000180
+#define AR_BTCOEX_CTRL_NUM_ANTENNAS_S                   7
+#define AR_BTCOEX_CTRL_RX_CHAIN_MASK                    0x00000E00
+#define AR_BTCOEX_CTRL_RX_CHAIN_MASK_S                  9
+#define AR_BTCOEX_CTRL_AGGR_THRESH                      0x00007000
+#define AR_BTCOEX_CTRL_AGGR_THRESH_S                    12
+#define AR_BTCOEX_CTRL_1_CHAIN_BCN                      0x00080000
+#define AR_BTCOEX_CTRL_1_CHAIN_BCN_S                    19
+#define AR_BTCOEX_CTRL_1_CHAIN_ACK                      0x00100000
+#define AR_BTCOEX_CTRL_1_CHAIN_ACK_S                    20
+#define AR_BTCOEX_CTRL_WAIT_BA_MARGIN                   0x1FE00000
+#define AR_BTCOEX_CTRL_WAIT_BA_MARGIN_S                 28
+#define AR_BTCOEX_CTRL_REDUCE_TXPWR                     0x20000000
+#define AR_BTCOEX_CTRL_REDUCE_TXPWR_S                   29
+#define AR_BTCOEX_CTRL_SPDT_ENABLE_10                   0x40000000
+#define AR_BTCOEX_CTRL_SPDT_ENABLE_10_S                 30
+#define AR_BTCOEX_CTRL_SPDT_POLARITY                    0x80000000
+#define AR_BTCOEX_CTRL_SPDT_POLARITY_S                  31
+
+#define AR_BTCOEX_WL_WEIGHTS0                           0x18b0
+#define AR_BTCOEX_WL_WEIGHTS1                           0x18b4
+#define AR_BTCOEX_WL_WEIGHTS2                           0x18b8
+#define AR_BTCOEX_WL_WEIGHTS3                           0x18bc
+
+#define AR_BTCOEX_MAX_TXPWR(_x)                         (0x18c0 + ((_x) << 2))
+#define AR_BTCOEX_WL_LNA                                0x1940
+#define AR_BTCOEX_RFGAIN_CTRL                           0x1944
+#define AR_BTCOEX_WL_LNA_TIMEOUT                        0x003FFFFF
+#define AR_BTCOEX_WL_LNA_TIMEOUT_S                      0
+
+#define AR_BTCOEX_CTRL2                                 0x1948
+#define AR_BTCOEX_CTRL2_TXPWR_THRESH                    0x0007F800
+#define AR_BTCOEX_CTRL2_TXPWR_THRESH_S                  11
+#define AR_BTCOEX_CTRL2_TX_CHAIN_MASK                   0x00380000
+#define AR_BTCOEX_CTRL2_TX_CHAIN_MASK_S                 19
+#define AR_BTCOEX_CTRL2_RX_DEWEIGHT                     0x00400000
+#define AR_BTCOEX_CTRL2_RX_DEWEIGHT_S                   22
+#define AR_BTCOEX_CTRL2_GPIO_OBS_SEL                    0x00800000
+#define AR_BTCOEX_CTRL2_GPIO_OBS_SEL_S                  23
+#define AR_BTCOEX_CTRL2_MAC_BB_OBS_SEL                  0x01000000
+#define AR_BTCOEX_CTRL2_MAC_BB_OBS_SEL_S                24
+#define AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE         0x02000000
+#define AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE_S       25
+
+#define AR_BTCOEX_CTRL_SPDT_ENABLE                      0x00000001
+#define AR_BTCOEX_CTRL_SPDT_ENABLE_S                    0
+#define AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL                 0x00000002
+#define AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL_S               1
+#define AR_BTCOEX_CTRL_USE_LATCHED_BT_ANT               0x00000004
+#define AR_BTCOEX_CTRL_USE_LATCHED_BT_ANT_S             2
+#define AR_GLB_WLAN_UART_INTF_EN                        0x00020000
+#define AR_GLB_WLAN_UART_INTF_EN_S                      17
+#define AR_GLB_DS_JTAG_DISABLE                          0x00040000
+#define AR_GLB_DS_JTAG_DISABLE_S                        18
+
+#define AR_BTCOEX_RC                                    0x194c
+#define AR_BTCOEX_MAX_RFGAIN(_x)                        (0x1950 + ((_x) << 2))
+#define AR_BTCOEX_DBG                                   0x1a50
+#define AR_MCI_LAST_HW_MSG_HDR                          0x1a54
+#define AR_MCI_LAST_HW_MSG_BDY                          0x1a58
+
+#define AR_MCI_SCHD_TABLE_2                             0x1a5c
+#define AR_MCI_SCHD_TABLE_2_MEM_BASED                   0x00000001
+#define AR_MCI_SCHD_TABLE_2_MEM_BASED_S                 0
+#define AR_MCI_SCHD_TABLE_2_HW_BASED                    0x00000002
+#define AR_MCI_SCHD_TABLE_2_HW_BASED_S                  1
+
+#define AR_BTCOEX_CTRL3                                 0x1a60
+#define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT               0x00000fff
+#define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT_S             0
+
+#define AR_GLB_SWREG_DISCONT_MODE                       0x2002c
+#define AR_GLB_SWREG_DISCONT_EN_BT_WLAN                 0x3
+
+#define AR_MCI_MISC                                     0x1a74
+#define AR_MCI_MISC_HW_FIX_EN                           0x00000001
+#define AR_MCI_MISC_HW_FIX_EN_S                         0
+
+#define AR_MCI_DBG_CNT_CTRL                             0x1a78
+#define AR_MCI_DBG_CNT_CTRL_ENABLE                      0x00000001
+#define AR_MCI_DBG_CNT_CTRL_ENABLE_S                    0
+#define AR_MCI_DBG_CNT_CTRL_BT_LINKID                   0x000007f8
+#define AR_MCI_DBG_CNT_CTRL_BT_LINKID_S                 3
+
+#define MCI_STAT_ALL_BT_LINKID                          0xffff
+
+#define AR_MCI_INTERRUPT_DEFAULT (AR_MCI_INTERRUPT_SW_MSG_DONE         | \
+				  AR_MCI_INTERRUPT_RX_INVALID_HDR      | \
+				  AR_MCI_INTERRUPT_RX_HW_MSG_FAIL      | \
+				  AR_MCI_INTERRUPT_RX_SW_MSG_FAIL      | \
+				  AR_MCI_INTERRUPT_TX_HW_MSG_FAIL      | \
+				  AR_MCI_INTERRUPT_TX_SW_MSG_FAIL      | \
+				  AR_MCI_INTERRUPT_RX_MSG              | \
+				  AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE | \
+				  AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)
+
+#define AR_MCI_INTERRUPT_MSG_FAIL_MASK (AR_MCI_INTERRUPT_RX_HW_MSG_FAIL | \
+                                        AR_MCI_INTERRUPT_RX_SW_MSG_FAIL | \
+                                        AR_MCI_INTERRUPT_TX_HW_MSG_FAIL | \
+                                        AR_MCI_INTERRUPT_TX_SW_MSG_FAIL)
+
+#define AR_MCI_INTERRUPT_RX_HW_MSG_MASK (AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO   | \
+					 AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL | \
+					 AR_MCI_INTERRUPT_RX_MSG_LNA_INFO    | \
+					 AR_MCI_INTERRUPT_RX_MSG_CONT_NACK   | \
+					 AR_MCI_INTERRUPT_RX_MSG_CONT_INFO   | \
+					 AR_MCI_INTERRUPT_RX_MSG_CONT_RST)
+
+#define AR_MCI_INTERRUPT_RX_MSG_DEFAULT (AR_MCI_INTERRUPT_RX_MSG_GPM           | \
+                                         AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET  | \
+                                         AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING    | \
+                                         AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING  | \
+                                         AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)
+
+#endif /* REG_MCI_H */
diff --git a/drivers/net/wireless/ath/ath9k/reg_wow.h b/drivers/net/wireless/ath/ath9k/reg_wow.h
new file mode 100644
index 0000000..4530540
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/reg_wow.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015 Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef REG_WOW_H
+#define REG_WOW_H
+
+#define AR_WOW_PATTERN                  0x825C
+#define AR_WOW_COUNT                    0x8260
+#define AR_WOW_BCN_EN                   0x8270
+#define AR_WOW_BCN_TIMO                 0x8274
+#define AR_WOW_KEEP_ALIVE_TIMO          0x8278
+#define AR_WOW_KEEP_ALIVE               0x827c
+#define AR_WOW_KEEP_ALIVE_DELAY         0x8288
+#define AR_WOW_PATTERN_MATCH            0x828c
+
+/*
+ * AR_WOW_LENGTH1
+ * bit 31:24 pattern 0 length
+ * bit 23:16 pattern 1 length
+ * bit 15:8 pattern 2 length
+ * bit 7:0 pattern 3 length
+ *
+ * AR_WOW_LENGTH2
+ * bit 31:24 pattern 4 length
+ * bit 23:16 pattern 5 length
+ * bit 15:8 pattern 6 length
+ * bit 7:0 pattern 7 length
+ *
+ * AR_WOW_LENGTH3
+ * bit 31:24 pattern 8 length
+ * bit 23:16 pattern 9 length
+ * bit 15:8 pattern 10 length
+ * bit 7:0 pattern 11 length
+ *
+ * AR_WOW_LENGTH4
+ * bit 31:24 pattern 12 length
+ * bit 23:16 pattern 13 length
+ * bit 15:8 pattern 14 length
+ * bit 7:0 pattern 15 length
+ */
+#define AR_WOW_LENGTH1                  0x8360
+#define AR_WOW_LENGTH2                  0X8364
+#define AR_WOW_LENGTH3                  0X8380
+#define AR_WOW_LENGTH4                  0X8384
+
+#define AR_WOW_PATTERN_MATCH_LT_256B    0x8368
+#define AR_MAC_PCU_WOW4                 0x8370
+
+#define AR_SW_WOW_CONTROL               0x20018
+#define AR_SW_WOW_ENABLE                0x1
+#define AR_SWITCH_TO_REFCLK             0x2
+#define AR_RESET_CONTROL                0x4
+#define AR_RESET_VALUE_MASK             0x8
+#define AR_HW_WOW_DISABLE               0x10
+#define AR_CLR_MAC_INTERRUPT            0x20
+#define AR_CLR_KA_INTERRUPT             0x40
+
+#define AR_WOW_BACK_OFF_SHIFT(x)        ((x & 0xf) << 27) /* in usecs */
+#define AR_WOW_MAC_INTR_EN              0x00040000
+#define AR_WOW_MAGIC_EN                 0x00010000
+#define AR_WOW_PATTERN_EN(x)            (x & 0xff)
+#define AR_WOW_PAT_FOUND_SHIFT          8
+#define AR_WOW_PATTERN_FOUND(x)         (x & (0xff << AR_WOW_PAT_FOUND_SHIFT))
+#define AR_WOW_PATTERN_FOUND_MASK       ((0xff) << AR_WOW_PAT_FOUND_SHIFT)
+#define AR_WOW_MAGIC_PAT_FOUND          0x00020000
+#define AR_WOW_MAC_INTR                 0x00080000
+#define AR_WOW_KEEP_ALIVE_FAIL          0x00100000
+#define AR_WOW_BEACON_FAIL              0x00200000
+
+#define AR_WOW_STATUS(x)                (x & (AR_WOW_PATTERN_FOUND_MASK | \
+                                              AR_WOW_MAGIC_PAT_FOUND    | \
+                                              AR_WOW_KEEP_ALIVE_FAIL    | \
+                                              AR_WOW_BEACON_FAIL))
+#define AR_WOW_CLEAR_EVENTS(x)          (x & ~(AR_WOW_PATTERN_EN(0xff) | \
+                                               AR_WOW_MAGIC_EN |	\
+                                               AR_WOW_MAC_INTR_EN |	\
+                                               AR_WOW_BEACON_FAIL |	\
+                                               AR_WOW_KEEP_ALIVE_FAIL))
+
+#define AR_WOW2_PATTERN_EN(x)           ((x & 0xff) << 0)
+#define AR_WOW2_PATTERN_FOUND_SHIFT     8
+#define AR_WOW2_PATTERN_FOUND(x)        (x & (0xff << AR_WOW2_PATTERN_FOUND_SHIFT))
+#define AR_WOW2_PATTERN_FOUND_MASK      ((0xff) << AR_WOW2_PATTERN_FOUND_SHIFT)
+
+#define AR_WOW_STATUS2(x)               (x & AR_WOW2_PATTERN_FOUND_MASK)
+#define AR_WOW_CLEAR_EVENTS2(x)         (x & ~(AR_WOW2_PATTERN_EN(0xff)))
+
+#define AR_WOW_AIFS_CNT(x)              (x & 0xff)
+#define AR_WOW_SLOT_CNT(x)              ((x & 0xff) << 8)
+#define AR_WOW_KEEP_ALIVE_CNT(x)        ((x & 0xff) << 16)
+
+#define AR_WOW_BEACON_FAIL_EN           0x00000001
+#define AR_WOW_BEACON_TIMO              0x40000000
+#define AR_WOW_KEEP_ALIVE_NEVER         0xffffffff
+#define AR_WOW_KEEP_ALIVE_AUTO_DIS      0x00000001
+#define AR_WOW_KEEP_ALIVE_FAIL_DIS      0x00000002
+#define AR_WOW_KEEP_ALIVE_DELAY_VALUE   0x000003e8 /* 1 msec */
+#define AR_WOW_BMISSTHRESHOLD           0x20
+#define AR_WOW_PAT_END_OF_PKT(x)        (x & 0xf)
+#define AR_WOW_PAT_OFF_MATCH(x)         ((x & 0xf) << 8)
+#define AR_WOW_PAT_BACKOFF              0x00000004
+#define AR_WOW_CNT_AIFS_CNT             0x00000022
+#define AR_WOW_CNT_SLOT_CNT             0x00000009
+#define AR_WOW_CNT_KA_CNT               0x00000008
+
+#define AR_WOW_TRANSMIT_BUFFER          0xe000
+#define AR_WOW_TXBUF(i)                 (AR_WOW_TRANSMIT_BUFFER + ((i) << 2))
+#define AR_WOW_KA_DESC_WORD2            0xe000
+#define AR_WOW_TB_PATTERN(i)            (0xe100 + (i << 8))
+#define AR_WOW_TB_MASK(i)               (0xec00 + (i << 5))
+#define AR_WOW_PATTERN_SUPPORTED_LEGACY 0xff
+#define AR_WOW_PATTERN_SUPPORTED        0xffff
+#define AR_WOW_LENGTH_MAX               0xff
+#define AR_WOW_LEN1_SHIFT(_i)           ((0x3 - ((_i) & 0x3)) << 0x3)
+#define AR_WOW_LENGTH1_MASK(_i)         (AR_WOW_LENGTH_MAX << AR_WOW_LEN1_SHIFT(_i))
+#define AR_WOW_LEN2_SHIFT(_i)           ((0x7 - ((_i) & 0x7)) << 0x3)
+#define AR_WOW_LENGTH2_MASK(_i)         (AR_WOW_LENGTH_MAX << AR_WOW_LEN2_SHIFT(_i))
+#define AR_WOW_LEN3_SHIFT(_i)           ((0xb - ((_i) & 0xb)) << 0x3)
+#define AR_WOW_LENGTH3_MASK(_i)         (AR_WOW_LENGTH_MAX << AR_WOW_LEN3_SHIFT(_i))
+#define AR_WOW_LEN4_SHIFT(_i)           ((0xf - ((_i) & 0xf)) << 0x3)
+#define AR_WOW_LENGTH4_MASK(_i)         (AR_WOW_LENGTH_MAX << AR_WOW_LEN4_SHIFT(_i))
+
+#endif /* REG_WOW_H */
diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c
index 65c8894..ca533b4 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.c
+++ b/drivers/net/wireless/ath/ath9k/wmi.c
@@ -61,6 +61,8 @@
 		return "WMI_REG_READ_CMDID";
 	case WMI_REG_WRITE_CMDID:
 		return "WMI_REG_WRITE_CMDID";
+	case WMI_REG_RMW_CMDID:
+		return "WMI_REG_RMW_CMDID";
 	case WMI_RC_STATE_CHANGE_CMDID:
 		return "WMI_RC_STATE_CHANGE_CMDID";
 	case WMI_RC_RATE_UPDATE_CMDID:
@@ -101,6 +103,7 @@
 	spin_lock_init(&wmi->event_lock);
 	mutex_init(&wmi->op_mutex);
 	mutex_init(&wmi->multi_write_mutex);
+	mutex_init(&wmi->multi_rmw_mutex);
 	init_completion(&wmi->cmd_wait);
 	INIT_LIST_HEAD(&wmi->pending_tx_events);
 	tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet,
@@ -224,7 +227,7 @@
 
 	/* Check if there has been a timeout. */
 	spin_lock(&wmi->wmi_lock);
-	if (cmd_id != wmi->last_cmd_id) {
+	if (be16_to_cpu(hdr->seq_no) != wmi->last_seq_id) {
 		spin_unlock(&wmi->wmi_lock);
 		goto free_skb;
 	}
@@ -272,11 +275,16 @@
 			       enum wmi_cmd_id cmd, u16 len)
 {
 	struct wmi_cmd_hdr *hdr;
+	unsigned long flags;
 
 	hdr = (struct wmi_cmd_hdr *) skb_push(skb, sizeof(struct wmi_cmd_hdr));
 	hdr->command_id = cpu_to_be16(cmd);
 	hdr->seq_no = cpu_to_be16(++wmi->tx_seq_id);
 
+	spin_lock_irqsave(&wmi->wmi_lock, flags);
+	wmi->last_seq_id = wmi->tx_seq_id;
+	spin_unlock_irqrestore(&wmi->wmi_lock, flags);
+
 	return htc_send_epid(wmi->htc, skb, wmi->ctrl_epid);
 }
 
@@ -292,7 +300,6 @@
 	struct sk_buff *skb;
 	u8 *data;
 	int time_left, ret = 0;
-	unsigned long flags;
 
 	if (ah->ah_flags & AH_UNPLUGGED)
 		return 0;
@@ -320,10 +327,6 @@
 	wmi->cmd_rsp_buf = rsp_buf;
 	wmi->cmd_rsp_len = rsp_len;
 
-	spin_lock_irqsave(&wmi->wmi_lock, flags);
-	wmi->last_cmd_id = cmd_id;
-	spin_unlock_irqrestore(&wmi->wmi_lock, flags);
-
 	ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len);
 	if (ret)
 		goto out;
diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h
index 0db37f2..380175d 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.h
+++ b/drivers/net/wireless/ath/ath9k/wmi.h
@@ -112,6 +112,7 @@
 	WMI_TX_STATS_CMDID,
 	WMI_RX_STATS_CMDID,
 	WMI_BITRATE_MASK_CMDID,
+	WMI_REG_RMW_CMDID,
 };
 
 enum wmi_event_id {
@@ -125,12 +126,19 @@
 };
 
 #define MAX_CMD_NUMBER 62
+#define MAX_RMW_CMD_NUMBER 15
 
 struct register_write {
 	__be32 reg;
 	__be32 val;
 };
 
+struct register_rmw {
+	__be32 reg;
+	__be32 set;
+	__be32 clr;
+} __packed;
+
 struct ath9k_htc_tx_event {
 	int count;
 	struct __wmi_event_txstatus txs;
@@ -143,7 +151,7 @@
 	enum htc_endpoint_id ctrl_epid;
 	struct mutex op_mutex;
 	struct completion cmd_wait;
-	enum wmi_cmd_id last_cmd_id;
+	u16 last_seq_id;
 	struct sk_buff_head wmi_event_queue;
 	struct tasklet_struct wmi_event_tasklet;
 	u16 tx_seq_id;
@@ -156,10 +164,18 @@
 
 	spinlock_t wmi_lock;
 
+	/* multi write section */
 	atomic_t mwrite_cnt;
 	struct register_write multi_write[MAX_CMD_NUMBER];
 	u32 multi_write_idx;
 	struct mutex multi_write_mutex;
+
+	/* multi rmw section */
+	atomic_t m_rmw_cnt;
+	struct register_rmw multi_rmw[MAX_RMW_CMD_NUMBER];
+	u32 multi_rmw_idx;
+	struct mutex multi_rmw_mutex;
+
 };
 
 struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv);
diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c
index 5f30e58..8d0b173 100644
--- a/drivers/net/wireless/ath/ath9k/wow.c
+++ b/drivers/net/wireless/ath/ath9k/wow.c
@@ -16,36 +16,43 @@
 
 #include "ath9k.h"
 
-static const struct wiphy_wowlan_support ath9k_wowlan_support = {
+static const struct wiphy_wowlan_support ath9k_wowlan_support_legacy = {
 	.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
 	.n_patterns = MAX_NUM_USER_PATTERN,
 	.pattern_min_len = 1,
 	.pattern_max_len = MAX_PATTERN_SIZE,
 };
 
-static void ath9k_wow_map_triggers(struct ath_softc *sc,
-				   struct cfg80211_wowlan *wowlan,
-				   u32 *wow_triggers)
+static const struct wiphy_wowlan_support ath9k_wowlan_support = {
+	.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
+	.n_patterns = MAX_NUM_PATTERN - 2,
+	.pattern_min_len = 1,
+	.pattern_max_len = MAX_PATTERN_SIZE,
+};
+
+static u8 ath9k_wow_map_triggers(struct ath_softc *sc,
+				 struct cfg80211_wowlan *wowlan)
 {
+	u8 wow_triggers = 0;
+
 	if (wowlan->disconnect)
-		*wow_triggers |= AH_WOW_LINK_CHANGE |
-				 AH_WOW_BEACON_MISS;
+		wow_triggers |= AH_WOW_LINK_CHANGE |
+				AH_WOW_BEACON_MISS;
 	if (wowlan->magic_pkt)
-		*wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
+		wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
 
 	if (wowlan->n_patterns)
-		*wow_triggers |= AH_WOW_USER_PATTERN_EN;
+		wow_triggers |= AH_WOW_USER_PATTERN_EN;
 
-	sc->wow_enabled = *wow_triggers;
-
+	return wow_triggers;
 }
 
-static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
+static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
 {
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_common *common = ath9k_hw_common(ah);
 	int pattern_count = 0;
-	int i, byte_cnt;
+	int ret, i, byte_cnt = 0;
 	u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
 	u8 dis_deauth_mask[MAX_PATTERN_SIZE];
 
@@ -80,12 +87,7 @@
 	 *			    | x:x:x:x:x:x  -- 22 bytes
 	 */
 
-	/* Create Disassociate Pattern first */
-
-	byte_cnt = 0;
-
 	/* Fill out the mask with all FF's */
-
 	for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
 		dis_deauth_mask[i] = 0xff;
 
@@ -108,19 +110,17 @@
 	byte_cnt += 6;
 
 	/* copy the bssid, its same as the source mac address */
-
 	memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
 
 	/* Create Disassociate pattern mask */
-
 	dis_deauth_mask[0] = 0xfe;
 	dis_deauth_mask[1] = 0x03;
 	dis_deauth_mask[2] = 0xc0;
 
-	ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
-
-	ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
-				   pattern_count, byte_cnt);
+	ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
+					 pattern_count, byte_cnt);
+	if (ret)
+		goto exit;
 
 	pattern_count++;
 	/*
@@ -129,59 +129,39 @@
 	 */
 	dis_deauth_pattern[0] = 0xC0;
 
-	ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
-				   pattern_count, byte_cnt);
-
+	ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
+					 pattern_count, byte_cnt);
+exit:
+	return ret;
 }
 
-static void ath9k_wow_add_pattern(struct ath_softc *sc,
-				  struct cfg80211_wowlan *wowlan)
+static int ath9k_wow_add_pattern(struct ath_softc *sc,
+				 struct cfg80211_wowlan *wowlan)
 {
 	struct ath_hw *ah = sc->sc_ah;
-	struct ath9k_wow_pattern *wow_pattern = NULL;
 	struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
-	int mask_len;
+	u8 wow_pattern[MAX_PATTERN_SIZE];
+	u8 wow_mask[MAX_PATTERN_SIZE];
+	int mask_len, ret = 0;
 	s8 i = 0;
 
-	if (!wowlan->n_patterns)
-		return;
-
-	/*
-	 * Add the new user configured patterns
-	 */
 	for (i = 0; i < wowlan->n_patterns; i++) {
+		mask_len = DIV_ROUND_UP(patterns[i].pattern_len, 8);
+		memset(wow_pattern, 0, MAX_PATTERN_SIZE);
+		memset(wow_mask, 0, MAX_PATTERN_SIZE);
+		memcpy(wow_pattern, patterns[i].pattern, patterns[i].pattern_len);
+		memcpy(wow_mask, patterns[i].mask, mask_len);
 
-		wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL);
-
-		if (!wow_pattern)
-			return;
-
-		/*
-		 * TODO: convert the generic user space pattern to
-		 * appropriate chip specific/802.11 pattern.
-		 */
-
-		mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
-		memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE);
-		memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE);
-		memcpy(wow_pattern->pattern_bytes, patterns[i].pattern,
-		       patterns[i].pattern_len);
-		memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len);
-		wow_pattern->pattern_len = patterns[i].pattern_len;
-
-		/*
-		 * just need to take care of deauth and disssoc pattern,
-		 * make sure we don't overwrite them.
-		 */
-
-		ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes,
-					   wow_pattern->mask_bytes,
-					   i + 2,
-					   wow_pattern->pattern_len);
-		kfree(wow_pattern);
-
+		ret = ath9k_hw_wow_apply_pattern(ah,
+						 wow_pattern,
+						 wow_mask,
+						 i + 2,
+						 patterns[i].pattern_len);
+		if (ret)
+			break;
 	}
 
+	return ret;
 }
 
 int ath9k_suspend(struct ieee80211_hw *hw,
@@ -190,58 +170,55 @@
 	struct ath_softc *sc = hw->priv;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_common *common = ath9k_hw_common(ah);
-	u32 wow_triggers_enabled = 0;
+	u8 triggers;
 	int ret = 0;
 
 	ath9k_deinit_channel_context(sc);
 
 	mutex_lock(&sc->mutex);
 
-	ath_cancel_work(sc);
-	ath_stop_ani(sc);
-
 	if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
-		ath_dbg(common, ANY, "Device not present\n");
-		ret = -EINVAL;
+		ath_err(common, "Device not present\n");
+		ret = -ENODEV;
 		goto fail_wow;
 	}
 
 	if (WARN_ON(!wowlan)) {
-		ath_dbg(common, WOW, "None of the WoW triggers enabled\n");
+		ath_err(common, "None of the WoW triggers enabled\n");
 		ret = -EINVAL;
 		goto fail_wow;
 	}
 
-	if (!device_can_wakeup(sc->dev)) {
-		ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n");
-		ret = 1;
-		goto fail_wow;
-	}
-
-	/*
-	 * none of the sta vifs are associated
-	 * and we are not currently handling multivif
-	 * cases, for instance we have to seperately
-	 * configure 'keep alive frame' for each
-	 * STA.
-	 */
-
-	if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
-		ath_dbg(common, WOW, "None of the STA vifs are associated\n");
-		ret = 1;
-		goto fail_wow;
-	}
-
 	if (sc->cur_chan->nvifs > 1) {
 		ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
 		ret = 1;
 		goto fail_wow;
 	}
 
-	ath9k_wow_map_triggers(sc, wowlan, &wow_triggers_enabled);
+	if (ath9k_is_chanctx_enabled()) {
+		if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) {
+			ath_dbg(common, WOW,
+				"Multi-channel WOW is not supported\n");
+			ret = 1;
+			goto fail_wow;
+		}
+	}
 
-	ath_dbg(common, WOW, "WoW triggers enabled 0x%x\n",
-		wow_triggers_enabled);
+	if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
+		ath_dbg(common, WOW, "None of the STA vifs are associated\n");
+		ret = 1;
+		goto fail_wow;
+	}
+
+	triggers = ath9k_wow_map_triggers(sc, wowlan);
+	if (!triggers) {
+		ath_dbg(common, WOW, "No valid WoW triggers\n");
+		ret = 1;
+		goto fail_wow;
+	}
+
+	ath_cancel_work(sc);
+	ath_stop_ani(sc);
 
 	ath9k_ps_wakeup(sc);
 
@@ -251,10 +228,21 @@
 	 * Enable wake up on recieving disassoc/deauth
 	 * frame by default.
 	 */
-	ath9k_wow_add_disassoc_deauth_pattern(sc);
+	ret = ath9k_wow_add_disassoc_deauth_pattern(sc);
+	if (ret) {
+		ath_err(common,
+			"Unable to add disassoc/deauth pattern: %d\n", ret);
+		goto fail_wow;
+	}
 
-	if (wow_triggers_enabled & AH_WOW_USER_PATTERN_EN)
-		ath9k_wow_add_pattern(sc, wowlan);
+	if (triggers & AH_WOW_USER_PATTERN_EN) {
+		ret = ath9k_wow_add_pattern(sc, wowlan);
+		if (ret) {
+			ath_err(common,
+				"Unable to add user pattern: %d\n", ret);
+			goto fail_wow;
+		}
+	}
 
 	spin_lock_bh(&sc->sc_pcu_lock);
 	/*
@@ -278,12 +266,12 @@
 	synchronize_irq(sc->irq);
 	tasklet_kill(&sc->intr_tq);
 
-	ath9k_hw_wow_enable(ah, wow_triggers_enabled);
+	ath9k_hw_wow_enable(ah, triggers);
 
 	ath9k_ps_restore(sc);
-	ath_dbg(common, ANY, "WoW enabled in ath9k\n");
-	atomic_inc(&sc->wow_sleep_proc_intr);
+	ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers);
 
+	set_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
 fail_wow:
 	mutex_unlock(&sc->mutex);
 	return ret;
@@ -294,7 +282,7 @@
 	struct ath_softc *sc = hw->priv;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_common *common = ath9k_hw_common(ah);
-	u32 wow_status;
+	u8 status;
 
 	mutex_lock(&sc->mutex);
 
@@ -309,29 +297,14 @@
 
 	spin_unlock_bh(&sc->sc_pcu_lock);
 
-	wow_status = ath9k_hw_wow_wakeup(ah);
-
-	if (atomic_read(&sc->wow_got_bmiss_intr) == 0) {
-		/*
-		 * some devices may not pick beacon miss
-		 * as the reason they woke up so we add
-		 * that here for that shortcoming.
-		 */
-		wow_status |= AH_WOW_BEACON_MISS;
-		atomic_dec(&sc->wow_got_bmiss_intr);
-		ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep\n");
-	}
-
-	atomic_dec(&sc->wow_sleep_proc_intr);
-
-	if (wow_status) {
-		ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n",
-			ath9k_hw_wow_event_to_string(wow_status), wow_status);
-	}
+	status = ath9k_hw_wow_wakeup(ah);
+	ath_dbg(common, WOW, "Resume with WoW status: 0x%x\n", status);
 
 	ath_restart_work(sc);
 	ath9k_start_btcoex(sc);
 
+	clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
+
 	ath9k_ps_restore(sc);
 	mutex_unlock(&sc->mutex);
 
@@ -341,22 +314,35 @@
 void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
 {
 	struct ath_softc *sc = hw->priv;
+	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 
 	mutex_lock(&sc->mutex);
-	device_init_wakeup(sc->dev, 1);
 	device_set_wakeup_enable(sc->dev, enabled);
 	mutex_unlock(&sc->mutex);
+
+	ath_dbg(common, WOW, "WoW wakeup source is %s\n",
+		(enabled) ? "enabled" : "disabled");
 }
 
 void ath9k_init_wow(struct ieee80211_hw *hw)
 {
 	struct ath_softc *sc = hw->priv;
+	struct ath_hw *ah = sc->sc_ah;
 
-	if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
-	    (sc->driver_data & ATH9K_PCI_WOW) &&
-	    device_can_wakeup(sc->dev))
-		hw->wiphy->wowlan = &ath9k_wowlan_support;
+	if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) {
+		if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah))
+			hw->wiphy->wowlan = &ath9k_wowlan_support;
+		else
+			hw->wiphy->wowlan = &ath9k_wowlan_support_legacy;
 
-	atomic_set(&sc->wow_sleep_proc_intr, -1);
-	atomic_set(&sc->wow_got_bmiss_intr, -1);
+		device_init_wakeup(sc->dev, 1);
+	}
+}
+
+void ath9k_deinit_wow(struct ieee80211_hw *hw)
+{
+	struct ath_softc *sc = hw->priv;
+
+	if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow)
+		device_init_wakeup(sc->dev, 0);
 }
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 6bf7bc7..4a94e8b 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1097,34 +1097,89 @@
 }
 
 static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
-			       u8 rateidx)
+			       u8 rateidx, bool is_40, bool is_cck)
 {
 	u8 max_power;
+	struct sk_buff *skb;
+	struct ath_frame_info *fi;
+	struct ieee80211_tx_info *info;
+	struct ieee80211_vif *vif;
 	struct ath_hw *ah = sc->sc_ah;
 
-	if (sc->tx99_state)
+	if (sc->tx99_state || !ah->tpc_enabled)
 		return MAX_RATE_POWER;
 
-	if (!AR_SREV_9300_20_OR_LATER(ah)) {
-		/* ar9002 is not sipported for the moment */
-		return MAX_RATE_POWER;
+	skb = bf->bf_mpdu;
+	info = IEEE80211_SKB_CB(skb);
+	vif = info->control.vif;
+
+	if (!vif) {
+		max_power = sc->cur_chan->cur_txpower;
+		goto out;
 	}
 
-	if (!bf->bf_state.bfs_paprd) {
-		struct sk_buff *skb = bf->bf_mpdu;
-		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-		struct ath_frame_info *fi = get_frame_info(skb);
+	if (vif->bss_conf.txpower_type != NL80211_TX_POWER_LIMITED) {
+		max_power = min_t(u8, sc->cur_chan->cur_txpower,
+				  2 * vif->bss_conf.txpower);
+		goto out;
+	}
 
+	fi = get_frame_info(skb);
+
+	if (!AR_SREV_9300_20_OR_LATER(ah)) {
+		int txpower = fi->tx_power;
+
+		if (is_40) {
+			u8 power_ht40delta;
+			struct ar5416_eeprom_def *eep = &ah->eeprom.def;
+
+			if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_2) {
+				bool is_2ghz;
+				struct modal_eep_header *pmodal;
+
+				is_2ghz = info->band == IEEE80211_BAND_2GHZ;
+				pmodal = &eep->modalHeader[is_2ghz];
+				power_ht40delta = pmodal->ht40PowerIncForPdadc;
+			} else {
+				power_ht40delta = 2;
+			}
+			txpower += power_ht40delta;
+		}
+
+		if (AR_SREV_9287(ah) || AR_SREV_9285(ah) ||
+		    AR_SREV_9271(ah)) {
+			txpower -= 2 * AR9287_PWR_TABLE_OFFSET_DB;
+		} else if (AR_SREV_9280_20_OR_LATER(ah)) {
+			s8 power_offset;
+
+			power_offset = ah->eep_ops->get_eeprom(ah,
+							EEP_PWR_TABLE_OFFSET);
+			txpower -= 2 * power_offset;
+		}
+
+		if (OLC_FOR_AR9280_20_LATER && is_cck)
+			txpower -= 2;
+
+		txpower = max(txpower, 0);
+		max_power = min_t(u8, ah->tx_power[rateidx],
+				  2 * vif->bss_conf.txpower);
+		max_power = min_t(u8, max_power, txpower);
+	} else if (!bf->bf_state.bfs_paprd) {
 		if (rateidx < 8 && (info->flags & IEEE80211_TX_CTL_STBC))
-			max_power = min(ah->tx_power_stbc[rateidx],
-					fi->tx_power);
+			max_power = min_t(u8, ah->tx_power_stbc[rateidx],
+					  2 * vif->bss_conf.txpower);
 		else
-			max_power = min(ah->tx_power[rateidx], fi->tx_power);
+			max_power = min_t(u8, ah->tx_power[rateidx],
+					  2 * vif->bss_conf.txpower);
+		max_power = min(max_power, fi->tx_power);
 	} else {
 		max_power = ah->paprd_training_power;
 	}
-
-	return max_power;
+out:
+	/* XXX: clamp minimum TX power at 1 for AR9160 since if max_power
+	 * is set to 0, frames are transmitted at max TX power
+	 */
+	return (!max_power && !AR_SREV_9280_20_OR_LATER(ah)) ? 1 : max_power;
 }
 
 static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
@@ -1152,7 +1207,7 @@
 	info->rtscts_rate = fi->rtscts_rate;
 
 	for (i = 0; i < ARRAY_SIZE(bf->rates); i++) {
-		bool is_40, is_sgi, is_sp;
+		bool is_40, is_sgi, is_sp, is_cck;
 		int phy;
 
 		if (!rates[i].count || (rates[i].idx < 0))
@@ -1198,7 +1253,8 @@
 			if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC))
 				info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC;
 
-			info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
+			info->txpower[i] = ath_get_rate_txpower(sc, bf, rix,
+								is_40, false);
 			continue;
 		}
 
@@ -1227,7 +1283,9 @@
 		info->rates[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah,
 			phy, rate->bitrate * 100, len, rix, is_sp);
 
-		info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
+		is_cck = IS_CCK_RATE(info->rates[i].Rate);
+		info->txpower[i] = ath_get_rate_txpower(sc, bf, rix, false,
+							is_cck);
 	}
 
 	/* For AR5416 - RTS cannot be followed by a frame larger than 8K */
@@ -2259,7 +2317,7 @@
 	struct ath_txq *txq = txctl->txq;
 	struct ath_atx_tid *tid = NULL;
 	struct ath_buf *bf;
-	bool queue, skip_uapsd = false;
+	bool queue, skip_uapsd = false, ps_resp;
 	int q, ret;
 
 	if (vif)
@@ -2268,6 +2326,8 @@
 	if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
 		txctl->force_channel = true;
 
+	ps_resp = !!(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE);
+
 	ret = ath_tx_prepare(hw, skb, txctl);
 	if (ret)
 	    return ret;
@@ -2310,7 +2370,7 @@
 	if (txctl->an && queue)
 		tid = ath_get_skb_tid(sc, txctl->an, skb);
 
-	if (!skip_uapsd && (info->flags & IEEE80211_TX_CTL_PS_RESPONSE)) {
+	if (!skip_uapsd && ps_resp) {
 		ath_txq_unlock(sc, txq);
 		txq = sc->tx.uapsdq;
 		ath_txq_lock(sc, txq);
@@ -2443,9 +2503,12 @@
 	if (sc->sc_ah->caldata)
 		set_bit(PAPRD_PACKET_SENT, &sc->sc_ah->caldata->cal_flags);
 
-	if (!(tx_flags & ATH_TX_ERROR))
-		/* Frame was ACKed */
-		tx_info->flags |= IEEE80211_TX_STAT_ACK;
+	if (!(tx_flags & ATH_TX_ERROR)) {
+		if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
+			tx_info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
+		else
+			tx_info->flags |= IEEE80211_TX_STAT_ACK;
+	}
 
 	padpos = ieee80211_hdrlen(hdr->frame_control);
 	padsize = padpos & 3;
diff --git a/drivers/net/wireless/ath/carl9170/cmd.c b/drivers/net/wireless/ath/carl9170/cmd.c
index 39a6387..f2b4f53 100644
--- a/drivers/net/wireless/ath/carl9170/cmd.c
+++ b/drivers/net/wireless/ath/carl9170/cmd.c
@@ -188,12 +188,12 @@
 
 		if (ar->channel) {
 			info = &ar->survey[ar->channel->hw_value];
-			info->channel_time = ar->tally.active;
-			info->channel_time_busy = ar->tally.cca;
-			info->channel_time_tx = ar->tally.tx_time;
-			do_div(info->channel_time, 1000);
-			do_div(info->channel_time_busy, 1000);
-			do_div(info->channel_time_tx, 1000);
+			info->time = ar->tally.active;
+			info->time_busy = ar->tally.cca;
+			info->time_tx = ar->tally.tx_time;
+			do_div(info->time, 1000);
+			do_div(info->time_busy, 1000);
+			do_div(info->time_tx, 1000);
 		}
 	}
 	return 0;
diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c
index 1c0af9c..6808db4 100644
--- a/drivers/net/wireless/ath/carl9170/debug.c
+++ b/drivers/net/wireless/ath/carl9170/debug.c
@@ -214,14 +214,10 @@
 static char *carl9170_debugfs_mem_usage_read(struct ar9170 *ar, char *buf,
 					     size_t bufsize, ssize_t *len)
 {
-	ADD(buf, *len, bufsize, "jar: [");
-
 	spin_lock_bh(&ar->mem_lock);
 
-	*len += bitmap_scnprintf(&buf[*len], bufsize - *len,
-				  ar->mem_bitmap, ar->fw.mem_blocks);
-
-	ADD(buf, *len, bufsize, "]\n");
+	ADD(buf, *len, bufsize, "jar: [%*pb]\n",
+	    ar->fw.mem_blocks, ar->mem_bitmap);
 
 	ADD(buf, *len, bufsize, "cookies: used:%3d / total:%3d, allocs:%d\n",
 	    bitmap_weight(ar->mem_bitmap, ar->fw.mem_blocks),
@@ -316,17 +312,13 @@
 		    cnt, iter->tid, iter->bsn, iter->snx, iter->hsn,
 		    iter->max, iter->state, iter->counter);
 
-		ADD(buf, *len, bufsize, "\tWindow:  [");
-
-		*len += bitmap_scnprintf(&buf[*len], bufsize - *len,
-			iter->bitmap, CARL9170_BAW_BITS);
+		ADD(buf, *len, bufsize, "\tWindow:  [%*pb,W]\n",
+		    CARL9170_BAW_BITS, iter->bitmap);
 
 #define BM_STR_OFF(offset)					\
 	((CARL9170_BAW_BITS - (offset) - 1) / 4 +		\
 	 (CARL9170_BAW_BITS - (offset) - 1) / 32 + 1)
 
-		ADD(buf, *len, bufsize, ",W]\n");
-
 		offset = BM_STR_OFF(0);
 		ADD(buf, *len, bufsize, "\tBase Seq: %*s\n", offset, "T");
 
@@ -448,12 +440,8 @@
 	ADD(buf, *len, bufsize, "registered VIFs:%d \\ %d\n",
 	    ar->vifs, ar->fw.vif_num);
 
-	ADD(buf, *len, bufsize, "VIF bitmap: [");
-
-	*len += bitmap_scnprintf(&buf[*len], bufsize - *len,
-				 &ar->vif_bitmap, ar->fw.vif_num);
-
-	ADD(buf, *len, bufsize, "]\n");
+	ADD(buf, *len, bufsize, "VIF bitmap: [%*pb]\n",
+	    ar->fw.vif_num, &ar->vif_bitmap);
 
 	rcu_read_lock();
 	list_for_each_entry_rcu(iter, &ar->vif_list, list) {
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index d561990..65f27d2 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -1690,9 +1690,9 @@
 		survey->filled |= SURVEY_INFO_IN_USE;
 
 	if (ar->fw.hw_counters) {
-		survey->filled |= SURVEY_INFO_CHANNEL_TIME |
-				  SURVEY_INFO_CHANNEL_TIME_BUSY |
-				  SURVEY_INFO_CHANNEL_TIME_TX;
+		survey->filled |= SURVEY_INFO_TIME |
+				  SURVEY_INFO_TIME_BUSY |
+				  SURVEY_INFO_TIME_TX;
 	}
 
 	return 0;
diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c
index 0e383ef..99c5a7d 100644
--- a/drivers/net/wireless/ath/dfs_pattern_detector.c
+++ b/drivers/net/wireless/ath/dfs_pattern_detector.c
@@ -86,7 +86,7 @@
 	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, 1, 20),
+	FCC_PATTERN(4, 50, 100, 1000, 2000, 1, 1),
 	FCC_PATTERN(5, 0, 1, 333, 333, 1, 9),
 };
 
@@ -289,7 +289,7 @@
 				"count=%d, count_false=%d\n",
 				event->freq, pd->rs->type_id,
 				ps->pri, ps->count, ps->count_falses);
-			channel_detector_reset(dpd, cd);
+			pd->reset(pd, dpd->last_pulse_ts);
 			return true;
 		}
 	}
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c
index 73f12f1..086549b 100644
--- a/drivers/net/wireless/ath/wcn36xx/dxe.c
+++ b/drivers/net/wireless/ath/wcn36xx/dxe.c
@@ -84,6 +84,7 @@
 		if (!cur_ctl)
 			goto out_fail;
 
+		spin_lock_init(&cur_ctl->skb_lock);
 		cur_ctl->ctl_blk_order = i;
 		if (i == 0) {
 			ch->head_blk_ctl = cur_ctl;
@@ -354,6 +355,8 @@
 	 * and while-do will not make any cycles.
 	 */
 	do {
+		if (ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)
+			break;
 		if (ctl->skb) {
 			dma_unmap_single(NULL, ctl->desc->src_addr_l,
 					 ctl->skb->len, DMA_TO_DEVICE);
diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c
index 7dd8873..0783d2e 100644
--- a/drivers/net/wireless/ath/wcn36xx/main.c
+++ b/drivers/net/wireless/ath/wcn36xx/main.c
@@ -298,6 +298,8 @@
 	wcn36xx_debugfs_init(wcn);
 
 	INIT_LIST_HEAD(&wcn->vif_list);
+	spin_lock_init(&wcn->dxe_lock);
+
 	return 0;
 
 out_smd_stop:
@@ -795,6 +797,7 @@
 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
 		    vif, sta->addr);
 
+	spin_lock_init(&sta_priv->ampdu_lock);
 	vif_priv->sta = sta_priv;
 	sta_priv->vif = vif_priv;
 	/*
@@ -873,21 +876,32 @@
 			get_sta_index(vif, sta_priv));
 		wcn36xx_smd_add_ba(wcn);
 		wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv));
-		ieee80211_start_tx_ba_session(sta, tid, 0);
 		break;
 	case IEEE80211_AMPDU_RX_STOP:
 		wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv));
 		break;
 	case IEEE80211_AMPDU_TX_START:
+		spin_lock_bh(&sta_priv->ampdu_lock);
+		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
+		spin_unlock_bh(&sta_priv->ampdu_lock);
+
 		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 		break;
 	case IEEE80211_AMPDU_TX_OPERATIONAL:
+		spin_lock_bh(&sta_priv->ampdu_lock);
+		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL;
+		spin_unlock_bh(&sta_priv->ampdu_lock);
+
 		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
 			get_sta_index(vif, sta_priv));
 		break;
 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
 	case IEEE80211_AMPDU_TX_STOP_CONT:
+		spin_lock_bh(&sta_priv->ampdu_lock);
+		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE;
+		spin_unlock_bh(&sta_priv->ampdu_lock);
+
 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 		break;
 	default:
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c
index 6398693..dbd8944 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.c
+++ b/drivers/net/wireless/ath/wcn36xx/smd.c
@@ -21,6 +21,61 @@
 #include <linux/bitops.h>
 #include "smd.h"
 
+struct wcn36xx_cfg_val {
+	u32 cfg_id;
+	u32 value;
+};
+
+#define WCN36XX_CFG_VAL(id, val) \
+{ \
+	.cfg_id = WCN36XX_HAL_CFG_ ## id, \
+	.value = val \
+}
+
+static struct wcn36xx_cfg_val wcn36xx_cfg_vals[] = {
+	WCN36XX_CFG_VAL(CURRENT_TX_ANTENNA, 1),
+	WCN36XX_CFG_VAL(CURRENT_RX_ANTENNA, 1),
+	WCN36XX_CFG_VAL(LOW_GAIN_OVERRIDE, 0),
+	WCN36XX_CFG_VAL(POWER_STATE_PER_CHAIN, 785),
+	WCN36XX_CFG_VAL(CAL_PERIOD, 5),
+	WCN36XX_CFG_VAL(CAL_CONTROL, 1),
+	WCN36XX_CFG_VAL(PROXIMITY, 0),
+	WCN36XX_CFG_VAL(NETWORK_DENSITY, 3),
+	WCN36XX_CFG_VAL(MAX_MEDIUM_TIME, 6000),
+	WCN36XX_CFG_VAL(MAX_MPDUS_IN_AMPDU, 64),
+	WCN36XX_CFG_VAL(RTS_THRESHOLD, 2347),
+	WCN36XX_CFG_VAL(SHORT_RETRY_LIMIT, 6),
+	WCN36XX_CFG_VAL(LONG_RETRY_LIMIT, 6),
+	WCN36XX_CFG_VAL(FRAGMENTATION_THRESHOLD, 8000),
+	WCN36XX_CFG_VAL(DYNAMIC_THRESHOLD_ZERO, 5),
+	WCN36XX_CFG_VAL(DYNAMIC_THRESHOLD_ONE, 10),
+	WCN36XX_CFG_VAL(DYNAMIC_THRESHOLD_TWO, 15),
+	WCN36XX_CFG_VAL(FIXED_RATE, 0),
+	WCN36XX_CFG_VAL(RETRYRATE_POLICY, 4),
+	WCN36XX_CFG_VAL(RETRYRATE_SECONDARY, 0),
+	WCN36XX_CFG_VAL(RETRYRATE_TERTIARY, 0),
+	WCN36XX_CFG_VAL(FORCE_POLICY_PROTECTION, 5),
+	WCN36XX_CFG_VAL(FIXED_RATE_MULTICAST_24GHZ, 1),
+	WCN36XX_CFG_VAL(FIXED_RATE_MULTICAST_5GHZ, 5),
+	WCN36XX_CFG_VAL(DEFAULT_RATE_INDEX_5GHZ, 5),
+	WCN36XX_CFG_VAL(MAX_BA_SESSIONS, 40),
+	WCN36XX_CFG_VAL(PS_DATA_INACTIVITY_TIMEOUT, 200),
+	WCN36XX_CFG_VAL(PS_ENABLE_BCN_FILTER, 1),
+	WCN36XX_CFG_VAL(PS_ENABLE_RSSI_MONITOR, 1),
+	WCN36XX_CFG_VAL(NUM_BEACON_PER_RSSI_AVERAGE, 20),
+	WCN36XX_CFG_VAL(STATS_PERIOD, 10),
+	WCN36XX_CFG_VAL(CFP_MAX_DURATION, 30000),
+	WCN36XX_CFG_VAL(FRAME_TRANS_ENABLED, 0),
+	WCN36XX_CFG_VAL(BA_THRESHOLD_HIGH, 128),
+	WCN36XX_CFG_VAL(MAX_BA_BUFFERS, 2560),
+	WCN36XX_CFG_VAL(DYNAMIC_PS_POLL_VALUE, 0),
+	WCN36XX_CFG_VAL(TX_PWR_CTRL_ENABLE, 1),
+	WCN36XX_CFG_VAL(ENABLE_CLOSE_LOOP, 1),
+	WCN36XX_CFG_VAL(ENABLE_LPWR_IMG_TRANSITION, 0),
+	WCN36XX_CFG_VAL(MAX_ASSOC_LIMIT, 10),
+	WCN36XX_CFG_VAL(ENABLE_MCC_ADAPTIVE_SCHEDULER, 0),
+};
+
 static int put_cfg_tlv_u32(struct wcn36xx *wcn, size_t *len, u32 id, u32 value)
 {
 	struct wcn36xx_hal_cfg *entry;
@@ -357,8 +412,10 @@
 
 int wcn36xx_smd_start(struct wcn36xx *wcn)
 {
-	struct wcn36xx_hal_mac_start_req_msg msg_body;
+	struct wcn36xx_hal_mac_start_req_msg msg_body, *body;
 	int ret = 0;
+	int i;
+	size_t len;
 
 	mutex_lock(&wcn->hal_mutex);
 	INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_REQ);
@@ -368,10 +425,22 @@
 
 	PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
 
+	body = (struct wcn36xx_hal_mac_start_req_msg *)wcn->hal_buf;
+	len = body->header.len;
+
+	for (i = 0; i < ARRAY_SIZE(wcn36xx_cfg_vals); i++) {
+		ret = put_cfg_tlv_u32(wcn, &len, wcn36xx_cfg_vals[i].cfg_id,
+				      wcn36xx_cfg_vals[i].value);
+		if (ret)
+			goto out;
+	}
+	body->header.len = len;
+	body->params.len = len - sizeof(*body);
+
 	wcn36xx_dbg(WCN36XX_DBG_HAL, "hal start type %d\n",
 		    msg_body.params.type);
 
-	ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+	ret = wcn36xx_smd_send_and_wait(wcn, body->header.len);
 	if (ret) {
 		wcn36xx_err("Sending hal_start failed\n");
 		goto out;
@@ -1632,7 +1701,7 @@
 	} else if (packet_type == WCN36XX_HAL_KEEP_ALIVE_UNSOLICIT_ARP_RSP) {
 		/* TODO: it also support ARP response type */
 	} else {
-		wcn36xx_warn("unknow keep alive packet type %d\n", packet_type);
+		wcn36xx_warn("unknown keep alive packet type %d\n", packet_type);
 		ret = -EINVAL;
 		goto out;
 	}
diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c
index 32bb26a..9bec823 100644
--- a/drivers/net/wireless/ath/wcn36xx/txrx.c
+++ b/drivers/net/wireless/ath/wcn36xx/txrx.c
@@ -93,6 +93,7 @@
 		bd->pdu.mpdu_header_off;
 	bd->pdu.mpdu_len = len;
 	bd->pdu.tid = tid;
+	bd->pdu.bd_ssn = WCN36XX_TXBD_SSN_FILL_DPU_QOS;
 }
 
 static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn,
@@ -110,15 +111,54 @@
 	wcn36xx_warn("vif %pM not found\n", addr);
 	return NULL;
 }
+
+static void wcn36xx_tx_start_ampdu(struct wcn36xx *wcn,
+				   struct wcn36xx_sta *sta_priv,
+				   struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_sta *sta;
+	u8 *qc, tid;
+
+	if (!conf_is_ht(&wcn->hw->conf))
+		return;
+
+	sta = wcn36xx_priv_to_sta(sta_priv);
+
+	if (WARN_ON(!ieee80211_is_data_qos(hdr->frame_control)))
+		return;
+
+	if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
+		return;
+
+	qc = ieee80211_get_qos_ctl(hdr);
+	tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+
+	spin_lock(&sta_priv->ampdu_lock);
+	if (sta_priv->ampdu_state[tid] != WCN36XX_AMPDU_NONE)
+		goto out_unlock;
+
+	if (sta_priv->non_agg_frame_ct++ >= WCN36XX_AMPDU_START_THRESH) {
+		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
+		sta_priv->non_agg_frame_ct = 0;
+		ieee80211_start_tx_ba_session(sta, tid, 0);
+	}
+out_unlock:
+	spin_unlock(&sta_priv->ampdu_lock);
+}
+
 static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
 				struct wcn36xx *wcn,
 				struct wcn36xx_vif **vif_priv,
 				struct wcn36xx_sta *sta_priv,
-				struct ieee80211_hdr *hdr,
+				struct sk_buff *skb,
 				bool bcast)
 {
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct ieee80211_vif *vif = NULL;
 	struct wcn36xx_vif *__vif_priv = NULL;
+	bool is_data_qos;
+
 	bd->bd_rate = WCN36XX_BD_RATE_DATA;
 
 	/*
@@ -157,14 +197,26 @@
 		bd->ack_policy = 1;
 	}
 	*vif_priv = __vif_priv;
+
+	is_data_qos = ieee80211_is_data_qos(hdr->frame_control);
+
+	wcn36xx_set_tx_pdu(bd,
+			   is_data_qos ?
+			   sizeof(struct ieee80211_qos_hdr) :
+			   sizeof(struct ieee80211_hdr_3addr),
+			   skb->len, sta_priv ? sta_priv->tid : 0);
+
+	if (sta_priv && is_data_qos)
+		wcn36xx_tx_start_ampdu(wcn, sta_priv, skb);
 }
 
 static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd,
 				struct wcn36xx *wcn,
 				struct wcn36xx_vif **vif_priv,
-				struct ieee80211_hdr *hdr,
+				struct sk_buff *skb,
 				bool bcast)
 {
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct wcn36xx_vif *__vif_priv =
 		get_vif_by_addr(wcn, hdr->addr2);
 	bd->sta_index = __vif_priv->self_sta_index;
@@ -198,6 +250,12 @@
 	} else
 		bd->queue_id = WCN36XX_TX_U_WQ_ID;
 	*vif_priv = __vif_priv;
+
+	wcn36xx_set_tx_pdu(bd,
+			   ieee80211_is_data_qos(hdr->frame_control) ?
+			   sizeof(struct ieee80211_qos_hdr) :
+			   sizeof(struct ieee80211_hdr_3addr),
+			   skb->len, WCN36XX_TID);
 }
 
 int wcn36xx_start_tx(struct wcn36xx *wcn,
@@ -237,7 +295,7 @@
 
 	bd->dpu_rf = WCN36XX_BMU_WQ_TX;
 
-	bd->tx_comp = info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS;
+	bd->tx_comp = !!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS);
 	if (bd->tx_comp) {
 		wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n");
 		spin_lock_irqsave(&wcn->dxe_lock, flags);
@@ -259,22 +317,11 @@
 	}
 
 	/* Data frames served first*/
-	if (is_low) {
-		wcn36xx_set_tx_data(bd, wcn, &vif_priv, sta_priv, hdr, bcast);
-		wcn36xx_set_tx_pdu(bd,
-			   ieee80211_is_data_qos(hdr->frame_control) ?
-			   sizeof(struct ieee80211_qos_hdr) :
-			   sizeof(struct ieee80211_hdr_3addr),
-			   skb->len, sta_priv ? sta_priv->tid : 0);
-	} else {
+	if (is_low)
+		wcn36xx_set_tx_data(bd, wcn, &vif_priv, sta_priv, skb, bcast);
+	else
 		/* MGMT and CTRL frames are handeld here*/
-		wcn36xx_set_tx_mgmt(bd, wcn, &vif_priv, hdr, bcast);
-		wcn36xx_set_tx_pdu(bd,
-			   ieee80211_is_data_qos(hdr->frame_control) ?
-			   sizeof(struct ieee80211_qos_hdr) :
-			   sizeof(struct ieee80211_hdr_3addr),
-			   skb->len, WCN36XX_TID);
-	}
+		wcn36xx_set_tx_mgmt(bd, wcn, &vif_priv, skb, bcast);
 
 	buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32));
 	bd->tx_bd_sign = 0xbdbdbdbd;
diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.h b/drivers/net/wireless/ath/wcn36xx/txrx.h
index bbfbcf8..032216e 100644
--- a/drivers/net/wireless/ath/wcn36xx/txrx.h
+++ b/drivers/net/wireless/ath/wcn36xx/txrx.h
@@ -32,6 +32,12 @@
 #define WCN36XX_BD_RATE_MGMT 2
 #define WCN36XX_BD_RATE_CTRL 3
 
+enum wcn36xx_txbd_ssn_type {
+	WCN36XX_TXBD_SSN_FILL_HOST = 0,
+	WCN36XX_TXBD_SSN_FILL_DPU_NON_QOS = 1,
+	WCN36XX_TXBD_SSN_FILL_DPU_QOS = 2,
+};
+
 struct wcn36xx_pdu {
 	u32	dpu_fb:8;
 	u32	adu_fb:8;
@@ -50,7 +56,8 @@
 	/* 0x0c*/
 	u32	reserved4:8;
 	u32	tid:4;
-	u32	reserved3:4;
+	u32	bd_ssn:2;
+	u32	reserved3:2;
 	u32	mpdu_len:16;
 };
 
diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
index 1ae5908..eb03be6 100644
--- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
+++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
@@ -32,6 +32,9 @@
 #define WLAN_NV_FILE               "wlan/prima/WCNSS_qcom_wlan_nv.bin"
 #define WCN36XX_AGGR_BUFFER_SIZE 64
 
+/* How many frames until we start a-mpdu TX session */
+#define WCN36XX_AMPDU_START_THRESH	20
+
 extern unsigned int wcn36xx_dbg_mask;
 
 enum wcn36xx_debug_mask {
@@ -74,6 +77,13 @@
 			       buf, len, false);		\
 } while (0)
 
+enum wcn36xx_ampdu_state {
+	WCN36XX_AMPDU_NONE,
+	WCN36XX_AMPDU_INIT,
+	WCN36XX_AMPDU_START,
+	WCN36XX_AMPDU_OPERATIONAL,
+};
+
 #define WCN36XX_HW_CHANNEL(__wcn) (__wcn->hw->conf.chandef.chan->hw_value)
 #define WCN36XX_BAND(__wcn) (__wcn->hw->conf.chandef.chan->band)
 #define WCN36XX_CENTER_FREQ(__wcn) (__wcn->hw->conf.chandef.chan->center_freq)
@@ -165,6 +175,10 @@
 	bool is_data_encrypted;
 	/* Rates */
 	struct wcn36xx_hal_supported_rates supported_rates;
+
+	spinlock_t ampdu_lock;		/* protects next two fields */
+	enum wcn36xx_ampdu_state ampdu_state[16];
+	int non_agg_frame_ct;
 };
 struct wcn36xx_dxe_ch;
 struct wcn36xx {
@@ -243,4 +257,10 @@
 }
 void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates);
 
+static inline
+struct ieee80211_sta *wcn36xx_priv_to_sta(struct wcn36xx_sta *sta_priv)
+{
+	return container_of((void *)sta_priv, struct ieee80211_sta, drv_priv);
+}
+
 #endif	/* _WCN36XX_H_ */
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig
index 821897d..b5cc906 100644
--- a/drivers/net/wireless/ath/wil6210/Kconfig
+++ b/drivers/net/wireless/ath/wil6210/Kconfig
@@ -40,12 +40,3 @@
 	  option if you are interested in debugging the driver.
 
 	  If unsure, say Y to make it easier to debug problems.
-
-config WIL6210_PLATFORM_MSM
-	bool "wil6210 MSM platform specific support"
-	depends on WIL6210
-	depends on ARCH_MSM
-	default y
-	---help---
-	  Say Y here to enable wil6210 driver support for MSM
-	  platform specific features
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
index 54394ea..565dff9 100644
--- a/drivers/net/wireless/ath/wil6210/Makefile
+++ b/drivers/net/wireless/ath/wil6210/Makefile
@@ -14,7 +14,6 @@
 wil6210-y += fw.o
 wil6210-$(CPTCFG_WIL6210_TRACING) += trace.o
 wil6210-y += wil_platform.o
-wil6210-$(CPTCFG_WIL6210_PLATFORM_MSM) += wil_platform_msm.o
 wil6210-y += ethtool.o
 
 # for tracing framework to find trace.h
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 38332a6..b971726 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -14,6 +14,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <linux/etherdevice.h>
 #include "wil6210.h"
 #include "wmi.h"
 
@@ -142,14 +143,14 @@
 
 	sinfo->generation = wil->sinfo_gen;
 
-	sinfo->filled = STATION_INFO_RX_BYTES |
-			STATION_INFO_TX_BYTES |
-			STATION_INFO_RX_PACKETS |
-			STATION_INFO_TX_PACKETS |
-			STATION_INFO_RX_BITRATE |
-			STATION_INFO_TX_BITRATE |
-			STATION_INFO_RX_DROP_MISC |
-			STATION_INFO_TX_FAILED;
+	sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) |
+			BIT(NL80211_STA_INFO_TX_BYTES) |
+			BIT(NL80211_STA_INFO_RX_PACKETS) |
+			BIT(NL80211_STA_INFO_TX_PACKETS) |
+			BIT(NL80211_STA_INFO_RX_BITRATE) |
+			BIT(NL80211_STA_INFO_TX_BITRATE) |
+			BIT(NL80211_STA_INFO_RX_DROP_MISC) |
+			BIT(NL80211_STA_INFO_TX_FAILED);
 
 	sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
 	sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs);
@@ -162,8 +163,8 @@
 	sinfo->tx_packets = stats->tx_packets;
 	sinfo->tx_failed = stats->tx_errors;
 
-	if (test_bit(wil_status_fwconnected, &wil->status)) {
-		sinfo->filled |= STATION_INFO_SIGNAL;
+	if (test_bit(wil_status_fwconnected, wil->status)) {
+		sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
 		sinfo->signal = reply.evt.sqi;
 	}
 
@@ -217,7 +218,7 @@
 	if (cid < 0)
 		return -ENOENT;
 
-	memcpy(mac, wil->sta[cid].addr, ETH_ALEN);
+	ether_addr_copy(mac, wil->sta[cid].addr);
 	wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid);
 
 	rc = wil_cid_fill_sinfo(wil, cid, sinfo);
@@ -282,7 +283,7 @@
 	}
 
 	/* FW don't support scan after connection attempt */
-	if (test_bit(wil_status_dontscan, &wil->status)) {
+	if (test_bit(wil_status_dontscan, wil->status)) {
 		wil_err(wil, "Can't scan now\n");
 		return -EBUSY;
 	}
@@ -334,6 +335,30 @@
 	return rc;
 }
 
+static void wil_print_crypto(struct wil6210_priv *wil,
+			     struct cfg80211_crypto_settings *c)
+{
+	int i, n;
+
+	wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
+		     c->wpa_versions, c->cipher_group);
+	wil_dbg_misc(wil, "Pairwise ciphers [%d] {\n", c->n_ciphers_pairwise);
+	n = min_t(int, c->n_ciphers_pairwise, ARRAY_SIZE(c->ciphers_pairwise));
+	for (i = 0; i < n; i++)
+		wil_dbg_misc(wil, "  [%d] = 0x%08x\n", i,
+			     c->ciphers_pairwise[i]);
+	wil_dbg_misc(wil, "}\n");
+	wil_dbg_misc(wil, "AKM suites [%d] {\n", c->n_akm_suites);
+	n = min_t(int, c->n_akm_suites, ARRAY_SIZE(c->akm_suites));
+	for (i = 0; i < n; i++)
+		wil_dbg_misc(wil, "  [%d] = 0x%08x\n", i,
+			     c->akm_suites[i]);
+	wil_dbg_misc(wil, "}\n");
+	wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
+		     c->control_port, be16_to_cpu(c->control_port_ethertype),
+		     c->control_port_no_encrypt);
+}
+
 static void wil_print_connect_params(struct wil6210_priv *wil,
 				     struct cfg80211_connect_params *sme)
 {
@@ -348,6 +373,7 @@
 		print_hex_dump(KERN_INFO, "  SSID: ", DUMP_PREFIX_OFFSET,
 			       16, 1, sme->ssid, sme->ssid_len, true);
 	wil_info(wil, "  Privacy: %s\n", sme->privacy ? "secure" : "open");
+	wil_print_crypto(wil, &sme->crypto);
 }
 
 static int wil_cfg80211_connect(struct wiphy *wiphy,
@@ -362,15 +388,29 @@
 	int ch;
 	int rc = 0;
 
-	if (test_bit(wil_status_fwconnecting, &wil->status) ||
-	    test_bit(wil_status_fwconnected, &wil->status))
+	wil_print_connect_params(wil, sme);
+
+	if (test_bit(wil_status_fwconnecting, wil->status) ||
+	    test_bit(wil_status_fwconnected, wil->status))
 		return -EALREADY;
 
-	wil_print_connect_params(wil, sme);
+	if (sme->ie_len > WMI_MAX_IE_LEN) {
+		wil_err(wil, "IE too large (%td bytes)\n", sme->ie_len);
+		return -ERANGE;
+	}
+
+	rsn_eid = sme->ie ?
+			cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) :
+			NULL;
+
+	if (sme->privacy && !rsn_eid) {
+		wil_err(wil, "Missing RSN IE for secure connection\n");
+		return -EINVAL;
+	}
 
 	bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
 			       sme->ssid, sme->ssid_len,
-			       WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+			       IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY);
 	if (!bss) {
 		wil_err(wil, "Unable to find BSS\n");
 		return -ENOENT;
@@ -382,17 +422,9 @@
 		rc = -ENOENT;
 		goto out;
 	}
+	wil->privacy = sme->privacy;
 
-	rsn_eid = sme->ie ?
-			cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) :
-			NULL;
-	if (rsn_eid) {
-		if (sme->ie_len > WMI_MAX_IE_LEN) {
-			rc = -ERANGE;
-			wil_err(wil, "IE too large (%td bytes)\n",
-				sme->ie_len);
-			goto out;
-		}
+	if (wil->privacy) {
 		/* For secure assoc, send WMI_DELETE_CIPHER_KEY_CMD */
 		rc = wmi_del_cipher_key(wil, 0, bss->bssid);
 		if (rc) {
@@ -425,7 +457,7 @@
 			bss->capability);
 		goto out;
 	}
-	if (rsn_eid) {
+	if (wil->privacy) {
 		conn.dot11_auth_mode = WMI_AUTH11_SHARED;
 		conn.auth_mode = WMI_AUTH_WPA2_PSK;
 		conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP;
@@ -447,18 +479,19 @@
 	}
 	conn.channel = ch - 1;
 
-	memcpy(conn.bssid, bss->bssid, ETH_ALEN);
-	memcpy(conn.dst_mac, bss->bssid, ETH_ALEN);
+	ether_addr_copy(conn.bssid, bss->bssid);
+	ether_addr_copy(conn.dst_mac, bss->bssid);
 
-	set_bit(wil_status_fwconnecting, &wil->status);
+	set_bit(wil_status_fwconnecting, wil->status);
 
 	rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
 	if (rc == 0) {
+		netif_carrier_on(ndev);
 		/* Connect can take lots of time */
 		mod_timer(&wil->connect_timer,
 			  jiffies + msecs_to_jiffies(2000));
 	} else {
-		clear_bit(wil_status_fwconnecting, &wil->status);
+		clear_bit(wil_status_fwconnecting, wil->status);
 	}
 
  out:
@@ -618,18 +651,6 @@
 			     b->assocresp_ies, b->assocresp_ies_len);
 }
 
-static void wil_print_crypto(struct wil6210_priv *wil,
-			     struct cfg80211_crypto_settings *c)
-{
-	wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
-		     c->wpa_versions, c->cipher_group);
-	wil_dbg_misc(wil, "Pairwise ciphers [%d]\n", c->n_ciphers_pairwise);
-	wil_dbg_misc(wil, "AKM suites [%d]\n", c->n_akm_suites);
-	wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
-		     c->control_port, be16_to_cpu(c->control_port_ethertype),
-		     c->control_port_no_encrypt);
-}
-
 static int wil_fix_bcon(struct wil6210_priv *wil,
 			struct cfg80211_beacon_data *bcon)
 {
@@ -755,15 +776,24 @@
 	wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len,
 		   bcon->assocresp_ies);
 
-	wil->secure_pcp = info->privacy;
+	wil->privacy = info->privacy;
+
+	netif_carrier_on(ndev);
 
 	rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype,
 			   channel->hw_value);
 	if (rc)
-		goto out;
+		goto err_pcp_start;
 
-	netif_carrier_on(ndev);
+	rc = wil_bcast_init(wil);
+	if (rc)
+		goto err_bcast;
 
+	goto out; /* success */
+err_bcast:
+	wmi_pcp_stop(wil);
+err_pcp_start:
+	netif_carrier_off(ndev);
 out:
 	mutex_unlock(&wil->mutex);
 	return rc;
@@ -772,23 +802,26 @@
 static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
 				struct net_device *ndev)
 {
-	int rc, rc1;
 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
 
 	wil_dbg_misc(wil, "%s()\n", __func__);
 
+	netif_carrier_off(ndev);
 	wil_set_recovery_state(wil, fw_recovery_idle);
 
 	mutex_lock(&wil->mutex);
 
-	rc = wmi_pcp_stop(wil);
+	wmi_pcp_stop(wil);
 
 	__wil_down(wil);
-	rc1 = __wil_up(wil);
+	__wil_up(wil);
 
 	mutex_unlock(&wil->mutex);
 
-	return min(rc, rc1);
+	/* some functions above might fail (e.g. __wil_up). Nevertheless, we
+	 * return success because AP has stopped
+	 */
+	return 0;
 }
 
 static int wil_cfg80211_del_station(struct wiphy *wiphy,
@@ -804,6 +837,111 @@
 	return 0;
 }
 
+/* probe_client handling */
+static void wil_probe_client_handle(struct wil6210_priv *wil,
+				    struct wil_probe_client_req *req)
+{
+	struct net_device *ndev = wil_to_ndev(wil);
+	struct wil_sta_info *sta = &wil->sta[req->cid];
+	/* assume STA is alive if it is still connected,
+	 * else FW will disconnect it
+	 */
+	bool alive = (sta->status == wil_sta_connected);
+
+	cfg80211_probe_status(ndev, sta->addr, req->cookie, alive, GFP_KERNEL);
+}
+
+static struct list_head *next_probe_client(struct wil6210_priv *wil)
+{
+	struct list_head *ret = NULL;
+
+	mutex_lock(&wil->probe_client_mutex);
+
+	if (!list_empty(&wil->probe_client_pending)) {
+		ret = wil->probe_client_pending.next;
+		list_del(ret);
+	}
+
+	mutex_unlock(&wil->probe_client_mutex);
+
+	return ret;
+}
+
+void wil_probe_client_worker(struct work_struct *work)
+{
+	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
+						probe_client_worker);
+	struct wil_probe_client_req *req;
+	struct list_head *lh;
+
+	while ((lh = next_probe_client(wil)) != NULL) {
+		req = list_entry(lh, struct wil_probe_client_req, list);
+
+		wil_probe_client_handle(wil, req);
+		kfree(req);
+	}
+}
+
+void wil_probe_client_flush(struct wil6210_priv *wil)
+{
+	struct wil_probe_client_req *req, *t;
+
+	wil_dbg_misc(wil, "%s()\n", __func__);
+
+	mutex_lock(&wil->probe_client_mutex);
+
+	list_for_each_entry_safe(req, t, &wil->probe_client_pending, list) {
+		list_del(&req->list);
+		kfree(req);
+	}
+
+	mutex_unlock(&wil->probe_client_mutex);
+}
+
+static int wil_cfg80211_probe_client(struct wiphy *wiphy,
+				     struct net_device *dev,
+				     const u8 *peer, u64 *cookie)
+{
+	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+	struct wil_probe_client_req *req;
+	int cid = wil_find_cid(wil, peer);
+
+	wil_dbg_misc(wil, "%s(%pM => CID %d)\n", __func__, peer, cid);
+
+	if (cid < 0)
+		return -ENOLINK;
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->cid = cid;
+	req->cookie = cid;
+
+	mutex_lock(&wil->probe_client_mutex);
+	list_add_tail(&req->list, &wil->probe_client_pending);
+	mutex_unlock(&wil->probe_client_mutex);
+
+	*cookie = req->cookie;
+	queue_work(wil->wq_service, &wil->probe_client_worker);
+	return 0;
+}
+
+static int wil_cfg80211_change_bss(struct wiphy *wiphy,
+				   struct net_device *dev,
+				   struct bss_parameters *params)
+{
+	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+	if (params->ap_isolate >= 0) {
+		wil_dbg_misc(wil, "%s(ap_isolate %d => %d)\n", __func__,
+			     wil->ap_isolate, params->ap_isolate);
+		wil->ap_isolate = params->ap_isolate;
+	}
+
+	return 0;
+}
+
 static struct cfg80211_ops wil_cfg80211_ops = {
 	.scan = wil_cfg80211_scan,
 	.connect = wil_cfg80211_connect,
@@ -823,6 +961,8 @@
 	.start_ap = wil_cfg80211_start_ap,
 	.stop_ap = wil_cfg80211_stop_ap,
 	.del_station = wil_cfg80211_del_station,
+	.probe_client = wil_cfg80211_probe_client,
+	.change_bss = wil_cfg80211_change_bss,
 };
 
 static void wil_wiphy_init(struct wiphy *wiphy)
@@ -854,6 +994,7 @@
 	wiphy->cipher_suites = wil_cipher_suites;
 	wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
 	wiphy->mgmt_stypes = wil_mgmt_stypes;
+	wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
 }
 
 struct wireless_dev *wil_cfg80211_init(struct device *dev)
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 4e6e145..bbc22d8 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -29,6 +29,7 @@
 static u32 mem_addr;
 static u32 dbg_txdesc_index;
 static u32 dbg_vring_index; /* 24+ for Rx, 0..23 for Tx */
+u32 vring_idle_trsh = 16; /* HW fetches up to 16 descriptors at once */
 
 enum dbg_off_type {
 	doff_u32 = 0,
@@ -50,6 +51,7 @@
 			    char _s, char _h)
 {
 	void __iomem *x = wmi_addr(wil, vring->hwtail);
+	u32 v;
 
 	seq_printf(s, "VRING %s = {\n", name);
 	seq_printf(s, "  pa     = %pad\n", &vring->pa);
@@ -58,10 +60,12 @@
 	seq_printf(s, "  swtail = %d\n", vring->swtail);
 	seq_printf(s, "  swhead = %d\n", vring->swhead);
 	seq_printf(s, "  hwtail = [0x%08x] -> ", vring->hwtail);
-	if (x)
-		seq_printf(s, "0x%08x\n", ioread32(x));
-	else
+	if (x) {
+		v = ioread32(x);
+		seq_printf(s, "0x%08x = %d\n", v, v);
+	} else {
 		seq_puts(s, "???\n");
+	}
 
 	if (vring->va && (vring->size < 1025)) {
 		uint i;
@@ -99,20 +103,36 @@
 				   % vring->size;
 			int avail = vring->size - used - 1;
 			char name[10];
+			char sidle[10];
 			/* performance monitoring */
 			cycles_t now = get_cycles();
-			cycles_t idle = txdata->idle * 100;
-			cycles_t total = now - txdata->begin;
+			uint64_t idle = txdata->idle * 100;
+			uint64_t total = now - txdata->begin;
 
-			do_div(idle, total);
+			if (total != 0) {
+				do_div(idle, total);
+				snprintf(sidle, sizeof(sidle), "%3d%%",
+					 (int)idle);
+			} else {
+				snprintf(sidle, sizeof(sidle), "N/A");
+			}
 			txdata->begin = now;
 			txdata->idle = 0ULL;
 
 			snprintf(name, sizeof(name), "tx_%2d", i);
 
-			seq_printf(s, "\n%pM CID %d TID %d [%3d|%3d] idle %3d%%\n",
-				   wil->sta[cid].addr, cid, tid, used, avail,
-				   (int)idle);
+			if (cid < WIL6210_MAX_CID)
+				seq_printf(s,
+					   "\n%pM CID %d TID %d BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n",
+					   wil->sta[cid].addr, cid, tid,
+					   txdata->agg_wsize,
+					   txdata->agg_timeout,
+					   txdata->agg_amsdu ? "+" : "-",
+					   used, avail, sidle);
+			else
+				seq_printf(s,
+					   "\nBroadcast [%3d|%3d] idle %s\n",
+					   used, avail, sidle);
 
 			wil_print_vring(s, wil, name, vring, '_', 'H');
 		}
@@ -384,24 +404,67 @@
 	return 0;
 }
 
-static const struct dbg_off itr_cnt_off[] = {
+static const struct dbg_off lgc_itr_cnt_off[] = {
 	{"TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_TRSH), doff_io32},
 	{"DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_DATA), doff_io32},
 	{"CTL",  S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_CRL), doff_io32},
 	{},
 };
 
+static const struct dbg_off tx_itr_cnt_off[] = {
+	{"TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_CNT_TRSH),
+	 doff_io32},
+	{"DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_CNT_DATA),
+	 doff_io32},
+	{"CTL",  S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_CNT_CTL),
+	 doff_io32},
+	{"IDL_TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_TRSH),
+	 doff_io32},
+	{"IDL_DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_DATA),
+	 doff_io32},
+	{"IDL_CTL",  S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_CTL),
+	 doff_io32},
+	{},
+};
+
+static const struct dbg_off rx_itr_cnt_off[] = {
+	{"TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_CNT_TRSH),
+	 doff_io32},
+	{"DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_CNT_DATA),
+	 doff_io32},
+	{"CTL",  S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_CNT_CTL),
+	 doff_io32},
+	{"IDL_TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_TRSH),
+	 doff_io32},
+	{"IDL_DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_DATA),
+	 doff_io32},
+	{"IDL_CTL",  S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_CTL),
+	 doff_io32},
+	{},
+};
+
 static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
 					  struct dentry *parent)
 {
-	struct dentry *d = debugfs_create_dir("ITR_CNT", parent);
+	struct dentry *d, *dtx, *drx;
 
+	d = debugfs_create_dir("ITR_CNT", parent);
 	if (IS_ERR_OR_NULL(d))
 		return -ENODEV;
 
-	wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr,
-				    itr_cnt_off);
+	dtx = debugfs_create_dir("TX", d);
+	drx = debugfs_create_dir("RX", d);
+	if (IS_ERR_OR_NULL(dtx) || IS_ERR_OR_NULL(drx))
+		return -ENODEV;
 
+	wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr,
+				    lgc_itr_cnt_off);
+
+	wil6210_debugfs_init_offset(wil, dtx, (void * __force)wil->csr,
+				    tx_itr_cnt_off);
+
+	wil6210_debugfs_init_offset(wil, drx, (void * __force)wil->csr,
+				    rx_itr_cnt_off);
 	return 0;
 }
 
@@ -500,7 +563,7 @@
 	dev_close(ndev);
 	ndev->flags &= ~IFF_UP;
 	rtnl_unlock();
-	wil_reset(wil);
+	wil_reset(wil, true);
 
 	return len;
 }
@@ -558,6 +621,87 @@
 	.open  = simple_open,
 };
 
+/* block ack control, write:
+ * - "add <ringid> <agg_size> <timeout>" to trigger ADDBA
+ * - "del_tx <ringid> <reason>" to trigger DELBA for Tx side
+ * - "del_rx <CID> <TID> <reason>" to trigger DELBA for Rx side
+ */
+static ssize_t wil_write_back(struct file *file, const char __user *buf,
+			      size_t len, loff_t *ppos)
+{
+	struct wil6210_priv *wil = file->private_data;
+	int rc;
+	char *kbuf = kmalloc(len + 1, GFP_KERNEL);
+	char cmd[9];
+	int p1, p2, p3;
+
+	if (!kbuf)
+		return -ENOMEM;
+
+	rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
+	if (rc != len) {
+		kfree(kbuf);
+		return rc >= 0 ? -EIO : rc;
+	}
+
+	kbuf[len] = '\0';
+	rc = sscanf(kbuf, "%8s %d %d %d", cmd, &p1, &p2, &p3);
+	kfree(kbuf);
+
+	if (rc < 0)
+		return rc;
+	if (rc < 2)
+		return -EINVAL;
+
+	if (0 == strcmp(cmd, "add")) {
+		if (rc < 3) {
+			wil_err(wil, "BACK: add require at least 2 params\n");
+			return -EINVAL;
+		}
+		if (rc < 4)
+			p3 = 0;
+		wmi_addba(wil, p1, p2, p3);
+	} else if (0 == strcmp(cmd, "del_tx")) {
+		if (rc < 3)
+			p2 = WLAN_REASON_QSTA_LEAVE_QBSS;
+		wmi_delba_tx(wil, p1, p2);
+	} else if (0 == strcmp(cmd, "del_rx")) {
+		if (rc < 3) {
+			wil_err(wil,
+				"BACK: del_rx require at least 2 params\n");
+			return -EINVAL;
+		}
+		if (rc < 4)
+			p3 = WLAN_REASON_QSTA_LEAVE_QBSS;
+		wmi_delba_rx(wil, mk_cidxtid(p1, p2), p3);
+	} else {
+		wil_err(wil, "BACK: Unrecognized command \"%s\"\n", cmd);
+		return -EINVAL;
+	}
+
+	return len;
+}
+
+static ssize_t wil_read_back(struct file *file, char __user *user_buf,
+			     size_t count, loff_t *ppos)
+{
+	static const char text[] = "block ack control, write:\n"
+	" - \"add <ringid> <agg_size> <timeout>\" to trigger ADDBA\n"
+	"If missing, <timeout> defaults to 0\n"
+	" - \"del_tx <ringid> <reason>\" to trigger DELBA for Tx side\n"
+	" - \"del_rx <CID> <TID> <reason>\" to trigger DELBA for Rx side\n"
+	"If missing, <reason> set to \"STA_LEAVING\" (36)\n";
+
+	return simple_read_from_buffer(user_buf, count, ppos, text,
+				       sizeof(text));
+}
+
+static const struct file_operations fops_back = {
+	.read = wil_read_back,
+	.write = wil_write_back,
+	.open  = simple_open,
+};
+
 /*---tx_mgmt---*/
 /* Write mgmt frame to this file to send it */
 static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
@@ -1116,7 +1260,8 @@
 	int i;
 	u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size;
 
-	seq_printf(s, "0x%03x [", r->head_seq_num);
+	seq_printf(s, "([%2d] %3d TU) 0x%03x [", r->buf_size, r->timeout,
+		   r->head_seq_num);
 	for (i = 0; i < r->buf_size; i++) {
 		if (i == index)
 			seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|');
@@ -1127,10 +1272,10 @@
 }
 
 static int wil_sta_debugfs_show(struct seq_file *s, void *data)
+__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
 {
 	struct wil6210_priv *wil = s->private;
 	int i, tid;
-	unsigned long flags;
 
 	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
 		struct wil_sta_info *p = &wil->sta[i];
@@ -1151,7 +1296,7 @@
 			   (p->data_port_open ? " data_port_open" : ""));
 
 		if (p->status == wil_sta_connected) {
-			spin_lock_irqsave(&p->tid_rx_lock, flags);
+			spin_lock_bh(&p->tid_rx_lock);
 			for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
 				struct wil_tid_ampdu_rx *r = p->tid_rx[tid];
 
@@ -1160,7 +1305,7 @@
 					wil_print_rxtid(s, r);
 				}
 			}
-			spin_unlock_irqrestore(&p->tid_rx_lock, flags);
+			spin_unlock_bh(&p->tid_rx_lock);
 		}
 	}
 
@@ -1217,6 +1362,7 @@
 	{"rxon",		  S_IWUSR,	&fops_rxon},
 	{"tx_mgmt",		  S_IWUSR,	&fops_txmgmt},
 	{"wmi_send",		  S_IWUSR,	&fops_wmi},
+	{"back",	S_IRUGO | S_IWUSR,	&fops_back},
 	{"temp",	S_IRUGO,		&fops_temp},
 	{"freq",	S_IRUGO,		&fops_freq},
 	{"link",	S_IRUGO,		&fops_link},
@@ -1260,11 +1406,12 @@
 
 /* fields in struct wil6210_priv */
 static const struct dbg_off dbg_wil_off[] = {
-	WIL_FIELD(secure_pcp,	S_IRUGO | S_IWUSR,	doff_u32),
-	WIL_FIELD(status,	S_IRUGO | S_IWUSR,	doff_ulong),
+	WIL_FIELD(privacy,	S_IRUGO,		doff_u32),
+	WIL_FIELD(status[0],	S_IRUGO | S_IWUSR,	doff_ulong),
 	WIL_FIELD(fw_version,	S_IRUGO,		doff_u32),
 	WIL_FIELD(hw_version,	S_IRUGO,		doff_x32),
 	WIL_FIELD(recovery_count, S_IRUGO,		doff_u32),
+	WIL_FIELD(ap_isolate,	S_IRUGO,		doff_u32),
 	{},
 };
 
@@ -1280,6 +1427,8 @@
 	{"desc_index",	S_IRUGO | S_IWUSR, (ulong)&dbg_txdesc_index, doff_u32},
 	{"vring_index",	S_IRUGO | S_IWUSR, (ulong)&dbg_vring_index, doff_u32},
 	{"mem_addr",	S_IRUGO | S_IWUSR, (ulong)&mem_addr, doff_u32},
+	{"vring_idle_trsh", S_IRUGO | S_IWUSR, (ulong)&vring_idle_trsh,
+	 doff_u32},
 	{},
 };
 
diff --git a/drivers/net/wireless/ath/wil6210/ethtool.c b/drivers/net/wireless/ath/wil6210/ethtool.c
index d686638..0ea695f 100644
--- a/drivers/net/wireless/ath/wil6210/ethtool.c
+++ b/drivers/net/wireless/ath/wil6210/ethtool.c
@@ -45,16 +45,27 @@
 				       struct ethtool_coalesce *cp)
 {
 	struct wil6210_priv *wil = ndev_to_wil(ndev);
-	u32 itr_en, itr_val = 0;
+	u32 tx_itr_en, tx_itr_val = 0;
+	u32 rx_itr_en, rx_itr_val = 0;
 
 	wil_dbg_misc(wil, "%s()\n", __func__);
 
-	itr_en = ioread32(wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL));
-	if (itr_en & BIT_DMA_ITR_CNT_CRL_EN)
-		itr_val = ioread32(wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
+	tx_itr_en = ioread32(wil->csr +
+			     HOSTADDR(RGF_DMA_ITR_TX_CNT_CTL));
+	if (tx_itr_en & BIT_DMA_ITR_TX_CNT_CTL_EN)
+		tx_itr_val =
+			ioread32(wil->csr +
+				 HOSTADDR(RGF_DMA_ITR_TX_CNT_TRSH));
 
-	cp->rx_coalesce_usecs = itr_val;
+	rx_itr_en = ioread32(wil->csr +
+			     HOSTADDR(RGF_DMA_ITR_RX_CNT_CTL));
+	if (rx_itr_en & BIT_DMA_ITR_RX_CNT_CTL_EN)
+		rx_itr_val =
+			ioread32(wil->csr +
+				 HOSTADDR(RGF_DMA_ITR_RX_CNT_TRSH));
 
+	cp->tx_coalesce_usecs = tx_itr_val;
+	cp->rx_coalesce_usecs = rx_itr_val;
 	return 0;
 }
 
@@ -63,22 +74,25 @@
 {
 	struct wil6210_priv *wil = ndev_to_wil(ndev);
 
-	wil_dbg_misc(wil, "%s(%d usec)\n", __func__, cp->rx_coalesce_usecs);
+	wil_dbg_misc(wil, "%s(rx %d usec, tx %d usec)\n", __func__,
+		     cp->rx_coalesce_usecs, cp->tx_coalesce_usecs);
 
 	if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
 		wil_dbg_misc(wil, "No IRQ coalescing in monitor mode\n");
 		return -EINVAL;
 	}
 
-	/* only @rx_coalesce_usecs supported, ignore
-	 * other parameters
+	/* only @rx_coalesce_usecs and @tx_coalesce_usecs supported,
+	 * ignore other parameters
 	 */
 
-	if (cp->rx_coalesce_usecs > WIL6210_ITR_TRSH_MAX)
+	if (cp->rx_coalesce_usecs > WIL6210_ITR_TRSH_MAX ||
+	    cp->tx_coalesce_usecs > WIL6210_ITR_TRSH_MAX)
 		goto out_bad;
 
-	wil->itr_trsh = cp->rx_coalesce_usecs;
-	wil_set_itr_trsh(wil);
+	wil->tx_max_burst_duration = cp->tx_coalesce_usecs;
+	wil->rx_max_burst_duration = cp->rx_coalesce_usecs;
+	wil_configure_interrupt_moderation(wil);
 
 	return 0;
 
diff --git a/drivers/net/wireless/ath/wil6210/fw.c b/drivers/net/wireless/ath/wil6210/fw.c
index 93c5cc1..4428345 100644
--- a/drivers/net/wireless/ath/wil6210/fw.c
+++ b/drivers/net/wireless/ath/wil6210/fw.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014-2015 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -20,6 +20,7 @@
 #include "fw.h"
 
 MODULE_FIRMWARE(WIL_FW_NAME);
+MODULE_FIRMWARE(WIL_FW2_NAME);
 
 /* target operations */
 /* register read */
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c
index d4acf93..157f5ef 100644
--- a/drivers/net/wireless/ath/wil6210/fw_inc.c
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014-2015 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -451,8 +451,6 @@
 		}
 		return -EINVAL;
 	}
-	/* Mark FW as loaded from host */
-	S(RGF_USER_USAGE_6, 1);
 
 	return rc;
 }
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 0b6ac97..803cf2f 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -102,7 +102,7 @@
 	iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
 		  HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
 
-	clear_bit(wil_status_irqen, &wil->status);
+	clear_bit(wil_status_irqen, wil->status);
 }
 
 void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
@@ -130,7 +130,7 @@
 {
 	wil_dbg_irq(wil, "%s()\n", __func__);
 
-	set_bit(wil_status_irqen, &wil->status);
+	set_bit(wil_status_irqen, wil->status);
 
 	iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr +
 		  HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
@@ -157,15 +157,64 @@
 	iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
 		  offsetof(struct RGF_ICR, ICC));
 
-	/* interrupt moderation parameters */
-	wil_set_itr_trsh(wil);
-
 	wil6210_unmask_irq_pseudo(wil);
 	wil6210_unmask_irq_tx(wil);
 	wil6210_unmask_irq_rx(wil);
 	wil6210_unmask_irq_misc(wil);
 }
 
+/* target write operation */
+#define W(a, v) do { iowrite32(v, wil->csr + HOSTADDR(a)); wmb(); } while (0)
+
+void wil_configure_interrupt_moderation(struct wil6210_priv *wil)
+{
+	wil_dbg_irq(wil, "%s()\n", __func__);
+
+	/* disable interrupt moderation for monitor
+	 * to get better timestamp precision
+	 */
+	if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR)
+		return;
+
+	/* Disable and clear tx counter before (re)configuration */
+	W(RGF_DMA_ITR_TX_CNT_CTL, BIT_DMA_ITR_TX_CNT_CTL_CLR);
+	W(RGF_DMA_ITR_TX_CNT_TRSH, wil->tx_max_burst_duration);
+	wil_info(wil, "set ITR_TX_CNT_TRSH = %d usec\n",
+		 wil->tx_max_burst_duration);
+	/* Configure TX max burst duration timer to use usec units */
+	W(RGF_DMA_ITR_TX_CNT_CTL,
+	  BIT_DMA_ITR_TX_CNT_CTL_EN | BIT_DMA_ITR_TX_CNT_CTL_EXT_TIC_SEL);
+
+	/* Disable and clear tx idle counter before (re)configuration */
+	W(RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_CLR);
+	W(RGF_DMA_ITR_TX_IDL_CNT_TRSH, wil->tx_interframe_timeout);
+	wil_info(wil, "set ITR_TX_IDL_CNT_TRSH = %d usec\n",
+		 wil->tx_interframe_timeout);
+	/* Configure TX max burst duration timer to use usec units */
+	W(RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_EN |
+				      BIT_DMA_ITR_TX_IDL_CNT_CTL_EXT_TIC_SEL);
+
+	/* Disable and clear rx counter before (re)configuration */
+	W(RGF_DMA_ITR_RX_CNT_CTL, BIT_DMA_ITR_RX_CNT_CTL_CLR);
+	W(RGF_DMA_ITR_RX_CNT_TRSH, wil->rx_max_burst_duration);
+	wil_info(wil, "set ITR_RX_CNT_TRSH = %d usec\n",
+		 wil->rx_max_burst_duration);
+	/* Configure TX max burst duration timer to use usec units */
+	W(RGF_DMA_ITR_RX_CNT_CTL,
+	  BIT_DMA_ITR_RX_CNT_CTL_EN | BIT_DMA_ITR_RX_CNT_CTL_EXT_TIC_SEL);
+
+	/* Disable and clear rx idle counter before (re)configuration */
+	W(RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_CLR);
+	W(RGF_DMA_ITR_RX_IDL_CNT_TRSH, wil->rx_interframe_timeout);
+	wil_info(wil, "set ITR_RX_IDL_CNT_TRSH = %d usec\n",
+		 wil->rx_interframe_timeout);
+	/* Configure TX max burst duration timer to use usec units */
+	W(RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_EN |
+				      BIT_DMA_ITR_RX_IDL_CNT_CTL_EXT_TIC_SEL);
+}
+
+#undef W
+
 static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
 {
 	struct wil6210_priv *wil = cookie;
@@ -177,7 +226,7 @@
 	trace_wil6210_irq_rx(isr);
 	wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
 
-	if (!isr) {
+	if (unlikely(!isr)) {
 		wil_err(wil, "spurious IRQ: RX\n");
 		return IRQ_NONE;
 	}
@@ -190,29 +239,31 @@
 	 * action is always the same - should empty the accumulated
 	 * packets from the RX ring.
 	 */
-	if (isr & (BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH)) {
+	if (likely(isr & (BIT_DMA_EP_RX_ICR_RX_DONE |
+			  BIT_DMA_EP_RX_ICR_RX_HTRSH))) {
 		wil_dbg_irq(wil, "RX done\n");
 
-		if (isr & BIT_DMA_EP_RX_ICR_RX_HTRSH)
-			wil_err_ratelimited(wil, "Received \"Rx buffer is in risk "
-				"of overflow\" interrupt\n");
+		if (unlikely(isr & BIT_DMA_EP_RX_ICR_RX_HTRSH))
+			wil_err_ratelimited(wil,
+					    "Received \"Rx buffer is in risk of overflow\" interrupt\n");
 
-		isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH);
-		if (test_bit(wil_status_reset_done, &wil->status)) {
-			if (test_bit(wil_status_napi_en, &wil->status)) {
+		isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE |
+			 BIT_DMA_EP_RX_ICR_RX_HTRSH);
+		if (likely(test_bit(wil_status_reset_done, wil->status))) {
+			if (likely(test_bit(wil_status_napi_en, wil->status))) {
 				wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
 				need_unmask = false;
 				napi_schedule(&wil->napi_rx);
 			} else {
-				wil_err(wil, "Got Rx interrupt while "
-					"stopping interface\n");
+				wil_err(wil,
+					"Got Rx interrupt while stopping interface\n");
 			}
 		} else {
 			wil_err(wil, "Got Rx interrupt while in reset\n");
 		}
 	}
 
-	if (isr)
+	if (unlikely(isr))
 		wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);
 
 	/* Rx IRQ will be enabled when NAPI processing finished */
@@ -236,19 +287,19 @@
 	trace_wil6210_irq_tx(isr);
 	wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
 
-	if (!isr) {
+	if (unlikely(!isr)) {
 		wil_err(wil, "spurious IRQ: TX\n");
 		return IRQ_NONE;
 	}
 
 	wil6210_mask_irq_tx(wil);
 
-	if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
+	if (likely(isr & BIT_DMA_EP_TX_ICR_TX_DONE)) {
 		wil_dbg_irq(wil, "TX done\n");
 		isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
 		/* clear also all VRING interrupts */
 		isr &= ~(BIT(25) - 1UL);
-		if (test_bit(wil_status_reset_done, &wil->status)) {
+		if (likely(test_bit(wil_status_reset_done, wil->status))) {
 			wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
 			need_unmask = false;
 			napi_schedule(&wil->napi_tx);
@@ -257,7 +308,7 @@
 		}
 	}
 
-	if (isr)
+	if (unlikely(isr))
 		wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
 
 	/* Tx IRQ will be enabled when NAPI processing finished */
@@ -310,7 +361,7 @@
 
 	if (isr & ISR_MISC_FW_ERROR) {
 		wil_err(wil, "Firmware error detected\n");
-		clear_bit(wil_status_fwready, &wil->status);
+		clear_bit(wil_status_fwready, wil->status);
 		/*
 		 * do not clear @isr here - we do 2-nd part in thread
 		 * there, user space get notified, and it should be done
@@ -321,7 +372,7 @@
 	if (isr & ISR_MISC_FW_READY) {
 		wil_dbg_irq(wil, "IRQ: FW ready\n");
 		wil_cache_mbox_regs(wil);
-		set_bit(wil_status_reset_done, &wil->status);
+		set_bit(wil_status_reset_done, wil->status);
 		/**
 		 * Actual FW ready indicated by the
 		 * WMI_FW_READY_EVENTID
@@ -394,7 +445,7 @@
  */
 static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause)
 {
-	if (!test_bit(wil_status_irqen, &wil->status)) {
+	if (!test_bit(wil_status_irqen, wil->status)) {
 		u32 icm_rx = wil_ioread32_and_clear(wil->csr +
 				HOSTADDR(RGF_DMA_EP_RX_ICR) +
 				offsetof(struct RGF_ICR, ICM));
@@ -446,11 +497,11 @@
 	/**
 	 * pseudo_cause is Clear-On-Read, no need to ACK
 	 */
-	if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff))
+	if (unlikely((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff)))
 		return IRQ_NONE;
 
 	/* FIXME: IRQ mask debug */
-	if (wil6210_debug_irq_mask(wil, pseudo_cause))
+	if (unlikely(wil6210_debug_irq_mask(wil, pseudo_cause)))
 		return IRQ_NONE;
 
 	trace_wil6210_irq_pseudo(pseudo_cause);
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 8ff3fe3..c2a2384 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -29,19 +29,18 @@
 module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery");
 
-static bool no_fw_load = true;
-module_param(no_fw_load, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(no_fw_load, " do not download FW, use one in on-card flash.");
-
-static unsigned int itr_trsh = WIL6210_ITR_TRSH_DEFAULT;
-
-module_param(itr_trsh, uint, S_IRUGO);
-MODULE_PARM_DESC(itr_trsh, " Interrupt moderation threshold, usecs.");
+/* if not set via modparam, will be set to default value of 1/8 of
+ * rx ring size during init flow
+ */
+unsigned short rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_INIT;
+module_param(rx_ring_overflow_thrsh, ushort, S_IRUGO);
+MODULE_PARM_DESC(rx_ring_overflow_thrsh,
+		 " RX ring overflow threshold in descriptors.");
 
 /* We allow allocation of more than 1 page buffers to support large packets.
  * It is suboptimal behavior performance wise in case MTU above page size.
  */
-unsigned int mtu_max = TXRX_BUF_LEN_DEFAULT - ETH_HLEN;
+unsigned int mtu_max = TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD;
 static int mtu_max_set(const char *val, const struct kernel_param *kp)
 {
 	int ret;
@@ -53,7 +52,7 @@
 	if (ret)
 		return ret;
 
-	if (mtu_max < 68 || mtu_max > IEEE80211_MAX_DATA_LEN_DMG)
+	if (mtu_max < 68 || mtu_max > WIL_MAX_ETH_MTU)
 		ret = -EINVAL;
 
 	return ret;
@@ -69,6 +68,7 @@
 
 static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
 static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
+static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT;
 
 static int ring_order_set(const char *val, const struct kernel_param *kp)
 {
@@ -135,12 +135,14 @@
 
 static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
 			       u16 reason_code, bool from_event)
+__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
 {
 	uint i;
 	struct net_device *ndev = wil_to_ndev(wil);
 	struct wireless_dev *wdev = wil->wdev;
 	struct wil_sta_info *sta = &wil->sta[cid];
 
+	might_sleep();
 	wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid,
 		     sta->status);
 
@@ -163,15 +165,14 @@
 
 	for (i = 0; i < WIL_STA_TID_NUM; i++) {
 		struct wil_tid_ampdu_rx *r;
-		unsigned long flags;
 
-		spin_lock_irqsave(&sta->tid_rx_lock, flags);
+		spin_lock_bh(&sta->tid_rx_lock);
 
 		r = sta->tid_rx[i];
 		sta->tid_rx[i] = NULL;
 		wil_tid_ampdu_rx_free(wil, r);
 
-		spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
+		spin_unlock_bh(&sta->tid_rx_lock);
 	}
 	for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
 		if (wil->vring2cid_tid[i][0] == cid)
@@ -188,34 +189,48 @@
 	struct wireless_dev *wdev = wil->wdev;
 
 	might_sleep();
-	if (bssid) {
-		cid = wil_find_cid(wil, bssid);
-		wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid);
-	} else {
-		wil_dbg_misc(wil, "%s(all)\n", __func__);
-	}
+	wil_dbg_misc(wil, "%s(bssid=%pM, reason=%d, ev%s)\n", __func__, bssid,
+		     reason_code, from_event ? "+" : "-");
 
-	if (cid >= 0) /* disconnect 1 peer */
-		wil_disconnect_cid(wil, cid, reason_code, from_event);
-	else /* disconnect all */
+	/* Cases are:
+	 * - disconnect single STA, still connected
+	 * - disconnect single STA, already disconnected
+	 * - disconnect all
+	 *
+	 * For "disconnect all", there are 2 options:
+	 * - bssid == NULL
+	 * - bssid is our MAC address
+	 */
+	if (bssid && memcmp(ndev->dev_addr, bssid, ETH_ALEN)) {
+		cid = wil_find_cid(wil, bssid);
+		wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
+			     bssid, cid, reason_code);
+		if (cid >= 0) /* disconnect 1 peer */
+			wil_disconnect_cid(wil, cid, reason_code, from_event);
+	} else { /* all */
+		wil_dbg_misc(wil, "Disconnect all\n");
 		for (cid = 0; cid < WIL6210_MAX_CID; cid++)
 			wil_disconnect_cid(wil, cid, reason_code, from_event);
+	}
 
 	/* link state */
 	switch (wdev->iftype) {
 	case NL80211_IFTYPE_STATION:
 	case NL80211_IFTYPE_P2P_CLIENT:
-		wil_link_off(wil);
-		if (test_bit(wil_status_fwconnected, &wil->status)) {
-			clear_bit(wil_status_fwconnected, &wil->status);
+		wil_bcast_fini(wil);
+		netif_tx_stop_all_queues(ndev);
+		netif_carrier_off(ndev);
+
+		if (test_bit(wil_status_fwconnected, wil->status)) {
+			clear_bit(wil_status_fwconnected, wil->status);
 			cfg80211_disconnected(ndev, reason_code,
 					      NULL, 0, GFP_KERNEL);
-		} else if (test_bit(wil_status_fwconnecting, &wil->status)) {
+		} else if (test_bit(wil_status_fwconnecting, wil->status)) {
 			cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
 						WLAN_STATUS_UNSPECIFIED_FAILURE,
 						GFP_KERNEL);
 		}
-		clear_bit(wil_status_fwconnecting, &wil->status);
+		clear_bit(wil_status_fwconnecting, wil->status);
 		break;
 	default:
 		break;
@@ -248,7 +263,7 @@
 {
 	struct wil6210_priv *wil = (void *)x;
 
-	clear_bit(wil_status_fwready, &wil->status);
+	clear_bit(wil_status_fwready, wil->status);
 	wil_err(wil, "Scan timeout detected, start fw error recovery\n");
 	wil->recovery_state = fw_recovery_pending;
 	schedule_work(&wil->fw_error_worker);
@@ -347,11 +362,42 @@
 	return -EINVAL;
 }
 
+int wil_bcast_init(struct wil6210_priv *wil)
+{
+	int ri = wil->bcast_vring, rc;
+
+	if ((ri >= 0) && wil->vring_tx[ri].va)
+		return 0;
+
+	ri = wil_find_free_vring(wil);
+	if (ri < 0)
+		return ri;
+
+	rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order);
+	if (rc == 0)
+		wil->bcast_vring = ri;
+
+	return rc;
+}
+
+void wil_bcast_fini(struct wil6210_priv *wil)
+{
+	int ri = wil->bcast_vring;
+
+	if (ri < 0)
+		return;
+
+	wil->bcast_vring = -1;
+	wil_vring_fini_tx(wil, ri);
+}
+
 static void wil_connect_worker(struct work_struct *work)
 {
 	int rc;
 	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
 						connect_worker);
+	struct net_device *ndev = wil_to_ndev(wil);
+
 	int cid = wil->pending_connect_cid;
 	int ringid = wil_find_free_vring(wil);
 
@@ -366,7 +412,7 @@
 	wil->pending_connect_cid = -1;
 	if (rc == 0) {
 		wil->sta[cid].status = wil_sta_connected;
-		wil_link_on(wil);
+		netif_tx_wake_all_queues(ndev);
 	} else {
 		wil->sta[cid].status = wil_sta_unused;
 	}
@@ -384,11 +430,15 @@
 
 	mutex_init(&wil->mutex);
 	mutex_init(&wil->wmi_mutex);
+	mutex_init(&wil->back_rx_mutex);
+	mutex_init(&wil->back_tx_mutex);
+	mutex_init(&wil->probe_client_mutex);
 
 	init_completion(&wil->wmi_ready);
 	init_completion(&wil->wmi_call);
 
 	wil->pending_connect_cid = -1;
+	wil->bcast_vring = -1;
 	setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
 	setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);
 
@@ -396,25 +446,39 @@
 	INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
 	INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
 	INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
+	INIT_WORK(&wil->back_rx_worker, wil_back_rx_worker);
+	INIT_WORK(&wil->back_tx_worker, wil_back_tx_worker);
+	INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker);
 
 	INIT_LIST_HEAD(&wil->pending_wmi_ev);
+	INIT_LIST_HEAD(&wil->back_rx_pending);
+	INIT_LIST_HEAD(&wil->back_tx_pending);
+	INIT_LIST_HEAD(&wil->probe_client_pending);
 	spin_lock_init(&wil->wmi_ev_lock);
 	init_waitqueue_head(&wil->wq);
 
-	wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi");
+	wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi");
 	if (!wil->wmi_wq)
 		return -EAGAIN;
 
-	wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect");
-	if (!wil->wmi_wq_conn) {
-		destroy_workqueue(wil->wmi_wq);
-		return -EAGAIN;
-	}
+	wil->wq_service = create_singlethread_workqueue(WIL_NAME "_service");
+	if (!wil->wq_service)
+		goto out_wmi_wq;
 
 	wil->last_fw_recovery = jiffies;
-	wil->itr_trsh = itr_trsh;
+	wil->tx_interframe_timeout = WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT;
+	wil->rx_interframe_timeout = WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT;
+	wil->tx_max_burst_duration = WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT;
+	wil->rx_max_burst_duration = WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT;
 
+	if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT)
+		rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT;
 	return 0;
+
+out_wmi_wq:
+	destroy_workqueue(wil->wmi_wq);
+
+	return -EAGAIN;
 }
 
 /**
@@ -448,7 +512,13 @@
 	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
 	mutex_unlock(&wil->mutex);
 	wmi_event_flush(wil);
-	destroy_workqueue(wil->wmi_wq_conn);
+	wil_back_rx_flush(wil);
+	cancel_work_sync(&wil->back_rx_worker);
+	wil_back_tx_flush(wil);
+	cancel_work_sync(&wil->back_tx_worker);
+	wil_probe_client_flush(wil);
+	cancel_work_sync(&wil->probe_client_worker);
+	destroy_workqueue(wil->wq_service);
 	destroy_workqueue(wil->wmi_wq);
 }
 
@@ -478,13 +548,8 @@
 {
 	int delay = 0;
 	u32 x;
-	u32 rev_id;
-	bool is_sparrow = (wil->board->board == WIL_BOARD_SPARROW);
 
-	wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->board->name);
-
-	wil->hw_version = R(RGF_USER_FW_REV_ID);
-	rev_id = wil->hw_version & 0xff;
+	wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name);
 
 	/* Clear MAC link up */
 	S(RGF_HP_CTRL, BIT(15));
@@ -493,107 +558,67 @@
 
 	wil_halt_cpu(wil);
 
+	/* clear all boot loader "ready" bits */
+	W(RGF_USER_BL + offsetof(struct RGF_BL, ready), 0);
 	/* Clear Fw Download notification */
 	C(RGF_USER_USAGE_6, BIT(0));
 
-	if (is_sparrow) {
-		S(RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN);
-		/* XTAL stabilization should take about 3ms */
-		usleep_range(5000, 7000);
-		x = R(RGF_CAF_PLL_LOCK_STATUS);
-		if (!(x & BIT_CAF_OSC_DIG_XTAL_STABLE)) {
-			wil_err(wil, "Xtal stabilization timeout\n"
-				"RGF_CAF_PLL_LOCK_STATUS = 0x%08x\n", x);
-			return -ETIME;
-		}
-		/* switch 10k to XTAL*/
-		C(RGF_USER_SPARROW_M_4, BIT_SPARROW_M_4_SEL_SLEEP_OR_REF);
-		/* 40 MHz */
-		C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL);
-
-		W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f);
-		W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf);
+	S(RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN);
+	/* XTAL stabilization should take about 3ms */
+	usleep_range(5000, 7000);
+	x = R(RGF_CAF_PLL_LOCK_STATUS);
+	if (!(x & BIT_CAF_OSC_DIG_XTAL_STABLE)) {
+		wil_err(wil, "Xtal stabilization timeout\n"
+			"RGF_CAF_PLL_LOCK_STATUS = 0x%08x\n", x);
+		return -ETIME;
 	}
+	/* switch 10k to XTAL*/
+	C(RGF_USER_SPARROW_M_4, BIT_SPARROW_M_4_SEL_SLEEP_OR_REF);
+	/* 40 MHz */
+	C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL);
+
+	W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f);
+	W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf);
 
 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
-	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, is_sparrow ? 0x000000f0 : 0x00000170);
+	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x000000f0);
 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FE00);
 
-	if (is_sparrow) {
-		W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0);
-		W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0);
-	}
+	W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0);
+	W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0);
 
 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
 
-	if (is_sparrow) {
-		W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003);
-		/* reset A2 PCIE AHB */
-		W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
-	} else {
-		W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
-		if (rev_id == 1) {
-			/* reset A1 BOTH PCIE AHB & PCIE RGF */
-			W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
-		} else {
-			W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
-			W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
-		}
-	}
+	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003);
+	W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); /* reset A2 PCIE AHB */
 
-	/* TODO: check order here!!! Erez code is different */
 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
 
-	/* wait until device ready. typical time is 200..250 msec */
+	/* wait until device ready. typical time is 20..80 msec */
 	do {
 		msleep(RST_DELAY);
-		x = R(RGF_USER_HW_MACHINE_STATE);
+		x = R(RGF_USER_BL + offsetof(struct RGF_BL, ready));
 		if (delay++ > RST_COUNT) {
-			wil_err(wil, "Reset not completed, hw_state 0x%08x\n",
+			wil_err(wil, "Reset not completed, bl.ready 0x%08x\n",
 				x);
 			return -ETIME;
 		}
-	} while (x != HW_MACHINE_BOOT_DONE);
-
-	/* TODO: Erez check rev_id != 1 */
-	if (!is_sparrow && (rev_id != 1))
-		W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8));
+	} while (!(x & BIT_BL_READY));
 
 	C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
 
+	/* enable fix for HW bug related to the SA/DA swap in AP Rx */
+	S(RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN |
+	  BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC);
+
 	wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY);
 	return 0;
 }
 
-/**
- * wil_set_itr_trsh: - apply interrupt coalescing params
- */
-void wil_set_itr_trsh(struct wil6210_priv *wil)
-{
-	/* disable, use usec resolution */
-	W(RGF_DMA_ITR_CNT_CRL, BIT_DMA_ITR_CNT_CRL_EXT_TICK);
-
-	/* disable interrupt moderation for monitor
-	 * to get better timestamp precision
-	 */
-	if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR)
-		return;
-
-	wil_info(wil, "set ITR_TRSH = %d usec\n", wil->itr_trsh);
-	W(RGF_DMA_ITR_CNT_TRSH, wil->itr_trsh);
-	W(RGF_DMA_ITR_CNT_CRL, BIT_DMA_ITR_CNT_CRL_EN |
-	  BIT_DMA_ITR_CNT_CRL_EXT_TICK); /* start it */
-}
-
-#undef R
-#undef W
-#undef S
-#undef C
-
 void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
 {
 	le32_to_cpus(&r->base);
@@ -603,6 +628,32 @@
 	le32_to_cpus(&r->head);
 }
 
+static int wil_get_bl_info(struct wil6210_priv *wil)
+{
+	struct net_device *ndev = wil_to_ndev(wil);
+	struct RGF_BL bl;
+
+	wil_memcpy_fromio_32(&bl, wil->csr + HOSTADDR(RGF_USER_BL), sizeof(bl));
+	le32_to_cpus(&bl.ready);
+	le32_to_cpus(&bl.version);
+	le32_to_cpus(&bl.rf_type);
+	le32_to_cpus(&bl.baseband_type);
+
+	if (!is_valid_ether_addr(bl.mac_address)) {
+		wil_err(wil, "BL: Invalid MAC %pM\n", bl.mac_address);
+		return -EINVAL;
+	}
+
+	ether_addr_copy(ndev->perm_addr, bl.mac_address);
+	if (!is_valid_ether_addr(ndev->dev_addr))
+		ether_addr_copy(ndev->dev_addr, bl.mac_address);
+	wil_info(wil,
+		 "Boot Loader: ver = %d MAC = %pM RF = 0x%08x bband = 0x%08x\n",
+		 bl.version, bl.mac_address, bl.rf_type, bl.baseband_type);
+
+	return 0;
+}
+
 static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
 {
 	ulong to = msecs_to_jiffies(1000);
@@ -623,19 +674,24 @@
  * After calling this routine, you're expected to reload
  * the firmware.
  */
-int wil_reset(struct wil6210_priv *wil)
+int wil_reset(struct wil6210_priv *wil, bool load_fw)
 {
 	int rc;
 
 	wil_dbg_misc(wil, "%s()\n", __func__);
 
+	if (wil->hw_version == HW_VER_UNKNOWN)
+		return -ENODEV;
+
 	WARN_ON(!mutex_is_locked(&wil->mutex));
-	WARN_ON(test_bit(wil_status_napi_en, &wil->status));
+	WARN_ON(test_bit(wil_status_napi_en, wil->status));
 
 	cancel_work_sync(&wil->disconnect_worker);
 	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
+	wil_bcast_fini(wil);
 
-	wil->status = 0; /* prevent NAPI from being scheduled */
+	/* prevent NAPI from being scheduled */
+	bitmap_zero(wil->status, wil_status_last);
 
 	if (wil->scan_request) {
 		wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
@@ -649,7 +705,7 @@
 
 	wmi_event_flush(wil);
 
-	flush_workqueue(wil->wmi_wq_conn);
+	flush_workqueue(wil->wq_service);
 	flush_workqueue(wil->wmi_wq);
 
 	rc = wil_target_reset(wil);
@@ -657,45 +713,62 @@
 	if (rc)
 		return rc;
 
-	if (!no_fw_load) {
-		wil_info(wil, "Use firmware <%s>\n", WIL_FW_NAME);
+	rc = wil_get_bl_info(wil);
+	if (rc)
+		return rc;
+
+	if (load_fw) {
+		wil_info(wil, "Use firmware <%s> + board <%s>\n", WIL_FW_NAME,
+			 WIL_FW2_NAME);
+
 		wil_halt_cpu(wil);
 		/* Loading f/w from the file */
 		rc = wil_request_firmware(wil, WIL_FW_NAME);
 		if (rc)
 			return rc;
+		rc = wil_request_firmware(wil, WIL_FW2_NAME);
+		if (rc)
+			return rc;
 
-		/* clear any interrupts which on-card-firmware may have set */
+		/* Mark FW as loaded from host */
+		S(RGF_USER_USAGE_6, 1);
+
+		/* clear any interrupts which on-card-firmware
+		 * may have set
+		 */
 		wil6210_clear_irq(wil);
-		{ /* CAF_ICR - clear and mask */
-			u32 a = HOSTADDR(RGF_CAF_ICR) +
-				offsetof(struct RGF_ICR, ICR);
-			u32 m = HOSTADDR(RGF_CAF_ICR) +
-				offsetof(struct RGF_ICR, IMV);
-			u32 icr = ioread32(wil->csr + a);
+		/* CAF_ICR - clear and mask */
+		/* it is W1C, clear by writing back same value */
+		S(RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
+		W(RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
 
-			iowrite32(icr, wil->csr + a); /* W1C */
-			iowrite32(~0, wil->csr + m);
-			wmb(); /* wait for completion */
-		}
 		wil_release_cpu(wil);
-	} else {
-		wil_info(wil, "Use firmware from on-card flash\n");
 	}
 
 	/* init after reset */
 	wil->pending_connect_cid = -1;
+	wil->ap_isolate = 0;
 	reinit_completion(&wil->wmi_ready);
 	reinit_completion(&wil->wmi_call);
 
-	wil_unmask_irq(wil);
+	if (load_fw) {
+		wil_configure_interrupt_moderation(wil);
+		wil_unmask_irq(wil);
 
-	/* we just started MAC, wait for FW ready */
-	rc = wil_wait_for_fw_ready(wil);
+		/* we just started MAC, wait for FW ready */
+		rc = wil_wait_for_fw_ready(wil);
+		if (rc == 0) /* check FW is responsive */
+			rc = wmi_echo(wil);
+	}
 
 	return rc;
 }
 
+#undef R
+#undef W
+#undef S
+#undef C
+
 void wil_fw_error_recovery(struct wil6210_priv *wil)
 {
 	wil_dbg_misc(wil, "starting fw error recovery\n");
@@ -703,28 +776,6 @@
 	schedule_work(&wil->fw_error_worker);
 }
 
-void wil_link_on(struct wil6210_priv *wil)
-{
-	struct net_device *ndev = wil_to_ndev(wil);
-
-	wil_dbg_misc(wil, "%s()\n", __func__);
-
-	netif_carrier_on(ndev);
-	wil_dbg_misc(wil, "netif_tx_wake : link on\n");
-	netif_tx_wake_all_queues(ndev);
-}
-
-void wil_link_off(struct wil6210_priv *wil)
-{
-	struct net_device *ndev = wil_to_ndev(wil);
-
-	wil_dbg_misc(wil, "%s()\n", __func__);
-
-	netif_tx_stop_all_queues(ndev);
-	wil_dbg_misc(wil, "netif_tx_stop : link off\n");
-	netif_carrier_off(ndev);
-}
-
 int __wil_up(struct wil6210_priv *wil)
 {
 	struct net_device *ndev = wil_to_ndev(wil);
@@ -733,7 +784,7 @@
 
 	WARN_ON(!mutex_is_locked(&wil->mutex));
 
-	rc = wil_reset(wil);
+	rc = wil_reset(wil, true);
 	if (rc)
 		return rc;
 
@@ -774,7 +825,7 @@
 	wil_dbg_misc(wil, "NAPI enable\n");
 	napi_enable(&wil->napi_rx);
 	napi_enable(&wil->napi_tx);
-	set_bit(wil_status_napi_en, &wil->status);
+	set_bit(wil_status_napi_en, wil->status);
 
 	if (wil->platform_ops.bus_request)
 		wil->platform_ops.bus_request(wil->platform_handle,
@@ -807,7 +858,7 @@
 		wil->platform_ops.bus_request(wil->platform_handle, 0);
 
 	wil_disable_irq(wil);
-	if (test_and_clear_bit(wil_status_napi_en, &wil->status)) {
+	if (test_and_clear_bit(wil_status_napi_en, wil->status)) {
 		napi_disable(&wil->napi_rx);
 		napi_disable(&wil->napi_tx);
 		wil_dbg_misc(wil, "NAPI disable\n");
@@ -822,15 +873,15 @@
 		wil->scan_request = NULL;
 	}
 
-	if (test_bit(wil_status_fwconnected, &wil->status) ||
-	    test_bit(wil_status_fwconnecting, &wil->status))
+	if (test_bit(wil_status_fwconnected, wil->status) ||
+	    test_bit(wil_status_fwconnecting, wil->status))
 		wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0);
 
 	/* make sure wil is idle (not connected) */
 	mutex_unlock(&wil->mutex);
 	while (iter--) {
-		int idle = !test_bit(wil_status_fwconnected, &wil->status) &&
-			   !test_bit(wil_status_fwconnecting, &wil->status);
+		int idle = !test_bit(wil_status_fwconnected, wil->status) &&
+			   !test_bit(wil_status_fwconnecting, wil->status);
 		if (idle)
 			break;
 		msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS);
@@ -840,7 +891,7 @@
 	if (!iter)
 		wil_err(wil, "timeout waiting for idle FW/HW\n");
 
-	wil_rx_fini(wil);
+	wil_reset(wil, false);
 
 	return 0;
 }
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index e81703c..f2f7ea2 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -15,7 +15,6 @@
  */
 
 #include <linux/etherdevice.h>
-
 #include "wil6210.h"
 #include "txrx.h"
 
@@ -83,7 +82,7 @@
 	wil_rx_handle(wil, &quota);
 	done = budget - quota;
 
-	if (done <= 1) { /* burst ends - only one packet processed */
+	if (done < budget) {
 		napi_complete(napi);
 		wil6210_unmask_irq_rx(wil);
 		wil_dbg_txrx(wil, "NAPI RX complete\n");
@@ -111,7 +110,7 @@
 		tx_done += wil_tx_complete(wil, i);
 	}
 
-	if (tx_done <= 1) { /* burst ends - only one packet processed */
+	if (tx_done < budget) {
 		napi_complete(napi);
 		wil6210_unmask_irq_tx(wil);
 		wil_dbg_txrx(wil, "NAPI TX complete\n");
@@ -122,6 +121,12 @@
 	return min(tx_done, budget);
 }
 
+static void wil_dev_setup(struct net_device *dev)
+{
+	ether_setup(dev);
+	dev->tx_queue_len = WIL_TX_Q_LEN_DEFAULT;
+}
+
 void *wil_if_alloc(struct device *dev, void __iomem *csr)
 {
 	struct net_device *ndev;
@@ -153,7 +158,7 @@
 	ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels;
 	cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT);
 
-	ndev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup);
+	ndev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, wil_dev_setup);
 	if (!ndev) {
 		dev_err(dev, "alloc_netdev_mqs failed\n");
 		rc = -ENOMEM;
@@ -174,7 +179,7 @@
 	netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
 		       WIL6210_NAPI_BUDGET);
 
-	wil_link_off(wil);
+	netif_tx_stop_all_queues(ndev);
 
 	return wil;
 
@@ -217,8 +222,6 @@
 		return rc;
 	}
 
-	wil_link_off(wil);
-
 	return 0;
 }
 
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 66626a8..1099861 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -31,6 +31,27 @@
 module_param(debug_fw, bool, S_IRUGO);
 MODULE_PARM_DESC(debug_fw, " load driver if FW not ready. For FW debug");
 
+static
+void wil_set_capabilities(struct wil6210_priv *wil)
+{
+	u32 rev_id = ioread32(wil->csr + HOSTADDR(RGF_USER_JTAG_DEV_ID));
+
+	bitmap_zero(wil->hw_capabilities, hw_capability_last);
+
+	switch (rev_id) {
+	case JTAG_DEV_ID_SPARROW_B0:
+		wil->hw_name = "Sparrow B0";
+		wil->hw_version = HW_VER_SPARROW_B0;
+		break;
+	default:
+		wil_err(wil, "Unknown board hardware 0x%08x\n", rev_id);
+		wil->hw_name = "Unknown";
+		wil->hw_version = HW_VER_UNKNOWN;
+	}
+
+	wil_info(wil, "Board hardware is %s\n", wil->hw_name);
+}
+
 void wil_disable_irq(struct wil6210_priv *wil)
 {
 	int irq = wil->pdev->irq;
@@ -110,7 +131,7 @@
 
 	/* need reset here to obtain MAC */
 	mutex_lock(&wil->mutex);
-	rc = wil_reset(wil);
+	rc = wil_reset(wil, false);
 	mutex_unlock(&wil->mutex);
 	if (debug_fw)
 		rc = 0;
@@ -149,12 +170,11 @@
 	struct wil6210_priv *wil;
 	struct device *dev = &pdev->dev;
 	void __iomem *csr;
-	struct wil_board *board = (struct wil_board *)id->driver_data;
 	int rc;
 
 	/* check HW */
 	dev_info(&pdev->dev, WIL_NAME
-		 " \"%s\" device found [%04x:%04x] (rev %x)\n", board->name,
+		 " device found [%04x:%04x] (rev %x)\n",
 		 (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
 
 	if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
@@ -204,8 +224,7 @@
 
 	pci_set_drvdata(pdev, wil);
 	wil->pdev = pdev;
-	wil->board = board;
-
+	wil_set_capabilities(wil);
 	wil6210_clear_irq(wil);
 
 	wil->platform_handle =
@@ -227,8 +246,6 @@
 
 	wil6210_debugfs_init(wil);
 
-	/* check FW is alive */
-	wmi_echo(wil);
 
 	return 0;
 
@@ -266,23 +283,9 @@
 	pci_disable_device(pdev);
 }
 
-static const struct wil_board wil_board_marlon = {
-	.board = WIL_BOARD_MARLON,
-	.name = "marlon",
-};
-
-static const struct wil_board wil_board_sparrow = {
-	.board = WIL_BOARD_SPARROW,
-	.name = "sparrow",
-};
-
 static const struct pci_device_id wil6210_pcie_ids[] = {
-	{ PCI_DEVICE(0x1ae9, 0x0301),
-	  .driver_data = (kernel_ulong_t)&wil_board_marlon },
-	{ PCI_DEVICE(0x1ae9, 0x0310),
-	  .driver_data = (kernel_ulong_t)&wil_board_sparrow },
-	{ PCI_DEVICE(0x1ae9, 0x0302), /* same as above, firmware broken */
-	  .driver_data = (kernel_ulong_t)&wil_board_sparrow },
+	{ PCI_DEVICE(0x1ae9, 0x0310) },
+	{ PCI_DEVICE(0x1ae9, 0x0302) }, /* same as above, firmware broken */
 	{ /* end: all zeroes */	},
 };
 MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c
index 489cb73..ca10dcf 100644
--- a/drivers/net/wireless/ath/wil6210/rx_reorder.c
+++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014-2015 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -89,7 +89,9 @@
 	}
 }
 
+/* called in NAPI context */
 void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
+__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
 {
 	struct net_device *ndev = wil_to_ndev(wil);
 	struct vring_rx_desc *d = wil_skb_rxdesc(skb);
@@ -97,22 +99,26 @@
 	int cid = wil_rxdesc_cid(d);
 	int mid = wil_rxdesc_mid(d);
 	u16 seq = wil_rxdesc_seq(d);
+	int mcast = wil_rxdesc_mcast(d);
 	struct wil_sta_info *sta = &wil->sta[cid];
 	struct wil_tid_ampdu_rx *r;
 	u16 hseq;
 	int index;
-	unsigned long flags;
 
-	wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x\n",
-		     mid, cid, tid, seq);
+	wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x mcast %01x\n",
+		     mid, cid, tid, seq, mcast);
 
-	spin_lock_irqsave(&sta->tid_rx_lock, flags);
+	if (unlikely(mcast)) {
+		wil_netif_rx_any(skb, ndev);
+		return;
+	}
+
+	spin_lock(&sta->tid_rx_lock);
 
 	r = sta->tid_rx[tid];
 	if (!r) {
-		spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
 		wil_netif_rx_any(skb, ndev);
-		return;
+		goto out;
 	}
 
 	hseq = r->head_seq_num;
@@ -121,13 +127,24 @@
 	 * 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
+	 *
+	 * Another scenario, Rx get delayed and we got packet from before
+	 * BACK. Pass it to the stack and wait.
 	 */
 	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);
+			if (seq_less(seq, r->head_seq_num)) {
+				wil_err(wil,
+					"Error: frame with early sequence 0x%03x, should be 0x%03x. Waiting...\n",
+					seq, r->head_seq_num);
+				r->first_time = true;
+				wil_netif_rx_any(skb, ndev);
+				goto out;
+			}
+			wil_err(wil,
+				"Error: 1-st frame with wrong sequence 0x%03x, should be 0x%03x. Fixing...\n",
+				seq, r->head_seq_num);
 			r->head_seq_num = seq;
 			r->ssn = seq;
 		}
@@ -179,7 +196,7 @@
 	wil_reorder_release(wil, r);
 
 out:
-	spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
+	spin_unlock(&sta->tid_rx_lock);
 }
 
 struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
@@ -219,3 +236,241 @@
 	kfree(r->reorder_time);
 	kfree(r);
 }
+
+/* ADDBA processing */
+static u16 wil_agg_size(struct wil6210_priv *wil, u16 req_agg_wsize)
+{
+	u16 max_agg_size = min_t(u16, WIL_MAX_AGG_WSIZE, WIL_MAX_AMPDU_SIZE /
+				 (mtu_max + WIL_MAX_MPDU_OVERHEAD));
+
+	if (!req_agg_wsize)
+		return max_agg_size;
+
+	return min(max_agg_size, req_agg_wsize);
+}
+
+/* Block Ack - Rx side (recipient */
+int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid,
+			 u8 dialog_token, __le16 ba_param_set,
+			 __le16 ba_timeout, __le16 ba_seq_ctrl)
+{
+	struct wil_back_rx *req = kzalloc(sizeof(*req), GFP_KERNEL);
+
+	if (!req)
+		return -ENOMEM;
+
+	req->cidxtid = cidxtid;
+	req->dialog_token = dialog_token;
+	req->ba_param_set = le16_to_cpu(ba_param_set);
+	req->ba_timeout = le16_to_cpu(ba_timeout);
+	req->ba_seq_ctrl = le16_to_cpu(ba_seq_ctrl);
+
+	mutex_lock(&wil->back_rx_mutex);
+	list_add_tail(&req->list, &wil->back_rx_pending);
+	mutex_unlock(&wil->back_rx_mutex);
+
+	queue_work(wil->wq_service, &wil->back_rx_worker);
+
+	return 0;
+}
+
+static void wil_back_rx_handle(struct wil6210_priv *wil,
+			       struct wil_back_rx *req)
+__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
+{
+	struct wil_sta_info *sta;
+	u8 cid, tid;
+	u16 agg_wsize = 0;
+	/* bit 0: A-MSDU supported
+	 * bit 1: policy (should be 0 for us)
+	 * bits 2..5: TID
+	 * bits 6..15: buffer size
+	 */
+	u16 req_agg_wsize = WIL_GET_BITS(req->ba_param_set, 6, 15);
+	bool agg_amsdu = !!(req->ba_param_set & BIT(0));
+	int ba_policy = req->ba_param_set & BIT(1);
+	u16 agg_timeout = req->ba_timeout;
+	u16 status = WLAN_STATUS_SUCCESS;
+	u16 ssn = req->ba_seq_ctrl >> 4;
+	struct wil_tid_ampdu_rx *r;
+	int rc;
+
+	might_sleep();
+	parse_cidxtid(req->cidxtid, &cid, &tid);
+
+	/* sanity checks */
+	if (cid >= WIL6210_MAX_CID) {
+		wil_err(wil, "BACK: invalid CID %d\n", cid);
+		return;
+	}
+
+	sta = &wil->sta[cid];
+	if (sta->status != wil_sta_connected) {
+		wil_err(wil, "BACK: CID %d not connected\n", cid);
+		return;
+	}
+
+	wil_dbg_wmi(wil,
+		    "ADDBA request for CID %d %pM TID %d size %d timeout %d AMSDU%s policy %d token %d SSN 0x%03x\n",
+		    cid, sta->addr, tid, req_agg_wsize, req->ba_timeout,
+		    agg_amsdu ? "+" : "-", !!ba_policy, req->dialog_token, ssn);
+
+	/* apply policies */
+	if (ba_policy) {
+		wil_err(wil, "BACK requested unsupported ba_policy == 1\n");
+		status = WLAN_STATUS_INVALID_QOS_PARAM;
+	}
+	if (status == WLAN_STATUS_SUCCESS)
+		agg_wsize = wil_agg_size(wil, req_agg_wsize);
+
+	rc = wmi_addba_rx_resp(wil, cid, tid, req->dialog_token, status,
+			       agg_amsdu, agg_wsize, agg_timeout);
+	if (rc || (status != WLAN_STATUS_SUCCESS))
+		return;
+
+	/* apply */
+	r = wil_tid_ampdu_rx_alloc(wil, agg_wsize, ssn);
+	spin_lock_bh(&sta->tid_rx_lock);
+	wil_tid_ampdu_rx_free(wil, sta->tid_rx[tid]);
+	sta->tid_rx[tid] = r;
+	spin_unlock_bh(&sta->tid_rx_lock);
+}
+
+void wil_back_rx_flush(struct wil6210_priv *wil)
+{
+	struct wil_back_rx *evt, *t;
+
+	wil_dbg_misc(wil, "%s()\n", __func__);
+
+	mutex_lock(&wil->back_rx_mutex);
+
+	list_for_each_entry_safe(evt, t, &wil->back_rx_pending, list) {
+		list_del(&evt->list);
+		kfree(evt);
+	}
+
+	mutex_unlock(&wil->back_rx_mutex);
+}
+
+/* Retrieve next ADDBA request from the pending list */
+static struct list_head *next_back_rx(struct wil6210_priv *wil)
+{
+	struct list_head *ret = NULL;
+
+	mutex_lock(&wil->back_rx_mutex);
+
+	if (!list_empty(&wil->back_rx_pending)) {
+		ret = wil->back_rx_pending.next;
+		list_del(ret);
+	}
+
+	mutex_unlock(&wil->back_rx_mutex);
+
+	return ret;
+}
+
+void wil_back_rx_worker(struct work_struct *work)
+{
+	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
+						back_rx_worker);
+	struct wil_back_rx *evt;
+	struct list_head *lh;
+
+	while ((lh = next_back_rx(wil)) != NULL) {
+		evt = list_entry(lh, struct wil_back_rx, list);
+
+		wil_back_rx_handle(wil, evt);
+		kfree(evt);
+	}
+}
+
+/* BACK - Tx (originator) side */
+static void wil_back_tx_handle(struct wil6210_priv *wil,
+			       struct wil_back_tx *req)
+{
+	struct vring_tx_data *txdata = &wil->vring_tx_data[req->ringid];
+	int rc;
+
+	if (txdata->addba_in_progress) {
+		wil_dbg_misc(wil, "ADDBA for vring[%d] already in progress\n",
+			     req->ringid);
+		return;
+	}
+	if (txdata->agg_wsize) {
+		wil_dbg_misc(wil,
+			     "ADDBA for vring[%d] already established wsize %d\n",
+			     req->ringid, txdata->agg_wsize);
+		return;
+	}
+	txdata->addba_in_progress = true;
+	rc = wmi_addba(wil, req->ringid, req->agg_wsize, req->agg_timeout);
+	if (rc)
+		txdata->addba_in_progress = false;
+}
+
+static struct list_head *next_back_tx(struct wil6210_priv *wil)
+{
+	struct list_head *ret = NULL;
+
+	mutex_lock(&wil->back_tx_mutex);
+
+	if (!list_empty(&wil->back_tx_pending)) {
+		ret = wil->back_tx_pending.next;
+		list_del(ret);
+	}
+
+	mutex_unlock(&wil->back_tx_mutex);
+
+	return ret;
+}
+
+void wil_back_tx_worker(struct work_struct *work)
+{
+	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
+						 back_tx_worker);
+	struct wil_back_tx *evt;
+	struct list_head *lh;
+
+	while ((lh = next_back_tx(wil)) != NULL) {
+		evt = list_entry(lh, struct wil_back_tx, list);
+
+		wil_back_tx_handle(wil, evt);
+		kfree(evt);
+	}
+}
+
+void wil_back_tx_flush(struct wil6210_priv *wil)
+{
+	struct wil_back_tx *evt, *t;
+
+	wil_dbg_misc(wil, "%s()\n", __func__);
+
+	mutex_lock(&wil->back_tx_mutex);
+
+	list_for_each_entry_safe(evt, t, &wil->back_tx_pending, list) {
+		list_del(&evt->list);
+		kfree(evt);
+	}
+
+	mutex_unlock(&wil->back_tx_mutex);
+}
+
+int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize)
+{
+	struct wil_back_tx *req = kzalloc(sizeof(*req), GFP_KERNEL);
+
+	if (!req)
+		return -ENOMEM;
+
+	req->ringid = ringid;
+	req->agg_wsize = wil_agg_size(wil, wsize);
+	req->agg_timeout = 0;
+
+	mutex_lock(&wil->back_tx_mutex);
+	list_add_tail(&req->list, &wil->back_tx_pending);
+	mutex_unlock(&wil->back_tx_mutex);
+
+	queue_work(wil->wq_service, &wil->back_tx_worker);
+
+	return 0;
+}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index e3f8bdc..e8bd512 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -33,6 +33,15 @@
 MODULE_PARM_DESC(rtap_include_phy_info,
 		 " Include PHY info in the radiotap header, default - no");
 
+bool rx_align_2;
+module_param(rx_align_2, bool, S_IRUGO);
+MODULE_PARM_DESC(rx_align_2, " align Rx buffers on 4*n+2, default - no");
+
+static inline uint wil_rx_snaplen(void)
+{
+	return rx_align_2 ? 6 : 0;
+}
+
 static inline int wil_vring_is_empty(struct vring *vring)
 {
 	return vring->swhead == vring->swtail;
@@ -53,34 +62,38 @@
 	return wil_vring_next_tail(vring) == vring->swhead;
 }
 
-/*
- * Available space in Tx Vring
- */
-static inline int wil_vring_avail_tx(struct vring *vring)
+/* Used space in Tx Vring */
+static inline int wil_vring_used_tx(struct vring *vring)
 {
 	u32 swhead = vring->swhead;
 	u32 swtail = vring->swtail;
-	int used = (vring->size + swhead - swtail) % vring->size;
-
-	return vring->size - used - 1;
+	return (vring->size + swhead - swtail) % vring->size;
 }
 
-/**
- * wil_vring_wmark_low - low watermark for available descriptor space
- */
+/* Available space in Tx Vring */
+static inline int wil_vring_avail_tx(struct vring *vring)
+{
+	return vring->size - wil_vring_used_tx(vring) - 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
- */
+/* wil_vring_wmark_high - high watermark for available descriptor space */
 static inline int wil_vring_wmark_high(struct vring *vring)
 {
 	return vring->size/4;
 }
 
+/* wil_val_in_range - check if value in [min,max) */
+static inline bool wil_val_in_range(int val, int min, int max)
+{
+	return val >= min && val < max;
+}
+
 static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
 {
 	struct device *dev = wil_to_dev(wil);
@@ -98,8 +111,7 @@
 		vring->va = NULL;
 		return -ENOMEM;
 	}
-	/*
-	 * vring->va should be aligned on its size rounded up to power of 2
+	/* vring->va should be aligned on its size rounded up to power of 2
 	 * This is granted by the dma_alloc_coherent
 	 */
 	vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL);
@@ -206,7 +218,7 @@
 			       u32 i, int headroom)
 {
 	struct device *dev = wil_to_dev(wil);
-	unsigned int sz = mtu_max + ETH_HLEN;
+	unsigned int sz = mtu_max + ETH_HLEN + wil_rx_snaplen();
 	struct vring_rx_desc dd, *d = &dd;
 	volatile struct vring_rx_desc *_d = &vring->va[i].rx;
 	dma_addr_t pa;
@@ -346,27 +358,6 @@
 	}
 }
 
-/*
- * Fast swap in place between 2 registers
- */
-static void wil_swap_u16(u16 *a, u16 *b)
-{
-	*a ^= *b;
-	*b ^= *a;
-	*a ^= *b;
-}
-
-static void wil_swap_ethaddr(void *data)
-{
-	struct ethhdr *eth = data;
-	u16 *s = (u16 *)eth->h_source;
-	u16 *d = (u16 *)eth->h_dest;
-
-	wil_swap_u16(s++, d++);
-	wil_swap_u16(s++, d++);
-	wil_swap_u16(s, d);
-}
-
 /**
  * reap 1 frame from @swhead
  *
@@ -383,40 +374,45 @@
 	struct vring_rx_desc *d;
 	struct sk_buff *skb;
 	dma_addr_t pa;
-	unsigned int sz = mtu_max + ETH_HLEN;
+	unsigned int snaplen = wil_rx_snaplen();
+	unsigned int sz = mtu_max + ETH_HLEN + snaplen;
 	u16 dmalen;
 	u8 ftype;
-	u8 ds_bits;
 	int cid;
+	int i = (int)vring->swhead;
 	struct wil_net_stats *stats;
 
 	BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb));
 
-	if (wil_vring_is_empty(vring))
+	if (unlikely(wil_vring_is_empty(vring)))
 		return NULL;
 
-	_d = &vring->va[vring->swhead].rx;
-	if (!(_d->dma.status & RX_DMA_STATUS_DU)) {
+	_d = &vring->va[i].rx;
+	if (unlikely(!(_d->dma.status & RX_DMA_STATUS_DU))) {
 		/* it is not error, we just reached end of Rx done area */
 		return NULL;
 	}
 
-	skb = vring->ctx[vring->swhead].skb;
+	skb = vring->ctx[i].skb;
+	vring->ctx[i].skb = NULL;
+	wil_vring_advance_head(vring, 1);
+	if (!skb) {
+		wil_err(wil, "No Rx skb at [%d]\n", i);
+		return NULL;
+	}
 	d = wil_skb_rxdesc(skb);
 	*d = *_d;
 	pa = wil_desc_addr(&d->dma.addr);
-	vring->ctx[vring->swhead].skb = NULL;
-	wil_vring_advance_head(vring, 1);
 
 	dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
 	dmalen = le16_to_cpu(d->dma.length);
 
-	trace_wil6210_rx(vring->swhead, d);
-	wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, dmalen);
+	trace_wil6210_rx(i, d);
+	wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", i, dmalen);
 	wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
 			  (const void *)d, sizeof(*d), false);
 
-	if (dmalen > sz) {
+	if (unlikely(dmalen > sz)) {
 		wil_err(wil, "Rx size too large: %d bytes!\n", dmalen);
 		kfree_skb(skb);
 		return NULL;
@@ -445,14 +441,14 @@
 	 * in Rx descriptor. If type is not data, it is 802.11 frame as is
 	 */
 	ftype = wil_rxdesc_ftype(d) << 2;
-	if (ftype != IEEE80211_FTYPE_DATA) {
+	if (unlikely(ftype != IEEE80211_FTYPE_DATA)) {
 		wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype);
 		/* TODO: process it */
 		kfree_skb(skb);
 		return NULL;
 	}
 
-	if (skb->len < ETH_HLEN) {
+	if (unlikely(skb->len < ETH_HLEN + snaplen)) {
 		wil_err(wil, "Short frame, len = %d\n", skb->len);
 		/* TODO: process it (i.e. BAR) */
 		kfree_skb(skb);
@@ -463,9 +459,9 @@
 	 * and in case of error drop the packet
 	 * higher stack layers will handle retransmission (if required)
 	 */
-	if (d->dma.status & RX_DMA_STATUS_L4_IDENT) {
+	if (likely(d->dma.status & RX_DMA_STATUS_L4I)) {
 		/* L4 protocol identified, csum calculated */
-		if ((d->dma.error & RX_DMA_ERROR_L4_ERR) == 0)
+		if (likely((d->dma.error & RX_DMA_ERROR_L4_ERR) == 0))
 			skb->ip_summed = CHECKSUM_UNNECESSARY;
 		/* If HW reports bad checksum, let IP stack re-check it
 		 * For example, HW don't understand Microsoft IP stack that
@@ -474,13 +470,15 @@
 		 */
 	}
 
-	ds_bits = wil_rxdesc_ds_bits(d);
-	if (ds_bits == 1) {
-		/*
-		 * HW bug - in ToDS mode, i.e. Rx on AP side,
-		 * addresses get swapped
+	if (snaplen) {
+		/* Packet layout
+		 * +-------+-------+---------+------------+------+
+		 * | SA(6) | DA(6) | SNAP(6) | ETHTYPE(2) | DATA |
+		 * +-------+-------+---------+------------+------+
+		 * Need to remove SNAP, shifting SA and DA forward
 		 */
-		wil_swap_ethaddr(skb->data);
+		memmove(skb->data + snaplen, skb->data, 2 * ETH_ALEN);
+		skb_pull(skb, snaplen);
 	}
 
 	return skb;
@@ -503,7 +501,7 @@
 			(next_tail != v->swhead) && (count-- > 0);
 			v->swtail = next_tail) {
 		rc = wil_vring_alloc_skb(wil, v, v->swtail, headroom);
-		if (rc) {
+		if (unlikely(rc)) {
 			wil_err(wil, "Error %d in wil_rx_refill[%d]\n",
 				rc, v->swtail);
 			break;
@@ -520,17 +518,71 @@
  */
 void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
 {
-	gro_result_t rc;
+	gro_result_t rc = GRO_NORMAL;
 	struct wil6210_priv *wil = ndev_to_wil(ndev);
+	struct wireless_dev *wdev = wil_to_wdev(wil);
 	unsigned int len = skb->len;
 	struct vring_rx_desc *d = wil_skb_rxdesc(skb);
-	int cid = wil_rxdesc_cid(d);
+	int cid = wil_rxdesc_cid(d); /* always 0..7, no need to check */
+	struct ethhdr *eth = (void *)skb->data;
+	/* here looking for DA, not A1, thus Rxdesc's 'mcast' indication
+	 * is not suitable, need to look at data
+	 */
+	int mcast = is_multicast_ether_addr(eth->h_dest);
 	struct wil_net_stats *stats = &wil->sta[cid].stats;
+	struct sk_buff *xmit_skb = NULL;
+	static const char * const gro_res_str[] = {
+		[GRO_MERGED]		= "GRO_MERGED",
+		[GRO_MERGED_FREE]	= "GRO_MERGED_FREE",
+		[GRO_HELD]		= "GRO_HELD",
+		[GRO_NORMAL]		= "GRO_NORMAL",
+		[GRO_DROP]		= "GRO_DROP",
+	};
 
 	skb_orphan(skb);
 
-	rc = napi_gro_receive(&wil->napi_rx, skb);
+	if (wdev->iftype == NL80211_IFTYPE_AP && !wil->ap_isolate) {
+		if (mcast) {
+			/* send multicast frames both to higher layers in
+			 * local net stack and back to the wireless medium
+			 */
+			xmit_skb = skb_copy(skb, GFP_ATOMIC);
+		} else {
+			int xmit_cid = wil_find_cid(wil, eth->h_dest);
 
+			if (xmit_cid >= 0) {
+				/* The destination station is associated to
+				 * this AP (in this VLAN), so send the frame
+				 * directly to it and do not pass it to local
+				 * net stack.
+				 */
+				xmit_skb = skb;
+				skb = NULL;
+			}
+		}
+	}
+	if (xmit_skb) {
+		/* Send to wireless media and increase priority by 256 to
+		 * keep the received priority instead of reclassifying
+		 * the frame (see cfg80211_classify8021d).
+		 */
+		xmit_skb->dev = ndev;
+		xmit_skb->priority += 256;
+		xmit_skb->protocol = htons(ETH_P_802_3);
+		skb_reset_network_header(xmit_skb);
+		skb_reset_mac_header(xmit_skb);
+		wil_dbg_txrx(wil, "Rx -> Tx %d bytes\n", len);
+		dev_queue_xmit(xmit_skb);
+	}
+
+	if (skb) { /* deliver to local stack */
+
+		skb->protocol = eth_type_trans(skb, ndev);
+		rc = napi_gro_receive(&wil->napi_rx, skb);
+		wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n",
+			     len, gro_res_str[rc]);
+	}
+	/* statistics. rc set to GRO_NORMAL for AP bridging */
 	if (unlikely(rc == GRO_DROP)) {
 		ndev->stats.rx_dropped++;
 		stats->rx_dropped++;
@@ -540,17 +592,8 @@
 		stats->rx_packets++;
 		ndev->stats.rx_bytes += len;
 		stats->rx_bytes += len;
-	}
-	{
-		static const char * const gro_res_str[] = {
-			[GRO_MERGED]		= "GRO_MERGED",
-			[GRO_MERGED_FREE]	= "GRO_MERGED_FREE",
-			[GRO_HELD]		= "GRO_HELD",
-			[GRO_NORMAL]		= "GRO_NORMAL",
-			[GRO_DROP]		= "GRO_DROP",
-		};
-		wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n",
-			     len, gro_res_str[rc]);
+		if (mcast)
+			ndev->stats.multicast++;
 	}
 }
 
@@ -565,7 +608,7 @@
 	struct vring *v = &wil->vring_rx;
 	struct sk_buff *skb;
 
-	if (!v->va) {
+	if (unlikely(!v->va)) {
 		wil_err(wil, "Rx IRQ while Rx not yet initialized\n");
 		return;
 	}
@@ -581,14 +624,7 @@
 			skb->protocol = htons(ETH_P_802_2);
 			wil_netif_rx_any(skb, ndev);
 		} else {
-			struct ethhdr *eth = (void *)skb->data;
-
-			skb->protocol = eth_type_trans(skb, ndev);
-
-			if (is_unicast_ether_addr(eth->h_dest))
-				wil_rx_reorder(wil, skb);
-			else
-				wil_netif_rx_any(skb, ndev);
+			wil_rx_reorder(wil, skb);
 		}
 	}
 	wil_rx_refill(wil, v->size);
@@ -645,7 +681,7 @@
 		.vring_cfg = {
 			.tx_sw_ring = {
 				.max_mpdu_size =
-					cpu_to_le16(mtu_max + ETH_HLEN),
+					cpu_to_le16(wil_mtu2macbuf(mtu_max)),
 				.ring_size = cpu_to_le16(size),
 			},
 			.ringid = id,
@@ -653,7 +689,7 @@
 			.encap_trans_type = WMI_VRING_ENC_TYPE_802_3,
 			.mac_ctrl = 0,
 			.to_resolution = 0,
-			.agg_max_wsize = 16,
+			.agg_max_wsize = 0,
 			.schd_params = {
 				.priority = cpu_to_le16(0),
 				.timeslot_us = cpu_to_le16(0xfff),
@@ -677,6 +713,7 @@
 	}
 
 	memset(txdata, 0, sizeof(*txdata));
+	spin_lock_init(&txdata->lock);
 	vring->size = size;
 	rc = wil_vring_alloc(wil, vring);
 	if (rc)
@@ -701,6 +738,74 @@
 	vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
 
 	txdata->enabled = 1;
+	if (wil->sta[cid].data_port_open && (agg_wsize >= 0))
+		wil_addba_tx_request(wil, id, agg_wsize);
+
+	return 0;
+ out_free:
+	wil_vring_free(wil, vring, 1);
+ out:
+
+	return rc;
+}
+
+int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size)
+{
+	int rc;
+	struct wmi_bcast_vring_cfg_cmd cmd = {
+		.action = cpu_to_le32(WMI_VRING_CMD_ADD),
+		.vring_cfg = {
+			.tx_sw_ring = {
+				.max_mpdu_size =
+					cpu_to_le16(wil_mtu2macbuf(mtu_max)),
+				.ring_size = cpu_to_le16(size),
+			},
+			.ringid = id,
+			.encap_trans_type = WMI_VRING_ENC_TYPE_802_3,
+		},
+	};
+	struct {
+		struct wil6210_mbox_hdr_wmi wmi;
+		struct wmi_vring_cfg_done_event cmd;
+	} __packed reply;
+	struct vring *vring = &wil->vring_tx[id];
+	struct vring_tx_data *txdata = &wil->vring_tx_data[id];
+
+	wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__,
+		     cmd.vring_cfg.tx_sw_ring.max_mpdu_size);
+
+	if (vring->va) {
+		wil_err(wil, "Tx ring [%d] already allocated\n", id);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	memset(txdata, 0, sizeof(*txdata));
+	spin_lock_init(&txdata->lock);
+	vring->size = size;
+	rc = wil_vring_alloc(wil, vring);
+	if (rc)
+		goto out;
+
+	wil->vring2cid_tid[id][0] = WIL6210_MAX_CID; /* CID */
+	wil->vring2cid_tid[id][1] = 0; /* TID */
+
+	cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
+
+	rc = wmi_call(wil, WMI_BCAST_VRING_CFG_CMDID, &cmd, sizeof(cmd),
+		      WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100);
+	if (rc)
+		goto out_free;
+
+	if (reply.cmd.status != WMI_FW_STATUS_SUCCESS) {
+		wil_err(wil, "Tx config failed, status 0x%02x\n",
+			reply.cmd.status);
+		rc = -EINVAL;
+		goto out_free;
+	}
+	vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
+
+	txdata->enabled = 1;
 
 	return 0;
  out_free:
@@ -713,6 +818,7 @@
 void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
 {
 	struct vring *vring = &wil->vring_tx[id];
+	struct vring_tx_data *txdata = &wil->vring_tx_data[id];
 
 	WARN_ON(!mutex_is_locked(&wil->mutex));
 
@@ -721,15 +827,18 @@
 
 	wil_dbg_misc(wil, "%s() id=%d\n", __func__, id);
 
+	spin_lock_bh(&txdata->lock);
+	txdata->enabled = 0; /* no Tx can be in progress or start anew */
+	spin_unlock_bh(&txdata->lock);
 	/* make sure NAPI won't touch this vring */
-	wil->vring_tx_data[id].enabled = 0;
-	if (test_bit(wil_status_napi_en, &wil->status))
+	if (test_bit(wil_status_napi_en, wil->status))
 		napi_synchronize(&wil->napi_tx);
 
 	wil_vring_free(wil, vring, 1);
+	memset(txdata, 0, sizeof(*txdata));
 }
 
-static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
+static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil,
 				       struct sk_buff *skb)
 {
 	int i;
@@ -762,28 +871,88 @@
 	return NULL;
 }
 
+static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
+			struct sk_buff *skb);
+
+static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil,
+					   struct sk_buff *skb)
+{
+	struct vring *v;
+	int i;
+	u8 cid;
+
+	/* In the STA mode, it is expected to have only 1 VRING
+	 * for the AP we connected to.
+	 * find 1-st vring and see whether it is eligible for data
+	 */
+	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
+		v = &wil->vring_tx[i];
+		if (!v->va)
+			continue;
+
+		cid = wil->vring2cid_tid[i][0];
+		if (cid >= WIL6210_MAX_CID) /* skip BCAST */
+			continue;
+
+		if (!wil->sta[cid].data_port_open &&
+		    (skb->protocol != cpu_to_be16(ETH_P_PAE)))
+			break;
+
+		wil_dbg_txrx(wil, "Tx -> ring %d\n", i);
+
+		return v;
+	}
+
+	wil_dbg_txrx(wil, "Tx while no vrings active?\n");
+
+	return NULL;
+}
+
+/* Use one of 2 strategies:
+ *
+ * 1. New (real broadcast):
+ *    use dedicated broadcast vring
+ * 2. Old (pseudo-DMS):
+ *    Find 1-st vring and return it;
+ *    duplicate skb and send it to other active vrings;
+ *    in all cases override dest address to unicast peer's address
+ * Use old strategy when new is not supported yet:
+ *  - for PBSS
+ *  - for secure link
+ */
+static struct vring *wil_find_tx_bcast_1(struct wil6210_priv *wil,
+					 struct sk_buff *skb)
+{
+	struct vring *v;
+	int i = wil->bcast_vring;
+
+	if (i < 0)
+		return NULL;
+	v = &wil->vring_tx[i];
+	if (!v->va)
+		return NULL;
+
+	return v;
+}
+
 static void wil_set_da_for_vring(struct wil6210_priv *wil,
 				 struct sk_buff *skb, int vring_index)
 {
 	struct ethhdr *eth = (void *)skb->data;
 	int cid = wil->vring2cid_tid[vring_index][0];
 
-	memcpy(eth->h_dest, wil->sta[cid].addr, ETH_ALEN);
+	ether_addr_copy(eth->h_dest, wil->sta[cid].addr);
 }
 
-static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
-			struct sk_buff *skb);
-/*
- * Find 1-st vring and return it; set dest address for this vring in skb
- * duplicate skb and send it to other active vrings
- */
-static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
-				  struct sk_buff *skb)
+static struct vring *wil_find_tx_bcast_2(struct wil6210_priv *wil,
+					 struct sk_buff *skb)
 {
 	struct vring *v, *v2;
 	struct sk_buff *skb2;
 	int i;
 	u8 cid;
+	struct ethhdr *eth = (void *)skb->data;
+	char *src = eth->h_source;
 
 	/* find 1-st vring eligible for data */
 	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
@@ -792,9 +961,15 @@
 			continue;
 
 		cid = wil->vring2cid_tid[i][0];
+		if (cid >= WIL6210_MAX_CID) /* skip BCAST */
+			continue;
 		if (!wil->sta[cid].data_port_open)
 			continue;
 
+		/* don't Tx back to source when re-routing Rx->Tx at the AP */
+		if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN))
+			continue;
+
 		goto found;
 	}
 
@@ -812,9 +987,14 @@
 		if (!v2->va)
 			continue;
 		cid = wil->vring2cid_tid[i][0];
+		if (cid >= WIL6210_MAX_CID) /* skip BCAST */
+			continue;
 		if (!wil->sta[cid].data_port_open)
 			continue;
 
+		if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN))
+			continue;
+
 		skb2 = skb_copy(skb, GFP_ATOMIC);
 		if (skb2) {
 			wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i);
@@ -828,6 +1008,20 @@
 	return v;
 }
 
+static struct vring *wil_find_tx_bcast(struct wil6210_priv *wil,
+				       struct sk_buff *skb)
+{
+	struct wireless_dev *wdev = wil->wdev;
+
+	if (wdev->iftype != NL80211_IFTYPE_AP)
+		return wil_find_tx_bcast_2(wil, skb);
+
+	if (wil->privacy)
+		return wil_find_tx_bcast_2(wil, skb);
+
+	return wil_find_tx_bcast_1(wil, skb);
+}
+
 static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
 			   int vring_index)
 {
@@ -843,9 +1037,6 @@
 	d->mac.d[1] = 0;
 	d->mac.d[2] = 0;
 	d->mac.ucode_cmd = 0;
-	/* use dst index 0 */
-	d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS) |
-		       (0 << MAC_CFG_DESC_TX_1_DST_INDEX_POS);
 	/* translation type:  0 - bypass; 1 - 802.3; 2 - native wifi */
 	d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) |
 		      (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS);
@@ -908,8 +1099,8 @@
 	return 0;
 }
 
-static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
-			struct sk_buff *skb)
+static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
+			  struct sk_buff *skb)
 {
 	struct device *dev = wil_to_dev(wil);
 	struct vring_tx_desc dd, *d = &dd;
@@ -922,21 +1113,27 @@
 	struct vring_tx_data *txdata = &wil->vring_tx_data[vring_index];
 	uint i = swhead;
 	dma_addr_t pa;
+	int used;
+	bool mcast = (vring_index == wil->bcast_vring);
+	uint len = skb_headlen(skb);
 
 	wil_dbg_txrx(wil, "%s()\n", __func__);
 
-	if (avail < 1 + nr_frags) {
+	if (unlikely(!txdata->enabled))
+		return -EINVAL;
+
+	if (unlikely(avail < 1 + nr_frags)) {
 		wil_err_ratelimited(wil,
-				    "Tx ring full. No space for %d fragments\n",
-				    1 + nr_frags);
+				    "Tx ring[%2d] full. No space for %d fragments\n",
+				    vring_index, 1 + nr_frags);
 		return -ENOMEM;
 	}
 	_d = &vring->va[i].tx;
 
 	pa = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
 
-	wil_dbg_txrx(wil, "Tx skb %d bytes 0x%p -> %pad\n", skb_headlen(skb),
-		     skb->data, &pa);
+	wil_dbg_txrx(wil, "Tx[%2d] skb %d bytes 0x%p -> %pad\n", vring_index,
+		     skb_headlen(skb), skb->data, &pa);
 	wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
 			  skb->data, skb_headlen(skb), false);
 
@@ -944,18 +1141,26 @@
 		return -EINVAL;
 	vring->ctx[i].mapped_as = wil_mapped_as_single;
 	/* 1-st segment */
-	wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);
+	wil_tx_desc_map(d, pa, len, vring_index);
+	if (unlikely(mcast)) {
+		d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_0_MCS_EN_POS); /* MCS 0 */
+		if (unlikely(len > WIL_BCAST_MCS0_LIMIT)) {
+			/* set MCS 1 */
+			d->mac.d[0] |= (1 << MAC_CFG_DESC_TX_0_MCS_INDEX_POS);
+			/* packet mode 2 */
+			d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS) |
+				       (2 << MAC_CFG_DESC_TX_1_PKT_MODE_POS);
+		}
+	}
 	/* Process TCP/UDP checksum offloading */
-	if (wil_tx_desc_offload_cksum_set(wil, d, skb)) {
-		wil_err(wil, "VRING #%d Failed to set cksum, drop packet\n",
+	if (unlikely(wil_tx_desc_offload_cksum_set(wil, d, skb))) {
+		wil_err(wil, "Tx[%2d] Failed to set cksum, drop packet\n",
 			vring_index);
 		goto dma_error;
 	}
 
 	vring->ctx[i].nr_frags = nr_frags;
 	wil_tx_desc_set_nr_frags(d, nr_frags);
-	if (nr_frags)
-		*_d = *d;
 
 	/* middle segments */
 	for (; f < nr_frags; f++) {
@@ -963,6 +1168,10 @@
 				&skb_shinfo(skb)->frags[f];
 		int len = skb_frag_size(frag);
 
+		*_d = *d;
+		wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", vring_index, i);
+		wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4,
+				  (const void *)d, sizeof(*d), false);
 		i = (swhead + f + 1) % vring->size;
 		_d = &vring->va[i].tx;
 		pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
@@ -976,13 +1185,15 @@
 		 * it will succeed here too
 		 */
 		wil_tx_desc_offload_cksum_set(wil, d, skb);
-		*_d = *d;
 	}
 	/* for the last seg only */
 	d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS);
 	d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS);
 	d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
 	*_d = *d;
+	wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", vring_index, i);
+	wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4,
+			  (const void *)d, sizeof(*d), false);
 
 	/* hold reference to skb
 	 * to prevent skb release before accounting
@@ -990,15 +1201,19 @@
 	 */
 	vring->ctx[i].skb = skb_get(skb);
 
-	wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
-			  (const void *)d, sizeof(*d), false);
-
-	if (wil_vring_is_empty(vring)) /* performance monitoring */
+	/* performance monitoring */
+	used = wil_vring_used_tx(vring);
+	if (wil_val_in_range(vring_idle_trsh,
+			     used, used + nr_frags + 1)) {
 		txdata->idle += get_cycles() - txdata->last_idle;
+		wil_dbg_txrx(wil,  "Ring[%2d] not idle %d -> %d\n",
+			     vring_index, used, used + nr_frags + 1);
+	}
 
 	/* advance swhead */
 	wil_vring_advance_head(vring, nr_frags + 1);
-	wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
+	wil_dbg_txrx(wil, "Tx[%2d] swhead %d -> %d\n", vring_index, swhead,
+		     vring->swhead);
 	trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
 	iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
 
@@ -1025,47 +1240,63 @@
 	return -EINVAL;
 }
 
+static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
+			struct sk_buff *skb)
+{
+	int vring_index = vring - wil->vring_tx;
+	struct vring_tx_data *txdata = &wil->vring_tx_data[vring_index];
+	int rc;
+
+	spin_lock(&txdata->lock);
+	rc = __wil_tx_vring(wil, vring, skb);
+	spin_unlock(&txdata->lock);
+	return rc;
+}
+
 netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
 	struct wil6210_priv *wil = ndev_to_wil(ndev);
 	struct ethhdr *eth = (void *)skb->data;
+	bool bcast = is_multicast_ether_addr(eth->h_dest);
 	struct vring *vring;
 	static bool pr_once_fw;
 	int rc;
 
 	wil_dbg_txrx(wil, "%s()\n", __func__);
-	if (!test_bit(wil_status_fwready, &wil->status)) {
+	if (unlikely(!test_bit(wil_status_fwready, wil->status))) {
 		if (!pr_once_fw) {
 			wil_err(wil, "FW not ready\n");
 			pr_once_fw = true;
 		}
 		goto drop;
 	}
-	if (!test_bit(wil_status_fwconnected, &wil->status)) {
+	if (unlikely(!test_bit(wil_status_fwconnected, wil->status))) {
 		wil_err(wil, "FW not connected\n");
 		goto drop;
 	}
-	if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+	if (unlikely(wil->wdev->iftype == NL80211_IFTYPE_MONITOR)) {
 		wil_err(wil, "Xmit in monitor mode not supported\n");
 		goto drop;
 	}
 	pr_once_fw = false;
 
 	/* find vring */
-	if (is_unicast_ether_addr(eth->h_dest))
-		vring = wil_find_tx_vring(wil, skb);
-	else
-		vring = wil_tx_bcast(wil, skb);
-	if (!vring) {
+	if (wil->wdev->iftype == NL80211_IFTYPE_STATION) {
+		/* in STA mode (ESS), all to same VRING */
+		vring = wil_find_tx_vring_sta(wil, skb);
+	} else { /* direct communication, find matching VRING */
+		vring = bcast ? wil_find_tx_bcast(wil, skb) :
+				wil_find_tx_ucast(wil, skb);
+	}
+	if (unlikely(!vring)) {
 		wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest);
 		goto drop;
 	}
-
 	/* set up vring entry */
 	rc = wil_tx_vring(wil, vring, skb);
 
 	/* do we still have enough room in the vring? */
-	if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring)) {
+	if (unlikely(wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring))) {
 		netif_tx_stop_all_queues(wil_to_ndev(wil));
 		wil_dbg_txrx(wil, "netif_tx_stop : ring full\n");
 	}
@@ -1087,6 +1318,22 @@
 	return NET_XMIT_DROP;
 }
 
+static inline bool wil_need_txstat(struct sk_buff *skb)
+{
+	struct ethhdr *eth = (void *)skb->data;
+
+	return is_unicast_ether_addr(eth->h_dest) && skb->sk &&
+	       (skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS);
+}
+
+static inline void wil_consume_skb(struct sk_buff *skb, bool acked)
+{
+	if (unlikely(wil_need_txstat(skb)))
+		skb_complete_wifi_ack(skb, acked);
+	else
+		acked ? dev_consume_skb_any(skb) : dev_kfree_skb_any(skb);
+}
+
 /**
  * Clean up transmitted skb's from the Tx VRING
  *
@@ -1102,21 +1349,28 @@
 	struct vring_tx_data *txdata = &wil->vring_tx_data[ringid];
 	int done = 0;
 	int cid = wil->vring2cid_tid[ringid][0];
-	struct wil_net_stats *stats = &wil->sta[cid].stats;
+	struct wil_net_stats *stats = NULL;
 	volatile struct vring_tx_desc *_d;
+	int used_before_complete;
+	int used_new;
 
-	if (!vring->va) {
+	if (unlikely(!vring->va)) {
 		wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
 		return 0;
 	}
 
-	if (!txdata->enabled) {
+	if (unlikely(!txdata->enabled)) {
 		wil_info(wil, "Tx irq[%d]: vring disabled\n", ringid);
 		return 0;
 	}
 
 	wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
 
+	used_before_complete = wil_vring_used_tx(vring);
+
+	if (cid < WIL6210_MAX_CID)
+		stats = &wil->sta[cid].stats;
+
 	while (!wil_vring_is_empty(vring)) {
 		int new_swtail;
 		struct wil_ctx *ctx = &vring->ctx[vring->swtail];
@@ -1128,7 +1382,7 @@
 		/* TODO: check we are not past head */
 
 		_d = &vring->va[lf].tx;
-		if (!(_d->dma.status & TX_DMA_STATUS_DU))
+		if (unlikely(!(_d->dma.status & TX_DMA_STATUS_DU)))
 			break;
 
 		new_swtail = (lf + 1) % vring->size;
@@ -1147,26 +1401,28 @@
 			trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
 					      d->dma.error);
 			wil_dbg_txrx(wil,
-				     "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
-				     vring->swtail, dmalen, d->dma.status,
-				     d->dma.error);
-			wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
+				     "TxC[%2d][%3d] : %d bytes, status 0x%02x err 0x%02x\n",
+				     ringid, vring->swtail, dmalen,
+				     d->dma.status, d->dma.error);
+			wil_hex_dump_txrx("TxCD ", DUMP_PREFIX_NONE, 32, 4,
 					  (const void *)d, sizeof(*d), false);
 
 			wil_txdesc_unmap(dev, d, ctx);
 
 			if (skb) {
-				if (d->dma.error == 0) {
+				if (likely(d->dma.error == 0)) {
 					ndev->stats.tx_packets++;
-					stats->tx_packets++;
 					ndev->stats.tx_bytes += skb->len;
-					stats->tx_bytes += skb->len;
+					if (stats) {
+						stats->tx_packets++;
+						stats->tx_bytes += skb->len;
+					}
 				} else {
 					ndev->stats.tx_errors++;
-					stats->tx_errors++;
+					if (stats)
+						stats->tx_errors++;
 				}
-
-				dev_kfree_skb_any(skb);
+				wil_consume_skb(skb, d->dma.error == 0);
 			}
 			memset(ctx, 0, sizeof(*ctx));
 			/* There is no need to touch HW descriptor:
@@ -1179,8 +1435,12 @@
 		}
 	}
 
-	if (wil_vring_is_empty(vring)) { /* performance monitoring */
-		wil_dbg_txrx(wil, "Ring[%2d] empty\n", ringid);
+	/* performance monitoring */
+	used_new = wil_vring_used_tx(vring);
+	if (wil_val_in_range(vring_idle_trsh,
+			     used_new, used_before_complete)) {
+		wil_dbg_txrx(wil, "Ring[%2d] idle %d -> %d\n",
+			     ringid, used_before_complete, used_new);
 		txdata->last_idle = get_cycles();
 	}
 
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index 630aeb5..d90c8aa 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -20,17 +20,15 @@
 #define BUF_SW_OWNED    (1)
 #define BUF_HW_OWNED    (0)
 
-/* size of max. Tx/Rx buffers, as supported by FW */
-#define TXRX_BUF_LEN_DEFAULT (2242)
+/* default size of MAC Tx/Rx buffers */
+#define TXRX_BUF_LEN_DEFAULT (2048)
 
 /* how many bytes to reserve for rtap header? */
 #define WIL6210_RTAP_SIZE (128)
 
 /* Tx/Rx path */
 
-/*
- * Common representation of physical address in Vring
- */
+/* Common representation of physical address in Vring */
 struct vring_dma_addr {
 	__le32 addr_low;
 	__le16 addr_high;
@@ -49,11 +47,10 @@
 	addr->addr_high = cpu_to_le16((u16)upper_32_bits(pa));
 }
 
-/*
- * Tx descriptor - MAC part
+/* Tx descriptor - MAC part
  * [dword 0]
  * bit  0.. 9 : lifetime_expiry_value:10
- * bit     10 : interrup_en:1
+ * bit     10 : interrupt_en:1
  * bit     11 : status_en:1
  * bit 12..13 : txss_override:2
  * bit     14 : timestamp_insertion:1
@@ -61,15 +58,12 @@
  * bit 16..21 : reserved0:6
  * bit 22..26 : mcs_index:5
  * bit     27 : mcs_en:1
- * bit 28..29 : reserved1:2
- * bit     30 : reserved2:1
+ * bit 28..30 : reserved1:3
  * bit     31 : sn_preserved:1
  * [dword 1]
  * bit  0.. 3 : pkt_mode:4
  * bit      4 : pkt_mode_en:1
- * bit  5.. 7 : reserved0:3
- * bit  8..13 : reserved1:6
- * bit     14 : reserved2:1
+ * bit  5..14 : reserved0:10
  * bit     15 : ack_policy_en:1
  * bit 16..19 : dst_index:4
  * bit     20 : dst_index_en:1
@@ -80,7 +74,7 @@
  * [dword 2]
  * bit  0.. 7 : num_of_descriptors:8
  * bit  8..17 : reserved:10
- * bit 18..19 : l2_translation_type:2
+ * bit 18..19 : l2_translation_type:2 00 - bypass, 01 - 802.3, 10 - 802.11
  * bit     20 : snap_hdr_insertion_en:1
  * bit     21 : vlan_removal_en:1
  * bit 22..31 : reserved0:10
@@ -247,6 +241,46 @@
 
 #define TX_DMA_STATUS_DU         BIT(0)
 
+/* Tx descriptor - DMA part
+ * [dword 0]
+ * bit  0.. 7 : l4_length:8 layer 4 length
+ * bit      8 : cmd_eop:1 This descriptor is the last one in the packet
+ * bit      9 : reserved
+ * bit     10 : cmd_dma_it:1 immediate interrupt
+ * bit 11..12 : SBD - Segment Buffer Details
+ *		00 - Header Segment
+ *		01 - First Data Segment
+ *		10 - Medium Data Segment
+ *		11 - Last Data Segment
+ * bit     13 : TSE - TCP Segmentation Enable
+ * bit     14 : IIC - Directs the HW to Insert IPv4 Checksum
+ * bit     15 : ITC - Directs the HW to Insert TCP/UDP Checksum
+ * bit 16..20 : QID - The target QID that the packet should be stored
+ *		in the MAC.
+ * bit     21 : PO - Pseudo header Offload:
+ *		0 - Use the pseudo header value from the TCP checksum field
+ *		1- Calculate Pseudo header Checksum
+ * bit     22 : NC - No UDP Checksum
+ * bit 23..29 : reserved
+ * bit 30..31 : L4T - Layer 4 Type: 00 - UDP , 10 - TCP , 10, 11 - Reserved
+ *		If L4Len equal 0, no L4 at all
+ * [dword 1]
+ * bit  0..31 : addr_low:32 The payload buffer low address
+ * [dword 2]
+ * bit  0..15 : addr_high:16 The payload buffer high address
+ * bit 16..23 : ip_length:8 The IP header length for the TX IP checksum
+ *		offload feature
+ * bit 24..30 : mac_length:7
+ * bit     31 : ip_version:1 1 - IPv4, 0 - IPv6
+ * [dword 3]
+ *  [byte 12] error
+ * bit  0   2 : mac_status:3
+ * bit  3   7 : reserved:5
+ *  [byte 13] status
+ * bit      0 : DU:1 Descriptor Used
+ * bit  1   7 : reserved:7
+ *  [word 7] length
+ */
 struct vring_tx_dma {
 	u32 d0;
 	struct vring_dma_addr addr;
@@ -257,45 +291,45 @@
 	__le16 length;
 } __packed;
 
-/*
- * Rx descriptor - MAC part
+/* Rx descriptor - MAC part
  * [dword 0]
  * bit  0.. 3 : tid:4 The QoS (b3-0) TID Field
- * bit  4.. 6 : connection_id:3 :The Source index that  was found during
- *  Parsing the TA.  This field is used to  define the source of the packet
+ * bit  4.. 6 : cid:3 The Source index that  was found during parsing the TA.
+ *		This field is used to define the source of the packet
  * bit      7 : reserved:1
- * bit  8.. 9 : mac_id:2 : The MAC virtual  Ring number (always zero)
- * bit 10..11 : frame_type:2 : The FC Control  (b3-2) -  MPDU Type
- *              (management, data, control  and extension)
- * bit 12..15 : frame_subtype:4 : The FC Control  (b7-4) -  Frame Subtype
+ * bit  8.. 9 : mid:2 The MAC virtual number
+ * bit 10..11 : frame_type:2 : The FC (b3-2) - MPDU Type
+ *		(management, data, control and extension)
+ * bit 12..15 : frame_subtype:4 : The FC (b7-4) - Frame Subtype
  * bit 16..27 : seq_number:12 The received Sequence number field
  * bit 28..31 : extended:4 extended subtype
  * [dword 1]
  * bit  0.. 3 : reserved
  * bit  4.. 5 : key_id:2
  * bit      6 : decrypt_bypass:1
- * bit      7 : security:1
- * bit  8.. 9 : ds_bits:2
- * bit     10 : a_msdu_present:1  from qos header
- * bit     11 : a_msdu_type:1  from qos header
+ * bit      7 : security:1 FC (b14)
+ * bit  8.. 9 : ds_bits:2 FC (b9-8)
+ * bit     10 : a_msdu_present:1  QoS (b7)
+ * bit     11 : a_msdu_type:1  QoS (b8)
  * bit     12 : a_mpdu:1  part of AMPDU aggregation
  * bit     13 : broadcast:1
  * bit     14 : mutlicast:1
  * bit     15 : reserved:1
- * bit 16..20 : rx_mac_qid:5   The Queue Identifier that the packet
- *                             is received from
+ * bit 16..20 : rx_mac_qid:5 The Queue Identifier that the packet
+ *		is received from
  * bit 21..24 : mcs:4
- * bit 25..28 : mic_icr:4
+ * bit 25..28 : mic_icr:4 this signal tells the DMA to assert an interrupt
+ *		after it writes the packet
  * bit 29..31 : reserved:3
  * [dword 2]
  * bit  0.. 2 : time_slot:3 The timeslot that the MPDU is received
- * bit      3 : fc_protocol_ver:1 The FC Control  (b0) - Protocol  Version
- * bit      4 : fc_order:1 The FC Control (b15) -Order
- * bit  5.. 7 : qos_ack_policy:3  The QoS (b6-5) ack policy Field
+ * bit  3.. 4 : fc_protocol_ver:1 The FC (b1-0) - Protocol Version
+ * bit      5 : fc_order:1 The FC Control (b15) -Order
+ * bit  6.. 7 : qos_ack_policy:2 The QoS (b6-5) ack policy Field
  * bit      8 : esop:1 The QoS (b4) ESOP field
- * bit      9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG  field
- * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved  field
- * bit     15 : qos_ac_constraint:1
+ * bit      9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG field
+ * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved field
+ * bit     15 : qos_ac_constraint:1 QoS (b15)
  * bit 16..31 : pn_15_0:16 low 2 bytes of PN
  * [dword 3]
  * bit  0..31 : pn_47_16:32 high 4 bytes of PN
@@ -308,35 +342,46 @@
 	u32 pn_47_16;
 } __packed;
 
-/*
- * Rx descriptor - DMA part
+/* Rx descriptor - DMA part
  * [dword 0]
- * bit  0.. 7 : l4_length:8 layer 4 length
- * bit  8.. 9 : reserved:2
- * bit     10 : cmd_dma_it:1
+ * bit  0.. 7 : l4_length:8 layer 4 length. The field is only valid if
+ *		L4I bit is set
+ * bit      8 : cmd_eop:1 set to 1
+ * bit      9 : cmd_rt:1 set to 1
+ * bit     10 : cmd_dma_it:1 immediate interrupt
  * bit 11..15 : reserved:5
- * bit 16..29 : phy_info_length:14
+ * bit 16..29 : phy_info_length:14 It is valid when the PII is set.
+ *		When the FFM bit is set bits 29-27 are used for for
+ *		Flex Filter Match. Matching Index to one of the L2
+ *		EtherType Flex Filter
  * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field
+ *		00 - UDP, 01 - TCP, 10, 11 - reserved
  * [dword 1]
  * bit  0..31 : addr_low:32 The payload buffer low address
  * [dword 2]
  * bit  0..15 : addr_high:16 The payload buffer high address
- * bit 16..23 : ip_length:8
+ * bit 16..23 : ip_length:8 The filed is valid only if the L3I bit is set
  * bit 24..30 : mac_length:7
- * bit     31 : ip_version:1
+ * bit     31 : ip_version:1 1 - IPv4, 0 - IPv6
  * [dword 3]
  *  [byte 12] error
+ * bit      0 : FCS:1
+ * bit      1 : MIC:1
+ * bit      2 : Key miss:1
+ * bit      3 : Replay:1
+ * bit      4 : L3:1 IPv4 checksum
+ * bit      5 : L4:1 TCP/UDP checksum
+ * bit  6   7 : reserved:2
  *  [byte 13] status
- * bit      0 : du:1
- * bit      1 : eop:1
+ * bit      0 : DU:1 Descriptor Used
+ * bit      1 : EOP:1 The descriptor indicates the End of Packet
  * bit      2 : error:1
- * bit      3 : mi:1
- * bit      4 : l3_identified:1
- * bit      5 : l4_identified:1
- * bit      6 : phy_info_included:1
- * bit      7 : reserved:1
+ * bit      3 : MI:1 MAC Interrupt is asserted (according to parser decision)
+ * bit      4 : L3I:1 L3 identified and checksum calculated
+ * bit      5 : L4I:1 L4 identified and checksum calculated
+ * bit      6 : PII:1 PHY Info Included in the packet
+ * bit      7 : FFM:1 EtherType Flex Filter Match
  *  [word 7] length
- *
  */
 
 #define RX_DMA_D0_CMD_DMA_IT     BIT(10)
@@ -349,9 +394,9 @@
 #define RX_DMA_STATUS_DU         BIT(0)
 #define RX_DMA_STATUS_ERROR      BIT(2)
 
-#define RX_DMA_STATUS_L3_IDENT   BIT(4)
-#define RX_DMA_STATUS_L4_IDENT   BIT(5)
-#define RX_DMA_STATUS_PHY_INFO   BIT(6)
+#define RX_DMA_STATUS_L3I	BIT(4)
+#define RX_DMA_STATUS_L4I	BIT(5)
+#define RX_DMA_STATUS_PHY_INFO	BIT(6)
 
 struct vring_rx_dma {
 	u32 d0;
@@ -423,6 +468,11 @@
 	return WIL_GET_BITS(d->mac.d1, 21, 24);
 }
 
+static inline int wil_rxdesc_mcast(struct vring_rx_desc *d)
+{
+	return WIL_GET_BITS(d->mac.d1, 13, 14);
+}
+
 static inline int wil_rxdesc_phy_length(struct vring_rx_desc *d)
 {
 	return WIL_GET_BITS(d->dma.d0, 16, 29);
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index c6ec5b9..4310972 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -25,19 +25,17 @@
 
 extern bool no_fw_recovery;
 extern unsigned int mtu_max;
+extern unsigned short rx_ring_overflow_thrsh;
+extern int agg_wsize;
+extern u32 vring_idle_trsh;
+extern bool rx_align_2;
 
 #define WIL_NAME "wil6210"
-#define WIL_FW_NAME "wil6210.fw"
+#define WIL_FW_NAME "wil6210.fw" /* code */
+#define WIL_FW2_NAME "wil6210.board" /* board & radio parameters */
 
 #define WIL_MAX_BUS_REQUEST_KBPS 800000 /* ~6.1Gbps */
 
-struct wil_board {
-	int board;
-#define WIL_BOARD_MARLON	(1)
-#define WIL_BOARD_SPARROW	(2)
-	const char * const name;
-};
-
 /**
  * extract bits [@b0:@b1] (inclusive) from the value @x
  * it should be @b0 <= @b1, or result is incorrect
@@ -49,21 +47,52 @@
 
 #define WIL6210_MEM_SIZE (2*1024*1024UL)
 
-#define WIL_RX_RING_SIZE_ORDER_DEFAULT	(9)
-#define WIL_TX_RING_SIZE_ORDER_DEFAULT	(9)
+#define WIL_TX_Q_LEN_DEFAULT		(4000)
+#define WIL_RX_RING_SIZE_ORDER_DEFAULT	(10)
+#define WIL_TX_RING_SIZE_ORDER_DEFAULT	(10)
+#define WIL_BCAST_RING_SIZE_ORDER_DEFAULT	(7)
+#define WIL_BCAST_MCS0_LIMIT		(1024) /* limit for MCS0 frame size */
 /* limit ring size in range [32..32k] */
 #define WIL_RING_SIZE_ORDER_MIN	(5)
 #define WIL_RING_SIZE_ORDER_MAX	(15)
 #define WIL6210_MAX_TX_RINGS	(24) /* HW limit */
 #define WIL6210_MAX_CID		(8) /* HW limit */
 #define WIL6210_NAPI_BUDGET	(16) /* arbitrary */
+#define WIL_MAX_AMPDU_SIZE	(64 * 1024) /* FW/HW limit */
+#define WIL_MAX_AGG_WSIZE	(32) /* FW/HW limit */
+/* Hardware offload block adds the following:
+ * 26 bytes - 3-address QoS data header
+ *  8 bytes - IV + EIV (for GCMP)
+ *  8 bytes - SNAP
+ * 16 bytes - MIC (for GCMP)
+ *  4 bytes - CRC
+ */
+#define WIL_MAX_MPDU_OVERHEAD	(62)
+
+/* Calculate MAC buffer size for the firmware. It includes all overhead,
+ * as it will go over the air, and need to be 8 byte aligned
+ */
+static inline u32 wil_mtu2macbuf(u32 mtu)
+{
+	return ALIGN(mtu + WIL_MAX_MPDU_OVERHEAD, 8);
+}
+
+/* MTU for Ethernet need to take into account 8-byte SNAP header
+ * to be added when encapsulating Ethernet frame into 802.11
+ */
+#define WIL_MAX_ETH_MTU		(IEEE80211_MAX_DATA_LEN_DMG - 8)
 /* Max supported by wil6210 value for interrupt threshold is 5sec. */
 #define WIL6210_ITR_TRSH_MAX (5000000)
-#define WIL6210_ITR_TRSH_DEFAULT	(300) /* usec */
+#define WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT (13) /* usec */
+#define WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT (13) /* usec */
+#define WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT (500) /* usec */
+#define WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT (500) /* usec */
 #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)
-
+#define WIL6210_RX_HIGH_TRSH_INIT		(0)
+#define WIL6210_RX_HIGH_TRSH_DEFAULT \
+				(1 << (WIL_RX_RING_SIZE_ORDER_DEFAULT - 3))
 /* Hardware definitions begin */
 
 /*
@@ -96,6 +125,16 @@
 	u32 IMC; /* Mask Clear, write 1 to clear */
 } __packed;
 
+struct RGF_BL {
+	u32 ready;		/* 0x880A3C bit [0] */
+#define BIT_BL_READY	BIT(0)
+	u32 version;		/* 0x880A40 version of the BL struct */
+	u32 rf_type;		/* 0x880A44 ID of the connected RF */
+	u32 baseband_type;	/* 0x880A48 ID of the baseband */
+	u8  mac_address[ETH_ALEN]; /* 0x880A4C permanent MAC */
+	u8 pad[2];
+} __packed;
+
 /* registers - FW addresses */
 #define RGF_USER_USAGE_1		(0x880004)
 #define RGF_USER_USAGE_6		(0x880018)
@@ -106,6 +145,7 @@
 #define RGF_USER_MAC_CPU_0		(0x8801fc)
 	#define BIT_USER_MAC_CPU_MAN_RST	BIT(1) /* mac_cpu_man_rst */
 #define RGF_USER_USER_SCRATCH_PAD	(0x8802bc)
+#define RGF_USER_BL			(0x880A3C) /* Boot Loader */
 #define RGF_USER_FW_REV_ID		(0x880a8c) /* chip revision */
 #define RGF_USER_CLKS_CTL_0		(0x880abc)
 	#define BIT_USER_CLKS_CAR_AHB_SW_SEL	BIT(1) /* ref clk/PLL */
@@ -135,7 +175,7 @@
 	#define BIT_DMA_EP_MISC_ICR_TX_NO_ACT	BIT(1)
 	#define BIT_DMA_EP_MISC_ICR_FW_INT(n)	BIT(28+n) /* n = [0..3] */
 
-/* Interrupt moderation control */
+/* Legacy interrupt moderation control (before Sparrow v2)*/
 #define RGF_DMA_ITR_CNT_TRSH		(0x881c5c)
 #define RGF_DMA_ITR_CNT_DATA		(0x881c60)
 #define RGF_DMA_ITR_CNT_CRL		(0x881c64)
@@ -145,6 +185,53 @@
 	#define BIT_DMA_ITR_CNT_CRL_CLR		BIT(3)
 	#define BIT_DMA_ITR_CNT_CRL_REACH_TRSH	BIT(4)
 
+/* Offload control (Sparrow B0+) */
+#define RGF_DMA_OFUL_NID_0		(0x881cd4)
+	#define BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN		BIT(0)
+	#define BIT_DMA_OFUL_NID_0_TX_EXT_TR_EN		BIT(1)
+	#define BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC	BIT(2)
+	#define BIT_DMA_OFUL_NID_0_TX_EXT_A3_SRC	BIT(3)
+
+/* New (sparrow v2+) interrupt moderation control */
+#define RGF_DMA_ITR_TX_DESQ_NO_MOD		(0x881d40)
+#define RGF_DMA_ITR_TX_CNT_TRSH			(0x881d34)
+#define RGF_DMA_ITR_TX_CNT_DATA			(0x881d38)
+#define RGF_DMA_ITR_TX_CNT_CTL			(0x881d3c)
+	#define BIT_DMA_ITR_TX_CNT_CTL_EN		BIT(0)
+	#define BIT_DMA_ITR_TX_CNT_CTL_EXT_TIC_SEL	BIT(1)
+	#define BIT_DMA_ITR_TX_CNT_CTL_FOREVER		BIT(2)
+	#define BIT_DMA_ITR_TX_CNT_CTL_CLR		BIT(3)
+	#define BIT_DMA_ITR_TX_CNT_CTL_REACHED_TRESH	BIT(4)
+	#define BIT_DMA_ITR_TX_CNT_CTL_CROSS_EN		BIT(5)
+	#define BIT_DMA_ITR_TX_CNT_CTL_FREE_RUNNIG	BIT(6)
+#define RGF_DMA_ITR_TX_IDL_CNT_TRSH			(0x881d60)
+#define RGF_DMA_ITR_TX_IDL_CNT_DATA			(0x881d64)
+#define RGF_DMA_ITR_TX_IDL_CNT_CTL			(0x881d68)
+	#define BIT_DMA_ITR_TX_IDL_CNT_CTL_EN			BIT(0)
+	#define BIT_DMA_ITR_TX_IDL_CNT_CTL_EXT_TIC_SEL		BIT(1)
+	#define BIT_DMA_ITR_TX_IDL_CNT_CTL_FOREVER		BIT(2)
+	#define BIT_DMA_ITR_TX_IDL_CNT_CTL_CLR			BIT(3)
+	#define BIT_DMA_ITR_TX_IDL_CNT_CTL_REACHED_TRESH	BIT(4)
+#define RGF_DMA_ITR_RX_DESQ_NO_MOD		(0x881d50)
+#define RGF_DMA_ITR_RX_CNT_TRSH			(0x881d44)
+#define RGF_DMA_ITR_RX_CNT_DATA			(0x881d48)
+#define RGF_DMA_ITR_RX_CNT_CTL			(0x881d4c)
+	#define BIT_DMA_ITR_RX_CNT_CTL_EN		BIT(0)
+	#define BIT_DMA_ITR_RX_CNT_CTL_EXT_TIC_SEL	BIT(1)
+	#define BIT_DMA_ITR_RX_CNT_CTL_FOREVER		BIT(2)
+	#define BIT_DMA_ITR_RX_CNT_CTL_CLR		BIT(3)
+	#define BIT_DMA_ITR_RX_CNT_CTL_REACHED_TRESH	BIT(4)
+	#define BIT_DMA_ITR_RX_CNT_CTL_CROSS_EN		BIT(5)
+	#define BIT_DMA_ITR_RX_CNT_CTL_FREE_RUNNIG	BIT(6)
+#define RGF_DMA_ITR_RX_IDL_CNT_TRSH			(0x881d54)
+#define RGF_DMA_ITR_RX_IDL_CNT_DATA			(0x881d58)
+#define RGF_DMA_ITR_RX_IDL_CNT_CTL			(0x881d5c)
+	#define BIT_DMA_ITR_RX_IDL_CNT_CTL_EN			BIT(0)
+	#define BIT_DMA_ITR_RX_IDL_CNT_CTL_EXT_TIC_SEL		BIT(1)
+	#define BIT_DMA_ITR_RX_IDL_CNT_CTL_FOREVER		BIT(2)
+	#define BIT_DMA_ITR_RX_IDL_CNT_CTL_CLR			BIT(3)
+	#define BIT_DMA_ITR_RX_IDL_CNT_CTL_REACHED_TRESH	BIT(4)
+
 #define RGF_DMA_PSEUDO_CAUSE		(0x881c68)
 #define RGF_DMA_PSEUDO_CAUSE_MASK_SW	(0x881c6c)
 #define RGF_DMA_PSEUDO_CAUSE_MASK_FW	(0x881c70)
@@ -164,6 +251,14 @@
 #define RGF_CAF_PLL_LOCK_STATUS		(0x88afec)
 	#define BIT_CAF_OSC_DIG_XTAL_STABLE	BIT(0)
 
+#define RGF_USER_JTAG_DEV_ID	(0x880b34) /* device ID */
+	#define JTAG_DEV_ID_SPARROW_B0	(0x2632072f)
+
+enum {
+	HW_VER_UNKNOWN,
+	HW_VER_SPARROW_B0, /* JTAG_DEV_ID_SPARROW_B0 */
+};
+
 /* popular locations */
 #define HOST_MBOX   HOSTADDR(RGF_USER_USER_SCRATCH_PAD)
 #define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \
@@ -303,6 +398,11 @@
 struct vring_tx_data {
 	int enabled;
 	cycles_t idle, last_idle, begin;
+	u8 agg_wsize; /* agreed aggregation window, 0 - no agg */
+	u16 agg_timeout;
+	u8 agg_amsdu;
+	bool addba_in_progress; /* if set, agg_xxx is for request in progress */
+	spinlock_t lock;
 };
 
 enum { /* for wil6210_priv.status */
@@ -313,6 +413,7 @@
 	wil_status_reset_done,
 	wil_status_irqen, /* FIXME: interrupts enabled - for debug */
 	wil_status_napi_en, /* NAPI enabled protected by wil->mutex */
+	wil_status_last /* keep last */
 };
 
 struct pci_dev;
@@ -397,15 +498,44 @@
 	fw_recovery_running = 2,
 };
 
+enum {
+	hw_capability_last
+};
+
+struct wil_back_rx {
+	struct list_head list;
+	/* request params, converted to CPU byte order - what we asked for */
+	u8 cidxtid;
+	u8 dialog_token;
+	u16 ba_param_set;
+	u16 ba_timeout;
+	u16 ba_seq_ctrl;
+};
+
+struct wil_back_tx {
+	struct list_head list;
+	/* request params, converted to CPU byte order - what we asked for */
+	u8 ringid;
+	u8 agg_wsize;
+	u16 agg_timeout;
+};
+
+struct wil_probe_client_req {
+	struct list_head list;
+	u64 cookie;
+	u8 cid;
+};
+
 struct wil6210_priv {
 	struct pci_dev *pdev;
 	int n_msi;
 	struct wireless_dev *wdev;
 	void __iomem *csr;
-	ulong status;
+	DECLARE_BITMAP(status, wil_status_last);
 	u32 fw_version;
 	u32 hw_version;
-	struct wil_board *board;
+	const char *hw_name;
+	DECLARE_BITMAP(hw_capabilities, hw_capability_last);
 	u8 n_mids; /* number of additional MIDs as reported by FW */
 	u32 recovery_count; /* num of FW recovery attempts in a short time */
 	u32 recovery_state; /* FW recovery state machine */
@@ -413,9 +543,14 @@
 	wait_queue_head_t wq; /* for all wait_event() use */
 	/* profile */
 	u32 monitor_flags;
-	u32 secure_pcp; /* create secure PCP? */
+	u32 privacy; /* secure connection? */
 	int sinfo_gen;
-	u32 itr_trsh;
+	u32 ap_isolate; /* no intra-BSS communication */
+	/* interrupt moderation */
+	u32 tx_max_burst_duration;
+	u32 tx_interframe_timeout;
+	u32 rx_max_burst_duration;
+	u32 rx_interframe_timeout;
 	/* cached ISR registers */
 	u32 isr_misc;
 	/* mailbox related */
@@ -429,7 +564,7 @@
 	u16 reply_size;
 	struct workqueue_struct *wmi_wq; /* for deferred calls */
 	struct work_struct wmi_event_worker;
-	struct workqueue_struct *wmi_wq_conn; /* for connect worker */
+	struct workqueue_struct *wq_service;
 	struct work_struct connect_worker;
 	struct work_struct disconnect_worker;
 	struct work_struct fw_error_worker;	/* for FW error recovery */
@@ -445,12 +580,24 @@
 	spinlock_t wmi_ev_lock;
 	struct napi_struct napi_rx;
 	struct napi_struct napi_tx;
+	/* BACK */
+	struct list_head back_rx_pending;
+	struct mutex back_rx_mutex; /* protect @back_rx_pending */
+	struct work_struct back_rx_worker;
+	struct list_head back_tx_pending;
+	struct mutex back_tx_mutex; /* protect @back_tx_pending */
+	struct work_struct back_tx_worker;
+	/* keep alive */
+	struct list_head probe_client_pending;
+	struct mutex probe_client_mutex; /* protect @probe_client_pending */
+	struct work_struct probe_client_worker;
 	/* DMA related */
 	struct vring vring_rx;
 	struct vring vring_tx[WIL6210_MAX_TX_RINGS];
 	struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS];
 	u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
 	struct wil_sta_info sta[WIL6210_MAX_CID];
+	int bcast_vring;
 	/* scan */
 	struct cfg80211_scan_request *scan_request;
 
@@ -528,12 +675,9 @@
 void wil_if_remove(struct wil6210_priv *wil);
 int wil_priv_init(struct wil6210_priv *wil);
 void wil_priv_deinit(struct wil6210_priv *wil);
-int wil_reset(struct wil6210_priv *wil);
-void wil_set_itr_trsh(struct wil6210_priv *wil);
+int wil_reset(struct wil6210_priv *wil, bool no_fw);
 void wil_fw_error_recovery(struct wil6210_priv *wil);
 void wil_set_recovery_state(struct wil6210_priv *wil, int state);
-void wil_link_on(struct wil6210_priv *wil);
-void wil_link_off(struct wil6210_priv *wil);
 int wil_up(struct wil6210_priv *wil);
 int __wil_up(struct wil6210_priv *wil);
 int wil_down(struct wil6210_priv *wil);
@@ -567,12 +711,26 @@
 int wmi_rxon(struct wil6210_priv *wil, bool on);
 int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r);
 int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason);
+int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout);
+int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason);
+int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason);
+int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token,
+		      u16 status, bool amsdu, u16 agg_wsize, u16 timeout);
+int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid,
+			 u8 dialog_token, __le16 ba_param_set,
+			 __le16 ba_timeout, __le16 ba_seq_ctrl);
+void wil_back_rx_worker(struct work_struct *work);
+void wil_back_rx_flush(struct wil6210_priv *wil);
+int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize);
+void wil_back_tx_worker(struct work_struct *work);
+void wil_back_tx_flush(struct wil6210_priv *wil);
 
 void wil6210_clear_irq(struct wil6210_priv *wil);
 int wil6210_init_irq(struct wil6210_priv *wil, int irq);
 void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
 void wil_mask_irq(struct wil6210_priv *wil);
 void wil_unmask_irq(struct wil6210_priv *wil);
+void wil_configure_interrupt_moderation(struct wil6210_priv *wil);
 void wil_disable_irq(struct wil6210_priv *wil);
 void wil_enable_irq(struct wil6210_priv *wil);
 int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
@@ -592,6 +750,8 @@
 int wmi_pcp_stop(struct wil6210_priv *wil);
 void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
 			u16 reason_code, bool from_event);
+void wil_probe_client_flush(struct wil6210_priv *wil);
+void wil_probe_client_worker(struct work_struct *work);
 
 int wil_rx_init(struct wil6210_priv *wil, u16 size);
 void wil_rx_fini(struct wil6210_priv *wil);
@@ -600,6 +760,9 @@
 int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
 		      int cid, int tid);
 void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
+int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size);
+int wil_bcast_init(struct wil6210_priv *wil);
+void wil_bcast_fini(struct wil6210_priv *wil);
 
 netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
 int wil_tx_complete(struct wil6210_priv *wil, int ringid);
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.c b/drivers/net/wireless/ath/wil6210/wil_platform.c
index 10d96e9..976a071 100644
--- a/drivers/net/wireless/ath/wil6210/wil_platform.c
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.c
@@ -17,10 +17,6 @@
 #include "linux/device.h"
 #include "wil_platform.h"
 
-#ifdef CPTCFG_WIL6210_PLATFORM_MSM
-#include "wil_platform_msm.h"
-#endif
-
 /**
  * wil_platform_init() - wil6210 platform module init
  *
@@ -37,13 +33,7 @@
 		return NULL;
 	}
 
-#ifdef CPTCFG_WIL6210_PLATFORM_MSM
-	handle = wil_platform_msm_init(dev, ops);
-	if (handle)
-		return handle;
-#endif
-
-	/* other platform specific init functions should be called here */
+	/* platform specific init functions should be called here */
 
 	return handle;
 }
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform_msm.c b/drivers/net/wireless/ath/wil6210/wil_platform_msm.c
deleted file mode 100644
index b354a74..0000000
--- a/drivers/net/wireless/ath/wil6210/wil_platform_msm.c
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/of.h>
-#include <linux/slab.h>
-#include <linux/msm-bus.h>
-
-#include "wil_platform.h"
-#include "wil_platform_msm.h"
-
-/**
- * struct wil_platform_msm - wil6210 msm platform module info
- *
- * @dev: device object
- * @msm_bus_handle: handle for using msm_bus API
- * @pdata: bus scale info retrieved from DT
- */
-struct wil_platform_msm {
-	struct device *dev;
-	uint32_t msm_bus_handle;
-	struct msm_bus_scale_pdata *pdata;
-};
-
-#define KBTOB(a) (a * 1000ULL)
-
-/**
- * wil_platform_get_pdata() - Generate bus client data from device tree
- * provided by clients.
- *
- * dev: device object
- * of_node: Device tree node to extract information from
- *
- * The function returns a valid pointer to the allocated bus-scale-pdata
- * if the vectors were correctly read from the client's device node.
- * Any error in reading or parsing the device node will return NULL
- * to the caller.
- */
-static struct msm_bus_scale_pdata *wil_platform_get_pdata(
-		struct device *dev,
-		struct device_node *of_node)
-{
-	struct msm_bus_scale_pdata *pdata;
-	struct msm_bus_paths *usecase;
-	int i, j, ret, len;
-	unsigned int num_usecases, num_paths, mem_size;
-	const uint32_t *vec_arr;
-	struct msm_bus_vectors *vectors;
-
-	/* first read num_usecases and num_paths so we can calculate
-	 * amount of memory to allocate
-	 */
-	ret = of_property_read_u32(of_node, "qcom,msm-bus,num-cases",
-				   &num_usecases);
-	if (ret) {
-		dev_err(dev, "Error: num-usecases not found\n");
-		return NULL;
-	}
-
-	ret = of_property_read_u32(of_node, "qcom,msm-bus,num-paths",
-				   &num_paths);
-	if (ret) {
-		dev_err(dev, "Error: num_paths not found\n");
-		return NULL;
-	}
-
-	/* pdata memory layout:
-	 *   msm_bus_scale_pdata
-	 *   msm_bus_paths[num_usecases]
-	 *   msm_bus_vectors[num_usecases][num_paths]
-	 */
-	mem_size = sizeof(struct msm_bus_scale_pdata) +
-		   sizeof(struct msm_bus_paths) * num_usecases +
-		   sizeof(struct msm_bus_vectors) * num_usecases * num_paths;
-
-	pdata = kzalloc(mem_size, GFP_KERNEL);
-	if (!pdata)
-		return NULL;
-
-	ret = of_property_read_string(of_node, "qcom,msm-bus,name",
-				      &pdata->name);
-	if (ret) {
-		dev_err(dev, "Error: Client name not found\n");
-		goto err;
-	}
-
-	if (of_property_read_bool(of_node, "qcom,msm-bus,active-only")) {
-		pdata->active_only = 1;
-	} else {
-		dev_info(dev, "active_only flag absent.\n");
-		dev_info(dev, "Using dual context by default\n");
-	}
-
-	pdata->num_usecases = num_usecases;
-	pdata->usecase = (struct msm_bus_paths *)(pdata + 1);
-
-	vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len);
-	if (vec_arr == NULL) {
-		dev_err(dev, "Error: Vector array not found\n");
-		goto err;
-	}
-
-	if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) {
-		dev_err(dev, "Error: Length-error on getting vectors\n");
-		goto err;
-	}
-
-	vectors = (struct msm_bus_vectors *)(pdata->usecase + num_usecases);
-	for (i = 0; i < num_usecases; i++) {
-		usecase = &pdata->usecase[i];
-		usecase->num_paths = num_paths;
-		usecase->vectors = &vectors[i];
-
-		for (j = 0; j < num_paths; j++) {
-			int index = ((i * num_paths) + j) * 4;
-
-			usecase->vectors[j].src = be32_to_cpu(vec_arr[index]);
-			usecase->vectors[j].dst =
-				be32_to_cpu(vec_arr[index + 1]);
-			usecase->vectors[j].ab = (uint64_t)
-				KBTOB(be32_to_cpu(vec_arr[index + 2]));
-			usecase->vectors[j].ib = (uint64_t)
-				KBTOB(be32_to_cpu(vec_arr[index + 3]));
-		}
-	}
-
-	return pdata;
-
-err:
-	kfree(pdata);
-
-	return NULL;
-}
-
-/* wil_platform API (callbacks) */
-
-static int wil_platform_bus_request(void *handle,
-				    uint32_t kbps /* KBytes/Sec */)
-{
-	int rc, i;
-	struct wil_platform_msm *msm = (struct wil_platform_msm *)handle;
-	int vote = 0; /* vote 0 in case requested kbps cannot be satisfied */
-	struct msm_bus_paths *usecase;
-	uint32_t usecase_kbps;
-	uint32_t min_kbps = ~0;
-
-	/* find the lowest usecase that is bigger than requested kbps */
-	for (i = 0; i < msm->pdata->num_usecases; i++) {
-		usecase = &msm->pdata->usecase[i];
-		/* assume we have single path (vectors[0]). If we ever
-		 * have multiple paths, need to define the behavior */
-		usecase_kbps = div64_u64(usecase->vectors[0].ib, 1000);
-		if (usecase_kbps >= kbps && usecase_kbps < min_kbps) {
-			min_kbps = usecase_kbps;
-			vote = i;
-		}
-	}
-
-	rc = msm_bus_scale_client_update_request(msm->msm_bus_handle, vote);
-	if (rc)
-		dev_err(msm->dev, "Failed msm_bus voting. kbps=%d vote=%d, rc=%d\n",
-			kbps, vote, rc);
-	else
-		/* TOOD: remove */
-		dev_info(msm->dev, "msm_bus_scale_client_update_request succeeded. kbps=%d vote=%d\n",
-			 kbps, vote);
-
-	return rc;
-}
-
-static void wil_platform_uninit(void *handle)
-{
-	struct wil_platform_msm *msm = (struct wil_platform_msm *)handle;
-
-	dev_info(msm->dev, "wil_platform_uninit\n");
-
-	if (msm->msm_bus_handle)
-		msm_bus_scale_unregister_client(msm->msm_bus_handle);
-
-	kfree(msm->pdata);
-	kfree(msm);
-}
-
-static int wil_platform_msm_bus_register(struct wil_platform_msm *msm,
-					 struct device_node *node)
-{
-	msm->pdata = wil_platform_get_pdata(msm->dev, node);
-	if (!msm->pdata) {
-		dev_err(msm->dev, "Failed getting DT info\n");
-		return -EINVAL;
-	}
-
-	msm->msm_bus_handle = msm_bus_scale_register_client(msm->pdata);
-	if (!msm->msm_bus_handle) {
-		dev_err(msm->dev, "Failed msm_bus registration\n");
-		return -EINVAL;
-	}
-
-	dev_info(msm->dev, "msm_bus registration succeeded! handle 0x%x\n",
-		 msm->msm_bus_handle);
-
-	return 0;
-}
-
-/**
- * wil_platform_msm_init() - wil6210 msm platform module init
- *
- * The function must be called before all other functions in this module.
- * It returns a handle which is used with the rest of the API
- *
- */
-void *wil_platform_msm_init(struct device *dev, struct wil_platform_ops *ops)
-{
-	struct device_node *of_node;
-	struct wil_platform_msm *msm;
-	int rc;
-
-	of_node = of_find_compatible_node(NULL, NULL, "qcom,wil6210");
-	if (!of_node) {
-		/* this could mean non-msm platform */
-		dev_err(dev, "DT node not found\n");
-		return NULL;
-	}
-
-	msm = kzalloc(sizeof(*msm), GFP_KERNEL);
-	if (!msm)
-		return NULL;
-
-	msm->dev = dev;
-
-	/* register with msm_bus module for scaling requests */
-	rc = wil_platform_msm_bus_register(msm, of_node);
-	if (rc)
-		goto cleanup;
-
-	memset(ops, 0, sizeof(*ops));
-	ops->bus_request = wil_platform_bus_request;
-	ops->uninit = wil_platform_uninit;
-
-	return (void *)msm;
-
-cleanup:
-	kfree(msm);
-	return NULL;
-}
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform_msm.h b/drivers/net/wireless/ath/wil6210/wil_platform_msm.h
deleted file mode 100644
index 2f2229e..0000000
--- a/drivers/net/wireless/ath/wil6210/wil_platform_msm.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef __WIL_PLATFORM__MSM_H__
-#define __WIL_PLATFORM_MSM_H__
-
-#include "wil_platform.h"
-
-void *wil_platform_msm_init(struct device *dev, struct wil_platform_ops *ops);
-
-#endif /* __WIL_PLATFORM__MSM_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 63476c8..9fe2085 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -23,10 +23,15 @@
 #include "wmi.h"
 #include "trace.h"
 
-static uint max_assoc_sta = 1;
+static uint max_assoc_sta = WIL6210_MAX_CID;
 module_param(max_assoc_sta, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(max_assoc_sta, " Max number of stations associated to the AP");
 
+int agg_wsize; /* = 0; */
+module_param(agg_wsize, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(agg_wsize, " Window size for Tx Block Ack after connect;"
+		 " 0 - use default; < 0 - don't auto-establish");
+
 /**
  * WMI event receiving - theory of operations
  *
@@ -197,7 +202,7 @@
 
 	might_sleep();
 
-	if (!test_bit(wil_status_fwready, &wil->status)) {
+	if (!test_bit(wil_status_fwready, wil->status)) {
 		wil_err(wil, "WMI: cannot send command while FW not ready\n");
 		return -EAGAIN;
 	}
@@ -276,7 +281,6 @@
 /*=== Event handlers ===*/
 static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
 {
-	struct net_device *ndev = wil_to_ndev(wil);
 	struct wireless_dev *wdev = wil->wdev;
 	struct wmi_ready_event *evt = d;
 
@@ -285,11 +289,7 @@
 
 	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);
-		memcpy(ndev->perm_addr, evt->mac, ETH_ALEN);
-	}
+	/* ignore MAC address, we already have it from the boot loader */
 	snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version),
 		 "%d", wil->fw_version);
 }
@@ -300,7 +300,7 @@
 	wil_dbg_wmi(wil, "WMI: got FW ready event\n");
 
 	wil_set_recovery_state(wil, fw_recovery_idle);
-	set_bit(wil_status_fwready, &wil->status);
+	set_bit(wil_status_fwready, wil->status);
 	/* let the reset sequence continue */
 	complete(&wil->wmi_ready);
 }
@@ -438,7 +438,7 @@
 
 	if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
 	    (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
-		if (!test_bit(wil_status_fwconnecting, &wil->status)) {
+		if (!test_bit(wil_status_fwconnecting, wil->status)) {
 			wil_err(wil, "Not in connecting state\n");
 			return;
 		}
@@ -457,21 +457,20 @@
 		if (assoc_req_ie) {
 			sinfo.assoc_req_ies = assoc_req_ie;
 			sinfo.assoc_req_ies_len = assoc_req_ielen;
-			sinfo.filled |= STATION_INFO_ASSOC_REQ_IES;
 		}
 
 		cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
 	}
-	clear_bit(wil_status_fwconnecting, &wil->status);
-	set_bit(wil_status_fwconnected, &wil->status);
+	clear_bit(wil_status_fwconnecting, wil->status);
+	set_bit(wil_status_fwconnected, wil->status);
 
 	/* FIXME FW can transmit only ucast frames to peer */
 	/* FIXME real ring_id instead of hard coded 0 */
-	memcpy(wil->sta[evt->cid].addr, evt->bssid, ETH_ALEN);
+	ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid);
 	wil->sta[evt->cid].status = wil_sta_conn_pending;
 
 	wil->pending_connect_cid = evt->cid;
-	queue_work(wil->wmi_wq_conn, &wil->connect_worker);
+	queue_work(wil->wq_service, &wil->connect_worker);
 }
 
 static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
@@ -525,8 +524,8 @@
 	}
 
 	eth = (struct ethhdr *)skb_put(skb, ETH_HLEN);
-	memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN);
-	memcpy(eth->h_source, evt->src_mac, ETH_ALEN);
+	ether_addr_copy(eth->h_dest, ndev->dev_addr);
+	ether_addr_copy(eth->h_source, evt->src_mac);
 	eth->h_proto = cpu_to_be16(ETH_P_PAE);
 	memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len);
 	skb->protocol = eth_type_trans(skb, ndev);
@@ -544,9 +543,24 @@
 	}
 }
 
+static void wil_addba_tx_cid(struct wil6210_priv *wil, u8 cid, u16 wsize)
+{
+	struct vring_tx_data *t;
+	int i;
+
+	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
+		if (cid != wil->vring2cid_tid[i][0])
+			continue;
+		t = &wil->vring_tx_data[i];
+		if (!t->enabled)
+			continue;
+
+		wil_addba_tx_request(wil, i, wsize);
+	}
+}
+
 static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len)
 {
-	struct net_device *ndev = wil_to_ndev(wil);
 	struct wmi_data_port_open_event *evt = d;
 	u8 cid = evt->cid;
 
@@ -558,7 +572,8 @@
 	}
 
 	wil->sta[cid].data_port_open = true;
-	netif_carrier_on(ndev);
+	if (agg_wsize >= 0)
+		wil_addba_tx_cid(wil, cid, agg_wsize);
 }
 
 static void wmi_evt_linkdown(struct wil6210_priv *wil, int id, void *d, int len)
@@ -583,55 +598,89 @@
 			      int len)
 {
 	struct wmi_vring_ba_status_event *evt = d;
-	struct wil_sta_info *sta;
-	uint i, cid;
+	struct vring_tx_data *txdata;
 
-	/* TODO: use Rx BA status, not Tx one */
-
-	wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d\n",
+	wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d AMSDU%s\n",
 		    evt->ringid,
 		    evt->status == WMI_BA_AGREED ? "OK" : "N/A",
-		    evt->agg_wsize, __le16_to_cpu(evt->ba_timeout));
+		    evt->agg_wsize, __le16_to_cpu(evt->ba_timeout),
+		    evt->amsdu ? "+" : "-");
 
 	if (evt->ringid >= WIL6210_MAX_TX_RINGS) {
 		wil_err(wil, "invalid ring id %d\n", evt->ringid);
 		return;
 	}
 
-	mutex_lock(&wil->mutex);
+	if (evt->status != WMI_BA_AGREED) {
+		evt->ba_timeout = 0;
+		evt->agg_wsize = 0;
+		evt->amsdu = 0;
+	}
 
-	cid = wil->vring2cid_tid[evt->ringid][0];
-	if (cid >= WIL6210_MAX_CID) {
-		wil_err(wil, "invalid CID %d for vring %d\n", cid, evt->ringid);
-		goto out;
+	txdata = &wil->vring_tx_data[evt->ringid];
+
+	txdata->agg_timeout = le16_to_cpu(evt->ba_timeout);
+	txdata->agg_wsize = evt->agg_wsize;
+	txdata->agg_amsdu = evt->amsdu;
+	txdata->addba_in_progress = false;
+}
+
+static void wmi_evt_addba_rx_req(struct wil6210_priv *wil, int id, void *d,
+				 int len)
+{
+	struct wmi_rcp_addba_req_event *evt = d;
+
+	wil_addba_rx_request(wil, evt->cidxtid, evt->dialog_token,
+			     evt->ba_param_set, evt->ba_timeout,
+			     evt->ba_seq_ctrl);
+}
+
+static void wmi_evt_delba(struct wil6210_priv *wil, int id, void *d, int len)
+__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
+{
+	struct wmi_delba_event *evt = d;
+	u8 cid, tid;
+	u16 reason = __le16_to_cpu(evt->reason);
+	struct wil_sta_info *sta;
+	struct wil_tid_ampdu_rx *r;
+
+	might_sleep();
+	parse_cidxtid(evt->cidxtid, &cid, &tid);
+	wil_dbg_wmi(wil, "DELBA CID %d TID %d from %s reason %d\n",
+		    cid, tid,
+		    evt->from_initiator ? "originator" : "recipient",
+		    reason);
+	if (!evt->from_initiator) {
+		int i;
+		/* find Tx vring it belongs to */
+		for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) {
+			if ((wil->vring2cid_tid[i][0] == cid) &&
+			    (wil->vring2cid_tid[i][1] == tid)) {
+				struct vring_tx_data *txdata =
+					&wil->vring_tx_data[i];
+
+				wil_dbg_wmi(wil, "DELBA Tx vring %d\n", i);
+				txdata->agg_timeout = 0;
+				txdata->agg_wsize = 0;
+				txdata->addba_in_progress = false;
+
+				break; /* max. 1 matching ring */
+			}
+		}
+		if (i >= ARRAY_SIZE(wil->vring2cid_tid))
+			wil_err(wil, "DELBA: unable to find Tx vring\n");
+		return;
 	}
 
 	sta = &wil->sta[cid];
-	if (sta->status == wil_sta_unused) {
-		wil_err(wil, "CID %d unused\n", cid);
-		goto out;
-	}
 
-	wil_dbg_wmi(wil, "BACK for CID %d %pM\n", cid, sta->addr);
-	for (i = 0; i < WIL_STA_TID_NUM; i++) {
-		struct wil_tid_ampdu_rx *r;
-		unsigned long flags;
+	spin_lock_bh(&sta->tid_rx_lock);
 
-		spin_lock_irqsave(&sta->tid_rx_lock, flags);
+	r = sta->tid_rx[tid];
+	sta->tid_rx[tid] = NULL;
+	wil_tid_ampdu_rx_free(wil, r);
 
-		r = sta->tid_rx[i];
-		sta->tid_rx[i] = NULL;
-		wil_tid_ampdu_rx_free(wil, r);
-
-		spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
-
-		if ((evt->status == WMI_BA_AGREED) && evt->agg_wsize)
-			sta->tid_rx[i] = wil_tid_ampdu_rx_alloc(wil,
-						evt->agg_wsize, 0);
-	}
-
-out:
-	mutex_unlock(&wil->mutex);
+	spin_unlock_bh(&sta->tid_rx_lock);
 }
 
 static const struct {
@@ -649,6 +698,8 @@
 	{WMI_DATA_PORT_OPEN_EVENTID,	wmi_evt_linkup},
 	{WMI_WBE_LINKDOWN_EVENTID,	wmi_evt_linkdown},
 	{WMI_BA_STATUS_EVENTID,		wmi_evt_ba_status},
+	{WMI_RCP_ADDBA_REQ_EVENTID,	wmi_evt_addba_rx_req},
+	{WMI_DELBA_EVENTID,		wmi_evt_delba},
 };
 
 /*
@@ -668,7 +719,7 @@
 	ulong flags;
 	unsigned n;
 
-	if (!test_bit(wil_status_reset_done, &wil->status)) {
+	if (!test_bit(wil_status_reset_done, wil->status)) {
 		wil_err(wil, "Reset in progress. Cannot handle WMI event\n");
 		return;
 	}
@@ -800,7 +851,7 @@
 {
 	struct wmi_set_mac_address_cmd cmd;
 
-	memcpy(cmd.mac, addr, ETH_ALEN);
+	ether_addr_copy(cmd.mac, addr);
 
 	wil_dbg_wmi(wil, "Set MAC %pM\n", addr);
 
@@ -823,7 +874,7 @@
 		struct wmi_pcp_started_event evt;
 	} __packed reply;
 
-	if (!wil->secure_pcp)
+	if (!wil->privacy)
 		cmd.disable_sec = 1;
 
 	if ((cmd.pcp_max_assoc_sta > WIL6210_MAX_CID) ||
@@ -1025,13 +1076,14 @@
 	struct wmi_cfg_rx_chain_cmd cmd = {
 		.action = WMI_RX_CHAIN_ADD,
 		.rx_sw_ring = {
-			.max_mpdu_size = cpu_to_le16(mtu_max + ETH_HLEN),
+			.max_mpdu_size = cpu_to_le16(wil_mtu2macbuf(mtu_max)),
 			.ring_mem_base = cpu_to_le64(vring->pa),
 			.ring_size = cpu_to_le16(vring->size),
 		},
 		.mid = 0, /* TODO - what is it? */
 		.decap_trans_type = WMI_DECAP_TYPE_802_3,
 		.reorder_type = WMI_RX_SW_REORDER,
+		.host_thrsh = cpu_to_le16(rx_ring_overflow_thrsh),
 	};
 	struct {
 		struct wil6210_mbox_hdr_wmi wmi;
@@ -1057,6 +1109,11 @@
 		 */
 		cmd.l3_l4_ctrl |= (1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS);
 	}
+
+	if (rx_align_2)
+		cmd.l2_802_3_offload_ctrl |=
+				L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_MSK;
+
 	/* typical time for secure PCP is 840ms */
 	rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
 		      WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000);
@@ -1074,12 +1131,13 @@
 	return rc;
 }
 
-int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r)
+int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf)
 {
 	int rc;
 	struct wmi_temp_sense_cmd cmd = {
-		.measure_marlon_m_en = cpu_to_le32(!!t_m),
-		.measure_marlon_r_en = cpu_to_le32(!!t_r),
+		.measure_baseband_en = cpu_to_le32(!!t_bb),
+		.measure_rf_en = cpu_to_le32(!!t_rf),
+		.measure_mode = cpu_to_le32(TEMPERATURE_MEASURE_NOW),
 	};
 	struct {
 		struct wil6210_mbox_hdr_wmi wmi;
@@ -1091,10 +1149,10 @@
 	if (rc)
 		return rc;
 
-	if (t_m)
-		*t_m = le32_to_cpu(reply.evt.marlon_m_t1000);
-	if (t_r)
-		*t_r = le32_to_cpu(reply.evt.marlon_r_t1000);
+	if (t_bb)
+		*t_bb = le32_to_cpu(reply.evt.baseband_t1000);
+	if (t_rf)
+		*t_rf = le32_to_cpu(reply.evt.rf_t1000);
 
 	return 0;
 }
@@ -1104,13 +1162,95 @@
 	struct wmi_disconnect_sta_cmd cmd = {
 		.disconnect_reason = cpu_to_le16(reason),
 	};
-	memcpy(cmd.dst_mac, mac, ETH_ALEN);
+
+	ether_addr_copy(cmd.dst_mac, mac);
 
 	wil_dbg_wmi(wil, "%s(%pM, reason %d)\n", __func__, mac, reason);
 
 	return wmi_send(wil, WMI_DISCONNECT_STA_CMDID, &cmd, sizeof(cmd));
 }
 
+int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout)
+{
+	struct wmi_vring_ba_en_cmd cmd = {
+		.ringid = ringid,
+		.agg_max_wsize = size,
+		.ba_timeout = cpu_to_le16(timeout),
+		.amsdu = 0,
+	};
+
+	wil_dbg_wmi(wil, "%s(ring %d size %d timeout %d)\n", __func__,
+		    ringid, size, timeout);
+
+	return wmi_send(wil, WMI_VRING_BA_EN_CMDID, &cmd, sizeof(cmd));
+}
+
+int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason)
+{
+	struct wmi_vring_ba_dis_cmd cmd = {
+		.ringid = ringid,
+		.reason = cpu_to_le16(reason),
+	};
+
+	wil_dbg_wmi(wil, "%s(ring %d reason %d)\n", __func__,
+		    ringid, reason);
+
+	return wmi_send(wil, WMI_VRING_BA_DIS_CMDID, &cmd, sizeof(cmd));
+}
+
+int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason)
+{
+	struct wmi_rcp_delba_cmd cmd = {
+		.cidxtid = cidxtid,
+		.reason = cpu_to_le16(reason),
+	};
+
+	wil_dbg_wmi(wil, "%s(CID %d TID %d reason %d)\n", __func__,
+		    cidxtid & 0xf, (cidxtid >> 4) & 0xf, reason);
+
+	return wmi_send(wil, WMI_RCP_DELBA_CMDID, &cmd, sizeof(cmd));
+}
+
+int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token,
+		      u16 status, bool amsdu, u16 agg_wsize, u16 timeout)
+{
+	int rc;
+	struct wmi_rcp_addba_resp_cmd cmd = {
+		.cidxtid = mk_cidxtid(cid, tid),
+		.dialog_token = token,
+		.status_code = cpu_to_le16(status),
+		/* bit 0: A-MSDU supported
+		 * bit 1: policy (should be 0 for us)
+		 * bits 2..5: TID
+		 * bits 6..15: buffer size
+		 */
+		.ba_param_set = cpu_to_le16((amsdu ? 1 : 0) | (tid << 2) |
+					    (agg_wsize << 6)),
+		.ba_timeout = cpu_to_le16(timeout),
+	};
+	struct {
+		struct wil6210_mbox_hdr_wmi wmi;
+		struct wmi_rcp_addba_resp_sent_event evt;
+	} __packed reply;
+
+	wil_dbg_wmi(wil,
+		    "ADDBA response for CID %d TID %d size %d timeout %d status %d AMSDU%s\n",
+		    cid, tid, agg_wsize, timeout, status, amsdu ? "+" : "-");
+
+	rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_CMDID, &cmd, sizeof(cmd),
+		      WMI_ADDBA_RESP_SENT_EVENTID, &reply, sizeof(reply), 100);
+	if (rc)
+		return rc;
+
+	if (reply.evt.status) {
+		wil_err(wil, "ADDBA response failed with status %d\n",
+			le16_to_cpu(reply.evt.status));
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
 void wmi_event_flush(struct wil6210_priv *wil)
 {
 	struct pending_wmi_event *evt, *t;
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index 27b9743..b290553 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -29,8 +29,10 @@
 
 /* General */
 #define WILOCITY_MAX_ASSOC_STA (8)
+#define WILOCITY_DEFAULT_ASSOC_STA (1)
 #define WMI_MAC_LEN		(6)
 #define WMI_PROX_RANGE_NUM	(3)
+#define WMI_MAX_LOSS_DMG_BEACONS	(32)
 
 /* List of Commands */
 enum wmi_command_id {
@@ -48,7 +50,7 @@
 	WMI_SET_WSC_STATUS_CMDID	= 0x0041,
 	WMI_PXMT_RANGE_CFG_CMDID	= 0x0042,
 	WMI_PXMT_SNR2_RANGE_CFG_CMDID	= 0x0043,
-	WMI_FAST_MEM_ACC_MODE_CMDID	= 0x0300,
+/*	WMI_FAST_MEM_ACC_MODE_CMDID	= 0x0300, */
 	WMI_MEM_READ_CMDID		= 0x0800,
 	WMI_MEM_WR_CMDID		= 0x0801,
 	WMI_ECHO_CMDID			= 0x0803,
@@ -68,7 +70,6 @@
 	WMI_SET_UCODE_IDLE_CMDID	= 0x0813,
 	WMI_SET_WORK_MODE_CMDID		= 0x0815,
 	WMI_LO_LEAKAGE_CALIB_CMDID	= 0x0816,
-	WMI_MARLON_R_ACTIVATE_CMDID	= 0x0817,
 	WMI_MARLON_R_READ_CMDID		= 0x0818,
 	WMI_MARLON_R_WRITE_CMDID	= 0x0819,
 	WMI_MARLON_R_TXRX_SEL_CMDID	= 0x081a,
@@ -78,6 +79,7 @@
 	WMI_RF_RX_TEST_CMDID		= 0x081e,
 	WMI_CFG_RX_CHAIN_CMDID		= 0x0820,
 	WMI_VRING_CFG_CMDID		= 0x0821,
+	WMI_BCAST_VRING_CFG_CMDID	= 0x0822,
 	WMI_VRING_BA_EN_CMDID		= 0x0823,
 	WMI_VRING_BA_DIS_CMDID		= 0x0824,
 	WMI_RCP_ADDBA_RESP_CMDID	= 0x0825,
@@ -97,11 +99,14 @@
 	WMI_BF_TXSS_MGMT_CMDID		= 0x0837,
 	WMI_BF_SM_MGMT_CMDID		= 0x0838,
 	WMI_BF_RXSS_MGMT_CMDID		= 0x0839,
+	WMI_BF_TRIG_CMDID		= 0x083A,
 	WMI_SET_SECTORS_CMDID		= 0x0849,
 	WMI_MAINTAIN_PAUSE_CMDID	= 0x0850,
 	WMI_MAINTAIN_RESUME_CMDID	= 0x0851,
 	WMI_RS_MGMT_CMDID		= 0x0852,
 	WMI_RF_MGMT_CMDID		= 0x0853,
+	WMI_THERMAL_THROTTLING_CTRL_CMDID	= 0x0854,
+	WMI_THERMAL_THROTTLING_GET_STATUS_CMDID	= 0x0855,
 	/* Performance monitoring commands */
 	WMI_BF_CTRL_CMDID		= 0x0862,
 	WMI_NOTIFY_REQ_CMDID		= 0x0863,
@@ -136,6 +141,7 @@
 	WMI_EAPOL_TX_CMDID		= 0xf04c,
 	WMI_MAC_ADDR_REQ_CMDID		= 0xf04d,
 	WMI_FW_VER_CMDID		= 0xf04e,
+	WMI_PMC_CMDID			= 0xf04f,
 };
 
 /*
@@ -283,8 +289,8 @@
 	WMI_LONG_SCAN		= 0,
 	WMI_SHORT_SCAN		= 1,
 	WMI_PBC_SCAN		= 2,
-	WMI_ACTIVE_SCAN		= 3,
-	WMI_DIRECT_SCAN		= 4,
+	WMI_DIRECT_SCAN		= 3,
+	WMI_ACTIVE_SCAN		= 4,
 };
 
 struct wmi_start_scan_cmd {
@@ -375,6 +381,17 @@
 } __packed;
 
 /*
+ * WMI_THERMAL_THROTTLING_CTRL_CMDID
+ */
+#define THERMAL_THROTTLING_USE_DEFAULT_MAX_TXOP_LENGTH	(0xFFFFFFFF)
+
+struct wmi_thermal_throttling_ctrl_cmd {
+	__le32 time_on_usec;
+	__le32 time_off_usec;
+	__le32 max_txop_length_usec;
+} __packed;
+
+/*
  * WMI_RF_RX_TEST_CMDID
  */
 struct wmi_rf_rx_test_cmd {
@@ -580,12 +597,29 @@
 } __packed;
 
 /*
+ * WMI_BCAST_VRING_CFG_CMDID
+ */
+struct wmi_bcast_vring_cfg {
+	struct wmi_sw_ring_cfg tx_sw_ring;
+	u8 ringid;				/* 0-23 vrings */
+	u8 encap_trans_type;
+	u8 ds_cfg;				/* 802.3 DS cfg */
+	u8 nwifi_ds_trans_type;
+} __packed;
+
+struct wmi_bcast_vring_cfg_cmd {
+	__le32 action;
+	struct wmi_bcast_vring_cfg vring_cfg;
+} __packed;
+
+/*
  * WMI_VRING_BA_EN_CMDID
  */
 struct wmi_vring_ba_en_cmd {
 	u8 ringid;
 	u8 agg_max_wsize;
 	__le16 ba_timeout;
+	u8 amsdu;
 } __packed;
 
 /*
@@ -647,6 +681,7 @@
 enum wmi_cfg_rx_chain_cmd_decap_trans_type {
 	WMI_DECAP_TYPE_802_3			= 0,
 	WMI_DECAP_TYPE_NATIVE_WIFI		= 1,
+	WMI_DECAP_TYPE_NONE			= 2,
 };
 
 enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type {
@@ -669,6 +704,9 @@
 	#define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_POS (0)
 	#define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_LEN (1)
 	#define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_MSK (0x1)
+	#define L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_POS (1)
+	#define L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_LEN (1)
+	#define L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_MSK (0x2)
 	u8 l2_802_3_offload_ctrl;
 
 	#define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_POS (0)
@@ -784,9 +822,17 @@
  *
  * Measure MAC and radio temperatures
  */
+
+/* Possible modes for temperature measurement */
+enum wmi_temperature_measure_mode {
+	TEMPERATURE_USE_OLD_VALUE	= 0x1,
+	TEMPERATURE_MEASURE_NOW		= 0x2,
+};
+
 struct wmi_temp_sense_cmd {
-	__le32 measure_marlon_m_en;
-	__le32 measure_marlon_r_en;
+	__le32 measure_baseband_en;
+	__le32 measure_rf_en;
+	__le32 measure_mode;
 } __packed;
 
 /*
@@ -815,7 +861,6 @@
 	WMI_IQ_RX_CALIB_DONE_EVENTID		= 0x1812,
 	WMI_SET_WORK_MODE_DONE_EVENTID		= 0x1815,
 	WMI_LO_LEAKAGE_CALIB_DONE_EVENTID	= 0x1816,
-	WMI_MARLON_R_ACTIVATE_DONE_EVENTID	= 0x1817,
 	WMI_MARLON_R_READ_DONE_EVENTID		= 0x1818,
 	WMI_MARLON_R_WRITE_DONE_EVENTID		= 0x1819,
 	WMI_MARLON_R_TXRX_SEL_DONE_EVENTID	= 0x181a,
@@ -842,6 +887,7 @@
 	WMI_BF_RXSS_MGMT_DONE_EVENTID		= 0x1839,
 	WMI_RS_MGMT_DONE_EVENTID		= 0x1852,
 	WMI_RF_MGMT_STATUS_EVENTID		= 0x1853,
+	WMI_THERMAL_THROTTLING_STATUS_EVENTID	= 0x1855,
 	WMI_BF_SM_MGMT_DONE_EVENTID		= 0x1838,
 	WMI_RX_MGMT_PACKET_EVENTID		= 0x1840,
 	WMI_TX_MGMT_PACKET_EVENTID		= 0x1841,
@@ -858,6 +904,7 @@
 	WMI_FLASH_READ_DONE_EVENTID		= 0x1902,
 	WMI_FLASH_WRITE_DONE_EVENTID		= 0x1903,
 	/*P2P*/
+	WMI_P2P_CFG_DONE_EVENTID		= 0x1910,
 	WMI_PORT_ALLOCATED_EVENTID		= 0x1911,
 	WMI_PORT_DELETED_EVENTID		= 0x1912,
 	WMI_LISTEN_STARTED_EVENTID		= 0x1914,
@@ -898,6 +945,15 @@
 } __packed;
 
 /*
+ * WMI_THERMAL_THROTTLING_STATUS_EVENTID
+ */
+struct wmi_thermal_throttling_status_event {
+	__le32 time_on_usec;
+	__le32 time_off_usec;
+	__le32 max_txop_length_usec;
+} __packed;
+
+/*
  * WMI_GET_STATUS_DONE_EVENTID
  */
 struct wmi_get_status_done_event {
@@ -1052,14 +1108,23 @@
 enum wmi_vring_ba_status {
 	WMI_BA_AGREED			= 0,
 	WMI_BA_NON_AGREED		= 1,
+	/* BA_EN in middle of teardown flow */
+	WMI_BA_TD_WIP			= 2,
+	/* BA_DIS or BA_EN in middle of BA SETUP flow */
+	WMI_BA_SETUP_WIP		= 3,
+	/* BA_EN when the BA session is already active */
+	WMI_BA_SESSION_ACTIVE		= 4,
+	/* BA_DIS when the BA session is not active */
+	WMI_BA_SESSION_NOT_ACTIVE	= 5,
 };
 
 struct wmi_vring_ba_status_event {
-	__le16 status;
+	__le16 status; /* enum wmi_vring_ba_status */
 	u8 reserved[2];
 	u8 ringid;
 	u8 agg_wsize;
 	__le16 ba_timeout;
+	u8 amsdu;
 } __packed;
 
 /*
@@ -1145,6 +1210,14 @@
 } __packed;
 
 /*
+ * WMI_P2P_CFG_DONE_EVENTID
+ */
+struct wmi_p2p_cfg_done_event {
+	u8 status;	/* wmi_fw_status */
+	u8 reserved[3];
+} __packed;
+
+/*
 * WMI_PORT_ALLOCATED_EVENTID
 */
 struct wmi_port_allocated_event {
@@ -1272,8 +1345,8 @@
  * Measure MAC and radio temperatures
  */
 struct wmi_temp_sense_done_event {
-	__le32 marlon_m_t1000;
-	__le32 marlon_r_t1000;
+	__le32 baseband_t1000;
+	__le32 rf_t1000;
 } __packed;
 
 #endif /* __WILOCITY_WMI_H__ */
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
new file mode 100644
index 0000000..6a1f03c
--- /dev/null
+++ b/drivers/net/wireless/atmel.c
@@ -0,0 +1,4549 @@
+/*** -*- linux-c -*- **********************************************************
+
+     Driver for Atmel at76c502 at76c504 and at76c506 wireless cards.
+
+	Copyright 2000-2001 ATMEL Corporation.
+	Copyright 2003-2004 Simon Kelley.
+
+    This code was developed from version 2.1.1 of the Atmel drivers,
+    released by Atmel corp. under the GPL in December 2002. It also
+    includes code from the Linux aironet drivers (C) Benjamin Reed,
+    and the Linux PCMCIA package, (C) David Hinds and the Linux wireless
+    extensions, (C) Jean Tourrilhes.
+
+    The firmware module for reading the MAC address of the card comes from
+    net.russotto.AtmelMACFW, written by Matthew T. Russotto and copyright
+    by him. net.russotto.AtmelMACFW is used under the GPL license version 2.
+    This file contains the module in binary form and, under the terms
+    of the GPL, in source form. The source is located at the end of the file.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This software is 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 Atmel wireless lan drivers; if not, see
+    <http://www.gnu.org/licenses/>.
+
+    For all queries about this code, please contact the current author,
+    Simon Kelley <simon@thekelleys.org.uk> and not Atmel Corporation.
+
+    Credit is due to HP UK and Cambridge Online Systems Ltd for supplying
+    hardware used during development of this driver.
+
+******************************************************************************/
+
+#include <linux/interrupt.h>
+
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <linux/crc32.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/jiffies.h>
+#include <net/cfg80211.h>
+#include "atmel.h"
+
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 98
+
+MODULE_AUTHOR("Simon Kelley");
+MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards.");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("Atmel at76c50x wireless cards");
+
+/* The name of the firmware file to be loaded
+   over-rides any automatic selection */
+static char *firmware = NULL;
+module_param(firmware, charp, 0);
+
+/* table of firmware file names */
+static struct {
+	AtmelFWType fw_type;
+	const char *fw_file;
+	const char *fw_file_ext;
+} fw_table[] = {
+	{ ATMEL_FW_TYPE_502,		"atmel_at76c502",	"bin" },
+	{ ATMEL_FW_TYPE_502D,		"atmel_at76c502d",	"bin" },
+	{ ATMEL_FW_TYPE_502E,		"atmel_at76c502e",	"bin" },
+	{ ATMEL_FW_TYPE_502_3COM,	"atmel_at76c502_3com",	"bin" },
+	{ ATMEL_FW_TYPE_504,		"atmel_at76c504",	"bin" },
+	{ ATMEL_FW_TYPE_504_2958,	"atmel_at76c504_2958",	"bin" },
+	{ ATMEL_FW_TYPE_504A_2958,	"atmel_at76c504a_2958",	"bin" },
+	{ ATMEL_FW_TYPE_506,		"atmel_at76c506",	"bin" },
+	{ ATMEL_FW_TYPE_NONE,		NULL,			NULL }
+};
+MODULE_FIRMWARE("atmel_at76c502-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c502.bin");
+MODULE_FIRMWARE("atmel_at76c502d-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c502d.bin");
+MODULE_FIRMWARE("atmel_at76c502e-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c502e.bin");
+MODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c502_3com.bin");
+MODULE_FIRMWARE("atmel_at76c504-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c504.bin");
+MODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c504_2958.bin");
+MODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c504a_2958.bin");
+MODULE_FIRMWARE("atmel_at76c506-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c506.bin");
+
+#define MAX_SSID_LENGTH 32
+#define MGMT_JIFFIES (256 * HZ / 100)
+
+#define MAX_BSS_ENTRIES	64
+
+/* registers */
+#define GCR  0x00    /* (SIR0)  General Configuration Register */
+#define BSR  0x02    /* (SIR1)  Bank Switching Select Register */
+#define AR   0x04
+#define DR   0x08
+#define MR1  0x12    /* Mirror Register 1 */
+#define MR2  0x14    /* Mirror Register 2 */
+#define MR3  0x16    /* Mirror Register 3 */
+#define MR4  0x18    /* Mirror Register 4 */
+
+#define GPR1                            0x0c
+#define GPR2                            0x0e
+#define GPR3                            0x10
+/*
+ * Constants for the GCR register.
+ */
+#define GCR_REMAP     0x0400          /* Remap internal SRAM to 0 */
+#define GCR_SWRES     0x0080          /* BIU reset (ARM and PAI are NOT reset) */
+#define GCR_CORES     0x0060          /* Core Reset (ARM and PAI are reset) */
+#define GCR_ENINT     0x0002          /* Enable Interrupts */
+#define GCR_ACKINT    0x0008          /* Acknowledge Interrupts */
+
+#define BSS_SRAM      0x0200          /* AMBA module selection --> SRAM */
+#define BSS_IRAM      0x0100          /* AMBA module selection --> IRAM */
+/*
+ *Constants for the MR registers.
+ */
+#define MAC_INIT_COMPLETE       0x0001        /* MAC init has been completed */
+#define MAC_BOOT_COMPLETE       0x0010        /* MAC boot has been completed */
+#define MAC_INIT_OK             0x0002        /* MAC boot has been completed */
+
+#define MIB_MAX_DATA_BYTES    212
+#define MIB_HEADER_SIZE       4    /* first four fields */
+
+struct get_set_mib {
+	u8 type;
+	u8 size;
+	u8 index;
+	u8 reserved;
+	u8 data[MIB_MAX_DATA_BYTES];
+};
+
+struct rx_desc {
+	u32          Next;
+	u16          MsduPos;
+	u16          MsduSize;
+
+	u8           State;
+	u8           Status;
+	u8           Rate;
+	u8           Rssi;
+	u8           LinkQuality;
+	u8           PreambleType;
+	u16          Duration;
+	u32          RxTime;
+};
+
+#define RX_DESC_FLAG_VALID       0x80
+#define RX_DESC_FLAG_CONSUMED    0x40
+#define RX_DESC_FLAG_IDLE        0x00
+
+#define RX_STATUS_SUCCESS        0x00
+
+#define RX_DESC_MSDU_POS_OFFSET      4
+#define RX_DESC_MSDU_SIZE_OFFSET     6
+#define RX_DESC_FLAGS_OFFSET         8
+#define RX_DESC_STATUS_OFFSET        9
+#define RX_DESC_RSSI_OFFSET          11
+#define RX_DESC_LINK_QUALITY_OFFSET  12
+#define RX_DESC_PREAMBLE_TYPE_OFFSET 13
+#define RX_DESC_DURATION_OFFSET      14
+#define RX_DESC_RX_TIME_OFFSET       16
+
+struct tx_desc {
+	u32       NextDescriptor;
+	u16       TxStartOfFrame;
+	u16       TxLength;
+
+	u8        TxState;
+	u8        TxStatus;
+	u8        RetryCount;
+
+	u8        TxRate;
+
+	u8        KeyIndex;
+	u8        ChiperType;
+	u8        ChipreLength;
+	u8        Reserved1;
+
+	u8        Reserved;
+	u8        PacketType;
+	u16       HostTxLength;
+};
+
+#define TX_DESC_NEXT_OFFSET          0
+#define TX_DESC_POS_OFFSET           4
+#define TX_DESC_SIZE_OFFSET          6
+#define TX_DESC_FLAGS_OFFSET         8
+#define TX_DESC_STATUS_OFFSET        9
+#define TX_DESC_RETRY_OFFSET         10
+#define TX_DESC_RATE_OFFSET          11
+#define TX_DESC_KEY_INDEX_OFFSET     12
+#define TX_DESC_CIPHER_TYPE_OFFSET   13
+#define TX_DESC_CIPHER_LENGTH_OFFSET 14
+#define TX_DESC_PACKET_TYPE_OFFSET   17
+#define TX_DESC_HOST_LENGTH_OFFSET   18
+
+/*
+ * Host-MAC interface
+ */
+
+#define TX_STATUS_SUCCESS       0x00
+
+#define TX_FIRM_OWN             0x80
+#define TX_DONE                 0x40
+
+#define TX_ERROR                0x01
+
+#define TX_PACKET_TYPE_DATA     0x01
+#define TX_PACKET_TYPE_MGMT     0x02
+
+#define ISR_EMPTY               0x00        /* no bits set in ISR */
+#define ISR_TxCOMPLETE          0x01        /* packet transmitted */
+#define ISR_RxCOMPLETE          0x02        /* packet received */
+#define ISR_RxFRAMELOST         0x04        /* Rx Frame lost */
+#define ISR_FATAL_ERROR         0x08        /* Fatal error */
+#define ISR_COMMAND_COMPLETE    0x10        /* command completed */
+#define ISR_OUT_OF_RANGE        0x20        /* command completed */
+#define ISR_IBSS_MERGE          0x40        /* (4.1.2.30): IBSS merge */
+#define ISR_GENERIC_IRQ         0x80
+
+#define Local_Mib_Type          0x01
+#define Mac_Address_Mib_Type    0x02
+#define Mac_Mib_Type            0x03
+#define Statistics_Mib_Type     0x04
+#define Mac_Mgmt_Mib_Type       0x05
+#define Mac_Wep_Mib_Type        0x06
+#define Phy_Mib_Type            0x07
+#define Multi_Domain_MIB        0x08
+
+#define MAC_MGMT_MIB_CUR_BSSID_POS            14
+#define MAC_MIB_FRAG_THRESHOLD_POS            8
+#define MAC_MIB_RTS_THRESHOLD_POS             10
+#define MAC_MIB_SHORT_RETRY_POS               16
+#define MAC_MIB_LONG_RETRY_POS                17
+#define MAC_MIB_SHORT_RETRY_LIMIT_POS         16
+#define MAC_MGMT_MIB_BEACON_PER_POS           0
+#define MAC_MGMT_MIB_STATION_ID_POS           6
+#define MAC_MGMT_MIB_CUR_PRIVACY_POS          11
+#define MAC_MGMT_MIB_CUR_BSSID_POS            14
+#define MAC_MGMT_MIB_PS_MODE_POS              53
+#define MAC_MGMT_MIB_LISTEN_INTERVAL_POS      54
+#define MAC_MGMT_MIB_MULTI_DOMAIN_IMPLEMENTED 56
+#define MAC_MGMT_MIB_MULTI_DOMAIN_ENABLED     57
+#define PHY_MIB_CHANNEL_POS                   14
+#define PHY_MIB_RATE_SET_POS                  20
+#define PHY_MIB_REG_DOMAIN_POS                26
+#define LOCAL_MIB_AUTO_TX_RATE_POS            3
+#define LOCAL_MIB_SSID_SIZE                   5
+#define LOCAL_MIB_TX_PROMISCUOUS_POS          6
+#define LOCAL_MIB_TX_MGMT_RATE_POS            7
+#define LOCAL_MIB_TX_CONTROL_RATE_POS         8
+#define LOCAL_MIB_PREAMBLE_TYPE               9
+#define MAC_ADDR_MIB_MAC_ADDR_POS             0
+
+#define         CMD_Set_MIB_Vars              0x01
+#define         CMD_Get_MIB_Vars              0x02
+#define         CMD_Scan                      0x03
+#define         CMD_Join                      0x04
+#define         CMD_Start                     0x05
+#define         CMD_EnableRadio               0x06
+#define         CMD_DisableRadio              0x07
+#define         CMD_SiteSurvey                0x0B
+
+#define         CMD_STATUS_IDLE                   0x00
+#define         CMD_STATUS_COMPLETE               0x01
+#define         CMD_STATUS_UNKNOWN                0x02
+#define         CMD_STATUS_INVALID_PARAMETER      0x03
+#define         CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04
+#define         CMD_STATUS_TIME_OUT               0x07
+#define         CMD_STATUS_IN_PROGRESS            0x08
+#define         CMD_STATUS_REJECTED_RADIO_OFF     0x09
+#define         CMD_STATUS_HOST_ERROR             0xFF
+#define         CMD_STATUS_BUSY                   0xFE
+
+#define CMD_BLOCK_COMMAND_OFFSET        0
+#define CMD_BLOCK_STATUS_OFFSET         1
+#define CMD_BLOCK_PARAMETERS_OFFSET     4
+
+#define SCAN_OPTIONS_SITE_SURVEY        0x80
+
+#define MGMT_FRAME_BODY_OFFSET		24
+#define MAX_AUTHENTICATION_RETRIES	3
+#define MAX_ASSOCIATION_RETRIES		3
+
+#define AUTHENTICATION_RESPONSE_TIME_OUT  1000
+
+#define MAX_WIRELESS_BODY  2316 /* mtu is 2312, CRC is 4 */
+#define LOOP_RETRY_LIMIT   500000
+
+#define ACTIVE_MODE	1
+#define PS_MODE		2
+
+#define MAX_ENCRYPTION_KEYS 4
+#define MAX_ENCRYPTION_KEY_SIZE 40
+
+/*
+ * 802.11 related definitions
+ */
+
+/*
+ * Regulatory Domains
+ */
+
+#define REG_DOMAIN_FCC		0x10	/* Channels	1-11	USA				*/
+#define REG_DOMAIN_DOC		0x20	/* Channel	1-11	Canada				*/
+#define REG_DOMAIN_ETSI		0x30	/* Channel	1-13	Europe (ex Spain/France)	*/
+#define REG_DOMAIN_SPAIN	0x31	/* Channel	10-11	Spain				*/
+#define REG_DOMAIN_FRANCE	0x32	/* Channel	10-13	France				*/
+#define REG_DOMAIN_MKK		0x40	/* Channel	14	Japan				*/
+#define REG_DOMAIN_MKK1		0x41	/* Channel	1-14	Japan(MKK1)			*/
+#define REG_DOMAIN_ISRAEL	0x50	/* Channel	3-9	ISRAEL				*/
+
+#define BSS_TYPE_AD_HOC		1
+#define BSS_TYPE_INFRASTRUCTURE 2
+
+#define SCAN_TYPE_ACTIVE	0
+#define SCAN_TYPE_PASSIVE	1
+
+#define LONG_PREAMBLE		0
+#define SHORT_PREAMBLE		1
+#define AUTO_PREAMBLE		2
+
+#define DATA_FRAME_WS_HEADER_SIZE   30
+
+/* promiscuous mode control */
+#define PROM_MODE_OFF			0x0
+#define PROM_MODE_UNKNOWN		0x1
+#define PROM_MODE_CRC_FAILED		0x2
+#define PROM_MODE_DUPLICATED		0x4
+#define PROM_MODE_MGMT			0x8
+#define PROM_MODE_CTRL			0x10
+#define PROM_MODE_BAD_PROTOCOL		0x20
+
+#define IFACE_INT_STATUS_OFFSET		0
+#define IFACE_INT_MASK_OFFSET		1
+#define IFACE_LOCKOUT_HOST_OFFSET	2
+#define IFACE_LOCKOUT_MAC_OFFSET	3
+#define IFACE_FUNC_CTRL_OFFSET		28
+#define IFACE_MAC_STAT_OFFSET		30
+#define IFACE_GENERIC_INT_TYPE_OFFSET	32
+
+#define CIPHER_SUITE_NONE     0
+#define CIPHER_SUITE_WEP_64   1
+#define CIPHER_SUITE_TKIP     2
+#define CIPHER_SUITE_AES      3
+#define CIPHER_SUITE_CCX      4
+#define CIPHER_SUITE_WEP_128  5
+
+/*
+ * IFACE MACROS & definitions
+ */
+
+/*
+ * FuncCtrl field:
+ */
+#define FUNC_CTRL_TxENABLE		0x10
+#define FUNC_CTRL_RxENABLE		0x20
+#define FUNC_CTRL_INIT_COMPLETE		0x01
+
+/* A stub firmware image which reads the MAC address from NVRAM on the card.
+   For copyright information and source see the end of this file. */
+static u8 mac_reader[] = {
+	0x06, 0x00, 0x00, 0xea, 0x04, 0x00, 0x00, 0xea, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xea,
+	0x01, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea,
+	0xd3, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x21, 0xe1, 0x0e, 0x04, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
+	0x81, 0x11, 0xa0, 0xe1, 0x00, 0x10, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x1c, 0x10, 0x90, 0xe5,
+	0x10, 0x10, 0xc1, 0xe3, 0x1c, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 0x08, 0x10, 0x80, 0xe5,
+	0x02, 0x03, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0xb0, 0x10, 0xc0, 0xe1, 0xb4, 0x10, 0xc0, 0xe1,
+	0xb8, 0x10, 0xc0, 0xe1, 0xbc, 0x10, 0xc0, 0xe1, 0x56, 0xdc, 0xa0, 0xe3, 0x21, 0x00, 0x00, 0xeb,
+	0x0a, 0x00, 0xa0, 0xe3, 0x1a, 0x00, 0x00, 0xeb, 0x10, 0x00, 0x00, 0xeb, 0x07, 0x00, 0x00, 0xeb,
+	0x02, 0x03, 0xa0, 0xe3, 0x02, 0x14, 0xa0, 0xe3, 0xb4, 0x10, 0xc0, 0xe1, 0x4c, 0x10, 0x9f, 0xe5,
+	0xbc, 0x10, 0xc0, 0xe1, 0x10, 0x10, 0xa0, 0xe3, 0xb8, 0x10, 0xc0, 0xe1, 0xfe, 0xff, 0xff, 0xea,
+	0x00, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe3, 0x02, 0x3c, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
+	0x28, 0x00, 0x9f, 0xe5, 0x37, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+	0x00, 0x40, 0x2d, 0xe9, 0x12, 0x2e, 0xa0, 0xe3, 0x06, 0x30, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
+	0x02, 0x04, 0xa0, 0xe3, 0x2f, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+	0x00, 0x02, 0x00, 0x02, 0x80, 0x01, 0x90, 0xe0, 0x01, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x50, 0xe2,
+	0xfc, 0xff, 0xff, 0xea, 0x1e, 0xff, 0x2f, 0xe1, 0x80, 0x10, 0xa0, 0xe3, 0xf3, 0x06, 0xa0, 0xe3,
+	0x00, 0x10, 0x80, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3,
+	0x04, 0x10, 0x80, 0xe5, 0x00, 0x10, 0x80, 0xe5, 0x0e, 0x34, 0xa0, 0xe3, 0x1c, 0x10, 0x93, 0xe5,
+	0x02, 0x1a, 0x81, 0xe3, 0x1c, 0x10, 0x83, 0xe5, 0x58, 0x11, 0x9f, 0xe5, 0x30, 0x10, 0x80, 0xe5,
+	0x54, 0x11, 0x9f, 0xe5, 0x34, 0x10, 0x80, 0xe5, 0x38, 0x10, 0x80, 0xe5, 0x3c, 0x10, 0x80, 0xe5,
+	0x10, 0x10, 0x90, 0xe5, 0x08, 0x00, 0x90, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0xf3, 0x16, 0xa0, 0xe3,
+	0x08, 0x00, 0x91, 0xe5, 0x05, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 0x10, 0x00, 0x91, 0xe5,
+	0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0xff, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5,
+	0x10, 0x00, 0x91, 0xe5, 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5,
+	0x10, 0x00, 0x91, 0xe5, 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5,
+	0xff, 0x00, 0x00, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x30, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe1,
+	0x03, 0x40, 0xa0, 0xe1, 0xa2, 0x02, 0xa0, 0xe1, 0x08, 0x00, 0x00, 0xe2, 0x03, 0x00, 0x80, 0xe2,
+	0xd8, 0x10, 0x9f, 0xe5, 0x00, 0x00, 0xc1, 0xe5, 0x01, 0x20, 0xc1, 0xe5, 0xe2, 0xff, 0xff, 0xeb,
+	0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x14, 0x00, 0xa0, 0xe3, 0xc4, 0xff, 0xff, 0xeb,
+	0x04, 0x20, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1, 0x02, 0x00, 0xa0, 0xe3, 0x01, 0x00, 0x00, 0xeb,
+	0x30, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x70, 0x40, 0x2d, 0xe9, 0xf3, 0x46, 0xa0, 0xe3,
+	0x00, 0x30, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x08, 0x00, 0x00, 0x9a, 0x8c, 0x50, 0x9f, 0xe5,
+	0x03, 0x60, 0xd5, 0xe7, 0x0c, 0x60, 0x84, 0xe5, 0x10, 0x60, 0x94, 0xe5, 0x02, 0x00, 0x16, 0xe3,
+	0xfc, 0xff, 0xff, 0x0a, 0x01, 0x30, 0x83, 0xe2, 0x00, 0x00, 0x53, 0xe1, 0xf7, 0xff, 0xff, 0x3a,
+	0xff, 0x30, 0xa0, 0xe3, 0x0c, 0x30, 0x84, 0xe5, 0x08, 0x00, 0x94, 0xe5, 0x10, 0x00, 0x94, 0xe5,
+	0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x94, 0xe5, 0x00, 0x00, 0xa0, 0xe3,
+	0x00, 0x00, 0x52, 0xe3, 0x0b, 0x00, 0x00, 0x9a, 0x10, 0x50, 0x94, 0xe5, 0x02, 0x00, 0x15, 0xe3,
+	0xfc, 0xff, 0xff, 0x0a, 0x0c, 0x30, 0x84, 0xe5, 0x10, 0x50, 0x94, 0xe5, 0x01, 0x00, 0x15, 0xe3,
+	0xfc, 0xff, 0xff, 0x0a, 0x08, 0x50, 0x94, 0xe5, 0x01, 0x50, 0xc1, 0xe4, 0x01, 0x00, 0x80, 0xe2,
+	0x02, 0x00, 0x50, 0xe1, 0xf3, 0xff, 0xff, 0x3a, 0xc8, 0x00, 0xa0, 0xe3, 0x98, 0xff, 0xff, 0xeb,
+	0x70, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x0c, 0x00, 0x02, 0x01, 0x02, 0x00, 0x02,
+	0x00, 0x01, 0x00, 0x02
+};
+
+struct atmel_private {
+	void *card; /* Bus dependent structure varies for PCcard */
+	int (*present_callback)(void *); /* And callback which uses it */
+	char firmware_id[32];
+	AtmelFWType firmware_type;
+	u8 *firmware;
+	int firmware_length;
+	struct timer_list management_timer;
+	struct net_device *dev;
+	struct device *sys_dev;
+	struct iw_statistics wstats;
+	spinlock_t irqlock, timerlock;	/* spinlocks */
+	enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type;
+	enum {
+		CARD_TYPE_PARALLEL_FLASH,
+		CARD_TYPE_SPI_FLASH,
+		CARD_TYPE_EEPROM
+	} card_type;
+	int do_rx_crc; /* If we need to CRC incoming packets */
+	int probe_crc; /* set if we don't yet know */
+	int crc_ok_cnt, crc_ko_cnt; /* counters for probing */
+	u16 rx_desc_head;
+	u16 tx_desc_free, tx_desc_head, tx_desc_tail, tx_desc_previous;
+	u16 tx_free_mem, tx_buff_head, tx_buff_tail;
+
+	u16 frag_seq, frag_len, frag_no;
+	u8 frag_source[6];
+
+	u8 wep_is_on, default_key, exclude_unencrypted, encryption_level;
+	u8 group_cipher_suite, pairwise_cipher_suite;
+	u8 wep_keys[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE];
+	int wep_key_len[MAX_ENCRYPTION_KEYS];
+	int use_wpa, radio_on_broken; /* firmware dependent stuff. */
+
+	u16 host_info_base;
+	struct host_info_struct {
+		/* NB this is matched to the hardware, don't change. */
+		u8 volatile int_status;
+		u8 volatile int_mask;
+		u8 volatile lockout_host;
+		u8 volatile lockout_mac;
+
+		u16 tx_buff_pos;
+		u16 tx_buff_size;
+		u16 tx_desc_pos;
+		u16 tx_desc_count;
+
+		u16 rx_buff_pos;
+		u16 rx_buff_size;
+		u16 rx_desc_pos;
+		u16 rx_desc_count;
+
+		u16 build_version;
+		u16 command_pos;
+
+		u16 major_version;
+		u16 minor_version;
+
+		u16 func_ctrl;
+		u16 mac_status;
+		u16 generic_IRQ_type;
+		u8  reserved[2];
+	} host_info;
+
+	enum {
+		STATION_STATE_SCANNING,
+		STATION_STATE_JOINNING,
+		STATION_STATE_AUTHENTICATING,
+		STATION_STATE_ASSOCIATING,
+		STATION_STATE_READY,
+		STATION_STATE_REASSOCIATING,
+		STATION_STATE_DOWN,
+		STATION_STATE_MGMT_ERROR
+	} station_state;
+
+	int operating_mode, power_mode;
+	time_t last_qual;
+	int beacons_this_sec;
+	int channel;
+	int reg_domain, config_reg_domain;
+	int tx_rate;
+	int auto_tx_rate;
+	int rts_threshold;
+	int frag_threshold;
+	int long_retry, short_retry;
+	int preamble;
+	int default_beacon_period, beacon_period, listen_interval;
+	int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum;
+	int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt;
+	enum {
+		SITE_SURVEY_IDLE,
+		SITE_SURVEY_IN_PROGRESS,
+		SITE_SURVEY_COMPLETED
+	} site_survey_state;
+	unsigned long last_survey;
+
+	int station_was_associated, station_is_associated;
+	int fast_scan;
+
+	struct bss_info {
+		int channel;
+		int SSIDsize;
+		int RSSI;
+		int UsingWEP;
+		int preamble;
+		int beacon_period;
+		int BSStype;
+		u8 BSSID[6];
+		u8 SSID[MAX_SSID_LENGTH];
+	} BSSinfo[MAX_BSS_ENTRIES];
+	int BSS_list_entries, current_BSS;
+	int connect_to_any_BSS;
+	int SSID_size, new_SSID_size;
+	u8 CurrentBSSID[6], BSSID[6];
+	u8 SSID[MAX_SSID_LENGTH], new_SSID[MAX_SSID_LENGTH];
+	u64 last_beacon_timestamp;
+	u8 rx_buf[MAX_WIRELESS_BODY];
+};
+
+static u8 atmel_basic_rates[4] = {0x82, 0x84, 0x0b, 0x16};
+
+static const struct {
+	int reg_domain;
+	int min, max;
+	char *name;
+} channel_table[] = { { REG_DOMAIN_FCC, 1, 11, "USA" },
+		      { REG_DOMAIN_DOC, 1, 11, "Canada" },
+		      { REG_DOMAIN_ETSI, 1, 13, "Europe" },
+		      { REG_DOMAIN_SPAIN, 10, 11, "Spain" },
+		      { REG_DOMAIN_FRANCE, 10, 13, "France" },
+		      { REG_DOMAIN_MKK, 14, 14, "MKK" },
+		      { REG_DOMAIN_MKK1, 1, 14, "MKK1" },
+		      { REG_DOMAIN_ISRAEL, 3, 9, "Israel"} };
+
+static void build_wpa_mib(struct atmel_private *priv);
+static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void atmel_copy_to_card(struct net_device *dev, u16 dest,
+			       const unsigned char *src, u16 len);
+static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest,
+			       u16 src, u16 len);
+static void atmel_set_gcr(struct net_device *dev, u16 mask);
+static void atmel_clear_gcr(struct net_device *dev, u16 mask);
+static int atmel_lock_mac(struct atmel_private *priv);
+static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data);
+static void atmel_command_irq(struct atmel_private *priv);
+static int atmel_validate_channel(struct atmel_private *priv, int channel);
+static void atmel_management_frame(struct atmel_private *priv,
+				   struct ieee80211_hdr *header,
+				   u16 frame_len, u8 rssi);
+static void atmel_management_timer(u_long a);
+static void atmel_send_command(struct atmel_private *priv, int command,
+			       void *cmd, int cmd_size);
+static int atmel_send_command_wait(struct atmel_private *priv, int command,
+				   void *cmd, int cmd_size);
+static void atmel_transmit_management_frame(struct atmel_private *priv,
+					    struct ieee80211_hdr *header,
+					    u8 *body, int body_len);
+
+static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index);
+static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index,
+			   u8 data);
+static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index,
+			    u16 data);
+static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index,
+			  u8 *data, int data_len);
+static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index,
+			  u8 *data, int data_len);
+static void atmel_scan(struct atmel_private *priv, int specific_ssid);
+static void atmel_join_bss(struct atmel_private *priv, int bss_index);
+static void atmel_smooth_qual(struct atmel_private *priv);
+static void atmel_writeAR(struct net_device *dev, u16 data);
+static int probe_atmel_card(struct net_device *dev);
+static int reset_atmel_card(struct net_device *dev);
+static void atmel_enter_state(struct atmel_private *priv, int new_state);
+int atmel_open (struct net_device *dev);
+
+static inline u16 atmel_hi(struct atmel_private *priv, u16 offset)
+{
+	return priv->host_info_base + offset;
+}
+
+static inline u16 atmel_co(struct atmel_private *priv, u16 offset)
+{
+	return priv->host_info.command_pos + offset;
+}
+
+static inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc)
+{
+	return priv->host_info.rx_desc_pos + (sizeof(struct rx_desc) * desc) + offset;
+}
+
+static inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc)
+{
+	return priv->host_info.tx_desc_pos + (sizeof(struct tx_desc) * desc) + offset;
+}
+
+static inline u8 atmel_read8(struct net_device *dev, u16 offset)
+{
+	return inb(dev->base_addr + offset);
+}
+
+static inline void atmel_write8(struct net_device *dev, u16 offset, u8 data)
+{
+	outb(data, dev->base_addr + offset);
+}
+
+static inline u16 atmel_read16(struct net_device *dev, u16 offset)
+{
+	return inw(dev->base_addr + offset);
+}
+
+static inline void atmel_write16(struct net_device *dev, u16 offset, u16 data)
+{
+	outw(data, dev->base_addr + offset);
+}
+
+static inline u8 atmel_rmem8(struct atmel_private *priv, u16 pos)
+{
+	atmel_writeAR(priv->dev, pos);
+	return atmel_read8(priv->dev, DR);
+}
+
+static inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data)
+{
+	atmel_writeAR(priv->dev, pos);
+	atmel_write8(priv->dev, DR, data);
+}
+
+static inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos)
+{
+	atmel_writeAR(priv->dev, pos);
+	return atmel_read16(priv->dev, DR);
+}
+
+static inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data)
+{
+	atmel_writeAR(priv->dev, pos);
+	atmel_write16(priv->dev, DR, data);
+}
+
+static const struct iw_handler_def atmel_handler_def;
+
+static void tx_done_irq(struct atmel_private *priv)
+{
+	int i;
+
+	for (i = 0;
+	     atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE &&
+		     i < priv->host_info.tx_desc_count;
+	     i++) {
+		u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head));
+		u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head));
+		u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head));
+
+		atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head), 0);
+
+		priv->tx_free_mem += msdu_size;
+		priv->tx_desc_free++;
+
+		if (priv->tx_buff_head + msdu_size > (priv->host_info.tx_buff_pos + priv->host_info.tx_buff_size))
+			priv->tx_buff_head = 0;
+		else
+			priv->tx_buff_head += msdu_size;
+
+		if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1))
+			priv->tx_desc_head++ ;
+		else
+			priv->tx_desc_head = 0;
+
+		if (type == TX_PACKET_TYPE_DATA) {
+			if (status == TX_STATUS_SUCCESS)
+				priv->dev->stats.tx_packets++;
+			else
+				priv->dev->stats.tx_errors++;
+			netif_wake_queue(priv->dev);
+		}
+	}
+}
+
+static u16 find_tx_buff(struct atmel_private *priv, u16 len)
+{
+	u16 bottom_free = priv->host_info.tx_buff_size - priv->tx_buff_tail;
+
+	if (priv->tx_desc_free == 3 || priv->tx_free_mem < len)
+		return 0;
+
+	if (bottom_free >= len)
+		return priv->host_info.tx_buff_pos + priv->tx_buff_tail;
+
+	if (priv->tx_free_mem - bottom_free >= len) {
+		priv->tx_buff_tail = 0;
+		return priv->host_info.tx_buff_pos;
+	}
+
+	return 0;
+}
+
+static void tx_update_descriptor(struct atmel_private *priv, int is_bcast,
+				 u16 len, u16 buff, u8 type)
+{
+	atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, priv->tx_desc_tail), buff);
+	atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_tail), len);
+	if (!priv->use_wpa)
+		atmel_wmem16(priv, atmel_tx(priv, TX_DESC_HOST_LENGTH_OFFSET, priv->tx_desc_tail), len);
+	atmel_wmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_tail), type);
+	atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RATE_OFFSET, priv->tx_desc_tail), priv->tx_rate);
+	atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RETRY_OFFSET, priv->tx_desc_tail), 0);
+	if (priv->use_wpa) {
+		int cipher_type, cipher_length;
+		if (is_bcast) {
+			cipher_type = priv->group_cipher_suite;
+			if (cipher_type == CIPHER_SUITE_WEP_64 ||
+			    cipher_type == CIPHER_SUITE_WEP_128)
+				cipher_length = 8;
+			else if (cipher_type == CIPHER_SUITE_TKIP)
+				cipher_length = 12;
+			else if (priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_64 ||
+				 priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_128) {
+				cipher_type = priv->pairwise_cipher_suite;
+				cipher_length = 8;
+			} else {
+				cipher_type = CIPHER_SUITE_NONE;
+				cipher_length = 0;
+			}
+		} else {
+			cipher_type = priv->pairwise_cipher_suite;
+			if (cipher_type == CIPHER_SUITE_WEP_64 ||
+			    cipher_type == CIPHER_SUITE_WEP_128)
+				cipher_length = 8;
+			else if (cipher_type == CIPHER_SUITE_TKIP)
+				cipher_length = 12;
+			else if (priv->group_cipher_suite == CIPHER_SUITE_WEP_64 ||
+				 priv->group_cipher_suite == CIPHER_SUITE_WEP_128) {
+				cipher_type = priv->group_cipher_suite;
+				cipher_length = 8;
+			} else {
+				cipher_type = CIPHER_SUITE_NONE;
+				cipher_length = 0;
+			}
+		}
+
+		atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail),
+			    cipher_type);
+		atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_LENGTH_OFFSET, priv->tx_desc_tail),
+			    cipher_length);
+	}
+	atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_tail), 0x80000000L);
+	atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_tail), TX_FIRM_OWN);
+	if (priv->tx_desc_previous != priv->tx_desc_tail)
+		atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_previous), 0);
+	priv->tx_desc_previous = priv->tx_desc_tail;
+	if (priv->tx_desc_tail < (priv->host_info.tx_desc_count - 1))
+		priv->tx_desc_tail++;
+	else
+		priv->tx_desc_tail = 0;
+	priv->tx_desc_free--;
+	priv->tx_free_mem -= len;
+}
+
+static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	static const u8 SNAP_RFC1024[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+	struct atmel_private *priv = netdev_priv(dev);
+	struct ieee80211_hdr header;
+	unsigned long flags;
+	u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+
+	if (priv->card && priv->present_callback &&
+	    !(*priv->present_callback)(priv->card)) {
+		dev->stats.tx_errors++;
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	if (priv->station_state != STATION_STATE_READY) {
+		dev->stats.tx_errors++;
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	/* first ensure the timer func cannot run */
+	spin_lock_bh(&priv->timerlock);
+	/* then stop the hardware ISR */
+	spin_lock_irqsave(&priv->irqlock, flags);
+	/* nb doing the above in the opposite order will deadlock */
+
+	/* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the
+	   12 first bytes (containing DA/SA) and put them in the appropriate
+	   fields of the Wireless Header. Thus the packet length is then the
+	   initial + 18 (+30-12) */
+
+	if (!(buff = find_tx_buff(priv, len + 18))) {
+		dev->stats.tx_dropped++;
+		spin_unlock_irqrestore(&priv->irqlock, flags);
+		spin_unlock_bh(&priv->timerlock);
+		netif_stop_queue(dev);
+		return NETDEV_TX_BUSY;
+	}
+
+	frame_ctl = IEEE80211_FTYPE_DATA;
+	header.duration_id = 0;
+	header.seq_ctrl = 0;
+	if (priv->wep_is_on)
+		frame_ctl |= IEEE80211_FCTL_PROTECTED;
+	if (priv->operating_mode == IW_MODE_ADHOC) {
+		skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN);
+		memcpy(&header.addr2, dev->dev_addr, ETH_ALEN);
+		memcpy(&header.addr3, priv->BSSID, ETH_ALEN);
+	} else {
+		frame_ctl |= IEEE80211_FCTL_TODS;
+		memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN);
+		memcpy(&header.addr2, dev->dev_addr, ETH_ALEN);
+		skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN);
+	}
+
+	if (priv->use_wpa)
+		memcpy(&header.addr4, SNAP_RFC1024, ETH_ALEN);
+
+	header.frame_control = cpu_to_le16(frame_ctl);
+	/* Copy the wireless header into the card */
+	atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE);
+	/* Copy the packet sans its 802.3 header addresses which have been replaced */
+	atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12);
+	priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE;
+
+	/* low bit of first byte of destination tells us if broadcast */
+	tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA);
+	dev->stats.tx_bytes += len;
+
+	spin_unlock_irqrestore(&priv->irqlock, flags);
+	spin_unlock_bh(&priv->timerlock);
+	dev_kfree_skb(skb);
+
+	return NETDEV_TX_OK;
+}
+
+static void atmel_transmit_management_frame(struct atmel_private *priv,
+					    struct ieee80211_hdr *header,
+					    u8 *body, int body_len)
+{
+	u16 buff;
+	int len = MGMT_FRAME_BODY_OFFSET + body_len;
+
+	if (!(buff = find_tx_buff(priv, len)))
+		return;
+
+	atmel_copy_to_card(priv->dev, buff, (u8 *)header, MGMT_FRAME_BODY_OFFSET);
+	atmel_copy_to_card(priv->dev, buff + MGMT_FRAME_BODY_OFFSET, body, body_len);
+	priv->tx_buff_tail += len;
+	tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT);
+}
+
+static void fast_rx_path(struct atmel_private *priv,
+			 struct ieee80211_hdr *header,
+			 u16 msdu_size, u16 rx_packet_loc, u32 crc)
+{
+	/* fast path: unfragmented packet copy directly into skbuf */
+	u8 mac4[6];
+	struct sk_buff	*skb;
+	unsigned char *skbp;
+
+	/* get the final, mac 4 header field, this tells us encapsulation */
+	atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6);
+	msdu_size -= 6;
+
+	if (priv->do_rx_crc) {
+		crc = crc32_le(crc, mac4, 6);
+		msdu_size -= 4;
+	}
+
+	if (!(skb = dev_alloc_skb(msdu_size + 14))) {
+		priv->dev->stats.rx_dropped++;
+		return;
+	}
+
+	skb_reserve(skb, 2);
+	skbp = skb_put(skb, msdu_size + 12);
+	atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size);
+
+	if (priv->do_rx_crc) {
+		u32 netcrc;
+		crc = crc32_le(crc, skbp + 12, msdu_size);
+		atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + 30 + msdu_size, 4);
+		if ((crc ^ 0xffffffff) != netcrc) {
+			priv->dev->stats.rx_crc_errors++;
+			dev_kfree_skb(skb);
+			return;
+		}
+	}
+
+	memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */
+	if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
+		memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN);
+	else
+		memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */
+
+	skb->protocol = eth_type_trans(skb, priv->dev);
+	skb->ip_summed = CHECKSUM_NONE;
+	netif_rx(skb);
+	priv->dev->stats.rx_bytes += 12 + msdu_size;
+	priv->dev->stats.rx_packets++;
+}
+
+/* Test to see if the packet in card memory at packet_loc has a valid CRC
+   It doesn't matter that this is slow: it is only used to proble the first few
+   packets. */
+static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size)
+{
+	int i = msdu_size - 4;
+	u32 netcrc, crc = 0xffffffff;
+
+	if (msdu_size < 4)
+		return 0;
+
+	atmel_copy_to_host(priv->dev, (void *)&netcrc, packet_loc + i, 4);
+
+	atmel_writeAR(priv->dev, packet_loc);
+	while (i--) {
+		u8 octet = atmel_read8(priv->dev, DR);
+		crc = crc32_le(crc, &octet, 1);
+	}
+
+	return (crc ^ 0xffffffff) == netcrc;
+}
+
+static void frag_rx_path(struct atmel_private *priv,
+			 struct ieee80211_hdr *header,
+			 u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no,
+			 u8 frag_no, int more_frags)
+{
+	u8 mac4[ETH_ALEN];
+	u8 source[ETH_ALEN];
+	struct sk_buff *skb;
+
+	if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
+		memcpy(source, header->addr3, ETH_ALEN);
+	else
+		memcpy(source, header->addr2, ETH_ALEN);
+
+	rx_packet_loc += 24; /* skip header */
+
+	if (priv->do_rx_crc)
+		msdu_size -= 4;
+
+	if (frag_no == 0) { /* first fragment */
+		atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN);
+		msdu_size -= ETH_ALEN;
+		rx_packet_loc += ETH_ALEN;
+
+		if (priv->do_rx_crc)
+			crc = crc32_le(crc, mac4, 6);
+
+		priv->frag_seq = seq_no;
+		priv->frag_no = 1;
+		priv->frag_len = msdu_size;
+		memcpy(priv->frag_source, source, ETH_ALEN);
+		memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN);
+		memcpy(priv->rx_buf, header->addr1, ETH_ALEN);
+
+		atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size);
+
+		if (priv->do_rx_crc) {
+			u32 netcrc;
+			crc = crc32_le(crc, &priv->rx_buf[12], msdu_size);
+			atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4);
+			if ((crc ^ 0xffffffff) != netcrc) {
+				priv->dev->stats.rx_crc_errors++;
+				eth_broadcast_addr(priv->frag_source);
+			}
+		}
+
+	} else if (priv->frag_no == frag_no &&
+		   priv->frag_seq == seq_no &&
+		   memcmp(priv->frag_source, source, ETH_ALEN) == 0) {
+
+		atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len],
+				   rx_packet_loc, msdu_size);
+		if (priv->do_rx_crc) {
+			u32 netcrc;
+			crc = crc32_le(crc,
+				       &priv->rx_buf[12 + priv->frag_len],
+				       msdu_size);
+			atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4);
+			if ((crc ^ 0xffffffff) != netcrc) {
+				priv->dev->stats.rx_crc_errors++;
+				eth_broadcast_addr(priv->frag_source);
+				more_frags = 1; /* don't send broken assembly */
+			}
+		}
+
+		priv->frag_len += msdu_size;
+		priv->frag_no++;
+
+		if (!more_frags) { /* last one */
+			eth_broadcast_addr(priv->frag_source);
+			if (!(skb = dev_alloc_skb(priv->frag_len + 14))) {
+				priv->dev->stats.rx_dropped++;
+			} else {
+				skb_reserve(skb, 2);
+				memcpy(skb_put(skb, priv->frag_len + 12),
+				       priv->rx_buf,
+				       priv->frag_len + 12);
+				skb->protocol = eth_type_trans(skb, priv->dev);
+				skb->ip_summed = CHECKSUM_NONE;
+				netif_rx(skb);
+				priv->dev->stats.rx_bytes += priv->frag_len + 12;
+				priv->dev->stats.rx_packets++;
+			}
+		}
+	} else
+		priv->wstats.discard.fragment++;
+}
+
+static void rx_done_irq(struct atmel_private *priv)
+{
+	int i;
+	struct ieee80211_hdr header;
+
+	for (i = 0;
+	     atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID &&
+		     i < priv->host_info.rx_desc_count;
+	     i++) {
+
+		u16 msdu_size, rx_packet_loc, frame_ctl, seq_control;
+		u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head));
+		u32 crc = 0xffffffff;
+
+		if (status != RX_STATUS_SUCCESS) {
+			if (status == 0xc1) /* determined by experiment */
+				priv->wstats.discard.nwid++;
+			else
+				priv->dev->stats.rx_errors++;
+			goto next;
+		}
+
+		msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head));
+		rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head));
+
+		if (msdu_size < 30) {
+			priv->dev->stats.rx_errors++;
+			goto next;
+		}
+
+		/* Get header as far as end of seq_ctrl */
+		atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24);
+		frame_ctl = le16_to_cpu(header.frame_control);
+		seq_control = le16_to_cpu(header.seq_ctrl);
+
+		/* probe for CRC use here if needed  once five packets have
+		   arrived with the same crc status, we assume we know what's
+		   happening and stop probing */
+		if (priv->probe_crc) {
+			if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) {
+				priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size);
+			} else {
+				priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24);
+			}
+			if (priv->do_rx_crc) {
+				if (priv->crc_ok_cnt++ > 5)
+					priv->probe_crc = 0;
+			} else {
+				if (priv->crc_ko_cnt++ > 5)
+					priv->probe_crc = 0;
+			}
+		}
+
+		/* don't CRC header when WEP in use */
+		if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) {
+			crc = crc32_le(0xffffffff, (unsigned char *)&header, 24);
+		}
+		msdu_size -= 24; /* header */
+
+		if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
+			int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS;
+			u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG;
+			u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4;
+
+			if (!more_fragments && packet_fragment_no == 0) {
+				fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc);
+			} else {
+				frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc,
+					     packet_sequence_no, packet_fragment_no, more_fragments);
+			}
+		}
+
+		if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
+			/* copy rest of packet into buffer */
+			atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size);
+
+			/* we use the same buffer for frag reassembly and control packets */
+			eth_broadcast_addr(priv->frag_source);
+
+			if (priv->do_rx_crc) {
+				/* last 4 octets is crc */
+				msdu_size -= 4;
+				crc = crc32_le(crc, (unsigned char *)&priv->rx_buf, msdu_size);
+				if ((crc ^ 0xffffffff) != (*((u32 *)&priv->rx_buf[msdu_size]))) {
+					priv->dev->stats.rx_crc_errors++;
+					goto next;
+				}
+			}
+
+			atmel_management_frame(priv, &header, msdu_size,
+					       atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head)));
+		}
+
+next:
+		/* release descriptor */
+		atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED);
+
+		if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1))
+			priv->rx_desc_head++;
+		else
+			priv->rx_desc_head = 0;
+	}
+}
+
+static irqreturn_t service_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = (struct net_device *) dev_id;
+	struct atmel_private *priv = netdev_priv(dev);
+	u8 isr;
+	int i = -1;
+	static const u8 irq_order[] = {
+		ISR_OUT_OF_RANGE,
+		ISR_RxCOMPLETE,
+		ISR_TxCOMPLETE,
+		ISR_RxFRAMELOST,
+		ISR_FATAL_ERROR,
+		ISR_COMMAND_COMPLETE,
+		ISR_IBSS_MERGE,
+		ISR_GENERIC_IRQ
+	};
+
+	if (priv->card && priv->present_callback &&
+	    !(*priv->present_callback)(priv->card))
+		return IRQ_HANDLED;
+
+	/* In this state upper-level code assumes it can mess with
+	   the card unhampered by interrupts which may change register state.
+	   Note that even though the card shouldn't generate interrupts
+	   the inturrupt line may be shared. This allows card setup
+	   to go on without disabling interrupts for a long time. */
+	if (priv->station_state == STATION_STATE_DOWN)
+		return IRQ_NONE;
+
+	atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */
+
+	while (1) {
+		if (!atmel_lock_mac(priv)) {
+			/* failed to contact card */
+			printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name);
+			return IRQ_HANDLED;
+		}
+
+		isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET));
+		atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);
+
+		if (!isr) {
+			atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */
+			return i == -1 ? IRQ_NONE : IRQ_HANDLED;
+		}
+
+		atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */
+
+		for (i = 0; i < ARRAY_SIZE(irq_order); i++)
+			if (isr & irq_order[i])
+				break;
+
+		if (!atmel_lock_mac(priv)) {
+			/* failed to contact card */
+			printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name);
+			return IRQ_HANDLED;
+		}
+
+		isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET));
+		isr ^= irq_order[i];
+		atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr);
+		atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);
+
+		switch (irq_order[i]) {
+
+		case ISR_OUT_OF_RANGE:
+			if (priv->operating_mode == IW_MODE_INFRA &&
+			    priv->station_state == STATION_STATE_READY) {
+				priv->station_is_associated = 0;
+				atmel_scan(priv, 1);
+			}
+			break;
+
+		case ISR_RxFRAMELOST:
+			priv->wstats.discard.misc++;
+			/* fall through */
+		case ISR_RxCOMPLETE:
+			rx_done_irq(priv);
+			break;
+
+		case ISR_TxCOMPLETE:
+			tx_done_irq(priv);
+			break;
+
+		case ISR_FATAL_ERROR:
+			printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name);
+			atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
+			break;
+
+		case ISR_COMMAND_COMPLETE:
+			atmel_command_irq(priv);
+			break;
+
+		case ISR_IBSS_MERGE:
+			atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS,
+				      priv->CurrentBSSID, 6);
+			/* The WPA stuff cares about the current AP address */
+			if (priv->use_wpa)
+				build_wpa_mib(priv);
+			break;
+		case ISR_GENERIC_IRQ:
+			printk(KERN_INFO "%s: Generic_irq received.\n", dev->name);
+			break;
+		}
+	}
+}
+
+static struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	/* update the link quality here in case we are seeing no beacons
+	   at all to drive the process */
+	atmel_smooth_qual(priv);
+
+	priv->wstats.status = priv->station_state;
+
+	if (priv->operating_mode == IW_MODE_INFRA) {
+		if (priv->station_state != STATION_STATE_READY) {
+			priv->wstats.qual.qual = 0;
+			priv->wstats.qual.level = 0;
+			priv->wstats.qual.updated = (IW_QUAL_QUAL_INVALID
+					| IW_QUAL_LEVEL_INVALID);
+		}
+		priv->wstats.qual.noise = 0;
+		priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID;
+	} else {
+		/* Quality levels cannot be determined in ad-hoc mode,
+		   because we can 'hear' more that one remote station. */
+		priv->wstats.qual.qual = 0;
+		priv->wstats.qual.level	= 0;
+		priv->wstats.qual.noise	= 0;
+		priv->wstats.qual.updated = IW_QUAL_QUAL_INVALID
+					| IW_QUAL_LEVEL_INVALID
+					| IW_QUAL_NOISE_INVALID;
+		priv->wstats.miss.beacon = 0;
+	}
+
+	return &priv->wstats;
+}
+
+static int atmel_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if ((new_mtu < 68) || (new_mtu > 2312))
+		return -EINVAL;
+	dev->mtu = new_mtu;
+	return 0;
+}
+
+static int atmel_set_mac_address(struct net_device *dev, void *p)
+{
+	struct sockaddr *addr = p;
+
+	memcpy (dev->dev_addr, addr->sa_data, dev->addr_len);
+	return atmel_open(dev);
+}
+
+EXPORT_SYMBOL(atmel_open);
+
+int atmel_open(struct net_device *dev)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	int i, channel, err;
+
+	/* any scheduled timer is no longer needed and might screw things up.. */
+	del_timer_sync(&priv->management_timer);
+
+	/* Interrupts will not touch the card once in this state... */
+	priv->station_state = STATION_STATE_DOWN;
+
+	if (priv->new_SSID_size) {
+		memcpy(priv->SSID, priv->new_SSID, priv->new_SSID_size);
+		priv->SSID_size = priv->new_SSID_size;
+		priv->new_SSID_size = 0;
+	}
+	priv->BSS_list_entries = 0;
+
+	priv->AuthenticationRequestRetryCnt = 0;
+	priv->AssociationRequestRetryCnt = 0;
+	priv->ReAssociationRequestRetryCnt = 0;
+	priv->CurrentAuthentTransactionSeqNum = 0x0001;
+	priv->ExpectedAuthentTransactionSeqNum = 0x0002;
+
+	priv->site_survey_state = SITE_SURVEY_IDLE;
+	priv->station_is_associated = 0;
+
+	err = reset_atmel_card(dev);
+	if (err)
+		return err;
+
+	if (priv->config_reg_domain) {
+		priv->reg_domain = priv->config_reg_domain;
+		atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS, priv->reg_domain);
+	} else {
+		priv->reg_domain = atmel_get_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS);
+		for (i = 0; i < ARRAY_SIZE(channel_table); i++)
+			if (priv->reg_domain == channel_table[i].reg_domain)
+				break;
+		if (i == ARRAY_SIZE(channel_table)) {
+			priv->reg_domain = REG_DOMAIN_MKK1;
+			printk(KERN_ALERT "%s: failed to get regulatory domain: assuming MKK1.\n", dev->name);
+		}
+	}
+
+	if ((channel = atmel_validate_channel(priv, priv->channel)))
+		priv->channel = channel;
+
+	/* this moves station_state on.... */
+	atmel_scan(priv, 1);
+
+	atmel_set_gcr(priv->dev, GCR_ENINT); /* enable interrupts */
+	return 0;
+}
+
+static int atmel_close(struct net_device *dev)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	/* Send event to userspace that we are disassociating */
+	if (priv->station_state == STATION_STATE_READY) {
+		union iwreq_data wrqu;
+
+		wrqu.data.length = 0;
+		wrqu.data.flags = 0;
+		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+		eth_zero_addr(wrqu.ap_addr.sa_data);
+		wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+	}
+
+	atmel_enter_state(priv, STATION_STATE_DOWN);
+
+	if (priv->bus_type == BUS_TYPE_PCCARD)
+		atmel_write16(dev, GCR, 0x0060);
+	atmel_write16(dev, GCR, 0x0040);
+	return 0;
+}
+
+static int atmel_validate_channel(struct atmel_private *priv, int channel)
+{
+	/* check that channel is OK, if so return zero,
+	   else return suitable default channel */
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(channel_table); i++)
+		if (priv->reg_domain == channel_table[i].reg_domain) {
+			if (channel >= channel_table[i].min &&
+			    channel <= channel_table[i].max)
+				return 0;
+			else
+				return channel_table[i].min;
+		}
+	return 0;
+}
+
+static int atmel_proc_show(struct seq_file *m, void *v)
+{
+	struct atmel_private *priv = m->private;
+	int i;
+	char *s, *r, *c;
+
+	seq_printf(m, "Driver version:\t\t%d.%d\n", DRIVER_MAJOR, DRIVER_MINOR);
+
+	if (priv->station_state != STATION_STATE_DOWN) {
+		seq_printf(m,
+			   "Firmware version:\t%d.%d build %d\n"
+			   "Firmware location:\t",
+			   priv->host_info.major_version,
+			   priv->host_info.minor_version,
+			   priv->host_info.build_version);
+
+		if (priv->card_type != CARD_TYPE_EEPROM)
+			seq_puts(m, "on card\n");
+		else if (priv->firmware)
+			seq_printf(m, "%s loaded by host\n", priv->firmware_id);
+		else
+			seq_printf(m, "%s loaded by hotplug\n", priv->firmware_id);
+
+		switch (priv->card_type) {
+		case CARD_TYPE_PARALLEL_FLASH:
+			c = "Parallel flash";
+			break;
+		case CARD_TYPE_SPI_FLASH:
+			c = "SPI flash\n";
+			break;
+		case CARD_TYPE_EEPROM:
+			c = "EEPROM";
+			break;
+		default:
+			c = "<unknown>";
+		}
+
+		r = "<unknown>";
+		for (i = 0; i < ARRAY_SIZE(channel_table); i++)
+			if (priv->reg_domain == channel_table[i].reg_domain)
+				r = channel_table[i].name;
+
+		seq_printf(m, "MAC memory type:\t%s\n", c);
+		seq_printf(m, "Regulatory domain:\t%s\n", r);
+		seq_printf(m, "Host CRC checking:\t%s\n",
+			 priv->do_rx_crc ? "On" : "Off");
+		seq_printf(m, "WPA-capable firmware:\t%s\n",
+			 priv->use_wpa ? "Yes" : "No");
+	}
+
+	switch (priv->station_state) {
+	case STATION_STATE_SCANNING:
+		s = "Scanning";
+		break;
+	case STATION_STATE_JOINNING:
+		s = "Joining";
+		break;
+	case STATION_STATE_AUTHENTICATING:
+		s = "Authenticating";
+		break;
+	case STATION_STATE_ASSOCIATING:
+		s = "Associating";
+		break;
+	case STATION_STATE_READY:
+		s = "Ready";
+		break;
+	case STATION_STATE_REASSOCIATING:
+		s = "Reassociating";
+		break;
+	case STATION_STATE_MGMT_ERROR:
+		s = "Management error";
+		break;
+	case STATION_STATE_DOWN:
+		s = "Down";
+		break;
+	default:
+		s = "<unknown>";
+	}
+
+	seq_printf(m, "Current state:\t\t%s\n", s);
+	return 0;
+}
+
+static int atmel_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, atmel_proc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations atmel_proc_fops = {
+	.open		= atmel_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static const struct net_device_ops atmel_netdev_ops = {
+	.ndo_open 		= atmel_open,
+	.ndo_stop		= atmel_close,
+	.ndo_change_mtu 	= atmel_change_mtu,
+	.ndo_set_mac_address 	= atmel_set_mac_address,
+	.ndo_start_xmit 	= start_tx,
+	.ndo_do_ioctl 		= atmel_ioctl,
+	.ndo_validate_addr	= eth_validate_addr,
+};
+
+struct net_device *init_atmel_card(unsigned short irq, unsigned long port,
+				   const AtmelFWType fw_type,
+				   struct device *sys_dev,
+				   int (*card_present)(void *), void *card)
+{
+	struct net_device *dev;
+	struct atmel_private *priv;
+	int rc;
+
+	/* Create the network device object. */
+	dev = alloc_etherdev(sizeof(*priv));
+	if (!dev)
+		return NULL;
+
+	if (dev_alloc_name(dev, dev->name) < 0) {
+		printk(KERN_ERR "atmel: Couldn't get name!\n");
+		goto err_out_free;
+	}
+
+	priv = netdev_priv(dev);
+	priv->dev = dev;
+	priv->sys_dev = sys_dev;
+	priv->present_callback = card_present;
+	priv->card = card;
+	priv->firmware = NULL;
+	priv->firmware_id[0] = '\0';
+	priv->firmware_type = fw_type;
+	if (firmware) /* module parameter */
+		strcpy(priv->firmware_id, firmware);
+	priv->bus_type = card_present ? BUS_TYPE_PCCARD : BUS_TYPE_PCI;
+	priv->station_state = STATION_STATE_DOWN;
+	priv->do_rx_crc = 0;
+	/* For PCMCIA cards, some chips need CRC, some don't
+	   so we have to probe. */
+	if (priv->bus_type == BUS_TYPE_PCCARD) {
+		priv->probe_crc = 1;
+		priv->crc_ok_cnt = priv->crc_ko_cnt = 0;
+	} else
+		priv->probe_crc = 0;
+	priv->last_qual = jiffies;
+	priv->last_beacon_timestamp = 0;
+	memset(priv->frag_source, 0xff, sizeof(priv->frag_source));
+	eth_zero_addr(priv->BSSID);
+	priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */
+	priv->station_was_associated = 0;
+
+	priv->last_survey = jiffies;
+	priv->preamble = LONG_PREAMBLE;
+	priv->operating_mode = IW_MODE_INFRA;
+	priv->connect_to_any_BSS = 0;
+	priv->config_reg_domain = 0;
+	priv->reg_domain = 0;
+	priv->tx_rate = 3;
+	priv->auto_tx_rate = 1;
+	priv->channel = 4;
+	priv->power_mode = 0;
+	priv->SSID[0] = '\0';
+	priv->SSID_size = 0;
+	priv->new_SSID_size = 0;
+	priv->frag_threshold = 2346;
+	priv->rts_threshold = 2347;
+	priv->short_retry = 7;
+	priv->long_retry = 4;
+
+	priv->wep_is_on = 0;
+	priv->default_key = 0;
+	priv->encryption_level = 0;
+	priv->exclude_unencrypted = 0;
+	priv->group_cipher_suite = priv->pairwise_cipher_suite = CIPHER_SUITE_NONE;
+	priv->use_wpa = 0;
+	memset(priv->wep_keys, 0, sizeof(priv->wep_keys));
+	memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len));
+
+	priv->default_beacon_period = priv->beacon_period = 100;
+	priv->listen_interval = 1;
+
+	init_timer(&priv->management_timer);
+	spin_lock_init(&priv->irqlock);
+	spin_lock_init(&priv->timerlock);
+	priv->management_timer.function = atmel_management_timer;
+	priv->management_timer.data = (unsigned long) dev;
+
+	dev->netdev_ops = &atmel_netdev_ops;
+	dev->wireless_handlers = &atmel_handler_def;
+	dev->irq = irq;
+	dev->base_addr = port;
+
+	SET_NETDEV_DEV(dev, sys_dev);
+
+	if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) {
+		printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc);
+		goto err_out_free;
+	}
+
+	if (!request_region(dev->base_addr, 32,
+			    priv->bus_type == BUS_TYPE_PCCARD ?  "atmel_cs" : "atmel_pci")) {
+		goto err_out_irq;
+	}
+
+	if (register_netdev(dev))
+		goto err_out_res;
+
+	if (!probe_atmel_card(dev)) {
+		unregister_netdev(dev);
+		goto err_out_res;
+	}
+
+	netif_carrier_off(dev);
+
+	if (!proc_create_data("driver/atmel", 0, NULL, &atmel_proc_fops, priv))
+		printk(KERN_WARNING "atmel: unable to create /proc entry.\n");
+
+	printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %pM\n",
+	       dev->name, DRIVER_MAJOR, DRIVER_MINOR, dev->dev_addr);
+
+	return dev;
+
+err_out_res:
+	release_region(dev->base_addr, 32);
+err_out_irq:
+	free_irq(dev->irq, dev);
+err_out_free:
+	free_netdev(dev);
+	return NULL;
+}
+
+EXPORT_SYMBOL(init_atmel_card);
+
+void stop_atmel_card(struct net_device *dev)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	/* put a brick on it... */
+	if (priv->bus_type == BUS_TYPE_PCCARD)
+		atmel_write16(dev, GCR, 0x0060);
+	atmel_write16(dev, GCR, 0x0040);
+
+	del_timer_sync(&priv->management_timer);
+	unregister_netdev(dev);
+	remove_proc_entry("driver/atmel", NULL);
+	free_irq(dev->irq, dev);
+	kfree(priv->firmware);
+	release_region(dev->base_addr, 32);
+	free_netdev(dev);
+}
+
+EXPORT_SYMBOL(stop_atmel_card);
+
+static int atmel_set_essid(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_point *dwrq,
+			   char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	/* Check if we asked for `any' */
+	if (dwrq->flags == 0) {
+		priv->connect_to_any_BSS = 1;
+	} else {
+		int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+		priv->connect_to_any_BSS = 0;
+
+		/* Check the size of the string */
+		if (dwrq->length > MAX_SSID_LENGTH)
+			 return -E2BIG;
+		if (index != 0)
+			return -EINVAL;
+
+		memcpy(priv->new_SSID, extra, dwrq->length);
+		priv->new_SSID_size = dwrq->length;
+	}
+
+	return -EINPROGRESS;
+}
+
+static int atmel_get_essid(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_point *dwrq,
+			   char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	/* Get the current SSID */
+	if (priv->new_SSID_size != 0) {
+		memcpy(extra, priv->new_SSID, priv->new_SSID_size);
+		dwrq->length = priv->new_SSID_size;
+	} else {
+		memcpy(extra, priv->SSID, priv->SSID_size);
+		dwrq->length = priv->SSID_size;
+	}
+
+	dwrq->flags = !priv->connect_to_any_BSS; /* active */
+
+	return 0;
+}
+
+static int atmel_get_wap(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct sockaddr *awrq,
+			 char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN);
+	awrq->sa_family = ARPHRD_ETHER;
+
+	return 0;
+}
+
+static int atmel_set_encode(struct net_device *dev,
+			    struct iw_request_info *info,
+			    struct iw_point *dwrq,
+			    char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	/* Basic checking: do we have a key to set ?
+	 * Note : with the new API, it's impossible to get a NULL pointer.
+	 * Therefore, we need to check a key size == 0 instead.
+	 * New version of iwconfig properly set the IW_ENCODE_NOKEY flag
+	 * when no key is present (only change flags), but older versions
+	 * don't do it. - Jean II */
+	if (dwrq->length > 0) {
+		int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+		int current_index = priv->default_key;
+		/* Check the size of the key */
+		if (dwrq->length > 13) {
+			return -EINVAL;
+		}
+		/* Check the index (none -> use current) */
+		if (index < 0 || index >= 4)
+			index = current_index;
+		else
+			priv->default_key = index;
+		/* Set the length */
+		if (dwrq->length > 5)
+			priv->wep_key_len[index] = 13;
+		else
+			if (dwrq->length > 0)
+				priv->wep_key_len[index] = 5;
+			else
+				/* Disable the key */
+				priv->wep_key_len[index] = 0;
+		/* Check if the key is not marked as invalid */
+		if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
+			/* Cleanup */
+			memset(priv->wep_keys[index], 0, 13);
+			/* Copy the key in the driver */
+			memcpy(priv->wep_keys[index], extra, dwrq->length);
+		}
+		/* WE specify that if a valid key is set, encryption
+		 * should be enabled (user may turn it off later)
+		 * This is also how "iwconfig ethX key on" works */
+		if (index == current_index &&
+		    priv->wep_key_len[index] > 0) {
+			priv->wep_is_on = 1;
+			priv->exclude_unencrypted = 1;
+			if (priv->wep_key_len[index] > 5) {
+				priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128;
+				priv->encryption_level = 2;
+			} else {
+				priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64;
+				priv->encryption_level = 1;
+			}
+		}
+	} else {
+		/* Do we want to just set the transmit key index ? */
+		int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+		if (index >= 0 && index < 4) {
+			priv->default_key = index;
+		} else
+			/* Don't complain if only change the mode */
+			if (!(dwrq->flags & IW_ENCODE_MODE))
+				return -EINVAL;
+	}
+	/* Read the flags */
+	if (dwrq->flags & IW_ENCODE_DISABLED) {
+		priv->wep_is_on = 0;
+		priv->encryption_level = 0;
+		priv->pairwise_cipher_suite = CIPHER_SUITE_NONE;
+	} else {
+		priv->wep_is_on = 1;
+		if (priv->wep_key_len[priv->default_key] > 5) {
+			priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128;
+			priv->encryption_level = 2;
+		} else {
+			priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64;
+			priv->encryption_level = 1;
+		}
+	}
+	if (dwrq->flags & IW_ENCODE_RESTRICTED)
+		priv->exclude_unencrypted = 1;
+	if (dwrq->flags & IW_ENCODE_OPEN)
+		priv->exclude_unencrypted = 0;
+
+	return -EINPROGRESS;		/* Call commit handler */
+}
+
+static int atmel_get_encode(struct net_device *dev,
+			    struct iw_request_info *info,
+			    struct iw_point *dwrq,
+			    char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+	if (!priv->wep_is_on)
+		dwrq->flags = IW_ENCODE_DISABLED;
+	else {
+		if (priv->exclude_unencrypted)
+			dwrq->flags = IW_ENCODE_RESTRICTED;
+		else
+			dwrq->flags = IW_ENCODE_OPEN;
+	}
+		/* Which key do we want ? -1 -> tx index */
+	if (index < 0 || index >= 4)
+		index = priv->default_key;
+	dwrq->flags |= index + 1;
+	/* Copy the key to the user buffer */
+	dwrq->length = priv->wep_key_len[index];
+	if (dwrq->length > 16) {
+		dwrq->length = 0;
+	} else {
+		memset(extra, 0, 16);
+		memcpy(extra, priv->wep_keys[index], dwrq->length);
+	}
+
+	return 0;
+}
+
+static int atmel_set_encodeext(struct net_device *dev,
+			    struct iw_request_info *info,
+			    union iwreq_data *wrqu,
+			    char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	struct iw_point *encoding = &wrqu->encoding;
+	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+	int idx, key_len, alg = ext->alg, set_key = 1;
+
+	/* Determine and validate the key index */
+	idx = encoding->flags & IW_ENCODE_INDEX;
+	if (idx) {
+		if (idx < 1 || idx > 4)
+			return -EINVAL;
+		idx--;
+	} else
+		idx = priv->default_key;
+
+	if (encoding->flags & IW_ENCODE_DISABLED)
+	    alg = IW_ENCODE_ALG_NONE;
+
+	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+		priv->default_key = idx;
+		set_key = ext->key_len > 0 ? 1 : 0;
+	}
+
+	if (set_key) {
+		/* Set the requested key first */
+		switch (alg) {
+		case IW_ENCODE_ALG_NONE:
+			priv->wep_is_on = 0;
+			priv->encryption_level = 0;
+			priv->pairwise_cipher_suite = CIPHER_SUITE_NONE;
+			break;
+		case IW_ENCODE_ALG_WEP:
+			if (ext->key_len > 5) {
+				priv->wep_key_len[idx] = 13;
+				priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128;
+				priv->encryption_level = 2;
+			} else if (ext->key_len > 0) {
+				priv->wep_key_len[idx] = 5;
+				priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64;
+				priv->encryption_level = 1;
+			} else {
+				return -EINVAL;
+			}
+			priv->wep_is_on = 1;
+			memset(priv->wep_keys[idx], 0, 13);
+			key_len = min ((int)ext->key_len, priv->wep_key_len[idx]);
+			memcpy(priv->wep_keys[idx], ext->key, key_len);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return -EINPROGRESS;
+}
+
+static int atmel_get_encodeext(struct net_device *dev,
+			    struct iw_request_info *info,
+			    union iwreq_data *wrqu,
+			    char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	struct iw_point *encoding = &wrqu->encoding;
+	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+	int idx, max_key_len;
+
+	max_key_len = encoding->length - sizeof(*ext);
+	if (max_key_len < 0)
+		return -EINVAL;
+
+	idx = encoding->flags & IW_ENCODE_INDEX;
+	if (idx) {
+		if (idx < 1 || idx > 4)
+			return -EINVAL;
+		idx--;
+	} else
+		idx = priv->default_key;
+
+	encoding->flags = idx + 1;
+	memset(ext, 0, sizeof(*ext));
+
+	if (!priv->wep_is_on) {
+		ext->alg = IW_ENCODE_ALG_NONE;
+		ext->key_len = 0;
+		encoding->flags |= IW_ENCODE_DISABLED;
+	} else {
+		if (priv->encryption_level > 0)
+			ext->alg = IW_ENCODE_ALG_WEP;
+		else
+			return -EINVAL;
+
+		ext->key_len = priv->wep_key_len[idx];
+		memcpy(ext->key, priv->wep_keys[idx], ext->key_len);
+		encoding->flags |= IW_ENCODE_ENABLED;
+	}
+
+	return 0;
+}
+
+static int atmel_set_auth(struct net_device *dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *wrqu, char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	struct iw_param *param = &wrqu->param;
+
+	switch (param->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_WPA_VERSION:
+	case IW_AUTH_CIPHER_PAIRWISE:
+	case IW_AUTH_CIPHER_GROUP:
+	case IW_AUTH_KEY_MGMT:
+	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+	case IW_AUTH_PRIVACY_INVOKED:
+		/*
+		 * atmel does not use these parameters
+		 */
+		break;
+
+	case IW_AUTH_DROP_UNENCRYPTED:
+		priv->exclude_unencrypted = param->value ? 1 : 0;
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG: {
+			if (param->value & IW_AUTH_ALG_SHARED_KEY) {
+				priv->exclude_unencrypted = 1;
+			} else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
+				priv->exclude_unencrypted = 0;
+			} else
+				return -EINVAL;
+			break;
+		}
+
+	case IW_AUTH_WPA_ENABLED:
+		/* Silently accept disable of WPA */
+		if (param->value > 0)
+			return -EOPNOTSUPP;
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+	return -EINPROGRESS;
+}
+
+static int atmel_get_auth(struct net_device *dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *wrqu, char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	struct iw_param *param = &wrqu->param;
+
+	switch (param->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_DROP_UNENCRYPTED:
+		param->value = priv->exclude_unencrypted;
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG:
+		if (priv->exclude_unencrypted == 1)
+			param->value = IW_AUTH_ALG_SHARED_KEY;
+		else
+			param->value = IW_AUTH_ALG_OPEN_SYSTEM;
+		break;
+
+	case IW_AUTH_WPA_ENABLED:
+		param->value = 0;
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+
+
+static int atmel_get_name(struct net_device *dev,
+			  struct iw_request_info *info,
+			  char *cwrq,
+			  char *extra)
+{
+	strcpy(cwrq, "IEEE 802.11-DS");
+	return 0;
+}
+
+static int atmel_set_rate(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *vwrq,
+			  char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	if (vwrq->fixed == 0) {
+		priv->tx_rate = 3;
+		priv->auto_tx_rate = 1;
+	} else {
+		priv->auto_tx_rate = 0;
+
+		/* Which type of value ? */
+		if ((vwrq->value < 4) && (vwrq->value >= 0)) {
+			/* Setting by rate index */
+			priv->tx_rate = vwrq->value;
+		} else {
+		/* Setting by frequency value */
+			switch (vwrq->value) {
+			case  1000000:
+				priv->tx_rate = 0;
+				break;
+			case  2000000:
+				priv->tx_rate = 1;
+				break;
+			case  5500000:
+				priv->tx_rate = 2;
+				break;
+			case 11000000:
+				priv->tx_rate = 3;
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
+	}
+
+	return -EINPROGRESS;
+}
+
+static int atmel_set_mode(struct net_device *dev,
+			  struct iw_request_info *info,
+			  __u32 *uwrq,
+			  char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	if (*uwrq != IW_MODE_ADHOC && *uwrq != IW_MODE_INFRA)
+		return -EINVAL;
+
+	priv->operating_mode = *uwrq;
+	return -EINPROGRESS;
+}
+
+static int atmel_get_mode(struct net_device *dev,
+			  struct iw_request_info *info,
+			  __u32 *uwrq,
+			  char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	*uwrq = priv->operating_mode;
+	return 0;
+}
+
+static int atmel_get_rate(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *vwrq,
+			 char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	if (priv->auto_tx_rate) {
+		vwrq->fixed = 0;
+		vwrq->value = 11000000;
+	} else {
+		vwrq->fixed = 1;
+		switch (priv->tx_rate) {
+		case 0:
+			vwrq->value =  1000000;
+			break;
+		case 1:
+			vwrq->value =  2000000;
+			break;
+		case 2:
+			vwrq->value =  5500000;
+			break;
+		case 3:
+			vwrq->value = 11000000;
+			break;
+		}
+	}
+	return 0;
+}
+
+static int atmel_set_power(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_param *vwrq,
+			   char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	priv->power_mode = vwrq->disabled ? 0 : 1;
+	return -EINPROGRESS;
+}
+
+static int atmel_get_power(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_param *vwrq,
+			   char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	vwrq->disabled = priv->power_mode ? 0 : 1;
+	vwrq->flags = IW_POWER_ON;
+	return 0;
+}
+
+static int atmel_set_retry(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_param *vwrq,
+			   char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) {
+		if (vwrq->flags & IW_RETRY_LONG)
+			priv->long_retry = vwrq->value;
+		else if (vwrq->flags & IW_RETRY_SHORT)
+			priv->short_retry = vwrq->value;
+		else {
+			/* No modifier : set both */
+			priv->long_retry = vwrq->value;
+			priv->short_retry = vwrq->value;
+		}
+		return -EINPROGRESS;
+	}
+
+	return -EINVAL;
+}
+
+static int atmel_get_retry(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_param *vwrq,
+			   char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	vwrq->disabled = 0;      /* Can't be disabled */
+
+	/* Note : by default, display the short retry number */
+	if (vwrq->flags & IW_RETRY_LONG) {
+		vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
+		vwrq->value = priv->long_retry;
+	} else {
+		vwrq->flags = IW_RETRY_LIMIT;
+		vwrq->value = priv->short_retry;
+		if (priv->long_retry != priv->short_retry)
+			vwrq->flags |= IW_RETRY_SHORT;
+	}
+
+	return 0;
+}
+
+static int atmel_set_rts(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *vwrq,
+			 char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	int rthr = vwrq->value;
+
+	if (vwrq->disabled)
+		rthr = 2347;
+	if ((rthr < 0) || (rthr > 2347)) {
+		return -EINVAL;
+	}
+	priv->rts_threshold = rthr;
+
+	return -EINPROGRESS;		/* Call commit handler */
+}
+
+static int atmel_get_rts(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *vwrq,
+			 char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	vwrq->value = priv->rts_threshold;
+	vwrq->disabled = (vwrq->value >= 2347);
+	vwrq->fixed = 1;
+
+	return 0;
+}
+
+static int atmel_set_frag(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *vwrq,
+			  char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	int fthr = vwrq->value;
+
+	if (vwrq->disabled)
+		fthr = 2346;
+	if ((fthr < 256) || (fthr > 2346)) {
+		return -EINVAL;
+	}
+	fthr &= ~0x1;	/* Get an even value - is it really needed ??? */
+	priv->frag_threshold = fthr;
+
+	return -EINPROGRESS;		/* Call commit handler */
+}
+
+static int atmel_get_frag(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *vwrq,
+			  char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	vwrq->value = priv->frag_threshold;
+	vwrq->disabled = (vwrq->value >= 2346);
+	vwrq->fixed = 1;
+
+	return 0;
+}
+
+static int atmel_set_freq(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_freq *fwrq,
+			  char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	int rc = -EINPROGRESS;		/* Call commit handler */
+
+	/* If setting by frequency, convert to a channel */
+	if (fwrq->e == 1) {
+		int f = fwrq->m / 100000;
+
+		/* Hack to fall through... */
+		fwrq->e = 0;
+		fwrq->m = ieee80211_frequency_to_channel(f);
+	}
+	/* Setting by channel number */
+	if ((fwrq->m > 1000) || (fwrq->e > 0))
+		rc = -EOPNOTSUPP;
+	else {
+		int channel = fwrq->m;
+		if (atmel_validate_channel(priv, channel) == 0) {
+			priv->channel = channel;
+		} else {
+			rc = -EINVAL;
+		}
+	}
+	return rc;
+}
+
+static int atmel_get_freq(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_freq *fwrq,
+			  char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+
+	fwrq->m = priv->channel;
+	fwrq->e = 0;
+	return 0;
+}
+
+static int atmel_set_scan(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_point *dwrq,
+			  char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	unsigned long flags;
+
+	/* Note : you may have realised that, as this is a SET operation,
+	 * this is privileged and therefore a normal user can't
+	 * perform scanning.
+	 * This is not an error, while the device perform scanning,
+	 * traffic doesn't flow, so it's a perfect DoS...
+	 * Jean II */
+
+	if (priv->station_state == STATION_STATE_DOWN)
+		return -EAGAIN;
+
+	/* Timeout old surveys. */
+	if (time_after(jiffies, priv->last_survey + 20 * HZ))
+		priv->site_survey_state = SITE_SURVEY_IDLE;
+	priv->last_survey = jiffies;
+
+	/* Initiate a scan command */
+	if (priv->site_survey_state == SITE_SURVEY_IN_PROGRESS)
+		return -EBUSY;
+
+	del_timer_sync(&priv->management_timer);
+	spin_lock_irqsave(&priv->irqlock, flags);
+
+	priv->site_survey_state = SITE_SURVEY_IN_PROGRESS;
+	priv->fast_scan = 0;
+	atmel_scan(priv, 0);
+	spin_unlock_irqrestore(&priv->irqlock, flags);
+
+	return 0;
+}
+
+static int atmel_get_scan(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_point *dwrq,
+			  char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	int i;
+	char *current_ev = extra;
+	struct iw_event	iwe;
+
+	if (priv->site_survey_state != SITE_SURVEY_COMPLETED)
+		return -EAGAIN;
+
+	for (i = 0; i < priv->BSS_list_entries; i++) {
+		iwe.cmd = SIOCGIWAP;
+		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+		memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN);
+		current_ev = iwe_stream_add_event(info, current_ev,
+						  extra + IW_SCAN_MAX_DATA,
+						  &iwe, IW_EV_ADDR_LEN);
+
+		iwe.u.data.length =  priv->BSSinfo[i].SSIDsize;
+		if (iwe.u.data.length > 32)
+			iwe.u.data.length = 32;
+		iwe.cmd = SIOCGIWESSID;
+		iwe.u.data.flags = 1;
+		current_ev = iwe_stream_add_point(info, current_ev,
+						  extra + IW_SCAN_MAX_DATA,
+						  &iwe, priv->BSSinfo[i].SSID);
+
+		iwe.cmd = SIOCGIWMODE;
+		iwe.u.mode = priv->BSSinfo[i].BSStype;
+		current_ev = iwe_stream_add_event(info, current_ev,
+						  extra + IW_SCAN_MAX_DATA,
+						  &iwe, IW_EV_UINT_LEN);
+
+		iwe.cmd = SIOCGIWFREQ;
+		iwe.u.freq.m = priv->BSSinfo[i].channel;
+		iwe.u.freq.e = 0;
+		current_ev = iwe_stream_add_event(info, current_ev,
+						  extra + IW_SCAN_MAX_DATA,
+						  &iwe, IW_EV_FREQ_LEN);
+
+		/* Add quality statistics */
+		iwe.cmd = IWEVQUAL;
+		iwe.u.qual.level = priv->BSSinfo[i].RSSI;
+		iwe.u.qual.qual  = iwe.u.qual.level;
+		/* iwe.u.qual.noise  = SOMETHING */
+		current_ev = iwe_stream_add_event(info, current_ev,
+						  extra + IW_SCAN_MAX_DATA,
+						  &iwe, IW_EV_QUAL_LEN);
+
+
+		iwe.cmd = SIOCGIWENCODE;
+		if (priv->BSSinfo[i].UsingWEP)
+			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+		else
+			iwe.u.data.flags = IW_ENCODE_DISABLED;
+		iwe.u.data.length = 0;
+		current_ev = iwe_stream_add_point(info, current_ev,
+						  extra + IW_SCAN_MAX_DATA,
+						  &iwe, NULL);
+	}
+
+	/* Length of data */
+	dwrq->length = (current_ev - extra);
+	dwrq->flags = 0;
+
+	return 0;
+}
+
+static int atmel_get_range(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_point *dwrq,
+			   char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	struct iw_range *range = (struct iw_range *) extra;
+	int k, i, j;
+
+	dwrq->length = sizeof(struct iw_range);
+	memset(range, 0, sizeof(struct iw_range));
+	range->min_nwid = 0x0000;
+	range->max_nwid = 0x0000;
+	range->num_channels = 0;
+	for (j = 0; j < ARRAY_SIZE(channel_table); j++)
+		if (priv->reg_domain == channel_table[j].reg_domain) {
+			range->num_channels = channel_table[j].max - channel_table[j].min + 1;
+			break;
+		}
+	if (range->num_channels != 0) {
+		for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) {
+			range->freq[k].i = i; /* List index */
+
+			/* Values in MHz -> * 10^5 * 10 */
+			range->freq[k].m = 100000 *
+			 ieee80211_channel_to_frequency(i, IEEE80211_BAND_2GHZ);
+			range->freq[k++].e = 1;
+		}
+		range->num_frequency = k;
+	}
+
+	range->max_qual.qual = 100;
+	range->max_qual.level = 100;
+	range->max_qual.noise = 0;
+	range->max_qual.updated = IW_QUAL_NOISE_INVALID;
+
+	range->avg_qual.qual = 50;
+	range->avg_qual.level = 50;
+	range->avg_qual.noise = 0;
+	range->avg_qual.updated = IW_QUAL_NOISE_INVALID;
+
+	range->sensitivity = 0;
+
+	range->bitrate[0] =  1000000;
+	range->bitrate[1] =  2000000;
+	range->bitrate[2] =  5500000;
+	range->bitrate[3] = 11000000;
+	range->num_bitrates = 4;
+
+	range->min_rts = 0;
+	range->max_rts = 2347;
+	range->min_frag = 256;
+	range->max_frag = 2346;
+
+	range->encoding_size[0] = 5;
+	range->encoding_size[1] = 13;
+	range->num_encoding_sizes = 2;
+	range->max_encoding_tokens = 4;
+
+	range->pmp_flags = IW_POWER_ON;
+	range->pmt_flags = IW_POWER_ON;
+	range->pm_capa = 0;
+
+	range->we_version_source = WIRELESS_EXT;
+	range->we_version_compiled = WIRELESS_EXT;
+	range->retry_capa = IW_RETRY_LIMIT ;
+	range->retry_flags = IW_RETRY_LIMIT;
+	range->r_time_flags = 0;
+	range->min_retry = 1;
+	range->max_retry = 65535;
+
+	return 0;
+}
+
+static int atmel_set_wap(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct sockaddr *awrq,
+			 char *extra)
+{
+	struct atmel_private *priv = netdev_priv(dev);
+	int i;
+	static const u8 any[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+	static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	unsigned long flags;
+
+	if (awrq->sa_family != ARPHRD_ETHER)
+		return -EINVAL;
+
+	if (!memcmp(any, awrq->sa_data, 6) ||
+	    !memcmp(off, awrq->sa_data, 6)) {
+		del_timer_sync(&priv->management_timer);
+		spin_lock_irqsave(&priv->irqlock, flags);
+		atmel_scan(priv, 1);
+		spin_unlock_irqrestore(&priv->irqlock, flags);
+		return 0;
+	}
+
+	for (i = 0; i < priv->BSS_list_entries; i++) {
+		if (memcmp(priv->BSSinfo[i].BSSID, awrq->sa_data, 6) == 0) {
+			if (!priv->wep_is_on && priv->BSSinfo[i].UsingWEP) {
+				return -EINVAL;
+			} else if  (priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) {
+				return -EINVAL;
+			} else {
+				del_timer_sync(&priv->management_timer);
+				spin_lock_irqsave(&priv->irqlock, flags);
+				atmel_join_bss(priv, i);
+				spin_unlock_irqrestore(&priv->irqlock, flags);
+				return 0;
+			}
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int atmel_config_commit(struct net_device *dev,
+			       struct iw_request_info *info,	/* NULL */
+			       void *zwrq,			/* NULL */
+			       char *extra)			/* NULL */
+{
+	return atmel_open(dev);
+}
+
+static const iw_handler atmel_handler[] =
+{
+	(iw_handler) atmel_config_commit,	/* SIOCSIWCOMMIT */
+	(iw_handler) atmel_get_name,		/* SIOCGIWNAME */
+	(iw_handler) NULL,			/* SIOCSIWNWID */
+	(iw_handler) NULL,			/* SIOCGIWNWID */
+	(iw_handler) atmel_set_freq,		/* SIOCSIWFREQ */
+	(iw_handler) atmel_get_freq,		/* SIOCGIWFREQ */
+	(iw_handler) atmel_set_mode,		/* SIOCSIWMODE */
+	(iw_handler) atmel_get_mode,		/* SIOCGIWMODE */
+	(iw_handler) NULL,			/* SIOCSIWSENS */
+	(iw_handler) NULL,			/* SIOCGIWSENS */
+	(iw_handler) NULL,			/* SIOCSIWRANGE */
+	(iw_handler) atmel_get_range,           /* SIOCGIWRANGE */
+	(iw_handler) NULL,			/* SIOCSIWPRIV */
+	(iw_handler) NULL,			/* SIOCGIWPRIV */
+	(iw_handler) NULL,			/* SIOCSIWSTATS */
+	(iw_handler) NULL,			/* SIOCGIWSTATS */
+	(iw_handler) NULL,			/* SIOCSIWSPY */
+	(iw_handler) NULL,			/* SIOCGIWSPY */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) atmel_set_wap,		/* SIOCSIWAP */
+	(iw_handler) atmel_get_wap,		/* SIOCGIWAP */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* SIOCGIWAPLIST */
+	(iw_handler) atmel_set_scan,		/* SIOCSIWSCAN */
+	(iw_handler) atmel_get_scan,		/* SIOCGIWSCAN */
+	(iw_handler) atmel_set_essid,		/* SIOCSIWESSID */
+	(iw_handler) atmel_get_essid,		/* SIOCGIWESSID */
+	(iw_handler) NULL,			/* SIOCSIWNICKN */
+	(iw_handler) NULL,			/* SIOCGIWNICKN */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) atmel_set_rate,		/* SIOCSIWRATE */
+	(iw_handler) atmel_get_rate,		/* SIOCGIWRATE */
+	(iw_handler) atmel_set_rts,		/* SIOCSIWRTS */
+	(iw_handler) atmel_get_rts,		/* SIOCGIWRTS */
+	(iw_handler) atmel_set_frag,		/* SIOCSIWFRAG */
+	(iw_handler) atmel_get_frag,		/* SIOCGIWFRAG */
+	(iw_handler) NULL,			/* SIOCSIWTXPOW */
+	(iw_handler) NULL,			/* SIOCGIWTXPOW */
+	(iw_handler) atmel_set_retry,		/* SIOCSIWRETRY */
+	(iw_handler) atmel_get_retry,		/* SIOCGIWRETRY */
+	(iw_handler) atmel_set_encode,		/* SIOCSIWENCODE */
+	(iw_handler) atmel_get_encode,		/* SIOCGIWENCODE */
+	(iw_handler) atmel_set_power,		/* SIOCSIWPOWER */
+	(iw_handler) atmel_get_power,		/* SIOCGIWPOWER */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* SIOCSIWGENIE */
+	(iw_handler) NULL,			/* SIOCGIWGENIE */
+	(iw_handler) atmel_set_auth,		/* SIOCSIWAUTH */
+	(iw_handler) atmel_get_auth,		/* SIOCGIWAUTH */
+	(iw_handler) atmel_set_encodeext,	/* SIOCSIWENCODEEXT */
+	(iw_handler) atmel_get_encodeext,	/* SIOCGIWENCODEEXT */
+	(iw_handler) NULL,			/* SIOCSIWPMKSA */
+};
+
+static const iw_handler atmel_private_handler[] =
+{
+	NULL,				/* SIOCIWFIRSTPRIV */
+};
+
+struct atmel_priv_ioctl {
+	char id[32];
+	unsigned char __user *data;
+	unsigned short len;
+};
+
+#define ATMELFWL	SIOCIWFIRSTPRIV
+#define ATMELIDIFC	ATMELFWL + 1
+#define ATMELRD		ATMELFWL + 2
+#define ATMELMAGIC 0x51807
+#define REGDOMAINSZ 20
+
+static const struct iw_priv_args atmel_private_args[] = {
+	{
+		.cmd = ATMELFWL,
+		.set_args = IW_PRIV_TYPE_BYTE
+				| IW_PRIV_SIZE_FIXED
+				| sizeof(struct atmel_priv_ioctl),
+		.get_args = IW_PRIV_TYPE_NONE,
+		.name = "atmelfwl"
+	}, {
+		.cmd = ATMELIDIFC,
+		.set_args = IW_PRIV_TYPE_NONE,
+		.get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+		.name = "atmelidifc"
+	}, {
+		.cmd = ATMELRD,
+		.set_args = IW_PRIV_TYPE_CHAR | REGDOMAINSZ,
+		.get_args = IW_PRIV_TYPE_NONE,
+		.name = "regdomain"
+	},
+};
+
+static const struct iw_handler_def atmel_handler_def = {
+	.num_standard	= ARRAY_SIZE(atmel_handler),
+	.num_private	= ARRAY_SIZE(atmel_private_handler),
+	.num_private_args = ARRAY_SIZE(atmel_private_args),
+	.standard	= (iw_handler *) atmel_handler,
+	.private	= (iw_handler *) atmel_private_handler,
+	.private_args	= (struct iw_priv_args *) atmel_private_args,
+	.get_wireless_stats = atmel_get_wireless_stats
+};
+
+static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int i, rc = 0;
+	struct atmel_private *priv = netdev_priv(dev);
+	struct atmel_priv_ioctl com;
+	struct iwreq *wrq = (struct iwreq *) rq;
+	unsigned char *new_firmware;
+	char domain[REGDOMAINSZ + 1];
+
+	switch (cmd) {
+	case ATMELIDIFC:
+		wrq->u.param.value = ATMELMAGIC;
+		break;
+
+	case ATMELFWL:
+		if (copy_from_user(&com, rq->ifr_data, sizeof(com))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		if (!capable(CAP_NET_ADMIN)) {
+			rc = -EPERM;
+			break;
+		}
+
+		if (!(new_firmware = kmalloc(com.len, GFP_KERNEL))) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(new_firmware, com.data, com.len)) {
+			kfree(new_firmware);
+			rc = -EFAULT;
+			break;
+		}
+
+		kfree(priv->firmware);
+
+		priv->firmware = new_firmware;
+		priv->firmware_length = com.len;
+		strncpy(priv->firmware_id, com.id, 31);
+		priv->firmware_id[31] = '\0';
+		break;
+
+	case ATMELRD:
+		if (copy_from_user(domain, rq->ifr_data, REGDOMAINSZ)) {
+			rc = -EFAULT;
+			break;
+		}
+
+		if (!capable(CAP_NET_ADMIN)) {
+			rc = -EPERM;
+			break;
+		}
+
+		domain[REGDOMAINSZ] = 0;
+		rc = -EINVAL;
+		for (i = 0; i < ARRAY_SIZE(channel_table); i++) {
+			if (!strcasecmp(channel_table[i].name, domain)) {
+				priv->config_reg_domain = channel_table[i].reg_domain;
+				rc = 0;
+			}
+		}
+
+		if (rc == 0 &&  priv->station_state != STATION_STATE_DOWN)
+			rc = atmel_open(dev);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+	}
+
+	return rc;
+}
+
+struct auth_body {
+	__le16 alg;
+	__le16 trans_seq;
+	__le16 status;
+	u8 el_id;
+	u8 chall_text_len;
+	u8 chall_text[253];
+};
+
+static void atmel_enter_state(struct atmel_private *priv, int new_state)
+{
+	int old_state = priv->station_state;
+
+	if (new_state == old_state)
+		return;
+
+	priv->station_state = new_state;
+
+	if (new_state == STATION_STATE_READY) {
+		netif_start_queue(priv->dev);
+		netif_carrier_on(priv->dev);
+	}
+
+	if (old_state == STATION_STATE_READY) {
+		netif_carrier_off(priv->dev);
+		if (netif_running(priv->dev))
+			netif_stop_queue(priv->dev);
+		priv->last_beacon_timestamp = 0;
+	}
+}
+
+static void atmel_scan(struct atmel_private *priv, int specific_ssid)
+{
+	struct {
+		u8 BSSID[ETH_ALEN];
+		u8 SSID[MAX_SSID_LENGTH];
+		u8 scan_type;
+		u8 channel;
+		__le16 BSS_type;
+		__le16 min_channel_time;
+		__le16 max_channel_time;
+		u8 options;
+		u8 SSID_size;
+	} cmd;
+
+	eth_broadcast_addr(cmd.BSSID);
+
+	if (priv->fast_scan) {
+		cmd.SSID_size = priv->SSID_size;
+		memcpy(cmd.SSID, priv->SSID, priv->SSID_size);
+		cmd.min_channel_time = cpu_to_le16(10);
+		cmd.max_channel_time = cpu_to_le16(50);
+	} else {
+		priv->BSS_list_entries = 0;
+		cmd.SSID_size = 0;
+		cmd.min_channel_time = cpu_to_le16(10);
+		cmd.max_channel_time = cpu_to_le16(120);
+	}
+
+	cmd.options = 0;
+
+	if (!specific_ssid)
+		cmd.options |= SCAN_OPTIONS_SITE_SURVEY;
+
+	cmd.channel = (priv->channel & 0x7f);
+	cmd.scan_type = SCAN_TYPE_ACTIVE;
+	cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ?
+		BSS_TYPE_AD_HOC : BSS_TYPE_INFRASTRUCTURE);
+
+	atmel_send_command(priv, CMD_Scan, &cmd, sizeof(cmd));
+
+	/* This must come after all hardware access to avoid being messed up
+	   by stuff happening in interrupt context after we leave STATE_DOWN */
+	atmel_enter_state(priv, STATION_STATE_SCANNING);
+}
+
+static void join(struct atmel_private *priv, int type)
+{
+	struct {
+		u8 BSSID[6];
+		u8 SSID[MAX_SSID_LENGTH];
+		u8 BSS_type; /* this is a short in a scan command - weird */
+		u8 channel;
+		__le16 timeout;
+		u8 SSID_size;
+		u8 reserved;
+	} cmd;
+
+	cmd.SSID_size = priv->SSID_size;
+	memcpy(cmd.SSID, priv->SSID, priv->SSID_size);
+	memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN);
+	cmd.channel = (priv->channel & 0x7f);
+	cmd.BSS_type = type;
+	cmd.timeout = cpu_to_le16(2000);
+
+	atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd));
+}
+
+static void start(struct atmel_private *priv, int type)
+{
+	struct {
+		u8 BSSID[6];
+		u8 SSID[MAX_SSID_LENGTH];
+		u8 BSS_type;
+		u8 channel;
+		u8 SSID_size;
+		u8 reserved[3];
+	} cmd;
+
+	cmd.SSID_size = priv->SSID_size;
+	memcpy(cmd.SSID, priv->SSID, priv->SSID_size);
+	memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN);
+	cmd.BSS_type = type;
+	cmd.channel = (priv->channel & 0x7f);
+
+	atmel_send_command(priv, CMD_Start, &cmd, sizeof(cmd));
+}
+
+static void handle_beacon_probe(struct atmel_private *priv, u16 capability,
+				u8 channel)
+{
+	int rejoin = 0;
+	int new = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ?
+		SHORT_PREAMBLE : LONG_PREAMBLE;
+
+	if (priv->preamble != new) {
+		priv->preamble = new;
+		rejoin = 1;
+		atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, new);
+	}
+
+	if (priv->channel != channel) {
+		priv->channel = channel;
+		rejoin = 1;
+		atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_CHANNEL_POS, channel);
+	}
+
+	if (rejoin) {
+		priv->station_is_associated = 0;
+		atmel_enter_state(priv, STATION_STATE_JOINNING);
+
+		if (priv->operating_mode == IW_MODE_INFRA)
+			join(priv, BSS_TYPE_INFRASTRUCTURE);
+		else
+			join(priv, BSS_TYPE_AD_HOC);
+	}
+}
+
+static void send_authentication_request(struct atmel_private *priv, u16 system,
+					u8 *challenge, int challenge_len)
+{
+	struct ieee80211_hdr header;
+	struct auth_body auth;
+
+	header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
+	header.duration_id = cpu_to_le16(0x8000);
+	header.seq_ctrl = 0;
+	memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN);
+	memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN);
+	memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN);
+
+	if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1)
+		/* no WEP for authentication frames with TrSeqNo 1 */
+		header.frame_control |=  cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+
+	auth.alg = cpu_to_le16(system);
+
+	auth.status = 0;
+	auth.trans_seq = cpu_to_le16(priv->CurrentAuthentTransactionSeqNum);
+	priv->ExpectedAuthentTransactionSeqNum = priv->CurrentAuthentTransactionSeqNum+1;
+	priv->CurrentAuthentTransactionSeqNum += 2;
+
+	if (challenge_len != 0)	{
+		auth.el_id = 16; /* challenge_text */
+		auth.chall_text_len = challenge_len;
+		memcpy(auth.chall_text, challenge, challenge_len);
+		atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 8 + challenge_len);
+	} else {
+		atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 6);
+	}
+}
+
+static void send_association_request(struct atmel_private *priv, int is_reassoc)
+{
+	u8 *ssid_el_p;
+	int bodysize;
+	struct ieee80211_hdr header;
+	struct ass_req_format {
+		__le16 capability;
+		__le16 listen_interval;
+		u8 ap[ETH_ALEN]; /* nothing after here directly accessible */
+		u8 ssid_el_id;
+		u8 ssid_len;
+		u8 ssid[MAX_SSID_LENGTH];
+		u8 sup_rates_el_id;
+		u8 sup_rates_len;
+		u8 rates[4];
+	} body;
+
+	header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+		(is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ));
+	header.duration_id = cpu_to_le16(0x8000);
+	header.seq_ctrl = 0;
+
+	memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN);
+	memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN);
+	memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN);
+
+	body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS);
+	if (priv->wep_is_on)
+		body.capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY);
+	if (priv->preamble == SHORT_PREAMBLE)
+		body.capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE);
+
+	body.listen_interval = cpu_to_le16(priv->listen_interval * priv->beacon_period);
+
+	/* current AP address - only in reassoc frame */
+	if (is_reassoc) {
+		memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN);
+		ssid_el_p = &body.ssid_el_id;
+		bodysize = 18 + priv->SSID_size;
+	} else {
+		ssid_el_p = &body.ap[0];
+		bodysize = 12 + priv->SSID_size;
+	}
+
+	ssid_el_p[0] = WLAN_EID_SSID;
+	ssid_el_p[1] = priv->SSID_size;
+	memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size);
+	ssid_el_p[2 + priv->SSID_size] = WLAN_EID_SUPP_RATES;
+	ssid_el_p[3 + priv->SSID_size] = 4; /* len of supported rates */
+	memcpy(ssid_el_p + 4 + priv->SSID_size, atmel_basic_rates, 4);
+
+	atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize);
+}
+
+static int is_frame_from_current_bss(struct atmel_private *priv,
+				     struct ieee80211_hdr *header)
+{
+	if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
+		return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0;
+	else
+		return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0;
+}
+
+static int retrieve_bss(struct atmel_private *priv)
+{
+	int i;
+	int max_rssi = -128;
+	int max_index = -1;
+
+	if (priv->BSS_list_entries == 0)
+		return -1;
+
+	if (priv->connect_to_any_BSS) {
+		/* Select a BSS with the max-RSSI but of the same type and of
+		   the same WEP mode and that it is not marked as 'bad' (i.e.
+		   we had previously failed to connect to this BSS with the
+		   settings that we currently use) */
+		priv->current_BSS = 0;
+		for (i = 0; i < priv->BSS_list_entries; i++) {
+			if (priv->operating_mode == priv->BSSinfo[i].BSStype &&
+			    ((!priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) ||
+			     (priv->wep_is_on && priv->BSSinfo[i].UsingWEP)) &&
+			    !(priv->BSSinfo[i].channel & 0x80)) {
+				max_rssi = priv->BSSinfo[i].RSSI;
+				priv->current_BSS = max_index = i;
+			}
+		}
+		return max_index;
+	}
+
+	for (i = 0; i < priv->BSS_list_entries; i++) {
+		if (priv->SSID_size == priv->BSSinfo[i].SSIDsize &&
+		    memcmp(priv->SSID, priv->BSSinfo[i].SSID, priv->SSID_size) == 0 &&
+		    priv->operating_mode == priv->BSSinfo[i].BSStype &&
+		    atmel_validate_channel(priv, priv->BSSinfo[i].channel) == 0) {
+			if (priv->BSSinfo[i].RSSI >= max_rssi) {
+				max_rssi = priv->BSSinfo[i].RSSI;
+				max_index = i;
+			}
+		}
+	}
+	return max_index;
+}
+
+static void store_bss_info(struct atmel_private *priv,
+			   struct ieee80211_hdr *header, u16 capability,
+			   u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len,
+			   u8 *ssid, int is_beacon)
+{
+	u8 *bss = capability & WLAN_CAPABILITY_ESS ? header->addr2 : header->addr3;
+	int i, index;
+
+	for (index = -1, i = 0; i < priv->BSS_list_entries; i++)
+		if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0)
+			index = i;
+
+	/* If we process a probe and an entry from this BSS exists
+	   we will update the BSS entry with the info from this BSS.
+	   If we process a beacon we will only update RSSI */
+
+	if (index == -1) {
+		if (priv->BSS_list_entries == MAX_BSS_ENTRIES)
+			return;
+		index = priv->BSS_list_entries++;
+		memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN);
+		priv->BSSinfo[index].RSSI = rssi;
+	} else {
+		if (rssi > priv->BSSinfo[index].RSSI)
+			priv->BSSinfo[index].RSSI = rssi;
+		if (is_beacon)
+			return;
+	}
+
+	priv->BSSinfo[index].channel = channel;
+	priv->BSSinfo[index].beacon_period = beacon_period;
+	priv->BSSinfo[index].UsingWEP = capability & WLAN_CAPABILITY_PRIVACY;
+	memcpy(priv->BSSinfo[index].SSID, ssid, ssid_len);
+	priv->BSSinfo[index].SSIDsize = ssid_len;
+
+	if (capability & WLAN_CAPABILITY_IBSS)
+		priv->BSSinfo[index].BSStype = IW_MODE_ADHOC;
+	else if (capability & WLAN_CAPABILITY_ESS)
+		priv->BSSinfo[index].BSStype = IW_MODE_INFRA;
+
+	priv->BSSinfo[index].preamble = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ?
+		SHORT_PREAMBLE : LONG_PREAMBLE;
+}
+
+static void authenticate(struct atmel_private *priv, u16 frame_len)
+{
+	struct auth_body *auth = (struct auth_body *)priv->rx_buf;
+	u16 status = le16_to_cpu(auth->status);
+	u16 trans_seq_no = le16_to_cpu(auth->trans_seq);
+	u16 system = le16_to_cpu(auth->alg);
+
+	if (status == WLAN_STATUS_SUCCESS && !priv->wep_is_on) {
+		/* no WEP */
+		if (priv->station_was_associated) {
+			atmel_enter_state(priv, STATION_STATE_REASSOCIATING);
+			send_association_request(priv, 1);
+			return;
+		} else {
+			atmel_enter_state(priv, STATION_STATE_ASSOCIATING);
+			send_association_request(priv, 0);
+			return;
+		}
+	}
+
+	if (status == WLAN_STATUS_SUCCESS && priv->wep_is_on) {
+		int should_associate = 0;
+		/* WEP */
+		if (trans_seq_no != priv->ExpectedAuthentTransactionSeqNum)
+			return;
+
+		if (system == WLAN_AUTH_OPEN) {
+			if (trans_seq_no == 0x0002) {
+				should_associate = 1;
+			}
+		} else if (system == WLAN_AUTH_SHARED_KEY) {
+			if (trans_seq_no == 0x0002 &&
+			    auth->el_id == WLAN_EID_CHALLENGE) {
+				send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len);
+				return;
+			} else if (trans_seq_no == 0x0004) {
+				should_associate = 1;
+			}
+		}
+
+		if (should_associate) {
+			if (priv->station_was_associated) {
+				atmel_enter_state(priv, STATION_STATE_REASSOCIATING);
+				send_association_request(priv, 1);
+				return;
+			} else {
+				atmel_enter_state(priv, STATION_STATE_ASSOCIATING);
+				send_association_request(priv, 0);
+				return;
+			}
+		}
+	}
+
+	if (status == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
+		/* Flip back and forth between WEP auth modes until the max
+		 * authentication tries has been exceeded.
+		 */
+		if (system == WLAN_AUTH_OPEN) {
+			priv->CurrentAuthentTransactionSeqNum = 0x001;
+			priv->exclude_unencrypted = 1;
+			send_authentication_request(priv, WLAN_AUTH_SHARED_KEY, NULL, 0);
+			return;
+		} else if (system == WLAN_AUTH_SHARED_KEY
+			   && priv->wep_is_on) {
+			priv->CurrentAuthentTransactionSeqNum = 0x001;
+			priv->exclude_unencrypted = 0;
+			send_authentication_request(priv, WLAN_AUTH_OPEN, NULL, 0);
+			return;
+		} else if (priv->connect_to_any_BSS) {
+			int bss_index;
+
+			priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80;
+
+			if ((bss_index  = retrieve_bss(priv)) != -1) {
+				atmel_join_bss(priv, bss_index);
+				return;
+			}
+		}
+	}
+
+	priv->AuthenticationRequestRetryCnt = 0;
+	atmel_enter_state(priv,  STATION_STATE_MGMT_ERROR);
+	priv->station_is_associated = 0;
+}
+
+static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype)
+{
+	struct ass_resp_format {
+		__le16 capability;
+		__le16 status;
+		__le16 ass_id;
+		u8 el_id;
+		u8 length;
+		u8 rates[4];
+	} *ass_resp = (struct ass_resp_format *)priv->rx_buf;
+
+	u16 status = le16_to_cpu(ass_resp->status);
+	u16 ass_id = le16_to_cpu(ass_resp->ass_id);
+	u16 rates_len = ass_resp->length > 4 ? 4 : ass_resp->length;
+
+	union iwreq_data wrqu;
+
+	if (frame_len < 8 + rates_len)
+		return;
+
+	if (status == WLAN_STATUS_SUCCESS) {
+		if (subtype == IEEE80211_STYPE_ASSOC_RESP)
+			priv->AssociationRequestRetryCnt = 0;
+		else
+			priv->ReAssociationRequestRetryCnt = 0;
+
+		atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
+				MAC_MGMT_MIB_STATION_ID_POS, ass_id & 0x3fff);
+		atmel_set_mib(priv, Phy_Mib_Type,
+			      PHY_MIB_RATE_SET_POS, ass_resp->rates, rates_len);
+		if (priv->power_mode == 0) {
+			priv->listen_interval = 1;
+			atmel_set_mib8(priv, Mac_Mgmt_Mib_Type,
+				       MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE);
+			atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
+					MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1);
+		} else {
+			priv->listen_interval = 2;
+			atmel_set_mib8(priv, Mac_Mgmt_Mib_Type,
+				       MAC_MGMT_MIB_PS_MODE_POS,  PS_MODE);
+			atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
+					MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 2);
+		}
+
+		priv->station_is_associated = 1;
+		priv->station_was_associated = 1;
+		atmel_enter_state(priv, STATION_STATE_READY);
+
+		/* Send association event to userspace */
+		wrqu.data.length = 0;
+		wrqu.data.flags = 0;
+		memcpy(wrqu.ap_addr.sa_data, priv->CurrentBSSID, ETH_ALEN);
+		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+		wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+
+		return;
+	}
+
+	if (subtype == IEEE80211_STYPE_ASSOC_RESP &&
+	    status != WLAN_STATUS_ASSOC_DENIED_RATES &&
+	    status != WLAN_STATUS_CAPS_UNSUPPORTED &&
+	    priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) {
+		mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
+		priv->AssociationRequestRetryCnt++;
+		send_association_request(priv, 0);
+		return;
+	}
+
+	if (subtype == IEEE80211_STYPE_REASSOC_RESP &&
+	    status != WLAN_STATUS_ASSOC_DENIED_RATES &&
+	    status != WLAN_STATUS_CAPS_UNSUPPORTED &&
+	    priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) {
+		mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
+		priv->ReAssociationRequestRetryCnt++;
+		send_association_request(priv, 1);
+		return;
+	}
+
+	atmel_enter_state(priv,  STATION_STATE_MGMT_ERROR);
+	priv->station_is_associated = 0;
+
+	if (priv->connect_to_any_BSS) {
+		int bss_index;
+		priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80;
+
+		if ((bss_index = retrieve_bss(priv)) != -1)
+			atmel_join_bss(priv, bss_index);
+	}
+}
+
+static void atmel_join_bss(struct atmel_private *priv, int bss_index)
+{
+	struct bss_info *bss =  &priv->BSSinfo[bss_index];
+
+	memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN);
+	memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize);
+
+	/* The WPA stuff cares about the current AP address */
+	if (priv->use_wpa)
+		build_wpa_mib(priv);
+
+	/* When switching to AdHoc turn OFF Power Save if needed */
+
+	if (bss->BSStype == IW_MODE_ADHOC &&
+	    priv->operating_mode != IW_MODE_ADHOC &&
+	    priv->power_mode) {
+		priv->power_mode = 0;
+		priv->listen_interval = 1;
+		atmel_set_mib8(priv, Mac_Mgmt_Mib_Type,
+			       MAC_MGMT_MIB_PS_MODE_POS,  ACTIVE_MODE);
+		atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
+				MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1);
+	}
+
+	priv->operating_mode = bss->BSStype;
+	priv->channel = bss->channel & 0x7f;
+	priv->beacon_period = bss->beacon_period;
+
+	if (priv->preamble != bss->preamble) {
+		priv->preamble = bss->preamble;
+		atmel_set_mib8(priv, Local_Mib_Type,
+			       LOCAL_MIB_PREAMBLE_TYPE, bss->preamble);
+	}
+
+	if (!priv->wep_is_on && bss->UsingWEP) {
+		atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
+		priv->station_is_associated = 0;
+		return;
+	}
+
+	if (priv->wep_is_on && !bss->UsingWEP) {
+		atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
+		priv->station_is_associated = 0;
+		return;
+	}
+
+	atmel_enter_state(priv, STATION_STATE_JOINNING);
+
+	if (priv->operating_mode == IW_MODE_INFRA)
+		join(priv, BSS_TYPE_INFRASTRUCTURE);
+	else
+		join(priv, BSS_TYPE_AD_HOC);
+}
+
+static void restart_search(struct atmel_private *priv)
+{
+	int bss_index;
+
+	if (!priv->connect_to_any_BSS) {
+		atmel_scan(priv, 1);
+	} else {
+		priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80;
+
+		if ((bss_index = retrieve_bss(priv)) != -1)
+			atmel_join_bss(priv, bss_index);
+		else
+			atmel_scan(priv, 0);
+	}
+}
+
+static void smooth_rssi(struct atmel_private *priv, u8 rssi)
+{
+	u8 old = priv->wstats.qual.level;
+	u8 max_rssi = 42; /* 502-rmfd-revd max by experiment, default for now */
+
+	switch (priv->firmware_type) {
+	case ATMEL_FW_TYPE_502E:
+		max_rssi = 63; /* 502-rmfd-reve max by experiment */
+		break;
+	default:
+		break;
+	}
+
+	rssi = rssi * 100 / max_rssi;
+	if ((rssi + old) % 2)
+		priv->wstats.qual.level = (rssi + old) / 2 + 1;
+	else
+		priv->wstats.qual.level = (rssi + old) / 2;
+	priv->wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;
+	priv->wstats.qual.updated &= ~IW_QUAL_LEVEL_INVALID;
+}
+
+static void atmel_smooth_qual(struct atmel_private *priv)
+{
+	unsigned long time_diff = (jiffies - priv->last_qual) / HZ;
+	while (time_diff--) {
+		priv->last_qual += HZ;
+		priv->wstats.qual.qual = priv->wstats.qual.qual / 2;
+		priv->wstats.qual.qual +=
+			priv->beacons_this_sec * priv->beacon_period * (priv->wstats.qual.level + 100) / 4000;
+		priv->beacons_this_sec = 0;
+	}
+	priv->wstats.qual.updated |= IW_QUAL_QUAL_UPDATED;
+	priv->wstats.qual.updated &= ~IW_QUAL_QUAL_INVALID;
+}
+
+/* deals with incoming management frames. */
+static void atmel_management_frame(struct atmel_private *priv,
+				   struct ieee80211_hdr *header,
+				   u16 frame_len, u8 rssi)
+{
+	u16 subtype;
+
+	subtype = le16_to_cpu(header->frame_control) & IEEE80211_FCTL_STYPE;
+	switch (subtype) {
+	case IEEE80211_STYPE_BEACON:
+	case IEEE80211_STYPE_PROBE_RESP:
+
+		/* beacon frame has multiple variable-length fields -
+		   never let an engineer loose with a data structure design. */
+		{
+			struct beacon_format {
+				__le64 timestamp;
+				__le16 interval;
+				__le16 capability;
+				u8 ssid_el_id;
+				u8 ssid_length;
+				/* ssid here */
+				u8 rates_el_id;
+				u8 rates_length;
+				/* rates here */
+				u8 ds_el_id;
+				u8 ds_length;
+				/* ds here */
+			} *beacon = (struct beacon_format *)priv->rx_buf;
+
+			u8 channel, rates_length, ssid_length;
+			u64 timestamp = le64_to_cpu(beacon->timestamp);
+			u16 beacon_interval = le16_to_cpu(beacon->interval);
+			u16 capability = le16_to_cpu(beacon->capability);
+			u8 *beaconp = priv->rx_buf;
+			ssid_length = beacon->ssid_length;
+			/* this blows chunks. */
+			if (frame_len < 14 || frame_len < ssid_length + 15)
+				return;
+			rates_length = beaconp[beacon->ssid_length + 15];
+			if (frame_len < ssid_length + rates_length + 18)
+				return;
+			if (ssid_length >  MAX_SSID_LENGTH)
+				return;
+			channel = beaconp[ssid_length + rates_length + 18];
+
+			if (priv->station_state == STATION_STATE_READY) {
+				smooth_rssi(priv, rssi);
+				if (is_frame_from_current_bss(priv, header)) {
+					priv->beacons_this_sec++;
+					atmel_smooth_qual(priv);
+					if (priv->last_beacon_timestamp) {
+						/* Note truncate this to 32 bits - kernel can't divide a long long */
+						u32 beacon_delay = timestamp - priv->last_beacon_timestamp;
+						int beacons = beacon_delay / (beacon_interval * 1000);
+						if (beacons > 1)
+							priv->wstats.miss.beacon += beacons - 1;
+					}
+					priv->last_beacon_timestamp = timestamp;
+					handle_beacon_probe(priv, capability, channel);
+				}
+			}
+
+			if (priv->station_state == STATION_STATE_SCANNING)
+				store_bss_info(priv, header, capability,
+					       beacon_interval, channel, rssi,
+					       ssid_length,
+					       &beacon->rates_el_id,
+					       subtype == IEEE80211_STYPE_BEACON);
+		}
+		break;
+
+	case IEEE80211_STYPE_AUTH:
+
+		if (priv->station_state == STATION_STATE_AUTHENTICATING)
+			authenticate(priv, frame_len);
+
+		break;
+
+	case IEEE80211_STYPE_ASSOC_RESP:
+	case IEEE80211_STYPE_REASSOC_RESP:
+
+		if (priv->station_state == STATION_STATE_ASSOCIATING ||
+		    priv->station_state == STATION_STATE_REASSOCIATING)
+			associate(priv, frame_len, subtype);
+
+		break;
+
+	case IEEE80211_STYPE_DISASSOC:
+		if (priv->station_is_associated &&
+		    priv->operating_mode == IW_MODE_INFRA &&
+		    is_frame_from_current_bss(priv, header)) {
+			priv->station_was_associated = 0;
+			priv->station_is_associated = 0;
+
+			atmel_enter_state(priv, STATION_STATE_JOINNING);
+			join(priv, BSS_TYPE_INFRASTRUCTURE);
+		}
+
+		break;
+
+	case IEEE80211_STYPE_DEAUTH:
+		if (priv->operating_mode == IW_MODE_INFRA &&
+		    is_frame_from_current_bss(priv, header)) {
+			priv->station_was_associated = 0;
+
+			atmel_enter_state(priv, STATION_STATE_JOINNING);
+			join(priv, BSS_TYPE_INFRASTRUCTURE);
+		}
+
+		break;
+	}
+}
+
+/* run when timer expires */
+static void atmel_management_timer(u_long a)
+{
+	struct net_device *dev = (struct net_device *) a;
+	struct atmel_private *priv = netdev_priv(dev);
+	unsigned long flags;
+
+	/* Check if the card has been yanked. */
+	if (priv->card && priv->present_callback &&
+		!(*priv->present_callback)(priv->card))
+		return;
+
+	spin_lock_irqsave(&priv->irqlock, flags);
+
+	switch (priv->station_state) {
+
+	case STATION_STATE_AUTHENTICATING:
+		if (priv->AuthenticationRequestRetryCnt >= MAX_AUTHENTICATION_RETRIES) {
+			atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
+			priv->station_is_associated = 0;
+			priv->AuthenticationRequestRetryCnt = 0;
+			restart_search(priv);
+		} else {
+			int auth = WLAN_AUTH_OPEN;
+			priv->AuthenticationRequestRetryCnt++;
+			priv->CurrentAuthentTransactionSeqNum = 0x0001;
+			mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
+			if (priv->wep_is_on && priv->exclude_unencrypted)
+				auth = WLAN_AUTH_SHARED_KEY;
+			send_authentication_request(priv, auth, NULL, 0);
+	  }
+	  break;
+
+	case STATION_STATE_ASSOCIATING:
+		if (priv->AssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) {
+			atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
+			priv->station_is_associated = 0;
+			priv->AssociationRequestRetryCnt = 0;
+			restart_search(priv);
+		} else {
+			priv->AssociationRequestRetryCnt++;
+			mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
+			send_association_request(priv, 0);
+		}
+	  break;
+
+	case STATION_STATE_REASSOCIATING:
+		if (priv->ReAssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) {
+			atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
+			priv->station_is_associated = 0;
+			priv->ReAssociationRequestRetryCnt = 0;
+			restart_search(priv);
+		} else {
+			priv->ReAssociationRequestRetryCnt++;
+			mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
+			send_association_request(priv, 1);
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	spin_unlock_irqrestore(&priv->irqlock, flags);
+}
+
+static void atmel_command_irq(struct atmel_private *priv)
+{
+	u8 status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET));
+	u8 command = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET));
+	int fast_scan;
+	union iwreq_data wrqu;
+
+	if (status == CMD_STATUS_IDLE ||
+	    status == CMD_STATUS_IN_PROGRESS)
+		return;
+
+	switch (command) {
+	case CMD_Start:
+		if (status == CMD_STATUS_COMPLETE) {
+			priv->station_was_associated = priv->station_is_associated;
+			atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS,
+				      (u8 *)priv->CurrentBSSID, 6);
+			atmel_enter_state(priv, STATION_STATE_READY);
+		}
+		break;
+
+	case CMD_Scan:
+		fast_scan = priv->fast_scan;
+		priv->fast_scan = 0;
+
+		if (status != CMD_STATUS_COMPLETE) {
+			atmel_scan(priv, 1);
+		} else {
+			int bss_index = retrieve_bss(priv);
+			int notify_scan_complete = 1;
+			if (bss_index != -1) {
+				atmel_join_bss(priv, bss_index);
+			} else if (priv->operating_mode == IW_MODE_ADHOC &&
+				   priv->SSID_size != 0) {
+				start(priv, BSS_TYPE_AD_HOC);
+			} else {
+				priv->fast_scan = !fast_scan;
+				atmel_scan(priv, 1);
+				notify_scan_complete = 0;
+			}
+			priv->site_survey_state = SITE_SURVEY_COMPLETED;
+			if (notify_scan_complete) {
+				wrqu.data.length = 0;
+				wrqu.data.flags = 0;
+				wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL);
+			}
+		}
+		break;
+
+	case CMD_SiteSurvey:
+		priv->fast_scan = 0;
+
+		if (status != CMD_STATUS_COMPLETE)
+			return;
+
+		priv->site_survey_state = SITE_SURVEY_COMPLETED;
+		if (priv->station_is_associated) {
+			atmel_enter_state(priv, STATION_STATE_READY);
+			wrqu.data.length = 0;
+			wrqu.data.flags = 0;
+			wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL);
+		} else {
+			atmel_scan(priv, 1);
+		}
+		break;
+
+	case CMD_Join:
+		if (status == CMD_STATUS_COMPLETE) {
+			if (priv->operating_mode == IW_MODE_ADHOC) {
+				priv->station_was_associated = priv->station_is_associated;
+				atmel_enter_state(priv, STATION_STATE_READY);
+			} else {
+				int auth = WLAN_AUTH_OPEN;
+				priv->AuthenticationRequestRetryCnt = 0;
+				atmel_enter_state(priv, STATION_STATE_AUTHENTICATING);
+
+				mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
+				priv->CurrentAuthentTransactionSeqNum = 0x0001;
+				if (priv->wep_is_on && priv->exclude_unencrypted)
+					auth = WLAN_AUTH_SHARED_KEY;
+				send_authentication_request(priv, auth, NULL, 0);
+			}
+			return;
+		}
+
+		atmel_scan(priv, 1);
+	}
+}
+
+static int atmel_wakeup_firmware(struct atmel_private *priv)
+{
+	struct host_info_struct *iface = &priv->host_info;
+	u16 mr1, mr3;
+	int i;
+
+	if (priv->card_type == CARD_TYPE_SPI_FLASH)
+		atmel_set_gcr(priv->dev, GCR_REMAP);
+
+	/* wake up on-board processor */
+	atmel_clear_gcr(priv->dev, 0x0040);
+	atmel_write16(priv->dev, BSR, BSS_SRAM);
+
+	if (priv->card_type == CARD_TYPE_SPI_FLASH)
+		mdelay(100);
+
+	/* and wait for it */
+	for (i = LOOP_RETRY_LIMIT; i; i--) {
+		mr1 = atmel_read16(priv->dev, MR1);
+		mr3 = atmel_read16(priv->dev, MR3);
+
+		if (mr3 & MAC_BOOT_COMPLETE)
+			break;
+		if (mr1 & MAC_BOOT_COMPLETE &&
+		    priv->bus_type == BUS_TYPE_PCCARD)
+			break;
+	}
+
+	if (i == 0) {
+		printk(KERN_ALERT "%s: MAC failed to boot.\n", priv->dev->name);
+		return -EIO;
+	}
+
+	if ((priv->host_info_base = atmel_read16(priv->dev, MR2)) == 0xffff) {
+		printk(KERN_ALERT "%s: card missing.\n", priv->dev->name);
+		return -ENODEV;
+	}
+
+	/* now check for completion of MAC initialization through
+	   the FunCtrl field of the IFACE, poll MR1 to detect completion of
+	   MAC initialization, check completion status, set interrupt mask,
+	   enables interrupts and calls Tx and Rx initialization functions */
+
+	atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), FUNC_CTRL_INIT_COMPLETE);
+
+	for (i = LOOP_RETRY_LIMIT; i; i--) {
+		mr1 = atmel_read16(priv->dev, MR1);
+		mr3 = atmel_read16(priv->dev, MR3);
+
+		if (mr3 & MAC_INIT_COMPLETE)
+			break;
+		if (mr1 & MAC_INIT_COMPLETE &&
+		    priv->bus_type == BUS_TYPE_PCCARD)
+			break;
+	}
+
+	if (i == 0) {
+		printk(KERN_ALERT "%s: MAC failed to initialise.\n",
+				priv->dev->name);
+		return -EIO;
+	}
+
+	/* Check for MAC_INIT_OK only on the register that the MAC_INIT_OK was set */
+	if ((mr3 & MAC_INIT_COMPLETE) &&
+	    !(atmel_read16(priv->dev, MR3) & MAC_INIT_OK)) {
+		printk(KERN_ALERT "%s: MAC failed MR3 self-test.\n", priv->dev->name);
+		return -EIO;
+	}
+	if ((mr1 & MAC_INIT_COMPLETE) &&
+	    !(atmel_read16(priv->dev, MR1) & MAC_INIT_OK)) {
+		printk(KERN_ALERT "%s: MAC failed MR1 self-test.\n", priv->dev->name);
+		return -EIO;
+	}
+
+	atmel_copy_to_host(priv->dev, (unsigned char *)iface,
+			   priv->host_info_base, sizeof(*iface));
+
+	iface->tx_buff_pos = le16_to_cpu(iface->tx_buff_pos);
+	iface->tx_buff_size = le16_to_cpu(iface->tx_buff_size);
+	iface->tx_desc_pos = le16_to_cpu(iface->tx_desc_pos);
+	iface->tx_desc_count = le16_to_cpu(iface->tx_desc_count);
+	iface->rx_buff_pos = le16_to_cpu(iface->rx_buff_pos);
+	iface->rx_buff_size = le16_to_cpu(iface->rx_buff_size);
+	iface->rx_desc_pos = le16_to_cpu(iface->rx_desc_pos);
+	iface->rx_desc_count = le16_to_cpu(iface->rx_desc_count);
+	iface->build_version = le16_to_cpu(iface->build_version);
+	iface->command_pos = le16_to_cpu(iface->command_pos);
+	iface->major_version = le16_to_cpu(iface->major_version);
+	iface->minor_version = le16_to_cpu(iface->minor_version);
+	iface->func_ctrl = le16_to_cpu(iface->func_ctrl);
+	iface->mac_status = le16_to_cpu(iface->mac_status);
+
+	return 0;
+}
+
+/* determine type of memory and MAC address */
+static int probe_atmel_card(struct net_device *dev)
+{
+	int rc = 0;
+	struct atmel_private *priv = netdev_priv(dev);
+
+	/* reset pccard */
+	if (priv->bus_type == BUS_TYPE_PCCARD)
+		atmel_write16(dev, GCR, 0x0060);
+
+	atmel_write16(dev, GCR, 0x0040);
+	mdelay(500);
+
+	if (atmel_read16(dev, MR2) == 0) {
+		/* No stored firmware so load a small stub which just
+		   tells us the MAC address */
+		int i;
+		priv->card_type = CARD_TYPE_EEPROM;
+		atmel_write16(dev, BSR, BSS_IRAM);
+		atmel_copy_to_card(dev, 0, mac_reader, sizeof(mac_reader));
+		atmel_set_gcr(dev, GCR_REMAP);
+		atmel_clear_gcr(priv->dev, 0x0040);
+		atmel_write16(dev, BSR, BSS_SRAM);
+		for (i = LOOP_RETRY_LIMIT; i; i--)
+			if (atmel_read16(dev, MR3) & MAC_BOOT_COMPLETE)
+				break;
+		if (i == 0) {
+			printk(KERN_ALERT "%s: MAC failed to boot MAC address reader.\n", dev->name);
+		} else {
+			atmel_copy_to_host(dev, dev->dev_addr, atmel_read16(dev, MR2), 6);
+			/* got address, now squash it again until the network
+			   interface is opened */
+			if (priv->bus_type == BUS_TYPE_PCCARD)
+				atmel_write16(dev, GCR, 0x0060);
+			atmel_write16(dev, GCR, 0x0040);
+			rc = 1;
+		}
+	} else if (atmel_read16(dev, MR4) == 0) {
+		/* Mac address easy in this case. */
+		priv->card_type = CARD_TYPE_PARALLEL_FLASH;
+		atmel_write16(dev,  BSR, 1);
+		atmel_copy_to_host(dev, dev->dev_addr, 0xc000, 6);
+		atmel_write16(dev,  BSR, 0x200);
+		rc = 1;
+	} else {
+		/* Standard firmware in flash, boot it up and ask
+		   for the Mac Address */
+		priv->card_type = CARD_TYPE_SPI_FLASH;
+		if (atmel_wakeup_firmware(priv) == 0) {
+			atmel_get_mib(priv, Mac_Address_Mib_Type, 0, dev->dev_addr, 6);
+
+			/* got address, now squash it again until the network
+			   interface is opened */
+			if (priv->bus_type == BUS_TYPE_PCCARD)
+				atmel_write16(dev, GCR, 0x0060);
+			atmel_write16(dev, GCR, 0x0040);
+			rc = 1;
+		}
+	}
+
+	if (rc) {
+		if (dev->dev_addr[0] == 0xFF) {
+			static const u8 default_mac[] = {
+				0x00, 0x04, 0x25, 0x00, 0x00, 0x00
+			};
+			printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name);
+			memcpy(dev->dev_addr, default_mac, ETH_ALEN);
+		}
+	}
+
+	return rc;
+}
+
+/* Move the encyption information on the MIB structure.
+   This routine is for the pre-WPA firmware: later firmware has
+   a different format MIB and a different routine. */
+static void build_wep_mib(struct atmel_private *priv)
+{
+	struct { /* NB this is matched to the hardware, don't change. */
+		u8 wep_is_on;
+		u8 default_key; /* 0..3 */
+		u8 reserved;
+		u8 exclude_unencrypted;
+
+		u32 WEPICV_error_count;
+		u32 WEP_excluded_count;
+
+		u8 wep_keys[MAX_ENCRYPTION_KEYS][13];
+		u8 encryption_level; /* 0, 1, 2 */
+		u8 reserved2[3];
+	} mib;
+	int i;
+
+	mib.wep_is_on = priv->wep_is_on;
+	if (priv->wep_is_on) {
+		if (priv->wep_key_len[priv->default_key] > 5)
+			mib.encryption_level = 2;
+		else
+			mib.encryption_level = 1;
+	} else {
+		mib.encryption_level = 0;
+	}
+
+	mib.default_key = priv->default_key;
+	mib.exclude_unencrypted = priv->exclude_unencrypted;
+
+	for (i = 0; i < MAX_ENCRYPTION_KEYS; i++)
+		memcpy(mib.wep_keys[i], priv->wep_keys[i], 13);
+
+	atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib));
+}
+
+static void build_wpa_mib(struct atmel_private *priv)
+{
+	/* This is for the later (WPA enabled) firmware. */
+
+	struct { /* NB this is matched to the hardware, don't change. */
+		u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE];
+		u8 receiver_address[ETH_ALEN];
+		u8 wep_is_on;
+		u8 default_key; /* 0..3 */
+		u8 group_key;
+		u8 exclude_unencrypted;
+		u8 encryption_type;
+		u8 reserved;
+
+		u32 WEPICV_error_count;
+		u32 WEP_excluded_count;
+
+		u8 key_RSC[4][8];
+	} mib;
+
+	int i;
+
+	mib.wep_is_on = priv->wep_is_on;
+	mib.exclude_unencrypted = priv->exclude_unencrypted;
+	memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN);
+
+	/* zero all the keys before adding in valid ones. */
+	memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value));
+
+	if (priv->wep_is_on) {
+		/* There's a comment in the Atmel code to the effect that this
+		   is only valid when still using WEP, it may need to be set to
+		   something to use WPA */
+		memset(mib.key_RSC, 0, sizeof(mib.key_RSC));
+
+		mib.default_key = mib.group_key = 255;
+		for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) {
+			if (priv->wep_key_len[i] > 0) {
+				memcpy(mib.cipher_default_key_value[i], priv->wep_keys[i], MAX_ENCRYPTION_KEY_SIZE);
+				if (i == priv->default_key) {
+					mib.default_key = i;
+					mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 7;
+					mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->pairwise_cipher_suite;
+				} else {
+					mib.group_key = i;
+					priv->group_cipher_suite = priv->pairwise_cipher_suite;
+					mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 1;
+					mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->group_cipher_suite;
+				}
+			}
+		}
+		if (mib.default_key == 255)
+			mib.default_key = mib.group_key != 255 ? mib.group_key : 0;
+		if (mib.group_key == 255)
+			mib.group_key = mib.default_key;
+
+	}
+
+	atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib));
+}
+
+static int reset_atmel_card(struct net_device *dev)
+{
+	/* do everything necessary to wake up the hardware, including
+	   waiting for the lightning strike and throwing the knife switch....
+
+	   set all the Mib values which matter in the card to match
+	   their settings in the atmel_private structure. Some of these
+	   can be altered on the fly, but many (WEP, infrastucture or ad-hoc)
+	   can only be changed by tearing down the world and coming back through
+	   here.
+
+	   This routine is also responsible for initialising some
+	   hardware-specific fields in the atmel_private structure,
+	   including a copy of the firmware's hostinfo structure
+	   which is the route into the rest of the firmware datastructures. */
+
+	struct atmel_private *priv = netdev_priv(dev);
+	u8 configuration;
+	int old_state = priv->station_state;
+	int err = 0;
+
+	/* data to add to the firmware names, in priority order
+	   this implemenents firmware versioning */
+
+	static char *firmware_modifier[] = {
+		"-wpa",
+		"",
+		NULL
+	};
+
+	/* reset pccard */
+	if (priv->bus_type == BUS_TYPE_PCCARD)
+		atmel_write16(priv->dev, GCR, 0x0060);
+
+	/* stop card , disable interrupts */
+	atmel_write16(priv->dev, GCR, 0x0040);
+
+	if (priv->card_type == CARD_TYPE_EEPROM) {
+		/* copy in firmware if needed */
+		const struct firmware *fw_entry = NULL;
+		const unsigned char *fw;
+		int len = priv->firmware_length;
+		if (!(fw = priv->firmware)) {
+			if (priv->firmware_type == ATMEL_FW_TYPE_NONE) {
+				if (strlen(priv->firmware_id) == 0) {
+					printk(KERN_INFO
+					       "%s: card type is unknown: assuming at76c502 firmware is OK.\n",
+					       dev->name);
+					printk(KERN_INFO
+					       "%s: if not, use the firmware= module parameter.\n",
+					       dev->name);
+					strcpy(priv->firmware_id, "atmel_at76c502.bin");
+				}
+				err = request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev);
+				if (err != 0) {
+					printk(KERN_ALERT
+					       "%s: firmware %s is missing, cannot continue.\n",
+					       dev->name, priv->firmware_id);
+					return err;
+				}
+			} else {
+				int fw_index = 0;
+				int success = 0;
+
+				/* get firmware filename entry based on firmware type ID */
+				while (fw_table[fw_index].fw_type != priv->firmware_type
+						&& fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE)
+					fw_index++;
+
+				/* construct the actual firmware file name */
+				if (fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) {
+					int i;
+					for (i = 0; firmware_modifier[i]; i++) {
+						snprintf(priv->firmware_id, 32, "%s%s.%s", fw_table[fw_index].fw_file,
+							firmware_modifier[i], fw_table[fw_index].fw_file_ext);
+						priv->firmware_id[31] = '\0';
+						if (request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev) == 0) {
+							success = 1;
+							break;
+						}
+					}
+				}
+				if (!success) {
+					printk(KERN_ALERT
+					       "%s: firmware %s is missing, cannot start.\n",
+					       dev->name, priv->firmware_id);
+					priv->firmware_id[0] = '\0';
+					return -ENOENT;
+				}
+			}
+
+			fw = fw_entry->data;
+			len = fw_entry->size;
+		}
+
+		if (len <= 0x6000) {
+			atmel_write16(priv->dev, BSR, BSS_IRAM);
+			atmel_copy_to_card(priv->dev, 0, fw, len);
+			atmel_set_gcr(priv->dev, GCR_REMAP);
+		} else {
+			/* Remap */
+			atmel_set_gcr(priv->dev, GCR_REMAP);
+			atmel_write16(priv->dev, BSR, BSS_IRAM);
+			atmel_copy_to_card(priv->dev, 0, fw, 0x6000);
+			atmel_write16(priv->dev, BSR, 0x2ff);
+			atmel_copy_to_card(priv->dev, 0x8000, &fw[0x6000], len - 0x6000);
+		}
+
+		release_firmware(fw_entry);
+	}
+
+	err = atmel_wakeup_firmware(priv);
+	if (err != 0)
+		return err;
+
+	/* Check the version and set the correct flag for wpa stuff,
+	   old and new firmware is incompatible.
+	   The pre-wpa 3com firmware reports major version 5,
+	   the wpa 3com firmware is major version 4 and doesn't need
+	   the 3com broken-ness filter. */
+	priv->use_wpa = (priv->host_info.major_version == 4);
+	priv->radio_on_broken = (priv->host_info.major_version == 5);
+
+	/* unmask all irq sources */
+	atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_MASK_OFFSET), 0xff);
+
+	/* int Tx system and enable Tx */
+	atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, 0), 0);
+	atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, 0), 0x80000000L);
+	atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, 0), 0);
+	atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, 0), 0);
+
+	priv->tx_desc_free = priv->host_info.tx_desc_count;
+	priv->tx_desc_head = 0;
+	priv->tx_desc_tail = 0;
+	priv->tx_desc_previous = 0;
+	priv->tx_free_mem = priv->host_info.tx_buff_size;
+	priv->tx_buff_head = 0;
+	priv->tx_buff_tail = 0;
+
+	configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET));
+	atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET),
+				   configuration | FUNC_CTRL_TxENABLE);
+
+	/* init Rx system and enable */
+	priv->rx_desc_head = 0;
+
+	configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET));
+	atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET),
+				   configuration | FUNC_CTRL_RxENABLE);
+
+	if (!priv->radio_on_broken) {
+		if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) ==
+		    CMD_STATUS_REJECTED_RADIO_OFF) {
+			printk(KERN_INFO "%s: cannot turn the radio on.\n",
+			       dev->name);
+			return -EIO;
+		}
+	}
+
+	/* set up enough MIB values to run. */
+	atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_AUTO_TX_RATE_POS, priv->auto_tx_rate);
+	atmel_set_mib8(priv, Local_Mib_Type,  LOCAL_MIB_TX_PROMISCUOUS_POS,  PROM_MODE_OFF);
+	atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_RTS_THRESHOLD_POS, priv->rts_threshold);
+	atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_FRAG_THRESHOLD_POS, priv->frag_threshold);
+	atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_SHORT_RETRY_POS, priv->short_retry);
+	atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_LONG_RETRY_POS, priv->long_retry);
+	atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, priv->preamble);
+	atmel_set_mib(priv, Mac_Address_Mib_Type, MAC_ADDR_MIB_MAC_ADDR_POS,
+		      priv->dev->dev_addr, 6);
+	atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE);
+	atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1);
+	atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_BEACON_PER_POS, priv->default_beacon_period);
+	atmel_set_mib(priv, Phy_Mib_Type, PHY_MIB_RATE_SET_POS, atmel_basic_rates, 4);
+	atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_PRIVACY_POS, priv->wep_is_on);
+	if (priv->use_wpa)
+		build_wpa_mib(priv);
+	else
+		build_wep_mib(priv);
+
+	if (old_state == STATION_STATE_READY) {
+		union iwreq_data wrqu;
+
+		wrqu.data.length = 0;
+		wrqu.data.flags = 0;
+		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+		eth_zero_addr(wrqu.ap_addr.sa_data);
+		wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+	}
+
+	return 0;
+}
+
+static void atmel_send_command(struct atmel_private *priv, int command,
+			       void *cmd, int cmd_size)
+{
+	if (cmd)
+		atmel_copy_to_card(priv->dev, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET),
+				   cmd, cmd_size);
+
+	atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET), command);
+	atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET), 0);
+}
+
+static int atmel_send_command_wait(struct atmel_private *priv, int command,
+				   void *cmd, int cmd_size)
+{
+	int i, status;
+
+	atmel_send_command(priv, command, cmd, cmd_size);
+
+	for (i = 5000; i; i--) {
+		status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET));
+		if (status != CMD_STATUS_IDLE &&
+		    status != CMD_STATUS_IN_PROGRESS)
+			break;
+		udelay(20);
+	}
+
+	if (i == 0) {
+		printk(KERN_ALERT "%s: failed to contact MAC.\n", priv->dev->name);
+		status =  CMD_STATUS_HOST_ERROR;
+	} else {
+		if (command != CMD_EnableRadio)
+			status = CMD_STATUS_COMPLETE;
+	}
+
+	return status;
+}
+
+static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index)
+{
+	struct get_set_mib m;
+	m.type = type;
+	m.size = 1;
+	m.index = index;
+
+	atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + 1);
+	return atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE));
+}
+
+static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, u8 data)
+{
+	struct get_set_mib m;
+	m.type = type;
+	m.size = 1;
+	m.index = index;
+	m.data[0] = data;
+
+	atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 1);
+}
+
+static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index,
+			    u16 data)
+{
+	struct get_set_mib m;
+	m.type = type;
+	m.size = 2;
+	m.index = index;
+	m.data[0] = data;
+	m.data[1] = data >> 8;
+
+	atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 2);
+}
+
+static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index,
+			  u8 *data, int data_len)
+{
+	struct get_set_mib m;
+	m.type = type;
+	m.size = data_len;
+	m.index = index;
+
+	if (data_len > MIB_MAX_DATA_BYTES)
+		printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name);
+
+	memcpy(m.data, data, data_len);
+	atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + data_len);
+}
+
+static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index,
+			  u8 *data, int data_len)
+{
+	struct get_set_mib m;
+	m.type = type;
+	m.size = data_len;
+	m.index = index;
+
+	if (data_len > MIB_MAX_DATA_BYTES)
+		printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name);
+
+	atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + data_len);
+	atmel_copy_to_host(priv->dev, data,
+			   atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE), data_len);
+}
+
+static void atmel_writeAR(struct net_device *dev, u16 data)
+{
+	int i;
+	outw(data, dev->base_addr + AR);
+	/* Address register appears to need some convincing..... */
+	for (i = 0; data != inw(dev->base_addr + AR) && i < 10; i++)
+		outw(data, dev->base_addr + AR);
+}
+
+static void atmel_copy_to_card(struct net_device *dev, u16 dest,
+			       const unsigned char *src, u16 len)
+{
+	int i;
+	atmel_writeAR(dev, dest);
+	if (dest % 2) {
+		atmel_write8(dev, DR, *src);
+		src++; len--;
+	}
+	for (i = len; i > 1 ; i -= 2) {
+		u8 lb = *src++;
+		u8 hb = *src++;
+		atmel_write16(dev, DR, lb | (hb << 8));
+	}
+	if (i)
+		atmel_write8(dev, DR, *src);
+}
+
+static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest,
+			       u16 src, u16 len)
+{
+	int i;
+	atmel_writeAR(dev, src);
+	if (src % 2) {
+		*dest = atmel_read8(dev, DR);
+		dest++; len--;
+	}
+	for (i = len; i > 1 ; i -= 2) {
+		u16 hw = atmel_read16(dev, DR);
+		*dest++ = hw;
+		*dest++ = hw >> 8;
+	}
+	if (i)
+		*dest = atmel_read8(dev, DR);
+}
+
+static void atmel_set_gcr(struct net_device *dev, u16 mask)
+{
+	outw(inw(dev->base_addr + GCR) | mask, dev->base_addr + GCR);
+}
+
+static void atmel_clear_gcr(struct net_device *dev, u16 mask)
+{
+	outw(inw(dev->base_addr + GCR) & ~mask, dev->base_addr + GCR);
+}
+
+static int atmel_lock_mac(struct atmel_private *priv)
+{
+	int i, j = 20;
+ retry:
+	for (i = 5000; i; i--) {
+		if (!atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET)))
+			break;
+		udelay(20);
+	}
+
+	if (!i)
+		return 0; /* timed out */
+
+	atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 1);
+	if (atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) {
+		atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);
+		if (!j--)
+			return 0; /* timed out */
+		goto retry;
+	}
+
+	return 1;
+}
+
+static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data)
+{
+	atmel_writeAR(priv->dev, pos);
+	atmel_write16(priv->dev, DR, data); /* card is little-endian */
+	atmel_write16(priv->dev, DR, data >> 16);
+}
+
+/***************************************************************************/
+/* There follows the source form of the MAC address reading firmware       */
+/***************************************************************************/
+#if 0
+
+/* Copyright 2003 Matthew T. Russotto                                      */
+/* But derived from the Atmel 76C502 firmware written by Atmel and         */
+/* included in "atmel wireless lan drivers" package                        */
+/**
+    This file is part of net.russotto.AtmelMACFW, hereto referred to
+    as AtmelMACFW
+
+    AtmelMACFW 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.
+
+    AtmelMACFW 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 AtmelMACFW; if not, see <http://www.gnu.org/licenses/>.
+
+****************************************************************************/
+/* This firmware should work on the 76C502 RFMD, RFMD_D, and RFMD_E        */
+/* It will probably work on the 76C504 and 76C502 RFMD_3COM                */
+/* It only works on SPI EEPROM versions of the card.                       */
+
+/* This firmware initializes the SPI controller and clock, reads the MAC   */
+/* address from the EEPROM into SRAM, and puts the SRAM offset of the MAC  */
+/* address in MR2, and sets MR3 to 0x10 to indicate it is done             */
+/* It also puts a complete copy of the EEPROM in SRAM with the offset in   */
+/* MR4, for investigational purposes (maybe we can determine chip type     */
+/* from that?)                                                             */
+
+	.org 0
+    .set MRBASE, 0x8000000
+	.set CPSR_INITIAL, 0xD3 /* IRQ/FIQ disabled, ARM mode, Supervisor state */
+	.set CPSR_USER, 0xD1 /* IRQ/FIQ disabled, ARM mode, USER state */
+	.set SRAM_BASE,  0x02000000
+	.set SP_BASE,    0x0F300000
+	.set UNK_BASE,   0x0F000000 /* Some internal device, but which one? */
+	.set SPI_CGEN_BASE,  0x0E000000 /* Some internal device, but which one? */
+	.set UNK3_BASE,  0x02014000 /* Some internal device, but which one? */
+	.set STACK_BASE, 0x5600
+	.set SP_SR, 0x10
+	.set SP_TDRE, 2 /* status register bit -- TDR empty */
+	.set SP_RDRF, 1 /* status register bit -- RDR full */
+	.set SP_SWRST, 0x80
+	.set SP_SPIEN, 0x1
+	.set SP_CR, 0   /* control register */
+	.set SP_MR, 4   /* mode register */
+	.set SP_RDR, 0x08 /* Read Data Register */
+	.set SP_TDR, 0x0C /* Transmit Data Register */
+	.set SP_CSR0, 0x30 /* chip select registers */
+	.set SP_CSR1, 0x34
+	.set SP_CSR2, 0x38
+	.set SP_CSR3, 0x3C
+	.set NVRAM_CMD_RDSR, 5 /* read status register */
+	.set NVRAM_CMD_READ, 3 /* read data */
+	.set NVRAM_SR_RDY, 1 /* RDY bit.  This bit is inverted */
+	.set SPI_8CLOCKS, 0xFF /* Writing this to the TDR doesn't do anything to the
+				  serial output, since SO is normally high.  But it
+				  does cause 8 clock cycles and thus 8 bits to be
+				  clocked in to the chip.  See Atmel's SPI
+				  controller (e.g. AT91M55800) timing and 4K
+				  SPI EEPROM manuals */
+
+	.set NVRAM_SCRATCH, 0x02000100  /* arbitrary area for scratchpad memory */
+	.set NVRAM_IMAGE, 0x02000200
+	.set NVRAM_LENGTH, 0x0200
+	.set MAC_ADDRESS_MIB, SRAM_BASE
+	.set MAC_ADDRESS_LENGTH, 6
+	.set MAC_BOOT_FLAG, 0x10
+	.set MR1, 0
+	.set MR2, 4
+	.set MR3, 8
+	.set MR4, 0xC
+RESET_VECTOR:
+	b RESET_HANDLER
+UNDEF_VECTOR:
+	b HALT1
+SWI_VECTOR:
+	b HALT1
+IABORT_VECTOR:
+	b HALT1
+DABORT_VECTOR:
+RESERVED_VECTOR:
+	b HALT1
+IRQ_VECTOR:
+	b HALT1
+FIQ_VECTOR:
+	b HALT1
+HALT1:	b HALT1
+RESET_HANDLER:
+	mov     r0, #CPSR_INITIAL
+	msr	CPSR_c, r0	/* This is probably unnecessary */
+
+/* I'm guessing this is initializing clock generator electronics for SPI */
+	ldr	r0, =SPI_CGEN_BASE
+	mov	r1, #0
+	mov	r1, r1, lsl #3
+	orr	r1, r1, #0
+	str	r1, [r0]
+	ldr	r1, [r0, #28]
+	bic	r1, r1, #16
+	str	r1, [r0, #28]
+	mov	r1, #1
+	str	r1, [r0, #8]
+
+	ldr	r0, =MRBASE
+	mov	r1, #0
+	strh	r1, [r0, #MR1]
+	strh	r1, [r0, #MR2]
+	strh	r1, [r0, #MR3]
+	strh	r1, [r0, #MR4]
+
+	mov	sp, #STACK_BASE
+	bl	SP_INIT
+	mov	r0, #10
+	bl	DELAY9
+	bl	GET_MAC_ADDR
+	bl	GET_WHOLE_NVRAM
+	ldr	r0, =MRBASE
+	ldr	r1, =MAC_ADDRESS_MIB
+	strh	r1, [r0, #MR2]
+	ldr	r1, =NVRAM_IMAGE
+	strh	r1, [r0, #MR4]
+	mov	r1, #MAC_BOOT_FLAG
+	strh	r1, [r0, #MR3]
+HALT2:	b HALT2
+.func Get_Whole_NVRAM, GET_WHOLE_NVRAM
+GET_WHOLE_NVRAM:
+	stmdb	sp!, {lr}
+	mov	r2, #0 /* 0th bytes of NVRAM */
+	mov	r3, #NVRAM_LENGTH
+	mov	r1, #0		/* not used in routine */
+	ldr	r0, =NVRAM_IMAGE
+	bl	NVRAM_XFER
+	ldmia	sp!, {lr}
+	bx	lr
+.endfunc
+
+.func Get_MAC_Addr, GET_MAC_ADDR
+GET_MAC_ADDR:
+	stmdb	sp!, {lr}
+	mov	r2, #0x120	/* address of MAC Address within NVRAM */
+	mov	r3, #MAC_ADDRESS_LENGTH
+	mov	r1, #0		/* not used in routine */
+	ldr	r0, =MAC_ADDRESS_MIB
+	bl	NVRAM_XFER
+	ldmia	sp!, {lr}
+	bx	lr
+.endfunc
+.ltorg
+.func Delay9, DELAY9
+DELAY9:
+	adds	r0, r0, r0, LSL #3   /* r0 = r0 * 9 */
+DELAYLOOP:
+	beq	DELAY9_done
+	subs	r0, r0, #1
+	b	DELAYLOOP
+DELAY9_done:
+	bx	lr
+.endfunc
+
+.func SP_Init, SP_INIT
+SP_INIT:
+	mov	r1, #SP_SWRST
+	ldr	r0, =SP_BASE
+	str	r1, [r0, #SP_CR] /* reset the SPI */
+	mov	r1, #0
+	str	r1, [r0, #SP_CR] /* release SPI from reset state */
+	mov	r1, #SP_SPIEN
+	str	r1, [r0, #SP_MR] /* set the SPI to MASTER mode*/
+	str	r1, [r0, #SP_CR] /* enable the SPI */
+
+/*  My guess would be this turns on the SPI clock */
+	ldr	r3, =SPI_CGEN_BASE
+	ldr	r1, [r3, #28]
+	orr	r1, r1, #0x2000
+	str	r1, [r3, #28]
+
+	ldr	r1, =0x2000c01
+	str	r1, [r0, #SP_CSR0]
+	ldr	r1, =0x2000201
+	str	r1, [r0, #SP_CSR1]
+	str	r1, [r0, #SP_CSR2]
+	str	r1, [r0, #SP_CSR3]
+	ldr	r1, [r0, #SP_SR]
+	ldr	r0, [r0, #SP_RDR]
+	bx	lr
+.endfunc
+.func NVRAM_Init, NVRAM_INIT
+NVRAM_INIT:
+	ldr	r1, =SP_BASE
+	ldr	r0, [r1, #SP_RDR]
+	mov	r0, #NVRAM_CMD_RDSR
+	str	r0, [r1, #SP_TDR]
+SP_loop1:
+	ldr	r0, [r1, #SP_SR]
+	tst	r0, #SP_TDRE
+	beq	SP_loop1
+
+	mov	r0, #SPI_8CLOCKS
+	str	r0, [r1, #SP_TDR]
+SP_loop2:
+	ldr	r0, [r1, #SP_SR]
+	tst	r0, #SP_TDRE
+	beq	SP_loop2
+
+	ldr	r0, [r1, #SP_RDR]
+SP_loop3:
+	ldr	r0, [r1, #SP_SR]
+	tst	r0, #SP_RDRF
+	beq	SP_loop3
+
+	ldr	r0, [r1, #SP_RDR]
+	and	r0, r0, #255
+	bx	lr
+.endfunc
+
+.func NVRAM_Xfer, NVRAM_XFER
+	/* r0 = dest address */
+	/* r1 = not used */
+	/* r2 = src address within NVRAM */
+	/* r3 = length */
+NVRAM_XFER:
+	stmdb	sp!, {r4, r5, lr}
+	mov	r5, r0		/* save r0 (dest address) */
+	mov	r4, r3		/* save r3 (length) */
+	mov	r0, r2, LSR #5 /*  SPI memories put A8 in the command field */
+	and	r0, r0, #8
+	add	r0, r0, #NVRAM_CMD_READ
+	ldr	r1, =NVRAM_SCRATCH
+	strb	r0, [r1, #0]	/* save command in NVRAM_SCRATCH[0] */
+	strb	r2, [r1, #1]    /* save low byte of source address in NVRAM_SCRATCH[1] */
+_local1:
+	bl	NVRAM_INIT
+	tst	r0, #NVRAM_SR_RDY
+	bne	_local1
+	mov	r0, #20
+	bl	DELAY9
+	mov	r2, r4		/* length */
+	mov	r1, r5		/* dest address */
+	mov	r0, #2		/* bytes to transfer in command */
+	bl	NVRAM_XFER2
+	ldmia	sp!, {r4, r5, lr}
+	bx	lr
+.endfunc
+
+.func NVRAM_Xfer2, NVRAM_XFER2
+NVRAM_XFER2:
+	stmdb	sp!, {r4, r5, r6, lr}
+	ldr	r4, =SP_BASE
+	mov	r3, #0
+	cmp	r0, #0
+	bls	_local2
+	ldr	r5, =NVRAM_SCRATCH
+_local4:
+	ldrb	r6, [r5, r3]
+	str	r6, [r4, #SP_TDR]
+_local3:
+	ldr	r6, [r4, #SP_SR]
+	tst	r6, #SP_TDRE
+	beq	_local3
+	add	r3, r3, #1
+	cmp	r3, r0 /* r0 is # of bytes to send out (command+addr) */
+	blo	_local4
+_local2:
+	mov	r3, #SPI_8CLOCKS
+	str	r3, [r4, #SP_TDR]
+	ldr	r0, [r4, #SP_RDR]
+_local5:
+	ldr	r0, [r4, #SP_SR]
+	tst	r0, #SP_RDRF
+	beq	_local5
+	ldr	r0, [r4, #SP_RDR] /* what's this byte?  It's the byte read while writing the TDR -- nonsense, because the NVRAM doesn't read and write at the same time */
+	mov	r0, #0
+	cmp	r2, #0  /* r2 is # of bytes to copy in */
+	bls	_local6
+_local7:
+	ldr	r5, [r4, #SP_SR]
+	tst	r5, #SP_TDRE
+	beq	_local7
+	str	r3, [r4, #SP_TDR]  /* r3 has SPI_8CLOCKS */
+_local8:
+	ldr	r5, [r4, #SP_SR]
+	tst	r5, #SP_RDRF
+	beq	_local8
+	ldr	r5, [r4, #SP_RDR] /* but didn't we read this byte above? */
+	strb	r5, [r1], #1 /* postindexed */
+	add	r0, r0, #1
+	cmp	r0, r2
+	blo	_local7 /* since we don't send another address, the NVRAM must be capable of sequential reads */
+_local6:
+	mov	r0, #200
+	bl	DELAY9
+	ldmia	sp!, {r4, r5, r6, lr}
+	bx	lr
+#endif
diff --git a/drivers/net/wireless/atmel.h b/drivers/net/wireless/atmel.h
new file mode 100644
index 0000000..96f7318
--- /dev/null
+++ b/drivers/net/wireless/atmel.h
@@ -0,0 +1,43 @@
+/*** -*- linux-c -*- **********************************************************
+
+     Driver for Atmel at76c502 at76c504 and at76c506 wireless cards.
+
+         Copyright 2005 Dan Williams and Red Hat, Inc.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This software 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 Atmel wireless lan drivers; if not, see
+    <http://www.gnu.org/licenses/>.
+
+******************************************************************************/
+
+#ifndef _ATMEL_H
+#define _ATMEL_H
+
+typedef enum {
+	ATMEL_FW_TYPE_NONE = 0,
+	ATMEL_FW_TYPE_502,
+	ATMEL_FW_TYPE_502D,
+	ATMEL_FW_TYPE_502E,
+	ATMEL_FW_TYPE_502_3COM,
+	ATMEL_FW_TYPE_504,
+	ATMEL_FW_TYPE_504_2958,
+	ATMEL_FW_TYPE_504A_2958,
+	ATMEL_FW_TYPE_506
+} AtmelFWType;
+
+struct net_device *init_atmel_card(unsigned short, unsigned long, const AtmelFWType, struct device *, 
+				    int (*present_func)(void *), void * );
+void stop_atmel_card( struct net_device *);
+int atmel_open( struct net_device * );
+
+#endif
diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c
new file mode 100644
index 0000000..7afc9c5
--- /dev/null
+++ b/drivers/net/wireless/atmel_cs.c
@@ -0,0 +1,286 @@
+/*** -*- linux-c -*- **********************************************************
+
+     Driver for Atmel at76c502 at76c504 and at76c506 wireless cards.
+
+        Copyright 2000-2001 ATMEL Corporation.
+        Copyright 2003 Simon Kelley.
+
+    This code was developed from version 2.1.1 of the Atmel drivers,
+    released by Atmel corp. under the GPL in December 2002. It also
+    includes code from the Linux aironet drivers (C) Benjamin Reed,
+    and the Linux PCMCIA package, (C) David Hinds.
+
+    For all queries about this code, please contact the current author,
+    Simon Kelley <simon@thekelleys.org.uk> and not Atmel 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.
+
+    This software 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 Atmel wireless lan drivers; if not, see
+    <http://www.gnu.org/licenses/>.
+
+******************************************************************************/
+
+#ifdef __IN_PCMCIA_PACKAGE__
+#include <pcmcia/k_compat.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ciscode.h>
+
+#include <asm/io.h>
+#include <linux/wireless.h>
+
+#include "atmel.h"
+
+
+/*====================================================================*/
+
+MODULE_AUTHOR("Simon Kelley");
+MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards.");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("Atmel at76c50x PCMCIA cards");
+
+/*====================================================================*/
+
+static int atmel_config(struct pcmcia_device *link);
+static void atmel_release(struct pcmcia_device *link);
+
+static void atmel_detach(struct pcmcia_device *p_dev);
+
+struct local_info {
+	struct net_device *eth_dev;
+};
+
+static int atmel_probe(struct pcmcia_device *p_dev)
+{
+	struct local_info *local;
+
+	dev_dbg(&p_dev->dev, "atmel_attach()\n");
+
+	/* Allocate space for private device-specific data */
+	local = kzalloc(sizeof(*local), GFP_KERNEL);
+	if (!local)
+		return -ENOMEM;
+
+	p_dev->priv = local;
+
+	return atmel_config(p_dev);
+} /* atmel_attach */
+
+static void atmel_detach(struct pcmcia_device *link)
+{
+	dev_dbg(&link->dev, "atmel_detach\n");
+
+	atmel_release(link);
+
+	kfree(link->priv);
+}
+
+/* Call-back function to interrogate PCMCIA-specific information
+   about the current existence of the card */
+static int card_present(void *arg)
+{
+	struct pcmcia_device *link = (struct pcmcia_device *)arg;
+
+	if (pcmcia_dev_present(link))
+		return 1;
+
+	return 0;
+}
+
+static int atmel_config_check(struct pcmcia_device *p_dev, void *priv_data)
+{
+	if (p_dev->config_index == 0)
+		return -EINVAL;
+
+	return pcmcia_request_io(p_dev);
+}
+
+static int atmel_config(struct pcmcia_device *link)
+{
+	struct local_info *dev;
+	int ret;
+	const struct pcmcia_device_id *did;
+
+	dev = link->priv;
+	did = dev_get_drvdata(&link->dev);
+
+	dev_dbg(&link->dev, "atmel_config\n");
+
+	link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
+		CONF_AUTO_AUDIO | CONF_AUTO_SET_IO;
+
+	if (pcmcia_loop_config(link, atmel_config_check, NULL))
+		goto failed;
+
+	if (!link->irq) {
+		dev_err(&link->dev, "atmel: cannot assign IRQ: check that CONFIG_ISA is set in kernel config.");
+		goto failed;
+	}
+
+	ret = pcmcia_enable_device(link);
+	if (ret)
+		goto failed;
+
+	((struct local_info *)link->priv)->eth_dev =
+		init_atmel_card(link->irq,
+				link->resource[0]->start,
+				did ? did->driver_info : ATMEL_FW_TYPE_NONE,
+				&link->dev,
+				card_present,
+				link);
+	if (!((struct local_info *)link->priv)->eth_dev)
+			goto failed;
+
+
+	return 0;
+
+ failed:
+	atmel_release(link);
+	return -ENODEV;
+}
+
+static void atmel_release(struct pcmcia_device *link)
+{
+	struct net_device *dev = ((struct local_info *)link->priv)->eth_dev;
+
+	dev_dbg(&link->dev, "atmel_release\n");
+
+	if (dev)
+		stop_atmel_card(dev);
+	((struct local_info *)link->priv)->eth_dev = NULL;
+
+	pcmcia_disable_device(link);
+}
+
+static int atmel_suspend(struct pcmcia_device *link)
+{
+	struct local_info *local = link->priv;
+
+	netif_device_detach(local->eth_dev);
+
+	return 0;
+}
+
+static int atmel_resume(struct pcmcia_device *link)
+{
+	struct local_info *local = link->priv;
+
+	atmel_open(local->eth_dev);
+	netif_device_attach(local->eth_dev);
+
+	return 0;
+}
+
+/*====================================================================*/
+/* We use the driver_info field to store the correct firmware type for a card. */
+
+#define PCMCIA_DEVICE_MANF_CARD_INFO(manf, card, info) { \
+	.match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
+			PCMCIA_DEV_ID_MATCH_CARD_ID, \
+	.manf_id = (manf), \
+	.card_id = (card), \
+        .driver_info = (kernel_ulong_t)(info), }
+
+#define PCMCIA_DEVICE_PROD_ID12_INFO(v1, v2, vh1, vh2, info) { \
+	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
+			PCMCIA_DEV_ID_MATCH_PROD_ID2, \
+	.prod_id = { (v1), (v2), NULL, NULL }, \
+	.prod_id_hash = { (vh1), (vh2), 0, 0 }, \
+        .driver_info = (kernel_ulong_t)(info), }
+
+static const struct pcmcia_device_id atmel_ids[] = {
+	PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0620, ATMEL_FW_TYPE_502_3COM),
+	PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0696, ATMEL_FW_TYPE_502_3COM),
+	PCMCIA_DEVICE_MANF_CARD_INFO(0x01bf, 0x3302, ATMEL_FW_TYPE_502E),
+	PCMCIA_DEVICE_MANF_CARD_INFO(0xd601, 0x0007, ATMEL_FW_TYPE_502),
+	PCMCIA_DEVICE_PROD_ID12_INFO("11WAVE", "11WP611AL-E", 0x9eb2da1f, 0xc9a0d3f9, ATMEL_FW_TYPE_502E),
+	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR", 0xabda4164, 0x41b37e1f, ATMEL_FW_TYPE_502),
+	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_D", 0xabda4164, 0x3675d704, ATMEL_FW_TYPE_502D),
+	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_E", 0xabda4164, 0x4172e792, ATMEL_FW_TYPE_502E),
+	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504_R", 0xabda4164, 0x917f3d72, ATMEL_FW_TYPE_504_2958),
+	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504", 0xabda4164, 0x5040670a, ATMEL_FW_TYPE_504),
+	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504A", 0xabda4164, 0xe15ed87f, ATMEL_FW_TYPE_504A_2958),
+	PCMCIA_DEVICE_PROD_ID12_INFO("BT", "Voyager 1020 Laptop Adapter", 0xae49b86a, 0x1e957cd5, ATMEL_FW_TYPE_502),
+	PCMCIA_DEVICE_PROD_ID12_INFO("CNet", "CNWLC 11Mbps Wireless PC Card V-5", 0xbc477dde, 0x502fae6b, ATMEL_FW_TYPE_502E),
+	PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN PC Card", 0x5b878724, 0x122f1df6, ATMEL_FW_TYPE_502),
+	PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN Card S", 0x5b878724, 0x5fba533a, ATMEL_FW_TYPE_504_2958),
+	PCMCIA_DEVICE_PROD_ID12_INFO("OEM", "11Mbps Wireless LAN PC Card V-3", 0xfea54c90, 0x1c5b0f68, ATMEL_FW_TYPE_502),
+	PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W", 0xc4f8b18b, 0x30f38774, ATMEL_FW_TYPE_502D),
+	PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W-V2", 0xc4f8b18b, 0x172d1377, ATMEL_FW_TYPE_502),
+	PCMCIA_DEVICE_PROD_ID12_INFO("Wireless", "PC_CARD", 0xa407ecdd, 0x119f6314, ATMEL_FW_TYPE_502D),
+	PCMCIA_DEVICE_PROD_ID12_INFO("WLAN", "802.11b PC CARD", 0x575c516c, 0xb1f6dbc4, ATMEL_FW_TYPE_502D),
+	PCMCIA_DEVICE_PROD_ID12_INFO("LG", "LW2100N", 0xb474d43a, 0x6b1fec94, ATMEL_FW_TYPE_502E),
+	PCMCIA_DEVICE_NULL
+};
+
+MODULE_DEVICE_TABLE(pcmcia, atmel_ids);
+
+static struct pcmcia_driver atmel_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "atmel_cs",
+	.probe          = atmel_probe,
+	.remove		= atmel_detach,
+	.id_table	= atmel_ids,
+	.suspend	= atmel_suspend,
+	.resume		= atmel_resume,
+};
+module_pcmcia_driver(atmel_driver);
+
+/*
+    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.
+
+    In addition:
+
+    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. The name of the author may not be used to endorse or promote
+       products derived from this software without specific prior written
+       permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+*/
diff --git a/drivers/net/wireless/atmel_pci.c b/drivers/net/wireless/atmel_pci.c
new file mode 100644
index 0000000..bcf1f27
--- /dev/null
+++ b/drivers/net/wireless/atmel_pci.c
@@ -0,0 +1,76 @@
+/*** -*- linux-c -*- **********************************************************
+
+     Driver for Atmel at76c502 at76c504 and at76c506 wireless cards.
+
+         Copyright 2004 Simon Kelley.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This software is 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 Atmel wireless lan drivers; if not, see
+    <http://www.gnu.org/licenses/>.
+
+******************************************************************************/
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include "atmel.h"
+
+MODULE_AUTHOR("Simon Kelley");
+MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards.");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("Atmel at76c506 PCI wireless cards");
+
+static const struct pci_device_id card_ids[] = {
+	{ 0x1114, 0x0506, PCI_ANY_ID, PCI_ANY_ID },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, card_ids);
+
+static int atmel_pci_probe(struct pci_dev *, const struct pci_device_id *);
+static void atmel_pci_remove(struct pci_dev *);
+
+static struct pci_driver atmel_driver = {
+	.name     = "atmel",
+	.id_table = card_ids,
+	.probe    = atmel_pci_probe,
+	.remove   = atmel_pci_remove,
+};
+
+
+static int atmel_pci_probe(struct pci_dev *pdev,
+				     const struct pci_device_id *pent)
+{
+	struct net_device *dev;
+
+	if (pci_enable_device(pdev))
+		return -ENODEV;
+
+	pci_set_master(pdev);
+
+	dev = init_atmel_card(pdev->irq, pdev->resource[1].start,
+			      ATMEL_FW_TYPE_506,
+			      &pdev->dev, NULL, NULL);
+	if (!dev)
+		return -ENODEV;
+
+	pci_set_drvdata(pdev, dev);
+	return 0;
+}
+
+static void atmel_pci_remove(struct pci_dev *pdev)
+{
+	stop_atmel_card(pci_get_drvdata(pdev));
+}
+
+module_pci_driver(atmel_driver);
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig
index a95de8d..a7a7d18 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/b43/Kconfig
@@ -167,6 +167,15 @@
 
 	  Say N, this is BROKEN and crashes driver.
 
+config B43_PHY_AC
+	bool "Support for AC-PHY (802.11ac) devices (BROKEN)"
+	depends on B43 && B43_BCMA && BROKEN
+	---help---
+	  This PHY type can be found in the following chipsets:
+	  PCI: BCM4352, BCM4360
+
+	  Say N, this is BROKEN and crashes driver.
+
 # This config option automatically enables b43 LEDS support,
 # if it's possible.
 config B43_LEDS
diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile
index 186c62f..9d65730 100644
--- a/drivers/net/wireless/b43/Makefile
+++ b/drivers/net/wireless/b43/Makefile
@@ -13,6 +13,7 @@
 b43-$(CPTCFG_B43_PHY_HT)	+= tables_phy_ht.o
 b43-$(CPTCFG_B43_PHY_HT)	+= radio_2059.o
 b43-$(CPTCFG_B43_PHY_LCN)	+= phy_lcn.o tables_phy_lcn.o
+b43-$(CPTCFG_B43_PHY_AC)	+= phy_ac.o
 b43-y				+= sysfs.o
 b43-y				+= xmit.o
 b43-y				+= dma.o
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h
index c58c307..4dda71f 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/b43/b43.h
@@ -500,6 +500,8 @@
 #define  B43_BCMA_IOCTL_PHY_BW_10MHZ	0x00000000	/* 10 MHz bandwidth, 40 MHz PHY */
 #define  B43_BCMA_IOCTL_PHY_BW_20MHZ	0x00000040	/* 20 MHz bandwidth, 80 MHz PHY */
 #define  B43_BCMA_IOCTL_PHY_BW_40MHZ	0x00000080	/* 40 MHz bandwidth, 160 MHz PHY */
+#define  B43_BCMA_IOCTL_PHY_BW_80MHZ	0x000000C0	/* 80 MHz bandwidth */
+#define B43_BCMA_IOCTL_DAC		0x00000300	/* Highspeed DAC mode control field */
 #define B43_BCMA_IOCTL_GMODE		0x00002000	/* G Mode Enable */
 
 /* BCMA 802.11 core specific IO status (BCMA_IOST) flags */
@@ -941,6 +943,7 @@
 	bool beacon1_uploaded;
 	bool beacon_templates_virgin; /* Never wrote the templates? */
 	struct work_struct beacon_update_trigger;
+	spinlock_t beacon_lock;
 
 	/* The current QOS parameters for the 4 queues. */
 	struct b43_qos_params qos_params[B43_QOS_QUEUE_NUM];
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c
index 2df6e03..d6b5852 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/b43/dma.c
@@ -553,7 +553,7 @@
 				  size_t buffersize, bool dma_to_device)
 {
 	if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr)))
-		return 1;
+		return true;
 
 	switch (ring->type) {
 	case B43_DMA_30BIT:
@@ -571,13 +571,13 @@
 	}
 
 	/* The address is OK. */
-	return 0;
+	return false;
 
 address_error:
 	/* We can't support this address. Unmap it again. */
 	unmap_descbuffer(ring, addr, buffersize, dma_to_device);
 
-	return 1;
+	return true;
 }
 
 static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb)
@@ -1099,16 +1099,16 @@
 					    enum b43_dmatype type)
 {
 	if (type != B43_DMA_64BIT)
-		return 1;
+		return true;
 
 #ifdef CPTCFG_B43_SSB
 	if (dev->dev->bus_type == B43_BUS_SSB &&
 	    dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
 	    !(pci_is_pcie(dev->dev->sdev->bus->host_pci) &&
 	      ssb_read32(dev->dev->sdev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64))
-			return 1;
+			return true;
 #endif
-	return 0;
+	return false;
 }
 
 int b43_dma_init(struct b43_wldev *dev)
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 809f01c..84b3f9c 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -127,7 +127,7 @@
 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1E, BCMA_ANY_CLASS),
 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x28, BCMA_ANY_CLASS),
 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x2A, BCMA_ANY_CLASS),
-	BCMA_CORETABLE_END
+	{},
 };
 MODULE_DEVICE_TABLE(bcma, b43_bcma_tbl);
 #endif
@@ -144,7 +144,7 @@
 	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 13),
 	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 15),
 	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 16),
-	SSB_DEVTABLE_END
+	{},
 };
 MODULE_DEVICE_TABLE(ssb, b43_ssb_tbl);
 #endif
@@ -1262,6 +1262,23 @@
 		flags |= B43_BCMA_IOCTL_GMODE;
 	b43_device_enable(dev, flags);
 
+	if (dev->phy.type == B43_PHYTYPE_AC) {
+		u16 tmp;
+
+		tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+		tmp &= ~B43_BCMA_IOCTL_DAC;
+		tmp |= 0x100;
+		bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+
+		tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+		tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN;
+		bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+
+		tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+		tmp |= B43_BCMA_IOCTL_PHY_CLKEN;
+		bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+	}
+
 	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);
@@ -1601,12 +1618,26 @@
 	unsigned int rate;
 	u16 ctl;
 	int antenna;
-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon);
+	struct ieee80211_tx_info *info;
+	unsigned long flags;
+	struct sk_buff *beacon_skb;
 
-	bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data);
-	len = min_t(size_t, dev->wl->current_beacon->len,
-		  0x200 - sizeof(struct b43_plcp_hdr6));
+	spin_lock_irqsave(&dev->wl->beacon_lock, flags);
+	info = IEEE80211_SKB_CB(dev->wl->current_beacon);
 	rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value;
+	/* Clone the beacon, so it cannot go away, while we write it to hw. */
+	beacon_skb = skb_clone(dev->wl->current_beacon, GFP_ATOMIC);
+	spin_unlock_irqrestore(&dev->wl->beacon_lock, flags);
+
+	if (!beacon_skb) {
+		b43dbg(dev->wl, "Could not upload beacon. "
+		       "Failed to clone beacon skb.");
+		return;
+	}
+
+	bcn = (const struct ieee80211_mgmt *)(beacon_skb->data);
+	len = min_t(size_t, beacon_skb->len,
+		    0x200 - sizeof(struct b43_plcp_hdr6));
 
 	b43_write_template_common(dev, (const u8 *)bcn,
 				  len, ram_offset, shm_size_offset, rate);
@@ -1674,6 +1705,8 @@
 				B43_SHM_SH_DTIMPER, 0);
 	}
 	b43dbg(dev->wl, "Updated beacon template at 0x%x\n", ram_offset);
+
+	dev_kfree_skb_any(beacon_skb);
 }
 
 static void b43_upload_beacon0(struct b43_wldev *dev)
@@ -1790,13 +1823,13 @@
 	mutex_unlock(&wl->mutex);
 }
 
-/* Asynchronously update the packet templates in template RAM.
- * Locking: Requires wl->mutex to be locked. */
+/* Asynchronously update the packet templates in template RAM. */
 static void b43_update_templates(struct b43_wl *wl)
 {
-	struct sk_buff *beacon;
+	struct sk_buff *beacon, *old_beacon;
+	unsigned long flags;
 
-	/* This is the top half of the ansynchronous beacon update.
+	/* This is the top half of the asynchronous beacon update.
 	 * The bottom half is the beacon IRQ.
 	 * Beacon update must be asynchronous to avoid sending an
 	 * invalid beacon. This can happen for example, if the firmware
@@ -1810,12 +1843,17 @@
 	if (unlikely(!beacon))
 		return;
 
-	if (wl->current_beacon)
-		dev_kfree_skb_any(wl->current_beacon);
+	spin_lock_irqsave(&wl->beacon_lock, flags);
+	old_beacon = wl->current_beacon;
 	wl->current_beacon = beacon;
 	wl->beacon0_uploaded = false;
 	wl->beacon1_uploaded = false;
+	spin_unlock_irqrestore(&wl->beacon_lock, flags);
+
 	ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger);
+
+	if (old_beacon)
+		dev_kfree_skb_any(old_beacon);
 }
 
 static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int)
@@ -4094,7 +4132,7 @@
 		if (conf->bssid)
 			memcpy(wl->bssid, conf->bssid, ETH_ALEN);
 		else
-			memset(wl->bssid, 0, ETH_ALEN);
+			eth_zero_addr(wl->bssid);
 	}
 
 	if (b43_status(dev) >= B43_STAT_INITIALIZED) {
@@ -4318,6 +4356,7 @@
 	mutex_unlock(&wl->mutex);
 	cancel_delayed_work_sync(&dev->periodic_work);
 	cancel_work_sync(&wl->tx_work);
+	b43_leds_stop(dev);
 	mutex_lock(&wl->mutex);
 	dev = wl->current_dev;
 	if (!dev || b43_status(dev) < B43_STAT_STARTED) {
@@ -4505,6 +4544,12 @@
 			unsupported = 1;
 		break;
 #endif
+#ifdef CPTCFG_B43_PHY_AC
+	case B43_PHYTYPE_AC:
+		if (phy_rev > 1)
+			unsupported = 1;
+		break;
+#endif
 	default:
 		unsupported = 1;
 	}
@@ -4601,6 +4646,10 @@
 		if (radio_id != 0x2064)
 			unsupported = 1;
 		break;
+	case B43_PHYTYPE_AC:
+		if (radio_id != 0x2069)
+			unsupported = 1;
+		break;
 	default:
 		B43_WARN_ON(1);
 	}
@@ -4770,7 +4819,7 @@
 	switch (dev->dev->bus_type) {
 #ifdef CPTCFG_B43_BCMA
 	case B43_BUS_BCMA:
-		bcma_core_pci_down(dev->dev->bdev->bus);
+		bcma_host_pci_down(dev->dev->bdev->bus);
 		break;
 #endif
 #ifdef CPTCFG_B43_SSB
@@ -4817,9 +4866,9 @@
 	switch (dev->dev->bus_type) {
 #ifdef CPTCFG_B43_BCMA
 	case B43_BUS_BCMA:
-		bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci[0],
+		bcma_host_pci_irq_ctl(dev->dev->bdev->bus,
 				      dev->dev->bdev, true);
-		bcma_core_pci_up(dev->dev->bdev->bus);
+		bcma_host_pci_up(dev->dev->bdev->bus);
 		break;
 #endif
 #ifdef CPTCFG_B43_SSB
@@ -5002,7 +5051,7 @@
 	wl->operating = false;
 
 	b43_adjust_opmode(dev);
-	memset(wl->mac_addr, 0, ETH_ALEN);
+	eth_zero_addr(wl->mac_addr);
 	b43_upload_card_macaddress(dev);
 
 	mutex_unlock(&wl->mutex);
@@ -5018,8 +5067,8 @@
 	/* Kill all old instance specific information to make sure
 	 * the card won't use it in the short timeframe between start
 	 * and mac80211 reconfiguring it. */
-	memset(wl->bssid, 0, ETH_ALEN);
-	memset(wl->mac_addr, 0, ETH_ALEN);
+	eth_zero_addr(wl->bssid);
+	eth_zero_addr(wl->mac_addr);
 	wl->filter_flags = 0;
 	wl->radiotap_enabled = false;
 	b43_qos_clear(wl);
@@ -5094,7 +5143,6 @@
 {
 	struct b43_wl *wl = hw_to_b43_wl(hw);
 
-	/* FIXME: add locking */
 	b43_update_templates(wl);
 
 	return 0;
@@ -5322,6 +5370,7 @@
 	case 0x432a: /* BCM4321 */
 	case 0x432d: /* BCM4322 */
 	case 0x4352: /* BCM43222 */
+	case 0x435a: /* BCM43228 */
 	case 0x4333: /* BCM4331 */
 	case 0x43a2: /* BCM4360 */
 	case 0x43b3: /* BCM4352 */
@@ -5584,6 +5633,7 @@
 	wl->hw = hw;
 	mutex_init(&wl->mutex);
 	spin_lock_init(&wl->hardirq_lock);
+	spin_lock_init(&wl->beacon_lock);
 	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);
diff --git a/drivers/net/wireless/b43/phy_ac.c b/drivers/net/wireless/b43/phy_ac.c
new file mode 100644
index 0000000..e75633d
--- /dev/null
+++ b/drivers/net/wireless/b43/phy_ac.c
@@ -0,0 +1,92 @@
+/*
+ * Broadcom B43 wireless driver
+ * IEEE 802.11ac AC-PHY support
+ *
+ * Copyright (c) 2015 Rafał Miłecki <zajec5@gmail.com>
+ *
+ * This program is free software; you can redistribute	it and/or modify it
+ * under  the terms of	the GNU General	 Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include "b43.h"
+#include "phy_ac.h"
+
+/**************************************************
+ * Basic PHY ops
+ **************************************************/
+
+static int b43_phy_ac_op_allocate(struct b43_wldev *dev)
+{
+	struct b43_phy_ac *phy_ac;
+
+	phy_ac = kzalloc(sizeof(*phy_ac), GFP_KERNEL);
+	if (!phy_ac)
+		return -ENOMEM;
+	dev->phy.ac = phy_ac;
+
+	return 0;
+}
+
+static void b43_phy_ac_op_free(struct b43_wldev *dev)
+{
+	struct b43_phy *phy = &dev->phy;
+	struct b43_phy_ac *phy_ac = phy->ac;
+
+	kfree(phy_ac);
+	phy->ac = NULL;
+}
+
+static void b43_phy_ac_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
+				  u16 set)
+{
+	b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg);
+	b43_write16(dev, B43_MMIO_PHY_DATA,
+		    (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set);
+}
+
+static u16 b43_phy_ac_op_radio_read(struct b43_wldev *dev, u16 reg)
+{
+	b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg);
+	return b43_read16(dev, B43_MMIO_RADIO24_DATA);
+}
+
+static void b43_phy_ac_op_radio_write(struct b43_wldev *dev, u16 reg,
+				      u16 value)
+{
+	b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg);
+	b43_write16(dev, B43_MMIO_RADIO24_DATA, value);
+}
+
+static unsigned int b43_phy_ac_op_get_default_chan(struct b43_wldev *dev)
+{
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+		return 11;
+	return 36;
+}
+
+static enum b43_txpwr_result
+b43_phy_ac_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi)
+{
+	return B43_TXPWR_RES_DONE;
+}
+
+static void b43_phy_ac_op_adjust_txpower(struct b43_wldev *dev)
+{
+}
+
+/**************************************************
+ * PHY ops struct
+ **************************************************/
+
+const struct b43_phy_operations b43_phyops_ac = {
+	.allocate		= b43_phy_ac_op_allocate,
+	.free			= b43_phy_ac_op_free,
+	.phy_maskset		= b43_phy_ac_op_maskset,
+	.radio_read		= b43_phy_ac_op_radio_read,
+	.radio_write		= b43_phy_ac_op_radio_write,
+	.get_default_chan	= b43_phy_ac_op_get_default_chan,
+	.recalc_txpower		= b43_phy_ac_op_recalc_txpower,
+	.adjust_txpower		= b43_phy_ac_op_adjust_txpower,
+};
diff --git a/drivers/net/wireless/b43/phy_ac.h b/drivers/net/wireless/b43/phy_ac.h
new file mode 100644
index 0000000..d1ca79e
--- /dev/null
+++ b/drivers/net/wireless/b43/phy_ac.h
@@ -0,0 +1,38 @@
+#ifndef B43_PHY_AC_H_
+#define B43_PHY_AC_H_
+
+#include "phy_common.h"
+
+#define B43_PHY_AC_BBCFG			0x001
+#define  B43_PHY_AC_BBCFG_RSTCCA		0x4000	/* Reset CCA */
+#define B43_PHY_AC_BANDCTL			0x003	/* Band control */
+#define  B43_PHY_AC_BANDCTL_5GHZ		0x0001
+#define B43_PHY_AC_TABLE_ID			0x00d
+#define B43_PHY_AC_TABLE_OFFSET			0x00e
+#define B43_PHY_AC_TABLE_DATA1			0x00f
+#define B43_PHY_AC_TABLE_DATA2			0x010
+#define B43_PHY_AC_TABLE_DATA3			0x011
+#define B43_PHY_AC_CLASSCTL			0x140	/* Classifier control */
+#define  B43_PHY_AC_CLASSCTL_CCKEN		0x0001	/* CCK enable */
+#define  B43_PHY_AC_CLASSCTL_OFDMEN		0x0002	/* OFDM enable */
+#define  B43_PHY_AC_CLASSCTL_WAITEDEN		0x0004	/* Waited enable */
+#define B43_PHY_AC_BW1A				0x371
+#define B43_PHY_AC_BW2				0x372
+#define B43_PHY_AC_BW3				0x373
+#define B43_PHY_AC_BW4				0x374
+#define B43_PHY_AC_BW5				0x375
+#define B43_PHY_AC_BW6				0x376
+#define B43_PHY_AC_RFCTL_CMD			0x408
+#define B43_PHY_AC_C1_CLIP			0x6d4
+#define  B43_PHY_AC_C1_CLIP_DIS			0x4000
+#define B43_PHY_AC_C2_CLIP			0x8d4
+#define  B43_PHY_AC_C2_CLIP_DIS			0x4000
+#define B43_PHY_AC_C3_CLIP			0xad4
+#define  B43_PHY_AC_C3_CLIP_DIS			0x4000
+
+struct b43_phy_ac {
+};
+
+extern const struct b43_phy_operations b43_phyops_ac;
+
+#endif /* B43_PHY_AC_H_ */
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c
index 9e59f8b..f9ddac3 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/b43/phy_common.c
@@ -33,6 +33,7 @@
 #include "phy_lp.h"
 #include "phy_ht.h"
 #include "phy_lcn.h"
+#include "phy_ac.h"
 #include "b43.h"
 #include "main.h"
 
@@ -70,6 +71,11 @@
 		phy->ops = &b43_phyops_lcn;
 #endif
 		break;
+	case B43_PHYTYPE_AC:
+#ifdef CPTCFG_B43_PHY_AC
+		phy->ops = &b43_phyops_ac;
+#endif
+		break;
 	}
 	if (B43_WARN_ON(!phy->ops))
 		return -ENODEV;
@@ -572,7 +578,8 @@
 	u32 tmp;
 
 	WARN_ON(dev->phy.type != B43_PHYTYPE_N &&
-		dev->phy.type != B43_PHYTYPE_HT);
+		dev->phy.type != B43_PHYTYPE_HT &&
+		dev->phy.type != B43_PHYTYPE_AC);
 
 	switch (dev->dev->bus_type) {
 #ifdef CPTCFG_B43_BCMA
diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h
index 910cf70..2d2f5ea 100644
--- a/drivers/net/wireless/b43/phy_common.h
+++ b/drivers/net/wireless/b43/phy_common.h
@@ -222,6 +222,8 @@
 		struct b43_phy_ht *ht;
 		/* LCN-PHY specific information */
 		struct b43_phy_lcn *lcn;
+		/* AC-PHY specific information */
+		struct b43_phy_ac *ac;
 	};
 
 	/* Band support flags. */
diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c
index e598d48..8aa81a2 100644
--- a/drivers/net/wireless/b43legacy/dma.c
+++ b/drivers/net/wireless/b43legacy/dma.c
@@ -427,7 +427,7 @@
 					 bool dma_to_device)
 {
 	if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr)))
-		return 1;
+		return true;
 
 	switch (ring->type) {
 	case B43legacy_DMA_30BIT:
@@ -441,13 +441,13 @@
 	}
 
 	/* The address is OK. */
-	return 0;
+	return false;
 
 address_error:
 	/* We can't support this address. Unmap it again. */
 	unmap_descbuffer(ring, addr, buffersize, dma_to_device);
 
-	return 1;
+	return true;
 }
 
 static int setup_rx_descbuffer(struct b43legacy_dmaring *ring,
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index 8e53e0f..49f3316 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -86,7 +86,7 @@
 static const struct ssb_device_id b43legacy_ssb_tbl[] = {
 	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 2),
 	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 4),
-	SSB_DEVTABLE_END
+	{},
 };
 MODULE_DEVICE_TABLE(ssb, b43legacy_ssb_tbl);
 
@@ -2866,7 +2866,7 @@
 		if (conf->bssid)
 			memcpy(wl->bssid, conf->bssid, ETH_ALEN);
 		else
-			memset(wl->bssid, 0, ETH_ALEN);
+			eth_zero_addr(wl->bssid);
 	}
 
 	if (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) {
@@ -3470,7 +3470,7 @@
 
 	spin_lock_irqsave(&wl->irq_lock, flags);
 	b43legacy_adjust_opmode(dev);
-	memset(wl->mac_addr, 0, ETH_ALEN);
+	eth_zero_addr(wl->mac_addr);
 	b43legacy_upload_card_macaddress(dev);
 	spin_unlock_irqrestore(&wl->irq_lock, flags);
 
@@ -3487,8 +3487,8 @@
 	/* Kill all old instance specific information to make sure
 	 * the card won't use it in the short timeframe between start
 	 * and mac80211 reconfiguring it. */
-	memset(wl->bssid, 0, ETH_ALEN);
-	memset(wl->mac_addr, 0, ETH_ALEN);
+	eth_zero_addr(wl->bssid);
+	eth_zero_addr(wl->mac_addr);
 	wl->filter_flags = 0;
 	wl->beacon0_uploaded = false;
 	wl->beacon1_uploaded = false;
diff --git a/drivers/net/wireless/b43legacy/radio.c b/drivers/net/wireless/b43legacy/radio.c
index 8961776..9501420 100644
--- a/drivers/net/wireless/b43legacy/radio.c
+++ b/drivers/net/wireless/b43legacy/radio.c
@@ -1743,25 +1743,6 @@
 	return value;
 }
 
-void b43legacy_radio_set_tx_iq(struct b43legacy_wldev *dev)
-{
-	static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 };
-	static const u8 data_low[5]  = { 0x00, 0x01, 0x05, 0x06, 0x0A };
-	u16 tmp = b43legacy_radio_read16(dev, 0x001E);
-	int i;
-	int j;
-
-	for (i = 0; i < 5; i++) {
-		for (j = 0; j < 5; j++) {
-			if (tmp == (data_high[i] | data_low[j])) {
-				b43legacy_phy_write(dev, 0x0069, (i - j) << 8 |
-						    0x00C0);
-				return;
-			}
-		}
-	}
-}
-
 int b43legacy_radio_selectchannel(struct b43legacy_wldev *dev,
 				  u8 channel,
 				  int synthetic_pu_workaround)
diff --git a/drivers/net/wireless/b43legacy/radio.h b/drivers/net/wireless/b43legacy/radio.h
index bccb3d7..dd2976d 100644
--- a/drivers/net/wireless/b43legacy/radio.h
+++ b/drivers/net/wireless/b43legacy/radio.h
@@ -92,7 +92,6 @@
 void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val);
 void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev);
 
-void b43legacy_radio_set_tx_iq(struct b43legacy_wldev *dev);
 u16 b43legacy_radio_calibrationvalue(struct b43legacy_wldev *dev);
 
 #endif /* B43legacy_RADIO_H_ */
diff --git a/drivers/net/wireless/b43legacy/rfkill.c b/drivers/net/wireless/b43legacy/rfkill.c
index c4559bc..7c1bdbc 100644
--- a/drivers/net/wireless/b43legacy/rfkill.c
+++ b/drivers/net/wireless/b43legacy/rfkill.c
@@ -32,7 +32,7 @@
 	if (dev->dev->id.revision >= 3) {
 		if (!(b43legacy_read32(dev, B43legacy_MMIO_RADIO_HWENABLED_HI)
 		      & B43legacy_MMIO_RADIO_HWENABLED_HI_MASK))
-			return 1;
+			return true;
 	} else {
 		/* To prevent CPU fault on PPC, do not read a register
 		 * unless the interface is started; however, on resume
@@ -40,12 +40,12 @@
 		 * that happens, unconditionally return TRUE.
 		 */
 		if (b43legacy_status(dev) < B43legacy_STAT_STARTED)
-			return 1;
+			return true;
 		if (b43legacy_read16(dev, B43legacy_MMIO_RADIO_HWENABLED_LO)
 		    & B43legacy_MMIO_RADIO_HWENABLED_LO_MASK)
-			return 1;
+			return true;
 	}
-	return 0;
+	return false;
 }
 
 /* The poll callback for the hardware button. */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
index 3c06e93..9b508bd 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -29,6 +29,7 @@
 #include <linux/mmc/host.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/brcmfmac-sdio.h>
+#include <linux/pm_runtime.h>
 #include <linux/suspend.h>
 #include <linux/errno.h>
 #include <linux/module.h>
@@ -58,6 +59,14 @@
 #define BRCMF_DEFAULT_TXGLOM_SIZE	32  /* max tx frames in glom chain */
 #define BRCMF_DEFAULT_RXGLOM_SIZE	32  /* max rx frames in glom chain */
 
+struct brcmf_sdiod_freezer {
+	atomic_t freezing;
+	atomic_t thread_count;
+	u32 frozen_count;
+	wait_queue_head_t thread_freeze;
+	struct completion resumed;
+};
+
 static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE;
 module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0);
 MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]");
@@ -97,25 +106,6 @@
 {
 }
 
-static bool brcmf_sdiod_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
-{
-	bool is_err = false;
-#ifdef CONFIG_PM_SLEEP
-	is_err = atomic_read(&sdiodev->suspend);
-#endif
-	return is_err;
-}
-
-static void brcmf_sdiod_pm_resume_wait(struct brcmf_sdio_dev *sdiodev,
-				       wait_queue_head_t *wq)
-{
-#ifdef CONFIG_PM_SLEEP
-	int retry = 0;
-	while (atomic_read(&sdiodev->suspend) && retry++ != 30)
-		wait_event_timeout(*wq, false, HZ/100);
-#endif
-}
-
 int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
 {
 	int ret = 0;
@@ -216,6 +206,30 @@
 	return 0;
 }
 
+void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
+			      enum brcmf_sdiod_state state)
+{
+	if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM ||
+	    state == sdiodev->state)
+		return;
+
+	brcmf_dbg(TRACE, "%d -> %d\n", sdiodev->state, state);
+	switch (sdiodev->state) {
+	case BRCMF_SDIOD_DATA:
+		/* any other state means bus interface is down */
+		brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN);
+		break;
+	case BRCMF_SDIOD_DOWN:
+		/* transition from DOWN to DATA means bus interface is up */
+		if (state == BRCMF_SDIOD_DATA)
+			brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_UP);
+		break;
+	default:
+		break;
+	}
+	sdiodev->state = state;
+}
+
 static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func,
 					uint regaddr, u8 byte)
 {
@@ -244,10 +258,6 @@
 	brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
 		  write, fn, addr, regsz);
 
-	brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_word_wait);
-	if (brcmf_sdiod_pm_resume_error(sdiodev))
-		return -EIO;
-
 	/* only allow byte access on F0 */
 	if (WARN_ON(regsz > 1 && !fn))
 		return -EINVAL;
@@ -299,7 +309,7 @@
 	s32 retry = 0;
 	int ret;
 
-	if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM)
+	if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
 		return -ENOMEDIUM;
 
 	/*
@@ -325,7 +335,7 @@
 		 retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
 
 	if (ret == -ENOMEDIUM)
-		brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM);
+		brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
 	else if (ret != 0) {
 		/*
 		 * SleepCSR register access can fail when
@@ -348,7 +358,7 @@
 	int err = 0, i;
 	u8 addr[3];
 
-	if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM)
+	if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
 		return -ENOMEDIUM;
 
 	addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK;
@@ -462,10 +472,6 @@
 	unsigned int req_sz;
 	int err;
 
-	brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
-	if (brcmf_sdiod_pm_resume_error(sdiodev))
-		return -EIO;
-
 	/* Single skb use the standard mmc interface */
 	req_sz = pkt->len + 3;
 	req_sz &= (uint)~3;
@@ -481,7 +487,7 @@
 		err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr,
 				  req_sz);
 	if (err == -ENOMEDIUM)
-		brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM);
+		brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
 	return err;
 }
 
@@ -516,10 +522,6 @@
 	if (!pktlist->qlen)
 		return -EINVAL;
 
-	brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
-	if (brcmf_sdiod_pm_resume_error(sdiodev))
-		return -EIO;
-
 	target_list = pktlist;
 	/* for host with broken sg support, prepare a page aligned list */
 	__skb_queue_head_init(&local_list);
@@ -620,8 +622,7 @@
 
 		ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error;
 		if (ret == -ENOMEDIUM) {
-			brcmf_bus_change_state(sdiodev->bus_if,
-					       BRCMF_BUS_NOMEDIUM);
+			brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
 			break;
 		} else if (ret != 0) {
 			brcmf_err("CMD53 sg block %s failed %d\n",
@@ -903,6 +904,87 @@
 	sdiodev->txglomsz = brcmf_sdiod_txglomsz;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev)
+{
+	sdiodev->freezer = kzalloc(sizeof(*sdiodev->freezer), GFP_KERNEL);
+	if (!sdiodev->freezer)
+		return -ENOMEM;
+	atomic_set(&sdiodev->freezer->thread_count, 0);
+	atomic_set(&sdiodev->freezer->freezing, 0);
+	init_waitqueue_head(&sdiodev->freezer->thread_freeze);
+	init_completion(&sdiodev->freezer->resumed);
+	return 0;
+}
+
+static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
+{
+	if (sdiodev->freezer) {
+		WARN_ON(atomic_read(&sdiodev->freezer->freezing));
+		kfree(sdiodev->freezer);
+	}
+}
+
+static int brcmf_sdiod_freezer_on(struct brcmf_sdio_dev *sdiodev)
+{
+	atomic_t *expect = &sdiodev->freezer->thread_count;
+	int res = 0;
+
+	sdiodev->freezer->frozen_count = 0;
+	reinit_completion(&sdiodev->freezer->resumed);
+	atomic_set(&sdiodev->freezer->freezing, 1);
+	brcmf_sdio_trigger_dpc(sdiodev->bus);
+	wait_event(sdiodev->freezer->thread_freeze,
+		   atomic_read(expect) == sdiodev->freezer->frozen_count);
+	sdio_claim_host(sdiodev->func[1]);
+	res = brcmf_sdio_sleep(sdiodev->bus, true);
+	sdio_release_host(sdiodev->func[1]);
+	return res;
+}
+
+static void brcmf_sdiod_freezer_off(struct brcmf_sdio_dev *sdiodev)
+{
+	sdio_claim_host(sdiodev->func[1]);
+	brcmf_sdio_sleep(sdiodev->bus, false);
+	sdio_release_host(sdiodev->func[1]);
+	atomic_set(&sdiodev->freezer->freezing, 0);
+	complete_all(&sdiodev->freezer->resumed);
+}
+
+bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev)
+{
+	return atomic_read(&sdiodev->freezer->freezing);
+}
+
+void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev)
+{
+	if (!brcmf_sdiod_freezing(sdiodev))
+		return;
+	sdiodev->freezer->frozen_count++;
+	wake_up(&sdiodev->freezer->thread_freeze);
+	wait_for_completion(&sdiodev->freezer->resumed);
+}
+
+void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev)
+{
+	atomic_inc(&sdiodev->freezer->thread_count);
+}
+
+void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev)
+{
+	atomic_dec(&sdiodev->freezer->thread_count);
+}
+#else
+static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev)
+{
+	return 0;
+}
+
+static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
+{
+}
+#endif /* CONFIG_PM_SLEEP */
+
 static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
 {
 	if (sdiodev->bus) {
@@ -910,6 +992,8 @@
 		sdiodev->bus = NULL;
 	}
 
+	brcmf_sdiod_freezer_detach(sdiodev);
+
 	/* Disable Function 2 */
 	sdio_claim_host(sdiodev->func[2]);
 	sdio_disable_func(sdiodev->func[2]);
@@ -923,6 +1007,7 @@
 	sg_free_table(&sdiodev->sgtable);
 	sdiodev->sbwad = 0;
 
+	pm_runtime_allow(sdiodev->func[1]->card->host->parent);
 	return 0;
 }
 
@@ -981,13 +1066,17 @@
 	 */
 	brcmf_sdiod_sgtable_alloc(sdiodev);
 
+	ret = brcmf_sdiod_freezer_attach(sdiodev);
+	if (ret)
+		goto out;
+
 	/* try to attach to the target device */
 	sdiodev->bus = brcmf_sdio_probe(sdiodev);
 	if (!sdiodev->bus) {
 		ret = -ENODEV;
 		goto out;
 	}
-
+	pm_runtime_forbid(host->parent);
 out:
 	if (ret)
 		brcmf_sdiod_remove(sdiodev);
@@ -996,18 +1085,22 @@
 }
 
 #define BRCMF_SDIO_DEVICE(dev_id)	\
-	{SDIO_DEVICE(BRCM_SDIO_VENDOR_ID_BROADCOM, dev_id)}
+	{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)}
 
 /* devices we support, null terminated */
 static const struct sdio_device_id brcmf_sdmmc_ids[] = {
-	BRCMF_SDIO_DEVICE(BRCM_SDIO_43143_DEVICE_ID),
-	BRCMF_SDIO_DEVICE(BRCM_SDIO_43241_DEVICE_ID),
-	BRCMF_SDIO_DEVICE(BRCM_SDIO_4329_DEVICE_ID),
-	BRCMF_SDIO_DEVICE(BRCM_SDIO_4330_DEVICE_ID),
-	BRCMF_SDIO_DEVICE(BRCM_SDIO_4334_DEVICE_ID),
-	BRCMF_SDIO_DEVICE(BRCM_SDIO_43362_DEVICE_ID),
-	BRCMF_SDIO_DEVICE(BRCM_SDIO_4335_4339_DEVICE_ID),
-	BRCMF_SDIO_DEVICE(BRCM_SDIO_4354_DEVICE_ID),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345),
+	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
 	{ /* end: all zeroes */ }
 };
 MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
@@ -1070,13 +1163,11 @@
 	 */
 	if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) &&
 	    ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) ||
-	     (sdiodev->pdata->oob_irq_supported)))
+	     (sdiodev->pdata && sdiodev->pdata->oob_irq_supported)))
 		bus_if->wowl_supported = true;
 #endif
 
-	atomic_set(&sdiodev->suspend, false);
-	init_waitqueue_head(&sdiodev->request_word_wait);
-	init_waitqueue_head(&sdiodev->request_buffer_wait);
+	brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN);
 
 	brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
 	err = brcmf_sdiod_probe(sdiodev);
@@ -1107,7 +1198,7 @@
 	brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
 	brcmf_dbg(SDIO, "Function: %d\n", func->num);
 
-	if (func->num != 1 && func->num != 2)
+	if (func->num != 1)
 		return;
 
 	bus_if = dev_get_drvdata(&func->dev);
@@ -1138,13 +1229,22 @@
 #ifdef CONFIG_PM_SLEEP
 static int brcmf_ops_sdio_suspend(struct device *dev)
 {
-	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
-	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+	struct sdio_func *func;
+	struct brcmf_bus *bus_if;
+	struct brcmf_sdio_dev *sdiodev;
 	mmc_pm_flag_t sdio_flags;
 
-	brcmf_dbg(SDIO, "Enter\n");
+	func = container_of(dev, struct sdio_func, dev);
+	brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
+	if (func->num != SDIO_FUNC_1)
+		return 0;
 
-	atomic_set(&sdiodev->suspend, true);
+
+	bus_if = dev_get_drvdata(dev);
+	sdiodev = bus_if->bus_priv.sdio;
+
+	brcmf_sdiod_freezer_on(sdiodev);
+	brcmf_sdio_wd_timer(sdiodev->bus, 0);
 
 	if (sdiodev->wowl_enabled) {
 		sdio_flags = MMC_PM_KEEP_POWER;
@@ -1155,9 +1255,6 @@
 		if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags))
 			brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
 	}
-
-	brcmf_sdio_wd_timer(sdiodev->bus, 0);
-
 	return 0;
 }
 
@@ -1165,12 +1262,13 @@
 {
 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+	struct sdio_func *func = container_of(dev, struct sdio_func, dev);
 
-	brcmf_dbg(SDIO, "Enter\n");
-	if (sdiodev->pdata->oob_irq_supported)
-		disable_irq_wake(sdiodev->pdata->oob_irq_nr);
-	brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS);
-	atomic_set(&sdiodev->suspend, false);
+	brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
+	if (func->num != SDIO_FUNC_2)
+		return 0;
+
+	brcmf_sdiod_freezer_off(sdiodev);
 	return 0;
 }
 
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/brcm80211/brcmfmac/bus.h
index ba6eb69..f04e5fe 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h
@@ -33,11 +33,8 @@
 
 /* The level of bus communication with the dongle */
 enum brcmf_bus_state {
-	BRCMF_BUS_UNKNOWN,	/* Not determined yet */
-	BRCMF_BUS_NOMEDIUM,	/* No medium access to dongle */
 	BRCMF_BUS_DOWN,		/* Not ready for frame transfers */
-	BRCMF_BUS_LOAD,		/* Download access only (CPU reset) */
-	BRCMF_BUS_DATA		/* Ready for frame transfers */
+	BRCMF_BUS_UP		/* Ready for frame transfers */
 };
 
 /* The level of bus communication with the dongle */
@@ -188,22 +185,6 @@
 		bus->ops->wowl_config(bus->dev, enabled);
 }
 
-static inline bool brcmf_bus_ready(struct brcmf_bus *bus)
-{
-	return bus->state == BRCMF_BUS_LOAD || bus->state == BRCMF_BUS_DATA;
-}
-
-static inline void brcmf_bus_change_state(struct brcmf_bus *bus,
-					  enum brcmf_bus_state new_state)
-{
-	/* NOMEDIUM is permanent */
-	if (bus->state == BRCMF_BUS_NOMEDIUM)
-		return;
-
-	brcmf_dbg(TRACE, "%d -> %d\n", bus->state, new_state);
-	bus->state = new_state;
-}
-
 /*
  * interface functions from common layer
  */
@@ -226,6 +207,9 @@
 /* Notify the bus has transferred the tx packet to firmware */
 void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success);
 
+/* Configure the "global" bus state used by upper layers */
+void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state);
+
 int brcmf_bus_start(struct device *dev);
 s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len);
 void brcmf_bus_add_txhdrlen(struct device *dev, uint len);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
index 3aecc5f..8a15ebb 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
@@ -38,6 +38,7 @@
 #include "proto.h"
 #include "vendor.h"
 #include "bus.h"
+#include "common.h"
 
 #define BRCMF_SCAN_IE_LEN_MAX		2048
 #define BRCMF_PNO_VERSION		2
@@ -452,16 +453,16 @@
 }
 
 static int
-send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key)
+send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
 {
 	int err;
 	struct brcmf_wsec_key_le key_le;
 
 	convert_key_from_CPU(key, &key_le);
 
-	brcmf_netdev_wait_pend8021x(ndev);
+	brcmf_netdev_wait_pend8021x(ifp);
 
-	err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le,
+	err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
 					sizeof(key_le));
 
 	if (err)
@@ -624,6 +625,7 @@
 
 static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
 						     const char *name,
+						     unsigned char name_assign_type,
 						     enum nl80211_iftype type,
 						     u32 *flags,
 						     struct vif_params *params)
@@ -647,7 +649,7 @@
 	case NL80211_IFTYPE_P2P_CLIENT:
 	case NL80211_IFTYPE_P2P_GO:
 	case NL80211_IFTYPE_P2P_DEVICE:
-		wdev = brcmf_p2p_add_vif(wiphy, name, type, flags, params);
+		wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
 		if (!IS_ERR(wdev))
 			brcmf_cfg80211_update_proto_addr_mode(wdev);
 		return wdev;
@@ -699,7 +701,7 @@
 		/* Do a scan abort to stop the driver's scan engine */
 		brcmf_dbg(SCAN, "ABORT scan in firmware\n");
 		memset(&params_le, 0, sizeof(params_le));
-		memset(params_le.bssid, 0xFF, ETH_ALEN);
+		eth_broadcast_addr(params_le.bssid);
 		params_le.bss_type = DOT11_BSSTYPE_ANY;
 		params_le.scan_type = 0;
 		params_le.channel_num = cpu_to_le32(1);
@@ -865,7 +867,7 @@
 	char *ptr;
 	struct brcmf_ssid_le ssid_le;
 
-	memset(params_le->bssid, 0xFF, ETH_ALEN);
+	eth_broadcast_addr(params_le->bssid);
 	params_le->bss_type = DOT11_BSSTYPE_ANY;
 	params_le->scan_type = 0;
 	params_le->channel_num = 0;
@@ -1049,10 +1051,6 @@
 	if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
 		vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
 
-	/* Arm scan timeout timer */
-	mod_timer(&cfg->escan_timeout, jiffies +
-			WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
-
 	escan_req = false;
 	if (request) {
 		/* scan bss */
@@ -1111,12 +1109,14 @@
 		}
 	}
 
+	/* Arm scan timeout timer */
+	mod_timer(&cfg->escan_timeout, jiffies +
+			WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
+
 	return 0;
 
 scan_out:
 	clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
-	if (timer_pending(&cfg->escan_timeout))
-		del_timer_sync(&cfg->escan_timeout);
 	cfg->scan_request = NULL;
 	return err;
 }
@@ -1228,7 +1228,25 @@
 	memset(prof, 0, sizeof(*prof));
 }
 
-static void brcmf_link_down(struct brcmf_cfg80211_vif *vif)
+static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
+{
+	u16 reason;
+
+	switch (e->event_code) {
+	case BRCMF_E_DEAUTH:
+	case BRCMF_E_DEAUTH_IND:
+	case BRCMF_E_DISASSOC_IND:
+		reason = e->reason;
+		break;
+	case BRCMF_E_LINK:
+	default:
+		reason = 0;
+		break;
+	}
+	return reason;
+}
+
+static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
 {
 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
 	s32 err = 0;
@@ -1243,7 +1261,8 @@
 			brcmf_err("WLC_DISASSOC failed (%d)\n", err);
 		}
 		clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
-		cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0, GFP_KERNEL);
+		cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
+				      GFP_KERNEL);
 
 	}
 	clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
@@ -1355,8 +1374,8 @@
 				   BRCMF_ASSOC_PARAMS_FIXED_SIZE;
 		memcpy(profile->bssid, params->bssid, ETH_ALEN);
 	} else {
-		memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
-		memset(profile->bssid, 0, ETH_ALEN);
+		eth_broadcast_addr(join_params.params_le.bssid);
+		eth_zero_addr(profile->bssid);
 	}
 
 	/* Channel */
@@ -1413,7 +1432,7 @@
 	if (!check_vif_up(ifp->vif))
 		return -EIO;
 
-	brcmf_link_down(ifp->vif);
+	brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
 
 	brcmf_dbg(TRACE, "Exit\n");
 
@@ -1670,7 +1689,7 @@
 	brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
 		  key.len, key.index, key.algo);
 	brcmf_dbg(CONN, "key \"%s\"\n", key.data);
-	err = send_key_to_dongle(ndev, &key);
+	err = send_key_to_dongle(netdev_priv(ndev), &key);
 	if (err)
 		return err;
 
@@ -1830,7 +1849,7 @@
 	if (sme->bssid)
 		memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
 	else
-		memset(&ext_join_params->assoc_le.bssid, 0xFF, ETH_ALEN);
+		eth_broadcast_addr(ext_join_params->assoc_le.bssid);
 
 	if (cfg->channel) {
 		ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
@@ -1875,7 +1894,7 @@
 	if (sme->bssid)
 		memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
 	else
-		memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
+		eth_broadcast_addr(join_params.params_le.bssid);
 
 	if (cfg->channel) {
 		join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
@@ -2052,7 +2071,7 @@
 	/* check for key index change */
 	if (key.len == 0) {
 		/* key delete */
-		err = send_key_to_dongle(ndev, &key);
+		err = send_key_to_dongle(ifp, &key);
 		if (err)
 			brcmf_err("key delete error (%d)\n", err);
 	} else {
@@ -2108,7 +2127,7 @@
 			brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
 			return -EINVAL;
 		}
-		err = send_key_to_dongle(ndev, &key);
+		err = send_key_to_dongle(ifp, &key);
 		if (err)
 			brcmf_err("wsec_key error (%d)\n", err);
 	}
@@ -2121,7 +2140,7 @@
 		    struct key_params *params)
 {
 	struct brcmf_if *ifp = netdev_priv(ndev);
-	struct brcmf_wsec_key key;
+	struct brcmf_wsec_key *key;
 	s32 val;
 	s32 wsec;
 	s32 err = 0;
@@ -2132,54 +2151,62 @@
 	if (!check_vif_up(ifp->vif))
 		return -EIO;
 
+	if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
+		/* we ignore this key index in this case */
+		brcmf_err("invalid key index (%d)\n", key_idx);
+		return -EINVAL;
+	}
+
 	if (mac_addr &&
 		(params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
 		(params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
 		brcmf_dbg(TRACE, "Exit");
 		return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
 	}
-	memset(&key, 0, sizeof(key));
 
-	key.len = (u32) params->key_len;
-	key.index = (u32) key_idx;
+	key = &ifp->vif->profile.key[key_idx];
+	memset(key, 0, sizeof(*key));
 
-	if (key.len > sizeof(key.data)) {
-		brcmf_err("Too long key length (%u)\n", key.len);
+	if (params->key_len > sizeof(key->data)) {
+		brcmf_err("Too long key length (%u)\n", params->key_len);
 		err = -EINVAL;
 		goto done;
 	}
-	memcpy(key.data, params->key, key.len);
+	key->len = params->key_len;
+	key->index = key_idx;
 
-	key.flags = BRCMF_PRIMARY_KEY;
+	memcpy(key->data, params->key, key->len);
+
+	key->flags = BRCMF_PRIMARY_KEY;
 	switch (params->cipher) {
 	case WLAN_CIPHER_SUITE_WEP40:
-		key.algo = CRYPTO_ALGO_WEP1;
+		key->algo = CRYPTO_ALGO_WEP1;
 		val = WEP_ENABLED;
 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
 		break;
 	case WLAN_CIPHER_SUITE_WEP104:
-		key.algo = CRYPTO_ALGO_WEP128;
+		key->algo = CRYPTO_ALGO_WEP128;
 		val = WEP_ENABLED;
 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
 		break;
 	case WLAN_CIPHER_SUITE_TKIP:
 		if (!brcmf_is_apmode(ifp->vif)) {
 			brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
-			memcpy(keybuf, &key.data[24], sizeof(keybuf));
-			memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
-			memcpy(&key.data[16], keybuf, sizeof(keybuf));
+			memcpy(keybuf, &key->data[24], sizeof(keybuf));
+			memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
+			memcpy(&key->data[16], keybuf, sizeof(keybuf));
 		}
-		key.algo = CRYPTO_ALGO_TKIP;
+		key->algo = CRYPTO_ALGO_TKIP;
 		val = TKIP_ENABLED;
 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
 		break;
 	case WLAN_CIPHER_SUITE_AES_CMAC:
-		key.algo = CRYPTO_ALGO_AES_CCM;
+		key->algo = CRYPTO_ALGO_AES_CCM;
 		val = AES_ENABLED;
 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
-		key.algo = CRYPTO_ALGO_AES_CCM;
+		key->algo = CRYPTO_ALGO_AES_CCM;
 		val = AES_ENABLED;
 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
 		break;
@@ -2189,7 +2216,7 @@
 		goto done;
 	}
 
-	err = send_key_to_dongle(ndev, &key);
+	err = send_key_to_dongle(ifp, key);
 	if (err)
 		goto done;
 
@@ -2222,9 +2249,8 @@
 	if (!check_vif_up(ifp->vif))
 		return -EIO;
 
-	if (key_idx >= DOT11_MAX_DEFAULT_KEYS) {
+	if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
 		/* we ignore this key index in this case */
-		brcmf_err("invalid key index (%d)\n", key_idx);
 		return -EINVAL;
 	}
 
@@ -2237,7 +2263,7 @@
 	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
 
 	/* Set the new key/index */
-	err = send_key_to_dongle(ndev, &key);
+	err = send_key_to_dongle(ifp, &key);
 
 	brcmf_dbg(TRACE, "Exit\n");
 	return err;
@@ -2305,6 +2331,39 @@
 	return -EOPNOTSUPP;
 }
 
+static void
+brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
+{
+	s32 err;
+	u8 key_idx;
+	struct brcmf_wsec_key *key;
+	s32 wsec;
+
+	for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
+		key = &ifp->vif->profile.key[key_idx];
+		if ((key->algo == CRYPTO_ALGO_WEP1) ||
+		    (key->algo == CRYPTO_ALGO_WEP128))
+			break;
+	}
+	if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
+		return;
+
+	err = send_key_to_dongle(ifp, key);
+	if (err) {
+		brcmf_err("Setting WEP key failed (%d)\n", err);
+		return;
+	}
+	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
+	if (err) {
+		brcmf_err("get wsec error (%d)\n", err);
+		return;
+	}
+	wsec |= WEP_ENABLED;
+	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
+	if (err)
+		brcmf_err("set wsec error (%d)\n", err);
+}
+
 static s32
 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
 			   const u8 *mac, struct station_info *sinfo)
@@ -2333,10 +2392,10 @@
 			brcmf_err("GET STA INFO failed, %d\n", err);
 			goto done;
 		}
-		sinfo->filled = STATION_INFO_INACTIVE_TIME;
+		sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
 		sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
 		if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
-			sinfo->filled |= STATION_INFO_CONNECTED_TIME;
+			sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
 			sinfo->connected_time = le32_to_cpu(sta_info_le.in);
 		}
 		brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n",
@@ -2354,7 +2413,7 @@
 			brcmf_err("Could not get rate (%d)\n", err);
 			goto done;
 		} else {
-			sinfo->filled |= STATION_INFO_TX_BITRATE;
+			sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
 			sinfo->txrate.legacy = rate * 5;
 			brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2);
 		}
@@ -2369,7 +2428,7 @@
 				goto done;
 			} else {
 				rssi = le32_to_cpu(scb_val.val);
-				sinfo->filled |= STATION_INFO_SIGNAL;
+				sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
 				sinfo->signal = rssi;
 				brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
 			}
@@ -2396,7 +2455,7 @@
 				brcmf_dbg(CONN, "DTIM peroid %d\n",
 					  dtim_period);
 			}
-			sinfo->filled |= STATION_INFO_BSS_PARAM;
+			sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
 		}
 	} else
 		err = -EPERM;
@@ -2999,7 +3058,7 @@
 			 * disassociate from AP to save power while system is
 			 * in suspended state
 			 */
-			brcmf_link_down(vif);
+			brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
 			/* Make sure WPA_Supplicant receives all the event
 			 * generated due to DISASSOC call to the fw to keep
 			 * the state fw and WPA_Supplicant state consistent
@@ -3695,17 +3754,12 @@
 brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
 {
 
-	__le32 iecount_le;
-	__le32 pktflag_le;
-
 	strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
 	iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
 
-	iecount_le = cpu_to_le32(1);
-	memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le));
+	put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
 
-	pktflag_le = cpu_to_le32(pktflag);
-	memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le));
+	put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
 
 	memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
 
@@ -3924,6 +3978,7 @@
 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 	struct brcmf_if *ifp = netdev_priv(ndev);
 	const struct brcmf_tlv *ssid_ie;
+	const struct brcmf_tlv *country_ie;
 	struct brcmf_ssid_le ssid_le;
 	s32 err = -EPERM;
 	const struct brcmf_tlv *rsn_ie;
@@ -3933,6 +3988,7 @@
 	struct brcmf_fil_bss_enable_le bss_enable;
 	u16 chanspec;
 	bool mbss;
+	int is_11d;
 
 	brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
 		  settings->chandef.chan->hw_value,
@@ -3941,10 +3997,16 @@
 	brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
 		  settings->ssid, settings->ssid_len, settings->auth_type,
 		  settings->inactivity_timeout);
-
 	dev_role = ifp->vif->wdev.iftype;
 	mbss = ifp->vif->mbss;
 
+	/* store current 11d setting */
+	brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
+	country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
+				      settings->beacon.tail_len,
+				      WLAN_EID_COUNTRY);
+	is_11d = country_ie ? 1 : 0;
+
 	memset(&ssid_le, 0, sizeof(ssid_le));
 	if (settings->ssid == NULL || settings->ssid_len == 0) {
 		ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
@@ -4010,6 +4072,14 @@
 			goto exit;
 		}
 
+		if (is_11d != ifp->vif->is_11d) {
+			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
+						    is_11d);
+			if (err < 0) {
+				brcmf_err("Regulatory Set Error, %d\n", err);
+				goto exit;
+			}
+		}
 		if (settings->beacon_interval) {
 			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
 						    settings->beacon_interval);
@@ -4042,6 +4112,10 @@
 			brcmf_err("SET INFRA error %d\n", err);
 			goto exit;
 		}
+	} else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
+		/* Multiple-BSS should use same 11d configuration */
+		err = -EINVAL;
+		goto exit;
 	}
 	if (dev_role == NL80211_IFTYPE_AP) {
 		if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
@@ -4057,6 +4131,10 @@
 			brcmf_err("BRCMF_C_UP error (%d)\n", err);
 			goto exit;
 		}
+		/* On DOWN the firmware removes the WEP keys, reconfigure
+		 * them if they were set.
+		 */
+		brcmf_cfg80211_reconfigure_wep(ifp);
 
 		memset(&join_params, 0, sizeof(join_params));
 		/* join parameters starts with ssid */
@@ -4133,6 +4211,11 @@
 			brcmf_err("setting INFRA mode failed %d\n", err);
 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
 			brcmf_fil_iovar_int_set(ifp, "mbss", 0);
+		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
+					    ifp->vif->is_11d);
+		if (err < 0)
+			brcmf_err("restoring REGULATORY setting failed %d\n",
+				  err);
 		/* Bring device back up so it can be used again */
 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
 		if (err < 0)
@@ -4187,7 +4270,7 @@
 		return -EIO;
 
 	memcpy(&scbval.ea, params->mac, ETH_ALEN);
-	scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
+	scbval.val = cpu_to_le32(params->reason_code);
 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
 				     &scbval, sizeof(scbval));
 	if (err)
@@ -4197,6 +4280,34 @@
 	return err;
 }
 
+static int
+brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
+			      const u8 *mac, struct station_parameters *params)
+{
+	struct brcmf_if *ifp = netdev_priv(ndev);
+	s32 err;
+
+	brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
+		  params->sta_flags_mask, params->sta_flags_set);
+
+	/* Ignore all 00 MAC */
+	if (is_zero_ether_addr(mac))
+		return 0;
+
+	if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
+		return 0;
+
+	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
+		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
+					     (void *)mac, ETH_ALEN);
+	else
+		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
+					     (void *)mac, ETH_ALEN);
+	if (err < 0)
+		brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
+
+	return err;
+}
 
 static void
 brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
@@ -4471,6 +4582,7 @@
 	.stop_ap = brcmf_cfg80211_stop_ap,
 	.change_beacon = brcmf_cfg80211_change_beacon,
 	.del_station = brcmf_cfg80211_del_station,
+	.change_station = brcmf_cfg80211_change_station,
 	.sched_scan_start = brcmf_cfg80211_sched_scan_start,
 	.sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
 	.mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
@@ -4778,7 +4890,6 @@
 	if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
 	    (reason == BRCMF_E_STATUS_SUCCESS)) {
 		memset(&sinfo, 0, sizeof(sinfo));
-		sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
 		if (!data) {
 			brcmf_err("No IEs present in ASSOC/REASSOC_IND");
 			return -EINVAL;
@@ -4833,7 +4944,7 @@
 		if (!brcmf_is_ibssmode(ifp->vif)) {
 			brcmf_bss_connect_done(cfg, ndev, e, false);
 		}
-		brcmf_link_down(ifp->vif);
+		brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
 		brcmf_init_prof(ndev_to_prof(ndev));
 		if (ndev != cfg_to_ndev(cfg))
 			complete(&cfg->vif_disabled);
@@ -5774,7 +5885,7 @@
 	 * from AP to save power
 	 */
 	if (check_vif_up(ifp->vif)) {
-		brcmf_link_down(ifp->vif);
+		brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
 
 		/* Make sure WPA_Supplicant receives all the event
 		   generated due to DISASSOC call to the fw to keep
@@ -5876,6 +5987,29 @@
 				  vif_event_equals(event, action), timeout);
 }
 
+static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
+					struct regulatory_request *req)
+{
+	struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+	struct brcmf_fil_country_le ccreq;
+	int i;
+
+	brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
+		  req->alpha2[0], req->alpha2[1]);
+
+	/* ignore non-ISO3166 country codes */
+	for (i = 0; i < sizeof(req->alpha2); i++)
+		if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
+			brcmf_err("not a ISO3166 code\n");
+			return;
+		}
+	memset(&ccreq, 0, sizeof(ccreq));
+	ccreq.rev = cpu_to_le32(-1);
+	memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
+	brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
+}
+
 static void brcmf_free_wiphy(struct wiphy *wiphy)
 {
 	kfree(wiphy->iface_combinations);
@@ -5952,6 +6086,7 @@
 		goto priv_out;
 
 	brcmf_dbg(INFO, "Registering custom regulatory\n");
+	wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
 	wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
 	wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
 
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
index 9e98b8d..d9e6d01 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
@@ -75,6 +75,8 @@
 
 #define BRCMF_VNDR_IE_P2PAF_SHIFT	12
 
+#define BRCMF_MAX_DEFAULT_KEYS		4
+
 
 /**
  * enum brcmf_scan_status - scan engine status
@@ -125,11 +127,13 @@
  * @ssid: ssid of associated/associating ap.
  * @bssid: bssid of joined/joining ibss.
  * @sec: security information.
+ * @key: key information
  */
 struct brcmf_cfg80211_profile {
 	struct brcmf_ssid ssid;
 	u8 bssid[ETH_ALEN];
 	struct brcmf_cfg80211_security sec;
+	struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS];
 };
 
 /**
@@ -196,6 +200,7 @@
 	struct list_head list;
 	u16 mgmt_rx_reg;
 	bool mbss;
+	int is_11d;
 };
 
 /* association inform */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
index ddae0b5..ab2fac8 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
@@ -100,16 +100,6 @@
 #define BCM4329_CORE_SOCRAM_BASE	0x18003000
 /* ARM Cortex M3 core, ID 0x82a */
 #define BCM4329_CORE_ARM_BASE		0x18002000
-#define BCM4329_RAMSIZE			0x48000
-
-/* bcm43143 */
-/* SDIO device core */
-#define BCM43143_CORE_BUS_BASE		0x18002000
-/* internal memory core */
-#define BCM43143_CORE_SOCRAM_BASE	0x18004000
-/* ARM Cortex M3 core, ID 0x82a */
-#define BCM43143_CORE_ARM_BASE		0x18003000
-#define BCM43143_RAMSIZE		0x70000
 
 #define CORE_SB(base, field) \
 		(base + SBCONFIGOFF + offsetof(struct sbconfig, field))
@@ -157,6 +147,78 @@
 	u32 sbidhigh;	/* identification */
 };
 
+/* bankidx and bankinfo reg defines corerev >= 8 */
+#define SOCRAM_BANKINFO_RETNTRAM_MASK	0x00010000
+#define SOCRAM_BANKINFO_SZMASK		0x0000007f
+#define SOCRAM_BANKIDX_ROM_MASK		0x00000100
+
+#define SOCRAM_BANKIDX_MEMTYPE_SHIFT	8
+/* socram bankinfo memtype */
+#define SOCRAM_MEMTYPE_RAM		0
+#define SOCRAM_MEMTYPE_R0M		1
+#define SOCRAM_MEMTYPE_DEVRAM		2
+
+#define SOCRAM_BANKINFO_SZBASE		8192
+#define SRCI_LSS_MASK		0x00f00000
+#define SRCI_LSS_SHIFT		20
+#define	SRCI_SRNB_MASK		0xf0
+#define	SRCI_SRNB_SHIFT		4
+#define	SRCI_SRBSZ_MASK		0xf
+#define	SRCI_SRBSZ_SHIFT	0
+#define SR_BSZ_BASE		14
+
+struct sbsocramregs {
+	u32 coreinfo;
+	u32 bwalloc;
+	u32 extracoreinfo;
+	u32 biststat;
+	u32 bankidx;
+	u32 standbyctrl;
+
+	u32 errlogstatus;	/* rev 6 */
+	u32 errlogaddr;	/* rev 6 */
+	/* used for patching rev 3 & 5 */
+	u32 cambankidx;
+	u32 cambankstandbyctrl;
+	u32 cambankpatchctrl;
+	u32 cambankpatchtblbaseaddr;
+	u32 cambankcmdreg;
+	u32 cambankdatareg;
+	u32 cambankmaskreg;
+	u32 PAD[1];
+	u32 bankinfo;	/* corev 8 */
+	u32 bankpda;
+	u32 PAD[14];
+	u32 extmemconfig;
+	u32 extmemparitycsr;
+	u32 extmemparityerrdata;
+	u32 extmemparityerrcnt;
+	u32 extmemwrctrlandsize;
+	u32 PAD[84];
+	u32 workaround;
+	u32 pwrctl;		/* corerev >= 2 */
+	u32 PAD[133];
+	u32 sr_control;     /* corerev >= 15 */
+	u32 sr_status;      /* corerev >= 15 */
+	u32 sr_address;     /* corerev >= 15 */
+	u32 sr_data;        /* corerev >= 15 */
+};
+
+#define SOCRAMREGOFFS(_f)	offsetof(struct sbsocramregs, _f)
+
+#define ARMCR4_CAP		(0x04)
+#define ARMCR4_BANKIDX		(0x40)
+#define ARMCR4_BANKINFO		(0x44)
+#define ARMCR4_BANKPDA		(0x4C)
+
+#define	ARMCR4_TCBBNB_MASK	0xf0
+#define	ARMCR4_TCBBNB_SHIFT	4
+#define	ARMCR4_TCBANB_MASK	0xf
+#define	ARMCR4_TCBANB_SHIFT	0
+
+#define	ARMCR4_BSZ_MASK		0x3f
+#define	ARMCR4_BSZ_MULT		8192
+
 struct brcmf_core_priv {
 	struct brcmf_core pub;
 	u32 wrapbase;
@@ -164,13 +226,6 @@
 	struct brcmf_chip_priv *chip;
 };
 
-/* ARM CR4 core specific control flag bits */
-#define ARMCR4_BCMA_IOCTL_CPUHALT	0x0020
-
-/* D11 core specific control flag bits */
-#define D11_BCMA_IOCTL_PHYCLOCKEN	0x0004
-#define D11_BCMA_IOCTL_PHYRESET		0x0008
-
 struct brcmf_chip_priv {
 	struct brcmf_chip pub;
 	const struct brcmf_buscore_ops *ops;
@@ -433,13 +488,13 @@
 	return &core->pub;
 }
 
-#ifdef DEBUG
 /* safety check for chipinfo */
 static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
 {
 	struct brcmf_core_priv *core;
 	bool need_socram = false;
 	bool has_socram = false;
+	bool cpu_found = false;
 	int idx = 1;
 
 	list_for_each_entry(core, &ci->cores, list) {
@@ -449,22 +504,24 @@
 
 		switch (core->pub.id) {
 		case BCMA_CORE_ARM_CM3:
+			cpu_found = true;
 			need_socram = true;
 			break;
 		case BCMA_CORE_INTERNAL_MEM:
 			has_socram = true;
 			break;
 		case BCMA_CORE_ARM_CR4:
-			if (ci->pub.rambase == 0) {
-				brcmf_err("RAM base not provided with ARM CR4 core\n");
-				return -ENOMEM;
-			}
+			cpu_found = true;
 			break;
 		default:
 			break;
 		}
 	}
 
+	if (!cpu_found) {
+		brcmf_err("CPU core not detected\n");
+		return -ENXIO;
+	}
 	/* check RAM core presence for ARM CM3 core */
 	if (need_socram && !has_socram) {
 		brcmf_err("RAM core not provided with ARM CM3 core\n");
@@ -472,55 +529,164 @@
 	}
 	return 0;
 }
-#else	/* DEBUG */
-static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
-{
-	return 0;
-}
-#endif
 
-static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
+static u32 brcmf_chip_core_read32(struct brcmf_core_priv *core, u16 reg)
+{
+	return core->chip->ops->read32(core->chip->ctx, core->pub.base + reg);
+}
+
+static void brcmf_chip_core_write32(struct brcmf_core_priv *core,
+				    u16 reg, u32 val)
+{
+	core->chip->ops->write32(core->chip->ctx, core->pub.base + reg, val);
+}
+
+static bool brcmf_chip_socram_banksize(struct brcmf_core_priv *core, u8 idx,
+				       u32 *banksize)
+{
+	u32 bankinfo;
+	u32 bankidx = (SOCRAM_MEMTYPE_RAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT);
+
+	bankidx |= idx;
+	brcmf_chip_core_write32(core, SOCRAMREGOFFS(bankidx), bankidx);
+	bankinfo = brcmf_chip_core_read32(core, SOCRAMREGOFFS(bankinfo));
+	*banksize = (bankinfo & SOCRAM_BANKINFO_SZMASK) + 1;
+	*banksize *= SOCRAM_BANKINFO_SZBASE;
+	return !!(bankinfo & SOCRAM_BANKINFO_RETNTRAM_MASK);
+}
+
+static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize,
+				      u32 *srsize)
+{
+	u32 coreinfo;
+	uint nb, banksize, lss;
+	bool retent;
+	int i;
+
+	*ramsize = 0;
+	*srsize = 0;
+
+	if (WARN_ON(sr->pub.rev < 4))
+		return;
+
+	if (!brcmf_chip_iscoreup(&sr->pub))
+		brcmf_chip_resetcore(&sr->pub, 0, 0, 0);
+
+	/* Get info for determining size */
+	coreinfo = brcmf_chip_core_read32(sr, SOCRAMREGOFFS(coreinfo));
+	nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+
+	if ((sr->pub.rev <= 7) || (sr->pub.rev == 12)) {
+		banksize = (coreinfo & SRCI_SRBSZ_MASK);
+		lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT;
+		if (lss != 0)
+			nb--;
+		*ramsize = nb * (1 << (banksize + SR_BSZ_BASE));
+		if (lss != 0)
+			*ramsize += (1 << ((lss - 1) + SR_BSZ_BASE));
+	} else {
+		nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+		for (i = 0; i < nb; i++) {
+			retent = brcmf_chip_socram_banksize(sr, i, &banksize);
+			*ramsize += banksize;
+			if (retent)
+				*srsize += banksize;
+		}
+	}
+
+	/* hardcoded save&restore memory sizes */
+	switch (sr->chip->pub.chip) {
+	case BRCM_CC_4334_CHIP_ID:
+		if (sr->chip->pub.chiprev < 2)
+			*srsize = (32 * 1024);
+		break;
+	case BRCM_CC_43430_CHIP_ID:
+		/* assume sr for now as we can not check
+		 * firmware sr capability at this point.
+		 */
+		*srsize = (64 * 1024);
+		break;
+	default:
+		break;
+	}
+}
+
+/** Return the TCM-RAM size of the ARMCR4 core. */
+static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4)
+{
+	u32 corecap;
+	u32 memsize = 0;
+	u32 nab;
+	u32 nbb;
+	u32 totb;
+	u32 bxinfo;
+	u32 idx;
+
+	corecap = brcmf_chip_core_read32(cr4, ARMCR4_CAP);
+
+	nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT;
+	nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT;
+	totb = nab + nbb;
+
+	for (idx = 0; idx < totb; idx++) {
+		brcmf_chip_core_write32(cr4, ARMCR4_BANKIDX, idx);
+		bxinfo = brcmf_chip_core_read32(cr4, ARMCR4_BANKINFO);
+		memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT;
+	}
+
+	return memsize;
+}
+
+static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
 {
 	switch (ci->pub.chip) {
-	case BRCM_CC_4329_CHIP_ID:
-		ci->pub.ramsize = BCM4329_RAMSIZE;
-		break;
-	case BRCM_CC_43143_CHIP_ID:
-		ci->pub.ramsize = BCM43143_RAMSIZE;
-		break;
-	case BRCM_CC_43241_CHIP_ID:
-		ci->pub.ramsize = 0x90000;
-		break;
-	case BRCM_CC_4330_CHIP_ID:
-		ci->pub.ramsize = 0x48000;
-		break;
-	case BRCM_CC_4334_CHIP_ID:
-		ci->pub.ramsize = 0x80000;
-		break;
+	case BRCM_CC_4345_CHIP_ID:
+		return 0x198000;
 	case BRCM_CC_4335_CHIP_ID:
-		ci->pub.ramsize = 0xc0000;
-		ci->pub.rambase = 0x180000;
-		break;
-	case BRCM_CC_43362_CHIP_ID:
-		ci->pub.ramsize = 0x3c000;
-		break;
 	case BRCM_CC_4339_CHIP_ID:
 	case BRCM_CC_4354_CHIP_ID:
 	case BRCM_CC_4356_CHIP_ID:
 	case BRCM_CC_43567_CHIP_ID:
 	case BRCM_CC_43569_CHIP_ID:
 	case BRCM_CC_43570_CHIP_ID:
-		ci->pub.ramsize = 0xc0000;
-		ci->pub.rambase = 0x180000;
-		break;
 	case BRCM_CC_43602_CHIP_ID:
-		ci->pub.ramsize = 0xf0000;
-		ci->pub.rambase = 0x180000;
-		break;
+		return 0x180000;
 	default:
 		brcmf_err("unknown chip: %s\n", ci->pub.name);
 		break;
 	}
+	return 0;
+}
+
+static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
+{
+	struct brcmf_core_priv *mem_core;
+	struct brcmf_core *mem;
+
+	mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_ARM_CR4);
+	if (mem) {
+		mem_core = container_of(mem, struct brcmf_core_priv, pub);
+		ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core);
+		ci->pub.rambase = brcmf_chip_tcm_rambase(ci);
+		if (!ci->pub.rambase) {
+			brcmf_err("RAM base not provided with ARM CR4 core\n");
+			return -EINVAL;
+		}
+	} else {
+		mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_INTERNAL_MEM);
+		mem_core = container_of(mem, struct brcmf_core_priv, pub);
+		brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize,
+					  &ci->pub.srsize);
+	}
+	brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n",
+		  ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize,
+		  ci->pub.srsize, ci->pub.srsize);
+
+	if (!ci->pub.ramsize) {
+		brcmf_err("RAM size is undetermined\n");
+		return -ENOMEM;
+	}
+	return 0;
 }
 
 static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr,
@@ -673,6 +839,7 @@
 	struct brcmf_core *core;
 	u32 regdata;
 	u32 socitype;
+	int ret;
 
 	/* Get CC core rev
 	 * Chipid is assume to be at offset 0 from SI_ENUM_BASE
@@ -725,9 +892,13 @@
 		return -ENODEV;
 	}
 
-	brcmf_chip_get_raminfo(ci);
+	ret = brcmf_chip_cores_check(ci);
+	if (ret)
+		return ret;
 
-	return brcmf_chip_cores_check(ci);
+	/* assure chip is passive for core access */
+	brcmf_chip_set_passive(&ci->pub);
+	return brcmf_chip_get_raminfo(ci);
 }
 
 static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id)
@@ -791,12 +962,6 @@
 	if (chip->ops->setup)
 		ret = chip->ops->setup(chip->ctx, pub);
 
-	/*
-	 * Make sure any on-chip ARM is off (in case strapping is wrong),
-	 * or downloaded code was already running.
-	 */
-	brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3);
-	brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4);
 	return ret;
 }
 
@@ -812,7 +977,7 @@
 		err = -EINVAL;
 	if (WARN_ON(!ops->prepare))
 		err = -EINVAL;
-	if (WARN_ON(!ops->exit_dl))
+	if (WARN_ON(!ops->activate))
 		err = -EINVAL;
 	if (err < 0)
 		return ERR_PTR(-EINVAL);
@@ -910,9 +1075,10 @@
 }
 
 static void
-brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip)
+brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip)
 {
 	struct brcmf_core *core;
+	struct brcmf_core_priv *sr;
 
 	brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3);
 	core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
@@ -922,9 +1088,16 @@
 			     D11_BCMA_IOCTL_PHYCLOCKEN);
 	core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
 	brcmf_chip_resetcore(core, 0, 0, 0);
+
+	/* disable bank #3 remap for this device */
+	if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) {
+		sr = container_of(core, struct brcmf_core_priv, pub);
+		brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3);
+		brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0);
+	}
 }
 
-static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip)
+static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip)
 {
 	struct brcmf_core *core;
 
@@ -934,7 +1107,7 @@
 		return false;
 	}
 
-	chip->ops->exit_dl(chip->ctx, &chip->pub, 0);
+	chip->ops->activate(chip->ctx, &chip->pub, 0);
 
 	core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3);
 	brcmf_chip_resetcore(core, 0, 0, 0);
@@ -943,7 +1116,7 @@
 }
 
 static inline void
-brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip)
+brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip)
 {
 	struct brcmf_core *core;
 
@@ -956,11 +1129,11 @@
 			     D11_BCMA_IOCTL_PHYCLOCKEN);
 }
 
-static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec)
+static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec)
 {
 	struct brcmf_core *core;
 
-	chip->ops->exit_dl(chip->ctx, &chip->pub, rstvec);
+	chip->ops->activate(chip->ctx, &chip->pub, rstvec);
 
 	/* restore ARM */
 	core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4);
@@ -969,7 +1142,7 @@
 	return true;
 }
 
-void brcmf_chip_enter_download(struct brcmf_chip *pub)
+void brcmf_chip_set_passive(struct brcmf_chip *pub)
 {
 	struct brcmf_chip_priv *chip;
 	struct brcmf_core *arm;
@@ -979,14 +1152,14 @@
 	chip = container_of(pub, struct brcmf_chip_priv, pub);
 	arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
 	if (arm) {
-		brcmf_chip_cr4_enterdl(chip);
+		brcmf_chip_cr4_set_passive(chip);
 		return;
 	}
 
-	brcmf_chip_cm3_enterdl(chip);
+	brcmf_chip_cm3_set_passive(chip);
 }
 
-bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec)
+bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec)
 {
 	struct brcmf_chip_priv *chip;
 	struct brcmf_core *arm;
@@ -996,9 +1169,9 @@
 	chip = container_of(pub, struct brcmf_chip_priv, pub);
 	arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
 	if (arm)
-		return brcmf_chip_cr4_exitdl(chip, rstvec);
+		return brcmf_chip_cr4_set_active(chip, rstvec);
 
-	return brcmf_chip_cm3_exitdl(chip);
+	return brcmf_chip_cm3_set_active(chip);
 }
 
 bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
@@ -1029,6 +1202,10 @@
 		addr = CORE_CC_REG(base, chipcontrol_data);
 		reg = chip->ops->read32(chip->ctx, addr);
 		return (reg & pmu_cc3_mask) != 0;
+	case BRCM_CC_43430_CHIP_ID:
+		addr = CORE_CC_REG(base, sr_control1);
+		reg = chip->ops->read32(chip->ctx, addr);
+		return reg != 0;
 	default:
 		addr = CORE_CC_REG(base, pmucapabilities_ext);
 		reg = chip->ops->read32(chip->ctx, addr);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h
index c32908d..60dcb38 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h
@@ -30,7 +30,8 @@
  * @pmucaps: PMU capabilities.
  * @pmurev: PMU revision.
  * @rambase: RAM base address (only applicable for ARM CR4 chips).
- * @ramsize: amount of RAM on chip.
+ * @ramsize: amount of RAM on chip including retention.
+ * @srsize: amount of retention RAM on chip.
  * @name: string representation of the chip identifier.
  */
 struct brcmf_chip {
@@ -41,6 +42,7 @@
 	u32 pmurev;
 	u32 rambase;
 	u32 ramsize;
+	u32 srsize;
 	char name[8];
 };
 
@@ -64,7 +66,7 @@
  * @write32: write 32-bit value over bus.
  * @prepare: prepare bus for core configuration.
  * @setup: bus-specific core setup.
- * @exit_dl: exit download state.
+ * @active: chip becomes active.
  *	The callback should use the provided @rstvec when non-zero.
  */
 struct brcmf_buscore_ops {
@@ -72,7 +74,7 @@
 	void (*write32)(void *ctx, u32 addr, u32 value);
 	int (*prepare)(void *ctx);
 	int (*setup)(void *ctx, struct brcmf_chip *chip);
-	void (*exit_dl)(void *ctx, struct brcmf_chip *chip, u32 rstvec);
+	void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec);
 };
 
 struct brcmf_chip *brcmf_chip_attach(void *ctx,
@@ -84,8 +86,8 @@
 void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset);
 void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset,
 			  u32 postreset);
-void brcmf_chip_enter_download(struct brcmf_chip *ci);
-bool brcmf_chip_exit_download(struct brcmf_chip *ci, u32 rstvec);
+void brcmf_chip_set_passive(struct brcmf_chip *ci);
+bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec);
 bool brcmf_chip_sr_capable(struct brcmf_chip *pub);
 
 #endif /* BRCMF_AXIDMP_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c
index ed8356d..41223a1 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c
@@ -25,6 +25,9 @@
 #include "fwil.h"
 #include "fwil_types.h"
 #include "tracepoint.h"
+#include "common.h"
+
+const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
 #define BRCMF_DEFAULT_BCN_TIMEOUT	3
 #define BRCMF_DEFAULT_SCAN_CHANNEL_TIME	40
@@ -38,6 +41,8 @@
 	s8 eventmask[BRCMF_EVENTING_MASK_LEN];
 	u8 buf[BRCMF_DCMD_SMLEN];
 	struct brcmf_join_pref_params join_pref_params[2];
+	struct brcmf_rev_info_le revinfo;
+	struct brcmf_rev_info *ri;
 	char *ptr;
 	s32 err;
 
@@ -45,12 +50,37 @@
 	err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
 				       sizeof(ifp->mac_addr));
 	if (err < 0) {
-		brcmf_err("Retreiving cur_etheraddr failed, %d\n",
-			  err);
+		brcmf_err("Retreiving cur_etheraddr failed, %d\n", err);
 		goto done;
 	}
 	memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac));
 
+	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO,
+				     &revinfo, sizeof(revinfo));
+	ri = &ifp->drvr->revinfo;
+	if (err < 0) {
+		brcmf_err("retrieving revision info failed, %d\n", err);
+	} else {
+		ri->vendorid = le32_to_cpu(revinfo.vendorid);
+		ri->deviceid = le32_to_cpu(revinfo.deviceid);
+		ri->radiorev = le32_to_cpu(revinfo.radiorev);
+		ri->chiprev = le32_to_cpu(revinfo.chiprev);
+		ri->corerev = le32_to_cpu(revinfo.corerev);
+		ri->boardid = le32_to_cpu(revinfo.boardid);
+		ri->boardvendor = le32_to_cpu(revinfo.boardvendor);
+		ri->boardrev = le32_to_cpu(revinfo.boardrev);
+		ri->driverrev = le32_to_cpu(revinfo.driverrev);
+		ri->ucoderev = le32_to_cpu(revinfo.ucoderev);
+		ri->bus = le32_to_cpu(revinfo.bus);
+		ri->chipnum = le32_to_cpu(revinfo.chipnum);
+		ri->phytype = le32_to_cpu(revinfo.phytype);
+		ri->phyrev = le32_to_cpu(revinfo.phyrev);
+		ri->anarev = le32_to_cpu(revinfo.anarev);
+		ri->chippkg = le32_to_cpu(revinfo.chippkg);
+		ri->nvramrev = le32_to_cpu(revinfo.nvramrev);
+	}
+	ri->result = err;
+
 	/* query for 'ver' to get version info from firmware */
 	memset(buf, 0, sizeof(buf));
 	strcpy(buf, "ver");
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.h b/drivers/net/wireless/brcm80211/brcmfmac/common.h
new file mode 100644
index 0000000..0d39d80
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/common.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2014 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.
+ */
+#ifndef BRCMFMAC_COMMON_H
+#define BRCMFMAC_COMMON_H
+
+extern const u8 ALLFFMAC[ETH_ALEN];
+
+#endif /* BRCMFMAC_COMMON_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h
index 002336e..3d40401 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h
@@ -37,6 +37,8 @@
 	unsigned long flags;
 	bool inited;
 	bool was_full;
+
+	atomic_t outstanding_tx;
 };
 
 
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c
index f8785da..538d695 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
@@ -197,7 +197,7 @@
 	brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx);
 
 	/* Can the device send data? */
-	if (drvr->bus_if->state != BRCMF_BUS_DATA) {
+	if (drvr->bus_if->state != BRCMF_BUS_UP) {
 		brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state);
 		netif_stop_queue(ndev);
 		dev_kfree_skb(skb);
@@ -601,9 +601,12 @@
 {
 	struct brcmf_if *ifp = netdev_priv(ndev);
 	struct brcmf_pub *drvr = ifp->drvr;
+	char drev[BRCMU_DOTREV_LEN] = "n/a";
 
+	if (drvr->revinfo.result == 0)
+		brcmu_dotrev_str(drvr->revinfo.driverrev, drev);
 	strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
-	snprintf(info->version, sizeof(info->version), "n/a");
+	strlcpy(info->version, drev, sizeof(info->version));
 	strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version));
 	strlcpy(info->bus_info, dev_name(drvr->bus_if->dev),
 		sizeof(info->bus_info));
@@ -637,7 +640,7 @@
 	brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
 
 	/* If bus is not ready, can't continue */
-	if (bus_if->state != BRCMF_BUS_DATA) {
+	if (bus_if->state != BRCMF_BUS_UP) {
 		brcmf_err("failed bus is not ready\n");
 		return -EAGAIN;
 	}
@@ -941,6 +944,34 @@
 	return ret;
 }
 
+static int brcmf_revinfo_read(struct seq_file *s, void *data)
+{
+	struct brcmf_bus *bus_if = dev_get_drvdata(s->private);
+	struct brcmf_rev_info *ri = &bus_if->drvr->revinfo;
+	char drev[BRCMU_DOTREV_LEN];
+	char brev[BRCMU_BOARDREV_LEN];
+
+	seq_printf(s, "vendorid: 0x%04x\n", ri->vendorid);
+	seq_printf(s, "deviceid: 0x%04x\n", ri->deviceid);
+	seq_printf(s, "radiorev: %s\n", brcmu_dotrev_str(ri->radiorev, drev));
+	seq_printf(s, "chipnum: %u (%x)\n", ri->chipnum, ri->chipnum);
+	seq_printf(s, "chiprev: %u\n", ri->chiprev);
+	seq_printf(s, "chippkg: %u\n", ri->chippkg);
+	seq_printf(s, "corerev: %u\n", ri->corerev);
+	seq_printf(s, "boardid: 0x%04x\n", ri->boardid);
+	seq_printf(s, "boardvendor: 0x%04x\n", ri->boardvendor);
+	seq_printf(s, "boardrev: %s\n", brcmu_boardrev_str(ri->boardrev, brev));
+	seq_printf(s, "driverrev: %s\n", brcmu_dotrev_str(ri->driverrev, drev));
+	seq_printf(s, "ucoderev: %u\n", ri->ucoderev);
+	seq_printf(s, "bus: %u\n", ri->bus);
+	seq_printf(s, "phytype: %u\n", ri->phytype);
+	seq_printf(s, "phyrev: %u\n", ri->phyrev);
+	seq_printf(s, "anarev: %u\n", ri->anarev);
+	seq_printf(s, "nvramrev: %08x\n", ri->nvramrev);
+
+	return 0;
+}
+
 int brcmf_bus_start(struct device *dev)
 {
 	int ret = -1;
@@ -964,13 +995,22 @@
 		p2p_ifp = NULL;
 
 	/* signal bus ready */
-	brcmf_bus_change_state(bus_if, BRCMF_BUS_DATA);
+	brcmf_bus_change_state(bus_if, BRCMF_BUS_UP);
 
 	/* Bus is ready, do any initialization */
 	ret = brcmf_c_preinit_dcmds(ifp);
 	if (ret < 0)
 		goto fail;
 
+	brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read);
+
+	/* assure we have chipid before feature attach */
+	if (!bus_if->chip) {
+		bus_if->chip = drvr->revinfo.chipnum;
+		bus_if->chiprev = drvr->revinfo.chiprev;
+		brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n",
+			  bus_if->chip, bus_if->chip, bus_if->chiprev);
+	}
 	brcmf_feat_attach(drvr);
 
 	ret = brcmf_fws_init(drvr);
@@ -1093,9 +1133,8 @@
 	return atomic_read(&ifp->pend_8021x_cnt);
 }
 
-int brcmf_netdev_wait_pend8021x(struct net_device *ndev)
+int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp)
 {
-	struct brcmf_if *ifp = netdev_priv(ndev);
 	int err;
 
 	err = wait_event_timeout(ifp->pend_8021x_wait,
@@ -1107,6 +1146,27 @@
 	return !err;
 }
 
+void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state)
+{
+	struct brcmf_pub *drvr = bus->drvr;
+	struct net_device *ndev;
+	int ifidx;
+
+	brcmf_dbg(TRACE, "%d -> %d\n", bus->state, state);
+	bus->state = state;
+
+	if (state == BRCMF_BUS_UP) {
+		for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
+			if ((drvr->iflist[ifidx]) &&
+			    (drvr->iflist[ifidx]->ndev)) {
+				ndev = drvr->iflist[ifidx]->ndev;
+				if (netif_queue_stopped(ndev))
+					netif_wake_queue(ndev);
+			}
+		}
+	}
+}
+
 static void brcmf_driver_register(struct work_struct *work)
 {
 #ifdef CPTCFG_BRCMFMAC_SDIO
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h
index 23f74b1..fd74a9c 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
@@ -29,8 +29,6 @@
 /* For supporting multiple interfaces */
 #define BRCMF_MAX_IFS	16
 
-#define DOT11_MAX_DEFAULT_KEYS	4
-
 /* Small, medium and maximum buffer size for dcmd
  */
 #define BRCMF_DCMD_SMLEN	256
@@ -73,6 +71,35 @@
 struct brcmf_cfg80211_dev; /* cfg80211 device info */
 struct brcmf_fws_info; /* firmware signalling info */
 
+/*
+ * struct brcmf_rev_info
+ *
+ * The result field stores the error code of the
+ * revision info request from firmware. For the
+ * other fields see struct brcmf_rev_info_le in
+ * fwil_types.h
+ */
+struct brcmf_rev_info {
+	int result;
+	u32 vendorid;
+	u32 deviceid;
+	u32 radiorev;
+	u32 chiprev;
+	u32 corerev;
+	u32 boardid;
+	u32 boardvendor;
+	u32 boardrev;
+	u32 driverrev;
+	u32 ucoderev;
+	u32 bus;
+	u32 chipnum;
+	u32 phytype;
+	u32 phyrev;
+	u32 anarev;
+	u32 chippkg;
+	u32 nvramrev;
+};
+
 /* Common structure for module and instance linkage */
 struct brcmf_pub {
 	/* Linkage ponters */
@@ -106,6 +133,7 @@
 	u32 feat_flags;
 	u32 chip_quirks;
 
+	struct brcmf_rev_info revinfo;
 #ifdef DEBUG
 	struct dentry *dbgfs_dir;
 #endif
@@ -167,7 +195,7 @@
 	u8 *reorder;
 };
 
-int brcmf_netdev_wait_pend8021x(struct net_device *ndev);
+int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
 
 /* Return pointer to interface name */
 char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
index defb7a4..7748a1c 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
@@ -126,7 +126,8 @@
 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
 	if (drvr->bus_if->wowl_supported)
 		brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
-	brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
+	if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID)
+		brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
 
 	/* set chip related quirks */
 	switch (drvr->bus_if->chip) {
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
index 1ff787d..9cb9915 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
@@ -103,7 +103,11 @@
 
 	c = nvp->fwnv->data[nvp->pos];
 	if (c == '=') {
-		st = VALUE;
+		/* ignore RAW1 by treating as comment */
+		if (strncmp(&nvp->fwnv->data[nvp->entry], "RAW1", 4) == 0)
+			st = COMMENT;
+		else
+			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);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
index 44f3a84..eb13253 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
@@ -25,6 +25,7 @@
 #include "proto.h"
 #include "flowring.h"
 #include "msgbuf.h"
+#include "common.h"
 
 
 #define BRCMF_FLOWRING_HIGH		1024
@@ -34,9 +35,6 @@
 #define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16)
 #define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16)
 
-static const u8 ALLZEROMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-static const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
 static const u8 brcmf_flowring_prio2fifo[] = {
 	1,
 	0,
@@ -137,7 +135,7 @@
 	hash = flow->hash;
 	for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
 		if ((hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX) &&
-		    (memcmp(hash[hash_idx].mac, ALLZEROMAC, ETH_ALEN) == 0)) {
+		    (is_zero_ether_addr(hash[hash_idx].mac))) {
 			found = true;
 			break;
 		}
@@ -238,7 +236,7 @@
 	brcmf_flowring_block(flow, flowid, false);
 	hash_idx = ring->hash_id;
 	flow->hash[hash_idx].ifidx = BRCMF_FLOWRING_INVALID_IFIDX;
-	memset(flow->hash[hash_idx].mac, 0, ETH_ALEN);
+	eth_zero_addr(flow->hash[hash_idx].mac);
 	flow->rings[flowid] = NULL;
 
 	skb = skb_dequeue(&ring->skblist);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
index 03f2c40..dcfa0bb 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
@@ -109,7 +109,7 @@
 	struct brcmf_pub *drvr = ifp->drvr;
 	s32 err;
 
-	if (drvr->bus_if->state != BRCMF_BUS_DATA) {
+	if (drvr->bus_if->state != BRCMF_BUS_UP) {
 		brcmf_err("bus is down. we have nothing to do.\n");
 		return -EIO;
 	}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h
index a30be68..5434dcf 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h
@@ -43,6 +43,8 @@
 #define BRCMF_C_SET_RADIO			38
 #define BRCMF_C_GET_PHYTYPE			39
 #define BRCMF_C_SET_KEY				45
+#define BRCMF_C_GET_REGULATORY			46
+#define BRCMF_C_SET_REGULATORY			47
 #define BRCMF_C_SET_PASSIVE_SCAN		49
 #define BRCMF_C_SCAN				50
 #define BRCMF_C_SCAN_RESULTS			51
@@ -57,9 +59,12 @@
 #define BRCMF_C_SET_COUNTRY			84
 #define BRCMF_C_GET_PM				85
 #define BRCMF_C_SET_PM				86
+#define BRCMF_C_GET_REVINFO			98
 #define BRCMF_C_GET_CURR_RATESET		114
 #define BRCMF_C_GET_AP				117
 #define BRCMF_C_SET_AP				118
+#define BRCMF_C_SET_SCB_AUTHORIZE		121
+#define BRCMF_C_SET_SCB_DEAUTHORIZE		122
 #define BRCMF_C_GET_RSSI			127
 #define BRCMF_C_GET_WSEC			133
 #define BRCMF_C_SET_WSEC			134
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
index 50891c0..3749209 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
@@ -112,6 +112,7 @@
 #define BRCMF_WOWL_MAXPATTERNS		8
 #define BRCMF_WOWL_MAXPATTERNSIZE	128
 
+#define BRCMF_COUNTRY_BUF_SZ		4
 
 /* join preference types for join_pref iovar */
 enum brcmf_join_pref_types {
@@ -525,4 +526,58 @@
 	unsigned char SSID[32];
 };
 
+/**
+ * struct brcmf_fil_country_le - country configuration structure.
+ *
+ * @country_abbrev: null-terminated country code used in the country IE.
+ * @rev: revision specifier for ccode. on set, -1 indicates unspecified.
+ * @ccode: null-terminated built-in country code.
+ */
+struct brcmf_fil_country_le {
+	char country_abbrev[BRCMF_COUNTRY_BUF_SZ];
+	__le32 rev;
+	char ccode[BRCMF_COUNTRY_BUF_SZ];
+};
+
+/**
+ * struct brcmf_rev_info_le - device revision info.
+ *
+ * @vendorid: PCI vendor id.
+ * @deviceid: device id of chip.
+ * @radiorev: radio revision.
+ * @chiprev: chip revision.
+ * @corerev: core revision.
+ * @boardid: board identifier (usu. PCI sub-device id).
+ * @boardvendor: board vendor (usu. PCI sub-vendor id).
+ * @boardrev: board revision.
+ * @driverrev: driver version.
+ * @ucoderev: microcode version.
+ * @bus: bus type.
+ * @chipnum: chip number.
+ * @phytype: phy type.
+ * @phyrev: phy revision.
+ * @anarev: anacore rev.
+ * @chippkg: chip package info.
+ * @nvramrev: nvram revision number.
+ */
+struct brcmf_rev_info_le {
+	__le32 vendorid;
+	__le32 deviceid;
+	__le32 radiorev;
+	__le32 chiprev;
+	__le32 corerev;
+	__le32 boardid;
+	__le32 boardvendor;
+	__le32 boardrev;
+	__le32 driverrev;
+	__le32 ucoderev;
+	__le32 bus;
+	__le32 chipnum;
+	__le32 phytype;
+	__le32 phyrev;
+	__le32 anarev;
+	__le32 chippkg;
+	__le32 nvramrev;
+};
+
 #endif /* FWIL_TYPES_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
index 456944a..4ec9811 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
@@ -73,6 +73,8 @@
 #define BRCMF_MSGBUF_TX_FLUSH_CNT1		32
 #define BRCMF_MSGBUF_TX_FLUSH_CNT2		96
 
+#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS	64
+#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS	32
 
 struct msgbuf_common_hdr {
 	u8				msgtype;
@@ -479,10 +481,9 @@
 
 static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf)
 {
-	if (waitqueue_active(&msgbuf->ioctl_resp_wait)) {
-		msgbuf->ctl_completed = true;
+	msgbuf->ctl_completed = true;
+	if (waitqueue_active(&msgbuf->ioctl_resp_wait))
 		wake_up(&msgbuf->ioctl_resp_wait);
-	}
 }
 
 
@@ -583,7 +584,7 @@
 	u32 flowid;
 	void *dma_buf;
 	u32 dma_sz;
-	long long address;
+	u64 address;
 	int err;
 
 	flowid = work->flowid;
@@ -620,7 +621,7 @@
 					   BRCMF_NROF_H2D_COMMON_MSGRINGS);
 	memcpy(create->sa, work->sa, ETH_ALEN);
 	memcpy(create->da, work->da, ETH_ALEN);
-	address = (long long)(long)msgbuf->flowring_dma_handle[flowid];
+	address = (u64)msgbuf->flowring_dma_handle[flowid];
 	create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32);
 	create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff);
 	create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM);
@@ -698,7 +699,7 @@
 	dma_addr_t physaddr;
 	u32 pktid;
 	struct msgbuf_tx_msghdr *tx_msghdr;
-	long long address;
+	u64 address;
 
 	commonring = msgbuf->flowrings[flowid];
 	if (!brcmf_commonring_write_available(commonring))
@@ -742,13 +743,14 @@
 		tx_msghdr->seg_cnt = 1;
 		memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN);
 		tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN);
-		address = (long long)(long)physaddr;
+		address = (u64)physaddr;
 		tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32);
 		tx_msghdr->data_buf_addr.low_addr =
 			cpu_to_le32(address & 0xffffffff);
 		tx_msghdr->metadata_buf_len = 0;
 		tx_msghdr->metadata_buf_addr.high_addr = 0;
 		tx_msghdr->metadata_buf_addr.low_addr = 0;
+		atomic_inc(&commonring->outstanding_tx);
 		if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) {
 			brcmf_commonring_write_complete(commonring);
 			count = 0;
@@ -773,10 +775,16 @@
 }
 
 
-static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid)
+static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid,
+					bool force)
 {
+	struct brcmf_commonring *commonring;
+
 	set_bit(flowid, msgbuf->flow_map);
-	queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work);
+	commonring = msgbuf->flowrings[flowid];
+	if ((force) || (atomic_read(&commonring->outstanding_tx) <
+			BRCMF_MSGBUF_DELAY_TXWORKER_THRS))
+		queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work);
 
 	return 0;
 }
@@ -797,7 +805,7 @@
 			return -ENOMEM;
 	}
 	brcmf_flowring_enqueue(flow, flowid, skb);
-	brcmf_msgbuf_schedule_txdata(msgbuf, flowid);
+	brcmf_msgbuf_schedule_txdata(msgbuf, flowid, false);
 
 	return 0;
 }
@@ -854,6 +862,7 @@
 static void
 brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf)
 {
+	struct brcmf_commonring *commonring;
 	struct msgbuf_tx_status *tx_status;
 	u32 idx;
 	struct sk_buff *skb;
@@ -871,6 +880,8 @@
 	}
 
 	set_bit(flowid, msgbuf->txstatus_done_map);
+	commonring = msgbuf->flowrings[flowid];
+	atomic_dec(&commonring->outstanding_tx);
 
 	brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true);
 }
@@ -885,7 +896,7 @@
 	u32 pktlen;
 	dma_addr_t physaddr;
 	struct msgbuf_rx_bufpost *rx_bufpost;
-	long long address;
+	u64 address;
 	u32 pktid;
 	u32 i;
 
@@ -894,7 +905,7 @@
 							      count,
 							      &alloced);
 	if (!ret_ptr) {
-		brcmf_err("Failed to reserve space in commonring\n");
+		brcmf_dbg(MSGBUF, "Failed to reserve space in commonring\n");
 		return 0;
 	}
 
@@ -921,7 +932,7 @@
 		}
 
 		if (msgbuf->rx_metadata_offset) {
-			address = (long long)(long)physaddr;
+			address = (u64)physaddr;
 			rx_bufpost->metadata_buf_len =
 				cpu_to_le16(msgbuf->rx_metadata_offset);
 			rx_bufpost->metadata_buf_addr.high_addr =
@@ -936,7 +947,7 @@
 		rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST;
 		rx_bufpost->msg.request_id = cpu_to_le32(pktid);
 
-		address = (long long)(long)physaddr;
+		address = (u64)physaddr;
 		rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen);
 		rx_bufpost->data_buf_addr.high_addr =
 			cpu_to_le32(address >> 32);
@@ -992,7 +1003,7 @@
 	u32 pktlen;
 	dma_addr_t physaddr;
 	struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost;
-	long long address;
+	u64 address;
 	u32 pktid;
 	u32 i;
 
@@ -1035,7 +1046,7 @@
 				MSGBUF_TYPE_IOCTLRESP_BUF_POST;
 		rx_bufpost->msg.request_id = cpu_to_le32(pktid);
 
-		address = (long long)(long)physaddr;
+		address = (u64)physaddr;
 		rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen);
 		rx_bufpost->host_buf_addr.high_addr =
 			cpu_to_le32(address >> 32);
@@ -1181,7 +1192,7 @@
 
 	brcmf_flowring_open(msgbuf->flow, flowid);
 
-	brcmf_msgbuf_schedule_txdata(msgbuf, flowid);
+	brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
 }
 
 
@@ -1280,8 +1291,10 @@
 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 	struct brcmf_pub *drvr = bus_if->drvr;
 	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+	struct brcmf_commonring *commonring;
 	void *buf;
 	u32 flowid;
+	int qlen;
 
 	buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
 	brcmf_msgbuf_process_rx(msgbuf, buf);
@@ -1293,8 +1306,12 @@
 	for_each_set_bit(flowid, msgbuf->txstatus_done_map,
 			 msgbuf->nrof_flowrings) {
 		clear_bit(flowid, msgbuf->txstatus_done_map);
-		if (brcmf_flowring_qlen(msgbuf->flow, flowid))
-			brcmf_msgbuf_schedule_txdata(msgbuf, flowid);
+		commonring = msgbuf->flowrings[flowid];
+		qlen = brcmf_flowring_qlen(msgbuf->flow, flowid);
+		if ((qlen > BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) ||
+		    ((qlen) && (atomic_read(&commonring->outstanding_tx) <
+				BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS)))
+			brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
 	}
 
 	return 0;
@@ -1348,7 +1365,7 @@
 {
 	struct brcmf_bus_msgbuf *if_msgbuf;
 	struct brcmf_msgbuf *msgbuf;
-	long long address;
+	u64 address;
 	u32 count;
 
 	if_msgbuf = drvr->bus_if->msgbuf;
@@ -1379,7 +1396,7 @@
 					     GFP_KERNEL);
 	if (!msgbuf->ioctbuf)
 		goto fail;
-	address = (long long)(long)msgbuf->ioctbuf_handle;
+	address = (u64)msgbuf->ioctbuf_handle;
 	msgbuf->ioctbuf_phys_hi = address >> 32;
 	msgbuf->ioctbuf_phys_lo = address & 0xffffffff;
 
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h
index caf16ea..3c1218c 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h
@@ -17,11 +17,11 @@
 
 #ifdef CPTCFG_BRCMFMAC_PROTO_MSGBUF
 
-#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM	20
-#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM	256
-#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM	20
+#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM	64
+#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM	512
+#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM	64
 #define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM		1024
-#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM		256
+#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM		512
 #define BRCMF_H2D_TXFLOWRING_MAX_ITEM			512
 
 #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE	40
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
index effb48e..e53d47f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
@@ -697,7 +697,7 @@
 	else
 		sparams->scan_type = 1;
 
-	memset(&sparams->bssid, 0xFF, ETH_ALEN);
+	eth_broadcast_addr(sparams->bssid);
 	if (ssid.SSID_len)
 		memcpy(sparams->ssid_le.SSID, ssid.SSID, ssid.SSID_len);
 	sparams->ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len);
@@ -2246,11 +2246,13 @@
  *
  * @wiphy: wiphy device of new interface.
  * @name: name of the new interface.
+ * @name_assign_type: origin of the interface name
  * @type: nl80211 interface type.
  * @flags: not used.
  * @params: contains mac address for P2P device.
  */
 struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
+				       unsigned char name_assign_type,
 				       enum nl80211_iftype type, u32 *flags,
 				       struct vif_params *params)
 {
@@ -2310,6 +2312,9 @@
 	}
 
 	strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+	ifp->ndev->name_assign_type = name_assign_type;
+#endif /* >= 3.17.0 */
 	err = brcmf_net_attach(ifp, true);
 	if (err) {
 		brcmf_err("Registering netdevice failed\n");
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h
index 6821b26..872f382 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h
@@ -149,6 +149,7 @@
 s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg);
 void brcmf_p2p_detach(struct brcmf_p2p_info *p2p);
 struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
+				       unsigned char name_assign_type,
 				       enum nl80211_iftype type, u32 *flags,
 				       struct vif_params *params);
 int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
index 905991f..1831ecd 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
@@ -47,8 +47,6 @@
 
 #define BRCMF_PCIE_43602_FW_NAME		"brcm/brcmfmac43602-pcie.bin"
 #define BRCMF_PCIE_43602_NVRAM_NAME		"brcm/brcmfmac43602-pcie.txt"
-#define BRCMF_PCIE_4354_FW_NAME			"brcm/brcmfmac4354-pcie.bin"
-#define BRCMF_PCIE_4354_NVRAM_NAME		"brcm/brcmfmac4354-pcie.txt"
 #define BRCMF_PCIE_4356_FW_NAME			"brcm/brcmfmac4356-pcie.bin"
 #define BRCMF_PCIE_4356_NVRAM_NAME		"brcm/brcmfmac4356-pcie.txt"
 #define BRCMF_PCIE_43570_FW_NAME		"brcm/brcmfmac43570-pcie.bin"
@@ -187,8 +185,8 @@
 
 MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME);
 MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4354_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4354_NVRAM_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME);
 MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME);
 MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME);
 
@@ -509,8 +507,6 @@
 
 static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo)
 {
-	brcmf_chip_enter_download(devinfo->ci);
-
 	if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) {
 		brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4);
 		brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX,
@@ -536,7 +532,7 @@
 		brcmf_chip_resetcore(core, 0, 0, 0);
 	}
 
-	return !brcmf_chip_exit_download(devinfo->ci, resetintr);
+	return !brcmf_chip_set_active(devinfo->ci, resetintr);
 }
 
 
@@ -653,10 +649,9 @@
 			console->log_str[console->log_idx] = ch;
 			console->log_idx++;
 		}
-
 		if (ch == '\n') {
 			console->log_str[console->log_idx] = 0;
-			brcmf_dbg(PCIE, "CONSOLE: %s\n", console->log_str);
+			brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str);
 			console->log_idx = 0;
 		}
 	}
@@ -959,14 +954,14 @@
 				     dma_addr_t *dma_handle)
 {
 	void *ring;
-	long long address;
+	u64 address;
 
 	ring = dma_alloc_coherent(&devinfo->pdev->dev, size, dma_handle,
 				  GFP_KERNEL);
 	if (!ring)
 		return NULL;
 
-	address = (long long)(long)*dma_handle;
+	address = (u64)*dma_handle;
 	brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr,
 			       address & 0xffffffff);
 	brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr + 4, address >> 32);
@@ -1166,7 +1161,7 @@
 
 static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo)
 {
-	long long address;
+	u64 address;
 	u32 addr;
 
 	devinfo->shared.scratch = dma_alloc_coherent(&devinfo->pdev->dev,
@@ -1180,7 +1175,7 @@
 
 	addr = devinfo->shared.tcm_base_address +
 	       BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET;
-	address = (long long)(long)devinfo->shared.scratch_dmahandle;
+	address = (u64)devinfo->shared.scratch_dmahandle;
 	brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
 	brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
 	addr = devinfo->shared.tcm_base_address +
@@ -1198,7 +1193,7 @@
 
 	addr = devinfo->shared.tcm_base_address +
 	       BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET;
-	address = (long long)(long)devinfo->shared.ringupd_dmahandle;
+	address = (u64)devinfo->shared.ringupd_dmahandle;
 	brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
 	brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
 	addr = devinfo->shared.tcm_base_address +
@@ -1328,10 +1323,6 @@
 		fw_name = BRCMF_PCIE_43602_FW_NAME;
 		nvram_name = BRCMF_PCIE_43602_NVRAM_NAME;
 		break;
-	case BRCM_CC_4354_CHIP_ID:
-		fw_name = BRCMF_PCIE_4354_FW_NAME;
-		nvram_name = BRCMF_PCIE_4354_NVRAM_NAME;
-		break;
 	case BRCM_CC_4356_CHIP_ID:
 		fw_name = BRCMF_PCIE_4356_FW_NAME;
 		nvram_name = BRCMF_PCIE_4356_NVRAM_NAME;
@@ -1566,8 +1557,8 @@
 }
 
 
-static void brcmf_pcie_buscore_exitdl(void *ctx, struct brcmf_chip *chip,
-				      u32 rstvec)
+static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip,
+					u32 rstvec)
 {
 	struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
 
@@ -1577,7 +1568,7 @@
 
 static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = {
 	.prepare = brcmf_pcie_buscoreprep,
-	.exit_dl = brcmf_pcie_buscore_exitdl,
+	.activate = brcmf_pcie_buscore_activate,
 	.read32 = brcmf_pcie_buscore_read32,
 	.write32 = brcmf_pcie_buscore_write32,
 };
@@ -1828,7 +1819,7 @@
 				goto cleanup;
 			brcmf_dbg(PCIE, "Hot resume, continue....\n");
 			brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
-			brcmf_bus_change_state(bus, BRCMF_BUS_DATA);
+			brcmf_bus_change_state(bus, BRCMF_BUS_UP);
 			brcmf_pcie_intr_enable(devinfo);
 			return 0;
 		}
@@ -1856,7 +1847,6 @@
 	PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
 
 static struct pci_device_id brcmf_pcie_devid_table[] = {
-	BRCMF_PCIE_DEVICE(BRCM_PCIE_4354_DEVICE_ID),
 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID),
 	BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
 	BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
index 0b0d51a..ab0c898 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
@@ -44,7 +44,8 @@
 #include "chip.h"
 #include "firmware.h"
 
-#define DCMD_RESP_TIMEOUT  2000	/* In milli second */
+#define DCMD_RESP_TIMEOUT	2000	/* In milli second */
+#define CTL_DONE_TIMEOUT	2000	/* In milli second */
 
 #ifdef DEBUG
 
@@ -431,8 +432,6 @@
 	struct brcmf_sdio_dev *sdiodev;	/* sdio device handler */
 	struct brcmf_chip *ci;	/* Chip info struct */
 
-	u32 ramsize;		/* Size of RAM in SOCRAM (bytes) */
-
 	u32 hostintmask;	/* Copy of Host Interrupt Mask */
 	atomic_t intstatus;	/* Intstatus bits (events) pending */
 	atomic_t fcstate;	/* State of dongle flow-control */
@@ -484,10 +483,9 @@
 #endif				/* DEBUG */
 
 	uint clkstate;		/* State of sd and backplane clock(s) */
-	bool activity;		/* Activity flag for clock down */
 	s32 idletime;		/* Control for activity timeout */
-	s32 idlecount;	/* Activity timeout counter */
-	s32 idleclock;	/* How to set bus driver when idle */
+	s32 idlecount;		/* Activity timeout counter */
+	s32 idleclock;		/* How to set bus driver when idle */
 	bool rxflow_mode;	/* Rx flow control mode */
 	bool rxflow;		/* Is rx flow control on */
 	bool alp_only;		/* Don't use HT clock (ALP only) */
@@ -495,9 +493,9 @@
 	u8 *ctrl_frame_buf;
 	u16 ctrl_frame_len;
 	bool ctrl_frame_stat;
+	int ctrl_frame_err;
 
 	spinlock_t txq_lock;		/* protect bus->txq */
-	struct semaphore tx_seq_lock;	/* protect bus->tx_seq */
 	wait_queue_head_t ctrl_wait;
 	wait_queue_head_t dcmd_resp_wait;
 
@@ -509,12 +507,13 @@
 
 	struct workqueue_struct *brcmf_wq;
 	struct work_struct datawork;
-	atomic_t dpc_tskcnt;
+	bool dpc_triggered;
+	bool dpc_running;
 
 	bool txoff;		/* Transmit flow-controlled */
 	struct brcmf_sdio_count sdcnt;
 	bool sr_enabled; /* SaveRestore enabled */
-	bool sleeping; /* SDIO bus sleeping */
+	bool sleeping;
 
 	u8 tx_hdrlen;		/* sdio bus header length for tx packet */
 	bool txglom;		/* host tx glomming enable flag */
@@ -608,12 +607,18 @@
 #define BCM4330_NVRAM_NAME		"brcm/brcmfmac4330-sdio.txt"
 #define BCM4334_FIRMWARE_NAME		"brcm/brcmfmac4334-sdio.bin"
 #define BCM4334_NVRAM_NAME		"brcm/brcmfmac4334-sdio.txt"
+#define BCM43340_FIRMWARE_NAME		"brcm/brcmfmac43340-sdio.bin"
+#define BCM43340_NVRAM_NAME		"brcm/brcmfmac43340-sdio.txt"
 #define BCM4335_FIRMWARE_NAME		"brcm/brcmfmac4335-sdio.bin"
 #define BCM4335_NVRAM_NAME		"brcm/brcmfmac4335-sdio.txt"
 #define BCM43362_FIRMWARE_NAME		"brcm/brcmfmac43362-sdio.bin"
 #define BCM43362_NVRAM_NAME		"brcm/brcmfmac43362-sdio.txt"
 #define BCM4339_FIRMWARE_NAME		"brcm/brcmfmac4339-sdio.bin"
 #define BCM4339_NVRAM_NAME		"brcm/brcmfmac4339-sdio.txt"
+#define BCM43430_FIRMWARE_NAME		"brcm/brcmfmac43430-sdio.bin"
+#define BCM43430_NVRAM_NAME		"brcm/brcmfmac43430-sdio.txt"
+#define BCM43455_FIRMWARE_NAME		"brcm/brcmfmac43455-sdio.bin"
+#define BCM43455_NVRAM_NAME		"brcm/brcmfmac43455-sdio.txt"
 #define BCM4354_FIRMWARE_NAME		"brcm/brcmfmac4354-sdio.bin"
 #define BCM4354_NVRAM_NAME		"brcm/brcmfmac4354-sdio.txt"
 
@@ -629,12 +634,18 @@
 MODULE_FIRMWARE(BCM4330_NVRAM_NAME);
 MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME);
 MODULE_FIRMWARE(BCM4334_NVRAM_NAME);
+MODULE_FIRMWARE(BCM43340_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM43340_NVRAM_NAME);
 MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME);
 MODULE_FIRMWARE(BCM4335_NVRAM_NAME);
 MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME);
 MODULE_FIRMWARE(BCM43362_NVRAM_NAME);
 MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME);
 MODULE_FIRMWARE(BCM4339_NVRAM_NAME);
+MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM43430_NVRAM_NAME);
+MODULE_FIRMWARE(BCM43455_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM43455_NVRAM_NAME);
 MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME);
 MODULE_FIRMWARE(BCM4354_NVRAM_NAME);
 
@@ -660,9 +671,12 @@
 	{ BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
 	{ BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
 	{ BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
+	{ BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43340) },
 	{ BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
 	{ BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
 	{ BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
+	{ BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) },
+	{ BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43455) },
 	{ BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
 };
 
@@ -953,13 +967,8 @@
 	brcmf_dbg(SDIO, "Enter\n");
 
 	/* Early exit if we're already there */
-	if (bus->clkstate == target) {
-		if (target == CLK_AVAIL) {
-			brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
-			bus->activity = true;
-		}
+	if (bus->clkstate == target)
 		return 0;
-	}
 
 	switch (target) {
 	case CLK_AVAIL:
@@ -968,8 +977,6 @@
 			brcmf_sdio_sdclk(bus, true);
 		/* Now request HT Avail on the backplane */
 		brcmf_sdio_htclk(bus, true, pendok);
-		brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
-		bus->activity = true;
 		break;
 
 	case CLK_SDONLY:
@@ -981,7 +988,6 @@
 		else
 			brcmf_err("request for %d -> %d\n",
 				  bus->clkstate, target);
-		brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
 		break;
 
 	case CLK_NONE:
@@ -990,7 +996,6 @@
 			brcmf_sdio_htclk(bus, false, false);
 		/* Now remove the SD clock */
 		brcmf_sdio_sdclk(bus, false);
-		brcmf_sdio_wd_timer(bus, 0);
 		break;
 	}
 #ifdef DEBUG
@@ -1018,16 +1023,6 @@
 
 		/* Going to sleep */
 		if (sleep) {
-			/* Don't sleep if something is pending */
-			if (atomic_read(&bus->intstatus) ||
-			    atomic_read(&bus->ipend) > 0 ||
-			    (!atomic_read(&bus->fcstate) &&
-			    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
-			    data_ok(bus))) {
-				 err = -EBUSY;
-				 goto done;
-			}
-
 			clkcsr = brcmf_sdiod_regrb(bus->sdiodev,
 						   SBSDIO_FUNC1_CHIPCLKCSR,
 						   &err);
@@ -1038,19 +1033,10 @@
 						  SBSDIO_ALP_AVAIL_REQ, &err);
 			}
 			err = brcmf_sdio_kso_control(bus, false);
-			/* disable watchdog */
-			if (!err)
-				brcmf_sdio_wd_timer(bus, 0);
 		} else {
-			bus->idlecount = 0;
 			err = brcmf_sdio_kso_control(bus, true);
 		}
-		if (!err) {
-			/* Change state */
-			bus->sleeping = sleep;
-			brcmf_dbg(SDIO, "new state %s\n",
-				  (sleep ? "SLEEP" : "WAKE"));
-		} else {
+		if (err) {
 			brcmf_err("error while changing bus sleep state %d\n",
 				  err);
 			goto done;
@@ -1064,7 +1050,11 @@
 			brcmf_sdio_clkctl(bus, CLK_NONE, pendok);
 	} else {
 		brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
+		brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
 	}
+	bus->sleeping = sleep;
+	brcmf_dbg(SDIO, "new state %s\n",
+		  (sleep ? "SLEEP" : "WAKE"));
 done:
 	brcmf_dbg(SDIO, "Exit: err=%d\n", err);
 	return err;
@@ -1080,44 +1070,47 @@
 static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
 				 struct sdpcm_shared *sh)
 {
-	u32 addr;
+	u32 addr = 0;
 	int rv;
 	u32 shaddr = 0;
 	struct sdpcm_shared_le sh_le;
 	__le32 addr_le;
 
-	shaddr = bus->ci->rambase + bus->ramsize - 4;
+	sdio_claim_host(bus->sdiodev->func[1]);
+	brcmf_sdio_bus_sleep(bus, false, false);
 
 	/*
 	 * Read last word in socram to determine
 	 * address of sdpcm_shared structure
 	 */
-	sdio_claim_host(bus->sdiodev->func[1]);
-	brcmf_sdio_bus_sleep(bus, false, false);
-	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4);
-	sdio_release_host(bus->sdiodev->func[1]);
+	shaddr = bus->ci->rambase + bus->ci->ramsize - 4;
+	if (!bus->ci->rambase && brcmf_chip_sr_capable(bus->ci))
+		shaddr -= bus->ci->srsize;
+	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr,
+			       (u8 *)&addr_le, 4);
 	if (rv < 0)
-		return rv;
-
-	addr = le32_to_cpu(addr_le);
-
-	brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr);
+		goto fail;
 
 	/*
 	 * Check if addr is valid.
 	 * NVRAM length at the end of memory should have been overwritten.
 	 */
+	addr = le32_to_cpu(addr_le);
 	if (!brcmf_sdio_valid_shared_address(addr)) {
-			brcmf_err("invalid sdpcm_shared address 0x%08X\n",
-				  addr);
-			return -EINVAL;
+		brcmf_err("invalid sdpcm_shared address 0x%08X\n", addr);
+		rv = -EINVAL;
+		goto fail;
 	}
 
+	brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr);
+
 	/* Read hndrte_shared structure */
 	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
 			       sizeof(struct sdpcm_shared_le));
 	if (rv < 0)
-		return rv;
+		goto fail;
+
+	sdio_release_host(bus->sdiodev->func[1]);
 
 	/* Endianness */
 	sh->flags = le32_to_cpu(sh_le.flags);
@@ -1134,8 +1127,13 @@
 			  sh->flags & SDPCM_SHARED_VERSION_MASK);
 		return -EPROTO;
 	}
-
 	return 0;
+
+fail:
+	brcmf_err("unable to obtain sdpcm_shared info: rv=%d (addr=0x%x)\n",
+		  rv, addr);
+	sdio_release_host(bus->sdiodev->func[1]);
+	return rv;
 }
 
 static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
@@ -1904,7 +1902,7 @@
 	bus->rxpending = true;
 
 	for (rd->seq_num = bus->rx_seq, rxleft = maxframes;
-	     !bus->rxskip && rxleft && brcmf_bus_ready(bus->sdiodev->bus_if);
+	     !bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_SDIOD_DATA;
 	     rd->seq_num++, rxleft--) {
 
 		/* Handle glomming separately */
@@ -2371,8 +2369,6 @@
 	/* Send frames until the limit or some other event */
 	for (cnt = 0; (cnt < maxframes) && data_ok(bus);) {
 		pkt_num = 1;
-		if (down_interruptible(&bus->tx_seq_lock))
-			return cnt;
 		if (bus->txglom)
 			pkt_num = min_t(u8, bus->tx_max - bus->tx_seq,
 					bus->sdiodev->txglomsz);
@@ -2388,13 +2384,10 @@
 			__skb_queue_tail(&pktq, pkt);
 		}
 		spin_unlock_bh(&bus->txq_lock);
-		if (i == 0) {
-			up(&bus->tx_seq_lock);
+		if (i == 0)
 			break;
-		}
 
 		ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL);
-		up(&bus->tx_seq_lock);
 
 		cnt += i;
 
@@ -2415,7 +2408,7 @@
 	}
 
 	/* Deflow-control stack if needed */
-	if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DATA) &&
+	if ((bus->sdiodev->state == BRCMF_SDIOD_DATA) &&
 	    bus->txoff && (pktq_len(&bus->txq) < TXLOW)) {
 		bus->txoff = false;
 		brcmf_txflowblock(bus->sdiodev->dev, false);
@@ -2503,7 +2496,7 @@
 		bus->watchdog_tsk = NULL;
 	}
 
-	if (bus_if->state == BRCMF_BUS_DOWN) {
+	if (sdiodev->state != BRCMF_SDIOD_NOMEDIUM) {
 		sdio_claim_host(sdiodev->func[1]);
 
 		/* Enable clock for device interrupts */
@@ -2538,8 +2531,7 @@
 	brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
 
 	/* Clear any held glomming stuff */
-	if (bus->glomd)
-		brcmu_pkt_buf_free_skb(bus->glomd);
+	brcmu_pkt_buf_free_skb(bus->glomd);
 	brcmf_sdio_free_glom(bus);
 
 	/* Clear rx control and wake any waiters */
@@ -2720,17 +2712,17 @@
 	brcmf_sdio_clrintr(bus);
 
 	if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) &&
-	    (down_interruptible(&bus->tx_seq_lock) == 0)) {
-		if (data_ok(bus)) {
-			sdio_claim_host(bus->sdiodev->func[1]);
+	    data_ok(bus)) {
+		sdio_claim_host(bus->sdiodev->func[1]);
+		if (bus->ctrl_frame_stat) {
 			err = brcmf_sdio_tx_ctrlframe(bus,  bus->ctrl_frame_buf,
 						      bus->ctrl_frame_len);
-			sdio_release_host(bus->sdiodev->func[1]);
-
+			bus->ctrl_frame_err = err;
+			wmb();
 			bus->ctrl_frame_stat = false;
-			brcmf_sdio_wait_event_wakeup(bus);
 		}
-		up(&bus->tx_seq_lock);
+		sdio_release_host(bus->sdiodev->func[1]);
+		brcmf_sdio_wait_event_wakeup(bus);
 	}
 	/* Send queued frames (limit 1 if rx may still be pending) */
 	if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
@@ -2741,15 +2733,25 @@
 		brcmf_sdio_sendfromq(bus, framecnt);
 	}
 
-	if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) {
+	if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) {
 		brcmf_err("failed backplane access over SDIO, halting operation\n");
 		atomic_set(&bus->intstatus, 0);
+		if (bus->ctrl_frame_stat) {
+			sdio_claim_host(bus->sdiodev->func[1]);
+			if (bus->ctrl_frame_stat) {
+				bus->ctrl_frame_err = -ENODEV;
+				wmb();
+				bus->ctrl_frame_stat = false;
+				brcmf_sdio_wait_event_wakeup(bus);
+			}
+			sdio_release_host(bus->sdiodev->func[1]);
+		}
 	} else if (atomic_read(&bus->intstatus) ||
 		   atomic_read(&bus->ipend) > 0 ||
 		   (!atomic_read(&bus->fcstate) &&
 		    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
 		    data_ok(bus))) {
-		atomic_inc(&bus->dpc_tskcnt);
+		bus->dpc_triggered = true;
 	}
 }
 
@@ -2848,11 +2850,7 @@
 		qcount[prec] = pktq_plen(&bus->txq, prec);
 #endif
 
-	if (atomic_read(&bus->dpc_tskcnt) == 0) {
-		atomic_inc(&bus->dpc_tskcnt);
-		queue_work(bus->brcmf_wq, &bus->datawork);
-	}
-
+	brcmf_sdio_trigger_dpc(bus);
 	return ret;
 }
 
@@ -2942,43 +2940,34 @@
 	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 ret = -1;
+	int ret;
 
 	brcmf_dbg(TRACE, "Enter\n");
 
-	if (down_interruptible(&bus->tx_seq_lock))
-		return -EINTR;
+	/* Send from dpc */
+	bus->ctrl_frame_buf = msg;
+	bus->ctrl_frame_len = msglen;
+	wmb();
+	bus->ctrl_frame_stat = true;
 
-	if (!data_ok(bus)) {
-		brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n",
-			  bus->tx_max, bus->tx_seq);
-		up(&bus->tx_seq_lock);
-		/* Send from dpc */
-		bus->ctrl_frame_buf = msg;
-		bus->ctrl_frame_len = msglen;
-		bus->ctrl_frame_stat = true;
-
-		wait_event_interruptible_timeout(bus->ctrl_wait,
-						 !bus->ctrl_frame_stat,
-						 msecs_to_jiffies(2000));
-
-		if (!bus->ctrl_frame_stat) {
-			brcmf_dbg(SDIO, "ctrl_frame_stat == false\n");
-			ret = 0;
-		} else {
-			brcmf_dbg(SDIO, "ctrl_frame_stat == true\n");
-			bus->ctrl_frame_stat = false;
-			if (down_interruptible(&bus->tx_seq_lock))
-				return -EINTR;
-			ret = -1;
-		}
-	}
-	if (ret == -1) {
+	brcmf_sdio_trigger_dpc(bus);
+	wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat,
+					 msecs_to_jiffies(CTL_DONE_TIMEOUT));
+	ret = 0;
+	if (bus->ctrl_frame_stat) {
 		sdio_claim_host(bus->sdiodev->func[1]);
-		brcmf_sdio_bus_sleep(bus, false, false);
-		ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen);
+		if (bus->ctrl_frame_stat) {
+			brcmf_dbg(SDIO, "ctrl_frame timeout\n");
+			bus->ctrl_frame_stat = false;
+			ret = -ETIMEDOUT;
+		}
 		sdio_release_host(bus->sdiodev->func[1]);
-		up(&bus->tx_seq_lock);
+	}
+	if (!ret) {
+		brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n",
+			  bus->ctrl_frame_err);
+		rmb();
+		ret = bus->ctrl_frame_err;
 	}
 
 	if (ret)
@@ -2986,7 +2975,7 @@
 	else
 		bus->sdcnt.tx_ctlpkts++;
 
-	return ret ? -EIO : 0;
+	return ret;
 }
 
 #ifdef DEBUG
@@ -3382,9 +3371,6 @@
 	sdio_claim_host(bus->sdiodev->func[1]);
 	brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
 
-	/* Keep arm in reset */
-	brcmf_chip_enter_download(bus->ci);
-
 	rstvec = get_unaligned_le32(fw->data);
 	brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec);
 
@@ -3404,13 +3390,13 @@
 	}
 
 	/* Take arm out of reset */
-	if (!brcmf_chip_exit_download(bus->ci, rstvec)) {
+	if (!brcmf_chip_set_active(bus->ci, rstvec)) {
 		brcmf_err("error getting out of ARM core reset\n");
 		goto err;
 	}
 
-	/* Allow HT Clock now that the ARM is running. */
-	brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_LOAD);
+	/* Allow full data communication using DPC from now on. */
+	brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA);
 	bcmerror = 0;
 
 err:
@@ -3547,6 +3533,14 @@
 	return err;
 }
 
+void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus)
+{
+	if (!bus->dpc_triggered) {
+		bus->dpc_triggered = true;
+		queue_work(bus->brcmf_wq, &bus->datawork);
+	}
+}
+
 void brcmf_sdio_isr(struct brcmf_sdio *bus)
 {
 	brcmf_dbg(TRACE, "Enter\n");
@@ -3556,7 +3550,7 @@
 		return;
 	}
 
-	if (!brcmf_bus_ready(bus->sdiodev->bus_if)) {
+	if (bus->sdiodev->state != BRCMF_SDIOD_DATA) {
 		brcmf_err("bus is down. we have nothing to do\n");
 		return;
 	}
@@ -3573,16 +3567,12 @@
 	if (!bus->intr)
 		brcmf_err("isr w/o interrupt configured!\n");
 
-	atomic_inc(&bus->dpc_tskcnt);
+	bus->dpc_triggered = true;
 	queue_work(bus->brcmf_wq, &bus->datawork);
 }
 
-static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
+static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
 {
-#ifdef DEBUG
-	struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev);
-#endif	/* DEBUG */
-
 	brcmf_dbg(TIMER, "Enter\n");
 
 	/* Poll period: check device if appropriate. */
@@ -3597,7 +3587,7 @@
 		if (!bus->intr ||
 		    (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {
 
-			if (atomic_read(&bus->dpc_tskcnt) == 0) {
+			if (!bus->dpc_triggered) {
 				u8 devpend;
 
 				sdio_claim_host(bus->sdiodev->func[1]);
@@ -3605,9 +3595,8 @@
 							    SDIO_CCCR_INTx,
 							    NULL);
 				sdio_release_host(bus->sdiodev->func[1]);
-				intstatus =
-				    devpend & (INTR_STATUS_FUNC1 |
-					       INTR_STATUS_FUNC2);
+				intstatus = devpend & (INTR_STATUS_FUNC1 |
+						       INTR_STATUS_FUNC2);
 			}
 
 			/* If there is something, make like the ISR and
@@ -3616,7 +3605,7 @@
 				bus->sdcnt.pollcnt++;
 				atomic_set(&bus->ipend, 1);
 
-				atomic_inc(&bus->dpc_tskcnt);
+				bus->dpc_triggered = true;
 				queue_work(bus->brcmf_wq, &bus->datawork);
 			}
 		}
@@ -3626,7 +3615,7 @@
 	}
 #ifdef DEBUG
 	/* Poll for console output periodically */
-	if (bus_if && bus_if->state == BRCMF_BUS_DATA &&
+	if (bus->sdiodev->state == BRCMF_SDIOD_DATA &&
 	    bus->console_interval != 0) {
 		bus->console.count += BRCMF_WD_POLL_MS;
 		if (bus->console.count >= bus->console_interval) {
@@ -3643,22 +3632,25 @@
 #endif				/* DEBUG */
 
 	/* On idle timeout clear activity flag and/or turn off clock */
-	if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
-		if (++bus->idlecount >= bus->idletime) {
-			bus->idlecount = 0;
-			if (bus->activity) {
-				bus->activity = false;
-				brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
-			} else {
+	if (!bus->dpc_triggered) {
+		rmb();
+		if ((!bus->dpc_running) && (bus->idletime > 0) &&
+		    (bus->clkstate == CLK_AVAIL)) {
+			bus->idlecount++;
+			if (bus->idlecount > bus->idletime) {
 				brcmf_dbg(SDIO, "idle\n");
 				sdio_claim_host(bus->sdiodev->func[1]);
+				brcmf_sdio_wd_timer(bus, 0);
+				bus->idlecount = 0;
 				brcmf_sdio_bus_sleep(bus, true, false);
 				sdio_release_host(bus->sdiodev->func[1]);
 			}
+		} else {
+			bus->idlecount = 0;
 		}
+	} else {
+		bus->idlecount = 0;
 	}
-
-	return (atomic_read(&bus->ipend) > 0);
 }
 
 static void brcmf_sdio_dataworker(struct work_struct *work)
@@ -3666,9 +3658,18 @@
 	struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio,
 					      datawork);
 
-	while (atomic_read(&bus->dpc_tskcnt)) {
-		atomic_set(&bus->dpc_tskcnt, 0);
+	bus->dpc_running = true;
+	wmb();
+	while (ACCESS_ONCE(bus->dpc_triggered)) {
+		bus->dpc_triggered = false;
 		brcmf_sdio_dpc(bus);
+		bus->idlecount = 0;
+	}
+	bus->dpc_running = false;
+	if (brcmf_sdiod_freezing(bus->sdiodev)) {
+		brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN);
+		brcmf_sdiod_try_freeze(bus->sdiodev);
+		brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA);
 	}
 }
 
@@ -3787,8 +3788,8 @@
 	return 0;
 }
 
-static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip,
-				      u32 rstvec)
+static void brcmf_sdio_buscore_activate(void *ctx, struct brcmf_chip *chip,
+					u32 rstvec)
 {
 	struct brcmf_sdio_dev *sdiodev = ctx;
 	struct brcmf_core *core;
@@ -3811,7 +3812,7 @@
 	u32 val, rev;
 
 	val = brcmf_sdiod_regrl(sdiodev, addr, NULL);
-	if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID &&
+	if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
 	    addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) {
 		rev = (val & CID_REV_MASK) >> CID_REV_SHIFT;
 		if (rev >= 2) {
@@ -3831,7 +3832,7 @@
 
 static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = {
 	.prepare = brcmf_sdio_buscoreprep,
-	.exit_dl = brcmf_sdio_buscore_exitdl,
+	.activate = brcmf_sdio_buscore_activate,
 	.read32 = brcmf_sdio_buscore_read32,
 	.write32 = brcmf_sdio_buscore_write32,
 };
@@ -3867,11 +3868,6 @@
 		goto fail;
 	}
 
-	/* SDIO register access works so moving
-	 * state from UNKNOWN to DOWN.
-	 */
-	brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN);
-
 	bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops);
 	if (IS_ERR(bus->ci)) {
 		brcmf_err("brcmf_chip_attach failed!\n");
@@ -3890,13 +3886,6 @@
 		drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
 	brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);
 
-	/* Get info on the SOCRAM cores... */
-	bus->ramsize = bus->ci->ramsize;
-	if (!(bus->ramsize)) {
-		brcmf_err("failed to find SOCRAM memory!\n");
-		goto fail;
-	}
-
 	/* Set card control so an SDIO card reset does a WLAN backplane reset */
 	reg_val = brcmf_sdiod_regrb(bus->sdiodev,
 				    SDIO_CCCR_BRCM_CARDCTRL, &err);
@@ -3952,13 +3941,19 @@
 brcmf_sdio_watchdog_thread(void *data)
 {
 	struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
+	int wait;
 
 	allow_signal(SIGTERM);
 	/* Run until signal received */
+	brcmf_sdiod_freezer_count(bus->sdiodev);
 	while (1) {
 		if (kthread_should_stop())
 			break;
-		if (!wait_for_completion_interruptible(&bus->watchdog_wait)) {
+		brcmf_sdiod_freezer_uncount(bus->sdiodev);
+		wait = wait_for_completion_interruptible(&bus->watchdog_wait);
+		brcmf_sdiod_freezer_count(bus->sdiodev);
+		brcmf_sdiod_try_freeze(bus->sdiodev);
+		if (!wait) {
 			brcmf_sdio_bus_watchdog(bus);
 			/* Count the tick for reference */
 			bus->sdcnt.tickcnt++;
@@ -3979,7 +3974,7 @@
 		/* Reschedule the watchdog */
 		if (bus->wd_timer_valid)
 			mod_timer(&bus->timer,
-				  jiffies + BRCMF_WD_POLL_MS * HZ / 1000);
+				  jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS));
 	}
 }
 
@@ -4005,18 +4000,16 @@
 
 	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;
 
+	/* try to download image and nvram to the dongle */
+	bus->alp_only = true;
+	err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len);
+	if (err)
+		goto fail;
+	bus->alp_only = false;
+
 	/* Start the watchdog timer */
 	bus->sdcnt.tickcnt = 0;
 	brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
@@ -4099,6 +4092,7 @@
 {
 	int ret;
 	struct brcmf_sdio *bus;
+	struct workqueue_struct *wq;
 
 	brcmf_dbg(TRACE, "Enter\n");
 
@@ -4127,12 +4121,16 @@
 			bus->sgentry_align = sdiodev->pdata->sd_sgentry_align;
 	}
 
-	INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
-	bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq");
-	if (bus->brcmf_wq == NULL) {
+	/* single-threaded workqueue */
+	wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM,
+				     dev_name(&sdiodev->func[1]->dev));
+	if (!wq) {
 		brcmf_err("insufficient memory to create txworkqueue\n");
 		goto fail;
 	}
+	brcmf_sdiod_freezer_count(sdiodev);
+	INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
+	bus->brcmf_wq = wq;
 
 	/* attempt to attach to the dongle */
 	if (!(brcmf_sdio_probe_attach(bus))) {
@@ -4142,7 +4140,6 @@
 
 	spin_lock_init(&bus->rxctl_lock);
 	spin_lock_init(&bus->txq_lock);
-	sema_init(&bus->tx_seq_lock, 1);
 	init_waitqueue_head(&bus->ctrl_wait);
 	init_waitqueue_head(&bus->dcmd_resp_wait);
 
@@ -4154,13 +4151,15 @@
 	/* Initialize watchdog thread */
 	init_completion(&bus->watchdog_wait);
 	bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread,
-					bus, "brcmf_watchdog");
+					bus, "brcmf_wdog/%s",
+					dev_name(&sdiodev->func[1]->dev));
 	if (IS_ERR(bus->watchdog_tsk)) {
 		pr_warn("brcmf_watchdog thread failed to start\n");
 		bus->watchdog_tsk = NULL;
 	}
 	/* Initialize DPC thread */
-	atomic_set(&bus->dpc_tskcnt, 0);
+	bus->dpc_triggered = false;
+	bus->dpc_running = false;
 
 	/* Assign bus interface call back */
 	bus->sdiodev->bus_if->dev = bus->sdiodev->dev;
@@ -4213,7 +4212,6 @@
 	bus->idleclock = BRCMF_IDLE_ACTIVE;
 
 	/* SR state */
-	bus->sleeping = false;
 	bus->sr_enabled = false;
 
 	brcmf_sdio_debugfs_create(bus);
@@ -4254,16 +4252,16 @@
 			destroy_workqueue(bus->brcmf_wq);
 
 		if (bus->ci) {
-			if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) {
+			if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) {
 				sdio_claim_host(bus->sdiodev->func[1]);
+				brcmf_sdio_wd_timer(bus, 0);
 				brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
 				/* Leave the device in state where it is
-				 * 'quiet'. This is done by putting it in
-				 * download_state which essentially resets
-				 * all necessary cores.
+				 * 'passive'. This is done by resetting all
+				 * necessary cores.
 				 */
 				msleep(20);
-				brcmf_chip_enter_download(bus->ci);
+				brcmf_chip_set_passive(bus->ci);
 				brcmf_sdio_clkctl(bus, CLK_NONE, false);
 				sdio_release_host(bus->sdiodev->func[1]);
 			}
@@ -4289,7 +4287,7 @@
 	}
 
 	/* don't start the wd until fw is loaded */
-	if (bus->sdiodev->bus_if->state != BRCMF_BUS_DATA)
+	if (bus->sdiodev->state != BRCMF_SDIOD_DATA)
 		return;
 
 	if (wdtick) {
@@ -4302,16 +4300,28 @@
 			   dynamically changed or in the first instance
 			 */
 			bus->timer.expires =
-				jiffies + BRCMF_WD_POLL_MS * HZ / 1000;
+				jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS);
 			add_timer(&bus->timer);
 
 		} else {
 			/* Re arm the timer, at last watchdog period */
 			mod_timer(&bus->timer,
-				jiffies + BRCMF_WD_POLL_MS * HZ / 1000);
+				jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS));
 		}
 
 		bus->wd_timer_valid = true;
 		bus->save_ms = wdtick;
 	}
 }
+
+int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep)
+{
+	int ret;
+
+	sdio_claim_host(bus->sdiodev->func[1]);
+	ret = brcmf_sdio_bus_sleep(bus, sleep, false);
+	sdio_release_host(bus->sdiodev->func[1]);
+
+	return ret;
+}
+
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h
index 8eb4262..7328478 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h
@@ -155,6 +155,19 @@
 /* watchdog polling interval in ms */
 #define BRCMF_WD_POLL_MS	10
 
+/**
+ * enum brcmf_sdiod_state - the state of the bus.
+ *
+ * @BRCMF_SDIOD_DOWN: Device can be accessed, no DPC.
+ * @BRCMF_SDIOD_DATA: Ready for data transfers, DPC enabled.
+ * @BRCMF_SDIOD_NOMEDIUM: No medium access to dongle possible.
+ */
+enum brcmf_sdiod_state {
+	BRCMF_SDIOD_DOWN,
+	BRCMF_SDIOD_DATA,
+	BRCMF_SDIOD_NOMEDIUM
+};
+
 struct brcmf_sdreg {
 	int func;
 	int offset;
@@ -162,15 +175,13 @@
 };
 
 struct brcmf_sdio;
+struct brcmf_sdiod_freezer;
 
 struct brcmf_sdio_dev {
 	struct sdio_func *func[SDIO_MAX_FUNCS];
 	u8 num_funcs;			/* Supported funcs on client */
 	u32 sbwad;			/* Save backplane window address */
 	struct brcmf_sdio *bus;
-	atomic_t suspend;		/* suspend flag */
-	wait_queue_head_t request_word_wait;
-	wait_queue_head_t request_buffer_wait;
 	struct device *dev;
 	struct brcmf_bus *bus_if;
 	struct brcmfmac_sdio_platform_data *pdata;
@@ -187,6 +198,8 @@
 	char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
 	char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
 	bool wowl_enabled;
+	enum brcmf_sdiod_state state;
+	struct brcmf_sdiod_freezer *freezer;
 };
 
 /* sdio core registers */
@@ -329,6 +342,28 @@
 
 /* Issue an abort to the specified function */
 int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
+void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
+			      enum brcmf_sdiod_state state);
+#ifdef CONFIG_PM_SLEEP
+bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev);
+#else
+static inline bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev)
+{
+	return false;
+}
+static inline void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev)
+{
+}
+static inline void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev)
+{
+}
+static inline void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev)
+{
+}
+#endif /* CONFIG_PM_SLEEP */
 
 struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
 void brcmf_sdio_remove(struct brcmf_sdio *bus);
@@ -336,5 +371,7 @@
 
 void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick);
 void brcmf_sdio_wowl_config(struct device *dev, bool enabled);
+int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep);
+void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus);
 
 #endif /* BRCMFMAC_SDIO_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
index a662707..abb95d8 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
@@ -421,7 +421,7 @@
 	brcmf_err("fail!\n");
 	while (!list_empty(q)) {
 		req = list_entry(q->next, struct brcmf_usbreq, list);
-		if (req && req->urb)
+		if (req)
 			usb_free_urb(req->urb);
 		list_del(q->next);
 	}
@@ -576,7 +576,7 @@
 		brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DOWN);
 	} else if (state == BRCMFMAC_USB_STATE_UP) {
 		brcmf_dbg(USB, "DBUS is up\n");
-		brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DATA);
+		brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_UP);
 	} else {
 		brcmf_dbg(USB, "DBUS current state=%d\n", state);
 	}
@@ -1263,6 +1263,8 @@
 		ret = brcmf_usb_bus_setup(devinfo);
 		if (ret)
 			goto fail;
+		/* we are done */
+		return 0;
 	}
 	bus->chip = bus_pub->devid;
 	bus->chiprev = bus_pub->chiprev;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c
index 50cdf70..8eff275 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c
@@ -39,13 +39,22 @@
 	void *dcmd_buf = NULL, *wr_pointer;
 	u16 msglen, maxmsglen = PAGE_SIZE - 0x100;
 
-	brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set,
-		  cmdhdr->len);
+	if (len < sizeof(*cmdhdr)) {
+		brcmf_err("vendor command too short: %d\n", len);
+		return -EINVAL;
+	}
 
 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
 	ifp = vif->ifp;
 
-	len -= sizeof(struct brcmf_vndr_dcmd_hdr);
+	brcmf_dbg(TRACE, "ifidx=%d, cmd=%d\n", ifp->ifidx, cmdhdr->cmd);
+
+	if (cmdhdr->offset > len) {
+		brcmf_err("bad buffer offset %d > %d\n", cmdhdr->offset, len);
+		return -EINVAL;
+	}
+
+	len -= cmdhdr->offset;
 	ret_len = cmdhdr->len;
 	if (ret_len > 0 || len > 0) {
 		if (len > BRCMF_DCMD_MAXLEN) {
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h
new file mode 100644
index 0000000..b896800
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2011 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.
+ */
+
+#if !defined(__TRACE_BRCMSMAC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __TRACE_BRCMSMAC_H
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM brcmsmac
+
+/*
+ * We define a tracepoint, its arguments, its printk format and its
+ * 'fast binary record' layout.
+ */
+TRACE_EVENT(brcms_timer,
+	/* TPPROTO is the prototype of the function called by this tracepoint */
+	TP_PROTO(struct brcms_timer *t),
+	/*
+	 * TPARGS(firstarg, p) are the parameters names, same as found in the
+	 * prototype.
+	 */
+	TP_ARGS(t),
+	/*
+	 * Fast binary tracing: define the trace record via TP_STRUCT__entry().
+	 * You can think about it like a regular C structure local variable
+	 * definition.
+	 */
+	TP_STRUCT__entry(
+		__field(uint, ms)
+		__field(uint, set)
+		__field(uint, periodic)
+	),
+	TP_fast_assign(
+		__entry->ms = t->ms;
+		__entry->set = t->set;
+		__entry->periodic = t->periodic;
+	),
+	TP_printk(
+		"ms=%u set=%u periodic=%u",
+		__entry->ms, __entry->set, __entry->periodic
+	)
+);
+
+TRACE_EVENT(brcms_dpc,
+	TP_PROTO(unsigned long data),
+	TP_ARGS(data),
+	TP_STRUCT__entry(
+		__field(unsigned long, data)
+	),
+	TP_fast_assign(
+		__entry->data = data;
+	),
+	TP_printk(
+		"data=%p",
+		(void *)__entry->data
+	)
+);
+
+TRACE_EVENT(brcms_macintstatus,
+	TP_PROTO(const struct device *dev, int in_isr, u32 macintstatus,
+		 u32 mask),
+	TP_ARGS(dev, in_isr, macintstatus, mask),
+	TP_STRUCT__entry(
+		__string(dev, dev_name(dev))
+		__field(int, in_isr)
+		__field(u32, macintstatus)
+		__field(u32, mask)
+	),
+	TP_fast_assign(
+		__assign_str(dev, dev_name(dev));
+		__entry->in_isr = in_isr;
+		__entry->macintstatus = macintstatus;
+		__entry->mask = mask;
+	),
+	TP_printk("[%s] in_isr=%d macintstatus=%#x mask=%#x", __get_str(dev),
+		  __entry->in_isr, __entry->macintstatus, __entry->mask)
+);
+#endif /* __TRACE_BRCMSMAC_H */
+
+#ifdef CPTCFG_BRCM_TRACING
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac
+#include <trace/define_trace.h>
+
+#endif /* CPTCFG_BRCM_TRACING */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h
new file mode 100644
index 0000000..5fd2638
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011 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.
+ */
+
+#if !defined(__TRACE_BRCMSMAC_MSG_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __TRACE_BRCMSMAC_MSG_H
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM brcmsmac_msg
+
+#define MAX_MSG_LEN	100
+
+DECLARE_EVENT_CLASS(brcms_msg_event,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf),
+	TP_STRUCT__entry(
+		__dynamic_array(char, msg, MAX_MSG_LEN)
+	),
+	TP_fast_assign(
+		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+				       MAX_MSG_LEN, vaf->fmt,
+				       *vaf->va) >= MAX_MSG_LEN);
+	),
+	TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(brcms_msg_event, brcms_info,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(brcms_msg_event, brcms_warn,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(brcms_msg_event, brcms_err,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(brcms_msg_event, brcms_crit,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+
+TRACE_EVENT(brcms_dbg,
+	TP_PROTO(u32 level, const char *func, struct va_format *vaf),
+	TP_ARGS(level, func, vaf),
+	TP_STRUCT__entry(
+		__field(u32, level)
+		__string(func, func)
+		__dynamic_array(char, msg, MAX_MSG_LEN)
+	),
+	TP_fast_assign(
+		__entry->level = level;
+		__assign_str(func, func);
+		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+				       MAX_MSG_LEN, vaf->fmt,
+				       *vaf->va) >= MAX_MSG_LEN);
+	),
+	TP_printk("%s: %s", __get_str(func), __get_str(msg))
+);
+#endif /* __TRACE_BRCMSMAC_MSG_H */
+
+#ifdef CPTCFG_BRCM_TRACING
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac_msg
+#include <trace/define_trace.h>
+
+#endif /* CPTCFG_BRCM_TRACING */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h
new file mode 100644
index 0000000..10660f6
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2011 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.
+ */
+
+#if !defined(__TRACE_BRCMSMAC_TX_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __TRACE_BRCMSMAC_TX_H
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM brcmsmac_tx
+
+TRACE_EVENT(brcms_txdesc,
+	TP_PROTO(const struct device *dev,
+		 void *txh, size_t txh_len),
+	TP_ARGS(dev, txh, txh_len),
+	TP_STRUCT__entry(
+		__string(dev, dev_name(dev))
+		__dynamic_array(u8, txh, txh_len)
+	),
+	TP_fast_assign(
+		__assign_str(dev, dev_name(dev));
+		memcpy(__get_dynamic_array(txh), txh, txh_len);
+	),
+	TP_printk("[%s] txdesc", __get_str(dev))
+);
+
+TRACE_EVENT(brcms_txstatus,
+	TP_PROTO(const struct device *dev, u16 framelen, u16 frameid,
+		 u16 status, u16 lasttxtime, u16 sequence, u16 phyerr,
+		 u16 ackphyrxsh),
+	TP_ARGS(dev, framelen, frameid, status, lasttxtime, sequence, phyerr,
+		ackphyrxsh),
+	TP_STRUCT__entry(
+		__string(dev, dev_name(dev))
+		__field(u16, framelen)
+		__field(u16, frameid)
+		__field(u16, status)
+		__field(u16, lasttxtime)
+		__field(u16, sequence)
+		__field(u16, phyerr)
+		__field(u16, ackphyrxsh)
+	),
+	TP_fast_assign(
+		__assign_str(dev, dev_name(dev));
+		__entry->framelen = framelen;
+		__entry->frameid = frameid;
+		__entry->status = status;
+		__entry->lasttxtime = lasttxtime;
+		__entry->sequence = sequence;
+		__entry->phyerr = phyerr;
+		__entry->ackphyrxsh = ackphyrxsh;
+	),
+	TP_printk("[%s] FrameId %#04x TxStatus %#04x LastTxTime %#04x "
+		  "Seq %#04x PHYTxStatus %#04x RxAck %#04x",
+		  __get_str(dev), __entry->frameid, __entry->status,
+		  __entry->lasttxtime, __entry->sequence, __entry->phyerr,
+		  __entry->ackphyrxsh)
+);
+
+TRACE_EVENT(brcms_ampdu_session,
+	TP_PROTO(const struct device *dev, unsigned max_ampdu_len,
+		 u16 max_ampdu_frames, u16 ampdu_len, u16 ampdu_frames,
+		 u16 dma_len),
+	TP_ARGS(dev, max_ampdu_len, max_ampdu_frames, ampdu_len, ampdu_frames,
+		dma_len),
+	TP_STRUCT__entry(
+		__string(dev, dev_name(dev))
+		__field(unsigned, max_ampdu_len)
+		__field(u16, max_ampdu_frames)
+		__field(u16, ampdu_len)
+		__field(u16, ampdu_frames)
+		__field(u16, dma_len)
+	),
+	TP_fast_assign(
+		__assign_str(dev, dev_name(dev));
+		__entry->max_ampdu_len = max_ampdu_len;
+		__entry->max_ampdu_frames = max_ampdu_frames;
+		__entry->ampdu_len = ampdu_len;
+		__entry->ampdu_frames = ampdu_frames;
+		__entry->dma_len = dma_len;
+	),
+	TP_printk("[%s] ampdu session max_len=%u max_frames=%u len=%u frames=%u dma_len=%u",
+		  __get_str(dev), __entry->max_ampdu_len,
+		  __entry->max_ampdu_frames, __entry->ampdu_len,
+		  __entry->ampdu_frames, __entry->dma_len)
+);
+#endif /* __TRACE_BRCMSMAC_TX_H */
+
+#ifdef CPTCFG_BRCM_TRACING
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac_tx
+#include <trace/define_trace.h>
+
+#endif /* CPTCFG_BRCM_TRACING */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h
index 18dda96..d3644af 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h
@@ -14,9 +14,8 @@
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#if !defined(__TRACE_BRCMSMAC_H) || defined(TRACE_HEADER_MULTI_READ)
-
-#define __TRACE_BRCMSMAC_H
+#ifndef __BRCMS_TRACE_EVENTS_H
+#define __BRCMS_TRACE_EVENTS_H
 
 #include <linux/types.h>
 #include <linux/device.h>
@@ -34,222 +33,8 @@
 static inline void trace_ ## name(proto) {}
 #endif
 
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM brcmsmac
-
-/*
- * We define a tracepoint, its arguments, its printk format and its
- * 'fast binary record' layout.
- */
-TRACE_EVENT(brcms_timer,
-	/* TPPROTO is the prototype of the function called by this tracepoint */
-	TP_PROTO(struct brcms_timer *t),
-	/*
-	 * TPARGS(firstarg, p) are the parameters names, same as found in the
-	 * prototype.
-	 */
-	TP_ARGS(t),
-	/*
-	 * Fast binary tracing: define the trace record via TP_STRUCT__entry().
-	 * You can think about it like a regular C structure local variable
-	 * definition.
-	 */
-	TP_STRUCT__entry(
-		__field(uint, ms)
-		__field(uint, set)
-		__field(uint, periodic)
-	),
-	TP_fast_assign(
-		__entry->ms = t->ms;
-		__entry->set = t->set;
-		__entry->periodic = t->periodic;
-	),
-	TP_printk(
-		"ms=%u set=%u periodic=%u",
-		__entry->ms, __entry->set, __entry->periodic
-	)
-);
-
-TRACE_EVENT(brcms_dpc,
-	TP_PROTO(unsigned long data),
-	TP_ARGS(data),
-	TP_STRUCT__entry(
-		__field(unsigned long, data)
-	),
-	TP_fast_assign(
-		__entry->data = data;
-	),
-	TP_printk(
-		"data=%p",
-		(void *)__entry->data
-	)
-);
-
-TRACE_EVENT(brcms_macintstatus,
-	TP_PROTO(const struct device *dev, int in_isr, u32 macintstatus,
-		 u32 mask),
-	TP_ARGS(dev, in_isr, macintstatus, mask),
-	TP_STRUCT__entry(
-		__string(dev, dev_name(dev))
-		__field(int, in_isr)
-		__field(u32, macintstatus)
-		__field(u32, mask)
-	),
-	TP_fast_assign(
-		__assign_str(dev, dev_name(dev));
-		__entry->in_isr = in_isr;
-		__entry->macintstatus = macintstatus;
-		__entry->mask = mask;
-	),
-	TP_printk("[%s] in_isr=%d macintstatus=%#x mask=%#x", __get_str(dev),
-		  __entry->in_isr, __entry->macintstatus, __entry->mask)
-);
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM brcmsmac_tx
-
-TRACE_EVENT(brcms_txdesc,
-	TP_PROTO(const struct device *dev,
-		 void *txh, size_t txh_len),
-	TP_ARGS(dev, txh, txh_len),
-	TP_STRUCT__entry(
-		__string(dev, dev_name(dev))
-		__dynamic_array(u8, txh, txh_len)
-	),
-	TP_fast_assign(
-		__assign_str(dev, dev_name(dev));
-		memcpy(__get_dynamic_array(txh), txh, txh_len);
-	),
-	TP_printk("[%s] txdesc", __get_str(dev))
-);
-
-TRACE_EVENT(brcms_txstatus,
-	TP_PROTO(const struct device *dev, u16 framelen, u16 frameid,
-		 u16 status, u16 lasttxtime, u16 sequence, u16 phyerr,
-		 u16 ackphyrxsh),
-	TP_ARGS(dev, framelen, frameid, status, lasttxtime, sequence, phyerr,
-		ackphyrxsh),
-	TP_STRUCT__entry(
-		__string(dev, dev_name(dev))
-		__field(u16, framelen)
-		__field(u16, frameid)
-		__field(u16, status)
-		__field(u16, lasttxtime)
-		__field(u16, sequence)
-		__field(u16, phyerr)
-		__field(u16, ackphyrxsh)
-	),
-	TP_fast_assign(
-		__assign_str(dev, dev_name(dev));
-		__entry->framelen = framelen;
-		__entry->frameid = frameid;
-		__entry->status = status;
-		__entry->lasttxtime = lasttxtime;
-		__entry->sequence = sequence;
-		__entry->phyerr = phyerr;
-		__entry->ackphyrxsh = ackphyrxsh;
-	),
-	TP_printk("[%s] FrameId %#04x TxStatus %#04x LastTxTime %#04x "
-		  "Seq %#04x PHYTxStatus %#04x RxAck %#04x",
-		  __get_str(dev), __entry->frameid, __entry->status,
-		  __entry->lasttxtime, __entry->sequence, __entry->phyerr,
-		  __entry->ackphyrxsh)
-);
-
-TRACE_EVENT(brcms_ampdu_session,
-	TP_PROTO(const struct device *dev, unsigned max_ampdu_len,
-		 u16 max_ampdu_frames, u16 ampdu_len, u16 ampdu_frames,
-		 u16 dma_len),
-	TP_ARGS(dev, max_ampdu_len, max_ampdu_frames, ampdu_len, ampdu_frames,
-		dma_len),
-	TP_STRUCT__entry(
-		__string(dev, dev_name(dev))
-		__field(unsigned, max_ampdu_len)
-		__field(u16, max_ampdu_frames)
-		__field(u16, ampdu_len)
-		__field(u16, ampdu_frames)
-		__field(u16, dma_len)
-	),
-	TP_fast_assign(
-		__assign_str(dev, dev_name(dev));
-		__entry->max_ampdu_len = max_ampdu_len;
-		__entry->max_ampdu_frames = max_ampdu_frames;
-		__entry->ampdu_len = ampdu_len;
-		__entry->ampdu_frames = ampdu_frames;
-		__entry->dma_len = dma_len;
-	),
-	TP_printk("[%s] ampdu session max_len=%u max_frames=%u len=%u frames=%u dma_len=%u",
-		  __get_str(dev), __entry->max_ampdu_len,
-		  __entry->max_ampdu_frames, __entry->ampdu_len,
-		  __entry->ampdu_frames, __entry->dma_len)
-);
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM brcmsmac_msg
-
-#define MAX_MSG_LEN	100
-
-DECLARE_EVENT_CLASS(brcms_msg_event,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf),
-	TP_STRUCT__entry(
-		__dynamic_array(char, msg, MAX_MSG_LEN)
-	),
-	TP_fast_assign(
-		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
-				       MAX_MSG_LEN, vaf->fmt,
-				       *vaf->va) >= MAX_MSG_LEN);
-	),
-	TP_printk("%s", __get_str(msg))
-);
-
-DEFINE_EVENT(brcms_msg_event, brcms_info,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf)
-);
-
-DEFINE_EVENT(brcms_msg_event, brcms_warn,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf)
-);
-
-DEFINE_EVENT(brcms_msg_event, brcms_err,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf)
-);
-
-DEFINE_EVENT(brcms_msg_event, brcms_crit,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf)
-);
-
-TRACE_EVENT(brcms_dbg,
-	TP_PROTO(u32 level, const char *func, struct va_format *vaf),
-	TP_ARGS(level, func, vaf),
-	TP_STRUCT__entry(
-		__field(u32, level)
-		__string(func, func)
-		__dynamic_array(char, msg, MAX_MSG_LEN)
-	),
-	TP_fast_assign(
-		__entry->level = level;
-		__assign_str(func, func);
-		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
-				       MAX_MSG_LEN, vaf->fmt,
-				       *vaf->va) >= MAX_MSG_LEN);
-	),
-	TP_printk("%s: %s", __get_str(func), __get_str(msg))
-);
+#include "brcms_trace_brcmsmac.h"
+#include "brcms_trace_brcmsmac_tx.h"
+#include "brcms_trace_brcmsmac_msg.h"
 
 #endif /* __TRACE_BRCMSMAC_H */
-
-#ifdef CPTCFG_BRCM_TRACING
-
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-#undef TRACE_INCLUDE_FILE
-#define TRACE_INCLUDE_FILE brcms_trace_events
-
-#include <trace/define_trace.h>
-
-#endif /* CPTCFG_BRCM_TRACING */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/brcm80211/brcmsmac/debug.c
index d382d94..ca4c3d0 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/debug.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/debug.c
@@ -78,7 +78,7 @@
 	struct brcms_hardware *hw = drvr->wlc->hw;
 	struct bcma_device *core = hw->d11core;
 	struct bcma_bus *bus = core->bus;
-	char boardrev[10];
+	char boardrev[BRCMU_BOARDREV_LEN];
 
 	seq_printf(s, "chipnum 0x%x\n"
 		   "chiprev 0x%x\n"
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index ac50a21..fa4c0ff 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -99,7 +99,7 @@
 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 17, BCMA_ANY_CLASS),
 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 23, BCMA_ANY_CLASS),
 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 24, BCMA_ANY_CLASS),
-	BCMA_CORETABLE_END
+	{},
 };
 MODULE_DEVICE_TABLE(bcma, brcms_coreid_table);
 
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c
index eb8584a..369527e 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/main.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c
@@ -4668,7 +4668,7 @@
 	brcms_c_coredisable(wlc_hw);
 
 	/* Match driver "down" state */
-	bcma_core_pci_down(wlc_hw->d11core->bus);
+	bcma_host_pci_down(wlc_hw->d11core->bus);
 
 	/* turn off pll and xtal to match driver "down" state */
 	brcms_b_xtal(wlc_hw, OFF);
@@ -4959,7 +4959,7 @@
 	 * Configure pci/pcmcia here instead of in brcms_c_attach()
 	 * to allow mfg hotswap:  down, hotswap (chip power cycle), up.
 	 */
-	bcma_core_pci_irq_ctl(&wlc_hw->d11core->bus->drv_pci[0], wlc_hw->d11core,
+	bcma_host_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core,
 			      true);
 
 	/*
@@ -4969,12 +4969,12 @@
 	 */
 	if (brcms_b_radio_read_hwdisabled(wlc_hw)) {
 		/* put SB PCI in down state again */
-		bcma_core_pci_down(wlc_hw->d11core->bus);
+		bcma_host_pci_down(wlc_hw->d11core->bus);
 		brcms_b_xtal(wlc_hw, OFF);
 		return -ENOMEDIUM;
 	}
 
-	bcma_core_pci_up(wlc_hw->d11core->bus);
+	bcma_host_pci_up(wlc_hw->d11core->bus);
 
 	/* reset the d11 core */
 	brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS);
@@ -5171,7 +5171,7 @@
 
 		/* turn off primary xtal and pll */
 		if (!wlc_hw->noreset) {
-			bcma_core_pci_down(wlc_hw->d11core->bus);
+			bcma_host_pci_down(wlc_hw->d11core->bus);
 			brcms_b_xtal(wlc_hw, OFF);
 		}
 	}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
index 941b1e4..1c4e9dd 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
@@ -2949,5 +2949,5 @@
 	if (ISNPHY(pi))
 		return wlc_phy_n_txpower_ipa_ison(pi);
 	else
-		return 0;
+		return false;
 }
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
index 5f13662..93d4cde 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
@@ -4999,7 +4999,7 @@
 bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi)
 {
 	if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi))
-		return 0;
+		return false;
 	else
 		return (LCNPHY_TX_PWR_CTRL_HW ==
 			wlc_lcnphy_get_tx_pwr_ctrl((pi)));
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c
index 084f18f..99dac9b 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c
@@ -23041,10 +23041,7 @@
 	else if (rssi_ctrl_state[0] == RADIO_2055_WBRSSI_G1_SEL)
 		wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1,
 				     NPHY_RSSI_SEL_W1);
-	else if (rssi_ctrl_state[0] == RADIO_2055_WBRSSI_G2_SEL)
-		wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1,
-				     NPHY_RSSI_SEL_W2);
-	else
+	else /* RADIO_2055_WBRSSI_G2_SEL */
 		wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1,
 				     NPHY_RSSI_SEL_W2);
 	if (rssi_ctrl_state[1] == RADIO_2055_NBRSSI_SEL)
@@ -23053,13 +23050,9 @@
 	else if (rssi_ctrl_state[1] == RADIO_2055_WBRSSI_G1_SEL)
 		wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2,
 				     NPHY_RSSI_SEL_W1);
-	else if (rssi_ctrl_state[1] == RADIO_2055_WBRSSI_G2_SEL)
+	else /* RADIO_2055_WBRSSI_G1_SEL */
 		wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2,
 				     NPHY_RSSI_SEL_W2);
-	else
-		wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2,
-				     NPHY_RSSI_SEL_W2);
-
 	wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_OFF, rssi_type);
 
 	write_phy_reg(pi, 0x91, rfctrlintc_state[0]);
diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/brcm80211/brcmutil/utils.c
index 906e89d..0543607 100644
--- a/drivers/net/wireless/brcm80211/brcmutil/utils.c
+++ b/drivers/net/wireless/brcm80211/brcmutil/utils.c
@@ -267,15 +267,43 @@
 	char c;
 
 	if (brev < 0x100) {
-		snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf);
+		snprintf(buf, BRCMU_BOARDREV_LEN, "%d.%d",
+			 (brev & 0xf0) >> 4, brev & 0xf);
 	} else {
 		c = (brev & 0xf000) == 0x1000 ? 'P' : 'A';
-		snprintf(buf, 8, "%c%03x", c, brev & 0xfff);
+		snprintf(buf, BRCMU_BOARDREV_LEN, "%c%03x", c, brev & 0xfff);
 	}
 	return buf;
 }
 EXPORT_SYMBOL(brcmu_boardrev_str);
 
+char *brcmu_dotrev_str(u32 dotrev, char *buf)
+{
+	u8 dotval[4];
+
+	if (!dotrev) {
+		snprintf(buf, BRCMU_DOTREV_LEN, "unknown");
+		return buf;
+	}
+	dotval[0] = (dotrev >> 24) & 0xFF;
+	dotval[1] = (dotrev >> 16) & 0xFF;
+	dotval[2] = (dotrev >> 8) & 0xFF;
+	dotval[3] = dotrev & 0xFF;
+
+	if (dotval[3])
+		snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d.%d", dotval[0],
+			dotval[1], dotval[2], dotval[3]);
+	else if (dotval[2])
+		snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d", dotval[0],
+			dotval[1], dotval[2]);
+	else
+		snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d", dotval[0],
+			dotval[1]);
+
+	return buf;
+}
+EXPORT_SYMBOL(brcmu_dotrev_str);
+
 #if defined(DEBUG)
 /* pretty hex print a pkt buffer chain */
 void brcmu_prpkt(const char *msg, struct sk_buff *p0)
diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
index 6996fcc..4efdd51 100644
--- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
+++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
@@ -22,7 +22,6 @@
 
 #define BRCM_USB_VENDOR_ID_BROADCOM	0x0a5c
 #define BRCM_PCIE_VENDOR_ID_BROADCOM	PCI_VENDOR_ID_BROADCOM
-#define BRCM_SDIO_VENDOR_ID_BROADCOM	SDIO_VENDOR_ID_BROADCOM
 
 /* Chipcommon Core Chip IDs */
 #define BRCM_CC_43143_CHIP_ID		43143
@@ -34,9 +33,12 @@
 #define BRCM_CC_4329_CHIP_ID		0x4329
 #define BRCM_CC_4330_CHIP_ID		0x4330
 #define BRCM_CC_4334_CHIP_ID		0x4334
+#define BRCM_CC_43340_CHIP_ID		43340
 #define BRCM_CC_43362_CHIP_ID		43362
 #define BRCM_CC_4335_CHIP_ID		0x4335
 #define BRCM_CC_4339_CHIP_ID		0x4339
+#define BRCM_CC_43430_CHIP_ID		43430
+#define BRCM_CC_4345_CHIP_ID		0x4345
 #define BRCM_CC_4354_CHIP_ID		0x4354
 #define BRCM_CC_4356_CHIP_ID		0x4356
 #define BRCM_CC_43566_CHIP_ID		43566
@@ -45,16 +47,6 @@
 #define BRCM_CC_43570_CHIP_ID		43570
 #define BRCM_CC_43602_CHIP_ID		43602
 
-/* SDIO Device IDs */
-#define BRCM_SDIO_43143_DEVICE_ID	BRCM_CC_43143_CHIP_ID
-#define BRCM_SDIO_43241_DEVICE_ID	BRCM_CC_43241_CHIP_ID
-#define BRCM_SDIO_4329_DEVICE_ID	BRCM_CC_4329_CHIP_ID
-#define BRCM_SDIO_4330_DEVICE_ID	BRCM_CC_4330_CHIP_ID
-#define BRCM_SDIO_4334_DEVICE_ID	BRCM_CC_4334_CHIP_ID
-#define BRCM_SDIO_43362_DEVICE_ID	BRCM_CC_43362_CHIP_ID
-#define BRCM_SDIO_4335_4339_DEVICE_ID	BRCM_CC_4335_CHIP_ID
-#define BRCM_SDIO_4354_DEVICE_ID	BRCM_CC_4354_CHIP_ID
-
 /* USB Device IDs */
 #define BRCM_USB_43143_DEVICE_ID	0xbd1e
 #define BRCM_USB_43236_DEVICE_ID	0xbd17
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/brcm80211/include/brcmu_utils.h
index a043e29..4196952 100644
--- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h
+++ b/drivers/net/wireless/brcm80211/include/brcmu_utils.h
@@ -218,6 +218,10 @@
 }
 #endif
 
+#define BRCMU_BOARDREV_LEN	8
+#define BRCMU_DOTREV_LEN	16
+
 char *brcmu_boardrev_str(u32 brev, char *buf);
+char *brcmu_dotrev_str(u32 dotrev, char *buf);
 
 #endif				/* _BRCMU_UTILS_H_ */
diff --git a/drivers/net/wireless/brcm80211/include/chipcommon.h b/drivers/net/wireless/brcm80211/include/chipcommon.h
index d242333..e1fd499 100644
--- a/drivers/net/wireless/brcm80211/include/chipcommon.h
+++ b/drivers/net/wireless/brcm80211/include/chipcommon.h
@@ -183,7 +183,14 @@
 	u8 uart1lsr;
 	u8 uart1msr;
 	u8 uart1scratch;
-	u32 PAD[126];
+	u32 PAD[62];
+
+	/* save/restore, corerev >= 48 */
+	u32 sr_capability;          /* 0x500 */
+	u32 sr_control0;            /* 0x504 */
+	u32 sr_control1;            /* 0x508 */
+	u32 gpio_control;           /* 0x50C */
+	u32 PAD[60];
 
 	/* PMU registers (corerev >= 20) */
 	u32 pmucontrol;	/* 0x600 */
diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c
index 964b64a..7603546 100644
--- a/drivers/net/wireless/cw1200/cw1200_spi.c
+++ b/drivers/net/wireless/cw1200/cw1200_spi.c
@@ -447,7 +447,7 @@
 }
 
 #ifdef CONFIG_PM
-static int cw1200_spi_suspend(struct device *dev, pm_message_t state)
+static int cw1200_spi_suspend(struct device *dev)
 {
 	struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev));
 
@@ -458,10 +458,8 @@
 	return 0;
 }
 
-static int cw1200_spi_resume(struct device *dev)
-{
-	return 0;
-}
+static SIMPLE_DEV_PM_OPS(cw1200_pm_ops, cw1200_spi_suspend, NULL);
+
 #endif
 
 static struct spi_driver spi_driver = {
@@ -472,8 +470,7 @@
 		.bus            = &spi_bus_type,
 		.owner          = THIS_MODULE,
 #ifdef CONFIG_PM
-		.suspend        = cw1200_spi_suspend,
-		.resume         = cw1200_spi_resume,
+		.pm		= &cw1200_pm_ops,
 #endif
 	},
 };
diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c
index 6f1b9aa..30e7646 100644
--- a/drivers/net/wireless/cw1200/fwio.c
+++ b/drivers/net/wireless/cw1200/fwio.c
@@ -66,25 +66,31 @@
 	do { \
 		ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
 		if (ret < 0) \
-			goto error; \
+			goto exit; \
+	} while (0)
+#define APB_WRITE2(reg, val) \
+	do { \
+		ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
+		if (ret < 0) \
+			goto free_buffer; \
 	} while (0)
 #define APB_READ(reg, val) \
 	do { \
 		ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \
 		if (ret < 0) \
-			goto error; \
+			goto free_buffer; \
 	} while (0)
 #define REG_WRITE(reg, val) \
 	do { \
 		ret = cw1200_reg_write_32(priv, (reg), (val)); \
 		if (ret < 0) \
-			goto error; \
+			goto exit; \
 	} while (0)
 #define REG_READ(reg, val) \
 	do { \
 		ret = cw1200_reg_read_32(priv, (reg), &(val)); \
 		if (ret < 0) \
-			goto error; \
+			goto exit; \
 	} while (0)
 
 	switch (priv->hw_revision) {
@@ -142,14 +148,14 @@
 	ret = request_firmware(&firmware, fw_path, priv->pdev);
 	if (ret) {
 		pr_err("Can't load firmware file %s.\n", fw_path);
-		goto error;
+		goto exit;
 	}
 
 	buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
 	if (!buf) {
 		pr_err("Can't allocate firmware load buffer.\n");
 		ret = -ENOMEM;
-		goto error;
+		goto firmware_release;
 	}
 
 	/* Check if the bootloader is ready */
@@ -163,7 +169,7 @@
 	if (val32 != DOWNLOAD_I_AM_HERE) {
 		pr_err("Bootloader is not ready.\n");
 		ret = -ETIMEDOUT;
-		goto error;
+		goto free_buffer;
 	}
 
 	/* Calculcate number of download blocks */
@@ -171,7 +177,7 @@
 
 	/* Updating the length in Download Ctrl Area */
 	val32 = firmware->size; /* Explicit cast from size_t to u32 */
-	APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32);
+	APB_WRITE2(DOWNLOAD_IMAGE_SIZE_REG, val32);
 
 	/* Firmware downloading loop */
 	for (block = 0; block < num_blocks; block++) {
@@ -183,7 +189,7 @@
 		if (val32 != DOWNLOAD_PENDING) {
 			pr_err("Bootloader reported error %d.\n", val32);
 			ret = -EIO;
-			goto error;
+			goto free_buffer;
 		}
 
 		/* loop until put - get <= 24K */
@@ -198,7 +204,7 @@
 		if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) {
 			pr_err("Timeout waiting for FIFO.\n");
 			ret = -ETIMEDOUT;
-			goto error;
+			goto free_buffer;
 		}
 
 		/* calculate the block size */
@@ -220,12 +226,12 @@
 		if (ret < 0) {
 			pr_err("Can't write firmware block @ %d!\n",
 			       put & (DOWNLOAD_FIFO_SIZE - 1));
-			goto error;
+			goto free_buffer;
 		}
 
 		/* update the put register */
 		put += block_size;
-		APB_WRITE(DOWNLOAD_PUT_REG, put);
+		APB_WRITE2(DOWNLOAD_PUT_REG, put);
 	} /* End of firmware download loop */
 
 	/* Wait for the download completion */
@@ -238,19 +244,21 @@
 	if (val32 != DOWNLOAD_SUCCESS) {
 		pr_err("Wait for download completion failed: 0x%.8X\n", val32);
 		ret = -ETIMEDOUT;
-		goto error;
+		goto free_buffer;
 	} else {
 		pr_info("Firmware download completed.\n");
 		ret = 0;
 	}
 
-error:
+free_buffer:
 	kfree(buf);
-	if (firmware)
-		release_firmware(firmware);
+firmware_release:
+	release_firmware(firmware);
+exit:
 	return ret;
 
 #undef APB_WRITE
+#undef APB_WRITE2
 #undef APB_READ
 #undef REG_WRITE
 #undef REG_READ
diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c
index 3e78cc3..3689dbb 100644
--- a/drivers/net/wireless/cw1200/main.c
+++ b/drivers/net/wireless/cw1200/main.c
@@ -282,7 +282,6 @@
 		    IEEE80211_HW_SUPPORTS_PS |
 		    IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
 		    IEEE80211_HW_REPORTS_TX_ACK_STATUS |
-		    IEEE80211_HW_SUPPORTS_UAPSD |
 		    IEEE80211_HW_CONNECTION_MONITOR |
 		    IEEE80211_HW_AMPDU_AGGREGATION |
 		    IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
@@ -374,9 +373,8 @@
 	INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work);
 	INIT_WORK(&priv->set_beacon_wakeup_period_work,
 		  cw1200_set_beacon_wakeup_period_work);
-	init_timer(&priv->mcast_timeout);
-	priv->mcast_timeout.data = (unsigned long)priv;
-	priv->mcast_timeout.function = cw1200_mcast_timeout;
+	setup_timer(&priv->mcast_timeout, cw1200_mcast_timeout,
+		    (unsigned long)priv);
 
 	if (cw1200_queue_stats_init(&priv->tx_queue_stats,
 				    CW1200_LINK_ID_MAX,
diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c
index 6907c8f..d2202ae 100644
--- a/drivers/net/wireless/cw1200/pm.c
+++ b/drivers/net/wireless/cw1200/pm.c
@@ -101,9 +101,8 @@
 {
 	spin_lock_init(&pm->lock);
 
-	init_timer(&pm->stay_awake);
-	pm->stay_awake.data = (unsigned long)pm;
-	pm->stay_awake.function = cw1200_pm_stay_awake_tmo;
+	setup_timer(&pm->stay_awake, cw1200_pm_stay_awake_tmo,
+		    (unsigned long)pm);
 
 	return 0;
 }
diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/cw1200/queue.c
index 9c3925f..0ba5ef9 100644
--- a/drivers/net/wireless/cw1200/queue.c
+++ b/drivers/net/wireless/cw1200/queue.c
@@ -179,9 +179,7 @@
 	INIT_LIST_HEAD(&queue->pending);
 	INIT_LIST_HEAD(&queue->free_pool);
 	spin_lock_init(&queue->lock);
-	init_timer(&queue->gc);
-	queue->gc.data = (unsigned long)queue;
-	queue->gc.function = cw1200_queue_gc;
+	setup_timer(&queue->gc, cw1200_queue_gc, (unsigned long)queue);
 
 	queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity,
 			GFP_KERNEL);
diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c
index f2e276f..bff81b8 100644
--- a/drivers/net/wireless/cw1200/scan.c
+++ b/drivers/net/wireless/cw1200/scan.c
@@ -39,9 +39,9 @@
 	cancel_delayed_work_sync(&priv->clear_recent_scan_work);
 	atomic_set(&priv->scan.in_progress, 1);
 	atomic_set(&priv->recent_scan, 1);
-	cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000);
+	cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo));
 	queue_delayed_work(priv->workqueue, &priv->scan.timeout,
-			   tmo * HZ / 1000);
+			   msecs_to_jiffies(tmo));
 	ret = wsm_scan(priv, scan);
 	if (ret) {
 		atomic_set(&priv->scan.in_progress, 0);
@@ -386,8 +386,8 @@
 	if (down_trylock(&priv->scan.lock)) {
 		/* Scan is already in progress. Requeue self. */
 		schedule();
-		queue_delayed_work(priv->workqueue,
-				   &priv->scan.probe_work, HZ / 10);
+		queue_delayed_work(priv->workqueue, &priv->scan.probe_work,
+				   msecs_to_jiffies(100));
 		mutex_unlock(&priv->conf_mutex);
 		return;
 	}
diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c
index 5b84664..b0f65fa 100644
--- a/drivers/net/wireless/cw1200/sta.c
+++ b/drivers/net/wireless/cw1200/sta.c
@@ -213,6 +213,7 @@
 	/* __le32 auto_calibration_mode = __cpu_to_le32(1); */
 
 	vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+			     IEEE80211_VIF_SUPPORTS_UAPSD |
 			     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
 
 	mutex_lock(&priv->conf_mutex);
@@ -292,7 +293,7 @@
 	}
 	priv->vif = NULL;
 	priv->mode = NL80211_IFTYPE_MONITOR;
-	memset(priv->mac_addr, 0, ETH_ALEN);
+	eth_zero_addr(priv->mac_addr);
 	memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo));
 	cw1200_free_keys(priv);
 	cw1200_setup_mac(priv);
@@ -708,7 +709,8 @@
 		if (sta)
 			peer_addr = sta->addr;
 
-		key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+		key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE |
+			      IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
 
 		switch (key->cipher) {
 		case WLAN_CIPHER_SUITE_WEP40:
@@ -1238,8 +1240,8 @@
 
 	bssid = priv->vif->bss_conf.bssid;
 
-	bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel,
-			bssid, NULL, 0, 0, 0);
+	bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0,
+			       IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
 
 	if (!bss && !conf->ibss_joined) {
 		wsm_unlock_tx(priv);
diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c
index 0bd5411..d28bd49 100644
--- a/drivers/net/wireless/cw1200/txrx.c
+++ b/drivers/net/wireless/cw1200/txrx.c
@@ -1429,7 +1429,7 @@
 				priv->link_id_map &= ~mask;
 				priv->sta_asleep_mask &= ~mask;
 				priv->pspoll_mask &= ~mask;
-				memset(map_link.mac_addr, 0, ETH_ALEN);
+				eth_zero_addr(map_link.mac_addr);
 				spin_unlock_bh(&priv->ps_state_lock);
 				reset.link_id = i + 1;
 				wsm_reset(priv, &reset);
diff --git a/drivers/net/wireless/ipw2x00/Kconfig b/drivers/net/wireless/ipw2x00/Kconfig
index 7cbeaff..fa69390 100644
--- a/drivers/net/wireless/ipw2x00/Kconfig
+++ b/drivers/net/wireless/ipw2x00/Kconfig
@@ -67,7 +67,8 @@
 config IPW2200
 	tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection"
 	depends on m
-	depends on PCI && CFG80211 && CFG80211_WEXT
+	depends on PCI && CFG80211
+	select CFG80211_WEXT_EXPORT
 	depends on WIRELESS_EXT
 	depends on WEXT_SPY
 	depends on WEXT_PRIV
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index 5e111ea..86e9e3a 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -2151,8 +2151,8 @@
 		return;
 	}
 
-	memset(priv->bssid, 0, ETH_ALEN);
-	memset(priv->ieee->bssid, 0, ETH_ALEN);
+	eth_zero_addr(priv->bssid);
+	eth_zero_addr(priv->ieee->bssid);
 
 	netif_carrier_off(priv->net_dev);
 	netif_stop_queue(priv->net_dev);
@@ -6960,7 +6960,7 @@
 		wrqu->ap_addr.sa_family = ARPHRD_ETHER;
 		memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
 	} else
-		memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+		eth_zero_addr(wrqu->ap_addr.sa_data);
 
 	IPW_DEBUG_WX("Getting WAP BSSID: %pM\n", wrqu->ap_addr.sa_data);
 	return 0;
@@ -8304,7 +8304,7 @@
 	    priv->status & STATUS_RF_KILL_MASK ||
 	    ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
 				&priv->bssid, &len)) {
-		memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+		eth_zero_addr(wrqu.ap_addr.sa_data);
 	} else {
 		/* We now have the BSSID, so can finish setting to the full
 		 * associated state */
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c
index d46df94..aaf6b77 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/ipw2x00/ipw2200.c
@@ -1964,7 +1964,7 @@
 	if (priv->status & STATUS_ASSOCIATED)
 		memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
 	else
-		memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+		eth_zero_addr(wrqu.ap_addr.sa_data);
 	wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
 }
 
@@ -7400,7 +7400,7 @@
 	memcpy(priv->assoc_request.bssid, network->bssid, ETH_ALEN);
 
 	if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
-		memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN);
+		eth_broadcast_addr(priv->assoc_request.dest);
 		priv->assoc_request.atim_window = cpu_to_le16(network->atim_window);
 	} else {
 		memcpy(priv->assoc_request.dest, network->bssid, ETH_ALEN);
@@ -8986,7 +8986,7 @@
 		wrqu->ap_addr.sa_family = ARPHRD_ETHER;
 		memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
 	} else
-		memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+		eth_zero_addr(wrqu->ap_addr.sa_data);
 
 	IPW_DEBUG_WX("Getting WAP BSSID: %pM\n",
 		     wrqu->ap_addr.sa_data);
diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c
index 9d7db12..241b385 100644
--- a/drivers/net/wireless/iwlegacy/3945-mac.c
+++ b/drivers/net/wireless/iwlegacy/3945-mac.c
@@ -3429,9 +3429,7 @@
 
 	il3945_hw_setup_deferred_work(il);
 
-	init_timer(&il->watchdog);
-	il->watchdog.data = (unsigned long)il;
-	il->watchdog.function = il_bg_watchdog;
+	setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il);
 
 	tasklet_init(&il->irq_tasklet,
 		     (void (*)(unsigned long))il3945_irq_tasklet,
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c
index cb90853..d40f7e7 100644
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/iwlegacy/4965-mac.c
@@ -6247,13 +6247,10 @@
 
 	INIT_WORK(&il->txpower_work, il4965_bg_txpower_work);
 
-	init_timer(&il->stats_periodic);
-	il->stats_periodic.data = (unsigned long)il;
-	il->stats_periodic.function = il4965_bg_stats_periodic;
+	setup_timer(&il->stats_periodic, il4965_bg_stats_periodic,
+		    (unsigned long)il);
 
-	init_timer(&il->watchdog);
-	il->watchdog.data = (unsigned long)il;
-	il->watchdog.function = il_bg_watchdog;
+	setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il);
 
 	tasklet_init(&il->irq_tasklet,
 		     (void (*)(unsigned long))il4965_irq_tasklet,
diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/iwlegacy/4965-rs.c
index 7110a35..405bbab 100644
--- a/drivers/net/wireless/iwlegacy/4965-rs.c
+++ b/drivers/net/wireless/iwlegacy/4965-rs.c
@@ -1678,7 +1678,7 @@
 		    lq_sta->total_success > lq_sta->max_success_limit ||
 		    (!lq_sta->search_better_tbl && lq_sta->flush_timer &&
 		     flush_interval_passed)) {
-			D_RATE("LQ: stay is expired %d %d %d\n:",
+			D_RATE("LQ: stay is expired %d %d %d\n",
 			       lq_sta->total_failed, lq_sta->total_success,
 			       flush_interval_passed);
 
diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c
index 4758f45..c1c63ed 100644
--- a/drivers/net/wireless/iwlegacy/common.c
+++ b/drivers/net/wireless/iwlegacy/common.c
@@ -4634,7 +4634,7 @@
 	il->vif = NULL;
 	il->iw_mode = NL80211_IFTYPE_UNSPECIFIED;
 	il_teardown_interface(il, vif);
-	memset(il->bssid, 0, ETH_ALEN);
+	eth_zero_addr(il->bssid);
 
 	D_MAC80211("leave\n");
 	mutex_unlock(&il->mutex);
diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h
index b65be18..b80fdf5 100644
--- a/drivers/net/wireless/iwlwifi/dvm/dev.h
+++ b/drivers/net/wireless/iwlwifi/dvm/dev.h
@@ -708,7 +708,6 @@
 	unsigned long reload_jiffies;
 	int reload_count;
 	bool ucode_loaded;
-	bool init_ucode_run;		/* Don't run init uCode again */
 
 	u8 plcp_delta_threshold;
 
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index dfa0626..8c45efa 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -1114,35 +1114,39 @@
 	scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) |
 			BIT(IWL_DEFAULT_CMD_QUEUE_NUM));
 
-	if (vif)
-		scd_queues &= ~BIT(vif->hw_queue[IEEE80211_AC_VO]);
-
-	IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n", scd_queues);
-	if (iwlagn_txfifo_flush(priv, scd_queues)) {
-		IWL_ERR(priv, "flush request fail\n");
-		goto done;
+	if (drop) {
+		IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n",
+				    scd_queues);
+		if (iwlagn_txfifo_flush(priv, scd_queues)) {
+			IWL_ERR(priv, "flush request fail\n");
+			goto done;
+		}
 	}
+
 	IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n");
-	iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff);
+	iwl_trans_wait_tx_queue_empty(priv->trans, scd_queues);
 done:
 	mutex_unlock(&priv->mutex);
 	IWL_DEBUG_MAC80211(priv, "leave\n");
 }
 
-static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
-				     struct ieee80211_vif *vif,
-				     enum ieee80211_rssi_event rssi_event)
+static void iwlagn_mac_event_callback(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif,
+				      const struct ieee80211_event *event)
 {
 	struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
 
+	if (event->type != RSSI_EVENT)
+		return;
+
 	IWL_DEBUG_MAC80211(priv, "enter\n");
 	mutex_lock(&priv->mutex);
 
 	if (priv->lib->bt_params &&
 	    priv->lib->bt_params->advanced_bt_coexist) {
-		if (rssi_event == RSSI_EVENT_LOW)
+		if (event->u.rssi.data == RSSI_EVENT_LOW)
 			priv->bt_enable_pspoll = true;
-		else if (rssi_event == RSSI_EVENT_HIGH)
+		else if (event->u.rssi.data == RSSI_EVENT_HIGH)
 			priv->bt_enable_pspoll = false;
 
 		iwlagn_send_advance_bt_config(priv);
@@ -1613,7 +1617,7 @@
 	.channel_switch = iwlagn_mac_channel_switch,
 	.flush = iwlagn_mac_flush,
 	.tx_last_beacon = iwlagn_mac_tx_last_beacon,
-	.rssi_callback = iwlagn_mac_rssi_callback,
+	.event_callback = iwlagn_mac_event_callback,
 	.set_tim = iwlagn_mac_set_tim,
 };
 
diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c
index c7e5e6e..d2d2cc4 100644
--- a/drivers/net/wireless/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/iwlwifi/dvm/main.c
@@ -64,22 +64,8 @@
  *
  ******************************************************************************/
 
-/*
- * module name, copyright, version, etc.
- */
 #define DRV_DESCRIPTION	"Intel(R) Wireless WiFi Link AGN driver for Linux"
-
-#ifdef CPTCFG_IWLWIFI_DEBUG
-#define VD "d"
-#else
-#define VD
-#endif
-
-#define DRV_VERSION     IWLWIFI_VERSION VD
-
-
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_VERSION(DRV_VERSION);
 MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
 MODULE_LICENSE("GPL");
 
@@ -1011,13 +997,11 @@
 	if (priv->lib->bt_params)
 		iwlagn_bt_setup_deferred_work(priv);
 
-	init_timer(&priv->statistics_periodic);
-	priv->statistics_periodic.data = (unsigned long)priv;
-	priv->statistics_periodic.function = iwl_bg_statistics_periodic;
+	setup_timer(&priv->statistics_periodic, iwl_bg_statistics_periodic,
+		    (unsigned long)priv);
 
-	init_timer(&priv->ucode_trace);
-	priv->ucode_trace.data = (unsigned long)priv;
-	priv->ucode_trace.function = iwl_bg_ucode_trace;
+	setup_timer(&priv->ucode_trace, iwl_bg_ucode_trace,
+		    (unsigned long)priv);
 }
 
 void iwl_cancel_deferred_work(struct iwl_priv *priv)
@@ -1244,11 +1228,8 @@
 	trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
 	trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
 	trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K;
-	if (!iwlwifi_mod_params.wd_disable)
-		trans_cfg.queue_watchdog_timeout =
-			priv->cfg->base_params->wd_timeout;
-	else
-		trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED;
+	trans_cfg.cmd_q_wdg_timeout = IWL_WATCHDOG_DISABLED;
+
 	trans_cfg.command_names = iwl_dvm_cmd_strings;
 	trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM;
 
@@ -1568,7 +1549,7 @@
 				      table.blink1, table.blink2, table.ilink1,
 				      table.ilink2, table.bcon_time, table.gp1,
 				      table.gp2, table.gp3, table.ucode_ver,
-				      table.hw_ver, table.brd_ver);
+				      table.hw_ver, 0, table.brd_ver);
 	IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id,
 		desc_lookup(table.error_id));
 	IWL_ERR(priv, "0x%08X | uPc\n", table.pc);
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c
index 99f8dd7..785da53 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rs.c
@@ -3153,12 +3153,13 @@
 	desc += sprintf(buff+desc, "lq type %s\n",
 	   (is_legacy(tbl->lq_type)) ? "legacy" : "HT");
 	if (is_Ht(tbl->lq_type)) {
-		desc += sprintf(buff+desc, " %s",
+		desc += sprintf(buff + desc, " %s",
 		   (is_siso(tbl->lq_type)) ? "SISO" :
 		   ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3"));
-		   desc += sprintf(buff+desc, " %s",
+		desc += sprintf(buff + desc, " %s",
 		   (tbl->is_ht40) ? "40MHz" : "20MHz");
-		   desc += sprintf(buff+desc, " %s %s %s\n", (tbl->is_SGI) ? "SGI" : "",
+		desc += sprintf(buff + desc, " %s %s %s\n",
+		   (tbl->is_SGI) ? "SGI" : "",
 		   (lq_sta->is_green) ? "GF enabled" : "",
 		   (lq_sta->is_agg) ? "AGG on" : "");
 	}
diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c
index 1fc24b2..31dea64 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tt.c
+++ b/drivers/net/wireless/iwlwifi/dvm/tt.c
@@ -612,15 +612,10 @@
 	memset(tt, 0, sizeof(struct iwl_tt_mgmt));
 
 	tt->state = IWL_TI_0;
-	init_timer(&priv->thermal_throttle.ct_kill_exit_tm);
-	priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv;
-	priv->thermal_throttle.ct_kill_exit_tm.function =
-		iwl_tt_check_exit_ct_kill;
-	init_timer(&priv->thermal_throttle.ct_kill_waiting_tm);
-	priv->thermal_throttle.ct_kill_waiting_tm.data =
-		(unsigned long)priv;
-	priv->thermal_throttle.ct_kill_waiting_tm.function =
-		iwl_tt_ready_for_ct_kill;
+	setup_timer(&priv->thermal_throttle.ct_kill_exit_tm,
+		    iwl_tt_check_exit_ct_kill, (unsigned long)priv);
+	setup_timer(&priv->thermal_throttle.ct_kill_waiting_tm,
+		    iwl_tt_ready_for_ct_kill, (unsigned long)priv);
 	/* setup deferred ct kill work */
 	INIT_WORK(&priv->tt_work, iwl_bg_tt_work);
 	INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter);
diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c
index 29f423b..64aa9f3 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/tx.c
@@ -189,9 +189,9 @@
 		rate_flags |= RATE_MCS_CCK_MSK;
 
 	/* Set up antennas */
-	 if (priv->lib->bt_params &&
-	     priv->lib->bt_params->advanced_bt_coexist &&
-	     priv->bt_full_concurrent) {
+	if (priv->lib->bt_params &&
+	    priv->lib->bt_params->advanced_bt_coexist &&
+	    priv->bt_full_concurrent) {
 		/* operated as 1x1 in full concurrency mode */
 		priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
 				first_antenna(priv->nvm_data->valid_tx_ant));
@@ -715,7 +715,7 @@
 	fifo = ctx->ac_to_fifo[tid_to_ac[tid]];
 
 	iwl_trans_txq_enable(priv->trans, q, fifo, sta_priv->sta_id, tid,
-			     buf_size, ssn);
+			     buf_size, ssn, 0);
 
 	/*
 	 * If the limit is 0, then it wasn't initialised yet,
diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c
index d5cee15..5244e43 100644
--- a/drivers/net/wireless/iwlwifi/dvm/ucode.c
+++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c
@@ -267,7 +267,7 @@
 	for (i = 0; i < n_queues; i++)
 		if (queue_to_txf[i] != IWL_TX_FIFO_UNUSED)
 			iwl_trans_ac_txq_enable(priv->trans, i,
-						queue_to_txf[i]);
+						queue_to_txf[i], 0);
 
 	priv->passive_no_rx = false;
 	priv->transport_queue_stop = 0;
@@ -418,9 +418,6 @@
 	if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len)
 		return 0;
 
-	if (priv->init_ucode_run)
-		return 0;
-
 	iwl_init_notification_wait(&priv->notif_wait, &calib_wait,
 				   calib_complete, ARRAY_SIZE(calib_complete),
 				   iwlagn_wait_calib, priv);
@@ -440,8 +437,6 @@
 	 */
 	ret = iwl_wait_notification(&priv->notif_wait, &calib_wait,
 					UCODE_CALIB_TIMEOUT);
-	if (!ret)
-		priv->init_ucode_run = true;
 
 	goto out;
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
index c3817fa..06f6cc0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-1000.c
@@ -95,7 +95,8 @@
 	.nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION,	\
 	.base_params = &iwl1000_base_params,			\
 	.eeprom_params = &iwl1000_eeprom_params,		\
-	.led_mode = IWL_LED_BLINK
+	.led_mode = IWL_LED_BLINK,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl1000_bgn_cfg = {
 	.name = "Intel(R) Centrino(R) Wireless-N 1000 BGN",
@@ -121,7 +122,8 @@
 	.base_params = &iwl1000_base_params,			\
 	.eeprom_params = &iwl1000_eeprom_params,		\
 	.led_mode = IWL_LED_RF_STATE,				\
-	.rx_with_siso_diversity = true
+	.rx_with_siso_diversity = true,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl100_bgn_cfg = {
 	.name = "Intel(R) Centrino(R) Wireless-N 100 BGN",
diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c
index 21e5d08..890b95f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-2000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-2000.c
@@ -123,7 +123,9 @@
 	.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION,		\
 	.base_params = &iwl2000_base_params,			\
 	.eeprom_params = &iwl20x0_eeprom_params,		\
-	.led_mode = IWL_LED_RF_STATE
+	.led_mode = IWL_LED_RF_STATE,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
 
 const struct iwl_cfg iwl2000_2bgn_cfg = {
 	.name = "Intel(R) Centrino(R) Wireless-N 2200 BGN",
@@ -149,7 +151,8 @@
 	.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION,	\
 	.base_params = &iwl2030_base_params,			\
 	.eeprom_params = &iwl20x0_eeprom_params,		\
-	.led_mode = IWL_LED_RF_STATE
+	.led_mode = IWL_LED_RF_STATE,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl2030_2bgn_cfg = {
 	.name = "Intel(R) Centrino(R) Wireless-N 2230 BGN",
@@ -170,7 +173,8 @@
 	.base_params = &iwl2000_base_params,			\
 	.eeprom_params = &iwl20x0_eeprom_params,		\
 	.led_mode = IWL_LED_RF_STATE,				\
-	.rx_with_siso_diversity = true
+	.rx_with_siso_diversity = true,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl105_bgn_cfg = {
 	.name = "Intel(R) Centrino(R) Wireless-N 105 BGN",
@@ -197,7 +201,8 @@
 	.base_params = &iwl2030_base_params,			\
 	.eeprom_params = &iwl20x0_eeprom_params,		\
 	.led_mode = IWL_LED_RF_STATE,				\
-	.rx_with_siso_diversity = true
+	.rx_with_siso_diversity = true,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl135_bgn_cfg = {
 	.name = "Intel(R) Centrino(R) Wireless-N 135 BGN",
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index 332bbed..724194e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -93,7 +93,8 @@
 	.nvm_calib_ver = EEPROM_5000_TX_POWER_VERSION,	\
 	.base_params = &iwl5000_base_params,			\
 	.eeprom_params = &iwl5000_eeprom_params,		\
-	.led_mode = IWL_LED_BLINK
+	.led_mode = IWL_LED_BLINK,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl5300_agn_cfg = {
 	.name = "Intel(R) Ultimate N WiFi Link 5300 AGN",
@@ -158,7 +159,8 @@
 	.base_params = &iwl5000_base_params,			\
 	.eeprom_params = &iwl5000_eeprom_params,		\
 	.led_mode = IWL_LED_BLINK,				\
-	.internal_wimax_coex = true
+	.internal_wimax_coex = true,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl5150_agn_cfg = {
 	.name = "Intel(R) WiMAX/WiFi Link 5150 AGN",
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index 8f2c3c8..21b2630 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -145,7 +145,8 @@
 	.nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION,	\
 	.base_params = &iwl6000_g2_base_params,			\
 	.eeprom_params = &iwl6000_eeprom_params,		\
-	.led_mode = IWL_LED_RF_STATE
+	.led_mode = IWL_LED_RF_STATE,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl6005_2agn_cfg = {
 	.name = "Intel(R) Centrino(R) Advanced-N 6205 AGN",
@@ -199,7 +200,8 @@
 	.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION,	\
 	.base_params = &iwl6000_g2_base_params,			\
 	.eeprom_params = &iwl6000_eeprom_params,		\
-	.led_mode = IWL_LED_RF_STATE
+	.led_mode = IWL_LED_RF_STATE,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl6030_2agn_cfg = {
 	.name = "Intel(R) Centrino(R) Advanced-N 6230 AGN",
@@ -235,7 +237,8 @@
 	.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION,	\
 	.base_params = &iwl6000_g2_base_params,			\
 	.eeprom_params = &iwl6000_eeprom_params,		\
-	.led_mode = IWL_LED_RF_STATE
+	.led_mode = IWL_LED_RF_STATE,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl6035_2agn_cfg = {
 	.name = "Intel(R) Centrino(R) Advanced-N 6235 AGN",
@@ -290,7 +293,8 @@
 	.nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION,	\
 	.base_params = &iwl6000_base_params,			\
 	.eeprom_params = &iwl6000_eeprom_params,		\
-	.led_mode = IWL_LED_BLINK
+	.led_mode = IWL_LED_BLINK,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl6000i_2agn_cfg = {
 	.name = "Intel(R) Centrino(R) Advanced-N 6200 AGN",
@@ -322,7 +326,8 @@
 	.base_params = &iwl6050_base_params,			\
 	.eeprom_params = &iwl6000_eeprom_params,		\
 	.led_mode = IWL_LED_BLINK,				\
-	.internal_wimax_coex = true
+	.internal_wimax_coex = true,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl6050_2agn_cfg = {
 	.name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN",
@@ -347,7 +352,8 @@
 	.base_params = &iwl6050_base_params,			\
 	.eeprom_params = &iwl6000_eeprom_params,		\
 	.led_mode = IWL_LED_BLINK,				\
-	.internal_wimax_coex = true
+	.internal_wimax_coex = true,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl6150_bgn_cfg = {
 	.name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN",
diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c
index e5be2d2..36e786f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-7000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-7000.c
@@ -69,16 +69,16 @@
 #include "iwl-agn-hw.h"
 
 /* Highest firmware API version supported */
-#define IWL7260_UCODE_API_MAX	10
-#define IWL3160_UCODE_API_MAX	10
+#define IWL7260_UCODE_API_MAX	13
+#define IWL3160_UCODE_API_MAX	13
 
 /* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK	10
-#define IWL3160_UCODE_API_OK	10
+#define IWL7260_UCODE_API_OK	12
+#define IWL3160_UCODE_API_OK	12
 
 /* Lowest firmware API version supported */
-#define IWL7260_UCODE_API_MIN	9
-#define IWL3160_UCODE_API_MIN	9
+#define IWL7260_UCODE_API_MIN	10
+#define IWL3160_UCODE_API_MIN	10
 
 /* NVM versions */
 #define IWL7260_NVM_VERSION		0x0a1d
@@ -92,6 +92,12 @@
 #define IWL7265D_NVM_VERSION		0x0c11
 #define IWL7265_TX_POWER_VERSION	0xffff /* meaningless */
 
+/* DCCM offsets and lengths */
+#define IWL7000_DCCM_OFFSET		0x800000
+#define IWL7260_DCCM_LEN		0x14000
+#define IWL3160_DCCM_LEN		0x10000
+#define IWL7265_DCCM_LEN		0x17A00
+
 #define IWL7260_FW_PRE "iwlwifi-7260-"
 #define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode"
 
@@ -105,7 +111,7 @@
 #define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
 
 #define IWL7265D_FW_PRE "iwlwifi-7265D-"
-#define IWL7265D_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
+#define IWL7265D_MODULE_FIRMWARE(api) IWL7265D_FW_PRE __stringify(api) ".ucode"
 
 #define NVM_HW_SECTION_NUM_FAMILY_7000		0
 
@@ -138,7 +144,8 @@
 	.led_mode = IWL_LED_RF_STATE,				\
 	.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000,	\
 	.non_shared_ant = ANT_A,				\
-	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,	\
+	.dccm_offset = IWL7000_DCCM_OFFSET
 
 const struct iwl_cfg iwl7260_2ac_cfg = {
 	.name = "Intel(R) Dual Band Wireless AC 7260",
@@ -149,6 +156,7 @@
 	.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
 	.host_interrupt_operation_mode = true,
 	.lp_xtal_workaround = true,
+	.dccm_len = IWL7260_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl7260_2ac_cfg_high_temp = {
@@ -161,6 +169,7 @@
 	.high_temp = true,
 	.host_interrupt_operation_mode = true,
 	.lp_xtal_workaround = true,
+	.dccm_len = IWL7260_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl7260_2n_cfg = {
@@ -172,6 +181,7 @@
 	.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
 	.host_interrupt_operation_mode = true,
 	.lp_xtal_workaround = true,
+	.dccm_len = IWL7260_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl7260_n_cfg = {
@@ -183,6 +193,7 @@
 	.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
 	.host_interrupt_operation_mode = true,
 	.lp_xtal_workaround = true,
+	.dccm_len = IWL7260_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl3160_2ac_cfg = {
@@ -193,6 +204,7 @@
 	.nvm_ver = IWL3160_NVM_VERSION,
 	.nvm_calib_ver = IWL3160_TX_POWER_VERSION,
 	.host_interrupt_operation_mode = true,
+	.dccm_len = IWL3160_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl3160_2n_cfg = {
@@ -203,6 +215,7 @@
 	.nvm_ver = IWL3160_NVM_VERSION,
 	.nvm_calib_ver = IWL3160_TX_POWER_VERSION,
 	.host_interrupt_operation_mode = true,
+	.dccm_len = IWL3160_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl3160_n_cfg = {
@@ -213,6 +226,7 @@
 	.nvm_ver = IWL3160_NVM_VERSION,
 	.nvm_calib_ver = IWL3160_TX_POWER_VERSION,
 	.host_interrupt_operation_mode = true,
+	.dccm_len = IWL3160_DCCM_LEN,
 };
 
 static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = {
@@ -240,6 +254,7 @@
 	.nvm_ver = IWL3165_NVM_VERSION,
 	.nvm_calib_ver = IWL3165_TX_POWER_VERSION,
 	.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+	.dccm_len = IWL7265_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl7265_2ac_cfg = {
@@ -250,6 +265,7 @@
 	.nvm_ver = IWL7265_NVM_VERSION,
 	.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
 	.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+	.dccm_len = IWL7265_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl7265_2n_cfg = {
@@ -260,6 +276,7 @@
 	.nvm_ver = IWL7265_NVM_VERSION,
 	.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
 	.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+	.dccm_len = IWL7265_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl7265_n_cfg = {
@@ -270,6 +287,7 @@
 	.nvm_ver = IWL7265_NVM_VERSION,
 	.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
 	.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+	.dccm_len = IWL7265_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl7265d_2ac_cfg = {
@@ -280,6 +298,7 @@
 	.nvm_ver = IWL7265D_NVM_VERSION,
 	.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
 	.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+	.dccm_len = IWL7265_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl7265d_2n_cfg = {
@@ -290,6 +309,7 @@
 	.nvm_ver = IWL7265D_NVM_VERSION,
 	.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
 	.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+	.dccm_len = IWL7265_DCCM_LEN,
 };
 
 const struct iwl_cfg iwl7265d_n_cfg = {
@@ -300,6 +320,7 @@
 	.nvm_ver = IWL7265D_NVM_VERSION,
 	.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
 	.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+	.dccm_len = IWL7265_DCCM_LEN,
 };
 
 MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c
index bf0a95c..ce6321b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-8000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-8000.c
@@ -69,24 +69,33 @@
 #include "iwl-agn-hw.h"
 
 /* Highest firmware API version supported */
-#define IWL8000_UCODE_API_MAX	10
+#define IWL8000_UCODE_API_MAX	13
 
 /* Oldest version we won't warn about */
-#define IWL8000_UCODE_API_OK	10
+#define IWL8000_UCODE_API_OK	12
 
 /* Lowest firmware API version supported */
-#define IWL8000_UCODE_API_MIN	9
+#define IWL8000_UCODE_API_MIN	10
 
 /* NVM versions */
 #define IWL8000_NVM_VERSION		0x0a1d
 #define IWL8000_TX_POWER_VERSION	0xffff /* meaningless */
 
+/* Memory offsets and lengths */
+#define IWL8260_DCCM_OFFSET		0x800000
+#define IWL8260_DCCM_LEN		0x18000
+#define IWL8260_DCCM2_OFFSET		0x880000
+#define IWL8260_DCCM2_LEN		0x8000
+#define IWL8260_SMEM_OFFSET		0x400000
+#define IWL8260_SMEM_LEN		0x68000
+
 #define IWL8000_FW_PRE "iwlwifi-8000"
 #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"
+#define DEFAULT_NVM_FILE_FAMILY_8000B		"nvmData-8000B"
+#define DEFAULT_NVM_FILE_FAMILY_8000C		"nvmData-8000C"
 
 /* Max SDIO RX aggregation size of the ADDBA request/response */
 #define MAX_RX_AGG_SIZE_8260_SDIO	28
@@ -124,7 +133,13 @@
 	.led_mode = IWL_LED_RF_STATE,				\
 	.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000,	\
 	.d0i3 = true,						\
-	.non_shared_ant = ANT_A
+	.non_shared_ant = ANT_A,				\
+	.dccm_offset = IWL8260_DCCM_OFFSET,			\
+	.dccm_len = IWL8260_DCCM_LEN,				\
+	.dccm2_offset = IWL8260_DCCM2_OFFSET,			\
+	.dccm2_len = IWL8260_DCCM2_LEN,				\
+	.smem_offset = IWL8260_SMEM_OFFSET,			\
+	.smem_len = IWL8260_SMEM_LEN
 
 const struct iwl_cfg iwl8260_2n_cfg = {
 	.name = "Intel(R) Dual Band Wireless N 8260",
@@ -145,6 +160,16 @@
 	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
 };
 
+const struct iwl_cfg iwl4165_2ac_cfg = {
+	.name = "Intel(R) Dual Band Wireless AC 4165",
+	.fw_name_pre = IWL8000_FW_PRE,
+	IWL_DEVICE_8000,
+	.ht_params = &iwl8000_ht_params,
+	.nvm_ver = IWL8000_NVM_VERSION,
+	.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+};
+
 const struct iwl_cfg iwl8260_2ac_sdio_cfg = {
 	.name = "Intel(R) Dual Band Wireless-AC 8260",
 	.fw_name_pre = IWL8000_FW_PRE,
@@ -152,7 +177,8 @@
 	.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,
+	.default_nvm_file_B_step = DEFAULT_NVM_FILE_FAMILY_8000B,
+	.default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C,
 	.max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
 	.disable_dummy_notification = true,
 	.max_ht_ampdu_exponent  = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
@@ -166,7 +192,8 @@
 	.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,
+	.default_nvm_file_B_step = DEFAULT_NVM_FILE_FAMILY_8000B,
+	.default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C,
 	.max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
 	.bt_shared_single_ant = true,
 	.disable_dummy_notification = true,
diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h
index ab079ff..c1fa28e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/iwlwifi/iwl-config.h
@@ -92,9 +92,9 @@
 {
 	/* return 1 only for family 8000 B0 */
 	if ((family == IWL_DEVICE_FAMILY_8000) && (hw_rev & 0xC))
-		return 1;
+		return true;
 
-	return 0;
+	return false;
 }
 
 /*
@@ -126,7 +126,7 @@
 
 /* TX queue watchdog timeouts in mSecs */
 #define IWL_WATCHDOG_DISABLED	0
-#define IWL_DEF_WD_TIMEOUT	2000
+#define IWL_DEF_WD_TIMEOUT	2500
 #define IWL_LONG_WD_TIMEOUT	10000
 #define IWL_MAX_WD_TIMEOUT	120000
 
@@ -228,7 +228,7 @@
 
 /**
  * struct iwl_cfg
- * @name: Offical name of the device
+ * @name: Official name of the device
  * @fw_name_pre: Firmware filename prefix. The api version and extension
  *	(.ucode) will be added to filename before loading from disk. The
  *	filename is constructed as fw_name_pre<api>.ucode.
@@ -261,6 +261,12 @@
  *	station can receive in HT
  * @max_vht_ampdu_exponent: the exponent of the max length of A-MPDU that the
  *	station can receive in VHT
+ * @dccm_offset: offset from which DCCM begins
+ * @dccm_len: length of DCCM (including runtime stack CCM)
+ * @dccm2_offset: offset from which the second DCCM begins
+ * @dccm2_len: length of the second DCCM
+ * @smem_offset: offset from which the SMEM begins
+ * @smem_len: the length of SMEM
  *
  * We enable the driver to be backward compatible wrt. hardware features.
  * API differences in uCode shouldn't be handled here but through TLVs
@@ -297,12 +303,19 @@
 	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;
+	const char *default_nvm_file_B_step;
+	const char *default_nvm_file_C_step;
 	unsigned int max_rx_agg_size;
 	bool disable_dummy_notification;
 	unsigned int max_tx_agg_size;
 	unsigned int max_ht_ampdu_exponent;
 	unsigned int max_vht_ampdu_exponent;
+	const u32 dccm_offset;
+	const u32 dccm_len;
+	const u32 dccm2_offset;
+	const u32 dccm2_len;
+	const u32 smem_offset;
+	const u32 smem_len;
 };
 
 /*
@@ -369,8 +382,8 @@
 extern const struct iwl_cfg iwl7265d_n_cfg;
 extern const struct iwl_cfg iwl8260_2n_cfg;
 extern const struct iwl_cfg iwl8260_2ac_cfg;
+extern const struct iwl_cfg iwl4165_2ac_cfg;
 extern const struct iwl_cfg iwl8260_2ac_sdio_cfg;
-extern const struct iwl_cfg iwl4265_2ac_sdio_cfg;
 extern const struct iwl_cfg iwl4165_2ac_sdio_cfg;
 #endif /* CPTCFG_IWLMVM */
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
index aff63c3..faa17f2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/iwlwifi/iwl-csr.h
@@ -184,6 +184,7 @@
 #define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY	(0x00400000) /* PCI_OWN_SEM */
 #define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */
 #define CSR_HW_IF_CONFIG_REG_PREPARE		  (0x08000000) /* WAKE_ME */
+#define CSR_HW_IF_CONFIG_REG_ENABLE_PME		  (0x10000000)
 #define CSR_HW_IF_CONFIG_REG_PERSIST_MODE	  (0x40000000) /* PERSISTENCE */
 
 #define CSR_MBOX_SET_REG_OS_ALIVE		BIT(5)
@@ -306,6 +307,7 @@
 enum {
 	SILICON_A_STEP = 0,
 	SILICON_B_STEP,
+	SILICON_C_STEP,
 };
 
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index 68b126b..5f1aadc 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -157,6 +157,7 @@
 /* 0x0000F000 - 0x00001000 */
 #define IWL_DL_ASSOC		0x00001000
 #define IWL_DL_DROP		0x00002000
+#define IWL_DL_LAR		0x00004000
 #define IWL_DL_COEX		0x00008000
 /* 0x000F0000 - 0x00010000 */
 #define IWL_DL_FW		0x00010000
@@ -219,5 +220,6 @@
 #define IWL_DEBUG_POWER(p, f, a...)	IWL_DEBUG(p, IWL_DL_POWER, f, ## a)
 #define IWL_DEBUG_11H(p, f, a...)	IWL_DEBUG(p, IWL_DL_11H, f, ## a)
 #define IWL_DEBUG_RPM(p, f, a...)	IWL_DEBUG(p, IWL_DL_RPM, f, ## a)
+#define IWL_DEBUG_LAR(p, f, a...)	IWL_DEBUG(p, IWL_DL_LAR, f, ## a)
 
 #endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h
new file mode 100644
index 0000000..04e6649
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h
@@ -0,0 +1,79 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#if !defined(__IWLWIFI_DEVICE_TRACE_DATA) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE_DATA
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi_data
+
+TRACE_EVENT(iwlwifi_dev_tx_data,
+	TP_PROTO(const struct device *dev,
+		 struct sk_buff *skb,
+		 void *data, size_t data_len),
+	TP_ARGS(dev, skb, data, data_len),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+
+		__dynamic_array(u8, data, iwl_trace_data(skb) ? data_len : 0)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		if (iwl_trace_data(skb))
+			memcpy(__get_dynamic_array(data), data, data_len);
+	),
+	TP_printk("[%s] TX frame data", __get_str(dev))
+);
+
+TRACE_EVENT(iwlwifi_dev_rx_data,
+	TP_PROTO(const struct device *dev,
+		 const struct iwl_trans *trans,
+		 void *rxbuf, size_t len),
+	TP_ARGS(dev, trans, rxbuf, len),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+
+		__dynamic_array(u8, data,
+				len - iwl_rx_trace_len(trans, rxbuf, len))
+	),
+	TP_fast_assign(
+		size_t offs = iwl_rx_trace_len(trans, rxbuf, len);
+		DEV_ASSIGN;
+		if (offs < len)
+			memcpy(__get_dynamic_array(data),
+			       ((u8 *)rxbuf) + offs, len - offs);
+	),
+	TP_printk("[%s] RX frame data", __get_str(dev))
+);
+#endif /* __IWLWIFI_DEVICE_TRACE_DATA */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace-data
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h
new file mode 100644
index 0000000..f62c544
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h
@@ -0,0 +1,155 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#if !defined(__IWLWIFI_DEVICE_TRACE_IO) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE_IO
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi_io
+
+TRACE_EVENT(iwlwifi_dev_ioread32,
+	TP_PROTO(const struct device *dev, u32 offs, u32 val),
+	TP_ARGS(dev, offs, val),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+		__field(u32, offs)
+		__field(u32, val)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		__entry->offs = offs;
+		__entry->val = val;
+	),
+	TP_printk("[%s] read io[%#x] = %#x",
+		  __get_str(dev), __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_iowrite8,
+	TP_PROTO(const struct device *dev, u32 offs, u8 val),
+	TP_ARGS(dev, offs, val),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+		__field(u32, offs)
+		__field(u8, val)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		__entry->offs = offs;
+		__entry->val = val;
+	),
+	TP_printk("[%s] write io[%#x] = %#x)",
+		  __get_str(dev), __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_iowrite32,
+	TP_PROTO(const struct device *dev, u32 offs, u32 val),
+	TP_ARGS(dev, offs, val),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+		__field(u32, offs)
+		__field(u32, val)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		__entry->offs = offs;
+		__entry->val = val;
+	),
+	TP_printk("[%s] write io[%#x] = %#x)",
+		  __get_str(dev), __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_iowrite_prph32,
+	TP_PROTO(const struct device *dev, u32 offs, u32 val),
+	TP_ARGS(dev, offs, val),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+		__field(u32, offs)
+		__field(u32, val)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		__entry->offs = offs;
+		__entry->val = val;
+	),
+	TP_printk("[%s] write PRPH[%#x] = %#x)",
+		  __get_str(dev), __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_ioread_prph32,
+	TP_PROTO(const struct device *dev, u32 offs, u32 val),
+	TP_ARGS(dev, offs, val),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+		__field(u32, offs)
+		__field(u32, val)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		__entry->offs = offs;
+		__entry->val = val;
+	),
+	TP_printk("[%s] read PRPH[%#x] = %#x",
+		  __get_str(dev), __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_irq,
+	TP_PROTO(const struct device *dev),
+	TP_ARGS(dev),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+	),
+	/* TP_printk("") doesn't compile */
+	TP_printk("%d", 0)
+);
+
+TRACE_EVENT(iwlwifi_dev_ict_read,
+	TP_PROTO(const struct device *dev, u32 index, u32 value),
+	TP_ARGS(dev, index, value),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+		__field(u32, index)
+		__field(u32, value)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		__entry->index = index;
+		__entry->value = value;
+	),
+	TP_printk("[%s] read ict[%d] = %#.8x",
+		  __get_str(dev), __entry->index, __entry->value)
+);
+#endif /* __IWLWIFI_DEVICE_TRACE_IO */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace-io
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h
new file mode 100644
index 0000000..223b875
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h
@@ -0,0 +1,202 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#if !defined(__IWLWIFI_DEVICE_TRACE_IWLWIFI) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE_IWLWIFI
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi
+
+TRACE_EVENT(iwlwifi_dev_hcmd,
+	TP_PROTO(const struct device *dev,
+		 struct iwl_host_cmd *cmd, u16 total_size,
+		 struct iwl_cmd_header *hdr),
+	TP_ARGS(dev, cmd, total_size, hdr),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+		__dynamic_array(u8, hcmd, total_size)
+		__field(u32, flags)
+	),
+	TP_fast_assign(
+		int i, offset = sizeof(*hdr);
+
+		DEV_ASSIGN;
+		__entry->flags = cmd->flags;
+		memcpy(__get_dynamic_array(hcmd), hdr, sizeof(*hdr));
+
+		for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
+			if (!cmd->len[i])
+				continue;
+			memcpy((u8 *)__get_dynamic_array(hcmd) + offset,
+			       cmd->data[i], cmd->len[i]);
+			offset += cmd->len[i];
+		}
+	),
+	TP_printk("[%s] hcmd %#.2x (%ssync)",
+		  __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[0],
+		  __entry->flags & CMD_ASYNC ? "a" : "")
+);
+
+TRACE_EVENT(iwlwifi_dev_rx,
+	TP_PROTO(const struct device *dev, const struct iwl_trans *trans,
+		 void *rxbuf, size_t len),
+	TP_ARGS(dev, trans, rxbuf, len),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+		__dynamic_array(u8, rxbuf, iwl_rx_trace_len(trans, rxbuf, len))
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		memcpy(__get_dynamic_array(rxbuf), rxbuf,
+		       iwl_rx_trace_len(trans, rxbuf, len));
+	),
+	TP_printk("[%s] RX cmd %#.2x",
+		  __get_str(dev), ((u8 *)__get_dynamic_array(rxbuf))[4])
+);
+
+TRACE_EVENT(iwlwifi_dev_tx,
+	TP_PROTO(const struct device *dev, struct sk_buff *skb,
+		 void *tfd, size_t tfdlen,
+		 void *buf0, size_t buf0_len,
+		 void *buf1, size_t buf1_len),
+	TP_ARGS(dev, skb, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+
+		__field(size_t, framelen)
+		__dynamic_array(u8, tfd, tfdlen)
+
+		/*
+		 * Do not insert between or below these items,
+		 * we want to keep the frame together (except
+		 * for the possible padding).
+		 */
+		__dynamic_array(u8, buf0, buf0_len)
+		__dynamic_array(u8, buf1, iwl_trace_data(skb) ? 0 : buf1_len)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		__entry->framelen = buf0_len + buf1_len;
+		memcpy(__get_dynamic_array(tfd), tfd, tfdlen);
+		memcpy(__get_dynamic_array(buf0), buf0, buf0_len);
+		if (!iwl_trace_data(skb))
+			memcpy(__get_dynamic_array(buf1), buf1, buf1_len);
+	),
+	TP_printk("[%s] TX %.2x (%zu bytes)",
+		  __get_str(dev), ((u8 *)__get_dynamic_array(buf0))[0],
+		  __entry->framelen)
+);
+
+TRACE_EVENT(iwlwifi_dev_ucode_error,
+	TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low,
+		 u32 data1, u32 data2, u32 line, u32 blink1,
+		 u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time,
+		 u32 gp1, u32 gp2, u32 gp3, u32 major, u32 minor, u32 hw_ver,
+		 u32 brd_ver),
+	TP_ARGS(dev, desc, tsf_low, data1, data2, line,
+		blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2,
+		gp3, major, minor, hw_ver, brd_ver),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+		__field(u32, desc)
+		__field(u32, tsf_low)
+		__field(u32, data1)
+		__field(u32, data2)
+		__field(u32, line)
+		__field(u32, blink1)
+		__field(u32, blink2)
+		__field(u32, ilink1)
+		__field(u32, ilink2)
+		__field(u32, bcon_time)
+		__field(u32, gp1)
+		__field(u32, gp2)
+		__field(u32, gp3)
+		__field(u32, major)
+		__field(u32, minor)
+		__field(u32, hw_ver)
+		__field(u32, brd_ver)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		__entry->desc = desc;
+		__entry->tsf_low = tsf_low;
+		__entry->data1 = data1;
+		__entry->data2 = data2;
+		__entry->line = line;
+		__entry->blink1 = blink1;
+		__entry->blink2 = blink2;
+		__entry->ilink1 = ilink1;
+		__entry->ilink2 = ilink2;
+		__entry->bcon_time = bcon_time;
+		__entry->gp1 = gp1;
+		__entry->gp2 = gp2;
+		__entry->gp3 = gp3;
+		__entry->major = major;
+		__entry->minor = minor;
+		__entry->hw_ver = hw_ver;
+		__entry->brd_ver = brd_ver;
+	),
+	TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, "
+		  "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X "
+		  "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X major 0x%08X "
+		  "minor 0x%08X hw 0x%08X brd 0x%08X",
+		  __get_str(dev), __entry->desc, __entry->tsf_low,
+		  __entry->data1,
+		  __entry->data2, __entry->line, __entry->blink1,
+		  __entry->blink2, __entry->ilink1, __entry->ilink2,
+		  __entry->bcon_time, __entry->gp1, __entry->gp2,
+		  __entry->gp3, __entry->major, __entry->minor,
+		  __entry->hw_ver, __entry->brd_ver)
+);
+
+TRACE_EVENT(iwlwifi_dev_ucode_event,
+	TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev),
+	TP_ARGS(dev, time, data, ev),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+
+		__field(u32, time)
+		__field(u32, data)
+		__field(u32, ev)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		__entry->time = time;
+		__entry->data = data;
+		__entry->ev = ev;
+	),
+	TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u",
+		  __get_str(dev), __entry->time, __entry->data, __entry->ev)
+);
+#endif /* __IWLWIFI_DEVICE_TRACE_IWLWIFI */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace-iwlwifi
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h
new file mode 100644
index 0000000..a3b3c24
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h
@@ -0,0 +1,97 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#if !defined(__IWLWIFI_DEVICE_TRACE_MSG) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE_MSG
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi_msg
+
+#define MAX_MSG_LEN	110
+
+DECLARE_EVENT_CLASS(iwlwifi_msg_event,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf),
+	TP_STRUCT__entry(
+		__dynamic_array(char, msg, MAX_MSG_LEN)
+	),
+	TP_fast_assign(
+		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+				       MAX_MSG_LEN, vaf->fmt,
+				       *vaf->va) >= MAX_MSG_LEN);
+	),
+	TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_err,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_warn,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_info,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_crit,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+
+TRACE_EVENT(iwlwifi_dbg,
+	TP_PROTO(u32 level, bool in_interrupt, const char *function,
+		 struct va_format *vaf),
+	TP_ARGS(level, in_interrupt, function, vaf),
+	TP_STRUCT__entry(
+		__field(u32, level)
+		__field(u8, in_interrupt)
+		__string(function, function)
+		__dynamic_array(char, msg, MAX_MSG_LEN)
+	),
+	TP_fast_assign(
+		__entry->level = level;
+		__entry->in_interrupt = in_interrupt;
+		__assign_str(function, function);
+		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+				       MAX_MSG_LEN, vaf->fmt,
+				       *vaf->va) >= MAX_MSG_LEN);
+	),
+	TP_printk("%s", __get_str(msg))
+);
+#endif /* __IWLWIFI_DEVICE_TRACE_MSG */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace-msg
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h
new file mode 100644
index 0000000..10839fa
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h
@@ -0,0 +1,81 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#if !defined(__IWLWIFI_DEVICE_TRACE_UCODE) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE_UCODE
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi_ucode
+
+TRACE_EVENT(iwlwifi_dev_ucode_cont_event,
+	TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev),
+	TP_ARGS(dev, time, data, ev),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+
+		__field(u32, time)
+		__field(u32, data)
+		__field(u32, ev)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		__entry->time = time;
+		__entry->data = data;
+		__entry->ev = ev;
+	),
+	TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u",
+		  __get_str(dev), __entry->time, __entry->data, __entry->ev)
+);
+
+TRACE_EVENT(iwlwifi_dev_ucode_wrap_event,
+	TP_PROTO(const struct device *dev, u32 wraps, u32 n_entry, u32 p_entry),
+	TP_ARGS(dev, wraps, n_entry, p_entry),
+	TP_STRUCT__entry(
+		DEV_ENTRY
+
+		__field(u32, wraps)
+		__field(u32, n_entry)
+		__field(u32, p_entry)
+	),
+	TP_fast_assign(
+		DEV_ASSIGN;
+		__entry->wraps = wraps;
+		__entry->n_entry = n_entry;
+		__entry->p_entry = p_entry;
+	),
+	TP_printk("[%s] wraps=#%02d n=0x%X p=0x%X",
+		  __get_str(dev), __entry->wraps, __entry->n_entry,
+		  __entry->p_entry)
+);
+#endif /* __IWLWIFI_DEVICE_TRACE_UCODE */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace-ucode
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h
index 27248ac..a124f30 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h
@@ -24,7 +24,7 @@
  *
  *****************************************************************************/
 
-#if !defined(__IWLWIFI_DEVICE_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#ifndef __IWLWIFI_DEVICE_TRACE
 #include <linux/skbuff.h>
 #include <linux/ieee80211.h>
 #include <net/cfg80211.h>
@@ -80,436 +80,10 @@
 #define DEV_ENTRY	__string(dev, dev_name(dev))
 #define DEV_ASSIGN	__assign_str(dev, dev_name(dev))
 
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM iwlwifi_io
+#include "iwl-devtrace-io.h"
+#include "iwl-devtrace-ucode.h"
+#include "iwl-devtrace-msg.h"
+#include "iwl-devtrace-data.h"
+#include "iwl-devtrace-iwlwifi.h"
 
-TRACE_EVENT(iwlwifi_dev_ioread32,
-	TP_PROTO(const struct device *dev, u32 offs, u32 val),
-	TP_ARGS(dev, offs, val),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-		__field(u32, offs)
-		__field(u32, val)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		__entry->offs = offs;
-		__entry->val = val;
-	),
-	TP_printk("[%s] read io[%#x] = %#x",
-		  __get_str(dev), __entry->offs, __entry->val)
-);
-
-TRACE_EVENT(iwlwifi_dev_iowrite8,
-	TP_PROTO(const struct device *dev, u32 offs, u8 val),
-	TP_ARGS(dev, offs, val),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-		__field(u32, offs)
-		__field(u8, val)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		__entry->offs = offs;
-		__entry->val = val;
-	),
-	TP_printk("[%s] write io[%#x] = %#x)",
-		  __get_str(dev), __entry->offs, __entry->val)
-);
-
-TRACE_EVENT(iwlwifi_dev_iowrite32,
-	TP_PROTO(const struct device *dev, u32 offs, u32 val),
-	TP_ARGS(dev, offs, val),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-		__field(u32, offs)
-		__field(u32, val)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		__entry->offs = offs;
-		__entry->val = val;
-	),
-	TP_printk("[%s] write io[%#x] = %#x)",
-		  __get_str(dev), __entry->offs, __entry->val)
-);
-
-TRACE_EVENT(iwlwifi_dev_iowrite_prph32,
-	TP_PROTO(const struct device *dev, u32 offs, u32 val),
-	TP_ARGS(dev, offs, val),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-		__field(u32, offs)
-		__field(u32, val)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		__entry->offs = offs;
-		__entry->val = val;
-	),
-	TP_printk("[%s] write PRPH[%#x] = %#x)",
-		  __get_str(dev), __entry->offs, __entry->val)
-);
-
-TRACE_EVENT(iwlwifi_dev_ioread_prph32,
-	TP_PROTO(const struct device *dev, u32 offs, u32 val),
-	TP_ARGS(dev, offs, val),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-		__field(u32, offs)
-		__field(u32, val)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		__entry->offs = offs;
-		__entry->val = val;
-	),
-	TP_printk("[%s] read PRPH[%#x] = %#x",
-		  __get_str(dev), __entry->offs, __entry->val)
-);
-
-TRACE_EVENT(iwlwifi_dev_irq,
-	TP_PROTO(const struct device *dev),
-	TP_ARGS(dev),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-	),
-	/* TP_printk("") doesn't compile */
-	TP_printk("%d", 0)
-);
-
-TRACE_EVENT(iwlwifi_dev_ict_read,
-	TP_PROTO(const struct device *dev, u32 index, u32 value),
-	TP_ARGS(dev, index, value),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-		__field(u32, index)
-		__field(u32, value)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		__entry->index = index;
-		__entry->value = value;
-	),
-	TP_printk("[%s] read ict[%d] = %#.8x",
-		  __get_str(dev), __entry->index, __entry->value)
-);
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM iwlwifi_ucode
-
-TRACE_EVENT(iwlwifi_dev_ucode_cont_event,
-	TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev),
-	TP_ARGS(dev, time, data, ev),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-
-		__field(u32, time)
-		__field(u32, data)
-		__field(u32, ev)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		__entry->time = time;
-		__entry->data = data;
-		__entry->ev = ev;
-	),
-	TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u",
-		  __get_str(dev), __entry->time, __entry->data, __entry->ev)
-);
-
-TRACE_EVENT(iwlwifi_dev_ucode_wrap_event,
-	TP_PROTO(const struct device *dev, u32 wraps, u32 n_entry, u32 p_entry),
-	TP_ARGS(dev, wraps, n_entry, p_entry),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-
-		__field(u32, wraps)
-		__field(u32, n_entry)
-		__field(u32, p_entry)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		__entry->wraps = wraps;
-		__entry->n_entry = n_entry;
-		__entry->p_entry = p_entry;
-	),
-	TP_printk("[%s] wraps=#%02d n=0x%X p=0x%X",
-		  __get_str(dev), __entry->wraps, __entry->n_entry,
-		  __entry->p_entry)
-);
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM iwlwifi_msg
-
-#define MAX_MSG_LEN	110
-
-DECLARE_EVENT_CLASS(iwlwifi_msg_event,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf),
-	TP_STRUCT__entry(
-		__dynamic_array(char, msg, MAX_MSG_LEN)
-	),
-	TP_fast_assign(
-		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
-				       MAX_MSG_LEN, vaf->fmt,
-				       *vaf->va) >= MAX_MSG_LEN);
-	),
-	TP_printk("%s", __get_str(msg))
-);
-
-DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_err,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf)
-);
-
-DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_warn,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf)
-);
-
-DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_info,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf)
-);
-
-DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_crit,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf)
-);
-
-TRACE_EVENT(iwlwifi_dbg,
-	TP_PROTO(u32 level, bool in_interrupt, const char *function,
-		 struct va_format *vaf),
-	TP_ARGS(level, in_interrupt, function, vaf),
-	TP_STRUCT__entry(
-		__field(u32, level)
-		__field(u8, in_interrupt)
-		__string(function, function)
-		__dynamic_array(char, msg, MAX_MSG_LEN)
-	),
-	TP_fast_assign(
-		__entry->level = level;
-		__entry->in_interrupt = in_interrupt;
-		__assign_str(function, function);
-		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
-				       MAX_MSG_LEN, vaf->fmt,
-				       *vaf->va) >= MAX_MSG_LEN);
-	),
-	TP_printk("%s", __get_str(msg))
-);
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM iwlwifi_data
-
-TRACE_EVENT(iwlwifi_dev_tx_data,
-	TP_PROTO(const struct device *dev,
-		 struct sk_buff *skb,
-		 void *data, size_t data_len),
-	TP_ARGS(dev, skb, data, data_len),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-
-		__dynamic_array(u8, data, iwl_trace_data(skb) ? data_len : 0)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		if (iwl_trace_data(skb))
-			memcpy(__get_dynamic_array(data), data, data_len);
-	),
-	TP_printk("[%s] TX frame data", __get_str(dev))
-);
-
-TRACE_EVENT(iwlwifi_dev_rx_data,
-	TP_PROTO(const struct device *dev,
-		 const struct iwl_trans *trans,
-		 void *rxbuf, size_t len),
-	TP_ARGS(dev, trans, rxbuf, len),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-
-		__dynamic_array(u8, data,
-				len - iwl_rx_trace_len(trans, rxbuf, len))
-	),
-	TP_fast_assign(
-		size_t offs = iwl_rx_trace_len(trans, rxbuf, len);
-		DEV_ASSIGN;
-		if (offs < len)
-			memcpy(__get_dynamic_array(data),
-			       ((u8 *)rxbuf) + offs, len - offs);
-	),
-	TP_printk("[%s] RX frame data", __get_str(dev))
-);
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM iwlwifi
-
-TRACE_EVENT(iwlwifi_dev_hcmd,
-	TP_PROTO(const struct device *dev,
-		 struct iwl_host_cmd *cmd, u16 total_size,
-		 struct iwl_cmd_header *hdr),
-	TP_ARGS(dev, cmd, total_size, hdr),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-		__dynamic_array(u8, hcmd, total_size)
-		__field(u32, flags)
-	),
-	TP_fast_assign(
-		int i, offset = sizeof(*hdr);
-
-		DEV_ASSIGN;
-		__entry->flags = cmd->flags;
-		memcpy(__get_dynamic_array(hcmd), hdr, sizeof(*hdr));
-
-		for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
-			if (!cmd->len[i])
-				continue;
-			memcpy((u8 *)__get_dynamic_array(hcmd) + offset,
-			       cmd->data[i], cmd->len[i]);
-			offset += cmd->len[i];
-		}
-	),
-	TP_printk("[%s] hcmd %#.2x (%ssync)",
-		  __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[0],
-		  __entry->flags & CMD_ASYNC ? "a" : "")
-);
-
-TRACE_EVENT(iwlwifi_dev_rx,
-	TP_PROTO(const struct device *dev, const struct iwl_trans *trans,
-		 void *rxbuf, size_t len),
-	TP_ARGS(dev, trans, rxbuf, len),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-		__dynamic_array(u8, rxbuf, iwl_rx_trace_len(trans, rxbuf, len))
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		memcpy(__get_dynamic_array(rxbuf), rxbuf,
-		       iwl_rx_trace_len(trans, rxbuf, len));
-	),
-	TP_printk("[%s] RX cmd %#.2x",
-		  __get_str(dev), ((u8 *)__get_dynamic_array(rxbuf))[4])
-);
-
-TRACE_EVENT(iwlwifi_dev_tx,
-	TP_PROTO(const struct device *dev, struct sk_buff *skb,
-		 void *tfd, size_t tfdlen,
-		 void *buf0, size_t buf0_len,
-		 void *buf1, size_t buf1_len),
-	TP_ARGS(dev, skb, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-
-		__field(size_t, framelen)
-		__dynamic_array(u8, tfd, tfdlen)
-
-		/*
-		 * Do not insert between or below these items,
-		 * we want to keep the frame together (except
-		 * for the possible padding).
-		 */
-		__dynamic_array(u8, buf0, buf0_len)
-		__dynamic_array(u8, buf1, iwl_trace_data(skb) ? 0 : buf1_len)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		__entry->framelen = buf0_len + buf1_len;
-		memcpy(__get_dynamic_array(tfd), tfd, tfdlen);
-		memcpy(__get_dynamic_array(buf0), buf0, buf0_len);
-		if (!iwl_trace_data(skb))
-			memcpy(__get_dynamic_array(buf1), buf1, buf1_len);
-	),
-	TP_printk("[%s] TX %.2x (%zu bytes)",
-		  __get_str(dev), ((u8 *)__get_dynamic_array(buf0))[0],
-		  __entry->framelen)
-);
-
-TRACE_EVENT(iwlwifi_dev_ucode_error,
-	TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low,
-		 u32 data1, u32 data2, u32 line, u32 blink1,
-		 u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time,
-		 u32 gp1, u32 gp2, u32 gp3, u32 ucode_ver, u32 hw_ver,
-		 u32 brd_ver),
-	TP_ARGS(dev, desc, tsf_low, data1, data2, line,
-		blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2,
-		gp3, ucode_ver, hw_ver, brd_ver),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-		__field(u32, desc)
-		__field(u32, tsf_low)
-		__field(u32, data1)
-		__field(u32, data2)
-		__field(u32, line)
-		__field(u32, blink1)
-		__field(u32, blink2)
-		__field(u32, ilink1)
-		__field(u32, ilink2)
-		__field(u32, bcon_time)
-		__field(u32, gp1)
-		__field(u32, gp2)
-		__field(u32, gp3)
-		__field(u32, ucode_ver)
-		__field(u32, hw_ver)
-		__field(u32, brd_ver)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		__entry->desc = desc;
-		__entry->tsf_low = tsf_low;
-		__entry->data1 = data1;
-		__entry->data2 = data2;
-		__entry->line = line;
-		__entry->blink1 = blink1;
-		__entry->blink2 = blink2;
-		__entry->ilink1 = ilink1;
-		__entry->ilink2 = ilink2;
-		__entry->bcon_time = bcon_time;
-		__entry->gp1 = gp1;
-		__entry->gp2 = gp2;
-		__entry->gp3 = gp3;
-		__entry->ucode_ver = ucode_ver;
-		__entry->hw_ver = hw_ver;
-		__entry->brd_ver = brd_ver;
-	),
-	TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, "
-		  "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X "
-		  "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X uCode 0x%08X "
-		  "hw 0x%08X brd 0x%08X",
-		  __get_str(dev), __entry->desc, __entry->tsf_low,
-		  __entry->data1,
-		  __entry->data2, __entry->line, __entry->blink1,
-		  __entry->blink2, __entry->ilink1, __entry->ilink2,
-		  __entry->bcon_time, __entry->gp1, __entry->gp2,
-		  __entry->gp3, __entry->ucode_ver, __entry->hw_ver,
-		  __entry->brd_ver)
-);
-
-TRACE_EVENT(iwlwifi_dev_ucode_event,
-	TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev),
-	TP_ARGS(dev, time, data, ev),
-	TP_STRUCT__entry(
-		DEV_ENTRY
-
-		__field(u32, time)
-		__field(u32, data)
-		__field(u32, ev)
-	),
-	TP_fast_assign(
-		DEV_ASSIGN;
-		__entry->time = time;
-		__entry->data = data;
-		__entry->ev = ev;
-	),
-	TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u",
-		  __get_str(dev), __entry->time, __entry->data, __entry->ev)
-);
 #endif /* __IWLWIFI_DEVICE_TRACE */
-
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-#undef TRACE_INCLUDE_FILE
-#define TRACE_INCLUDE_FILE iwl-devtrace
-#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index 4322c55..6224e0a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -84,21 +84,8 @@
  *
  ******************************************************************************/
 
-/*
- * module name, copyright, version, etc.
- */
 #define DRV_DESCRIPTION	"Intel(R) Wireless WiFi driver for Linux"
-
-#ifdef CPTCFG_IWLWIFI_DEBUG
-#define VD "d"
-#else
-#define VD
-#endif
-
-#define DRV_VERSION     IWLWIFI_VERSION VD
-
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_VERSION(DRV_VERSION);
 MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
 MODULE_LICENSE("GPL");
 
@@ -158,7 +145,7 @@
 #define IWL_DEFAULT_SCAN_CHANNELS 40
 
 /*
- * struct fw_sec: Just for the image parsing proccess.
+ * struct fw_sec: Just for the image parsing process.
  * For the fw storage we are using struct fw_desc.
  */
 struct fw_sec {
@@ -188,6 +175,8 @@
 	kfree(drv->fw.dbg_dest_tlv);
 	for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
 		kfree(drv->fw.dbg_conf_tlv[i]);
+	for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++)
+		kfree(drv->fw.dbg_trigger_tlv[i]);
 
 	for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
 		iwl_free_fw_img(drv, drv->fw.img + i);
@@ -250,28 +239,12 @@
 	/*
 	 * Starting 8000B - FW name format has changed. This overwrites the
 	 * previous name and uses the new format.
-	 *
-	 * TODO:
-	 * Once there is only one supported step for 8000 family - delete this!
 	 */
 	if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
-		char rev_step[2] = {
-			'A' + CSR_HW_REV_STEP(drv->trans->hw_rev), 0
-		};
-
-		/* A-step doesn't have an indication */
-		if (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP)
-			rev_step[0] = 0;
-
-		/*
-		 * If hw_rev wasn't set yet - default as B-step. If it IS A-step
-		 * we'll reload that FW later instead.
-		 */
-		if (drv->trans->hw_rev == 0)
-			rev_step[0] = 'B';
+		char rev_step = 'A' + CSR_HW_REV_STEP(drv->trans->hw_rev);
 
 		snprintf(drv->firmware_name, sizeof(drv->firmware_name),
-			 "%s%s-%s.ucode", name_pre, rev_step, tag);
+			 "%s%c-%s.ucode", name_pre, rev_step, tag);
 	}
 
 	IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n",
@@ -316,8 +289,10 @@
 
 	/* FW debug data parsed for driver usage */
 	struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
-	struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
-	size_t dbg_conf_tlv_len[FW_DBG_MAX];
+	struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
+	size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
+	struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
+	size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
 };
 
 /*
@@ -865,6 +840,23 @@
 			capa->n_scan_channels =
 				le32_to_cpup((__le32 *)tlv_data);
 			break;
+		case IWL_UCODE_TLV_FW_VERSION: {
+			__le32 *ptr = (void *)tlv_data;
+			u32 major, minor;
+			u8 local_comp;
+
+			if (tlv_len != sizeof(u32) * 3)
+				goto invalid_tlv_len;
+
+			major = le32_to_cpup(ptr++);
+			minor = le32_to_cpup(ptr++);
+			local_comp = le32_to_cpup(ptr);
+
+			snprintf(drv->fw.fw_version,
+				 sizeof(drv->fw.fw_version), "%u.%u.%u",
+				 major, minor, local_comp);
+			break;
+			}
 		case IWL_UCODE_TLV_FW_DBG_DEST: {
 			struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
 
@@ -920,12 +912,43 @@
 			pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
 			break;
 			}
+		case IWL_UCODE_TLV_FW_DBG_TRIGGER: {
+			struct iwl_fw_dbg_trigger_tlv *trigger =
+				(void *)tlv_data;
+			u32 trigger_id = le32_to_cpu(trigger->id);
+
+			if (trigger_id >= ARRAY_SIZE(drv->fw.dbg_trigger_tlv)) {
+				IWL_ERR(drv,
+					"Skip unknown trigger: %u\n",
+					trigger->id);
+				break;
+			}
+
+			if (pieces->dbg_trigger_tlv[trigger_id]) {
+				IWL_ERR(drv,
+					"Ignore duplicate dbg trigger %u\n",
+					trigger->id);
+				break;
+			}
+
+			IWL_INFO(drv, "Found debug trigger: %u\n", trigger->id);
+
+			pieces->dbg_trigger_tlv[trigger_id] = trigger;
+			pieces->dbg_trigger_tlv_len[trigger_id] = tlv_len;
+			break;
+			}
 		case IWL_UCODE_TLV_SEC_RT_USNIFFER:
 			usniffer_images = true;
 			iwl_store_ucode_sec(pieces, tlv_data,
 					    IWL_UCODE_REGULAR_USNIFFER,
 					    tlv_len);
 			break;
+		case IWL_UCODE_TLV_SDIO_ADMA_ADDR:
+			if (tlv_len != sizeof(u32))
+				goto invalid_tlv_len;
+			drv->fw.sdio_adma_addr =
+				le32_to_cpup((__le32 *)tlv_data);
+			break;
 		default:
 			IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
 			break;
@@ -985,34 +1008,34 @@
 
 	/* Verify that uCode images will fit in card's SRAM. */
 	if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) >
-							cfg->max_inst_size) {
+	    cfg->max_inst_size) {
 		IWL_ERR(drv, "uCode instr len %Zd too large to fit in\n",
 			get_sec_size(pieces, IWL_UCODE_REGULAR,
-						IWL_UCODE_SECTION_INST));
+				     IWL_UCODE_SECTION_INST));
 		return -1;
 	}
 
 	if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) >
-							cfg->max_data_size) {
+	    cfg->max_data_size) {
 		IWL_ERR(drv, "uCode data len %Zd too large to fit in\n",
 			get_sec_size(pieces, IWL_UCODE_REGULAR,
-						IWL_UCODE_SECTION_DATA));
+				     IWL_UCODE_SECTION_DATA));
 		return -1;
 	}
 
-	 if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) >
-							cfg->max_inst_size) {
+	if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) >
+	     cfg->max_inst_size) {
 		IWL_ERR(drv, "uCode init instr len %Zd too large to fit in\n",
 			get_sec_size(pieces, IWL_UCODE_INIT,
-						IWL_UCODE_SECTION_INST));
+				     IWL_UCODE_SECTION_INST));
 		return -1;
 	}
 
 	if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) >
-							cfg->max_data_size) {
+	    cfg->max_data_size) {
 		IWL_ERR(drv, "uCode init data len %Zd too large to fit in\n",
 			get_sec_size(pieces, IWL_UCODE_REGULAR,
-						IWL_UCODE_SECTION_DATA));
+				     IWL_UCODE_SECTION_DATA));
 		return -1;
 	}
 	return 0;
@@ -1079,10 +1102,10 @@
 	const unsigned int api_max = drv->cfg->ucode_api_max;
 	unsigned int api_ok = drv->cfg->ucode_api_ok;
 	const unsigned int api_min = drv->cfg->ucode_api_min;
+	size_t trigger_tlv_sz[FW_DBG_TRIGGER_MAX];
 	u32 api_ver;
 	int i;
 	bool load_module = false;
-	u32 hw_rev = drv->trans->hw_rev;
 
 	fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
 	fw->ucode_capa.standard_phy_calibration_size =
@@ -1125,7 +1148,10 @@
 	if (err)
 		goto try_again;
 
-	api_ver = IWL_UCODE_API(drv->fw.ucode_ver);
+	if (drv->fw.ucode_capa.api[0] & IWL_UCODE_TLV_API_NEW_VERSION)
+		api_ver = drv->fw.ucode_ver;
+	else
+		api_ver = IWL_UCODE_API(drv->fw.ucode_ver);
 
 	/*
 	 * api_ver should match the api version forming part of the
@@ -1196,6 +1222,48 @@
 		}
 	}
 
+	memset(&trigger_tlv_sz, 0xff, sizeof(trigger_tlv_sz));
+
+	trigger_tlv_sz[FW_DBG_TRIGGER_MISSED_BEACONS] =
+		sizeof(struct iwl_fw_dbg_trigger_missed_bcon);
+	trigger_tlv_sz[FW_DBG_TRIGGER_CHANNEL_SWITCH] = 0;
+	trigger_tlv_sz[FW_DBG_TRIGGER_FW_NOTIF] =
+		sizeof(struct iwl_fw_dbg_trigger_cmd);
+	trigger_tlv_sz[FW_DBG_TRIGGER_MLME] =
+		sizeof(struct iwl_fw_dbg_trigger_mlme);
+	trigger_tlv_sz[FW_DBG_TRIGGER_STATS] =
+		sizeof(struct iwl_fw_dbg_trigger_stats);
+	trigger_tlv_sz[FW_DBG_TRIGGER_RSSI] =
+		sizeof(struct iwl_fw_dbg_trigger_low_rssi);
+	trigger_tlv_sz[FW_DBG_TRIGGER_TXQ_TIMERS] =
+		sizeof(struct iwl_fw_dbg_trigger_txq_timer);
+	trigger_tlv_sz[FW_DBG_TRIGGER_TIME_EVENT] =
+		sizeof(struct iwl_fw_dbg_trigger_time_event);
+
+	for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) {
+		if (pieces->dbg_trigger_tlv[i]) {
+			/*
+			 * If the trigger isn't long enough, WARN and exit.
+			 * Someone is trying to debug something and he won't
+			 * be able to catch the bug he is trying to chase.
+			 * We'd better be noisy to be sure he knows what's
+			 * going on.
+			 */
+			if (WARN_ON(pieces->dbg_trigger_tlv_len[i] <
+				    (trigger_tlv_sz[i] +
+				     sizeof(struct iwl_fw_dbg_trigger_tlv))))
+				goto out_free_fw;
+			drv->fw.dbg_trigger_tlv_len[i] =
+				pieces->dbg_trigger_tlv_len[i];
+			drv->fw.dbg_trigger_tlv[i] =
+				kmemdup(pieces->dbg_trigger_tlv[i],
+					drv->fw.dbg_trigger_tlv_len[i],
+					GFP_KERNEL);
+			if (!drv->fw.dbg_trigger_tlv[i])
+				goto out_free_fw;
+		}
+	}
+
 	/* Now that we can no longer fail, copy information */
 
 	/*
@@ -1275,58 +1343,15 @@
 				op->name, err);
 #endif
 	}
-
-	/*
-	 * We may have loaded the wrong FW file in 8000 HW family if it is an
-	 * A-step card, and if drv->trans->hw_rev wasn't properly read when
-	 * the FW file had been loaded. (This might happen in SDIO.) In such a
-	 * case - unload and reload the correct file.
-	 *
-	 * TODO:
-	 * Once there is only one supported step for 8000 family - delete this!
-	 */
-	if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
-	    CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP &&
-	    drv->trans->hw_rev != hw_rev) {
-		char firmware_name[32];
-
-		/* Free previous FW resources */
-		if (drv->op_mode)
-			_iwl_op_mode_stop(drv);
-		iwl_dealloc_ucode(drv);
-
-		/* Build name of correct-step FW */
-		snprintf(firmware_name, sizeof(firmware_name),
-			 strrchr(drv->firmware_name, '-'));
-		snprintf(drv->firmware_name, sizeof(drv->firmware_name),
-			 "%s%s", drv->cfg->fw_name_pre, firmware_name);
-
-		/* Clear data before loading correct FW */
-		list_del(&drv->list);
-
-		/* Request correct FW file this time */
-		IWL_DEBUG_INFO(drv, "attempting to load A-step FW %s\n",
-			       drv->firmware_name);
-		err = request_firmware(&ucode_raw, drv->firmware_name,
-				       drv->trans->dev);
-		if (err) {
-			IWL_ERR(drv, "Failed swapping FW!\n");
-			goto out_unbind;
-		}
-
-		/* Redo callback function - this time with right FW */
-		iwl_req_fw_callback(ucode_raw, context);
-	}
-
 	kfree(pieces);
 	return;
 
  try_again:
 	/* try next, if any */
-	kfree(pieces);
 	release_firmware(ucode_raw);
 	if (iwl_request_firmware(drv, false))
 		goto out_unbind;
+	kfree(pieces);
 	return;
 
  out_free_fw:
@@ -1429,7 +1454,7 @@
 	.restart_fw = true,
 	.bt_coex_active = true,
 	.power_level = IWL_POWER_INDEX_1,
-	.wd_disable = true,
+	.d0i3_disable = true,
 #ifndef CPTCFG_IWLWIFI_UAPSD
 	.uapsd_disable = true,
 #endif /* CPTCFG_IWLWIFI_UAPSD */
@@ -1492,7 +1517,7 @@
 	for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++)
 		INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv);
 
-	pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n");
+	pr_info(DRV_DESCRIPTION "\n");
 	pr_info(DRV_COPYRIGHT "\n");
 
 #ifdef CPTCFG_IWLWIFI_DEBUGFS
@@ -1539,15 +1564,19 @@
 MODULE_PARM_DESC(antenna_coupling,
 		 "specify antenna coupling in dB (default: 0 dB)");
 
-module_param_named(wd_disable, iwlwifi_mod_params.wd_disable, int, S_IRUGO);
-MODULE_PARM_DESC(wd_disable,
-		"Disable stuck queue watchdog timer 0=system default, 1=disable (default: 1)");
-
 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,
+module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable,
 		   bool, S_IRUGO);
+MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)");
+
+module_param_named(lar_disable, iwlwifi_mod_params.lar_disable,
+		   bool, S_IRUGO);
+MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)");
+
+module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable,
+		   bool, S_IRUGO | S_IWUSR);
 #ifdef CPTCFG_IWLWIFI_UAPSD
 MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)");
 #else
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h
index b987412..8c18403 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.h
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.h
@@ -68,8 +68,7 @@
 
 /* for all modules */
 #define DRV_NAME        "iwlwifi"
-#define IWLWIFI_VERSION "in-tree:"
-#define DRV_COPYRIGHT	"Copyright(c) 2003- 2014 Intel Corporation"
+#define DRV_COPYRIGHT	"Copyright(c) 2003- 2015 Intel Corporation"
 #define DRV_AUTHOR     "<ilw@linux.intel.com>"
 
 /* radio config bits (actual values from NVM definition) */
@@ -124,7 +123,7 @@
  * starts the driver: fetches the firmware. This should be called by bus
  * specific system flows implementations. For example, the bus specific probe
  * function should do bus related operations only, and then call to this
- * function. It returns the driver object or %NULL if an error occured.
+ * function. It returns the driver object or %NULL if an error occurred.
  */
 struct iwl_drv *iwl_drv_start(struct iwl_trans *trans,
 			      const struct iwl_cfg *cfg);
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
index f0548b8..5234a0b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
@@ -94,6 +94,7 @@
 	u32 nvm_version;
 	s8 max_tx_pwr_half_dbm;
 
+	bool lar_enabled;
 	struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
 	struct ieee80211_channel channels[];
 };
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
index 25d0105..219ca8a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
@@ -248,7 +248,7 @@
 	otpgp = iwl_read32(trans, CSR_OTP_GP_REG);
 	if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) {
 		/* stop in this case */
-		/* set the uncorrectable OTP ECC bit for acknowledgement */
+		/* set the uncorrectable OTP ECC bit for acknowledgment */
 		iwl_set_bit(trans, CSR_OTP_GP_REG,
 			    CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
 		IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n");
@@ -256,7 +256,7 @@
 	}
 	if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) {
 		/* continue in this case */
-		/* set the correctable OTP ECC bit for acknowledgement */
+		/* set the correctable OTP ECC bit for acknowledgment */
 		iwl_set_bit(trans, CSR_OTP_GP_REG,
 			    CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
 		IWL_ERR(trans, "Correctable OTP ECC error, continue read\n");
diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h
index 9564ae1..d45dc02 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fh.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fh.h
@@ -310,6 +310,7 @@
 #define FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE	(0x01000000)
 
 #define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT	28
+#define FH_MEM_TB_MAX_LENGTH			(0x00020000)
 
 /* TFDB  Area - TFDs buffer table */
 #define FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK      (0xFFFFFFFF)
@@ -444,7 +445,7 @@
 #define RX_LOW_WATERMARK 8
 
 /**
- * struct iwl_rb_status - reseve buffer status
+ * struct iwl_rb_status - reserve buffer status
  * 	host memory mapped FH registers
  * @closed_rb_num [0:11] - Indicates the index of the RB which was closed
  * @closed_fr_num [0:11] - Indicates the index of the RX Frame which was closed
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
index 20a8a64..251bf8d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
@@ -71,7 +71,6 @@
 
 /**
  * enum iwl_fw_error_dump_type - types of data in the dump file
- * @IWL_FW_ERROR_DUMP_SRAM:
  * @IWL_FW_ERROR_DUMP_CSR: Control Status Registers - from offset 0
  * @IWL_FW_ERROR_DUMP_RXF:
  * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as
@@ -82,9 +81,12 @@
  * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several
  *	sections like this in a single file.
  * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers
+ * @IWL_FW_ERROR_DUMP_MEM: chunk of memory
+ * @IWL_FW_ERROR_DUMP_ERROR_INFO: description of what triggered this dump.
+ *	Structured as &struct iwl_fw_error_dump_trigger_desc.
  */
 enum iwl_fw_error_dump_type {
-	IWL_FW_ERROR_DUMP_SRAM = 0,
+	/* 0 is deprecated */
 	IWL_FW_ERROR_DUMP_CSR = 1,
 	IWL_FW_ERROR_DUMP_RXF = 2,
 	IWL_FW_ERROR_DUMP_TXCMD = 3,
@@ -93,6 +95,8 @@
 	IWL_FW_ERROR_DUMP_PRPH = 6,
 	IWL_FW_ERROR_DUMP_TXF = 7,
 	IWL_FW_ERROR_DUMP_FH_REGS = 8,
+	IWL_FW_ERROR_DUMP_MEM = 9,
+	IWL_FW_ERROR_DUMP_ERROR_INFO = 10,
 
 	IWL_FW_ERROR_DUMP_MAX,
 };
@@ -133,6 +137,27 @@
 	u8 data[];
 } __packed;
 
+/**
+ * struct iwl_fw_error_dump_fifo - RX/TX FIFO data
+ * @fifo_num: number of FIFO (starting from 0)
+ * @available_bytes: num of bytes available in FIFO (may be less than FIFO size)
+ * @wr_ptr: position of write pointer
+ * @rd_ptr: position of read pointer
+ * @fence_ptr: position of fence pointer
+ * @fence_mode: the current mode of the fence (before locking) -
+ *	0=follow RD pointer ; 1 = freeze
+ * @data: all of the FIFO's data
+ */
+struct iwl_fw_error_dump_fifo {
+	__le32 fifo_num;
+	__le32 available_bytes;
+	__le32 wr_ptr;
+	__le32 rd_ptr;
+	__le32 fence_ptr;
+	__le32 fence_mode;
+	u8 data[];
+} __packed;
+
 enum iwl_fw_error_dump_family {
 	IWL_FW_ERROR_DUMP_FAMILY_7 = 7,
 	IWL_FW_ERROR_DUMP_FAMILY_8 = 8,
@@ -158,7 +183,7 @@
  * struct iwl_fw_error_dump_fw_mon - FW monitor data
  * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer
  * @fw_mon_base_ptr: base pointer of the data
- * @fw_mon_cycle_cnt: number of wrap arounds
+ * @fw_mon_cycle_cnt: number of wraparounds
  * @reserved: for future use
  * @data: captured data
  */
@@ -180,6 +205,23 @@
 	__le32 data[];
 };
 
+enum iwl_fw_error_dump_mem_type {
+	IWL_FW_ERROR_DUMP_MEM_SRAM,
+	IWL_FW_ERROR_DUMP_MEM_SMEM,
+};
+
+/**
+ * struct iwl_fw_error_dump_mem - chunk of memory
+ * @type: %enum iwl_fw_error_dump_mem_type
+ * @offset: the offset from which the memory was read
+ * @data: the content of the memory
+ */
+struct iwl_fw_error_dump_mem {
+	__le32 type;
+	__le32 offset;
+	u8 data[];
+};
+
 /**
  * iwl_fw_error_next_data - advance fw error dump data pointer
  * @data: previous data block
@@ -191,4 +233,53 @@
 	return (void *)(data->data + le32_to_cpu(data->len));
 }
 
+/**
+ * enum iwl_fw_dbg_trigger - triggers available
+ *
+ * @FW_DBG_TRIGGER_USER: trigger log collection by user
+ *	This should not be defined as a trigger to the driver, but a value the
+ *	driver should set to indicate that the trigger was initiated by the
+ *	user.
+ * @FW_DBG_TRIGGER_FW_ASSERT: trigger log collection when the firmware asserts
+ * @FW_DBG_TRIGGER_MISSED_BEACONS: trigger log collection when beacons are
+ *	missed.
+ * @FW_DBG_TRIGGER_CHANNEL_SWITCH: trigger log collection upon channel switch.
+ * @FW_DBG_TRIGGER_FW_NOTIF: trigger log collection when the firmware sends a
+ *	command response or a notification.
+ * @FW_DBG_TRIGGER_MLME: trigger log collection upon MLME event.
+ * @FW_DBG_TRIGGER_STATS: trigger log collection upon statistics threshold.
+ * @FW_DBG_TRIGGER_RSSI: trigger log collection when the rssi of the beacon
+ *	goes below a threshold.
+ * @FW_DBG_TRIGGER_TXQ_TIMERS: configures the timers for the Tx queue hang
+ *	detection.
+ * @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related
+ *	events.
+ */
+enum iwl_fw_dbg_trigger {
+	FW_DBG_TRIGGER_INVALID = 0,
+	FW_DBG_TRIGGER_USER,
+	FW_DBG_TRIGGER_FW_ASSERT,
+	FW_DBG_TRIGGER_MISSED_BEACONS,
+	FW_DBG_TRIGGER_CHANNEL_SWITCH,
+	FW_DBG_TRIGGER_FW_NOTIF,
+	FW_DBG_TRIGGER_MLME,
+	FW_DBG_TRIGGER_STATS,
+	FW_DBG_TRIGGER_RSSI,
+	FW_DBG_TRIGGER_TXQ_TIMERS,
+	FW_DBG_TRIGGER_TIME_EVENT,
+
+	/* must be last */
+	FW_DBG_TRIGGER_MAX,
+};
+
+/**
+ * struct iwl_fw_error_dump_trigger_desc - describes the trigger condition
+ * @type: %enum iwl_fw_dbg_trigger
+ * @data: raw data about what happened
+ */
+struct iwl_fw_error_dump_trigger_desc {
+	__le32 type;
+	u8 data[];
+};
+
 #endif /* __fw_error_dump_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
index f2a047f..bfdf3fa 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
@@ -66,6 +66,7 @@
 #define __iwl_fw_file_h__
 
 #include <linux/netdevice.h>
+#include <linux/nl80211.h>
 
 /* v1/v2 uCode file layout */
 struct iwl_ucode_header {
@@ -132,8 +133,11 @@
 	IWL_UCODE_TLV_ENABLED_CAPABILITIES	= 30,
 	IWL_UCODE_TLV_N_SCAN_CHANNELS		= 31,
 	IWL_UCODE_TLV_SEC_RT_USNIFFER	= 34,
+	IWL_UCODE_TLV_SDIO_ADMA_ADDR	= 35,
+	IWL_UCODE_TLV_FW_VERSION	= 36,
 	IWL_UCODE_TLV_FW_DBG_DEST	= 38,
 	IWL_UCODE_TLV_FW_DBG_CONF	= 39,
+	IWL_UCODE_TLV_FW_DBG_TRIGGER	= 40,
 };
 
 struct iwl_ucode_tlv {
@@ -155,7 +159,8 @@
 	__le32 zero;
 	__le32 magic;
 	u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
-	__le32 ver;		/* major/minor/API/serial */
+	/* major/minor/API/serial or major in new format */
+	__le32 ver;
 	__le32 build;
 	__le64 ignore;
 	/*
@@ -186,7 +191,7 @@
  * enum iwl_ucode_tlv_flag - ucode API flags
  * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
  *	was a separate TLV but moved here to save space.
- * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID,
+ * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behavior on hidden SSID,
  *	treats good CRC threshold as a boolean
  * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
  * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
@@ -234,25 +239,34 @@
 
 /**
  * enum iwl_ucode_tlv_api - ucode api
- * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
- * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification
  * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
- * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
- * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
- * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
- * @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif.
  * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
  *	longer than the passive one, which is essential for fragmented scan.
+ * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source.
+ * IWL_UCODE_TLV_API_HDC_PHASE_0: ucode supports finer configuration of LTR
+ * @IWL_UCODE_TLV_API_BASIC_DWELL: use only basic dwell time in scan command,
+ *	regardless of the band or the number of the probes. FW will calculate
+ *	the actual dwell time.
+ * @IWL_UCODE_TLV_API_SCD_CFG: This firmware can configure the scheduler
+ *	through the dedicated host command.
+ * @IWL_UCODE_TLV_API_SINGLE_SCAN_EBS: EBS is supported for single scans too.
+ * @IWL_UCODE_TLV_API_ASYNC_DTM: Async temperature notifications are supported.
+ * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params
+ * @IWL_UCODE_TLV_API_STATS_V10: uCode supports/uses statistics API version 10
+ * @IWL_UCODE_TLV_API_NEW_VERSION: new versioning format
  */
 enum iwl_ucode_tlv_api {
-	IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID	= BIT(0),
-	IWL_UCODE_TLV_CAPA_EXTENDED_BEACON	= BIT(1),
 	IWL_UCODE_TLV_API_BT_COEX_SPLIT         = BIT(3),
-	IWL_UCODE_TLV_API_CSA_FLOW		= BIT(4),
-	IWL_UCODE_TLV_API_DISABLE_STA_TX	= BIT(5),
-	IWL_UCODE_TLV_API_LMAC_SCAN		= BIT(6),
-	IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF	= BIT(7),
 	IWL_UCODE_TLV_API_FRAGMENTED_SCAN	= BIT(8),
+	IWL_UCODE_TLV_API_WIFI_MCC_UPDATE	= BIT(9),
+	IWL_UCODE_TLV_API_HDC_PHASE_0		= BIT(10),
+	IWL_UCODE_TLV_API_BASIC_DWELL		= BIT(13),
+	IWL_UCODE_TLV_API_SCD_CFG		= BIT(15),
+	IWL_UCODE_TLV_API_SINGLE_SCAN_EBS	= BIT(16),
+	IWL_UCODE_TLV_API_ASYNC_DTM		= BIT(17),
+	IWL_UCODE_TLV_API_LQ_SS_PARAMS		= BIT(18),
+	IWL_UCODE_TLV_API_STATS_V10		= BIT(19),
+	IWL_UCODE_TLV_API_NEW_VERSION		= BIT(20),
 };
 
 /**
@@ -260,6 +274,7 @@
  * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3
  * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory
  * @IWL_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan.
+ * @IWL_UCODE_TLV_CAPA_BEAMFORMER: supports Beamformer
  * @IWL_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality
  * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current
  *	tx power value into TPC Report action frame and Link Measurement Report
@@ -273,11 +288,19 @@
  *	which also implies support for the scheduler configuration command
  * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching
  * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
+ * @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics
+ * @IWL_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running
+ * @IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC: ucode supports LAR updates with different
+ *	sources for the MCC. This TLV bit is a future replacement to
+ *	IWL_UCODE_TLV_API_WIFI_MCC_UPDATE. When either is set, multi-source LAR
+ *	is supported.
+ * @IWL_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC
  */
 enum iwl_ucode_tlv_capa {
 	IWL_UCODE_TLV_CAPA_D0I3_SUPPORT			= BIT(0),
 	IWL_UCODE_TLV_CAPA_LAR_SUPPORT			= BIT(1),
 	IWL_UCODE_TLV_CAPA_UMAC_SCAN			= BIT(2),
+	IWL_UCODE_TLV_CAPA_BEAMFORMER			= BIT(3),
 	IWL_UCODE_TLV_CAPA_TDLS_SUPPORT			= BIT(6),
 	IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT	= BIT(8),
 	IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT	= BIT(9),
@@ -286,6 +309,10 @@
 	IWL_UCODE_TLV_CAPA_DQA_SUPPORT			= BIT(12),
 	IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH		= BIT(13),
 	IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT		= BIT(18),
+	IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS		= BIT(22),
+	IWL_UCODE_TLV_CAPA_BT_COEX_PLCR			= BIT(28),
+	IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC		= BIT(29),
+	IWL_UCODE_TLV_CAPA_BT_COEX_RRC			= BIT(30),
 };
 
 /* The default calibrate table size if not specified by firmware file */
@@ -438,44 +465,207 @@
 } __packed;
 
 /**
- * struct iwl_fw_dbg_trigger - a TLV that describes a debug configuration
+ * enum iwl_fw_dbg_trigger_mode - triggers functionalities
  *
- * @enabled: is this trigger enabled
- * @reserved:
- * @len: length, in bytes, of the %trigger field
- * @trigger: pointer to a trigger struct
+ * @IWL_FW_DBG_TRIGGER_START: when trigger occurs re-conf the dbg mechanism
+ * @IWL_FW_DBG_TRIGGER_STOP: when trigger occurs pull the dbg data
  */
-struct iwl_fw_dbg_trigger {
-	u8 enabled;
-	u8 reserved;
-	u8 len;
-	u8 trigger[0];
-} __packed;
-
-/**
- * enum iwl_fw_dbg_conf - configurations available
- *
- * @FW_DBG_CUSTOM: take this configuration from alive
- *	Note that the trigger is NO-OP for this configuration
- */
-enum iwl_fw_dbg_conf {
-	FW_DBG_CUSTOM = 0,
-
-	/* must be last */
-	FW_DBG_MAX,
-	FW_DBG_INVALID = 0xff,
+enum iwl_fw_dbg_trigger_mode {
+	IWL_FW_DBG_TRIGGER_START = BIT(0),
+	IWL_FW_DBG_TRIGGER_STOP = BIT(1),
 };
 
 /**
- * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration
+ * enum iwl_fw_dbg_trigger_vif_type - define the VIF type for a trigger
+ * @IWL_FW_DBG_CONF_VIF_ANY: any vif type
+ * @IWL_FW_DBG_CONF_VIF_IBSS: IBSS mode
+ * @IWL_FW_DBG_CONF_VIF_STATION: BSS mode
+ * @IWL_FW_DBG_CONF_VIF_AP: AP mode
+ * @IWL_FW_DBG_CONF_VIF_P2P_CLIENT: P2P Client mode
+ * @IWL_FW_DBG_CONF_VIF_P2P_GO: P2P GO mode
+ * @IWL_FW_DBG_CONF_VIF_P2P_DEVICE: P2P device
+ */
+enum iwl_fw_dbg_trigger_vif_type {
+	IWL_FW_DBG_CONF_VIF_ANY = NL80211_IFTYPE_UNSPECIFIED,
+	IWL_FW_DBG_CONF_VIF_IBSS = NL80211_IFTYPE_ADHOC,
+	IWL_FW_DBG_CONF_VIF_STATION = NL80211_IFTYPE_STATION,
+	IWL_FW_DBG_CONF_VIF_AP = NL80211_IFTYPE_AP,
+	IWL_FW_DBG_CONF_VIF_P2P_CLIENT = NL80211_IFTYPE_P2P_CLIENT,
+	IWL_FW_DBG_CONF_VIF_P2P_GO = NL80211_IFTYPE_P2P_GO,
+	IWL_FW_DBG_CONF_VIF_P2P_DEVICE = NL80211_IFTYPE_P2P_DEVICE,
+};
+
+/**
+ * struct iwl_fw_dbg_trigger_tlv - a TLV that describes the trigger
+ * @id: %enum iwl_fw_dbg_trigger
+ * @vif_type: %enum iwl_fw_dbg_trigger_vif_type
+ * @stop_conf_ids: bitmap of configurations this trigger relates to.
+ *	if the mode is %IWL_FW_DBG_TRIGGER_STOP, then if the bit corresponding
+ *	to the currently running configuration is set, the data should be
+ *	collected.
+ * @stop_delay: how many milliseconds to wait before collecting the data
+ *	after the STOP trigger fires.
+ * @mode: %enum iwl_fw_dbg_trigger_mode - can be stop / start of both
+ * @start_conf_id: if mode is %IWL_FW_DBG_TRIGGER_START, this defines what
+ *	configuration should be applied when the triggers kicks in.
+ * @occurrences: number of occurrences. 0 means the trigger will never fire.
+ */
+struct iwl_fw_dbg_trigger_tlv {
+	__le32 id;
+	__le32 vif_type;
+	__le32 stop_conf_ids;
+	__le32 stop_delay;
+	u8 mode;
+	u8 start_conf_id;
+	__le16 occurrences;
+	__le32 reserved[2];
+
+	u8 data[0];
+} __packed;
+
+#define FW_DBG_START_FROM_ALIVE	0
+#define FW_DBG_CONF_MAX		32
+#define FW_DBG_INVALID		0xff
+
+/**
+ * struct iwl_fw_dbg_trigger_missed_bcon - configures trigger for missed beacons
+ * @stop_consec_missed_bcon: stop recording if threshold is crossed.
+ * @stop_consec_missed_bcon_since_rx: stop recording if threshold is crossed.
+ * @start_consec_missed_bcon: start recording if threshold is crossed.
+ * @start_consec_missed_bcon_since_rx: start recording if threshold is crossed.
+ * @reserved1: reserved
+ * @reserved2: reserved
+ */
+struct iwl_fw_dbg_trigger_missed_bcon {
+	__le32 stop_consec_missed_bcon;
+	__le32 stop_consec_missed_bcon_since_rx;
+	__le32 reserved2[2];
+	__le32 start_consec_missed_bcon;
+	__le32 start_consec_missed_bcon_since_rx;
+	__le32 reserved1[2];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_cmd - configures trigger for messages from FW.
+ * cmds: the list of commands to trigger the collection on
+ */
+struct iwl_fw_dbg_trigger_cmd {
+	struct cmd {
+		u8 cmd_id;
+		u8 group_id;
+	} __packed cmds[16];
+} __packed;
+
+/**
+ * iwl_fw_dbg_trigger_stats - configures trigger for statistics
+ * @stop_offset: the offset of the value to be monitored
+ * @stop_threshold: the threshold above which to collect
+ * @start_offset: the offset of the value to be monitored
+ * @start_threshold: the threshold above which to start recording
+ */
+struct iwl_fw_dbg_trigger_stats {
+	__le32 stop_offset;
+	__le32 stop_threshold;
+	__le32 start_offset;
+	__le32 start_threshold;
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_low_rssi - trigger for low beacon RSSI
+ * @rssi: RSSI value to trigger at
+ */
+struct iwl_fw_dbg_trigger_low_rssi {
+	__le32 rssi;
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_mlme - configures trigger for mlme events
+ * @stop_auth_denied: number of denied authentication to collect
+ * @stop_auth_timeout: number of authentication timeout to collect
+ * @stop_rx_deauth: number of Rx deauth before to collect
+ * @stop_tx_deauth: number of Tx deauth before to collect
+ * @stop_assoc_denied: number of denied association to collect
+ * @stop_assoc_timeout: number of association timeout to collect
+ * @stop_connection_loss: number of connection loss to collect
+ * @start_auth_denied: number of denied authentication to start recording
+ * @start_auth_timeout: number of authentication timeout to start recording
+ * @start_rx_deauth: number of Rx deauth to start recording
+ * @start_tx_deauth: number of Tx deauth to start recording
+ * @start_assoc_denied: number of denied association to start recording
+ * @start_assoc_timeout: number of association timeout to start recording
+ * @start_connection_loss: number of connection loss to start recording
+ */
+struct iwl_fw_dbg_trigger_mlme {
+	u8 stop_auth_denied;
+	u8 stop_auth_timeout;
+	u8 stop_rx_deauth;
+	u8 stop_tx_deauth;
+
+	u8 stop_assoc_denied;
+	u8 stop_assoc_timeout;
+	u8 stop_connection_loss;
+	u8 reserved;
+
+	u8 start_auth_denied;
+	u8 start_auth_timeout;
+	u8 start_rx_deauth;
+	u8 start_tx_deauth;
+
+	u8 start_assoc_denied;
+	u8 start_assoc_timeout;
+	u8 start_connection_loss;
+	u8 reserved2;
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_txq_timer - configures the Tx queue's timer
+ * @command_queue: timeout for the command queue in ms
+ * @bss: timeout for the queues of a BSS (except for TDLS queues) in ms
+ * @softap: timeout for the queues of a softAP in ms
+ * @p2p_go: timeout for the queues of a P2P GO in ms
+ * @p2p_client: timeout for the queues of a P2P client in ms
+ * @p2p_device: timeout for the queues of a P2P device in ms
+ * @ibss: timeout for the queues of an IBSS in ms
+ * @tdls: timeout for the queues of a TDLS station in ms
+ */
+struct iwl_fw_dbg_trigger_txq_timer {
+	__le32 command_queue;
+	__le32 bss;
+	__le32 softap;
+	__le32 p2p_go;
+	__le32 p2p_client;
+	__le32 p2p_device;
+	__le32 ibss;
+	__le32 tdls;
+	__le32 reserved[4];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_time_event - configures a time event trigger
+ * time_Events: a list of tuples <id, action_bitmap>. The driver will issue a
+ *	trigger each time a time event notification that relates to time event
+ *	id with one of the actions in the bitmap is received and
+ *	BIT(notif->status) is set in status_bitmap.
  *
- * @id: %enum iwl_fw_dbg_conf
+ */
+struct iwl_fw_dbg_trigger_time_event {
+	struct {
+		__le32 id;
+		__le32 action_bitmap;
+		__le32 status_bitmap;
+	} __packed time_events[16];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration.
+ * @id: conf id
  * @usniffer: should the uSniffer image be used
  * @num_of_hcmds: how many HCMDs to send are present here
  * @hcmd: a variable length host command to be sent to apply the configuration.
  *	If there is more than one HCMD to send, they will appear one after the
  *	other and be sent in the order that they appear in.
- * This parses IWL_UCODE_TLV_FW_DBG_CONF
+ * This parses IWL_UCODE_TLV_FW_DBG_CONF. The user can add up-to
+ * %FW_DBG_CONF_MAX configuration per run.
  */
 struct iwl_fw_dbg_conf_tlv {
 	u8 id;
@@ -483,8 +673,6 @@
 	u8 reserved;
 	u8 num_of_hcmds;
 	struct iwl_fw_dbg_conf_hcmd hcmd;
-
-	/* struct iwl_fw_dbg_trigger sits after all variable length hcmds */
 } __packed;
 
 #endif  /* __iwl_fw_file_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index e6dc3b8..cf75baf 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -68,6 +68,7 @@
 #include <net/mac80211.h>
 
 #include "iwl-fw-file.h"
+#include "iwl-fw-error-dump.h"
 
 /**
  * enum iwl_ucode_type
@@ -152,9 +153,13 @@
  * @mvm_fw: indicates this is MVM firmware
  * @cipher_scheme: optional external cipher scheme.
  * @human_readable: human readable version
+ * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until
+ *	we get the ALIVE from the uCode
  * @dbg_dest_tlv: points to the destination TLV for debug
  * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
  * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries
+ * @dbg_trigger_tlv: array of pointers to triggers TLVs
+ * @dbg_trigger_tlv_len: lengths of the @dbg_trigger_tlv entries
  * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
  */
 struct iwl_fw {
@@ -181,10 +186,13 @@
 	struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
 	u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
 
-	struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
-	struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
-	size_t dbg_conf_tlv_len[FW_DBG_MAX];
+	u32 sdio_adma_addr;
 
+	struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+	struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
+	size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
+	struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
+	size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
 	u8 dbg_dest_reg_num;
 };
 
@@ -202,37 +210,6 @@
 	}
 }
 
-static inline const struct iwl_fw_dbg_trigger *
-iwl_fw_dbg_conf_get_trigger(const struct iwl_fw *fw, u8 id)
-{
-	const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
-	u8 *ptr;
-	int i;
-
-	if (!conf_tlv)
-		return NULL;
-
-	ptr = (void *)&conf_tlv->hcmd;
-	for (i = 0; i < conf_tlv->num_of_hcmds; i++) {
-		ptr += sizeof(conf_tlv->hcmd);
-		ptr += le16_to_cpu(conf_tlv->hcmd.len);
-	}
-
-	return (const struct iwl_fw_dbg_trigger *)ptr;
-}
-
-static inline bool
-iwl_fw_dbg_conf_enabled(const struct iwl_fw *fw, u8 id)
-{
-	const struct iwl_fw_dbg_trigger *trigger =
-		iwl_fw_dbg_conf_get_trigger(fw, id);
-
-	if (!trigger)
-		return false;
-
-	return trigger->enabled;
-}
-
 static inline bool
 iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id)
 {
@@ -244,4 +221,18 @@
 	return conf_tlv->usniffer;
 }
 
+#define iwl_fw_dbg_trigger_enabled(fw, id) ({			\
+	void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)];	\
+	unlikely(__dbg_trigger);				\
+})
+
+static inline struct iwl_fw_dbg_trigger_tlv*
+iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, u8 id)
+{
+	if (WARN_ON(id >= ARRAY_SIZE(fw->dbg_trigger_tlv)))
+		return NULL;
+
+	return fw->dbg_trigger_tlv[id];
+}
+
 #endif  /* __iwl_fw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c
index 17bd1b5..063a187 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.c
+++ b/drivers/net/wireless/iwlwifi/iwl-io.c
@@ -186,18 +186,17 @@
 
 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) ||
-	    (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP))
-		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);
+	if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+		iwl_write_prph(trans, DEVICE_SET_NMI_REG,
+			       DEVICE_SET_NMI_VAL_DRV);
+		iwl_write_prph(trans, DEVICE_SET_NMI_REG,
+			       DEVICE_SET_NMI_VAL_HW);
+	} else {
+		iwl_write_prph(trans, DEVICE_SET_NMI_8000_REG,
+			       DEVICE_SET_NMI_8000_VAL);
+		iwl_write_prph(trans, DEVICE_SET_NMI_REG,
+			       DEVICE_SET_NMI_VAL_DRV);
+	}
 }
 IWL_EXPORT_SYMBOL(iwl_force_nmi);
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h
index fe9213e..e639a97 100644
--- a/drivers/net/wireless/iwlwifi/iwl-modparams.h
+++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h
@@ -96,13 +96,14 @@
  *	use IWL_[DIS,EN]ABLE_HT_* constants
  * @amsdu_size_8K: enable 8K amsdu size, default = 0
  * @restart_fw: restart firmware, default = 1
- * @wd_disable: disable stuck queue check, default = 1
  * @bt_coex_active: enable bt coex, default = true
  * @led_mode: system default, default = 0
  * @power_save: enable power save, default = false
  * @power_level: power level, default = 1
  * @debug_level: levels are IWL_DL_*
  * @ant_coupling: antenna coupling in dB, default = 0
+ * @d0i3_disable: disable d0i3, default = 1,
+ * @lar_disable: disable LAR (regulatory), default = 0
  * @fw_monitor: allow to use firmware monitor
  */
 struct iwl_mod_params {
@@ -110,7 +111,6 @@
 	unsigned int disable_11n;
 	int amsdu_size_8K;
 	bool restart_fw;
-	int  wd_disable;
 	bool bt_coex_active;
 	int led_mode;
 	bool power_save;
@@ -121,6 +121,8 @@
 	int ant_coupling;
 	char *nvm_file;
 	bool uapsd_disable;
+	bool d0i3_disable;
+	bool lar_disable;
 	bool fw_monitor;
 };
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
index 06e02fc..83903a5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
@@ -99,12 +99,15 @@
 	/* NVM SW-Section offset (in words) definitions */
 	NVM_SW_SECTION_FAMILY_8000 = 0x1C0,
 	NVM_VERSION_FAMILY_8000 = 0,
-	RADIO_CFG_FAMILY_8000 = 2,
-	SKU_FAMILY_8000 = 4,
-	N_HW_ADDRS_FAMILY_8000 = 5,
+	RADIO_CFG_FAMILY_8000 = 0,
+	SKU_FAMILY_8000 = 2,
+	N_HW_ADDRS_FAMILY_8000 = 3,
 
 	/* NVM REGULATORY -Section offset (in words) definitions */
 	NVM_CHANNELS_FAMILY_8000 = 0,
+	NVM_LAR_OFFSET_FAMILY_8000_OLD = 0x4C7,
+	NVM_LAR_OFFSET_FAMILY_8000 = 0x507,
+	NVM_LAR_ENABLED_FAMILY_8000 = 0x7,
 
 	/* NVM calibration section offset (in words) definitions */
 	NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8,
@@ -146,7 +149,9 @@
 #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 LAST_5GHZ_HT			165
+#define LAST_5GHZ_HT_FAMILY_8000	181
+#define N_HW_ADDR_MASK			0xF
 
 /* rate data (static) */
 static struct ieee80211_rate iwl_cfg80211_rates[] = {
@@ -201,9 +206,57 @@
 #define CHECK_AND_PRINT_I(x)	\
 	((ch_flags & NVM_CHANNEL_##x) ? # x " " : "")
 
+static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz,
+				 u16 nvm_flags, const struct iwl_cfg *cfg)
+{
+	u32 flags = IEEE80211_CHAN_NO_HT40;
+	u32 last_5ghz_ht = LAST_5GHZ_HT;
+
+	if (cfg->device_family == IWL_DEVICE_FAMILY_8000)
+		last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000;
+
+	if (!is_5ghz && (nvm_flags & NVM_CHANNEL_40MHZ)) {
+		if (ch_num <= LAST_2GHZ_HT_PLUS)
+			flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+		if (ch_num >= FIRST_2GHZ_HT_MINUS)
+			flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+	} else if (ch_num <= last_5ghz_ht && (nvm_flags & NVM_CHANNEL_40MHZ)) {
+		if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
+			flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+		else
+			flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+	}
+	if (!(nvm_flags & NVM_CHANNEL_80MHZ))
+		flags |= IEEE80211_CHAN_NO_80MHZ;
+	if (!(nvm_flags & NVM_CHANNEL_160MHZ))
+		flags |= IEEE80211_CHAN_NO_160MHZ;
+
+	if (!(nvm_flags & NVM_CHANNEL_IBSS))
+		flags |= IEEE80211_CHAN_NO_IR;
+
+	if (!(nvm_flags & NVM_CHANNEL_ACTIVE))
+		flags |= IEEE80211_CHAN_NO_IR;
+
+	if (nvm_flags & NVM_CHANNEL_RADAR)
+		flags |= IEEE80211_CHAN_RADAR;
+
+	if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY)
+		flags |= IEEE80211_CHAN_INDOOR_ONLY;
+
+	/* Set the GO concurrent flag only in case that NO_IR is set.
+	 * Otherwise it is meaningless
+	 */
+	if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) &&
+	    (flags & IEEE80211_CHAN_NO_IR))
+		flags |= IEEE80211_CHAN_GO_CONCURRENT;
+
+	return flags;
+}
+
 static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
 				struct iwl_nvm_data *data,
-				const __le16 * const nvm_ch_flags)
+				const __le16 * const nvm_ch_flags,
+				bool lar_supported)
 {
 	int ch_idx;
 	int n_channels = 0;
@@ -228,9 +281,14 @@
 
 		if (ch_idx >= num_2ghz_channels &&
 		    !data->sku_cap_band_52GHz_enable)
-			ch_flags &= ~NVM_CHANNEL_VALID;
+			continue;
 
-		if (!(ch_flags & NVM_CHANNEL_VALID)) {
+		if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) {
+			/*
+			 * Channels might become valid later if lar is
+			 * supported, hence we still want to add them to
+			 * the list of supported channels to cfg80211.
+			 */
 			IWL_DEBUG_EEPROM(dev,
 					 "Ch. %d Flags %x [%sGHz] - No traffic\n",
 					 nvm_chan[ch_idx],
@@ -250,45 +308,6 @@
 			ieee80211_channel_to_frequency(
 				channel->hw_value, channel->band);
 
-		/* TODO: Need to be dependent to the NVM */
-		channel->flags = IEEE80211_CHAN_NO_HT40;
-		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;
-			if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS)
-				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)
-				channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
-			else
-				channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
-		}
-		if (!(ch_flags & NVM_CHANNEL_80MHZ))
-			channel->flags |= IEEE80211_CHAN_NO_80MHZ;
-		if (!(ch_flags & NVM_CHANNEL_160MHZ))
-			channel->flags |= IEEE80211_CHAN_NO_160MHZ;
-
-		if (!(ch_flags & NVM_CHANNEL_IBSS))
-			channel->flags |= IEEE80211_CHAN_NO_IR;
-
-		if (!(ch_flags & NVM_CHANNEL_ACTIVE))
-			channel->flags |= IEEE80211_CHAN_NO_IR;
-
-		if (ch_flags & NVM_CHANNEL_RADAR)
-			channel->flags |= IEEE80211_CHAN_RADAR;
-
-		if (ch_flags & NVM_CHANNEL_INDOOR_ONLY)
-			channel->flags |= IEEE80211_CHAN_INDOOR_ONLY;
-
-		/* Set the GO concurrent flag only in case that NO_IR is set.
-		 * Otherwise it is meaningless
-		 */
-		if ((ch_flags & NVM_CHANNEL_GO_CONCURRENT) &&
-		    (channel->flags & IEEE80211_CHAN_NO_IR))
-			channel->flags |= IEEE80211_CHAN_GO_CONCURRENT;
-
 		/* Initialize regulatory-based run-time data */
 
 		/*
@@ -297,6 +316,15 @@
 		 */
 		channel->max_power = IWL_DEFAULT_MAX_TX_POWER;
 		is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
+
+		/* don't put limitations in case we're using LAR */
+		if (!lar_supported)
+			channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx],
+							       ch_idx, is_5ghz,
+							       ch_flags, cfg);
+		else
+			channel->flags = 0;
+
 		IWL_DEBUG_EEPROM(dev,
 				 "Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
 				 channel->hw_value,
@@ -370,8 +398,8 @@
 
 static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
 			    struct iwl_nvm_data *data,
-			    const __le16 *ch_section, bool enable_vht,
-			    u8 tx_chains, u8 rx_chains)
+			    const __le16 *ch_section,
+			    u8 tx_chains, u8 rx_chains, bool lar_supported)
 {
 	int n_channels;
 	int n_used = 0;
@@ -380,11 +408,12 @@
 	if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
 		n_channels = iwl_init_channel_map(
 				dev, cfg, data,
-				&ch_section[NVM_CHANNELS]);
+				&ch_section[NVM_CHANNELS], lar_supported);
 	else
 		n_channels = iwl_init_channel_map(
 				dev, cfg, data,
-				&ch_section[NVM_CHANNELS_FAMILY_8000]);
+				&ch_section[NVM_CHANNELS_FAMILY_8000],
+				lar_supported);
 
 	sband = &data->bands[IEEE80211_BAND_2GHZ];
 	sband->band = IEEE80211_BAND_2GHZ;
@@ -403,7 +432,7 @@
 					  IEEE80211_BAND_5GHZ);
 	iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
 			     tx_chains, rx_chains);
-	if (enable_vht)
+	if (data->sku_cap_11ac_enable)
 		iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap,
 				      tx_chains, rx_chains);
 
@@ -412,17 +441,16 @@
 			    n_used, n_channels);
 }
 
-static int iwl_get_sku(const struct iwl_cfg *cfg,
-		       const __le16 *nvm_sw)
+static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
+		       const __le16 *phy_sku)
 {
 	if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
 		return le16_to_cpup(nvm_sw + SKU);
-	else
-		return le32_to_cpup((__le32 *)(nvm_sw + SKU_FAMILY_8000));
+
+	return le32_to_cpup((__le32 *)(phy_sku + SKU_FAMILY_8000));
 }
 
-static int iwl_get_nvm_version(const struct iwl_cfg *cfg,
-			       const __le16 *nvm_sw)
+static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
 {
 	if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
 		return le16_to_cpup(nvm_sw + NVM_VERSION);
@@ -431,24 +459,26 @@
 					       NVM_VERSION_FAMILY_8000));
 }
 
-static int iwl_get_radio_cfg(const struct iwl_cfg *cfg,
-			     const __le16 *nvm_sw)
+static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
+			     const __le16 *phy_sku)
 {
 	if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
 		return le16_to_cpup(nvm_sw + RADIO_CFG);
-	else
-		return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000));
+
+	return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000));
+
 }
 
-#define N_HW_ADDRS_MASK_FAMILY_8000	0xF
-static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg,
-			      const __le16 *nvm_sw)
+static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
 {
+	int n_hw_addr;
+
 	if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
 		return le16_to_cpup(nvm_sw + N_HW_ADDRS);
-	else
-		return le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000))
-		       & N_HW_ADDRS_MASK_FAMILY_8000;
+
+	n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000));
+
+	return n_hw_addr & N_HW_ADDR_MASK;
 }
 
 static void iwl_set_radio_cfg(const struct iwl_cfg *cfg,
@@ -468,6 +498,8 @@
 	data->radio_cfg_step = NVM_RF_CFG_STEP_MSK_FAMILY_8000(radio_cfg);
 	data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK_FAMILY_8000(radio_cfg);
 	data->radio_cfg_pnum = NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(radio_cfg);
+	data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(radio_cfg);
+	data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(radio_cfg);
 }
 
 static void iwl_set_hw_address(const struct iwl_cfg *cfg,
@@ -489,7 +521,8 @@
 					   const struct iwl_cfg *cfg,
 					   struct iwl_nvm_data *data,
 					   const __le16 *mac_override,
-					   const __le16 *nvm_hw)
+					   const __le16 *nvm_hw,
+					   u32 mac_addr0, u32 mac_addr1)
 {
 	const u8 *hw_addr;
 
@@ -513,48 +546,17 @@
 	}
 
 	if (nvm_hw) {
-		/* read the MAC address from OTP */
-		if (!dev_is_pci(dev) || (data->nvm_version < 0xE08)) {
-			/* read the mac address from the WFPM location */
-			hw_addr = (const u8 *)(nvm_hw +
-					       HW_ADDR0_WFPM_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];
+		/* read the MAC address from HW resisters */
+		hw_addr = (const u8 *)&mac_addr0;
+		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_WFPM_FAMILY_8000);
-			data->hw_addr[4] = hw_addr[1];
-			data->hw_addr[5] = hw_addr[0];
-		} else if ((data->nvm_version >= 0xE08) &&
-			   (data->nvm_version < 0xE0B)) {
-			/* read "reverse order"  from the PCIe location */
-			hw_addr = (const u8 *)(nvm_hw +
-					       HW_ADDR0_PCIE_FAMILY_8000);
-			data->hw_addr[5] = hw_addr[2];
-			data->hw_addr[4] = hw_addr[1];
-			data->hw_addr[3] = hw_addr[0];
+		hw_addr = (const u8 *)&mac_addr1;
+		data->hw_addr[4] = hw_addr[1];
+		data->hw_addr[5] = hw_addr[0];
 
-			hw_addr = (const u8 *)(nvm_hw +
-					       HW_ADDR1_PCIE_FAMILY_8000);
-			data->hw_addr[2] = hw_addr[3];
-			data->hw_addr[1] = hw_addr[2];
-			data->hw_addr[0] = hw_addr[1];
-		} else {
-			/* read from the PCIe location */
-			hw_addr = (const u8 *)(nvm_hw +
-					       HW_ADDR0_PCIE_FAMILY_8000);
-			data->hw_addr[5] = hw_addr[0];
-			data->hw_addr[4] = hw_addr[1];
-			data->hw_addr[3] = hw_addr[2];
-
-			hw_addr = (const u8 *)(nvm_hw +
-					       HW_ADDR1_PCIE_FAMILY_8000);
-			data->hw_addr[2] = hw_addr[1];
-			data->hw_addr[1] = hw_addr[2];
-			data->hw_addr[0] = hw_addr[3];
-		}
 		if (!is_valid_ether_addr(data->hw_addr))
 			IWL_ERR_DEV(dev,
 				    "mac address from hw section is not valid\n");
@@ -569,11 +571,14 @@
 iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
 		   const __le16 *nvm_hw, const __le16 *nvm_sw,
 		   const __le16 *nvm_calib, const __le16 *regulatory,
-		   const __le16 *mac_override, u8 tx_chains, u8 rx_chains)
+		   const __le16 *mac_override, const __le16 *phy_sku,
+		   u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
+		   u32 mac_addr0, u32 mac_addr1)
 {
 	struct iwl_nvm_data *data;
 	u32 sku;
 	u32 radio_cfg;
+	u16 lar_config;
 
 	if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
 		data = kzalloc(sizeof(*data) +
@@ -590,16 +595,21 @@
 
 	data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw);
 
-	radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw);
+	radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw, phy_sku);
 	iwl_set_radio_cfg(cfg, data, radio_cfg);
+	if (data->valid_tx_ant)
+		tx_chains &= data->valid_tx_ant;
+	if (data->valid_rx_ant)
+		rx_chains &= data->valid_rx_ant;
 
-	sku = iwl_get_sku(cfg, nvm_sw);
+	sku = iwl_get_sku(cfg, nvm_sw, phy_sku);
 	data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
 	data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
 	data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE;
-	data->sku_cap_11ac_enable = sku & NVM_SKU_CAP_11AC_ENABLE;
 	if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
 		data->sku_cap_11n_enable = false;
+	data->sku_cap_11ac_enable = data->sku_cap_11n_enable &&
+				    (sku & NVM_SKU_CAP_11AC_ENABLE);
 
 	data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw);
 
@@ -620,16 +630,23 @@
 		iwl_set_hw_address(cfg, data, nvm_hw);
 
 		iwl_init_sbands(dev, cfg, data, nvm_sw,
-				sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
-				rx_chains);
+				tx_chains, rx_chains, lar_fw_supported);
 	} else {
+		u16 lar_offset = data->nvm_version < 0xE39 ?
+				 NVM_LAR_OFFSET_FAMILY_8000_OLD :
+				 NVM_LAR_OFFSET_FAMILY_8000;
+
+		lar_config = le16_to_cpup(regulatory + lar_offset);
+		data->lar_enabled = !!(lar_config &
+				       NVM_LAR_ENABLED_FAMILY_8000);
+
 		/* MAC address in family 8000 */
 		iwl_set_hw_address_family_8000(dev, cfg, data, mac_override,
-					       nvm_hw);
+					       nvm_hw, mac_addr0, mac_addr1);
 
 		iwl_init_sbands(dev, cfg, data, regulatory,
-				sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
-				rx_chains);
+				tx_chains, rx_chains,
+				lar_fw_supported && data->lar_enabled);
 	}
 
 	data->calib_version = 255;
@@ -637,3 +654,164 @@
 	return data;
 }
 IWL_EXPORT_SYMBOL(iwl_parse_nvm_data);
+
+static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan,
+				       int ch_idx, u16 nvm_flags,
+				       const struct iwl_cfg *cfg)
+{
+	u32 flags = NL80211_RRF_NO_HT40;
+	u32 last_5ghz_ht = LAST_5GHZ_HT;
+
+	if (cfg->device_family == IWL_DEVICE_FAMILY_8000)
+		last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000;
+
+	if (ch_idx < NUM_2GHZ_CHANNELS &&
+	    (nvm_flags & NVM_CHANNEL_40MHZ)) {
+		if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS)
+			flags &= ~NL80211_RRF_NO_HT40PLUS;
+		if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS)
+			flags &= ~NL80211_RRF_NO_HT40MINUS;
+	} else if (nvm_chan[ch_idx] <= last_5ghz_ht &&
+		   (nvm_flags & NVM_CHANNEL_40MHZ)) {
+		if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
+			flags &= ~NL80211_RRF_NO_HT40PLUS;
+		else
+			flags &= ~NL80211_RRF_NO_HT40MINUS;
+	}
+
+	if (!(nvm_flags & NVM_CHANNEL_80MHZ))
+		flags |= NL80211_RRF_NO_80MHZ;
+	if (!(nvm_flags & NVM_CHANNEL_160MHZ))
+		flags |= NL80211_RRF_NO_160MHZ;
+
+	if (!(nvm_flags & NVM_CHANNEL_ACTIVE))
+		flags |= NL80211_RRF_NO_IR;
+
+	if (nvm_flags & NVM_CHANNEL_RADAR)
+		flags |= NL80211_RRF_DFS;
+
+	if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY)
+		flags |= NL80211_RRF_NO_OUTDOOR;
+
+	/* Set the GO concurrent flag only in case that NO_IR is set.
+	 * Otherwise it is meaningless
+	 */
+	if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) &&
+	    (flags & NL80211_RRF_NO_IR))
+		flags |= NL80211_RRF_GO_CONCURRENT;
+
+	return flags;
+}
+
+struct ieee80211_regdomain *
+iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
+		       int num_of_ch, __le32 *channels, u16 fw_mcc)
+{
+	int ch_idx;
+	u16 ch_flags, prev_ch_flags = 0;
+	const u8 *nvm_chan = cfg->device_family == IWL_DEVICE_FAMILY_8000 ?
+			     iwl_nvm_channels_family_8000 : iwl_nvm_channels;
+	struct ieee80211_regdomain *regd;
+	int size_of_regd;
+	struct ieee80211_reg_rule *rule;
+	enum ieee80211_band band;
+	int center_freq, prev_center_freq = 0;
+	int valid_rules = 0;
+	bool new_rule;
+	int max_num_ch = cfg->device_family == IWL_DEVICE_FAMILY_8000 ?
+			 IWL_NUM_CHANNELS_FAMILY_8000 : IWL_NUM_CHANNELS;
+
+	if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES))
+		return ERR_PTR(-EINVAL);
+
+	if (WARN_ON(num_of_ch > max_num_ch))
+		num_of_ch = max_num_ch;
+
+	IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n",
+		      num_of_ch);
+
+	/* build a regdomain rule for every valid channel */
+	size_of_regd =
+		sizeof(struct ieee80211_regdomain) +
+		num_of_ch * sizeof(struct ieee80211_reg_rule);
+
+	regd = kzalloc(size_of_regd, GFP_KERNEL);
+	if (!regd)
+		return ERR_PTR(-ENOMEM);
+
+	for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
+		ch_flags = (u16)__le32_to_cpup(channels + ch_idx);
+		band = (ch_idx < NUM_2GHZ_CHANNELS) ?
+		       IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+		center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx],
+							     band);
+		new_rule = false;
+
+		if (!(ch_flags & NVM_CHANNEL_VALID)) {
+			IWL_DEBUG_DEV(dev, IWL_DL_LAR,
+				      "Ch. %d Flags %x [%sGHz] - No traffic\n",
+				      nvm_chan[ch_idx],
+				      ch_flags,
+				      (ch_idx >= NUM_2GHZ_CHANNELS) ?
+				      "5.2" : "2.4");
+			continue;
+		}
+
+		/* we can't continue the same rule */
+		if (ch_idx == 0 || prev_ch_flags != ch_flags ||
+		    center_freq - prev_center_freq > 20) {
+			valid_rules++;
+			new_rule = true;
+		}
+
+		rule = &regd->reg_rules[valid_rules - 1];
+
+		if (new_rule)
+			rule->freq_range.start_freq_khz =
+						MHZ_TO_KHZ(center_freq - 10);
+
+		rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10);
+
+		/* this doesn't matter - not used by FW */
+		rule->power_rule.max_antenna_gain = DBI_TO_MBI(6);
+		rule->power_rule.max_eirp =
+			DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER);
+
+		rule->flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx,
+							  ch_flags, cfg);
+
+		/* rely on auto-calculation to merge BW of contiguous chans */
+		rule->flags |= NL80211_RRF_AUTO_BW;
+		rule->freq_range.max_bandwidth_khz = 0;
+
+		prev_ch_flags = ch_flags;
+		prev_center_freq = center_freq;
+
+		IWL_DEBUG_DEV(dev, IWL_DL_LAR,
+			      "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s(0x%02x): Ad-Hoc %ssupported\n",
+			      center_freq,
+			      band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4",
+			      CHECK_AND_PRINT_I(VALID),
+			      CHECK_AND_PRINT_I(ACTIVE),
+			      CHECK_AND_PRINT_I(RADAR),
+			      CHECK_AND_PRINT_I(WIDE),
+			      CHECK_AND_PRINT_I(40MHZ),
+			      CHECK_AND_PRINT_I(80MHZ),
+			      CHECK_AND_PRINT_I(160MHZ),
+			      CHECK_AND_PRINT_I(INDOOR_ONLY),
+			      CHECK_AND_PRINT_I(GO_CONCURRENT),
+			      ch_flags,
+			      ((ch_flags & NVM_CHANNEL_ACTIVE) &&
+			       !(ch_flags & NVM_CHANNEL_RADAR))
+					 ? "" : "not ");
+	}
+
+	regd->n_reg_rules = valid_rules;
+
+	/* set alpha2 from FW. */
+	regd->alpha2[0] = fw_mcc >> 8;
+	regd->alpha2[1] = fw_mcc & 0xff;
+
+	return regd;
+}
+IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info);
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
index c9c45a3..822ba52 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
@@ -62,6 +62,7 @@
 #ifndef __iwl_nvm_parse_h__
 #define __iwl_nvm_parse_h__
 
+#include <net/cfg80211.h>
 #include "iwl-eeprom-parse.h"
 
 /**
@@ -76,6 +77,21 @@
 iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
 		   const __le16 *nvm_hw, const __le16 *nvm_sw,
 		   const __le16 *nvm_calib, const __le16 *regulatory,
-		   const __le16 *mac_override, u8 tx_chains, u8 rx_chains);
+		   const __le16 *mac_override, const __le16 *phy_sku,
+		   u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
+		   u32 mac_addr0, u32 mac_addr1);
+
+/**
+ * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW
+ *
+ * This function parses the regulatory channel data received as a
+ * MCC_UPDATE_CMD command. It returns a newly allocation regulatory domain,
+ * to be fed into the regulatory core. An ERR_PTR is returned on error.
+ * If not given to the regulatory core, the user is responsible for freeing
+ * the regdomain returned here with kfree.
+ */
+struct ieee80211_regdomain *
+iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
+		       int num_of_ch, __le32 *channels, u16 fw_mcc);
 
 #endif /* __iwl_nvm_parse_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
index 17de6d4..ce1cdd7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h
+++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
@@ -94,7 +94,7 @@
  * The operational mode has a very simple life cycle.
  *
  *	1) The driver layer (iwl-drv.c) chooses the op_mode based on the
- *	   capabilities advertized by the fw file (in TLV format).
+ *	   capabilities advertised by the fw file (in TLV format).
  *	2) The driver layer starts the op_mode (ops->start)
  *	3) The op_mode registers mac80211
  *	4) The op_mode is governed by mac80211
@@ -116,7 +116,7 @@
  *	May sleep
  * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the
  *	HCMD this Rx responds to. Can't sleep.
- * @napi_add: NAPI initialisation. The transport is fully responsible for NAPI,
+ * @napi_add: NAPI initialization. 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.
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
index d4fb5ca..a105455 100644
--- a/drivers/net/wireless/iwlwifi/iwl-phy-db.c
+++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
@@ -72,7 +72,7 @@
 #include "iwl-trans.h"
 
 #define CHANNEL_NUM_SIZE	4	/* num of channels in calib_ch size */
-#define IWL_NUM_PAPD_CH_GROUPS	7
+#define IWL_NUM_PAPD_CH_GROUPS	9
 #define IWL_NUM_TXP_CH_GROUPS	9
 
 struct iwl_phy_db_entry {
@@ -125,7 +125,7 @@
 } __packed;
 
 /*
- * phy db - Receieve phy db chunk after calibrations
+ * phy db - Receive phy db chunk after calibrations
  */
 struct iwl_calib_res_notif_phy_db {
 	__le16 type;
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index 2df51ea..88a57e6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -99,6 +99,7 @@
 
 #define APMG_PCIDEV_STT_VAL_PERSIST_DIS	(0x00000200)
 #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS	(0x00000800)
+#define APMG_PCIDEV_STT_VAL_WAKE_ME	(0x00004000)
 
 #define APMG_RTC_INT_STT_RFKILL		(0x10000000)
 
@@ -107,9 +108,10 @@
 
 /* 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
+#define DEVICE_SET_NMI_VAL_HW BIT(0)
+#define DEVICE_SET_NMI_VAL_DRV BIT(7)
+#define DEVICE_SET_NMI_8000_REG 0x00a01c24
+#define DEVICE_SET_NMI_8000_VAL 0x1000000
 
 /* Shared registers (0x0..0x3ff, via target indirect or periphery */
 #define SHR_BASE	0x00a10000
@@ -250,6 +252,7 @@
 #define SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK		(0x0000007F)
 #define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS	(16)
 #define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK	(0x007F0000)
+#define SCD_GP_CTRL_ENABLE_31_QUEUES		BIT(0)
 
 /* Context Data */
 #define SCD_CONTEXT_MEM_LOWER_BOUND	(SCD_MEM_LOWER_BOUND + 0x600)
@@ -283,57 +286,15 @@
 #define SCD_CHAINEXT_EN		(SCD_BASE + 0x244)
 #define SCD_AGGR_SEL		(SCD_BASE + 0x248)
 #define SCD_INTERRUPT_MASK	(SCD_BASE + 0x108)
+#define SCD_GP_CTRL		(SCD_BASE + 0x1a8)
 #define SCD_EN_CTRL		(SCD_BASE + 0x254)
 
-static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl)
-{
-	if (chnl < 20)
-		return SCD_BASE + 0x18 + chnl * 4;
-	WARN_ON_ONCE(chnl >= 32);
-	return SCD_BASE + 0x284 + (chnl - 20) * 4;
-}
-
-static inline unsigned int SCD_QUEUE_RDPTR(unsigned int chnl)
-{
-	if (chnl < 20)
-		return SCD_BASE + 0x68 + chnl * 4;
-	WARN_ON_ONCE(chnl >= 32);
-	return SCD_BASE + 0x2B4 + (chnl - 20) * 4;
-}
-
-static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl)
-{
-	if (chnl < 20)
-		return SCD_BASE + 0x10c + chnl * 4;
-	WARN_ON_ONCE(chnl >= 32);
-	return SCD_BASE + 0x384 + (chnl - 20) * 4;
-}
-
 /*********************** END TX SCHEDULER *************************************/
 
 /* Oscillator clock */
 #define OSC_CLK				(0xa04068)
 #define OSC_CLK_FORCE_CONTROL		(0x8)
 
-/* SECURE boot registers */
-#define LMPM_SECURE_BOOT_CONFIG_ADDR	(0x100)
-enum secure_boot_config_reg {
-	LMPM_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP	= 0x00000001,
-	LMPM_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ	= 0x00000002,
-};
-
-#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0	(0xA01E30)
-#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR	(0x1E30)
-#define LMPM_SECURE_BOOT_CPU2_STATUS_ADDR	(0x1E34)
-enum secure_boot_status_reg {
-	LMPM_SECURE_BOOT_CPU_STATUS_VERF_STATUS		= 0x00000001,
-	LMPM_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED	= 0x00000002,
-	LMPM_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS	= 0x00000004,
-	LMPM_SECURE_BOOT_CPU_STATUS_VERF_FAIL		= 0x00000008,
-	LMPM_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL	= 0x00000010,
-	LMPM_SECURE_BOOT_STATUS_SUCCESS			= 0x00000003,
-};
-
 #define FH_UCODE_LOAD_STATUS		(0x1AF0)
 #define CSR_UCODE_LOAD_STATUS_ADDR	(0x1E70)
 enum secure_load_status_reg {
@@ -354,22 +315,68 @@
 #define LMPM_SECURE_CPU1_HDR_MEM_SPACE		(0x420000)
 #define LMPM_SECURE_CPU2_HDR_MEM_SPACE		(0x420400)
 
-#define LMPM_SECURE_TIME_OUT	(100) /* 10 micro */
-
 /* Rx FIFO */
 #define RXF_SIZE_ADDR			(0xa00c88)
+#define RXF_RD_D_SPACE			(0xa00c40)
+#define RXF_RD_WR_PTR			(0xa00c50)
+#define RXF_RD_RD_PTR			(0xa00c54)
+#define RXF_RD_FENCE_PTR		(0xa00c4c)
+#define RXF_SET_FENCE_MODE		(0xa00c14)
+#define RXF_LD_WR2FENCE		(0xa00c1c)
+#define RXF_FIFO_RD_FENCE_INC		(0xa00c68)
 #define RXF_SIZE_BYTE_CND_POS		(7)
 #define RXF_SIZE_BYTE_CNT_MSK		(0x3ff << RXF_SIZE_BYTE_CND_POS)
+#define RXF_DIFF_FROM_PREV		(0x200)
 
 #define RXF_LD_FENCE_OFFSET_ADDR	(0xa00c10)
 #define RXF_FIFO_RD_FENCE_ADDR		(0xa00c0c)
 
+/* Tx FIFO */
+#define TXF_FIFO_ITEM_CNT		(0xa00438)
+#define TXF_WR_PTR			(0xa00414)
+#define TXF_RD_PTR			(0xa00410)
+#define TXF_FENCE_PTR			(0xa00418)
+#define TXF_LOCK_FENCE			(0xa00424)
+#define TXF_LARC_NUM			(0xa0043c)
+#define TXF_READ_MODIFY_DATA		(0xa00448)
+#define TXF_READ_MODIFY_ADDR		(0xa0044c)
+
 /* FW monitor */
+#define MON_BUFF_SAMPLE_CTL		(0xa03c00)
 #define MON_BUFF_BASE_ADDR		(0xa03c3c)
 #define MON_BUFF_END_ADDR		(0xa03c40)
 #define MON_BUFF_WRPTR			(0xa03c44)
 #define MON_BUFF_CYCLE_CNT		(0xa03c48)
 
+#define DBGC_IN_SAMPLE			(0xa03c00)
+
+/* enable the ID buf for read */
+#define WFPM_PS_CTL_CLR			0xA0300C
+#define WFMP_MAC_ADDR_0			0xA03080
+#define WFMP_MAC_ADDR_1			0xA03084
+#define LMPM_PMG_EN			0xA01CEC
+#define RADIO_REG_SYS_MANUAL_DFT_0	0xAD4078
+#define RFIC_REG_RD			0xAD0470
+#define WFPM_CTRL_REG			0xA03030
+enum {
+	ENABLE_WFPM = BIT(31),
+	WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK	= 0x80000000,
+};
+
+#define AUX_MISC_REG			0xA200B0
+enum {
+	HW_STEP_LOCATION_BITS = 24,
+};
+
+#define AUX_MISC_MASTER1_EN		0xA20818
+enum aux_misc_master1_en {
+	AUX_MISC_MASTER1_EN_SBE_MSK	= 0x1,
+};
+
+#define AUX_MISC_MASTER1_SMPHR_STATUS	0xA20800
+#define RSA_ENABLE			0xA24B08
+#define PREG_AUX_BUS_WPROT_0		0xA04CC0
+
 /* FW chicken bits */
 #define LMPM_CHICK			0xA01FF8
 enum {
diff --git a/drivers/net/wireless/iwlwifi/iwl-scd.h b/drivers/net/wireless/iwlwifi/iwl-scd.h
index 6c622b2..f2353eb 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scd.h
+++ b/drivers/net/wireless/iwlwifi/iwl-scd.h
@@ -69,14 +69,6 @@
 #include "iwl-prph.h"
 
 
-static inline void iwl_scd_txq_set_inactive(struct iwl_trans *trans,
-					    u16 txq_id)
-{
-	iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id),
-		       (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
-		       (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
-}
-
 static inline void iwl_scd_txq_set_chain(struct iwl_trans *trans,
 					 u16 txq_id)
 {
@@ -115,4 +107,37 @@
 {
 	iwl_write_prph(trans, SCD_EN_CTRL, value);
 }
+
+static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl)
+{
+	if (chnl < 20)
+		return SCD_BASE + 0x18 + chnl * 4;
+	WARN_ON_ONCE(chnl >= 32);
+	return SCD_BASE + 0x284 + (chnl - 20) * 4;
+}
+
+static inline unsigned int SCD_QUEUE_RDPTR(unsigned int chnl)
+{
+	if (chnl < 20)
+		return SCD_BASE + 0x68 + chnl * 4;
+	WARN_ON_ONCE(chnl >= 32);
+	return SCD_BASE + 0x2B4 + chnl * 4;
+}
+
+static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl)
+{
+	if (chnl < 20)
+		return SCD_BASE + 0x10c + chnl * 4;
+	WARN_ON_ONCE(chnl >= 32);
+	return SCD_BASE + 0x334 + chnl * 4;
+}
+
+static inline void iwl_scd_txq_set_inactive(struct iwl_trans *trans,
+					    u16 txq_id)
+{
+	iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id),
+		       (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
+		       (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
+}
+
 #endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 028408a..6dfed12 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -77,10 +77,10 @@
 /**
  * DOC: Transport layer - what is it ?
  *
- * The tranport layer is the layer that deals with the HW directly. It provides
+ * The transport layer is the layer that deals with the HW directly. It provides
  * an abstraction of the underlying HW to the upper layer. The transport layer
  * doesn't provide any policy, algorithm or anything of this kind, but only
- * mechanisms to make the HW do something.It is not completely stateless but
+ * mechanisms to make the HW do something. It is not completely stateless but
  * close to it.
  * We will have an implementation for each different supported bus.
  */
@@ -111,10 +111,10 @@
 /**
  * DOC: Host command section
  *
- * A host command is a commaned issued by the upper layer to the fw. There are
+ * A host command is a command issued by the upper layer to the fw. There are
  * several versions of fw that have several APIs. The transport layer is
  * completely agnostic to these differences.
- * The transport does provide helper functionnality (i.e. SYNC / ASYNC mode),
+ * The transport does provide helper functionality (i.e. SYNC / ASYNC mode),
  */
 #define SEQ_TO_QUEUE(s)	(((s) >> 8) & 0x1f)
 #define QUEUE_TO_SEQ(q)	(((q) & 0x1f) << 8)
@@ -195,7 +195,7 @@
  * @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
+ *	command queue, but after other high priority commands. Valid only
  *	with CMD_ASYNC.
  * @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle.
  * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle.
@@ -368,6 +368,7 @@
  * @cmd_queue: the index of the command queue.
  *	Must be set before start_fw.
  * @cmd_fifo: the fifo for host commands
+ * @cmd_q_wdg_timeout: the timeout of the watchdog timer for the command queue.
  * @no_reclaim_cmds: Some devices erroneously don't set the
  *	SEQ_RX_FRAME bit on some notifications, this is the
  *	list of such notifications to filter. Max length is
@@ -378,24 +379,26 @@
  * @bc_table_dword: set to true if the BC table expects the byte count to be
  *	in DWORD (as opposed to bytes)
  * @scd_set_active: should the transport configure the SCD for HCMD queue
- * @queue_watchdog_timeout: time (in ms) after which queues
- *	are considered stuck and will trigger device restart
  * @command_names: array of command names, must be 256 entries
  *	(one for each command); for debugging only
+ * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until
+ *	we get the ALIVE from the uCode
  */
 struct iwl_trans_config {
 	struct iwl_op_mode *op_mode;
 
 	u8 cmd_queue;
 	u8 cmd_fifo;
+	unsigned int cmd_q_wdg_timeout;
 	const u8 *no_reclaim_cmds;
 	unsigned int n_no_reclaim_cmds;
 
 	bool rx_buf_size_8k;
 	bool bc_table_dword;
 	bool scd_set_active;
-	unsigned int queue_watchdog_timeout;
 	const char *const *command_names;
+
+	u32 sdio_adma_addr;
 };
 
 struct iwl_trans_dump_data {
@@ -455,6 +458,8 @@
  * @txq_disable: de-configure a Tx queue to send AMPDUs
  *	Must be atomic
  * @wait_tx_queue_empty: wait until tx queues are empty. May sleep.
+ * @freeze_txq_timer: prevents the timer of the queue from firing until the
+ *	queue is set to awake. Must be atomic.
  * @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
@@ -507,12 +512,15 @@
 			struct sk_buff_head *skbs);
 
 	void (*txq_enable)(struct iwl_trans *trans, int queue, u16 ssn,
-			   const struct iwl_trans_txq_scd_cfg *cfg);
+			   const struct iwl_trans_txq_scd_cfg *cfg,
+			   unsigned int queue_wdg_timeout);
 	void (*txq_disable)(struct iwl_trans *trans, int queue,
 			    bool configure_scd);
 
 	int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir);
 	int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm);
+	void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs,
+				 bool freeze);
 
 	void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val);
 	void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val);
@@ -552,6 +560,21 @@
 };
 
 /**
+ * enum iwl_d0i3_mode - d0i3 mode
+ *
+ * @IWL_D0I3_MODE_OFF - d0i3 is disabled
+ * @IWL_D0I3_MODE_ON_IDLE - enter d0i3 when device is idle
+ *	(e.g. no active references)
+ * @IWL_D0I3_MODE_ON_SUSPEND - enter d0i3 only on suspend
+ *	(in case of 'any' trigger)
+ */
+enum iwl_d0i3_mode {
+	IWL_D0I3_MODE_OFF = 0,
+	IWL_D0I3_MODE_ON_IDLE,
+	IWL_D0I3_MODE_ON_SUSPEND,
+};
+
+/**
  * struct iwl_trans - transport common data
  *
  * @ops - pointer to iwl_trans_ops
@@ -559,7 +582,7 @@
  * @cfg - pointer to the configuration
  * @status: a bit-mask of transport status flags
  * @dev - pointer to struct device * that represents the device
- * @hw_id: a u32 with the ID of the device / subdevice.
+ * @hw_id: a u32 with the ID of the device / sub-device.
  *	Set during transport allocation.
  * @hw_id_str: a string with info about HW ID. Set during transport allocation.
  * @pm_support: set to true in start_hw if link pm is supported
@@ -576,6 +599,7 @@
  * @dflt_pwr_limit: default power limit fetched from the platform (ACPI)
  * @dbg_dest_tlv: points to the destination TLV for debug
  * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
+ * @dbg_trigger_tlv: array of pointers to triggers TLVs for debug
  * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
  */
 struct iwl_trans {
@@ -609,9 +633,12 @@
 	u64 dflt_pwr_limit;
 
 	const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
-	const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
+	const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
+	struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv;
 	u8 dbg_dest_reg_num;
 
+	enum iwl_d0i3_mode d0i3_mode;
+
 	/* pointer to trans specific struct */
 	/*Ensure that this pointer will always be aligned to sizeof pointer */
 	char trans_specific[0] __aligned(sizeof(void *));
@@ -808,19 +835,21 @@
 
 static inline void
 iwl_trans_txq_enable_cfg(struct iwl_trans *trans, int queue, u16 ssn,
-			 const struct iwl_trans_txq_scd_cfg *cfg)
+			 const struct iwl_trans_txq_scd_cfg *cfg,
+			 unsigned int queue_wdg_timeout)
 {
 	might_sleep();
 
 	if (unlikely((trans->state != IWL_TRANS_FW_ALIVE)))
 		IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
 
-	trans->ops->txq_enable(trans, queue, ssn, cfg);
+	trans->ops->txq_enable(trans, queue, ssn, cfg, queue_wdg_timeout);
 }
 
 static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue,
 					int fifo, int sta_id, int tid,
-					int frame_limit, u16 ssn)
+					int frame_limit, u16 ssn,
+					unsigned int queue_wdg_timeout)
 {
 	struct iwl_trans_txq_scd_cfg cfg = {
 		.fifo = fifo,
@@ -830,11 +859,12 @@
 		.aggregate = sta_id >= 0,
 	};
 
-	iwl_trans_txq_enable_cfg(trans, queue, ssn, &cfg);
+	iwl_trans_txq_enable_cfg(trans, queue, ssn, &cfg, queue_wdg_timeout);
 }
 
-static inline void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue,
-					   int fifo)
+static inline
+void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, int fifo,
+			     unsigned int queue_wdg_timeout)
 {
 	struct iwl_trans_txq_scd_cfg cfg = {
 		.fifo = fifo,
@@ -844,16 +874,27 @@
 		.aggregate = false,
 	};
 
-	iwl_trans_txq_enable_cfg(trans, queue, 0, &cfg);
+	iwl_trans_txq_enable_cfg(trans, queue, 0, &cfg, queue_wdg_timeout);
 }
 
-static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans,
-						u32 txq_bm)
+static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans,
+					      unsigned long txqs,
+					      bool freeze)
 {
 	if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
 		IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
 
-	return trans->ops->wait_tx_queue_empty(trans, txq_bm);
+	if (trans->ops->freeze_txq_timer)
+		trans->ops->freeze_txq_timer(trans, txqs, freeze);
+}
+
+static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans,
+						u32 txqs)
+{
+	if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
+		IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+
+	return trans->ops->wait_tx_queue_empty(trans, txqs);
 }
 
 static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans,
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c
index a3bfda4..13a0a03 100644
--- a/drivers/net/wireless/iwlwifi/mvm/coex.c
+++ b/drivers/net/wireless/iwlwifi/mvm/coex.c
@@ -72,158 +72,6 @@
 #include "mvm.h"
 #include "iwl-debug.h"
 
-const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = {
-	[BT_KILL_MSK_DEFAULT] = 0xfffffc00,
-	[BT_KILL_MSK_NEVER] = 0xffffffff,
-	[BT_KILL_MSK_ALWAYS] = 0,
-};
-
-const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
-	{
-		BT_KILL_MSK_ALWAYS,
-		BT_KILL_MSK_ALWAYS,
-		BT_KILL_MSK_ALWAYS,
-	},
-	{
-		BT_KILL_MSK_NEVER,
-		BT_KILL_MSK_NEVER,
-		BT_KILL_MSK_NEVER,
-	},
-	{
-		BT_KILL_MSK_NEVER,
-		BT_KILL_MSK_NEVER,
-		BT_KILL_MSK_NEVER,
-	},
-	{
-		BT_KILL_MSK_DEFAULT,
-		BT_KILL_MSK_NEVER,
-		BT_KILL_MSK_DEFAULT,
-	},
-};
-
-const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
-	{
-		BT_KILL_MSK_ALWAYS,
-		BT_KILL_MSK_ALWAYS,
-		BT_KILL_MSK_ALWAYS,
-	},
-	{
-		BT_KILL_MSK_ALWAYS,
-		BT_KILL_MSK_ALWAYS,
-		BT_KILL_MSK_ALWAYS,
-	},
-	{
-		BT_KILL_MSK_ALWAYS,
-		BT_KILL_MSK_ALWAYS,
-		BT_KILL_MSK_ALWAYS,
-	},
-	{
-		BT_KILL_MSK_DEFAULT,
-		BT_KILL_MSK_ALWAYS,
-		BT_KILL_MSK_DEFAULT,
-	},
-};
-
-static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
-	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] = {
-	{
-		cpu_to_le32(0x40000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0x44000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0x40000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0x44000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0xc0004000),
-		cpu_to_le32(0xf0005000),
-		cpu_to_le32(0xc0004000),
-		cpu_to_le32(0xf0005000),
-	},
-	{
-		cpu_to_le32(0x40000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0x44000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0x40000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0x44000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0xc0004000),
-		cpu_to_le32(0xf0005000),
-		cpu_to_le32(0xc0004000),
-		cpu_to_le32(0xf0005000),
-	},
-	{
-		cpu_to_le32(0x40000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0x44000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0x40000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0x44000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0xc0004000),
-		cpu_to_le32(0xf0005000),
-		cpu_to_le32(0xc0004000),
-		cpu_to_le32(0xf0005000),
-	},
-};
-
-static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
-	{
-		/* Tight */
-		cpu_to_le32(0xaaaaaaaa),
-		cpu_to_le32(0xaaaaaaaa),
-		cpu_to_le32(0xaeaaaaaa),
-		cpu_to_le32(0xaaaaaaaa),
-		cpu_to_le32(0xcc00ff28),
-		cpu_to_le32(0x0000aaaa),
-		cpu_to_le32(0xcc00aaaa),
-		cpu_to_le32(0x0000aaaa),
-		cpu_to_le32(0xc0004000),
-		cpu_to_le32(0x00004000),
-		cpu_to_le32(0xf0005000),
-		cpu_to_le32(0xf0005000),
-	},
-	{
-		/* Loose */
-		cpu_to_le32(0xaaaaaaaa),
-		cpu_to_le32(0xaaaaaaaa),
-		cpu_to_le32(0xaaaaaaaa),
-		cpu_to_le32(0xaaaaaaaa),
-		cpu_to_le32(0xcc00ff28),
-		cpu_to_le32(0x0000aaaa),
-		cpu_to_le32(0xcc00aaaa),
-		cpu_to_le32(0x0000aaaa),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0x00000000),
-		cpu_to_le32(0xf0005000),
-		cpu_to_le32(0xf0005000),
-	},
-	{
-		/* Tx Tx disabled */
-		cpu_to_le32(0xaaaaaaaa),
-		cpu_to_le32(0xaaaaaaaa),
-		cpu_to_le32(0xeeaaaaaa),
-		cpu_to_le32(0xaaaaaaaa),
-		cpu_to_le32(0xcc00ff28),
-		cpu_to_le32(0x0000aaaa),
-		cpu_to_le32(0xcc00aaaa),
-		cpu_to_le32(0x0000aaaa),
-		cpu_to_le32(0xc0004000),
-		cpu_to_le32(0xc0004000),
-		cpu_to_le32(0xf0005000),
-		cpu_to_le32(0xf0005000),
-	},
-};
-
 /* 20MHz / 40MHz below / 40Mhz above*/
 static const __le64 iwl_ci_mask[][3] = {
 	/* dummy entry for channel 0 */
@@ -342,7 +190,7 @@
 	{
 		.range = 12,
 		.lut20 = {
-			cpu_to_le32(0x00000001),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -363,7 +211,7 @@
 	{
 		.range = 20,
 		.lut20 = {
-			cpu_to_le32(0x00000002),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -384,7 +232,7 @@
 	{
 		.range = 21,
 		.lut20 = {
-			cpu_to_le32(0x00000003),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -405,7 +253,7 @@
 	{
 		.range = 23,
 		.lut20 = {
-			cpu_to_le32(0x00000004),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -426,7 +274,7 @@
 	{
 		.range = 27,
 		.lut20 = {
-			cpu_to_le32(0x00000005),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -447,7 +295,7 @@
 	{
 		.range = 30,
 		.lut20 = {
-			cpu_to_le32(0x00000006),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -468,7 +316,7 @@
 	{
 		.range = 32,
 		.lut20 = {
-			cpu_to_le32(0x00000007),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -489,7 +337,7 @@
 	{
 		.range = 33,
 		.lut20 = {
-			cpu_to_le32(0x00000008),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -596,14 +444,6 @@
 		goto send_cmd;
 	}
 
-	bt_cmd->max_kill = cpu_to_le32(5);
-	bt_cmd->bt4_antenna_isolation_thr =
-		cpu_to_le32(IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS);
-	bt_cmd->bt4_tx_tx_delta_freq_thr = cpu_to_le32(15);
-	bt_cmd->bt4_tx_rx_max_freq0 = cpu_to_le32(15);
-	bt_cmd->override_primary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
-	bt_cmd->override_secondary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
-
 	mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE;
 	bt_cmd->mode = cpu_to_le32(mode);
 
@@ -611,7 +451,7 @@
 		bt_cmd->enabled_modules |=
 			cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED);
 
-	if (IWL_MVM_BT_COEX_CORUNNING)
+	if (iwl_mvm_bt_is_plcr_supported(mvm))
 		bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED);
 
 	if (IWL_MVM_BT_COEX_MPLUT) {
@@ -622,18 +462,6 @@
 
 	bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET);
 
-	if (mvm->cfg->bt_shared_single_ant)
-		memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
-		       sizeof(iwl_single_shared_ant));
-	else
-		memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
-		       sizeof(iwl_combined_lookup));
-
-	memcpy(&bt_cmd->mplut_prio_boost, iwl_bt_prio_boost,
-	       sizeof(iwl_bt_prio_boost));
-	bt_cmd->multiprio_lut[0] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG0);
-	bt_cmd->multiprio_lut[1] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG1);
-
 send_cmd:
 	memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
 	memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
@@ -644,48 +472,6 @@
 	return ret;
 }
 
-static int iwl_mvm_bt_udpate_sw_boost(struct iwl_mvm *mvm)
-{
-	struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
-	u32 primary_lut = le32_to_cpu(notif->primary_ch_lut);
-	u32 secondary_lut = le32_to_cpu(notif->secondary_ch_lut);
-	u32 ag = le32_to_cpu(notif->bt_activity_grading);
-	struct iwl_bt_coex_sw_boost_update_cmd cmd = {};
-	u8 ack_kill_msk[NUM_PHY_CTX] = {};
-	u8 cts_kill_msk[NUM_PHY_CTX] = {};
-	int i;
-
-	lockdep_assert_held(&mvm->mutex);
-
-	ack_kill_msk[0] = iwl_bt_ack_kill_msk[ag][primary_lut];
-	cts_kill_msk[0] = iwl_bt_cts_kill_msk[ag][primary_lut];
-
-	ack_kill_msk[1] = iwl_bt_ack_kill_msk[ag][secondary_lut];
-	cts_kill_msk[1] = iwl_bt_cts_kill_msk[ag][secondary_lut];
-
-	/* Don't send HCMD if there is no update */
-	if (!memcmp(ack_kill_msk, mvm->bt_ack_kill_msk, sizeof(ack_kill_msk)) ||
-	    !memcmp(cts_kill_msk, mvm->bt_cts_kill_msk, sizeof(cts_kill_msk)))
-		return 0;
-
-	memcpy(mvm->bt_ack_kill_msk, ack_kill_msk,
-	       sizeof(mvm->bt_ack_kill_msk));
-	memcpy(mvm->bt_cts_kill_msk, cts_kill_msk,
-	       sizeof(mvm->bt_cts_kill_msk));
-
-	BUILD_BUG_ON(ARRAY_SIZE(ack_kill_msk) < ARRAY_SIZE(cmd.boost_values));
-
-	for (i = 0; i < ARRAY_SIZE(cmd.boost_values); i++) {
-		cmd.boost_values[i].kill_ack_msk =
-			cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk[i]]);
-		cmd.boost_values[i].kill_cts_msk =
-			cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk[i]]);
-	}
-
-	return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_SW_BOOST, 0,
-				    sizeof(cmd), &cmd);
-}
-
 static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
 				       bool enable)
 {
@@ -793,7 +579,8 @@
 	if (!vif->bss_conf.assoc)
 		smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
-	if (IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status,
+	if (mvmvif->phy_ctxt &&
+	    IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status,
 			       mvmvif->phy_ctxt->id))
 		smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
@@ -950,9 +737,6 @@
 			IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
 		memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
 	}
-
-	if (iwl_mvm_bt_udpate_sw_boost(mvm))
-		IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
 int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
@@ -989,7 +773,7 @@
 static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
 				   struct ieee80211_vif *vif)
 {
-	struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 	struct iwl_bt_iterator_data *data = _data;
 	struct iwl_mvm *mvm = data->mvm;
 
@@ -1023,9 +807,9 @@
 }
 
 void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-			   enum ieee80211_rssi_event rssi_event)
+			   enum ieee80211_rssi_event_data rssi_event)
 {
-	struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 	struct iwl_bt_iterator_data data = {
 		.mvm = mvm,
 	};
@@ -1073,9 +857,6 @@
 	ieee80211_iterate_active_interfaces_atomic(
 		mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 		iwl_mvm_bt_rssi_iterator, &data);
-
-	if (iwl_mvm_bt_udpate_sw_boost(mvm))
-		IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
 #define LINK_QUAL_AGG_TIME_LIMIT_DEF	(4000)
@@ -1234,7 +1015,7 @@
 	if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
 		return iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb, dev_cmd);
 
-	if (!IWL_MVM_BT_COEX_CORUNNING)
+	if (!iwl_mvm_bt_is_plcr_supported(mvm))
 		return 0;
 
 	lockdep_assert_held(&mvm->mutex);
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
index b3210cf..d954591 100644
--- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
+++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
@@ -288,6 +288,65 @@
 	},
 };
 
+enum iwl_bt_kill_msk {
+	BT_KILL_MSK_DEFAULT,
+	BT_KILL_MSK_NEVER,
+	BT_KILL_MSK_ALWAYS,
+	BT_KILL_MSK_MAX,
+};
+
+static const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = {
+	[BT_KILL_MSK_DEFAULT] = 0xfffffc00,
+	[BT_KILL_MSK_NEVER] = 0xffffffff,
+	[BT_KILL_MSK_ALWAYS] = 0,
+};
+
+static const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
+	{
+		BT_KILL_MSK_ALWAYS,
+		BT_KILL_MSK_ALWAYS,
+		BT_KILL_MSK_ALWAYS,
+	},
+	{
+		BT_KILL_MSK_NEVER,
+		BT_KILL_MSK_NEVER,
+		BT_KILL_MSK_NEVER,
+	},
+	{
+		BT_KILL_MSK_NEVER,
+		BT_KILL_MSK_NEVER,
+		BT_KILL_MSK_NEVER,
+	},
+	{
+		BT_KILL_MSK_DEFAULT,
+		BT_KILL_MSK_NEVER,
+		BT_KILL_MSK_DEFAULT,
+	},
+};
+
+static const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
+	{
+		BT_KILL_MSK_ALWAYS,
+		BT_KILL_MSK_ALWAYS,
+		BT_KILL_MSK_ALWAYS,
+	},
+	{
+		BT_KILL_MSK_ALWAYS,
+		BT_KILL_MSK_ALWAYS,
+		BT_KILL_MSK_ALWAYS,
+	},
+	{
+		BT_KILL_MSK_ALWAYS,
+		BT_KILL_MSK_ALWAYS,
+		BT_KILL_MSK_ALWAYS,
+	},
+	{
+		BT_KILL_MSK_DEFAULT,
+		BT_KILL_MSK_ALWAYS,
+		BT_KILL_MSK_DEFAULT,
+	},
+};
+
 struct corunning_block_luts {
 	u8 range;
 	__le32 lut20[BT_COEX_CORUN_LUT_SIZE];
@@ -330,7 +389,7 @@
 	{
 		.range = 12,
 		.lut20 = {
-			cpu_to_le32(0x00000001),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -351,7 +410,7 @@
 	{
 		.range = 20,
 		.lut20 = {
-			cpu_to_le32(0x00000002),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -372,7 +431,7 @@
 	{
 		.range = 21,
 		.lut20 = {
-			cpu_to_le32(0x00000003),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -393,7 +452,7 @@
 	{
 		.range = 23,
 		.lut20 = {
-			cpu_to_le32(0x00000004),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -414,7 +473,7 @@
 	{
 		.range = 27,
 		.lut20 = {
-			cpu_to_le32(0x00000005),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -435,7 +494,7 @@
 	{
 		.range = 30,
 		.lut20 = {
-			cpu_to_le32(0x00000006),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -456,7 +515,7 @@
 	{
 		.range = 32,
 		.lut20 = {
-			cpu_to_le32(0x00000007),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -477,7 +536,7 @@
 	{
 		.range = 33,
 		.lut20 = {
-			cpu_to_le32(0x00000008),  cpu_to_le32(0x00000000),
+			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
 			cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
@@ -619,7 +678,7 @@
 	if (IWL_MVM_BT_COEX_SYNC2SCO)
 		bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
 
-	if (IWL_MVM_BT_COEX_CORUNNING) {
+	if (iwl_mvm_bt_is_plcr_supported(mvm)) {
 		bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
 						     BT_VALID_CORUN_LUT_40);
 		bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
@@ -633,7 +692,7 @@
 	if (IWL_MVM_BT_COEX_TTC)
 		bt_cmd->flags |= cpu_to_le32(BT_COEX_TTC);
 
-	if (IWL_MVM_BT_COEX_RRC)
+	if (iwl_mvm_bt_is_rrc_supported(mvm))
 		bt_cmd->flags |= cpu_to_le32(BT_COEX_RRC);
 
 	if (mvm->cfg->bt_shared_single_ant)
@@ -832,7 +891,8 @@
 	if (!vif->bss_conf.assoc)
 		smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
-	if (data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id))
+	if (mvmvif->phy_ctxt &&
+	    data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id))
 		smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
 	IWL_DEBUG_COEX(data->mvm,
@@ -1034,7 +1094,7 @@
 static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
 				     struct ieee80211_vif *vif)
 {
-	struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 	struct iwl_bt_iterator_data *data = _data;
 	struct iwl_mvm *mvm = data->mvm;
 
@@ -1068,9 +1128,9 @@
 }
 
 void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-			       enum ieee80211_rssi_event rssi_event)
+			       enum ieee80211_rssi_event_data rssi_event)
 {
-	struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 	struct iwl_bt_iterator_data data = {
 		.mvm = mvm,
 	};
@@ -1167,16 +1227,10 @@
 	return lut_type != BT_COEX_LOOSE_LUT;
 }
 
-bool iwl_mvm_bt_coex_is_ant_avail_old(struct iwl_mvm *mvm, u8 ant)
-{
-	u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
-	return ag < BT_HIGH_TRAFFIC;
-}
-
 bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm)
 {
 	u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
-	return ag == BT_OFF;
+	return ag < BT_HIGH_TRAFFIC;
 }
 
 bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
@@ -1213,7 +1267,7 @@
 		.dataflags = { IWL_HCMD_DFL_NOCOPY, },
 	};
 
-	if (!IWL_MVM_BT_COEX_CORUNNING)
+	if (!iwl_mvm_bt_is_plcr_supported(mvm))
 		return 0;
 
 	lockdep_assert_held(&mvm->mutex);
diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h
index 3bd9347..beba375 100644
--- a/drivers/net/wireless/iwlwifi/mvm/constants.h
+++ b/drivers/net/wireless/iwlwifi/mvm/constants.h
@@ -94,13 +94,42 @@
 #define IWL_MVM_BT_COEX_MPLUT			1
 #define IWL_MVM_BT_COEX_RRC			1
 #define IWL_MVM_BT_COEX_TTC			1
-#define IWL_MVM_BT_COEX_MPLUT_REG0		0x28412201
+#define IWL_MVM_BT_COEX_MPLUT_REG0		0x22002200
 #define IWL_MVM_BT_COEX_MPLUT_REG1		0x11118451
 #define IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS	30
 #define IWL_MVM_FW_MCAST_FILTER_PASS_ALL	0
 #define IWL_MVM_FW_BCAST_FILTER_PASS_ALL	0
-#define IWL_MVM_QUOTA_THRESHOLD			8
+#define IWL_MVM_QUOTA_THRESHOLD			4
 #define IWL_MVM_RS_RSSI_BASED_INIT_RATE         0
-#define IWL_MVM_RS_DISABLE_MIMO			0
+#define IWL_MVM_RS_DISABLE_P2P_MIMO		0
+#define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE    1
+#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE      2
+#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW   1
+#define IWL_MVM_RS_INITIAL_MIMO_NUM_RATES       3
+#define IWL_MVM_RS_INITIAL_SISO_NUM_RATES       3
+#define IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES     2
+#define IWL_MVM_RS_INITIAL_LEGACY_RETRIES       2
+#define IWL_MVM_RS_SECONDARY_LEGACY_RETRIES	1
+#define IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES   16
+#define IWL_MVM_RS_SECONDARY_SISO_NUM_RATES     3
+#define IWL_MVM_RS_SECONDARY_SISO_RETRIES       1
+#define IWL_MVM_RS_RATE_MIN_FAILURE_TH		3
+#define IWL_MVM_RS_RATE_MIN_SUCCESS_TH		8
+#define IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT	5	/* Seconds */
+#define IWL_MVM_RS_IDLE_TIMEOUT			5	/* Seconds */
+#define IWL_MVM_RS_MISSED_RATE_MAX		15
+#define IWL_MVM_RS_LEGACY_FAILURE_LIMIT		160
+#define IWL_MVM_RS_LEGACY_SUCCESS_LIMIT		480
+#define IWL_MVM_RS_LEGACY_TABLE_COUNT		160
+#define IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT	400
+#define IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT	4500
+#define IWL_MVM_RS_NON_LEGACY_TABLE_COUNT	1500
+#define IWL_MVM_RS_SR_FORCE_DECREASE		15	/* percent */
+#define IWL_MVM_RS_SR_NO_DECREASE		85	/* percent */
+#define IWL_MVM_RS_AGG_TIME_LIMIT	        4000    /* 4 msecs. valid 100-8000 */
+#define IWL_MVM_RS_AGG_DISABLE_START	        3
+#define IWL_MVM_RS_TPC_SR_FORCE_INCREASE	75	/* percent */
+#define IWL_MVM_RS_TPC_SR_NO_INCREASE		85	/* percent */
+#define IWL_MVM_RS_TPC_TX_POWER_STEP		3
 
 #endif /* __MVM_CONSTANTS_H */
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
index db454c8..b979e03 100644
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/iwlwifi/mvm/d3.c
@@ -694,6 +694,9 @@
 	if (ret)
 		IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
 
+	if (iwl_mvm_is_lar_supported(mvm) && iwl_mvm_init_fw_regd(mvm))
+		IWL_ERR(mvm, "Failed to initialize D3 LAR information\n");
+
 	return 0;
 }
 
@@ -793,7 +796,7 @@
 			  struct ieee80211_sta *ap_sta)
 {
 	int ret;
-	struct iwl_mvm_sta *mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
+	struct iwl_mvm_sta *mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
 
 	/* TODO: wowlan_config_cmd->wowlan_ba_teardown_tids */
 
@@ -1128,6 +1131,7 @@
 	iwl_trans_d3_suspend(mvm->trans, test);
  out:
 	if (ret < 0) {
+		iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
 		ieee80211_restart_hw(mvm->hw);
 		iwl_mvm_free_nd(mvm);
 	}
@@ -1137,12 +1141,43 @@
 	return ret;
 }
 
+static int iwl_mvm_enter_d0i3_sync(struct iwl_mvm *mvm)
+{
+	struct iwl_notification_wait wait_d3;
+	static const u8 d3_notif[] = { D3_CONFIG_CMD };
+	int ret;
+
+	iwl_init_notification_wait(&mvm->notif_wait, &wait_d3,
+				   d3_notif, ARRAY_SIZE(d3_notif),
+				   NULL, NULL);
+
+	ret = iwl_mvm_enter_d0i3(mvm->hw->priv);
+	if (ret)
+		goto remove_notif;
+
+	ret = iwl_wait_notification(&mvm->notif_wait, &wait_d3, HZ);
+	WARN_ON_ONCE(ret);
+	return ret;
+
+remove_notif:
+	iwl_remove_notification(&mvm->notif_wait, &wait_d3);
+	return ret;
+}
+
 int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 {
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
 	iwl_trans_suspend(mvm->trans);
-	if (iwl_mvm_is_d0i3_supported(mvm)) {
+	if (wowlan->any) {
+		/* 'any' trigger means d0i3 usage */
+		if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) {
+			int ret = iwl_mvm_enter_d0i3_sync(mvm);
+
+			if (ret)
+				return ret;
+		}
+
 		mutex_lock(&mvm->d0i3_suspend_mutex);
 		__set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
 		mutex_unlock(&mvm->d0i3_suspend_mutex);
@@ -1565,7 +1600,7 @@
 
 	/* RF-kill already asserted again... */
 	if (!cmd.resp_pkt) {
-		ret = -ERFKILL;
+		fw_status = ERR_PTR(-ERFKILL);
 		goto out_free_resp;
 	}
 
@@ -1574,7 +1609,7 @@
 	len = iwl_rx_packet_payload_len(cmd.resp_pkt);
 	if (len < status_size) {
 		IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
-		ret = -EIO;
+		fw_status = ERR_PTR(-EIO);
 		goto out_free_resp;
 	}
 
@@ -1582,7 +1617,7 @@
 	if (len != (status_size +
 		    ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) {
 		IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
-		ret = -EIO;
+		fw_status = ERR_PTR(-EIO);
 		goto out_free_resp;
 	}
 
@@ -1590,7 +1625,7 @@
 
 out_free_resp:
 	iwl_free_resp(&cmd);
-	return ret ? ERR_PTR(ret) : fw_status;
+	return fw_status;
 }
 
 /* releases the MVM mutex */
@@ -1626,7 +1661,7 @@
 	if (IS_ERR_OR_NULL(ap_sta))
 		goto out_free;
 
-	mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
+	mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
 	for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
 		u16 seq = status.qos_seq_ctr[i];
 		/* firmware stores last-used value, we store next value */
@@ -1691,6 +1726,10 @@
 	results->matched_profiles = le32_to_cpu(query->matched_profiles);
 	memcpy(results->matches, query->matches, sizeof(results->matches));
 
+#ifdef CPTCFG_IWLWIFI_DEBUGFS
+	mvm->last_netdetect_scans = le32_to_cpu(query->n_scans_done);
+#endif
+
 out_free_resp:
 	iwl_free_resp(&cmd);
 	return ret;
@@ -1843,27 +1882,36 @@
 	/* query SRAM first in case we want event logging */
 	iwl_mvm_read_d3_sram(mvm);
 
+	/*
+	 * Query the current location and source from the D3 firmware so we
+	 * can play it back when we re-intiailize the D0 firmware
+	 */
+	iwl_mvm_update_changed_regdom(mvm);
+
 	if (mvm->net_detect) {
 		iwl_mvm_query_netdetect_reasons(mvm, vif);
+		/* has unlocked the mutex, so skip that */
+		goto out;
 	} else {
 		keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
 #ifdef CPTCFG_IWLWIFI_DEBUGFS
 		if (keep)
 			mvm->keep_vif = vif;
 #endif
+		/* has unlocked the mutex, so skip that */
+		goto out_iterate;
 	}
-	/* has unlocked the mutex, so skip that */
-	goto out;
 
  out_unlock:
 	mutex_unlock(&mvm->mutex);
 
- out:
+out_iterate:
 	if (!test)
 		ieee80211_iterate_active_interfaces_rtnl(mvm->hw,
 			IEEE80211_IFACE_ITER_NORMAL,
 			iwl_mvm_d3_disconnect_iter, keep ? vif : NULL);
 
+out:
 	/* return 1 to reconfigure the device */
 	set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
 	set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status);
@@ -1876,8 +1924,20 @@
 
 	iwl_trans_resume(mvm->trans);
 
-	if (iwl_mvm_is_d0i3_supported(mvm))
+	if (mvm->hw->wiphy->wowlan_config->any) {
+		/* 'any' trigger means d0i3 usage */
+		if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) {
+			int ret = iwl_mvm_exit_d0i3(hw->priv);
+
+			if (ret)
+				return ret;
+			/*
+			 * d0i3 exit will be deferred until reconfig_complete.
+			 * make sure there we are out of d0i3.
+			 */
+		}
 		return 0;
+	}
 
 	return __iwl_mvm_resume(mvm, false);
 }
@@ -1961,6 +2021,7 @@
 	__iwl_mvm_resume(mvm, true);
 	rtnl_unlock();
 	iwl_abort_notification_waits(&mvm->notif_wait);
+	iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
 	ieee80211_restart_hw(mvm->hw);
 
 	/* wait for restart and disconnect all interfaces */
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
index 9aa2311..5f37eab 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
@@ -268,7 +268,7 @@
 		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id],
 						lockdep_is_held(&mvm->mutex));
 		if (!IS_ERR_OR_NULL(sta)) {
-			struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+			struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 
 			pos += scnprintf(buf+pos, bufsz-pos,
 					 "ap_sta_id %d - reduced Tx power %d\n",
@@ -517,6 +517,85 @@
 	return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
 }
 
+static ssize_t iwl_dbgfs_uapsd_misbehaving_read(struct file *file,
+						char __user *user_buf,
+						size_t count, loff_t *ppos)
+{
+	struct ieee80211_vif *vif = file->private_data;
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+	char buf[20];
+	int len;
+
+	len = sprintf(buf, "%pM\n", mvmvif->uapsd_misbehaving_bssid);
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t iwl_dbgfs_uapsd_misbehaving_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;
+	bool ret;
+
+	mutex_lock(&mvm->mutex);
+	ret = mac_pton(buf, mvmvif->uapsd_misbehaving_bssid);
+	mutex_unlock(&mvm->mutex);
+
+	return ret ? count : -EINVAL;
+}
+
+static ssize_t iwl_dbgfs_rx_phyinfo_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 ieee80211_chanctx_conf *chanctx_conf;
+	struct iwl_mvm_phy_ctxt *phy_ctxt;
+	u16 value;
+	int ret;
+
+	ret = kstrtou16(buf, 0, &value);
+	if (ret)
+		return ret;
+
+	mutex_lock(&mvm->mutex);
+	rcu_read_lock();
+
+	chanctx_conf = rcu_dereference(vif->chanctx_conf);
+	/* make sure the channel context is assigned */
+	if (!chanctx_conf) {
+		rcu_read_unlock();
+		mutex_unlock(&mvm->mutex);
+		return -EINVAL;
+	}
+
+	phy_ctxt = &mvm->phy_ctxts[*(u16 *)chanctx_conf->drv_priv];
+	rcu_read_unlock();
+
+	mvm->dbgfs_rx_phyinfo = value;
+
+	ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chanctx_conf->min_def,
+				       chanctx_conf->rx_chains_static,
+				       chanctx_conf->rx_chains_dynamic);
+	mutex_unlock(&mvm->mutex);
+
+	return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_rx_phyinfo_read(struct file *file,
+					 char __user *user_buf,
+					 size_t count, loff_t *ppos)
+{
+	struct ieee80211_vif *vif = file->private_data;
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+	char buf[8];
+
+	snprintf(buf, sizeof(buf), "0x%04x\n", mvmvif->mvm->dbgfs_rx_phyinfo);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
+}
+
 #define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
 	_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
 #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
@@ -531,6 +610,8 @@
 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_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10);
 
 void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
@@ -546,7 +627,6 @@
 		return;
 
 	mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir);
-	mvmvif->mvm = mvm;
 
 	if (!mvmvif->dbgfs_dir) {
 		IWL_ERR(mvm, "Failed to create debugfs directory under %s\n",
@@ -564,6 +644,10 @@
 	MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR);
 	MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir,
 				 S_IRUSR | S_IWUSR);
+	MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir,
+				 S_IRUSR | S_IWUSR);
+	MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir,
+				 S_IRUSR | S_IWUSR);
 
 	if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
 	    mvmvif == mvm->bf_allowed_vif)
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
index 2c87e2d..0917337 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
@@ -562,11 +562,12 @@
 			       "\tSecondary Channel Bitmap 0x%016llx\n",
 			       le64_to_cpu(cmd->bt_secondary_ci));
 
-		pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
-		pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
-				 iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[0]]);
-		pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
-				 iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[0]]);
+		pos += scnprintf(buf+pos, bufsz-pos,
+				 "BT Configuration CMD - 0=default, 1=never, 2=always\n");
+		pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill msk idx %d\n",
+				 mvm->bt_ack_kill_msk[0]);
+		pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill msk idx %d\n",
+				 mvm->bt_cts_kill_msk[0]);
 
 	} else {
 		struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
@@ -579,21 +580,6 @@
 		pos += scnprintf(buf+pos, bufsz-pos,
 			       "\tSecondary Channel Bitmap 0x%016llx\n",
 			       le64_to_cpu(cmd->bt_secondary_ci));
-
-		pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
-		pos += scnprintf(buf+pos, bufsz-pos,
-				 "\tPrimary: ACK Kill Mask 0x%08x\n",
-				 iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[0]]);
-		pos += scnprintf(buf+pos, bufsz-pos,
-				 "\tPrimary: CTS Kill Mask 0x%08x\n",
-				 iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[0]]);
-		pos += scnprintf(buf+pos, bufsz-pos,
-				 "\tSecondary: ACK Kill Mask 0x%08x\n",
-				 iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[1]]);
-		pos += scnprintf(buf+pos, bufsz-pos,
-				 "\tSecondary: CTS Kill Mask 0x%08x\n",
-				 iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[1]]);
-
 	}
 
 	mutex_unlock(&mvm->mutex);
@@ -654,10 +640,10 @@
 	return ret ?: count;
 }
 
-#define PRINT_STATS_LE32(_str, _val)					\
+#define PRINT_STATS_LE32(_struct, _memb)				\
 			 pos += scnprintf(buf + pos, bufsz - pos,	\
-					  fmt_table, _str,		\
-					  le32_to_cpu(_val))
+					  fmt_table, #_memb,		\
+					  le32_to_cpu(_struct->_memb))
 
 static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
 					  char __user *user_buf, size_t count,
@@ -692,97 +678,89 @@
 
 	pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
 			 "Statistics_Rx - OFDM");
-	PRINT_STATS_LE32("ina_cnt", ofdm->ina_cnt);
-	PRINT_STATS_LE32("fina_cnt", ofdm->fina_cnt);
-	PRINT_STATS_LE32("plcp_err", ofdm->plcp_err);
-	PRINT_STATS_LE32("crc32_err", ofdm->crc32_err);
-	PRINT_STATS_LE32("overrun_err", ofdm->overrun_err);
-	PRINT_STATS_LE32("early_overrun_err", ofdm->early_overrun_err);
-	PRINT_STATS_LE32("crc32_good", ofdm->crc32_good);
-	PRINT_STATS_LE32("false_alarm_cnt", ofdm->false_alarm_cnt);
-	PRINT_STATS_LE32("fina_sync_err_cnt", ofdm->fina_sync_err_cnt);
-	PRINT_STATS_LE32("sfd_timeout", ofdm->sfd_timeout);
-	PRINT_STATS_LE32("fina_timeout", ofdm->fina_timeout);
-	PRINT_STATS_LE32("unresponded_rts", ofdm->unresponded_rts);
-	PRINT_STATS_LE32("rxe_frame_lmt_overrun",
-			 ofdm->rxe_frame_limit_overrun);
-	PRINT_STATS_LE32("sent_ack_cnt", ofdm->sent_ack_cnt);
-	PRINT_STATS_LE32("sent_cts_cnt", ofdm->sent_cts_cnt);
-	PRINT_STATS_LE32("sent_ba_rsp_cnt", ofdm->sent_ba_rsp_cnt);
-	PRINT_STATS_LE32("dsp_self_kill", ofdm->dsp_self_kill);
-	PRINT_STATS_LE32("mh_format_err", ofdm->mh_format_err);
-	PRINT_STATS_LE32("re_acq_main_rssi_sum", ofdm->re_acq_main_rssi_sum);
-	PRINT_STATS_LE32("reserved", ofdm->reserved);
+	PRINT_STATS_LE32(ofdm, ina_cnt);
+	PRINT_STATS_LE32(ofdm, fina_cnt);
+	PRINT_STATS_LE32(ofdm, plcp_err);
+	PRINT_STATS_LE32(ofdm, crc32_err);
+	PRINT_STATS_LE32(ofdm, overrun_err);
+	PRINT_STATS_LE32(ofdm, early_overrun_err);
+	PRINT_STATS_LE32(ofdm, crc32_good);
+	PRINT_STATS_LE32(ofdm, false_alarm_cnt);
+	PRINT_STATS_LE32(ofdm, fina_sync_err_cnt);
+	PRINT_STATS_LE32(ofdm, sfd_timeout);
+	PRINT_STATS_LE32(ofdm, fina_timeout);
+	PRINT_STATS_LE32(ofdm, unresponded_rts);
+	PRINT_STATS_LE32(ofdm, rxe_frame_lmt_overrun);
+	PRINT_STATS_LE32(ofdm, sent_ack_cnt);
+	PRINT_STATS_LE32(ofdm, sent_cts_cnt);
+	PRINT_STATS_LE32(ofdm, sent_ba_rsp_cnt);
+	PRINT_STATS_LE32(ofdm, dsp_self_kill);
+	PRINT_STATS_LE32(ofdm, mh_format_err);
+	PRINT_STATS_LE32(ofdm, re_acq_main_rssi_sum);
+	PRINT_STATS_LE32(ofdm, reserved);
 
 	pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
 			 "Statistics_Rx - CCK");
-	PRINT_STATS_LE32("ina_cnt", cck->ina_cnt);
-	PRINT_STATS_LE32("fina_cnt", cck->fina_cnt);
-	PRINT_STATS_LE32("plcp_err", cck->plcp_err);
-	PRINT_STATS_LE32("crc32_err", cck->crc32_err);
-	PRINT_STATS_LE32("overrun_err", cck->overrun_err);
-	PRINT_STATS_LE32("early_overrun_err", cck->early_overrun_err);
-	PRINT_STATS_LE32("crc32_good", cck->crc32_good);
-	PRINT_STATS_LE32("false_alarm_cnt", cck->false_alarm_cnt);
-	PRINT_STATS_LE32("fina_sync_err_cnt", cck->fina_sync_err_cnt);
-	PRINT_STATS_LE32("sfd_timeout", cck->sfd_timeout);
-	PRINT_STATS_LE32("fina_timeout", cck->fina_timeout);
-	PRINT_STATS_LE32("unresponded_rts", cck->unresponded_rts);
-	PRINT_STATS_LE32("rxe_frame_lmt_overrun",
-			 cck->rxe_frame_limit_overrun);
-	PRINT_STATS_LE32("sent_ack_cnt", cck->sent_ack_cnt);
-	PRINT_STATS_LE32("sent_cts_cnt", cck->sent_cts_cnt);
-	PRINT_STATS_LE32("sent_ba_rsp_cnt", cck->sent_ba_rsp_cnt);
-	PRINT_STATS_LE32("dsp_self_kill", cck->dsp_self_kill);
-	PRINT_STATS_LE32("mh_format_err", cck->mh_format_err);
-	PRINT_STATS_LE32("re_acq_main_rssi_sum", cck->re_acq_main_rssi_sum);
-	PRINT_STATS_LE32("reserved", cck->reserved);
+	PRINT_STATS_LE32(cck, ina_cnt);
+	PRINT_STATS_LE32(cck, fina_cnt);
+	PRINT_STATS_LE32(cck, plcp_err);
+	PRINT_STATS_LE32(cck, crc32_err);
+	PRINT_STATS_LE32(cck, overrun_err);
+	PRINT_STATS_LE32(cck, early_overrun_err);
+	PRINT_STATS_LE32(cck, crc32_good);
+	PRINT_STATS_LE32(cck, false_alarm_cnt);
+	PRINT_STATS_LE32(cck, fina_sync_err_cnt);
+	PRINT_STATS_LE32(cck, sfd_timeout);
+	PRINT_STATS_LE32(cck, fina_timeout);
+	PRINT_STATS_LE32(cck, unresponded_rts);
+	PRINT_STATS_LE32(cck, rxe_frame_lmt_overrun);
+	PRINT_STATS_LE32(cck, sent_ack_cnt);
+	PRINT_STATS_LE32(cck, sent_cts_cnt);
+	PRINT_STATS_LE32(cck, sent_ba_rsp_cnt);
+	PRINT_STATS_LE32(cck, dsp_self_kill);
+	PRINT_STATS_LE32(cck, mh_format_err);
+	PRINT_STATS_LE32(cck, re_acq_main_rssi_sum);
+	PRINT_STATS_LE32(cck, reserved);
 
 	pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
 			 "Statistics_Rx - GENERAL");
-	PRINT_STATS_LE32("bogus_cts", general->bogus_cts);
-	PRINT_STATS_LE32("bogus_ack", general->bogus_ack);
-	PRINT_STATS_LE32("non_bssid_frames", general->non_bssid_frames);
-	PRINT_STATS_LE32("filtered_frames", general->filtered_frames);
-	PRINT_STATS_LE32("non_channel_beacons", general->non_channel_beacons);
-	PRINT_STATS_LE32("channel_beacons", general->channel_beacons);
-	PRINT_STATS_LE32("num_missed_bcon", general->num_missed_bcon);
-	PRINT_STATS_LE32("adc_rx_saturation_time",
-			 general->adc_rx_saturation_time);
-	PRINT_STATS_LE32("ina_detection_search_time",
-			 general->ina_detection_search_time);
-	PRINT_STATS_LE32("beacon_silence_rssi_a",
-			 general->beacon_silence_rssi_a);
-	PRINT_STATS_LE32("beacon_silence_rssi_b",
-			 general->beacon_silence_rssi_b);
-	PRINT_STATS_LE32("beacon_silence_rssi_c",
-			 general->beacon_silence_rssi_c);
-	PRINT_STATS_LE32("interference_data_flag",
-			 general->interference_data_flag);
-	PRINT_STATS_LE32("channel_load", general->channel_load);
-	PRINT_STATS_LE32("dsp_false_alarms", general->dsp_false_alarms);
-	PRINT_STATS_LE32("beacon_rssi_a", general->beacon_rssi_a);
-	PRINT_STATS_LE32("beacon_rssi_b", general->beacon_rssi_b);
-	PRINT_STATS_LE32("beacon_rssi_c", general->beacon_rssi_c);
-	PRINT_STATS_LE32("beacon_energy_a", general->beacon_energy_a);
-	PRINT_STATS_LE32("beacon_energy_b", general->beacon_energy_b);
-	PRINT_STATS_LE32("beacon_energy_c", general->beacon_energy_c);
-	PRINT_STATS_LE32("num_bt_kills", general->num_bt_kills);
-	PRINT_STATS_LE32("mac_id", general->mac_id);
-	PRINT_STATS_LE32("directed_data_mpdu", general->directed_data_mpdu);
+	PRINT_STATS_LE32(general, bogus_cts);
+	PRINT_STATS_LE32(general, bogus_ack);
+	PRINT_STATS_LE32(general, non_bssid_frames);
+	PRINT_STATS_LE32(general, filtered_frames);
+	PRINT_STATS_LE32(general, non_channel_beacons);
+	PRINT_STATS_LE32(general, channel_beacons);
+	PRINT_STATS_LE32(general, num_missed_bcon);
+	PRINT_STATS_LE32(general, adc_rx_saturation_time);
+	PRINT_STATS_LE32(general, ina_detection_search_time);
+	PRINT_STATS_LE32(general, beacon_silence_rssi_a);
+	PRINT_STATS_LE32(general, beacon_silence_rssi_b);
+	PRINT_STATS_LE32(general, beacon_silence_rssi_c);
+	PRINT_STATS_LE32(general, interference_data_flag);
+	PRINT_STATS_LE32(general, channel_load);
+	PRINT_STATS_LE32(general, dsp_false_alarms);
+	PRINT_STATS_LE32(general, beacon_rssi_a);
+	PRINT_STATS_LE32(general, beacon_rssi_b);
+	PRINT_STATS_LE32(general, beacon_rssi_c);
+	PRINT_STATS_LE32(general, beacon_energy_a);
+	PRINT_STATS_LE32(general, beacon_energy_b);
+	PRINT_STATS_LE32(general, beacon_energy_c);
+	PRINT_STATS_LE32(general, num_bt_kills);
+	PRINT_STATS_LE32(general, mac_id);
+	PRINT_STATS_LE32(general, directed_data_mpdu);
 
 	pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
 			 "Statistics_Rx - HT");
-	PRINT_STATS_LE32("plcp_err", ht->plcp_err);
-	PRINT_STATS_LE32("overrun_err", ht->overrun_err);
-	PRINT_STATS_LE32("early_overrun_err", ht->early_overrun_err);
-	PRINT_STATS_LE32("crc32_good", ht->crc32_good);
-	PRINT_STATS_LE32("crc32_err", ht->crc32_err);
-	PRINT_STATS_LE32("mh_format_err", ht->mh_format_err);
-	PRINT_STATS_LE32("agg_crc32_good", ht->agg_crc32_good);
-	PRINT_STATS_LE32("agg_mpdu_cnt", ht->agg_mpdu_cnt);
-	PRINT_STATS_LE32("agg_cnt", ht->agg_cnt);
-	PRINT_STATS_LE32("unsupport_mcs", ht->unsupport_mcs);
+	PRINT_STATS_LE32(ht, plcp_err);
+	PRINT_STATS_LE32(ht, overrun_err);
+	PRINT_STATS_LE32(ht, early_overrun_err);
+	PRINT_STATS_LE32(ht, crc32_good);
+	PRINT_STATS_LE32(ht, crc32_err);
+	PRINT_STATS_LE32(ht, mh_format_err);
+	PRINT_STATS_LE32(ht, agg_crc32_good);
+	PRINT_STATS_LE32(ht, agg_mpdu_cnt);
+	PRINT_STATS_LE32(ht, agg_cnt);
+	PRINT_STATS_LE32(ht, unsupport_mcs);
 
 	mutex_unlock(&mvm->mutex);
 
@@ -933,7 +911,7 @@
 		return -EINVAL;
 	if (scan_rx_ant > ANT_ABC)
 		return -EINVAL;
-	if (scan_rx_ant & ~mvm->fw->valid_rx_ant)
+	if (scan_rx_ant & ~(iwl_mvm_get_valid_rx_ant(mvm)))
 		return -EINVAL;
 
 	if (mvm->scan_rx_ant != scan_rx_ant) {
@@ -945,6 +923,61 @@
 	return count;
 }
 
+static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file,
+					  char __user *user_buf,
+					  size_t count, loff_t *ppos)
+{
+	struct iwl_mvm *mvm = file->private_data;
+	int conf;
+	char buf[8];
+	const size_t bufsz = sizeof(buf);
+	int pos = 0;
+
+	mutex_lock(&mvm->mutex);
+	conf = mvm->fw_dbg_conf;
+	mutex_unlock(&mvm->mutex);
+
+	pos += scnprintf(buf + pos, bufsz - pos, "%d\n", conf);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm,
+					   char *buf, size_t count,
+					   loff_t *ppos)
+{
+	int ret, conf_id;
+
+	ret = kstrtoint(buf, 0, &conf_id);
+	if (ret)
+		return ret;
+
+	if (WARN_ON(conf_id >= FW_DBG_CONF_MAX))
+		return -EINVAL;
+
+	mutex_lock(&mvm->mutex);
+	ret = iwl_mvm_start_fw_dbg_conf(mvm, conf_id);
+	mutex_unlock(&mvm->mutex);
+
+	return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm,
+					      char *buf, size_t count,
+					      loff_t *ppos)
+{
+	int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE);
+
+	if (ret)
+		return ret;
+
+	iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, NULL, 0, 0);
+
+	iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
+
+	return count;
+}
+
 #define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__)
 #ifdef CPTCFG_IWLWIFI_BCAST_FILTERING
 static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file,
@@ -1340,6 +1373,7 @@
 	PRINT_MVM_REF(IWL_MVM_REF_TM_CMD);
 	PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK);
 	PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA);
+	PRINT_MVM_REF(IWL_MVM_REF_FW_DBG_COLLECT);
 
 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
@@ -1459,6 +1493,8 @@
 MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8);
+MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8);
 
 #ifdef CPTCFG_IWLWIFI_BCAST_FILTERING
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
@@ -1500,6 +1536,13 @@
 			     S_IWUSR | S_IRUSR);
 	MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
 	MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
+	MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
+	MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR);
+	if (!debugfs_create_bool("enable_scan_iteration_notif",
+				 S_IRUSR | S_IWUSR,
+				 mvm->debugfs_dir,
+				 &mvm->scan_iter_notif_enabled))
+		goto err;
 
 #ifdef CPTCFG_IWLWIFI_BCAST_FILTERING
 	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
@@ -1526,6 +1569,9 @@
 	if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR,
 				 mvm->debugfs_dir, &mvm->d3_wake_sysassert))
 		goto err;
+	if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR,
+				mvm->debugfs_dir, &mvm->last_netdetect_scans))
+		goto err;
 	MVM_DEBUGFS_ADD_FILE(netdetect, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
 #endif
 
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
index f3b1189..d398a61 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
@@ -235,36 +235,12 @@
  * struct iwl_bt_coex_cmd - bt coex configuration command
  * @mode: enum %iwl_bt_coex_mode
  * @enabled_modules: enum %iwl_bt_coex_enabled_modules
- * @max_kill: max count of Tx retries due to kill from PTA
- * @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_thr: antenna threshold value
- * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency
- * @bt4_tx_rx_max_freq0: TxRx max frequency
- * @multiprio_lut: multi priority LUT configuration
- * @mplut_prio_boost: BT priority boost registers
- * @decision_lut: PTA decision LUT, per Prio-Ch
  *
  * The structure is used for the BT_COEX command.
  */
 struct iwl_bt_coex_cmd {
 	__le32 mode;
 	__le32 enabled_modules;
-
-	__le32 max_kill;
-	__le32 override_primary_lut;
-	__le32 override_secondary_lut;
-	__le32 bt4_antenna_isolation_thr;
-
-	__le32 bt4_tx_tx_delta_freq_thr;
-	__le32 bt4_tx_rx_max_freq0;
-
-	__le32 multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE];
-	__le32 mplut_prio_boost[BT_COEX_BOOST_SIZE];
-
-	__le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE];
 } __packed; /* BT_COEX_CMD_API_S_VER_6 */
 
 /**
@@ -280,29 +256,6 @@
 } __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */
 
 /**
- * struct iwl_bt_coex_sw_boost - SW boost values
- * @wifi_tx_prio_boost: SW boost of wifi tx priority
- * @wifi_rx_prio_boost: SW boost of wifi rx priority
- * @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.
- */
-struct iwl_bt_coex_sw_boost {
-	__le32 wifi_tx_prio_boost;
-	__le32 wifi_rx_prio_boost;
-	__le32 kill_ack_msk;
-	__le32 kill_cts_msk;
-};
-
-/**
- * struct iwl_bt_coex_sw_boost_update_cmd - command to update the SW boost
- * @boost_values: check struct  %iwl_bt_coex_sw_boost - one for each channel
- *	primary / secondary / low priority
- */
-struct iwl_bt_coex_sw_boost_update_cmd {
-	struct iwl_bt_coex_sw_boost boost_values[3];
-} __packed; /* BT_COEX_UPDATE_SW_BOOST_S_VER_1 */
-
-/**
  * struct iwl_bt_coex_reduced_txp_update_cmd
  * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the
  *	bits are the sta_id (value)
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
index 6d3bea5..d7658d1 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
@@ -132,7 +132,7 @@
  * @solicited_node_ipv6_addr: broken -- solicited node address exists
  *	for each target address
  * @target_ipv6_addr: our target addresses
- * @ndp_mac_addr: neighbor soliciation response MAC address
+ * @ndp_mac_addr: neighbor solicitation response MAC address
  */
 struct iwl_proto_offload_cmd_v1 {
 	struct iwl_proto_offload_cmd_common common;
@@ -150,7 +150,7 @@
  * @solicited_node_ipv6_addr: broken -- solicited node address exists
  *	for each target address
  * @target_ipv6_addr: our target addresses
- * @ndp_mac_addr: neighbor soliciation response MAC address
+ * @ndp_mac_addr: neighbor solicitation response MAC address
  */
 struct iwl_proto_offload_cmd_v2 {
 	struct iwl_proto_offload_cmd_common common;
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
index c405cda..f3f3ee0 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
@@ -70,6 +70,7 @@
 #define MAC_INDEX_AUX		4
 #define MAC_INDEX_MIN_DRIVER	0
 #define NUM_MAC_INDEX_DRIVER	MAC_INDEX_AUX
+#define NUM_MAC_INDEX		(MAC_INDEX_AUX + 1)
 
 enum iwl_ac {
 	AC_BK,
@@ -254,7 +255,7 @@
 /**
  * enum iwl_mac_filter_flags - MAC context filter flags
  * @MAC_FILTER_IN_PROMISC: accept all data frames
- * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all mangement and
+ * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all management and
  *	control frames to the host
  * @MAC_FILTER_ACCEPT_GRP: accept multicast frames
  * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
index 4300200..4fc0938 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
@@ -92,14 +92,32 @@
 };
 
 /**
+ * struct iwl_ltr_config_cmd_v1 - configures the LTR
+ * @flags: See %enum iwl_ltr_config_flags
+ */
+struct iwl_ltr_config_cmd_v1 {
+	__le32 flags;
+	__le32 static_long;
+	__le32 static_short;
+} __packed; /* LTR_CAPABLE_API_S_VER_1 */
+
+#define LTR_VALID_STATES_NUM 4
+
+/**
  * struct iwl_ltr_config_cmd - configures the LTR
  * @flags: See %enum iwl_ltr_config_flags
+ * @static_long:
+ * @static_short:
+ * @ltr_cfg_values:
+ * @ltr_short_idle_timeout:
  */
 struct iwl_ltr_config_cmd {
 	__le32 flags;
 	__le32 static_long;
 	__le32 static_short;
-} __packed;
+	__le32 ltr_cfg_values[LTR_VALID_STATES_NUM];
+	__le32 ltr_short_idle_timeout;
+} __packed; /* LTR_CAPABLE_API_S_VER_2 */
 
 /* Radio LP RX Energy Threshold measured in dBm */
 #define POWER_LPRX_RSSI_THRESHOLD	75
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
index 8bb5b94..0f1ea80 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
@@ -308,6 +308,42 @@
 #define LQ_FLAG_DYNAMIC_BW_POS          6
 #define LQ_FLAG_DYNAMIC_BW_MSK          (1 << LQ_FLAG_DYNAMIC_BW_POS)
 
+/* Single Stream Tx Parameters (lq_cmd->ss_params)
+ * Flags to control a smart FW decision about whether BFER/STBC/SISO will be
+ * used for single stream Tx.
+ */
+
+/* Bit 0-1: Max STBC streams allowed. Can be 0-3.
+ * (0) - No STBC allowed
+ * (1) - 2x1 STBC allowed (HT/VHT)
+ * (2) - 4x2 STBC allowed (HT/VHT)
+ * (3) - 3x2 STBC allowed (HT only)
+ * All our chips are at most 2 antennas so only (1) is valid for now.
+ */
+#define LQ_SS_STBC_ALLOWED_POS          0
+#define LQ_SS_STBC_ALLOWED_MSK		(3 << LQ_SS_STBC_ALLOWED_MSK)
+
+/* 2x1 STBC is allowed */
+#define LQ_SS_STBC_1SS_ALLOWED		(1 << LQ_SS_STBC_ALLOWED_POS)
+
+/* Bit 2: Beamformer (VHT only) is allowed */
+#define LQ_SS_BFER_ALLOWED_POS		2
+#define LQ_SS_BFER_ALLOWED		(1 << LQ_SS_BFER_ALLOWED_POS)
+
+/* Bit 3: Force BFER or STBC for testing
+ * If this is set:
+ * If BFER is allowed then force the ucode to choose BFER else
+ * If STBC is allowed then force the ucode to choose STBC over SISO
+ */
+#define LQ_SS_FORCE_POS			3
+#define LQ_SS_FORCE			(1 << LQ_SS_FORCE_POS)
+
+/* Bit 31: ss_params field is valid. Used for FW backward compatibility
+ * with other drivers which don't support the ss_params API yet
+ */
+#define LQ_SS_PARAMS_VALID_POS		31
+#define LQ_SS_PARAMS_VALID		(1 << LQ_SS_PARAMS_VALID_POS)
+
 /**
  * struct iwl_lq_cmd - link quality command
  * @sta_id: station to update
@@ -330,7 +366,7 @@
  *	2 - 0x3f: maximal number of frames (up to 3f == 63)
  * @rs_table: array of rates for each TX try, each is rate_n_flags,
  *	meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP
- * @bf_params: beam forming params, currently not used
+ * @ss_params: single stream features. declare whether STBC or BFER are allowed.
  */
 struct iwl_lq_cmd {
 	u8 sta_id;
@@ -348,6 +384,6 @@
 	u8 agg_frame_cnt_limit;
 	__le32 reserved2;
 	__le32 rs_table[LQ_MAX_RETRY_NUM];
-	__le32 bf_params;
+	__le32 ss_params;
 }; /* LINK_QUALITY_CMD_API_S_VER_1 */
 #endif /* __fw_api_rs_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
index 1f2acf4..4f81dcf 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
@@ -70,55 +70,10 @@
 
 /* Scan Commands, Responses, Notifications */
 
-/* Masks for iwl_scan_channel.type flags */
-#define SCAN_CHANNEL_TYPE_ACTIVE	BIT(0)
-#define SCAN_CHANNEL_NARROW_BAND	BIT(22)
-
 /* Max number of IEs for direct SSID scans in a command */
 #define PROBE_OPTION_MAX		20
 
 /**
- * struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table
- * @channel: band is selected by iwl_scan_cmd "flags" field
- * @tx_gain: gain for analog radio
- * @dsp_atten: gain for DSP
- * @active_dwell: dwell time for active scan in TU, typically 5-50
- * @passive_dwell: dwell time for passive scan in TU, typically 20-500
- * @type: type is broken down to these bits:
- *	bit 0: 0 = passive, 1 = active
- *	bits 1-20: SSID direct bit map. If any of these bits is set then
- *		the corresponding SSID IE is transmitted in probe request
- *		(bit i adds IE in position i to the probe request)
- *	bit 22: channel width, 0 = regular, 1 = TGj narrow channel
- *
- * @iteration_count:
- * @iteration_interval:
- * This struct is used once for each channel in the scan list.
- * Each channel can independently select:
- * 1)  SSID for directed active scans
- * 2)  Txpower setting (for rate specified within Tx command)
- * 3)  How long to stay on-channel (behavior may be modified by quiet_time,
- *     quiet_plcp_th, good_CRC_th)
- *
- * To avoid uCode errors, make sure the following are true (see comments
- * under struct iwl_scan_cmd about max_out_time and quiet_time):
- * 1)  If using passive_dwell (i.e. passive_dwell != 0):
- *     active_dwell <= passive_dwell (< max_out_time if max_out_time != 0)
- * 2)  quiet_time <= active_dwell
- * 3)  If restricting off-channel time (i.e. max_out_time !=0):
- *     passive_dwell < max_out_time
- *     active_dwell < max_out_time
- */
-struct iwl_scan_channel {
-	__le32 type;
-	__le16 channel;
-	__le16 iteration_count;
-	__le32 iteration_interval;
-	__le16 active_dwell;
-	__le16 passive_dwell;
-} __packed; /* SCAN_CHANNEL_CONTROL_API_S_VER_1 */
-
-/**
  * struct iwl_ssid_ie - directed scan network information element
  *
  * Up to 20 of these may appear in REPLY_SCAN_CMD,
@@ -132,152 +87,6 @@
 	u8 ssid[IEEE80211_MAX_SSID_LEN];
 } __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */
 
-/**
- * iwl_scan_flags - masks for scan command flags
- *@SCAN_FLAGS_PERIODIC_SCAN:
- *@SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX:
- *@SCAN_FLAGS_DELAYED_SCAN_LOWBAND:
- *@SCAN_FLAGS_DELAYED_SCAN_HIGHBAND:
- *@SCAN_FLAGS_FRAGMENTED_SCAN:
- *@SCAN_FLAGS_PASSIVE2ACTIVE: use active scan on channels that was active
- *	in the past hour, even if they are marked as passive.
- */
-enum iwl_scan_flags {
-	SCAN_FLAGS_PERIODIC_SCAN		= BIT(0),
-	SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX	= BIT(1),
-	SCAN_FLAGS_DELAYED_SCAN_LOWBAND		= BIT(2),
-	SCAN_FLAGS_DELAYED_SCAN_HIGHBAND	= BIT(3),
-	SCAN_FLAGS_FRAGMENTED_SCAN		= BIT(4),
-	SCAN_FLAGS_PASSIVE2ACTIVE		= BIT(5),
-};
-
-/**
- * enum iwl_scan_type - Scan types for scan command
- * @SCAN_TYPE_FORCED:
- * @SCAN_TYPE_BACKGROUND:
- * @SCAN_TYPE_OS:
- * @SCAN_TYPE_ROAMING:
- * @SCAN_TYPE_ACTION:
- * @SCAN_TYPE_DISCOVERY:
- * @SCAN_TYPE_DISCOVERY_FORCED:
- */
-enum iwl_scan_type {
-	SCAN_TYPE_FORCED		= 0,
-	SCAN_TYPE_BACKGROUND		= 1,
-	SCAN_TYPE_OS			= 2,
-	SCAN_TYPE_ROAMING		= 3,
-	SCAN_TYPE_ACTION		= 4,
-	SCAN_TYPE_DISCOVERY		= 5,
-	SCAN_TYPE_DISCOVERY_FORCED	= 6,
-}; /* SCAN_ACTIVITY_TYPE_E_VER_1 */
-
-/**
- * struct iwl_scan_cmd - scan request command
- * ( SCAN_REQUEST_CMD = 0x80 )
- * @len: command length in bytes
- * @scan_flags: scan flags from SCAN_FLAGS_*
- * @channel_count: num of channels in channel list
- *	(1 - ucode_capa.n_scan_channels)
- * @quiet_time: in msecs, dwell this time for active scan on quiet channels
- * @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than
- *	this number of packets were received (typically 1)
- * @passive2active: is auto switching from passive to active during scan allowed
- * @rxchain_sel_flags: RXON_RX_CHAIN_*
- * @max_out_time: in TUs, max out of serving channel time
- * @suspend_time: how long to pause scan when returning to service channel:
- *	bits 0-19: beacon interal in TUs (suspend before executing)
- *	bits 20-23: reserved
- *	bits 24-31: number of beacons (suspend between channels)
- * @rxon_flags: RXON_FLG_*
- * @filter_flags: RXON_FILTER_*
- * @tx_cmd: for active scans (zero for passive), w/o payload,
- *	no RS so specify TX rate
- * @direct_scan: direct scan SSIDs
- * @type: one of SCAN_TYPE_*
- * @repeats: how many time to repeat the scan
- */
-struct iwl_scan_cmd {
-	__le16 len;
-	u8 scan_flags;
-	u8 channel_count;
-	__le16 quiet_time;
-	__le16 quiet_plcp_th;
-	__le16 passive2active;
-	__le16 rxchain_sel_flags;
-	__le32 max_out_time;
-	__le32 suspend_time;
-	/* RX_ON_FLAGS_API_S_VER_1 */
-	__le32 rxon_flags;
-	__le32 filter_flags;
-	struct iwl_tx_cmd tx_cmd;
-	struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
-	__le32 type;
-	__le32 repeats;
-
-	/*
-	 * Probe request frame, followed by channel list.
-	 *
-	 * Size of probe request frame is specified by byte count in tx_cmd.
-	 * Channel list follows immediately after probe request frame.
-	 * Number of channels in list is specified by channel_count.
-	 * Each channel in list is of type:
-	 *
-	 * struct iwl_scan_channel channels[0];
-	 *
-	 * NOTE:  Only one band of channels can be scanned per pass.  You
-	 * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait
-	 * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION)
-	 * before requesting another scan.
-	 */
-	u8 data[0];
-} __packed; /* SCAN_REQUEST_FIXED_PART_API_S_VER_5 */
-
-/* Response to scan request contains only status with one of these values */
-#define SCAN_RESPONSE_OK	0x1
-#define SCAN_RESPONSE_ERROR	0x2
-
-/*
- * SCAN_ABORT_CMD = 0x81
- * When scan abort is requested, the command has no fields except the common
- * header. The response contains only a status with one of these values.
- */
-#define SCAN_ABORT_POSSIBLE	0x1
-#define SCAN_ABORT_IGNORED	0x2 /* no pending scans */
-
-/* TODO: complete documentation */
-#define  SCAN_OWNER_STATUS 0x1
-#define  MEASURE_OWNER_STATUS 0x2
-
-/**
- * struct iwl_scan_start_notif - notifies start of scan in the device
- * ( SCAN_START_NOTIFICATION = 0x82 )
- * @tsf_low: TSF timer (lower half) in usecs
- * @tsf_high: TSF timer (higher half) in usecs
- * @beacon_timer: structured as follows:
- *	bits 0:19 - beacon interval in usecs
- *	bits 20:23 - reserved (0)
- *	bits 24:31 - number of beacons
- * @channel: which channel is scanned
- * @band: 0 for 5.2 GHz, 1 for 2.4 GHz
- * @status: one of *_OWNER_STATUS
- */
-struct iwl_scan_start_notif {
-	__le32 tsf_low;
-	__le32 tsf_high;
-	__le32 beacon_timer;
-	u8 channel;
-	u8 band;
-	u8 reserved[2];
-	__le32 status;
-} __packed; /* SCAN_START_NTF_API_S_VER_1 */
-
-/* scan results probe_status first bit indicates success */
-#define SCAN_PROBE_STATUS_OK		0
-#define SCAN_PROBE_STATUS_TX_FAILED	BIT(0)
-/* error statuses combined with TX_FAILED */
-#define SCAN_PROBE_STATUS_FAIL_TTL	BIT(1)
-#define SCAN_PROBE_STATUS_FAIL_BT	BIT(2)
-
 /* How many statistics are gathered for each channel */
 #define SCAN_RESULTS_STATISTICS 1
 
@@ -294,7 +103,7 @@
  * @SCAN_COMP_STATUS_ERR_COEX: medium was lost ot WiMax
  * @SCAN_COMP_STATUS_P2P_ACTION_OK: P2P public action frame TX was successful
  *	(not an error!)
- * @SCAN_COMP_STATUS_ITERATION_END: indicates end of one repeatition the driver
+ * @SCAN_COMP_STATUS_ITERATION_END: indicates end of one repetition the driver
  *	asked for
  * @SCAN_COMP_STATUS_ERR_ALLOC_TE: scan could not allocate time events
 */
@@ -378,11 +187,11 @@
  * struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6
  * @scan_flags:		see enum iwl_scan_flags
  * @channel_count:	channels in channel list
- * @quiet_time:		dwell time, in milisiconds, on quiet channel
+ * @quiet_time:		dwell time, in milliseconds, on quiet channel
  * @quiet_plcp_th:	quiet channel num of packets threshold
  * @good_CRC_th:	passive to active promotion threshold
  * @rx_chain:		RXON rx chain.
- * @max_out_time:	max TUs to be out of assoceated channel
+ * @max_out_time:	max TUs to be out of associated channel
  * @suspend_time:	pause scan this TUs when returning to service channel
  * @flags:		RXON flags
  * @filter_flags:	RXONfilter
@@ -423,7 +232,7 @@
  *	see enum iwl_scan_offload_channel_flags.
  * __le16 channel_number: channel number 1-13 etc.
  * __le16 iter_count: repetition count for the channel.
- * __le32 iter_interval: interval between two innteration on one channel.
+ * __le32 iter_interval: interval between two iterations on one channel.
  * u8 active_dwell.
  * u8 passive_dwell.
  */
@@ -466,8 +275,8 @@
 /**
  * iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S
  * @ssid_index:		index to ssid list in fixed part
- * @unicast_cipher:	encryption olgorithm to match - bitmap
- * @aut_alg:		authentication olgorithm to match - bitmap
+ * @unicast_cipher:	encryption algorithm to match - bitmap
+ * @aut_alg:		authentication algorithm to match - bitmap
  * @network_type:	enum iwl_scan_offload_network_type
  * @band_selection:	enum iwl_scan_offload_band_selection
  * @client_bitmap:	clients waiting for match - enum scan_framework_client
@@ -653,8 +462,11 @@
 };
 
 /* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S
- * @flags: enum iwl_scan_channel_flgs
- * @non_ebs_ratio: how many regular scan iteration before EBS
+ * @flags: enum iwl_scan_channel_flags
+ * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is
+ *	involved.
+ *	1 - EBS is disabled.
+ *	2 - every second scan will be full scan(and so on).
  */
 struct iwl_scan_channel_opt {
 	__le16 flags;
@@ -672,6 +484,7 @@
  * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented
  * @IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report
  *	and DS parameter set IEs into probe requests.
+ * @IWL_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches
  */
 enum iwl_mvm_lmac_scan_flags {
 	IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL		= BIT(0),
@@ -681,6 +494,7 @@
 	IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS	= BIT(4),
 	IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED	= BIT(5),
 	IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED	= BIT(6),
+	IWL_MVM_LMAC_SCAN_FLAG_MATCH		= BIT(9),
 };
 
 enum iwl_scan_priority {
@@ -934,7 +748,7 @@
  * @flags:		bitmap - 0-19:	directed scan to i'th ssid.
  * @channel_num:	channel number 1-13 etc.
  * @iter_count:		repetition count for the channel.
- * @iter_interval:	interval between two scan interations on one channel.
+ * @iter_interval:	interval between two scan iterations on one channel.
  */
 struct iwl_scan_channel_cfg_umac {
 	__le32 flags;
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h
new file mode 100644
index 0000000..709e28d
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h
@@ -0,0 +1,317 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_stats_h__
+#define __fw_api_stats_h__
+#include "fw-api-mac.h"
+
+struct mvm_statistics_dbg {
+	__le32 burst_check;
+	__le32 burst_count;
+	__le32 wait_for_silence_timeout_cnt;
+	__le32 reserved[3];
+} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */
+
+struct mvm_statistics_div {
+	__le32 tx_on_a;
+	__le32 tx_on_b;
+	__le32 exec_time;
+	__le32 probe_time;
+	__le32 rssi_ant;
+	__le32 reserved2;
+} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */
+
+struct mvm_statistics_rx_non_phy {
+	__le32 bogus_cts;	/* CTS received when not expecting CTS */
+	__le32 bogus_ack;	/* ACK received when not expecting ACK */
+	__le32 non_bssid_frames;	/* number of frames with BSSID that
+					 * doesn't belong to the STA BSSID */
+	__le32 filtered_frames;	/* count frames that were dumped in the
+				 * filtering process */
+	__le32 non_channel_beacons;	/* beacons with our bss id but not on
+					 * our serving channel */
+	__le32 channel_beacons;	/* beacons with our bss id and in our
+				 * serving channel */
+	__le32 num_missed_bcon;	/* number of missed beacons */
+	__le32 adc_rx_saturation_time;	/* count in 0.8us units the time the
+					 * ADC was in saturation */
+	__le32 ina_detection_search_time;/* total time (in 0.8us) searched
+					  * for INA */
+	__le32 beacon_silence_rssi_a;	/* RSSI silence after beacon frame */
+	__le32 beacon_silence_rssi_b;	/* RSSI silence after beacon frame */
+	__le32 beacon_silence_rssi_c;	/* RSSI silence after beacon frame */
+	__le32 interference_data_flag;	/* flag for interference data
+					 * availability. 1 when data is
+					 * available. */
+	__le32 channel_load;		/* counts RX Enable time in uSec */
+	__le32 dsp_false_alarms;	/* DSP false alarm (both OFDM
+					 * and CCK) counter */
+	__le32 beacon_rssi_a;
+	__le32 beacon_rssi_b;
+	__le32 beacon_rssi_c;
+	__le32 beacon_energy_a;
+	__le32 beacon_energy_b;
+	__le32 beacon_energy_c;
+	__le32 num_bt_kills;
+	__le32 mac_id;
+	__le32 directed_data_mpdu;
+} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */
+
+struct mvm_statistics_rx_phy {
+	__le32 ina_cnt;
+	__le32 fina_cnt;
+	__le32 plcp_err;
+	__le32 crc32_err;
+	__le32 overrun_err;
+	__le32 early_overrun_err;
+	__le32 crc32_good;
+	__le32 false_alarm_cnt;
+	__le32 fina_sync_err_cnt;
+	__le32 sfd_timeout;
+	__le32 fina_timeout;
+	__le32 unresponded_rts;
+	__le32 rxe_frame_lmt_overrun;
+	__le32 sent_ack_cnt;
+	__le32 sent_cts_cnt;
+	__le32 sent_ba_rsp_cnt;
+	__le32 dsp_self_kill;
+	__le32 mh_format_err;
+	__le32 re_acq_main_rssi_sum;
+	__le32 reserved;
+} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */
+
+struct mvm_statistics_rx_ht_phy {
+	__le32 plcp_err;
+	__le32 overrun_err;
+	__le32 early_overrun_err;
+	__le32 crc32_good;
+	__le32 crc32_err;
+	__le32 mh_format_err;
+	__le32 agg_crc32_good;
+	__le32 agg_mpdu_cnt;
+	__le32 agg_cnt;
+	__le32 unsupport_mcs;
+} __packed;  /* STATISTICS_HT_RX_PHY_API_S_VER_1 */
+
+struct mvm_statistics_tx_non_phy {
+	__le32 preamble_cnt;
+	__le32 rx_detected_cnt;
+	__le32 bt_prio_defer_cnt;
+	__le32 bt_prio_kill_cnt;
+	__le32 few_bytes_cnt;
+	__le32 cts_timeout;
+	__le32 ack_timeout;
+	__le32 expected_ack_cnt;
+	__le32 actual_ack_cnt;
+	__le32 dump_msdu_cnt;
+	__le32 burst_abort_next_frame_mismatch_cnt;
+	__le32 burst_abort_missing_next_frame_cnt;
+	__le32 cts_timeout_collision;
+	__le32 ack_or_ba_timeout_collision;
+} __packed; /* STATISTICS_TX_NON_PHY_API_S_VER_3 */
+
+#define MAX_CHAINS 3
+
+struct mvm_statistics_tx_non_phy_agg {
+	__le32 ba_timeout;
+	__le32 ba_reschedule_frames;
+	__le32 scd_query_agg_frame_cnt;
+	__le32 scd_query_no_agg;
+	__le32 scd_query_agg;
+	__le32 scd_query_mismatch;
+	__le32 frame_not_ready;
+	__le32 underrun;
+	__le32 bt_prio_kill;
+	__le32 rx_ba_rsp_cnt;
+	__s8 txpower[MAX_CHAINS];
+	__s8 reserved;
+	__le32 reserved2;
+} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */
+
+struct mvm_statistics_tx_channel_width {
+	__le32 ext_cca_narrow_ch20[1];
+	__le32 ext_cca_narrow_ch40[2];
+	__le32 ext_cca_narrow_ch80[3];
+	__le32 ext_cca_narrow_ch160[4];
+	__le32 last_tx_ch_width_indx;
+	__le32 rx_detected_per_ch_width[4];
+	__le32 success_per_ch_width[4];
+	__le32 fail_per_ch_width[4];
+}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */
+
+struct mvm_statistics_tx {
+	struct mvm_statistics_tx_non_phy general;
+	struct mvm_statistics_tx_non_phy_agg agg;
+	struct mvm_statistics_tx_channel_width channel_width;
+} __packed; /* STATISTICS_TX_API_S_VER_4 */
+
+
+struct mvm_statistics_bt_activity {
+	__le32 hi_priority_tx_req_cnt;
+	__le32 hi_priority_tx_denied_cnt;
+	__le32 lo_priority_tx_req_cnt;
+	__le32 lo_priority_tx_denied_cnt;
+	__le32 hi_priority_rx_req_cnt;
+	__le32 hi_priority_rx_denied_cnt;
+	__le32 lo_priority_rx_req_cnt;
+	__le32 lo_priority_rx_denied_cnt;
+} __packed;  /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */
+
+struct mvm_statistics_general_v5 {
+	__le32 radio_temperature;
+	__le32 radio_voltage;
+	struct mvm_statistics_dbg dbg;
+	__le32 sleep_time;
+	__le32 slots_out;
+	__le32 slots_idle;
+	__le32 ttl_timestamp;
+	struct mvm_statistics_div slow_div;
+	__le32 rx_enable_counter;
+	/*
+	 * num_of_sos_states:
+	 *  count the number of times we have to re-tune
+	 *  in order to get out of bad PHY status
+	 */
+	__le32 num_of_sos_states;
+	__le32 beacon_filtered;
+	__le32 missed_beacons;
+	__s8 beacon_filter_average_energy;
+	__s8 beacon_filter_reason;
+	__s8 beacon_filter_current_energy;
+	__s8 beacon_filter_reserved;
+	__le32 beacon_filter_delta_time;
+	struct mvm_statistics_bt_activity bt_activity;
+} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */
+
+struct mvm_statistics_general_v8 {
+	__le32 radio_temperature;
+	__le32 radio_voltage;
+	struct mvm_statistics_dbg dbg;
+	__le32 sleep_time;
+	__le32 slots_out;
+	__le32 slots_idle;
+	__le32 ttl_timestamp;
+	struct mvm_statistics_div slow_div;
+	__le32 rx_enable_counter;
+	/*
+	 * num_of_sos_states:
+	 *  count the number of times we have to re-tune
+	 *  in order to get out of bad PHY status
+	 */
+	__le32 num_of_sos_states;
+	__le32 beacon_filtered;
+	__le32 missed_beacons;
+	__s8 beacon_filter_average_energy;
+	__s8 beacon_filter_reason;
+	__s8 beacon_filter_current_energy;
+	__s8 beacon_filter_reserved;
+	__le32 beacon_filter_delta_time;
+	struct mvm_statistics_bt_activity bt_activity;
+	__le64 rx_time;
+	__le64 on_time_rf;
+	__le64 on_time_scan;
+	__le64 tx_time;
+	__le32 beacon_counter[NUM_MAC_INDEX];
+	u8 beacon_average_energy[NUM_MAC_INDEX];
+	u8 reserved[4 - (NUM_MAC_INDEX % 4)];
+} __packed; /* STATISTICS_GENERAL_API_S_VER_8 */
+
+struct mvm_statistics_rx {
+	struct mvm_statistics_rx_phy ofdm;
+	struct mvm_statistics_rx_phy cck;
+	struct mvm_statistics_rx_non_phy general;
+	struct mvm_statistics_rx_ht_phy ofdm_ht;
+} __packed; /* STATISTICS_RX_API_S_VER_3 */
+
+/*
+ * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command)
+ *
+ * By default, uCode issues this notification after receiving a beacon
+ * while associated.  To disable this behavior, set DISABLE_NOTIF flag in the
+ * STATISTICS_CMD (0x9c), below.
+ */
+
+struct iwl_notif_statistics_v8 {
+	__le32 flag;
+	struct mvm_statistics_rx rx;
+	struct mvm_statistics_tx tx;
+	struct mvm_statistics_general_v5 general;
+} __packed; /* STATISTICS_NTFY_API_S_VER_8 */
+
+struct iwl_notif_statistics_v10 {
+	__le32 flag;
+	struct mvm_statistics_rx rx;
+	struct mvm_statistics_tx tx;
+	struct mvm_statistics_general_v8 general;
+} __packed; /* STATISTICS_NTFY_API_S_VER_10 */
+
+#define IWL_STATISTICS_FLG_CLEAR		0x1
+#define IWL_STATISTICS_FLG_DISABLE_NOTIF	0x2
+
+struct iwl_statistics_cmd {
+	__le32 flags;
+} __packed; /* STATISTICS_CMD_API_S_VER_1 */
+
+#endif /* __fw_api_stats_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
index 5bca1f8..81c4ea3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
@@ -592,4 +592,43 @@
 			    tx_resp->frame_count) & 0xfff;
 }
 
+/**
+ * struct iwl_scd_txq_cfg_cmd - New txq hw scheduler config command
+ * @token:
+ * @sta_id: station id
+ * @tid:
+ * @scd_queue: scheduler queue to confiug
+ * @enable: 1 queue enable, 0 queue disable
+ * @aggregate: 1 aggregated queue, 0 otherwise
+ * @tx_fifo: %enum iwl_mvm_tx_fifo
+ * @window: BA window size
+ * @ssn: SSN for the BA agreement
+ */
+struct iwl_scd_txq_cfg_cmd {
+	u8 token;
+	u8 sta_id;
+	u8 tid;
+	u8 scd_queue;
+	u8 enable;
+	u8 aggregate;
+	u8 tx_fifo;
+	u8 window;
+	__le16 ssn;
+	__le16 reserved;
+} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */
+
+/**
+ * struct iwl_scd_txq_cfg_rsp
+ * @token: taken from the command
+ * @sta_id: station id from the command
+ * @tid: tid from the command
+ * @scd_queue: scd_queue from the command
+ */
+struct iwl_scd_txq_cfg_rsp {
+	u8 token;
+	u8 sta_id;
+	u8 tid;
+	u8 scd_queue;
+} __packed; /* SCD_QUEUE_CFG_RSP_API_S_VER_1 */
+
 #endif /* __fw_api_tx_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index 88af6dd..aab68cb 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -74,6 +74,7 @@
 #include "fw-api-d3.h"
 #include "fw-api-coex.h"
 #include "fw-api-scan.h"
+#include "fw-api-stats.h"
 
 /* Tx queue numbers */
 enum {
@@ -128,6 +129,9 @@
 	/* global key */
 	WEP_KEY = 0x20,
 
+	/* Memory */
+	SHARED_MEM_CFG = 0x25,
+
 	/* TDLS */
 	TDLS_CHANNEL_SWITCH_CMD = 0x27,
 	TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa,
@@ -188,6 +192,7 @@
 	BEACON_NOTIFICATION = 0x90,
 	BEACON_TEMPLATE_CMD = 0x91,
 	TX_ANT_CONFIGURATION_CMD = 0x98,
+	STATISTICS_CMD = 0x9c,
 	STATISTICS_NOTIFICATION = 0x9d,
 	EOSP_NOTIFICATION = 0x9e,
 	REDUCE_TX_POWER_CMD = 0x9f,
@@ -207,6 +212,10 @@
 	REPLY_RX_MPDU_CMD = 0xc1,
 	BA_NOTIF = 0xc5,
 
+	/* Location Aware Regulatory */
+	MCC_UPDATE_CMD = 0xc8,
+	MCC_CHUB_UPDATE_CMD = 0xc9,
+
 	MARKER_CMD = 0xcb,
 
 	/* BT Coex */
@@ -357,7 +366,8 @@
 	NVM_SECTION_TYPE_CALIBRATION = 4,
 	NVM_SECTION_TYPE_PRODUCTION = 5,
 	NVM_SECTION_TYPE_MAC_OVERRIDE = 11,
-	NVM_MAX_NUM_SECTIONS = 12,
+	NVM_SECTION_TYPE_PHY_SKU = 12,
+	NVM_MAX_NUM_SECTIONS = 13,
 };
 
 /**
@@ -427,7 +437,7 @@
 
 #define IWL_ALIVE_FLG_RFKILL	BIT(0)
 
-struct mvm_alive_resp {
+struct mvm_alive_resp_ver1 {
 	__le16 status;
 	__le16 flags;
 	u8 ucode_minor;
@@ -478,6 +488,30 @@
 	__le32 dbg_print_buff_addr;
 } __packed; /* ALIVE_RES_API_S_VER_2 */
 
+struct mvm_alive_resp {
+	__le16 status;
+	__le16 flags;
+	__le32 ucode_minor;
+	__le32 ucode_major;
+	u8 ver_subtype;
+	u8 ver_type;
+	u8 mac;
+	u8 opt;
+	__le32 timestamp;
+	__le32 error_event_table_ptr;	/* SRAM address for error log */
+	__le32 log_event_table_ptr;	/* SRAM address for LMAC event log */
+	__le32 cpu_register_ptr;
+	__le32 dbgm_config_ptr;
+	__le32 alive_counter_ptr;
+	__le32 scd_base_ptr;		/* SRAM address for SCD */
+	__le32 st_fwrd_addr;		/* pointer to Store and forward */
+	__le32 st_fwrd_size;
+	__le32 umac_minor;		/* UMAC version: minor */
+	__le32 umac_major;		/* UMAC version: major */
+	__le32 error_info_addr;		/* SRAM address for UMAC error log */
+	__le32 dbg_print_buff_addr;
+} __packed; /* ALIVE_RES_API_S_VER_3 */
+
 /* Error response/notification */
 enum {
 	FW_ERR_UNKNOWN_CMD = 0x0,
@@ -1381,214 +1415,6 @@
 	__le32 metadata[0];
 } __packed; /* MARKER_API_S_VER_1 */
 
-struct mvm_statistics_dbg {
-	__le32 burst_check;
-	__le32 burst_count;
-	__le32 wait_for_silence_timeout_cnt;
-	__le32 reserved[3];
-} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */
-
-struct mvm_statistics_div {
-	__le32 tx_on_a;
-	__le32 tx_on_b;
-	__le32 exec_time;
-	__le32 probe_time;
-	__le32 rssi_ant;
-	__le32 reserved2;
-} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */
-
-struct mvm_statistics_general_common {
-	__le32 temperature;   /* radio temperature */
-	__le32 temperature_m; /* radio voltage */
-	struct mvm_statistics_dbg dbg;
-	__le32 sleep_time;
-	__le32 slots_out;
-	__le32 slots_idle;
-	__le32 ttl_timestamp;
-	struct mvm_statistics_div div;
-	__le32 rx_enable_counter;
-	/*
-	 * num_of_sos_states:
-	 *  count the number of times we have to re-tune
-	 *  in order to get out of bad PHY status
-	 */
-	__le32 num_of_sos_states;
-} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */
-
-struct mvm_statistics_rx_non_phy {
-	__le32 bogus_cts;	/* CTS received when not expecting CTS */
-	__le32 bogus_ack;	/* ACK received when not expecting ACK */
-	__le32 non_bssid_frames;	/* number of frames with BSSID that
-					 * doesn't belong to the STA BSSID */
-	__le32 filtered_frames;	/* count frames that were dumped in the
-				 * filtering process */
-	__le32 non_channel_beacons;	/* beacons with our bss id but not on
-					 * our serving channel */
-	__le32 channel_beacons;	/* beacons with our bss id and in our
-				 * serving channel */
-	__le32 num_missed_bcon;	/* number of missed beacons */
-	__le32 adc_rx_saturation_time;	/* count in 0.8us units the time the
-					 * ADC was in saturation */
-	__le32 ina_detection_search_time;/* total time (in 0.8us) searched
-					  * for INA */
-	__le32 beacon_silence_rssi_a;	/* RSSI silence after beacon frame */
-	__le32 beacon_silence_rssi_b;	/* RSSI silence after beacon frame */
-	__le32 beacon_silence_rssi_c;	/* RSSI silence after beacon frame */
-	__le32 interference_data_flag;	/* flag for interference data
-					 * availability. 1 when data is
-					 * available. */
-	__le32 channel_load;		/* counts RX Enable time in uSec */
-	__le32 dsp_false_alarms;	/* DSP false alarm (both OFDM
-					 * and CCK) counter */
-	__le32 beacon_rssi_a;
-	__le32 beacon_rssi_b;
-	__le32 beacon_rssi_c;
-	__le32 beacon_energy_a;
-	__le32 beacon_energy_b;
-	__le32 beacon_energy_c;
-	__le32 num_bt_kills;
-	__le32 mac_id;
-	__le32 directed_data_mpdu;
-} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */
-
-struct mvm_statistics_rx_phy {
-	__le32 ina_cnt;
-	__le32 fina_cnt;
-	__le32 plcp_err;
-	__le32 crc32_err;
-	__le32 overrun_err;
-	__le32 early_overrun_err;
-	__le32 crc32_good;
-	__le32 false_alarm_cnt;
-	__le32 fina_sync_err_cnt;
-	__le32 sfd_timeout;
-	__le32 fina_timeout;
-	__le32 unresponded_rts;
-	__le32 rxe_frame_limit_overrun;
-	__le32 sent_ack_cnt;
-	__le32 sent_cts_cnt;
-	__le32 sent_ba_rsp_cnt;
-	__le32 dsp_self_kill;
-	__le32 mh_format_err;
-	__le32 re_acq_main_rssi_sum;
-	__le32 reserved;
-} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */
-
-struct mvm_statistics_rx_ht_phy {
-	__le32 plcp_err;
-	__le32 overrun_err;
-	__le32 early_overrun_err;
-	__le32 crc32_good;
-	__le32 crc32_err;
-	__le32 mh_format_err;
-	__le32 agg_crc32_good;
-	__le32 agg_mpdu_cnt;
-	__le32 agg_cnt;
-	__le32 unsupport_mcs;
-} __packed;  /* STATISTICS_HT_RX_PHY_API_S_VER_1 */
-
-#define MAX_CHAINS 3
-
-struct mvm_statistics_tx_non_phy_agg {
-	__le32 ba_timeout;
-	__le32 ba_reschedule_frames;
-	__le32 scd_query_agg_frame_cnt;
-	__le32 scd_query_no_agg;
-	__le32 scd_query_agg;
-	__le32 scd_query_mismatch;
-	__le32 frame_not_ready;
-	__le32 underrun;
-	__le32 bt_prio_kill;
-	__le32 rx_ba_rsp_cnt;
-	__s8 txpower[MAX_CHAINS];
-	__s8 reserved;
-	__le32 reserved2;
-} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */
-
-struct mvm_statistics_tx_channel_width {
-	__le32 ext_cca_narrow_ch20[1];
-	__le32 ext_cca_narrow_ch40[2];
-	__le32 ext_cca_narrow_ch80[3];
-	__le32 ext_cca_narrow_ch160[4];
-	__le32 last_tx_ch_width_indx;
-	__le32 rx_detected_per_ch_width[4];
-	__le32 success_per_ch_width[4];
-	__le32 fail_per_ch_width[4];
-}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */
-
-struct mvm_statistics_tx {
-	__le32 preamble_cnt;
-	__le32 rx_detected_cnt;
-	__le32 bt_prio_defer_cnt;
-	__le32 bt_prio_kill_cnt;
-	__le32 few_bytes_cnt;
-	__le32 cts_timeout;
-	__le32 ack_timeout;
-	__le32 expected_ack_cnt;
-	__le32 actual_ack_cnt;
-	__le32 dump_msdu_cnt;
-	__le32 burst_abort_next_frame_mismatch_cnt;
-	__le32 burst_abort_missing_next_frame_cnt;
-	__le32 cts_timeout_collision;
-	__le32 ack_or_ba_timeout_collision;
-	struct mvm_statistics_tx_non_phy_agg agg;
-	struct mvm_statistics_tx_channel_width channel_width;
-} __packed; /* STATISTICS_TX_API_S_VER_4 */
-
-
-struct mvm_statistics_bt_activity {
-	__le32 hi_priority_tx_req_cnt;
-	__le32 hi_priority_tx_denied_cnt;
-	__le32 lo_priority_tx_req_cnt;
-	__le32 lo_priority_tx_denied_cnt;
-	__le32 hi_priority_rx_req_cnt;
-	__le32 hi_priority_rx_denied_cnt;
-	__le32 lo_priority_rx_req_cnt;
-	__le32 lo_priority_rx_denied_cnt;
-} __packed;  /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */
-
-struct mvm_statistics_general {
-	struct mvm_statistics_general_common common;
-	__le32 beacon_filtered;
-	__le32 missed_beacons;
-	__s8 beacon_filter_average_energy;
-	__s8 beacon_filter_reason;
-	__s8 beacon_filter_current_energy;
-	__s8 beacon_filter_reserved;
-	__le32 beacon_filter_delta_time;
-	struct mvm_statistics_bt_activity bt_activity;
-} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */
-
-struct mvm_statistics_rx {
-	struct mvm_statistics_rx_phy ofdm;
-	struct mvm_statistics_rx_phy cck;
-	struct mvm_statistics_rx_non_phy general;
-	struct mvm_statistics_rx_ht_phy ofdm_ht;
-} __packed; /* STATISTICS_RX_API_S_VER_3 */
-
-/*
- * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command)
- *
- * By default, uCode issues this notification after receiving a beacon
- * while associated.  To disable this behavior, set DISABLE_NOTIF flag in the
- * REPLY_STATISTICS_CMD 0x9c, above.
- *
- * Statistics counters continue to increment beacon after beacon, but are
- * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD
- * 0x9c with CLEAR_STATS bit set (see above).
- *
- * uCode also issues this notification during scans.  uCode clears statistics
- * appropriately so that each notification contains statistics for only the
- * one channel that has just been scanned.
- */
-
-struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */
-	__le32 flag;
-	struct mvm_statistics_rx rx;
-	struct mvm_statistics_tx tx;
-	struct mvm_statistics_general general;
-} __packed;
-
 /***********************************
  * Smart Fifo API
  ***********************************/
@@ -1621,7 +1447,19 @@
 #define SF_W_MARK_LEGACY 4096
 #define SF_W_MARK_SCAN 4096
 
-/* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */
+/* SF Scenarios timers for default configuration (aligned to 32 uSec) */
+#define SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160	/* 150 uSec  */
+#define SF_SINGLE_UNICAST_AGING_TIMER_DEF 400	/* 0.4 mSec */
+#define SF_AGG_UNICAST_IDLE_TIMER_DEF 160		/* 150 uSec */
+#define SF_AGG_UNICAST_AGING_TIMER_DEF 400		/* 0.4 mSec */
+#define SF_MCAST_IDLE_TIMER_DEF 160		/* 150 mSec */
+#define SF_MCAST_AGING_TIMER_DEF 400		/* 0.4 mSec */
+#define SF_BA_IDLE_TIMER_DEF 160			/* 150 uSec */
+#define SF_BA_AGING_TIMER_DEF 400			/* 0.4 mSec */
+#define SF_TX_RE_IDLE_TIMER_DEF 160			/* 150 uSec */
+#define SF_TX_RE_AGING_TIMER_DEF 400		/* 0.4 mSec */
+
+/* SF Scenarios timers for BSS MAC configuration (aligned to 32 uSec) */
 #define SF_SINGLE_UNICAST_IDLE_TIMER 320	/* 300 uSec  */
 #define SF_SINGLE_UNICAST_AGING_TIMER 2016	/* 2 mSec */
 #define SF_AGG_UNICAST_IDLE_TIMER 320		/* 300 uSec */
@@ -1652,6 +1490,92 @@
 	__le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
 } __packed; /* SF_CFG_API_S_VER_2 */
 
+/***********************************
+ * Location Aware Regulatory (LAR) API - MCC updates
+ ***********************************/
+
+/**
+ * struct iwl_mcc_update_cmd - Request the device to update geographic
+ * regulatory profile according to the given MCC (Mobile Country Code).
+ * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
+ * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the
+ * MCC in the cmd response will be the relevant MCC in the NVM.
+ * @mcc: given mobile country code
+ * @source_id: the source from where we got the MCC, see iwl_mcc_source
+ * @reserved: reserved for alignment
+ */
+struct iwl_mcc_update_cmd {
+	__le16 mcc;
+	u8 source_id;
+	u8 reserved;
+} __packed; /* LAR_UPDATE_MCC_CMD_API_S */
+
+/**
+ * iwl_mcc_update_resp - response to MCC_UPDATE_CMD.
+ * Contains the new channel control profile map, if changed, and the new MCC
+ * (mobile country code).
+ * The new MCC may be different than what was requested in MCC_UPDATE_CMD.
+ * @status: see &enum iwl_mcc_update_status
+ * @mcc: the new applied MCC
+ * @cap: capabilities for all channels which matches the MCC
+ * @source_id: the MCC source, see iwl_mcc_source
+ * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51
+ *		channels, depending on platform)
+ * @channels: channel control data map, DWORD for each channel. Only the first
+ *	16bits are used.
+ */
+struct iwl_mcc_update_resp {
+	__le32 status;
+	__le16 mcc;
+	u8 cap;
+	u8 source_id;
+	__le32 n_channels;
+	__le32 channels[0];
+} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S */
+
+/**
+ * struct iwl_mcc_chub_notif - chub notifies of mcc change
+ * (MCC_CHUB_UPDATE_CMD = 0xc9)
+ * The Chub (Communication Hub, CommsHUB) is a HW component that connects to
+ * the cellular and connectivity cores that gets updates of the mcc, and
+ * notifies the ucode directly of any mcc change.
+ * The ucode requests the driver to request the device to update geographic
+ * regulatory  profile according to the given MCC (Mobile Country Code).
+ * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
+ * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the
+ * MCC in the cmd response will be the relevant MCC in the NVM.
+ * @mcc: given mobile country code
+ * @source_id: identity of the change originator, see iwl_mcc_source
+ * @reserved1: reserved for alignment
+ */
+struct iwl_mcc_chub_notif {
+	u16 mcc;
+	u8 source_id;
+	u8 reserved1;
+} __packed; /* LAR_MCC_NOTIFY_S */
+
+enum iwl_mcc_update_status {
+	MCC_RESP_NEW_CHAN_PROFILE,
+	MCC_RESP_SAME_CHAN_PROFILE,
+	MCC_RESP_INVALID,
+	MCC_RESP_NVM_DISABLED,
+	MCC_RESP_ILLEGAL,
+	MCC_RESP_LOW_PRIORITY,
+};
+
+enum iwl_mcc_source {
+	MCC_SOURCE_OLD_FW = 0,
+	MCC_SOURCE_ME = 1,
+	MCC_SOURCE_BIOS = 2,
+	MCC_SOURCE_3G_LTE_HOST = 3,
+	MCC_SOURCE_3G_LTE_DEVICE = 4,
+	MCC_SOURCE_WIFI = 5,
+	MCC_SOURCE_RESERVED = 6,
+	MCC_SOURCE_DEFAULT = 7,
+	MCC_SOURCE_UNINITIALIZED = 8,
+	MCC_SOURCE_GET_CURRENT = 0x10
+};
+
 /* DTS measurements */
 
 enum iwl_dts_measurement_flags {
@@ -1680,63 +1604,6 @@
 	__le32 voltage;
 } __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S */
 
-/**
- * enum iwl_scd_control - scheduler config command control flags
- * @IWL_SCD_CONTROL_RM_TID: remove TID from this queue
- * @IWL_SCD_CONTROL_SET_SSN: use the SSN and program it into HW
- */
-enum iwl_scd_control {
-	IWL_SCD_CONTROL_RM_TID	= BIT(4),
-	IWL_SCD_CONTROL_SET_SSN	= BIT(5),
-};
-
-/**
- * enum iwl_scd_flags - scheduler config command flags
- * @IWL_SCD_FLAGS_SHARE_TID: multiple TIDs map to this queue
- * @IWL_SCD_FLAGS_SHARE_RA: multiple RAs map to this queue
- * @IWL_SCD_FLAGS_DQA_ENABLED: DQA is enabled
- */
-enum iwl_scd_flags {
-	IWL_SCD_FLAGS_SHARE_TID		= BIT(0),
-	IWL_SCD_FLAGS_SHARE_RA		= BIT(1),
-	IWL_SCD_FLAGS_DQA_ENABLED	= BIT(2),
-};
-
-#define IWL_SCDQ_INVALID_STA	0xff
-
-/**
- * struct iwl_scd_txq_cfg_cmd - New txq hw scheduler config command
- * @token:	dialog token addba - unused legacy
- * @sta_id:	station id 4-bit
- * @tid:	TID 0..7
- * @scd_queue:	TFD queue num 0 .. 31
- * @enable:	1 queue enable, 0 queue disable
- * @aggregate:	1 aggregated queue, 0 otherwise
- * @tx_fifo:	tx fifo num 0..7
- * @window:	up to 64
- * @ssn:	starting seq num 12-bit
- * @control:	command control flags
- * @flags:	flags - see &enum iwl_scd_flags
- *
- * Note that every time the command is sent, all parameters must
- * be filled with the exception of
- *  - the SSN, which is only used with @IWL_SCD_CONTROL_SET_SSN
- *  - the window, which is only relevant when starting aggregation
- */
-struct iwl_scd_txq_cfg_cmd {
-	u8 token;
-	u8 sta_id;
-	u8 tid;
-	u8 scd_queue;
-	u8 enable;
-	u8 aggregate;
-	u8 tx_fifo;
-	u8 window;
-	__le16 ssn;
-	u8 control;
-	u8 flags;
-} __packed;
-
 /***********************************
  * TDLS API
  ***********************************/
@@ -1878,4 +1745,36 @@
 	struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT];
 } __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */
 
+#define TX_FIFO_MAX_NUM		8
+#define RX_FIFO_MAX_NUM		2
+
+/**
+ * Shared memory configuration information from the FW
+ *
+ * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not
+ *	accessible)
+ * @shared_mem_size: shared memory size
+ * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to
+ *	0x0 as accessible only via DBGM RDAT)
+ * @sample_buff_size: internal sample buff size
+ * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre
+ *	8000 HW set to 0x0 as not accessible)
+ * @txfifo_size: size of TXF0 ... TXF7
+ * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0
+ * @page_buff_addr: used by UMAC and performance debug (page miss analysis),
+ *	when paging is not supported this should be 0
+ * @page_buff_size: size of %page_buff_addr
+ */
+struct iwl_shared_mem_cfg {
+	__le32 shared_mem_addr;
+	__le32 shared_mem_size;
+	__le32 sample_buff_addr;
+	__le32 sample_buff_size;
+	__le32 txfifo_addr;
+	__le32 txfifo_size[TX_FIFO_MAX_NUM];
+	__le32 rxfifo_size[RX_FIFO_MAX_NUM];
+	__le32 page_buff_addr;
+	__le32 page_buff_size;
+} __packed; /* SHARED_MEM_ALLOC_API_S_VER_1 */
+
 #endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index d0fa6e9..bc5eac4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -70,6 +70,7 @@
 #include "iwl-debug.h"
 #include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */
 #include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */
+#include "iwl-prph.h"
 #include "iwl-eeprom-parse.h"
 
 #include "mvm.h"
@@ -111,25 +112,27 @@
 	struct iwl_mvm *mvm =
 		container_of(notif_wait, struct iwl_mvm, notif_wait);
 	struct iwl_mvm_alive_data *alive_data = data;
-	struct mvm_alive_resp *palive;
+	struct mvm_alive_resp_ver1 *palive1;
 	struct mvm_alive_resp_ver2 *palive2;
+	struct mvm_alive_resp *palive;
 
-	if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) {
-		palive = (void *)pkt->data;
+	if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive1)) {
+		palive1 = (void *)pkt->data;
 
 		mvm->support_umac_log = false;
 		mvm->error_event_table =
-			le32_to_cpu(palive->error_event_table_ptr);
-		mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr);
-		alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
+			le32_to_cpu(palive1->error_event_table_ptr);
+		mvm->log_event_table =
+			le32_to_cpu(palive1->log_event_table_ptr);
+		alive_data->scd_base_addr = le32_to_cpu(palive1->scd_base_ptr);
 
-		alive_data->valid = le16_to_cpu(palive->status) ==
+		alive_data->valid = le16_to_cpu(palive1->status) ==
 				    IWL_ALIVE_STATUS_OK;
 		IWL_DEBUG_FW(mvm,
 			     "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
-			     le16_to_cpu(palive->status), palive->ver_type,
-			     palive->ver_subtype, palive->flags);
-	} else {
+			     le16_to_cpu(palive1->status), palive1->ver_type,
+			     palive1->ver_subtype, palive1->flags);
+	} else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive2)) {
 		palive2 = (void *)pkt->data;
 
 		mvm->error_event_table =
@@ -155,6 +158,33 @@
 		IWL_DEBUG_FW(mvm,
 			     "UMAC version: Major - 0x%x, Minor - 0x%x\n",
 			     palive2->umac_major, palive2->umac_minor);
+	} else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) {
+		palive = (void *)pkt->data;
+
+		mvm->error_event_table =
+			le32_to_cpu(palive->error_event_table_ptr);
+		mvm->log_event_table =
+			le32_to_cpu(palive->log_event_table_ptr);
+		alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
+		mvm->umac_error_event_table =
+			le32_to_cpu(palive->error_info_addr);
+		mvm->sf_space.addr = le32_to_cpu(palive->st_fwrd_addr);
+		mvm->sf_space.size = le32_to_cpu(palive->st_fwrd_size);
+
+		alive_data->valid = le16_to_cpu(palive->status) ==
+				    IWL_ALIVE_STATUS_OK;
+		if (mvm->umac_error_event_table)
+			mvm->support_umac_log = true;
+
+		IWL_DEBUG_FW(mvm,
+			     "Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
+			     le16_to_cpu(palive->status), palive->ver_type,
+			     palive->ver_subtype, palive->flags);
+
+		IWL_DEBUG_FW(mvm,
+			     "UMAC version: Major - 0x%x, Minor - 0x%x\n",
+			     le32_to_cpu(palive->umac_major),
+			     le32_to_cpu(palive->umac_minor));
 	}
 
 	return true;
@@ -187,8 +217,7 @@
 	struct iwl_sf_region st_fwrd_space;
 
 	if (ucode_type == IWL_UCODE_REGULAR &&
-	    iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_CUSTOM) &&
-	    iwl_fw_dbg_conf_enabled(mvm->fw, FW_DBG_CUSTOM))
+	    iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE))
 		fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER);
 	else
 		fw = iwl_get_ucode_image(mvm, ucode_type);
@@ -269,7 +298,7 @@
 	enum iwl_ucode_type ucode_type = mvm->cur_ucode;
 
 	/* Set parameters */
-	phy_cfg_cmd.phy_cfg = cpu_to_le32(mvm->fw->phy_config);
+	phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm));
 	phy_cfg_cmd.calib_control.event_trigger =
 		mvm->fw->default_calib[ucode_type].event_trigger;
 	phy_cfg_cmd.calib_control.flow_trigger =
@@ -346,7 +375,7 @@
 	mvm->calibrating = true;
 
 	/* Send TX valid antennas before triggering calibrations */
-	ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
+	ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
 	if (ret)
 		goto error;
 
@@ -399,8 +428,148 @@
 	return ret;
 }
 
-static int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm,
-				     enum iwl_fw_dbg_conf conf_id)
+static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm)
+{
+	struct iwl_host_cmd cmd = {
+		.id = SHARED_MEM_CFG,
+		.flags = CMD_WANT_SKB,
+		.data = { NULL, },
+		.len = { 0, },
+	};
+	struct iwl_rx_packet *pkt;
+	struct iwl_shared_mem_cfg *mem_cfg;
+	u32 i;
+
+	lockdep_assert_held(&mvm->mutex);
+
+	if (WARN_ON(iwl_mvm_send_cmd(mvm, &cmd)))
+		return;
+
+	pkt = cmd.resp_pkt;
+	if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+		IWL_ERR(mvm, "Bad return from SHARED_MEM_CFG (0x%08X)\n",
+			pkt->hdr.flags);
+		goto exit;
+	}
+
+	mem_cfg = (void *)pkt->data;
+
+	mvm->shared_mem_cfg.shared_mem_addr =
+		le32_to_cpu(mem_cfg->shared_mem_addr);
+	mvm->shared_mem_cfg.shared_mem_size =
+		le32_to_cpu(mem_cfg->shared_mem_size);
+	mvm->shared_mem_cfg.sample_buff_addr =
+		le32_to_cpu(mem_cfg->sample_buff_addr);
+	mvm->shared_mem_cfg.sample_buff_size =
+		le32_to_cpu(mem_cfg->sample_buff_size);
+	mvm->shared_mem_cfg.txfifo_addr = le32_to_cpu(mem_cfg->txfifo_addr);
+	for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++)
+		mvm->shared_mem_cfg.txfifo_size[i] =
+			le32_to_cpu(mem_cfg->txfifo_size[i]);
+	for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++)
+		mvm->shared_mem_cfg.rxfifo_size[i] =
+			le32_to_cpu(mem_cfg->rxfifo_size[i]);
+	mvm->shared_mem_cfg.page_buff_addr =
+		le32_to_cpu(mem_cfg->page_buff_addr);
+	mvm->shared_mem_cfg.page_buff_size =
+		le32_to_cpu(mem_cfg->page_buff_size);
+	IWL_DEBUG_INFO(mvm, "SHARED MEM CFG: got memory offsets/sizes\n");
+
+exit:
+	iwl_free_resp(&cmd);
+}
+
+int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
+				struct iwl_mvm_dump_desc *desc,
+				unsigned int delay)
+{
+	if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status))
+		return -EBUSY;
+
+	if (WARN_ON(mvm->fw_dump_desc))
+		iwl_mvm_free_fw_dump_desc(mvm);
+
+	IWL_WARN(mvm, "Collecting data: trigger %d fired.\n",
+		 le32_to_cpu(desc->trig_desc.type));
+
+	mvm->fw_dump_desc = desc;
+
+	/* stop recording */
+	if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+		iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
+	} else {
+		iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0);
+		/* wait before we collect the data till the DBGC stop */
+		udelay(100);
+	}
+
+	queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay);
+
+	return 0;
+}
+
+int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
+			   const char *str, size_t len, unsigned int delay)
+{
+	struct iwl_mvm_dump_desc *desc;
+
+	desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
+	if (!desc)
+		return -ENOMEM;
+
+	desc->len = len;
+	desc->trig_desc.type = cpu_to_le32(trig);
+	memcpy(desc->trig_desc.data, str, len);
+
+	return iwl_mvm_fw_dbg_collect_desc(mvm, desc, delay);
+}
+
+int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
+				struct iwl_fw_dbg_trigger_tlv *trigger,
+				const char *fmt, ...)
+{
+	unsigned int delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
+	u16 occurrences = le16_to_cpu(trigger->occurrences);
+	int ret, len = 0;
+	char buf[64];
+
+	if (!occurrences)
+		return 0;
+
+	if (fmt) {
+		va_list ap;
+
+		buf[sizeof(buf) - 1] = '\0';
+
+		va_start(ap, fmt);
+		vsnprintf(buf, sizeof(buf), fmt, ap);
+		va_end(ap);
+
+		/* check for truncation */
+		if (WARN_ON_ONCE(buf[sizeof(buf) - 1]))
+			buf[sizeof(buf) - 1] = '\0';
+
+		len = strlen(buf) + 1;
+	}
+
+	ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf,
+				     len, delay);
+	if (ret)
+		return ret;
+
+	trigger->occurrences = cpu_to_le16(occurrences - 1);
+	return 0;
+}
+
+static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm)
+{
+	if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
+		iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
+	else
+		iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1);
+}
+
+int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
 {
 	u8 *ptr;
 	int ret;
@@ -410,6 +579,14 @@
 		      "Invalid configuration %d\n", conf_id))
 		return -EINVAL;
 
+	/* EARLY START - firmware's configuration is hard coded */
+	if ((!mvm->fw->dbg_conf_tlv[conf_id] ||
+	     !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) &&
+	    conf_id == FW_DBG_START_FROM_ALIVE) {
+		iwl_mvm_restart_early_start(mvm);
+		return 0;
+	}
+
 	if (!mvm->fw->dbg_conf_tlv[conf_id])
 		return -EINVAL;
 
@@ -435,6 +612,35 @@
 	return ret;
 }
 
+static int iwl_mvm_config_ltr_v1(struct iwl_mvm *mvm)
+{
+	struct iwl_ltr_config_cmd_v1 cmd_v1 = {
+		.flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
+	};
+
+	if (!mvm->trans->ltr_enabled)
+		return 0;
+
+	return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
+				    sizeof(cmd_v1), &cmd_v1);
+}
+
+static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
+{
+	struct iwl_ltr_config_cmd cmd = {
+		.flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
+	};
+
+	if (!mvm->trans->ltr_enabled)
+		return 0;
+
+	if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_HDC_PHASE_0))
+		return iwl_mvm_config_ltr_v1(mvm);
+
+	return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
+				    sizeof(cmd), &cmd);
+}
+
 int iwl_mvm_up(struct iwl_mvm *mvm)
 {
 	int ret, i;
@@ -482,14 +688,20 @@
 		goto error;
 	}
 
+	if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 10)
+		iwl_mvm_get_shared_mem_conf(mvm);
+
 	ret = iwl_mvm_sf_update(mvm, NULL, false);
 	if (ret)
 		IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
 
 	mvm->fw_dbg_conf = FW_DBG_INVALID;
-	iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_CUSTOM);
+	/* if we have a destination, assume EARLY START */
+	if (mvm->fw->dbg_dest_tlv)
+		mvm->fw_dbg_conf = FW_DBG_START_FROM_ALIVE;
+	iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_START_FROM_ALIVE);
 
-	ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
+	ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
 	if (ret)
 		goto error;
 
@@ -538,19 +750,22 @@
 	/* Initialize tx backoffs to the minimal possible */
 	iwl_mvm_tt_tx_backoff(mvm, 0);
 
-	if (mvm->trans->ltr_enabled) {
-		struct iwl_ltr_config_cmd cmd = {
-			.flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
-		};
-
-		WARN_ON(iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
-					     sizeof(cmd), &cmd));
-	}
+	WARN_ON(iwl_mvm_config_ltr(mvm));
 
 	ret = iwl_mvm_power_update_device(mvm);
 	if (ret)
 		goto error;
 
+	/*
+	 * RTNL is not taken during Ct-kill, but we don't need to scan/Tx
+	 * anyway, so don't init MCC.
+	 */
+	if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) {
+		ret = iwl_mvm_init_mcc(mvm);
+		if (ret)
+			goto error;
+	}
+
 	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
 		ret = iwl_mvm_config_scan(mvm);
 		if (ret)
@@ -584,7 +799,7 @@
 		goto error;
 	}
 
-	ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
+	ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
 	if (ret)
 		goto error;
 
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
index f6d86cc..8088c71 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
@@ -208,8 +208,10 @@
 	if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
 		return BIT(IWL_MVM_OFFCHANNEL_QUEUE);
 
-	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
-		qmask |= BIT(vif->hw_queue[ac]);
+	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+		if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
+			qmask |= BIT(vif->hw_queue[ac]);
+	}
 
 	if (vif->type == NL80211_IFTYPE_AP)
 		qmask |= BIT(vif->cab_queue);
@@ -242,6 +244,7 @@
 unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
 					 struct ieee80211_vif *exclude_vif)
 {
+	u8 sta_id;
 	struct iwl_mvm_hw_queues_iface_iterator_data data = {
 		.exclude_vif = exclude_vif,
 		.used_hw_queues =
@@ -262,6 +265,13 @@
 					  iwl_mvm_mac_sta_hw_queues_iter,
 					  &data);
 
+	/*
+	 * Some TDLS stations may be removed but are in the process of being
+	 * drained. Don't touch their queues.
+	 */
+	for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT)
+		data.used_hw_queues |= mvm->tfd_drained[sta_id];
+
 	return data.used_hw_queues;
 }
 
@@ -460,6 +470,8 @@
 
 int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
+	unsigned int wdg_timeout =
+		iwl_mvm_get_wd_timeout(mvm, vif, false, false);
 	u32 ac;
 	int ret;
 
@@ -472,16 +484,17 @@
 	switch (vif->type) {
 	case NL80211_IFTYPE_P2P_DEVICE:
 		iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE,
-				      IWL_MVM_TX_FIFO_VO);
+				      IWL_MVM_TX_FIFO_VO, wdg_timeout);
 		break;
 	case NL80211_IFTYPE_AP:
 		iwl_mvm_enable_ac_txq(mvm, vif->cab_queue,
-				      IWL_MVM_TX_FIFO_MCAST);
+				      IWL_MVM_TX_FIFO_MCAST, wdg_timeout);
 		/* fall through */
 	default:
 		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
 			iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac],
-					      iwl_mvm_ac_to_tx_fifo[ac]);
+					      iwl_mvm_ac_to_tx_fifo[ac],
+					      wdg_timeout);
 		break;
 	}
 
@@ -496,14 +509,14 @@
 
 	switch (vif->type) {
 	case NL80211_IFTYPE_P2P_DEVICE:
-		iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE);
+		iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, 0);
 		break;
 	case NL80211_IFTYPE_AP:
-		iwl_mvm_disable_txq(mvm, vif->cab_queue);
+		iwl_mvm_disable_txq(mvm, vif->cab_queue, 0);
 		/* fall through */
 	default:
 		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
-			iwl_mvm_disable_txq(mvm, vif->hw_queue[ac]);
+			iwl_mvm_disable_txq(mvm, vif->hw_queue[ac], 0);
 	}
 }
 
@@ -975,7 +988,7 @@
 	beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags);
 
 	mvm->mgmt_last_antenna_idx =
-		iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant,
+		iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
 				     mvm->mgmt_last_antenna_idx);
 
 	beacon_cmd.tx.rate_n_flags =
@@ -1361,10 +1374,18 @@
 {
 	struct iwl_missed_beacons_notif *missed_beacons = _data;
 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+	struct iwl_mvm *mvm = mvmvif->mvm;
+	struct iwl_fw_dbg_trigger_missed_bcon *bcon_trig;
+	struct iwl_fw_dbg_trigger_tlv *trigger;
+	u32 stop_trig_missed_bcon, stop_trig_missed_bcon_since_rx;
+	u32 rx_missed_bcon, rx_missed_bcon_since_rx;
 
 	if (mvmvif->id != (u16)le32_to_cpu(missed_beacons->mac_id))
 		return;
 
+	rx_missed_bcon = le32_to_cpu(missed_beacons->consec_missed_beacons);
+	rx_missed_bcon_since_rx =
+		le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx);
 	/*
 	 * TODO: the threshold should be adjusted based on latency conditions,
 	 * and/or in case of a CS flow on one of the other AP vifs.
@@ -1372,6 +1393,26 @@
 	if (le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx) >
 	     IWL_MVM_MISSED_BEACONS_THRESHOLD)
 		ieee80211_beacon_loss(vif);
+
+	if (!iwl_fw_dbg_trigger_enabled(mvm->fw,
+					FW_DBG_TRIGGER_MISSED_BEACONS))
+		return;
+
+	trigger = iwl_fw_dbg_get_trigger(mvm->fw,
+					 FW_DBG_TRIGGER_MISSED_BEACONS);
+	bcon_trig = (void *)trigger->data;
+	stop_trig_missed_bcon = le32_to_cpu(bcon_trig->stop_consec_missed_bcon);
+	stop_trig_missed_bcon_since_rx =
+		le32_to_cpu(bcon_trig->stop_consec_missed_bcon_since_rx);
+
+	/* TODO: implement start trigger */
+
+	if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
+		return;
+
+	if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx ||
+	    rx_missed_bcon >= stop_trig_missed_bcon)
+		iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
 }
 
 int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 137f3da..e2f0994 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -85,6 +85,8 @@
 #include "testmode.h"
 #include "iwl-fw-error-dump.h"
 #include "iwl-prph.h"
+#include "iwl-csr.h"
+#include "iwl-nvm-parse.h"
 
 static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
 	{
@@ -105,7 +107,7 @@
 
 static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
 	{
-		.num_different_channels = 1,
+		.num_different_channels = 2,
 		.max_interfaces = 3,
 		.limits = iwl_mvm_limits,
 		.n_limits = ARRAY_SIZE(iwl_mvm_limits),
@@ -300,6 +302,116 @@
 	}
 }
 
+struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
+						  const char *alpha2,
+						  enum iwl_mcc_source src_id,
+						  bool *changed)
+{
+	struct ieee80211_regdomain *regd = NULL;
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+	struct iwl_mcc_update_resp *resp;
+
+	IWL_DEBUG_LAR(mvm, "Getting regdomain data for %s from FW\n", alpha2);
+
+	lockdep_assert_held(&mvm->mutex);
+
+	resp = iwl_mvm_update_mcc(mvm, alpha2, src_id);
+	if (IS_ERR_OR_NULL(resp)) {
+		IWL_DEBUG_LAR(mvm, "Could not get update from FW %d\n",
+			      PTR_RET(resp));
+		goto out;
+	}
+
+	if (changed)
+		*changed = (resp->status == MCC_RESP_NEW_CHAN_PROFILE);
+
+	regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg,
+				      __le32_to_cpu(resp->n_channels),
+				      resp->channels,
+				      __le16_to_cpu(resp->mcc));
+	/* Store the return source id */
+	src_id = resp->source_id;
+	kfree(resp);
+	if (IS_ERR_OR_NULL(regd)) {
+		IWL_DEBUG_LAR(mvm, "Could not get parse update from FW %d\n",
+			      PTR_RET(regd));
+		goto out;
+	}
+
+	IWL_DEBUG_LAR(mvm, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n",
+		      regd->alpha2, regd->alpha2[0], regd->alpha2[1], src_id);
+	mvm->lar_regdom_set = true;
+	mvm->mcc_src = src_id;
+
+out:
+	return regd;
+}
+
+void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm)
+{
+	bool changed;
+	struct ieee80211_regdomain *regd;
+
+	if (!iwl_mvm_is_lar_supported(mvm))
+		return;
+
+	regd = iwl_mvm_get_current_regdomain(mvm, &changed);
+	if (!IS_ERR_OR_NULL(regd)) {
+		/* only update the regulatory core if changed */
+		if (changed)
+			regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
+
+		kfree(regd);
+	}
+}
+
+struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm,
+							  bool *changed)
+{
+	return iwl_mvm_get_regdomain(mvm->hw->wiphy, "ZZ",
+				     iwl_mvm_is_wifi_mcc_supported(mvm) ?
+				     MCC_SOURCE_GET_CURRENT :
+				     MCC_SOURCE_OLD_FW, changed);
+}
+
+int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
+{
+	enum iwl_mcc_source used_src;
+	struct ieee80211_regdomain *regd;
+	int ret;
+	bool changed;
+	const struct ieee80211_regdomain *r =
+			rtnl_dereference(mvm->hw->wiphy->regd);
+
+	if (!r)
+		return -ENOENT;
+
+	/* save the last source in case we overwrite it below */
+	used_src = mvm->mcc_src;
+	if (iwl_mvm_is_wifi_mcc_supported(mvm)) {
+		/* Notify the firmware we support wifi location updates */
+		regd = iwl_mvm_get_current_regdomain(mvm, NULL);
+		if (!IS_ERR_OR_NULL(regd))
+			kfree(regd);
+	}
+
+	/* Now set our last stored MCC and source */
+	regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src,
+				     &changed);
+	if (IS_ERR_OR_NULL(regd))
+		return -EIO;
+
+	/* update cfg80211 if the regdomain was changed */
+	if (changed)
+		ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
+	else
+		ret = 0;
+
+	kfree(regd);
+	return ret;
+}
+
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 {
 	struct ieee80211_hw *hw = mvm->hw;
@@ -326,6 +438,8 @@
 	hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
 		IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
 	hw->rate_control_algorithm = "iwl-mvm-rs";
+	hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
+	hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
 
 	/*
 	 * Enable 11w if advertised by firmware and software crypto
@@ -336,20 +450,10 @@
 	    !iwlwifi_mod_params.sw_crypto)
 		hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
-	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
-	    !iwlwifi_mod_params.uapsd_disable) {
-		hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
-		hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
-		hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
-	}
-
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
-	    mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
-		hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
-		hw->wiphy->features |=
-			NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
-			NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
-	}
+	hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
+	hw->wiphy->features |=
+		NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
+		NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
 
 	hw->sta_data_size = sizeof(struct iwl_mvm_sta);
 	hw->vif_data_size = sizeof(struct iwl_mvm_vif);
@@ -363,8 +467,12 @@
 		BIT(NL80211_IFTYPE_ADHOC);
 
 	hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
-	hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
-				       REGULATORY_DISABLE_BEACON_HINTS;
+	hw->wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR;
+	if (iwl_mvm_is_lar_supported(mvm))
+		hw->wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
+	else
+		hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
+					       REGULATORY_DISABLE_BEACON_HINTS;
 
 	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
 		hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
@@ -377,6 +485,8 @@
 
 	hw->wiphy->max_remain_on_channel_duration = 10000;
 	hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
+	/* we can compensate an offset of up to 3 channels = 15 MHz */
+	hw->wiphy->max_adj_channel_rssi_comp = 3 * 5;
 
 	/* Extract MAC address */
 	memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
@@ -403,10 +513,18 @@
 	if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
 		hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
 			&mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
-	if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels)
+	if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels) {
 		hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
 			&mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
 
+		if ((mvm->fw->ucode_capa.capa[0] &
+		     IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
+		    (mvm->fw->ucode_capa.api[0] &
+		     IWL_UCODE_TLV_API_LQ_SS_PARAMS))
+			hw->wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap |=
+				IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+	}
+
 	hw->wiphy->hw_version = mvm->trans->hw_id;
 
 	if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
@@ -459,15 +577,17 @@
 	    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 &&
+	}
+
+	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)) {
-		mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-				    WIPHY_WOWLAN_DISCONNECT |
-				    WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-				    WIPHY_WOWLAN_RFKILL_RELEASE |
-				    WIPHY_WOWLAN_NET_DETECT;
+		mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT |
+				     WIPHY_WOWLAN_DISCONNECT |
+				     WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+				     WIPHY_WOWLAN_RFKILL_RELEASE |
+				     WIPHY_WOWLAN_NET_DETECT;
 		if (!iwlwifi_mod_params.sw_crypto)
 			mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
 					     WIPHY_WOWLAN_GTK_REKEY_FAILURE |
@@ -707,9 +827,6 @@
 	mvmvif->uploaded = false;
 	mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
 
-	/* does this make sense at all? */
-	mvmvif->color++;
-
 	spin_lock_bh(&mvm->time_event_lock);
 	iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);
 	spin_unlock_bh(&mvm->time_event_lock);
@@ -761,17 +878,157 @@
 	kfree(fw_error_dump);
 }
 
+static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm,
+			       struct iwl_fw_error_dump_data **dump_data)
+{
+	struct iwl_fw_error_dump_fifo *fifo_hdr;
+	u32 *fifo_data;
+	u32 fifo_len;
+	unsigned long flags;
+	int i, j;
+
+	if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags))
+		return;
+
+	/* Pull RXF data from all RXFs */
+	for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) {
+		/*
+		 * Keep aside the additional offset that might be needed for
+		 * next RXF
+		 */
+		u32 offset_diff = RXF_DIFF_FROM_PREV * i;
+
+		fifo_hdr = (void *)(*dump_data)->data;
+		fifo_data = (void *)fifo_hdr->data;
+		fifo_len = mvm->shared_mem_cfg.rxfifo_size[i];
+
+		/* No need to try to read the data if the length is 0 */
+		if (fifo_len == 0)
+			continue;
+
+		/* Add a TLV for the RXF */
+		(*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
+		(*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
+
+		fifo_hdr->fifo_num = cpu_to_le32(i);
+		fifo_hdr->available_bytes =
+			cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+							RXF_RD_D_SPACE +
+							offset_diff));
+		fifo_hdr->wr_ptr =
+			cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+							RXF_RD_WR_PTR +
+							offset_diff));
+		fifo_hdr->rd_ptr =
+			cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+							RXF_RD_RD_PTR +
+							offset_diff));
+		fifo_hdr->fence_ptr =
+			cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+							RXF_RD_FENCE_PTR +
+							offset_diff));
+		fifo_hdr->fence_mode =
+			cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+							RXF_SET_FENCE_MODE +
+							offset_diff));
+
+		/* Lock fence */
+		iwl_trans_write_prph(mvm->trans,
+				     RXF_SET_FENCE_MODE + offset_diff, 0x1);
+		/* Set fence pointer to the same place like WR pointer */
+		iwl_trans_write_prph(mvm->trans,
+				     RXF_LD_WR2FENCE + offset_diff, 0x1);
+		/* Set fence offset */
+		iwl_trans_write_prph(mvm->trans,
+				     RXF_LD_FENCE_OFFSET_ADDR + offset_diff,
+				     0x0);
+
+		/* Read FIFO */
+		fifo_len /= sizeof(u32); /* Size in DWORDS */
+		for (j = 0; j < fifo_len; j++)
+			fifo_data[j] = iwl_trans_read_prph(mvm->trans,
+							 RXF_FIFO_RD_FENCE_INC +
+							 offset_diff);
+		*dump_data = iwl_fw_error_next_data(*dump_data);
+	}
+
+	/* Pull TXF data from all TXFs */
+	for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) {
+		/* Mark the number of TXF we're pulling now */
+		iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i);
+
+		fifo_hdr = (void *)(*dump_data)->data;
+		fifo_data = (void *)fifo_hdr->data;
+		fifo_len = mvm->shared_mem_cfg.txfifo_size[i];
+
+		/* No need to try to read the data if the length is 0 */
+		if (fifo_len == 0)
+			continue;
+
+		/* Add a TLV for the FIFO */
+		(*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF);
+		(*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
+
+		fifo_hdr->fifo_num = cpu_to_le32(i);
+		fifo_hdr->available_bytes =
+			cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+							TXF_FIFO_ITEM_CNT));
+		fifo_hdr->wr_ptr =
+			cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+							TXF_WR_PTR));
+		fifo_hdr->rd_ptr =
+			cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+							TXF_RD_PTR));
+		fifo_hdr->fence_ptr =
+			cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+							TXF_FENCE_PTR));
+		fifo_hdr->fence_mode =
+			cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+							TXF_LOCK_FENCE));
+
+		/* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
+		iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR,
+				     TXF_WR_PTR);
+
+		/* Dummy-read to advance the read pointer to the head */
+		iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA);
+
+		/* Read FIFO */
+		fifo_len /= sizeof(u32); /* Size in DWORDS */
+		for (j = 0; j < fifo_len; j++)
+			fifo_data[j] = iwl_trans_read_prph(mvm->trans,
+							  TXF_READ_MODIFY_DATA);
+		*dump_data = iwl_fw_error_next_data(*dump_data);
+	}
+
+	iwl_trans_release_nic_access(mvm->trans, &flags);
+}
+
+void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm)
+{
+	if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert ||
+	    !mvm->fw_dump_desc)
+		return;
+
+	kfree(mvm->fw_dump_desc);
+	mvm->fw_dump_desc = NULL;
+}
+
+#define IWL8260_ICCM_OFFSET		0x44000 /* Only for B-step */
+#define IWL8260_ICCM_LEN		0xC000 /* Only for B-step */
+
 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;
 	struct iwl_fw_error_dump_info *dump_info;
+	struct iwl_fw_error_dump_mem *dump_mem;
+	struct iwl_fw_error_dump_trigger_desc *dump_trig;
 	struct iwl_mvm_dump_ptrs *fw_error_dump;
-	const struct fw_img *img;
 	u32 sram_len, sram_ofs;
-	u32 file_len, rxf_len;
-	unsigned long flags;
-	int reg_val;
+	u32 file_len, fifo_data_len = 0;
+	u32 smem_len = mvm->cfg->smem_len;
+	u32 sram2_len = mvm->cfg->dccm2_len;
 
 	lockdep_assert_held(&mvm->mutex);
 
@@ -779,26 +1036,77 @@
 	if (!fw_error_dump)
 		return;
 
-	img = &mvm->fw->img[mvm->cur_ucode];
-	sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
-	sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+	/* SRAM - include stack CCM if driver knows the values for it */
+	if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) {
+		const struct fw_img *img;
 
-	/* reading buffer size */
-	reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
-	rxf_len = (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
+		img = &mvm->fw->img[mvm->cur_ucode];
+		sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+		sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+	} else {
+		sram_ofs = mvm->cfg->dccm_offset;
+		sram_len = mvm->cfg->dccm_len;
+	}
 
-	/* the register holds the value divided by 128 */
-	rxf_len = rxf_len << 7;
+	/* reading RXF/TXF sizes */
+	if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
+		struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg;
+		int i;
+
+		fifo_data_len = 0;
+
+		/* Count RXF size */
+		for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) {
+			if (!mem_cfg->rxfifo_size[i])
+				continue;
+
+			/* Add header info */
+			fifo_data_len += mem_cfg->rxfifo_size[i] +
+					 sizeof(*dump_data) +
+					 sizeof(struct iwl_fw_error_dump_fifo);
+		}
+
+		for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) {
+			if (!mem_cfg->txfifo_size[i])
+				continue;
+
+			/* Add header info */
+			fifo_data_len += mem_cfg->txfifo_size[i] +
+					 sizeof(*dump_data) +
+					 sizeof(struct iwl_fw_error_dump_fifo);
+		}
+	}
 
 	file_len = sizeof(*dump_file) +
-		   sizeof(*dump_data) * 3 +
-		   sram_len +
-		   rxf_len +
+		   sizeof(*dump_data) * 2 +
+		   sram_len + sizeof(*dump_mem) +
+		   fifo_data_len +
 		   sizeof(*dump_info);
 
+	/*
+	 * In 8000 HW family B-step include the ICCM (which resides separately)
+	 */
+	if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+	    CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP)
+		file_len += sizeof(*dump_data) + sizeof(*dump_mem) +
+			    IWL8260_ICCM_LEN;
+
+	if (mvm->fw_dump_desc)
+		file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
+			    mvm->fw_dump_desc->len;
+
+	/* Make room for the SMEM, if it exists */
+	if (smem_len)
+		file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
+
+	/* Make room for the secondary SRAM, if it exists */
+	if (sram2_len)
+		file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len;
+
 	dump_file = vzalloc(file_len);
 	if (!dump_file) {
 		kfree(fw_error_dump);
+		iwl_mvm_free_fw_dump_desc(mvm);
 		return;
 	}
 
@@ -814,6 +1122,7 @@
 		mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
 			cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
 			cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
+	dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev));
 	memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
 	       sizeof(dump_info->fw_human_readable));
 	strncpy(dump_info->dev_human_readable, mvm->cfg->name,
@@ -822,29 +1131,66 @@
 		sizeof(dump_info->bus_human_readable));
 
 	dump_data = iwl_fw_error_next_data(dump_data);
-	dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
-	dump_data->len = cpu_to_le32(rxf_len);
+	/* We only dump the FIFOs if the FW is in error state */
+	if (test_bit(STATUS_FW_ERROR, &mvm->trans->status))
+		iwl_mvm_dump_fifos(mvm, &dump_data);
 
-	if (iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
-		u32 *rxf = (void *)dump_data->data;
-		int i;
+	if (mvm->fw_dump_desc) {
+		dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
+		dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
+					     mvm->fw_dump_desc->len);
+		dump_trig = (void *)dump_data->data;
+		memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc,
+		       sizeof(*dump_trig) + mvm->fw_dump_desc->len);
 
-		for (i = 0; i < (rxf_len / sizeof(u32)); i++) {
-			iwl_trans_write_prph(mvm->trans,
-					     RXF_LD_FENCE_OFFSET_ADDR,
-					     i * sizeof(u32));
-			rxf[i] = iwl_trans_read_prph(mvm->trans,
-						     RXF_FIFO_RD_FENCE_ADDR);
-		}
-		iwl_trans_release_nic_access(mvm->trans, &flags);
+		/* now we can free this copy */
+		iwl_mvm_free_fw_dump_desc(mvm);
+		dump_data = iwl_fw_error_next_data(dump_data);
 	}
 
-	dump_data = iwl_fw_error_next_data(dump_data);
-	dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
-	dump_data->len = cpu_to_le32(sram_len);
-	iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data,
+	dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+	dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
+	dump_mem = (void *)dump_data->data;
+	dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+	dump_mem->offset = cpu_to_le32(sram_ofs);
+	iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data,
 				 sram_len);
 
+	if (smem_len) {
+		dump_data = iwl_fw_error_next_data(dump_data);
+		dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+		dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem));
+		dump_mem = (void *)dump_data->data;
+		dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM);
+		dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset);
+		iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset,
+					 dump_mem->data, smem_len);
+	}
+
+	if (sram2_len) {
+		dump_data = iwl_fw_error_next_data(dump_data);
+		dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+		dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem));
+		dump_mem = (void *)dump_data->data;
+		dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+		dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset);
+		iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset,
+					 dump_mem->data, sram2_len);
+	}
+
+	if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+	    CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) {
+		dump_data = iwl_fw_error_next_data(dump_data);
+		dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+		dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN +
+					     sizeof(*dump_mem));
+		dump_mem = (void *)dump_data->data;
+		dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+		dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET);
+		iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET,
+					 dump_mem->data, IWL8260_ICCM_LEN);
+	}
+
 	fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans);
 	fw_error_dump->op_mode_len = file_len;
 	if (fw_error_dump->trans_ptr)
@@ -853,16 +1199,31 @@
 
 	dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
 		      GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
+
+	clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status);
 }
 
+struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = {
+	.trig_desc = {
+		.type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT),
+	},
+};
+
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 {
 	/* clear the D3 reconfig, we only need it to avoid dumping a
 	 * firmware coredump on reconfiguration, we shouldn't do that
 	 * on D3->D0 transition
 	 */
-	if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status))
+	if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) {
+		mvm->fw_dump_desc = &iwl_mvm_dump_desc_assert;
 		iwl_mvm_fw_error_dump(mvm);
+	}
+
+	/* cleanup all stale references (scan, roc), but keep the
+	 * ucode_down ref until reconfig is complete
+	 */
+	iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
 
 	iwl_trans_stop_device(mvm->trans);
 
@@ -893,15 +1254,15 @@
 
 	ieee80211_wake_queues(mvm->hw);
 
-	/* cleanup all stale references (scan, roc), but keep the
-	 * ucode_down ref until reconfig is complete */
-	iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
-
 	/* clear any stale d0i3 state */
 	clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
 
 	mvm->vif_count = 0;
 	mvm->rx_ba_sessions = 0;
+	mvm->fw_dbg_conf = FW_DBG_INVALID;
+
+	/* keep statistics ticking */
+	iwl_mvm_accu_radio_stats(mvm);
 }
 
 int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
@@ -933,6 +1294,19 @@
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 	int ret;
 
+	/* Some hw restart cleanups must not hold the mutex */
+	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+		/*
+		 * Make sure we are out of d0i3. This is needed
+		 * to make sure the reference accounting is correct
+		 * (and there is no stale d0i3_exit_work).
+		 */
+		wait_event_timeout(mvm->d0i3_exit_waitq,
+				   !test_bit(IWL_MVM_STATUS_IN_D0I3,
+					     &mvm->status),
+				   HZ);
+	}
+
 	mutex_lock(&mvm->mutex);
 	ret = __iwl_mvm_mac_start(mvm);
 	mutex_unlock(&mvm->mutex);
@@ -948,7 +1322,7 @@
 
 	clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
 	iwl_mvm_d0i3_enable_tx(mvm, NULL);
-	ret = iwl_mvm_update_quotas(mvm, NULL);
+	ret = iwl_mvm_update_quotas(mvm, false, NULL);
 	if (ret)
 		IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
 			ret);
@@ -982,6 +1356,13 @@
 		IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n");
 		_iwl_mvm_exit_d0i3(mvm);
 	}
+
+	if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND)
+		if (!wait_event_timeout(mvm->d0i3_exit_waitq,
+					!test_bit(IWL_MVM_STATUS_IN_D0I3,
+						  &mvm->status),
+					HZ))
+			WARN_ONCE(1, "D0i3 exit on resume timed out\n");
 }
 
 static void
@@ -1004,8 +1385,18 @@
 {
 	lockdep_assert_held(&mvm->mutex);
 
-	/* disallow low power states when the FW is down */
-	iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+	/* firmware counters are obviously reset now, but we shouldn't
+	 * partially track so also clear the fw_reset_accu counters.
+	 */
+	memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats));
+
+	/*
+	 * Disallow low power states when the FW is down by taking
+	 * the UCODE_DOWN ref. in case of ongoing hw restart the
+	 * ref is already taken, so don't take it again.
+	 */
+	if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+		iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
 
 	/* async_handlers_wk is now blocked */
 
@@ -1023,6 +1414,26 @@
 	/* the fw is stopped, the aux sta is dead: clean up driver state */
 	iwl_mvm_del_aux_sta(mvm);
 
+	/*
+	 * Clear IN_HW_RESTART flag when stopping the hw (as restart_complete()
+	 * won't be called in this case).
+	 */
+	clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+
+	/* We shouldn't have any UIDs still set.  Loop over all the UIDs to
+	 * make sure there's nothing left there and warn if any is found.
+	 */
+	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+		int i;
+
+		for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+			if (WARN_ONCE(mvm->scan_uid[i],
+				      "UMAC scan UID %d was not cleaned\n",
+				      mvm->scan_uid[i]))
+				mvm->scan_uid[i] = 0;
+		}
+	}
+
 	mvm->ucode_loaded = false;
 }
 
@@ -1032,7 +1443,8 @@
 
 	flush_work(&mvm->d0i3_exit_work);
 	flush_work(&mvm->async_handlers_wk);
-	flush_work(&mvm->fw_error_dump_wk);
+	cancel_delayed_work_sync(&mvm->fw_dump_wk);
+	iwl_mvm_free_fw_dump_desc(mvm);
 
 	mutex_lock(&mvm->mutex);
 	__iwl_mvm_mac_stop(mvm);
@@ -1080,6 +1492,8 @@
 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 	int ret;
 
+	mvmvif->mvm = mvm;
+
 	/*
 	 * make sure D0i3 exit is completed, otherwise a target access
 	 * during tx queue configuration could be done when still in
@@ -1097,6 +1511,11 @@
 
 	mutex_lock(&mvm->mutex);
 
+	/* make sure that beacon statistics don't go backwards with FW reset */
+	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+		mvmvif->beacon_stats.accu_num_beacons +=
+			mvmvif->beacon_stats.num_beacons;
+
 	/* Allocate resources for the MAC context, and add it to the fw  */
 	ret = iwl_mvm_mac_ctxt_init(mvm, vif);
 	if (ret)
@@ -1135,7 +1554,7 @@
 
 	ret = iwl_mvm_power_update_mac(mvm);
 	if (ret)
-		goto out_release;
+		goto out_remove_mac;
 
 	/* beacon filtering */
 	ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
@@ -1211,9 +1630,33 @@
 	u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif);
 
 	if (tfd_msk) {
+		/*
+		 * mac80211 first removes all the stations of the vif and
+		 * then removes the vif. When it removes a station it also
+		 * flushes the AMPDU session. So by now, all the AMPDU sessions
+		 * of all the stations of this vif are closed, and the queues
+		 * of these AMPDU sessions are properly closed.
+		 * We still need to take care of the shared queues of the vif.
+		 * Flush them here.
+		 */
 		mutex_lock(&mvm->mutex);
 		iwl_mvm_flush_tx_path(mvm, tfd_msk, true);
 		mutex_unlock(&mvm->mutex);
+
+		/*
+		 * There are transports that buffer a few frames in the host.
+		 * For these, the flush above isn't enough since while we were
+		 * flushing, the transport might have sent more frames to the
+		 * device. To solve this, wait here until the transport is
+		 * empty. Technically, this could have replaced the flush
+		 * above, but flush is much faster than draining. So flush
+		 * first, and drain to make sure we have no frames in the
+		 * transport anymore.
+		 * If a station still had frames on the shared queues, it is
+		 * already marked as draining, so to complete the draining, we
+		 * just need to wait until the transport is empty.
+		 */
+		iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_msk);
 	}
 
 	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
@@ -1590,8 +2033,13 @@
 
 	if (changes & BSS_CHANGED_ASSOC) {
 		if (bss_conf->assoc) {
+			/* clear statistics to get clean beacon counter */
+			iwl_mvm_request_statistics(mvm, true);
+			memset(&mvmvif->beacon_stats, 0,
+			       sizeof(mvmvif->beacon_stats));
+
 			/* add quota for this interface */
-			ret = iwl_mvm_update_quotas(mvm, NULL);
+			ret = iwl_mvm_update_quotas(mvm, true, NULL);
 			if (ret) {
 				IWL_ERR(mvm, "failed to update quotas\n");
 				return;
@@ -1643,7 +2091,7 @@
 				mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
 			mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
 			/* remove quota for this interface */
-			ret = iwl_mvm_update_quotas(mvm, NULL);
+			ret = iwl_mvm_update_quotas(mvm, false, NULL);
 			if (ret)
 				IWL_ERR(mvm, "failed to update quotas\n");
 
@@ -1762,7 +2210,7 @@
 	/* power updated needs to be done before quotas */
 	iwl_mvm_power_update_mac(mvm);
 
-	ret = iwl_mvm_update_quotas(mvm, NULL);
+	ret = iwl_mvm_update_quotas(mvm, false, NULL);
 	if (ret)
 		goto out_quota_failed;
 
@@ -1778,8 +2226,7 @@
 	if (iwl_mvm_phy_ctx_count(mvm) > 1)
 		iwl_mvm_teardown_tdls_peers(mvm);
 
-	mutex_unlock(&mvm->mutex);
-	return 0;
+	goto out_unlock;
 
 out_quota_failed:
 	iwl_mvm_power_update_mac(mvm);
@@ -1828,7 +2275,7 @@
 	if (vif->p2p && mvm->p2p_device_vif)
 		iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL);
 
-	iwl_mvm_update_quotas(mvm, NULL);
+	iwl_mvm_update_quotas(mvm, false, NULL);
 	iwl_mvm_send_rm_bcast_sta(mvm, vif);
 	iwl_mvm_binding_remove_vif(mvm, vif);
 
@@ -1967,6 +2414,12 @@
 
 	mutex_lock(&mvm->mutex);
 
+	if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
+		IWL_ERR(mvm, "scan while LAR regdomain is not set\n");
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
 		ret = -EBUSY;
 		goto out;
@@ -1976,10 +2429,8 @@
 
 	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
 		ret = iwl_mvm_scan_umac(mvm, vif, hw_req);
-	else if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
-		ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
 	else
-		ret = iwl_mvm_scan_request(mvm, vif, req);
+		ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
 
 	if (ret)
 		iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
@@ -1995,7 +2446,19 @@
 
 	mutex_lock(&mvm->mutex);
 
-	iwl_mvm_cancel_scan(mvm);
+	/* Due to a race condition, it's possible that mac80211 asks
+	 * us to stop a hw_scan when it's already stopped.  This can
+	 * happen, for instance, if we stopped the scan ourselves,
+	 * called ieee80211_scan_completed() and the userspace called
+	 * cancel scan scan before ieee80211_scan_work() could run.
+	 * To handle that, simply return if the scan is not running.
+	*/
+	/* FIXME: for now, we ignore this race for UMAC scans, since
+	 * they don't set the scan_status.
+	 */
+	if ((mvm->scan_status == IWL_MVM_SCAN_OS) ||
+	    (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+		iwl_mvm_cancel_scan(mvm);
 
 	mutex_unlock(&mvm->mutex);
 }
@@ -2037,25 +2500,35 @@
 {
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+	unsigned long txqs = 0, tids = 0;
 	int tid;
 
+	spin_lock_bh(&mvmsta->lock);
+	for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+		struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+
+		if (tid_data->state != IWL_AGG_ON &&
+		    tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
+			continue;
+
+		__set_bit(tid_data->txq_id, &txqs);
+
+		if (iwl_mvm_tid_queued(tid_data) == 0)
+			continue;
+
+		__set_bit(tid, &tids);
+	}
+
 	switch (cmd) {
 	case STA_NOTIFY_SLEEP:
 		if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)
 			ieee80211_sta_block_awake(hw, sta, true);
-		spin_lock_bh(&mvmsta->lock);
-		for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
-			struct iwl_mvm_tid_data *tid_data;
 
-			tid_data = &mvmsta->tid_data[tid];
-			if (tid_data->state != IWL_AGG_ON &&
-			    tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
-				continue;
-			if (iwl_mvm_tid_queued(tid_data) == 0)
-				continue;
+		for_each_set_bit(tid, &tids, IWL_MAX_TID_COUNT)
 			ieee80211_sta_set_buffered(sta, tid, true);
-		}
-		spin_unlock_bh(&mvmsta->lock);
+
+		if (txqs)
+			iwl_trans_freeze_txq_timer(mvm->trans, txqs, true);
 		/*
 		 * The fw updates the STA to be asleep. Tx packets on the Tx
 		 * queues to this station will not be transmitted. The fw will
@@ -2065,11 +2538,15 @@
 	case STA_NOTIFY_AWAKE:
 		if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
 			break;
+
+		if (txqs)
+			iwl_trans_freeze_txq_timer(mvm->trans, txqs, false);
 		iwl_mvm_sta_modify_ps_wake(mvm, sta);
 		break;
 	default:
 		break;
 	}
+	spin_unlock_bh(&mvmsta->lock);
 }
 
 static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
@@ -2077,7 +2554,7 @@
 				       struct ieee80211_sta *sta)
 {
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-	struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 
 	/*
 	 * This is called before mac80211 does RCU synchronisation,
@@ -2094,6 +2571,20 @@
 	mutex_unlock(&mvm->mutex);
 }
 
+static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+				const u8 *bssid)
+{
+	if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT))
+		return;
+
+	if (iwlwifi_mod_params.uapsd_disable) {
+		vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
+		return;
+	}
+
+	vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
+}
+
 static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
 				 struct ieee80211_vif *vif,
 				 struct ieee80211_sta *sta,
@@ -2153,6 +2644,7 @@
 		 * Reset EBS status here assuming environment has been changed.
 		 */
 		mvm->last_ebs_successful = true;
+		iwl_mvm_check_uapsd(mvm, vif, sta->addr);
 		ret = 0;
 	} else if (old_state == IEEE80211_STA_AUTH &&
 		   new_state == IEEE80211_STA_ASSOC) {
@@ -2292,13 +2784,13 @@
 
 	mutex_lock(&mvm->mutex);
 
-	/* Newest FW fixes sched scan while connected on another interface */
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) {
-		if (!vif->bss_conf.idle) {
-			ret = -EBUSY;
-			goto out;
-		}
-	} else if (!iwl_mvm_is_idle(mvm)) {
+	if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
+		IWL_ERR(mvm, "sched-scan while LAR regdomain is not set\n");
+		ret = -EBUSY;
+		goto out;
+	}
+
+	if (!vif->bss_conf.idle) {
 		ret = -EBUSY;
 		goto out;
 	}
@@ -2324,12 +2816,29 @@
 	int ret;
 
 	mutex_lock(&mvm->mutex);
+
+	/* Due to a race condition, it's possible that mac80211 asks
+	 * us to stop a sched_scan when it's already stopped.  This
+	 * can happen, for instance, if we stopped the scan ourselves,
+	 * called ieee80211_sched_scan_stopped() and the userspace called
+	 * stop sched scan scan before ieee80211_sched_scan_stopped_work()
+	 * could run.  To handle this, simply return if the scan is
+	 * not running.
+	*/
+	/* FIXME: for now, we ignore this race for UMAC scans, since
+	 * they don't set the scan_status.
+	 */
+	if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
+	    !(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+		mutex_unlock(&mvm->mutex);
+		return 0;
+	}
+
 	ret = iwl_mvm_scan_offload_stop(mvm, false);
 	mutex_unlock(&mvm->mutex);
 	iwl_mvm_wait_for_async_handlers(mvm);
 
 	return ret;
-
 }
 
 static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
@@ -2571,6 +3080,8 @@
 	IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
 			   duration, type);
 
+	flush_work(&mvm->roc_done_wk);
+
 	mutex_lock(&mvm->mutex);
 
 	switch (vif->type) {
@@ -2842,14 +3353,14 @@
 	 */
 	if (vif->type == NL80211_IFTYPE_MONITOR) {
 		mvmvif->monitor_active = true;
-		ret = iwl_mvm_update_quotas(mvm, NULL);
+		ret = iwl_mvm_update_quotas(mvm, false, NULL);
 		if (ret)
 			goto out_remove_binding;
 	}
 
 	/* Handle binding during CSA */
 	if (vif->type == NL80211_IFTYPE_AP) {
-		iwl_mvm_update_quotas(mvm, NULL);
+		iwl_mvm_update_quotas(mvm, false, NULL);
 		iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
 	}
 
@@ -2873,7 +3384,7 @@
 
 		iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
 
-		iwl_mvm_update_quotas(mvm, NULL);
+		iwl_mvm_update_quotas(mvm, false, NULL);
 	}
 
 	goto out;
@@ -2946,7 +3457,7 @@
 		break;
 	}
 
-	iwl_mvm_update_quotas(mvm, disabled_vif);
+	iwl_mvm_update_quotas(mvm, false, disabled_vif);
 	iwl_mvm_binding_remove_vif(mvm, vif);
 
 out:
@@ -3092,7 +3603,7 @@
 			   bool set)
 {
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-	struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 
 	if (!mvm_sta || !mvm_sta->vif) {
 		IWL_ERR(mvm, "Station is not associated to a vif\n");
@@ -3138,7 +3649,7 @@
 		mvm->noa_duration = noa_duration;
 		mvm->noa_vif = vif;
 
-		return iwl_mvm_update_quotas(mvm, NULL);
+		return iwl_mvm_update_quotas(mvm, false, NULL);
 	case IWL_MVM_TM_CMD_SET_BEACON_FILTER:
 		/* must be associated client vif - ignore authorized */
 		if (!vif || vif->type != NL80211_IFTYPE_STATION ||
@@ -3195,9 +3706,13 @@
 
 	mutex_lock(&mvm->mutex);
 
+	mvmvif->csa_failed = false;
+
 	IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
 			   chsw->chandef.center_freq1);
 
+	iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH);
+
 	switch (vif->type) {
 	case NL80211_IFTYPE_AP:
 		csa_vif =
@@ -3267,6 +3782,12 @@
 
 	mutex_lock(&mvm->mutex);
 
+	if (mvmvif->csa_failed) {
+		mvmvif->csa_failed = false;
+		ret = -EIO;
+		goto out_unlock;
+	}
+
 	if (vif->type == NL80211_IFTYPE_STATION) {
 		struct iwl_mvm_sta *mvmsta;
 
@@ -3346,6 +3867,154 @@
 	}
 }
 
+static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
+				  struct survey_info *survey)
+{
+	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+	int ret;
+
+	memset(survey, 0, sizeof(*survey));
+
+	/* only support global statistics right now */
+	if (idx != 0)
+		return -ENOENT;
+
+	if (!(mvm->fw->ucode_capa.capa[0] &
+			IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+		return -ENOENT;
+
+	mutex_lock(&mvm->mutex);
+
+	if (mvm->ucode_loaded) {
+		ret = iwl_mvm_request_statistics(mvm, false);
+		if (ret)
+			goto out;
+	}
+
+	survey->filled = SURVEY_INFO_TIME |
+			 SURVEY_INFO_TIME_RX |
+			 SURVEY_INFO_TIME_TX |
+			 SURVEY_INFO_TIME_SCAN;
+	survey->time = mvm->accu_radio_stats.on_time_rf +
+		       mvm->radio_stats.on_time_rf;
+	do_div(survey->time, USEC_PER_MSEC);
+
+	survey->time_rx = mvm->accu_radio_stats.rx_time +
+			  mvm->radio_stats.rx_time;
+	do_div(survey->time_rx, USEC_PER_MSEC);
+
+	survey->time_tx = mvm->accu_radio_stats.tx_time +
+			  mvm->radio_stats.tx_time;
+	do_div(survey->time_tx, USEC_PER_MSEC);
+
+	survey->time_scan = mvm->accu_radio_stats.on_time_scan +
+			    mvm->radio_stats.on_time_scan;
+	do_div(survey->time_scan, USEC_PER_MSEC);
+
+	ret = 0;
+ out:
+	mutex_unlock(&mvm->mutex);
+	return ret;
+}
+
+static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_sta *sta,
+				       struct station_info *sinfo)
+{
+	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+	if (!(mvm->fw->ucode_capa.capa[0] &
+				IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+		return;
+
+	/* if beacon filtering isn't on mac80211 does it anyway */
+	if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER))
+		return;
+
+	if (!vif->bss_conf.assoc)
+		return;
+
+	mutex_lock(&mvm->mutex);
+
+	if (mvmvif->ap_sta_id != mvmsta->sta_id)
+		goto unlock;
+
+	if (iwl_mvm_request_statistics(mvm, false))
+		goto unlock;
+
+	sinfo->rx_beacon = mvmvif->beacon_stats.num_beacons +
+			   mvmvif->beacon_stats.accu_num_beacons;
+	sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX);
+	if (mvmvif->beacon_stats.avg_signal) {
+		/* firmware only reports a value after RXing a few beacons */
+		sinfo->rx_beacon_signal_avg = mvmvif->beacon_stats.avg_signal;
+		sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
+	}
+ unlock:
+	mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       const struct ieee80211_event *event)
+{
+#define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...)	\
+	do {							\
+		if ((_cnt) && --(_cnt))				\
+			break;					\
+		iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\
+	} while (0)
+
+	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+	struct iwl_fw_dbg_trigger_tlv *trig;
+	struct iwl_fw_dbg_trigger_mlme *trig_mlme;
+
+	if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
+		return;
+
+	if (event->u.mlme.status == MLME_SUCCESS)
+		return;
+
+	trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
+	trig_mlme = (void *)trig->data;
+	if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+		return;
+
+	if (event->u.mlme.data == ASSOC_EVENT) {
+		if (event->u.mlme.status == MLME_DENIED)
+			CHECK_MLME_TRIGGER(mvm, trig, buf,
+					   trig_mlme->stop_assoc_denied,
+					   "DENIED ASSOC: reason %d",
+					    event->u.mlme.reason);
+		else if (event->u.mlme.status == MLME_TIMEOUT)
+			CHECK_MLME_TRIGGER(mvm, trig, buf,
+					   trig_mlme->stop_assoc_timeout,
+					   "ASSOC TIMEOUT");
+	} else if (event->u.mlme.data == AUTH_EVENT) {
+		if (event->u.mlme.status == MLME_DENIED)
+			CHECK_MLME_TRIGGER(mvm, trig, buf,
+					   trig_mlme->stop_auth_denied,
+					   "DENIED AUTH: reason %d",
+					   event->u.mlme.reason);
+		else if (event->u.mlme.status == MLME_TIMEOUT)
+			CHECK_MLME_TRIGGER(mvm, trig, buf,
+					   trig_mlme->stop_auth_timeout,
+					   "AUTH TIMEOUT");
+	} else if (event->u.mlme.data == DEAUTH_RX_EVENT) {
+		CHECK_MLME_TRIGGER(mvm, trig, buf,
+				   trig_mlme->stop_rx_deauth,
+				   "DEAUTH RX %d", event->u.mlme.reason);
+	} else if (event->u.mlme.data == DEAUTH_TX_EVENT) {
+		CHECK_MLME_TRIGGER(mvm, trig, buf,
+				   trig_mlme->stop_tx_deauth,
+				   "DEAUTH TX %d", event->u.mlme.reason);
+	}
+#undef CHECK_MLME_TRIGGER
+}
+
 const struct ieee80211_ops iwl_mvm_hw_ops = {
 	.tx = iwl_mvm_mac_tx,
 	.ampdu_action = iwl_mvm_mac_ampdu_action,
@@ -3399,6 +4068,8 @@
 	.tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch,
 	.tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch,
 
+	.event_callback = iwl_mvm_mac_event_callback,
+
 	CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
 
 #ifdef CONFIG_PM_SLEEP
@@ -3412,4 +4083,6 @@
 #endif
 	.set_default_unicast_key = iwl_mvm_set_default_unicast_key,
 #endif
+	.get_survey = iwl_mvm_mac_get_survey,
+	.sta_statistics = iwl_mvm_mac_sta_statistics,
 };
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index d4d4d72..52a0da5 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -75,6 +75,7 @@
 #include "iwl-trans.h"
 #include "iwl-notif-wait.h"
 #include "iwl-eeprom-parse.h"
+#include "iwl-fw-file.h"
 #include "sta.h"
 #include "fw-api.h"
 #include "constants.h"
@@ -119,11 +120,13 @@
  *	We will register to mac80211 to have testmode working. The NIC must not
  *	be up'ed after the INIT fw asserted. This is useful to be able to use
  *	proprietary tools over testmode to debug the INIT fw.
+ * @tfd_q_hang_detect: enabled the detection of hung transmit queues
  * @power_scheme: CAM(Continuous Active Mode)-1, BPS(Balanced Power
  *	Save)-2(default), LP(Low Power)-3
  */
 struct iwl_mvm_mod_params {
 	bool init_dbg;
+	bool tfd_q_hang_detect;
 	int power_scheme;
 };
 extern struct iwl_mvm_mod_params iwlmvm_mod_params;
@@ -143,6 +146,19 @@
 	u32 op_mode_len;
 };
 
+/**
+ * struct iwl_mvm_dump_desc - describes the dump
+ * @len: length of trig_desc->data
+ * @trig_desc: the description of the dump
+ */
+struct iwl_mvm_dump_desc {
+	size_t len;
+	/* must be last */
+	struct iwl_fw_error_dump_trigger_desc trig_desc;
+};
+
+extern struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert;
+
 struct iwl_mvm_phy_ctxt {
 	u16 id;
 	u16 color;
@@ -276,6 +292,7 @@
 	IWL_MVM_REF_TM_CMD,
 	IWL_MVM_REF_EXIT_WORK,
 	IWL_MVM_REF_PROTECT_CSA,
+	IWL_MVM_REF_FW_DBG_COLLECT,
 
 	/* update debugfs.c when changing this */
 
@@ -332,10 +349,15 @@
  * @bcast_sta: station used for broadcast packets. Used by the following
  *  vifs: P2P_DEVICE, GO and AP.
  * @beacon_skb: the skb used to hold the AP/GO beacon template
- * @smps_requests: the SMPS requests of differents parts of the driver,
+ * @smps_requests: the SMPS requests of different parts of the driver,
  *	combined on update to yield the overall request to mac80211.
+ * @beacon_stats: beacon statistics, containing the # of received beacons,
+ *	# of received beacons accumulated over FW restart, and the current
+ *	average signal of beacons retrieved from the firmware
+ * @csa_failed: CSA failed to schedule time event, report an error later
  */
 struct iwl_mvm_vif {
+	struct iwl_mvm *mvm;
 	u16 id;
 	u16 color;
 	u8 ap_sta_id;
@@ -351,6 +373,11 @@
 	bool ps_disabled;
 	struct iwl_mvm_vif_bf_data bf_data;
 
+	struct {
+		u32 num_beacons, accu_num_beacons;
+		u8 avg_signal;
+	} beacon_stats;
+
 	u32 ap_beacon_time;
 
 	enum iwl_tsf_id tsf_id;
@@ -393,7 +420,6 @@
 #endif
 
 #ifdef CPTCFG_IWLWIFI_DEBUGFS
-	struct iwl_mvm *mvm;
 	struct dentry *dbgfs_dir;
 	struct dentry *dbgfs_slink;
 	struct iwl_dbgfs_pm dbgfs_pm;
@@ -408,6 +434,7 @@
 
 	/* Indicates that CSA countdown may be started */
 	bool csa_countdown;
+	bool csa_failed;
 };
 
 static inline struct iwl_mvm_vif *
@@ -531,10 +558,23 @@
 enum iwl_mvm_tdls_cs_state {
 	IWL_MVM_TDLS_SW_IDLE = 0,
 	IWL_MVM_TDLS_SW_REQ_SENT,
+	IWL_MVM_TDLS_SW_RESP_RCVD,
 	IWL_MVM_TDLS_SW_REQ_RCVD,
 	IWL_MVM_TDLS_SW_ACTIVE,
 };
 
+struct iwl_mvm_shared_mem_cfg {
+	u32 shared_mem_addr;
+	u32 shared_mem_size;
+	u32 sample_buff_addr;
+	u32 sample_buff_size;
+	u32 txfifo_addr;
+	u32 txfifo_size[TX_FIFO_MAX_NUM];
+	u32 rxfifo_size[RX_FIFO_MAX_NUM];
+	u32 page_buff_addr;
+	u32 page_buff_size;
+};
+
 struct iwl_mvm {
 	/* for logger access */
 	struct device *dev;
@@ -577,6 +617,13 @@
 
 	struct mvm_statistics_rx rx_stats;
 
+	struct {
+		u64 rx_time;
+		u64 tx_time;
+		u64 on_time_rf;
+		u64 on_time_scan;
+	} radio_stats, accu_radio_stats;
+
 	u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
 	atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES];
 
@@ -641,6 +688,8 @@
 	bool disable_power_off;
 	bool disable_power_off_d3;
 
+	u32 scan_iter_notif_enabled; /* must be u32 for debugfs_create_bool */
+
 	struct debugfs_blob_wrapper nvm_hw_blob;
 	struct debugfs_blob_wrapper nvm_sw_blob;
 	struct debugfs_blob_wrapper nvm_calib_blob;
@@ -648,6 +697,7 @@
 
 	struct iwl_mvm_frame_stats drv_rx_stats;
 	spinlock_t drv_stats_lock;
+	u16 dbgfs_rx_phyinfo;
 #endif
 
 	struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX];
@@ -669,8 +719,9 @@
 
 	/* -1 for always, 0 for never, >0 for that many times */
 	s8 restart_fw;
-	struct work_struct fw_error_dump_wk;
-	enum iwl_fw_dbg_conf fw_dbg_conf;
+	u8 fw_dbg_conf;
+	struct delayed_work fw_dump_wk;
+	struct iwl_mvm_dump_desc *fw_dump_desc;
 
 #ifdef CPTCFG_IWLWIFI_LEDS
 	struct led_classdev led;
@@ -697,6 +748,7 @@
 	void *d3_resume_sram;
 	u32 d3_test_pme_ptr;
 	struct ieee80211_vif *keep_vif;
+	u32 last_netdetect_scans; /* no. of scans in the last net-detect wake */
 #endif
 #endif
 
@@ -761,6 +813,9 @@
 	/* system time of last beacon (for AP/GO interface) */
 	u32 ap_last_beacon_gp2;
 
+	bool lar_regdom_set;
+	enum iwl_mcc_source mcc_src;
+
 	u8 low_latency_agg_frame_limit;
 
 	/* TDLS channel switch data */
@@ -782,8 +837,13 @@
 			struct cfg80211_chan_def chandef;
 			struct sk_buff *skb; /* ch sw template */
 			u32 ch_sw_tm_ie;
+
+			/* timestamp of last ch-sw request sent (GP2 time) */
+			u32 sent_timestamp;
 		} peer;
 	} tdls_cs;
+
+	struct iwl_mvm_shared_mem_cfg shared_mem_cfg;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -801,6 +861,7 @@
 	IWL_MVM_STATUS_IN_D0I3,
 	IWL_MVM_STATUS_ROC_AUX_RUNNING,
 	IWL_MVM_STATUS_D3_RECONFIG,
+	IWL_MVM_STATUS_DUMPING_FW_LOG,
 };
 
 static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
@@ -850,12 +911,51 @@
 static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm)
 {
 	return mvm->trans->cfg->d0i3 &&
+	       mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF &&
+	       !iwlwifi_mod_params.d0i3_disable &&
 	       (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_D0I3_SUPPORT);
 }
 
-static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm)
+static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm)
 {
-	return mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_DQA_SUPPORT;
+	bool nvm_lar = mvm->nvm_data->lar_enabled;
+	bool tlv_lar = mvm->fw->ucode_capa.capa[0] &
+		IWL_UCODE_TLV_CAPA_LAR_SUPPORT;
+
+	if (iwlwifi_mod_params.lar_disable)
+		return false;
+
+	/*
+	 * Enable LAR only if it is supported by the FW (TLV) &&
+	 * enabled in the NVM
+	 */
+	if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+		return nvm_lar && tlv_lar;
+	else
+		return tlv_lar;
+}
+
+static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm)
+{
+	return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WIFI_MCC_UPDATE ||
+	       mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC;
+}
+
+static inline bool iwl_mvm_is_scd_cfg_supported(struct iwl_mvm *mvm)
+{
+	return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SCD_CFG;
+}
+
+static inline bool iwl_mvm_bt_is_plcr_supported(struct iwl_mvm *mvm)
+{
+	return (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_BT_COEX_PLCR) &&
+		IWL_MVM_BT_COEX_CORUNNING;
+}
+
+static inline bool iwl_mvm_bt_is_rrc_supported(struct iwl_mvm *mvm)
+{
+	return (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_BT_COEX_RRC) &&
+		IWL_MVM_BT_COEX_RRC;
 }
 
 extern const u8 iwl_mvm_ac_to_tx_fifo[];
@@ -926,17 +1026,45 @@
 }
 
 /* Statistics */
-int iwl_mvm_rx_reply_statistics(struct iwl_mvm *mvm,
-				struct iwl_rx_cmd_buffer *rxb,
-				struct iwl_device_cmd *cmd);
+void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
+				  struct iwl_rx_packet *pkt);
 int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
 			  struct iwl_rx_cmd_buffer *rxb,
 			  struct iwl_device_cmd *cmd);
+int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear);
+void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm);
 
 /* NVM */
 int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic);
 int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm);
 
+static inline u8 iwl_mvm_get_valid_tx_ant(struct iwl_mvm *mvm)
+{
+	return mvm->nvm_data && mvm->nvm_data->valid_tx_ant ?
+	       mvm->fw->valid_tx_ant & mvm->nvm_data->valid_tx_ant :
+	       mvm->fw->valid_tx_ant;
+}
+
+static inline u8 iwl_mvm_get_valid_rx_ant(struct iwl_mvm *mvm)
+{
+	return mvm->nvm_data && mvm->nvm_data->valid_rx_ant ?
+	       mvm->fw->valid_rx_ant & mvm->nvm_data->valid_rx_ant :
+	       mvm->fw->valid_rx_ant;
+}
+
+static inline u32 iwl_mvm_get_phy_config(struct iwl_mvm *mvm)
+{
+	u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN |
+			   FW_PHY_CFG_RX_CHAIN);
+	u32 valid_rx_ant = iwl_mvm_get_valid_rx_ant(mvm);
+	u32 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
+
+	phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS |
+		      valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS;
+
+	return mvm->fw->phy_config & phy_config;
+}
+
 int iwl_mvm_up(struct iwl_mvm *mvm);
 int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm);
 
@@ -970,6 +1098,9 @@
 			 struct iwl_device_cmd *cmd);
 int iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
 			    struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_shared_mem_cfg_notif(struct iwl_mvm *mvm,
+				    struct iwl_rx_cmd_buffer *rxb,
+				    struct iwl_device_cmd *cmd);
 
 /* MVM PHY */
 int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
@@ -1012,33 +1143,24 @@
 int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 
 /* Quota management */
-int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm, bool force_upload,
 			  struct ieee80211_vif *disabled_vif);
 
 /* Scanning */
 int iwl_mvm_scan_size(struct iwl_mvm *mvm);
-int iwl_mvm_scan_request(struct iwl_mvm *mvm,
-			 struct ieee80211_vif *vif,
-			 struct cfg80211_scan_request *req);
-int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
-			     struct iwl_device_cmd *cmd);
-int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
-			     struct iwl_device_cmd *cmd);
 int iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
 int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm, bool is_sched_scan);
+void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm);
 
 /* Scheduled scan */
 int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
 					   struct iwl_rx_cmd_buffer *rxb,
 					   struct iwl_device_cmd *cmd);
-int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
-			      struct ieee80211_vif *vif,
-			      struct cfg80211_sched_scan_request *req,
-			      struct ieee80211_scan_ies *ies);
+int iwl_mvm_rx_scan_offload_iter_complete_notif(struct iwl_mvm *mvm,
+						struct iwl_rx_cmd_buffer *rxb,
+						struct iwl_device_cmd *cmd);
 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_scan_offload_start(struct iwl_mvm *mvm,
 			       struct ieee80211_vif *vif,
 			       struct cfg80211_sched_scan_request *req,
@@ -1091,9 +1213,7 @@
 
 /* rate scaling */
 int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init);
-void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm,
-				struct iwl_mvm_frame_stats *stats,
-				u32 rate, bool agg);
+void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg);
 int rs_pretty_print_rate(char *buf, const u32 rate);
 void rs_update_last_rssi(struct iwl_mvm *mvm,
 			 struct iwl_lq_sta *lq_sta,
@@ -1159,6 +1279,8 @@
 int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 bool iwl_mvm_ref_taken(struct iwl_mvm *mvm);
 void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
+int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode);
+int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode);
 int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
 
 /* BT Coex */
@@ -1167,7 +1289,7 @@
 			     struct iwl_rx_cmd_buffer *rxb,
 			     struct iwl_device_cmd *cmd);
 void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-			   enum ieee80211_rssi_event rssi_event);
+			   enum ieee80211_rssi_event_data);
 void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm);
 u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
 				struct ieee80211_sta *sta);
@@ -1180,7 +1302,6 @@
 u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
 			   struct ieee80211_tx_info *info, u8 ac);
 
-bool iwl_mvm_bt_coex_is_ant_avail_old(struct iwl_mvm *mvm, u8 ant);
 bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm);
 void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm);
 int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm);
@@ -1188,7 +1309,7 @@
 				 struct iwl_rx_cmd_buffer *rxb,
 				 struct iwl_device_cmd *cmd);
 void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-			       enum ieee80211_rssi_event rssi_event);
+			       enum ieee80211_rssi_event_data);
 u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
 				    struct ieee80211_sta *sta);
 bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
@@ -1199,17 +1320,6 @@
 				      struct iwl_rx_cmd_buffer *rxb,
 				      struct iwl_device_cmd *cmd);
 
-enum iwl_bt_kill_msk {
-	BT_KILL_MSK_DEFAULT,
-	BT_KILL_MSK_NEVER,
-	BT_KILL_MSK_ALWAYS,
-	BT_KILL_MSK_MAX,
-};
-
-extern const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT];
-extern const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT];
-extern const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX];
-
 /* beacon filtering */
 #ifdef CPTCFG_IWLWIFI_DEBUGFS
 void
@@ -1260,11 +1370,13 @@
 
 /* hw scheduler queue config */
 void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn,
-			const struct iwl_trans_txq_scd_cfg *cfg);
-void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue);
+			const struct iwl_trans_txq_scd_cfg *cfg,
+			unsigned int wdg_timeout);
+void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, u8 flags);
 
-static inline void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue,
-					 u8 fifo)
+static inline
+void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue,
+			   u8 fifo, unsigned int wdg_timeout)
 {
 	struct iwl_trans_txq_scd_cfg cfg = {
 		.fifo = fifo,
@@ -1273,12 +1385,13 @@
 		.frame_limit = IWL_FRAME_LIMIT,
 	};
 
-	iwl_mvm_enable_txq(mvm, queue, 0, &cfg);
+	iwl_mvm_enable_txq(mvm, queue, 0, &cfg, wdg_timeout);
 }
 
 static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue,
 					  int fifo, int sta_id, int tid,
-					  int frame_limit, u16 ssn)
+					  int frame_limit, u16 ssn,
+					  unsigned int wdg_timeout)
 {
 	struct iwl_trans_txq_scd_cfg cfg = {
 		.fifo = fifo,
@@ -1288,12 +1401,9 @@
 		.aggregate = true,
 	};
 
-	iwl_mvm_enable_txq(mvm, queue, ssn, &cfg);
+	iwl_mvm_enable_txq(mvm, queue, ssn, &cfg, wdg_timeout);
 }
 
-/* Assoc status */
-bool iwl_mvm_is_idle(struct iwl_mvm *mvm);
-
 /* Thermal management and CT-kill */
 void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff);
 void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp);
@@ -1306,6 +1416,23 @@
 void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
 int iwl_mvm_get_temp(struct iwl_mvm *mvm);
 
+/* Location Aware Regulatory */
+struct iwl_mcc_update_resp *
+iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
+		   enum iwl_mcc_source src_id);
+int iwl_mvm_init_mcc(struct iwl_mvm *mvm);
+int iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
+			       struct iwl_rx_cmd_buffer *rxb,
+			       struct iwl_device_cmd *cmd);
+struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
+						  const char *alpha2,
+						  enum iwl_mcc_source src_id,
+						  bool *changed);
+struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm,
+							  bool *changed);
+int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm);
+void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm);
+
 /* smart fifo */
 int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 		      bool added_vif);
@@ -1344,4 +1471,65 @@
 void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
 void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
 
+int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id);
+int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
+			   const char *str, size_t len, unsigned int delay);
+int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
+				struct iwl_mvm_dump_desc *desc,
+				unsigned int delay);
+void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm);
+int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
+				struct iwl_fw_dbg_trigger_tlv *trigger,
+				const char *fmt, ...) __printf(3, 4);
+unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
+				    struct ieee80211_vif *vif,
+				    bool tdls, bool cmd_q);
+void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+			     const char *errmsg);
+static inline bool
+iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
+			     struct ieee80211_vif *vif)
+{
+	u32 trig_vif = le32_to_cpu(trig->vif_type);
+
+	return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif;
+}
+
+static inline bool
+iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm,
+				   struct iwl_fw_dbg_trigger_tlv *trig)
+{
+	return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) &&
+		(mvm->fw_dbg_conf == FW_DBG_INVALID ||
+		(BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids))));
+}
+
+static inline bool
+iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm,
+			      struct ieee80211_vif *vif,
+			      struct iwl_fw_dbg_trigger_tlv *trig)
+{
+	if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif))
+		return false;
+
+	return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig);
+}
+
+static inline void
+iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
+			       struct ieee80211_vif *vif,
+			       enum iwl_fw_dbg_trigger trig)
+{
+	struct iwl_fw_dbg_trigger_tlv *trigger;
+
+	if (!iwl_fw_dbg_trigger_enabled(mvm->fw, trig))
+		return;
+
+	trigger = iwl_fw_dbg_get_trigger(mvm->fw, trig);
+	if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
+		return;
+
+	iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
+}
+
 #endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c
index 98203ab..748749e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/nvm.c
+++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c
@@ -63,18 +63,21 @@
  *
  *****************************************************************************/
 #include <linux/firmware.h>
+#include <linux/rtnetlink.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
 #include "iwl-trans.h"
 #include "iwl-csr.h"
 #include "mvm.h"
 #include "iwl-eeprom-parse.h"
 #include "iwl-eeprom-read.h"
 #include "iwl-nvm-parse.h"
+#include "iwl-prph.h"
 
 /* Default NVM size to read */
 #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
 #define IWL_MAX_NVM_SECTION_SIZE	0x1b58
-#define IWL_MAX_NVM_8000A_SECTION_SIZE	0xffc
-#define IWL_MAX_NVM_8000B_SECTION_SIZE	0x1ffc
+#define IWL_MAX_NVM_8000_SECTION_SIZE	0x1ffc
 
 #define NVM_WRITE_OPCODE 1
 #define NVM_READ_OPCODE 0
@@ -262,7 +265,9 @@
 iwl_parse_nvm_sections(struct iwl_mvm *mvm)
 {
 	struct iwl_nvm_section *sections = mvm->nvm_sections;
-	const __le16 *hw, *sw, *calib, *regulatory, *mac_override;
+	const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku;
+	bool lar_enabled;
+	u32 mac_addr0, mac_addr1;
 
 	/* Checking for required sections */
 	if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
@@ -286,22 +291,38 @@
 				"Can't parse mac_address, empty sections\n");
 			return NULL;
 		}
+
+		/* PHY_SKU section is mandatory in B0 */
+		if (!mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) {
+			IWL_ERR(mvm,
+				"Can't parse phy_sku in B0, empty sections\n");
+			return NULL;
+		}
 	}
 
 	if (WARN_ON(!mvm->cfg))
 		return NULL;
 
+	/* read the mac address from WFMP registers */
+	mac_addr0 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_0);
+	mac_addr1 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_1);
+
 	hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data;
 	sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
 	calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
 	regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data;
 	mac_override =
 		(const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data;
+	phy_sku = (const __le16 *)sections[NVM_SECTION_TYPE_PHY_SKU].data;
+
+	lar_enabled = !iwlwifi_mod_params.lar_disable &&
+		      (mvm->fw->ucode_capa.capa[0] &
+		       IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
 
 	return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
-				  regulatory, mac_override,
-				  mvm->fw->valid_tx_ant,
-				  mvm->fw->valid_rx_ant);
+				  regulatory, mac_override, phy_sku,
+				  mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant,
+				  lar_enabled, mac_addr0, mac_addr1);
 }
 
 #define MAX_NVM_FILE_LEN	16384
@@ -354,10 +375,8 @@
 	/* Maximal size depends on HW family and step */
 	if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
 		max_section_size = IWL_MAX_NVM_SECTION_SIZE;
-	else if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP)
-		max_section_size = IWL_MAX_NVM_8000A_SECTION_SIZE;
-	else /* Family 8000 B-step */
-		max_section_size = IWL_MAX_NVM_8000B_SECTION_SIZE;
+	else
+		max_section_size = IWL_MAX_NVM_8000_SECTION_SIZE;
 
 	/*
 	 * Obtain NVM image via request_firmware. Since we already used
@@ -399,6 +418,15 @@
 		IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2]));
 		IWL_INFO(mvm, "NVM Manufacturing date %08X\n",
 			 le32_to_cpu(dword_buff[3]));
+
+		/* nvm file validation, dword_buff[2] holds the file version */
+		if ((CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_C_STEP &&
+		     le32_to_cpu(dword_buff[2]) < 0xE4A) ||
+		    (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP &&
+		     le32_to_cpu(dword_buff[2]) >= 0xE4A)) {
+			ret = -EFAULT;
+			goto out;
+		}
 	} else {
 		file_sec = (void *)fw_entry->data;
 	}
@@ -497,6 +525,8 @@
 	int ret, section;
 	u32 size_read = 0;
 	u8 *nvm_buffer, *temp;
+	const char *nvm_file_B = mvm->cfg->default_nvm_file_B_step;
+	const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step;
 
 	if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
 		return -EINVAL;
@@ -555,16 +585,289 @@
 
 	/* load external NVM if configured */
 	if (mvm->nvm_file_name) {
-		/* move to External NVM flow */
+		/* read External NVM file - take the default */
 		ret = iwl_mvm_read_external_nvm(mvm);
-		if (ret)
-			return ret;
+		if (ret) {
+			/* choose the nvm_file name according to the
+			 * HW step
+			 */
+			if (CSR_HW_REV_STEP(mvm->trans->hw_rev) ==
+			    SILICON_B_STEP)
+				mvm->nvm_file_name = nvm_file_B;
+			else
+				mvm->nvm_file_name = nvm_file_C;
+
+			if (ret == -EFAULT && mvm->nvm_file_name) {
+				/* in case nvm file was failed try again */
+				ret = iwl_mvm_read_external_nvm(mvm);
+				if (ret)
+					return ret;
+			} else {
+				return ret;
+			}
+		}
 	}
 
 	/* parse the relevant nvm sections */
 	mvm->nvm_data = iwl_parse_nvm_sections(mvm);
 	if (!mvm->nvm_data)
 		return -ENODATA;
+	IWL_DEBUG_EEPROM(mvm->trans->dev, "nvm version = %x\n",
+			 mvm->nvm_data->nvm_version);
+
+	return 0;
+}
+
+struct iwl_mcc_update_resp *
+iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
+		   enum iwl_mcc_source src_id)
+{
+	struct iwl_mcc_update_cmd mcc_update_cmd = {
+		.mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]),
+		.source_id = (u8)src_id,
+	};
+	struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL;
+	struct iwl_rx_packet *pkt;
+	struct iwl_host_cmd cmd = {
+		.id = MCC_UPDATE_CMD,
+		.flags = CMD_WANT_SKB,
+		.data = { &mcc_update_cmd },
+	};
+
+	int ret;
+	u32 status;
+	int resp_len, n_channels;
+	u16 mcc;
+
+	if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
+		return ERR_PTR(-EOPNOTSUPP);
+
+	cmd.len[0] = sizeof(struct iwl_mcc_update_cmd);
+
+	IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n",
+		      alpha2[0], alpha2[1], src_id);
+
+	ret = iwl_mvm_send_cmd(mvm, &cmd);
+	if (ret)
+		return ERR_PTR(ret);
+
+	pkt = cmd.resp_pkt;
+	if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+		IWL_ERR(mvm, "Bad return from MCC_UPDATE_COMMAND (0x%08X)\n",
+			pkt->hdr.flags);
+		ret = -EIO;
+		goto exit;
+	}
+
+	/* Extract MCC response */
+	mcc_resp = (void *)pkt->data;
+	status = le32_to_cpu(mcc_resp->status);
+
+	mcc = le16_to_cpu(mcc_resp->mcc);
+
+	/* W/A for a FW/NVM issue - returns 0x00 for the world domain */
+	if (mcc == 0) {
+		mcc = 0x3030;  /* "00" - world */
+		mcc_resp->mcc = cpu_to_le16(mcc);
+	}
+
+	n_channels =  __le32_to_cpu(mcc_resp->n_channels);
+	IWL_DEBUG_LAR(mvm,
+		      "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n",
+		      status, mcc, mcc >> 8, mcc & 0xff,
+		      !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels);
+
+	resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32);
+	resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL);
+	if (!resp_cp) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	ret = 0;
+exit:
+	iwl_free_resp(&cmd);
+	if (ret)
+		return ERR_PTR(ret);
+	return resp_cp;
+}
+
+#ifdef CONFIG_ACPI
+#define WRD_METHOD		"WRDD"
+#define WRDD_WIFI		(0x07)
+#define WRDD_WIGIG		(0x10)
+
+static u32 iwl_mvm_wrdd_get_mcc(struct iwl_mvm *mvm, union acpi_object *wrdd)
+{
+	union acpi_object *mcc_pkg, *domain_type, *mcc_value;
+	u32 i;
+
+	if (wrdd->type != ACPI_TYPE_PACKAGE ||
+	    wrdd->package.count < 2 ||
+	    wrdd->package.elements[0].type != ACPI_TYPE_INTEGER ||
+	    wrdd->package.elements[0].integer.value != 0) {
+		IWL_DEBUG_LAR(mvm, "Unsupported wrdd structure\n");
+		return 0;
+	}
+
+	for (i = 1 ; i < wrdd->package.count ; ++i) {
+		mcc_pkg = &wrdd->package.elements[i];
+
+		if (mcc_pkg->type != ACPI_TYPE_PACKAGE ||
+		    mcc_pkg->package.count < 2 ||
+		    mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
+		    mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
+			mcc_pkg = NULL;
+			continue;
+		}
+
+		domain_type = &mcc_pkg->package.elements[0];
+		if (domain_type->integer.value == WRDD_WIFI)
+			break;
+
+		mcc_pkg = NULL;
+	}
+
+	if (mcc_pkg) {
+		mcc_value = &mcc_pkg->package.elements[1];
+		return mcc_value->integer.value;
+	}
+
+	return 0;
+}
+
+static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc)
+{
+	acpi_handle root_handle;
+	acpi_handle handle;
+	struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL};
+	acpi_status status;
+	u32 mcc_val;
+	struct pci_dev *pdev = to_pci_dev(mvm->dev);
+
+	root_handle = ACPI_HANDLE(&pdev->dev);
+	if (!root_handle) {
+		IWL_DEBUG_LAR(mvm,
+			      "Could not retrieve root port ACPI handle\n");
+		return -ENOENT;
+	}
+
+	/* Get the method's handle */
+	status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle);
+	if (ACPI_FAILURE(status)) {
+		IWL_DEBUG_LAR(mvm, "WRD method not found\n");
+		return -ENOENT;
+	}
+
+	/* Call WRDD with no arguments */
+	status = acpi_evaluate_object(handle, NULL, NULL, &wrdd);
+	if (ACPI_FAILURE(status)) {
+		IWL_DEBUG_LAR(mvm, "WRDC invocation failed (0x%x)\n", status);
+		return -ENOENT;
+	}
+
+	mcc_val = iwl_mvm_wrdd_get_mcc(mvm, wrdd.pointer);
+	kfree(wrdd.pointer);
+	if (!mcc_val)
+		return -ENOENT;
+
+	mcc[0] = (mcc_val >> 8) & 0xff;
+	mcc[1] = mcc_val & 0xff;
+	mcc[2] = '\0';
+	return 0;
+}
+#else /* CONFIG_ACPI */
+static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc)
+{
+	return -ENOENT;
+}
+#endif
+
+int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
+{
+	bool tlv_lar;
+	bool nvm_lar;
+	int retval;
+	struct ieee80211_regdomain *regd;
+	char mcc[3];
+
+	if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+		tlv_lar = mvm->fw->ucode_capa.capa[0] &
+			IWL_UCODE_TLV_CAPA_LAR_SUPPORT;
+		nvm_lar = mvm->nvm_data->lar_enabled;
+		if (tlv_lar != nvm_lar)
+			IWL_INFO(mvm,
+				 "Conflict between TLV & NVM regarding enabling LAR (TLV = %s NVM =%s)\n",
+				 tlv_lar ? "enabled" : "disabled",
+				 nvm_lar ? "enabled" : "disabled");
+	}
+
+	if (!iwl_mvm_is_lar_supported(mvm))
+		return 0;
+
+	/*
+	 * try to replay the last set MCC to FW. If it doesn't exist,
+	 * queue an update to cfg80211 to retrieve the default alpha2 from FW.
+	 */
+	retval = iwl_mvm_init_fw_regd(mvm);
+	if (retval != -ENOENT)
+		return retval;
+
+	/*
+	 * Driver regulatory hint for initial update, this also informs the
+	 * firmware we support wifi location updates.
+	 * Disallow scans that might crash the FW while the LAR regdomain
+	 * is not set.
+	 */
+	mvm->lar_regdom_set = false;
+
+	regd = iwl_mvm_get_current_regdomain(mvm, NULL);
+	if (IS_ERR_OR_NULL(regd))
+		return -EIO;
+
+	if (iwl_mvm_is_wifi_mcc_supported(mvm) &&
+	    !iwl_mvm_get_bios_mcc(mvm, mcc)) {
+		kfree(regd);
+		regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc,
+					     MCC_SOURCE_BIOS, NULL);
+		if (IS_ERR_OR_NULL(regd))
+			return -EIO;
+	}
+
+	retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
+	kfree(regd);
+	return retval;
+}
+
+int iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
+			       struct iwl_rx_cmd_buffer *rxb,
+			       struct iwl_device_cmd *cmd)
+{
+	struct iwl_rx_packet *pkt = rxb_addr(rxb);
+	struct iwl_mcc_chub_notif *notif = (void *)pkt->data;
+	enum iwl_mcc_source src;
+	char mcc[3];
+	struct ieee80211_regdomain *regd;
+
+	lockdep_assert_held(&mvm->mutex);
+
+	if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
+		return 0;
+
+	mcc[0] = notif->mcc >> 8;
+	mcc[1] = notif->mcc & 0xff;
+	mcc[2] = '\0';
+	src = notif->source_id;
+
+	IWL_DEBUG_LAR(mvm,
+		      "RX: received chub update mcc cmd (mcc '%s' src %d)\n",
+		      mcc, src);
+	regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL);
+	if (IS_ERR_OR_NULL(regd))
+		return 0;
+
+	regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
+	kfree(regd);
 
 	return 0;
 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index f10c327..2ae2cea 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -82,17 +82,9 @@
 #include "rs.h"
 #include "fw-api-scan.h"
 #include "time-event.h"
-#include "iwl-fw-error-dump.h"
 
-/*
- * module name, copyright, version, etc.
- */
 #define DRV_DESCRIPTION	"The new Intel(R) wireless AGN driver for Linux"
-
-#define DRV_VERSION     IWLWIFI_VERSION
-
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_VERSION(DRV_VERSION);
 MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
 MODULE_LICENSE("GPL");
 
@@ -100,6 +92,7 @@
 
 struct iwl_mvm_mod_params iwlmvm_mod_params = {
 	.power_scheme = IWL_POWER_SCHEME_BPS,
+	.tfd_q_hang_detect = true
 	/* rest of fields are 0 by default */
 };
 
@@ -109,6 +102,10 @@
 module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO);
 MODULE_PARM_DESC(power_scheme,
 		 "power management scheme: 1-active, 2-balanced, 3-low power, default: 2");
+module_param_named(tfd_q_hang_detect, iwlmvm_mod_params.tfd_q_hang_detect,
+		   bool, S_IRUGO);
+MODULE_PARM_DESC(tfd_q_hang_detect,
+		 "TFD queues hang detection (default: true");
 
 /*
  * module init and exit functions
@@ -146,13 +143,14 @@
 	struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 	u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash;
 	u32 reg_val = 0;
+	u32 phy_config = iwl_mvm_get_phy_config(mvm);
 
-	radio_cfg_type = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_TYPE) >>
-			  FW_PHY_CFG_RADIO_TYPE_POS;
-	radio_cfg_step = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_STEP) >>
-			  FW_PHY_CFG_RADIO_STEP_POS;
-	radio_cfg_dash = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_DASH) >>
-			  FW_PHY_CFG_RADIO_DASH_POS;
+	radio_cfg_type = (phy_config & FW_PHY_CFG_RADIO_TYPE) >>
+			 FW_PHY_CFG_RADIO_TYPE_POS;
+	radio_cfg_step = (phy_config & FW_PHY_CFG_RADIO_STEP) >>
+			 FW_PHY_CFG_RADIO_STEP_POS;
+	radio_cfg_dash = (phy_config & FW_PHY_CFG_RADIO_DASH) >>
+			 FW_PHY_CFG_RADIO_DASH_POS;
 
 	/* SKU control */
 	reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) <<
@@ -235,11 +233,12 @@
 		   iwl_mvm_rx_ant_coupling_notif, true),
 
 	RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
+	RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, true),
 
 	RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false),
 
-	RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
-	RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true),
+	RX_HANDLER(SCAN_ITERATION_COMPLETE,
+		   iwl_mvm_rx_scan_offload_iter_complete_notif, false),
 	RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
 		   iwl_mvm_rx_scan_offload_complete_notif, true),
 	RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_offload_results,
@@ -274,6 +273,7 @@
 	CMD(MGMT_MCAST_KEY),
 	CMD(TX_CMD),
 	CMD(TXPATH_FLUSH),
+	CMD(SHARED_MEM_CFG),
 	CMD(MAC_CONTEXT_CMD),
 	CMD(TIME_EVENT_CMD),
 	CMD(TIME_EVENT_NOTIFICATION),
@@ -309,6 +309,7 @@
 	CMD(REPLY_RX_MPDU_CMD),
 	CMD(BEACON_NOTIFICATION),
 	CMD(BEACON_TEMPLATE_CMD),
+	CMD(STATISTICS_CMD),
 	CMD(STATISTICS_NOTIFICATION),
 	CMD(EOSP_NOTIFICATION),
 	CMD(REDUCE_TX_POWER_CMD),
@@ -357,6 +358,7 @@
 	CMD(TDLS_CHANNEL_SWITCH_CMD),
 	CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION),
 	CMD(TDLS_CONFIG_CMD),
+	CMD(MCC_UPDATE_CMD),
 };
 #undef CMD
 
@@ -454,7 +456,7 @@
 	INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
 	INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
 	INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
-	INIT_WORK(&mvm->fw_error_dump_wk, iwl_mvm_fw_error_dump_wk);
+	INIT_DELAYED_WORK(&mvm->fw_dump_wk, iwl_mvm_fw_error_dump_wk);
 	INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work);
 
 	spin_lock_init(&mvm->d0i3_tx_lock);
@@ -476,17 +478,18 @@
 	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE)
 		trans_cfg.bc_table_dword = true;
 
-	if (!iwlwifi_mod_params.wd_disable)
-		trans_cfg.queue_watchdog_timeout = cfg->base_params->wd_timeout;
-	else
-		trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED;
-
 	trans_cfg.command_names = iwl_mvm_cmd_strings;
 
 	trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE;
 	trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD;
 	trans_cfg.scd_set_active = true;
 
+	trans_cfg.sdio_adma_addr = fw->sdio_adma_addr;
+
+	/* Set a short watchdog for the command queue */
+	trans_cfg.cmd_q_wdg_timeout =
+		iwl_mvm_get_wd_timeout(mvm, NULL, false, true);
+
 	snprintf(mvm->hw->wiphy->fw_version,
 		 sizeof(mvm->hw->wiphy->fw_version),
 		 "%s", fw->fw_version);
@@ -500,6 +503,7 @@
 	trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num;
 	memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv,
 	       sizeof(trans->dbg_conf_tlv));
+	trans->dbg_trigger_tlv = mvm->fw->dbg_trigger_tlv;
 
 	/* set up notification wait support */
 	iwl_notification_wait_init(&mvm->notif_wait);
@@ -517,10 +521,14 @@
 	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)
+	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;
+	} else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+		if (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_B_STEP)
+			mvm->nvm_file_name = mvm->cfg->default_nvm_file_B_step;
+		else
+			mvm->nvm_file_name = mvm->cfg->default_nvm_file_C_step;
+	}
 
 	if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name,
 		 "not allowing power-up and not having nvm_file\n"))
@@ -559,6 +567,9 @@
 	if (!mvm->scan_cmd)
 		goto out_free;
 
+	/* Set EBS as successful as long as not stated otherwise by the FW. */
+	mvm->last_ebs_successful = true;
+
 	err = iwl_mvm_mac_setup_register(mvm);
 	if (err)
 		goto out_free;
@@ -673,6 +684,37 @@
 	mutex_unlock(&mvm->mutex);
 }
 
+static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm,
+					    struct iwl_rx_packet *pkt)
+{
+	struct iwl_fw_dbg_trigger_tlv *trig;
+	struct iwl_fw_dbg_trigger_cmd *cmds_trig;
+	int i;
+
+	if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF))
+		return;
+
+	trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF);
+	cmds_trig = (void *)trig->data;
+
+	if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
+		return;
+
+	for (i = 0; i < ARRAY_SIZE(cmds_trig->cmds); i++) {
+		/* don't collect on CMD 0 */
+		if (!cmds_trig->cmds[i].cmd_id)
+			break;
+
+		if (cmds_trig->cmds[i].cmd_id != pkt->hdr.cmd)
+			continue;
+
+		iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+					    "CMD 0x%02x received",
+					    pkt->hdr.cmd);
+		break;
+	}
+}
+
 static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode,
 			       struct iwl_rx_cmd_buffer *rxb,
 			       struct iwl_device_cmd *cmd)
@@ -681,6 +723,8 @@
 	struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 	u8 i;
 
+	iwl_mvm_rx_check_trigger(mvm, pkt);
+
 	/*
 	 * Do the notification wait before RX handlers so
 	 * even if the RX handler consumes the RXB we have
@@ -815,11 +859,22 @@
 static void iwl_mvm_fw_error_dump_wk(struct work_struct *work)
 {
 	struct iwl_mvm *mvm =
-		container_of(work, struct iwl_mvm, fw_error_dump_wk);
+		container_of(work, struct iwl_mvm, fw_dump_wk.work);
+
+	if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT))
+		return;
 
 	mutex_lock(&mvm->mutex);
 	iwl_mvm_fw_error_dump(mvm);
+
+	/* start recording again if the firmware is not crashed */
+	WARN_ON_ONCE((!test_bit(STATUS_FW_ERROR, &mvm->trans->status)) &&
+		     mvm->fw->dbg_dest_tlv &&
+		     iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf));
+
 	mutex_unlock(&mvm->mutex);
+
+	iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT);
 }
 
 void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
@@ -836,18 +891,7 @@
 	 * the next start() call from mac80211. If restart isn't called
 	 * (no fw restart) scan status will stay busy.
 	 */
-	switch (mvm->scan_status) {
-	case IWL_MVM_SCAN_NONE:
-		break;
-	case IWL_MVM_SCAN_OS:
-		ieee80211_scan_completed(mvm->hw, true);
-		break;
-	case IWL_MVM_SCAN_SCHED:
-		/* Sched scan will be restarted by mac80211 in restart_hw. */
-		if (!mvm->restart_fw)
-			ieee80211_sched_scan_stopped(mvm->hw);
-		break;
-	}
+	iwl_mvm_report_scan_aborted(mvm);
 
 	/*
 	 * If we're restarting already, don't cycle restarts.
@@ -855,7 +899,10 @@
 	 * If WoWLAN fw asserted, don't restart either, mac80211
 	 * can't recover this since we're already half suspended.
 	 */
-	if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+	if (!mvm->restart_fw && fw_error) {
+		iwl_mvm_fw_dbg_collect_desc(mvm, &iwl_mvm_dump_desc_assert, 0);
+	} else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART,
+				    &mvm->status)) {
 		struct iwl_mvm_reprobe *reprobe;
 
 		IWL_ERR(mvm,
@@ -879,16 +926,13 @@
 		reprobe->dev = mvm->trans->dev;
 		INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
 		schedule_work(&reprobe->work);
-	} else if (mvm->cur_ucode == IWL_UCODE_REGULAR &&
-		   (!fw_error || mvm->restart_fw)) {
+	} else if (mvm->cur_ucode == IWL_UCODE_REGULAR) {
 		/* don't let the transport/FW power down */
 		iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
 
 		if (fw_error && mvm->restart_fw > 0)
 			mvm->restart_fw--;
 		ieee80211_restart_hw(mvm->hw);
-	} else if (fw_error) {
-		schedule_work(&mvm->fw_error_dump_wk);
 	}
 }
 
@@ -1031,7 +1075,8 @@
 out:
 	rcu_read_unlock();
 }
-static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
+
+int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
 {
 	struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 	u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
@@ -1047,6 +1092,7 @@
 	};
 	struct iwl_d3_manager_config d3_cfg_cmd = {
 		.min_sleep_time = cpu_to_le32(1000),
+		.wakeup_flags = cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR),
 	};
 
 	IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n");
@@ -1115,7 +1161,7 @@
 
 	if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc &&
 	    mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
-		ieee80211_connection_loss(vif);
+		iwl_mvm_connection_loss(mvm, vif, "D0i3");
 }
 
 void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq)
@@ -1146,7 +1192,7 @@
 
 	if (mvm->d0i3_offloading && qos_seq) {
 		/* update qos seq numbers if offloading was enabled */
-		mvm_ap_sta = (struct iwl_mvm_sta *)sta->drv_priv;
+		mvm_ap_sta = iwl_mvm_sta_from_mac80211(sta);
 		for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
 			u16 seq = le16_to_cpu(qos_seq[i]);
 			/* firmware stores last-used one, we store next one */
@@ -1211,6 +1257,10 @@
 	iwl_free_resp(&get_status_cmd);
 out:
 	iwl_mvm_d0i3_enable_tx(mvm, qos_seq);
+
+	/* the FW might have updated the regdomain */
+	iwl_mvm_update_changed_regdom(mvm);
+
 	iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK);
 	mutex_unlock(&mvm->mutex);
 }
@@ -1245,7 +1295,7 @@
 	return ret;
 }
 
-static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
+int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
 {
 	struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 
diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
index 1c0d4a4..6c29e02 100644
--- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
@@ -67,7 +67,7 @@
 #include "fw-api.h"
 #include "mvm.h"
 
-/* Maps the driver specific channel width definition to the the fw values */
+/* Maps the driver specific channel width definition to the fw values */
 u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
 {
 	switch (chandef->width) {
@@ -170,13 +170,17 @@
 		active_cnt = 2;
 	}
 
-	cmd->rxchain_info = cpu_to_le32(mvm->fw->valid_rx_ant <<
+	cmd->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
 					PHY_RX_CHAIN_VALID_POS);
 	cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
 	cmd->rxchain_info |= cpu_to_le32(active_cnt <<
 					 PHY_RX_CHAIN_MIMO_CNT_POS);
+#ifdef CPTCFG_IWLWIFI_DEBUGFS
+	if (unlikely(mvm->dbgfs_rx_phyinfo))
+		cmd->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
+#endif
 
-	cmd->txchain_info = cpu_to_le32(mvm->fw->valid_tx_ant);
+	cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
 }
 
 /*
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c
index a33a46e..cb87f0c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/power.c
+++ b/drivers/net/wireless/iwlwifi/mvm/power.c
@@ -66,6 +66,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/etherdevice.h>
 
 #include <net/mac80211.h>
 
@@ -357,7 +358,7 @@
 	cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
 
 	if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) ||
-	    !mvmvif->pm_enabled || iwl_mvm_tdls_sta_count(mvm, vif))
+	    !mvmvif->pm_enabled)
 		return;
 
 	cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@@ -491,7 +492,7 @@
 
 	if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid,
 		   ETH_ALEN))
-		memset(mvmvif->uapsd_misbehaving_bssid, 0, ETH_ALEN);
+		eth_zero_addr(mvmvif->uapsd_misbehaving_bssid);
 }
 
 static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac,
@@ -638,6 +639,10 @@
 	if (vifs->ap_vif)
 		ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);
 
+	/* don't allow PM if any TDLS stations exist */
+	if (iwl_mvm_tdls_sta_count(mvm, NULL))
+		return;
+
 	/* enable PM on bss if bss stand alone */
 	if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) {
 		bss_mvmvif->pm_enabled = true;
diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c
index f392b35..cd19bf7 100644
--- a/drivers/net/wireless/iwlwifi/mvm/quota.c
+++ b/drivers/net/wireless/iwlwifi/mvm/quota.c
@@ -172,6 +172,7 @@
 }
 
 int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
+			  bool force_update,
 			  struct ieee80211_vif *disabled_vif)
 {
 	struct iwl_time_quota_cmd cmd = {};
@@ -309,7 +310,7 @@
 			  "zero quota on binding %d\n", i);
 	}
 
-	if (!send) {
+	if (!send && !force_update) {
 		/* don't send a practically unchanged command, the firmware has
 		 * to re-initialize a lot of state and that can have an adverse
 		 * impact on it
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
index cf25e6b..9cc013c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.c
@@ -39,28 +39,16 @@
 #include "sta.h"
 #include "iwl-op-mode.h"
 #include "mvm.h"
+#include "debugfs.h"
 
 #define RS_NAME "iwl-mvm-rs"
 
-#define NUM_TRY_BEFORE_ANT_TOGGLE       1
-#define RS_LEGACY_RETRIES_PER_RATE      1
-#define RS_HT_VHT_RETRIES_PER_RATE      2
-#define RS_HT_VHT_RETRIES_PER_RATE_TW   1
-#define RS_INITIAL_MIMO_NUM_RATES       3
-#define RS_INITIAL_SISO_NUM_RATES       3
-#define RS_INITIAL_LEGACY_NUM_RATES     LINK_QUAL_MAX_RETRY_NUM
-#define RS_SECONDARY_LEGACY_NUM_RATES   LINK_QUAL_MAX_RETRY_NUM
-#define RS_SECONDARY_SISO_NUM_RATES     3
-#define RS_SECONDARY_SISO_RETRIES       1
-
 #define IWL_RATE_MAX_WINDOW		62	/* # tx in history window */
-#define IWL_RATE_MIN_FAILURE_TH		3	/* min failures to calc tpt */
-#define IWL_RATE_MIN_SUCCESS_TH		8	/* min successes to calc tpt */
 
-/* max allowed rate miss before sync LQ cmd */
-#define IWL_MISSED_RATE_MAX		15
-#define RS_STAY_IN_COLUMN_TIMEOUT       (5*HZ)
-#define RS_IDLE_TIMEOUT                 (5*HZ)
+/* Calculations of success ratio are done in fixed point where 12800 is 100%.
+ * Use this macro when dealing with thresholds consts set as a percentage
+ */
+#define RS_PERCENT(x) (128 * x)
 
 static u8 rs_ht_to_legacy[] = {
 	[IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX,
@@ -146,9 +134,12 @@
 #define MAX_NEXT_COLUMNS 7
 #define MAX_COLUMN_CHECKS 3
 
+struct rs_tx_column;
+
 typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm,
 				     struct ieee80211_sta *sta,
-				     struct iwl_scale_tbl_info *tbl);
+				     struct iwl_scale_tbl_info *tbl,
+				     const struct rs_tx_column *next_col);
 
 struct rs_tx_column {
 	enum rs_column_mode mode;
@@ -159,31 +150,42 @@
 };
 
 static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
-			 struct iwl_scale_tbl_info *tbl)
+			 struct iwl_scale_tbl_info *tbl,
+			 const struct rs_tx_column *next_col)
 {
-	return iwl_mvm_bt_coex_is_ant_avail(mvm, tbl->rate.ant);
+	return iwl_mvm_bt_coex_is_ant_avail(mvm, next_col->ant);
 }
 
 static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
-			  struct iwl_scale_tbl_info *tbl)
+			  struct iwl_scale_tbl_info *tbl,
+			  const struct rs_tx_column *next_col)
 {
+	struct iwl_mvm_sta *mvmsta;
+	struct iwl_mvm_vif *mvmvif;
+
 	if (!sta->ht_cap.ht_supported)
 		return false;
 
 	if (sta->smps_mode == IEEE80211_SMPS_STATIC)
 		return false;
 
-	if (num_of_ant(mvm->fw->valid_tx_ant) < 2)
+	if (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) < 2)
 		return false;
 
 	if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
 		return false;
 
+	mvmsta = iwl_mvm_sta_from_mac80211(sta);
+	mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+	if (iwl_mvm_vif_low_latency(mvmvif) && mvmsta->vif->p2p)
+		return false;
+
 	return true;
 }
 
 static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
-			  struct iwl_scale_tbl_info *tbl)
+			  struct iwl_scale_tbl_info *tbl,
+			  const struct rs_tx_column *next_col)
 {
 	if (!sta->ht_cap.ht_supported)
 		return false;
@@ -192,7 +194,8 @@
 }
 
 static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
-			 struct iwl_scale_tbl_info *tbl)
+			 struct iwl_scale_tbl_info *tbl,
+			 const struct rs_tx_column *next_col)
 {
 	struct rs_rate *rate = &tbl->rate;
 	struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
@@ -613,7 +616,8 @@
  * at this rate.  window->data contains the bitmask of successful
  * packets.
  */
-static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
+static int _rs_collect_tx_data(struct iwl_mvm *mvm,
+			       struct iwl_scale_tbl_info *tbl,
 			       int scale_index, int attempts, int successes,
 			       struct iwl_rate_scale_data *window)
 {
@@ -668,8 +672,8 @@
 	fail_count = window->counter - window->success_counter;
 
 	/* Calculate average throughput, if we have enough history. */
-	if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) ||
-	    (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH))
+	if ((fail_count >= IWL_MVM_RS_RATE_MIN_FAILURE_TH) ||
+	    (window->success_counter >= IWL_MVM_RS_RATE_MIN_SUCCESS_TH))
 		window->average_tpt = (window->success_ratio * tpt + 64) / 128;
 	else
 		window->average_tpt = IWL_INVALID_VALUE;
@@ -677,7 +681,8 @@
 	return 0;
 }
 
-static int rs_collect_tx_data(struct iwl_lq_sta *lq_sta,
+static int rs_collect_tx_data(struct iwl_mvm *mvm,
+			      struct iwl_lq_sta *lq_sta,
 			      struct iwl_scale_tbl_info *tbl,
 			      int scale_index, int attempts, int successes,
 			      u8 reduced_txp)
@@ -698,7 +703,7 @@
 	/* Select window for current tx bit rate */
 	window = &(tbl->win[scale_index]);
 
-	ret = _rs_collect_tx_data(tbl, scale_index, attempts, successes,
+	ret = _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes,
 				  window);
 	if (ret)
 		return ret;
@@ -707,7 +712,7 @@
 		return -EINVAL;
 
 	window = &tbl->tpc_win[reduced_txp];
-	return _rs_collect_tx_data(tbl, scale_index, attempts, successes,
+	return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes,
 				   window);
 }
 
@@ -810,6 +815,8 @@
 		rate->ldpc = true;
 	if (ucode_rate & RATE_MCS_VHT_STBC_MSK)
 		rate->stbc = true;
+	if (ucode_rate & RATE_MCS_BF_MSK)
+		rate->bfer = true;
 
 	rate->bw = ucode_rate & RATE_MCS_CHAN_WIDTH_MSK;
 
@@ -819,7 +826,9 @@
 
 		if (nss == 1) {
 			rate->type = LQ_HT_SISO;
-			WARN_ON_ONCE(!rate->stbc && num_of_ant != 1);
+			WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1,
+				  "stbc %d bfer %d",
+				  rate->stbc, rate->bfer);
 		} else if (nss == 2) {
 			rate->type = LQ_HT_MIMO2;
 			WARN_ON_ONCE(num_of_ant != 2);
@@ -832,7 +841,9 @@
 
 		if (nss == 1) {
 			rate->type = LQ_VHT_SISO;
-			WARN_ON_ONCE(!rate->stbc && num_of_ant != 1);
+			WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1,
+				  "stbc %d bfer %d",
+				  rate->stbc, rate->bfer);
 		} else if (nss == 2) {
 			rate->type = LQ_VHT_MIMO2;
 			WARN_ON_ONCE(num_of_ant != 2);
@@ -928,7 +939,6 @@
 			break;
 		if (rate_mask & (1 << low))
 			break;
-		IWL_DEBUG_RATE(mvm, "Skipping masked lower rate: %d\n", low);
 	}
 
 	high = index;
@@ -938,7 +948,6 @@
 			break;
 		if (rate_mask & (1 << high))
 			break;
-		IWL_DEBUG_RATE(mvm, "Skipping masked higher rate: %d\n", high);
 	}
 
 	return (high << 8) | low;
@@ -1004,7 +1013,7 @@
 	}
 
 	if (num_of_ant(rate->ant) > 1)
-		rate->ant = first_antenna(mvm->fw->valid_tx_ant);
+		rate->ant = first_antenna(iwl_mvm_get_valid_tx_ant(mvm));
 
 	/* Relevant in both switching to SISO or Legacy */
 	rate->sgi = false;
@@ -1013,13 +1022,41 @@
 		rs_get_lower_rate_in_column(lq_sta, rate);
 }
 
-/* Simple function to compare two rate scale table types */
-static inline bool rs_rate_match(struct rs_rate *a,
-				 struct rs_rate *b)
+/* Check if both rates are identical
+ * allow_ant_mismatch enables matching a SISO rate on ANT_A or ANT_B
+ * with a rate indicating STBC/BFER and ANT_AB.
+ */
+static inline bool rs_rate_equal(struct rs_rate *a,
+				 struct rs_rate *b,
+				 bool allow_ant_mismatch)
+
+{
+	bool ant_match = (a->ant == b->ant) && (a->stbc == b->stbc) &&
+		(a->bfer == b->bfer);
+
+	if (allow_ant_mismatch) {
+		if (a->stbc || a->bfer) {
+			WARN_ONCE(a->ant != ANT_AB, "stbc %d bfer %d ant %d",
+				  a->stbc, a->bfer, a->ant);
+			ant_match |= (b->ant == ANT_A || b->ant == ANT_B);
+		} else if (b->stbc || b->bfer) {
+			WARN_ONCE(b->ant != ANT_AB, "stbc %d bfer %d ant %d",
+				  b->stbc, b->bfer, b->ant);
+			ant_match |= (a->ant == ANT_A || a->ant == ANT_B);
+		}
+	}
+
+	return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi) &&
+		(a->ldpc == b->ldpc) && (a->index == b->index) && ant_match;
+}
+
+/* Check if both rates share the same column */
+static inline bool rs_rate_column_match(struct rs_rate *a,
+					struct rs_rate *b)
 {
 	bool ant_match;
 
-	if (a->stbc)
+	if (a->stbc || a->bfer)
 		ant_match = (b->ant == ANT_A || b->ant == ANT_B);
 	else
 		ant_match = (a->ant == b->ant);
@@ -1028,16 +1065,35 @@
 		&& ant_match;
 }
 
-static u32 rs_ch_width_from_mac_flags(enum mac80211_rate_control_flags flags)
+static inline enum rs_column rs_get_column_from_rate(struct rs_rate *rate)
 {
-	if (flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
-		return RATE_MCS_CHAN_WIDTH_40;
-	else if (flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
-		return RATE_MCS_CHAN_WIDTH_80;
-	else if (flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
-		return RATE_MCS_CHAN_WIDTH_160;
+	if (is_legacy(rate)) {
+		if (rate->ant == ANT_A)
+			return RS_COLUMN_LEGACY_ANT_A;
 
-	return RATE_MCS_CHAN_WIDTH_20;
+		if (rate->ant == ANT_B)
+			return RS_COLUMN_LEGACY_ANT_B;
+
+		goto err;
+	}
+
+	if (is_siso(rate)) {
+		if (rate->ant == ANT_A || rate->stbc || rate->bfer)
+			return rate->sgi ? RS_COLUMN_SISO_ANT_A_SGI :
+				RS_COLUMN_SISO_ANT_A;
+
+		if (rate->ant == ANT_B)
+			return rate->sgi ? RS_COLUMN_SISO_ANT_B_SGI :
+				RS_COLUMN_SISO_ANT_B;
+
+		goto err;
+	}
+
+	if (is_mimo(rate))
+		return rate->sgi ? RS_COLUMN_MIMO2_SGI : RS_COLUMN_MIMO2;
+
+err:
+	return RS_COLUMN_INVALID;
 }
 
 static u8 rs_get_tid(struct ieee80211_hdr *hdr)
@@ -1060,15 +1116,17 @@
 {
 	int legacy_success;
 	int retries;
-	int mac_index, i;
+	int i;
 	struct iwl_lq_cmd *table;
-	enum mac80211_rate_control_flags mac_flags;
-	u32 ucode_rate;
-	struct rs_rate rate;
+	u32 lq_hwrate;
+	struct rs_rate lq_rate, tx_resp_rate;
 	struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
 	u8 reduced_txp = (uintptr_t)info->status.status_driver_data[0];
+	u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1];
 	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
 	struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta;
+	bool allow_ant_mismatch = mvm->fw->ucode_capa.api[0] &
+		IWL_UCODE_TLV_API_LQ_SS_PARAMS;
 
 	/* Treat uninitialized rate scaling data same as non-existing. */
 	if (!lq_sta) {
@@ -1079,53 +1137,47 @@
 		return;
 	}
 
-#ifdef CPTCFG_MAC80211_DEBUGFS
-	/* Disable last tx check if we are debugging with fixed rate */
-	if (lq_sta->pers.dbg_fixed_rate) {
-		IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n");
-		return;
-	}
-#endif
 	/* This packet was aggregated but doesn't carry status info */
 	if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
 	    !(info->flags & IEEE80211_TX_STAT_AMPDU))
 		return;
 
-	/*
-	 * Ignore this Tx frame response if its initial rate doesn't match
-	 * that of latest Link Quality command.  There may be stragglers
-	 * from a previous Link Quality command, but we're no longer interested
-	 * in those; they're either from the "active" mode while we're trying
-	 * to check "search" mode, or a prior "search" mode after we've moved
-	 * to a new "search" mode (which might become the new "active" mode).
-	 */
-	table = &lq_sta->lq;
-	ucode_rate = le32_to_cpu(table->rs_table[0]);
-	rs_rate_from_ucode_rate(ucode_rate, info->band, &rate);
-	if (info->band == IEEE80211_BAND_5GHZ)
-		rate.index -= IWL_FIRST_OFDM_RATE;
-	mac_flags = info->status.rates[0].flags;
-	mac_index = info->status.rates[0].idx;
-	/* For HT packets, map MCS to PLCP */
-	if (mac_flags & IEEE80211_TX_RC_MCS) {
-		/* Remove # of streams */
-		mac_index &= RATE_HT_MCS_RATE_CODE_MSK;
-		if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE))
-			mac_index++;
-		/*
-		 * mac80211 HT index is always zero-indexed; we need to move
-		 * HT OFDM rates after CCK rates in 2.4 GHz band
-		 */
-		if (info->band == IEEE80211_BAND_2GHZ)
-			mac_index += IWL_FIRST_OFDM_RATE;
-	} else if (mac_flags & IEEE80211_TX_RC_VHT_MCS) {
-		mac_index &= RATE_VHT_MCS_RATE_CODE_MSK;
-		if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE))
-			mac_index++;
+	rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, &tx_resp_rate);
+
+#ifdef CPTCFG_MAC80211_DEBUGFS
+	/* Disable last tx check if we are debugging with fixed rate but
+	 * update tx stats */
+	if (lq_sta->pers.dbg_fixed_rate) {
+		int index = tx_resp_rate.index;
+		enum rs_column column;
+		int attempts, success;
+
+		column = rs_get_column_from_rate(&tx_resp_rate);
+		if (WARN_ONCE(column == RS_COLUMN_INVALID,
+			      "Can't map rate 0x%x to column",
+			      tx_resp_hwrate))
+			return;
+
+		if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+			attempts = info->status.ampdu_len;
+			success = info->status.ampdu_ack_len;
+		} else {
+			attempts = info->status.rates[0].count;
+			success = !!(info->flags & IEEE80211_TX_STAT_ACK);
+		}
+
+		lq_sta->pers.tx_stats[column][index].total += attempts;
+		lq_sta->pers.tx_stats[column][index].success += success;
+
+		IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n",
+			       tx_resp_hwrate, success, attempts);
+		return;
 	}
+#endif
 
 	if (time_after(jiffies,
-		       (unsigned long)(lq_sta->last_tx + RS_IDLE_TIMEOUT))) {
+		       (unsigned long)(lq_sta->last_tx +
+				       (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) {
 		int t;
 
 		IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n");
@@ -1137,28 +1189,30 @@
 	}
 	lq_sta->last_tx = jiffies;
 
+	/* Ignore this Tx frame response if its initial rate doesn't match
+	 * that of latest Link Quality command.  There may be stragglers
+	 * from a previous Link Quality command, but we're no longer interested
+	 * in those; they're either from the "active" mode while we're trying
+	 * to check "search" mode, or a prior "search" mode after we've moved
+	 * to a new "search" mode (which might become the new "active" mode).
+	 */
+	table = &lq_sta->lq;
+	lq_hwrate = le32_to_cpu(table->rs_table[0]);
+	rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate);
+
 	/* Here we actually compare this rate to the latest LQ command */
-	if ((mac_index < 0) ||
-	    (rate.sgi != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
-	    (rate.bw != rs_ch_width_from_mac_flags(mac_flags)) ||
-	    (rate.ant != info->status.antenna) ||
-	    (!!(ucode_rate & RATE_MCS_HT_MSK) !=
-	     !!(mac_flags & IEEE80211_TX_RC_MCS)) ||
-	    (!!(ucode_rate & RATE_MCS_VHT_MSK) !=
-	     !!(mac_flags & IEEE80211_TX_RC_VHT_MCS)) ||
-	    (!!(ucode_rate & RATE_HT_MCS_GF_MSK) !=
-	     !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
-	    (rate.index != mac_index)) {
+	if (!rs_rate_equal(&tx_resp_rate, &lq_rate, allow_ant_mismatch)) {
 		IWL_DEBUG_RATE(mvm,
-			       "initial rate %d does not match %d (0x%x)\n",
-			       mac_index, rate.index, ucode_rate);
+			       "initial tx resp rate 0x%x does not match 0x%x\n",
+			       tx_resp_hwrate, lq_hwrate);
+
 		/*
 		 * Since rates mis-match, the last LQ command may have failed.
 		 * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with
 		 * ... driver.
 		 */
 		lq_sta->missed_rate_counter++;
-		if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) {
+		if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) {
 			lq_sta->missed_rate_counter = 0;
 			IWL_DEBUG_RATE(mvm,
 				       "Too many rates mismatch. Send sync LQ. rs_state %d\n",
@@ -1179,14 +1233,14 @@
 		other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
 	}
 
-	if (WARN_ON_ONCE(!rs_rate_match(&rate, &curr_tbl->rate))) {
+	if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) {
 		IWL_DEBUG_RATE(mvm,
 			       "Neither active nor search matches tx rate\n");
 		tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
 		rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE");
 		tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
 		rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH");
-		rs_dump_rate(mvm, &rate, "ACTUAL");
+		rs_dump_rate(mvm, &lq_rate, "ACTUAL");
 
 		/*
 		 * no matching table found, let's by-pass the data collection
@@ -1211,9 +1265,7 @@
 		if (info->status.ampdu_ack_len == 0)
 			info->status.ampdu_len = 1;
 
-		ucode_rate = le32_to_cpu(table->rs_table[0]);
-		rs_rate_from_ucode_rate(ucode_rate, info->band, &rate);
-		rs_collect_tx_data(lq_sta, curr_tbl, rate.index,
+		rs_collect_tx_data(mvm, lq_sta, curr_tbl, lq_rate.index,
 				   info->status.ampdu_len,
 				   info->status.ampdu_ack_len,
 				   reduced_txp);
@@ -1225,9 +1277,7 @@
 					info->status.ampdu_ack_len);
 		}
 	} else {
-	/*
-	 * For legacy, update frame history with for each Tx retry.
-	 */
+		/* For legacy, update frame history with for each Tx retry. */
 		retries = info->status.rates[0].count - 1;
 		/* HW doesn't send more than 15 retries */
 		retries = min(retries, 15);
@@ -1236,21 +1286,23 @@
 		legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK);
 		/* Collect data for each rate used during failed TX attempts */
 		for (i = 0; i <= retries; ++i) {
-			ucode_rate = le32_to_cpu(table->rs_table[i]);
-			rs_rate_from_ucode_rate(ucode_rate, info->band, &rate);
+			lq_hwrate = le32_to_cpu(table->rs_table[i]);
+			rs_rate_from_ucode_rate(lq_hwrate, info->band,
+						&lq_rate);
 			/*
 			 * Only collect stats if retried rate is in the same RS
 			 * table as active/search.
 			 */
-			if (rs_rate_match(&rate, &curr_tbl->rate))
+			if (rs_rate_column_match(&lq_rate, &curr_tbl->rate))
 				tmp_tbl = curr_tbl;
-			else if (rs_rate_match(&rate, &other_tbl->rate))
+			else if (rs_rate_column_match(&lq_rate,
+						      &other_tbl->rate))
 				tmp_tbl = other_tbl;
 			else
 				continue;
 
-			rs_collect_tx_data(lq_sta, tmp_tbl, rate.index, 1,
-					   i < retries ? 0 : legacy_success,
+			rs_collect_tx_data(mvm, lq_sta, tmp_tbl, lq_rate.index,
+					   1, i < retries ? 0 : legacy_success,
 					   reduced_txp);
 		}
 
@@ -1261,7 +1313,7 @@
 		}
 	}
 	/* The last TX rate is cached in lq_sta; it's set in if/else above */
-	lq_sta->last_rate_n_flags = ucode_rate;
+	lq_sta->last_rate_n_flags = lq_hwrate;
 	IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
 done:
 	/* See if there's a better rate or modulation mode to try. */
@@ -1282,6 +1334,9 @@
 	struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 
+	if (!iwl_mvm_sta_from_mac80211(sta)->vif)
+		return;
+
 	if (!ieee80211_is_data(hdr->frame_control) ||
 	    info->flags & IEEE80211_TX_CTL_NO_ACK)
 		return;
@@ -1303,13 +1358,13 @@
 	IWL_DEBUG_RATE(mvm, "Moving to RS_STATE_STAY_IN_COLUMN\n");
 	lq_sta->rs_state = RS_STATE_STAY_IN_COLUMN;
 	if (is_legacy) {
-		lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT;
-		lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT;
-		lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT;
+		lq_sta->table_count_limit = IWL_MVM_RS_LEGACY_TABLE_COUNT;
+		lq_sta->max_failure_limit = IWL_MVM_RS_LEGACY_FAILURE_LIMIT;
+		lq_sta->max_success_limit = IWL_MVM_RS_LEGACY_SUCCESS_LIMIT;
 	} else {
-		lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT;
-		lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT;
-		lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT;
+		lq_sta->table_count_limit = IWL_MVM_RS_NON_LEGACY_TABLE_COUNT;
+		lq_sta->max_failure_limit = IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT;
+		lq_sta->max_success_limit = IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT;
 	}
 	lq_sta->table_count = 0;
 	lq_sta->total_failed = 0;
@@ -1318,6 +1373,13 @@
 	lq_sta->visited_columns = 0;
 }
 
+static inline int rs_get_max_rate_from_mask(unsigned long rate_mask)
+{
+	if (rate_mask)
+		return find_last_bit(&rate_mask, BITS_PER_LONG);
+	return IWL_RATE_INVALID;
+}
+
 static int rs_get_max_allowed_rate(struct iwl_lq_sta *lq_sta,
 				   const struct rs_tx_column *column)
 {
@@ -1420,7 +1482,7 @@
 	u32 target_tpt;
 	int rate_idx;
 
-	if (success_ratio > RS_SR_NO_DECREASE) {
+	if (success_ratio > IWL_MVM_RS_SR_NO_DECREASE) {
 		target_tpt = 100 * expected_current_tpt;
 		IWL_DEBUG_RATE(mvm,
 			       "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n",
@@ -1488,7 +1550,7 @@
 			flush_interval_passed =
 				time_after(jiffies,
 					   (unsigned long)(lq_sta->flush_timer +
-						RS_STAY_IN_COLUMN_TIMEOUT));
+							   (IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT * HZ)));
 
 		/*
 		 * Check if we should allow search for new modulation mode.
@@ -1551,9 +1613,9 @@
 static void rs_update_rate_tbl(struct iwl_mvm *mvm,
 			       struct ieee80211_sta *sta,
 			       struct iwl_lq_sta *lq_sta,
-			       struct rs_rate *rate)
+			       struct iwl_scale_tbl_info *tbl)
 {
-	rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
+	rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
 	iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
 }
 
@@ -1567,7 +1629,7 @@
 	const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column];
 	const struct rs_tx_column *next_col;
 	allow_column_func_t allow_func;
-	u8 valid_ants = mvm->fw->valid_tx_ant;
+	u8 valid_ants = iwl_mvm_get_valid_tx_ant(mvm);
 	const u16 *expected_tpt_tbl;
 	u16 tpt, max_expected_tpt;
 
@@ -1594,7 +1656,7 @@
 
 		for (j = 0; j < MAX_COLUMN_CHECKS; j++) {
 			allow_func = next_col->checks[j];
-			if (allow_func && !allow_func(mvm, sta, tbl))
+			if (allow_func && !allow_func(mvm, sta, tbl, next_col))
 				break;
 		}
 
@@ -1613,8 +1675,12 @@
 			continue;
 
 		max_rate = rs_get_max_allowed_rate(lq_sta, next_col);
-		if (WARN_ON_ONCE(max_rate == IWL_RATE_INVALID))
+		if (max_rate == IWL_RATE_INVALID) {
+			IWL_DEBUG_RATE(mvm,
+				       "Skip column %d: no rate is allowed in this column\n",
+				       next_col_id);
 			continue;
+		}
 
 		max_expected_tpt = expected_tpt_tbl[max_rate];
 		if (tpt >= max_expected_tpt) {
@@ -1724,7 +1790,8 @@
 {
 	enum rs_action action = RS_ACTION_STAY;
 
-	if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) {
+	if ((sr <= RS_PERCENT(IWL_MVM_RS_SR_FORCE_DECREASE)) ||
+	    (current_tpt == 0)) {
 		IWL_DEBUG_RATE(mvm,
 			       "Decrease rate because of low SR\n");
 		return RS_ACTION_DOWNSCALE;
@@ -1783,7 +1850,7 @@
 
 out:
 	if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID)) {
-		if (sr >= RS_SR_NO_DECREASE) {
+		if (sr >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) {
 			IWL_DEBUG_RATE(mvm,
 				       "SR is above NO DECREASE. Avoid downscale\n");
 			action = RS_ACTION_STAY;
@@ -1802,18 +1869,10 @@
 static bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 			  struct iwl_lq_sta *lq_sta)
 {
-	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-	struct ieee80211_vif *vif = mvmsta->vif;
-	bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION &&
-				!vif->bss_conf.ps);
-
 	/* Our chip supports Tx STBC and the peer is an HT/VHT STA which
 	 * supports STBC of at least 1*SS
 	 */
-	if (!lq_sta->stbc)
-		return false;
-
-	if (!mvm->ps_disabled && !sta_ps_disabled)
+	if (!lq_sta->stbc_capable)
 		return false;
 
 	if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
@@ -1825,11 +1884,11 @@
 static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index,
 				int *weaker, int *stronger)
 {
-	*weaker = index + TPC_TX_POWER_STEP;
+	*weaker = index + IWL_MVM_RS_TPC_TX_POWER_STEP;
 	if (*weaker > TPC_MAX_REDUCTION)
 		*weaker = TPC_INVALID;
 
-	*stronger = index - TPC_TX_POWER_STEP;
+	*stronger = index - IWL_MVM_RS_TPC_TX_POWER_STEP;
 	if (*stronger < 0)
 		*stronger = TPC_INVALID;
 }
@@ -1885,7 +1944,8 @@
 	}
 
 	/* Too many failures, increase txp */
-	if (sr <= TPC_SR_FORCE_INCREASE || current_tpt == 0) {
+	if (sr <= RS_PERCENT(IWL_MVM_RS_TPC_SR_FORCE_INCREASE) ||
+	    current_tpt == 0) {
 		IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n");
 		return TPC_ACTION_NO_RESTIRCTION;
 	}
@@ -1908,7 +1968,8 @@
 	}
 
 	/* next, increase if needed */
-	if (sr < TPC_SR_NO_INCREASE && strong != TPC_INVALID) {
+	if (sr < RS_PERCENT(IWL_MVM_RS_TPC_SR_NO_INCREASE) &&
+	    strong != TPC_INVALID) {
 		if (weak_tpt == IWL_INVALID_VALUE &&
 		    strong_tpt != IWL_INVALID_VALUE &&
 		    current_tpt < strong_tpt) {
@@ -1935,7 +1996,7 @@
 			   struct iwl_lq_sta *lq_sta,
 			   struct iwl_scale_tbl_info *tbl)
 {
-	struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 	struct ieee80211_vif *vif = mvm_sta->vif;
 	struct ieee80211_chanctx_conf *chanctx_conf;
 	enum ieee80211_band band;
@@ -2044,7 +2105,7 @@
 	u16 high_low;
 	s32 sr;
 	u8 prev_agg = lq_sta->is_agg;
-	struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv;
+	struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta);
 	struct iwl_mvm_tid_data *tid_data;
 	struct rs_rate *rate;
 
@@ -2084,7 +2145,7 @@
 			rate->type = LQ_NONE;
 			lq_sta->search_better_tbl = 0;
 			tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
-			rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate);
+			rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
 		}
 		return;
 	}
@@ -2106,8 +2167,8 @@
 	 * in current association (use new rate found above).
 	 */
 	fail_count = window->counter - window->success_counter;
-	if ((fail_count < IWL_RATE_MIN_FAILURE_TH) &&
-	    (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) {
+	if ((fail_count < IWL_MVM_RS_RATE_MIN_FAILURE_TH) &&
+	    (window->success_counter < IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) {
 		IWL_DEBUG_RATE(mvm,
 			       "(%s: %d): Test Window: succ %d total %d\n",
 			       rs_pretty_lq_type(rate->type),
@@ -2247,7 +2308,7 @@
 	/* Replace uCode's rate table for the destination station. */
 	if (update_lq) {
 		tbl->rate.index = index;
-		rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate);
+		rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
 	}
 
 	rs_stay_in_table(lq_sta, false);
@@ -2294,8 +2355,7 @@
 
 			rs_dump_rate(mvm, &tbl->rate,
 				     "Switch to SEARCH TABLE:");
-			rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
-			iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
+			rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
 		} else {
 			done_search = 1;
 		}
@@ -2385,7 +2445,7 @@
 	int i, nentries;
 	s8 best_rssi = S8_MIN;
 	u8 best_ant = ANT_NONE;
-	u8 valid_tx_ant = mvm->fw->valid_tx_ant;
+	u8 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
 	const struct rs_init_rate_info *initial_rates;
 
 	for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) {
@@ -2509,6 +2569,14 @@
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct iwl_lq_sta *lq_sta = mvm_sta;
 
+	if (sta && !iwl_mvm_sta_from_mac80211(sta)->vif) {
+		/* if vif isn't initialized mvm doesn't know about
+		 * this station, so don't do anything with the it
+		 */
+		sta = NULL;
+		mvm_sta = NULL;
+	}
+
 	/* TODO: handle rate_idx_mask and rate_idx_mcs_mask */
 
 	/* Treat uninitialized rate scaling data same as non-existing. */
@@ -2530,7 +2598,7 @@
 static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
 			  gfp_t gfp)
 {
-	struct iwl_mvm_sta *sta_priv = (struct iwl_mvm_sta *)sta->drv_priv;
+	struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta);
 	struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate;
 	struct iwl_mvm *mvm  = IWL_OP_MODE_GET_MVM(op_mode);
 	struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta;
@@ -2541,6 +2609,7 @@
 #ifdef CPTCFG_MAC80211_DEBUGFS
 	lq_sta->pers.dbg_fixed_rate = 0;
 	lq_sta->pers.dbg_fixed_txp_reduction = TPC_INVALID;
+	lq_sta->pers.ss_force = RS_SS_FORCE_NONE;
 #endif
 	lq_sta->pers.chains = 0;
 	memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal));
@@ -2606,68 +2675,121 @@
 	}
 }
 
+static void rs_ht_init(struct iwl_mvm *mvm,
+		       struct ieee80211_sta *sta,
+		       struct iwl_lq_sta *lq_sta,
+		       struct ieee80211_sta_ht_cap *ht_cap)
+{
+	/* active_siso_rate mask includes 9 MBits (bit 5),
+	 * and CCK (bits 0-3), supp_rates[] does not;
+	 * shift to convert format, force 9 MBits off.
+	 */
+	lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
+	lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
+	lq_sta->active_siso_rate &= ~((u16)0x2);
+	lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
+
+	lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
+	lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
+	lq_sta->active_mimo2_rate &= ~((u16)0x2);
+	lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
+
+	if (mvm->cfg->ht_params->ldpc &&
+	    (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING))
+		lq_sta->ldpc = true;
+
+	if (mvm->cfg->ht_params->stbc &&
+	    (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
+	    (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC))
+		lq_sta->stbc_capable = true;
+
+	lq_sta->is_vht = false;
+}
+
+static void rs_vht_init(struct iwl_mvm *mvm,
+			struct ieee80211_sta *sta,
+			struct iwl_lq_sta *lq_sta,
+			struct ieee80211_sta_vht_cap *vht_cap)
+{
+	rs_vht_set_enabled_rates(sta, vht_cap, lq_sta);
+
+	if (mvm->cfg->ht_params->ldpc &&
+	    (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))
+		lq_sta->ldpc = true;
+
+	if (mvm->cfg->ht_params->stbc &&
+	    (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
+	    (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK))
+		lq_sta->stbc_capable = true;
+
+	if ((mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
+	    (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
+	    (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+		lq_sta->bfer_capable = true;
+
+	lq_sta->is_vht = true;
+}
+
 #ifdef CPTCFG_IWLWIFI_DEBUGFS
-static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm,
-				      struct iwl_mvm_frame_stats *stats)
+static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm)
 {
 	spin_lock_bh(&mvm->drv_stats_lock);
-	memset(stats, 0, sizeof(*stats));
+	memset(&mvm->drv_rx_stats, 0, sizeof(mvm->drv_rx_stats));
 	spin_unlock_bh(&mvm->drv_stats_lock);
 }
 
-void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm,
-				struct iwl_mvm_frame_stats *stats,
-				u32 rate, bool agg)
+void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg)
 {
 	u8 nss = 0, mcs = 0;
 
 	spin_lock(&mvm->drv_stats_lock);
 
 	if (agg)
-		stats->agg_frames++;
+		mvm->drv_rx_stats.agg_frames++;
 
-	stats->success_frames++;
+	mvm->drv_rx_stats.success_frames++;
 
 	switch (rate & RATE_MCS_CHAN_WIDTH_MSK) {
 	case RATE_MCS_CHAN_WIDTH_20:
-		stats->bw_20_frames++;
+		mvm->drv_rx_stats.bw_20_frames++;
 		break;
 	case RATE_MCS_CHAN_WIDTH_40:
-		stats->bw_40_frames++;
+		mvm->drv_rx_stats.bw_40_frames++;
 		break;
 	case RATE_MCS_CHAN_WIDTH_80:
-		stats->bw_80_frames++;
+		mvm->drv_rx_stats.bw_80_frames++;
 		break;
 	default:
 		WARN_ONCE(1, "bad BW. rate 0x%x", rate);
 	}
 
 	if (rate & RATE_MCS_HT_MSK) {
-		stats->ht_frames++;
+		mvm->drv_rx_stats.ht_frames++;
 		mcs = rate & RATE_HT_MCS_RATE_CODE_MSK;
 		nss = ((rate & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS) + 1;
 	} else if (rate & RATE_MCS_VHT_MSK) {
-		stats->vht_frames++;
+		mvm->drv_rx_stats.vht_frames++;
 		mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK;
 		nss = ((rate & RATE_VHT_MCS_NSS_MSK) >>
 		       RATE_VHT_MCS_NSS_POS) + 1;
 	} else {
-		stats->legacy_frames++;
+		mvm->drv_rx_stats.legacy_frames++;
 	}
 
 	if (nss == 1)
-		stats->siso_frames++;
+		mvm->drv_rx_stats.siso_frames++;
 	else if (nss == 2)
-		stats->mimo2_frames++;
+		mvm->drv_rx_stats.mimo2_frames++;
 
 	if (rate & RATE_MCS_SGI_MSK)
-		stats->sgi_frames++;
+		mvm->drv_rx_stats.sgi_frames++;
 	else
-		stats->ngi_frames++;
+		mvm->drv_rx_stats.ngi_frames++;
 
-	stats->last_rates[stats->last_frame_idx] = rate;
-	stats->last_frame_idx = (stats->last_frame_idx + 1) %
-		ARRAY_SIZE(stats->last_rates);
+	mvm->drv_rx_stats.last_rates[mvm->drv_rx_stats.last_frame_idx] = rate;
+	mvm->drv_rx_stats.last_frame_idx =
+		(mvm->drv_rx_stats.last_frame_idx + 1) %
+			ARRAY_SIZE(mvm->drv_rx_stats.last_rates);
 
 	spin_unlock(&mvm->drv_stats_lock);
 }
@@ -2683,14 +2805,11 @@
 	struct ieee80211_hw *hw = mvm->hw;
 	struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
 	struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
-	struct iwl_mvm_sta *sta_priv;
-	struct iwl_lq_sta *lq_sta;
+	struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta);
+	struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta;
 	struct ieee80211_supported_band *sband;
 	unsigned long supp; /* must be unsigned long for for_each_set_bit */
 
-	sta_priv = (struct iwl_mvm_sta *)sta->drv_priv;
-	lq_sta = &sta_priv->lq_sta;
-
 	/* clear all non-persistent lq data */
 	memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
 
@@ -2712,7 +2831,7 @@
 	 * previous packets? Need to have IEEE 802.1X auth succeed immediately
 	 * after assoc.. */
 
-	lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX;
+	lq_sta->missed_rate_counter = IWL_MVM_RS_MISSED_RATE_MAX;
 	lq_sta->band = sband->band;
 	/*
 	 * active legacy rates as per supported rates bitmap
@@ -2723,61 +2842,28 @@
 		lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value);
 
 	/* TODO: should probably account for rx_highest for both HT/VHT */
-	if (!vht_cap || !vht_cap->vht_supported) {
-		/* active_siso_rate mask includes 9 MBits (bit 5),
-		 * and CCK (bits 0-3), supp_rates[] does not;
-		 * shift to convert format, force 9 MBits off.
-		 */
-		lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
-		lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
-		lq_sta->active_siso_rate &= ~((u16)0x2);
-		lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
+	if (!vht_cap || !vht_cap->vht_supported)
+		rs_ht_init(mvm, sta, lq_sta, ht_cap);
+	else
+		rs_vht_init(mvm, sta, lq_sta, vht_cap);
 
-		/* Same here */
-		lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
-		lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
-		lq_sta->active_mimo2_rate &= ~((u16)0x2);
-		lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
-
-		lq_sta->is_vht = false;
-		if (mvm->cfg->ht_params->ldpc &&
-		    (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING))
-			lq_sta->ldpc = true;
-
-		if (mvm->cfg->ht_params->stbc &&
-		    (num_of_ant(mvm->fw->valid_tx_ant) > 1) &&
-		    (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC))
-			lq_sta->stbc = true;
-	} else {
-		rs_vht_set_enabled_rates(sta, vht_cap, lq_sta);
-		lq_sta->is_vht = true;
-
-		if (mvm->cfg->ht_params->ldpc &&
-		    (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))
-			lq_sta->ldpc = true;
-
-		if (mvm->cfg->ht_params->stbc &&
-		    (num_of_ant(mvm->fw->valid_tx_ant) > 1) &&
-		    (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK))
-			lq_sta->stbc = true;
-	}
-
-	if (IWL_MVM_RS_DISABLE_MIMO)
+	if (IWL_MVM_RS_DISABLE_P2P_MIMO && sta_priv->vif->p2p)
 		lq_sta->active_mimo2_rate = 0;
 
-	lq_sta->max_legacy_rate_idx = find_last_bit(&lq_sta->active_legacy_rate,
-						    BITS_PER_LONG);
-	lq_sta->max_siso_rate_idx = find_last_bit(&lq_sta->active_siso_rate,
-						  BITS_PER_LONG);
-	lq_sta->max_mimo2_rate_idx = find_last_bit(&lq_sta->active_mimo2_rate,
-						   BITS_PER_LONG);
+	lq_sta->max_legacy_rate_idx =
+		rs_get_max_rate_from_mask(lq_sta->active_legacy_rate);
+	lq_sta->max_siso_rate_idx =
+		rs_get_max_rate_from_mask(lq_sta->active_siso_rate);
+	lq_sta->max_mimo2_rate_idx =
+		rs_get_max_rate_from_mask(lq_sta->active_mimo2_rate);
 
 	IWL_DEBUG_RATE(mvm,
-		       "RATE MASK: LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC%d\n",
+		       "LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC=%d BFER=%d\n",
 		       lq_sta->active_legacy_rate,
 		       lq_sta->active_siso_rate,
 		       lq_sta->active_mimo2_rate,
-		       lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc);
+		       lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc_capable,
+		       lq_sta->bfer_capable);
 	IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n",
 		       lq_sta->max_legacy_rate_idx,
 		       lq_sta->max_siso_rate_idx,
@@ -2785,14 +2871,14 @@
 
 	/* These values will be overridden later */
 	lq_sta->lq.single_stream_ant_msk =
-		first_antenna(mvm->fw->valid_tx_ant);
+		first_antenna(iwl_mvm_get_valid_tx_ant(mvm));
 	lq_sta->lq.dual_stream_ant_msk = ANT_AB;
 
 	/* as default allow aggregation for all tids */
 	lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
 	lq_sta->is_agg = 0;
 #ifdef CPTCFG_IWLWIFI_DEBUGFS
-	iwl_mvm_reset_frame_stats(mvm, &mvm->drv_rx_stats);
+	iwl_mvm_reset_frame_stats(mvm);
 #endif
 	rs_initialize_lq(mvm, sta, lq_sta, band, init);
 }
@@ -2808,6 +2894,9 @@
 			(struct iwl_op_mode *)mvm_r;
 	struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 
+	if (!iwl_mvm_sta_from_mac80211(sta)->vif)
+		return;
+
 	/* Stop any ongoing aggregations as rs starts off assuming no agg */
 	for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
 		ieee80211_stop_tx_ba_session(sta, tid);
@@ -2861,12 +2950,13 @@
 	int index = *rs_table_index;
 
 	for (i = 0; i < num_rates && index < end; i++) {
-		ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, rate));
-		for (j = 0; j < num_retries && index < end; j++, index++)
+		for (j = 0; j < num_retries && index < end; j++, index++) {
+			ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm,
+									 rate));
 			rs_table[index] = ucode_rate;
-
-		if (toggle_ant)
-			rs_toggle_antenna(valid_tx_ant, rate);
+			if (toggle_ant)
+				rs_toggle_antenna(valid_tx_ant, rate);
+		}
 
 		prev_rate_idx = rate->index;
 		bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate);
@@ -2874,7 +2964,7 @@
 			break;
 	}
 
-	if (!bottom_reached)
+	if (!bottom_reached && !is_legacy(rate))
 		rate->index = prev_rate_idx;
 
 	*rs_table_index = index;
@@ -2913,18 +3003,22 @@
 
 	memcpy(&rate, initial_rate, sizeof(rate));
 
-	valid_tx_ant = mvm->fw->valid_tx_ant;
-	rate.stbc = rs_stbc_allow(mvm, sta, lq_sta);
+	valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
+
+	/* TODO: remove old API when min FW API hits 14 */
+	if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LQ_SS_PARAMS) &&
+	    rs_stbc_allow(mvm, sta, lq_sta))
+		rate.stbc = true;
 
 	if (is_siso(&rate)) {
-		num_rates = RS_INITIAL_SISO_NUM_RATES;
-		num_retries = RS_HT_VHT_RETRIES_PER_RATE;
+		num_rates = IWL_MVM_RS_INITIAL_SISO_NUM_RATES;
+		num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE;
 	} else if (is_mimo(&rate)) {
-		num_rates = RS_INITIAL_MIMO_NUM_RATES;
-		num_retries = RS_HT_VHT_RETRIES_PER_RATE;
+		num_rates = IWL_MVM_RS_INITIAL_MIMO_NUM_RATES;
+		num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE;
 	} else {
-		num_rates = RS_INITIAL_LEGACY_NUM_RATES;
-		num_retries = RS_LEGACY_RETRIES_PER_RATE;
+		num_rates = IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES;
+		num_retries = IWL_MVM_RS_INITIAL_LEGACY_RETRIES;
 		toggle_ant = true;
 	}
 
@@ -2935,12 +3029,12 @@
 	rs_get_lower_rate_down_column(lq_sta, &rate);
 
 	if (is_siso(&rate)) {
-		num_rates = RS_SECONDARY_SISO_NUM_RATES;
-		num_retries = RS_SECONDARY_SISO_RETRIES;
+		num_rates = IWL_MVM_RS_SECONDARY_SISO_NUM_RATES;
+		num_retries = IWL_MVM_RS_SECONDARY_SISO_RETRIES;
 		lq_cmd->mimo_delim = index;
 	} else if (is_legacy(&rate)) {
-		num_rates = RS_SECONDARY_LEGACY_NUM_RATES;
-		num_retries = RS_LEGACY_RETRIES_PER_RATE;
+		num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES;
+		num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES;
 	} else {
 		WARN_ON_ONCE(1);
 	}
@@ -2953,8 +3047,8 @@
 
 	rs_get_lower_rate_down_column(lq_sta, &rate);
 
-	num_rates = RS_SECONDARY_LEGACY_NUM_RATES;
-	num_retries = RS_LEGACY_RETRIES_PER_RATE;
+	num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES;
+	num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES;
 
 	rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
 				 num_rates, num_retries, valid_tx_ant,
@@ -2962,6 +3056,144 @@
 
 }
 
+struct rs_bfer_active_iter_data {
+	struct ieee80211_sta *exclude_sta;
+	struct iwl_mvm_sta *bfer_mvmsta;
+};
+
+static void rs_bfer_active_iter(void *_data,
+				struct ieee80211_sta *sta)
+{
+	struct rs_bfer_active_iter_data *data = _data;
+	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+	struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.lq;
+	u32 ss_params = le32_to_cpu(lq_cmd->ss_params);
+
+	if (sta == data->exclude_sta)
+		return;
+
+	/* The current sta has BFER allowed */
+	if (ss_params & LQ_SS_BFER_ALLOWED) {
+		WARN_ON_ONCE(data->bfer_mvmsta != NULL);
+
+		data->bfer_mvmsta = mvmsta;
+	}
+}
+
+static int rs_bfer_priority(struct iwl_mvm_sta *sta)
+{
+	int prio = -1;
+	enum nl80211_iftype viftype = ieee80211_vif_type_p2p(sta->vif);
+
+	switch (viftype) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_P2P_GO:
+		prio = 3;
+		break;
+	case NL80211_IFTYPE_P2P_CLIENT:
+		prio = 2;
+		break;
+	case NL80211_IFTYPE_STATION:
+		prio = 1;
+		break;
+	default:
+		WARN_ONCE(true, "viftype %d sta_id %d", viftype, sta->sta_id);
+		prio = -1;
+	}
+
+	return prio;
+}
+
+/* Returns >0 if sta1 has a higher BFER priority compared to sta2 */
+static int rs_bfer_priority_cmp(struct iwl_mvm_sta *sta1,
+				struct iwl_mvm_sta *sta2)
+{
+	int prio1 = rs_bfer_priority(sta1);
+	int prio2 = rs_bfer_priority(sta2);
+
+	if (prio1 > prio2)
+		return 1;
+	if (prio1 < prio2)
+		return -1;
+	return 0;
+}
+
+static void rs_set_lq_ss_params(struct iwl_mvm *mvm,
+				struct ieee80211_sta *sta,
+				struct iwl_lq_sta *lq_sta,
+				const struct rs_rate *initial_rate)
+{
+	struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
+	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+	struct rs_bfer_active_iter_data data = {
+		.exclude_sta = sta,
+		.bfer_mvmsta = NULL,
+	};
+	struct iwl_mvm_sta *bfer_mvmsta = NULL;
+	u32 ss_params = LQ_SS_PARAMS_VALID;
+
+	if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
+		goto out;
+
+#ifdef CPTCFG_MAC80211_DEBUGFS
+	/* Check if forcing the decision is configured.
+	 * Note that SISO is forced by not allowing STBC or BFER
+	 */
+	if (lq_sta->pers.ss_force == RS_SS_FORCE_STBC)
+		ss_params |= (LQ_SS_STBC_1SS_ALLOWED | LQ_SS_FORCE);
+	else if (lq_sta->pers.ss_force == RS_SS_FORCE_BFER)
+		ss_params |= (LQ_SS_BFER_ALLOWED | LQ_SS_FORCE);
+
+	if (lq_sta->pers.ss_force != RS_SS_FORCE_NONE) {
+		IWL_DEBUG_RATE(mvm, "Forcing single stream Tx decision %d\n",
+			       lq_sta->pers.ss_force);
+		goto out;
+	}
+#endif
+
+	if (lq_sta->stbc_capable)
+		ss_params |= LQ_SS_STBC_1SS_ALLOWED;
+
+	if (!lq_sta->bfer_capable)
+		goto out;
+
+	ieee80211_iterate_stations_atomic(mvm->hw,
+					  rs_bfer_active_iter,
+					  &data);
+	bfer_mvmsta = data.bfer_mvmsta;
+
+	/* This code is safe as it doesn't run concurrently for different
+	 * stations. This is guaranteed by the fact that calls to
+	 * ieee80211_tx_status wouldn't run concurrently for a single HW.
+	 */
+	if (!bfer_mvmsta) {
+		IWL_DEBUG_RATE(mvm, "No sta with BFER allowed found. Allow\n");
+
+		ss_params |= LQ_SS_BFER_ALLOWED;
+		goto out;
+	}
+
+	IWL_DEBUG_RATE(mvm, "Found existing sta %d with BFER activated\n",
+		       bfer_mvmsta->sta_id);
+
+	/* Disallow BFER on another STA if active and we're a higher priority */
+	if (rs_bfer_priority_cmp(mvmsta, bfer_mvmsta) > 0) {
+		struct iwl_lq_cmd *bfersta_lq_cmd = &bfer_mvmsta->lq_sta.lq;
+		u32 bfersta_ss_params = le32_to_cpu(bfersta_lq_cmd->ss_params);
+
+		bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED;
+		bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params);
+		iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd, false);
+
+		ss_params |= LQ_SS_BFER_ALLOWED;
+		IWL_DEBUG_RATE(mvm,
+			       "Lower priority BFER sta found (%d). Switch BFER\n",
+			       bfer_mvmsta->sta_id);
+	}
+out:
+	lq_cmd->ss_params = cpu_to_le32(ss_params);
+}
+
 static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
 			   struct ieee80211_sta *sta,
 			   struct iwl_lq_sta *lq_sta,
@@ -2971,9 +3203,9 @@
 	struct iwl_mvm_sta *mvmsta;
 	struct iwl_mvm_vif *mvmvif;
 
-	lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
+	lq_cmd->agg_disable_start_th = IWL_MVM_RS_AGG_DISABLE_START;
 	lq_cmd->agg_time_limit =
-		cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
+		cpu_to_le16(IWL_MVM_RS_AGG_TIME_LIMIT);
 
 #ifdef CPTCFG_MAC80211_DEBUGFS
 	if (lq_sta->pers.dbg_fixed_rate) {
@@ -2988,6 +3220,9 @@
 
 	rs_build_rates_table(mvm, sta, lq_sta, initial_rate);
 
+	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LQ_SS_PARAMS)
+		rs_set_lq_ss_params(mvm, sta, lq_sta, initial_rate);
+
 	if (num_of_ant(initial_rate->ant) == 1)
 		lq_cmd->single_stream_ant_msk = initial_rate->ant;
 
@@ -3000,7 +3235,7 @@
 	lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
 
 	/*
-	 * In case of low latency, tell the firwmare to leave a frame in the
+	 * In case of low latency, tell the firmware to leave a frame in the
 	 * Tx Fifo so that it can start a transaction in the same TxOP. This
 	 * basically allows the firmware to send bursts.
 	 */
@@ -3155,6 +3390,7 @@
 	struct iwl_mvm *mvm;
 	struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
 	struct rs_rate *rate = &tbl->rate;
+	u32 ss_params;
 	mvm = lq_sta->pers.drv;
 	buff = kmalloc(2048, GFP_KERNEL);
 	if (!buff)
@@ -3167,23 +3403,23 @@
 	desc += sprintf(buff+desc, "fixed rate 0x%X\n",
 			lq_sta->pers.dbg_fixed_rate);
 	desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
-	    (mvm->fw->valid_tx_ant & ANT_A) ? "ANT_A," : "",
-	    (mvm->fw->valid_tx_ant & ANT_B) ? "ANT_B," : "",
-	    (mvm->fw->valid_tx_ant & ANT_C) ? "ANT_C" : "");
+	    (iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "",
+	    (iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "",
+	    (iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : "");
 	desc += sprintf(buff+desc, "lq type %s\n",
 			(is_legacy(rate)) ? "legacy" :
 			is_vht(rate) ? "VHT" : "HT");
 	if (!is_legacy(rate)) {
-		desc += sprintf(buff+desc, " %s",
+		desc += sprintf(buff + desc, " %s",
 		   (is_siso(rate)) ? "SISO" : "MIMO2");
-		   desc += sprintf(buff+desc, " %s",
-				   (is_ht20(rate)) ? "20MHz" :
-				   (is_ht40(rate)) ? "40MHz" :
-				   (is_ht80(rate)) ? "80Mhz" : "BAD BW");
-		   desc += sprintf(buff+desc, " %s %s %s\n",
-				   (rate->sgi) ? "SGI" : "NGI",
-				   (rate->ldpc) ? "LDPC" : "BCC",
-				   (lq_sta->is_agg) ? "AGG on" : "");
+		desc += sprintf(buff + desc, " %s",
+				(is_ht20(rate)) ? "20MHz" :
+				(is_ht40(rate)) ? "40MHz" :
+				(is_ht80(rate)) ? "80Mhz" : "BAD BW");
+		desc += sprintf(buff + desc, " %s %s %s\n",
+				(rate->sgi) ? "SGI" : "NGI",
+				(rate->ldpc) ? "LDPC" : "BCC",
+				(lq_sta->is_agg) ? "AGG on" : "");
 	}
 	desc += sprintf(buff+desc, "last tx rate=0x%X\n",
 			lq_sta->last_rate_n_flags);
@@ -3201,6 +3437,16 @@
 			lq_sta->lq.agg_frame_cnt_limit);
 
 	desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc);
+	ss_params = le32_to_cpu(lq_sta->lq.ss_params);
+	desc += sprintf(buff+desc, "single stream params: %s%s%s%s\n",
+			(ss_params & LQ_SS_PARAMS_VALID) ?
+			"VALID" : "INVALID",
+			(ss_params & LQ_SS_BFER_ALLOWED) ?
+			", BFER" : "",
+			(ss_params & LQ_SS_STBC_1SS_ALLOWED) ?
+			", STBC" : "",
+			(ss_params & LQ_SS_FORCE) ?
+			", FORCE" : "");
 	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],
@@ -3361,9 +3607,79 @@
 	.llseek = default_llseek,
 };
 
-static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
+static ssize_t iwl_dbgfs_ss_force_read(struct file *file,
+				       char __user *user_buf,
+				       size_t count, loff_t *ppos)
 {
-	struct iwl_lq_sta *lq_sta = mvm_sta;
+	struct iwl_lq_sta *lq_sta = file->private_data;
+	char buf[12];
+	int bufsz = sizeof(buf);
+	int pos = 0;
+	static const char * const ss_force_name[] = {
+		[RS_SS_FORCE_NONE] = "none",
+		[RS_SS_FORCE_STBC] = "stbc",
+		[RS_SS_FORCE_BFER] = "bfer",
+		[RS_SS_FORCE_SISO] = "siso",
+	};
+
+	pos += scnprintf(buf+pos, bufsz-pos, "%s\n",
+			 ss_force_name[lq_sta->pers.ss_force]);
+	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf,
+					size_t count, loff_t *ppos)
+{
+	struct iwl_mvm *mvm = lq_sta->pers.drv;
+	int ret = 0;
+
+	if (!strncmp("none", buf, 4)) {
+		lq_sta->pers.ss_force = RS_SS_FORCE_NONE;
+	} else if (!strncmp("siso", buf, 4)) {
+		lq_sta->pers.ss_force = RS_SS_FORCE_SISO;
+	} else if (!strncmp("stbc", buf, 4)) {
+		if (lq_sta->stbc_capable) {
+			lq_sta->pers.ss_force = RS_SS_FORCE_STBC;
+		} else {
+			IWL_ERR(mvm,
+				"can't force STBC. peer doesn't support\n");
+			ret = -EINVAL;
+		}
+	} else if (!strncmp("bfer", buf, 4)) {
+		if (lq_sta->bfer_capable) {
+			lq_sta->pers.ss_force = RS_SS_FORCE_BFER;
+		} else {
+			IWL_ERR(mvm,
+				"can't force BFER. peer doesn't support\n");
+			ret = -EINVAL;
+		}
+	} else {
+		IWL_ERR(mvm, "valid values none|siso|stbc|bfer\n");
+		ret = -EINVAL;
+	}
+	return ret ?: count;
+}
+
+#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
+	_MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_lq_sta)
+#define MVM_DEBUGFS_ADD_FILE_RS(name, parent, mode) do {		\
+		if (!debugfs_create_file(#name, mode, parent, lq_sta,	\
+					 &iwl_dbgfs_##name##_ops))	\
+			goto err;					\
+	} while (0)
+
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32);
+
+static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir)
+{
+	struct iwl_lq_sta *lq_sta = priv_sta;
+	struct iwl_mvm_sta *mvmsta;
+
+	mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta);
+
+	if (!mvmsta->vif)
+		return;
+
 	debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
 			    lq_sta, &rs_sta_dbgfs_scale_table_ops);
 	debugfs_create_file("rate_stats_table", S_IRUSR, dir,
@@ -3374,6 +3690,11 @@
 			  &lq_sta->tx_agg_tid_en);
 	debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
 			  &lq_sta->pers.dbg_fixed_txp_reduction);
+
+	MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, S_IRUSR | S_IWUSR);
+	return;
+err:
+	IWL_ERR((struct iwl_mvm *)mvm, "Can't create debugfs entity\n");
 }
 
 static void rs_remove_debugfs(void *mvm, void *mvm_sta)
@@ -3421,7 +3742,7 @@
 
 /**
  * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
- * Tx protection, according to this rquest and previous requests,
+ * Tx protection, according to this request and previous requests,
  * and send the LQ command.
  * @mvmsta: The station
  * @enable: Enable Tx protection?
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h
index fdd0da5..ed4834b 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.h
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.h
@@ -137,42 +137,10 @@
 
 #define IWL_INVALID_VALUE    -1
 
-#define IWL_MIN_RSSI_VAL                 -100
-#define IWL_MAX_RSSI_VAL                    0
-
-/* These values specify how many Tx frame attempts before
- * searching for a new modulation mode */
-#define IWL_LEGACY_FAILURE_LIMIT	160
-#define IWL_LEGACY_SUCCESS_LIMIT	480
-#define IWL_LEGACY_TABLE_COUNT		160
-
-#define IWL_NONE_LEGACY_FAILURE_LIMIT	400
-#define IWL_NONE_LEGACY_SUCCESS_LIMIT	4500
-#define IWL_NONE_LEGACY_TABLE_COUNT	1500
-
-/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */
-#define IWL_RS_GOOD_RATIO		12800	/* 100% */
-#define IWL_RATE_SCALE_SWITCH		10880	/*  85% */
-#define IWL_RATE_HIGH_TH		10880	/*  85% */
-#define IWL_RATE_INCREASE_TH		6400	/*  50% */
-#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)
-
-#define LINK_QUAL_AGG_DISABLE_START_DEF	(3)
-#define LINK_QUAL_AGG_DISABLE_START_MAX	(255)
-#define LINK_QUAL_AGG_DISABLE_START_MIN	(0)
-
 #define LINK_QUAL_AGG_FRAME_LIMIT_DEF	(63)
 #define LINK_QUAL_AGG_FRAME_LIMIT_MAX	(63)
 #define LINK_QUAL_AGG_FRAME_LIMIT_MIN	(0)
@@ -181,14 +149,7 @@
 
 /* load per tid defines for A-MPDU activation */
 #define IWL_AGG_TPT_THREHOLD	0
-#define IWL_AGG_LOAD_THRESHOLD	10
 #define IWL_AGG_ALL_TID		0xff
-#define TID_QUEUE_CELL_SPACING	50	/*mS */
-#define TID_QUEUE_MAX_SIZE	20
-#define TID_ROUND_VALUE		5	/* mS */
-
-#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING)
-#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y))
 
 enum iwl_table_type {
 	LQ_NONE,
@@ -209,6 +170,7 @@
 	bool sgi;
 	bool ldpc;
 	bool stbc;
+	bool bfer;
 };
 
 
@@ -279,6 +241,13 @@
 	RS_COLUMN_INVALID,
 };
 
+enum rs_ss_force_opt {
+	RS_SS_FORCE_NONE = 0,
+	RS_SS_FORCE_STBC,
+	RS_SS_FORCE_BFER,
+	RS_SS_FORCE_SISO,
+};
+
 /* Packet stats per rate */
 struct rs_rate_stats {
 	u64 success;
@@ -332,7 +301,9 @@
 	u64 last_tx;
 	bool is_vht;
 	bool ldpc;              /* LDPC Rx is supported by the STA */
-	bool stbc;              /* Tx STBC is supported by chip and Rx by STA */
+	bool stbc_capable;      /* Tx STBC is supported by chip and Rx by STA */
+	bool bfer_capable;      /* Remote supports beamformee and we BFer */
+
 	enum ieee80211_band band;
 
 	/* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
@@ -366,6 +337,9 @@
 #ifdef CPTCFG_MAC80211_DEBUGFS
 		u32 dbg_fixed_rate;
 		u8 dbg_fixed_txp_reduction;
+
+		/* force STBC/BFER/SISO for testing */
+		enum rs_ss_force_opt ss_force;
 #endif
 		u8 chains;
 		s8 chain_signal[IEEE80211_MAX_CHAINS];
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
index 0f6a40d..d7f4aa8 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -6,7 +6,7 @@
  * GPL LICENSE SUMMARY
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  *
  * 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
@@ -32,7 +32,7 @@
  * BSD LICENSE
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -345,6 +345,25 @@
 		struct iwl_mvm_sta *mvmsta;
 		mvmsta = iwl_mvm_sta_from_mac80211(sta);
 		rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
+
+		if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) &&
+		    ieee80211_is_beacon(hdr->frame_control)) {
+			struct iwl_fw_dbg_trigger_tlv *trig;
+			struct iwl_fw_dbg_trigger_low_rssi *rssi_trig;
+			bool trig_check;
+			s32 rssi;
+
+			trig = iwl_fw_dbg_get_trigger(mvm->fw,
+						      FW_DBG_TRIGGER_RSSI);
+			rssi_trig = (void *)trig->data;
+			rssi = le32_to_cpu(rssi_trig->rssi);
+
+			trig_check =
+				iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif,
+							      trig);
+			if (trig_check && rx_status->signal < rssi)
+				iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
+		}
 	}
 
 	rcu_read_unlock();
@@ -407,7 +426,7 @@
 	}
 
 #ifdef CPTCFG_IWLWIFI_DEBUGFS
-	iwl_mvm_update_frame_stats(mvm, &mvm->drv_rx_stats, rate_n_flags,
+	iwl_mvm_update_frame_stats(mvm, rate_n_flags,
 				   rx_status->flag & RX_FLAG_AMPDU_DETAILS);
 #endif
 	iwl_mvm_pass_packet_to_mac80211(mvm, skb, hdr, len, ampdu_status,
@@ -416,35 +435,43 @@
 }
 
 static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm,
-					 struct iwl_notif_statistics *stats)
+					 struct mvm_statistics_rx *rx_stats)
 {
-	/*
-	 * NOTE FW aggregates the statistics - BUT the statistics are cleared
-	 * when the driver issues REPLY_STATISTICS_CMD 0x9c with CLEAR_STATS
-	 * bit set.
-	 */
 	lockdep_assert_held(&mvm->mutex);
-	memcpy(&mvm->rx_stats, &stats->rx, sizeof(struct mvm_statistics_rx));
+
+	mvm->rx_stats = *rx_stats;
 }
 
 struct iwl_mvm_stat_data {
-	struct iwl_notif_statistics *stats;
 	struct iwl_mvm *mvm;
+	__le32 mac_id;
+	__s8 beacon_filter_average_energy;
+	struct mvm_statistics_general_v8 *general;
 };
 
 static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
 				  struct ieee80211_vif *vif)
 {
 	struct iwl_mvm_stat_data *data = _data;
-	struct iwl_notif_statistics *stats = data->stats;
 	struct iwl_mvm *mvm = data->mvm;
-	int sig = -stats->general.beacon_filter_average_energy;
+	int sig = -data->beacon_filter_average_energy;
 	int last_event;
 	int thold = vif->bss_conf.cqm_rssi_thold;
 	int hyst = vif->bss_conf.cqm_rssi_hyst;
-	u16 id = le32_to_cpu(stats->rx.general.mac_id);
+	u16 id = le32_to_cpu(data->mac_id);
 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
+	/* This doesn't need the MAC ID check since it's not taking the
+	 * data copied into the "data" struct, but rather the data from
+	 * the notification directly.
+	 */
+	if (data->general) {
+		mvmvif->beacon_stats.num_beacons =
+			le32_to_cpu(data->general->beacon_counter[mvmvif->id]);
+		mvmvif->beacon_stats.avg_signal =
+			-data->general->beacon_average_energy[mvmvif->id];
+	}
+
 	if (mvmvif->id != id)
 		return;
 
@@ -500,30 +527,101 @@
 	}
 }
 
-/*
- * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler
- *
- * TODO: This handler is implemented partially.
- */
-int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
-			  struct iwl_rx_cmd_buffer *rxb,
-			  struct iwl_device_cmd *cmd)
+static inline void
+iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
 {
-	struct iwl_rx_packet *pkt = rxb_addr(rxb);
-	struct iwl_notif_statistics *stats = (void *)&pkt->data;
-	struct mvm_statistics_general_common *common = &stats->general.common;
+	struct iwl_fw_dbg_trigger_tlv *trig;
+	struct iwl_fw_dbg_trigger_stats *trig_stats;
+	u32 trig_offset, trig_thold;
+
+	if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_STATS))
+		return;
+
+	trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_STATS);
+	trig_stats = (void *)trig->data;
+
+	if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
+		return;
+
+	trig_offset = le32_to_cpu(trig_stats->stop_offset);
+	trig_thold = le32_to_cpu(trig_stats->stop_threshold);
+
+	if (WARN_ON_ONCE(trig_offset >= iwl_rx_packet_payload_len(pkt)))
+		return;
+
+	if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold)
+		return;
+
+	iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
+}
+
+void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
+				  struct iwl_rx_packet *pkt)
+{
+	size_t v8_len = sizeof(struct iwl_notif_statistics_v8);
+	size_t v10_len = sizeof(struct iwl_notif_statistics_v10);
 	struct iwl_mvm_stat_data data = {
-		.stats = stats,
 		.mvm = mvm,
 	};
+	u32 temperature;
 
-	iwl_mvm_tt_temp_changed(mvm, le32_to_cpu(common->temperature));
+	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_STATS_V10) {
+		struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data;
 
-	iwl_mvm_update_rx_statistics(mvm, stats);
+		if (iwl_rx_packet_payload_len(pkt) != v10_len)
+			goto invalid;
+
+		temperature = le32_to_cpu(stats->general.radio_temperature);
+		data.mac_id = stats->rx.general.mac_id;
+		data.beacon_filter_average_energy =
+			stats->general.beacon_filter_average_energy;
+
+		iwl_mvm_update_rx_statistics(mvm, &stats->rx);
+
+		mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time);
+		mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time);
+		mvm->radio_stats.on_time_rf =
+			le64_to_cpu(stats->general.on_time_rf);
+		mvm->radio_stats.on_time_scan =
+			le64_to_cpu(stats->general.on_time_scan);
+
+		data.general = &stats->general;
+	} else {
+		struct iwl_notif_statistics_v8 *stats = (void *)&pkt->data;
+
+		if (iwl_rx_packet_payload_len(pkt) != v8_len)
+			goto invalid;
+
+		temperature = le32_to_cpu(stats->general.radio_temperature);
+		data.mac_id = stats->rx.general.mac_id;
+		data.beacon_filter_average_energy =
+			stats->general.beacon_filter_average_energy;
+
+		iwl_mvm_update_rx_statistics(mvm, &stats->rx);
+	}
+
+	iwl_mvm_rx_stats_check_trigger(mvm, pkt);
+
+	/* Only handle rx statistics temperature changes if async temp
+	 * notifications are not supported
+	 */
+	if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_ASYNC_DTM))
+		iwl_mvm_tt_temp_changed(mvm, temperature);
 
 	ieee80211_iterate_active_interfaces(mvm->hw,
 					    IEEE80211_IFACE_ITER_NORMAL,
 					    iwl_mvm_stat_iterator,
 					    &data);
+	return;
+ invalid:
+	IWL_ERR(mvm, "received invalid statistics size (%d)!\n",
+		iwl_rx_packet_payload_len(pkt));
+}
+
+int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
+			  struct iwl_rx_cmd_buffer *rxb,
+			  struct iwl_device_cmd *cmd)
+{
+	iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb));
 	return 0;
 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
index e5294d0..405e509 100644
--- a/drivers/net/wireless/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/mvm/scan.c
@@ -72,6 +72,8 @@
 
 #define IWL_PLCP_QUIET_THRESH 1
 #define IWL_ACTIVE_QUIET_TIME 10
+#define IWL_DENSE_EBS_SCAN_RATIO 5
+#define IWL_SPARSE_EBS_SCAN_RATIO 1
 
 struct iwl_mvm_scan_params {
 	u32 max_out_time;
@@ -80,6 +82,7 @@
 	struct _dwell {
 		u16 passive;
 		u16 active;
+		u16 fragmented;
 	} dwell[IEEE80211_NUM_BANDS];
 };
 
@@ -97,7 +100,7 @@
 {
 	if (mvm->scan_rx_ant != ANT_NONE)
 		return mvm->scan_rx_ant;
-	return mvm->fw->valid_rx_ant;
+	return iwl_mvm_get_valid_rx_ant(mvm);
 }
 
 static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
@@ -128,7 +131,7 @@
 	u32 tx_ant;
 
 	mvm->scan_last_antenna_idx =
-		iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant,
+		iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
 				     mvm->scan_last_antenna_idx);
 	tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS;
 
@@ -171,122 +174,33 @@
  * already included in the probe template, so we need to set only
  * req->n_ssids - 1 bits in addition to the first bit.
  */
-static u16 iwl_mvm_get_active_dwell(enum ieee80211_band band, int n_ssids)
+static u16 iwl_mvm_get_active_dwell(struct iwl_mvm *mvm,
+				    enum ieee80211_band band, int n_ssids)
 {
+	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BASIC_DWELL)
+		return 10;
 	if (band == IEEE80211_BAND_2GHZ)
 		return 20  + 3 * (n_ssids + 1);
 	return 10  + 2 * (n_ssids + 1);
 }
 
-static u16 iwl_mvm_get_passive_dwell(enum ieee80211_band band)
+static u16 iwl_mvm_get_passive_dwell(struct iwl_mvm *mvm,
+				     enum ieee80211_band band)
 {
+	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BASIC_DWELL)
+			return 110;
 	return band == IEEE80211_BAND_2GHZ ? 100 + 20 : 100 + 10;
 }
 
-static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
-				       struct cfg80211_scan_request *req,
-				       bool basic_ssid,
-				       struct iwl_mvm_scan_params *params)
-{
-	struct iwl_scan_channel *chan = (struct iwl_scan_channel *)
-		(cmd->data + le16_to_cpu(cmd->tx_cmd.len));
-	int i;
-	int type = BIT(req->n_ssids) - 1;
-	enum ieee80211_band band = req->channels[0]->band;
-
-	if (!basic_ssid)
-		type |= BIT(req->n_ssids);
-
-	for (i = 0; i < cmd->channel_count; i++) {
-		chan->channel = cpu_to_le16(req->channels[i]->hw_value);
-		chan->type = cpu_to_le32(type);
-		if (req->channels[i]->flags & IEEE80211_CHAN_NO_IR)
-			chan->type &= cpu_to_le32(~SCAN_CHANNEL_TYPE_ACTIVE);
-		chan->active_dwell = cpu_to_le16(params->dwell[band].active);
-		chan->passive_dwell = cpu_to_le16(params->dwell[band].passive);
-		chan->iteration_count = cpu_to_le16(1);
-		chan++;
-	}
-}
-
-/*
- * Fill in probe request with the following parameters:
- * TA is our vif HW address, which mac80211 ensures we have.
- * Packet is broadcasted, so this is both SA and DA.
- * The probe request IE is made out of two: first comes the most prioritized
- * SSID if a directed scan is requested. Second comes whatever extra
- * information was given to us as the scan request IE.
- */
-static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
-				  int n_ssids, const u8 *ssid, int ssid_len,
-				  const u8 *band_ie, int band_ie_len,
-				  const u8 *common_ie, int common_ie_len,
-				  int left)
-{
-	int len = 0;
-	u8 *pos = NULL;
-
-	/* Make sure there is enough space for the probe request,
-	 * two mandatory IEs and the data */
-	left -= 24;
-	if (left < 0)
-		return 0;
-
-	frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
-	eth_broadcast_addr(frame->da);
-	memcpy(frame->sa, ta, ETH_ALEN);
-	eth_broadcast_addr(frame->bssid);
-	frame->seq_ctrl = 0;
-
-	len += 24;
-
-	/* for passive scans, no need to fill anything */
-	if (n_ssids == 0)
-		return (u16)len;
-
-	/* points to the payload of the request */
-	pos = &frame->u.probe_req.variable[0];
-
-	/* fill in our SSID IE */
-	left -= ssid_len + 2;
-	if (left < 0)
-		return 0;
-	*pos++ = WLAN_EID_SSID;
-	*pos++ = ssid_len;
-	if (ssid && ssid_len) { /* ssid_len may be == 0 even if ssid is valid */
-		memcpy(pos, ssid, ssid_len);
-		pos += ssid_len;
-	}
-
-	len += ssid_len + 2;
-
-	if (WARN_ON(left < band_ie_len + common_ie_len))
-		return len;
-
-	if (band_ie && band_ie_len) {
-		memcpy(pos, band_ie, band_ie_len);
-		pos += band_ie_len;
-		len += band_ie_len;
-	}
-
-	if (common_ie && common_ie_len) {
-		memcpy(pos, common_ie, common_ie_len);
-		pos += common_ie_len;
-		len += common_ie_len;
-	}
-
-	return (u16)len;
-}
-
 static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac,
 					    struct ieee80211_vif *vif)
 {
 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-	bool *global_bound = data;
+	int *global_cnt = data;
 
 	if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt &&
 	    mvmvif->phy_ctxt->id < MAX_PHYS)
-		*global_bound = true;
+		*global_cnt += 1;
 }
 
 static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm,
@@ -294,27 +208,31 @@
 				     int n_ssids, u32 flags,
 				     struct iwl_mvm_scan_params *params)
 {
-	bool global_bound = false;
+	int global_cnt = 0;
 	enum ieee80211_band band;
 	u8 frag_passive_dwell = 0;
 
 	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
 					    IEEE80211_IFACE_ITER_NORMAL,
 					    iwl_mvm_scan_condition_iterator,
-					    &global_bound);
+					    &global_cnt);
 
-	if (!global_bound)
+	if (!global_cnt)
 		goto not_bound;
 
 	params->suspend_time = 30;
-	params->max_out_time = 170;
+	params->max_out_time = 120;
 
 	if (iwl_mvm_low_latency(mvm)) {
 		if (mvm->fw->ucode_capa.api[0] &
 		    IWL_UCODE_TLV_API_FRAGMENTED_SCAN) {
 			params->suspend_time = 105;
-			params->max_out_time = 70;
-			frag_passive_dwell = 20;
+			/*
+			 * If there is more than one active interface make
+			 * passive scan more fragmented.
+			 */
+			frag_passive_dwell = 40;
+			params->max_out_time = frag_passive_dwell;
 		} else {
 			params->suspend_time = 120;
 			params->max_out_time = 120;
@@ -331,7 +249,8 @@
 		 */
 		if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
 			u32 passive_dwell =
-				iwl_mvm_get_passive_dwell(IEEE80211_BAND_2GHZ);
+				iwl_mvm_get_passive_dwell(mvm,
+							  IEEE80211_BAND_2GHZ);
 			params->max_out_time = passive_dwell;
 		} else {
 			params->passive_fragmented = true;
@@ -345,11 +264,11 @@
 
 	for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
 		if (params->passive_fragmented)
-			params->dwell[band].passive = frag_passive_dwell;
-		else
-			params->dwell[band].passive =
-				iwl_mvm_get_passive_dwell(band);
-		params->dwell[band].active = iwl_mvm_get_active_dwell(band,
+			params->dwell[band].fragmented = frag_passive_dwell;
+
+		params->dwell[band].passive = iwl_mvm_get_passive_dwell(mvm,
+									band);
+		params->dwell[band].active = iwl_mvm_get_active_dwell(mvm, band,
 								      n_ssids);
 	}
 }
@@ -366,20 +285,11 @@
 {
 	int max_probe_len;
 
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
-		max_probe_len = SCAN_OFFLOAD_PROBE_REQ_SIZE;
-	else
-		max_probe_len = mvm->fw->ucode_capa.max_probe_length;
+	max_probe_len = SCAN_OFFLOAD_PROBE_REQ_SIZE;
 
 	/* we create the 802.11 header and SSID element */
 	max_probe_len -= 24 + 2;
 
-	/* basic ssid is added only for hw_scan with and old api */
-	if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID) &&
-	    !(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) &&
-	    !is_sched_scan)
-		max_probe_len -= 32;
-
 	/* DS parameter set element is added on 2.4GHZ band if required */
 	if (iwl_mvm_rrm_scan_needed(mvm))
 		max_probe_len -= 3;
@@ -391,9 +301,6 @@
 {
 	int max_ie_len = iwl_mvm_max_scan_ie_fw_cmd_room(mvm, is_sched_scan);
 
-	if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN))
-		return max_ie_len;
-
 	/* TODO: [BUG] This function should return the maximum allowed size of
 	 * scan IEs, however the LMAC scan api contains both 2GHZ and 5GHZ IEs
 	 * in the same command. So the correct implementation of this function
@@ -407,146 +314,16 @@
 	return max_ie_len;
 }
 
-int iwl_mvm_scan_request(struct iwl_mvm *mvm,
-			 struct ieee80211_vif *vif,
-			 struct cfg80211_scan_request *req)
-{
-	struct iwl_host_cmd hcmd = {
-		.id = SCAN_REQUEST_CMD,
-		.len = { 0, },
-		.data = { mvm->scan_cmd, },
-		.dataflags = { IWL_HCMD_DFL_NOCOPY, },
-	};
-	struct iwl_scan_cmd *cmd = mvm->scan_cmd;
-	int ret;
-	u32 status;
-	int ssid_len = 0;
-	u8 *ssid = NULL;
-	bool basic_ssid = !(mvm->fw->ucode_capa.flags &
-			   IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID);
-	struct iwl_mvm_scan_params params = {};
-
-	lockdep_assert_held(&mvm->mutex);
-
-	/* 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;
-	memset(cmd, 0, ksize(cmd));
-
-	cmd->channel_count = (u8)req->n_channels;
-	cmd->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME);
-	cmd->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH);
-	cmd->rxchain_sel_flags = iwl_mvm_scan_rx_chain(mvm);
-
-	iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, req->flags, &params);
-	cmd->max_out_time = cpu_to_le32(params.max_out_time);
-	cmd->suspend_time = cpu_to_le32(params.suspend_time);
-	if (params.passive_fragmented)
-		cmd->scan_flags |= SCAN_FLAGS_FRAGMENTED_SCAN;
-
-	cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req->channels[0]->band);
-	cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
-					MAC_FILTER_IN_BEACON);
-
-	if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
-		cmd->type = cpu_to_le32(SCAN_TYPE_DISCOVERY_FORCED);
-	else
-		cmd->type = cpu_to_le32(SCAN_TYPE_FORCED);
-
-	cmd->repeats = cpu_to_le32(1);
-
-	/*
-	 * If the user asked for passive scan, don't change to active scan if
-	 * you see any activity on the channel - remain passive.
-	 */
-	if (req->n_ssids > 0) {
-		cmd->passive2active = cpu_to_le16(1);
-		cmd->scan_flags |= SCAN_FLAGS_PASSIVE2ACTIVE;
-		if (basic_ssid) {
-			ssid = req->ssids[0].ssid;
-			ssid_len = req->ssids[0].ssid_len;
-		}
-	} else {
-		cmd->passive2active = 0;
-		cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE;
-	}
-
-	iwl_mvm_scan_fill_ssids(cmd->direct_scan, req->ssids, req->n_ssids,
-				basic_ssid ? 1 : 0);
-
-	cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
-					   3 << TX_CMD_FLG_BT_PRIO_POS);
-
-	cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id;
-	cmd->tx_cmd.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
-	cmd->tx_cmd.rate_n_flags =
-			iwl_mvm_scan_rate_n_flags(mvm, req->channels[0]->band,
-						  req->no_cck);
-
-	cmd->tx_cmd.len =
-		cpu_to_le16(iwl_mvm_fill_probe_req(
-			    (struct ieee80211_mgmt *)cmd->data,
-			    vif->addr,
-			    req->n_ssids, ssid, ssid_len,
-			    req->ie, req->ie_len, NULL, 0,
-			    mvm->fw->ucode_capa.max_probe_length));
-
-	iwl_mvm_scan_fill_channels(cmd, req, basic_ssid, &params);
-
-	cmd->len = cpu_to_le16(sizeof(struct iwl_scan_cmd) +
-		le16_to_cpu(cmd->tx_cmd.len) +
-		(cmd->channel_count * sizeof(struct iwl_scan_channel)));
-	hcmd.len[0] = le16_to_cpu(cmd->len);
-
-	status = SCAN_RESPONSE_OK;
-	ret = iwl_mvm_send_cmd_status(mvm, &hcmd, &status);
-	if (!ret && status == SCAN_RESPONSE_OK) {
-		IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n");
-	} else {
-		/*
-		 * If the scan failed, it usually means that the FW was unable
-		 * to allocate the time events. Warn on it, but maybe we
-		 * should try to send the command again with different params.
-		 */
-		IWL_ERR(mvm, "Scan failed! status 0x%x ret %d\n",
-			status, ret);
-		mvm->scan_status = IWL_MVM_SCAN_NONE;
-		ret = -EIO;
-	}
-	return ret;
-}
-
-int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
-			  struct iwl_device_cmd *cmd)
-{
-	struct iwl_rx_packet *pkt = rxb_addr(rxb);
-	struct iwl_cmd_response *resp = (void *)pkt->data;
-
-	IWL_DEBUG_SCAN(mvm, "Scan response received. status 0x%x\n",
-		       le32_to_cpu(resp->status));
-	return 0;
-}
-
-int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
-			  struct iwl_device_cmd *cmd)
+int iwl_mvm_rx_scan_offload_iter_complete_notif(struct iwl_mvm *mvm,
+						struct iwl_rx_cmd_buffer *rxb,
+						struct iwl_device_cmd *cmd)
 {
 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
 	struct iwl_scan_complete_notif *notif = (void *)pkt->data;
 
-	lockdep_assert_held(&mvm->mutex);
-
-	IWL_DEBUG_SCAN(mvm, "Scan complete: status=0x%x scanned channels=%d\n",
+	IWL_DEBUG_SCAN(mvm,
+		       "Scan offload iteration complete: status=0x%x scanned channels=%d\n",
 		       notif->status, notif->scanned_channels);
-
-	if (mvm->scan_status == IWL_MVM_SCAN_OS)
-		mvm->scan_status = IWL_MVM_SCAN_NONE;
-	ieee80211_scan_completed(mvm->hw, notif->status != SCAN_COMP_STATUS_OK);
-
-	iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
-
 	return 0;
 }
 
@@ -554,106 +331,21 @@
 				    struct iwl_rx_cmd_buffer *rxb,
 				    struct iwl_device_cmd *cmd)
 {
-	struct iwl_rx_packet *pkt = rxb_addr(rxb);
-
-	if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) &&
-	    !(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
-		struct iwl_sched_scan_results *notif = (void *)pkt->data;
-
-		if (!(notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN))
-			return 0;
-	}
-
 	IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
 	ieee80211_sched_scan_results(mvm->hw);
 
 	return 0;
 }
 
-static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
-				     struct iwl_rx_packet *pkt, void *data)
-{
-	struct iwl_mvm *mvm =
-		container_of(notif_wait, struct iwl_mvm, notif_wait);
-	struct iwl_scan_complete_notif *notif;
-	u32 *resp;
-
-	switch (pkt->hdr.cmd) {
-	case SCAN_ABORT_CMD:
-		resp = (void *)pkt->data;
-		if (*resp == CAN_ABORT_STATUS) {
-			IWL_DEBUG_SCAN(mvm,
-				       "Scan can be aborted, wait until completion\n");
-			return false;
-		}
-
-		/*
-		 * If scan cannot be aborted, it means that we had a
-		 * SCAN_COMPLETE_NOTIFICATION in the pipe and it called
-		 * ieee80211_scan_completed already.
-		 */
-		IWL_DEBUG_SCAN(mvm, "Scan cannot be aborted, exit now: %d\n",
-			       *resp);
-		return true;
-
-	case SCAN_COMPLETE_NOTIFICATION:
-		notif = (void *)pkt->data;
-		IWL_DEBUG_SCAN(mvm, "Scan aborted: status 0x%x\n",
-			       notif->status);
-		return true;
-
-	default:
-		WARN_ON(1);
-		return false;
-	};
-}
-
-static int iwl_mvm_cancel_regular_scan(struct iwl_mvm *mvm)
-{
-	struct iwl_notification_wait wait_scan_abort;
-	static const u8 scan_abort_notif[] = { SCAN_ABORT_CMD,
-					       SCAN_COMPLETE_NOTIFICATION };
-	int ret;
-
-	iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
-				   scan_abort_notif,
-				   ARRAY_SIZE(scan_abort_notif),
-				   iwl_mvm_scan_abort_notif, 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 */
-		goto out_remove_notif;
-	}
-
-	return iwl_wait_notification(&mvm->notif_wait, &wait_scan_abort, HZ);
-
-out_remove_notif:
-	iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort);
-	return ret;
-}
-
 int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
 					   struct iwl_rx_cmd_buffer *rxb,
 					   struct iwl_device_cmd *cmd)
 {
 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
-	u8 status, ebs_status;
+	struct iwl_periodic_scan_complete *scan_notif;
 
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) {
-		struct iwl_periodic_scan_complete *scan_notif;
+	scan_notif = (void *)pkt->data;
 
-		scan_notif = (void *)pkt->data;
-		status = scan_notif->status;
-		ebs_status = scan_notif->ebs_status;
-	} else  {
-		struct iwl_scan_offload_complete *scan_notif;
-
-		scan_notif = (void *)pkt->data;
-		status = scan_notif->status;
-		ebs_status = scan_notif->ebs_status;
-	}
 	/* scan status must be locked for proper checking */
 	lockdep_assert_held(&mvm->mutex);
 
@@ -661,9 +353,9 @@
 		       "%s completed, status %s, EBS status %s\n",
 		       mvm->scan_status == IWL_MVM_SCAN_SCHED ?
 				"Scheduled scan" : "Scan",
-		       status == IWL_SCAN_OFFLOAD_COMPLETED ?
+		       scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
 				"completed" : "aborted",
-		       ebs_status == IWL_SCAN_EBS_SUCCESS ?
+		       scan_notif->ebs_status == IWL_SCAN_EBS_SUCCESS ?
 				"success" : "failed");
 
 
@@ -674,63 +366,16 @@
 	} else if (mvm->scan_status == IWL_MVM_SCAN_OS) {
 		mvm->scan_status = IWL_MVM_SCAN_NONE;
 		ieee80211_scan_completed(mvm->hw,
-					 status == IWL_SCAN_OFFLOAD_ABORTED);
+				scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED);
 		iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
 	}
 
-	mvm->last_ebs_successful = !ebs_status;
+	if (scan_notif->ebs_status)
+		mvm->last_ebs_successful = false;
 
 	return 0;
 }
 
-static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
-					  struct ieee80211_vif *vif,
-					  struct ieee80211_scan_ies *ies,
-					  enum ieee80211_band band,
-					  struct iwl_tx_cmd *cmd,
-					  u8 *data)
-{
-	u16 cmd_len;
-
-	cmd->tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL);
-	cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
-	cmd->sta_id = mvm->aux_sta.sta_id;
-
-	cmd->rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, band, false);
-
-	cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data,
-					 vif->addr,
-					 1, NULL, 0,
-					 ies->ies[band], ies->len[band],
-					 ies->common_ies, ies->common_ie_len,
-					 SCAN_OFFLOAD_PROBE_REQ_SIZE);
-	cmd->len = cpu_to_le16(cmd_len);
-}
-
-static void iwl_build_scan_cmd(struct iwl_mvm *mvm,
-			       struct ieee80211_vif *vif,
-			       struct cfg80211_sched_scan_request *req,
-			       struct iwl_scan_offload_cmd *scan,
-			       struct iwl_mvm_scan_params *params)
-{
-	scan->channel_count = req->n_channels;
-	scan->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME);
-	scan->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH);
-	scan->good_CRC_th = IWL_GOOD_CRC_TH_DEFAULT;
-	scan->rx_chain = iwl_mvm_scan_rx_chain(mvm);
-
-	scan->max_out_time = cpu_to_le32(params->max_out_time);
-	scan->suspend_time = cpu_to_le32(params->suspend_time);
-
-	scan->filter_flags |= cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
-					  MAC_FILTER_IN_BEACON);
-	scan->scan_type = cpu_to_le32(SCAN_TYPE_BACKGROUND);
-	scan->rep_count = cpu_to_le32(1);
-
-	if (params->passive_fragmented)
-		scan->scan_flags |= SCAN_FLAGS_FRAGMENTED_SCAN;
-}
-
 static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list)
 {
 	int i;
@@ -788,127 +433,6 @@
 	}
 }
 
-static void iwl_build_channel_cfg(struct iwl_mvm *mvm,
-				  struct cfg80211_sched_scan_request *req,
-				  u8 *channels_buffer,
-				  enum ieee80211_band band,
-				  int *head,
-				  u32 ssid_bitmap,
-				  struct iwl_mvm_scan_params *params)
-{
-	u32 n_channels = mvm->fw->ucode_capa.n_scan_channels;
-	__le32 *type = (__le32 *)channels_buffer;
-	__le16 *channel_number = (__le16 *)(type + n_channels);
-	__le16 *iter_count = channel_number + n_channels;
-	__le32 *iter_interval = (__le32 *)(iter_count + n_channels);
-	u8 *active_dwell = (u8 *)(iter_interval + n_channels);
-	u8 *passive_dwell = active_dwell + n_channels;
-	int i, index = 0;
-
-	for (i = 0; i < req->n_channels; i++) {
-		struct ieee80211_channel *chan = req->channels[i];
-
-		if (chan->band != band)
-			continue;
-
-		index = *head;
-		(*head)++;
-
-		channel_number[index] = cpu_to_le16(chan->hw_value);
-		active_dwell[index] = params->dwell[band].active;
-		passive_dwell[index] = params->dwell[band].passive;
-
-		iter_count[index] = cpu_to_le16(1);
-		iter_interval[index] = 0;
-
-		if (!(chan->flags & IEEE80211_CHAN_NO_IR))
-			type[index] |=
-				cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE);
-
-		type[index] |= cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL |
-					   IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL);
-
-		if (chan->flags & IEEE80211_CHAN_NO_HT40)
-			type[index] |=
-				cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_NARROW);
-
-		/* scan for all SSIDs from req->ssids */
-		type[index] |= cpu_to_le32(ssid_bitmap);
-	}
-}
-
-int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
-			      struct ieee80211_vif *vif,
-			      struct cfg80211_sched_scan_request *req,
-			      struct ieee80211_scan_ies *ies)
-{
-	int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels;
-	int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
-	int head = 0;
-	u32 ssid_bitmap;
-	int cmd_len;
-	int ret;
-	u8 *probes;
-	bool basic_ssid = !(mvm->fw->ucode_capa.flags &
-			    IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID);
-
-	struct iwl_scan_offload_cfg *scan_cfg;
-	struct iwl_host_cmd cmd = {
-		.id = SCAN_OFFLOAD_CONFIG_CMD,
-	};
-	struct iwl_mvm_scan_params params = {};
-
-	lockdep_assert_held(&mvm->mutex);
-
-	cmd_len = sizeof(struct iwl_scan_offload_cfg) +
-		  mvm->fw->ucode_capa.n_scan_channels * IWL_SCAN_CHAN_SIZE +
-		  2 * SCAN_OFFLOAD_PROBE_REQ_SIZE;
-
-	scan_cfg = kzalloc(cmd_len, GFP_KERNEL);
-	if (!scan_cfg)
-		return -ENOMEM;
-
-	probes = scan_cfg->data +
-		mvm->fw->ucode_capa.n_scan_channels * IWL_SCAN_CHAN_SIZE;
-
-	iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, 0, &params);
-	iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd, &params);
-	scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len);
-
-	iwl_scan_offload_build_ssid(req, scan_cfg->scan_cmd.direct_scan,
-				    &ssid_bitmap, basic_ssid);
-	/* build tx frames for supported bands */
-	if (band_2ghz) {
-		iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
-					      IEEE80211_BAND_2GHZ,
-					      &scan_cfg->scan_cmd.tx_cmd[0],
-					      probes);
-		iwl_build_channel_cfg(mvm, req, scan_cfg->data,
-				      IEEE80211_BAND_2GHZ, &head,
-				      ssid_bitmap, &params);
-	}
-	if (band_5ghz) {
-		iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
-					      IEEE80211_BAND_5GHZ,
-					      &scan_cfg->scan_cmd.tx_cmd[1],
-					      probes +
-						SCAN_OFFLOAD_PROBE_REQ_SIZE);
-		iwl_build_channel_cfg(mvm, req, scan_cfg->data,
-				      IEEE80211_BAND_5GHZ, &head,
-				      ssid_bitmap, &params);
-	}
-
-	cmd.data[0] = scan_cfg;
-	cmd.len[0] = cmd_len;
-	cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
-
-	IWL_DEBUG_SCAN(mvm, "Sending scheduled scan config\n");
-
-	ret = iwl_mvm_send_cmd(mvm, &cmd);
-	kfree(scan_cfg);
-	return ret;
-}
-
 int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
 				       struct cfg80211_sched_scan_request *req)
 {
@@ -991,33 +515,6 @@
 	return true;
 }
 
-int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
-			     struct cfg80211_sched_scan_request *req)
-{
-	struct iwl_scan_offload_req scan_req = {
-		.watchdog = IWL_SCHED_SCAN_WATCHDOG,
-
-		.schedule_line[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS,
-		.schedule_line[0].delay = cpu_to_le16(req->interval / 1000),
-		.schedule_line[0].full_scan_mul = 1,
-
-		.schedule_line[1].iterations = 0xff,
-		.schedule_line[1].delay = cpu_to_le16(req->interval / 1000),
-		.schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER,
-	};
-
-	if (iwl_mvm_scan_pass_all(mvm, req))
-		scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_PASS_ALL);
-
-	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);
-}
-
 int iwl_mvm_scan_offload_start(struct iwl_mvm *mvm,
 			       struct ieee80211_vif *vif,
 			       struct cfg80211_sched_scan_request *req,
@@ -1030,21 +527,12 @@
 		if (ret)
 			return ret;
 		ret = iwl_mvm_sched_scan_umac(mvm, vif, req, ies);
-	} else if ((mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+	} else {
 		mvm->scan_status = IWL_MVM_SCAN_SCHED;
 		ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
 		if (ret)
 			return ret;
 		ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
-	} else {
-		mvm->scan_status = IWL_MVM_SCAN_SCHED;
-		ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
-		if (ret)
-			return ret;
-		ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
-		if (ret)
-			return ret;
-		ret = iwl_mvm_sched_scan_start(mvm, req);
 	}
 
 	return ret;
@@ -1061,9 +549,7 @@
 	/* Exit instantly with error when device is not ready
 	 * to receive scan abort command or it does not perform
 	 * scheduled scan currently */
-	if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
-	    (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
-	     mvm->scan_status != IWL_MVM_SCAN_OS))
+	if (mvm->scan_status == IWL_MVM_SCAN_NONE)
 		return -EIO;
 
 	ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status);
@@ -1098,11 +584,12 @@
 		return iwl_umac_scan_stop(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN,
 					  notify);
 
-	if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
-	    (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
-	     mvm->scan_status != IWL_MVM_SCAN_OS)) {
-		IWL_DEBUG_SCAN(mvm, "No scan to stop\n");
+	if (mvm->scan_status == IWL_MVM_SCAN_NONE)
 		return 0;
+
+	if (iwl_mvm_is_radio_killed(mvm)) {
+		ret = 0;
+		goto out;
 	}
 
 	iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done,
@@ -1115,16 +602,14 @@
 		IWL_DEBUG_SCAN(mvm, "Send stop %sscan failed %d\n",
 			       sched ? "offloaded " : "", ret);
 		iwl_remove_notification(&mvm->notif_wait, &wait_scan_done);
-		return ret;
+		goto out;
 	}
 
 	IWL_DEBUG_SCAN(mvm, "Successfully sent stop %sscan\n",
 		       sched ? "offloaded " : "");
 
 	ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ);
-	if (ret)
-		return ret;
-
+out:
 	/*
 	 * Clear the scan status so the next scan requests will succeed. This
 	 * also ensures the Rx handler doesn't do anything, as the scan was
@@ -1143,7 +628,7 @@
 			ieee80211_scan_completed(mvm->hw, true);
 	}
 
-	return 0;
+	return ret;
 }
 
 static void iwl_mvm_unified_scan_fill_tx_cmd(struct iwl_mvm *mvm,
@@ -1283,25 +768,13 @@
 	cmd->passive_dwell = params->dwell[IEEE80211_BAND_2GHZ].passive;
 	if (params->passive_fragmented)
 		cmd->fragmented_dwell =
-				params->dwell[IEEE80211_BAND_2GHZ].passive;
+				params->dwell[IEEE80211_BAND_2GHZ].fragmented;
 	cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm);
 	cmd->max_out_time = cpu_to_le32(params->max_out_time);
 	cmd->suspend_time = cpu_to_le32(params->suspend_time);
 	cmd->scan_prio = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
 	cmd->iter_num = cpu_to_le32(1);
 
-	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT &&
-	    mvm->last_ebs_successful) {
-		cmd->channel_opt[0].flags =
-			cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
-				    IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
-				    IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
-		cmd->channel_opt[1].flags =
-			cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
-				    IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
-				    IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
-	}
-
 	if (iwl_mvm_rrm_scan_needed(mvm))
 		cmd->scan_flags |=
 			cpu_to_le32(IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED);
@@ -1376,6 +849,22 @@
 	cmd->schedule[1].iterations = 0;
 	cmd->schedule[1].full_scan_mul = 0;
 
+	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SINGLE_SCAN_EBS &&
+	    mvm->last_ebs_successful) {
+		cmd->channel_opt[0].flags =
+			cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+				    IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+				    IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+		cmd->channel_opt[0].non_ebs_ratio =
+			cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO);
+		cmd->channel_opt[1].flags =
+			cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+				    IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+				    IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+		cmd->channel_opt[1].non_ebs_ratio =
+			cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO);
+	}
+
 	for (i = 1; i <= req->req.n_ssids; i++)
 		ssid_bitmap |= BIT(i);
 
@@ -1446,8 +935,12 @@
 
 	cmd->n_channels = (u8)req->n_channels;
 
+	cmd->delay = cpu_to_le32(req->delay);
+
 	if (iwl_mvm_scan_pass_all(mvm, req))
 		flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
+	else
+		flags |= IWL_MVM_LMAC_SCAN_FLAG_MATCH;
 
 	if (req->n_ssids == 1 && req->ssids[0].ssid_len != 0)
 		flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
@@ -1458,6 +951,11 @@
 	if (req->n_ssids == 0)
 		flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE;
 
+#ifdef CPTCFG_IWLWIFI_DEBUGFS
+	if (mvm->scan_iter_notif_enabled)
+		flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE;
+#endif
+
 	cmd->scan_flags |= cpu_to_le32(flags);
 
 	cmd->flags = iwl_mvm_scan_rxon_flags(req->channels[0]->band);
@@ -1474,6 +972,22 @@
 	cmd->schedule[1].iterations = 0xff;
 	cmd->schedule[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER;
 
+	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT &&
+	    mvm->last_ebs_successful) {
+		cmd->channel_opt[0].flags =
+			cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+				    IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+				    IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+		cmd->channel_opt[0].non_ebs_ratio =
+			cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO);
+		cmd->channel_opt[1].flags =
+			cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+				    IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+				    IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+		cmd->channel_opt[1].non_ebs_ratio =
+			cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO);
+	}
+
 	iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels,
 				       ssid_bitmap, cmd);
 
@@ -1519,9 +1033,7 @@
 		return 0;
 	}
 
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
-		return iwl_mvm_scan_offload_stop(mvm, true);
-	return iwl_mvm_cancel_regular_scan(mvm);
+	return iwl_mvm_scan_offload_stop(mvm, true);
 }
 
 /* UMAC scan API */
@@ -1603,7 +1115,7 @@
 					 SCAN_CONFIG_FLAG_SET_MAC_ADDR |
 					 SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS|
 					 SCAN_CONFIG_N_CHANNELS(num_channels));
-	scan_config->tx_chains = cpu_to_le32(mvm->fw->valid_tx_ant);
+	scan_config->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
 	scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm));
 	scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm);
 	scan_config->out_of_channel_time = cpu_to_le32(170);
@@ -1622,10 +1134,10 @@
 
 	band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
 	for (i = 0; i < band->n_channels; i++, j++)
-		scan_config->channel_array[j] = band->channels[i].center_freq;
+		scan_config->channel_array[j] = band->channels[i].hw_value;
 	band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
 	for (i = 0; i < band->n_channels; i++, j++)
-		scan_config->channel_array[j] = band->channels[i].center_freq;
+		scan_config->channel_array[j] = band->channels[i].hw_value;
 
 	cmd.data[0] = scan_config;
 	cmd.len[0] = cmd_size;
@@ -1667,6 +1179,18 @@
 	return false;
 }
 
+static int iwl_mvm_find_first_scan(struct iwl_mvm *mvm,
+				   enum iwl_umac_scan_uid_type type)
+{
+	int i;
+
+	for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+		if (mvm->scan_uid[i] & type)
+			return i;
+
+	return i;
+}
+
 static u32 iwl_generate_scan_uid(struct iwl_mvm *mvm,
 				 enum iwl_umac_scan_uid_type type)
 {
@@ -1704,7 +1228,7 @@
 	cmd->passive_dwell = params->dwell[IEEE80211_BAND_2GHZ].passive;
 	if (params->passive_fragmented)
 		cmd->fragmented_dwell =
-				params->dwell[IEEE80211_BAND_2GHZ].passive;
+				params->dwell[IEEE80211_BAND_2GHZ].fragmented;
 	cmd->max_out_time = cpu_to_le32(params->max_out_time);
 	cmd->suspend_time = cpu_to_le32(params->suspend_time);
 	cmd->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
@@ -1802,6 +1326,13 @@
 	flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL;
 
 	cmd->general_flags = cpu_to_le32(flags);
+
+	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SINGLE_SCAN_EBS &&
+	    mvm->last_ebs_successful)
+		cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS |
+				     IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+				     IWL_SCAN_CHANNEL_FLAG_CACHE_ADD;
+
 	cmd->n_channels = req->req.n_channels;
 
 	for (i = 0; i < req->req.n_ssids; i++)
@@ -1919,7 +1450,13 @@
 				cpu_to_le16(req->interval / MSEC_PER_SEC);
 	sec_part->schedule[0].iter_count = 0xff;
 
-	sec_part->delay = 0;
+	if (req->delay > U16_MAX) {
+		IWL_DEBUG_SCAN(mvm,
+			       "delay value is > 16-bits, set to max possible\n");
+		sec_part->delay = cpu_to_le16(U16_MAX);
+	} else {
+		sec_part->delay = cpu_to_le16(req->delay);
+	}
 
 	iwl_mvm_build_unified_scan_probe(mvm, vif, ies, &sec_part->preq,
 		req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
@@ -1965,7 +1502,9 @@
 		       notif->ebs_status == IWL_SCAN_EBS_SUCCESS ?
 				"success" : "failed");
 
-	mvm->last_ebs_successful = !notif->ebs_status;
+	if (notif->ebs_status)
+		mvm->last_ebs_successful = false;
+
 	mvm->scan_uid[uid_idx] = 0;
 
 	if (!sched) {
@@ -1998,10 +1537,14 @@
 
 	/*
 	 * Clear scan uid of scans that was aborted from above and completed
-	 * in FW so the RX handler does nothing.
+	 * in FW so the RX handler does nothing. Set last_ebs_successful here if
+	 * needed.
 	 */
 	scan_done->mvm->scan_uid[uid_idx] = 0;
 
+	if (notif->ebs_status)
+		scan_done->mvm->last_ebs_successful = false;
+
 	return !iwl_mvm_find_scan_type(scan_done->mvm, scan_done->type);
 }
 
@@ -2085,14 +1628,59 @@
 				mvm->fw->ucode_capa.n_scan_channels +
 			sizeof(struct iwl_scan_req_umac_tail);
 
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
-		return sizeof(struct iwl_scan_req_unified_lmac) +
-			sizeof(struct iwl_scan_channel_cfg_lmac) *
-				mvm->fw->ucode_capa.n_scan_channels +
-			sizeof(struct iwl_scan_probe_req);
+	return sizeof(struct iwl_scan_req_unified_lmac) +
+		sizeof(struct iwl_scan_channel_cfg_lmac) *
+		mvm->fw->ucode_capa.n_scan_channels +
+		sizeof(struct iwl_scan_probe_req);
+}
 
-	return sizeof(struct iwl_scan_cmd) +
-		mvm->fw->ucode_capa.max_probe_length +
-			mvm->fw->ucode_capa.n_scan_channels *
-		sizeof(struct iwl_scan_channel);
+/*
+ * This function is used in nic restart flow, to inform mac80211 about scans
+ * that was aborted by restart flow or by an assert.
+ */
+void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm)
+{
+	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+		u32 uid, i;
+
+		uid = iwl_mvm_find_first_scan(mvm, IWL_UMAC_SCAN_UID_REG_SCAN);
+		if (uid < IWL_MVM_MAX_SIMULTANEOUS_SCANS) {
+			ieee80211_scan_completed(mvm->hw, true);
+			mvm->scan_uid[uid] = 0;
+		}
+		uid = iwl_mvm_find_first_scan(mvm,
+					      IWL_UMAC_SCAN_UID_SCHED_SCAN);
+		if (uid < IWL_MVM_MAX_SIMULTANEOUS_SCANS && !mvm->restart_fw) {
+			ieee80211_sched_scan_stopped(mvm->hw);
+			mvm->scan_uid[uid] = 0;
+		}
+
+		/* We shouldn't have any UIDs still set.  Loop over all the
+		 * UIDs to make sure there's nothing left there and warn if
+		 * any is found.
+		 */
+		for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+			if (WARN_ONCE(mvm->scan_uid[i],
+				      "UMAC scan UID %d was not cleaned\n",
+				      mvm->scan_uid[i]))
+				mvm->scan_uid[i] = 0;
+		}
+	} else {
+		switch (mvm->scan_status) {
+		case IWL_MVM_SCAN_NONE:
+			break;
+		case IWL_MVM_SCAN_OS:
+			ieee80211_scan_completed(mvm->hw, true);
+			break;
+		case IWL_MVM_SCAN_SCHED:
+			/*
+			 * Sched scan will be restarted by mac80211 in
+			 * restart_hw, so do not report if FW is about to be
+			 * restarted.
+			 */
+			if (!mvm->restart_fw)
+				ieee80211_sched_scan_stopped(mvm->hw);
+			break;
+		}
+	}
 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c
index 7eb78e2..b0f59fd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sf.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sf.c
@@ -99,7 +99,35 @@
 
 /*
  * Aging and idle timeouts for the different possible scenarios
- * in SF_FULL_ON state.
+ * in default configuration
+ */
+static const
+__le32 sf_full_timeout_def[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
+	{
+		cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER_DEF),
+		cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
+	},
+	{
+		cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER_DEF),
+		cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER_DEF)
+	},
+	{
+		cpu_to_le32(SF_MCAST_AGING_TIMER_DEF),
+		cpu_to_le32(SF_MCAST_IDLE_TIMER_DEF)
+	},
+	{
+		cpu_to_le32(SF_BA_AGING_TIMER_DEF),
+		cpu_to_le32(SF_BA_IDLE_TIMER_DEF)
+	},
+	{
+		cpu_to_le32(SF_TX_RE_AGING_TIMER_DEF),
+		cpu_to_le32(SF_TX_RE_IDLE_TIMER_DEF)
+	},
+};
+
+/*
+ * Aging and idle timeouts for the different possible scenarios
+ * in single BSS MAC configuration.
  */
 static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
 	{
@@ -124,7 +152,8 @@
 	},
 };
 
-static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd,
+static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm,
+				    struct iwl_sf_cfg_cmd *sf_cmd,
 				    struct ieee80211_sta *sta)
 {
 	int i, j, watermark;
@@ -163,24 +192,38 @@
 					cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
 		}
 	}
-	BUILD_BUG_ON(sizeof(sf_full_timeout) !=
-		     sizeof(__le32) * SF_NUM_SCENARIO * SF_NUM_TIMEOUT_TYPES);
 
-	memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
-	       sizeof(sf_full_timeout));
+	if (sta || IWL_UCODE_API(mvm->fw->ucode_ver) < 13) {
+		BUILD_BUG_ON(sizeof(sf_full_timeout) !=
+			     sizeof(__le32) * SF_NUM_SCENARIO *
+			     SF_NUM_TIMEOUT_TYPES);
+
+		memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
+		       sizeof(sf_full_timeout));
+	} else {
+		BUILD_BUG_ON(sizeof(sf_full_timeout_def) !=
+			     sizeof(__le32) * SF_NUM_SCENARIO *
+			     SF_NUM_TIMEOUT_TYPES);
+
+		memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def,
+		       sizeof(sf_full_timeout_def));
+	}
+
 }
 
 static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
 			     enum iwl_sf_state new_state)
 {
 	struct iwl_sf_cfg_cmd sf_cmd = {
-		.state = cpu_to_le32(new_state),
+		.state = cpu_to_le32(SF_FULL_ON),
 	};
 	struct ieee80211_sta *sta;
 	int ret = 0;
 
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF &&
-	    mvm->cfg->disable_dummy_notification)
+	if (IWL_UCODE_API(mvm->fw->ucode_ver) < 13)
+		sf_cmd.state = cpu_to_le32(new_state);
+
+	if (mvm->cfg->disable_dummy_notification)
 		sf_cmd.state |= cpu_to_le32(SF_CFG_DUMMY_NOTIF_OFF);
 
 	/*
@@ -192,6 +235,8 @@
 
 	switch (new_state) {
 	case SF_UNINIT:
+		if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 13)
+			iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
 		break;
 	case SF_FULL_ON:
 		if (sta_id == IWL_MVM_STATION_COUNT) {
@@ -206,11 +251,11 @@
 			rcu_read_unlock();
 			return -EINVAL;
 		}
-		iwl_mvm_fill_sf_command(&sf_cmd, sta);
+		iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta);
 		rcu_read_unlock();
 		break;
 	case SF_INIT_OFF:
-		iwl_mvm_fill_sf_command(&sf_cmd, NULL);
+		iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
 		break;
 	default:
 		WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index d86fe43..1845b79 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -99,7 +99,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_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 	struct iwl_mvm_add_sta_cmd add_sta_cmd = {
 		.sta_id = mvm_sta->sta_id,
 		.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color),
@@ -209,6 +209,8 @@
 {
 	unsigned long used_hw_queues;
 	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+	unsigned int wdg_timeout =
+		iwl_mvm_get_wd_timeout(mvm, NULL, true, false);
 	u32 ac;
 
 	lockdep_assert_held(&mvm->mutex);
@@ -232,7 +234,7 @@
 	/* Found a place for all queues - enable them */
 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
 		iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac],
-				      iwl_mvm_ac_to_tx_fifo[ac]);
+				      iwl_mvm_ac_to_tx_fifo[ac], wdg_timeout);
 		mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]);
 	}
 
@@ -250,8 +252,8 @@
 
 	/* disable the TDLS STA-specific queues */
 	sta_msk = mvmsta->tfd_queue_msk;
-	for_each_set_bit(i, &sta_msk, sizeof(sta_msk))
-		iwl_mvm_disable_txq(mvm, i);
+	for_each_set_bit(i, &sta_msk, sizeof(sta_msk) * BITS_PER_BYTE)
+		iwl_mvm_disable_txq(mvm, i, 0);
 }
 
 int iwl_mvm_add_sta(struct iwl_mvm *mvm,
@@ -259,7 +261,7 @@
 		    struct ieee80211_sta *sta)
 {
 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-	struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 	int i, ret, sta_id;
 
 	lockdep_assert_held(&mvm->mutex);
@@ -270,7 +272,7 @@
 	else
 		sta_id = mvm_sta->sta_id;
 
-	if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
+	if (sta_id == IWL_MVM_STATION_COUNT)
 		return -ENOSPC;
 
 	spin_lock_init(&mvm_sta->lock);
@@ -464,8 +466,8 @@
 		if (mvm->tfd_drained[sta_id]) {
 			unsigned long i, msk = mvm->tfd_drained[sta_id];
 
-			for_each_set_bit(i, &msk, sizeof(msk))
-				iwl_mvm_disable_txq(mvm, i);
+			for_each_set_bit(i, &msk, sizeof(msk) * BITS_PER_BYTE)
+				iwl_mvm_disable_txq(mvm, i, 0);
 
 			mvm->tfd_drained[sta_id] = 0;
 			IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n",
@@ -481,15 +483,25 @@
 		   struct ieee80211_sta *sta)
 {
 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-	struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 	int ret;
 
 	lockdep_assert_held(&mvm->mutex);
 
 	if (vif->type == NL80211_IFTYPE_STATION &&
 	    mvmvif->ap_sta_id == mvm_sta->sta_id) {
+		ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
+		if (ret)
+			return ret;
 		/* flush its queues here since we are freeing mvm_sta */
 		ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
+		if (ret)
+			return ret;
+		ret = iwl_trans_wait_tx_queue_empty(mvm->trans,
+						    mvm_sta->tfd_queue_msk);
+		if (ret)
+			return ret;
+		ret = iwl_mvm_drain_sta(mvm, mvm_sta, false);
 
 		/* if we are associated - we can't remove the AP STA now */
 		if (vif->bss_conf.assoc)
@@ -626,13 +638,16 @@
 
 int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
 {
+	unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ?
+					mvm->cfg->base_params->wd_timeout :
+					IWL_WATCHDOG_DISABLED;
 	int ret;
 
 	lockdep_assert_held(&mvm->mutex);
 
 	/* Map Aux queue to fifo - needs to happen before adding Aux station */
 	iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue,
-			      IWL_MVM_TX_FIFO_MCAST);
+			      IWL_MVM_TX_FIFO_MCAST, wdg_timeout);
 
 	/* Allocate aux station and assign to it the aux queue */
 	ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue),
@@ -774,7 +789,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_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 	struct iwl_mvm_add_sta_cmd cmd = {};
 	int ret;
 	u32 status;
@@ -834,7 +849,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_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 	struct iwl_mvm_add_sta_cmd cmd = {};
 	int ret;
 	u32 status;
@@ -965,6 +980,8 @@
 {
 	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
 	struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+	unsigned int wdg_timeout =
+		iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false);
 	int queue, fifo, ret;
 	u16 ssn;
 
@@ -988,7 +1005,7 @@
 		return -EIO;
 
 	iwl_mvm_enable_agg_txq(mvm, queue, fifo, mvmsta->sta_id, tid,
-			       buf_size, ssn);
+			       buf_size, ssn, wdg_timeout);
 
 	/*
 	 * Even though in theory the peer could have different
@@ -1058,7 +1075,7 @@
 
 		iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false);
 
-		iwl_mvm_disable_txq(mvm, txq_id);
+		iwl_mvm_disable_txq(mvm, txq_id, 0);
 		return 0;
 	case IWL_AGG_STARTING:
 	case IWL_EMPTYING_HW_QUEUE_ADDBA:
@@ -1111,12 +1128,16 @@
 	spin_unlock_bh(&mvmsta->lock);
 
 	if (old_state >= IWL_AGG_ON) {
+		iwl_mvm_drain_sta(mvm, mvmsta, true);
 		if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true))
 			IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
+		iwl_trans_wait_tx_queue_empty(mvm->trans,
+					      mvmsta->tfd_queue_msk);
+		iwl_mvm_drain_sta(mvm, mvmsta, false);
 
 		iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false);
 
-		iwl_mvm_disable_txq(mvm, tid_data->txq_id);
+		iwl_mvm_disable_txq(mvm, tid_data->txq_id, 0);
 	}
 
 	mvm->queue_to_mac80211[tid_data->txq_id] =
@@ -1144,10 +1165,10 @@
 static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif,
 				 struct ieee80211_sta *sta)
 {
-	struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
 	if (sta) {
-		struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+		struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 
 		return mvm_sta->sta_id;
 	}
@@ -1196,6 +1217,7 @@
 		break;
 	case WLAN_CIPHER_SUITE_WEP104:
 		key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES);
+		/* fall through */
 	case WLAN_CIPHER_SUITE_WEP40:
 		key_flags |= cpu_to_le16(STA_KEY_FLG_WEP);
 		memcpy(cmd.key + 3, keyconf->key, keyconf->keylen);
@@ -1280,7 +1302,7 @@
 				       struct ieee80211_vif *vif,
 				       struct ieee80211_sta *sta)
 {
-	struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
 	if (sta)
 		return sta->addr;
@@ -1671,9 +1693,6 @@
 	};
 	int ret;
 
-	if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_DISABLE_STA_TX))
-		return;
-
 	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);
@@ -1695,8 +1714,8 @@
 	mvm_sta->disable_tx = disable;
 
 	/*
-	 * Tell mac80211 to start/stop queueing tx for this station,
-	 * but don't stop queueing if there are still pending frames
+	 * Tell mac80211 to start/stop queuing tx for this station,
+	 * but don't stop queuing if there are still pending frames
 	 * for this station.
 	 */
 	if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id]))
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
index d8f4897..748f5dc 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -150,7 +150,7 @@
  * DOC: station table - AP Station in STA mode
  *
  * %iwl_mvm_vif includes the index of the AP station in the fw's STA table:
- * %ap_sta_id. To get the point to the coresponsding %ieee80211_sta,
+ * %ap_sta_id. To get the point to the corresponding %ieee80211_sta,
  * &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove
  * the AP station from the fw before setting the MAC context as unassociated.
  * Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is
@@ -209,14 +209,14 @@
  * When a trigger frame is received, mac80211 tells the driver to send frames
  * from the AMPDU queues or sends frames to non-aggregation queues itself,
  * depending on which ACs are delivery-enabled and what TID has frames to
- * transmit. Note that mac80211 has all the knowledege since all the non-agg
+ * transmit. Note that mac80211 has all the knowledge since all the non-agg
  * frames are buffered / filtered, and the driver tells mac80211 about agg
  * frames). The driver needs to tell the fw to let frames out even if the
  * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count.
  *
  * When we receive a frame from that station with PM bit unset, the driver
  * needs to let the fw know that this station isn't asleep any more. This is
- * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signalling the
+ * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signaling the
  * station's wakeup.
  *
  * For a GO, the Service Period might be cut short due to an absence period
diff --git a/drivers/net/wireless/iwlwifi/mvm/tdls.c b/drivers/net/wireless/iwlwifi/mvm/tdls.c
index c0e00ba..a87b506 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tdls.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tdls.c
@@ -64,6 +64,8 @@
 #include <linux/etherdevice.h>
 #include "mvm.h"
 #include "time-event.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
 
 #define TU_TO_US(x) (x * 1024)
 #define TU_TO_MS(x) (TU_TO_US(x) / 1000)
@@ -228,6 +230,8 @@
 		return "IDLE";
 	case IWL_MVM_TDLS_SW_REQ_SENT:
 		return "REQ SENT";
+	case IWL_MVM_TDLS_SW_RESP_RCVD:
+		return "RESP RECEIVED";
 	case IWL_MVM_TDLS_SW_REQ_RCVD:
 		return "REQ RECEIVED";
 	case IWL_MVM_TDLS_SW_ACTIVE:
@@ -248,6 +252,11 @@
 		       iwl_mvm_tdls_cs_state_str(state));
 	mvm->tdls_cs.state = state;
 
+	/* we only send requests to our switching peer - update sent time */
+	if (state == IWL_MVM_TDLS_SW_REQ_SENT)
+		mvm->tdls_cs.peer.sent_timestamp =
+			iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG);
+
 	if (state == IWL_MVM_TDLS_SW_IDLE)
 		mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT;
 }
@@ -300,7 +309,7 @@
 static int
 iwl_mvm_tdls_check_action(struct iwl_mvm *mvm,
 			  enum iwl_tdls_channel_switch_type type,
-			  const u8 *peer, bool peer_initiator)
+			  const u8 *peer, bool peer_initiator, u32 timestamp)
 {
 	bool same_peer = false;
 	int ret = 0;
@@ -325,17 +334,30 @@
 			ret = -EINVAL;
 		break;
 	case IWL_MVM_TDLS_SW_REQ_SENT:
+		/* only allow requests from the same peer */
+		if (!same_peer)
+			ret = -EBUSY;
+		else if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH &&
+			 !peer_initiator)
+			/*
+			 * We received a ch-switch request while an outgoing
+			 * one is pending. Allow it if the peer is the link
+			 * initiator.
+			 */
+			ret = -EBUSY;
+		else if (type == TDLS_SEND_CHAN_SW_REQ)
+			/* wait for idle before sending another request */
+			ret = -EBUSY;
+		else if (timestamp <= mvm->tdls_cs.peer.sent_timestamp)
+			/* we got a stale response - ignore it */
+			ret = -EINVAL;
+		break;
+	case IWL_MVM_TDLS_SW_RESP_RCVD:
 		/*
-		 * We received a ch-switch request while an outgoing one is
-		 * pending. Allow it to proceed if the other peer is the same
-		 * one we sent to, and we are not the link initiator.
+		 * we are waiting for the FW to give an "active" notification,
+		 * so ignore requests in the meantime
 		 */
-		if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH) {
-			if (!same_peer)
-				ret = -EBUSY;
-			else if (!peer_initiator) /* we are the initiator */
-				ret = -EBUSY;
-		}
+		ret = -EBUSY;
 		break;
 	case IWL_MVM_TDLS_SW_REQ_RCVD:
 		/* as above, allow the link initiator to proceed */
@@ -349,9 +371,12 @@
 		}
 		break;
 	case IWL_MVM_TDLS_SW_ACTIVE:
-		/* we don't allow initiations during active channel switch */
-		if (type == TDLS_SEND_CHAN_SW_REQ)
-			ret = -EINVAL;
+		/*
+		 * the only valid request when active is a request to return
+		 * to the base channel by the current off-channel peer
+		 */
+		if (type != TDLS_MOVE_CH || !same_peer)
+			ret = -EBUSY;
 		break;
 	}
 
@@ -384,7 +409,8 @@
 
 	lockdep_assert_held(&mvm->mutex);
 
-	ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator);
+	ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator,
+					timestamp);
 	if (ret)
 		return ret;
 
@@ -473,6 +499,8 @@
 					     type == TDLS_SEND_CHAN_SW_REQ ?
 					     IWL_MVM_TDLS_SW_REQ_SENT :
 					     IWL_MVM_TDLS_SW_REQ_RCVD);
+	} else {
+		iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_RESP_RCVD);
 	}
 
 out:
@@ -657,12 +685,15 @@
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 	enum iwl_tdls_channel_switch_type type;
 	unsigned int delay;
+	const char *action_str =
+		params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ?
+		"REQ" : "RESP";
 
 	mutex_lock(&mvm->mutex);
 
 	IWL_DEBUG_TDLS(mvm,
-		       "Received TDLS ch switch action %d from %pM status %d\n",
-		       params->action_code, params->sta->addr, params->status);
+		       "Received TDLS ch switch action %s from %pM status %d\n",
+		       action_str, params->sta->addr, params->status);
 
 	/*
 	 * we got a non-zero status from a peer we were switching to - move to
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
index 54fafbf..fd7b0d3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c
@@ -119,7 +119,7 @@
 
 	/*
 	 * Flush the offchannel queue -- this is called when the time
-	 * event finishes or is cancelled, so that frames queued for it
+	 * event finishes or is canceled, so that frames queued for it
 	 * won't get stuck on the queue and be transmitted in the next
 	 * time event.
 	 * We have to send the command asynchronously since this cannot
@@ -187,7 +187,8 @@
 		return false;
 	if (errmsg)
 		IWL_ERR(mvm, "%s\n", errmsg);
-	ieee80211_connection_loss(vif);
+
+	iwl_mvm_connection_loss(mvm, vif, errmsg);
 	return true;
 }
 
@@ -196,17 +197,24 @@
 			     struct iwl_mvm_time_event_data *te_data,
 			     struct iwl_time_event_notif *notif)
 {
-	if (!le32_to_cpu(notif->status)) {
+	struct ieee80211_vif *vif = te_data->vif;
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+	if (!notif->status)
 		IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
-		iwl_mvm_te_clear_data(mvm, te_data);
-		return;
-	}
 
 	switch (te_data->vif->type) {
 	case NL80211_IFTYPE_AP:
+		if (!notif->status)
+			mvmvif->csa_failed = true;
 		iwl_mvm_csa_noa_start(mvm);
 		break;
 	case NL80211_IFTYPE_STATION:
+		if (!notif->status) {
+			iwl_mvm_connection_loss(mvm, vif,
+						"CSA TE failed to start");
+			break;
+		}
 		iwl_mvm_csa_client_absent(mvm, te_data->vif);
 		ieee80211_chswitch_done(te_data->vif, true);
 		break;
@@ -220,6 +228,44 @@
 	iwl_mvm_te_clear_data(mvm, te_data);
 }
 
+static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm,
+				     struct iwl_time_event_notif *notif,
+				     struct iwl_mvm_time_event_data *te_data)
+{
+	struct iwl_fw_dbg_trigger_tlv *trig;
+	struct iwl_fw_dbg_trigger_time_event *te_trig;
+	int i;
+
+	if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT))
+		return;
+
+	trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT);
+	te_trig = (void *)trig->data;
+
+	if (!iwl_fw_dbg_trigger_check_stop(mvm, te_data->vif, trig))
+		return;
+
+	for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) {
+		u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id);
+		u32 trig_action_bitmap =
+			le32_to_cpu(te_trig->time_events[i].action_bitmap);
+		u32 trig_status_bitmap =
+			le32_to_cpu(te_trig->time_events[i].status_bitmap);
+
+		if (trig_te_id != te_data->id ||
+		    !(trig_action_bitmap & le32_to_cpu(notif->action)) ||
+		    !(trig_status_bitmap & BIT(le32_to_cpu(notif->status))))
+			continue;
+
+		iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+					    "Time event %d Action 0x%x received status: %d",
+					    te_data->id,
+					    le32_to_cpu(notif->action),
+					    le32_to_cpu(notif->status));
+		break;
+	}
+}
+
 /*
  * Handles a FW notification for an event that is known to the driver.
  *
@@ -237,6 +283,8 @@
 		     le32_to_cpu(notif->unique_id),
 		     le32_to_cpu(notif->action));
 
+	iwl_mvm_te_check_trigger(mvm, notif, te_data);
+
 	/*
 	 * The FW sends the start/end time event notifications even for events
 	 * that it fails to schedule. This is indicated in the status field of
@@ -246,11 +294,16 @@
 	 * events in the system).
 	 */
 	if (!le32_to_cpu(notif->status)) {
-		bool start = le32_to_cpu(notif->action) &
-				TE_V2_NOTIF_HOST_EVENT_START;
-		IWL_WARN(mvm, "Time Event %s notification failure\n",
-			 start ? "start" : "end");
-		if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, NULL)) {
+		const char *msg;
+
+		if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START))
+			msg = "Time Event start notification failure";
+		else
+			msg = "Time Event end notification failure";
+
+		IWL_DEBUG_TE(mvm, "%s\n", msg);
+
+		if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) {
 			iwl_mvm_te_clear_data(mvm, te_data);
 			return;
 		}
@@ -261,17 +314,23 @@
 			     "TE ended - current time %lu, estimated end %lu\n",
 			     jiffies, te_data->end_jiffies);
 
-		if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+		switch (te_data->vif->type) {
+		case NL80211_IFTYPE_P2P_DEVICE:
 			ieee80211_remain_on_channel_expired(mvm->hw);
 			iwl_mvm_roc_finished(mvm);
+			break;
+		case NL80211_IFTYPE_STATION:
+			/*
+			 * By now, we should have finished association
+			 * and know the dtim period.
+			 */
+			iwl_mvm_te_check_disconnect(mvm, te_data->vif,
+				"No association and the time event is over already...");
+			break;
+		default:
+			break;
 		}
 
-		/*
-		 * By now, we should have finished association
-		 * and know the dtim period.
-		 */
-		iwl_mvm_te_check_disconnect(mvm, te_data->vif,
-			"No association and the time event is over already...");
 		iwl_mvm_te_clear_data(mvm, te_data);
 	} else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) {
 		te_data->running = true;
@@ -307,6 +366,8 @@
 	if (!aux_roc_te) /* Not a Aux ROC time event */
 		return -EINVAL;
 
+	iwl_mvm_te_check_trigger(mvm, notif, te_data);
+
 	if (!le32_to_cpu(notif->status)) {
 		IWL_DEBUG_TE(mvm,
 			     "ERROR: Aux ROC Time Event %s notification failure\n",
@@ -750,8 +811,7 @@
 	 * request
 	 */
 	list_for_each_entry(te_data, &mvm->time_event_list, list) {
-		if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE &&
-		    te_data->running) {
+		if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
 			mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
 			is_p2p = true;
 			goto remove_te;
@@ -762,14 +822,12 @@
 	 * Iterate over the list of aux roc time events and find the time
 	 * event that is associated with a BSS interface.
 	 * This assumes that a BSS interface can have only a single time
-	 * event at any given time and this time event coresponds to a ROC
+	 * event at any given time and this time event corresponds to a ROC
 	 * request
 	 */
 	list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) {
-		if (te_data->running) {
-			mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
-			goto remove_te;
-		}
+		mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+		goto remove_te;
 	}
 
 remove_te:
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h
index 6f6b35d..de4fbc6 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.h
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h
@@ -147,7 +147,7 @@
  * @vif: the virtual interface for which the session is issued
  *
  * This functions cancels the session protection which is an act of good
- * citizenship. If it is not needed any more it should be cancelled because
+ * citizenship. If it is not needed any more it should be canceled because
  * the other bindings wait for the medium during that time.
  * This funtions doesn't sleep.
  */
@@ -162,7 +162,7 @@
 				struct iwl_device_cmd *cmd);
 
 /**
- * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionlity
+ * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionality
  * @mvm: the mvm component
  * @vif: the virtual interface for which the roc is requested. It is assumed
  * that the vif type is NL80211_IFTYPE_P2P_DEVICE
diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c
index 2b1e61f..ba615ad 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tt.c
@@ -69,6 +69,7 @@
 
 static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
 {
+	struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 	u32 duration = mvm->thermal_throttle.params->ct_kill_duration;
 
 	if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
@@ -77,12 +78,15 @@
 	IWL_ERR(mvm, "Enter CT Kill\n");
 	iwl_mvm_set_hw_ctkill_state(mvm, true);
 
+	tt->throttle = false;
+	tt->dynamic_smps = false;
+
 	/* Don't schedule an exit work if we're in test mode, since
 	 * the temperature will not change unless we manually set it
 	 * again (or disable testing).
 	 */
 	if (!mvm->temperature_test)
-		schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
+		schedule_delayed_work(&tt->ct_kill_exit,
 				      round_jiffies_relative(duration * HZ));
 }
 
@@ -452,6 +456,7 @@
 		tt->params = &iwl7000_tt_params;
 
 	tt->throttle = false;
+	tt->dynamic_smps = false;
 	tt->min_backoff = min_backoff;
 	INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
index 178bdce..7c78d06 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -90,8 +90,6 @@
 
 	if (ieee80211_is_probe_resp(fc))
 		tx_flags |= TX_CMD_FLG_TSF;
-	else if (ieee80211_is_back_req(fc))
-		tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR;
 
 	if (ieee80211_has_morefrags(fc))
 		tx_flags |= TX_CMD_FLG_MORE_FRAG;
@@ -100,6 +98,15 @@
 		u8 *qc = ieee80211_get_qos_ctl(hdr);
 		tx_cmd->tid_tspec = qc[0] & 0xf;
 		tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
+	} else if (ieee80211_is_back_req(fc)) {
+		struct ieee80211_bar *bar = (void *)skb->data;
+		u16 control = le16_to_cpu(bar->control);
+
+		tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR;
+		tx_cmd->tid_tspec = (control &
+				     IEEE80211_BAR_CTRL_TID_INFO_MASK) >>
+			IEEE80211_BAR_CTRL_TID_INFO_SHIFT;
+		WARN_ON_ONCE(tx_cmd->tid_tspec >= IWL_MAX_TID_COUNT);
 	} else {
 		tx_cmd->tid_tspec = IWL_TID_NON_QOS;
 		if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
@@ -108,8 +115,12 @@
 			tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
 	}
 
-	/* tid_tspec will default to 0 = BE when QOS isn't enabled */
-	ac = tid_to_mac80211_ac[tx_cmd->tid_tspec];
+	/* Default to 0 (BE) when tid_spec is set to IWL_TID_NON_QOS */
+	if (tx_cmd->tid_tspec < IWL_MAX_TID_COUNT)
+		ac = tid_to_mac80211_ac[tx_cmd->tid_tspec];
+	else
+		ac = tid_to_mac80211_ac[0];
+
 	tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, hdr, info, ac) <<
 			TX_CMD_FLG_BT_PRIO_POS;
 
@@ -209,7 +220,7 @@
 	rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx);
 
 	mvm->mgmt_last_antenna_idx =
-		iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant,
+		iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
 				     mvm->mgmt_last_antenna_idx);
 
 	if (info->band == IEEE80211_BAND_2GHZ &&
@@ -496,7 +507,7 @@
 		IWL_DEBUG_TX_QUEUES(mvm,
 				    "Can continue DELBA flow ssn = next_recl = %d\n",
 				    tid_data->next_reclaimed);
-		iwl_mvm_disable_txq(mvm, tid_data->txq_id);
+		iwl_mvm_disable_txq(mvm, tid_data->txq_id, CMD_ASYNC);
 		tid_data->state = IWL_AGG_OFF;
 		/*
 		 * we can't hold the mutex - but since we are after a sequence
@@ -653,10 +664,13 @@
 		info->status.rates[0].count = tx_resp->failure_frame + 1;
 		iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate),
 					    info);
+		info->status.status_driver_data[1] =
+			(void *)(uintptr_t)le32_to_cpu(tx_resp->initial_rate);
 
 		/* Single frame failure in an AMPDU queue => send BAR */
 		if (txq_id >= mvm->first_agg_queue &&
-		    !(info->flags & IEEE80211_TX_STAT_ACK))
+		    !(info->flags & IEEE80211_TX_STAT_ACK) &&
+		    !(info->flags & IEEE80211_TX_STAT_TX_FILTERED))
 			info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
 
 		/* W/A FW bug: seq_ctl is wrong when the status isn't success */
@@ -897,6 +911,8 @@
 	info->status.tx_time = tid_data->tx_time;
 	info->status.status_driver_data[0] =
 		(void *)(uintptr_t)tid_data->reduced_tpc;
+	info->status.status_driver_data[1] =
+		(void *)(uintptr_t)tid_data->rate_n_flags;
 }
 
 int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
@@ -919,6 +935,11 @@
 	sta_id = ba_notif->sta_id;
 	tid = ba_notif->tid;
 
+	if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT ||
+		      tid >= IWL_MAX_TID_COUNT,
+		      "sta_id %d tid %d", sta_id, tid))
+		return 0;
+
 	rcu_read_lock();
 
 	sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
@@ -932,8 +953,10 @@
 	mvmsta = iwl_mvm_sta_from_mac80211(sta);
 	tid_data = &mvmsta->tid_data[tid];
 
-	if (WARN_ONCE(tid_data->txq_id != scd_flow, "Q %d, tid %d, flow %d",
-		      tid_data->txq_id, tid, scd_flow)) {
+	if (tid_data->txq_id != scd_flow) {
+		IWL_ERR(mvm,
+			"invalid BA notification: Q %d, tid %d, flow %d\n",
+			tid_data->txq_id, tid, scd_flow);
 		rcu_read_unlock();
 		return 0;
 	}
@@ -1026,6 +1049,14 @@
 	return 0;
 }
 
+/*
+ * Note that there are transports that buffer frames before they reach
+ * the firmware. This means that after flush_tx_path is called, the
+ * queue might not be empty. The race-free way to handle this is to:
+ * 1) set the station as draining
+ * 2) flush the Tx path
+ * 3) wait for the transport queues to be empty
+ */
 int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync)
 {
 	int ret;
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
index 8497660..881c8a0 100644
--- a/drivers/net/wireless/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/iwlwifi/mvm/utils.c
@@ -122,7 +122,7 @@
 }
 
 /*
- * We assume that the caller set the status to the sucess value
+ * We assume that the caller set the status to the success value
  */
 int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
 			    u32 *status)
@@ -332,7 +332,7 @@
  * read with u32-sized accesses, any members with a different size
  * need to be ordered correctly though!
  */
-struct iwl_error_event_table {
+struct iwl_error_event_table_v1 {
 	u32 valid;		/* (nonzero) valid, (0) log is empty */
 	u32 error_id;		/* type of error */
 	u32 pc;			/* program counter */
@@ -377,7 +377,55 @@
 	u32 u_timestamp;	/* indicate when the date and time of the
 				 * compilation */
 	u32 flow_handler;	/* FH read/write pointers, RX credit */
-} __packed;
+} __packed /* LOG_ERROR_TABLE_API_S_VER_1 */;
+
+struct iwl_error_event_table {
+	u32 valid;		/* (nonzero) valid, (0) log is empty */
+	u32 error_id;		/* type of error */
+	u32 pc;			/* program counter */
+	u32 blink1;		/* branch link */
+	u32 blink2;		/* branch link */
+	u32 ilink1;		/* interrupt link */
+	u32 ilink2;		/* interrupt link */
+	u32 data1;		/* error-specific data */
+	u32 data2;		/* error-specific data */
+	u32 data3;		/* error-specific data */
+	u32 bcon_time;		/* beacon timer */
+	u32 tsf_low;		/* network timestamp function timer */
+	u32 tsf_hi;		/* network timestamp function timer */
+	u32 gp1;		/* GP1 timer register */
+	u32 gp2;		/* GP2 timer register */
+	u32 gp3;		/* GP3 timer register */
+	u32 major;		/* uCode version major */
+	u32 minor;		/* uCode version minor */
+	u32 hw_ver;		/* HW Silicon version */
+	u32 brd_ver;		/* HW board version */
+	u32 log_pc;		/* log program counter */
+	u32 frame_ptr;		/* frame pointer */
+	u32 stack_ptr;		/* stack pointer */
+	u32 hcmd;		/* last host command header */
+	u32 isr0;		/* isr status register LMPM_NIC_ISR0:
+				 * rxtx_flag */
+	u32 isr1;		/* isr status register LMPM_NIC_ISR1:
+				 * host_flag */
+	u32 isr2;		/* isr status register LMPM_NIC_ISR2:
+				 * enc_flag */
+	u32 isr3;		/* isr status register LMPM_NIC_ISR3:
+				 * time_flag */
+	u32 isr4;		/* isr status register LMPM_NIC_ISR4:
+				 * wico interrupt */
+	u32 isr_pref;		/* isr status register LMPM_NIC_PREF_STAT */
+	u32 wait_event;		/* wait event() caller address */
+	u32 l2p_control;	/* L2pControlField */
+	u32 l2p_duration;	/* L2pDurationField */
+	u32 l2p_mhvalid;	/* L2pMhValidBits */
+	u32 l2p_addr_match;	/* L2pAddrMatchStat */
+	u32 lmpm_pmg_sel;	/* indicate which clocks are turned on
+				 * (LMPM_PMG_SEL) */
+	u32 u_timestamp;	/* indicate when the date and time of the
+				 * compilation */
+	u32 flow_handler;	/* FH read/write pointers, RX credit */
+} __packed /* LOG_ERROR_TABLE_API_S_VER_2 */;
 
 /*
  * UMAC error struct - relevant starting from family 8000 chip.
@@ -396,11 +444,11 @@
 	u32 data1;		/* error-specific data */
 	u32 data2;		/* error-specific data */
 	u32 data3;		/* error-specific data */
-	u32 umac_fw_ver;	/* UMAC version */
-	u32 umac_fw_api_ver;	/* UMAC FW API ver */
+	u32 umac_major;
+	u32 umac_minor;
 	u32 frame_pointer;	/* core register 27*/
 	u32 stack_pointer;	/* core register 28 */
-	u32 cmd_header;	/* latest host cmd sent to UMAC */
+	u32 cmd_header;		/* latest host cmd sent to UMAC */
 	u32 nic_isr_pref;	/* ISR status register */
 } __packed;
 
@@ -432,7 +480,7 @@
 			mvm->status, table.valid);
 	}
 
-	IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
+	IWL_ERR(mvm, "0x%08X | %s\n", table.error_id,
 		desc_lookup(table.error_id));
 	IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1);
 	IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2);
@@ -441,18 +489,18 @@
 	IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1);
 	IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2);
 	IWL_ERR(mvm, "0x%08X | umac data3\n", table.data3);
-	IWL_ERR(mvm, "0x%08X | umac version\n", table.umac_fw_ver);
-	IWL_ERR(mvm, "0x%08X | umac api version\n", table.umac_fw_api_ver);
+	IWL_ERR(mvm, "0x%08X | umac major\n", table.umac_major);
+	IWL_ERR(mvm, "0x%08X | umac minor\n", table.umac_minor);
 	IWL_ERR(mvm, "0x%08X | frame pointer\n", table.frame_pointer);
 	IWL_ERR(mvm, "0x%08X | stack pointer\n", table.stack_pointer);
 	IWL_ERR(mvm, "0x%08X | last host cmd\n", table.cmd_header);
 	IWL_ERR(mvm, "0x%08X | isr status reg\n", table.nic_isr_pref);
 }
 
-void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
+static void iwl_mvm_dump_nic_error_log_old(struct iwl_mvm *mvm)
 {
 	struct iwl_trans *trans = mvm->trans;
-	struct iwl_error_event_table table;
+	struct iwl_error_event_table_v1 table;
 	u32 base;
 
 	base = mvm->error_event_table;
@@ -489,7 +537,7 @@
 				      table.data1, table.data2, table.data3,
 				      table.blink1, table.blink2, table.ilink1,
 				      table.ilink2, table.bcon_time, table.gp1,
-				      table.gp2, table.gp3, table.ucode_ver,
+				      table.gp2, table.gp3, table.ucode_ver, 0,
 				      table.hw_ver, table.brd_ver);
 	IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
 		desc_lookup(table.error_id));
@@ -530,50 +578,137 @@
 		iwl_mvm_dump_umac_error_log(mvm);
 }
 
-void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn,
-			const struct iwl_trans_txq_scd_cfg *cfg)
+void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
 {
-	if (iwl_mvm_is_dqa_supported(mvm)) {
-		struct iwl_scd_txq_cfg_cmd cmd = {
-			.scd_queue = queue,
-			.enable = 1,
-			.window = cfg->frame_limit,
-			.sta_id = cfg->sta_id,
-			.ssn = cpu_to_le16(ssn),
-			.tx_fifo = cfg->fifo,
-			.aggregate = cfg->aggregate,
-			.flags = IWL_SCD_FLAGS_DQA_ENABLED,
-			.tid = cfg->tid,
-			.control = IWL_SCD_CONTROL_SET_SSN,
-		};
-		int ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0,
-					       sizeof(cmd), &cmd);
-		if (ret)
-			IWL_ERR(mvm,
-				"Failed to configure queue %d on FIFO %d\n",
-				queue, cfg->fifo);
+	struct iwl_trans *trans = mvm->trans;
+	struct iwl_error_event_table table;
+	u32 base;
+
+	if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_NEW_VERSION)) {
+		iwl_mvm_dump_nic_error_log_old(mvm);
+		return;
 	}
 
-	iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn,
-				 iwl_mvm_is_dqa_supported(mvm) ? NULL : cfg);
+	base = mvm->error_event_table;
+	if (mvm->cur_ucode == IWL_UCODE_INIT) {
+		if (!base)
+			base = mvm->fw->init_errlog_ptr;
+	} else {
+		if (!base)
+			base = mvm->fw->inst_errlog_ptr;
+	}
+
+	if (base < 0x800000) {
+		IWL_ERR(mvm,
+			"Not valid error log pointer 0x%08X for %s uCode\n",
+			base,
+			(mvm->cur_ucode == IWL_UCODE_INIT)
+					? "Init" : "RT");
+		return;
+	}
+
+	iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
+
+	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
+		IWL_ERR(trans, "Start IWL Error Log Dump:\n");
+		IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
+			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,
+				      table.data1, table.data2, table.data3,
+				      table.blink1, table.blink2, table.ilink1,
+				      table.ilink2, table.bcon_time, table.gp1,
+				      table.gp2, table.gp3, table.major,
+				      table.minor, table.hw_ver, table.brd_ver);
+	IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
+		desc_lookup(table.error_id));
+	IWL_ERR(mvm, "0x%08X | uPc\n", table.pc);
+	IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1);
+	IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2);
+	IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1);
+	IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2);
+	IWL_ERR(mvm, "0x%08X | data1\n", table.data1);
+	IWL_ERR(mvm, "0x%08X | data2\n", table.data2);
+	IWL_ERR(mvm, "0x%08X | data3\n", table.data3);
+	IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time);
+	IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low);
+	IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi);
+	IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1);
+	IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2);
+	IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3);
+	IWL_ERR(mvm, "0x%08X | uCode version major\n", table.major);
+	IWL_ERR(mvm, "0x%08X | uCode version minor\n", table.minor);
+	IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver);
+	IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver);
+	IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd);
+	IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0);
+	IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1);
+	IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2);
+	IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3);
+	IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4);
+	IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref);
+	IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event);
+	IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control);
+	IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration);
+	IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
+	IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
+	IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
+	IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
+	IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
+
+	if (mvm->support_umac_log)
+		iwl_mvm_dump_umac_error_log(mvm);
+}
+void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn,
+			const struct iwl_trans_txq_scd_cfg *cfg,
+			unsigned int wdg_timeout)
+{
+	struct iwl_scd_txq_cfg_cmd cmd = {
+		.scd_queue = queue,
+		.enable = 1,
+		.window = cfg->frame_limit,
+		.sta_id = cfg->sta_id,
+		.ssn = cpu_to_le16(ssn),
+		.tx_fifo = cfg->fifo,
+		.aggregate = cfg->aggregate,
+		.tid = cfg->tid,
+	};
+
+	if (!iwl_mvm_is_scd_cfg_supported(mvm)) {
+		iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, cfg,
+					 wdg_timeout);
+		return;
+	}
+
+	iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, wdg_timeout);
+	WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd),
+	     "Failed to configure queue %d on FIFO %d\n", queue, cfg->fifo);
 }
 
-void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue)
+void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, u8 flags)
 {
-	iwl_trans_txq_disable(mvm->trans, queue,
-			      !iwl_mvm_is_dqa_supported(mvm));
+	struct iwl_scd_txq_cfg_cmd cmd = {
+		.scd_queue = queue,
+		.enable = 0,
+	};
+	int ret;
 
-	if (iwl_mvm_is_dqa_supported(mvm)) {
-		struct iwl_scd_txq_cfg_cmd cmd = {
-			.scd_queue = queue,
-			.enable = 0,
-		};
-		int ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, CMD_ASYNC,
-					       sizeof(cmd), &cmd);
-		if (ret)
-			IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n",
-				queue, ret);
+	if (!iwl_mvm_is_scd_cfg_supported(mvm)) {
+		iwl_trans_txq_disable(mvm->trans, queue, true);
+		return;
 	}
+
+	iwl_trans_txq_disable(mvm->trans, queue, false);
+	ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags,
+				   sizeof(cmd), &cmd);
+	if (ret)
+		IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n",
+			queue, ret);
 }
 
 /**
@@ -602,7 +737,7 @@
 }
 
 /**
- * iwl_mvm_update_smps - Get a requst to change the SMPS mode
+ * iwl_mvm_update_smps - Get a request to change the SMPS mode
  * @req_type: The part of the driver who call for a change.
  * @smps_requests: The request to change the SMPS mode.
  *
@@ -620,7 +755,7 @@
 	lockdep_assert_held(&mvm->mutex);
 
 	/* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */
-	if (num_of_ant(mvm->fw->valid_rx_ant) == 1)
+	if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1)
 		return;
 
 	if (vif->type == NL80211_IFTYPE_AP)
@@ -642,6 +777,40 @@
 	ieee80211_request_smps(vif, smps_mode);
 }
 
+int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear)
+{
+	struct iwl_statistics_cmd scmd = {
+		.flags = clear ? cpu_to_le32(IWL_STATISTICS_FLG_CLEAR) : 0,
+	};
+	struct iwl_host_cmd cmd = {
+		.id = STATISTICS_CMD,
+		.len[0] = sizeof(scmd),
+		.data[0] = &scmd,
+		.flags = CMD_WANT_SKB,
+	};
+	int ret;
+
+	ret = iwl_mvm_send_cmd(mvm, &cmd);
+	if (ret)
+		return ret;
+
+	iwl_mvm_handle_rx_statistics(mvm, cmd.resp_pkt);
+	iwl_free_resp(&cmd);
+
+	if (clear)
+		iwl_mvm_accu_radio_stats(mvm);
+
+	return 0;
+}
+
+void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm)
+{
+	mvm->accu_radio_stats.rx_time += mvm->radio_stats.rx_time;
+	mvm->accu_radio_stats.tx_time += mvm->radio_stats.tx_time;
+	mvm->accu_radio_stats.on_time_rf += mvm->radio_stats.on_time_rf;
+	mvm->accu_radio_stats.on_time_scan += mvm->radio_stats.on_time_scan;
+}
+
 static void iwl_mvm_diversity_iter(void *_data, u8 *mac,
 				   struct ieee80211_vif *vif)
 {
@@ -662,10 +831,10 @@
 
 	lockdep_assert_held(&mvm->mutex);
 
-	if (num_of_ant(mvm->fw->valid_rx_ant) == 1)
+	if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1)
 		return false;
 
-	if (!mvm->cfg->rx_with_siso_diversity)
+	if (mvm->cfg->rx_with_siso_diversity)
 		return false;
 
 	ieee80211_iterate_active_interfaces_atomic(
@@ -688,7 +857,7 @@
 
 	mvmvif->low_latency = value;
 
-	res = iwl_mvm_update_quotas(mvm, NULL);
+	res = iwl_mvm_update_quotas(mvm, false, NULL);
 	if (res)
 		return res;
 
@@ -716,25 +885,6 @@
 	return result;
 }
 
-static void iwl_mvm_idle_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)
-{
-	bool *idle = _data;
-
-	if (!vif->bss_conf.idle)
-		*idle = false;
-}
-
-bool iwl_mvm_is_idle(struct iwl_mvm *mvm)
-{
-	bool idle = true;
-
-	ieee80211_iterate_active_interfaces_atomic(
-			mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
-			iwl_mvm_idle_iter, &idle);
-
-	return idle;
-}
-
 struct iwl_bss_iter_data {
 	struct ieee80211_vif *vif;
 	bool error;
@@ -771,3 +921,71 @@
 
 	return bss_iter_data.vif;
 }
+
+unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
+				    struct ieee80211_vif *vif,
+				    bool tdls, bool cmd_q)
+{
+	struct iwl_fw_dbg_trigger_tlv *trigger;
+	struct iwl_fw_dbg_trigger_txq_timer *txq_timer;
+	unsigned int default_timeout =
+		cmd_q ? IWL_DEF_WD_TIMEOUT : mvm->cfg->base_params->wd_timeout;
+
+	if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS))
+		return iwlmvm_mod_params.tfd_q_hang_detect ?
+			default_timeout : IWL_WATCHDOG_DISABLED;
+
+	trigger = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS);
+	txq_timer = (void *)trigger->data;
+
+	if (tdls)
+		return le32_to_cpu(txq_timer->tdls);
+
+	if (cmd_q)
+		return le32_to_cpu(txq_timer->command_queue);
+
+	if (WARN_ON(!vif))
+		return default_timeout;
+
+	switch (ieee80211_vif_type_p2p(vif)) {
+	case NL80211_IFTYPE_ADHOC:
+		return le32_to_cpu(txq_timer->ibss);
+	case NL80211_IFTYPE_STATION:
+		return le32_to_cpu(txq_timer->bss);
+	case NL80211_IFTYPE_AP:
+		return le32_to_cpu(txq_timer->softap);
+	case NL80211_IFTYPE_P2P_CLIENT:
+		return le32_to_cpu(txq_timer->p2p_client);
+	case NL80211_IFTYPE_P2P_GO:
+		return le32_to_cpu(txq_timer->p2p_go);
+	case NL80211_IFTYPE_P2P_DEVICE:
+		return le32_to_cpu(txq_timer->p2p_device);
+	default:
+		WARN_ON(1);
+		return mvm->cfg->base_params->wd_timeout;
+	}
+}
+
+void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+			     const char *errmsg)
+{
+	struct iwl_fw_dbg_trigger_tlv *trig;
+	struct iwl_fw_dbg_trigger_mlme *trig_mlme;
+
+	if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
+		goto out;
+
+	trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
+	trig_mlme = (void *)trig->data;
+	if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+		goto out;
+
+	if (trig_mlme->stop_connection_loss &&
+	    --trig_mlme->stop_connection_loss)
+		goto out;
+
+	iwl_mvm_fw_dbg_collect_trig(mvm, trig, "%s", errmsg);
+
+out:
+	ieee80211_connection_loss(vif);
+}
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
index 14d92d1..a3709e1 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c
@@ -367,7 +367,13 @@
 
 /* 3165 Series */
 	{IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)},
-	{IWL_PCI_DEVICE(0x3165, 0x4210, iwl3165_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x3166, 0x4310, iwl3165_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x3166, 0x4210, iwl3165_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)},
 
 /* 7265 Series */
 	{IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
@@ -409,8 +415,35 @@
 
 /* 8000 Series */
 	{IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
-	{IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8260_2ac_cfg)},
 	{IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F4, 0x1130, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F4, 0xC030, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F4, 0xD030, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)},
+	{IWL_PCI_DEVICE(0x24F5, 0x0010, iwl4165_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F6, 0x0030, iwl4165_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)},
 #endif /* CPTCFG_IWLMVM */
 
 	{0}
@@ -523,8 +556,10 @@
 	else if (cfg == &iwl7265_n_cfg)
 		cfg_7265d = &iwl7265d_n_cfg;
 	if (cfg_7265d &&
-	    (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D)
+	    (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D) {
 		cfg = cfg_7265d;
+		iwl_trans->cfg = cfg_7265d;
+	}
 #endif
 
 	pci_set_drvdata(pdev, iwl_trans);
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index e6129af..ed1f399 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -216,6 +216,9 @@
  * @need_update: indicates need to update read/write index
  * @active: stores if queue is active
  * @ampdu: true if this queue is an ampdu queue for an specific RA/TID
+ * @wd_timeout: queue watchdog timeout (jiffies) - per queue
+ * @frozen: tx stuck queue timer is frozen
+ * @frozen_expiry_remainder: remember how long until the timer fires
  *
  * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
  * descriptors) and required locking structures.
@@ -227,11 +230,14 @@
 	dma_addr_t scratchbufs_dma;
 	struct iwl_pcie_txq_entry *entries;
 	spinlock_t lock;
+	unsigned long frozen_expiry_remainder;
 	struct timer_list stuck_timer;
 	struct iwl_trans_pcie *trans_pcie;
 	bool need_update;
+	bool frozen;
 	u8 active;
 	bool ampdu;
+	unsigned long wd_timeout;
 };
 
 static inline dma_addr_t
@@ -259,7 +265,6 @@
  * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes)
  * @scd_set_active: should the transport configure the SCD for HCMD queue
  * @rx_page_order: page order for receive buffer size
- * @wd_timeout: queue watchdog timeout (jiffies)
  * @reg_lock: protect hw register access
  * @cmd_in_flight: true when we have a host command in flight
  * @fw_mon_phys: physical address of the buffer for the firmware monitor
@@ -302,6 +307,7 @@
 
 	u8 cmd_queue;
 	u8 cmd_fifo;
+	unsigned int cmd_q_wdg_timeout;
 	u8 n_no_reclaim_cmds;
 	u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS];
 
@@ -312,12 +318,14 @@
 
 	const char *const *command_names;
 
-	/* queue watchdog */
-	unsigned long wd_timeout;
-
 	/*protect hw register */
 	spinlock_t reg_lock;
 	bool cmd_in_flight;
+	bool ref_cmd_in_flight;
+
+	/* protect ref counter */
+	spinlock_t ref_lock;
+	u32 ref_count;
 
 	dma_addr_t fw_mon_phys;
 	struct page *fw_mon_page;
@@ -368,7 +376,8 @@
 int iwl_pcie_tx_stop(struct iwl_trans *trans);
 void iwl_pcie_tx_free(struct iwl_trans *trans);
 void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int queue, u16 ssn,
-			       const struct iwl_trans_txq_scd_cfg *cfg);
+			       const struct iwl_trans_txq_scd_cfg *cfg,
+			       unsigned int wdg_timeout);
 void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue,
 				bool configure_scd);
 int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
@@ -381,6 +390,9 @@
 			    struct sk_buff_head *skbs);
 void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
 
+void iwl_trans_pcie_ref(struct iwl_trans *trans);
+void iwl_trans_pcie_unref(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];
diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c
index 7b7e2f2..7ff69c6 100644
--- a/drivers/net/wireless/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/rx.c
@@ -600,9 +600,11 @@
 		if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID))
 			break;
 
-		IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x)\n",
-			rxcb._offset, get_cmd_string(trans_pcie, pkt->hdr.cmd),
-			pkt->hdr.cmd);
+		IWL_DEBUG_RX(trans,
+			     "cmd at offset %d: %s (0x%.2x, seq 0x%x)\n",
+			     rxcb._offset,
+			     get_cmd_string(trans_pcie, pkt->hdr.cmd),
+			     pkt->hdr.cmd, le16_to_cpu(pkt->hdr.sequence));
 
 		len = iwl_rx_packet_len(pkt);
 		len += sizeof(u32); /* account for status word */
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 93a9e59..1cf2741 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -75,6 +75,7 @@
 #include "iwl-trans.h"
 #include "iwl-csr.h"
 #include "iwl-prph.h"
+#include "iwl-scd.h"
 #include "iwl-agn-hw.h"
 #include "iwl-fw-error-dump.h"
 #include "internal.h"
@@ -443,10 +444,25 @@
 	return ret;
 }
 
-static void iwl_pcie_apm_stop(struct iwl_trans *trans)
+static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
 {
 	IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n");
 
+	if (op_mode_leave) {
+		if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
+			iwl_pcie_apm_init(trans);
+
+		/* inform ME that we are leaving */
+		if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000)
+			iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
+					  APMG_PCIDEV_STT_VAL_WAKE_ME);
+		else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+			iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+				    CSR_HW_IF_CONFIG_REG_PREPARE |
+				    CSR_HW_IF_CONFIG_REG_ENABLE_PME);
+		mdelay(5);
+	}
+
 	clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
 
 	/* Stop device's DMA activity */
@@ -614,7 +630,7 @@
 {
 	u8 *v_addr;
 	dma_addr_t p_addr;
-	u32 offset, chunk_sz = section->len;
+	u32 offset, chunk_sz = min_t(u32, FH_MEM_TB_MAX_LENGTH, section->len);
 	int ret = 0;
 
 	IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
@@ -666,10 +682,51 @@
 	return ret;
 }
 
-static int iwl_pcie_load_cpu_sections_8000b(struct iwl_trans *trans,
-					    const struct fw_img *image,
-					    int cpu,
-					    int *first_ucode_section)
+/*
+ * Driver Takes the ownership on secure machine before FW load
+ * and prevent race with the BT load.
+ * W/A for ROM bug. (should be remove in the next Si step)
+ */
+static int iwl_pcie_rsa_race_bug_wa(struct iwl_trans *trans)
+{
+	u32 val, loop = 1000;
+
+	/*
+	 * Check the RSA semaphore is accessible.
+	 * If the HW isn't locked and the rsa semaphore isn't accessible,
+	 * we are in trouble.
+	 */
+	val = iwl_read_prph(trans, PREG_AUX_BUS_WPROT_0);
+	if (val & (BIT(1) | BIT(17))) {
+		IWL_INFO(trans,
+			 "can't access the RSA semaphore it is write protected\n");
+		return 0;
+	}
+
+	/* take ownership on the AUX IF */
+	iwl_write_prph(trans, WFPM_CTRL_REG, WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK);
+	iwl_write_prph(trans, AUX_MISC_MASTER1_EN, AUX_MISC_MASTER1_EN_SBE_MSK);
+
+	do {
+		iwl_write_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS, 0x1);
+		val = iwl_read_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS);
+		if (val == 0x1) {
+			iwl_write_prph(trans, RSA_ENABLE, 0);
+			return 0;
+		}
+
+		udelay(10);
+		loop--;
+	} while (loop > 0);
+
+	IWL_ERR(trans, "Failed to take ownership on secure machine\n");
+	return -EIO;
+}
+
+static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans,
+					   const struct fw_img *image,
+					   int cpu,
+					   int *first_ucode_section)
 {
 	int shift_param;
 	int i, ret = 0, sec_num = 0x1;
@@ -707,6 +764,11 @@
 
 	*first_ucode_section = last_read_idx;
 
+	if (cpu == 1)
+		iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFF);
+	else
+		iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
+
 	return 0;
 }
 
@@ -859,57 +921,44 @@
 	}
 
 	/* release CPU reset */
-	if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
-		iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
-	else
-		iwl_write32(trans, CSR_RESET, 0);
+	iwl_write32(trans, CSR_RESET, 0);
 
 	return 0;
 }
 
-static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
-					   const struct fw_img *image)
+static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans,
+					  const struct fw_img *image)
 {
 	int ret = 0;
 	int first_ucode_section;
-	u32 reg;
 
 	IWL_DEBUG_FW(trans, "working with %s CPU\n",
 		     image->is_dual_cpus ? "Dual" : "Single");
 
+	if (trans->dbg_dest_tlv)
+		iwl_pcie_apply_destination(trans);
+
+	/* TODO: remove in the next Si step */
+	ret = iwl_pcie_rsa_race_bug_wa(trans);
+	if (ret)
+		return ret;
+
 	/* configure the ucode to be ready to get the secured image */
 	/* release CPU reset */
 	iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
 
 	/* load to FW the binary Secured sections of CPU1 */
-	ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 1,
-					       &first_ucode_section);
+	ret = iwl_pcie_load_cpu_sections_8000(trans, image, 1,
+					      &first_ucode_section);
 	if (ret)
 		return ret;
 
 	/* load to FW the binary sections of CPU2 */
-	ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 2,
-					       &first_ucode_section);
+	ret = iwl_pcie_load_cpu_sections_8000(trans, image, 2,
+					      &first_ucode_section);
 	if (ret)
 		return ret;
 
-	/* Notify FW loading is done */
-	iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
-
-	/* wait for image verification to complete  */
-	ret = iwl_poll_prph_bit(trans, LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0,
-				LMPM_SECURE_BOOT_STATUS_SUCCESS,
-				LMPM_SECURE_BOOT_STATUS_SUCCESS,
-				LMPM_SECURE_TIME_OUT);
-	if (ret < 0) {
-		reg = iwl_read_prph(trans,
-				    LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0);
-
-		IWL_ERR(trans, "Timeout on secure boot process, reg = %x\n",
-			reg);
-		return ret;
-	}
-
 	return 0;
 }
 
@@ -959,9 +1008,8 @@
 	iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
 
 	/* Load the given image to the HW */
-	if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) &&
-	    (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_B_STEP))
-		return iwl_pcie_load_given_ucode_8000b(trans, fw);
+	if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+		return iwl_pcie_load_given_ucode_8000(trans, fw);
 	else
 		return iwl_pcie_load_given_ucode(trans, fw);
 }
@@ -1010,18 +1058,23 @@
 		      CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
 
 	/* Stop the device, and put it in low power state */
-	iwl_pcie_apm_stop(trans);
+	iwl_pcie_apm_stop(trans, false);
 
-	/* Upon stop, the APM issues an interrupt if HW RF kill is set.
-	 * Clean again the interrupt here
+	/* stop and reset the on-board processor */
+	iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+	udelay(20);
+
+	/*
+	 * Upon stop, the APM issues an interrupt if HW RF kill is set.
+	 * This is a bug in certain verions of the hardware.
+	 * Certain devices also keep sending HW RF kill interrupt all
+	 * the time, unless the interrupt is ACKed even if the interrupt
+	 * should be masked. Re-ACK all the interrupts here.
 	 */
 	spin_lock(&trans_pcie->irq_lock);
 	iwl_disable_interrupts(trans);
 	spin_unlock(&trans_pcie->irq_lock);
 
-	/* stop and reset the on-board processor */
-	iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-	udelay(20);
 
 	/* clear all status bits */
 	clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
@@ -1187,7 +1240,7 @@
 	iwl_disable_interrupts(trans);
 	spin_unlock(&trans_pcie->irq_lock);
 
-	iwl_pcie_apm_stop(trans);
+	iwl_pcie_apm_stop(trans, true);
 
 	spin_lock(&trans_pcie->irq_lock);
 	iwl_disable_interrupts(trans);
@@ -1239,6 +1292,7 @@
 
 	trans_pcie->cmd_queue = trans_cfg->cmd_queue;
 	trans_pcie->cmd_fifo = trans_cfg->cmd_fifo;
+	trans_pcie->cmd_q_wdg_timeout = trans_cfg->cmd_q_wdg_timeout;
 	if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS))
 		trans_pcie->n_no_reclaim_cmds = 0;
 	else
@@ -1253,13 +1307,13 @@
 	else
 		trans_pcie->rx_page_order = get_order(4 * 1024);
 
-	trans_pcie->wd_timeout =
-		msecs_to_jiffies(trans_cfg->queue_watchdog_timeout);
-
 	trans_pcie->command_names = trans_cfg->command_names;
 	trans_pcie->bc_table_dword = trans_cfg->bc_table_dword;
 	trans_pcie->scd_set_active = trans_cfg->scd_set_active;
 
+	/* init ref_count to 1 (should be cleared when ucode is loaded) */
+	trans_pcie->ref_count = 1;
+
 	/* 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
@@ -1434,6 +1488,60 @@
 	return ret;
 }
 
+static void iwl_trans_pcie_freeze_txq_timer(struct iwl_trans *trans,
+					    unsigned long txqs,
+					    bool freeze)
+{
+	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+	int queue;
+
+	for_each_set_bit(queue, &txqs, BITS_PER_LONG) {
+		struct iwl_txq *txq = &trans_pcie->txq[queue];
+		unsigned long now;
+
+		spin_lock_bh(&txq->lock);
+
+		now = jiffies;
+
+		if (txq->frozen == freeze)
+			goto next_queue;
+
+		IWL_DEBUG_TX_QUEUES(trans, "%s TXQ %d\n",
+				    freeze ? "Freezing" : "Waking", queue);
+
+		txq->frozen = freeze;
+
+		if (txq->q.read_ptr == txq->q.write_ptr)
+			goto next_queue;
+
+		if (freeze) {
+			if (unlikely(time_after(now,
+						txq->stuck_timer.expires))) {
+				/*
+				 * The timer should have fired, maybe it is
+				 * spinning right now on the lock.
+				 */
+				goto next_queue;
+			}
+			/* remember how long until the timer fires */
+			txq->frozen_expiry_remainder =
+				txq->stuck_timer.expires - now;
+			del_timer(&txq->stuck_timer);
+			goto next_queue;
+		}
+
+		/*
+		 * Wake a non-empty queue -> arm timer with the
+		 * remainder before it froze
+		 */
+		mod_timer(&txq->stuck_timer,
+			  now + txq->frozen_expiry_remainder);
+
+next_queue:
+		spin_unlock_bh(&txq->lock);
+	}
+}
+
 #define IWL_FLUSH_WAIT_MS	2000
 
 static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
@@ -1535,6 +1643,38 @@
 	spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
 }
 
+void iwl_trans_pcie_ref(struct iwl_trans *trans)
+{
+	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+	unsigned long flags;
+
+	if (iwlwifi_mod_params.d0i3_disable)
+		return;
+
+	spin_lock_irqsave(&trans_pcie->ref_lock, flags);
+	IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
+	trans_pcie->ref_count++;
+	spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+}
+
+void iwl_trans_pcie_unref(struct iwl_trans *trans)
+{
+	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+	unsigned long flags;
+
+	if (iwlwifi_mod_params.d0i3_disable)
+		return;
+
+	spin_lock_irqsave(&trans_pcie->ref_lock, flags);
+	IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
+	if (WARN_ON_ONCE(trans_pcie->ref_count == 0)) {
+		spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+		return;
+	}
+	trans_pcie->ref_count--;
+	spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+}
+
 static const char *get_csr_string(int cmd)
 {
 #define IWL_CMD(x) case x: return #x
@@ -1653,7 +1793,7 @@
 	int ret;
 	size_t bufsz;
 
-	bufsz = sizeof(char) * 64 * trans->cfg->base_params->num_of_queues;
+	bufsz = sizeof(char) * 75 * trans->cfg->base_params->num_of_queues;
 
 	if (!trans_pcie->txq)
 		return -EAGAIN;
@@ -1666,11 +1806,11 @@
 		txq = &trans_pcie->txq[cnt];
 		q = &txq->q;
 		pos += scnprintf(buf + pos, bufsz - pos,
-				"hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d%s\n",
+				"hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d frozen=%d%s\n",
 				cnt, q->read_ptr, q->write_ptr,
 				!!test_bit(cnt, trans_pcie->queue_used),
 				 !!test_bit(cnt, trans_pcie->queue_stopped),
-				 txq->need_update,
+				 txq->need_update, txq->frozen,
 				 (cnt == trans_pcie->cmd_queue ? " HCMD" : ""));
 	}
 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
@@ -1901,24 +2041,25 @@
 	{ .start = 0x00a01c7c, .end = 0x00a01c7c },
 	{ .start = 0x00a01c28, .end = 0x00a01c54 },
 	{ .start = 0x00a01c5c, .end = 0x00a01c5c },
-	{ .start = 0x00a01c84, .end = 0x00a01c84 },
+	{ .start = 0x00a01c60, .end = 0x00a01cdc },
 	{ .start = 0x00a01ce0, .end = 0x00a01d0c },
 	{ .start = 0x00a01d18, .end = 0x00a01d20 },
 	{ .start = 0x00a01d2c, .end = 0x00a01d30 },
 	{ .start = 0x00a01d40, .end = 0x00a01d5c },
 	{ .start = 0x00a01d80, .end = 0x00a01d80 },
-	{ .start = 0x00a01d98, .end = 0x00a01d98 },
+	{ .start = 0x00a01d98, .end = 0x00a01d9c },
+	{ .start = 0x00a01da8, .end = 0x00a01da8 },
+	{ .start = 0x00a01db8, .end = 0x00a01df4 },
 	{ .start = 0x00a01dc0, .end = 0x00a01dfc },
 	{ .start = 0x00a01e00, .end = 0x00a01e2c },
 	{ .start = 0x00a01e40, .end = 0x00a01e60 },
+	{ .start = 0x00a01e68, .end = 0x00a01e6c },
+	{ .start = 0x00a01e74, .end = 0x00a01e74 },
 	{ .start = 0x00a01e84, .end = 0x00a01e90 },
 	{ .start = 0x00a01e9c, .end = 0x00a01ec4 },
-	{ .start = 0x00a01ed0, .end = 0x00a01ed0 },
-	{ .start = 0x00a01f00, .end = 0x00a01f14 },
-	{ .start = 0x00a01f44, .end = 0x00a01f58 },
-	{ .start = 0x00a01f80, .end = 0x00a01fa8 },
-	{ .start = 0x00a01fb0, .end = 0x00a01fbc },
-	{ .start = 0x00a01ff8, .end = 0x00a01ffc },
+	{ .start = 0x00a01ed0, .end = 0x00a01ee0 },
+	{ .start = 0x00a01f00, .end = 0x00a01f1c },
+	{ .start = 0x00a01f44, .end = 0x00a01ffc },
 	{ .start = 0x00a02000, .end = 0x00a02048 },
 	{ .start = 0x00a02068, .end = 0x00a020f0 },
 	{ .start = 0x00a02100, .end = 0x00a02118 },
@@ -2245,6 +2386,7 @@
 	.dbgfs_register = iwl_trans_pcie_dbgfs_register,
 
 	.wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty,
+	.freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
 
 	.write8 = iwl_trans_pcie_write8,
 	.write32 = iwl_trans_pcie_write32,
@@ -2259,6 +2401,9 @@
 	.release_nic_access = iwl_trans_pcie_release_nic_access,
 	.set_bits_mask = iwl_trans_pcie_set_bits_mask,
 
+	.ref = iwl_trans_pcie_ref,
+	.unref = iwl_trans_pcie_unref,
+
 	.dump_data = iwl_trans_pcie_dump_data,
 };
 
@@ -2286,6 +2431,7 @@
 	trans_pcie->trans = trans;
 	spin_lock_init(&trans_pcie->irq_lock);
 	spin_lock_init(&trans_pcie->reg_lock);
+	spin_lock_init(&trans_pcie->ref_lock);
 	init_waitqueue_head(&trans_pcie->ucode_write_waitq);
 
 	err = pci_enable_device(pdev);
@@ -2359,10 +2505,45 @@
 	 * "dash" value). To keep hw_rev backwards compatible - we'll store it
 	 * in the old format.
 	 */
-	if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+	if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+		unsigned long flags;
+		int ret;
+
 		trans->hw_rev = (trans->hw_rev & 0xfff0) |
 				(CSR_HW_REV_STEP(trans->hw_rev << 2) << 2);
 
+		/*
+		 * in-order to recognize C step driver should read chip version
+		 * id located at the AUX bus MISC address space.
+		 */
+		iwl_set_bit(trans, CSR_GP_CNTRL,
+			    CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+		udelay(2);
+
+		ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+				   CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+				   CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+				   25000);
+		if (ret < 0) {
+			IWL_DEBUG_INFO(trans, "Failed to wake up the nic\n");
+			goto out_pci_disable_msi;
+		}
+
+		if (iwl_trans_grab_nic_access(trans, false, &flags)) {
+			u32 hw_step;
+
+			hw_step = __iwl_read_prph(trans, WFPM_CTRL_REG);
+			hw_step |= ENABLE_WFPM;
+			__iwl_write_prph(trans, WFPM_CTRL_REG, hw_step);
+			hw_step = __iwl_read_prph(trans, AUX_MISC_REG);
+			hw_step = (hw_step >> HW_STEP_LOCATION_BITS) & 0xF;
+			if (hw_step == 0x3)
+				trans->hw_rev = (trans->hw_rev & 0xFFFFFFF3) |
+						(SILICON_C_STEP << 2);
+			iwl_trans_release_nic_access(trans, &flags);
+		}
+	}
+
 	trans->hw_id = (pdev->device << 16) + pdev->subsystem_device;
 	snprintf(trans->hw_id_str, sizeof(trans->hw_id_str),
 		 "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device);
@@ -2399,6 +2580,7 @@
 	}
 
 	trans_pcie->inta_mask = CSR_INI_SET_MASK;
+	trans->d0i3_mode = IWL_D0I3_MODE_ON_SUSPEND;
 
 	return trans;
 
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index 8a6c7a0..06952aa 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -147,7 +147,6 @@
 static void iwl_pcie_txq_stuck_timer(unsigned long data)
 {
 	struct iwl_txq *txq = (void *)data;
-	struct iwl_queue *q = &txq->q;
 	struct iwl_trans_pcie *trans_pcie = txq->trans_pcie;
 	struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie);
 	u32 scd_sram_addr = trans_pcie->scd_base_addr +
@@ -164,7 +163,7 @@
 	spin_unlock(&txq->lock);
 
 	IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id,
-		jiffies_to_msecs(trans_pcie->wd_timeout));
+		jiffies_to_msecs(txq->wd_timeout));
 	IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n",
 		txq->q.read_ptr, txq->q.write_ptr);
 
@@ -198,11 +197,6 @@
 			iwl_read_prph(trans, SCD_QUEUE_WRPTR(i)));
 	}
 
-	for (i = q->read_ptr; i != q->write_ptr;
-	     i = iwl_queue_inc_wrap(i))
-		IWL_ERR(trans, "scratch %d = 0x%08x\n", i,
-			le32_to_cpu(txq->scratchbufs[i].scratch));
-
 	iwl_force_nmi(trans);
 }
 
@@ -680,7 +674,8 @@
 		iwl_write_prph(trans, SCD_CHAINEXT_EN, 0);
 
 	iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue,
-				trans_pcie->cmd_fifo);
+				trans_pcie->cmd_fifo,
+				trans_pcie->cmd_q_wdg_timeout);
 
 	/* Activate all Tx DMA/FIFO channels */
 	iwl_scd_activate_fifos(trans);
@@ -722,7 +717,43 @@
 	iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG,
 			   trans_pcie->kw.dma >> 4);
 
-	iwl_pcie_tx_start(trans, trans_pcie->scd_base_addr);
+	/*
+	 * Send 0 as the scd_base_addr since the device may have be reset
+	 * while we were in WoWLAN in which case SCD_SRAM_BASE_ADDR will
+	 * contain garbage.
+	 */
+	iwl_pcie_tx_start(trans, 0);
+}
+
+static void iwl_pcie_tx_stop_fh(struct iwl_trans *trans)
+{
+	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+	unsigned long flags;
+	int ch, ret;
+	u32 mask = 0;
+
+	spin_lock(&trans_pcie->irq_lock);
+
+	if (!iwl_trans_grab_nic_access(trans, false, &flags))
+		goto out;
+
+	/* Stop each Tx DMA channel */
+	for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) {
+		iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
+		mask |= FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch);
+	}
+
+	/* Wait for DMA channels to be idle */
+	ret = iwl_poll_bit(trans, FH_TSSR_TX_STATUS_REG, mask, mask, 5000);
+	if (ret < 0)
+		IWL_ERR(trans,
+			"Failing on timeout while stopping DMA channel %d [0x%08x]\n",
+			ch, iwl_read32(trans, FH_TSSR_TX_STATUS_REG));
+
+	iwl_trans_release_nic_access(trans, &flags);
+
+out:
+	spin_unlock(&trans_pcie->irq_lock);
 }
 
 /*
@@ -731,27 +762,13 @@
 int iwl_pcie_tx_stop(struct iwl_trans *trans)
 {
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-	int ch, txq_id, ret;
+	int txq_id;
 
 	/* Turn off all Tx DMA fifos */
-	spin_lock(&trans_pcie->irq_lock);
-
 	iwl_scd_deactivate_fifos(trans);
 
-	/* Stop each Tx DMA channel, and wait for it to be idle */
-	for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) {
-		iwl_write_direct32(trans,
-				   FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
-		ret = iwl_poll_direct_bit(trans, FH_TSSR_TX_STATUS_REG,
-			FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 1000);
-		if (ret < 0)
-			IWL_ERR(trans,
-				"Failing on timeout while stopping DMA channel %d [0x%08x]\n",
-				ch,
-				iwl_read_direct32(trans,
-						  FH_TSSR_TX_STATUS_REG));
-	}
-	spin_unlock(&trans_pcie->irq_lock);
+	/* Turn off all Tx DMA channels */
+	iwl_pcie_tx_stop_fh(trans);
 
 	/*
 	 * This function can be called before the op_mode disabled the
@@ -898,6 +915,10 @@
 		}
 	}
 
+	if (trans->cfg->base_params->num_of_queues > 20)
+		iwl_set_bits_prph(trans, SCD_GP_CTRL,
+				  SCD_GP_CTRL_ENABLE_31_QUEUES);
+
 	return 0;
 error:
 	/*Upon error, free only if we allocated something */
@@ -906,10 +927,18 @@
 	return ret;
 }
 
-static inline void iwl_pcie_txq_progress(struct iwl_trans_pcie *trans_pcie,
-					   struct iwl_txq *txq)
+static inline void iwl_pcie_txq_progress(struct iwl_txq *txq)
 {
-	if (!trans_pcie->wd_timeout)
+	lockdep_assert_held(&txq->lock);
+
+	if (!txq->wd_timeout)
+		return;
+
+	/*
+	 * station is asleep and we send data - that must
+	 * be uAPSD or PS-Poll. Don't rearm the timer.
+	 */
+	if (txq->frozen)
 		return;
 
 	/*
@@ -919,7 +948,7 @@
 	if (txq->q.read_ptr == txq->q.write_ptr)
 		del_timer(&txq->stuck_timer);
 	else
-		mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
+		mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout);
 }
 
 /* Frees buffers until index _not_ inclusive */
@@ -981,21 +1010,35 @@
 		iwl_pcie_txq_free_tfd(trans, txq);
 	}
 
-	iwl_pcie_txq_progress(trans_pcie, txq);
+	iwl_pcie_txq_progress(txq);
 
 	if (iwl_queue_space(&txq->q) > txq->q.low_mark)
 		iwl_wake_queue(trans, txq);
+
+	if (q->read_ptr == q->write_ptr) {
+		IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id);
+		iwl_trans_pcie_unref(trans);
+	}
+
 out:
 	spin_unlock_bh(&txq->lock);
 }
 
-static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans)
+static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
+				      const struct iwl_host_cmd *cmd)
 {
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 	int ret;
 
 	lockdep_assert_held(&trans_pcie->reg_lock);
 
+	if (!(cmd->flags & CMD_SEND_IN_IDLE) &&
+	    !trans_pcie->ref_cmd_in_flight) {
+		trans_pcie->ref_cmd_in_flight = true;
+		IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n");
+		iwl_trans_pcie_ref(trans);
+	}
+
 	if (trans_pcie->cmd_in_flight)
 		return 0;
 
@@ -1036,6 +1079,12 @@
 
 	lockdep_assert_held(&trans_pcie->reg_lock);
 
+	if (trans_pcie->ref_cmd_in_flight) {
+		trans_pcie->ref_cmd_in_flight = false;
+		IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n");
+		iwl_trans_pcie_unref(trans);
+	}
+
 	if (WARN_ON(!trans_pcie->cmd_in_flight))
 		return 0;
 
@@ -1089,7 +1138,7 @@
 		spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
 	}
 
-	iwl_pcie_txq_progress(trans_pcie, txq);
+	iwl_pcie_txq_progress(txq);
 }
 
 static int iwl_pcie_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid,
@@ -1122,14 +1171,18 @@
 #define BUILD_RAxTID(sta_id, tid)	(((sta_id) << 4) + (tid))
 
 void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn,
-			       const struct iwl_trans_txq_scd_cfg *cfg)
+			       const struct iwl_trans_txq_scd_cfg *cfg,
+			       unsigned int wdg_timeout)
 {
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+	struct iwl_txq *txq = &trans_pcie->txq[txq_id];
 	int fifo = -1;
 
 	if (test_and_set_bit(txq_id, trans_pcie->queue_used))
 		WARN_ONCE(1, "queue %d already used - expect issues", txq_id);
 
+	txq->wd_timeout = msecs_to_jiffies(wdg_timeout);
+
 	if (cfg) {
 		fifo = cfg->fifo;
 
@@ -1153,7 +1206,7 @@
 
 			/* enable aggregations for the queue */
 			iwl_scd_txq_enable_agg(trans, txq_id);
-			trans_pcie->txq[txq_id].ampdu = true;
+			txq->ampdu = true;
 		} else {
 			/*
 			 * disable aggregations for the queue, this will also
@@ -1162,20 +1215,20 @@
 			 */
 			iwl_scd_txq_disable_agg(trans, txq_id);
 
-			ssn = trans_pcie->txq[txq_id].q.read_ptr;
+			ssn = txq->q.read_ptr;
 		}
 	}
 
 	/* Place first TFD at index corresponding to start sequence number.
 	 * Assumes that ssn_idx is valid (!= 0xFFF) */
-	trans_pcie->txq[txq_id].q.read_ptr = (ssn & 0xff);
-	trans_pcie->txq[txq_id].q.write_ptr = (ssn & 0xff);
+	txq->q.read_ptr = (ssn & 0xff);
+	txq->q.write_ptr = (ssn & 0xff);
+	iwl_write_direct32(trans, HBUS_TARG_WRPTR,
+			   (ssn & 0xff) | (txq_id << 8));
 
 	if (cfg) {
 		u8 frame_limit = cfg->frame_limit;
 
-		iwl_write_direct32(trans, HBUS_TARG_WRPTR,
-				   (ssn & 0xff) | (txq_id << 8));
 		iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn);
 
 		/* Set up Tx window size and frame limit for this queue */
@@ -1200,11 +1253,17 @@
 		if (txq_id == trans_pcie->cmd_queue &&
 		    trans_pcie->scd_set_active)
 			iwl_scd_enable_set_active(trans, BIT(txq_id));
+
+		IWL_DEBUG_TX_QUEUES(trans,
+				    "Activate queue %d on FIFO %d WrPtr: %d\n",
+				    txq_id, fifo, ssn & 0xff);
+	} else {
+		IWL_DEBUG_TX_QUEUES(trans,
+				    "Activate queue %d WrPtr: %d\n",
+				    txq_id, ssn & 0xff);
 	}
 
-	trans_pcie->txq[txq_id].active = true;
-	IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n",
-			    txq_id, fifo, ssn & 0xff);
+	txq->active = true;
 }
 
 void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id,
@@ -1215,6 +1274,9 @@
 			SCD_TX_STTS_QUEUE_OFFSET(txq_id);
 	static const u32 zero_val[4] = {};
 
+	trans_pcie->txq[txq_id].frozen_expiry_remainder = 0;
+	trans_pcie->txq[txq_id].frozen = false;
+
 	/*
 	 * Upon HW Rfkill - we stop the device, and then stop the queues
 	 * in the op_mode. Just for the sake of the simplicity of the op_mode,
@@ -1469,11 +1531,11 @@
 	trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr);
 
 	/* start timer if queue currently empty */
-	if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout)
-		mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
+	if (q->read_ptr == q->write_ptr && txq->wd_timeout)
+		mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout);
 
 	spin_lock_irqsave(&trans_pcie->reg_lock, flags);
-	ret = iwl_pcie_set_cmd_in_flight(trans);
+	ret = iwl_pcie_set_cmd_in_flight(trans, cmd);
 	if (ret < 0) {
 		idx = ret;
 		spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
@@ -1819,9 +1881,12 @@
 	wait_write_ptr = ieee80211_has_morefrags(fc);
 
 	/* start timer if queue currently empty */
-	if (txq->need_update && q->read_ptr == q->write_ptr &&
-	    trans_pcie->wd_timeout)
-		mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
+	if (q->read_ptr == q->write_ptr) {
+		if (txq->wd_timeout)
+			mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout);
+		IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id);
+		iwl_trans_pcie_ref(trans);
+	}
 
 	/* Tell device the write index *just past* this latest filled TFD */
 	q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 34f09ef..1a4d558 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -1356,8 +1356,8 @@
 
 	/* Find the BSS we want using available scan results */
 	bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
-		sme->ssid, sme->ssid_len,
-		WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+		sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS,
+		IEEE80211_PRIVACY_ANY);
 	if (!bss) {
 		wiphy_err(wiphy, "assoc: bss %pM not in scan results\n",
 			  sme->bssid);
@@ -1616,10 +1616,10 @@
 
 	lbs_deb_enter(LBS_DEB_CFG80211);
 
-	sinfo->filled |= STATION_INFO_TX_BYTES |
-			 STATION_INFO_TX_PACKETS |
-			 STATION_INFO_RX_BYTES |
-			 STATION_INFO_RX_PACKETS;
+	sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES) |
+			 BIT(NL80211_STA_INFO_TX_PACKETS) |
+			 BIT(NL80211_STA_INFO_RX_BYTES) |
+			 BIT(NL80211_STA_INFO_RX_PACKETS);
 	sinfo->tx_bytes = priv->dev->stats.tx_bytes;
 	sinfo->tx_packets = priv->dev->stats.tx_packets;
 	sinfo->rx_bytes = priv->dev->stats.rx_bytes;
@@ -1629,14 +1629,14 @@
 	ret = lbs_get_rssi(priv, &signal, &noise);
 	if (ret == 0) {
 		sinfo->signal = signal;
-		sinfo->filled |= STATION_INFO_SIGNAL;
+		sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
 	}
 
 	/* Convert priv->cur_rate from hw_value to NL80211 value */
 	for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) {
 		if (priv->cur_rate == lbs_rates[i].hw_value) {
 			sinfo->txrate.legacy = lbs_rates[i].bitrate;
-			sinfo->filled |= STATION_INFO_TX_BITRATE;
+			sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
 			break;
 		}
 	}
@@ -2000,7 +2000,7 @@
 	 * bss list is populated already */
 	bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid,
 		params->ssid, params->ssid_len,
-		WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
+		IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
 
 	if (bss) {
 		ret = lbs_ibss_join_existing(priv, params, bss);
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
index cc6a0a5..26cbf1d 100644
--- a/drivers/net/wireless/libertas/debugfs.c
+++ b/drivers/net/wireless/libertas/debugfs.c
@@ -742,8 +742,7 @@
 
 void lbs_debugfs_remove(void)
 {
-	if (lbs_dir)
-		 debugfs_remove(lbs_dir);
+	debugfs_remove(lbs_dir);
 }
 
 void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev)
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 569b64e..8079560 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -667,7 +667,7 @@
 	lbs_deb_enter(LBS_DEB_FW);
 
 	/* Read MAC address from firmware */
-	memset(priv->current_addr, 0xff, ETH_ALEN);
+	eth_broadcast_addr(priv->current_addr);
 	ret = lbs_update_hw_spec(priv);
 	if (ret)
 		goto done;
@@ -871,7 +871,7 @@
 
 	lbs_deb_enter(LBS_DEB_MAIN);
 
-	memset(priv->current_addr, 0xff, ETH_ALEN);
+	eth_broadcast_addr(priv->current_addr);
 
 	priv->connect_status = LBS_DISCONNECTED;
 	priv->channel = DEFAULT_AD_HOC_CHANNEL;
diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c
index dd12cc3..c75a95b 100644
--- a/drivers/net/wireless/libertas_tf/if_usb.c
+++ b/drivers/net/wireless/libertas_tf/if_usb.c
@@ -365,7 +365,6 @@
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(if_usb_reset_device);
 
 /**
  *  usb_tx_block - transfer data to the device
@@ -907,7 +906,6 @@
 	lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret);
 	return ret;
 }
-EXPORT_SYMBOL_GPL(if_usb_prog_firmware);
 
 
 #define if_usb_suspend NULL
diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c
index 25c5acc..ed02e4b 100644
--- a/drivers/net/wireless/libertas_tf/main.c
+++ b/drivers/net/wireless/libertas_tf/main.c
@@ -152,7 +152,7 @@
 	/*
 	 * Read priv address from HW
 	 */
-	memset(priv->current_addr, 0xff, ETH_ALEN);
+	eth_broadcast_addr(priv->current_addr);
 	ret = lbtf_update_hw_spec(priv);
 	if (ret) {
 		ret = -1;
@@ -199,7 +199,7 @@
 static int lbtf_init_adapter(struct lbtf_private *priv)
 {
 	lbtf_deb_enter(LBTF_DEB_MAIN);
-	memset(priv->current_addr, 0xff, ETH_ALEN);
+	eth_broadcast_addr(priv->current_addr);
 	mutex_init(&priv->lock);
 
 	priv->vif = NULL;
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index c6755b8..5dd6ba8 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -330,6 +330,83 @@
 	{ .bitrate = 540 }
 };
 
+#define OUI_QCA 0x001374
+#define QCA_NL80211_SUBCMD_TEST 1
+enum qca_nl80211_vendor_subcmds {
+	QCA_WLAN_VENDOR_ATTR_TEST = 8,
+	QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_TEST
+};
+
+static const struct nla_policy
+hwsim_vendor_test_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = {
+	[QCA_WLAN_VENDOR_ATTR_MAX] = { .type = NLA_U32 },
+};
+
+static int mac80211_hwsim_vendor_cmd_test(struct wiphy *wiphy,
+					  struct wireless_dev *wdev,
+					  const void *data, int data_len)
+{
+	struct sk_buff *skb;
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+	int err;
+	u32 val;
+
+	err = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len,
+			hwsim_vendor_test_policy);
+	if (err)
+		return err;
+	if (!tb[QCA_WLAN_VENDOR_ATTR_TEST])
+		return -EINVAL;
+	val = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_TEST]);
+	wiphy_debug(wiphy, "%s: test=%u\n", __func__, val);
+
+	/* Send a vendor event as a test. Note that this would not normally be
+	 * done within a command handler, but rather, based on some other
+	 * trigger. For simplicity, this command is used to trigger the event
+	 * here.
+	 *
+	 * event_idx = 0 (index in mac80211_hwsim_vendor_commands)
+	 */
+	skb = cfg80211_vendor_event_alloc(wiphy, wdev, 100, 0, GFP_KERNEL);
+	if (skb) {
+		/* skb_put() or nla_put() will fill up data within
+		 * NL80211_ATTR_VENDOR_DATA.
+		 */
+
+		/* Add vendor data */
+		nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TEST, val + 1);
+
+		/* Send the event - this will call nla_nest_end() */
+		cfg80211_vendor_event(skb, GFP_KERNEL);
+	}
+
+	/* Send a response to the command */
+	skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 10);
+	if (!skb)
+		return -ENOMEM;
+
+	/* skb_put() or nla_put() will fill up data within
+	 * NL80211_ATTR_VENDOR_DATA
+	 */
+	nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TEST, val + 2);
+
+	return cfg80211_vendor_cmd_reply(skb);
+}
+
+static struct wiphy_vendor_command mac80211_hwsim_vendor_commands[] = {
+	{
+		.info = { .vendor_id = OUI_QCA,
+			  .subcmd = QCA_NL80211_SUBCMD_TEST },
+		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = mac80211_hwsim_vendor_cmd_test,
+	}
+};
+
+/* Advertise support vendor specific events */
+static const struct nl80211_vendor_cmd_info mac80211_hwsim_vendor_events[] = {
+	{ .vendor_id = OUI_QCA, .subcmd = 1 },
+};
+
 static const struct ieee80211_iface_limit hwsim_if_limits[] = {
 	{ .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
 	{ .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) |
@@ -625,22 +702,22 @@
 	old_ps = data->ps;
 	data->ps = val;
 
+	local_bh_disable();
 	if (val == PS_MANUAL_POLL) {
-		ieee80211_iterate_active_interfaces(data->hw,
-						    IEEE80211_IFACE_ITER_NORMAL,
-						    hwsim_send_ps_poll, data);
+		ieee80211_iterate_active_interfaces_atomic(
+			data->hw, IEEE80211_IFACE_ITER_NORMAL,
+			hwsim_send_ps_poll, data);
 		data->ps_poll_pending = true;
 	} else if (old_ps == PS_DISABLED && val != PS_DISABLED) {
-		ieee80211_iterate_active_interfaces(data->hw,
-						    IEEE80211_IFACE_ITER_NORMAL,
-						    hwsim_send_nullfunc_ps,
-						    data);
+		ieee80211_iterate_active_interfaces_atomic(
+			data->hw, IEEE80211_IFACE_ITER_NORMAL,
+			hwsim_send_nullfunc_ps, data);
 	} else if (old_ps != PS_DISABLED && val == PS_DISABLED) {
-		ieee80211_iterate_active_interfaces(data->hw,
-						    IEEE80211_IFACE_ITER_NORMAL,
-						    hwsim_send_nullfunc_no_ps,
-						    data);
+		ieee80211_iterate_active_interfaces_atomic(
+			data->hw, IEEE80211_IFACE_ITER_NORMAL,
+			hwsim_send_nullfunc_no_ps, data);
 	}
+	local_bh_enable();
 
 	return 0;
 }
@@ -906,8 +983,7 @@
 		goto nla_put_failure;
 	}
 
-	if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
-		    ETH_ALEN, data->addresses[1].addr))
+	if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER, ETH_ALEN, hdr->addr2))
 		goto nla_put_failure;
 
 	/* We get the skb->data */
@@ -946,7 +1022,8 @@
 		goto nla_put_failure;
 
 	genlmsg_end(skb, msg_head);
-	genlmsg_unicast(&init_net, skb, dst_portid);
+	if (genlmsg_unicast(&init_net, skb, dst_portid))
+		goto err_free_txskb;
 
 	/* Enqueue the packet */
 	skb_queue_tail(&data->pending, my_skb);
@@ -955,6 +1032,8 @@
 	return;
 
 nla_put_failure:
+	nlmsg_free(skb);
+err_free_txskb:
 	printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
 	ieee80211_free_txskb(hw, my_skb);
 	data->tx_failed++;
@@ -1519,21 +1598,16 @@
 		vp->aid = info->aid;
 	}
 
-	if (changed & BSS_CHANGED_BEACON_INT) {
-		wiphy_debug(hw->wiphy, "  BCNINT: %d\n", info->beacon_int);
-		data->beacon_int = info->beacon_int * 1024;
-	}
-
 	if (changed & BSS_CHANGED_BEACON_ENABLED) {
-		wiphy_debug(hw->wiphy, "  BCN EN: %d\n", info->enable_beacon);
+		wiphy_debug(hw->wiphy, "  BCN EN: %d (BI=%u)\n",
+			    info->enable_beacon, info->beacon_int);
 		vp->bcn_en = info->enable_beacon;
 		if (data->started &&
 		    !hrtimer_is_queued(&data->beacon_timer.timer) &&
 		    info->enable_beacon) {
 			u64 tsf, until_tbtt;
 			u32 bcn_int;
-			if (WARN_ON(!data->beacon_int))
-				data->beacon_int = 1000 * 1024;
+			data->beacon_int = info->beacon_int * 1024;
 			tsf = mac80211_hwsim_get_tsf(hw, vif);
 			bcn_int = data->beacon_int;
 			until_tbtt = bcn_int - do_div(tsf, bcn_int);
@@ -1547,8 +1621,10 @@
 				mac80211_hwsim_bcn_en_iter, &count);
 			wiphy_debug(hw->wiphy, "  beaconing vifs remaining: %u",
 				    count);
-			if (count == 0)
+			if (count == 0) {
 				tasklet_hrtimer_cancel(&data->beacon_timer);
+				data->beacon_int = 0;
+			}
 		}
 	}
 
@@ -1908,7 +1984,7 @@
 
 	printk(KERN_DEBUG "hwsim sw_scan_complete\n");
 	hwsim->scanning = false;
-	memset(hwsim->scan_addr, 0, ETH_ALEN);
+	eth_zero_addr(hwsim->scan_addr);
 
 	mutex_unlock(&hwsim->mutex);
 }
@@ -2149,14 +2225,14 @@
 	if (param->regd) {
 		int i;
 
-		for (i = 0; hwsim_world_regdom_custom[i] != param->regd &&
-		     i < ARRAY_SIZE(hwsim_world_regdom_custom); i++)
-			;
+		for (i = 0; i < ARRAY_SIZE(hwsim_world_regdom_custom); i++) {
+			if (hwsim_world_regdom_custom[i] != param->regd)
+				continue;
 
-		if (i < ARRAY_SIZE(hwsim_world_regdom_custom)) {
 			ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i);
 			if (ret < 0)
 				return ret;
+			break;
 		}
 	}
 
@@ -2264,7 +2340,7 @@
 	skb_queue_head_init(&data->pending);
 
 	SET_IEEE80211_DEV(hw, data->dev);
-	memset(addr, 0, ETH_ALEN);
+	eth_zero_addr(addr);
 	addr[0] = 0x02;
 	addr[3] = idx >> 8;
 	addr[4] = idx;
@@ -2417,6 +2493,12 @@
 	hw->max_rates = 4;
 	hw->max_rate_tries = 11;
 
+	hw->wiphy->vendor_commands = mac80211_hwsim_vendor_commands;
+	hw->wiphy->n_vendor_commands =
+		ARRAY_SIZE(mac80211_hwsim_vendor_commands);
+	hw->wiphy->vendor_events = mac80211_hwsim_vendor_events;
+	hw->wiphy->n_vendor_events = ARRAY_SIZE(mac80211_hwsim_vendor_events);
+
 	if (param->reg_strict)
 		hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
 	if (param->regd) {
@@ -2557,7 +2639,8 @@
 	if (res < 0)
 		goto out_err;
 
-	return genlmsg_end(skb, hdr);
+	genlmsg_end(skb, hdr);
+	return 0;
 
 out_err:
 	genlmsg_cancel(skb, hdr);
@@ -2596,7 +2679,7 @@
 	ether_setup(dev);
 	dev->tx_queue_len = 0;
 	dev->type = ARPHRD_IEEE80211_RADIOTAP;
-	memset(dev->dev_addr, 0, ETH_ALEN);
+	eth_zero_addr(dev->dev_addr);
 	dev->dev_addr[0] = 0x12;
 }
 
@@ -2607,7 +2690,7 @@
 
 	spin_lock_bh(&hwsim_radio_lock);
 	list_for_each_entry(data, &hwsim_radios, list) {
-		if (memcmp(data->addresses[1].addr, addr, ETH_ALEN) == 0) {
+		if (mac80211_hwsim_addr_match(data, addr)) {
 			_found = true;
 			break;
 		}
@@ -2891,8 +2974,7 @@
 			goto out_err;
 		}
 
-		res = mac80211_hwsim_get_radio(skb, data,
-					       genl_info_snd_portid(info),
+		res = mac80211_hwsim_get_radio(skb, data, genl_info_snd_portid(info),
 					       info->snd_seq, NULL, 0);
 		if (res < 0) {
 			nlmsg_free(skb);
diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c
index 2668e83..3ab87a8 100644
--- a/drivers/net/wireless/mwifiex/11h.c
+++ b/drivers/net/wireless/mwifiex/11h.c
@@ -21,6 +21,16 @@
 #include "fw.h"
 
 
+void mwifiex_init_11h_params(struct mwifiex_private *priv)
+{
+	priv->state_11h.is_11h_enabled = true;
+	priv->state_11h.is_11h_active = false;
+}
+
+inline int mwifiex_is_11h_active(struct mwifiex_private *priv)
+{
+	return priv->state_11h.is_11h_active;
+}
 /* This function appends 11h info to a buffer while joining an
  * infrastructure BSS
  */
@@ -39,7 +49,7 @@
 		return;
 
 	radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
-	sband = priv->wdev->wiphy->bands[radio_type];
+	sband = priv->wdev.wiphy->bands[radio_type];
 
 	cap = (struct mwifiex_ie_types_pwr_capability *)*buffer;
 	cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY);
@@ -69,10 +79,14 @@
 }
 
 /* Enable or disable the 11h extensions in the firmware */
-static int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag)
+int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag)
 {
 	u32 enable = flag;
 
+	/* enable master mode radar detection on AP interface */
+	if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && enable)
+		enable |= MWIFIEX_MASTER_RADAR_DET_MASK;
+
 	return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
 				HostCmd_ACT_GEN_SET, DOT11H_I, &enable, true);
 }
@@ -91,11 +105,191 @@
 		 * bit
 		 */
 		mwifiex_11h_activate(priv, true);
+		priv->state_11h.is_11h_active = true;
 		bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT;
 		mwifiex_11h_process_infra_join(priv, buffer, bss_desc);
 	} else {
 		/* Deactivate 11h functions in the firmware */
 		mwifiex_11h_activate(priv, false);
+		priv->state_11h.is_11h_active = false;
 		bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT;
 	}
 }
+
+/* This is DFS CAC work queue function.
+ * This delayed work emits CAC finished event for cfg80211 if
+ * CAC was started earlier.
+ */
+void mwifiex_dfs_cac_work_queue(struct work_struct *work)
+{
+	struct cfg80211_chan_def chandef;
+	struct delayed_work *delayed_work =
+			container_of(work, struct delayed_work, work);
+	struct mwifiex_private *priv =
+			container_of(delayed_work, struct mwifiex_private,
+				     dfs_cac_work);
+
+	if (WARN_ON(!priv))
+		return;
+
+	chandef = priv->dfs_chandef;
+	if (priv->wdev.cac_started) {
+		dev_dbg(priv->adapter->dev,
+			"CAC timer finished; No radar detected\n");
+		cfg80211_cac_event(priv->netdev, &chandef,
+				   NL80211_RADAR_CAC_FINISHED,
+				   GFP_KERNEL);
+	}
+}
+
+/* This function prepares channel report request command to FW for
+ * starting radar detection.
+ */
+int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv,
+					  struct host_cmd_ds_command *cmd,
+					  void *data_buf)
+{
+	struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req;
+	struct mwifiex_radar_params *radar_params = (void *)data_buf;
+
+	cmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST);
+	cmd->size = cpu_to_le16(S_DS_GEN);
+	le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_chan_rpt_req));
+
+	cr_req->chan_desc.start_freq = cpu_to_le16(MWIFIEX_A_BAND_START_FREQ);
+	cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value;
+	cr_req->chan_desc.chan_width = radar_params->chandef->width;
+	cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms);
+
+	dev_dbg(priv->adapter->dev,
+		"11h: issuing DFS Radar check for channel=%d\n",
+		radar_params->chandef->chan->hw_value);
+
+	return 0;
+}
+
+/* This function is to abort ongoing CAC upon stopping AP operations
+ * or during unload.
+ */
+void mwifiex_abort_cac(struct mwifiex_private *priv)
+{
+	if (priv->wdev.cac_started) {
+		dev_dbg(priv->adapter->dev,
+			"Aborting delayed work for CAC.\n");
+		cancel_delayed_work_sync(&priv->dfs_cac_work);
+		cfg80211_cac_event(priv->netdev, &priv->dfs_chandef,
+				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+	}
+}
+
+/* This function handles channel report event from FW during CAC period.
+ * If radar is detected during CAC, driver indicates the same to cfg80211
+ * and also cancels ongoing delayed work.
+ */
+int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv,
+				     struct sk_buff *skb)
+{
+	struct host_cmd_ds_chan_rpt_event *rpt_event;
+	struct mwifiex_ie_types_chan_rpt_data *rpt;
+	u8 *evt_buf;
+	u16 event_len, tlv_len;
+
+	rpt_event = (void *)(skb->data + sizeof(u32));
+	event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event)+
+				sizeof(u32));
+
+	if (le32_to_cpu(rpt_event->result) != HostCmd_RESULT_OK) {
+		dev_err(priv->adapter->dev, "Error in channel report event\n");
+		return -1;
+	}
+
+	evt_buf = (void *)&rpt_event->tlvbuf;
+
+	while (event_len >= sizeof(struct mwifiex_ie_types_header)) {
+		rpt = (void *)&rpt_event->tlvbuf;
+		tlv_len = le16_to_cpu(rpt->header.len);
+
+		switch (le16_to_cpu(rpt->header.type)) {
+		case TLV_TYPE_CHANRPT_11H_BASIC:
+			if (rpt->map.radar) {
+				dev_notice(priv->adapter->dev,
+					   "RADAR Detected on channel %d!\n",
+					    priv->dfs_chandef.chan->hw_value);
+				cancel_delayed_work_sync(&priv->dfs_cac_work);
+				cfg80211_cac_event(priv->netdev,
+						   &priv->dfs_chandef,
+						   NL80211_RADAR_DETECTED,
+						   GFP_KERNEL);
+			}
+			break;
+		default:
+			break;
+		}
+
+		evt_buf += (tlv_len + sizeof(rpt->header));
+		event_len -= (tlv_len + sizeof(rpt->header));
+	}
+
+	return 0;
+}
+
+/* Handler for radar detected event from FW.*/
+int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv,
+				      struct sk_buff *skb)
+{
+	struct mwifiex_radar_det_event *rdr_event;
+
+	rdr_event = (void *)(skb->data + sizeof(u32));
+
+	if (le32_to_cpu(rdr_event->passed)) {
+		dev_notice(priv->adapter->dev,
+			   "radar detected; indicating kernel\n");
+		cfg80211_radar_event(priv->adapter->wiphy, &priv->dfs_chandef,
+				     GFP_KERNEL);
+		dev_dbg(priv->adapter->dev, "regdomain: %d\n",
+			rdr_event->reg_domain);
+		dev_dbg(priv->adapter->dev, "radar detection type: %d\n",
+			rdr_event->det_type);
+	} else {
+		dev_dbg(priv->adapter->dev, "false radar detection event!\n");
+	}
+
+	return 0;
+}
+
+/* This is work queue function for channel switch handling.
+ * This function takes care of updating new channel definitin to
+ * bss config structure, restart AP and indicate channel switch success
+ * to cfg80211.
+ */
+void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work)
+{
+	struct mwifiex_uap_bss_param *bss_cfg;
+	struct delayed_work *delayed_work =
+			container_of(work, struct delayed_work, work);
+	struct mwifiex_private *priv =
+			container_of(delayed_work, struct mwifiex_private,
+				     dfs_chan_sw_work);
+
+	if (WARN_ON(!priv))
+		return;
+
+	bss_cfg = &priv->bss_cfg;
+	if (!bss_cfg->beacon_period) {
+		dev_err(priv->adapter->dev,
+			"channel switch: AP already stopped\n");
+		return;
+	}
+
+	mwifiex_uap_set_channel(bss_cfg, priv->dfs_chandef);
+
+	if (mwifiex_config_start_uap(priv, bss_cfg)) {
+		dev_dbg(priv->adapter->dev,
+			"Failed to start AP after channel switch\n");
+		return;
+	}
+
+	dev_notice(priv->adapter->dev,
+		   "indicating channel switch completion to kernel\n");
+	cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef);
+}
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c
index 9d4786e..433bd68 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/mwifiex/11n.c
@@ -39,7 +39,7 @@
 {
 	uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info);
 	struct ieee80211_supported_band *sband =
-					priv->wdev->wiphy->bands[radio_type];
+					priv->wdev.wiphy->bands[radio_type];
 
 	if (WARN_ON_ONCE(!sband)) {
 		dev_err(priv->adapter->dev, "Invalid radio type!\n");
@@ -159,6 +159,7 @@
 	int tid;
 	struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
 	struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;
+	struct mwifiex_ra_list_tbl *ra_list;
 	u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
 
 	add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn))
@@ -166,7 +167,13 @@
 
 	tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
 	       >> BLOCKACKPARAM_TID_POS;
+	ra_list = mwifiex_wmm_get_ralist_node(priv, tid, add_ba_rsp->
+		peer_mac_addr);
 	if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
+		if (ra_list) {
+			ra_list->ba_status = BA_SETUP_NONE;
+			ra_list->amsdu_in_ampdu = false;
+		}
 		mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr,
 				   TYPE_DELBA_SENT, true);
 		if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT)
@@ -185,6 +192,10 @@
 			tx_ba_tbl->amsdu = true;
 		else
 			tx_ba_tbl->amsdu = false;
+		if (ra_list) {
+			ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu;
+			ra_list->ba_status = BA_SETUP_COMPLETE;
+		}
 	} else {
 		dev_err(priv->adapter->dev, "BA stream not created\n");
 	}
@@ -314,7 +325,7 @@
 		return ret_len;
 
 	radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
-	sband = priv->wdev->wiphy->bands[radio_type];
+	sband = priv->wdev.wiphy->bands[radio_type];
 
 	if (bss_desc->bcn_ht_cap) {
 		ht_cap = (struct mwifiex_ie_types_htcap *) *buffer;
@@ -515,6 +526,7 @@
 			   enum mwifiex_ba_status ba_status)
 {
 	struct mwifiex_tx_ba_stream_tbl *new_node;
+	struct mwifiex_ra_list_tbl *ra_list;
 	unsigned long flags;
 
 	if (!mwifiex_get_ba_tbl(priv, tid, ra)) {
@@ -522,7 +534,11 @@
 				   GFP_ATOMIC);
 		if (!new_node)
 			return;
-
+		ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra);
+		if (ra_list) {
+			ra_list->ba_status = ba_status;
+			ra_list->amsdu_in_ampdu = false;
+		}
 		INIT_LIST_HEAD(&new_node->list);
 
 		new_node->tid = tid;
@@ -558,10 +574,10 @@
 		spin_lock_irqsave(&priv->sta_list_spinlock, flags);
 		sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);
 		if (!sta_ptr) {
+			spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
 			dev_warn(priv->adapter->dev,
 				 "BA setup with unknown TDLS peer %pM!\n",
 				peer_mac);
-			spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
 			return -1;
 		}
 		if (sta_ptr->is_11ac_enabled)
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h
index f275675..afdd58a 100644
--- a/drivers/net/wireless/mwifiex/11n.h
+++ b/drivers/net/wireless/mwifiex/11n.h
@@ -77,22 +77,6 @@
 	return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false;
 }
 
-/* This function checks whether AMSDU is allowed for BA stream. */
-static inline u8
-mwifiex_is_amsdu_in_ampdu_allowed(struct mwifiex_private *priv,
-				  struct mwifiex_ra_list_tbl *ptr, int tid)
-{
-	struct mwifiex_tx_ba_stream_tbl *tx_tbl;
-
-	if (is_broadcast_ether_addr(ptr->ra))
-		return false;
-	tx_tbl = mwifiex_get_ba_tbl(priv, tid, ptr->ra);
-	if (tx_tbl)
-		return tx_tbl->amsdu;
-
-	return false;
-}
-
 /* This function checks whether AMPDU is allowed or not for a particular TID. */
 static inline u8
 mwifiex_is_ampdu_allowed(struct mwifiex_private *priv,
@@ -130,7 +114,9 @@
 {
 	struct mwifiex_private *priv;
 	u8 i;
-	u32 ba_stream_num = 0;
+	u32 ba_stream_num = 0, ba_stream_max;
+
+	ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED;
 
 	for (i = 0; i < adapter->priv_num; i++) {
 		priv = adapter->priv[i];
@@ -139,8 +125,14 @@
 				&priv->tx_ba_stream_tbl_ptr);
 	}
 
-	return ((ba_stream_num <
-		 MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) ? true : false);
+	if (adapter->fw_api_ver == MWIFIEX_FW_V15) {
+		ba_stream_max =
+			       GETSUPP_TXBASTREAMS(adapter->hw_dot_11n_dev_cap);
+		if (!ba_stream_max)
+			ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED;
+	}
+
+	return ((ba_stream_num < ba_stream_max) ? true : false);
 }
 
 /*
@@ -174,22 +166,6 @@
 }
 
 /*
- * This function checks whether BA stream is set up or not.
- */
-static inline int
-mwifiex_is_ba_stream_setup(struct mwifiex_private *priv,
-			   struct mwifiex_ra_list_tbl *ptr, int tid)
-{
-	struct mwifiex_tx_ba_stream_tbl *tx_tbl;
-
-	tx_tbl = mwifiex_get_ba_tbl(priv, tid, ptr->ra);
-	if (tx_tbl && IS_BASTREAM_SETUP(tx_tbl))
-		return true;
-
-	return false;
-}
-
-/*
  * This function checks whether associated station is 11n enabled
  */
 static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv,
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c
index 8720a3d..6183e25 100644
--- a/drivers/net/wireless/mwifiex/11n_aggr.c
+++ b/drivers/net/wireless/mwifiex/11n_aggr.c
@@ -101,6 +101,13 @@
 {
 	struct txpd *local_tx_pd;
 	struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
+	unsigned int pad;
+	int headroom = (priv->adapter->iface_type ==
+			MWIFIEX_USB) ? 0 : INTF_HEADER_LEN;
+
+	pad = ((void *)skb->data - sizeof(*local_tx_pd) -
+		headroom - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1);
+	skb_push(skb, pad);
 
 	skb_push(skb, sizeof(*local_tx_pd));
 
@@ -114,10 +121,12 @@
 	local_tx_pd->bss_num = priv->bss_num;
 	local_tx_pd->bss_type = priv->bss_type;
 	/* Always zero as the data is followed by struct txpd */
-	local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd));
+	local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) +
+						 pad);
 	local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU);
 	local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len -
-						 sizeof(*local_tx_pd));
+						 sizeof(*local_tx_pd) -
+						 pad);
 
 	if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
 		local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET;
@@ -161,7 +170,7 @@
 	struct mwifiex_adapter *adapter = priv->adapter;
 	struct sk_buff *skb_aggr, *skb_src;
 	struct mwifiex_txinfo *tx_info_aggr, *tx_info_src;
-	int pad = 0, ret;
+	int pad = 0, aggr_num = 0, ret;
 	struct mwifiex_tx_param tx_param;
 	struct txpd *ptx_pd = NULL;
 	struct timeval tv;
@@ -175,14 +184,15 @@
 	}
 
 	tx_info_src = MWIFIEX_SKB_TXCB(skb_src);
-	skb_aggr = dev_alloc_skb(adapter->tx_buf_size);
+	skb_aggr = mwifiex_alloc_dma_align_buf(adapter->tx_buf_size,
+					       GFP_ATOMIC | GFP_DMA);
 	if (!skb_aggr) {
 		dev_err(adapter->dev, "%s: alloc skb_aggr\n", __func__);
 		spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
 				       ra_list_flags);
 		return -1;
 	}
-	skb_reserve(skb_aggr, headroom + sizeof(struct txpd));
+	skb_reserve(skb_aggr, MWIFIEX_MIN_DATA_HEADER_LEN);
 	tx_info_aggr =  MWIFIEX_SKB_TXCB(skb_aggr);
 
 	memset(tx_info_aggr, 0, sizeof(*tx_info_aggr));
@@ -191,6 +201,7 @@
 
 	if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
 		tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
+	tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT;
 	skb_aggr->priority = skb_src->priority;
 
 	do_gettimeofday(&tv);
@@ -202,11 +213,9 @@
 			break;
 
 		skb_src = skb_dequeue(&pra_list->skb_head);
-
 		pra_list->total_pkt_count--;
-
 		atomic_dec(&priv->wmm.tx_pkts_queued);
-
+		aggr_num++;
 		spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
 				       ra_list_flags);
 		mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad);
@@ -242,6 +251,12 @@
 		ptx_pd = (struct txpd *)skb_aggr->data;
 
 	skb_push(skb_aggr, headroom);
+	tx_info_aggr->aggr_num = aggr_num * 2;
+	if (adapter->data_sent || adapter->tx_lock_flag) {
+		atomic_add(aggr_num * 2, &adapter->tx_queued);
+		skb_queue_tail(&adapter->tx_data_q, skb_aggr);
+		return 0;
+	}
 
 	if (adapter->iface_type == MWIFIEX_USB) {
 		adapter->data_sent = true;
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c
index d73fda3..f75f8ac 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c
@@ -45,7 +45,7 @@
 		skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
 
 		ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
-					 priv->wdev->iftype, 0, false);
+					 priv->wdev.iftype, 0, false);
 
 		while (!skb_queue_empty(&list)) {
 			rx_skb = __skb_dequeue(&list);
@@ -353,9 +353,6 @@
 
 	spin_lock_irqsave(&priv->sta_list_spinlock, flags);
 	if (mwifiex_queuing_ra_based(priv)) {
-		dev_dbg(priv->adapter->dev,
-			"info: AP/ADHOC:last_seq=%d start_win=%d\n",
-			last_seq, new_node->start_win);
 		if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) {
 			node = mwifiex_get_sta_entry(priv, ta);
 			if (node)
@@ -370,6 +367,9 @@
 	}
 	spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
 
+	dev_dbg(priv->adapter->dev, "info: last_seq=%d start_win=%d\n",
+		last_seq, new_node->start_win);
+
 	if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM &&
 	    last_seq >= new_node->start_win) {
 		new_node->start_win = last_seq + 1;
@@ -391,10 +391,8 @@
 	new_node->timer_context.priv = priv;
 	new_node->timer_context.timer_is_set = false;
 
-	init_timer(&new_node->timer_context.timer);
-	new_node->timer_context.timer.function = mwifiex_flush_data;
-	new_node->timer_context.timer.data =
-			(unsigned long) &new_node->timer_context;
+	setup_timer(&new_node->timer_context.timer, mwifiex_flush_data,
+		    (unsigned long)&new_node->timer_context);
 
 	for (i = 0; i < win_size; ++i)
 		new_node->rx_reorder_ptr[i] = NULL;
@@ -468,10 +466,10 @@
 		sta_ptr = mwifiex_get_sta_entry(priv,
 						cmd_addba_req->peer_mac_addr);
 		if (!sta_ptr) {
+			spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
 			dev_warn(priv->adapter->dev,
 				 "BA setup with unknown TDLS peer %pM!\n",
 				 cmd_addba_req->peer_mac_addr);
-			spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
 			return -1;
 		}
 		if (sta_ptr->is_11ac_enabled)
@@ -661,6 +659,7 @@
 {
 	struct mwifiex_rx_reorder_tbl *tbl;
 	struct mwifiex_tx_ba_stream_tbl *ptx_tbl;
+	struct mwifiex_ra_list_tbl *ra_list;
 	u8 cleanup_rx_reorder_tbl;
 	unsigned long flags;
 
@@ -688,7 +687,11 @@
 				"event: TID, RA not found in table\n");
 			return;
 		}
-
+		ra_list = mwifiex_wmm_get_ralist_node(priv, tid, peer_mac);
+		if (ra_list) {
+			ra_list->amsdu_in_ampdu = false;
+			ra_list->ba_status = BA_SETUP_NONE;
+		}
 		spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
 		mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl);
 		spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile
index 7355aed..7c5407d 100644
--- a/drivers/net/wireless/mwifiex/Makefile
+++ b/drivers/net/wireless/mwifiex/Makefile
@@ -53,3 +53,5 @@
 
 mwifiex_usb-y += usb.o
 obj-$(CPTCFG_MWIFIEX_USB) += mwifiex_usb.o
+
+ccflags-y += -D__CHECK_ENDIAN
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index 4a66a65..bf9020f 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -590,77 +590,62 @@
 	struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
 	struct mwifiex_private *priv;
 	struct mwifiex_uap_bss_param *bss_cfg;
-	int ret, bss_started, i;
+	int ret;
 
-	for (i = 0; i < adapter->priv_num; i++) {
-		priv = adapter->priv[i];
+	priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
 
-		switch (priv->bss_role) {
-		case MWIFIEX_BSS_ROLE_UAP:
-			bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param),
-					  GFP_KERNEL);
-			if (!bss_cfg)
-				return -ENOMEM;
-
-			mwifiex_set_sys_config_invalid_data(bss_cfg);
-
-			if (changed & WIPHY_PARAM_RTS_THRESHOLD)
-				bss_cfg->rts_threshold = wiphy->rts_threshold;
-			if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
-				bss_cfg->frag_threshold = wiphy->frag_threshold;
-			if (changed & WIPHY_PARAM_RETRY_LONG)
-				bss_cfg->retry_limit = wiphy->retry_long;
-
-			bss_started = priv->bss_started;
-
-			ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
-					       HostCmd_ACT_GEN_SET, 0,
-					       NULL, true);
-			if (ret) {
-				wiphy_err(wiphy, "Failed to stop the BSS\n");
-				kfree(bss_cfg);
-				return ret;
-			}
-
-			ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
-					       HostCmd_ACT_GEN_SET,
-					       UAP_BSS_PARAMS_I, bss_cfg,
-					       false);
-
-			kfree(bss_cfg);
-
-			if (ret) {
-				wiphy_err(wiphy, "Failed to set bss config\n");
-				return ret;
-			}
-
-			if (!bss_started)
-				break;
-
-			ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START,
-					       HostCmd_ACT_GEN_SET, 0,
-					       NULL, false);
-			if (ret) {
-				wiphy_err(wiphy, "Failed to start BSS\n");
-				return ret;
-			}
-
-			break;
-		case MWIFIEX_BSS_ROLE_STA:
-			if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
-				ret = mwifiex_set_rts(priv,
-						      wiphy->rts_threshold);
-				if (ret)
-					return ret;
-			}
-			if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
-				ret = mwifiex_set_frag(priv,
-						       wiphy->frag_threshold);
-				if (ret)
-					return ret;
-			}
-			break;
+	switch (priv->bss_role) {
+	case MWIFIEX_BSS_ROLE_UAP:
+		if (priv->bss_started) {
+			dev_err(adapter->dev,
+				"cannot change wiphy params when bss started");
+			return -EINVAL;
 		}
+
+		bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL);
+		if (!bss_cfg)
+			return -ENOMEM;
+
+		mwifiex_set_sys_config_invalid_data(bss_cfg);
+
+		if (changed & WIPHY_PARAM_RTS_THRESHOLD)
+			bss_cfg->rts_threshold = wiphy->rts_threshold;
+		if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
+			bss_cfg->frag_threshold = wiphy->frag_threshold;
+		if (changed & WIPHY_PARAM_RETRY_LONG)
+			bss_cfg->retry_limit = wiphy->retry_long;
+
+		ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
+				       HostCmd_ACT_GEN_SET,
+				       UAP_BSS_PARAMS_I, bss_cfg,
+				       false);
+
+		kfree(bss_cfg);
+		if (ret) {
+			wiphy_err(wiphy, "Failed to set wiphy phy params\n");
+			return ret;
+		}
+		break;
+
+		case MWIFIEX_BSS_ROLE_STA:
+		if (priv->media_connected) {
+			dev_err(adapter->dev,
+				"cannot change wiphy params when connected");
+			return -EINVAL;
+		}
+		if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
+			ret = mwifiex_set_rts(priv,
+					      wiphy->rts_threshold);
+			if (ret)
+				return ret;
+		}
+		if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
+			ret = mwifiex_set_frag(priv,
+					       wiphy->frag_threshold);
+			if (ret)
+				return ret;
+		}
+		break;
 	}
 
 	return 0;
@@ -671,9 +656,6 @@
 {
 	u16 mode = P2P_MODE_DISABLE;
 
-	if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA)
-		mwifiex_set_bss_role(priv, MWIFIEX_BSS_ROLE_STA);
-
 	if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
 			     HostCmd_ACT_GEN_SET, 0, &mode, true))
 		return -1;
@@ -730,12 +712,282 @@
 			     HostCmd_ACT_GEN_SET, 0, &mode, true))
 		return -1;
 
-	if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP)
-		mwifiex_set_bss_role(priv, MWIFIEX_BSS_ROLE_UAP);
+	return 0;
+}
+
+static int mwifiex_deinit_priv_params(struct mwifiex_private *priv)
+{
+	struct mwifiex_adapter *adapter = priv->adapter;
+	unsigned long flags;
+
+	priv->mgmt_frame_mask = 0;
+	if (mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG,
+			     HostCmd_ACT_GEN_SET, 0,
+			     &priv->mgmt_frame_mask, false)) {
+		dev_warn(priv->adapter->dev,
+			 "could not unregister mgmt frame rx\n");
+		return -1;
+	}
+
+	mwifiex_deauthenticate(priv, NULL);
+
+	spin_lock_irqsave(&adapter->main_proc_lock, flags);
+	adapter->main_locked = true;
+	if (adapter->mwifiex_processing) {
+		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+		flush_workqueue(adapter->workqueue);
+	} else {
+		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+	}
+
+	spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+	adapter->rx_locked = true;
+	if (adapter->rx_processing) {
+		spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+		flush_workqueue(adapter->rx_workqueue);
+	} else {
+	spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+	}
+
+	mwifiex_free_priv(priv);
+	priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+	priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+	priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
 
 	return 0;
 }
 
+static int
+mwifiex_init_new_priv_params(struct mwifiex_private *priv,
+			     struct net_device *dev,
+			     enum nl80211_iftype type)
+{
+	struct mwifiex_adapter *adapter = priv->adapter;
+	unsigned long flags;
+
+	mwifiex_init_priv(priv);
+
+	priv->bss_mode = type;
+	priv->wdev.iftype = type;
+
+	mwifiex_init_priv_params(priv, priv->netdev);
+	priv->bss_started = 0;
+
+	switch (type) {
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_ADHOC:
+		priv->bss_role =  MWIFIEX_BSS_ROLE_STA;
+		priv->bss_type = MWIFIEX_BSS_TYPE_STA;
+		break;
+	case NL80211_IFTYPE_P2P_CLIENT:
+	case NL80211_IFTYPE_P2P_GO:
+		priv->bss_role =  MWIFIEX_BSS_ROLE_STA;
+		priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
+		break;
+	case NL80211_IFTYPE_AP:
+		priv->bss_type = MWIFIEX_BSS_TYPE_UAP;
+		priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
+		break;
+	default:
+		dev_err(priv->adapter->dev,
+			"%s: changing to %d not supported\n",
+			dev->name, type);
+		return -EOPNOTSUPP;
+	}
+
+	spin_lock_irqsave(&adapter->main_proc_lock, flags);
+	adapter->main_locked = false;
+	spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+
+	spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+	adapter->rx_locked = false;
+	spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+
+	return 0;
+}
+
+static int
+mwifiex_change_vif_to_p2p(struct net_device *dev,
+			  enum nl80211_iftype curr_iftype,
+			  enum nl80211_iftype type, u32 *flags,
+			  struct vif_params *params)
+{
+	struct mwifiex_private *priv;
+	struct mwifiex_adapter *adapter;
+
+	priv = mwifiex_netdev_get_priv(dev);
+
+	if (!priv)
+		return -1;
+
+	adapter = priv->adapter;
+
+	if (adapter->curr_iface_comb.p2p_intf ==
+	    adapter->iface_limit.p2p_intf) {
+		dev_err(adapter->dev,
+			"cannot create multiple P2P ifaces\n");
+		return -1;
+	}
+
+	dev_dbg(priv->adapter->dev, "%s: changing role to p2p\n", dev->name);
+
+	if (mwifiex_deinit_priv_params(priv))
+		return -1;
+	if (mwifiex_init_new_priv_params(priv, dev, type))
+		return -1;
+
+	switch (type) {
+	case NL80211_IFTYPE_P2P_CLIENT:
+		if (mwifiex_cfg80211_init_p2p_client(priv))
+			return -EFAULT;
+		break;
+	case NL80211_IFTYPE_P2P_GO:
+		if (mwifiex_cfg80211_init_p2p_go(priv))
+			return -EFAULT;
+		break;
+	default:
+		dev_err(priv->adapter->dev,
+			"%s: changing to %d not supported\n",
+			dev->name, type);
+		return -EOPNOTSUPP;
+	}
+
+	if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+			     HostCmd_ACT_GEN_SET, 0, NULL, true))
+		return -1;
+
+	if (mwifiex_sta_init_cmd(priv, false, false))
+		return -1;
+
+	switch (curr_iftype) {
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_ADHOC:
+		adapter->curr_iface_comb.sta_intf--;
+		break;
+	case NL80211_IFTYPE_AP:
+		adapter->curr_iface_comb.uap_intf--;
+		break;
+	default:
+		break;
+	}
+
+	adapter->curr_iface_comb.p2p_intf++;
+	dev->ieee80211_ptr->iftype = type;
+
+	return 0;
+}
+
+static int
+mwifiex_change_vif_to_sta_adhoc(struct net_device *dev,
+				enum nl80211_iftype curr_iftype,
+				enum nl80211_iftype type, u32 *flags,
+				struct vif_params *params)
+{
+	struct mwifiex_private *priv;
+	struct mwifiex_adapter *adapter;
+
+	priv = mwifiex_netdev_get_priv(dev);
+
+	if (!priv)
+		return -1;
+
+	adapter = priv->adapter;
+
+	if ((curr_iftype != NL80211_IFTYPE_P2P_CLIENT &&
+	     curr_iftype != NL80211_IFTYPE_P2P_GO) &&
+	    (adapter->curr_iface_comb.sta_intf ==
+	     adapter->iface_limit.sta_intf)) {
+		dev_err(adapter->dev,
+			"cannot create multiple station/adhoc ifaces\n");
+		return -1;
+	}
+
+	if (type == NL80211_IFTYPE_STATION)
+		dev_notice(adapter->dev,
+			   "%s: changing role to station\n", dev->name);
+	else
+		dev_notice(adapter->dev,
+			   "%s: changing role to adhoc\n", dev->name);
+
+	if (mwifiex_deinit_priv_params(priv))
+		return -1;
+	if (mwifiex_init_new_priv_params(priv, dev, type))
+		return -1;
+	if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+			     HostCmd_ACT_GEN_SET, 0, NULL, true))
+		return -1;
+	if (mwifiex_sta_init_cmd(priv, false, false))
+		return -1;
+
+	switch (curr_iftype) {
+	case NL80211_IFTYPE_P2P_CLIENT:
+	case NL80211_IFTYPE_P2P_GO:
+		adapter->curr_iface_comb.p2p_intf--;
+		break;
+	case NL80211_IFTYPE_AP:
+		adapter->curr_iface_comb.uap_intf--;
+		break;
+	default:
+		break;
+	}
+
+	adapter->curr_iface_comb.sta_intf++;
+	dev->ieee80211_ptr->iftype = type;
+	return 0;
+}
+
+static int
+mwifiex_change_vif_to_ap(struct net_device *dev,
+			 enum nl80211_iftype curr_iftype,
+			 enum nl80211_iftype type, u32 *flags,
+			 struct vif_params *params)
+{
+	struct mwifiex_private *priv;
+	struct mwifiex_adapter *adapter;
+
+	priv = mwifiex_netdev_get_priv(dev);
+
+	if (!priv)
+		return -1;
+
+	adapter = priv->adapter;
+
+	if (adapter->curr_iface_comb.uap_intf ==
+	    adapter->iface_limit.uap_intf) {
+		dev_err(adapter->dev,
+			"cannot create multiple AP ifaces\n");
+		return -1;
+	}
+
+	dev_notice(adapter->dev, "%s: changing role to AP\n", dev->name);
+
+	if (mwifiex_deinit_priv_params(priv))
+		return -1;
+	if (mwifiex_init_new_priv_params(priv, dev, type))
+		return -1;
+	if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+			     HostCmd_ACT_GEN_SET, 0, NULL, true))
+		return -1;
+	if (mwifiex_sta_init_cmd(priv, false, false))
+		return -1;
+
+	switch (curr_iftype) {
+	case NL80211_IFTYPE_P2P_CLIENT:
+	case NL80211_IFTYPE_P2P_GO:
+		adapter->curr_iface_comb.p2p_intf--;
+		break;
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_ADHOC:
+		adapter->curr_iface_comb.sta_intf--;
+		break;
+	default:
+		break;
+	}
+
+	adapter->curr_iface_comb.uap_intf++;
+	dev->ieee80211_ptr->iftype = type;
+	return 0;
+}
 /*
  * CFG802.11 operation handler to change interface type.
  */
@@ -745,19 +997,32 @@
 				     enum nl80211_iftype type, u32 *flags,
 				     struct vif_params *params)
 {
-	int ret;
 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+	enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype;
 
-	switch (dev->ieee80211_ptr->iftype) {
+	switch (curr_iftype) {
 	case NL80211_IFTYPE_ADHOC:
 		switch (type) {
 		case NL80211_IFTYPE_STATION:
-			break;
+			priv->bss_mode = type;
+			priv->sec_info.authentication_mode =
+						   NL80211_AUTHTYPE_OPEN_SYSTEM;
+			dev->ieee80211_ptr->iftype = type;
+			mwifiex_deauthenticate(priv, NULL);
+			return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+						HostCmd_ACT_GEN_SET, 0, NULL,
+						true);
+		case NL80211_IFTYPE_P2P_CLIENT:
+		case NL80211_IFTYPE_P2P_GO:
+			return mwifiex_change_vif_to_p2p(dev, curr_iftype,
+							 type, flags, params);
+		case NL80211_IFTYPE_AP:
+			return mwifiex_change_vif_to_ap(dev, curr_iftype, type,
+							flags, params);
 		case NL80211_IFTYPE_UNSPECIFIED:
 			wiphy_warn(wiphy, "%s: kept type as IBSS\n", dev->name);
 		case NL80211_IFTYPE_ADHOC:	/* This shouldn't happen */
 			return 0;
-		case NL80211_IFTYPE_AP:
 		default:
 			wiphy_err(wiphy, "%s: changing to %d not supported\n",
 				  dev->name, type);
@@ -767,22 +1032,25 @@
 	case NL80211_IFTYPE_STATION:
 		switch (type) {
 		case NL80211_IFTYPE_ADHOC:
-			break;
+			priv->bss_mode = type;
+			priv->sec_info.authentication_mode =
+						   NL80211_AUTHTYPE_OPEN_SYSTEM;
+			dev->ieee80211_ptr->iftype = type;
+			mwifiex_deauthenticate(priv, NULL);
+			return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+						HostCmd_ACT_GEN_SET, 0, NULL,
+						true);
 		case NL80211_IFTYPE_P2P_CLIENT:
-			if (mwifiex_cfg80211_init_p2p_client(priv))
-				return -EFAULT;
-			dev->ieee80211_ptr->iftype = type;
-			return 0;
 		case NL80211_IFTYPE_P2P_GO:
-			if (mwifiex_cfg80211_init_p2p_go(priv))
-				return -EFAULT;
-			dev->ieee80211_ptr->iftype = type;
-			return 0;
+			return mwifiex_change_vif_to_p2p(dev, curr_iftype,
+							 type, flags, params);
+		case NL80211_IFTYPE_AP:
+			return mwifiex_change_vif_to_ap(dev, curr_iftype, type,
+							flags, params);
 		case NL80211_IFTYPE_UNSPECIFIED:
 			wiphy_warn(wiphy, "%s: kept type as STA\n", dev->name);
 		case NL80211_IFTYPE_STATION:	/* This shouldn't happen */
 			return 0;
-		case NL80211_IFTYPE_AP:
 		default:
 			wiphy_err(wiphy, "%s: changing to %d not supported\n",
 				  dev->name, type);
@@ -791,12 +1059,20 @@
 		break;
 	case NL80211_IFTYPE_AP:
 		switch (type) {
+		case NL80211_IFTYPE_ADHOC:
+		case NL80211_IFTYPE_STATION:
+			return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype,
+							       type, flags,
+							       params);
+			break;
+		case NL80211_IFTYPE_P2P_CLIENT:
+		case NL80211_IFTYPE_P2P_GO:
+			return mwifiex_change_vif_to_p2p(dev, curr_iftype,
+							 type, flags, params);
 		case NL80211_IFTYPE_UNSPECIFIED:
 			wiphy_warn(wiphy, "%s: kept type as AP\n", dev->name);
 		case NL80211_IFTYPE_AP:		/* This shouldn't happen */
 			return 0;
-		case NL80211_IFTYPE_ADHOC:
-		case NL80211_IFTYPE_STATION:
 		default:
 			wiphy_err(wiphy, "%s: changing to %d not supported\n",
 				  dev->name, type);
@@ -807,11 +1083,30 @@
 	case NL80211_IFTYPE_P2P_GO:
 		switch (type) {
 		case NL80211_IFTYPE_STATION:
-			if (mwifiex_cfg80211_deinit_p2p(priv))
+			if (mwifiex_cfg80211_init_p2p_client(priv))
 				return -EFAULT;
 			dev->ieee80211_ptr->iftype = type;
+			break;
+		case NL80211_IFTYPE_ADHOC:
+			if (mwifiex_cfg80211_deinit_p2p(priv))
+				return -EFAULT;
+			return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype,
+							       type, flags,
+							       params);
+			break;
+		case NL80211_IFTYPE_AP:
+			if (mwifiex_cfg80211_deinit_p2p(priv))
+				return -EFAULT;
+			return mwifiex_change_vif_to_ap(dev, curr_iftype, type,
+							flags, params);
+		case NL80211_IFTYPE_UNSPECIFIED:
+			wiphy_warn(wiphy, "%s: kept type as P2P\n", dev->name);
+		case NL80211_IFTYPE_P2P_CLIENT:
+		case NL80211_IFTYPE_P2P_GO:
 			return 0;
 		default:
+			wiphy_err(wiphy, "%s: changing to %d not supported\n",
+				  dev->name, type);
 			return -EOPNOTSUPP;
 		}
 		break;
@@ -821,16 +1116,8 @@
 		return -EOPNOTSUPP;
 	}
 
-	dev->ieee80211_ptr->iftype = type;
-	priv->bss_mode = type;
-	mwifiex_deauthenticate(priv, NULL);
 
-	priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
-
-	ret = mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
-			       HostCmd_ACT_GEN_SET, 0, NULL, true);
-
-	return ret;
+	return 0;
 }
 
 static void
@@ -856,16 +1143,16 @@
 			/* HT or VHT */
 			switch (tx_htinfo & (BIT(3) | BIT(2))) {
 			case 0:
-				/* This will be 20MHz */
+				rate->bw = RATE_INFO_BW_20;
 				break;
 			case (BIT(2)):
-				rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+				rate->bw = RATE_INFO_BW_40;
 				break;
 			case (BIT(3)):
-				rate->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+				rate->bw = RATE_INFO_BW_80;
 				break;
 			case (BIT(3) | BIT(2)):
-				rate->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
+				rate->bw = RATE_INFO_BW_160;
 				break;
 			}
 
@@ -885,8 +1172,9 @@
 		if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) {
 			rate->mcs = priv->tx_rate;
 			rate->flags |= RATE_INFO_FLAGS_MCS;
+			rate->bw = RATE_INFO_BW_20;
 			if (tx_htinfo & BIT(1))
-				rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+				rate->bw = RATE_INFO_BW_40;
 			if (tx_htinfo & BIT(2))
 				rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
 		}
@@ -910,10 +1198,10 @@
 {
 	u32 rate;
 
-	sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES |
-			STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS |
-			STATION_INFO_TX_BITRATE |
-			STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
+	sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES) |
+			BIT(NL80211_STA_INFO_RX_PACKETS) | BIT(NL80211_STA_INFO_TX_PACKETS) |
+			BIT(NL80211_STA_INFO_TX_BITRATE) |
+			BIT(NL80211_STA_INFO_SIGNAL) | BIT(NL80211_STA_INFO_SIGNAL_AVG);
 
 	/* Get signal information from the firmware */
 	if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO,
@@ -944,7 +1232,7 @@
 	sinfo->txrate.legacy = rate * 5;
 
 	if (priv->bss_mode == NL80211_IFTYPE_STATION) {
-		sinfo->filled |= STATION_INFO_BSS_PARAM;
+		sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
 		sinfo->bss_param.flags = 0;
 		if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap &
 						WLAN_CAPABILITY_SHORT_PREAMBLE)
@@ -1037,10 +1325,11 @@
 	survey->channel = ieee80211_get_channel(wiphy,
 	    ieee80211_channel_to_frequency(pchan_stats[idx].chan_num, band));
 	survey->filled = SURVEY_INFO_NOISE_DBM |
-		SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY;
+			 SURVEY_INFO_TIME |
+			 SURVEY_INFO_TIME_BUSY;
 	survey->noise = pchan_stats[idx].noise;
-	survey->channel_time = pchan_stats[idx].cca_scan_dur;
-	survey->channel_time_busy = pchan_stats[idx].cca_busy_dur;
+	survey->time = pchan_stats[idx].cca_scan_dur;
+	survey->time_busy = pchan_stats[idx].cca_busy_dur;
 
 	return 0;
 }
@@ -1307,7 +1596,7 @@
 
 	wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, params->mac);
 
-	memset(deauth_mac, 0, ETH_ALEN);
+	eth_zero_addr(deauth_mac);
 
 	spin_lock_irqsave(&priv->sta_list_spinlock, flags);
 	sta_node = mwifiex_get_sta_entry(priv, params->mac);
@@ -1395,10 +1684,13 @@
 {
 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 
+	mwifiex_abort_cac(priv);
+
 	if (mwifiex_del_mgmt_ies(priv))
 		wiphy_err(wiphy, "Failed to delete mgmt IEs!\n");
 
 	priv->ap_11n_enabled = 0;
+	memset(&priv->bss_cfg, 0, sizeof(priv->bss_cfg));
 
 	if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
 			     HostCmd_ACT_GEN_SET, 0, NULL, true)) {
@@ -1420,12 +1712,9 @@
 {
 	struct mwifiex_uap_bss_param *bss_cfg;
 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-	u8 config_bands = 0;
 
 	if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP)
 		return -1;
-	if (mwifiex_set_mgmt_ies(priv, &params->beacon))
-		return -1;
 
 	bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL);
 	if (!bss_cfg)
@@ -1442,6 +1731,11 @@
 		memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len);
 		bss_cfg->ssid.ssid_len = params->ssid_len;
 	}
+	if (params->inactivity_timeout > 0) {
+		/* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */
+		bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout;
+		bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout;
+	}
 
 	switch (params->hidden_ssid) {
 	case NL80211_HIDDEN_SSID_NOT_IN_USE:
@@ -1457,33 +1751,8 @@
 		return -EINVAL;
 	}
 
-	bss_cfg->channel = ieee80211_frequency_to_channel(
-				params->chandef.chan->center_freq);
-
-	/* Set appropriate bands */
-	if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) {
-		bss_cfg->band_cfg = BAND_CONFIG_BG;
-		config_bands = BAND_B | BAND_G;
-
-		if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
-			config_bands |= BAND_GN;
-	} else {
-		bss_cfg->band_cfg = BAND_CONFIG_A;
-		config_bands = BAND_A;
-
-		if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
-			config_bands |= BAND_AN;
-
-		if (params->chandef.width > NL80211_CHAN_WIDTH_40)
-			config_bands |= BAND_AAC;
-	}
-
-	if (!((config_bands | priv->adapter->fw_bands) &
-	      ~priv->adapter->fw_bands))
-		priv->adapter->config_bands = config_bands;
-
+	mwifiex_uap_set_channel(bss_cfg, params->chandef);
 	mwifiex_set_uap_rates(bss_cfg, params);
-	mwifiex_send_domain_info_cmd_fw(wiphy);
 
 	if (mwifiex_set_secure_params(priv, bss_cfg, params)) {
 		kfree(bss_cfg);
@@ -1506,45 +1775,29 @@
 
 	mwifiex_set_wmm_params(priv, bss_cfg, params);
 
-	if (params->inactivity_timeout > 0) {
-		/* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */
-		bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout;
-		bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout;
+	if (mwifiex_is_11h_active(priv) &&
+	    !cfg80211_chandef_dfs_required(wiphy, &params->chandef,
+					   priv->bss_mode)) {
+		dev_dbg(priv->adapter->dev, "Disable 11h extensions in FW\n");
+		if (mwifiex_11h_activate(priv, false)) {
+			dev_err(priv->adapter->dev,
+				"Failed to disable 11h extensions!!");
+			return -1;
+		}
+		priv->state_11h.is_11h_active = true;
 	}
 
-	if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
-			     HostCmd_ACT_GEN_SET, 0, NULL, true)) {
-		wiphy_err(wiphy, "Failed to stop the BSS\n");
+	if (mwifiex_config_start_uap(priv, bss_cfg)) {
+		wiphy_err(wiphy, "Failed to start AP\n");
 		kfree(bss_cfg);
 		return -1;
 	}
 
-	if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
-			     HostCmd_ACT_GEN_SET,
-			     UAP_BSS_PARAMS_I, bss_cfg, false)) {
-		wiphy_err(wiphy, "Failed to set the SSID\n");
-		kfree(bss_cfg);
+	if (mwifiex_set_mgmt_ies(priv, &params->beacon))
 		return -1;
-	}
 
+	memcpy(&priv->bss_cfg, bss_cfg, sizeof(priv->bss_cfg));
 	kfree(bss_cfg);
-
-	if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START,
-			     HostCmd_ACT_GEN_SET, 0, NULL, false)) {
-		wiphy_err(wiphy, "Failed to start the BSS\n");
-		return -1;
-	}
-
-	if (priv->sec_info.wep_enabled)
-		priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
-	else
-		priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
-
-	if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
-			     HostCmd_ACT_GEN_SET, 0,
-			     &priv->curr_pkt_filter, true))
-		return -1;
-
 	return 0;
 }
 
@@ -1566,7 +1819,7 @@
 	wiphy_dbg(wiphy, "info: successfully disconnected from %pM:"
 		" reason code %d\n", priv->cfg_bssid, reason_code);
 
-	memset(priv->cfg_bssid, 0, ETH_ALEN);
+	eth_zero_addr(priv->cfg_bssid);
 	priv->hs2_enabled = false;
 
 	return 0;
@@ -1603,15 +1856,15 @@
 	ie_len = ie_buf[1] + sizeof(struct ieee_types_header);
 
 	band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
-	chan = __ieee80211_get_channel(priv->wdev->wiphy,
+	chan = __ieee80211_get_channel(priv->wdev.wiphy,
 			ieee80211_channel_to_frequency(bss_info.bss_chan,
 						       band));
 
-	bss = cfg80211_inform_bss(priv->wdev->wiphy, chan,
+	bss = cfg80211_inform_bss(priv->wdev.wiphy, chan,
 				  CFG80211_BSS_FTYPE_UNKNOWN,
 				  bss_info.bssid, 0, WLAN_CAPABILITY_IBSS,
 				  0, ie_buf, ie_len, 0, GFP_KERNEL);
-	cfg80211_put_bss(priv->wdev->wiphy, bss);
+	cfg80211_put_bss(priv->wdev.wiphy, bss);
 	memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN);
 
 	return 0;
@@ -1732,15 +1985,15 @@
 
 		/* Find the BSS we want using available scan results */
 		if (mode == NL80211_IFTYPE_ADHOC)
-			bss = cfg80211_get_bss(priv->wdev->wiphy, channel,
+			bss = cfg80211_get_bss(priv->wdev.wiphy, channel,
 					       bssid, ssid, ssid_len,
-					       WLAN_CAPABILITY_IBSS,
-					       WLAN_CAPABILITY_IBSS);
+					       IEEE80211_BSS_TYPE_IBSS,
+					       IEEE80211_PRIVACY_ANY);
 		else
-			bss = cfg80211_get_bss(priv->wdev->wiphy, channel,
+			bss = cfg80211_get_bss(priv->wdev.wiphy, channel,
 					       bssid, ssid, ssid_len,
-					       WLAN_CAPABILITY_ESS,
-					       WLAN_CAPABILITY_ESS);
+					       IEEE80211_BSS_TYPE_ESS,
+					       IEEE80211_PRIVACY_ANY);
 
 		if (!bss) {
 			if (is_scanning_required) {
@@ -1784,6 +2037,7 @@
 			 struct cfg80211_connect_params *sme)
 {
 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+	struct mwifiex_adapter *adapter = priv->adapter;
 	int ret;
 
 	if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) {
@@ -1793,11 +2047,18 @@
 		return -EINVAL;
 	}
 
-	if (priv->wdev && priv->wdev->current_bss) {
+	if (priv->wdev.current_bss) {
 		wiphy_warn(wiphy, "%s: already connected\n", dev->name);
 		return -EALREADY;
 	}
 
+	if (adapter->surprise_removed || adapter->is_cmd_timedout) {
+		wiphy_err(wiphy,
+			  "%s: Ignore connection. Card removed or FW in bad state\n",
+			  dev->name);
+		return -EFAULT;
+	}
+
 	wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n",
 		  (char *) sme->ssid, sme->bssid);
 
@@ -1818,7 +2079,7 @@
 		dev_dbg(priv->adapter->dev,
 			"info: association to bssid %pM failed\n",
 			priv->cfg_bssid);
-		memset(priv->cfg_bssid, 0, ETH_ALEN);
+		eth_zero_addr(priv->cfg_bssid);
 
 		if (ret > 0)
 			cfg80211_connect_result(priv->netdev, priv->cfg_bssid,
@@ -1844,7 +2105,7 @@
 static int mwifiex_set_ibss_params(struct mwifiex_private *priv,
 				   struct cfg80211_ibss_params *params)
 {
-	struct wiphy *wiphy = priv->wdev->wiphy;
+	struct wiphy *wiphy = priv->wdev.wiphy;
 	struct mwifiex_adapter *adapter = priv->adapter;
 	int index = 0, i;
 	u8 config_bands = 0;
@@ -1966,7 +2227,7 @@
 	if (mwifiex_deauthenticate(priv, NULL))
 		return -EFAULT;
 
-	memset(priv->cfg_bssid, 0, ETH_ALEN);
+	eth_zero_addr(priv->cfg_bssid);
 
 	return 0;
 }
@@ -2170,10 +2431,11 @@
 }
 
 /*
- *  create a new virtual interface with the given name
+ *  create a new virtual interface with the given name and name assign type
  */
 struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
 					      const char *name,
+					      unsigned char name_assign_type,
 					      enum nl80211_iftype type,
 					      u32 *flags,
 					      struct vif_params *params)
@@ -2182,7 +2444,6 @@
 	struct mwifiex_private *priv;
 	struct net_device *dev;
 	void *mdev_priv;
-	struct wireless_dev *wdev;
 
 	if (!adapter)
 		return ERR_PTR(-EFAULT);
@@ -2191,20 +2452,22 @@
 	case NL80211_IFTYPE_UNSPECIFIED:
 	case NL80211_IFTYPE_STATION:
 	case NL80211_IFTYPE_ADHOC:
-		priv = adapter->priv[MWIFIEX_BSS_TYPE_STA];
-		if (priv->bss_mode) {
+		if (adapter->curr_iface_comb.sta_intf ==
+		    adapter->iface_limit.sta_intf) {
 			wiphy_err(wiphy,
 				  "cannot create multiple sta/adhoc ifaces\n");
 			return ERR_PTR(-EINVAL);
 		}
 
-		wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
-		if (!wdev)
-			return ERR_PTR(-ENOMEM);
+		priv = mwifiex_get_unused_priv(adapter);
+		if (!priv) {
+			wiphy_err(wiphy,
+				  "could not get free private struct\n");
+			return ERR_PTR(-EFAULT);
+		}
 
-		wdev->wiphy = wiphy;
-		priv->wdev = wdev;
-		wdev->iftype = NL80211_IFTYPE_STATION;
+		priv->wdev.wiphy = wiphy;
+		priv->wdev.iftype = NL80211_IFTYPE_STATION;
 
 		if (type == NL80211_IFTYPE_UNSPECIFIED)
 			priv->bss_mode = NL80211_IFTYPE_STATION;
@@ -2219,20 +2482,22 @@
 
 		break;
 	case NL80211_IFTYPE_AP:
-		priv = adapter->priv[MWIFIEX_BSS_TYPE_UAP];
-
-		if (priv->bss_mode) {
-			wiphy_err(wiphy, "Can't create multiple AP interfaces");
+		if (adapter->curr_iface_comb.uap_intf ==
+		    adapter->iface_limit.uap_intf) {
+			wiphy_err(wiphy,
+				  "cannot create multiple AP ifaces\n");
 			return ERR_PTR(-EINVAL);
 		}
 
-		wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
-		if (!wdev)
-			return ERR_PTR(-ENOMEM);
+		priv = mwifiex_get_unused_priv(adapter);
+		if (!priv) {
+			wiphy_err(wiphy,
+				  "could not get free private struct\n");
+			return ERR_PTR(-EFAULT);
+		}
 
-		priv->wdev = wdev;
-		wdev->wiphy = wiphy;
-		wdev->iftype = NL80211_IFTYPE_AP;
+		priv->wdev.wiphy = wiphy;
+		priv->wdev.iftype = NL80211_IFTYPE_AP;
 
 		priv->bss_type = MWIFIEX_BSS_TYPE_UAP;
 		priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
@@ -2244,24 +2509,25 @@
 
 		break;
 	case NL80211_IFTYPE_P2P_CLIENT:
-		priv = adapter->priv[MWIFIEX_BSS_TYPE_P2P];
-
-		if (priv->bss_mode) {
-			wiphy_err(wiphy, "Can't create multiple P2P ifaces");
+		if (adapter->curr_iface_comb.p2p_intf ==
+		    adapter->iface_limit.p2p_intf) {
+			wiphy_err(wiphy,
+				  "cannot create multiple P2P ifaces\n");
 			return ERR_PTR(-EINVAL);
 		}
 
-		wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
-		if (!wdev)
-			return ERR_PTR(-ENOMEM);
+		priv = mwifiex_get_unused_priv(adapter);
+		if (!priv) {
+			wiphy_err(wiphy,
+				  "could not get free private struct\n");
+			return ERR_PTR(-EFAULT);
+		}
 
-		priv->wdev = wdev;
-		wdev->wiphy = wiphy;
-
+		priv->wdev.wiphy = wiphy;
 		/* At start-up, wpa_supplicant tries to change the interface
 		 * to NL80211_IFTYPE_STATION if it is not managed mode.
 		 */
-		wdev->iftype = NL80211_IFTYPE_P2P_CLIENT;
+		priv->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT;
 		priv->bss_mode = NL80211_IFTYPE_P2P_CLIENT;
 
 		/* Setting bss_type to P2P tells firmware that this interface
@@ -2277,8 +2543,9 @@
 		priv->bss_num = 0;
 
 		if (mwifiex_cfg80211_init_p2p_client(priv)) {
-			wdev = ERR_PTR(-EFAULT);
-			goto done;
+			memset(&priv->wdev, 0, sizeof(priv->wdev));
+			priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+			return ERR_PTR(-EFAULT);
 		}
 
 		break;
@@ -2288,13 +2555,14 @@
 	}
 
 	dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name,
-			       NET_NAME_UNKNOWN, ether_setup,
+			       name_assign_type, ether_setup,
 			       IEEE80211_NUM_ACS, 1);
 	if (!dev) {
 		wiphy_err(wiphy, "no memory available for netdevice\n");
+		memset(&priv->wdev, 0, sizeof(priv->wdev));
+		priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
 		priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
-		wdev = ERR_PTR(-ENOMEM);
-		goto done;
+		return ERR_PTR(-ENOMEM);
 	}
 
 	mwifiex_init_priv_params(priv, dev);
@@ -2314,7 +2582,7 @@
 			&wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap, priv);
 
 	dev_net_set(dev, wiphy_net(wiphy));
-	dev->ieee80211_ptr = priv->wdev;
+	dev->ieee80211_ptr = &priv->wdev;
 	dev->ieee80211_ptr->iftype = priv->bss_mode;
 	memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN);
 	SET_NETDEV_DEV(dev, wiphy_dev(wiphy));
@@ -2335,10 +2603,43 @@
 		free_netdev(dev);
 		priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
 		priv->netdev = NULL;
-		wdev = ERR_PTR(-EFAULT);
-		goto done;
+		memset(&priv->wdev, 0, sizeof(priv->wdev));
+		priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+		return ERR_PTR(-EFAULT);
 	}
 
+	priv->dfs_cac_workqueue = alloc_workqueue("MWIFIEX_DFS_CAC%s",
+						  WQ_HIGHPRI |
+						  WQ_MEM_RECLAIM |
+						  WQ_UNBOUND, 1, name);
+	if (!priv->dfs_cac_workqueue) {
+		wiphy_err(wiphy, "cannot register virtual network device\n");
+		free_netdev(dev);
+		priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+		priv->netdev = NULL;
+		memset(&priv->wdev, 0, sizeof(priv->wdev));
+		priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+		return ERR_PTR(-ENOMEM);
+	}
+
+	INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue);
+
+	priv->dfs_chan_sw_workqueue = alloc_workqueue("MWIFIEX_DFS_CHSW%s",
+						      WQ_HIGHPRI | WQ_UNBOUND |
+						      WQ_MEM_RECLAIM, 1, name);
+	if (!priv->dfs_chan_sw_workqueue) {
+		wiphy_err(wiphy, "cannot register virtual network device\n");
+		free_netdev(dev);
+		priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+		priv->netdev = NULL;
+		memset(&priv->wdev, 0, sizeof(priv->wdev));
+		priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+		return ERR_PTR(-ENOMEM);
+	}
+
+	INIT_DELAYED_WORK(&priv->dfs_chan_sw_work,
+			  mwifiex_dfs_chan_sw_work_queue);
+
 	sema_init(&priv->async_sem, 1);
 
 	dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name);
@@ -2347,13 +2648,24 @@
 	mwifiex_dev_debugfs_init(priv);
 #endif
 
-done:
-	if (IS_ERR(wdev)) {
-		kfree(priv->wdev);
-		priv->wdev = NULL;
+	switch (type) {
+	case NL80211_IFTYPE_UNSPECIFIED:
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_ADHOC:
+		adapter->curr_iface_comb.sta_intf++;
+		break;
+	case NL80211_IFTYPE_AP:
+		adapter->curr_iface_comb.uap_intf++;
+		break;
+	case NL80211_IFTYPE_P2P_CLIENT:
+		adapter->curr_iface_comb.p2p_intf++;
+		break;
+	default:
+		wiphy_err(wiphy, "type not supported\n");
+		return ERR_PTR(-EINVAL);
 	}
 
-	return wdev;
+	return &priv->wdev;
 }
 EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf);
 
@@ -2363,12 +2675,13 @@
 int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
 {
 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+	struct mwifiex_adapter *adapter = priv->adapter;
 
 #ifdef CONFIG_DEBUG_FS
 	mwifiex_dev_debugfs_remove(priv);
 #endif
 
-	mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
+	mwifiex_stop_net_dev_queue(priv->netdev, adapter);
 
 	if (netif_carrier_ok(priv->netdev))
 		netif_carrier_off(priv->netdev);
@@ -2376,16 +2689,48 @@
 	if (wdev->netdev->reg_state == NETREG_REGISTERED)
 		unregister_netdevice(wdev->netdev);
 
+	if (priv->dfs_cac_workqueue) {
+		flush_workqueue(priv->dfs_cac_workqueue);
+		destroy_workqueue(priv->dfs_cac_workqueue);
+		priv->dfs_cac_workqueue = NULL;
+	}
+
+	if (priv->dfs_chan_sw_workqueue) {
+		flush_workqueue(priv->dfs_chan_sw_workqueue);
+		destroy_workqueue(priv->dfs_chan_sw_workqueue);
+		priv->dfs_chan_sw_workqueue = NULL;
+	}
 	/* Clear the priv in adapter */
 	priv->netdev->ieee80211_ptr = NULL;
 	priv->netdev = NULL;
-	kfree(wdev);
-	priv->wdev = NULL;
+	priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
 
 	priv->media_connected = false;
 
+	switch (priv->bss_mode) {
+	case NL80211_IFTYPE_UNSPECIFIED:
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_ADHOC:
+		adapter->curr_iface_comb.sta_intf++;
+		break;
+	case NL80211_IFTYPE_AP:
+		adapter->curr_iface_comb.uap_intf++;
+		break;
+	case NL80211_IFTYPE_P2P_CLIENT:
+	case NL80211_IFTYPE_P2P_GO:
+		adapter->curr_iface_comb.p2p_intf++;
+		break;
+	default:
+		dev_err(adapter->dev, "del_virtual_intf: type not supported\n");
+		break;
+	}
+
 	priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
 
+	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
+	    GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
+		kfree(priv->hist_data);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
@@ -2421,47 +2766,80 @@
 }
 
 #ifdef CONFIG_PM
-static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
-				    struct cfg80211_wowlan *wowlan)
+static void mwifiex_set_auto_arp_mef_entry(struct mwifiex_private *priv,
+					   struct mwifiex_mef_entry *mef_entry)
 {
-	struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
-	struct mwifiex_ds_mef_cfg mef_cfg;
-	struct mwifiex_mef_entry *mef_entry;
-	int i, filt_num = 0, ret;
+	int i, filt_num = 0, num_ipv4 = 0;
+	struct in_device *in_dev;
+	struct in_ifaddr *ifa;
+	__be32 ips[MWIFIEX_MAX_SUPPORTED_IPADDR];
+	struct mwifiex_adapter *adapter = priv->adapter;
+
+	mef_entry->mode = MEF_MODE_HOST_SLEEP;
+	mef_entry->action = MEF_ACTION_AUTO_ARP;
+
+	/* Enable ARP offload feature */
+	memset(ips, 0, sizeof(ips));
+	for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) {
+		if (adapter->priv[i]->netdev) {
+			in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev);
+			if (!in_dev)
+				continue;
+			ifa = in_dev->ifa_list;
+			if (!ifa || !ifa->ifa_local)
+				continue;
+			ips[i] = ifa->ifa_local;
+			num_ipv4++;
+		}
+	}
+
+	for (i = 0; i < num_ipv4; i++) {
+		if (!ips[i])
+			continue;
+		mef_entry->filter[filt_num].repeat = 1;
+		memcpy(mef_entry->filter[filt_num].byte_seq,
+		       (u8 *)&ips[i], sizeof(ips[i]));
+		mef_entry->filter[filt_num].
+			byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
+			sizeof(ips[i]);
+		mef_entry->filter[filt_num].offset = 46;
+		mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+		if (filt_num) {
+			mef_entry->filter[filt_num].filt_action =
+				TYPE_OR;
+		}
+		filt_num++;
+	}
+
+	mef_entry->filter[filt_num].repeat = 1;
+	mef_entry->filter[filt_num].byte_seq[0] = 0x08;
+	mef_entry->filter[filt_num].byte_seq[1] = 0x06;
+	mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = 2;
+	mef_entry->filter[filt_num].offset = 20;
+	mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+	mef_entry->filter[filt_num].filt_action = TYPE_AND;
+}
+
+static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv,
+					struct mwifiex_ds_mef_cfg *mef_cfg,
+					struct mwifiex_mef_entry *mef_entry,
+					struct cfg80211_wowlan *wowlan)
+{
+	int i, filt_num = 0, ret = 0;
 	bool first_pat = true;
 	u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1];
 	const u8 ipv4_mc_mac[] = {0x33, 0x33};
 	const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
-	struct mwifiex_private *priv =
-			mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
 
-	if (!wowlan) {
-		dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n");
-		return 0;
-	}
-
-	if (!priv->media_connected) {
-		dev_warn(adapter->dev,
-			 "Can not configure WOWLAN in disconnected state\n");
-		return 0;
-	}
-
-	mef_entry = kzalloc(sizeof(*mef_entry), GFP_KERNEL);
-	if (!mef_entry)
-		return -ENOMEM;
-
-	memset(&mef_cfg, 0, sizeof(mef_cfg));
-	mef_cfg.num_entries = 1;
-	mef_cfg.mef_entry = mef_entry;
 	mef_entry->mode = MEF_MODE_HOST_SLEEP;
 	mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST;
 
 	for (i = 0; i < wowlan->n_patterns; i++) {
 		memset(byte_seq, 0, sizeof(byte_seq));
 		if (!mwifiex_is_pattern_supported(&wowlan->patterns[i],
-						  byte_seq,
-						  MWIFIEX_MEF_MAX_BYTESEQ)) {
-			wiphy_err(wiphy, "Pattern not supported\n");
+					byte_seq,
+					MWIFIEX_MEF_MAX_BYTESEQ)) {
+			dev_err(priv->adapter->dev, "Pattern not supported\n");
 			kfree(mef_entry);
 			return -EOPNOTSUPP;
 		}
@@ -2469,25 +2847,24 @@
 		if (!wowlan->patterns[i].pkt_offset) {
 			if (!(byte_seq[0] & 0x01) &&
 			    (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) {
-				mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
+				mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST;
 				continue;
 			} else if (is_broadcast_ether_addr(byte_seq)) {
-				mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST;
+				mef_cfg->criteria |= MWIFIEX_CRITERIA_BROADCAST;
 				continue;
 			} else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
 				    (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) ||
 				   (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
 				    (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) {
-				mef_cfg.criteria |= MWIFIEX_CRITERIA_MULTICAST;
+				mef_cfg->criteria |= MWIFIEX_CRITERIA_MULTICAST;
 				continue;
 			}
 		}
-
 		mef_entry->filter[filt_num].repeat = 1;
 		mef_entry->filter[filt_num].offset =
-						wowlan->patterns[i].pkt_offset;
+			wowlan->patterns[i].pkt_offset;
 		memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq,
-		       sizeof(byte_seq));
+				sizeof(byte_seq));
 		mef_entry->filter[filt_num].filt_type = TYPE_EQ;
 
 		if (first_pat)
@@ -2499,12 +2876,12 @@
 	}
 
 	if (wowlan->magic_pkt) {
-		mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
+		mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST;
 		mef_entry->filter[filt_num].repeat = 16;
 		memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
-		       ETH_ALEN);
+				ETH_ALEN);
 		mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
-								ETH_ALEN;
+			ETH_ALEN;
 		mef_entry->filter[filt_num].offset = 28;
 		mef_entry->filter[filt_num].filt_type = TYPE_EQ;
 		if (filt_num)
@@ -2513,26 +2890,105 @@
 		filt_num++;
 		mef_entry->filter[filt_num].repeat = 16;
 		memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
-		       ETH_ALEN);
+				ETH_ALEN);
 		mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
-								ETH_ALEN;
+			ETH_ALEN;
 		mef_entry->filter[filt_num].offset = 56;
 		mef_entry->filter[filt_num].filt_type = TYPE_EQ;
 		mef_entry->filter[filt_num].filt_action = TYPE_OR;
 	}
+	return ret;
+}
+
+static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
+				  struct cfg80211_wowlan *wowlan)
+{
+	int ret = 0, num_entries = 1;
+	struct mwifiex_ds_mef_cfg mef_cfg;
+	struct mwifiex_mef_entry *mef_entry;
+
+	if (wowlan->n_patterns || wowlan->magic_pkt)
+		num_entries++;
+
+	mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL);
+	if (!mef_entry)
+		return -ENOMEM;
+
+	memset(&mef_cfg, 0, sizeof(mef_cfg));
+	mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST |
+		MWIFIEX_CRITERIA_UNICAST;
+	mef_cfg.num_entries = num_entries;
+	mef_cfg.mef_entry = mef_entry;
+
+	mwifiex_set_auto_arp_mef_entry(priv, &mef_entry[0]);
+
+	if (wowlan->n_patterns || wowlan->magic_pkt)
+		ret = mwifiex_set_wowlan_mef_entry(priv, &mef_cfg,
+						   &mef_entry[1], wowlan);
 
 	if (!mef_cfg.criteria)
 		mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST |
-				   MWIFIEX_CRITERIA_UNICAST |
-				   MWIFIEX_CRITERIA_MULTICAST;
+			MWIFIEX_CRITERIA_UNICAST |
+			MWIFIEX_CRITERIA_MULTICAST;
 
 	ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG,
-			       HostCmd_ACT_GEN_SET, 0, &mef_cfg, true);
-
+			HostCmd_ACT_GEN_SET, 0,
+			&mef_cfg, true);
 	kfree(mef_entry);
 	return ret;
 }
 
+static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
+				    struct cfg80211_wowlan *wowlan)
+{
+	struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+	struct mwifiex_ds_hs_cfg hs_cfg;
+	int i, ret = 0;
+	struct mwifiex_private *priv;
+
+	for (i = 0; i < adapter->priv_num; i++) {
+		priv = adapter->priv[i];
+		mwifiex_abort_cac(priv);
+	}
+
+	mwifiex_cancel_all_pending_cmd(adapter);
+
+	if (!wowlan) {
+		dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n");
+		return 0;
+	}
+
+	priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
+
+	if (!priv->media_connected) {
+		dev_warn(adapter->dev,
+			 "Can not configure WOWLAN in disconnected state\n");
+		return 0;
+	}
+
+	ret = mwifiex_set_mef_filter(priv, wowlan);
+	if (ret) {
+		dev_err(adapter->dev, "Failed to set MEF filter\n");
+		return ret;
+	}
+
+	if (wowlan->disconnect) {
+		memset(&hs_cfg, 0, sizeof(hs_cfg));
+		hs_cfg.is_invoke_hostcmd = false;
+		hs_cfg.conditions = HS_CFG_COND_MAC_EVENT;
+		hs_cfg.gpio = HS_CFG_GPIO_DEF;
+		hs_cfg.gap = HS_CFG_GAP_DEF;
+		ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
+					    MWIFIEX_SYNC_CMD, &hs_cfg);
+		if (ret) {
+			dev_err(adapter->dev, "Failed to set HS params\n");
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
 static int mwifiex_cfg80211_resume(struct wiphy *wiphy)
 {
 	return 0;
@@ -2803,6 +3259,102 @@
 }
 
 static int
+mwifiex_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+				struct cfg80211_csa_settings *params)
+{
+	struct ieee_types_header *chsw_ie;
+	struct ieee80211_channel_sw_ie *channel_sw;
+	int chsw_msec;
+	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+	if (priv->adapter->scan_processing) {
+		dev_err(priv->adapter->dev,
+			"radar detection: scan in process...\n");
+		return -EBUSY;
+	}
+
+	if (priv->wdev.cac_started)
+		return -EBUSY;
+
+	if (cfg80211_chandef_identical(&params->chandef,
+				       &priv->dfs_chandef))
+		return -EINVAL;
+
+	chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH,
+					   params->beacon_csa.tail,
+					   params->beacon_csa.tail_len);
+	if (!chsw_ie) {
+		dev_err(priv->adapter->dev,
+			"Could not parse channel switch announcement IE\n");
+		return -EINVAL;
+	}
+
+	channel_sw = (void *)(chsw_ie + 1);
+	if (channel_sw->mode) {
+		if (netif_carrier_ok(priv->netdev))
+			netif_carrier_off(priv->netdev);
+		mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
+	}
+
+	if (mwifiex_del_mgmt_ies(priv))
+		wiphy_err(wiphy, "Failed to delete mgmt IEs!\n");
+
+	if (mwifiex_set_mgmt_ies(priv, &params->beacon_csa)) {
+		wiphy_err(wiphy, "%s: setting mgmt ies failed\n", __func__);
+		return -EFAULT;
+	}
+
+	memcpy(&priv->dfs_chandef, &params->chandef, sizeof(priv->dfs_chandef));
+	memcpy(&priv->beacon_after, &params->beacon_after,
+	       sizeof(priv->beacon_after));
+
+	chsw_msec = max(channel_sw->count * priv->bss_cfg.beacon_period, 100);
+	queue_delayed_work(priv->dfs_chan_sw_workqueue, &priv->dfs_chan_sw_work,
+			   msecs_to_jiffies(chsw_msec));
+	return 0;
+}
+
+static int
+mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy,
+				       struct net_device *dev,
+				       struct cfg80211_chan_def *chandef,
+				       u32 cac_time_ms)
+{
+	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+	struct mwifiex_radar_params radar_params;
+
+	if (priv->adapter->scan_processing) {
+		dev_err(priv->adapter->dev,
+			"radar detection: scan already in process...\n");
+		return -EBUSY;
+	}
+
+	if (!mwifiex_is_11h_active(priv)) {
+		dev_dbg(priv->adapter->dev, "Enable 11h extensions in FW\n");
+		if (mwifiex_11h_activate(priv, true)) {
+			dev_err(priv->adapter->dev,
+				"Failed to activate 11h extensions!!");
+			return -1;
+		}
+		priv->state_11h.is_11h_active = true;
+	}
+
+	memset(&radar_params, 0, sizeof(struct mwifiex_radar_params));
+	radar_params.chandef = chandef;
+	radar_params.cac_time_ms = cac_time_ms;
+
+	memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef));
+
+	if (mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST,
+			     HostCmd_ACT_GEN_SET, 0, &radar_params, true))
+		return -1;
+
+	queue_delayed_work(priv->dfs_cac_workqueue, &priv->dfs_cac_work,
+			   msecs_to_jiffies(cac_time_ms));
+	return 0;
+}
+
+static int
 mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
 				const u8 *mac,
 				struct station_parameters *params)
@@ -2866,11 +3418,13 @@
 	.tdls_oper = mwifiex_cfg80211_tdls_oper,
 	.add_station = mwifiex_cfg80211_add_station,
 	.change_station = mwifiex_cfg80211_change_station,
+	.start_radar_detection = mwifiex_cfg80211_start_radar_detection,
+	.channel_switch = mwifiex_cfg80211_channel_switch,
 };
 
 #ifdef CONFIG_PM
 static const struct wiphy_wowlan_support mwifiex_wowlan_support = {
-	.flags = WIPHY_WOWLAN_MAGIC_PKT,
+	.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
 	.n_patterns = MWIFIEX_MEF_MAX_FILTERS,
 	.pattern_min_len = 1,
 	.pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
@@ -2964,12 +3518,13 @@
 	wiphy->cipher_suites = mwifiex_cipher_suites;
 	wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites);
 
-	memcpy(wiphy->perm_addr, priv->curr_addr, ETH_ALEN);
+	ether_addr_copy(wiphy->perm_addr, adapter->perm_addr);
 	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
 	wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
 			WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
 			WIPHY_FLAG_AP_UAPSD |
-			WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+			WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+			WIPHY_FLAG_HAS_CHANNEL_SWITCH;
 
 	if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info))
 		wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c
index b8242eb..e9df882 100644
--- a/drivers/net/wireless/mwifiex/cfp.c
+++ b/drivers/net/wireless/mwifiex/cfp.c
@@ -322,9 +322,9 @@
 		return cfp;
 
 	if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG)
-		sband = priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ];
+		sband = priv->wdev.wiphy->bands[IEEE80211_BAND_2GHZ];
 	else
-		sband = priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ];
+		sband = priv->wdev.wiphy->bands[IEEE80211_BAND_5GHZ];
 
 	if (!sband) {
 		dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d\n",
@@ -509,3 +509,21 @@
 
 	return k;
 }
+
+u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv,
+			    u8 rx_rate, u8 rate_info)
+{
+	u8 rate_index = 0;
+
+	/* HT40 */
+	if ((rate_info & BIT(0)) && (rate_info & BIT(1)))
+		rate_index = MWIFIEX_RATE_INDEX_MCS0 +
+			     MWIFIEX_BW20_MCS_NUM + rx_rate;
+	else if (rate_info & BIT(0)) /* HT20 */
+		rate_index = MWIFIEX_RATE_INDEX_MCS0 + rx_rate;
+	else
+		rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ?
+			      rx_rate - 1 : rx_rate;
+
+	return rate_index;
+}
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index 8559720..c5a14ff 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -315,22 +315,19 @@
 		adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++;
 		return -1;
 	}
-	if (GET_BSS_ROLE(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY))
-	    == MWIFIEX_BSS_ROLE_STA) {
-		if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl))
-			/* Response is not needed for sleep
-			   confirm command */
-			adapter->ps_state = PS_STATE_SLEEP;
-		else
-			adapter->ps_state = PS_STATE_SLEEP_CFM;
 
-		if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) &&
-		    (adapter->is_hs_configured &&
-		     !adapter->sleep_period.period)) {
-			adapter->pm_wakeup_card_req = true;
-			mwifiex_hs_activated_event(mwifiex_get_priv
-					(adapter, MWIFIEX_BSS_ROLE_STA), true);
-		}
+	if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl))
+		/* Response is not needed for sleep confirm command */
+		adapter->ps_state = PS_STATE_SLEEP;
+	else
+		adapter->ps_state = PS_STATE_SLEEP_CFM;
+
+	if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) &&
+	    (adapter->is_hs_configured &&
+	     !adapter->sleep_period.period)) {
+		adapter->pm_wakeup_card_req = true;
+		mwifiex_hs_activated_event(mwifiex_get_priv
+				(adapter, MWIFIEX_BSS_ROLE_ANY), true);
 	}
 
 	return ret;
@@ -450,6 +447,7 @@
 				      EVENT_GET_BSS_TYPE(eventcause));
 	if (!priv)
 		priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+
 	/* Clear BSS_NO_BITS from event */
 	eventcause &= EVENT_ID_MASK;
 	adapter->event_cause = eventcause;
@@ -462,12 +460,6 @@
 	}
 
 	dev_dbg(adapter->dev, "EVENT: cause: %#x\n", eventcause);
-	if (eventcause == EVENT_PS_SLEEP || eventcause == EVENT_PS_AWAKE) {
-		/* Handle PS_SLEEP/AWAKE events on STA */
-		priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
-		if (!priv)
-			priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
-	}
 
 	if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
 		ret = mwifiex_process_uap_event(priv);
@@ -1008,11 +1000,9 @@
 	list_for_each_entry_safe(cmd_node, tmp_node,
 				 &adapter->scan_pending_q, list) {
 		list_del(&cmd_node->list);
-		spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
 
 		cmd_node->wait_q_enabled = false;
 		mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
-		spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
 	}
 	spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
 
@@ -1070,12 +1060,8 @@
 	list_for_each_entry_safe(cmd_node, tmp_node,
 				 &adapter->scan_pending_q, list) {
 		list_del(&cmd_node->list);
-		spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
-				       scan_pending_q_flags);
 		cmd_node->wait_q_enabled = false;
 		mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
-		spin_lock_irqsave(&adapter->scan_pending_q_lock,
-				  scan_pending_q_flags);
 	}
 	spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
 			       scan_pending_q_flags);
@@ -1588,9 +1574,7 @@
 		le16_to_cpu(hw_spec->hw_if_version),
 		le16_to_cpu(hw_spec->version));
 
-	if (priv->curr_addr[0] == 0xff)
-		memmove(priv->curr_addr, hw_spec->permanent_addr, ETH_ALEN);
-
+	ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr);
 	adapter->region_code = le16_to_cpu(hw_spec->region_code);
 
 	for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++)
diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c
index 2713f7a..1fb329d 100644
--- a/drivers/net/wireless/mwifiex/debugfs.c
+++ b/drivers/net/wireless/mwifiex/debugfs.c
@@ -39,111 +39,6 @@
 	"P2P_DEVICE",
 };
 
-/* size/addr for mwifiex_debug_info */
-#define item_size(n)		(FIELD_SIZEOF(struct mwifiex_debug_info, n))
-#define item_addr(n)		(offsetof(struct mwifiex_debug_info, n))
-
-/* size/addr for struct mwifiex_adapter */
-#define adapter_item_size(n)	(FIELD_SIZEOF(struct mwifiex_adapter, n))
-#define adapter_item_addr(n)	(offsetof(struct mwifiex_adapter, n))
-
-struct mwifiex_debug_data {
-	char name[32];		/* variable/array name */
-	u32 size;		/* size of the variable/array */
-	size_t addr;		/* address of the variable/array */
-	int num;		/* number of variables in an array */
-};
-
-static struct mwifiex_debug_data items[] = {
-	{"int_counter", item_size(int_counter),
-	 item_addr(int_counter), 1},
-	{"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]),
-	 item_addr(packets_out[WMM_AC_VO]), 1},
-	{"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]),
-	 item_addr(packets_out[WMM_AC_VI]), 1},
-	{"wmm_ac_be", item_size(packets_out[WMM_AC_BE]),
-	 item_addr(packets_out[WMM_AC_BE]), 1},
-	{"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]),
-	 item_addr(packets_out[WMM_AC_BK]), 1},
-	{"tx_buf_size", item_size(tx_buf_size),
-	 item_addr(tx_buf_size), 1},
-	{"curr_tx_buf_size", item_size(curr_tx_buf_size),
-	 item_addr(curr_tx_buf_size), 1},
-	{"ps_mode", item_size(ps_mode),
-	 item_addr(ps_mode), 1},
-	{"ps_state", item_size(ps_state),
-	 item_addr(ps_state), 1},
-	{"is_deep_sleep", item_size(is_deep_sleep),
-	 item_addr(is_deep_sleep), 1},
-	{"wakeup_dev_req", item_size(pm_wakeup_card_req),
-	 item_addr(pm_wakeup_card_req), 1},
-	{"wakeup_tries", item_size(pm_wakeup_fw_try),
-	 item_addr(pm_wakeup_fw_try), 1},
-	{"hs_configured", item_size(is_hs_configured),
-	 item_addr(is_hs_configured), 1},
-	{"hs_activated", item_size(hs_activated),
-	 item_addr(hs_activated), 1},
-	{"num_tx_timeout", item_size(num_tx_timeout),
-	 item_addr(num_tx_timeout), 1},
-	{"is_cmd_timedout", item_size(is_cmd_timedout),
-	 item_addr(is_cmd_timedout), 1},
-	{"timeout_cmd_id", item_size(timeout_cmd_id),
-	 item_addr(timeout_cmd_id), 1},
-	{"timeout_cmd_act", item_size(timeout_cmd_act),
-	 item_addr(timeout_cmd_act), 1},
-	{"last_cmd_id", item_size(last_cmd_id),
-	 item_addr(last_cmd_id), DBG_CMD_NUM},
-	{"last_cmd_act", item_size(last_cmd_act),
-	 item_addr(last_cmd_act), DBG_CMD_NUM},
-	{"last_cmd_index", item_size(last_cmd_index),
-	 item_addr(last_cmd_index), 1},
-	{"last_cmd_resp_id", item_size(last_cmd_resp_id),
-	 item_addr(last_cmd_resp_id), DBG_CMD_NUM},
-	{"last_cmd_resp_index", item_size(last_cmd_resp_index),
-	 item_addr(last_cmd_resp_index), 1},
-	{"last_event", item_size(last_event),
-	 item_addr(last_event), DBG_CMD_NUM},
-	{"last_event_index", item_size(last_event_index),
-	 item_addr(last_event_index), 1},
-	{"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure),
-	 item_addr(num_cmd_host_to_card_failure), 1},
-	{"num_cmd_sleep_cfm_fail",
-	 item_size(num_cmd_sleep_cfm_host_to_card_failure),
-	 item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1},
-	{"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure),
-	 item_addr(num_tx_host_to_card_failure), 1},
-	{"num_evt_deauth", item_size(num_event_deauth),
-	 item_addr(num_event_deauth), 1},
-	{"num_evt_disassoc", item_size(num_event_disassoc),
-	 item_addr(num_event_disassoc), 1},
-	{"num_evt_link_lost", item_size(num_event_link_lost),
-	 item_addr(num_event_link_lost), 1},
-	{"num_cmd_deauth", item_size(num_cmd_deauth),
-	 item_addr(num_cmd_deauth), 1},
-	{"num_cmd_assoc_ok", item_size(num_cmd_assoc_success),
-	 item_addr(num_cmd_assoc_success), 1},
-	{"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure),
-	 item_addr(num_cmd_assoc_failure), 1},
-	{"cmd_sent", item_size(cmd_sent),
-	 item_addr(cmd_sent), 1},
-	{"data_sent", item_size(data_sent),
-	 item_addr(data_sent), 1},
-	{"cmd_resp_received", item_size(cmd_resp_received),
-	 item_addr(cmd_resp_received), 1},
-	{"event_received", item_size(event_received),
-	 item_addr(event_received), 1},
-
-	/* variables defined in struct mwifiex_adapter */
-	{"cmd_pending", adapter_item_size(cmd_pending),
-	 adapter_item_addr(cmd_pending), 1},
-	{"tx_pending", adapter_item_size(tx_pending),
-	 adapter_item_addr(tx_pending), 1},
-	{"rx_pending", adapter_item_size(rx_pending),
-	 adapter_item_addr(rx_pending), 1},
-};
-
-static int num_of_items = ARRAY_SIZE(items);
-
 /*
  * Proc info file read handler.
  *
@@ -297,6 +192,8 @@
  *      - Number of FCS errors
  *      - Number of Tx frames
  *      - WEP ICV error counts
+ *      - Number of received beacons
+ *      - Number of missed beacons
  */
 static ssize_t
 mwifiex_getlog_read(struct file *file, char __user *ubuf,
@@ -333,7 +230,9 @@
 		     "wepicverrcnt-1   %u\n"
 		     "wepicverrcnt-2   %u\n"
 		     "wepicverrcnt-3   %u\n"
-		     "wepicverrcnt-4   %u\n",
+		     "wepicverrcnt-4   %u\n"
+		     "bcn_rcv_cnt   %u\n"
+		     "bcn_miss_cnt   %u\n",
 		     stats.mcast_tx_frame,
 		     stats.failed,
 		     stats.retry,
@@ -349,7 +248,9 @@
 		     stats.wep_icv_error[0],
 		     stats.wep_icv_error[1],
 		     stats.wep_icv_error[2],
-		     stats.wep_icv_error[3]);
+		     stats.wep_icv_error[3],
+		     stats.bcn_rcv_cnt,
+		     stats.bcn_miss_cnt);
 
 
 	ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
@@ -360,6 +261,103 @@
 	return ret;
 }
 
+/* Sysfs histogram file read handler.
+ *
+ * This function is called when the 'histogram' file is opened for reading
+ * It prints the following histogram information -
+ *      - Number of histogram samples
+ *      - Receive packet number of each rx_rate
+ *      - Receive packet number of each snr
+ *      - Receive packet number of each nosie_flr
+ *      - Receive packet number of each signal streath
+ */
+static ssize_t
+mwifiex_histogram_read(struct file *file, char __user *ubuf,
+		       size_t count, loff_t *ppos)
+{
+	struct mwifiex_private *priv =
+		(struct mwifiex_private *)file->private_data;
+	ssize_t ret;
+	struct mwifiex_histogram_data *phist_data;
+	int i, value;
+	unsigned long page = get_zeroed_page(GFP_KERNEL);
+	char *p = (char *)page;
+
+	if (!p)
+		return -ENOMEM;
+
+	if (!priv || !priv->hist_data)
+		return -EFAULT;
+	phist_data = priv->hist_data;
+
+	p += sprintf(p, "\n"
+		     "total samples = %d\n",
+		     atomic_read(&phist_data->num_samples));
+
+	p += sprintf(p, "rx rates (in Mbps): 0=1M   1=2M");
+	p += sprintf(p, "2=5.5M  3=11M   4=6M   5=9M  6=12M\n");
+	p += sprintf(p, "7=18M  8=24M  9=36M  10=48M  11=54M");
+	p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n");
+
+	if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
+		p += sprintf(p, "44-53=MCS0-9(VHT:BW20)");
+		p += sprintf(p, "54-63=MCS0-9(VHT:BW40)");
+		p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n");
+	} else {
+		p += sprintf(p, "\n");
+	}
+
+	for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) {
+		value = atomic_read(&phist_data->rx_rate[i]);
+		if (value)
+			p += sprintf(p, "rx_rate[%02d] = %d\n", i, value);
+	}
+
+	if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
+		for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES;
+		     i++) {
+			value = atomic_read(&phist_data->rx_rate[i]);
+			if (value)
+				p += sprintf(p, "rx_rate[%02d] = %d\n",
+					   i, value);
+		}
+	}
+
+	for (i = 0; i < MWIFIEX_MAX_SNR; i++) {
+		value =  atomic_read(&phist_data->snr[i]);
+		if (value)
+			p += sprintf(p, "snr[%02ddB] = %d\n", i, value);
+	}
+	for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) {
+		value = atomic_read(&phist_data->noise_flr[i]);
+		if (value)
+			p += sprintf(p, "noise_flr[-%02ddBm] = %d\n",
+				(int)(i-128), value);
+	}
+	for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) {
+		value = atomic_read(&phist_data->sig_str[i]);
+		if (value)
+			p += sprintf(p, "sig_strength[-%02ddBm] = %d\n",
+				i, value);
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
+				      (unsigned long)p - page);
+
+	return ret;
+}
+
+static ssize_t
+mwifiex_histogram_write(struct file *file, const char __user *ubuf,
+			size_t count, loff_t *ppos)
+{
+	struct mwifiex_private *priv = (void *)file->private_data;
+
+	if (priv && priv->hist_data)
+		mwifiex_hist_data_reset(priv);
+	return 0;
+}
+
 static struct mwifiex_debug_info info;
 
 /*
@@ -415,13 +413,9 @@
 {
 	struct mwifiex_private *priv =
 		(struct mwifiex_private *) file->private_data;
-	struct mwifiex_debug_data *d = &items[0];
 	unsigned long page = get_zeroed_page(GFP_KERNEL);
 	char *p = (char *) page;
 	ssize_t ret;
-	size_t size, addr;
-	long val;
-	int i, j;
 
 	if (!p)
 		return -ENOMEM;
@@ -430,68 +424,7 @@
 	if (ret)
 		goto free_and_exit;
 
-	for (i = 0; i < num_of_items; i++) {
-		p += sprintf(p, "%s=", d[i].name);
-
-		size = d[i].size / d[i].num;
-
-		if (i < (num_of_items - 3))
-			addr = d[i].addr + (size_t) &info;
-		else /* The last 3 items are struct mwifiex_adapter variables */
-			addr = d[i].addr + (size_t) priv->adapter;
-
-		for (j = 0; j < d[i].num; j++) {
-			switch (size) {
-			case 1:
-				val = *((u8 *) addr);
-				break;
-			case 2:
-				val = *((u16 *) addr);
-				break;
-			case 4:
-				val = *((u32 *) addr);
-				break;
-			case 8:
-				val = *((long long *) addr);
-				break;
-			default:
-				val = -1;
-				break;
-			}
-
-			p += sprintf(p, "%#lx ", val);
-			addr += size;
-		}
-
-		p += sprintf(p, "\n");
-	}
-
-	if (info.tx_tbl_num) {
-		p += sprintf(p, "Tx BA stream table:\n");
-		for (i = 0; i < info.tx_tbl_num; i++)
-			p += sprintf(p, "tid = %d, ra = %pM\n",
-				     info.tx_tbl[i].tid, info.tx_tbl[i].ra);
-	}
-
-	if (info.rx_tbl_num) {
-		p += sprintf(p, "Rx reorder table:\n");
-		for (i = 0; i < info.rx_tbl_num; i++) {
-			p += sprintf(p, "tid = %d, ta = %pM, "
-				     "start_win = %d, "
-				     "win_size = %d, buffer: ",
-				     info.rx_tbl[i].tid,
-				     info.rx_tbl[i].ta,
-				     info.rx_tbl[i].start_win,
-				     info.rx_tbl[i].win_size);
-
-			for (j = 0; j < info.rx_tbl[i].win_size; j++)
-				p += sprintf(p, "%c ",
-					     info.rx_tbl[i].buffer[j] ?
-					     '1' : '0');
-
-			p += sprintf(p, "\n");
-		}
-	}
+	p += mwifiex_debug_info_to_buffer(priv, p, &info);
 
 	ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
 				      (unsigned long) p - page);
@@ -817,6 +750,7 @@
 MWIFIEX_DFS_FILE_OPS(regrdwr);
 MWIFIEX_DFS_FILE_OPS(rdeeprom);
 MWIFIEX_DFS_FILE_OPS(hscfg);
+MWIFIEX_DFS_FILE_OPS(histogram);
 
 /*
  * This function creates the debug FS directory structure and the files.
@@ -840,6 +774,7 @@
 	MWIFIEX_DFS_ADD_FILE(rdeeprom);
 	MWIFIEX_DFS_ADD_FILE(fw_dump);
 	MWIFIEX_DFS_ADD_FILE(hscfg);
+	MWIFIEX_DFS_ADD_FILE(histogram);
 }
 
 /*
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
index 343240a..e16e564 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/mwifiex/decl.h
@@ -32,15 +32,20 @@
 
 #define MWIFIEX_MAX_BSS_NUM         (3)
 
-#define MWIFIEX_MIN_DATA_HEADER_LEN 36	/* sizeof(mwifiex_txpd)
-					 *   + 4 byte alignment
-					 */
+#define MWIFIEX_DMA_ALIGN_SZ	    64
+#define MWIFIEX_RX_HEADROOM	    64
+#define MAX_TXPD_SZ		    32
+#define INTF_HDR_ALIGN		     4
+
+#define MWIFIEX_MIN_DATA_HEADER_LEN (MWIFIEX_DMA_ALIGN_SZ + INTF_HDR_ALIGN + \
+				     MAX_TXPD_SZ)
 #define MWIFIEX_MGMT_FRAME_HEADER_SIZE	8	/* sizeof(pkt_type)
 						 *   + sizeof(tx_control)
 						 */
 
 #define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED	2
 #define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED	16
+#define MWIFIEX_MAX_TDLS_PEER_SUPPORTED 8
 
 #define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE        64
 #define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE        64
@@ -78,6 +83,7 @@
 #define MWIFIEX_BUF_FLAG_TDLS_PKT	   BIT(2)
 #define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS   BIT(3)
 #define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS  BIT(4)
+#define MWIFIEX_BUF_FLAG_AGGR_PKT          BIT(5)
 
 #define MWIFIEX_BRIDGED_PKTS_THR_HIGH      1024
 #define MWIFIEX_BRIDGED_PKTS_THR_LOW        128
@@ -92,6 +98,25 @@
 #define MWIFIEX_TDLS_MAX_FAIL_COUNT      4
 #define MWIFIEX_AUTO_TDLS_IDLE_TIME     10
 
+/* 54M rates, index from 0 to 11 */
+#define MWIFIEX_RATE_INDEX_MCS0 12
+/* 12-27=MCS0-15(BW20) */
+#define MWIFIEX_BW20_MCS_NUM 15
+
+/* Rate index for OFDM 0 */
+#define MWIFIEX_RATE_INDEX_OFDM0   4
+
+#define MWIFIEX_MAX_STA_NUM		1
+#define MWIFIEX_MAX_UAP_NUM		1
+#define MWIFIEX_MAX_P2P_NUM		1
+
+#define MWIFIEX_A_BAND_START_FREQ	5000
+
+/* SDIO Aggr data packet special info */
+#define SDIO_MAX_AGGR_BUF_SIZE		(256 * 255)
+#define BLOCK_NUMBER_OFFSET		15
+#define SDIO_HEADER_OFFSET		28
+
 enum mwifiex_bss_type {
 	MWIFIEX_BSS_TYPE_STA = 0,
 	MWIFIEX_BSS_TYPE_UAP = 1,
@@ -149,10 +174,11 @@
 };
 
 struct mwifiex_rxinfo {
+	struct sk_buff *parent;
 	u8 bss_num;
 	u8 bss_type;
-	struct sk_buff *parent;
 	u8 use_count;
+	u8 buf_type;
 };
 
 struct mwifiex_txinfo {
@@ -160,6 +186,7 @@
 	u8 flags;
 	u8 bss_num;
 	u8 bss_type;
+	u8 aggr_num;
 	u32 pkt_len;
 	u8 ack_frame_id;
 	u64 cookie;
@@ -204,4 +231,35 @@
 	u16 cca_scan_dur;
 	u16 cca_busy_dur;
 } __packed;
+
+#define MWIFIEX_HIST_MAX_SAMPLES	1048576
+#define MWIFIEX_MAX_RX_RATES		     44
+#define MWIFIEX_MAX_AC_RX_RATES		     74
+#define MWIFIEX_MAX_SNR			    256
+#define MWIFIEX_MAX_NOISE_FLR		    256
+#define MWIFIEX_MAX_SIG_STRENGTH	    256
+
+struct mwifiex_histogram_data {
+	atomic_t rx_rate[MWIFIEX_MAX_AC_RX_RATES];
+	atomic_t snr[MWIFIEX_MAX_SNR];
+	atomic_t noise_flr[MWIFIEX_MAX_NOISE_FLR];
+	atomic_t sig_str[MWIFIEX_MAX_SIG_STRENGTH];
+	atomic_t num_samples;
+};
+
+struct mwifiex_iface_comb {
+	u8 sta_intf;
+	u8 uap_intf;
+	u8 p2p_intf;
+};
+
+struct mwifiex_radar_params {
+	struct cfg80211_chan_def *chandef;
+	u32 cac_time_ms;
+} __packed;
+
+struct mwifiex_11h_intf_state {
+	bool is_11h_enabled;
+	bool is_11h_active;
+} __packed;
 #endif /* !_MWIFIEX_DECL_H_ */
diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/mwifiex/ethtool.c
index 04e56b5..65d8d6d 100644
--- a/drivers/net/wireless/mwifiex/ethtool.c
+++ b/drivers/net/wireless/mwifiex/ethtool.c
@@ -76,7 +76,9 @@
 
 	dump->flag = adapter->curr_mem_idx;
 	dump->version = 1;
-	if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) {
+	if (adapter->curr_mem_idx == MWIFIEX_DRV_INFO_IDX) {
+		dump->len = adapter->drv_info_size;
+	} else if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) {
 		entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx];
 		dump->len = entry->mem_size;
 	} else {
@@ -98,6 +100,13 @@
 	if (!adapter->if_ops.fw_dump)
 		return -ENOTSUPP;
 
+	if (adapter->curr_mem_idx == MWIFIEX_DRV_INFO_IDX) {
+		if (!adapter->drv_info_dump)
+			return -EFAULT;
+		memcpy(p, adapter->drv_info_dump, adapter->drv_info_size);
+		return 0;
+	}
+
 	if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) {
 		dev_err(adapter->dev, "firmware dump in progress!!\n");
 		return -EBUSY;
@@ -125,6 +134,11 @@
 	if (!adapter->if_ops.fw_dump)
 		return -ENOTSUPP;
 
+	if (val->flag == MWIFIEX_DRV_INFO_IDX) {
+		adapter->curr_mem_idx = MWIFIEX_DRV_INFO_IDX;
+		return 0;
+	}
+
 	if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) {
 		dev_err(adapter->dev, "firmware dump in progress!!\n");
 		return -EBUSY;
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index fb5936e..59d8964 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -158,6 +158,7 @@
 #define TLV_TYPE_POWER_GROUP        (PROPRIETARY_TLV_BASE_ID + 84)
 #define TLV_TYPE_BSS_SCAN_RSP       (PROPRIETARY_TLV_BASE_ID + 86)
 #define TLV_TYPE_BSS_SCAN_INFO      (PROPRIETARY_TLV_BASE_ID + 87)
+#define TLV_TYPE_CHANRPT_11H_BASIC  (PROPRIETARY_TLV_BASE_ID + 91)
 #define TLV_TYPE_UAP_RETRY_LIMIT    (PROPRIETARY_TLV_BASE_ID + 93)
 #define TLV_TYPE_WAPI_IE            (PROPRIETARY_TLV_BASE_ID + 94)
 #define TLV_TYPE_UAP_MGMT_FRAME     (PROPRIETARY_TLV_BASE_ID + 104)
@@ -196,6 +197,7 @@
 
 #define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11))
 #define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14))
+#define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16))
 
 #define MWIFIEX_DEF_HT_CAP	(IEEE80211_HT_CAP_DSSSCCK40 | \
 				 (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \
@@ -233,6 +235,7 @@
 #define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22))
 #define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30))
 #define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2))
+#define GETSUPP_TXBASTREAMS(Dot11nDevCap) ((Dot11nDevCap >> 18) & 0xF)
 
 /* httxcfg bitmap
  * 0		reserved
@@ -335,6 +338,7 @@
 #define HostCmd_CMD_11N_ADDBA_RSP                     0x00cf
 #define HostCmd_CMD_11N_DELBA                         0x00d0
 #define HostCmd_CMD_RECONFIGURE_TX_BUFF               0x00d9
+#define HostCmd_CMD_CHAN_REPORT_REQUEST               0x00dd
 #define HostCmd_CMD_AMSDU_AGGR_CTRL                   0x00df
 #define HostCmd_CMD_TXPWR_CFG                         0x00d1
 #define HostCmd_CMD_TX_RATE_CFG                       0x00d6
@@ -350,6 +354,7 @@
 #define HostCmd_CMD_REMAIN_ON_CHAN                    0x010d
 #define HostCmd_CMD_11AC_CFG			      0x0112
 #define HostCmd_CMD_TDLS_OPER                         0x0122
+#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG               0x0223
 
 #define PROTOCOL_NO_SECURITY        0x01
 #define PROTOCOL_STATIC_WEP         0x02
@@ -492,6 +497,8 @@
 #define EVENT_HOSTWAKE_STAIE		0x0000004d
 #define EVENT_CHANNEL_SWITCH_ANN        0x00000050
 #define EVENT_TDLS_GENERIC_EVENT        0x00000052
+#define EVENT_RADAR_DETECTED		0x00000053
+#define EVENT_CHANNEL_REPORT_RDY        0x00000054
 #define EVENT_EXT_SCAN_REPORT           0x00000058
 #define EVENT_REMAIN_ON_CHAN_EXPIRED    0x0000005f
 #define EVENT_TX_STATUS_REPORT		0x00000074
@@ -518,9 +525,11 @@
 #define TYPE_OR				(MAX_OPERAND+5)
 #define MEF_MODE_HOST_SLEEP			1
 #define MEF_ACTION_ALLOW_AND_WAKEUP_HOST	3
+#define MEF_ACTION_AUTO_ARP                    0x10
 #define MWIFIEX_CRITERIA_BROADCAST	BIT(0)
 #define MWIFIEX_CRITERIA_UNICAST	BIT(1)
 #define MWIFIEX_CRITERIA_MULTICAST	BIT(3)
+#define MWIFIEX_MAX_SUPPORTED_IPADDR              4
 
 #define ACT_TDLS_DELETE            0x00
 #define ACT_TDLS_CREATE            0x01
@@ -529,6 +538,8 @@
 
 #define MWIFIEX_FW_V15		   15
 
+#define MWIFIEX_MASTER_RADAR_DET_MASK BIT(1)
+
 struct mwifiex_ie_types_header {
 	__le16 type;
 	__le16 len;
@@ -1076,6 +1087,8 @@
 	__le32 tx_frame;
 	__le32 reserved;
 	__le32 wep_icv_err_cnt[4];
+	__le32 bcn_rcv_cnt;
+	__le32 bcn_miss_cnt;
 };
 
 /* Enumeration for rate format */
@@ -1213,6 +1226,30 @@
 	u8 peer_mac[ETH_ALEN];
 } __packed;
 
+struct mwifiex_chan_desc {
+	__le16 start_freq;
+	u8 chan_width;
+	u8 chan_num;
+} __packed;
+
+struct host_cmd_ds_chan_rpt_req {
+	struct mwifiex_chan_desc chan_desc;
+	__le32 msec_dwell_time;
+} __packed;
+
+struct host_cmd_ds_chan_rpt_event {
+	__le32 result;
+	__le64 start_tsf;
+	__le32 duration;
+	u8 tlvbuf[0];
+} __packed;
+
+struct host_cmd_sdio_sp_rx_aggr_cfg {
+	u8 action;
+	u8 enable;
+	__le16 block_size;
+} __packed;
+
 struct mwifiex_fixed_bcn_param {
 	__le64 timestamp;
 	__le16 beacon_period;
@@ -1789,6 +1826,39 @@
 	u8 evt_freq;
 } __packed;
 
+#define MWIFIEX_DFS_REC_HDR_LEN		8
+#define MWIFIEX_DFS_REC_HDR_NUM		10
+#define MWIFIEX_BIN_COUNTER_LEN		7
+
+struct mwifiex_radar_det_event {
+	__le32 detect_count;
+	u8 reg_domain;  /*1=fcc, 2=etsi, 3=mic*/
+	u8 det_type;  /*0=none, 1=pw(chirp), 2=pri(radar)*/
+	__le16 pw_chirp_type;
+	u8 pw_chirp_idx;
+	u8 pw_value;
+	u8 pri_radar_type;
+	u8 pri_bincnt;
+	u8 bin_counter[MWIFIEX_BIN_COUNTER_LEN];
+	u8 num_dfs_records;
+	u8 dfs_record_hdr[MWIFIEX_DFS_REC_HDR_NUM][MWIFIEX_DFS_REC_HDR_LEN];
+	__le32 passed;
+} __packed;
+
+struct meas_rpt_map {
+	u8 rssi:3;
+	u8 unmeasured:1;
+	u8 radar:1;
+	u8 unidentified_sig:1;
+	u8 ofdm_preamble:1;
+	u8 bss:1;
+} __packed;
+
+struct mwifiex_ie_types_chan_rpt_data {
+	struct mwifiex_ie_types_header header;
+	struct meas_rpt_map map;
+} __packed;
+
 struct host_cmd_ds_802_11_subsc_evt {
 	__le16 action;
 	__le16 events;
@@ -1901,6 +1971,8 @@
 		struct host_cmd_11ac_vht_cfg vht_cfg;
 		struct host_cmd_ds_coalesce_cfg coalesce_cfg;
 		struct host_cmd_ds_tdls_oper tdls_oper;
+		struct host_cmd_ds_chan_rpt_req chan_rpt_req;
+		struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg;
 	} params;
 } __packed;
 
diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c
index b933794..f3b6ed2 100644
--- a/drivers/net/wireless/mwifiex/ie.c
+++ b/drivers/net/wireless/mwifiex/ie.c
@@ -317,27 +317,27 @@
 	return ret;
 }
 
-/* This function parses different IEs-tail IEs, beacon IEs, probe response IEs,
- * association response IEs from cfg80211_ap_settings function and sets these IE
- * to FW.
+/* This function parses  head and tail IEs, from cfg80211_beacon_data and sets
+ * these IE to FW.
  */
-int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
-			 struct cfg80211_beacon_data *info)
+static int mwifiex_uap_set_head_tail_ies(struct mwifiex_private *priv,
+					 struct cfg80211_beacon_data *info)
 {
 	struct mwifiex_ie *gen_ie;
-	struct ieee_types_header *rsn_ie, *wpa_ie = NULL;
-	u16 rsn_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
+	struct ieee_types_header *rsn_ie = NULL, *wpa_ie = NULL;
+	struct ieee_types_header *chsw_ie = NULL;
+	u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
 	const u8 *vendor_ie;
 
-	if (info->tail && info->tail_len) {
-		gen_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
-		if (!gen_ie)
-			return -ENOMEM;
-		gen_ie->ie_index = cpu_to_le16(rsn_idx);
-		gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
-							MGMT_MASK_PROBE_RESP |
-							MGMT_MASK_ASSOC_RESP);
+	gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL);
+	if (!gen_ie)
+		return -ENOMEM;
+	gen_ie->ie_index = cpu_to_le16(gen_idx);
+	gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
+						MGMT_MASK_PROBE_RESP |
+						MGMT_MASK_ASSOC_RESP);
 
+	if (info->tail && info->tail_len) {
 		rsn_ie = (void *)cfg80211_find_ie(WLAN_EID_RSN,
 						  info->tail, info->tail_len);
 		if (rsn_ie) {
@@ -358,19 +358,41 @@
 			gen_ie->ie_length = cpu_to_le16(ie_len);
 		}
 
-		if (rsn_ie || wpa_ie) {
-			if (mwifiex_update_uap_custom_ie(priv, gen_ie, &rsn_idx,
-							 NULL, NULL,
-							 NULL, NULL)) {
-				kfree(gen_ie);
-				return -1;
-			}
-			priv->rsn_idx = rsn_idx;
+		chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH,
+						   info->tail, info->tail_len);
+		if (chsw_ie) {
+			memcpy(gen_ie->ie_buffer + ie_len,
+			       chsw_ie, chsw_ie->len + 2);
+			ie_len += chsw_ie->len + 2;
+			gen_ie->ie_length = cpu_to_le16(ie_len);
 		}
-
-		kfree(gen_ie);
 	}
 
+	if (rsn_ie || wpa_ie || chsw_ie) {
+		if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL,
+						 NULL, NULL, NULL)) {
+			kfree(gen_ie);
+			return -1;
+		}
+		priv->gen_idx = gen_idx;
+	}
+
+	kfree(gen_ie);
+	return 0;
+}
+
+/* This function parses different IEs-head & tail IEs, beacon IEs,
+ * probe response IEs, association response IEs from cfg80211_ap_settings
+ * function and sets these IE to FW.
+ */
+int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
+			 struct cfg80211_beacon_data *info)
+{
+	int ret;
+
+	ret = mwifiex_uap_set_head_tail_ies(priv, info);
+		return ret;
+
 	return mwifiex_set_mgmt_beacon_data_ies(priv, info);
 }
 
@@ -378,25 +400,25 @@
 int mwifiex_del_mgmt_ies(struct mwifiex_private *priv)
 {
 	struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
-	struct mwifiex_ie *ar_ie = NULL, *rsn_ie = NULL;
+	struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL;
 	int ret = 0;
 
-	if (priv->rsn_idx != MWIFIEX_AUTO_IDX_MASK) {
-		rsn_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
-		if (!rsn_ie)
+	if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) {
+		gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL);
+		if (!gen_ie)
 			return -ENOMEM;
 
-		rsn_ie->ie_index = cpu_to_le16(priv->rsn_idx);
-		rsn_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
-		rsn_ie->ie_length = 0;
-		if (mwifiex_update_uap_custom_ie(priv, rsn_ie, &priv->rsn_idx,
+		gen_ie->ie_index = cpu_to_le16(priv->gen_idx);
+		gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
+		gen_ie->ie_length = 0;
+		if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx,
 						 NULL, &priv->proberesp_idx,
 						 NULL, &priv->assocresp_idx)) {
 			ret = -1;
 			goto done;
 		}
 
-		priv->rsn_idx = MWIFIEX_AUTO_IDX_MASK;
+		priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
 	}
 
 	if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) {
@@ -440,7 +462,6 @@
 	kfree(beacon_ie);
 	kfree(pr_ie);
 	kfree(ar_ie);
-	kfree(rsn_ie);
 
 	return ret;
 }
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index 520ad4a..e12192f 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -52,6 +52,18 @@
 	return 0;
 }
 
+static void wakeup_timer_fn(unsigned long data)
+{
+	struct mwifiex_adapter *adapter = (struct mwifiex_adapter *)data;
+
+	dev_err(adapter->dev, "Firmware wakeup failed\n");
+	adapter->hw_status = MWIFIEX_HW_STATUS_RESET;
+	mwifiex_cancel_all_pending_cmd(adapter);
+
+	if (adapter->if_ops.card_reset)
+		adapter->if_ops.card_reset(adapter);
+}
+
 /*
  * This function initializes the private structure and sets default
  * values to the members.
@@ -64,7 +76,7 @@
 	u32 i;
 
 	priv->media_connected = false;
-	memset(priv->curr_addr, 0xff, ETH_ALEN);
+	eth_broadcast_addr(priv->curr_addr);
 
 	priv->pkt_tx_ctrl = 0;
 	priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
@@ -140,6 +152,8 @@
 	priv->check_tdls_tx = false;
 	memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID);
 
+	mwifiex_init_11h_params(priv);
+
 	return mwifiex_add_bss_prio_tbl(priv);
 }
 
@@ -252,18 +266,15 @@
 
 	mwifiex_wmm_init(adapter);
 
-	if (adapter->sleep_cfm) {
-		sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *)
-						adapter->sleep_cfm->data;
-		memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len);
-		sleep_cfm_buf->command =
-				cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
-		sleep_cfm_buf->size =
-				cpu_to_le16(adapter->sleep_cfm->len);
-		sleep_cfm_buf->result = 0;
-		sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM);
-		sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED);
-	}
+	sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *)
+					adapter->sleep_cfm->data;
+	memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len);
+	sleep_cfm_buf->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
+	sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len);
+	sleep_cfm_buf->result = 0;
+	sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM);
+	sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED);
+
 	memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params));
 	memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period));
 	adapter->tx_lock_flag = false;
@@ -282,9 +293,15 @@
 	memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter));
 	adapter->arp_filter_size = 0;
 	adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX;
-	adapter->ext_scan = true;
 	adapter->key_api_major_ver = 0;
 	adapter->key_api_minor_ver = 0;
+	eth_broadcast_addr(adapter->perm_addr);
+	adapter->iface_limit.sta_intf = MWIFIEX_MAX_STA_NUM;
+	adapter->iface_limit.uap_intf = MWIFIEX_MAX_UAP_NUM;
+	adapter->iface_limit.p2p_intf = MWIFIEX_MAX_P2P_NUM;
+
+	setup_timer(&adapter->wakeup_timer, wakeup_timer_fn,
+		    (unsigned long)adapter);
 }
 
 /*
@@ -391,7 +408,10 @@
 		return;
 	}
 
+	del_timer(&adapter->wakeup_timer);
 	mwifiex_cancel_all_pending_cmd(adapter);
+	wake_up_interruptible(&adapter->cmd_wait_q.wait);
+	wake_up_interruptible(&adapter->hs_activate_wait_q);
 
 	/* Free lock variables */
 	mwifiex_free_lock_list(adapter);
@@ -411,6 +431,11 @@
 		entry->mem_size = 0;
 	}
 
+	if (adapter->drv_info_dump) {
+		vfree(adapter->drv_info_dump);
+		adapter->drv_info_size = 0;
+	}
+
 	if (adapter->sleep_cfm)
 		dev_kfree_skb_any(adapter->sleep_cfm);
 }
@@ -453,6 +478,7 @@
 	spin_lock_init(&adapter->rx_proc_lock);
 
 	skb_queue_head_init(&adapter->rx_data_q);
+	skb_queue_head_init(&adapter->tx_data_q);
 
 	for (i = 0; i < adapter->priv_num; ++i) {
 		INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head);
@@ -528,7 +554,8 @@
 
 	for (i = 0; i < adapter->priv_num; i++) {
 		if (adapter->priv[i]) {
-			ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta);
+			ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta,
+						   true);
 			if (ret == -1)
 				return -1;
 
@@ -653,11 +680,16 @@
 			priv = adapter->priv[i];
 
 			mwifiex_clean_auto_tdls(priv);
+			mwifiex_abort_cac(priv);
 			mwifiex_clean_txrx(priv);
 			mwifiex_delete_bss_prio_tbl(priv);
 		}
 	}
 
+	atomic_set(&adapter->tx_queued, 0);
+	while ((skb = skb_dequeue(&adapter->tx_data_q)))
+		mwifiex_write_data_complete(adapter, skb, 0, 0);
+
 	spin_lock_irqsave(&adapter->rx_proc_lock, flags);
 
 	while ((skb = skb_dequeue(&adapter->rx_data_q))) {
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h
index 0847f3e..d2b05c3 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/mwifiex/ioctl.h
@@ -137,6 +137,8 @@
 	u32 fcs_error;
 	u32 tx_frame;
 	u32 wep_icv_error[4];
+	u32 bcn_rcv_cnt;
+	u32 bcn_miss_cnt;
 };
 
 #define MWIFIEX_MAX_VER_STR_LEN    128
@@ -180,7 +182,11 @@
 	u8 amsdu;
 };
 
-#define DBG_CMD_NUM	5
+#define DBG_CMD_NUM    5
+
+struct tdls_peer_info {
+	u8 peer_addr[ETH_ALEN];
+};
 
 struct mwifiex_debug_info {
 	u32 int_counter;
@@ -193,6 +199,9 @@
 	u32 rx_tbl_num;
 	struct mwifiex_ds_rx_reorder_tbl rx_tbl
 		[MWIFIEX_MAX_RX_BASTREAM_SUPPORTED];
+	u32 tdls_peer_num;
+	struct tdls_peer_info tdls_list
+		[MWIFIEX_MAX_TDLS_PEER_SUPPORTED];
 	u16 ps_mode;
 	u32 ps_state;
 	u8 is_deep_sleep;
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index 875eb77..e83b258 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -83,9 +83,8 @@
 	}
 	mwifiex_init_lock_list(adapter);
 
-	init_timer(&adapter->cmd_timer);
-	adapter->cmd_timer.function = mwifiex_cmd_timeout_func;
-	adapter->cmd_timer.data = (unsigned long) adapter;
+	setup_timer(&adapter->cmd_timer, mwifiex_cmd_timeout_func,
+		    (unsigned long)adapter);
 
 	return 0;
 
@@ -132,10 +131,39 @@
 	return 0;
 }
 
+void mwifiex_queue_main_work(struct mwifiex_adapter *adapter)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->main_proc_lock, flags);
+	if (adapter->mwifiex_processing) {
+		adapter->more_task_flag = true;
+		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+	} else {
+		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+		queue_work(adapter->workqueue, &adapter->main_work);
+	}
+}
+EXPORT_SYMBOL_GPL(mwifiex_queue_main_work);
+
+static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+	if (adapter->rx_processing) {
+		spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+	} else {
+		spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+		queue_work(adapter->rx_workqueue, &adapter->rx_work);
+	}
+}
+
 static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
 {
 	unsigned long flags;
 	struct sk_buff *skb;
+	struct mwifiex_rxinfo *rx_info;
 
 	spin_lock_irqsave(&adapter->rx_proc_lock, flags);
 	if (adapter->rx_processing || adapter->rx_locked) {
@@ -155,9 +183,16 @@
 			if (adapter->if_ops.submit_rem_rx_urbs)
 				adapter->if_ops.submit_rem_rx_urbs(adapter);
 			adapter->delay_main_work = false;
-			queue_work(adapter->workqueue, &adapter->main_work);
+			mwifiex_queue_main_work(adapter);
 		}
-		mwifiex_handle_rx_packet(adapter, skb);
+		rx_info = MWIFIEX_SKB_RXCB(skb);
+		if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) {
+			if (adapter->if_ops.deaggr_pkt)
+				adapter->if_ops.deaggr_pkt(adapter, skb);
+			dev_kfree_skb_any(skb);
+		} else {
+			mwifiex_handle_rx_packet(adapter, skb);
+		}
 	}
 	spin_lock_irqsave(&adapter->rx_proc_lock, flags);
 	adapter->rx_processing = false;
@@ -190,15 +225,17 @@
 	spin_lock_irqsave(&adapter->main_proc_lock, flags);
 
 	/* Check if already processing */
-	if (adapter->mwifiex_processing) {
+	if (adapter->mwifiex_processing || adapter->main_locked) {
+		adapter->more_task_flag = true;
 		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
 		goto exit_main_proc;
 	} else {
 		adapter->mwifiex_processing = true;
-		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
 	}
 process_start:
 	do {
+		adapter->more_task_flag = false;
+		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
 		if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) ||
 		    (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY))
 			break;
@@ -213,9 +250,7 @@
 		if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
 		    adapter->iface_type != MWIFIEX_USB) {
 			adapter->delay_main_work = true;
-			if (!adapter->rx_processing)
-				queue_work(adapter->rx_workqueue,
-					   &adapter->rx_work);
+			mwifiex_queue_rx_work(adapter);
 			break;
 		}
 
@@ -228,22 +263,26 @@
 		}
 
 		if (adapter->rx_work_enabled && adapter->data_received)
-			queue_work(adapter->rx_workqueue, &adapter->rx_work);
+			mwifiex_queue_rx_work(adapter);
 
 		/* Need to wake up the card ? */
 		if ((adapter->ps_state == PS_STATE_SLEEP) &&
 		    (adapter->pm_wakeup_card_req &&
 		     !adapter->pm_wakeup_fw_try) &&
 		    (is_command_pending(adapter) ||
+		     !skb_queue_empty(&adapter->tx_data_q) ||
 		     !mwifiex_wmm_lists_empty(adapter))) {
 			adapter->pm_wakeup_fw_try = true;
+			mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3));
 			adapter->if_ops.wakeup(adapter);
+			spin_lock_irqsave(&adapter->main_proc_lock, flags);
 			continue;
 		}
 
 		if (IS_CARD_RX_RCVD(adapter)) {
 			adapter->data_received = false;
 			adapter->pm_wakeup_fw_try = false;
+			del_timer(&adapter->wakeup_timer);
 			if (adapter->ps_state == PS_STATE_SLEEP)
 				adapter->ps_state = PS_STATE_AWAKE;
 		} else {
@@ -256,7 +295,8 @@
 
 			if ((!adapter->scan_chan_gap_enabled &&
 			     adapter->scan_processing) || adapter->data_sent ||
-			    mwifiex_wmm_lists_empty(adapter)) {
+			    (mwifiex_wmm_lists_empty(adapter) &&
+			     skb_queue_empty(&adapter->tx_data_q))) {
 				if (adapter->cmd_sent || adapter->curr_cmd ||
 				    (!is_command_pending(adapter)))
 					break;
@@ -294,8 +334,10 @@
 		if ((adapter->ps_state == PS_STATE_SLEEP) ||
 		    (adapter->ps_state == PS_STATE_PRE_SLEEP) ||
 		    (adapter->ps_state == PS_STATE_SLEEP_CFM) ||
-		    adapter->tx_lock_flag)
+		    adapter->tx_lock_flag){
+			spin_lock_irqsave(&adapter->main_proc_lock, flags);
 			continue;
+		}
 
 		if (!adapter->cmd_sent && !adapter->curr_cmd) {
 			if (mwifiex_exec_next_cmd(adapter) == -1) {
@@ -306,6 +348,20 @@
 
 		if ((adapter->scan_chan_gap_enabled ||
 		     !adapter->scan_processing) &&
+		    !adapter->data_sent &&
+		    !skb_queue_empty(&adapter->tx_data_q)) {
+			mwifiex_process_tx_queue(adapter);
+			if (adapter->hs_activated) {
+				adapter->is_hs_configured = false;
+				mwifiex_hs_activated_event
+					(mwifiex_get_priv
+					(adapter, MWIFIEX_BSS_ROLE_ANY),
+					false);
+			}
+		}
+
+		if ((adapter->scan_chan_gap_enabled ||
+		     !adapter->scan_processing) &&
 		    !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter)) {
 			mwifiex_wmm_process_tx(adapter);
 			if (adapter->hs_activated) {
@@ -319,7 +375,8 @@
 
 		if (adapter->delay_null_pkt && !adapter->cmd_sent &&
 		    !adapter->curr_cmd && !is_command_pending(adapter) &&
-		    mwifiex_wmm_lists_empty(adapter)) {
+		    (mwifiex_wmm_lists_empty(adapter) &&
+		     skb_queue_empty(&adapter->tx_data_q))) {
 			if (!mwifiex_send_null_packet
 			    (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
 			     MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET |
@@ -329,15 +386,12 @@
 			}
 			break;
 		}
+		spin_lock_irqsave(&adapter->main_proc_lock, flags);
 	} while (true);
 
 	spin_lock_irqsave(&adapter->main_proc_lock, flags);
-	if (!adapter->delay_main_work &&
-	    (adapter->int_status || IS_CARD_RX_RCVD(adapter))) {
-		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+	if (adapter->more_task_flag)
 		goto process_start;
-	}
-
 	adapter->mwifiex_processing = false;
 	spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
 
@@ -465,7 +519,7 @@
 
 	rtnl_lock();
 	/* Create station interface by default */
-	wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d",
+	wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM,
 					NL80211_IFTYPE_STATION, NULL, NULL);
 	if (IS_ERR(wdev)) {
 		dev_err(adapter->dev, "cannot create default STA interface\n");
@@ -474,7 +528,7 @@
 	}
 
 	if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) {
-		wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d",
+		wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM,
 						NL80211_IFTYPE_AP, NULL, NULL);
 		if (IS_ERR(wdev)) {
 			dev_err(adapter->dev, "cannot create AP interface\n");
@@ -484,7 +538,7 @@
 	}
 
 	if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) {
-		wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d",
+		wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM,
 						NL80211_IFTYPE_P2P_CLIENT, NULL,
 						NULL);
 		if (IS_ERR(wdev)) {
@@ -511,8 +565,7 @@
 	if (adapter->if_ops.unregister_dev)
 		adapter->if_ops.unregister_dev(adapter);
 
-	if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) ||
-	    (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) {
+	if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
 		pr_debug("info: %s: shutdown mwifiex\n", __func__);
 		adapter->init_wait_q_woken = false;
 
@@ -562,7 +615,8 @@
 static int
 mwifiex_open(struct net_device *dev)
 {
-	netif_tx_start_all_queues(dev);
+	netif_carrier_off(dev);
+
 	return 0;
 }
 
@@ -603,7 +657,7 @@
 	atomic_inc(&priv->adapter->tx_pending);
 	mwifiex_wmm_add_buf_txqueue(priv, skb);
 
-	queue_work(priv->adapter->workqueue, &priv->adapter->main_work);
+	mwifiex_queue_main_work(priv->adapter);
 
 	return 0;
 }
@@ -659,9 +713,7 @@
 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 	struct sk_buff *new_skb;
 	struct mwifiex_txinfo *tx_info;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
 	bool multicast;
-#endif
 
 	dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n",
 		jiffies, priv->bss_type, priv->bss_num);
@@ -702,7 +754,6 @@
 	tx_info->bss_type = priv->bss_type;
 	tx_info->pkt_len = skb->len;
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
 	multicast = is_multicast_ether_addr(skb->data);
 
 	if (unlikely(!multicast && skb->sk &&
@@ -711,7 +762,6 @@
 		skb = mwifiex_clone_skb_for_tx_status(priv,
 						      skb,
 					MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL);
-#endif
 
 	/* Record the current time the packet was queued; used to
 	 * determine the amount of time the packet was queued in
@@ -805,6 +855,114 @@
 	}
 }
 
+void mwifiex_dump_drv_info(struct mwifiex_adapter *adapter)
+{
+	void *p;
+	char drv_version[64];
+	struct usb_card_rec *cardp;
+	struct sdio_mmc_card *sdio_card;
+	struct mwifiex_private *priv;
+	int i, idx;
+	struct netdev_queue *txq;
+	struct mwifiex_debug_info *debug_info;
+
+	if (adapter->drv_info_dump) {
+		vfree(adapter->drv_info_dump);
+		adapter->drv_info_size = 0;
+	}
+
+	dev_info(adapter->dev, "=== DRIVER INFO DUMP START===\n");
+
+	adapter->drv_info_dump = vzalloc(MWIFIEX_DRV_INFO_SIZE_MAX);
+
+	if (!adapter->drv_info_dump)
+		return;
+
+	p = (char *)(adapter->drv_info_dump);
+	p += sprintf(p, "driver_name = " "\"mwifiex\"\n");
+
+	mwifiex_drv_get_driver_version(adapter, drv_version,
+				       sizeof(drv_version) - 1);
+	p += sprintf(p, "driver_version = %s\n", drv_version);
+
+	if (adapter->iface_type == MWIFIEX_USB) {
+		cardp = (struct usb_card_rec *)adapter->card;
+		p += sprintf(p, "tx_cmd_urb_pending = %d\n",
+			     atomic_read(&cardp->tx_cmd_urb_pending));
+		p += sprintf(p, "tx_data_urb_pending = %d\n",
+			     atomic_read(&cardp->tx_data_urb_pending));
+		p += sprintf(p, "rx_cmd_urb_pending = %d\n",
+			     atomic_read(&cardp->rx_cmd_urb_pending));
+		p += sprintf(p, "rx_data_urb_pending = %d\n",
+			     atomic_read(&cardp->rx_data_urb_pending));
+	}
+
+	p += sprintf(p, "tx_pending = %d\n",
+		     atomic_read(&adapter->tx_pending));
+	p += sprintf(p, "rx_pending = %d\n",
+		     atomic_read(&adapter->rx_pending));
+
+	if (adapter->iface_type == MWIFIEX_SDIO) {
+		sdio_card = (struct sdio_mmc_card *)adapter->card;
+		p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n",
+			     sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port);
+		p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n",
+			     sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port);
+	}
+
+	for (i = 0; i < adapter->priv_num; i++) {
+		if (!adapter->priv[i] || !adapter->priv[i]->netdev)
+			continue;
+		priv = adapter->priv[i];
+		p += sprintf(p, "\n[interface  : \"%s\"]\n",
+			     priv->netdev->name);
+		p += sprintf(p, "wmm_tx_pending[0] = %d\n",
+			     atomic_read(&priv->wmm_tx_pending[0]));
+		p += sprintf(p, "wmm_tx_pending[1] = %d\n",
+			     atomic_read(&priv->wmm_tx_pending[1]));
+		p += sprintf(p, "wmm_tx_pending[2] = %d\n",
+			     atomic_read(&priv->wmm_tx_pending[2]));
+		p += sprintf(p, "wmm_tx_pending[3] = %d\n",
+			     atomic_read(&priv->wmm_tx_pending[3]));
+		p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ?
+			     "Disconnected" : "Connected");
+		p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev)
+			     ? "on" : "off"));
+		for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) {
+			txq = netdev_get_tx_queue(priv->netdev, idx);
+			p += sprintf(p, "tx queue %d:%s  ", idx,
+				     netif_tx_queue_stopped(txq) ?
+				     "stopped" : "started");
+		}
+		p += sprintf(p, "\n%s: num_tx_timeout = %d\n",
+			     priv->netdev->name, priv->num_tx_timeout);
+	}
+
+	if (adapter->iface_type == MWIFIEX_SDIO) {
+		p += sprintf(p, "\n=== SDIO register DUMP===\n");
+		if (adapter->if_ops.reg_dump)
+			p += adapter->if_ops.reg_dump(adapter, p);
+	}
+
+	p += sprintf(p, "\n=== MORE DEBUG INFORMATION\n");
+	debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL);
+	if (debug_info) {
+		for (i = 0; i < adapter->priv_num; i++) {
+			if (!adapter->priv[i] || !adapter->priv[i]->netdev)
+				continue;
+			priv = adapter->priv[i];
+			mwifiex_get_debug_info(priv, debug_info);
+			p += mwifiex_debug_info_to_buffer(priv, p, debug_info);
+			break;
+		}
+		kfree(debug_info);
+	}
+
+	adapter->drv_info_size = p - adapter->drv_info_dump;
+	dev_info(adapter->dev, "=== DRIVER INFO DUMP END===\n");
+}
+EXPORT_SYMBOL_GPL(mwifiex_dump_drv_info);
+
 /*
  * CFG802.11 network device handler for statistics retrieval.
  */
@@ -861,26 +1019,34 @@
  *      - Nick name             : Set to null
  *      - Number of Tx timeout  : Set to 0
  *      - Device address        : Set to current address
+ *      - Rx histogram statistc : Set to 0
  *
  * In addition, the CFG80211 work queue is also created.
  */
 void mwifiex_init_priv_params(struct mwifiex_private *priv,
-						struct net_device *dev)
+			      struct net_device *dev)
 {
 	dev->netdev_ops = &mwifiex_netdev_ops;
 	dev->destructor = free_netdev;
 	/* Initialize private structure */
 	priv->current_key_index = 0;
 	priv->media_connected = false;
-	memset(&priv->nick_name, 0, sizeof(priv->nick_name));
 	memset(priv->mgmt_ie, 0,
 	       sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX);
 	priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK;
 	priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK;
 	priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK;
-	priv->rsn_idx = MWIFIEX_AUTO_IDX_MASK;
+	priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
 	priv->num_tx_timeout = 0;
+	ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr);
 	memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
+
+	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
+	    GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+		priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL);
+		if (priv->hist_data)
+			mwifiex_hist_data_reset(priv);
+	}
 }
 
 /*
@@ -993,9 +1159,6 @@
 		INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue);
 	}
 
-	if (adapter->if_ops.iface_work)
-		INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work);
-
 	/* Register the device. Fill up the private data structure with relevant
 	   information from the card. */
 	if (adapter->if_ops.register_dev(adapter)) {
@@ -1014,8 +1177,7 @@
 	pr_debug("info: %s: unregister device\n", __func__);
 	if (adapter->if_ops.unregister_dev)
 		adapter->if_ops.unregister_dev(adapter);
-	if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) ||
-	    (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) {
+	if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
 		pr_debug("info: %s: shutdown mwifiex\n", __func__);
 		adapter->init_wait_q_woken = false;
 
@@ -1066,6 +1228,8 @@
 
 	adapter->surprise_removed = true;
 
+	mwifiex_terminate_workqueue(adapter);
+
 	/* Stop data */
 	for (i = 0; i < adapter->priv_num; i++) {
 		priv = adapter->priv[i];
@@ -1100,16 +1264,15 @@
 			continue;
 
 		rtnl_lock();
-		if (priv->wdev && priv->netdev)
-			mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev);
+		if (priv->netdev &&
+		    priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED)
+			mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
 		rtnl_unlock();
 	}
 
 	wiphy_unregister(adapter->wiphy);
 	wiphy_free(adapter->wiphy);
 
-	mwifiex_terminate_workqueue(adapter);
-
 	/* Unregister device */
 	dev_dbg(adapter->dev, "info: unregister device\n");
 	if (adapter->if_ops.unregister_dev)
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index e66993c..fe12560 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -35,12 +35,15 @@
 #include <linux/ctype.h>
 #include <linux/of.h>
 #include <linux/idr.h>
+#include <linux/inetdevice.h>
 
 #include "decl.h"
 #include "ioctl.h"
 #include "util.h"
 #include "fw.h"
 #include "pcie.h"
+#include "usb.h"
+#include "sdio.h"
 
 extern const char driver_version[];
 
@@ -56,6 +59,8 @@
 
 #define MWIFIEX_MAX_AP				64
 
+#define MWIFIEX_MAX_PKTS_TXQ			16
+
 #define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT	(5 * HZ)
 
 #define MWIFIEX_TIMER_10S			10000
@@ -116,6 +121,7 @@
 
 #define MWIFIEX_TYPE_CMD				1
 #define MWIFIEX_TYPE_DATA				0
+#define MWIFIEX_TYPE_AGGR_DATA				10
 #define MWIFIEX_TYPE_EVENT				3
 
 #define MAX_BITMAP_RATES_SIZE			18
@@ -136,6 +142,11 @@
 /* Threshold for tx_timeout_cnt before we trigger a card reset */
 #define TX_TIMEOUT_THRESHOLD	6
 
+#define MWIFIEX_DRV_INFO_SIZE_MAX 0x40000
+
+/* Address alignment */
+#define MWIFIEX_ALIGN_ADDR(p, a) (((long)(p) + (a) - 1) & ~((a) - 1))
+
 struct mwifiex_dbg {
 	u32 num_cmd_host_to_card_failure;
 	u32 num_cmd_sleep_cfm_host_to_card_failure;
@@ -161,7 +172,6 @@
 enum MWIFIEX_HARDWARE_STATUS {
 	MWIFIEX_HW_STATUS_READY,
 	MWIFIEX_HW_STATUS_INITIALIZING,
-	MWIFIEX_HW_STATUS_FW_READY,
 	MWIFIEX_HW_STATUS_INIT_DONE,
 	MWIFIEX_HW_STATUS_RESET,
 	MWIFIEX_HW_STATUS_CLOSING,
@@ -204,6 +214,12 @@
 	u8 amsdu;
 };
 
+enum mwifiex_ba_status {
+	BA_SETUP_NONE = 0,
+	BA_SETUP_INPROGRESS,
+	BA_SETUP_COMPLETE
+};
+
 struct mwifiex_ra_list_tbl {
 	struct list_head list;
 	struct sk_buff_head skb_head;
@@ -212,6 +228,8 @@
 	u16 max_amsdu;
 	u16 ba_pkt_count;
 	u8 ba_packet_thr;
+	enum mwifiex_ba_status ba_status;
+	u8 amsdu_in_ampdu;
 	u16 total_pkt_count;
 	bool tdls_link;
 };
@@ -413,6 +431,7 @@
 };
 
 #define MWIFIEX_FW_DUMP_IDX		0xff
+#define MWIFIEX_DRV_INFO_IDX		20
 #define FW_DUMP_MAX_NAME_LEN		8
 #define FW_DUMP_HOST_READY		0xEE
 #define FW_DUMP_DONE			0xFF
@@ -543,13 +562,12 @@
 	u32 curr_bcn_size;
 	/* spin lock for beacon buffer */
 	spinlock_t curr_bcn_buf_lock;
-	struct wireless_dev *wdev;
+	struct wireless_dev wdev;
 	struct mwifiex_chan_freq_power cfp;
 	char version_str[128];
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *dfs_dev_dir;
 #endif
-	u8 nick_name[16];
 	u16 current_key_index;
 	struct semaphore async_sem;
 	struct cfg80211_scan_request *scan_request;
@@ -564,7 +582,7 @@
 	u16 beacon_idx;
 	u16 proberesp_idx;
 	u16 assocresp_idx;
-	u16 rsn_idx;
+	u16 gen_idx;
 	u8 ap_11n_enabled;
 	u8 ap_11ac_enabled;
 	u32 mgmt_frame_mask;
@@ -574,6 +592,7 @@
 	unsigned long csa_expire_time;
 	u8 del_list_idx;
 	bool hs2_enabled;
+	struct mwifiex_uap_bss_param bss_cfg;
 	struct station_parameters *sta_params;
 	struct sk_buff_head tdls_txq;
 	u8 check_tdls_tx;
@@ -582,13 +601,18 @@
 	struct idr ack_status_frames;
 	/* spin lock for ack status */
 	spinlock_t ack_status_lock;
+	/** rx histogram data */
+	struct mwifiex_histogram_data *hist_data;
+	struct cfg80211_chan_def dfs_chandef;
+	struct workqueue_struct *dfs_cac_workqueue;
+	struct delayed_work dfs_cac_work;
+	struct timer_list dfs_chan_switch_timer;
+	struct workqueue_struct *dfs_chan_sw_workqueue;
+	struct delayed_work dfs_chan_sw_work;
+	struct cfg80211_beacon_data beacon_after;
+	struct mwifiex_11h_intf_state state_11h;
 };
 
-enum mwifiex_ba_status {
-	BA_SETUP_NONE = 0,
-	BA_SETUP_INPROGRESS,
-	BA_SETUP_COMPLETE
-};
 
 struct mwifiex_tx_ba_stream_tbl {
 	struct list_head list;
@@ -717,13 +741,17 @@
 	int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
 	void (*card_reset) (struct mwifiex_adapter *);
 	void (*fw_dump)(struct mwifiex_adapter *);
+	int (*reg_dump)(struct mwifiex_adapter *, char *);
 	int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
 	void (*iface_work)(struct work_struct *work);
 	void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter);
+	void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *);
 };
 
 struct mwifiex_adapter {
 	u8 iface_type;
+	struct mwifiex_iface_comb iface_limit;
+	struct mwifiex_iface_comb curr_iface_comb;
 	struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM];
 	u8 priv_num;
 	const struct firmware *firmware;
@@ -731,6 +759,7 @@
 	int winner;
 	struct device *dev;
 	struct wiphy *wiphy;
+	u8 perm_addr[ETH_ALEN];
 	bool surprise_removed;
 	u32 fw_release_number;
 	u16 init_wait_q_woken;
@@ -744,18 +773,24 @@
 	struct work_struct main_work;
 	struct workqueue_struct *rx_workqueue;
 	struct work_struct rx_work;
+	struct workqueue_struct *dfs_workqueue;
+	struct work_struct dfs_work;
 	bool rx_work_enabled;
 	bool rx_processing;
 	bool delay_main_work;
 	bool rx_locked;
+	bool main_locked;
 	struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM];
 	/* spin lock for init/shutdown */
 	spinlock_t mwifiex_lock;
 	/* spin lock for main process */
 	spinlock_t main_proc_lock;
 	u32 mwifiex_processing;
+	u8 more_task_flag;
 	u16 tx_buf_size;
 	u16 curr_tx_buf_size;
+	bool sdio_rx_aggr_enable;
+	u16 sdio_rx_block_size;
 	u32 ioport;
 	enum MWIFIEX_HARDWARE_STATUS hw_status;
 	u16 number_of_antenna;
@@ -790,6 +825,8 @@
 	spinlock_t scan_pending_q_lock;
 	/* spin lock for RX processing routine */
 	spinlock_t rx_proc_lock;
+	struct sk_buff_head tx_data_q;
+	atomic_t tx_queued;
 	u32 scan_processing;
 	u16 region_code;
 	struct mwifiex_802_11d_domain_reg domain_reg;
@@ -823,6 +860,7 @@
 	u16 gen_null_pkt;
 	u16 pps_uapsd_mode;
 	u32 pm_wakeup_fw_try;
+	struct timer_list wakeup_timer;
 	u8 is_hs_configured;
 	struct mwifiex_hs_config_param hs_cfg;
 	u8 hs_activated;
@@ -860,11 +898,11 @@
 	bool ext_scan;
 	u8 fw_api_ver;
 	u8 key_api_major_ver, key_api_minor_ver;
-	struct work_struct iface_work;
-	unsigned long iface_work_flags;
 	struct memory_type_mapping *mem_type_mapping_tbl;
 	u8 num_mem_types;
 	u8 curr_mem_idx;
+	void *drv_info_dump;
+	u32 drv_info_size;
 	bool scan_chan_gap_enabled;
 	struct sk_buff_head rx_data_q;
 	struct mwifiex_chan_stats *chan_stats;
@@ -873,6 +911,8 @@
 	bool auto_tdls;
 };
 
+void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter);
+
 int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
 
 void mwifiex_set_trans_start(struct net_device *dev);
@@ -979,7 +1019,7 @@
 				  const u8 *ra_addr);
 void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb);
 void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb);
-int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta);
+int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta, bool init);
 int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
 			    struct mwifiex_scan_cmd_config *scan_cfg);
 void mwifiex_queue_scan_cmd(struct mwifiex_private *priv,
@@ -1140,6 +1180,25 @@
 }
 
 /*
+ * This function returns the first available unused private structure pointer.
+ */
+static inline struct mwifiex_private *
+mwifiex_get_unused_priv(struct mwifiex_adapter *adapter)
+{
+	int i;
+
+	for (i = 0; i < adapter->priv_num; i++) {
+		if (adapter->priv[i]) {
+			if (adapter->priv[i]->bss_mode ==
+			    NL80211_IFTYPE_UNSPECIFIED)
+				break;
+		}
+	}
+
+	return ((i < adapter->priv_num) ? adapter->priv[i] : NULL);
+}
+
+/*
  * This function returns the driver private structure of a network device.
  */
 static inline struct mwifiex_private *
@@ -1230,8 +1289,6 @@
 			       struct ieee80211_channel *chan,
 			       unsigned int duration);
 
-int mwifiex_set_bss_role(struct mwifiex_private *priv, u8 bss_role);
-
 int mwifiex_get_stats_info(struct mwifiex_private *priv,
 			   struct mwifiex_ds_get_stats *log);
 
@@ -1278,6 +1335,7 @@
 
 struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
 					      const char *name,
+					      unsigned char name_assign_type,
 					      enum nl80211_iftype type,
 					      u32 *flags,
 					      struct vif_params *params);
@@ -1291,9 +1349,17 @@
 			 struct cfg80211_beacon_data *data);
 int mwifiex_del_mgmt_ies(struct mwifiex_private *priv);
 u8 *mwifiex_11d_code_2_region(u8 code);
+void mwifiex_uap_set_channel(struct mwifiex_uap_bss_param *bss_cfg,
+			     struct cfg80211_chan_def chandef);
+int mwifiex_config_start_uap(struct mwifiex_private *priv,
+			     struct mwifiex_uap_bss_param *bss_cfg);
 void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
 			      struct mwifiex_sta_node *node);
 
+void mwifiex_init_11h_params(struct mwifiex_private *priv);
+int mwifiex_is_11h_active(struct mwifiex_private *priv);
+int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag);
+
 void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
 			      struct mwifiex_bssdescriptor *bss_desc);
 int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv);
@@ -1324,6 +1390,8 @@
 				       u8 *buf, int len);
 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);
+int mwifiex_get_tdls_list(struct mwifiex_private *priv,
+			  struct tdls_peer_info *buf);
 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,
@@ -1340,6 +1408,11 @@
 void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac);
 void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv);
 void mwifiex_clean_auto_tdls(struct mwifiex_private *priv);
+int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv,
+					  struct host_cmd_ds_command *cmd,
+					  void *data_buf);
+int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv,
+				     struct sk_buff *skb);
 
 void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
 				   void *event_body);
@@ -1347,6 +1420,23 @@
 struct sk_buff *
 mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
 				struct sk_buff *skb, u8 flag, u64 *cookie);
+void mwifiex_dfs_cac_work_queue(struct work_struct *work);
+void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work);
+void mwifiex_abort_cac(struct mwifiex_private *priv);
+int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv,
+				      struct sk_buff *skb);
+
+void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr,
+			   s8 nflr);
+void mwifiex_hist_data_reset(struct mwifiex_private *priv);
+void mwifiex_hist_data_add(struct mwifiex_private *priv,
+			   u8 rx_rate, s8 snr, s8 nflr);
+u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv,
+			    u8 rx_rate, u8 ht_info);
+
+void mwifiex_dump_drv_info(struct mwifiex_adapter *adapter);
+void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags);
+void mwifiex_queue_main_work(struct mwifiex_adapter *adapter);
 
 #ifdef CONFIG_DEBUG_FS
 void mwifiex_debugfs_init(void);
diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c
index c3a20f9..bcc7751 100644
--- a/drivers/net/wireless/mwifiex/pcie.c
+++ b/drivers/net/wireless/mwifiex/pcie.c
@@ -203,7 +203,8 @@
 		card->pcie.reg = data->reg;
 		card->pcie.blksz_fw_dl = data->blksz_fw_dl;
 		card->pcie.tx_buf_size = data->tx_buf_size;
-		card->pcie.supports_fw_dump = data->supports_fw_dump;
+		card->pcie.can_dump_fw = data->can_dump_fw;
+		card->pcie.can_ext_scan = data->can_ext_scan;
 	}
 
 	if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops,
@@ -233,8 +234,6 @@
 	if (!adapter || !adapter->priv_num)
 		return;
 
-	cancel_work_sync(&adapter->iface_work);
-
 	if (user_rmmod) {
 #ifdef CONFIG_PM_SLEEP
 		if (adapter->is_suspended)
@@ -497,7 +496,8 @@
 
 	for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
 		/* Allocate skb here so that firmware can DMA data from it */
-		skb = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE);
+		skb = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE,
+						  GFP_KERNEL | GFP_DMA);
 		if (!skb) {
 			dev_err(adapter->dev,
 				"Unable to allocate skb for RX ring.\n");
@@ -1296,7 +1296,8 @@
 			}
 		}
 
-		skb_tmp = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE);
+		skb_tmp = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE,
+						      GFP_KERNEL | GFP_DMA);
 		if (!skb_tmp) {
 			dev_err(adapter->dev,
 				"Unable to allocate skb.\n");
@@ -1952,8 +1953,8 @@
 		offset += txlen;
 	} while (true);
 
-	dev_dbg(adapter->dev, "info:\nFW download over, size %d bytes\n",
-		offset);
+	dev_notice(adapter->dev,
+		   "info: FW download over, size %d bytes\n", offset);
 
 	ret = 0;
 
@@ -2064,6 +2065,7 @@
 				 * state until cookie is set */
 				adapter->ps_state = PS_STATE_AWAKE;
 				adapter->pm_wakeup_fw_try = false;
+				del_timer(&adapter->wakeup_timer);
 		}
 	}
 }
@@ -2097,7 +2099,7 @@
 		goto exit;
 
 	mwifiex_interrupt_status(adapter);
-	queue_work(adapter->workqueue, &adapter->main_work);
+	mwifiex_queue_main_work(adapter);
 
 exit:
 	return IRQ_HANDLED;
@@ -2269,7 +2271,7 @@
 	int ret;
 	static char *env[] = { "DRIVER=mwifiex_pcie", "EVENT=fw_dump", NULL };
 
-	if (!card->pcie.supports_fw_dump)
+	if (!card->pcie.can_dump_fw)
 		return;
 
 	for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
@@ -2369,25 +2371,26 @@
 	adapter->curr_mem_idx = 0;
 }
 
+static unsigned long iface_work_flags;
+static struct mwifiex_adapter *save_adapter;
 static void mwifiex_pcie_work(struct work_struct *work)
 {
-	struct mwifiex_adapter *adapter =
-			container_of(work, struct mwifiex_adapter, iface_work);
-
 	if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP,
-			       &adapter->iface_work_flags))
-		mwifiex_pcie_fw_dump_work(adapter);
+			       &iface_work_flags))
+		mwifiex_pcie_fw_dump_work(save_adapter);
 }
 
+static DECLARE_WORK(pcie_work, mwifiex_pcie_work);
 /* This function dumps FW information */
 static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter)
 {
-	if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags))
+	save_adapter = adapter;
+	if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags))
 		return;
 
-	set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags);
+	set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags);
 
-	schedule_work(&adapter->iface_work);
+	schedule_work(&pcie_work);
 }
 
 /*
@@ -2562,6 +2565,7 @@
 	adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
 	adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
 	strcpy(adapter->fw_name, card->pcie.firmware);
+	adapter->ext_scan = card->pcie.can_ext_scan;
 
 	return 0;
 }
@@ -2614,7 +2618,6 @@
 	.init_fw_port =			mwifiex_pcie_init_fw_port,
 	.clean_pcie_ring =		mwifiex_clean_pcie_ring_buf,
 	.fw_dump =			mwifiex_pcie_fw_dump,
-	.iface_work =			mwifiex_pcie_work,
 };
 
 /*
@@ -2660,6 +2663,7 @@
 	/* Set the flag as user is removing this module. */
 	user_rmmod = 1;
 
+	cancel_work_sync(&pcie_work);
 	pci_unregister_driver(&mwifiex_pcie);
 }
 
diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h
index 200e8b0..0e7ee8b 100644
--- a/drivers/net/wireless/mwifiex/pcie.h
+++ b/drivers/net/wireless/mwifiex/pcie.h
@@ -205,7 +205,8 @@
 	const struct mwifiex_pcie_card_reg *reg;
 	u16 blksz_fw_dl;
 	u16 tx_buf_size;
-	bool supports_fw_dump;
+	bool can_dump_fw;
+	bool can_ext_scan;
 };
 
 static const struct mwifiex_pcie_device mwifiex_pcie8766 = {
@@ -213,7 +214,8 @@
 	.reg            = &mwifiex_reg_8766,
 	.blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
 	.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
-	.supports_fw_dump = false,
+	.can_dump_fw = false,
+	.can_ext_scan = true,
 };
 
 static const struct mwifiex_pcie_device mwifiex_pcie8897 = {
@@ -221,7 +223,8 @@
 	.reg            = &mwifiex_reg_8897,
 	.blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
 	.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
-	.supports_fw_dump = true,
+	.can_dump_fw = true,
+	.can_ext_scan = true,
 };
 
 struct mwifiex_evt_buf_desc {
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index 984a7a4..0ffdb7c 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -496,10 +496,10 @@
 
 	for (band = 0; (band < IEEE80211_NUM_BANDS) ; band++) {
 
-		if (!priv->wdev->wiphy->bands[band])
+		if (!priv->wdev.wiphy->bands[band])
 			continue;
 
-		sband = priv->wdev->wiphy->bands[band];
+		sband = priv->wdev.wiphy->bands[band];
 
 		for (i = 0; (i < sband->n_channels) ; i++) {
 			ch = &sband->channels[i];
@@ -1429,6 +1429,12 @@
 		return -EBUSY;
 	}
 
+	if (adapter->surprise_removed || adapter->is_cmd_timedout) {
+		dev_err(adapter->dev,
+			"Ignore scan. Card removed or firmware in bad state\n");
+		return -EFAULT;
+	}
+
 	spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
 	adapter->scan_processing = true;
 	spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
@@ -1727,10 +1733,10 @@
 
 		freq = cfp ? cfp->freq : 0;
 
-		chan = ieee80211_get_channel(priv->wdev->wiphy, freq);
+		chan = ieee80211_get_channel(priv->wdev.wiphy, freq);
 
 		if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
-			bss = cfg80211_inform_bss(priv->wdev->wiphy,
+			bss = cfg80211_inform_bss(priv->wdev.wiphy,
 					    chan, CFG80211_BSS_FTYPE_UNKNOWN,
 					    bssid, timestamp,
 					    cap_info_bitmap, beacon_period,
@@ -1742,7 +1748,7 @@
 			    !memcmp(bssid, priv->curr_bss_params.bss_descriptor
 				    .mac_address, ETH_ALEN))
 				mwifiex_update_curr_bss_params(priv, bss);
-			cfg80211_put_bss(priv->wdev->wiphy, bss);
+			cfg80211_put_bss(priv->wdev.wiphy, bss);
 		}
 	} else {
 		dev_dbg(adapter->dev, "missing BSS channel IE\n");
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
index a6ab0a8..923e4b0 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/mwifiex/sdio.c
@@ -47,6 +47,7 @@
 static u8 user_rmmod;
 
 static struct mwifiex_if_ops sdio_ops;
+static unsigned long iface_work_flags;
 
 static struct semaphore add_remove_card_sem;
 
@@ -105,8 +106,9 @@
 		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;
-		card->supports_fw_dump = data->supports_fw_dump;
-		card->auto_tdls = data->auto_tdls;
+		card->can_dump_fw = data->can_dump_fw;
+		card->can_auto_tdls = data->can_auto_tdls;
+		card->can_ext_scan = data->can_ext_scan;
 	}
 
 	sdio_claim_host(func);
@@ -199,8 +201,6 @@
 	if (!adapter || !adapter->priv_num)
 		return;
 
-	cancel_work_sync(&adapter->iface_work);
-
 	if (user_rmmod) {
 		if (adapter->is_suspended)
 			mwifiex_sdio_resume(adapter->dev);
@@ -282,6 +282,9 @@
 #define SDIO_DEVICE_ID_MARVELL_8897   (0x912d)
 /* Device ID for SD8887 */
 #define SDIO_DEVICE_ID_MARVELL_8887   (0x9135)
+/* Device ID for SD8801 */
+#define SDIO_DEVICE_ID_MARVELL_8801   (0x9139)
+
 
 /* WLAN IDs */
 static const struct sdio_device_id mwifiex_ids[] = {
@@ -295,6 +298,8 @@
 		.driver_data = (unsigned long) &mwifiex_sdio_sd8897},
 	{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887),
 		.driver_data = (unsigned long)&mwifiex_sdio_sd8887},
+	{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801),
+		.driver_data = (unsigned long)&mwifiex_sdio_sd8801},
 	{},
 };
 
@@ -986,8 +991,8 @@
 		offset += txlen;
 	} while (true);
 
-	dev_dbg(adapter->dev, "info: FW download over, size %d bytes\n",
-		offset);
+	dev_notice(adapter->dev,
+		   "info: FW download over, size %d bytes\n", offset);
 
 	ret = 0;
 done:
@@ -1037,6 +1042,59 @@
 }
 
 /*
+ * This function decode sdio aggreation pkt.
+ *
+ * Based on the the data block size and pkt_len,
+ * skb data will be decoded to few packets.
+ */
+static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter,
+				    struct sk_buff *skb)
+{
+	u32 total_pkt_len, pkt_len;
+	struct sk_buff *skb_deaggr;
+	u32 pkt_type;
+	u16 blk_size;
+	u8 blk_num;
+	u8 *data;
+
+	data = skb->data;
+	total_pkt_len = skb->len;
+
+	while (total_pkt_len >= (SDIO_HEADER_OFFSET + INTF_HEADER_LEN)) {
+		if (total_pkt_len < adapter->sdio_rx_block_size)
+			break;
+		blk_num = *(data + BLOCK_NUMBER_OFFSET);
+		blk_size = adapter->sdio_rx_block_size * blk_num;
+		if (blk_size > total_pkt_len) {
+			dev_err(adapter->dev, "%s: error in pkt,\t"
+				"blk_num=%d, blk_size=%d, total_pkt_len=%d\n",
+				__func__, blk_num, blk_size, total_pkt_len);
+			break;
+		}
+		pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET));
+		pkt_type = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET +
+					 2));
+		if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) {
+			dev_err(adapter->dev, "%s: error in pkt,\t"
+				"pkt_len=%d, blk_size=%d\n",
+				__func__, pkt_len, blk_size);
+			break;
+		}
+		skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len,
+							 GFP_KERNEL | GFP_DMA);
+		if (!skb_deaggr)
+			break;
+		skb_put(skb_deaggr, pkt_len);
+		memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len);
+		skb_pull(skb_deaggr, INTF_HEADER_LEN);
+
+		mwifiex_handle_rx_packet(adapter, skb_deaggr);
+		data += blk_size;
+		total_pkt_len -= blk_size;
+	}
+}
+
+/*
  * This function decodes a received packet.
  *
  * Based on the type, the packet is treated as either a data, or
@@ -1049,11 +1107,28 @@
 	u8 *cmd_buf;
 	__le16 *curr_ptr = (__le16 *)skb->data;
 	u16 pkt_len = le16_to_cpu(*curr_ptr);
+	struct mwifiex_rxinfo *rx_info;
 
-	skb_trim(skb, pkt_len);
-	skb_pull(skb, INTF_HEADER_LEN);
+	if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) {
+		skb_trim(skb, pkt_len);
+		skb_pull(skb, INTF_HEADER_LEN);
+	}
 
 	switch (upld_typ) {
+	case MWIFIEX_TYPE_AGGR_DATA:
+		dev_dbg(adapter->dev, "info: --- Rx: Aggr Data packet ---\n");
+		rx_info = MWIFIEX_SKB_RXCB(skb);
+		rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA;
+		if (adapter->rx_work_enabled) {
+			skb_queue_tail(&adapter->rx_data_q, skb);
+			atomic_inc(&adapter->rx_pending);
+			adapter->data_received = true;
+		} else {
+			mwifiex_deaggr_sdio_pkt(adapter, skb);
+			dev_kfree_skb_any(skb);
+		}
+		break;
+
 	case MWIFIEX_TYPE_DATA:
 		dev_dbg(adapter->dev, "info: --- Rx: Data packet ---\n");
 		if (adapter->rx_work_enabled) {
@@ -1121,17 +1196,17 @@
  * provided there is space left, processed and finally uploaded.
  */
 static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
-					     struct sk_buff *skb, u8 port)
+					     u16 rx_len, u8 port)
 {
 	struct sdio_mmc_card *card = adapter->card;
 	s32 f_do_rx_aggr = 0;
 	s32 f_do_rx_cur = 0;
 	s32 f_aggr_cur = 0;
+	s32 f_post_aggr_cur = 0;
 	struct sk_buff *skb_deaggr;
-	u32 pind;
-	u32 pkt_len, pkt_type, mport;
+	struct sk_buff *skb = NULL;
+	u32 pkt_len, pkt_type, mport, pind;
 	u8 *curr_ptr;
-	u32 rx_len = skb->len;
 
 	if ((card->has_control_mask) && (port == CTRL_PORT)) {
 		/* Read the command Resp without aggr */
@@ -1158,12 +1233,12 @@
 		dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__);
 
 		if (MP_RX_AGGR_IN_PROGRESS(card)) {
-			if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len)) {
+			if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) {
 				f_aggr_cur = 1;
 			} else {
 				/* No room in Aggr buf, do rx aggr now */
 				f_do_rx_aggr = 1;
-				f_do_rx_cur = 1;
+				f_post_aggr_cur = 1;
 			}
 		} else {
 			/* Rx aggr not in progress */
@@ -1176,7 +1251,7 @@
 
 		if (MP_RX_AGGR_IN_PROGRESS(card)) {
 			f_do_rx_aggr = 1;
-			if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len))
+			if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len))
 				f_aggr_cur = 1;
 			else
 				/* No room in Aggr buf, do rx aggr now */
@@ -1189,7 +1264,7 @@
 	if (f_aggr_cur) {
 		dev_dbg(adapter->dev, "info: current packet aggregation\n");
 		/* Curr pkt can be aggregated */
-		mp_rx_aggr_setup(card, skb, port);
+		mp_rx_aggr_setup(card, rx_len, port);
 
 		if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) ||
 		    mp_rx_aggr_port_limit_reached(card)) {
@@ -1232,16 +1307,29 @@
 		curr_ptr = card->mpa_rx.buf;
 
 		for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) {
+			u32 *len_arr = card->mpa_rx.len_arr;
 
 			/* get curr PKT len & type */
 			pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]);
 			pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]);
 
 			/* copy pkt to deaggr buf */
-			skb_deaggr = card->mpa_rx.skb_arr[pind];
+			skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind],
+								 GFP_KERNEL |
+								 GFP_DMA);
+			if (!skb_deaggr) {
+				dev_err(adapter->dev, "skb allocation failure drop pkt len=%d type=%d\n",
+					pkt_len, pkt_type);
+				curr_ptr += len_arr[pind];
+				continue;
+			}
 
-			if ((pkt_type == MWIFIEX_TYPE_DATA) && (pkt_len <=
-					 card->mpa_rx.len_arr[pind])) {
+			skb_put(skb_deaggr, len_arr[pind]);
+
+			if ((pkt_type == MWIFIEX_TYPE_DATA ||
+			     (pkt_type == MWIFIEX_TYPE_AGGR_DATA &&
+			      adapter->sdio_rx_aggr_enable)) &&
+			    (pkt_len <= len_arr[pind])) {
 
 				memcpy(skb_deaggr->data, curr_ptr, pkt_len);
 
@@ -1251,13 +1339,15 @@
 				mwifiex_decode_rx_packet(adapter, skb_deaggr,
 							 pkt_type);
 			} else {
-				dev_err(adapter->dev, "wrong aggr pkt:"
-					" type=%d len=%d max_len=%d\n",
+				dev_err(adapter->dev, " drop wrong aggr pkt:\t"
+					"sdio_single_port_rx_aggr=%d\t"
+					"type=%d len=%d max_len=%d\n",
+					adapter->sdio_rx_aggr_enable,
 					pkt_type, pkt_len,
-					card->mpa_rx.len_arr[pind]);
+					len_arr[pind]);
 				dev_kfree_skb_any(skb_deaggr);
 			}
-			curr_ptr += card->mpa_rx.len_arr[pind];
+			curr_ptr += len_arr[pind];
 		}
 		MP_RX_AGGR_BUF_RESET(card);
 	}
@@ -1267,28 +1357,46 @@
 		dev_dbg(adapter->dev, "info: RX: port: %d, rx_len: %d\n",
 			port, rx_len);
 
+		skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA);
+		if (!skb) {
+			dev_err(adapter->dev, "single skb allocated fail,\t"
+				"drop pkt port=%d len=%d\n", port, rx_len);
+			if (mwifiex_sdio_card_to_host(adapter, &pkt_type,
+						      card->mpa_rx.buf, rx_len,
+						      adapter->ioport + port))
+				goto error;
+			return 0;
+		}
+
+		skb_put(skb, rx_len);
+
 		if (mwifiex_sdio_card_to_host(adapter, &pkt_type,
 					      skb->data, skb->len,
 					      adapter->ioport + port))
 			goto error;
+		if (!adapter->sdio_rx_aggr_enable &&
+		    pkt_type == MWIFIEX_TYPE_AGGR_DATA) {
+			dev_err(adapter->dev, "drop wrong pkt type %d\t"
+				"current SDIO RX Aggr not enabled\n",
+				pkt_type);
+			dev_kfree_skb_any(skb);
+			return 0;
+		}
 
 		mwifiex_decode_rx_packet(adapter, skb, pkt_type);
 	}
-
-	return 0;
-
-error:
-	if (MP_RX_AGGR_IN_PROGRESS(card)) {
-		/* Multiport-aggregation transfer failed - cleanup */
-		for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) {
-			/* copy pkt to deaggr buf */
-			skb_deaggr = card->mpa_rx.skb_arr[pind];
-			dev_kfree_skb_any(skb_deaggr);
-		}
-		MP_RX_AGGR_BUF_RESET(card);
+	if (f_post_aggr_cur) {
+		dev_dbg(adapter->dev, "info: current packet aggregation\n");
+		/* Curr pkt can be aggregated */
+		mp_rx_aggr_setup(card, rx_len, port);
 	}
 
-	if (f_do_rx_cur)
+	return 0;
+error:
+	if (MP_RX_AGGR_IN_PROGRESS(card))
+		MP_RX_AGGR_BUF_RESET(card);
+
+	if (f_do_rx_cur && skb)
 		/* Single transfer pending. Free curr buff also */
 		dev_kfree_skb_any(skb);
 
@@ -1350,8 +1458,9 @@
 		     MWIFIEX_RX_DATA_BUF_SIZE)
 			return -1;
 		rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
+		dev_dbg(adapter->dev, "info: rx_len = %d\n", rx_len);
 
-		skb = dev_alloc_skb(rx_len);
+		skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA);
 		if (!skb)
 			return -1;
 
@@ -1441,27 +1550,16 @@
 				 1) / MWIFIEX_SDIO_BLOCK_SIZE;
 			if (rx_len <= INTF_HEADER_LEN ||
 			    (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
-			     MWIFIEX_RX_DATA_BUF_SIZE) {
+			     card->mpa_rx.buf_size) {
 				dev_err(adapter->dev, "invalid rx_len=%d\n",
 					rx_len);
 				return -1;
 			}
+
 			rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
+			dev_dbg(adapter->dev, "info: rx_len = %d\n", rx_len);
 
-			skb = dev_alloc_skb(rx_len);
-
-			if (!skb) {
-				dev_err(adapter->dev, "%s: failed to alloc skb",
-					__func__);
-				return -1;
-			}
-
-			skb_put(skb, rx_len);
-
-			dev_dbg(adapter->dev, "info: rx_len = %d skb->len = %d\n",
-				rx_len, skb->len);
-
-			if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb,
+			if (mwifiex_sdio_card_to_host_mp_aggr(adapter, rx_len,
 							      port)) {
 				dev_err(adapter->dev, "card_to_host_mpa failed:"
 					" int status=%#x\n", sdio_ireg);
@@ -1729,6 +1827,7 @@
 				   u32 mpa_tx_buf_size, u32 mpa_rx_buf_size)
 {
 	struct sdio_mmc_card *card = adapter->card;
+	u32 rx_buf_size;
 	int ret = 0;
 
 	card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL);
@@ -1739,13 +1838,15 @@
 
 	card->mpa_tx.buf_size = mpa_tx_buf_size;
 
-	card->mpa_rx.buf = kzalloc(mpa_rx_buf_size, GFP_KERNEL);
+	rx_buf_size = max_t(u32, mpa_rx_buf_size,
+			    (u32)SDIO_MAX_AGGR_BUF_SIZE);
+	card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL);
 	if (!card->mpa_rx.buf) {
 		ret = -1;
 		goto error;
 	}
 
-	card->mpa_rx.buf_size = mpa_rx_buf_size;
+	card->mpa_rx.buf_size = rx_buf_size;
 
 error:
 	if (ret) {
@@ -1881,7 +1982,8 @@
 		return -1;
 	}
 
-	adapter->auto_tdls = card->auto_tdls;
+	adapter->auto_tdls = card->can_auto_tdls;
+	adapter->ext_scan = card->can_ext_scan;
 	return ret;
 }
 
@@ -1943,6 +2045,7 @@
 		port, card->mp_data_port_mask);
 }
 
+static struct mwifiex_adapter *save_adapter;
 static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter)
 {
 	struct sdio_mmc_card *card = adapter->card;
@@ -1958,8 +2061,8 @@
 
 	pr_err("Resetting card...\n");
 	mmc_remove_host(target);
-	/* 20ms delay is based on experiment with sdhci controller */
-	mdelay(20);
+	/* 200ms delay is based on experiment with sdhci controller */
+	mdelay(200);
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
 	target->rescan_entered = 0; /* rescan non-removable cards */
 #endif
@@ -2013,10 +2116,8 @@
 }
 
 /* This function dump firmware memory to file */
-static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
+static void mwifiex_sdio_fw_dump_work(struct mwifiex_adapter *adapter)
 {
-	struct mwifiex_adapter *adapter =
-			container_of(work, struct mwifiex_adapter, iface_work);
 	struct sdio_mmc_card *card = adapter->card;
 	int ret = 0;
 	unsigned int reg, reg_start, reg_end;
@@ -2025,7 +2126,9 @@
 	u32 memory_size;
 	static char *env[] = { "DRIVER=mwifiex_sdio", "EVENT=fw_dump", NULL };
 
-	if (!card->supports_fw_dump)
+	mwifiex_dump_drv_info(adapter);
+
+	if (!card->can_dump_fw)
 		return;
 
 	for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
@@ -2136,36 +2239,129 @@
 
 static void mwifiex_sdio_work(struct work_struct *work)
 {
-	struct mwifiex_adapter *adapter =
-			container_of(work, struct mwifiex_adapter, iface_work);
-
-	if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET,
-			       &adapter->iface_work_flags))
-		mwifiex_sdio_card_reset_work(adapter);
 	if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP,
-			       &adapter->iface_work_flags))
-		mwifiex_sdio_fw_dump_work(work);
+			       &iface_work_flags))
+		mwifiex_sdio_fw_dump_work(save_adapter);
+	if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET,
+			       &iface_work_flags))
+		mwifiex_sdio_card_reset_work(save_adapter);
 }
 
+static DECLARE_WORK(sdio_work, mwifiex_sdio_work);
 /* This function resets the card */
 static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter)
 {
-	if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags))
+	save_adapter = adapter;
+	if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags))
 		return;
 
-	set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags);
+	set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags);
 
-	schedule_work(&adapter->iface_work);
+	schedule_work(&sdio_work);
 }
 
 /* This function dumps FW information */
 static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter)
 {
-	if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags))
+	save_adapter = adapter;
+	if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags))
 		return;
 
-	set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags);
-	schedule_work(&adapter->iface_work);
+	set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags);
+	schedule_work(&sdio_work);
+}
+
+/* Function to dump SDIO function registers and SDIO scratch registers in case
+ * of FW crash
+ */
+static int
+mwifiex_sdio_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf)
+{
+	char *p = drv_buf;
+	struct sdio_mmc_card *cardp = adapter->card;
+	int ret = 0;
+	u8 count, func, data, index = 0, size = 0;
+	u8 reg, reg_start, reg_end;
+	char buf[256], *ptr;
+
+	if (!p)
+		return 0;
+
+	dev_info(adapter->dev, "SDIO register DUMP START\n");
+
+	mwifiex_pm_wakeup_card(adapter);
+
+	sdio_claim_host(cardp->func);
+
+	for (count = 0; count < 5; count++) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+
+		switch (count) {
+		case 0:
+			/* Read the registers of SDIO function0 */
+			func = count;
+			reg_start = 0;
+			reg_end = 9;
+			break;
+		case 1:
+			/* Read the registers of SDIO function1 */
+			func = count;
+			reg_start = cardp->reg->func1_dump_reg_start;
+			reg_end = cardp->reg->func1_dump_reg_end;
+			break;
+		case 2:
+			index = 0;
+			func = 1;
+			reg_start = cardp->reg->func1_spec_reg_table[index++];
+			size = cardp->reg->func1_spec_reg_num;
+			reg_end = cardp->reg->func1_spec_reg_table[size-1];
+			break;
+		default:
+			/* Read the scratch registers of SDIO function1 */
+			if (count == 4)
+				mdelay(100);
+			func = 1;
+			reg_start = cardp->reg->func1_scratch_reg;
+			reg_end = reg_start + MWIFIEX_SDIO_SCRATCH_SIZE;
+		}
+
+		if (count != 2)
+			ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ",
+				       func, reg_start, reg_end);
+		else
+			ptr += sprintf(ptr, "SDIO Func%d: ", func);
+
+		for (reg = reg_start; reg <= reg_end;) {
+			if (func == 0)
+				data = sdio_f0_readb(cardp->func, reg, &ret);
+			else
+				data = sdio_readb(cardp->func, reg, &ret);
+
+			if (count == 2)
+				ptr += sprintf(ptr, "(%#x) ", reg);
+			if (!ret) {
+				ptr += sprintf(ptr, "%02x ", data);
+			} else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+
+			if (count == 2 && reg < reg_end)
+				reg = cardp->reg->func1_spec_reg_table[index++];
+			else
+				reg++;
+		}
+
+		dev_info(adapter->dev, "%s\n", buf);
+		p += sprintf(p, "%s\n", buf);
+	}
+
+	sdio_release_host(cardp->func);
+
+	dev_info(adapter->dev, "SDIO register DUMP END\n");
+
+	return p - drv_buf;
 }
 
 static struct mwifiex_if_ops sdio_ops = {
@@ -2188,8 +2384,9 @@
 	.cmdrsp_complete = mwifiex_sdio_cmdrsp_complete,
 	.event_complete = mwifiex_sdio_event_complete,
 	.card_reset = mwifiex_sdio_card_reset,
-	.iface_work = mwifiex_sdio_work,
 	.fw_dump = mwifiex_sdio_fw_dump,
+	.reg_dump = mwifiex_sdio_reg_dump,
+	.deaggr_pkt = mwifiex_deaggr_sdio_pkt,
 };
 
 /*
@@ -2226,6 +2423,7 @@
 
 	/* Set the flag as user is removing this module. */
 	user_rmmod = 1;
+	cancel_work_sync(&sdio_work);
 
 	sdio_unregister_driver(&mwifiex_sdio);
 }
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h
index 54c0715..6f645cf 100644
--- a/drivers/net/wireless/mwifiex/sdio.h
+++ b/drivers/net/wireless/mwifiex/sdio.h
@@ -34,6 +34,7 @@
 #define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin"
 #define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin"
 #define SD8887_DEFAULT_FW_NAME "mrvl/sd8887_uapsta.bin"
+#define SD8801_DEFAULT_FW_NAME "mrvl/sd8801_uapsta.bin"
 
 #define BLOCK_MODE	1
 #define BYTE_MODE	0
@@ -44,6 +45,9 @@
 
 #define MWIFIEX_SDIO_BYTE_MODE_MASK	0x80000000
 
+#define MWIFIEX_MAX_FUNC2_REG_NUM	13
+#define MWIFIEX_SDIO_SCRATCH_SIZE	10
+
 #define SDIO_MPA_ADDR_BASE		0x1000
 #define CTRL_PORT			0
 #define CTRL_PORT_MASK			0x0001
@@ -63,6 +67,8 @@
 
 #define MWIFIEX_MP_AGGR_BUF_SIZE_16K	(16384)
 #define MWIFIEX_MP_AGGR_BUF_SIZE_32K	(32768)
+/* we leave one block of 256 bytes for DMA alignment*/
+#define MWIFIEX_MP_AGGR_BUF_SIZE_MAX    (65280)
 
 /* Misc. Config Register : Auto Re-enable interrupts */
 #define AUTO_RE_ENABLE_INT              BIT(4)
@@ -219,6 +225,11 @@
 	u8 fw_dump_ctrl;
 	u8 fw_dump_start;
 	u8 fw_dump_end;
+	u8 func1_dump_reg_start;
+	u8 func1_dump_reg_end;
+	u8 func1_scratch_reg;
+	u8 func1_spec_reg_num;
+	u8 func1_spec_reg_table[MWIFIEX_MAX_FUNC2_REG_NUM];
 };
 
 struct sdio_mmc_card {
@@ -229,9 +240,6 @@
 	const struct mwifiex_sdio_card_reg *reg;
 	u8 max_ports;
 	u8 mp_agg_pkt_limit;
-	bool supports_sdio_new_mode;
-	bool has_control_mask;
-	bool supports_fw_dump;
 	u16 tx_buf_size;
 	u32 mp_tx_agg_buf_size;
 	u32 mp_rx_agg_buf_size;
@@ -246,7 +254,11 @@
 	u8 curr_wr_port;
 
 	u8 *mp_regs;
-	u8 auto_tdls;
+	bool supports_sdio_new_mode;
+	bool has_control_mask;
+	bool can_dump_fw;
+	bool can_auto_tdls;
+	bool can_ext_scan;
 
 	struct mwifiex_sdio_mpa_tx mpa_tx;
 	struct mwifiex_sdio_mpa_rx mpa_rx;
@@ -257,13 +269,14 @@
 	const struct mwifiex_sdio_card_reg *reg;
 	u8 max_ports;
 	u8 mp_agg_pkt_limit;
-	bool supports_sdio_new_mode;
-	bool has_control_mask;
-	bool supports_fw_dump;
 	u16 tx_buf_size;
 	u32 mp_tx_agg_buf_size;
 	u32 mp_rx_agg_buf_size;
-	u8 auto_tdls;
+	bool supports_sdio_new_mode;
+	bool has_control_mask;
+	bool can_dump_fw;
+	bool can_auto_tdls;
+	bool can_ext_scan;
 };
 
 static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = {
@@ -291,6 +304,11 @@
 	.rd_len_p0_l = 0x08,
 	.rd_len_p0_u = 0x09,
 	.card_misc_cfg_reg = 0x6c,
+	.func1_dump_reg_start = 0x0,
+	.func1_dump_reg_end = 0x9,
+	.func1_scratch_reg = 0x60,
+	.func1_spec_reg_num = 5,
+	.func1_spec_reg_table = {0x28, 0x30, 0x34, 0x38, 0x3c},
 };
 
 static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = {
@@ -335,6 +353,12 @@
 	.fw_dump_ctrl = 0xe2,
 	.fw_dump_start = 0xe3,
 	.fw_dump_end = 0xea,
+	.func1_dump_reg_start = 0x0,
+	.func1_dump_reg_end = 0xb,
+	.func1_scratch_reg = 0xc0,
+	.func1_spec_reg_num = 8,
+	.func1_spec_reg_table = {0x4C, 0x50, 0x54, 0x55, 0x58,
+				 0x59, 0x5c, 0x5d},
 };
 
 static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = {
@@ -376,6 +400,13 @@
 	.cmd_cfg_1 = 0xc5,
 	.cmd_cfg_2 = 0xc6,
 	.cmd_cfg_3 = 0xc7,
+	.func1_dump_reg_start = 0x10,
+	.func1_dump_reg_end = 0x17,
+	.func1_scratch_reg = 0x90,
+	.func1_spec_reg_num = 13,
+	.func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60,
+				 0x61, 0x62, 0x64, 0x65, 0x66,
+				 0x68, 0x69, 0x6a},
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
@@ -383,13 +414,14 @@
 	.reg = &mwifiex_reg_sd87xx,
 	.max_ports = 16,
 	.mp_agg_pkt_limit = 8,
-	.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,
-	.supports_fw_dump = false,
-	.auto_tdls = false,
+	.supports_sdio_new_mode = false,
+	.has_control_mask = true,
+	.can_dump_fw = false,
+	.can_auto_tdls = false,
+	.can_ext_scan = false,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
@@ -397,13 +429,14 @@
 	.reg = &mwifiex_reg_sd87xx,
 	.max_ports = 16,
 	.mp_agg_pkt_limit = 8,
-	.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,
-	.supports_fw_dump = false,
-	.auto_tdls = false,
+	.supports_sdio_new_mode = false,
+	.has_control_mask = true,
+	.can_dump_fw = false,
+	.can_auto_tdls = false,
+	.can_ext_scan = true,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
@@ -411,41 +444,59 @@
 	.reg = &mwifiex_reg_sd87xx,
 	.max_ports = 16,
 	.mp_agg_pkt_limit = 8,
+	.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,
+	.supports_sdio_new_mode = false,
+	.has_control_mask = true,
+	.can_dump_fw = false,
+	.can_auto_tdls = false,
+	.can_ext_scan = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
+	.firmware = SD8897_DEFAULT_FW_NAME,
+	.reg = &mwifiex_reg_sd8897,
+	.max_ports = 32,
+	.mp_agg_pkt_limit = 16,
+	.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
+	.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
+	.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
+	.supports_sdio_new_mode = true,
+	.has_control_mask = false,
+	.can_dump_fw = true,
+	.can_auto_tdls = false,
+	.can_ext_scan = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = {
+	.firmware = SD8887_DEFAULT_FW_NAME,
+	.reg = &mwifiex_reg_sd8887,
+	.max_ports = 32,
+	.mp_agg_pkt_limit = 16,
+	.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+	.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
+	.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
+	.supports_sdio_new_mode = true,
+	.has_control_mask = false,
+	.can_dump_fw = false,
+	.can_auto_tdls = true,
+	.can_ext_scan = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = {
+	.firmware = SD8801_DEFAULT_FW_NAME,
+	.reg = &mwifiex_reg_sd87xx,
+	.max_ports = 16,
+	.mp_agg_pkt_limit = 8,
 	.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,
-	.supports_fw_dump = false,
-	.auto_tdls = false,
-};
-
-static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
-	.firmware = SD8897_DEFAULT_FW_NAME,
-	.reg = &mwifiex_reg_sd8897,
-	.max_ports = 32,
-	.mp_agg_pkt_limit = 16,
-	.supports_sdio_new_mode = true,
-	.has_control_mask = false,
-	.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,
-	.supports_fw_dump = true,
-	.auto_tdls = false,
-};
-
-static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = {
-	.firmware = SD8887_DEFAULT_FW_NAME,
-	.reg = &mwifiex_reg_sd8887,
-	.max_ports = 32,
-	.mp_agg_pkt_limit = 16,
-	.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,
-	.supports_fw_dump = false,
-	.auto_tdls = true,
+	.can_dump_fw = false,
+	.can_auto_tdls = false,
+	.can_ext_scan = true,
 };
 
 /*
@@ -522,9 +573,9 @@
 
 /* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
 static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card,
-				    struct sk_buff *skb, u8 port)
+				    u16 rx_len, u8 port)
 {
-	card->mpa_rx.buf_len += skb->len;
+	card->mpa_rx.buf_len += rx_len;
 
 	if (!card->mpa_rx.pkt_cnt)
 		card->mpa_rx.start_port = port;
@@ -537,8 +588,8 @@
 		else
 			card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1);
 	}
-	card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = skb;
-	card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = skb->len;
+	card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = NULL;
+	card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = rx_len;
 	card->mpa_rx.pkt_cnt++;
 }
 #endif /* _MWIFIEX_SDIO_H */
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c
index 1c2ca29..49422f2 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/mwifiex/sta_cmd.c
@@ -26,6 +26,10 @@
 #include "11n.h"
 #include "11ac.h"
 
+static bool disable_auto_ds;
+module_param(disable_auto_ds, bool, 0);
+MODULE_PARM_DESC(disable_auto_ds,
+		 "deepsleep enabled=0(default), deepsleep disabled=1");
 /*
  * This function prepares command to set/get RSSI information.
  *
@@ -1366,22 +1370,29 @@
 		    struct mwifiex_ds_mef_cfg *mef)
 {
 	struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg;
+	struct mwifiex_fw_mef_entry *mef_entry = NULL;
 	u8 *pos = (u8 *)mef_cfg;
+	u16 i;
 
 	cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG);
 
 	mef_cfg->criteria = cpu_to_le32(mef->criteria);
 	mef_cfg->num_entries = cpu_to_le16(mef->num_entries);
 	pos += sizeof(*mef_cfg);
-	mef_cfg->mef_entry->mode = mef->mef_entry->mode;
-	mef_cfg->mef_entry->action = mef->mef_entry->action;
-	pos += sizeof(*(mef_cfg->mef_entry));
 
-	if (mwifiex_cmd_append_rpn_expression(priv, mef->mef_entry, &pos))
-		return -1;
+	for (i = 0; i < mef->num_entries; i++) {
+		mef_entry = (struct mwifiex_fw_mef_entry *)pos;
+		mef_entry->mode = mef->mef_entry[i].mode;
+		mef_entry->action = mef->mef_entry[i].action;
+		pos += sizeof(*mef_cfg->mef_entry);
 
-	mef_cfg->mef_entry->exprsize =
-			cpu_to_le16(pos - mef_cfg->mef_entry->expr);
+		if (mwifiex_cmd_append_rpn_expression(priv,
+						      &mef->mef_entry[i], &pos))
+			return -1;
+
+		mef_entry->exprsize =
+			cpu_to_le16(pos - mef_entry->expr);
+	}
 	cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN);
 
 	return 0;
@@ -1660,6 +1671,25 @@
 
 	return 0;
 }
+
+/* This function prepares command of sdio rx aggr info. */
+static int mwifiex_cmd_sdio_rx_aggr_cfg(struct host_cmd_ds_command *cmd,
+					u16 cmd_action, void *data_buf)
+{
+	struct host_cmd_sdio_sp_rx_aggr_cfg *cfg =
+					&cmd->params.sdio_rx_aggr_cfg;
+
+	cmd->command = cpu_to_le16(HostCmd_CMD_SDIO_SP_RX_AGGR_CFG);
+	cmd->size =
+		cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) +
+			    S_DS_GEN);
+	cfg->action = cmd_action;
+	if (cmd_action == HostCmd_ACT_GEN_SET)
+		cfg->enable = *(u8 *)data_buf;
+
+	return 0;
+}
+
 /*
  * This function prepares the commands before sending them to the firmware.
  *
@@ -1893,6 +1923,14 @@
 	case HostCmd_CMD_TDLS_OPER:
 		ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf);
 		break;
+	case HostCmd_CMD_CHAN_REPORT_REQUEST:
+		ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr,
+							    data_buf);
+		break;
+	case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG:
+		ret = mwifiex_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action,
+						   data_buf);
+		break;
 	default:
 		dev_err(priv->adapter->dev,
 			"PREP_CMD: unknown cmd- %#x\n", cmd_no);
@@ -1907,6 +1945,8 @@
  *
  * This is called after firmware download to bring the card to
  * working state.
+ * Function is also called during reinitialization of virtual
+ * interfaces.
  *
  * The following commands are issued sequentially -
  *      - Set PCI-Express host buffer configuration (PCIE only)
@@ -1921,7 +1961,7 @@
  *      - Set 11d control
  *      - Set MAC control (this must be the last command to initialize firmware)
  */
-int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
+int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init)
 {
 	struct mwifiex_adapter *adapter = priv->adapter;
 	int ret;
@@ -1930,6 +1970,7 @@
 	struct mwifiex_ds_auto_ds auto_ds;
 	enum state_11d_t state_11d;
 	struct mwifiex_ds_11n_tx_cfg tx_cfg;
+	u8 sdio_sp_rx_aggr_enable;
 
 	if (first_sta) {
 		if (priv->adapter->iface_type == MWIFIEX_PCIE) {
@@ -1973,6 +2014,22 @@
 		if (ret)
 			return -1;
 
+		/** Set SDIO Single Port RX Aggr Info */
+		if (priv->adapter->iface_type == MWIFIEX_SDIO &&
+		    ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info)) {
+			sdio_sp_rx_aggr_enable = true;
+			ret = mwifiex_send_cmd(priv,
+					       HostCmd_CMD_SDIO_SP_RX_AGGR_CFG,
+					       HostCmd_ACT_GEN_SET, 0,
+					       &sdio_sp_rx_aggr_enable,
+					       true);
+			if (ret) {
+				dev_err(priv->adapter->dev,
+					"error while enabling SP aggregation..disable it");
+				adapter->sdio_rx_aggr_enable = false;
+			}
+		}
+
 		/* Reconfigure tx buf size */
 		ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
 				       HostCmd_ACT_GEN_SET, 0,
@@ -2031,7 +2088,8 @@
 	if (ret)
 		return -1;
 
-	if (first_sta && priv->adapter->iface_type != MWIFIEX_USB &&
+	if (!disable_auto_ds &&
+	    first_sta && priv->adapter->iface_type != MWIFIEX_USB &&
 	    priv->bss_type != MWIFIEX_BSS_TYPE_UAP) {
 		/* Enable auto deep sleep */
 		auto_ds.auto_ds = DEEP_SLEEP_ON;
@@ -2054,9 +2112,6 @@
 				"11D: failed to enable 11D\n");
 	}
 
-	/* set last_init_cmd before sending the command */
-	priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG;
-
 	/* Send cmd to FW to configure 11n specific configuration
 	 * (Short GI, Channel BW, Green field support etc.) for transmit
 	 */
@@ -2064,7 +2119,11 @@
 	ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_CFG,
 			       HostCmd_ACT_GEN_SET, 0, &tx_cfg, true);
 
-	ret = -EINPROGRESS;
+	if (init) {
+		/* set last_init_cmd before sending the command */
+		priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG;
+		ret = -EINPROGRESS;
+	}
 
 	return ret;
 }
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c
index b65e101..88dc6b6 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -90,6 +90,10 @@
 	case HostCmd_CMD_MAC_CONTROL:
 		break;
 
+	case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG:
+		dev_err(priv->adapter->dev, "SDIO RX single-port aggregation Not support\n");
+		break;
+
 	default:
 		break;
 	}
@@ -248,6 +252,8 @@
 			le32_to_cpu(get_log->wep_icv_err_cnt[2]);
 		stats->wep_icv_error[3] =
 			le32_to_cpu(get_log->wep_icv_err_cnt[3]);
+		stats->bcn_rcv_cnt = le32_to_cpu(get_log->bcn_rcv_cnt);
+		stats->bcn_miss_cnt = le32_to_cpu(get_log->bcn_miss_cnt);
 	}
 
 	return 0;
@@ -941,6 +947,20 @@
 	return 0;
 }
 
+/** This Function handles the command response of sdio rx aggr */
+static int mwifiex_ret_sdio_rx_aggr_cfg(struct mwifiex_private *priv,
+					struct host_cmd_ds_command *resp)
+{
+	struct mwifiex_adapter *adapter = priv->adapter;
+	struct host_cmd_sdio_sp_rx_aggr_cfg *cfg =
+				&resp->params.sdio_rx_aggr_cfg;
+
+	adapter->sdio_rx_aggr_enable = cfg->enable;
+	adapter->sdio_rx_block_size = le16_to_cpu(cfg->block_size);
+
+	return 0;
+}
+
 /*
  * This function handles the command responses.
  *
@@ -1103,6 +1123,9 @@
 	case HostCmd_CMD_UAP_SYS_CONFIG:
 		break;
 	case HostCmd_CMD_UAP_BSS_START:
+		adapter->tx_lock_flag = false;
+		adapter->pps_uapsd_mode = false;
+		adapter->delay_null_pkt = false;
 		priv->bss_started = 1;
 		break;
 	case HostCmd_CMD_UAP_BSS_STOP:
@@ -1117,6 +1140,11 @@
 	case HostCmd_CMD_TDLS_OPER:
 		ret = mwifiex_ret_tdls_oper(priv, resp);
 		break;
+	case HostCmd_CMD_CHAN_REPORT_REQUEST:
+		break;
+	case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG:
+		ret = mwifiex_ret_sdio_rx_aggr_cfg(priv, resp);
+		break;
 	default:
 		dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
 			resp->command);
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index b8c171d..0dc7a1d 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -90,6 +90,10 @@
 	priv->is_data_rate_auto = true;
 	priv->data_rate = 0;
 
+	if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
+	     GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && priv->hist_data)
+		mwifiex_hist_data_reset(priv);
+
 	if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
 		priv->adhoc_state = ADHOC_IDLE;
 		priv->adhoc_is_link_sensed = false;
@@ -131,7 +135,7 @@
 		cfg80211_disconnected(priv->netdev, reason_code, NULL, 0,
 				      GFP_KERNEL);
 	}
-	memset(priv->cfg_bssid, 0, ETH_ALEN);
+	eth_zero_addr(priv->cfg_bssid);
 
 	mwifiex_stop_net_dev_queue(priv->netdev, adapter);
 	if (netif_carrier_ok(priv->netdev))
@@ -308,6 +312,7 @@
 					adapter->ps_state = PS_STATE_AWAKE;
 					adapter->pm_wakeup_card_req = false;
 					adapter->pm_wakeup_fw_try = false;
+					del_timer(&adapter->wakeup_timer);
 					break;
 				}
 				if (!mwifiex_send_null_packet
@@ -322,6 +327,7 @@
 		adapter->ps_state = PS_STATE_AWAKE;
 		adapter->pm_wakeup_card_req = false;
 		adapter->pm_wakeup_fw_try = false;
+		del_timer(&adapter->wakeup_timer);
 
 		break;
 
@@ -480,7 +486,7 @@
 
 	case EVENT_REMAIN_ON_CHAN_EXPIRED:
 		dev_dbg(adapter->dev, "event: Remain on channel expired\n");
-		cfg80211_remain_on_channel_expired(priv->wdev,
+		cfg80211_remain_on_channel_expired(&priv->wdev,
 						   priv->roc_cfg.cookie,
 						   &priv->roc_cfg.chan,
 						   GFP_ATOMIC);
@@ -509,6 +515,16 @@
 		mwifiex_parse_tx_status_event(priv, adapter->event_body);
 		break;
 
+	case EVENT_CHANNEL_REPORT_RDY:
+		dev_dbg(adapter->dev, "event: Channel Report\n");
+		ret = mwifiex_11h_handle_chanrpt_ready(priv,
+						       adapter->event_skb);
+		break;
+	case EVENT_RADAR_DETECTED:
+		dev_dbg(adapter->dev, "event: Radar detected\n");
+		ret = mwifiex_11h_handle_radar_detected(priv,
+							adapter->event_skb);
+		break;
 	default:
 		dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
 			eventcause);
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index 1626868..0599e41 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -219,7 +219,7 @@
 
 	if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) {
 		rcu_read_unlock();
-		wiphy_dbg(priv->wdev->wiphy,
+		wiphy_dbg(priv->wdev.wiphy,
 			  "11D: skip setting domain info in FW\n");
 		return 0;
 	}
@@ -902,9 +902,12 @@
 	if (wep_key->key_length) {
 		void *enc_key;
 
-		if (encrypt_key->key_disable)
+		if (encrypt_key->key_disable) {
 			memset(&priv->wep_key[index], 0,
 			       sizeof(struct mwifiex_wep_key));
+			if (wep_key->key_length)
+				goto done;
+			}
 
 		if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2)
 			enc_key = encrypt_key;
@@ -918,6 +921,7 @@
 			return ret;
 	}
 
+done:
 	if (priv->sec_info.wep_enabled)
 		priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
 	else
@@ -1131,36 +1135,6 @@
 	return roc_cfg.status;
 }
 
-int
-mwifiex_set_bss_role(struct mwifiex_private *priv, u8 bss_role)
-{
-	if (GET_BSS_ROLE(priv) == bss_role) {
-		dev_dbg(priv->adapter->dev,
-			"info: already in the desired role.\n");
-		return 0;
-	}
-
-	mwifiex_free_priv(priv);
-	mwifiex_init_priv(priv);
-
-	priv->bss_role = bss_role;
-	switch (bss_role) {
-	case MWIFIEX_BSS_ROLE_UAP:
-		priv->bss_mode = NL80211_IFTYPE_AP;
-		break;
-	case MWIFIEX_BSS_ROLE_STA:
-	case MWIFIEX_BSS_ROLE_ANY:
-	default:
-		priv->bss_mode = NL80211_IFTYPE_STATION;
-		break;
-	}
-
-	mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
-			 HostCmd_ACT_GEN_SET, 0, NULL, true);
-
-	return mwifiex_sta_init_cmd(priv, false);
-}
-
 /*
  * Sends IOCTL request to get statistics information.
  *
diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c
index 860fbb9..e93e8f7 100644
--- a/drivers/net/wireless/mwifiex/sta_rx.c
+++ b/drivers/net/wireless/mwifiex/sta_rx.c
@@ -90,6 +90,7 @@
 	struct ethhdr *eth;
 	u16 rx_pkt_off, rx_pkt_len;
 	u8 *offset;
+	u8 adj_rx_rate = 0;
 
 	local_rx_pd = (struct rxpd *) (skb->data);
 
@@ -155,6 +156,14 @@
 
 	priv->rxpd_htinfo = local_rx_pd->ht_info;
 
+	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
+	    GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+		adj_rx_rate = mwifiex_adjust_data_rate(priv, priv->rxpd_rate,
+						       priv->rxpd_htinfo);
+		mwifiex_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr,
+				      local_rx_pd->nf);
+	}
+
 	ret = mwifiex_recv_packet(priv, skb);
 	if (ret == -1)
 		dev_err(priv->adapter->dev, "recv packet failed\n");
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c
index b896d73..5ce2d9a 100644
--- a/drivers/net/wireless/mwifiex/sta_tx.c
+++ b/drivers/net/wireless/mwifiex/sta_tx.c
@@ -47,8 +47,10 @@
 	struct mwifiex_adapter *adapter = priv->adapter;
 	struct txpd *local_tx_pd;
 	struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
-	u8 pad;
+	unsigned int pad;
 	u16 pkt_type, pkt_offset;
+	int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 :
+		       INTF_HEADER_LEN;
 
 	if (!skb->len) {
 		dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len);
@@ -56,13 +58,12 @@
 		return skb->data;
 	}
 
+	BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN);
+
 	pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0;
 
-	/* If skb->data is not aligned; add padding */
-	pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4;
-
-	BUG_ON(skb_headroom(skb) < (sizeof(*local_tx_pd) + INTF_HEADER_LEN
-				    + pad));
+	pad = ((void *)skb->data - (sizeof(*local_tx_pd) + hroom)-
+			 NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1);
 	skb_push(skb, sizeof(*local_tx_pd) + pad);
 
 	local_tx_pd = (struct txpd *) skb->data;
@@ -70,8 +71,8 @@
 	local_tx_pd->bss_num = priv->bss_num;
 	local_tx_pd->bss_type = priv->bss_type;
 	local_tx_pd->tx_pkt_length = cpu_to_le16((u16)(skb->len -
-						       (sizeof(struct txpd)
-							+ pad)));
+						       (sizeof(struct txpd) +
+							pad)));
 
 	local_tx_pd->priority = (u8) skb->priority;
 	local_tx_pd->pkt_delay_2ms =
@@ -115,7 +116,7 @@
 	local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset);
 
 	/* make space for INTF_HEADER_LEN */
-	skb_push(skb, INTF_HEADER_LEN);
+	skb_push(skb, hroom);
 
 	if (!local_tx_pd->tx_control)
 		/* TxCtrl set by user or default */
@@ -182,9 +183,13 @@
 	}
 	switch (ret) {
 	case -EBUSY:
-		adapter->data_sent = true;
-		/* Fall through FAILURE handling */
+		dev_kfree_skb_any(skb);
+		dev_err(adapter->dev, "%s: host_to_card failed: ret=%d\n",
+			__func__, ret);
+		adapter->dbg.num_tx_host_to_card_failure++;
+		break;
 	case -1:
+		adapter->data_sent = false;
 		dev_kfree_skb_any(skb);
 		dev_err(adapter->dev, "%s: host_to_card failed: ret=%d\n",
 			__func__, ret);
@@ -197,6 +202,7 @@
 		adapter->tx_lock_flag = true;
 		break;
 	case -EINPROGRESS:
+		adapter->tx_lock_flag = true;
 		break;
 	default:
 		break;
diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c
index 22884b4..087d847 100644
--- a/drivers/net/wireless/mwifiex/tdls.c
+++ b/drivers/net/wireless/mwifiex/tdls.c
@@ -1123,6 +1123,36 @@
 	return TDLS_NOT_SETUP;
 }
 
+int mwifiex_get_tdls_list(struct mwifiex_private *priv,
+			  struct tdls_peer_info *buf)
+{
+	struct mwifiex_sta_node *sta_ptr;
+	struct tdls_peer_info *peer = buf;
+	int count = 0;
+	unsigned long flags;
+
+	if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
+		return 0;
+
+	/* make sure we are in station mode and connected */
+	if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected))
+		return 0;
+
+	spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+	list_for_each_entry(sta_ptr, &priv->sta_list, list) {
+		if (sta_ptr->tdls_status == TDLS_SETUP_COMPLETE) {
+			ether_addr_copy(peer->peer_addr, sta_ptr->mac_addr);
+			peer++;
+			count++;
+			if (count >= MWIFIEX_MAX_TDLS_PEER_SUPPORTED)
+				break;
+		}
+	}
+	spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+	return count;
+}
+
 void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv)
 {
 	struct mwifiex_sta_node *sta_ptr;
@@ -1367,9 +1397,8 @@
 
 void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv)
 {
-	init_timer(&priv->auto_tdls_timer);
-	priv->auto_tdls_timer.function = mwifiex_check_auto_tdls;
-	priv->auto_tdls_timer.data = (unsigned long)priv;
+	setup_timer(&priv->auto_tdls_timer, mwifiex_check_auto_tdls,
+		    (unsigned long)priv);
 	priv->auto_tdls_timer_active = true;
 	mod_timer(&priv->auto_tdls_timer,
 		  jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S));
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c
index 6ae1333..a245f44 100644
--- a/drivers/net/wireless/mwifiex/txrx.c
+++ b/drivers/net/wireless/mwifiex/txrx.c
@@ -80,23 +80,29 @@
 int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
 		       struct mwifiex_tx_param *tx_param)
 {
-	int ret = -1;
+	int hroom, ret = -1;
 	struct mwifiex_adapter *adapter = priv->adapter;
 	u8 *head_ptr;
 	struct txpd *local_tx_pd = NULL;
 
+	hroom = (adapter->iface_type == MWIFIEX_USB) ? 0 : INTF_HEADER_LEN;
+
 	if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
 		head_ptr = mwifiex_process_uap_txpd(priv, skb);
 	else
 		head_ptr = mwifiex_process_sta_txpd(priv, skb);
 
+	if ((adapter->data_sent || adapter->tx_lock_flag) && head_ptr) {
+		skb_queue_tail(&adapter->tx_data_q, skb);
+		atomic_inc(&adapter->tx_queued);
+		return 0;
+	}
+
 	if (head_ptr) {
 		if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
-			local_tx_pd =
-				(struct txpd *) (head_ptr + INTF_HEADER_LEN);
+			local_tx_pd = (struct txpd *)(head_ptr + hroom);
 		if (adapter->iface_type == MWIFIEX_USB) {
 			adapter->data_sent = true;
-			skb_pull(skb, INTF_HEADER_LEN);
 			ret = adapter->if_ops.host_to_card(adapter,
 							   MWIFIEX_USB_EP_DATA,
 							   skb, NULL);
@@ -142,6 +148,123 @@
 	return ret;
 }
 
+static int mwifiex_host_to_card(struct mwifiex_adapter *adapter,
+				struct sk_buff *skb,
+				struct mwifiex_tx_param *tx_param)
+{
+	struct txpd *local_tx_pd = NULL;
+	u8 *head_ptr = skb->data;
+	int ret = 0;
+	struct mwifiex_private *priv;
+	struct mwifiex_txinfo *tx_info;
+
+	tx_info = MWIFIEX_SKB_TXCB(skb);
+	priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num,
+				      tx_info->bss_type);
+	if (!priv) {
+		dev_err(adapter->dev, "data: priv not found. Drop TX packet\n");
+		adapter->dbg.num_tx_host_to_card_failure++;
+		mwifiex_write_data_complete(adapter, skb, 0, 0);
+		return ret;
+	}
+	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {
+		if (adapter->iface_type == MWIFIEX_USB)
+			local_tx_pd = (struct txpd *)head_ptr;
+		else
+			local_tx_pd = (struct txpd *) (head_ptr +
+				INTF_HEADER_LEN);
+	}
+
+	if (adapter->iface_type == MWIFIEX_USB) {
+		adapter->data_sent = true;
+		ret = adapter->if_ops.host_to_card(adapter,
+						   MWIFIEX_USB_EP_DATA,
+						   skb, NULL);
+	} else {
+		ret = adapter->if_ops.host_to_card(adapter,
+						   MWIFIEX_TYPE_DATA,
+						   skb, tx_param);
+	}
+	switch (ret) {
+	case -ENOSR:
+		dev_err(adapter->dev, "data: -ENOSR is returned\n");
+		break;
+	case -EBUSY:
+		if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+		    (adapter->pps_uapsd_mode) &&
+		    (adapter->tx_lock_flag)) {
+			priv->adapter->tx_lock_flag = false;
+			if (local_tx_pd)
+				local_tx_pd->flags = 0;
+		}
+		skb_queue_head(&adapter->tx_data_q, skb);
+		if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT)
+			atomic_add(tx_info->aggr_num, &adapter->tx_queued);
+		else
+			atomic_inc(&adapter->tx_queued);
+		dev_dbg(adapter->dev, "data: -EBUSY is returned\n");
+		break;
+	case -1:
+		if (adapter->iface_type != MWIFIEX_PCIE)
+			adapter->data_sent = false;
+		dev_err(adapter->dev, "mwifiex_write_data_async failed: 0x%X\n",
+			ret);
+		adapter->dbg.num_tx_host_to_card_failure++;
+		mwifiex_write_data_complete(adapter, skb, 0, ret);
+		break;
+	case -EINPROGRESS:
+		if (adapter->iface_type != MWIFIEX_PCIE)
+			adapter->data_sent = false;
+		break;
+	case 0:
+		mwifiex_write_data_complete(adapter, skb, 0, ret);
+		break;
+	default:
+		break;
+	}
+	return ret;
+}
+
+static int
+mwifiex_dequeue_tx_queue(struct mwifiex_adapter *adapter)
+{
+	struct sk_buff *skb, *skb_next;
+	struct mwifiex_txinfo *tx_info;
+	struct mwifiex_tx_param tx_param;
+
+	skb = skb_dequeue(&adapter->tx_data_q);
+	if (!skb)
+		return -1;
+
+	tx_info = MWIFIEX_SKB_TXCB(skb);
+	if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT)
+		atomic_sub(tx_info->aggr_num, &adapter->tx_queued);
+	else
+		atomic_dec(&adapter->tx_queued);
+
+	if (!skb_queue_empty(&adapter->tx_data_q))
+		skb_next = skb_peek(&adapter->tx_data_q);
+	else
+		skb_next = NULL;
+	tx_param.next_pkt_len = ((skb_next) ? skb_next->len : 0);
+	if (!tx_param.next_pkt_len) {
+		if (!mwifiex_wmm_lists_empty(adapter))
+			tx_param.next_pkt_len = 1;
+	}
+	return mwifiex_host_to_card(adapter, skb, &tx_param);
+}
+
+void
+mwifiex_process_tx_queue(struct mwifiex_adapter *adapter)
+{
+	do {
+		if (adapter->data_sent || adapter->tx_lock_flag)
+			break;
+		if (mwifiex_dequeue_tx_queue(adapter))
+			break;
+	} while (!skb_queue_empty(&adapter->tx_data_q));
+}
+
 /*
  * Packet send completion callback handler.
  *
@@ -179,8 +302,11 @@
 		priv->stats.tx_errors++;
 	}
 
-	if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT)
+	if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) {
 		atomic_dec_return(&adapter->pending_bridged_pkts);
+		if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT)
+			goto done;
+	}
 
 	if (aggr)
 		/* For skb_aggr, do not wake up tx queue */
@@ -227,7 +353,7 @@
 			/* consumes ack_skb */
 			skb_complete_wifi_ack(ack_skb, !tx_status->status);
 		} else {
-			cfg80211_mgmt_tx_status(priv->wdev, tx_info->cookie,
+			cfg80211_mgmt_tx_status(&priv->wdev, tx_info->cookie,
 						ack_skb->data, ack_skb->len,
 						!tx_status->status, GFP_ATOMIC);
 			dev_kfree_skb_any(ack_skb);
diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c
index 0f347fd..f5c2af0 100644
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/mwifiex/uap_cmd.c
@@ -761,6 +761,11 @@
 		if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf))
 			return -1;
 		break;
+	case HostCmd_CMD_CHAN_REPORT_REQUEST:
+		if (mwifiex_cmd_issue_chan_report_request(priv, cmd_buf,
+							  data_buf))
+			return -1;
+		break;
 	default:
 		dev_err(priv->adapter->dev,
 			"PREP_CMD: unknown cmd %#x\n", cmd_no);
@@ -769,3 +774,68 @@
 
 	return 0;
 }
+
+void mwifiex_uap_set_channel(struct mwifiex_uap_bss_param *bss_cfg,
+			     struct cfg80211_chan_def chandef)
+{
+	u8 config_bands = 0;
+
+	bss_cfg->channel = ieee80211_frequency_to_channel(
+						     chandef.chan->center_freq);
+
+	/* Set appropriate bands */
+	if (chandef.chan->band == IEEE80211_BAND_2GHZ) {
+		bss_cfg->band_cfg = BAND_CONFIG_BG;
+		config_bands = BAND_B | BAND_G;
+
+		if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
+			config_bands |= BAND_GN;
+	} else {
+		bss_cfg->band_cfg = BAND_CONFIG_A;
+		config_bands = BAND_A;
+
+		if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
+			config_bands |= BAND_AN;
+
+		if (chandef.width > NL80211_CHAN_WIDTH_40)
+			config_bands |= BAND_AAC;
+	}
+}
+
+int mwifiex_config_start_uap(struct mwifiex_private *priv,
+			     struct mwifiex_uap_bss_param *bss_cfg)
+{
+	if (mwifiex_del_mgmt_ies(priv))
+		dev_err(priv->adapter->dev, "Failed to delete mgmt IEs!\n");
+
+	if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
+			     HostCmd_ACT_GEN_SET, 0, NULL, true)) {
+		dev_err(priv->adapter->dev, "Failed to stop the BSS\n");
+		return -1;
+	}
+
+	if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
+			     HostCmd_ACT_GEN_SET,
+			     UAP_BSS_PARAMS_I, bss_cfg, false)) {
+		dev_err(priv->adapter->dev, "Failed to set the SSID\n");
+		return -1;
+	}
+
+	if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START,
+			     HostCmd_ACT_GEN_SET, 0, NULL, false)) {
+		dev_err(priv->adapter->dev, "Failed to start the BSS\n");
+		return -1;
+	}
+
+	if (priv->sec_info.wep_enabled)
+		priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
+	else
+		priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
+
+	if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+			     HostCmd_ACT_GEN_SET, 0,
+			     &priv->curr_pkt_filter, true))
+		return -1;
+
+	return 0;
+}
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c
index c54a537..f4794cd 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/mwifiex/uap_event.c
@@ -68,7 +68,6 @@
 				len = ETH_ALEN;
 
 			if (len != -1) {
-				sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
 				sinfo.assoc_req_ies = &event->data[len];
 				len = (u8 *)sinfo.assoc_req_ies -
 				      (u8 *)&event->frame_control;
@@ -132,6 +131,8 @@
 		dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause);
 		memcpy(priv->netdev->dev_addr, adapter->event_body + 2,
 		       ETH_ALEN);
+		if (priv->hist_data)
+			mwifiex_hist_data_reset(priv);
 		break;
 	case EVENT_UAP_MIC_COUNTERMEASURES:
 		/* For future development */
@@ -177,6 +178,53 @@
 		dev_dbg(adapter->dev, "event: TX_STATUS Report\n");
 		mwifiex_parse_tx_status_event(priv, adapter->event_body);
 		break;
+	case EVENT_PS_SLEEP:
+		dev_dbg(adapter->dev, "info: EVENT: SLEEP\n");
+
+		adapter->ps_state = PS_STATE_PRE_SLEEP;
+
+		mwifiex_check_ps_cond(adapter);
+		break;
+
+	case EVENT_PS_AWAKE:
+		dev_dbg(adapter->dev, "info: EVENT: AWAKE\n");
+		if (!adapter->pps_uapsd_mode &&
+		    priv->media_connected && adapter->sleep_period.period) {
+				adapter->pps_uapsd_mode = true;
+				dev_dbg(adapter->dev,
+					"event: PPS/UAPSD mode activated\n");
+		}
+		adapter->tx_lock_flag = false;
+		if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) {
+			if (mwifiex_check_last_packet_indication(priv)) {
+				if (adapter->data_sent) {
+					adapter->ps_state = PS_STATE_AWAKE;
+					adapter->pm_wakeup_card_req = false;
+					adapter->pm_wakeup_fw_try = false;
+					break;
+				}
+				if (!mwifiex_send_null_packet
+					(priv,
+					 MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET |
+					 MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET))
+						adapter->ps_state =
+							PS_STATE_SLEEP;
+					return 0;
+			}
+		}
+		adapter->ps_state = PS_STATE_AWAKE;
+		adapter->pm_wakeup_card_req = false;
+		adapter->pm_wakeup_fw_try = false;
+		break;
+
+	case EVENT_CHANNEL_REPORT_RDY:
+		dev_dbg(adapter->dev, "event: Channel Report\n");
+		mwifiex_11h_handle_chanrpt_ready(priv, adapter->event_skb);
+		break;
+	case EVENT_RADAR_DETECTED:
+		dev_dbg(adapter->dev, "event: Radar detected\n");
+		mwifiex_11h_handle_radar_detected(priv, adapter->event_skb);
+		break;
 	default:
 		dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
 			eventcause);
diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c
index be3a203..38ac4d7 100644
--- a/drivers/net/wireless/mwifiex/uap_txrx.c
+++ b/drivers/net/wireless/mwifiex/uap_txrx.c
@@ -348,8 +348,10 @@
 	struct mwifiex_adapter *adapter = priv->adapter;
 	struct uap_txpd *txpd;
 	struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
-	int pad, len;
-	u16 pkt_type;
+	int pad;
+	u16 pkt_type, pkt_offset;
+	int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 :
+		       INTF_HEADER_LEN;
 
 	if (!skb->len) {
 		dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len);
@@ -357,22 +359,21 @@
 		return skb->data;
 	}
 
+	BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN);
+
 	pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0;
 
-	/* If skb->data is not aligned, add padding */
-	pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4;
+	pad = ((void *)skb->data - (sizeof(*txpd) + hroom) - NULL) &
+			(MWIFIEX_DMA_ALIGN_SZ - 1);
 
-	len = sizeof(*txpd) + pad;
-
-	BUG_ON(skb_headroom(skb) < len + INTF_HEADER_LEN);
-
-	skb_push(skb, len);
+	skb_push(skb, sizeof(*txpd) + pad);
 
 	txpd = (struct uap_txpd *)skb->data;
 	memset(txpd, 0, sizeof(*txpd));
 	txpd->bss_num = priv->bss_num;
 	txpd->bss_type = priv->bss_type;
-	txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - len));
+	txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) +
+						pad)));
 	txpd->priority = (u8)skb->priority;
 
 	txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
@@ -392,16 +393,17 @@
 		    cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]);
 
 	/* Offset of actual data */
+	pkt_offset = sizeof(*txpd) + pad;
 	if (pkt_type == PKT_TYPE_MGMT) {
 		/* Set the packet type and add header for management frame */
 		txpd->tx_pkt_type = cpu_to_le16(pkt_type);
-		len += MWIFIEX_MGMT_FRAME_HEADER_SIZE;
+		pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE;
 	}
 
-	txpd->tx_pkt_offset = cpu_to_le16(len);
+	txpd->tx_pkt_offset = cpu_to_le16(pkt_offset);
 
 	/* make space for INTF_HEADER_LEN */
-	skb_push(skb, INTF_HEADER_LEN);
+	skb_push(skb, hroom);
 
 	if (!txpd->tx_control)
 		/* TxCtrl set by user or default */
diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c
index 1b56495..fd8027f 100644
--- a/drivers/net/wireless/mwifiex/usb.c
+++ b/drivers/net/wireless/mwifiex/usb.c
@@ -37,6 +37,11 @@
 	{USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8797_PID_2,
 				       USB_CLASS_VENDOR_SPEC,
 				       USB_SUBCLASS_VENDOR_SPEC, 0xff)},
+	/* 8801 */
+	{USB_DEVICE(USB8XXX_VID, USB8801_PID_1)},
+	{USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8801_PID_2,
+				       USB_CLASS_VENDOR_SPEC,
+				       USB_SUBCLASS_VENDOR_SPEC, 0xff)},
 	/* 8897 */
 	{USB_DEVICE(USB8XXX_VID, USB8897_PID_1)},
 	{USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8897_PID_2,
@@ -188,7 +193,7 @@
 		dev_dbg(adapter->dev, "info: recv_length=%d, status=%d\n",
 			recv_length, status);
 		if (status == -EINPROGRESS) {
-			queue_work(adapter->workqueue, &adapter->main_work);
+			mwifiex_queue_main_work(adapter);
 
 			/* urb for data_ep is re-submitted now;
 			 * urb for cmd_ep will be re-submitted in callback
@@ -257,7 +262,7 @@
 					    urb->status ? -1 : 0);
 	}
 
-	queue_work(adapter->workqueue, &adapter->main_work);
+	mwifiex_queue_main_work(adapter);
 
 	return;
 }
@@ -361,11 +366,13 @@
 	switch (id_product) {
 	case USB8766_PID_1:
 	case USB8797_PID_1:
+	case USB8801_PID_1:
 	case USB8897_PID_1:
 		card->usb_boot_state = USB8XXX_FW_DNLD;
 		break;
 	case USB8766_PID_2:
 	case USB8797_PID_2:
+	case USB8801_PID_2:
 	case USB8897_PID_2:
 		card->usb_boot_state = USB8XXX_FW_READY;
 		break;
@@ -792,11 +799,19 @@
 	case USB8897_PID_2:
 		adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K;
 		strcpy(adapter->fw_name, USB8897_DEFAULT_FW_NAME);
+		adapter->ext_scan = true;
 		break;
 	case USB8766_PID_1:
 	case USB8766_PID_2:
 		adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
 		strcpy(adapter->fw_name, USB8766_DEFAULT_FW_NAME);
+		adapter->ext_scan = true;
+		break;
+	case USB8801_PID_1:
+	case USB8801_PID_2:
+		adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
+		strcpy(adapter->fw_name, USB8801_DEFAULT_FW_NAME);
+		adapter->ext_scan = false;
 		break;
 	case USB8797_PID_1:
 	case USB8797_PID_2:
@@ -930,7 +945,8 @@
 	} while ((dnld_cmd != FW_HAS_LAST_BLOCK) && retries);
 
 cleanup:
-	dev_dbg(adapter->dev, "%s: %d bytes downloaded\n", __func__, tlen);
+	dev_notice(adapter->dev,
+		   "info: FW download over, size %d bytes\n", tlen);
 
 	kfree(recv_buff);
 	kfree(fwdata);
@@ -990,6 +1006,7 @@
 {
 	/* Simulation of HS_AWAKE event */
 	adapter->pm_wakeup_fw_try = false;
+	del_timer(&adapter->wakeup_timer);
 	adapter->pm_wakeup_card_req = false;
 	adapter->ps_state = PS_STATE_AWAKE;
 
@@ -1010,6 +1027,13 @@
 	}
 }
 
+/* This function is called after the card has woken up. */
+static inline int
+mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
+{
+	return 0;
+}
+
 static struct mwifiex_if_ops usb_ops = {
 	.register_dev =		mwifiex_register_dev,
 	.unregister_dev =	mwifiex_unregister_dev,
@@ -1074,4 +1098,5 @@
 MODULE_LICENSE("GPL v2");
 MODULE_FIRMWARE(USB8766_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(USB8801_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(USB8897_DEFAULT_FW_NAME);
diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h
index a7cbba1..57e1a57 100644
--- a/drivers/net/wireless/mwifiex/usb.h
+++ b/drivers/net/wireless/mwifiex/usb.h
@@ -30,6 +30,9 @@
 #define USB8797_PID_2		0x2044
 #define USB8897_PID_1		0x2045
 #define USB8897_PID_2		0x2046
+#define USB8801_PID_1		0x2049
+#define USB8801_PID_2		0x204a
+
 
 #define USB8XXX_FW_DNLD		1
 #define USB8XXX_FW_READY	2
@@ -41,6 +44,7 @@
 
 #define USB8766_DEFAULT_FW_NAME	"mrvl/usb8766_uapsta.bin"
 #define USB8797_DEFAULT_FW_NAME	"mrvl/usb8797_uapsta.bin"
+#define USB8801_DEFAULT_FW_NAME	"mrvl/usb8801_uapsta.bin"
 #define USB8897_DEFAULT_FW_NAME	"mrvl/usb8897_uapsta.bin"
 
 #define FW_DNLD_TX_BUF_SIZE	620
@@ -96,11 +100,4 @@
 	u8 data[1];
 };
 
-/* This function is called after the card has woken up. */
-static inline int
-mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
-{
-	return 0;
-}
-
 #endif /*_MWIFIEX_USB_H */
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index b1768fb..b8a4587 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/mwifiex/util.c
@@ -25,6 +25,96 @@
 #include "wmm.h"
 #include "11n.h"
 
+static struct mwifiex_debug_data items[] = {
+	{"int_counter", item_size(int_counter),
+	 item_addr(int_counter), 1},
+	{"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]),
+	 item_addr(packets_out[WMM_AC_VO]), 1},
+	{"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]),
+	 item_addr(packets_out[WMM_AC_VI]), 1},
+	{"wmm_ac_be", item_size(packets_out[WMM_AC_BE]),
+	 item_addr(packets_out[WMM_AC_BE]), 1},
+	{"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]),
+	 item_addr(packets_out[WMM_AC_BK]), 1},
+	{"tx_buf_size", item_size(tx_buf_size),
+	 item_addr(tx_buf_size), 1},
+	{"curr_tx_buf_size", item_size(curr_tx_buf_size),
+	 item_addr(curr_tx_buf_size), 1},
+	{"ps_mode", item_size(ps_mode),
+	 item_addr(ps_mode), 1},
+	{"ps_state", item_size(ps_state),
+	 item_addr(ps_state), 1},
+	{"is_deep_sleep", item_size(is_deep_sleep),
+	 item_addr(is_deep_sleep), 1},
+	{"wakeup_dev_req", item_size(pm_wakeup_card_req),
+	 item_addr(pm_wakeup_card_req), 1},
+	{"wakeup_tries", item_size(pm_wakeup_fw_try),
+	 item_addr(pm_wakeup_fw_try), 1},
+	{"hs_configured", item_size(is_hs_configured),
+	 item_addr(is_hs_configured), 1},
+	{"hs_activated", item_size(hs_activated),
+	 item_addr(hs_activated), 1},
+	{"num_tx_timeout", item_size(num_tx_timeout),
+	 item_addr(num_tx_timeout), 1},
+	{"is_cmd_timedout", item_size(is_cmd_timedout),
+	 item_addr(is_cmd_timedout), 1},
+	{"timeout_cmd_id", item_size(timeout_cmd_id),
+	 item_addr(timeout_cmd_id), 1},
+	{"timeout_cmd_act", item_size(timeout_cmd_act),
+	 item_addr(timeout_cmd_act), 1},
+	{"last_cmd_id", item_size(last_cmd_id),
+	 item_addr(last_cmd_id), DBG_CMD_NUM},
+	{"last_cmd_act", item_size(last_cmd_act),
+	 item_addr(last_cmd_act), DBG_CMD_NUM},
+	{"last_cmd_index", item_size(last_cmd_index),
+	 item_addr(last_cmd_index), 1},
+	{"last_cmd_resp_id", item_size(last_cmd_resp_id),
+	 item_addr(last_cmd_resp_id), DBG_CMD_NUM},
+	{"last_cmd_resp_index", item_size(last_cmd_resp_index),
+	 item_addr(last_cmd_resp_index), 1},
+	{"last_event", item_size(last_event),
+	 item_addr(last_event), DBG_CMD_NUM},
+	{"last_event_index", item_size(last_event_index),
+	 item_addr(last_event_index), 1},
+	{"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure),
+	 item_addr(num_cmd_host_to_card_failure), 1},
+	{"num_cmd_sleep_cfm_fail",
+	 item_size(num_cmd_sleep_cfm_host_to_card_failure),
+	 item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1},
+	{"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure),
+	 item_addr(num_tx_host_to_card_failure), 1},
+	{"num_evt_deauth", item_size(num_event_deauth),
+	 item_addr(num_event_deauth), 1},
+	{"num_evt_disassoc", item_size(num_event_disassoc),
+	 item_addr(num_event_disassoc), 1},
+	{"num_evt_link_lost", item_size(num_event_link_lost),
+	 item_addr(num_event_link_lost), 1},
+	{"num_cmd_deauth", item_size(num_cmd_deauth),
+	 item_addr(num_cmd_deauth), 1},
+	{"num_cmd_assoc_ok", item_size(num_cmd_assoc_success),
+	 item_addr(num_cmd_assoc_success), 1},
+	{"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure),
+	 item_addr(num_cmd_assoc_failure), 1},
+	{"cmd_sent", item_size(cmd_sent),
+	 item_addr(cmd_sent), 1},
+	{"data_sent", item_size(data_sent),
+	 item_addr(data_sent), 1},
+	{"cmd_resp_received", item_size(cmd_resp_received),
+	 item_addr(cmd_resp_received), 1},
+	{"event_received", item_size(event_received),
+	 item_addr(event_received), 1},
+
+	/* variables defined in struct mwifiex_adapter */
+	{"cmd_pending", adapter_item_size(cmd_pending),
+	 adapter_item_addr(cmd_pending), 1},
+	{"tx_pending", adapter_item_size(tx_pending),
+	 adapter_item_addr(tx_pending), 1},
+	{"rx_pending", adapter_item_size(rx_pending),
+	 adapter_item_addr(rx_pending), 1},
+};
+
+static int num_of_items = ARRAY_SIZE(items);
+
 /*
  * Firmware initialization complete callback handler.
  *
@@ -97,6 +187,8 @@
 							      info->rx_tbl);
 		info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl(priv,
 								info->tx_tbl);
+		info->tdls_peer_num = mwifiex_get_tdls_list(priv,
+							    info->tdls_list);
 		info->ps_mode = adapter->ps_mode;
 		info->ps_state = adapter->ps_state;
 		info->is_deep_sleep = adapter->is_deep_sleep;
@@ -141,6 +233,93 @@
 	return 0;
 }
 
+int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf,
+				 struct mwifiex_debug_info *info)
+{
+	char *p = buf;
+	struct mwifiex_debug_data *d = &items[0];
+	size_t size, addr;
+	long val;
+	int i, j;
+
+	if (!info)
+		return 0;
+
+	for (i = 0; i < num_of_items; i++) {
+		p += sprintf(p, "%s=", d[i].name);
+
+		size = d[i].size / d[i].num;
+
+		if (i < (num_of_items - 3))
+			addr = d[i].addr + (size_t)info;
+		else /* The last 3 items are struct mwifiex_adapter variables */
+			addr = d[i].addr + (size_t)priv->adapter;
+
+		for (j = 0; j < d[i].num; j++) {
+			switch (size) {
+			case 1:
+				val = *((u8 *)addr);
+				break;
+			case 2:
+				val = *((u16 *)addr);
+				break;
+			case 4:
+				val = *((u32 *)addr);
+				break;
+			case 8:
+				val = *((long long *)addr);
+				break;
+			default:
+				val = -1;
+				break;
+			}
+
+			p += sprintf(p, "%#lx ", val);
+			addr += size;
+		}
+
+		p += sprintf(p, "\n");
+	}
+
+	if (info->tx_tbl_num) {
+		p += sprintf(p, "Tx BA stream table:\n");
+		for (i = 0; i < info->tx_tbl_num; i++)
+			p += sprintf(p, "tid = %d, ra = %pM\n",
+				     info->tx_tbl[i].tid, info->tx_tbl[i].ra);
+	}
+
+	if (info->rx_tbl_num) {
+		p += sprintf(p, "Rx reorder table:\n");
+		for (i = 0; i < info->rx_tbl_num; i++) {
+			p += sprintf(p, "tid = %d, ta = %pM, ",
+				     info->rx_tbl[i].tid,
+				     info->rx_tbl[i].ta);
+			p += sprintf(p, "start_win = %d, ",
+				     info->rx_tbl[i].start_win);
+			p += sprintf(p, "win_size = %d, buffer: ",
+				     info->rx_tbl[i].win_size);
+
+			for (j = 0; j < info->rx_tbl[i].win_size; j++)
+				p += sprintf(p, "%c ",
+					     info->rx_tbl[i].buffer[j] ?
+					     '1' : '0');
+
+			p += sprintf(p, "\n");
+		}
+	}
+
+	if (info->tdls_peer_num) {
+		p += sprintf(p, "TDLS peer table:\n");
+		for (i = 0; i < info->tdls_peer_num; i++) {
+			p += sprintf(p, "peer = %pM",
+				     info->tdls_list[i].peer_addr);
+			p += sprintf(p, "\n");
+		}
+	}
+
+	return p - buf;
+}
+
 static int
 mwifiex_parse_mgmt_packet(struct mwifiex_private *priv, u8 *payload, u16 len,
 			  struct rxpd *rx_pd)
@@ -188,6 +367,13 @@
 	if (!skb)
 		return -1;
 
+	if (!priv->mgmt_frame_mask ||
+	    priv->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) {
+		dev_dbg(priv->adapter->dev,
+			"do not receive mgmt frames on uninitialized intf");
+		return -1;
+	}
+
 	rx_pd = (struct rxpd *)skb->data;
 
 	skb_pull(skb, le16_to_cpu(rx_pd->rx_pkt_offset));
@@ -208,7 +394,7 @@
 	pkt_len -= ETH_ALEN + sizeof(pkt_len);
 	rx_pd->rx_pkt_length = cpu_to_le16(pkt_len);
 
-	cfg80211_rx_mgmt(priv->wdev, priv->roc_cfg.chan.center_freq,
+	cfg80211_rx_mgmt(&priv->wdev, priv->roc_cfg.chan.center_freq,
 			 CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len,
 			 0);
 
@@ -404,3 +590,67 @@
 	spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
 	return;
 }
+
+/* This function adds histogram data to histogram array*/
+void mwifiex_hist_data_add(struct mwifiex_private *priv,
+			   u8 rx_rate, s8 snr, s8 nflr)
+{
+	struct mwifiex_histogram_data *phist_data = priv->hist_data;
+
+	if (atomic_read(&phist_data->num_samples) > MWIFIEX_HIST_MAX_SAMPLES)
+		mwifiex_hist_data_reset(priv);
+	mwifiex_hist_data_set(priv, rx_rate, snr, nflr);
+}
+
+/* function to add histogram record */
+void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr,
+			   s8 nflr)
+{
+	struct mwifiex_histogram_data *phist_data = priv->hist_data;
+
+	atomic_inc(&phist_data->num_samples);
+	atomic_inc(&phist_data->rx_rate[rx_rate]);
+	atomic_inc(&phist_data->snr[snr]);
+	atomic_inc(&phist_data->noise_flr[128 + nflr]);
+	atomic_inc(&phist_data->sig_str[nflr - snr]);
+}
+
+/* function to reset histogram data during init/reset */
+void mwifiex_hist_data_reset(struct mwifiex_private *priv)
+{
+	int ix;
+	struct mwifiex_histogram_data *phist_data = priv->hist_data;
+
+	atomic_set(&phist_data->num_samples, 0);
+	for (ix = 0; ix < MWIFIEX_MAX_AC_RX_RATES; ix++)
+		atomic_set(&phist_data->rx_rate[ix], 0);
+	for (ix = 0; ix < MWIFIEX_MAX_SNR; ix++)
+		atomic_set(&phist_data->snr[ix], 0);
+	for (ix = 0; ix < MWIFIEX_MAX_NOISE_FLR; ix++)
+		atomic_set(&phist_data->noise_flr[ix], 0);
+	for (ix = 0; ix < MWIFIEX_MAX_SIG_STRENGTH; ix++)
+		atomic_set(&phist_data->sig_str[ix], 0);
+}
+
+void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags)
+{
+	struct sk_buff *skb;
+	int buf_len, pad;
+
+	buf_len = rx_len + MWIFIEX_RX_HEADROOM + MWIFIEX_DMA_ALIGN_SZ;
+
+	skb = __dev_alloc_skb(buf_len, flags);
+
+	if (!skb)
+		return NULL;
+
+	skb_reserve(skb, MWIFIEX_RX_HEADROOM);
+
+	pad = MWIFIEX_ALIGN_ADDR(skb->data, MWIFIEX_DMA_ALIGN_SZ) -
+	      (long)skb->data;
+
+	skb_reserve(skb, pad);
+
+	return skb;
+}
+EXPORT_SYMBOL_GPL(mwifiex_alloc_dma_align_buf);
diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h
index 40296cb..b541d66 100644
--- a/drivers/net/wireless/mwifiex/util.h
+++ b/drivers/net/wireless/mwifiex/util.h
@@ -20,6 +20,8 @@
 #ifndef _MWIFIEX_UTIL_H_
 #define _MWIFIEX_UTIL_H_
 
+struct mwifiex_private;
+
 struct mwifiex_dma_mapping {
 	dma_addr_t addr;
 	size_t len;
@@ -33,6 +35,21 @@
 	};
 };
 
+/* size/addr for mwifiex_debug_info */
+#define item_size(n)		(FIELD_SIZEOF(struct mwifiex_debug_info, n))
+#define item_addr(n)		(offsetof(struct mwifiex_debug_info, n))
+
+/* size/addr for struct mwifiex_adapter */
+#define adapter_item_size(n)	(FIELD_SIZEOF(struct mwifiex_adapter, n))
+#define adapter_item_addr(n)	(offsetof(struct mwifiex_adapter, n))
+
+struct mwifiex_debug_data {
+	char name[32];		/* variable/array name */
+	u32 size;		/* size of the variable/array */
+	size_t addr;		/* address of the variable/array */
+	int num;		/* number of variables in an array */
+};
+
 static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb)
 {
 	struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb;
@@ -73,4 +90,7 @@
 	return mapping.addr;
 }
 
+int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf,
+				 struct mwifiex_debug_info *info);
+
 #endif /* !_MWIFIEX_UTIL_H_ */
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c
index ffffd2c..b2e9956 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/mwifiex/wmm.c
@@ -157,6 +157,8 @@
 
 		ra_list->is_11n_enabled = 0;
 		ra_list->tdls_link = false;
+		ra_list->ba_status = BA_SETUP_NONE;
+		ra_list->amsdu_in_ampdu = false;
 		if (!mwifiex_queuing_ra_based(priv)) {
 			if (mwifiex_get_tdls_link_status(priv, ra) ==
 			    TDLS_SETUP_COMPLETE) {
@@ -574,7 +576,7 @@
  * This function retrieves a particular RA list node, matching with the
  * given TID and RA address.
  */
-static struct mwifiex_ra_list_tbl *
+struct mwifiex_ra_list_tbl *
 mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid,
 			    const u8 *ra_addr)
 {
@@ -730,7 +732,7 @@
 	} else {
 		memcpy(ra, skb->data, ETH_ALEN);
 		if (ra[0] & 0x01 || mwifiex_is_skb_mgmt_frame(skb))
-			memset(ra, 0xff, ETH_ALEN);
+			eth_broadcast_addr(ra);
 		ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, ra);
 	}
 
@@ -942,14 +944,11 @@
 	struct mwifiex_ra_list_tbl *ptr;
 	struct mwifiex_tid_tbl *tid_ptr;
 	atomic_t *hqp;
-	unsigned long flags_bss, flags_ra;
+	unsigned long flags_ra;
 	int i, j;
 
 	/* check the BSS with highest priority first */
 	for (j = adapter->priv_num - 1; j >= 0; --j) {
-		spin_lock_irqsave(&adapter->bss_prio_tbl[j].bss_prio_lock,
-				  flags_bss);
-
 		/* iterate over BSS with the equal priority */
 		list_for_each_entry(adapter->bss_prio_tbl[j].bss_prio_cur,
 				    &adapter->bss_prio_tbl[j].bss_prio_head,
@@ -985,19 +984,15 @@
 			}
 		}
 
-		spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock,
-				       flags_bss);
 	}
 
 	return NULL;
 
 found:
-	/* holds bss_prio_lock / ra_list_spinlock */
+	/* holds ra_list_spinlock */
 	if (atomic_read(hqp) > i)
 		atomic_set(hqp, i);
 	spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags_ra);
-	spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock,
-			       flags_bss);
 
 	*priv = priv_tmp;
 	*tid = tos_to_tid[i];
@@ -1179,6 +1174,14 @@
 
 	skb = skb_dequeue(&ptr->skb_head);
 
+	if (adapter->data_sent || adapter->tx_lock_flag) {
+		spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+				       ra_list_flags);
+		skb_queue_tail(&adapter->tx_data_q, skb);
+		atomic_inc(&adapter->tx_queued);
+		return;
+	}
+
 	if (!skb_queue_empty(&ptr->skb_head))
 		skb_next = skb_peek(&ptr->skb_head);
 	else
@@ -1228,6 +1231,9 @@
 	case -EINPROGRESS:
 		if (adapter->iface_type != MWIFIEX_PCIE)
 			adapter->data_sent = false;
+		break;
+	case 0:
+		mwifiex_write_data_complete(adapter, skb, 0, ret);
 	default:
 		break;
 	}
@@ -1273,13 +1279,13 @@
 	}
 
 	if (!ptr->is_11n_enabled ||
-	    mwifiex_is_ba_stream_setup(priv, ptr, tid) ||
-	    priv->wps.session_enable) {
+		ptr->ba_status ||
+		priv->wps.session_enable) {
 		if (ptr->is_11n_enabled &&
-		    mwifiex_is_ba_stream_setup(priv, ptr, tid) &&
-		    mwifiex_is_amsdu_in_ampdu_allowed(priv, ptr, tid) &&
-		    mwifiex_is_amsdu_allowed(priv, tid) &&
-		    mwifiex_is_11n_aggragation_possible(priv, ptr,
+			ptr->ba_status &&
+			ptr->amsdu_in_ampdu &&
+			mwifiex_is_amsdu_allowed(priv, tid) &&
+			mwifiex_is_11n_aggragation_possible(priv, ptr,
 							adapter->tx_buf_size))
 			mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags);
 			/* ra_list_spinlock has been freed in
@@ -1326,11 +1332,16 @@
 mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter)
 {
 	do {
-		/* Check if busy */
-		if (adapter->data_sent || adapter->tx_lock_flag)
-			break;
-
 		if (mwifiex_dequeue_tx_packet(adapter))
 			break;
+		if (adapter->iface_type != MWIFIEX_SDIO) {
+			if (adapter->data_sent ||
+			    adapter->tx_lock_flag)
+				break;
+		} else {
+			if (atomic_read(&adapter->tx_queued) >=
+			    MWIFIEX_MAX_PKTS_TXQ)
+				break;
+		}
 	} while (!mwifiex_wmm_lists_empty(adapter));
 }
diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h
index 569bd73..48ece0b 100644
--- a/drivers/net/wireless/mwifiex/wmm.h
+++ b/drivers/net/wireless/mwifiex/wmm.h
@@ -127,4 +127,6 @@
 			    const u8 *ra_addr);
 u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid);
 
+struct mwifiex_ra_list_tbl *mwifiex_wmm_get_ralist_node(struct mwifiex_private
+					*priv, u8 tid, const u8 *ra_addr);
 #endif /* !_MWIFIEX_WMM_H_ */
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index b8d1e04..9592116 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -1277,7 +1277,7 @@
 	struct mwl8k_priv *priv = hw->priv;
 
 	priv->capture_beacon = false;
-	memset(priv->capture_bssid, 0, ETH_ALEN);
+	eth_zero_addr(priv->capture_bssid);
 
 	/*
 	 * Use GFP_ATOMIC as rxq_process is called from
@@ -3098,14 +3098,14 @@
 
 	cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG);
 	cca_cnt /= 1000; /* uSecs to mSecs */
-	survey->channel_time_busy = (u64) cca_cnt;
+	survey->time_busy = (u64) cca_cnt;
 
 	rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG);
 	rx_rdy /= 1000; /* uSecs to mSecs */
-	survey->channel_time_rx = (u64) rx_rdy;
+	survey->time_rx = (u64) rx_rdy;
 
 	priv->channel_time = jiffies - priv->channel_time;
-	survey->channel_time = jiffies_to_msecs(priv->channel_time);
+	survey->time = jiffies_to_msecs(priv->channel_time);
 
 	survey->channel = channel;
 
@@ -3115,9 +3115,9 @@
 	survey->noise = nf * -1;
 
 	survey->filled = SURVEY_INFO_NOISE_DBM |
-			 SURVEY_INFO_CHANNEL_TIME |
-			 SURVEY_INFO_CHANNEL_TIME_BUSY |
-			 SURVEY_INFO_CHANNEL_TIME_RX;
+			 SURVEY_INFO_TIME |
+			 SURVEY_INFO_TIME_BUSY |
+			 SURVEY_INFO_TIME_RX;
 }
 
 /*
diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig
index 6cd9c0d..adc2a37 100644
--- a/drivers/net/wireless/orinoco/Kconfig
+++ b/drivers/net/wireless/orinoco/Kconfig
@@ -2,7 +2,8 @@
 	tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)"
 	depends on m
 	depends on (PPC_PMAC || PCI || PCMCIA)
-	depends on CFG80211 && CFG80211_WEXT
+	depends on CFG80211
+	select CFG80211_WEXT_EXPORT
 	depends on WIRELESS_EXT
 	depends on WEXT_SPY
 	depends on WEXT_PRIV
diff --git a/drivers/net/wireless/orinoco/airport.c b/drivers/net/wireless/orinoco/airport.c
index 0ca8b14..77e6c53 100644
--- a/drivers/net/wireless/orinoco/airport.c
+++ b/drivers/net/wireless/orinoco/airport.c
@@ -228,7 +228,7 @@
 MODULE_DESCRIPTION("Driver for the Apple Airport wireless card.");
 MODULE_LICENSE("Dual MPL/GPL");
 
-static struct of_device_id airport_match[] = {
+static const struct of_device_id airport_match[] = {
 	{
 	.name		= "radio",
 	},
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c
index 5699e52..fd27753 100644
--- a/drivers/net/wireless/orinoco/main.c
+++ b/drivers/net/wireless/orinoco/main.c
@@ -2342,7 +2342,7 @@
 	list_for_each_entry_safe(sd, sdtemp, &priv->scan_list, list) {
 		list_del(&sd->list);
 
-		if ((sd->len > 0) && sd->buf)
+		if (sd->len > 0)
 			kfree(sd->buf);
 		kfree(sd);
 	}
diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/orinoco/orinoco_pci.c
index b6bdad6..74219d5 100644
--- a/drivers/net/wireless/orinoco/orinoco_pci.c
+++ b/drivers/net/wireless/orinoco/orinoco_pci.c
@@ -94,7 +94,7 @@
 	mdelay(HERMES_PCI_COR_OFFT);
 
 	/* The card is ready when it's no longer busy */
-	timeout = jiffies + (HERMES_PCI_COR_BUSYT * HZ / 1000);
+	timeout = jiffies + msecs_to_jiffies(HERMES_PCI_COR_BUSYT);
 	reg = hermes_read_regn(hw, CMD);
 	while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) {
 		mdelay(1);
diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/orinoco/orinoco_plx.c
index b8f6e5c..8b04523 100644
--- a/drivers/net/wireless/orinoco/orinoco_plx.c
+++ b/drivers/net/wireless/orinoco/orinoco_plx.c
@@ -121,7 +121,7 @@
 	mdelay(1);
 
 	/* Just in case, wait more until the card is no longer busy */
-	timeout = jiffies + (PLX_RESET_TIME * HZ / 1000);
+	timeout = jiffies + msecs_to_jiffies(PLX_RESET_TIME);
 	reg = hermes_read_regn(hw, CMD);
 	while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) {
 		mdelay(1);
diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/orinoco/orinoco_tmd.c
index 79d0e33..20ce569 100644
--- a/drivers/net/wireless/orinoco/orinoco_tmd.c
+++ b/drivers/net/wireless/orinoco/orinoco_tmd.c
@@ -71,7 +71,7 @@
 	mdelay(1);
 
 	/* Just in case, wait more until the card is no longer busy */
-	timeout = jiffies + (TMD_RESET_TIME * HZ / 1000);
+	timeout = jiffies + msecs_to_jiffies(TMD_RESET_TIME);
 	reg = hermes_read_regn(hw, CMD);
 	while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) {
 		mdelay(1);
diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c
index 4178ddd..28b5d0d 100644
--- a/drivers/net/wireless/orinoco/orinoco_usb.c
+++ b/drivers/net/wireless/orinoco/orinoco_usb.c
@@ -364,9 +364,7 @@
 	atomic_set(&ctx->refcount, 1);
 	init_completion(&ctx->done);
 
-	init_timer(&ctx->timer);
-	ctx->timer.function = ezusb_request_timerfn;
-	ctx->timer.data = (u_long) ctx;
+	setup_timer(&ctx->timer, ezusb_request_timerfn, (u_long)ctx);
 	return ctx;
 }
 
diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c
index 6abdaf0..1d4dae4 100644
--- a/drivers/net/wireless/orinoco/wext.c
+++ b/drivers/net/wireless/orinoco/wext.c
@@ -168,7 +168,7 @@
 	if (is_zero_ether_addr(ap_addr->sa_data) ||
 	    is_broadcast_ether_addr(ap_addr->sa_data)) {
 		priv->bssid_fixed = 0;
-		memset(priv->desired_bssid, 0, ETH_ALEN);
+		eth_zero_addr(priv->desired_bssid);
 
 		/* "off" means keep existing connection */
 		if (ap_addr->sa_data[0] == 0) {
diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c
index 0fe67d2..2fe713e 100644
--- a/drivers/net/wireless/p54/eeprom.c
+++ b/drivers/net/wireless/p54/eeprom.c
@@ -196,9 +196,9 @@
 		dest->max_power = chan->max_power;
 		priv->survey[*chan_num].channel = &tmp->channels[j];
 		priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM |
-			SURVEY_INFO_CHANNEL_TIME |
-			SURVEY_INFO_CHANNEL_TIME_BUSY |
-			SURVEY_INFO_CHANNEL_TIME_TX;
+			SURVEY_INFO_TIME |
+			SURVEY_INFO_TIME_BUSY |
+			SURVEY_INFO_TIME_TX;
 		dest->hw_value = (*chan_num);
 		j++;
 		(*chan_num)++;
diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/p54/fwio.c
index bc065e8..275408e 100644
--- a/drivers/net/wireless/p54/fwio.c
+++ b/drivers/net/wireless/p54/fwio.c
@@ -220,6 +220,7 @@
 	struct sk_buff *skb;
 	size_t eeprom_hdr_size;
 	int ret = 0;
+	long timeout;
 
 	if (priv->fw_var >= 0x509)
 		eeprom_hdr_size = sizeof(*eeprom_hdr);
@@ -249,9 +250,11 @@
 
 	p54_tx(priv, skb);
 
-	if (!wait_for_completion_interruptible_timeout(
-	     &priv->eeprom_comp, HZ)) {
-		wiphy_err(priv->hw->wiphy, "device does not respond!\n");
+	timeout = wait_for_completion_interruptible_timeout(
+			&priv->eeprom_comp, HZ);
+	if (timeout <= 0) {
+		wiphy_err(priv->hw->wiphy,
+			"device does not respond or signal received!\n");
 		ret = -EBUSY;
 	}
 	priv->eeprom = NULL;
@@ -668,7 +671,7 @@
 	if (addr)
 		memcpy(rxkey->mac, addr, ETH_ALEN);
 	else
-		memset(rxkey->mac, ~0, ETH_ALEN);
+		eth_broadcast_addr(rxkey->mac);
 
 	switch (algo) {
 	case P54_CRYPTO_WEP:
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c
index 58a1f09..91b93cc 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/p54/main.c
@@ -182,7 +182,7 @@
 	if (err)
 		goto out;
 
-	memset(priv->bssid, ~0, ETH_ALEN);
+	eth_broadcast_addr(priv->bssid);
 	priv->mode = NL80211_IFTYPE_MONITOR;
 	err = p54_setup_mac(priv);
 	if (err) {
@@ -274,8 +274,8 @@
 		wait_for_completion_interruptible_timeout(&priv->beacon_comp, HZ);
 	}
 	priv->mode = NL80211_IFTYPE_MONITOR;
-	memset(priv->mac_addr, 0, ETH_ALEN);
-	memset(priv->bssid, 0, ETH_ALEN);
+	eth_zero_addr(priv->mac_addr);
+	eth_zero_addr(priv->bssid);
 	p54_setup_mac(priv);
 	mutex_unlock(&priv->conf_mutex);
 }
@@ -305,9 +305,9 @@
 		struct survey_info *info = &priv->survey[chan->hw_value];
 
 		/* only reset channel statistics, don't touch .filled, etc. */
-		info->channel_time = 0;
-		info->channel_time_busy = 0;
-		info->channel_time_tx = 0;
+		info->time = 0;
+		info->time_busy = 0;
+		info->time_tx = 0;
 	}
 
 	priv->update_stats = true;
@@ -575,6 +575,8 @@
 			key->hw_key_idx = 0xff;
 			goto out_unlock;
 		}
+
+		key->flags |= IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
 	} else {
 		slot = key->hw_key_idx;
 
@@ -634,7 +636,7 @@
 
 		if (in_use) {
 			/* test if the reported statistics are valid. */
-			if  (survey->channel_time != 0) {
+			if  (survey->time != 0) {
 				survey->filled |= SURVEY_INFO_IN_USE;
 			} else {
 				/*
@@ -792,7 +794,7 @@
 	init_completion(&priv->beacon_comp);
 	INIT_DELAYED_WORK(&priv->work, p54_work);
 
-	memset(&priv->mc_maclist[0], ~0, ETH_ALEN);
+	eth_broadcast_addr(priv->mc_maclist[0]);
 	priv->curchan = NULL;
 	p54_reset_stats(priv);
 	return dev;
diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c
index d4aee64..27a4906 100644
--- a/drivers/net/wireless/p54/p54pci.c
+++ b/drivers/net/wireless/p54/p54pci.c
@@ -431,6 +431,7 @@
 {
 	struct p54p_priv *priv = dev->priv;
 	int err;
+	long timeout;
 
 	init_completion(&priv->boot_comp);
 	err = request_irq(priv->pdev->irq, p54p_interrupt,
@@ -468,10 +469,12 @@
 	P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET));
 	P54P_READ(dev_int);
 
-	if (!wait_for_completion_interruptible_timeout(&priv->boot_comp, HZ)) {
+	timeout = wait_for_completion_interruptible_timeout(
+			&priv->boot_comp, HZ);
+	if (timeout <= 0) {
 		wiphy_err(dev->wiphy, "Cannot boot firmware!\n");
 		p54p_stop(dev);
-		return -ETIMEDOUT;
+		return timeout ? -ERESTARTSYS : -ETIMEDOUT;
 	}
 
 	P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_UPDATE));
diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c
index 153c615..24e5ff9 100644
--- a/drivers/net/wireless/p54/txrx.c
+++ b/drivers/net/wireless/p54/txrx.c
@@ -587,13 +587,13 @@
 	if (chan) {
 		struct survey_info *survey = &priv->survey[chan->hw_value];
 		survey->noise = clamp(priv->noise, -128, 127);
-		survey->channel_time = priv->survey_raw.active;
-		survey->channel_time_tx = priv->survey_raw.tx;
-		survey->channel_time_busy = priv->survey_raw.tx +
+		survey->time = priv->survey_raw.active;
+		survey->time_tx = priv->survey_raw.tx;
+		survey->time_busy = priv->survey_raw.tx +
 			priv->survey_raw.cca;
-		do_div(survey->channel_time, 1024);
-		do_div(survey->channel_time_tx, 1024);
-		do_div(survey->channel_time_busy, 1024);
+		do_div(survey->time, 1024);
+		do_div(survey->time_tx, 1024);
+		do_div(survey->time_busy, 1024);
 	}
 
 	tmp = p54_find_and_unlink_skb(priv, hdr->req_id);
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 0f7b6d6..089cb4c 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -199,13 +199,13 @@
 
 struct ndis_80211_auth_request {
 	__le32 length;
-	u8 bssid[6];
+	u8 bssid[ETH_ALEN];
 	u8 padding[2];
 	__le32 flags;
 } __packed;
 
 struct ndis_80211_pmkid_candidate {
-	u8 bssid[6];
+	u8 bssid[ETH_ALEN];
 	u8 padding[2];
 	__le32 flags;
 } __packed;
@@ -248,7 +248,7 @@
 
 struct ndis_80211_bssid_ex {
 	__le32 length;
-	u8 mac[6];
+	u8 mac[ETH_ALEN];
 	u8 padding[2];
 	struct ndis_80211_ssid ssid;
 	__le32 privacy;
@@ -283,7 +283,7 @@
 	__le32 size;
 	__le32 index;
 	__le32 length;
-	u8 bssid[6];
+	u8 bssid[ETH_ALEN];
 	u8 padding[6];
 	u8 rsc[8];
 	u8 material[32];
@@ -292,7 +292,7 @@
 struct ndis_80211_remove_key {
 	__le32 size;
 	__le32 index;
-	u8 bssid[6];
+	u8 bssid[ETH_ALEN];
 	u8 padding[2];
 } __packed;
 
@@ -310,7 +310,7 @@
 	struct req_ie {
 		__le16 capa;
 		__le16 listen_interval;
-		u8 cur_ap_address[6];
+		u8 cur_ap_address[ETH_ALEN];
 	} req_ie;
 	__le32 req_ie_length;
 	__le32 offset_req_ies;
@@ -338,7 +338,7 @@
 } __packed;
 
 struct ndis_80211_bssid_info {
-	u8 bssid[6];
+	u8 bssid[ETH_ALEN];
 	u8 pmkid[16];
 } __packed;
 
@@ -1037,7 +1037,7 @@
 			      bssid, &len);
 
 	if (ret != 0)
-		memset(bssid, 0, ETH_ALEN);
+		eth_zero_addr(bssid);
 
 	return ret;
 }
@@ -1391,7 +1391,7 @@
 	priv->encr_keys[index].len = key_len;
 	priv->encr_keys[index].cipher = cipher;
 	memcpy(&priv->encr_keys[index].material, key, key_len);
-	memset(&priv->encr_keys[index].bssid, 0xff, ETH_ALEN);
+	eth_broadcast_addr(priv->encr_keys[index].bssid);
 
 	return 0;
 }
@@ -1466,7 +1466,7 @@
 	} else {
 		/* group key */
 		if (priv->infra_mode == NDIS_80211_INFRA_ADHOC)
-			memset(ndis_key.bssid, 0xff, ETH_ALEN);
+			eth_broadcast_addr(ndis_key.bssid);
 		else
 			get_bssid(usbdev, ndis_key.bssid);
 	}
@@ -1486,7 +1486,7 @@
 	if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY)
 		memcpy(&priv->encr_keys[index].bssid, ndis_key.bssid, ETH_ALEN);
 	else
-		memset(&priv->encr_keys[index].bssid, 0xff, ETH_ALEN);
+		eth_broadcast_addr(priv->encr_keys[index].bssid);
 
 	if (flags & NDIS_80211_ADDKEY_TRANSMIT_KEY)
 		priv->encr_tx_key_index = index;
@@ -2280,7 +2280,7 @@
 	netdev_dbg(usbdev->net, "cfg80211.disconnect(%d)\n", reason_code);
 
 	priv->connected = false;
-	memset(priv->bssid, 0, ETH_ALEN);
+	eth_zero_addr(priv->bssid);
 
 	return deauthenticate(usbdev);
 }
@@ -2392,7 +2392,7 @@
 	netdev_dbg(usbdev->net, "cfg80211.leave_ibss()\n");
 
 	priv->connected = false;
-	memset(priv->bssid, 0, ETH_ALEN);
+	eth_zero_addr(priv->bssid);
 
 	return deauthenticate(usbdev);
 }
@@ -2478,7 +2478,7 @@
 	ret = rndis_query_oid(usbdev, RNDIS_OID_GEN_LINK_SPEED, &linkspeed, &len);
 	if (ret == 0) {
 		sinfo->txrate.legacy = le32_to_cpu(linkspeed) / 1000;
-		sinfo->filled |= STATION_INFO_TX_BITRATE;
+		sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
 	}
 
 	len = sizeof(rssi);
@@ -2486,7 +2486,7 @@
 			      &rssi, &len);
 	if (ret == 0) {
 		sinfo->signal = level_to_qual(le32_to_cpu(rssi));
-		sinfo->filled |= STATION_INFO_SIGNAL;
+		sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
 	}
 }
 
@@ -2857,7 +2857,7 @@
 
 	if (priv->connected) {
 		priv->connected = false;
-		memset(priv->bssid, 0, ETH_ALEN);
+		eth_zero_addr(priv->bssid);
 
 		deauthenticate(usbdev);
 
diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
index 4834a9a..b6cc9ff 100644
--- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
@@ -172,7 +172,6 @@
 		(struct rsi_91x_sdiodev *)adapter->rsi_dev;
 	u32 len;
 	u32 num_blocks;
-	const u8 *fw;
 	const struct firmware *fw_entry = NULL;
 	u32 block_size = dev->tx_blk_size;
 	int status = 0;
@@ -201,7 +200,6 @@
 		return status;
 	}
 
-	fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL);
 	len = fw_entry->size;
 
 	if (len % 4)
@@ -212,7 +210,7 @@
 	rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len);
 	rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks);
 
-	status = rsi_copy_to_card(common, fw, len, num_blocks);
+	status = rsi_copy_to_card(common, fw_entry->data, len, num_blocks);
 	release_firmware(fw_entry);
 	return status;
 }
diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig
index b6c87b7..0860304 100644
--- a/drivers/net/wireless/rt2x00/Kconfig
+++ b/drivers/net/wireless/rt2x00/Kconfig
@@ -259,14 +259,14 @@
 	select BPAUTO_AVERAGE
 
 config RT2X00_LIB_FIRMWARE
-	boolean
+	bool
 	depends on FW_LOADER
 
 config RT2X00_LIB_CRYPTO
-	boolean
+	bool
 
 config RT2X00_LIB_LEDS
-	boolean
+	bool
 	default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n)
 
 config RT2X00_LIB_DEBUGFS
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index b12c863..1b25a60 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -8020,13 +8020,13 @@
 	rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, &busy_ext);
 
 	if (idle || busy) {
-		survey->filled = SURVEY_INFO_CHANNEL_TIME |
-				 SURVEY_INFO_CHANNEL_TIME_BUSY |
-				 SURVEY_INFO_CHANNEL_TIME_EXT_BUSY;
+		survey->filled = SURVEY_INFO_TIME |
+				 SURVEY_INFO_TIME_BUSY |
+				 SURVEY_INFO_TIME_EXT_BUSY;
 
-		survey->channel_time = (idle + busy) / 1000;
-		survey->channel_time_busy = busy / 1000;
-		survey->channel_time_ext_busy = busy_ext / 1000;
+		survey->time = (idle + busy) / 1000;
+		survey->time_busy = busy / 1000;
+		survey->time_ext_busy = busy_ext / 1000;
 	}
 
 	if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index df74814..4381d51 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -233,6 +233,7 @@
 {
 	__le32 *reg;
 	u32 fw_mode;
+	int ret;
 
 	reg = kmalloc(sizeof(*reg), GFP_KERNEL);
 	if (reg == NULL)
@@ -242,11 +243,14 @@
 	 * magic value USB_MODE_AUTORUN (0x11) to the device, thus the
 	 * returned value would be invalid.
 	 */
-	rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE,
-				 USB_VENDOR_REQUEST_IN, 0, USB_MODE_AUTORUN,
-				 reg, sizeof(*reg), REGISTER_TIMEOUT_FIRMWARE);
+	ret = rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE,
+				       USB_VENDOR_REQUEST_IN, 0,
+				       USB_MODE_AUTORUN, reg, sizeof(*reg),
+				       REGISTER_TIMEOUT_FIRMWARE);
 	fw_mode = le32_to_cpu(*reg);
 	kfree(reg);
+	if (ret < 0)
+		return ret;
 
 	if ((fw_mode & 0x00000003) == 2)
 		return 1;
@@ -289,6 +293,7 @@
 	if (retval) {
 		rt2x00_info(rt2x00dev,
 			    "Firmware loading not required - NIC in AutoRun mode\n");
+		__clear_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags);
 	} else {
 		rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE,
 					      data + offset, length);
@@ -374,7 +379,6 @@
 static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev)
 {
 	rt2800_disable_radio(rt2x00dev);
-	rt2x00usb_disable_radio(rt2x00dev);
 }
 
 static int rt2800usb_set_state(struct rt2x00_dev *rt2x00dev,
@@ -1040,6 +1044,7 @@
 	{ USB_DEVICE(0x07d1, 0x3c17) },
 	{ USB_DEVICE(0x2001, 0x3317) },
 	{ USB_DEVICE(0x2001, 0x3c1b) },
+	{ USB_DEVICE(0x2001, 0x3c25) },
 	/* Draytek */
 	{ USB_DEVICE(0x07fa, 0x7712) },
 	/* DVICO */
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c
index 1122dc4..48a2cad 100644
--- a/drivers/net/wireless/rt2x00/rt2x00config.c
+++ b/drivers/net/wireless/rt2x00/rt2x00config.c
@@ -240,7 +240,7 @@
 		rt2x00dev->rf_channel = libconf.rf.channel;
 	}
 
-	if (test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) &&
+	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) &&
 	    (ieee80211_flags & IEEE80211_CONF_CHANGE_PS))
 		cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);
 
@@ -257,7 +257,7 @@
 		rt2x00link_reset_tuner(rt2x00dev, false);
 
 	if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
-	    test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) &&
+	    rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) &&
 	    (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) &&
 	    (conf->flags & IEEE80211_CONF_PS)) {
 		beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon;
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 5e535e3..a3d3fb9 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -351,7 +351,7 @@
 	/*
 	 * Remove L2 padding which was added during
 	 */
-	if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags))
+	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD))
 		rt2x00queue_remove_l2pad(entry->skb, header_length);
 
 	/*
@@ -460,7 +460,7 @@
 	 * send the status report back.
 	 */
 	if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) {
-		if (test_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags))
+		if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TASKLET_CONTEXT))
 			ieee80211_tx_status(rt2x00dev->hw, entry->skb);
 		else
 			ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb);
@@ -1056,9 +1056,9 @@
 	/*
 	 * Take TX headroom required for alignment into account.
 	 */
-	if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags))
+	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD))
 		rt2x00dev->hw->extra_tx_headroom += RT2X00_L2PAD_SIZE;
-	else if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags))
+	else if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA))
 		rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE;
 
 	/*
@@ -1069,7 +1069,7 @@
 	/*
 	 * Allocate tx status FIFO for driver use.
 	 */
-	if (test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags)) {
+	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TXSTATUS_FIFO)) {
 		/*
 		 * Allocate the txstatus fifo. In the worst case the tx
 		 * status fifo has to hold the tx status of all entries
@@ -1131,7 +1131,7 @@
 	/*
 	 * Stop rfkill polling.
 	 */
-	if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags))
+	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL))
 		rt2x00rfkill_unregister(rt2x00dev);
 
 	/*
@@ -1173,7 +1173,7 @@
 	/*
 	 * Start rfkill polling.
 	 */
-	if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags))
+	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL))
 		rt2x00rfkill_register(rt2x00dev);
 
 	return 0;
@@ -1389,7 +1389,7 @@
 	/*
 	 * Start rfkill polling.
 	 */
-	if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags))
+	if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL))
 		rt2x00rfkill_register(rt2x00dev);
 
 	return 0;
@@ -1408,7 +1408,7 @@
 	/*
 	 * Stop rfkill polling.
 	 */
-	if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags))
+	if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL))
 		rt2x00rfkill_unregister(rt2x00dev);
 
 	/*
diff --git a/drivers/net/wireless/rt2x00/rt2x00firmware.c b/drivers/net/wireless/rt2x00/rt2x00firmware.c
index fbae279..5813300 100644
--- a/drivers/net/wireless/rt2x00/rt2x00firmware.c
+++ b/drivers/net/wireless/rt2x00/rt2x00firmware.c
@@ -96,7 +96,7 @@
 {
 	int retval;
 
-	if (!test_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags))
+	if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_FIRMWARE))
 		return 0;
 
 	if (!rt2x00dev->fw) {
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 7f9e471..d5d759a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -119,7 +119,7 @@
 	 * Use the ATIM queue if appropriate and present.
 	 */
 	if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM &&
-	    test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags))
+	    rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE))
 		qid = QID_ATIM;
 
 	queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 66ff364..68b620b 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -85,7 +85,7 @@
 	memset(skbdesc, 0, sizeof(*skbdesc));
 	skbdesc->entry = entry;
 
-	if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) {
+	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA)) {
 		dma_addr_t skb_dma;
 
 		skb_dma = dma_map_single(rt2x00dev->dev, skb->data, skb->len,
@@ -198,7 +198,7 @@
 
 	__set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
 
-	if (!test_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags)) {
+	if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_SW_SEQNO)) {
 		/*
 		 * rt2800 has a H/W (or F/W) bug, device incorrectly increase
 		 * seqno on retransmited data (non-QOS) frames. To workaround
@@ -484,7 +484,7 @@
 	rt2x00crypto_create_tx_descriptor(rt2x00dev, skb, txdesc);
 	rt2x00queue_create_tx_descriptor_seq(rt2x00dev, skb, txdesc);
 
-	if (test_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags))
+	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_HT_TX_DESC))
 		rt2x00queue_create_tx_descriptor_ht(rt2x00dev, skb, txdesc,
 						   sta, hwrate);
 	else
@@ -526,7 +526,7 @@
 	/*
 	 * Map the skb to DMA.
 	 */
-	if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags) &&
+	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA) &&
 	    rt2x00queue_map_txskb(entry))
 		return -ENOMEM;
 
@@ -646,7 +646,7 @@
 	 */
 	if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) &&
 	    !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) {
-		if (test_bit(REQUIRE_COPY_IV, &queue->rt2x00dev->cap_flags))
+		if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_COPY_IV))
 			rt2x00crypto_tx_copy_iv(skb, &txdesc);
 		else
 			rt2x00crypto_tx_remove_iv(skb, &txdesc);
@@ -660,9 +660,9 @@
 	 * PCI and USB devices, while header alignment only is valid
 	 * for PCI devices.
 	 */
-	if (test_bit(REQUIRE_L2PAD, &queue->rt2x00dev->cap_flags))
+	if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_L2PAD))
 		rt2x00queue_insert_l2pad(skb, txdesc.header_length);
-	else if (test_bit(REQUIRE_DMA, &queue->rt2x00dev->cap_flags))
+	else if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_DMA))
 		rt2x00queue_align_frame(skb);
 
 	/*
@@ -1178,7 +1178,7 @@
 	if (status)
 		goto exit;
 
-	if (test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) {
+	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE)) {
 		status = rt2x00queue_alloc_entries(rt2x00dev->atim);
 		if (status)
 			goto exit;
@@ -1234,7 +1234,7 @@
 	struct data_queue *queue;
 	enum data_queue_qid qid;
 	unsigned int req_atim =
-	    !!test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags);
+	    rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE);
 
 	/*
 	 * We need the following queues:
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index 892270d..7627af6 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -274,7 +274,7 @@
 	 * Schedule the delayed work for reading the TX status
 	 * from the device.
 	 */
-	if (!test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags) ||
+	if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TXSTATUS_FIFO) ||
 	    !kfifo_is_empty(&rt2x00dev->txstatus_fifo))
 		queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
 }
@@ -456,7 +456,7 @@
 	 * Kill guardian urb (if required by driver).
 	 */
 	if ((entry->queue->qid == QID_BEACON) &&
-	    (test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags)))
+	    (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD)))
 		usb_kill_urb(bcn_priv->guardian_urb);
 
 	return false;
@@ -655,7 +655,7 @@
 	 * then we are done.
 	 */
 	if (queue->qid != QID_BEACON ||
-	    !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags))
+	    !rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD))
 		return 0;
 
 	for (i = 0; i < queue->limit; i++) {
@@ -690,7 +690,7 @@
 	 * then we are done.
 	 */
 	if (queue->qid != QID_BEACON ||
-	    !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags))
+	    !rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD))
 		return;
 
 	for (i = 0; i < queue->limit; i++) {
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h
index 8f85fbd..569363d 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.h
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.h
@@ -199,7 +199,7 @@
 					   const unsigned int offset,
 					   u32 *value)
 {
-	__le32 reg;
+	__le32 reg = 0;
 	rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
 				      USB_VENDOR_REQUEST_IN, offset,
 				      &reg, sizeof(reg));
@@ -219,7 +219,7 @@
 						const unsigned int offset,
 						u32 *value)
 {
-	__le32 reg;
+	__le32 reg = 0;
 	rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ,
 				       USB_VENDOR_REQUEST_IN, offset,
 				       &reg, sizeof(reg), REGISTER_TIMEOUT);
diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c
index 40b6d1d..01f56c7 100644
--- a/drivers/net/wireless/rtlwifi/base.c
+++ b/drivers/net/wireless/rtlwifi/base.c
@@ -867,63 +867,135 @@
  *
  * B/G rate:
  * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC92_RATE1M-->DESC92_RATE54M ==> idx is 0-->11,
+ * DESC_RATE1M-->DESC_RATE54M ==> idx is 0-->11,
  *
  * N rate:
  * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC92_RATEMCS0-->DESC92_RATEMCS15 ==> idx is 0-->15
+ * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15
  *
  * 5G band:rx_status->band == IEEE80211_BAND_5GHZ
  * A rate:
  * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC92_RATE6M-->DESC92_RATE54M ==> idx is 0-->7,
+ * DESC_RATE6M-->DESC_RATE54M ==> idx is 0-->7,
  *
  * N rate:
  * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC92_RATEMCS0-->DESC92_RATEMCS15 ==> idx is 0-->15
+ * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15
+ *
+ * VHT rates:
+ * DESC_RATEVHT1SS_MCS0-->DESC_RATEVHT1SS_MCS9 ==> idx is 0-->9
+ * DESC_RATEVHT2SS_MCS0-->DESC_RATEVHT2SS_MCS9 ==> idx is 0-->9
  */
-int rtlwifi_rate_mapping(struct ieee80211_hw *hw,
-			 bool isht, u8 desc_rate, bool first_ampdu)
+int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht, bool isvht,
+			 u8 desc_rate)
 {
 	int rate_idx;
 
+	if (isvht) {
+		switch (desc_rate) {
+		case DESC_RATEVHT1SS_MCS0:
+			rate_idx = 0;
+			break;
+		case DESC_RATEVHT1SS_MCS1:
+			rate_idx = 1;
+			break;
+		case DESC_RATEVHT1SS_MCS2:
+			rate_idx = 2;
+			break;
+		case DESC_RATEVHT1SS_MCS3:
+			rate_idx = 3;
+			break;
+		case DESC_RATEVHT1SS_MCS4:
+			rate_idx = 4;
+			break;
+		case DESC_RATEVHT1SS_MCS5:
+			rate_idx = 5;
+			break;
+		case DESC_RATEVHT1SS_MCS6:
+			rate_idx = 6;
+			break;
+		case DESC_RATEVHT1SS_MCS7:
+			rate_idx = 7;
+			break;
+		case DESC_RATEVHT1SS_MCS8:
+			rate_idx = 8;
+			break;
+		case DESC_RATEVHT1SS_MCS9:
+			rate_idx = 9;
+			break;
+		case DESC_RATEVHT2SS_MCS0:
+			rate_idx = 0;
+			break;
+		case DESC_RATEVHT2SS_MCS1:
+			rate_idx = 1;
+			break;
+		case DESC_RATEVHT2SS_MCS2:
+			rate_idx = 2;
+			break;
+		case DESC_RATEVHT2SS_MCS3:
+			rate_idx = 3;
+			break;
+		case DESC_RATEVHT2SS_MCS4:
+			rate_idx = 4;
+			break;
+		case DESC_RATEVHT2SS_MCS5:
+			rate_idx = 5;
+			break;
+		case DESC_RATEVHT2SS_MCS6:
+			rate_idx = 6;
+			break;
+		case DESC_RATEVHT2SS_MCS7:
+			rate_idx = 7;
+			break;
+		case DESC_RATEVHT2SS_MCS8:
+			rate_idx = 8;
+			break;
+		case DESC_RATEVHT2SS_MCS9:
+			rate_idx = 9;
+			break;
+		default:
+			rate_idx = 0;
+			break;
+		}
+		return rate_idx;
+	}
 	if (false == isht) {
 		if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) {
 			switch (desc_rate) {
-			case DESC92_RATE1M:
+			case DESC_RATE1M:
 				rate_idx = 0;
 				break;
-			case DESC92_RATE2M:
+			case DESC_RATE2M:
 				rate_idx = 1;
 				break;
-			case DESC92_RATE5_5M:
+			case DESC_RATE5_5M:
 				rate_idx = 2;
 				break;
-			case DESC92_RATE11M:
+			case DESC_RATE11M:
 				rate_idx = 3;
 				break;
-			case DESC92_RATE6M:
+			case DESC_RATE6M:
 				rate_idx = 4;
 				break;
-			case DESC92_RATE9M:
+			case DESC_RATE9M:
 				rate_idx = 5;
 				break;
-			case DESC92_RATE12M:
+			case DESC_RATE12M:
 				rate_idx = 6;
 				break;
-			case DESC92_RATE18M:
+			case DESC_RATE18M:
 				rate_idx = 7;
 				break;
-			case DESC92_RATE24M:
+			case DESC_RATE24M:
 				rate_idx = 8;
 				break;
-			case DESC92_RATE36M:
+			case DESC_RATE36M:
 				rate_idx = 9;
 				break;
-			case DESC92_RATE48M:
+			case DESC_RATE48M:
 				rate_idx = 10;
 				break;
-			case DESC92_RATE54M:
+			case DESC_RATE54M:
 				rate_idx = 11;
 				break;
 			default:
@@ -932,28 +1004,28 @@
 			}
 		} else {
 			switch (desc_rate) {
-			case DESC92_RATE6M:
+			case DESC_RATE6M:
 				rate_idx = 0;
 				break;
-			case DESC92_RATE9M:
+			case DESC_RATE9M:
 				rate_idx = 1;
 				break;
-			case DESC92_RATE12M:
+			case DESC_RATE12M:
 				rate_idx = 2;
 				break;
-			case DESC92_RATE18M:
+			case DESC_RATE18M:
 				rate_idx = 3;
 				break;
-			case DESC92_RATE24M:
+			case DESC_RATE24M:
 				rate_idx = 4;
 				break;
-			case DESC92_RATE36M:
+			case DESC_RATE36M:
 				rate_idx = 5;
 				break;
-			case DESC92_RATE48M:
+			case DESC_RATE48M:
 				rate_idx = 6;
 				break;
-			case DESC92_RATE54M:
+			case DESC_RATE54M:
 				rate_idx = 7;
 				break;
 			default:
@@ -963,52 +1035,52 @@
 		}
 	} else {
 		switch (desc_rate) {
-		case DESC92_RATEMCS0:
+		case DESC_RATEMCS0:
 			rate_idx = 0;
 			break;
-		case DESC92_RATEMCS1:
+		case DESC_RATEMCS1:
 			rate_idx = 1;
 			break;
-		case DESC92_RATEMCS2:
+		case DESC_RATEMCS2:
 			rate_idx = 2;
 			break;
-		case DESC92_RATEMCS3:
+		case DESC_RATEMCS3:
 			rate_idx = 3;
 			break;
-		case DESC92_RATEMCS4:
+		case DESC_RATEMCS4:
 			rate_idx = 4;
 			break;
-		case DESC92_RATEMCS5:
+		case DESC_RATEMCS5:
 			rate_idx = 5;
 			break;
-		case DESC92_RATEMCS6:
+		case DESC_RATEMCS6:
 			rate_idx = 6;
 			break;
-		case DESC92_RATEMCS7:
+		case DESC_RATEMCS7:
 			rate_idx = 7;
 			break;
-		case DESC92_RATEMCS8:
+		case DESC_RATEMCS8:
 			rate_idx = 8;
 			break;
-		case DESC92_RATEMCS9:
+		case DESC_RATEMCS9:
 			rate_idx = 9;
 			break;
-		case DESC92_RATEMCS10:
+		case DESC_RATEMCS10:
 			rate_idx = 10;
 			break;
-		case DESC92_RATEMCS11:
+		case DESC_RATEMCS11:
 			rate_idx = 11;
 			break;
-		case DESC92_RATEMCS12:
+		case DESC_RATEMCS12:
 			rate_idx = 12;
 			break;
-		case DESC92_RATEMCS13:
+		case DESC_RATEMCS13:
 			rate_idx = 13;
 			break;
-		case DESC92_RATEMCS14:
+		case DESC_RATEMCS14:
 			rate_idx = 14;
 			break;
-		case DESC92_RATEMCS15:
+		case DESC_RATEMCS15:
 			rate_idx = 15;
 			break;
 		default:
@@ -1243,7 +1315,8 @@
 }
 
 /*should call before software enc*/
-u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
+u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,
+		       bool is_enc)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
@@ -1272,7 +1345,9 @@
 		break;
 	}
 
-	offset = mac_hdr_len + SNAP_SIZE + encrypt_header_len;
+	offset = mac_hdr_len + SNAP_SIZE;
+	if (is_enc)
+		offset += encrypt_header_len;
 	ether_type = be16_to_cpup((__be16 *)(skb->data + offset));
 
 	if (ETH_P_IP == ether_type) {
@@ -1314,8 +1389,11 @@
 		}
 
 		return true;
-	} else if (0x86DD == ether_type) {
-		return true;
+	} else if (ETH_P_IPV6 == ether_type) {
+		/* TODO: Handle any IPv6 cases that need special handling.
+		 * For now, always return false
+		 */
+		goto end;
 	}
 
 end:
diff --git a/drivers/net/wireless/rtlwifi/base.h b/drivers/net/wireless/rtlwifi/base.h
index 982f245..74233d6 100644
--- a/drivers/net/wireless/rtlwifi/base.h
+++ b/drivers/net/wireless/rtlwifi/base.h
@@ -45,9 +45,6 @@
 #define RTL_TX_DESC_SIZE	32
 #define RTL_TX_HEADER_SIZE	(RTL_TX_DESC_SIZE + RTL_TX_DUMMY_SIZE)
 
-#define HT_AMSDU_SIZE_4K	3839
-#define HT_AMSDU_SIZE_8K	7935
-
 #define MAX_BIT_RATE_40MHZ_MCS15	300	/* Mbps */
 #define MAX_BIT_RATE_40MHZ_MCS7		150	/* Mbps */
 
@@ -61,9 +58,6 @@
 #define MAX_BIT_RATE_LONG_GI_1NSS_80MHZ_MCS9	390	/* Mbps */
 #define MAX_BIT_RATE_LONG_GI_1NSS_80MHZ_MCS7	293	/* Mbps */
 
-#define RTL_RATE_COUNT_LEGACY		12
-#define RTL_CHANNEL_COUNT		14
-
 #define FRAME_OFFSET_FRAME_CONTROL	0
 #define FRAME_OFFSET_DURATION		2
 #define FRAME_OFFSET_ADDRESS1		4
@@ -123,13 +117,13 @@
 void rtl_deinit_deferred_work(struct ieee80211_hw *hw);
 
 bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx);
-int rtlwifi_rate_mapping(struct ieee80211_hw *hw,
-			 bool isht, u8 desc_rate, bool first_ampdu);
+int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht,
+			 bool isvht, u8 desc_rate);
 bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb);
-u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx);
+u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,
+		       bool is_enc);
 
 void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb);
-void rtl_watch_dog_timer_callback(unsigned long data);
 int rtl_tx_agg_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 	struct ieee80211_sta *sta, u16 tid, u16 *ssn);
 int rtl_tx_agg_stop(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/rtlwifi/cam.h b/drivers/net/wireless/rtlwifi/cam.h
index 3550808..e2e647d 100644
--- a/drivers/net/wireless/rtlwifi/cam.h
+++ b/drivers/net/wireless/rtlwifi/cam.h
@@ -28,13 +28,11 @@
 
 #define CAM_CONTENT_COUNT				8
 
-#define CFG_DEFAULT_KEY					BIT(5)
 #define CFG_VALID					BIT(15)
 
 #define PAIRWISE_KEYIDX					0
 #define CAM_PAIRWISE_KEY_POSITION			4
 
-#define	CAM_CONFIG_USEDK				1
 #define	CAM_CONFIG_NO_USEDK				0
 
 void rtl_cam_reset_all_entry(struct ieee80211_hw *hw);
diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c
index 5fc6f52..3b3a88b 100644
--- a/drivers/net/wireless/rtlwifi/core.c
+++ b/drivers/net/wireless/rtlwifi/core.c
@@ -95,7 +95,8 @@
 }
 EXPORT_SYMBOL(rtl_bb_delay);
 
-void rtl_fw_cb(const struct firmware *firmware, void *context)
+static void rtl_fw_do_work(const struct firmware *firmware, void *context,
+			   bool is_wow)
 {
 	struct ieee80211_hw *hw = context;
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -125,12 +126,31 @@
 		release_firmware(firmware);
 		return;
 	}
-	memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, firmware->size);
+	if (!is_wow) {
+		memcpy(rtlpriv->rtlhal.pfirmware, firmware->data,
+		       firmware->size);
+		rtlpriv->rtlhal.fwsize = firmware->size;
+	} else {
+		memcpy(rtlpriv->rtlhal.wowlan_firmware, firmware->data,
+		       firmware->size);
+		rtlpriv->rtlhal.wowlan_fwsize = firmware->size;
+	}
 	rtlpriv->rtlhal.fwsize = firmware->size;
 	release_firmware(firmware);
 }
+
+void rtl_fw_cb(const struct firmware *firmware, void *context)
+{
+	rtl_fw_do_work(firmware, context, false);
+}
 EXPORT_SYMBOL(rtl_fw_cb);
 
+void rtl_wowlan_fw_cb(const struct firmware *firmware, void *context)
+{
+	rtl_fw_do_work(firmware, context, true);
+}
+EXPORT_SYMBOL(rtl_wowlan_fw_cb);
+
 /*mutex for start & stop is must here. */
 static int rtl_op_start(struct ieee80211_hw *hw)
 {
@@ -175,7 +195,7 @@
 	if (!(support_remote_wakeup &&
 	      rtlhal->enter_pnp_sleep)) {
 		mac->link_state = MAC80211_NOLINK;
-		memset(mac->bssid, 0, 6);
+		eth_zero_addr(mac->bssid);
 		mac->vendor = PEER_UNKNOWN;
 
 		/* reset sec info */
@@ -337,7 +357,7 @@
 	mac->p2p = 0;
 	mac->vif = NULL;
 	mac->link_state = MAC80211_NOLINK;
-	memset(mac->bssid, 0, ETH_ALEN);
+	eth_zero_addr(mac->bssid);
 	mac->vendor = PEER_UNKNOWN;
 	mac->opmode = NL80211_IFTYPE_UNSPECIFIED;
 	rtlpriv->cfg->ops->set_network_type(hw, mac->opmode);
@@ -990,6 +1010,16 @@
 	return 0;
 }
 
+static void send_beacon_frame(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif)
+{
+	struct rtl_priv *rtlpriv = rtl_priv(hw);
+	struct sk_buff *skb = ieee80211_beacon_get(hw, vif);
+
+	if (skb)
+		rtlpriv->intf_ops->adapter_tx(hw, NULL, skb, NULL);
+}
+
 static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
 				    struct ieee80211_vif *vif,
 				    struct ieee80211_bss_conf *bss_conf,
@@ -1020,6 +1050,7 @@
 
 				if (rtlpriv->cfg->ops->linked_set_reg)
 					rtlpriv->cfg->ops->linked_set_reg(hw);
+				send_beacon_frame(hw, vif);
 			}
 		}
 		if ((changed & BSS_CHANGED_BEACON_ENABLED &&
@@ -1126,7 +1157,7 @@
 			if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE)
 				rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
 			mac->link_state = MAC80211_NOLINK;
-			memset(mac->bssid, 0, ETH_ALEN);
+			eth_zero_addr(mac->bssid);
 			mac->vendor = PEER_UNKNOWN;
 			mac->mode = 0;
 
@@ -1851,3 +1882,40 @@
 	return false;
 }
 EXPORT_SYMBOL_GPL(rtl_btc_status_false);
+
+void rtl_dm_diginit(struct ieee80211_hw *hw, u32 cur_igvalue)
+{
+	struct rtl_priv *rtlpriv = rtl_priv(hw);
+	struct dig_t *dm_digtable = &rtlpriv->dm_digtable;
+
+	dm_digtable->dig_enable_flag = true;
+	dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX;
+	dm_digtable->cur_igvalue = cur_igvalue;
+	dm_digtable->pre_igvalue = 0;
+	dm_digtable->cur_sta_cstate = DIG_STA_DISCONNECT;
+	dm_digtable->presta_cstate = DIG_STA_DISCONNECT;
+	dm_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT;
+	dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW;
+	dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH;
+	dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
+	dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
+	dm_digtable->rx_gain_max = DM_DIG_MAX;
+	dm_digtable->rx_gain_min = DM_DIG_MIN;
+	dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT;
+	dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX;
+	dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN;
+	dm_digtable->pre_cck_cca_thres = 0xff;
+	dm_digtable->cur_cck_cca_thres = 0x83;
+	dm_digtable->forbidden_igi = DM_DIG_MIN;
+	dm_digtable->large_fa_hit = 0;
+	dm_digtable->recover_cnt = 0;
+	dm_digtable->dig_min_0 = 0x25;
+	dm_digtable->dig_min_1 = 0x25;
+	dm_digtable->media_connect_0 = false;
+	dm_digtable->media_connect_1 = false;
+	rtlpriv->dm.dm_initialgain_enable = true;
+	dm_digtable->bt30_cur_igi = 0x32;
+	dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX;
+	dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_LOWRSSI;
+}
+EXPORT_SYMBOL(rtl_dm_diginit);
diff --git a/drivers/net/wireless/rtlwifi/core.h b/drivers/net/wireless/rtlwifi/core.h
index 624e1dc..82733c6 100644
--- a/drivers/net/wireless/rtlwifi/core.h
+++ b/drivers/net/wireless/rtlwifi/core.h
@@ -33,15 +33,55 @@
 	FIF_FCSFAIL | \
 	FIF_BCN_PRBRESP_PROMISC)
 
-#define RTL_SUPPORTED_CTRL_FILTER	0xFF
+#define DM_DIG_THRESH_HIGH		40
+#define DM_DIG_THRESH_LOW		35
+#define DM_FALSEALARM_THRESH_LOW	400
+#define DM_FALSEALARM_THRESH_HIGH	1000
+
+#define DM_DIG_MAX			0x3e
+#define DM_DIG_MIN			0x1e
+#define DM_DIG_MAX_AP			0x32
+#define DM_DIG_BACKOFF_MAX		12
+#define DM_DIG_BACKOFF_MIN		-4
+#define DM_DIG_BACKOFF_DEFAULT		10
+
+enum cck_packet_detection_threshold {
+	CCK_PD_STAGE_LOWRSSI = 0,
+	CCK_PD_STAGE_HIGHRSSI = 1,
+	CCK_FA_STAGE_LOW = 2,
+	CCK_FA_STAGE_HIGH = 3,
+	CCK_PD_STAGE_MAX = 4,
+};
+
+enum dm_dig_ext_port_alg_e {
+	DIG_EXT_PORT_STAGE_0 = 0,
+	DIG_EXT_PORT_STAGE_1 = 1,
+	DIG_EXT_PORT_STAGE_2 = 2,
+	DIG_EXT_PORT_STAGE_3 = 3,
+	DIG_EXT_PORT_STAGE_MAX = 4,
+};
+
+enum dm_dig_connect_e {
+	DIG_STA_DISCONNECT,
+	DIG_STA_CONNECT,
+	DIG_STA_BEFORE_CONNECT,
+	DIG_MULTISTA_DISCONNECT,
+	DIG_MULTISTA_CONNECT,
+	DIG_AP_DISCONNECT,
+	DIG_AP_CONNECT,
+	DIG_AP_ADD_STATION,
+	DIG_CONNECT_MAX
+};
 
 extern const struct ieee80211_ops rtl_ops;
 void rtl_fw_cb(const struct firmware *firmware, void *context);
+void rtl_wowlan_fw_cb(const struct firmware *firmware, void *context);
 void rtl_addr_delay(u32 addr);
 void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr,
 		     u32 mask, u32 data);
 void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data);
 bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb);
 bool rtl_btc_status_false(void);
+void rtl_dm_diginit(struct ieee80211_hw *hw, u32 cur_igval);
 
 #endif
diff --git a/drivers/net/wireless/rtlwifi/efuse.h b/drivers/net/wireless/rtlwifi/efuse.h
index fdab824..be02e78 100644
--- a/drivers/net/wireless/rtlwifi/efuse.h
+++ b/drivers/net/wireless/rtlwifi/efuse.h
@@ -40,12 +40,6 @@
 #define PG_STATE_WORD_3			0x10
 #define PG_STATE_DATA			0x20
 
-#define PG_SWBYTE_H			0x01
-#define PG_SWBYTE_L			0x02
-
-#define _POWERON_DELAY_
-#define _PRE_EXECUTE_READ_CMD_
-
 #define EFUSE_REPEAT_THRESHOLD_		3
 #define EFUSE_ERROE_HANDLE		1
 
diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c
index 846a2e6..f46c9d7 100644
--- a/drivers/net/wireless/rtlwifi/pci.c
+++ b/drivers/net/wireless/rtlwifi/pci.c
@@ -578,6 +578,13 @@
 		else
 			entry = (u8 *)(&ring->desc[ring->idx]);
 
+		if (rtlpriv->cfg->ops->get_available_desc &&
+		    rtlpriv->cfg->ops->get_available_desc(hw, prio) <= 1) {
+			RT_TRACE(rtlpriv, (COMP_INTR | COMP_SEND), DBG_DMESG,
+				 "no available desc!\n");
+			return;
+		}
+
 		if (!rtlpriv->cfg->ops->is_tx_desc_closed(hw, prio, ring->idx))
 			return;
 		ring->idx = (ring->idx + 1) % ring->entries;
@@ -641,10 +648,9 @@
 
 		ieee80211_tx_status_irqsafe(hw, skb);
 
-		if ((ring->entries - skb_queue_len(&ring->queue))
-				== 2) {
+		if ((ring->entries - skb_queue_len(&ring->queue)) <= 4) {
 
-			RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
+			RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
 				 "more desc left, wake skb_queue@%d, ring->idx = %d, skb_queue_len = 0x%x\n",
 				 prio, ring->idx,
 				 skb_queue_len(&ring->queue));
@@ -666,7 +672,8 @@
 }
 
 static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw,
-				    u8 *entry, int rxring_idx, int desc_idx)
+				    struct sk_buff *new_skb, u8 *entry,
+				    int rxring_idx, int desc_idx)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
 	struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
@@ -674,11 +681,15 @@
 	u8 tmp_one = 1;
 	struct sk_buff *skb;
 
+	if (likely(new_skb)) {
+		skb = new_skb;
+		goto remap;
+	}
 	skb = dev_alloc_skb(rtlpci->rxbuffersize);
 	if (!skb)
 		return 0;
-	rtlpci->rx_ring[rxring_idx].rx_buf[desc_idx] = skb;
 
+remap:
 	/* just set skb->cb to mapping addr for pci_unmap_single use */
 	*((dma_addr_t *)skb->cb) =
 		pci_map_single(rtlpci->pdev, skb_tail_pointer(skb),
@@ -686,6 +697,7 @@
 	bufferaddress = *((dma_addr_t *)skb->cb);
 	if (pci_dma_mapping_error(rtlpci->pdev, bufferaddress))
 		return 0;
+	rtlpci->rx_ring[rxring_idx].rx_buf[desc_idx] = skb;
 	if (rtlpriv->use_new_trx_flow) {
 		rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false,
 					    HW_DESC_RX_PREPARE,
@@ -781,12 +793,13 @@
 		/*rx pkt */
 		struct sk_buff *skb = rtlpci->rx_ring[rxring_idx].rx_buf[
 				      rtlpci->rx_ring[rxring_idx].idx];
+		struct sk_buff *new_skb;
 
 		if (rtlpriv->use_new_trx_flow) {
 			rx_remained_cnt =
 				rtlpriv->cfg->ops->rx_desc_buff_remained_cnt(hw,
 								      hw_queue);
-			if (rx_remained_cnt < 1)
+			if (rx_remained_cnt == 0)
 				return;
 
 		} else {	/* rx descriptor */
@@ -807,6 +820,10 @@
 		pci_unmap_single(rtlpci->pdev, *((dma_addr_t *)skb->cb),
 				 rtlpci->rxbuffersize, PCI_DMA_FROMDEVICE);
 
+		/* get a new skb - if fail, old one will be reused */
+		new_skb = dev_alloc_skb(rtlpci->rxbuffersize);
+		if (unlikely(!new_skb))
+			goto no_new;
 		if (rtlpriv->use_new_trx_flow) {
 			buffer_desc =
 			  &rtlpci->rx_ring[rxring_idx].buffer_desc
@@ -834,18 +851,18 @@
 			else
 				skb_reserve(skb, stats.rx_drvinfo_size +
 					    stats.rx_bufshift);
-
 		} else {
 			RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
 				 "skb->end - skb->tail = %d, len is %d\n",
 				 skb->end - skb->tail, len);
-			break;
+			dev_kfree_skb_any(skb);
+			goto new_trx_end;
 		}
 		/* handle command packet here */
 		if (rtlpriv->cfg->ops->rx_command_packet &&
 		    rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
 				dev_kfree_skb_any(skb);
-				goto end;
+				goto new_trx_end;
 		}
 
 		/*
@@ -870,7 +887,7 @@
 				unicast = true;
 				rtlpriv->stats.rxbytesunicast += skb->len;
 			}
-			rtl_is_special_data(hw, skb, false);
+			rtl_is_special_data(hw, skb, false, true);
 
 			if (ieee80211_is_data(fc)) {
 				rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX);
@@ -895,6 +912,7 @@
 		} else {
 			dev_kfree_skb_any(skb);
 		}
+new_trx_end:
 		if (rtlpriv->use_new_trx_flow) {
 			rtlpci->rx_ring[hw_queue].next_rx_rp += 1;
 			rtlpci->rx_ring[hw_queue].next_rx_rp %=
@@ -910,15 +928,16 @@
 			rtlpriv->enter_ps = false;
 			schedule_work(&rtlpriv->works.lps_change_work);
 		}
-end:
+		skb = new_skb;
+no_new:
 		if (rtlpriv->use_new_trx_flow) {
-			_rtl_pci_init_one_rxdesc(hw, (u8 *)buffer_desc,
+			_rtl_pci_init_one_rxdesc(hw, skb, (u8 *)buffer_desc,
 						 rxring_idx,
-					       rtlpci->rx_ring[rxring_idx].idx);
-		} else {
-			_rtl_pci_init_one_rxdesc(hw, (u8 *)pdesc, rxring_idx,
 						 rtlpci->rx_ring[rxring_idx].idx);
-
+		} else {
+			_rtl_pci_init_one_rxdesc(hw, skb, (u8 *)pdesc,
+						 rxring_idx,
+						 rtlpci->rx_ring[rxring_idx].idx);
 			if (rtlpci->rx_ring[rxring_idx].idx ==
 			    rtlpci->rxringcount - 1)
 				rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc,
@@ -1105,12 +1124,22 @@
 	/*This is for new trx flow*/
 	struct rtl_tx_buffer_desc *pbuffer_desc = NULL;
 	u8 temp_one = 1;
+	u8 *entry;
 
 	memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc));
 	ring = &rtlpci->tx_ring[BEACON_QUEUE];
 	pskb = __skb_dequeue(&ring->queue);
-	if (pskb)
+	if (rtlpriv->use_new_trx_flow)
+		entry = (u8 *)(&ring->buffer_desc[ring->idx]);
+	else
+		entry = (u8 *)(&ring->desc[ring->idx]);
+	if (pskb) {
+		pci_unmap_single(rtlpci->pdev,
+				 rtlpriv->cfg->ops->get_desc(
+				 (u8 *)entry, true, HW_DESC_TXBUFF_ADDR),
+				 pskb->len, PCI_DMA_TODEVICE);
 		kfree_skb(pskb);
+	}
 
 	/*NB: the beacon data buffer must be 32-bit aligned. */
 	pskb = ieee80211_beacon_get(hw, mac->vif);
@@ -1307,7 +1336,7 @@
 		rtlpci->rx_ring[rxring_idx].idx = 0;
 		for (i = 0; i < rtlpci->rxringcount; i++) {
 			entry = &rtlpci->rx_ring[rxring_idx].buffer_desc[i];
-			if (!_rtl_pci_init_one_rxdesc(hw, (u8 *)entry,
+			if (!_rtl_pci_init_one_rxdesc(hw, NULL, (u8 *)entry,
 						      rxring_idx, i))
 				return -ENOMEM;
 		}
@@ -1332,7 +1361,7 @@
 
 		for (i = 0; i < rtlpci->rxringcount; i++) {
 			entry = &rtlpci->rx_ring[rxring_idx].desc[i];
-			if (!_rtl_pci_init_one_rxdesc(hw, (u8 *)entry,
+			if (!_rtl_pci_init_one_rxdesc(hw, NULL, (u8 *)entry,
 						      rxring_idx, i))
 				return -ENOMEM;
 		}
@@ -1672,6 +1701,15 @@
 		}
 	}
 
+	if (rtlpriv->cfg->ops->get_available_desc &&
+	    rtlpriv->cfg->ops->get_available_desc(hw, hw_queue) == 0) {
+			RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+				 "get_available_desc fail\n");
+			spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock,
+					       flags);
+			return skb->len;
+	}
+
 	if (ieee80211_is_data_qos(fc)) {
 		tid = rtl_get_tid(skb);
 		if (sta) {
diff --git a/drivers/net/wireless/rtlwifi/pci.h b/drivers/net/wireless/rtlwifi/pci.h
index 5e83230..d4567d1 100644
--- a/drivers/net/wireless/rtlwifi/pci.h
+++ b/drivers/net/wireless/rtlwifi/pci.h
@@ -325,4 +325,11 @@
 	writel(val, (u8 __iomem *) rtlpriv->io.pci_mem_start + addr);
 }
 
+static inline u16 calc_fifo_space(u16 rp, u16 wp)
+{
+	if (rp <= wp)
+		return RTL_PCI_MAX_RX_COUNT - 1 + rp - wp;
+	return rp - wp - 1;
+}
+
 #endif
diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c
index 7863bd2..74c14ce 100644
--- a/drivers/net/wireless/rtlwifi/rc.c
+++ b/drivers/net/wireless/rtlwifi/rc.c
@@ -56,7 +56,8 @@
 		wireless_mode = sta_entry->wireless_mode;
 	}
 
-	if (rtl_is_special_data(rtlpriv->mac80211.hw, skb, true) || not_data) {
+	if (rtl_is_special_data(rtlpriv->mac80211.hw, skb, true, false) ||
+	    not_data) {
 		return 0;
 	} else {
 		if (rtlhal->current_bandtype == BAND_ON_2_4G) {
@@ -201,7 +202,7 @@
 	if (!priv_sta || !ieee80211_is_data(fc))
 		return;
 
-	if (rtl_is_special_data(mac->hw, skb, true))
+	if (rtl_is_special_data(mac->hw, skb, true, true))
 		return;
 
 	if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) ||
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/def.h b/drivers/net/wireless/rtlwifi/rtl8188ee/def.h
index d9ea9d0..0532b98 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/def.h
@@ -26,53 +26,12 @@
 #ifndef __RTL92C_DEF_H__
 #define __RTL92C_DEF_H__
 
-#define HAL_RETRY_LIMIT_INFRA				48
-#define HAL_RETRY_LIMIT_AP_ADHOC			7
-
-#define RESET_DELAY_8185				20
-
-#define RT_IBSS_INT_MASKS	(IMR_BCNINT | IMR_TBDOK | IMR_TBDER)
-#define RT_AC_INT_MASKS		(IMR_VIDOK | IMR_VODOK | IMR_BEDOK|IMR_BKDOK)
-
-#define NUM_OF_FIRMWARE_QUEUE				10
-#define NUM_OF_PAGES_IN_FW				0x100
-#define NUM_OF_PAGE_IN_FW_QUEUE_BK			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_BE			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_VI			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_VO			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA			0x0
-#define NUM_OF_PAGE_IN_FW_QUEUE_CMD			0x0
-#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT			0x02
-#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH			0x02
-#define NUM_OF_PAGE_IN_FW_QUEUE_BCN			0x2
-#define NUM_OF_PAGE_IN_FW_QUEUE_PUB			0xA1
-
-#define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM			0x026
-#define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM			0x048
-#define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM			0x048
-#define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM			0x026
-#define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM			0x00
-
-#define MAX_LINES_HWCONFIG_TXT				1000
-#define MAX_BYTES_LINE_HWCONFIG_TXT			256
-
-#define SW_THREE_WIRE					0
-#define HW_THREE_WIRE					2
-
-#define BT_DEMO_BOARD					0
-#define BT_QA_BOARD					1
-#define BT_FPGA						2
-
 #define HAL_PRIME_CHNL_OFFSET_DONT_CARE			0
 #define HAL_PRIME_CHNL_OFFSET_LOWER			1
 #define HAL_PRIME_CHNL_OFFSET_UPPER			2
 
-#define MAX_H2C_QUEUE_NUM				10
-
 #define RX_MPDU_QUEUE					0
 #define RX_CMD_QUEUE					1
-#define RX_MAX_QUEUE					2
-#define AC2QUEUEID(_AC)					(_AC)
 
 #define	C2H_RX_CMD_HDR_LEN				8
 #define	GET_C2H_CMD_CMD_LEN(__prxhdr)			\
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c
index 2aa34d9..d930c1f 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c
@@ -26,6 +26,7 @@
 #include "../wifi.h"
 #include "../base.h"
 #include "../pci.h"
+#include "../core.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -341,38 +342,6 @@
 	}
 }
 
-static void rtl88e_dm_diginit(struct ieee80211_hw *hw)
-{
-	struct rtl_priv *rtlpriv = rtl_priv(hw);
-	struct dig_t *dm_dig = &rtlpriv->dm_digtable;
-
-	dm_dig->dig_enable_flag = true;
-	dm_dig->cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f);
-	dm_dig->pre_igvalue = 0;
-	dm_dig->cur_sta_cstate = DIG_STA_DISCONNECT;
-	dm_dig->presta_cstate = DIG_STA_DISCONNECT;
-	dm_dig->curmultista_cstate = DIG_MULTISTA_DISCONNECT;
-	dm_dig->rssi_lowthresh = DM_DIG_THRESH_LOW;
-	dm_dig->rssi_highthresh = DM_DIG_THRESH_HIGH;
-	dm_dig->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
-	dm_dig->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
-	dm_dig->rx_gain_max = DM_DIG_MAX;
-	dm_dig->rx_gain_min = DM_DIG_MIN;
-	dm_dig->back_val = DM_DIG_BACKOFF_DEFAULT;
-	dm_dig->back_range_max = DM_DIG_BACKOFF_MAX;
-	dm_dig->back_range_min = DM_DIG_BACKOFF_MIN;
-	dm_dig->pre_cck_cca_thres = 0xff;
-	dm_dig->cur_cck_cca_thres = 0x83;
-	dm_dig->forbidden_igi = DM_DIG_MIN;
-	dm_dig->large_fa_hit = 0;
-	dm_dig->recover_cnt = 0;
-	dm_dig->dig_min_0 = 0x25;
-	dm_dig->dig_min_1 = 0x25;
-	dm_dig->media_connect_0 = false;
-	dm_dig->media_connect_1 = false;
-	rtlpriv->dm.dm_initialgain_enable = true;
-}
-
 static u8 rtl88e_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -1796,9 +1765,10 @@
 void rtl88e_dm_init(struct ieee80211_hw *hw)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
+	u32 cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f);
 
 	rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER;
-	rtl88e_dm_diginit(hw);
+	rtl_dm_diginit(hw, cur_igvalue);
 	rtl88e_dm_init_dynamic_txpower(hw);
 	rtl88e_dm_init_edca_turbo(hw);
 	rtl88e_dm_init_rate_adaptive_mask(hw);
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h
index 64f1f3e..071ccee 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h
@@ -186,28 +186,12 @@
 #define BW_AUTO_SWITCH_HIGH_LOW				25
 #define BW_AUTO_SWITCH_LOW_HIGH				30
 
-#define DM_DIG_THRESH_HIGH				40
-#define DM_DIG_THRESH_LOW				35
-
-#define DM_FALSEALARM_THRESH_LOW			400
-#define DM_FALSEALARM_THRESH_HIGH			1000
-
-#define DM_DIG_MAX					0x3e
-#define DM_DIG_MIN					0x1e
-
-#define DM_DIG_MAX_AP					0x32
-#define DM_DIG_MIN_AP					0x20
-
 #define DM_DIG_FA_UPPER					0x3e
 #define DM_DIG_FA_LOWER					0x1e
 #define DM_DIG_FA_TH0					0x200
 #define DM_DIG_FA_TH1					0x300
 #define DM_DIG_FA_TH2					0x400
 
-#define DM_DIG_BACKOFF_MAX				12
-#define DM_DIG_BACKOFF_MIN				-4
-#define DM_DIG_BACKOFF_DEFAULT				10
-
 #define RXPATHSELECTION_SS_TH_W				30
 #define RXPATHSELECTION_DIFF_TH				18
 
@@ -262,14 +246,6 @@
 	DIG_OP_TYPE_MAX
 };
 
-enum tag_cck_packet_detection_threshold_type_definition {
-	CCK_PD_STAGE_LOWRSSI = 0,
-	CCK_PD_STAGE_HIGHRSSI = 1,
-	CCK_FA_STAGE_LOW = 2,
-	CCK_FA_STAGE_HIGH = 3,
-	CCK_PD_STAGE_MAX = 4,
-};
-
 enum dm_1r_cca_e {
 	CCA_1R = 0,
 	CCA_2R = 1,
@@ -288,23 +264,6 @@
 	ANS_ANTENNA_MAX = 3,
 };
 
-enum dm_dig_ext_port_alg_e {
-	DIG_EXT_PORT_STAGE_0 = 0,
-	DIG_EXT_PORT_STAGE_1 = 1,
-	DIG_EXT_PORT_STAGE_2 = 2,
-	DIG_EXT_PORT_STAGE_3 = 3,
-	DIG_EXT_PORT_STAGE_MAX = 4,
-};
-
-enum dm_dig_connect_e {
-	DIG_STA_DISCONNECT = 0,
-	DIG_STA_CONNECT = 1,
-	DIG_STA_BEFORE_CONNECT = 2,
-	DIG_MULTISTA_DISCONNECT = 3,
-	DIG_MULTISTA_CONNECT = 4,
-	DIG_CONNECT_MAX
-};
-
 enum pwr_track_control_method {
 	BBSWING,
 	TXAGC
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
index f2b9713..86ce5b1 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
@@ -30,6 +30,7 @@
 #include "../cam.h"
 #include "../ps.h"
 #include "../pci.h"
+#include "../pwrseqcmd.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -566,7 +567,7 @@
 				acm_ctrl &= (~ACMHW_VIQEN);
 				break;
 			case AC3_VO:
-				acm_ctrl &= (~ACMHW_BEQEN);
+				acm_ctrl &= (~ACMHW_VOQEN);
 				break;
 			default:
 				RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -885,7 +886,7 @@
 
 	rtl_write_word(rtlpriv, REG_CR, 0x2ff);
 	rtl_write_byte(rtlpriv, REG_CR+1, 0x06);
-	rtl_write_byte(rtlpriv, REG_CR+2, 0x00);
+	rtl_write_byte(rtlpriv, MSR, 0x00);
 
 	if (!rtlhal->mac_func_enable) {
 		if (_rtl88ee_llt_table_init(hw) == false) {
@@ -1277,7 +1278,7 @@
 			 mode);
 	}
 
-	rtl_write_byte(rtlpriv, (MSR), bt_msr | mode);
+	rtl_write_byte(rtlpriv, MSR, bt_msr | mode);
 	rtlpriv->cfg->ops->led_control(hw, ledaction);
 	if (mode == MSR_AP)
 		rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c
index 3f6c59c..a2bb02c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c
@@ -452,9 +452,10 @@
 				READ_NEXT_PAIR(v1, v2, i);
 				while (v2 != 0xDEAD &&
 				       v2 != 0xCDEF &&
-				       v2 != 0xCDCD && i < arraylen - 2)
+				       v2 != 0xCDCD && i < arraylen - 2) {
 					_rtl8188e_config_bb_reg(hw, v1, v2);
 					READ_NEXT_PAIR(v1, v2, i);
+				}
 
 				while (v2 != 0xDEAD && i < arraylen - 2)
 					READ_NEXT_PAIR(v1, v2, i);
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/rf.h b/drivers/net/wireless/rtlwifi/rtl8188ee/rf.h
index 5c1472d..0eca030 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/rf.h
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/rf.h
@@ -27,7 +27,6 @@
 #define __RTL92C_RF_H__
 
 #define RF6052_MAX_TX_PWR		0x3F
-#define RF6052_MAX_REG			0x3F
 
 void rtl88e_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
 				     u8 bandwidth);
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c
index df549c9..791efbe 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c
@@ -47,164 +47,6 @@
 	return skb->priority;
 }
 
-/* mac80211's rate_idx is like this:
- *
- * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ
- *
- * B/G rate:
- * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11,
- *
- * N rate:
- * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15
- *
- * 5G band:rx_status->band == IEEE80211_BAND_5GHZ
- * A rate:
- * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7,
- *
- * N rate:
- * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15
- */
-static int _rtl88ee_rate_mapping(struct ieee80211_hw *hw,
-				 bool isht, u8 desc_rate)
-{
-	int rate_idx;
-
-	if (!isht) {
-		if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) {
-			switch (desc_rate) {
-			case DESC92C_RATE1M:
-				rate_idx = 0;
-				break;
-			case DESC92C_RATE2M:
-				rate_idx = 1;
-				break;
-			case DESC92C_RATE5_5M:
-				rate_idx = 2;
-				break;
-			case DESC92C_RATE11M:
-				rate_idx = 3;
-				break;
-			case DESC92C_RATE6M:
-				rate_idx = 4;
-				break;
-			case DESC92C_RATE9M:
-				rate_idx = 5;
-				break;
-			case DESC92C_RATE12M:
-				rate_idx = 6;
-				break;
-			case DESC92C_RATE18M:
-				rate_idx = 7;
-				break;
-			case DESC92C_RATE24M:
-				rate_idx = 8;
-				break;
-			case DESC92C_RATE36M:
-				rate_idx = 9;
-				break;
-			case DESC92C_RATE48M:
-				rate_idx = 10;
-				break;
-			case DESC92C_RATE54M:
-				rate_idx = 11;
-				break;
-			default:
-				rate_idx = 0;
-				break;
-			}
-		} else {
-			switch (desc_rate) {
-			case DESC92C_RATE6M:
-				rate_idx = 0;
-				break;
-			case DESC92C_RATE9M:
-				rate_idx = 1;
-				break;
-			case DESC92C_RATE12M:
-				rate_idx = 2;
-				break;
-			case DESC92C_RATE18M:
-				rate_idx = 3;
-				break;
-			case DESC92C_RATE24M:
-				rate_idx = 4;
-				break;
-			case DESC92C_RATE36M:
-				rate_idx = 5;
-				break;
-			case DESC92C_RATE48M:
-				rate_idx = 6;
-				break;
-			case DESC92C_RATE54M:
-				rate_idx = 7;
-				break;
-			default:
-				rate_idx = 0;
-				break;
-			}
-		}
-	} else {
-		switch (desc_rate) {
-		case DESC92C_RATEMCS0:
-			rate_idx = 0;
-			break;
-		case DESC92C_RATEMCS1:
-			rate_idx = 1;
-			break;
-		case DESC92C_RATEMCS2:
-			rate_idx = 2;
-			break;
-		case DESC92C_RATEMCS3:
-			rate_idx = 3;
-			break;
-		case DESC92C_RATEMCS4:
-			rate_idx = 4;
-			break;
-		case DESC92C_RATEMCS5:
-			rate_idx = 5;
-			break;
-		case DESC92C_RATEMCS6:
-			rate_idx = 6;
-			break;
-		case DESC92C_RATEMCS7:
-			rate_idx = 7;
-			break;
-		case DESC92C_RATEMCS8:
-			rate_idx = 8;
-			break;
-		case DESC92C_RATEMCS9:
-			rate_idx = 9;
-			break;
-		case DESC92C_RATEMCS10:
-			rate_idx = 10;
-			break;
-		case DESC92C_RATEMCS11:
-			rate_idx = 11;
-			break;
-		case DESC92C_RATEMCS12:
-			rate_idx = 12;
-			break;
-		case DESC92C_RATEMCS13:
-			rate_idx = 13;
-			break;
-		case DESC92C_RATEMCS14:
-			rate_idx = 14;
-			break;
-		case DESC92C_RATEMCS15:
-			rate_idx = 15;
-			break;
-		default:
-			rate_idx = 0;
-			break;
-		}
-	}
-	return rate_idx;
-}
-
 static void _rtl88ee_query_rxphystatus(struct ieee80211_hw *hw,
 			struct rtl_stats *pstatus, u8 *pdesc,
 			struct rx_fwinfo_88e *p_drvinfo,
@@ -630,8 +472,8 @@
 	 * are use (RX_FLAG_HT)
 	 * Notice: this is diff with windows define
 	 */
-	rx_status->rate_idx = _rtl88ee_rate_mapping(hw,
-				status->is_ht, status->rate);
+	rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht,
+						   false, status->rate);
 
 	rx_status->mactime = status->timestamp_low;
 	if (phystatus == true) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
index f6cb5ae..f5ee67c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
@@ -32,6 +32,7 @@
 #include "phy_common.h"
 #include "../pci.h"
 #include "../base.h"
+#include "../core.h"
 
 #define BT_RSSI_STATE_NORMAL_POWER	BIT_OFFSET_LEN_MASK_32(0, 1)
 #define BT_RSSI_STATE_AMDPU_OFF		BIT_OFFSET_LEN_MASK_32(1, 1)
@@ -194,36 +195,6 @@
 }
 EXPORT_SYMBOL_GPL(dm_savepowerindex);
 
-static void rtl92c_dm_diginit(struct ieee80211_hw *hw)
-{
-	struct rtl_priv *rtlpriv = rtl_priv(hw);
-	struct dig_t *dm_digtable = &rtlpriv->dm_digtable;
-
-	dm_digtable->dig_enable_flag = true;
-	dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX;
-	dm_digtable->cur_igvalue = 0x20;
-	dm_digtable->pre_igvalue = 0x0;
-	dm_digtable->cursta_cstate = DIG_STA_DISCONNECT;
-	dm_digtable->presta_cstate = DIG_STA_DISCONNECT;
-	dm_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT;
-	dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW;
-	dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH;
-	dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
-	dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
-	dm_digtable->rx_gain_max = DM_DIG_MAX;
-	dm_digtable->rx_gain_min = DM_DIG_MIN;
-	dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT;
-	dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX;
-	dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN;
-	dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX;
-	dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_LowRssi;
-
-	dm_digtable->forbidden_igi = DM_DIG_MIN;
-	dm_digtable->large_fa_hit = 0;
-	dm_digtable->recover_cnt = 0;
-	dm_digtable->dig_dynamic_min  = 0x25;
-}
-
 static u8 rtl92c_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -507,27 +478,27 @@
 		if (dm_digtable->rssi_val_min > 100)
 			dm_digtable->rssi_val_min = 100;
 
-		if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LowRssi) {
+		if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LOWRSSI) {
 			if (dm_digtable->rssi_val_min <= 25)
 				dm_digtable->cur_cck_pd_state =
-				    CCK_PD_STAGE_LowRssi;
+				    CCK_PD_STAGE_LOWRSSI;
 			else
 				dm_digtable->cur_cck_pd_state =
-				    CCK_PD_STAGE_HighRssi;
+				    CCK_PD_STAGE_HIGHRSSI;
 		} else {
 			if (dm_digtable->rssi_val_min <= 20)
 				dm_digtable->cur_cck_pd_state =
-				    CCK_PD_STAGE_LowRssi;
+				    CCK_PD_STAGE_LOWRSSI;
 			else
 				dm_digtable->cur_cck_pd_state =
-				    CCK_PD_STAGE_HighRssi;
+				    CCK_PD_STAGE_HIGHRSSI;
 		}
 	} else {
 		dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX;
 	}
 
 	if (dm_digtable->pre_cck_pd_state != dm_digtable->cur_cck_pd_state) {
-		if ((dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LowRssi) ||
+		if ((dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI) ||
 		    (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_MAX))
 			rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0x83);
 		else
@@ -1374,7 +1345,7 @@
 	rtlpriv->dm.undec_sm_pwdb = -1;
 	rtlpriv->dm.undec_sm_cck = -1;
 	rtlpriv->dm.dm_initialgain_enable = true;
-	rtl92c_dm_diginit(hw);
+	rtl_dm_diginit(hw, 0x20);
 
 	rtlpriv->dm.dm_flag |= HAL_DM_HIPWR_DISABLE;
 	rtl92c_dm_init_dynamic_txpower(hw);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h
index 4f232a0..4422e31 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h
@@ -47,25 +47,12 @@
 #define BW_AUTO_SWITCH_HIGH_LOW			25
 #define BW_AUTO_SWITCH_LOW_HIGH			30
 
-#define DM_DIG_THRESH_HIGH			40
-#define DM_DIG_THRESH_LOW			35
-
-#define DM_FALSEALARM_THRESH_LOW		400
-#define DM_FALSEALARM_THRESH_HIGH		1000
-
-#define DM_DIG_MAX				0x3e
-#define DM_DIG_MIN				0x1e
-
 #define DM_DIG_FA_UPPER				0x32
 #define DM_DIG_FA_LOWER				0x20
 #define DM_DIG_FA_TH0				0x20
 #define DM_DIG_FA_TH1				0x100
 #define DM_DIG_FA_TH2				0x200
 
-#define DM_DIG_BACKOFF_MAX			12
-#define DM_DIG_BACKOFF_MIN			-4
-#define DM_DIG_BACKOFF_DEFAULT			10
-
 #define RXPATHSELECTION_SS_TH_lOW		30
 #define RXPATHSELECTION_DIFF_TH			18
 
@@ -123,14 +110,6 @@
 	DIG_OP_TYPE_MAX
 };
 
-enum tag_cck_packet_detection_threshold_type_definition {
-	CCK_PD_STAGE_LowRssi = 0,
-	CCK_PD_STAGE_HighRssi = 1,
-	CCK_FA_STAGE_Low = 2,
-	CCK_FA_STAGE_High = 3,
-	CCK_PD_STAGE_MAX = 4,
-};
-
 enum dm_1r_cca_e {
 	CCA_1R = 0,
 	CCA_2R = 1,
@@ -149,23 +128,6 @@
 	ANS_ANTENNA_MAX = 3,
 };
 
-enum dm_dig_ext_port_alg_e {
-	DIG_EXT_PORT_STAGE_0 = 0,
-	DIG_EXT_PORT_STAGE_1 = 1,
-	DIG_EXT_PORT_STAGE_2 = 2,
-	DIG_EXT_PORT_STAGE_3 = 3,
-	DIG_EXT_PORT_STAGE_MAX = 4,
-};
-
-enum dm_dig_connect_e {
-	DIG_STA_DISCONNECT = 0,
-	DIG_STA_CONNECT = 1,
-	DIG_STA_BEFORE_CONNECT = 2,
-	DIG_MULTISTA_DISCONNECT = 3,
-	DIG_MULTISTA_CONNECT = 4,
-	DIG_CONNECT_MAX
-};
-
 void rtl92c_dm_init(struct ieee80211_hw *hw);
 void rtl92c_dm_watchdog(struct ieee80211_hw *hw);
 void rtl92c_dm_write_dig(struct ieee80211_hw *hw);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h
index b64ae45..e9f4281 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h
@@ -37,6 +37,7 @@
 #define FW_8192C_POLLING_DELAY			5
 #define FW_8192C_POLLING_TIMEOUT_COUNT		100
 #define NORMAL_CHIP				BIT(4)
+#define H2C_92C_KEEP_ALIVE_CTRL			48
 
 #define IS_FW_HEADER_EXIST(_pfwhdr)	\
 	((le16_to_cpu(_pfwhdr->signature)&0xFFF0) == 0x92C0 ||\
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
index 9b660df..690a7a1 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
@@ -30,59 +30,18 @@
 #ifndef __RTL92C_DEF_H__
 #define __RTL92C_DEF_H__
 
-#define HAL_RETRY_LIMIT_INFRA				48
-#define HAL_RETRY_LIMIT_AP_ADHOC			7
-
 #define	PHY_RSSI_SLID_WIN_MAX				100
 #define	PHY_LINKQUALITY_SLID_WIN_MAX			20
 #define	PHY_BEACON_RSSI_SLID_WIN_MAX			10
 
-#define RESET_DELAY_8185				20
-
-#define RT_IBSS_INT_MASKS	(IMR_BCNINT | IMR_TBDOK | IMR_TBDER)
-#define RT_AC_INT_MASKS		(IMR_VIDOK | IMR_VODOK | IMR_BEDOK|IMR_BKDOK)
-
-#define NUM_OF_FIRMWARE_QUEUE				10
-#define NUM_OF_PAGES_IN_FW				0x100
-#define NUM_OF_PAGE_IN_FW_QUEUE_BK			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_BE			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_VI			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_VO			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA			0x0
-#define NUM_OF_PAGE_IN_FW_QUEUE_CMD			0x0
-#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT			0x02
-#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH			0x02
-#define NUM_OF_PAGE_IN_FW_QUEUE_BCN			0x2
-#define NUM_OF_PAGE_IN_FW_QUEUE_PUB			0xA1
-
-#define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM			0x026
-#define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM			0x048
-#define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM			0x048
-#define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM			0x026
-#define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM			0x00
-
-#define MAX_LINES_HWCONFIG_TXT				1000
-#define MAX_BYTES_LINE_HWCONFIG_TXT			256
-
-#define SW_THREE_WIRE					0
-#define HW_THREE_WIRE					2
-
-#define BT_DEMO_BOARD					0
-#define BT_QA_BOARD					1
-#define BT_FPGA						2
-
 #define RX_SMOOTH_FACTOR				20
 
 #define HAL_PRIME_CHNL_OFFSET_DONT_CARE			0
 #define HAL_PRIME_CHNL_OFFSET_LOWER			1
 #define HAL_PRIME_CHNL_OFFSET_UPPER			2
 
-#define MAX_H2C_QUEUE_NUM				10
-
 #define RX_MPDU_QUEUE					0
 #define RX_CMD_QUEUE					1
-#define RX_MAX_QUEUE					2
-#define AC2QUEUEID(_AC)					(_AC)
 
 #define	C2H_RX_CMD_HDR_LEN				8
 #define	GET_C2H_CMD_CMD_LEN(__prxhdr)		\
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c
index 74f9c08..09898cf 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c
@@ -30,6 +30,7 @@
 #include "../wifi.h"
 #include "../base.h"
 #include "../pci.h"
+#include "../core.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h
index 9c5311c..38ba707 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h
@@ -42,25 +42,12 @@
 #define BW_AUTO_SWITCH_HIGH_LOW			25
 #define BW_AUTO_SWITCH_LOW_HIGH			30
 
-#define DM_DIG_THRESH_HIGH			40
-#define DM_DIG_THRESH_LOW			35
-
-#define DM_FALSEALARM_THRESH_LOW		400
-#define DM_FALSEALARM_THRESH_HIGH		1000
-
-#define DM_DIG_MAX				0x3e
-#define DM_DIG_MIN				0x1e
-
 #define DM_DIG_FA_UPPER				0x32
 #define DM_DIG_FA_LOWER				0x20
 #define DM_DIG_FA_TH0				0x20
 #define DM_DIG_FA_TH1				0x100
 #define DM_DIG_FA_TH2				0x200
 
-#define DM_DIG_BACKOFF_MAX			12
-#define DM_DIG_BACKOFF_MIN			-4
-#define DM_DIG_BACKOFF_DEFAULT			10
-
 #define RXPATHSELECTION_SS_TH_lOW		30
 #define RXPATHSELECTION_DIFF_TH			18
 
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
index 5c646d5..04eb5c3 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
@@ -363,7 +363,7 @@
 					acm_ctrl &= (~AcmHw_ViqEn);
 					break;
 				case AC3_VO:
-					acm_ctrl &= (~AcmHw_BeqEn);
+					acm_ctrl &= (~AcmHw_VoqEn);
 					break;
 				default:
 					RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -544,8 +544,13 @@
 						(u8 *)(&fw_current_inps));
 			}
 		break; }
-	case HW_VAR_KEEP_ALIVE:
-		break;
+	case HW_VAR_KEEP_ALIVE: {
+		u8 array[2];
+
+		array[0] = 0xff;
+		array[1] = *((u8 *)val);
+		rtl92c_fill_h2c_cmd(hw, H2C_92C_KEEP_ALIVE_CTRL, 2, array);
+		break; }
 	default:
 		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
 			 "switch case %d not processed\n", variable);
@@ -1156,47 +1161,35 @@
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
 	u8 bt_msr = rtl_read_byte(rtlpriv, MSR);
 	enum led_ctl_mode ledaction = LED_CTL_NO_LINK;
-	bt_msr &= 0xfc;
+	u8 mode = MSR_NOLINK;
 
-	if (type == NL80211_IFTYPE_UNSPECIFIED ||
-	    type == NL80211_IFTYPE_STATION) {
-		_rtl92ce_stop_tx_beacon(hw);
-		_rtl92ce_enable_bcn_sub_func(hw);
-	} else if (type == NL80211_IFTYPE_ADHOC || type == NL80211_IFTYPE_AP ||
-		   type == NL80211_IFTYPE_MESH_POINT) {
-		_rtl92ce_resume_tx_beacon(hw);
-		_rtl92ce_disable_bcn_sub_func(hw);
-	} else {
-		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
-			 "Set HW_VAR_MEDIA_STATUS: No such media status(%x)\n",
-			 type);
-	}
+	bt_msr &= 0xfc;
 
 	switch (type) {
 	case NL80211_IFTYPE_UNSPECIFIED:
-		bt_msr |= MSR_NOLINK;
-		ledaction = LED_CTL_LINK;
+		mode = MSR_NOLINK;
 		RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
 			 "Set Network type to NO LINK!\n");
 		break;
 	case NL80211_IFTYPE_ADHOC:
-		bt_msr |= MSR_ADHOC;
+		mode = MSR_ADHOC;
 		RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
 			 "Set Network type to Ad Hoc!\n");
 		break;
 	case NL80211_IFTYPE_STATION:
-		bt_msr |= MSR_INFRA;
+		mode = MSR_INFRA;
 		ledaction = LED_CTL_LINK;
 		RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
 			 "Set Network type to STA!\n");
 		break;
 	case NL80211_IFTYPE_AP:
-		bt_msr |= MSR_AP;
+		mode = MSR_AP;
+		ledaction = LED_CTL_LINK;
 		RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
 			 "Set Network type to AP!\n");
 		break;
 	case NL80211_IFTYPE_MESH_POINT:
-		bt_msr |= MSR_ADHOC;
+		mode = MSR_ADHOC;
 		RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
 			 "Set Network type to Mesh Point!\n");
 		break;
@@ -1207,9 +1200,32 @@
 
 	}
 
-	rtl_write_byte(rtlpriv, (MSR), bt_msr);
+	/* MSR_INFRA == Link in infrastructure network;
+	 * MSR_ADHOC == Link in ad hoc network;
+	 * Therefore, check link state is necessary.
+	 *
+	 * MSR_AP == AP mode; link state does not matter here.
+	 */
+	if (mode != MSR_AP &&
+	    rtlpriv->mac80211.link_state < MAC80211_LINKED) {
+		mode = MSR_NOLINK;
+		ledaction = LED_CTL_NO_LINK;
+	}
+	if (mode == MSR_NOLINK || mode == MSR_INFRA) {
+		_rtl92ce_stop_tx_beacon(hw);
+		_rtl92ce_enable_bcn_sub_func(hw);
+	} else if (mode == MSR_ADHOC || mode == MSR_AP) {
+		_rtl92ce_resume_tx_beacon(hw);
+		_rtl92ce_disable_bcn_sub_func(hw);
+	} else {
+		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+			 "Set HW_VAR_MEDIA_STATUS: No such media status(%x).\n",
+			 mode);
+	}
+	rtl_write_byte(rtlpriv, MSR, bt_msr | mode);
+
 	rtlpriv->cfg->ops->led_control(hw, ledaction);
-	if ((bt_msr & MSR_MASK) == MSR_AP)
+	if (mode == MSR_AP)
 		rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
 	else
 		rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
@@ -1833,7 +1849,6 @@
 	u32 ratr_value;
 	u8 ratr_index = 0;
 	u8 nmode = mac->ht_enable;
-	u8 mimo_ps = IEEE80211_SMPS_OFF;
 	u16 shortgi_rate;
 	u32 tmp_ratr_value;
 	u8 curtxbw_40mhz = mac->bw_40;
@@ -1842,6 +1857,7 @@
 	u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ?
 			       1 : 0;
 	enum wireless_mode wirelessmode = mac->mode;
+	u32 ratr_mask;
 
 	if (rtlhal->current_bandtype == BAND_ON_5G)
 		ratr_value = sta->supp_rates[1] << 4;
@@ -1865,19 +1881,13 @@
 	case WIRELESS_MODE_N_24G:
 	case WIRELESS_MODE_N_5G:
 		nmode = 1;
-		if (mimo_ps == IEEE80211_SMPS_STATIC) {
-			ratr_value &= 0x0007F005;
-		} else {
-			u32 ratr_mask;
+		if (get_rf_type(rtlphy) == RF_1T2R ||
+		    get_rf_type(rtlphy) == RF_1T1R)
+			ratr_mask = 0x000ff005;
+		else
+			ratr_mask = 0x0f0ff005;
 
-			if (get_rf_type(rtlphy) == RF_1T2R ||
-			    get_rf_type(rtlphy) == RF_1T1R)
-				ratr_mask = 0x000ff005;
-			else
-				ratr_mask = 0x0f0ff005;
-
-			ratr_value &= ratr_mask;
-		}
+		ratr_value &= ratr_mask;
 		break;
 	default:
 		if (rtlphy->rf_type == RF_1T2R)
@@ -1930,17 +1940,16 @@
 	struct rtl_sta_info *sta_entry = NULL;
 	u32 ratr_bitmap;
 	u8 ratr_index;
-	u8 curtxbw_40mhz = (sta->bandwidth >= IEEE80211_STA_RX_BW_40) ? 1 : 0;
-	u8 curshortgi_40mhz = curtxbw_40mhz &&
-			      (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ?
-				1 : 0;
+	u8 curtxbw_40mhz = (sta->ht_cap.cap &
+			    IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1 : 0;
+	u8 curshortgi_40mhz = (sta->ht_cap.cap &
+			       IEEE80211_HT_CAP_SGI_40) ?  1 : 0;
 	u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ?
 				1 : 0;
 	enum wireless_mode wirelessmode = 0;
 	bool shortgi = false;
 	u8 rate_mask[5];
 	u8 macid = 0;
-	u8 mimo_ps = IEEE80211_SMPS_OFF;
 
 	sta_entry = (struct rtl_sta_info *) sta->drv_priv;
 	wirelessmode = sta_entry->wireless_mode;
@@ -1985,47 +1994,38 @@
 	case WIRELESS_MODE_N_5G:
 		ratr_index = RATR_INX_WIRELESS_NGB;
 
-		if (mimo_ps == IEEE80211_SMPS_STATIC) {
-			if (rssi_level == 1)
-				ratr_bitmap &= 0x00070000;
-			else if (rssi_level == 2)
-				ratr_bitmap &= 0x0007f000;
-			else
-				ratr_bitmap &= 0x0007f005;
-		} else {
-			if (rtlphy->rf_type == RF_1T2R ||
-			    rtlphy->rf_type == RF_1T1R) {
-				if (curtxbw_40mhz) {
-					if (rssi_level == 1)
-						ratr_bitmap &= 0x000f0000;
-					else if (rssi_level == 2)
-						ratr_bitmap &= 0x000ff000;
-					else
-						ratr_bitmap &= 0x000ff015;
-				} else {
-					if (rssi_level == 1)
-						ratr_bitmap &= 0x000f0000;
-					else if (rssi_level == 2)
-						ratr_bitmap &= 0x000ff000;
-					else
-						ratr_bitmap &= 0x000ff005;
-				}
+		if (rtlphy->rf_type == RF_1T2R ||
+		    rtlphy->rf_type == RF_1T1R) {
+			if (curtxbw_40mhz) {
+				if (rssi_level == 1)
+					ratr_bitmap &= 0x000f0000;
+				else if (rssi_level == 2)
+					ratr_bitmap &= 0x000ff000;
+				else
+					ratr_bitmap &= 0x000ff015;
 			} else {
-				if (curtxbw_40mhz) {
-					if (rssi_level == 1)
-						ratr_bitmap &= 0x0f0f0000;
-					else if (rssi_level == 2)
-						ratr_bitmap &= 0x0f0ff000;
-					else
-						ratr_bitmap &= 0x0f0ff015;
-				} else {
-					if (rssi_level == 1)
-						ratr_bitmap &= 0x0f0f0000;
-					else if (rssi_level == 2)
-						ratr_bitmap &= 0x0f0ff000;
-					else
-						ratr_bitmap &= 0x0f0ff005;
-				}
+				if (rssi_level == 1)
+					ratr_bitmap &= 0x000f0000;
+				else if (rssi_level == 2)
+					ratr_bitmap &= 0x000ff000;
+				else
+					ratr_bitmap &= 0x000ff005;
+			}
+		} else {
+			if (curtxbw_40mhz) {
+				if (rssi_level == 1)
+					ratr_bitmap &= 0x0f0f0000;
+				else if (rssi_level == 2)
+					ratr_bitmap &= 0x0f0ff000;
+				else
+					ratr_bitmap &= 0x0f0ff015;
+			} else {
+				if (rssi_level == 1)
+					ratr_bitmap &= 0x0f0f0000;
+				else if (rssi_level == 2)
+					ratr_bitmap &= 0x0f0ff000;
+				else
+					ratr_bitmap &= 0x0f0ff005;
 			}
 		}
 
@@ -2058,9 +2058,6 @@
 		 "Rate_index:%x, ratr_val:%x, %5phC\n",
 		 ratr_index, ratr_bitmap, rate_mask);
 	rtl92c_fill_h2c_cmd(hw, H2C_RA_MASK, 5, rate_mask);
-
-	if (macid != 0)
-		sta_entry->ratr_index = ratr_index;
 }
 
 void rtl92ce_update_hal_rate_tbl(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c
index bc5ca98..1ee5a6a 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c
@@ -518,11 +518,12 @@
 		}
 	case ERFSLEEP:{
 			if (ppsc->rfpwr_state == ERFOFF)
-				return false;
+				break;
 			for (queue_id = 0, i = 0;
 			     queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) {
 				ring = &pcipriv->dev.tx_ring[queue_id];
-				if (skb_queue_len(&ring->queue) == 0) {
+				if (queue_id == BEACON_QUEUE ||
+				    skb_queue_len(&ring->queue) == 0) {
 					queue_id++;
 					continue;
 				} else {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h
index d8fe68b..ebd72ca 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h
@@ -31,7 +31,6 @@
 #define __RTL92C_RF_H__
 
 #define RF6052_MAX_TX_PWR		0x3F
-#define RF6052_MAX_REG			0x3F
 #define RF6052_MAX_PATH			2
 
 void rtl92ce_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
index dd5aa08..de6cb6c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
@@ -334,21 +334,21 @@
 	.maps[RTL_IMR_ROK] = IMR_ROK,
 	.maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER),
 
-	.maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M,
-	.maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M,
-	.maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M,
-	.maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M,
-	.maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M,
-	.maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M,
-	.maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M,
-	.maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M,
-	.maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M,
-	.maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M,
-	.maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M,
-	.maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M,
+	.maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M,
+	.maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M,
+	.maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M,
+	.maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M,
+	.maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M,
+	.maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M,
+	.maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M,
+	.maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M,
+	.maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M,
+	.maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M,
+	.maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M,
+	.maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M,
 
-	.maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7,
-	.maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15,
+	.maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7,
+	.maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15,
 };
 
 static const struct pci_device_id rtl92ce_pci_ids[] = {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
index e88dcd0..84ddd4d 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
@@ -257,8 +257,8 @@
 		pstats->recvsignalpower = rx_pwr_all;
 
 		/* (3)EVM of HT rate */
-		if (pstats->is_ht && pstats->rate >= DESC92_RATEMCS8 &&
-		    pstats->rate <= DESC92_RATEMCS15)
+		if (pstats->is_ht && pstats->rate >= DESC_RATEMCS8 &&
+		    pstats->rate <= DESC_RATEMCS15)
 			max_spatial_stream = 2;
 		else
 			max_spatial_stream = 1;
@@ -400,9 +400,8 @@
 	 * are use (RX_FLAG_HT)
 	 * Notice: this is diff with windows define
 	 */
-	rx_status->rate_idx = rtlwifi_rate_mapping(hw,
-				stats->is_ht, stats->rate,
-				stats->isfirst_ampdu);
+	rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht,
+						   false, stats->rate);
 
 	rx_status->mactime = stats->timestamp_low;
 	if (phystatus) {
@@ -501,7 +500,7 @@
 		SET_TX_DESC_RTS_BW(pdesc, 0);
 		SET_TX_DESC_RTS_SC(pdesc, tcb_desc->rts_sc);
 		SET_TX_DESC_RTS_SHORT(pdesc,
-				      ((tcb_desc->rts_rate <= DESC92_RATE54M) ?
+				      ((tcb_desc->rts_rate <= DESC_RATE54M) ?
 				       (tcb_desc->rts_use_shortpreamble ? 1 : 0)
 				       : (tcb_desc->rts_use_shortgi ? 1 : 0)));
 
@@ -624,7 +623,7 @@
 	if (firstseg)
 		SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN);
 
-	SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M);
+	SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M);
 
 	SET_TX_DESC_SEQ(pdesc, 0);
 
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
index 5513217..d310d55 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
@@ -1000,6 +1000,7 @@
 	local_save_flags(flags);
 	local_irq_enable();
 
+	rtlhal->fw_ready = false;
 	rtlhal->hw_type = HARDWARE_TYPE_RTL8192CU;
 	err = _rtl92cu_init_mac(hw);
 	if (err) {
@@ -1013,6 +1014,8 @@
 		err = 1;
 		goto exit;
 	}
+
+	rtlhal->fw_ready = true;
 	rtlhal->last_hmeboxnum = 0; /* h2c */
 	_rtl92cu_phy_param_tab_init(hw);
 	rtl92cu_phy_mac_config(hw);
@@ -1361,7 +1364,7 @@
 			 "Network type %d not supported!\n", type);
 		goto error_out;
 	}
-	rtl_write_byte(rtlpriv, (MSR), bt_msr);
+	rtl_write_byte(rtlpriv, MSR, bt_msr);
 	rtlpriv->cfg->ops->led_control(hw, ledaction);
 	if ((bt_msr & MSR_MASK) == MSR_AP)
 		rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
@@ -1468,8 +1471,7 @@
 		rtl_write_word(rtlpriv, REG_BCNTCFG, 0x66FF);
 }
 
-static void _beacon_function_enable(struct ieee80211_hw *hw, bool Enable,
-				    bool Linked)
+static void _beacon_function_enable(struct ieee80211_hw *hw)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
 
@@ -1509,11 +1511,12 @@
 	/* TODO: Modify later (Find the right parameters)
 	 * NOTE: Fix test chip's bug (about contention windows's randomness) */
 	if ((mac->opmode == NL80211_IFTYPE_ADHOC) ||
+	    (mac->opmode == NL80211_IFTYPE_MESH_POINT) ||
 	    (mac->opmode == NL80211_IFTYPE_AP)) {
 		rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x50);
 		rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x50);
 	}
-	_beacon_function_enable(hw, true, true);
+	_beacon_function_enable(hw);
 }
 
 void rtl92cu_set_beacon_interval(struct ieee80211_hw *hw)
@@ -1585,6 +1588,8 @@
 	case HW_VAR_DATA_FILTER:
 		*((u16 *) (val)) = rtl_read_word(rtlpriv, REG_RXFLTMAP2);
 		break;
+	case HAL_DEF_WOWLAN:
+		break;
 	default:
 		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
 			 "switch case not processed\n");
@@ -1867,7 +1872,7 @@
 					acm_ctrl &= (~AcmHw_ViqEn);
 					break;
 				case AC3_VO:
-					acm_ctrl &= (~AcmHw_BeqEn);
+					acm_ctrl &= (~AcmHw_VoqEn);
 					break;
 				default:
 					RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
index c1e33b0..6758808 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
@@ -32,8 +32,6 @@
 
 #define H2C_RA_MASK	6
 
-#define LLT_POLLING_LLT_THRESHOLD		20
-#define LLT_POLLING_READY_TIMEOUT_COUNT		100
 #define LLT_LAST_ENTRY_OF_TX_PKT_BUFFER		255
 
 #define RX_PAGE_SIZE_REG_VALUE			PBP_128
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c
index c2d8ec6..adb8107 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c
@@ -497,7 +497,7 @@
 			 "Network type %d not supported!\n", type);
 		return -EOPNOTSUPP;
 	}
-	rtl_write_byte(rtlpriv, (REG_CR + 2), value);
+	rtl_write_byte(rtlpriv, MSR, value);
 	return 0;
 }
 
@@ -880,8 +880,8 @@
 		pstats->rxpower = rx_pwr_all;
 		pstats->recvsignalpower = rx_pwr_all;
 		if (GET_RX_DESC_RX_MCS(pdesc) &&
-		    GET_RX_DESC_RX_MCS(pdesc) >= DESC92_RATEMCS8 &&
-		    GET_RX_DESC_RX_MCS(pdesc) <= DESC92_RATEMCS15)
+		    GET_RX_DESC_RX_MCS(pdesc) >= DESC_RATEMCS8 &&
+		    GET_RX_DESC_RX_MCS(pdesc) <= DESC_RATEMCS15)
 			max_spatial_stream = 2;
 		else
 			max_spatial_stream = 1;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h
index 11b439d..6f987de 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h
@@ -31,7 +31,6 @@
 #define __RTL92CU_RF_H__
 
 #define RF6052_MAX_TX_PWR		0x3F
-#define RF6052_MAX_REG			0x3F
 #define RF6052_MAX_PATH			2
 
 void rtl92cu_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
index 97f85b7..6a88ccf 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
@@ -257,20 +257,20 @@
 	.maps[RTL_IMR_ROK] = IMR_ROK,
 	.maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER),
 
-	.maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M,
-	.maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M,
-	.maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M,
-	.maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M,
-	.maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M,
-	.maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M,
-	.maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M,
-	.maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M,
-	.maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M,
-	.maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M,
-	.maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M,
-	.maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M,
-	.maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7,
-	.maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15,
+	.maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M,
+	.maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M,
+	.maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M,
+	.maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M,
+	.maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M,
+	.maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M,
+	.maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M,
+	.maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M,
+	.maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M,
+	.maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M,
+	.maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M,
+	.maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M,
+	.maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7,
+	.maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15,
 };
 
 #define USB_VENDER_ID_REALTEK		0x0bda
@@ -321,6 +321,7 @@
 	{RTL_USB_DEVICE(0x07b8, 0x8188, rtl92cu_hal_cfg)}, /*Abocom - Abocom*/
 	{RTL_USB_DEVICE(0x07b8, 0x8189, rtl92cu_hal_cfg)}, /*Funai - Abocom*/
 	{RTL_USB_DEVICE(0x0846, 0x9041, rtl92cu_hal_cfg)}, /*NetGear WNA1000M*/
+	{RTL_USB_DEVICE(0x0b05, 0x17ba, rtl92cu_hal_cfg)}, /*ASUS-Edimax*/
 	{RTL_USB_DEVICE(0x0bda, 0x5088, rtl92cu_hal_cfg)}, /*Thinkware-CC&C*/
 	{RTL_USB_DEVICE(0x0df6, 0x0052, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/
 	{RTL_USB_DEVICE(0x0df6, 0x005c, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/
@@ -377,6 +378,7 @@
 	{RTL_USB_DEVICE(0x2001, 0x3307, rtl92cu_hal_cfg)}, /*D-Link-Cameo*/
 	{RTL_USB_DEVICE(0x2001, 0x3309, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/
 	{RTL_USB_DEVICE(0x2001, 0x330a, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/
+	{RTL_USB_DEVICE(0x2001, 0x330d, rtl92cu_hal_cfg)}, /*D-Link DWA-131 */
 	{RTL_USB_DEVICE(0x2019, 0xab2b, rtl92cu_hal_cfg)}, /*Planex -Abocom*/
 	{RTL_USB_DEVICE(0x20f4, 0x624d, rtl92cu_hal_cfg)}, /*TRENDNet*/
 	{RTL_USB_DEVICE(0x2357, 0x0100, rtl92cu_hal_cfg)}, /*TP-Link WN8200ND*/
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
index f383d5f..cbead00 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
@@ -325,6 +325,7 @@
 				   && (GET_RX_DESC_FAGGR(pdesc) == 1));
 	stats->timestamp_low = GET_RX_DESC_TSFL(pdesc);
 	stats->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc);
+	stats->is_ht = (bool)GET_RX_DESC_RX_HT(pdesc);
 	rx_status->freq = hw->conf.chandef.chan->center_freq;
 	rx_status->band = hw->conf.chandef.chan->band;
 	if (GET_RX_DESC_CRC32(pdesc))
@@ -338,10 +339,8 @@
 	rx_status->flag |= RX_FLAG_MACTIME_START;
 	if (stats->decrypted)
 		rx_status->flag |= RX_FLAG_DECRYPTED;
-	rx_status->rate_idx = rtlwifi_rate_mapping(hw,
-					(bool)GET_RX_DESC_RX_HT(pdesc),
-					(u8)GET_RX_DESC_RX_MCS(pdesc),
-					(bool)GET_RX_DESC_PAGGR(pdesc));
+	rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht,
+						   false, stats->rate);
 	rx_status->mactime = GET_RX_DESC_TSFL(pdesc);
 	if (phystatus) {
 		p_drvinfo = (struct rx_fwinfo_92c *)(skb->data +
@@ -393,6 +392,7 @@
 				   && (GET_RX_DESC_FAGGR(rxdesc) == 1));
 	stats.timestamp_low = GET_RX_DESC_TSFL(rxdesc);
 	stats.rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(rxdesc);
+	stats.is_ht = (bool)GET_RX_DESC_RX_HT(rxdesc);
 	/* TODO: is center_freq changed when doing scan? */
 	/* TODO: Shall we add protection or just skip those two step? */
 	rx_status->freq = hw->conf.chandef.chan->center_freq;
@@ -406,10 +406,8 @@
 	if (GET_RX_DESC_RX_HT(rxdesc))
 		rx_status->flag |= RX_FLAG_HT;
 	/* Data rate */
-	rx_status->rate_idx = rtlwifi_rate_mapping(hw,
-					(bool)GET_RX_DESC_RX_HT(rxdesc),
-					(u8)GET_RX_DESC_RX_MCS(rxdesc),
-					(bool)GET_RX_DESC_PAGGR(rxdesc));
+	rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats.is_ht,
+						   false, stats.rate);
 	/*  There is a phy status after this rx descriptor. */
 	if (GET_RX_DESC_PHY_STATUS(rxdesc)) {
 		p_drvinfo = (struct rx_fwinfo_92c *)(rxdesc + RTL_RX_DESC_SIZE);
@@ -545,7 +543,7 @@
 	SET_TX_DESC_RTS_BW(txdesc, 0);
 	SET_TX_DESC_RTS_SC(txdesc, tcb_desc->rts_sc);
 	SET_TX_DESC_RTS_SHORT(txdesc,
-			      ((tcb_desc->rts_rate <= DESC92_RATE54M) ?
+			      ((tcb_desc->rts_rate <= DESC_RATE54M) ?
 			       (tcb_desc->rts_use_shortpreamble ? 1 : 0)
 			       : (tcb_desc->rts_use_shortgi ? 1 : 0)));
 	if (mac->bw_40) {
@@ -644,7 +642,7 @@
 	}
 	SET_TX_DESC_USE_RATE(pDesc, 1); /* use data rate which is set by Sw */
 	SET_TX_DESC_OWN(pDesc, 1);
-	SET_TX_DESC_TX_RATE(pDesc, DESC92_RATE1M);
+	SET_TX_DESC_TX_RATE(pDesc, DESC_RATE1M);
 	_rtl_tx_desc_checksum(pDesc);
 }
 
@@ -660,7 +658,7 @@
 	memset((void *)pdesc, 0, RTL_TX_HEADER_SIZE);
 	if (firstseg)
 		SET_TX_DESC_OFFSET(pdesc, RTL_TX_HEADER_SIZE);
-	SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M);
+	SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M);
 	SET_TX_DESC_SEQ(pdesc, 0);
 	SET_TX_DESC_LINIP(pdesc, 0);
 	SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/def.h b/drivers/net/wireless/rtlwifi/rtl8192de/def.h
index 939c905..0a443ed 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/def.h
@@ -35,61 +35,22 @@
 #define	MAX_MSS_DENSITY_1T				0x0A
 
 #define RF6052_MAX_TX_PWR				0x3F
-#define RF6052_MAX_REG					0x3F
 #define RF6052_MAX_PATH					2
 
-#define HAL_RETRY_LIMIT_INFRA				48
-#define HAL_RETRY_LIMIT_AP_ADHOC			7
-
 #define	PHY_RSSI_SLID_WIN_MAX				100
 #define	PHY_LINKQUALITY_SLID_WIN_MAX			20
 #define	PHY_BEACON_RSSI_SLID_WIN_MAX			10
 
-#define RESET_DELAY_8185				20
-
-#define RT_IBSS_INT_MASKS	(IMR_BCNINT | IMR_TBDOK | IMR_TBDER)
 #define RT_AC_INT_MASKS		(IMR_VIDOK | IMR_VODOK | IMR_BEDOK|IMR_BKDOK)
 
-#define NUM_OF_FIRMWARE_QUEUE				10
-#define NUM_OF_PAGES_IN_FW				0x100
-#define NUM_OF_PAGE_IN_FW_QUEUE_BK			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_BE			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_VI			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_VO			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA			0x0
-#define NUM_OF_PAGE_IN_FW_QUEUE_CMD			0x0
-#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT			0x02
-#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH			0x02
-#define NUM_OF_PAGE_IN_FW_QUEUE_BCN			0x2
-#define NUM_OF_PAGE_IN_FW_QUEUE_PUB			0xA1
-
-#define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM			0x026
-#define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM			0x048
-#define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM			0x048
-#define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM			0x026
-#define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM			0x00
-
-#define MAX_LINES_HWCONFIG_TXT				1000
-#define MAX_BYTES_LINE_HWCONFIG_TXT			256
-
-#define SW_THREE_WIRE					0
-#define HW_THREE_WIRE					2
-
-#define BT_DEMO_BOARD					0
-#define BT_QA_BOARD					1
-#define BT_FPGA						2
-
 #define RX_SMOOTH_FACTOR				20
 
 #define HAL_PRIME_CHNL_OFFSET_DONT_CARE			0
 #define HAL_PRIME_CHNL_OFFSET_LOWER			1
 #define HAL_PRIME_CHNL_OFFSET_UPPER			2
 
-#define MAX_H2C_QUEUE_NUM				10
-
 #define RX_MPDU_QUEUE					0
 #define RX_CMD_QUEUE					1
-#define RX_MAX_QUEUE					2
 
 #define	C2H_RX_CMD_HDR_LEN				8
 #define	GET_C2H_CMD_CMD_LEN(__prxhdr)			\
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
index 304c443..a1be5a6 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
@@ -29,6 +29,7 @@
 
 #include "../wifi.h"
 #include "../base.h"
+#include "../core.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -155,34 +156,6 @@
 	{0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00}     /* 32, -16.0dB */
 };
 
-static void rtl92d_dm_diginit(struct ieee80211_hw *hw)
-{
-	struct rtl_priv *rtlpriv = rtl_priv(hw);
-	struct dig_t *de_digtable = &rtlpriv->dm_digtable;
-
-	de_digtable->dig_enable_flag = true;
-	de_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX;
-	de_digtable->cur_igvalue = 0x20;
-	de_digtable->pre_igvalue = 0x0;
-	de_digtable->cursta_cstate = DIG_STA_DISCONNECT;
-	de_digtable->presta_cstate = DIG_STA_DISCONNECT;
-	de_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT;
-	de_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW;
-	de_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH;
-	de_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
-	de_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
-	de_digtable->rx_gain_max = DM_DIG_FA_UPPER;
-	de_digtable->rx_gain_min = DM_DIG_FA_LOWER;
-	de_digtable->back_val = DM_DIG_BACKOFF_DEFAULT;
-	de_digtable->back_range_max = DM_DIG_BACKOFF_MAX;
-	de_digtable->back_range_min = DM_DIG_BACKOFF_MIN;
-	de_digtable->pre_cck_pd_state = CCK_PD_STAGE_LOWRSSI;
-	de_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX;
-	de_digtable->large_fa_hit = 0;
-	de_digtable->recover_cnt = 0;
-	de_digtable->forbidden_igi = DM_DIG_FA_LOWER;
-}
-
 static void rtl92d_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw)
 {
 	u32 ret_value;
@@ -1305,7 +1278,9 @@
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
 
 	rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER;
-	rtl92d_dm_diginit(hw);
+	rtl_dm_diginit(hw, 0x20);
+	rtlpriv->dm_digtable.rx_gain_max = DM_DIG_FA_UPPER;
+	rtlpriv->dm_digtable.rx_gain_min = DM_DIG_FA_LOWER;
 	rtl92d_dm_init_dynamic_txpower(hw);
 	rtl92d_dm_init_edca_turbo(hw);
 	rtl92d_dm_init_rate_adaptive_mask(hw);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.h b/drivers/net/wireless/rtlwifi/rtl8192de/dm.h
index 3fea0c1..f2d318c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.h
@@ -42,25 +42,12 @@
 #define BW_AUTO_SWITCH_HIGH_LOW			25
 #define BW_AUTO_SWITCH_LOW_HIGH			30
 
-#define DM_DIG_THRESH_HIGH			40
-#define DM_DIG_THRESH_LOW			35
-
-#define DM_FALSEALARM_THRESH_LOW		400
-#define DM_FALSEALARM_THRESH_HIGH		1000
-
-#define DM_DIG_MAX				0x3e
-#define DM_DIG_MIN				0x1c
-
 #define DM_DIG_FA_UPPER				0x32
 #define DM_DIG_FA_LOWER				0x20
 #define DM_DIG_FA_TH0				0x100
 #define DM_DIG_FA_TH1				0x400
 #define DM_DIG_FA_TH2				0x600
 
-#define DM_DIG_BACKOFF_MAX			12
-#define DM_DIG_BACKOFF_MIN			-4
-#define DM_DIG_BACKOFF_DEFAULT			10
-
 #define RXPATHSELECTION_SS_TH_lOW		30
 #define RXPATHSELECTION_DIFF_TH			18
 
@@ -108,14 +95,6 @@
 	DIG_OP_TYPE_MAX
 };
 
-enum tag_cck_packet_detection_threshold_type_definition {
-	CCK_PD_STAGE_LOWRSSI = 0,
-	CCK_PD_STAGE_HIGHRSSI = 1,
-	CCK_FA_STAGE_LOW = 2,
-	CCK_FA_STAGE_HIGH = 3,
-	CCK_PD_STAGE_MAX = 4,
-};
-
 enum dm_1r_cca {
 	CCA_1R = 0,
 	CCA_2R = 1,
@@ -134,23 +113,6 @@
 	ANS_ANTENNA_MAX = 3,
 };
 
-enum dm_dig_ext_port_alg {
-	DIG_EXT_PORT_STAGE_0 = 0,
-	DIG_EXT_PORT_STAGE_1 = 1,
-	DIG_EXT_PORT_STAGE_2 = 2,
-	DIG_EXT_PORT_STAGE_3 = 3,
-	DIG_EXT_PORT_STAGE_MAX = 4,
-};
-
-enum dm_dig_connect {
-	DIG_STA_DISCONNECT = 0,
-	DIG_STA_CONNECT = 1,
-	DIG_STA_BEFORE_CONNECT = 2,
-	DIG_MULTISTA_DISCONNECT = 3,
-	DIG_MULTISTA_CONNECT = 4,
-	DIG_CONNECT_MAX
-};
-
 void rtl92d_dm_init(struct ieee80211_hw *hw);
 void rtl92d_dm_watchdog(struct ieee80211_hw *hw);
 void rtl92d_dm_init_edca_turbo(struct ieee80211_hw *hw);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
index 2317707..62ef820 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
@@ -540,23 +540,6 @@
 	return;
 }
 
-void rtl92d_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode)
-{
-	struct rtl_priv *rtlpriv = rtl_priv(hw);
-	u8 u1_h2c_set_pwrmode[3] = { 0 };
-	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
-
-	RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode);
-	SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode);
-	SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, 1);
-	SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode,
-					      ppsc->reg_max_lps_awakeintvl);
-	RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
-		      "rtl92d_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode",
-		      u1_h2c_set_pwrmode, 3);
-	rtl92d_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode);
-}
-
 static bool _rtl92d_cmd_send_packet(struct ieee80211_hw *hw,
 				    struct sk_buff *skb)
 {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.h b/drivers/net/wireless/rtlwifi/rtl8192de/fw.h
index a55a803..1646e7c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/fw.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.h
@@ -136,7 +136,6 @@
 void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id,
 			 u32 cmd_len, u8 *p_cmdbuffer);
 void rtl92d_firmware_selfreset(struct ieee80211_hw *hw);
-void rtl92d_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
 void rtl92d_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished);
 void rtl92d_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
 
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
index 280c3da..f49b60d 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
@@ -546,7 +546,7 @@
 		txpktbuf_bndy = 246;
 		value8 = 0;
 		value32 = 0x80bf0d29;
-	} else if (rtlpriv->rtlhal.macphymode != SINGLEMAC_SINGLEPHY) {
+	} else {
 		maxPage = 127;
 		txpktbuf_bndy = 123;
 		value8 = 0;
@@ -1126,7 +1126,7 @@
 		break;
 
 	}
-	rtl_write_byte(rtlpriv, REG_CR + 2, bt_msr);
+	rtl_write_byte(rtlpriv, MSR, bt_msr);
 	rtlpriv->cfg->ops->led_control(hw, ledaction);
 	if ((bt_msr & MSR_MASK) == MSR_AP)
 		rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
index a0aba08..b19d039 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
@@ -337,21 +337,21 @@
 	.maps[RTL_IMR_ROK] = IMR_ROK,
 	.maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER),
 
-	.maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M,
-	.maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M,
-	.maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M,
-	.maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M,
-	.maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M,
-	.maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M,
-	.maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M,
-	.maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M,
-	.maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M,
-	.maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M,
-	.maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M,
-	.maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M,
+	.maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M,
+	.maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M,
+	.maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M,
+	.maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M,
+	.maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M,
+	.maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M,
+	.maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M,
+	.maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M,
+	.maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M,
+	.maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M,
+	.maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M,
+	.maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M,
 
-	.maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7,
-	.maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15,
+	.maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7,
+	.maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15,
 };
 
 static struct pci_device_id rtl92de_pci_ids[] = {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c
index 8efbcc7..1feaa62 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c
@@ -235,8 +235,8 @@
 		pstats->rx_pwdb_all = pwdb_all;
 		pstats->rxpower = rx_pwr_all;
 		pstats->recvsignalpower = rx_pwr_all;
-		if (pdesc->rxht && pdesc->rxmcs >= DESC92_RATEMCS8 &&
-		    pdesc->rxmcs <= DESC92_RATEMCS15)
+		if (pdesc->rxht && pdesc->rxmcs >= DESC_RATEMCS8 &&
+		    pdesc->rxmcs <= DESC_RATEMCS15)
 			max_spatial_stream = 2;
 		else
 			max_spatial_stream = 1;
@@ -499,6 +499,7 @@
 					 && (GET_RX_DESC_FAGGR(pdesc) == 1));
 	stats->timestamp_low = GET_RX_DESC_TSFL(pdesc);
 	stats->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc);
+	stats->is_ht = (bool)GET_RX_DESC_RXHT(pdesc);
 	rx_status->freq = hw->conf.chandef.chan->center_freq;
 	rx_status->band = hw->conf.chandef.chan->band;
 	if (GET_RX_DESC_CRC32(pdesc))
@@ -512,10 +513,8 @@
 	rx_status->flag |= RX_FLAG_MACTIME_START;
 	if (stats->decrypted)
 		rx_status->flag |= RX_FLAG_DECRYPTED;
-	rx_status->rate_idx = rtlwifi_rate_mapping(hw,
-					(bool)GET_RX_DESC_RXHT(pdesc),
-					(u8)GET_RX_DESC_RXMCS(pdesc),
-					(bool)GET_RX_DESC_PAGGR(pdesc));
+	rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht,
+						   false, stats->rate);
 	rx_status->mactime = GET_RX_DESC_TSFL(pdesc);
 	if (phystatus) {
 		p_drvinfo = (struct rx_fwinfo_92d *)(skb->data +
@@ -612,14 +611,14 @@
 		}
 		/* 5G have no CCK rate */
 		if (rtlhal->current_bandtype == BAND_ON_5G)
-			if (ptcb_desc->hw_rate < DESC92_RATE6M)
-				ptcb_desc->hw_rate = DESC92_RATE6M;
+			if (ptcb_desc->hw_rate < DESC_RATE6M)
+				ptcb_desc->hw_rate = DESC_RATE6M;
 		SET_TX_DESC_TX_RATE(pdesc, ptcb_desc->hw_rate);
 		if (ptcb_desc->use_shortgi || ptcb_desc->use_shortpreamble)
 			SET_TX_DESC_DATA_SHORTGI(pdesc, 1);
 
 		if (rtlhal->macphymode == DUALMAC_DUALPHY &&
-			ptcb_desc->hw_rate == DESC92_RATEMCS7)
+			ptcb_desc->hw_rate == DESC_RATEMCS7)
 			SET_TX_DESC_DATA_SHORTGI(pdesc, 1);
 
 		if (info->flags & IEEE80211_TX_CTL_AMPDU) {
@@ -635,13 +634,13 @@
 		SET_TX_DESC_RTS_STBC(pdesc, ((ptcb_desc->rts_stbc) ? 1 : 0));
 		/* 5G have no CCK rate */
 		if (rtlhal->current_bandtype == BAND_ON_5G)
-			if (ptcb_desc->rts_rate < DESC92_RATE6M)
-				ptcb_desc->rts_rate = DESC92_RATE6M;
+			if (ptcb_desc->rts_rate < DESC_RATE6M)
+				ptcb_desc->rts_rate = DESC_RATE6M;
 		SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate);
 		SET_TX_DESC_RTS_BW(pdesc, 0);
 		SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc);
 		SET_TX_DESC_RTS_SHORT(pdesc, ((ptcb_desc->rts_rate <=
-			DESC92_RATE54M) ?
+			DESC_RATE54M) ?
 			(ptcb_desc->rts_use_shortpreamble ? 1 : 0) :
 			(ptcb_desc->rts_use_shortgi ? 1 : 0)));
 		if (bw_40) {
@@ -756,9 +755,9 @@
 	 * The braces are needed no matter what checkpatch says
 	 */
 	if (rtlhal->current_bandtype == BAND_ON_5G) {
-		SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE6M);
+		SET_TX_DESC_TX_RATE(pdesc, DESC_RATE6M);
 	} else {
-		SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M);
+		SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M);
 	}
 	SET_TX_DESC_SEQ(pdesc, 0);
 	SET_TX_DESC_LINIP(pdesc, 0);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c
index 77deedf..459f3d0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c
@@ -26,6 +26,7 @@
 #include "../wifi.h"
 #include "../base.h"
 #include "../pci.h"
+#include "../core.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -151,35 +152,6 @@
 	{0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00}  /* 32, -16.0dB */
 };
 
-static void rtl92ee_dm_diginit(struct ieee80211_hw *hw)
-{
-	struct rtl_priv *rtlpriv = rtl_priv(hw);
-	struct dig_t *dm_dig = &rtlpriv->dm_digtable;
-
-	dm_dig->cur_igvalue = rtl_get_bbreg(hw, DM_REG_IGI_A_11N,
-					    DM_BIT_IGI_11N);
-	dm_dig->rssi_lowthresh = DM_DIG_THRESH_LOW;
-	dm_dig->rssi_highthresh = DM_DIG_THRESH_HIGH;
-	dm_dig->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
-	dm_dig->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
-	dm_dig->rx_gain_max = DM_DIG_MAX;
-	dm_dig->rx_gain_min = DM_DIG_MIN;
-	dm_dig->back_val = DM_DIG_BACKOFF_DEFAULT;
-	dm_dig->back_range_max = DM_DIG_BACKOFF_MAX;
-	dm_dig->back_range_min = DM_DIG_BACKOFF_MIN;
-	dm_dig->pre_cck_cca_thres = 0xff;
-	dm_dig->cur_cck_cca_thres = 0x83;
-	dm_dig->forbidden_igi = DM_DIG_MIN;
-	dm_dig->large_fa_hit = 0;
-	dm_dig->recover_cnt = 0;
-	dm_dig->dig_dynamic_min = DM_DIG_MIN;
-	dm_dig->dig_dynamic_min_1 = DM_DIG_MIN;
-	dm_dig->media_connect_0 = false;
-	dm_dig->media_connect_1 = false;
-	rtlpriv->dm.dm_initialgain_enable = true;
-	dm_dig->bt30_cur_igi = 0x32;
-}
-
 static void rtl92ee_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw)
 {
 	u32 ret_value;
@@ -298,7 +270,7 @@
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
 	struct dig_t *dm_dig = &rtlpriv->dm_digtable;
-	u8 dig_dynamic_min , dig_maxofmin;
+	u8 dig_min_0, dig_maxofmin;
 	bool bfirstconnect , bfirstdisconnect;
 	u8 dm_dig_max, dm_dig_min;
 	u8 current_igi = dm_dig->cur_igvalue;
@@ -308,7 +280,7 @@
 	if (mac->act_scanning)
 		return;
 
-	dig_dynamic_min = dm_dig->dig_dynamic_min;
+	dig_min_0 = dm_dig->dig_min_0;
 	bfirstconnect = (mac->link_state >= MAC80211_LINKED) &&
 			!dm_dig->media_connect_0;
 	bfirstdisconnect = (mac->link_state < MAC80211_LINKED) &&
@@ -329,19 +301,19 @@
 		if (rtlpriv->dm.one_entry_only) {
 			offset = 0;
 			if (dm_dig->rssi_val_min - offset < dm_dig_min)
-				dig_dynamic_min = dm_dig_min;
+				dig_min_0 = dm_dig_min;
 			else if (dm_dig->rssi_val_min - offset >
 				 dig_maxofmin)
-				dig_dynamic_min = dig_maxofmin;
+				dig_min_0 = dig_maxofmin;
 			else
-				dig_dynamic_min = dm_dig->rssi_val_min - offset;
+				dig_min_0 = dm_dig->rssi_val_min - offset;
 		} else {
-			dig_dynamic_min = dm_dig_min;
+			dig_min_0 = dm_dig_min;
 		}
 
 	} else {
 		dm_dig->rx_gain_max = dm_dig_max;
-		dig_dynamic_min = dm_dig_min;
+		dig_min_0 = dm_dig_min;
 		RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "no link\n");
 	}
 
@@ -368,10 +340,10 @@
 		} else {
 			if (dm_dig->large_fa_hit < 3) {
 				if ((dm_dig->forbidden_igi - 1) <
-				    dig_dynamic_min) {
-					dm_dig->forbidden_igi = dig_dynamic_min;
+				    dig_min_0) {
+					dm_dig->forbidden_igi = dig_min_0;
 					dm_dig->rx_gain_min =
-								dig_dynamic_min;
+								dig_min_0;
 				} else {
 					dm_dig->forbidden_igi--;
 					dm_dig->rx_gain_min =
@@ -430,7 +402,7 @@
 	rtl92ee_dm_write_dig(hw , current_igi);
 	dm_dig->media_connect_0 = ((mac->link_state >= MAC80211_LINKED) ?
 				   true : false);
-	dm_dig->dig_dynamic_min = dig_dynamic_min;
+	dm_dig->dig_min_0 = dig_min_0;
 }
 
 void rtl92ee_dm_write_cck_cca_thres(struct ieee80211_hw *hw, u8 cur_thres)
@@ -1088,10 +1060,11 @@
 void rtl92ee_dm_init(struct ieee80211_hw *hw)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
+	u32 cur_igvalue = rtl_get_bbreg(hw, DM_REG_IGI_A_11N, DM_BIT_IGI_11N);
 
 	rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER;
 
-	rtl92ee_dm_diginit(hw);
+	rtl_dm_diginit(hw, cur_igvalue);
 	rtl92ee_dm_init_rate_adaptive_mask(hw);
 	rtl92ee_dm_init_primary_cca_check(hw);
 	rtl92ee_dm_init_edca_turbo(hw);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h
index 881db7d..107d5a4 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h
@@ -189,28 +189,12 @@
 #define BW_AUTO_SWITCH_HIGH_LOW			25
 #define BW_AUTO_SWITCH_LOW_HIGH			30
 
-#define DM_DIG_THRESH_HIGH			40
-#define DM_DIG_THRESH_LOW			35
-
-#define DM_FALSEALARM_THRESH_LOW		400
-#define DM_FALSEALARM_THRESH_HIGH		1000
-
-#define DM_DIG_MAX				0x3e
-#define DM_DIG_MIN				0x1e
-
-#define DM_DIG_MAX_AP				0x32
-#define DM_DIG_MIN_AP				0x20
-
 #define DM_DIG_FA_UPPER				0x3e
 #define DM_DIG_FA_LOWER				0x1e
 #define DM_DIG_FA_TH0				0x200
 #define DM_DIG_FA_TH1				0x300
 #define DM_DIG_FA_TH2				0x400
 
-#define DM_DIG_BACKOFF_MAX			12
-#define DM_DIG_BACKOFF_MIN			-4
-#define DM_DIG_BACKOFF_DEFAULT			10
-
 #define RXPATHSELECTION_SS_TH_LOW		30
 #define RXPATHSELECTION_DIFF_TH			18
 
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c
index 45c128b..c5d4b80 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c
@@ -666,7 +666,6 @@
 	struct sk_buff *skb = NULL;
 
 	u32 totalpacketlen;
-	bool rtstatus;
 	u8 u1rsvdpageloc[5] = { 0 };
 	bool b_dlok = false;
 
@@ -728,10 +727,7 @@
 	memcpy((u8 *)skb_put(skb, totalpacketlen),
 	       &reserved_page_packet, totalpacketlen);
 
-	rtstatus = rtl_cmd_send_packet(hw, skb);
-
-	if (rtstatus)
-		b_dlok = true;
+	b_dlok = true;
 
 	if (b_dlok) {
 		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD ,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c
index 1a87edc..da0a612 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c
@@ -85,29 +85,6 @@
 	_rtl92ee_set_bcn_ctrl_reg(hw, 0, BIT(1));
 }
 
-static void _rtl92ee_return_beacon_queue_skb(struct ieee80211_hw *hw)
-{
-	struct rtl_priv *rtlpriv = rtl_priv(hw);
-	struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-	struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[BEACON_QUEUE];
-	unsigned long flags;
-
-	spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
-	while (skb_queue_len(&ring->queue)) {
-		struct rtl_tx_buffer_desc *entry =
-						&ring->buffer_desc[ring->idx];
-		struct sk_buff *skb = __skb_dequeue(&ring->queue);
-
-		pci_unmap_single(rtlpci->pdev,
-				 rtlpriv->cfg->ops->get_desc(
-				 (u8 *)entry, true, HW_DESC_TXBUFF_ADDR),
-				 skb->len, PCI_DMA_TODEVICE);
-		kfree_skb(skb);
-		ring->idx = (ring->idx + 1) % ring->entries;
-	}
-	spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags);
-}
-
 static void _rtl92ee_disable_bcn_sub_func(struct ieee80211_hw *hw)
 {
 	_rtl92ee_set_bcn_ctrl_reg(hw, BIT(1), 0);
@@ -403,9 +380,6 @@
 		rtl_write_byte(rtlpriv, REG_DWBCN0_CTRL + 2,
 			       bcnvalid_reg | BIT(0));
 
-		/* Return Beacon TCB */
-		_rtl92ee_return_beacon_queue_skb(hw);
-
 		/* download rsvd page */
 		rtl92ee_set_fw_rsvdpagepkt(hw, false);
 
@@ -588,7 +562,7 @@
 				acm_ctrl &= (~ACMHW_VIQEN);
 				break;
 			case AC3_VO:
-				acm_ctrl &= (~ACMHW_BEQEN);
+				acm_ctrl &= (~ACMHW_VOQEN);
 				break;
 			default:
 				RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
@@ -1163,6 +1137,139 @@
 	rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value);
 }
 
+static bool _rtl8192ee_check_pcie_dma_hang(struct rtl_priv *rtlpriv)
+{
+	u8 tmp;
+
+	/* write reg 0x350 Bit[26]=1. Enable debug port. */
+	tmp = rtl_read_byte(rtlpriv, REG_BACKDOOR_DBI_DATA + 3);
+	if (!(tmp & BIT(2))) {
+		rtl_write_byte(rtlpriv, REG_BACKDOOR_DBI_DATA + 3,
+			       tmp | BIT(2));
+		mdelay(100); /* Suggested by DD Justin_tsai. */
+	}
+
+	/* read reg 0x350 Bit[25] if 1 : RX hang
+	 * read reg 0x350 Bit[24] if 1 : TX hang
+	 */
+	tmp = rtl_read_byte(rtlpriv, REG_BACKDOOR_DBI_DATA + 3);
+	if ((tmp & BIT(0)) || (tmp & BIT(1))) {
+		RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+			 "CheckPcieDMAHang8192EE(): true!!\n");
+		return true;
+	}
+	return false;
+}
+
+static void _rtl8192ee_reset_pcie_interface_dma(struct rtl_priv *rtlpriv,
+						bool mac_power_on)
+{
+	u8 tmp;
+	bool release_mac_rx_pause;
+	u8 backup_pcie_dma_pause;
+
+	RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+		 "ResetPcieInterfaceDMA8192EE()\n");
+
+	/* Revise Note: Follow the document "PCIe RX DMA Hang Reset Flow_v03"
+	 * released by SD1 Alan.
+	 */
+
+	/* 1. disable register write lock
+	 *	write 0x1C bit[1:0] = 2'h0
+	 *	write 0xCC bit[2] = 1'b1
+	 */
+	tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL);
+	tmp &= ~(BIT(1) | BIT(0));
+	rtl_write_byte(rtlpriv, REG_RSV_CTRL, tmp);
+	tmp = rtl_read_byte(rtlpriv, REG_PMC_DBG_CTRL2);
+	tmp |= BIT(2);
+	rtl_write_byte(rtlpriv, REG_PMC_DBG_CTRL2, tmp);
+
+	/* 2. Check and pause TRX DMA
+	 *	write 0x284 bit[18] = 1'b1
+	 *	write 0x301 = 0xFF
+	 */
+	tmp = rtl_read_byte(rtlpriv, REG_RXDMA_CONTROL);
+	if (tmp & BIT(2)) {
+		/* Already pause before the function for another reason. */
+		release_mac_rx_pause = false;
+	} else {
+		rtl_write_byte(rtlpriv, REG_RXDMA_CONTROL, (tmp | BIT(2)));
+		release_mac_rx_pause = true;
+	}
+
+	backup_pcie_dma_pause = rtl_read_byte(rtlpriv, REG_PCIE_CTRL_REG + 1);
+	if (backup_pcie_dma_pause != 0xFF)
+		rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1, 0xFF);
+
+	if (mac_power_on) {
+		/* 3. reset TRX function
+		 *	write 0x100 = 0x00
+		 */
+		rtl_write_byte(rtlpriv, REG_CR, 0);
+	}
+
+	/* 4. Reset PCIe DMA
+	 *	write 0x003 bit[0] = 0
+	 */
+	tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+	tmp &= ~(BIT(0));
+	rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp);
+
+	/* 5. Enable PCIe DMA
+	 *	write 0x003 bit[0] = 1
+	 */
+	tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+	tmp |= BIT(0);
+	rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp);
+
+	if (mac_power_on) {
+		/* 6. enable TRX function
+		 *	write 0x100 = 0xFF
+		 */
+		rtl_write_byte(rtlpriv, REG_CR, 0xFF);
+
+		/* We should init LLT & RQPN and
+		 * prepare Tx/Rx descrptor address later
+		 * because MAC function is reset.
+		 */
+	}
+
+	/* 7. Restore PCIe autoload down bit
+	 *	write 0xF8 bit[17] = 1'b1
+	 */
+	tmp = rtl_read_byte(rtlpriv, REG_MAC_PHY_CTRL_NORMAL + 2);
+	tmp |= BIT(1);
+	rtl_write_byte(rtlpriv, REG_MAC_PHY_CTRL_NORMAL + 2, tmp);
+
+	/* In MAC power on state, BB and RF maybe in ON state,
+	 * if we release TRx DMA here
+	 * it will cause packets to be started to Tx/Rx,
+	 * so we release Tx/Rx DMA later.
+	 */
+	if (!mac_power_on) {
+		/* 8. release TRX DMA
+		 *	write 0x284 bit[18] = 1'b0
+		 *	write 0x301 = 0x00
+		 */
+		if (release_mac_rx_pause) {
+			tmp = rtl_read_byte(rtlpriv, REG_RXDMA_CONTROL);
+			rtl_write_byte(rtlpriv, REG_RXDMA_CONTROL,
+				       (tmp & (~BIT(2))));
+		}
+		rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1,
+			       backup_pcie_dma_pause);
+	}
+
+	/* 9. lock system register
+	 *	write 0xCC bit[2] = 1'b0
+	 */
+	tmp = rtl_read_byte(rtlpriv, REG_PMC_DBG_CTRL2);
+	tmp &= ~(BIT(2));
+	rtl_write_byte(rtlpriv, REG_PMC_DBG_CTRL2, tmp);
+}
+
 int rtl92ee_hw_init(struct ieee80211_hw *hw)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -1188,6 +1295,13 @@
 		rtlhal->fw_ps_state = FW_PS_STATE_ALL_ON_92E;
 	}
 
+	if (_rtl8192ee_check_pcie_dma_hang(rtlpriv)) {
+		RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "92ee dma hang!\n");
+		_rtl8192ee_reset_pcie_interface_dma(rtlpriv,
+						    rtlhal->mac_func_enable);
+		rtlhal->mac_func_enable = false;
+	}
+
 	rtstatus = _rtl92ee_init_mac(hw);
 
 	rtl_write_byte(rtlpriv, 0x577, 0x03);
@@ -1396,7 +1510,7 @@
 			 mode);
 	}
 
-	rtl_write_byte(rtlpriv, (MSR), bt_msr | mode);
+	rtl_write_byte(rtlpriv, MSR, bt_msr | mode);
 	rtlpriv->cfg->ops->led_control(hw, ledaction);
 	if (mode == MSR_AP)
 		rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h b/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h
index 3f2a959..1eaa1fa 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h
@@ -77,9 +77,11 @@
 #define REG_HIMRE				0x00B8
 #define REG_HISRE				0x00BC
 
+#define REG_PMC_DBG_CTRL2			0x00CC
 #define REG_EFUSE_ACCESS			0x00CF
 #define REG_HPON_FSM				0x00EC
 #define REG_SYS_CFG1				0x00F0
+#define REG_MAC_PHY_CTRL_NORMAL			0x00F8
 #define REG_SYS_CFG2				0x00FC
 
 #define REG_CR					0x0100
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/rf.h b/drivers/net/wireless/rtlwifi/rtl8192ee/rf.h
index 8bdeed3..039c013 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/rf.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/rf.h
@@ -27,7 +27,6 @@
 #define __RTL92E_RF_H__
 
 #define RF6052_MAX_TX_PWR		0x3F
-#define RF6052_MAX_REG			0x3F
 
 void rtl92ee_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
 				      u8 bandwidth);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/sw.c
index 9b5a7d5..c31c6bf 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/sw.c
@@ -113,8 +113,6 @@
 				  RCR_HTC_LOC_CTRL		|
 				  RCR_AMF			|
 				  RCR_ACF			|
-				  RCR_ADF			|
-				  RCR_AICV			|
 				  RCR_ACRC32			|
 				  RCR_AB			|
 				  RCR_AM			|
@@ -241,6 +239,7 @@
 	.set_desc = rtl92ee_set_desc,
 	.get_desc = rtl92ee_get_desc,
 	.is_tx_desc_closed = rtl92ee_is_tx_desc_closed,
+	.get_available_desc = rtl92ee_get_available_desc,
 	.tx_polling = rtl92ee_tx_polling,
 	.enable_hw_sec = rtl92ee_enable_hw_security_config,
 	.set_key = rtl92ee_set_key,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c
index 2fcbef1..d39ee67 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c
@@ -47,164 +47,6 @@
 	return skb->priority;
 }
 
-/* mac80211's rate_idx is like this:
- *
- * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ
- *
- * B/G rate:
- * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11,
- *
- * N rate:
- * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15
- *
- * 5G band:rx_status->band == IEEE80211_BAND_5GHZ
- * A rate:
- * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7,
- *
- * N rate:
- * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15
- */
-static int _rtl92ee_rate_mapping(struct ieee80211_hw *hw,
-				 bool isht, u8 desc_rate)
-{
-	int rate_idx;
-
-	if (!isht) {
-		if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) {
-			switch (desc_rate) {
-			case DESC92C_RATE1M:
-				rate_idx = 0;
-				break;
-			case DESC92C_RATE2M:
-				rate_idx = 1;
-				break;
-			case DESC92C_RATE5_5M:
-				rate_idx = 2;
-				break;
-			case DESC92C_RATE11M:
-				rate_idx = 3;
-				break;
-			case DESC92C_RATE6M:
-				rate_idx = 4;
-				break;
-			case DESC92C_RATE9M:
-				rate_idx = 5;
-				break;
-			case DESC92C_RATE12M:
-				rate_idx = 6;
-				break;
-			case DESC92C_RATE18M:
-				rate_idx = 7;
-				break;
-			case DESC92C_RATE24M:
-				rate_idx = 8;
-				break;
-			case DESC92C_RATE36M:
-				rate_idx = 9;
-				break;
-			case DESC92C_RATE48M:
-				rate_idx = 10;
-				break;
-			case DESC92C_RATE54M:
-				rate_idx = 11;
-				break;
-			default:
-				rate_idx = 0;
-				break;
-			}
-		} else {
-			switch (desc_rate) {
-			case DESC92C_RATE6M:
-				rate_idx = 0;
-				break;
-			case DESC92C_RATE9M:
-				rate_idx = 1;
-				break;
-			case DESC92C_RATE12M:
-				rate_idx = 2;
-				break;
-			case DESC92C_RATE18M:
-				rate_idx = 3;
-				break;
-			case DESC92C_RATE24M:
-				rate_idx = 4;
-				break;
-			case DESC92C_RATE36M:
-				rate_idx = 5;
-				break;
-			case DESC92C_RATE48M:
-				rate_idx = 6;
-				break;
-			case DESC92C_RATE54M:
-				rate_idx = 7;
-				break;
-			default:
-				rate_idx = 0;
-				break;
-			}
-		}
-	} else {
-		switch (desc_rate) {
-		case DESC92C_RATEMCS0:
-			rate_idx = 0;
-			break;
-		case DESC92C_RATEMCS1:
-			rate_idx = 1;
-			break;
-		case DESC92C_RATEMCS2:
-			rate_idx = 2;
-			break;
-		case DESC92C_RATEMCS3:
-			rate_idx = 3;
-			break;
-		case DESC92C_RATEMCS4:
-			rate_idx = 4;
-			break;
-		case DESC92C_RATEMCS5:
-			rate_idx = 5;
-			break;
-		case DESC92C_RATEMCS6:
-			rate_idx = 6;
-			break;
-		case DESC92C_RATEMCS7:
-			rate_idx = 7;
-			break;
-		case DESC92C_RATEMCS8:
-			rate_idx = 8;
-			break;
-		case DESC92C_RATEMCS9:
-			rate_idx = 9;
-			break;
-		case DESC92C_RATEMCS10:
-			rate_idx = 10;
-			break;
-		case DESC92C_RATEMCS11:
-			rate_idx = 11;
-			break;
-		case DESC92C_RATEMCS12:
-			rate_idx = 12;
-			break;
-		case DESC92C_RATEMCS13:
-			rate_idx = 13;
-			break;
-		case DESC92C_RATEMCS14:
-			rate_idx = 14;
-			break;
-		case DESC92C_RATEMCS15:
-			rate_idx = 15;
-			break;
-		default:
-			rate_idx = 0;
-			break;
-		}
-	}
-	return rate_idx;
-}
-
 static void _rtl92ee_query_rxphystatus(struct ieee80211_hw *hw,
 				       struct rtl_stats *pstatus, u8 *pdesc,
 				       struct rx_fwinfo *p_drvinfo,
@@ -345,8 +187,8 @@
 		pstatus->recvsignalpower = rx_pwr_all;
 
 		/* (3)EVM of HT rate */
-		if (pstatus->rate >= DESC92C_RATEMCS8 &&
-		    pstatus->rate <= DESC92C_RATEMCS15)
+		if (pstatus->rate >= DESC_RATEMCS8 &&
+		    pstatus->rate <= DESC_RATEMCS15)
 			max_spatial_stream = 2;
 		else
 			max_spatial_stream = 1;
@@ -512,6 +354,10 @@
 	struct ieee80211_hdr *hdr;
 	u32 phystatus = GET_RX_DESC_PHYST(pdesc);
 
+	if (GET_RX_STATUS_DESC_RPT_SEL(pdesc) == 0)
+		status->packet_report_type = NORMAL_RX;
+	else
+		status->packet_report_type = C2H_PACKET;
 	status->length = (u16)GET_RX_DESC_PKT_LEN(pdesc);
 	status->rx_drvinfo_size = (u8)GET_RX_DESC_DRV_INFO_SIZE(pdesc) *
 				  RX_DRV_INFO_SIZE_UNIT;
@@ -576,9 +422,8 @@
 	 * are use (RX_FLAG_HT)
 	 * Notice: this is diff with windows define
 	 */
-	rx_status->rate_idx = _rtl92ee_rate_mapping(hw,
-						    status->is_ht,
-						    status->rate);
+	rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht,
+						   false, status->rate);
 
 	rx_status->mactime = status->timestamp_low;
 	if (phystatus) {
@@ -654,14 +499,7 @@
 	if (!start_rx)
 		return 0;
 
-	if ((last_read_point > (RX_DESC_NUM_92E / 2)) &&
-	    (read_point <= (RX_DESC_NUM_92E / 2))) {
-		remind_cnt = RX_DESC_NUM_92E - write_point;
-	} else {
-		remind_cnt = (read_point >= write_point) ?
-			     (read_point - write_point) :
-			     (RX_DESC_NUM_92E - write_point + read_point);
-	}
+	remind_cnt = calc_fifo_space(read_point, write_point);
 
 	if (remind_cnt == 0)
 		return 0;
@@ -710,7 +548,7 @@
 	return desc_address;
 }
 
-void rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 q_idx)
+u16 rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 q_idx)
 {
 	struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -723,12 +561,11 @@
 	current_tx_read_point = (u16)((tmp_4byte >> 16) & 0x0fff);
 	current_tx_write_point = (u16)((tmp_4byte) & 0x0fff);
 
-	point_diff = ((current_tx_read_point > current_tx_write_point) ?
-		      (current_tx_read_point - current_tx_write_point) :
-		      (TX_DESC_NUM_92E - current_tx_write_point +
-		       current_tx_read_point));
+	point_diff = calc_fifo_space(current_tx_read_point,
+				     current_tx_write_point);
 
 	rtlpci->tx_ring[q_idx].avl_desc = point_diff;
+	return point_diff;
 }
 
 void rtl92ee_pre_fill_tx_bd_desc(struct ieee80211_hw *hw,
@@ -901,13 +738,13 @@
 		} else {
 			if (rtlpriv->ra.is_special_data) {
 				ptcb_desc->use_driver_rate = true;
-				SET_TX_DESC_TX_RATE(pdesc, DESC92C_RATE11M);
+				SET_TX_DESC_TX_RATE(pdesc, DESC_RATE11M);
 			} else {
 				ptcb_desc->use_driver_rate = false;
 			}
 		}
 
-		if (ptcb_desc->hw_rate > DESC92C_RATEMCS0)
+		if (ptcb_desc->hw_rate > DESC_RATEMCS0)
 			short_gi = (ptcb_desc->use_shortgi) ? 1 : 0;
 		else
 			short_gi = (ptcb_desc->use_shortpreamble) ? 1 : 0;
@@ -927,7 +764,7 @@
 		SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate);
 		SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc);
 		SET_TX_DESC_RTS_SHORT(pdesc,
-				((ptcb_desc->rts_rate <= DESC92C_RATE54M) ?
+				((ptcb_desc->rts_rate <= DESC_RATE54M) ?
 				 (ptcb_desc->rts_use_shortpreamble ? 1 : 0) :
 				 (ptcb_desc->rts_use_shortgi ? 1 : 0)));
 
@@ -1038,7 +875,7 @@
 	if (firstseg)
 		SET_TX_DESC_OFFSET(pdesc, txdesc_len);
 
-	SET_TX_DESC_TX_RATE(pdesc, DESC92C_RATE1M);
+	SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M);
 
 	SET_TX_DESC_SEQ(pdesc, 0);
 
@@ -1207,8 +1044,7 @@
 	static u8 stop_report_cnt;
 	struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue];
 
-	/*checking Read/Write Point each interrupt wastes CPU */
-	if (stop_report_cnt > 15 || !rtlpriv->link_info.busytraffic) {
+	{
 		u16 point_diff = 0;
 		u16 cur_tx_rp, cur_tx_wp;
 		u32 tmpu32 = 0;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h
index 6f9be1c..8f78ac9 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h
@@ -542,6 +542,8 @@
 	LE_BITS_TO_4BYTE(__pdesc+8, 12, 4)
 #define GET_RX_DESC_RX_IS_QOS(__pdesc)			\
 	LE_BITS_TO_4BYTE(__pdesc+8, 16, 1)
+#define GET_RX_STATUS_DESC_RPT_SEL(__pdesc)		\
+	LE_BITS_TO_4BYTE(__pdesc+8, 28, 1)
 
 #define GET_RX_DESC_RXMCS(__pdesc)			\
 	LE_BITS_TO_4BYTE(__pdesc+12, 0, 7)
@@ -591,10 +593,10 @@
 } while (0)
 
 #define RTL92EE_RX_HAL_IS_CCK_RATE(rxmcs)\
-	(rxmcs == DESC92C_RATE1M ||\
-	 rxmcs == DESC92C_RATE2M ||\
-	 rxmcs == DESC92C_RATE5_5M ||\
-	 rxmcs == DESC92C_RATE11M)
+	(rxmcs == DESC_RATE1M ||\
+	 rxmcs == DESC_RATE2M ||\
+	 rxmcs == DESC_RATE5_5M ||\
+	 rxmcs == DESC_RATE11M)
 
 #define IS_LITTLE_ENDIAN	1
 
@@ -829,7 +831,7 @@
 			     u8 queue_index);
 u16	rtl92ee_rx_desc_buff_remained_cnt(struct ieee80211_hw *hw,
 					  u8 queue_index);
-void rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 queue_index);
+u16 rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 queue_index);
 void rtl92ee_pre_fill_tx_bd_desc(struct ieee80211_hw *hw,
 				 u8 *tx_bd_desc, u8 *desc, u8 queue_index,
 				 struct sk_buff *skb, dma_addr_t addr);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/def.h b/drivers/net/wireless/rtlwifi/rtl8192se/def.h
index 6e7a70b..41466f9 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/def.h
@@ -31,7 +31,6 @@
 
 #define RX_MPDU_QUEUE				0
 #define RX_CMD_QUEUE				1
-#define RX_MAX_QUEUE				2
 
 #define SHORT_SLOT_TIME				9
 #define NON_SHORT_SLOT_TIME			20
@@ -450,10 +449,10 @@
 	SHIFT_AND_MASK_LE(__pdesc + 24, 0, 32)
 
 #define SE_RX_HAL_IS_CCK_RATE(_pdesc)\
-	(GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE1M ||	\
-	 GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE2M ||	\
-	 GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE5_5M ||\
-	 GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE11M)
+	(GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE1M ||	\
+	 GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE2M ||	\
+	 GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE5_5M ||\
+	 GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE11M)
 
 enum rf_optype {
 	RF_OP_BY_SW_3WIRE = 0,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c
index b3a2d5e..575980b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c
@@ -29,6 +29,7 @@
 
 #include "../wifi.h"
 #include "../base.h"
+#include "../core.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -469,7 +470,7 @@
 			if (digtable->backoff_enable_flag)
 				rtl92s_backoff_enable_flag(hw);
 			else
-				digtable->back_val = DM_DIG_BACKOFF;
+				digtable->back_val = DM_DIG_BACKOFF_MAX;
 
 			if ((digtable->rssi_val + 10 - digtable->back_val) >
 				digtable->rx_gain_max)
@@ -503,7 +504,7 @@
 		digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX;
 		rtl92s_phy_set_fw_cmd(hw, FW_CMD_DIG_ENABLE);
 
-		digtable->back_val = DM_DIG_BACKOFF;
+		digtable->back_val = DM_DIG_BACKOFF_MAX;
 		digtable->cur_igvalue = rtlpriv->phy.default_initialgain[0];
 		digtable->pre_igvalue = 0;
 		return;
@@ -691,7 +692,7 @@
 
 	/* for dig debug rssi value */
 	digtable->rssi_val = 50;
-	digtable->back_val = DM_DIG_BACKOFF;
+	digtable->back_val = DM_DIG_BACKOFF_MAX;
 	digtable->rx_gain_max = DM_DIG_MAX;
 
 	digtable->rx_gain_min = DM_DIG_MIN;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/dm.h b/drivers/net/wireless/rtlwifi/rtl8192se/dm.h
index 2e9052c..de6ac79 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/dm.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/dm.h
@@ -54,24 +54,6 @@
 	DM_STA_DIG_MAX
 };
 
-enum dm_dig_connect {
-	DIG_STA_DISCONNECT = 0,
-	DIG_STA_CONNECT = 1,
-	DIG_STA_BEFORE_CONNECT = 2,
-	DIG_AP_DISCONNECT = 3,
-	DIG_AP_CONNECT = 4,
-	DIG_AP_ADD_STATION = 5,
-	DIG_CONNECT_MAX
-};
-
-enum dm_dig_ext_port_alg {
-	DIG_EXT_PORT_STAGE_0 = 0,
-	DIG_EXT_PORT_STAGE_1 = 1,
-	DIG_EXT_PORT_STAGE_2 = 2,
-	DIG_EXT_PORT_STAGE_3 = 3,
-	DIG_EXT_PORT_STAGE_MAX = 4,
-};
-
 enum dm_ratr_sta {
 	DM_RATR_STA_HIGH = 0,
 	DM_RATR_STA_MIDDLEHIGH = 1,
@@ -99,22 +81,12 @@
 #define	TX_POWER_NEAR_FIELD_THRESH_LVL2	74
 #define	TX_POWER_NEAR_FIELD_THRESH_LVL1	67
 
-#define DM_DIG_THRESH_HIGH		40
-#define DM_DIG_THRESH_LOW		35
-#define	DM_FALSEALARM_THRESH_LOW	40
-#define	DM_FALSEALARM_THRESH_HIGH	1000
 #define	DM_DIG_HIGH_PWR_THRESH_HIGH	75
 #define	DM_DIG_HIGH_PWR_THRESH_LOW	70
-#define	DM_DIG_BACKOFF			12
-#define	DM_DIG_MAX			0x3e
-#define	DM_DIG_MIN			0x1c
 #define	DM_DIG_MIN_Netcore		0x12
-#define	DM_DIG_BACKOFF_MAX		12
-#define	DM_DIG_BACKOFF_MIN		-4
 
 void rtl92s_dm_watchdog(struct ieee80211_hw *hw);
 void rtl92s_dm_init(struct ieee80211_hw *hw);
 void rtl92s_dm_init_edca_turbo(struct ieee80211_hw *hw);
 
 #endif
-
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
index 5761d5b..12b0978 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
@@ -293,7 +293,7 @@
 					acm_ctrl &= (~AcmHw_ViqEn);
 					break;
 				case AC3_VO:
-					acm_ctrl &= (~AcmHw_BeqEn);
+					acm_ctrl &= (~AcmHw_VoqEn);
 					break;
 				default:
 					RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -1204,7 +1204,7 @@
 	if (type != NL80211_IFTYPE_AP &&
 	    rtlpriv->mac80211.link_state < MAC80211_LINKED)
 		bt_msr = rtl_read_byte(rtlpriv, MSR) & ~MSR_LINK_MASK;
-	rtl_write_byte(rtlpriv, (MSR), bt_msr);
+	rtl_write_byte(rtlpriv, MSR, bt_msr);
 
 	temp = rtl_read_dword(rtlpriv, TCR);
 	rtl_write_dword(rtlpriv, TCR, temp & (~BIT(8)));
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
index fb00386..e1fd27c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
@@ -383,21 +383,21 @@
 	.maps[RTL_IMR_ROK] = IMR_ROK,
 	.maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER),
 
-	.maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M,
-	.maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M,
-	.maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M,
-	.maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M,
-	.maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M,
-	.maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M,
-	.maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M,
-	.maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M,
-	.maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M,
-	.maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M,
-	.maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M,
-	.maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M,
+	.maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M,
+	.maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M,
+	.maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M,
+	.maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M,
+	.maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M,
+	.maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M,
+	.maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M,
+	.maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M,
+	.maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M,
+	.maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M,
+	.maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M,
+	.maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M,
 
-	.maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7,
-	.maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15,
+	.maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7,
+	.maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15,
 };
 
 static struct pci_device_id rtl92se_pci_ids[] = {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
index 672fd3b..125b29b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
@@ -191,8 +191,8 @@
 		pstats->rxpower = rx_pwr_all;
 		pstats->recvsignalpower = rx_pwr_all;
 
-		if (pstats->is_ht && pstats->rate >= DESC92_RATEMCS8 &&
-		    pstats->rate <= DESC92_RATEMCS15)
+		if (pstats->is_ht && pstats->rate >= DESC_RATEMCS8 &&
+		    pstats->rate <= DESC_RATEMCS15)
 			max_spatial_stream = 2;
 		else
 			max_spatial_stream = 1;
@@ -264,7 +264,6 @@
 	struct rx_fwinfo *p_drvinfo;
 	u32 phystatus = (u32)GET_RX_STATUS_DESC_PHY_STATUS(pdesc);
 	struct ieee80211_hdr *hdr;
-	bool first_ampdu = false;
 
 	stats->length = (u16)GET_RX_STATUS_DESC_PKT_LEN(pdesc);
 	stats->rx_drvinfo_size = (u8)GET_RX_STATUS_DESC_DRVINFO_SIZE(pdesc) * 8;
@@ -319,8 +318,8 @@
 			rx_status->flag |= RX_FLAG_DECRYPTED;
 	}
 
-	rx_status->rate_idx = rtlwifi_rate_mapping(hw,
-			      stats->is_ht, stats->rate, first_ampdu);
+	rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht,
+						   false, stats->rate);
 
 	rx_status->mactime = stats->timestamp_low;
 	if (phystatus) {
@@ -394,14 +393,14 @@
 		SET_TX_DESC_RSVD_MACID(pdesc, reserved_macid);
 
 		SET_TX_DESC_TXHT(pdesc, ((ptcb_desc->hw_rate >=
-				 DESC92_RATEMCS0) ? 1 : 0));
+				 DESC_RATEMCS0) ? 1 : 0));
 
 		if (rtlhal->version == VERSION_8192S_ACUT) {
-			if (ptcb_desc->hw_rate == DESC92_RATE1M ||
-				ptcb_desc->hw_rate  == DESC92_RATE2M ||
-				ptcb_desc->hw_rate == DESC92_RATE5_5M ||
-				ptcb_desc->hw_rate == DESC92_RATE11M) {
-				ptcb_desc->hw_rate = DESC92_RATE12M;
+			if (ptcb_desc->hw_rate == DESC_RATE1M ||
+			    ptcb_desc->hw_rate  == DESC_RATE2M ||
+			    ptcb_desc->hw_rate == DESC_RATE5_5M ||
+			    ptcb_desc->hw_rate == DESC_RATE11M) {
+				ptcb_desc->hw_rate = DESC_RATE12M;
 			}
 		}
 
@@ -430,7 +429,7 @@
 		SET_TX_DESC_RTS_BANDWIDTH(pdesc, 0);
 		SET_TX_DESC_RTS_SUB_CARRIER(pdesc, ptcb_desc->rts_sc);
 		SET_TX_DESC_RTS_SHORT(pdesc, ((ptcb_desc->rts_rate <=
-		       DESC92_RATE54M) ?
+		       DESC_RATE54M) ?
 		       (ptcb_desc->rts_use_shortpreamble ? 1 : 0)
 		       : (ptcb_desc->rts_use_shortgi ? 1 : 0)));
 
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/def.h b/drivers/net/wireless/rtlwifi/rtl8723ae/def.h
index 94bdd4b..bcdf227 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/def.h
@@ -26,53 +26,12 @@
 #ifndef __RTL8723E_DEF_H__
 #define __RTL8723E_DEF_H__
 
-#define HAL_RETRY_LIMIT_INFRA				48
-#define HAL_RETRY_LIMIT_AP_ADHOC			7
-
-#define RESET_DELAY_8185					20
-
-#define RT_IBSS_INT_MASKS	(IMR_BCNINT | IMR_TBDOK | IMR_TBDER)
-#define RT_AC_INT_MASKS		(IMR_VIDOK | IMR_VODOK | IMR_BEDOK|IMR_BKDOK)
-
-#define NUM_OF_FIRMWARE_QUEUE				10
-#define NUM_OF_PAGES_IN_FW					0x100
-#define NUM_OF_PAGE_IN_FW_QUEUE_BK			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_BE			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_VI			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_VO			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA		0x0
-#define NUM_OF_PAGE_IN_FW_QUEUE_CMD			0x0
-#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT		0x02
-#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH		0x02
-#define NUM_OF_PAGE_IN_FW_QUEUE_BCN			0x2
-#define NUM_OF_PAGE_IN_FW_QUEUE_PUB			0xA1
-
-#define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM		0x026
-#define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM		0x048
-#define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM		0x048
-#define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM		0x026
-#define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM		0x00
-
-#define MAX_LINES_HWCONFIG_TXT				1000
-#define MAX_BYTES_LINE_HWCONFIG_TXT			256
-
-#define SW_THREE_WIRE						0
-#define HW_THREE_WIRE						2
-
-#define BT_DEMO_BOARD						0
-#define BT_QA_BOARD							1
-#define BT_FPGA								2
-
 #define HAL_PRIME_CHNL_OFFSET_DONT_CARE		0
 #define HAL_PRIME_CHNL_OFFSET_LOWER			1
 #define HAL_PRIME_CHNL_OFFSET_UPPER			2
 
-#define MAX_H2C_QUEUE_NUM					10
-
 #define RX_MPDU_QUEUE						0
 #define RX_CMD_QUEUE						1
-#define RX_MAX_QUEUE						2
-#define AC2QUEUEID(_AC)						(_AC)
 
 #define	C2H_RX_CMD_HDR_LEN					8
 #define	GET_C2H_CMD_CMD_LEN(__prxhdr)		\
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c
index a0e8692..4c1c96c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c
@@ -26,6 +26,7 @@
 #include "../wifi.h"
 #include "../base.h"
 #include "../pci.h"
+#include "../core.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -146,31 +147,6 @@
 	{0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00}
 };
 
-static void rtl8723e_dm_diginit(struct ieee80211_hw *hw)
-{
-	struct rtl_priv *rtlpriv = rtl_priv(hw);
-	struct dig_t *dm_digtable = &rtlpriv->dm_digtable;
-
-	dm_digtable->dig_enable_flag = true;
-	dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX;
-	dm_digtable->cur_igvalue = 0x20;
-	dm_digtable->pre_igvalue = 0x0;
-	dm_digtable->cursta_cstate = DIG_STA_DISCONNECT;
-	dm_digtable->presta_cstate = DIG_STA_DISCONNECT;
-	dm_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT;
-	dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW;
-	dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH;
-	dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
-	dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
-	dm_digtable->rx_gain_max = DM_DIG_MAX;
-	dm_digtable->rx_gain_min = DM_DIG_MIN;
-	dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT;
-	dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX;
-	dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN;
-	dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX;
-	dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX;
-}
-
 static u8 rtl8723e_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -395,30 +371,30 @@
 	if (dm_digtable->cursta_cstate == DIG_STA_CONNECT) {
 		dm_digtable->rssi_val_min = rtl8723e_dm_initial_gain_min_pwdb(hw);
 
-		if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LowRssi) {
+		if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LOWRSSI) {
 			if (dm_digtable->rssi_val_min <= 25)
 				dm_digtable->cur_cck_pd_state =
-				    CCK_PD_STAGE_LowRssi;
+				    CCK_PD_STAGE_LOWRSSI;
 			else
 				dm_digtable->cur_cck_pd_state =
-				    CCK_PD_STAGE_HighRssi;
+				    CCK_PD_STAGE_HIGHRSSI;
 		} else {
 			if (dm_digtable->rssi_val_min <= 20)
 				dm_digtable->cur_cck_pd_state =
-				    CCK_PD_STAGE_LowRssi;
+				    CCK_PD_STAGE_LOWRSSI;
 			else
 				dm_digtable->cur_cck_pd_state =
-				    CCK_PD_STAGE_HighRssi;
+				    CCK_PD_STAGE_HIGHRSSI;
 		}
 	} else {
 		dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX;
 	}
 
 	if (dm_digtable->pre_cck_pd_state != dm_digtable->cur_cck_pd_state) {
-		if (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LowRssi) {
+		if (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI) {
 			if (rtlpriv->falsealm_cnt.cnt_cck_fail > 800)
 				dm_digtable->cur_cck_fa_state =
-				    CCK_FA_STAGE_High;
+				    CCK_FA_STAGE_HIGH;
 			else
 				dm_digtable->cur_cck_fa_state =
 				    CCK_FA_STAGE_LOW;
@@ -818,7 +794,7 @@
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
 
 	rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER;
-	rtl8723e_dm_diginit(hw);
+	rtl_dm_diginit(hw, 0x20);
 	rtl8723_dm_init_dynamic_txpower(hw);
 	rtl8723_dm_init_edca_turbo(hw);
 	rtl8723e_dm_init_rate_adaptive_mask(hw);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h
index 6fa0feb..5711105 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h
@@ -42,25 +42,12 @@
 #define BW_AUTO_SWITCH_HIGH_LOW			25
 #define BW_AUTO_SWITCH_LOW_HIGH			30
 
-#define DM_DIG_THRESH_HIGH			40
-#define DM_DIG_THRESH_LOW			35
-
-#define DM_FALSEALARM_THRESH_LOW		400
-#define DM_FALSEALARM_THRESH_HIGH		1000
-
-#define DM_DIG_MAX				0x3e
-#define DM_DIG_MIN				0x1e
-
 #define DM_DIG_FA_UPPER				0x32
 #define DM_DIG_FA_LOWER				0x20
 #define DM_DIG_FA_TH0				0x20
 #define DM_DIG_FA_TH1				0x100
 #define DM_DIG_FA_TH2				0x200
 
-#define DM_DIG_BACKOFF_MAX			12
-#define DM_DIG_BACKOFF_MIN			-4
-#define DM_DIG_BACKOFF_DEFAULT			10
-
 #define RXPATHSELECTION_SS_TH_LOW		30
 #define RXPATHSELECTION_DIFF_TH			18
 
@@ -108,14 +95,6 @@
 	DIG_OP_TYPE_MAX
 };
 
-enum tag_cck_packet_detection_threshold_type_definition {
-	CCK_PD_STAGE_LowRssi = 0,
-	CCK_PD_STAGE_HighRssi = 1,
-	CCK_FA_STAGE_LOW = 2,
-	CCK_FA_STAGE_High = 3,
-	CCK_PD_STAGE_MAX = 4,
-};
-
 enum dm_1r_cca_e {
 	CCA_1R = 0,
 	CCA_2R = 1,
@@ -134,23 +113,6 @@
 	ANS_ANTENNA_MAX = 3,
 };
 
-enum dm_dig_ext_port_alg_e {
-	DIG_EXT_PORT_STAGE_0 = 0,
-	DIG_EXT_PORT_STAGE_1 = 1,
-	DIG_EXT_PORT_STAGE_2 = 2,
-	DIG_EXT_PORT_STAGE_3 = 3,
-	DIG_EXT_PORT_STAGE_MAX = 4,
-};
-
-enum dm_dig_connect_e {
-	DIG_STA_DISCONNECT = 0,
-	DIG_STA_CONNECT = 1,
-	DIG_STA_BEFORE_CONNECT = 2,
-	DIG_MULTISTA_DISCONNECT = 3,
-	DIG_MULTISTA_CONNECT = 4,
-	DIG_CONNECT_MAX
-};
-
 #define BT_RSSI_STATE_NORMAL_POWER      BIT_OFFSET_LEN_MASK_32(0, 1)
 #define BT_RSSI_STATE_AMDPU_OFF         BIT_OFFSET_LEN_MASK_32(1, 1)
 #define BT_RSSI_STATE_SPECIAL_LOW       BIT_OFFSET_LEN_MASK_32(2, 1)
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
index aa08546..67bb47d 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
@@ -362,7 +362,7 @@
 					acm_ctrl &= (~ACMHW_VIQEN);
 					break;
 				case AC3_VO:
-					acm_ctrl &= (~ACMHW_BEQEN);
+					acm_ctrl &= (~ACMHW_VOQEN);
 					break;
 				default:
 					RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
@@ -1183,7 +1183,7 @@
 			 mode);
 	}
 
-	rtl_write_byte(rtlpriv, (MSR), bt_msr | mode);
+	rtl_write_byte(rtlpriv, MSR, bt_msr | mode);
 	rtlpriv->cfg->ops->led_control(hw, ledaction);
 	if (mode == MSR_AP)
 		rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h b/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h
index f3f45b1..7b44ebc 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h
@@ -27,7 +27,6 @@
 #define __RTL8723E_RF_H__
 
 #define RF6052_MAX_TX_PWR		0x3F
-#define RF6052_MAX_REG			0x3F
 
 void rtl8723e_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
 				       u8 bandwidth);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
index d372cca..2f7c144 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
@@ -45,164 +45,6 @@
 	return skb->priority;
 }
 
-/* mac80211's rate_idx is like this:
- *
- * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ
- *
- * B/G rate:
- * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11,
- *
- * N rate:
- * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15
- *
- * 5G band:rx_status->band == IEEE80211_BAND_5GHZ
- * A rate:
- * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7,
- *
- * N rate:
- * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15
- */
-static int _rtl8723e_rate_mapping(struct ieee80211_hw *hw,
-				  bool isht, u8 desc_rate)
-{
-	int rate_idx;
-
-	if (!isht) {
-		if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) {
-			switch (desc_rate) {
-			case DESC92C_RATE1M:
-				rate_idx = 0;
-				break;
-			case DESC92C_RATE2M:
-				rate_idx = 1;
-				break;
-			case DESC92C_RATE5_5M:
-				rate_idx = 2;
-				break;
-			case DESC92C_RATE11M:
-				rate_idx = 3;
-				break;
-			case DESC92C_RATE6M:
-				rate_idx = 4;
-				break;
-			case DESC92C_RATE9M:
-				rate_idx = 5;
-				break;
-			case DESC92C_RATE12M:
-				rate_idx = 6;
-				break;
-			case DESC92C_RATE18M:
-				rate_idx = 7;
-				break;
-			case DESC92C_RATE24M:
-				rate_idx = 8;
-				break;
-			case DESC92C_RATE36M:
-				rate_idx = 9;
-				break;
-			case DESC92C_RATE48M:
-				rate_idx = 10;
-				break;
-			case DESC92C_RATE54M:
-				rate_idx = 11;
-				break;
-			default:
-				rate_idx = 0;
-				break;
-			}
-		} else {
-			switch (desc_rate) {
-			case DESC92C_RATE6M:
-				rate_idx = 0;
-				break;
-			case DESC92C_RATE9M:
-				rate_idx = 1;
-				break;
-			case DESC92C_RATE12M:
-				rate_idx = 2;
-				break;
-			case DESC92C_RATE18M:
-				rate_idx = 3;
-				break;
-			case DESC92C_RATE24M:
-				rate_idx = 4;
-				break;
-			case DESC92C_RATE36M:
-				rate_idx = 5;
-				break;
-			case DESC92C_RATE48M:
-				rate_idx = 6;
-				break;
-			case DESC92C_RATE54M:
-				rate_idx = 7;
-				break;
-			default:
-				rate_idx = 0;
-				break;
-			}
-		}
-	} else {
-		switch (desc_rate) {
-		case DESC92C_RATEMCS0:
-			rate_idx = 0;
-			break;
-		case DESC92C_RATEMCS1:
-			rate_idx = 1;
-			break;
-		case DESC92C_RATEMCS2:
-			rate_idx = 2;
-			break;
-		case DESC92C_RATEMCS3:
-			rate_idx = 3;
-			break;
-		case DESC92C_RATEMCS4:
-			rate_idx = 4;
-			break;
-		case DESC92C_RATEMCS5:
-			rate_idx = 5;
-			break;
-		case DESC92C_RATEMCS6:
-			rate_idx = 6;
-			break;
-		case DESC92C_RATEMCS7:
-			rate_idx = 7;
-			break;
-		case DESC92C_RATEMCS8:
-			rate_idx = 8;
-			break;
-		case DESC92C_RATEMCS9:
-			rate_idx = 9;
-			break;
-		case DESC92C_RATEMCS10:
-			rate_idx = 10;
-			break;
-		case DESC92C_RATEMCS11:
-			rate_idx = 11;
-			break;
-		case DESC92C_RATEMCS12:
-			rate_idx = 12;
-			break;
-		case DESC92C_RATEMCS13:
-			rate_idx = 13;
-			break;
-		case DESC92C_RATEMCS14:
-			rate_idx = 14;
-			break;
-		case DESC92C_RATEMCS15:
-			rate_idx = 15;
-			break;
-		default:
-			rate_idx = 0;
-			break;
-		}
-	}
-	return rate_idx;
-}
-
 static void _rtl8723e_query_rxphystatus(struct ieee80211_hw *hw,
 					struct rtl_stats *pstatus, u8 *pdesc,
 					struct rx_fwinfo_8723e *p_drvinfo,
@@ -503,8 +345,8 @@
 	 * are use (RX_FLAG_HT)
 	 * Notice: this is diff with windows define
 	 */
-	rx_status->rate_idx = _rtl8723e_rate_mapping(hw,
-				status->is_ht, status->rate);
+	rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht,
+						   false, status->rate);
 
 	rx_status->mactime = status->timestamp_low;
 	if (phystatus == true) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c
index dd7eb43..e77c3a4 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c
@@ -26,6 +26,7 @@
 #include "../wifi.h"
 #include "../base.h"
 #include "../pci.h"
+#include "../core.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -211,35 +212,6 @@
 		(pwr_val << 16) | (pwr_val << 24);
 }
 
-static void rtl8723be_dm_diginit(struct ieee80211_hw *hw)
-{
-	struct rtl_priv *rtlpriv = rtl_priv(hw);
-	struct dig_t *dm_digtable = &rtlpriv->dm_digtable;
-
-	dm_digtable->dig_enable_flag = true;
-	dm_digtable->cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f);
-	dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW;
-	dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH;
-	dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
-	dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
-	dm_digtable->rx_gain_max = DM_DIG_MAX;
-	dm_digtable->rx_gain_min = DM_DIG_MIN;
-	dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT;
-	dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX;
-	dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN;
-	dm_digtable->pre_cck_cca_thres = 0xff;
-	dm_digtable->cur_cck_cca_thres = 0x83;
-	dm_digtable->forbidden_igi = DM_DIG_MIN;
-	dm_digtable->large_fa_hit = 0;
-	dm_digtable->recover_cnt = 0;
-	dm_digtable->dig_dynamic_min = DM_DIG_MIN;
-	dm_digtable->dig_dynamic_min_1 = DM_DIG_MIN;
-	dm_digtable->media_connect_0 = false;
-	dm_digtable->media_connect_1 = false;
-	rtlpriv->dm.dm_initialgain_enable = true;
-	dm_digtable->bt30_cur_igi = 0x32;
-}
-
 void rtl8723be_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -293,9 +265,10 @@
 void rtl8723be_dm_init(struct ieee80211_hw *hw)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
+	u32 cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f);
 
 	rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER;
-	rtl8723be_dm_diginit(hw);
+	rtl_dm_diginit(hw, cur_igvalue);
 	rtl8723be_dm_init_rate_adaptive_mask(hw);
 	rtl8723_dm_init_edca_turbo(hw);
 	rtl8723_dm_init_dynamic_bb_powersaving(hw);
@@ -336,7 +309,7 @@
 		rtl_dm_dig->min_undec_pwdb_for_dm =
 				rtlpriv->dm.entry_min_undec_sm_pwdb;
 		RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD,
-			 "AP Ext Port or disconnet PWDB = 0x%x\n",
+			 "AP Ext Port or disconnect PWDB = 0x%x\n",
 			  rtl_dm_dig->min_undec_pwdb_for_dm);
 	}
 	RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "MinUndecoratedPWDBForDM =%d\n",
@@ -424,7 +397,7 @@
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
 	struct dig_t *dm_digtable = &rtlpriv->dm_digtable;
 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
-	u8 dig_dynamic_min, dig_maxofmin;
+	u8 dig_min_0, dig_maxofmin;
 	bool bfirstconnect, bfirstdisconnect;
 	u8 dm_dig_max, dm_dig_min;
 	u8 current_igi = dm_digtable->cur_igvalue;
@@ -434,7 +407,7 @@
 	if (mac->act_scanning)
 		return;
 
-	dig_dynamic_min = dm_digtable->dig_dynamic_min;
+	dig_min_0 = dm_digtable->dig_min_0;
 	bfirstconnect = (mac->link_state >= MAC80211_LINKED) &&
 			!dm_digtable->media_connect_0;
 	bfirstdisconnect = (mac->link_state < MAC80211_LINKED) &&
@@ -456,20 +429,20 @@
 		if (rtlpriv->dm.one_entry_only) {
 			offset = 12;
 			if (dm_digtable->rssi_val_min - offset < dm_dig_min)
-				dig_dynamic_min = dm_dig_min;
+				dig_min_0 = dm_dig_min;
 			else if (dm_digtable->rssi_val_min - offset >
 							dig_maxofmin)
-				dig_dynamic_min = dig_maxofmin;
+				dig_min_0 = dig_maxofmin;
 			else
-				dig_dynamic_min =
+				dig_min_0 =
 					dm_digtable->rssi_val_min - offset;
 		} else {
-			dig_dynamic_min = dm_dig_min;
+			dig_min_0 = dm_dig_min;
 		}
 
 	} else {
 		dm_digtable->rx_gain_max = dm_dig_max;
-		dig_dynamic_min = dm_dig_min;
+		dig_min_0 = dm_dig_min;
 		RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "no link\n");
 	}
 
@@ -497,11 +470,11 @@
 		} else {
 			if (dm_digtable->large_fa_hit < 3) {
 				if ((dm_digtable->forbidden_igi - 1) <
-				     dig_dynamic_min) {
+				     dig_min_0) {
 					dm_digtable->forbidden_igi =
-							dig_dynamic_min;
+							dig_min_0;
 					dm_digtable->rx_gain_min =
-							dig_dynamic_min;
+							dig_min_0;
 				} else {
 					dm_digtable->forbidden_igi--;
 					dm_digtable->rx_gain_min =
@@ -552,7 +525,7 @@
 	rtl8723be_dm_write_dig(hw, current_igi);
 	dm_digtable->media_connect_0 =
 		((mac->link_state >= MAC80211_LINKED) ? true : false);
-	dm_digtable->dig_dynamic_min = dig_dynamic_min;
+	dm_digtable->dig_min_0 = dig_min_0;
 }
 
 static void rtl8723be_dm_false_alarm_counter_statistics(
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h
index e4c0e8a..f752a2c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h
@@ -180,28 +180,12 @@
 #define BW_AUTO_SWITCH_HIGH_LOW			25
 #define BW_AUTO_SWITCH_LOW_HIGH			30
 
-#define DM_DIG_THRESH_HIGH			40
-#define DM_DIG_THRESH_LOW			35
-
-#define DM_FALSEALARM_THRESH_LOW		400
-#define DM_FALSEALARM_THRESH_HIGH		1000
-
-#define DM_DIG_MAX				0x3e
-#define DM_DIG_MIN				0x1e
-
-#define DM_DIG_MAX_AP				0x32
-#define DM_DIG_MIN_AP				0x20
-
 #define DM_DIG_FA_UPPER				0x3e
 #define DM_DIG_FA_LOWER				0x1e
 #define DM_DIG_FA_TH0				0x200
 #define DM_DIG_FA_TH1				0x300
 #define DM_DIG_FA_TH2				0x400
 
-#define DM_DIG_BACKOFF_MAX			12
-#define DM_DIG_BACKOFF_MIN			-4
-#define DM_DIG_BACKOFF_DEFAULT			10
-
 #define RXPATHSELECTION_SS_TH_LOW		30
 #define RXPATHSELECTION_DIFF_TH			18
 
@@ -252,23 +236,6 @@
 	ANS_ANTENNA_MAX	= 3,
 };
 
-enum dm_dig_ext_port_alg_e {
-	DIG_EXT_PORT_STAGE_0	= 0,
-	DIG_EXT_PORT_STAGE_1	= 1,
-	DIG_EXT_PORT_STAGE_2	= 2,
-	DIG_EXT_PORT_STAGE_3	= 3,
-	DIG_EXT_PORT_STAGE_MAX	= 4,
-};
-
-enum dm_dig_connect_e {
-	DIG_STA_DISCONNECT	= 0,
-	DIG_STA_CONNECT		= 1,
-	DIG_STA_BEFORE_CONNECT	= 2,
-	DIG_MULTISTA_DISCONNECT	= 3,
-	DIG_MULTISTA_CONNECT	= 4,
-	DIG_CONNECT_MAX
-};
-
 enum pwr_track_control_method {
 	BBSWING,
 	TXAGC
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c
index 6dad28e..b681af3 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c
@@ -603,7 +603,7 @@
 				acm_ctrl &= (~ACMHW_VIQEN);
 				break;
 			case AC3_VO:
-				acm_ctrl &= (~ACMHW_BEQEN);
+				acm_ctrl &= (~ACMHW_VOQEN);
 				break;
 			default:
 				RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
@@ -1558,7 +1558,7 @@
 			 mode);
 	}
 
-	rtl_write_byte(rtlpriv, (MSR), bt_msr | mode);
+	rtl_write_byte(rtlpriv, MSR, bt_msr | mode);
 	rtlpriv->cfg->ops->led_control(hw, ledaction);
 	if (mode == MSR_AP)
 		rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/phy.c b/drivers/net/wireless/rtlwifi/rtl8723be/phy.c
index 20dcc25..b7b73cb 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/phy.c
@@ -874,31 +874,6 @@
 		  ROFDM0_RXDETECTOR3, rtlphy->framesync);
 }
 
-void rtl8723be_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel)
-{
-	struct rtl_priv *rtlpriv = rtl_priv(hw);
-	struct rtl_phy *rtlphy = &rtlpriv->phy;
-	u8 txpwr_level;
-	long txpwr_dbm;
-
-	txpwr_level = rtlphy->cur_cck_txpwridx;
-	txpwr_dbm = rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_B,
-						 txpwr_level);
-	txpwr_level = rtlphy->cur_ofdm24g_txpwridx;
-	if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level) >
-	    txpwr_dbm)
-		txpwr_dbm =
-		    rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G,
-						 txpwr_level);
-	txpwr_level = rtlphy->cur_ofdm24g_txpwridx;
-	if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G,
-					 txpwr_level) > txpwr_dbm)
-		txpwr_dbm =
-		    rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G,
-						 txpwr_level);
-	*powerlevel = txpwr_dbm;
-}
-
 static u8 _rtl8723be_phy_get_ratesection_intxpower_byrate(enum radio_path path,
 							  u8 rate)
 {
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/phy.h b/drivers/net/wireless/rtlwifi/rtl8723be/phy.h
index 6339738..9021d47 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/phy.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/phy.h
@@ -114,8 +114,6 @@
 bool rtl8723be_phy_bb_config(struct ieee80211_hw *hw);
 bool rtl8723be_phy_rf_config(struct ieee80211_hw *hw);
 void rtl8723be_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw);
-void rtl8723be_phy_get_txpower_level(struct ieee80211_hw *hw,
-				     long *powerlevel);
 void rtl8723be_phy_set_txpower_level(struct ieee80211_hw *hw,
 				     u8 channel);
 void rtl8723be_phy_scan_operation_backup(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/rf.h b/drivers/net/wireless/rtlwifi/rtl8723be/rf.h
index a6fea10..f423e15 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/rf.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/rf.h
@@ -27,7 +27,6 @@
 #define __RTL8723BE_RF_H__
 
 #define RF6052_MAX_TX_PWR		0x3F
-#define RF6052_MAX_REG			0x3F
 
 void rtl8723be_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
 					u8 bandwidth);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c
index 223eb42..1017f02 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c
@@ -387,12 +387,14 @@
 module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444);
 module_param_named(disable_watchdog, rtl8723be_mod_params.disable_watchdog,
 		   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(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)");
-MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n");
+MODULE_PARM_DESC(disable_watchdog,
+		 "Set to 1 to disable the watchdog (default 0)\n");
 
 static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
 
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c
index d6a1c70..338ec9a 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c
@@ -47,164 +47,6 @@
 	return skb->priority;
 }
 
-/* mac80211's rate_idx is like this:
- *
- * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ
- *
- * B/G rate:
- * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11,
- *
- * N rate:
- * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15
- *
- * 5G band:rx_status->band == IEEE80211_BAND_5GHZ
- * A rate:
- * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7,
- *
- * N rate:
- * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15
- */
-static int _rtl8723be_rate_mapping(struct ieee80211_hw *hw,
-				   bool isht, u8 desc_rate)
-{
-	int rate_idx;
-
-	if (!isht) {
-		if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) {
-			switch (desc_rate) {
-			case DESC92C_RATE1M:
-				rate_idx = 0;
-				break;
-			case DESC92C_RATE2M:
-				rate_idx = 1;
-				break;
-			case DESC92C_RATE5_5M:
-				rate_idx = 2;
-				break;
-			case DESC92C_RATE11M:
-				rate_idx = 3;
-				break;
-			case DESC92C_RATE6M:
-				rate_idx = 4;
-				break;
-			case DESC92C_RATE9M:
-				rate_idx = 5;
-				break;
-			case DESC92C_RATE12M:
-				rate_idx = 6;
-				break;
-			case DESC92C_RATE18M:
-				rate_idx = 7;
-				break;
-			case DESC92C_RATE24M:
-				rate_idx = 8;
-				break;
-			case DESC92C_RATE36M:
-				rate_idx = 9;
-				break;
-			case DESC92C_RATE48M:
-				rate_idx = 10;
-				break;
-			case DESC92C_RATE54M:
-				rate_idx = 11;
-				break;
-			default:
-				rate_idx = 0;
-				break;
-			}
-		} else {
-			switch (desc_rate) {
-			case DESC92C_RATE6M:
-				rate_idx = 0;
-				break;
-			case DESC92C_RATE9M:
-				rate_idx = 1;
-				break;
-			case DESC92C_RATE12M:
-				rate_idx = 2;
-				break;
-			case DESC92C_RATE18M:
-				rate_idx = 3;
-				break;
-			case DESC92C_RATE24M:
-				rate_idx = 4;
-				break;
-			case DESC92C_RATE36M:
-				rate_idx = 5;
-				break;
-			case DESC92C_RATE48M:
-				rate_idx = 6;
-				break;
-			case DESC92C_RATE54M:
-				rate_idx = 7;
-				break;
-			default:
-				rate_idx = 0;
-				break;
-			}
-		}
-	} else {
-		switch (desc_rate) {
-		case DESC92C_RATEMCS0:
-			rate_idx = 0;
-			break;
-		case DESC92C_RATEMCS1:
-			rate_idx = 1;
-			break;
-		case DESC92C_RATEMCS2:
-			rate_idx = 2;
-			break;
-		case DESC92C_RATEMCS3:
-			rate_idx = 3;
-			break;
-		case DESC92C_RATEMCS4:
-			rate_idx = 4;
-			break;
-		case DESC92C_RATEMCS5:
-			rate_idx = 5;
-			break;
-		case DESC92C_RATEMCS6:
-			rate_idx = 6;
-			break;
-		case DESC92C_RATEMCS7:
-			rate_idx = 7;
-			break;
-		case DESC92C_RATEMCS8:
-			rate_idx = 8;
-			break;
-		case DESC92C_RATEMCS9:
-			rate_idx = 9;
-			break;
-		case DESC92C_RATEMCS10:
-			rate_idx = 10;
-			break;
-		case DESC92C_RATEMCS11:
-			rate_idx = 11;
-			break;
-		case DESC92C_RATEMCS12:
-			rate_idx = 12;
-			break;
-		case DESC92C_RATEMCS13:
-			rate_idx = 13;
-			break;
-		case DESC92C_RATEMCS14:
-			rate_idx = 14;
-			break;
-		case DESC92C_RATEMCS15:
-			rate_idx = 15;
-			break;
-		default:
-			rate_idx = 0;
-			break;
-		}
-	}
-	return rate_idx;
-}
-
 static void _rtl8723be_query_rxphystatus(struct ieee80211_hw *hw,
 					 struct rtl_stats *pstatus, u8 *pdesc,
 					 struct rx_fwinfo_8723be *p_drvinfo,
@@ -558,8 +400,8 @@
 	 * supported rates or MCS index if HT rates
 	 * are use (RX_FLAG_HT)
 	 */
-	rx_status->rate_idx = _rtl8723be_rate_mapping(hw, status->is_ht,
-						      status->rate);
+	rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht,
+						   false, status->rate);
 
 	rx_status->mactime = status->timestamp_low;
 	if (phystatus) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/def.h b/drivers/net/wireless/rtlwifi/rtl8821ae/def.h
index a730985..dfbdf53 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/def.h
@@ -118,55 +118,14 @@
 #define	WIFI_NAV_UPPER_US				30000
 #define HAL_92C_NAV_UPPER_UNIT			128
 
-#define HAL_RETRY_LIMIT_INFRA				48
-#define HAL_RETRY_LIMIT_AP_ADHOC			7
-
-#define RESET_DELAY_8185					20
-
-#define RT_IBSS_INT_MASKS	(IMR_BCNINT | IMR_TBDOK | IMR_TBDER)
-#define RT_AC_INT_MASKS		(IMR_VIDOK | IMR_VODOK | IMR_BEDOK|IMR_BKDOK)
-
-#define NUM_OF_FIRMWARE_QUEUE				10
-#define NUM_OF_PAGES_IN_FW					0x100
-#define NUM_OF_PAGE_IN_FW_QUEUE_BK			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_BE			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_VI			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_VO			0x07
-#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA		0x0
-#define NUM_OF_PAGE_IN_FW_QUEUE_CMD			0x0
-#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT		0x02
-#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH		0x02
-#define NUM_OF_PAGE_IN_FW_QUEUE_BCN			0x2
-#define NUM_OF_PAGE_IN_FW_QUEUE_PUB			0xA1
-
-#define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM		0x026
-#define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM		0x048
-#define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM		0x048
-#define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM		0x026
-#define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM		0x00
-
 #define MAX_RX_DMA_BUFFER_SIZE				0x3E80
 
-#define MAX_LINES_HWCONFIG_TXT				1000
-#define MAX_BYTES_LINE_HWCONFIG_TXT			256
-
-#define SW_THREE_WIRE						0
-#define HW_THREE_WIRE						2
-
-#define BT_DEMO_BOARD						0
-#define BT_QA_BOARD							1
-#define BT_FPGA								2
-
 #define HAL_PRIME_CHNL_OFFSET_DONT_CARE		0
 #define HAL_PRIME_CHNL_OFFSET_LOWER			1
 #define HAL_PRIME_CHNL_OFFSET_UPPER			2
 
-#define MAX_H2C_QUEUE_NUM					10
-
 #define RX_MPDU_QUEUE						0
 #define RX_CMD_QUEUE						1
-#define RX_MAX_QUEUE						2
-#define AC2QUEUEID(_AC)						(_AC)
 
 #define MAX_RX_DMA_BUFFER_SIZE_8812	0x3E80
 
@@ -373,60 +332,6 @@
 	QSLT_CMD = 0x13,
 };
 
-enum rtl_desc8821ae_rate {
-	DESC_RATE1M = 0x00,
-	DESC_RATE2M = 0x01,
-	DESC_RATE5_5M = 0x02,
-	DESC_RATE11M = 0x03,
-
-	DESC_RATE6M = 0x04,
-	DESC_RATE9M = 0x05,
-	DESC_RATE12M = 0x06,
-	DESC_RATE18M = 0x07,
-	DESC_RATE24M = 0x08,
-	DESC_RATE36M = 0x09,
-	DESC_RATE48M = 0x0a,
-	DESC_RATE54M = 0x0b,
-
-	DESC_RATEMCS0 = 0x0c,
-	DESC_RATEMCS1 = 0x0d,
-	DESC_RATEMCS2 = 0x0e,
-	DESC_RATEMCS3 = 0x0f,
-	DESC_RATEMCS4 = 0x10,
-	DESC_RATEMCS5 = 0x11,
-	DESC_RATEMCS6 = 0x12,
-	DESC_RATEMCS7 = 0x13,
-	DESC_RATEMCS8 = 0x14,
-	DESC_RATEMCS9 = 0x15,
-	DESC_RATEMCS10 = 0x16,
-	DESC_RATEMCS11 = 0x17,
-	DESC_RATEMCS12 = 0x18,
-	DESC_RATEMCS13 = 0x19,
-	DESC_RATEMCS14 = 0x1a,
-	DESC_RATEMCS15 = 0x1b,
-
-	DESC_RATEVHT1SS_MCS0 = 0x2c,
-	DESC_RATEVHT1SS_MCS1 = 0x2d,
-	DESC_RATEVHT1SS_MCS2 = 0x2e,
-	DESC_RATEVHT1SS_MCS3 = 0x2f,
-	DESC_RATEVHT1SS_MCS4 = 0x30,
-	DESC_RATEVHT1SS_MCS5 = 0x31,
-	DESC_RATEVHT1SS_MCS6 = 0x32,
-	DESC_RATEVHT1SS_MCS7 = 0x33,
-	DESC_RATEVHT1SS_MCS8 = 0x34,
-	DESC_RATEVHT1SS_MCS9 = 0x35,
-	DESC_RATEVHT2SS_MCS0 = 0x36,
-	DESC_RATEVHT2SS_MCS1 = 0x37,
-	DESC_RATEVHT2SS_MCS2 = 0x38,
-	DESC_RATEVHT2SS_MCS3 = 0x39,
-	DESC_RATEVHT2SS_MCS4 = 0x3a,
-	DESC_RATEVHT2SS_MCS5 = 0x3b,
-	DESC_RATEVHT2SS_MCS6 = 0x3c,
-	DESC_RATEVHT2SS_MCS7 = 0x3d,
-	DESC_RATEVHT2SS_MCS8 = 0x3e,
-	DESC_RATEVHT2SS_MCS9 = 0x3f,
-};
-
 enum rx_packet_type {
 	NORMAL_RX,
 	TX_REPORT1,
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c
index ba30b0d..342678d 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c
@@ -26,6 +26,7 @@
 #include "../wifi.h"
 #include "../base.h"
 #include "../pci.h"
+#include "../core.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -519,34 +520,6 @@
 	}
 }
 
-static void rtl8821ae_dm_diginit(struct ieee80211_hw *hw)
-{
-	struct rtl_priv *rtlpriv = rtl_priv(hw);
-	struct dig_t *dm_digtable = &rtlpriv->dm_digtable;
-
-	dm_digtable->cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f);
-	dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW;
-	dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH;
-	dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
-	dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
-	dm_digtable->rx_gain_max = DM_DIG_MAX;
-	dm_digtable->rx_gain_min = DM_DIG_MIN;
-	dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT;
-	dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX;
-	dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN;
-	dm_digtable->pre_cck_cca_thres = 0xff;
-	dm_digtable->cur_cck_cca_thres = 0x83;
-	dm_digtable->forbidden_igi = DM_DIG_MIN;
-	dm_digtable->large_fa_hit = 0;
-	dm_digtable->recover_cnt = 0;
-	dm_digtable->dig_dynamic_min = DM_DIG_MIN;
-	dm_digtable->dig_dynamic_min_1 = DM_DIG_MIN;
-	dm_digtable->media_connect_0 = false;
-	dm_digtable->media_connect_1 = false;
-	rtlpriv->dm.dm_initialgain_enable = true;
-	dm_digtable->bt30_cur_igi = 0x32;
-}
-
 void rtl8821ae_dm_init_edca_turbo(struct ieee80211_hw *hw)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -606,6 +579,7 @@
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
 	struct rtl_phy *rtlphy = &rtlpriv->phy;
+	u32 cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f);
 
 	spin_lock(&rtlpriv->locks.iqk_lock);
 	rtlphy->lck_inprogress = false;
@@ -613,7 +587,7 @@
 
 	rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER;
 	rtl8821ae_dm_common_info_self_init(hw);
-	rtl8821ae_dm_diginit(hw);
+	rtl_dm_diginit(hw, cur_igvalue);
 	rtl8821ae_dm_init_rate_adaptive_mask(hw);
 	rtl8821ae_dm_init_edca_turbo(hw);
 	rtl8821ae_dm_initialize_txpower_tracking_thermalmeter(hw);
@@ -822,7 +796,7 @@
 	struct dig_t *dm_digtable = &rtlpriv->dm_digtable;
 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
 	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
-	u8 dig_dynamic_min;
+	u8 dig_min_0;
 	u8 dig_max_of_min;
 	bool first_connect, first_disconnect;
 	u8 dm_dig_max, dm_dig_min, offset;
@@ -837,7 +811,7 @@
 	}
 
 	/*add by Neil Chen to avoid PSD is processing*/
-	dig_dynamic_min = dm_digtable->dig_dynamic_min;
+	dig_min_0 = dm_digtable->dig_min_0;
 	first_connect = (mac->link_state >= MAC80211_LINKED) &&
 			(!dm_digtable->media_connect_0);
 	first_disconnect = (mac->link_state < MAC80211_LINKED) &&
@@ -876,30 +850,30 @@
 			offset = 0;
 
 			if (dm_digtable->rssi_val_min - offset < dm_dig_min)
-				dig_dynamic_min = dm_dig_min;
+				dig_min_0 = dm_dig_min;
 			else if (dm_digtable->rssi_val_min -
 				offset > dig_max_of_min)
-				dig_dynamic_min = dig_max_of_min;
+				dig_min_0 = dig_max_of_min;
 			else
-				dig_dynamic_min =
+				dig_min_0 =
 					dm_digtable->rssi_val_min - offset;
 
 			RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
-				 "bOneEntryOnly=TRUE, dig_dynamic_min=0x%x\n",
-				 dig_dynamic_min);
+				 "bOneEntryOnly=TRUE, dig_min_0=0x%x\n",
+				 dig_min_0);
 		} else {
-			dig_dynamic_min = dm_dig_min;
+			dig_min_0 = dm_dig_min;
 		}
 	} else {
 		dm_digtable->rx_gain_max = dm_dig_max;
-		dig_dynamic_min = dm_dig_min;
+		dig_min_0 = dm_dig_min;
 		RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
 			 "No Link\n");
 	}
 
 	if (rtlpriv->falsealm_cnt.cnt_all > 10000) {
 		RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
-			 "Abnornally false alarm case.\n");
+			 "Abnormally false alarm case.\n");
 
 		if (dm_digtable->large_fa_hit != 3)
 			dm_digtable->large_fa_hit++;
@@ -925,11 +899,11 @@
 		} else {
 			if (dm_digtable->large_fa_hit < 3) {
 				if ((dm_digtable->forbidden_igi - 1) <
-				    dig_dynamic_min) {
+				    dig_min_0) {
 					dm_digtable->forbidden_igi =
-						dig_dynamic_min;
+						dig_min_0;
 					dm_digtable->rx_gain_min =
-						dig_dynamic_min;
+						dig_min_0;
 					RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD,
 						 "Normal Case: At Lower Bound\n");
 				} else {
@@ -1024,7 +998,7 @@
 	rtl8821ae_dm_write_dig(hw, current_igi);
 	dm_digtable->media_connect_0 =
 		((mac->link_state >= MAC80211_LINKED) ? true : false);
-	dm_digtable->dig_dynamic_min = dig_dynamic_min;
+	dm_digtable->dig_min_0 = dig_min_0;
 }
 
 static void rtl8821ae_dm_common_info_self_update(struct ieee80211_hw *hw)
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h
index 9dd40dd..625a6bb 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h
@@ -187,28 +187,12 @@
 #define BW_AUTO_SWITCH_HIGH_LOW		25
 #define BW_AUTO_SWITCH_LOW_HIGH		30
 
-#define DM_DIG_THRESH_HIGH			40
-#define DM_DIG_THRESH_LOW			35
-
-#define DM_FALSEALARM_THRESH_LOW	400
-#define DM_FALSEALARM_THRESH_HIGH	1000
-
-#define DM_DIG_MAX					0x3e
-#define DM_DIG_MIN					0x1e
-
-#define DM_DIG_MAX_AP				0x32
-#define DM_DIG_MIN_AP				0x20
-
 #define DM_DIG_FA_UPPER				0x3e
 #define DM_DIG_FA_LOWER				0x1e
 #define DM_DIG_FA_TH0				200
 #define DM_DIG_FA_TH1				0x300
 #define DM_DIG_FA_TH2				0x400
 
-#define DM_DIG_BACKOFF_MAX			12
-#define DM_DIG_BACKOFF_MIN			-4
-#define DM_DIG_BACKOFF_DEFAULT		10
-
 #define RXPATHSELECTION_SS_TH_LOW	30
 #define RXPATHSELECTION_DIFF_TH		18
 
@@ -262,14 +246,6 @@
 	DIG_OP_TYPE_MAX
 };
 
-enum tag_cck_packet_detection_threshold_type_definition {
-	CCK_PD_STAGE_LOWRSSI = 0,
-	CCK_PD_STAGE_HIGHRSSI = 1,
-	CCK_FA_STAGE_LOW = 2,
-	CCK_FA_STAGE_HIGH = 3,
-	CCK_PD_STAGE_MAX = 4,
-};
-
 enum dm_1r_cca_e {
 	CCA_1R = 0,
 	CCA_2R = 1,
@@ -288,23 +264,6 @@
 	ANS_ANTENNA_MAX = 3,
 };
 
-enum dm_dig_ext_port_alg_e {
-	DIG_EXT_PORT_STAGE_0 = 0,
-	DIG_EXT_PORT_STAGE_1 = 1,
-	DIG_EXT_PORT_STAGE_2 = 2,
-	DIG_EXT_PORT_STAGE_3 = 3,
-	DIG_EXT_PORT_STAGE_MAX = 4,
-};
-
-enum dm_dig_connect_e {
-	DIG_STA_DISCONNECT = 0,
-	DIG_STA_CONNECT = 1,
-	DIG_STA_BEFORE_CONNECT = 2,
-	DIG_MULTISTA_DISCONNECT = 3,
-	DIG_MULTISTA_CONNECT = 4,
-	DIG_CONNECT_MAX
-};
-
 enum pwr_track_control_method {
 	BBSWING,
 	TXAGC,
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c
index 8ec8200..8704eee 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c
@@ -423,7 +423,7 @@
 		*((u16 *)(val+4)) = rtl_read_word(rtlpriv, REG_BSSID+4);
 		break;
 	case HW_VAR_MEDIA_STATUS:
-		val[0] = rtl_read_byte(rtlpriv, REG_CR+2) & 0x3;
+		val[0] = rtl_read_byte(rtlpriv, MSR) & 0x3;
 		break;
 	case HW_VAR_SLOT_TIME:
 		*((u8 *)(val)) = mac->slot_time;
@@ -667,7 +667,7 @@
 				acm_ctrl &= (~ACMHW_VIQEN);
 				break;
 			case AC3_VO:
-				acm_ctrl &= (~ACMHW_BEQEN);
+				acm_ctrl &= (~ACMHW_VOQEN);
 				break;
 			default:
 				RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
@@ -1515,7 +1515,7 @@
 				      (u8 *)(&support_remote_wakeup));
 
 	RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
-		 "boundary=0x%#X, NPQ_RQPNValue=0x%#X, RQPNValue=0x%#X\n",
+		 "boundary=%#X, NPQ_RQPNValue=%#X, RQPNValue=%#X\n",
 		  boundary, npq_rqpn_value, rqpn_val);
 
 	/* stop PCIe DMA
@@ -2178,7 +2178,7 @@
 		return 1;
 	}
 
-	rtl_write_byte(rtlpriv, (MSR), bt_msr);
+	rtl_write_byte(rtlpriv, MSR, bt_msr);
 	rtlpriv->cfg->ops->led_control(hw, ledaction);
 	if ((bt_msr & 0xfc) == MSR_AP)
 		rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h b/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h
index bf0b0ce..36b3e91 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h
@@ -93,9 +93,9 @@
 
 #define RTL8812_TRANS_CARDEMU_TO_SUS					\
 	{0x0042, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK,\
-	PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xcc}, \
+	PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xc0}, \
 	{0x0042, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK,\
-	PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xEC}, \
+	PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xE0}, \
 	{0x0043, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,\
 	PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x07 \
 	/* gpio11 input mode, gpio10~8 output mode */},	\
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/rf.h b/drivers/net/wireless/rtlwifi/rtl8821ae/rf.h
index d9582ee..efd22bd 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/rf.h
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/rf.h
@@ -27,7 +27,6 @@
 #define __RTL8821AE_RF_H__
 
 #define RF6052_MAX_TX_PWR		0x3F
-#define RF6052_MAX_REG			0x3F
 
 void rtl8821ae_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
 					u8 bandwidth);
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c
index fc92dd6..a498812 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c
@@ -85,52 +85,6 @@
 	rtlpci->const_support_pciaspm = 1;
 }
 
-static void load_wowlan_fw(struct rtl_priv *rtlpriv)
-{
-	/* callback routine to load wowlan firmware after main fw has
-	 * been loaded
-	 */
-	const struct firmware *wowlan_firmware;
-	char *fw_name = NULL;
-	int err;
-
-	/* for wowlan firmware buf */
-	rtlpriv->rtlhal.wowlan_firmware = vzalloc(0x8000);
-	if (!rtlpriv->rtlhal.wowlan_firmware) {
-		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
-			 "Can't alloc buffer for wowlan fw.\n");
-		return;
-	}
-
-	if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8821AE)
-		fw_name = "rtlwifi/rtl8821aefw_wowlan.bin";
-	else
-		fw_name = "rtlwifi/rtl8812aefw_wowlan.bin";
-	err = request_firmware(&wowlan_firmware, fw_name, rtlpriv->io.dev);
-	if (err) {
-		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
-			 "Failed to request wowlan firmware!\n");
-		goto error;
-	}
-
-	if (wowlan_firmware->size > 0x8000) {
-		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
-			 "Wowlan Firmware is too big!\n");
-		goto error;
-	}
-
-	memcpy(rtlpriv->rtlhal.wowlan_firmware, wowlan_firmware->data,
-	       wowlan_firmware->size);
-	rtlpriv->rtlhal.wowlan_fwsize = wowlan_firmware->size;
-	release_firmware(wowlan_firmware);
-
-	RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "WOWLAN FirmwareDownload OK\n");
-	return;
-error:
-	release_firmware(wowlan_firmware);
-	vfree(rtlpriv->rtlhal.wowlan_firmware);
-}
-
 /*InitializeVariables8812E*/
 int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw)
 {
@@ -231,7 +185,6 @@
 	else if (rtlpriv->psc.reg_fwctrl_lps == 3)
 		rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE;
 
-	rtlpriv->rtl_fw_second_cb = load_wowlan_fw;
 	/* for firmware buf */
 	rtlpriv->rtlhal.pfirmware = vzalloc(0x8000);
 	if (!rtlpriv->rtlhal.pfirmware) {
@@ -239,20 +192,41 @@
 			 "Can't alloc buffer for fw.\n");
 		return 1;
 	}
+	rtlpriv->rtlhal.wowlan_firmware = vzalloc(0x8000);
+	if (!rtlpriv->rtlhal.wowlan_firmware) {
+		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+			 "Can't alloc buffer for wowlan fw.\n");
+		return 1;
+	}
 
-	if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)
+	if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
 		rtlpriv->cfg->fw_name = "rtlwifi/rtl8812aefw.bin";
-	else
+		rtlpriv->cfg->wowlan_fw_name = "rtlwifi/rtl8812aefw_wowlan.bin";
+	} else {
 		rtlpriv->cfg->fw_name = "rtlwifi/rtl8821aefw.bin";
+		rtlpriv->cfg->wowlan_fw_name = "rtlwifi/rtl8821aefw_wowlan.bin";
+	}
 
 	rtlpriv->max_fw_size = 0x8000;
+	/*load normal firmware*/
 	pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
 	err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
 				      rtlpriv->io.dev, GFP_KERNEL, hw,
 				      rtl_fw_cb);
 	if (err) {
 		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
-			 "Failed to request firmware!\n");
+			 "Failed to request normal firmware!\n");
+		return 1;
+	}
+	/*load wowlan firmware*/
+	pr_info("Using firmware %s\n", rtlpriv->cfg->wowlan_fw_name);
+	err = request_firmware_nowait(THIS_MODULE, 1,
+				      rtlpriv->cfg->wowlan_fw_name,
+				      rtlpriv->io.dev, GFP_KERNEL, hw,
+				      rtl_wowlan_fw_cb);
+	if (err) {
+		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+			 "Failed to request wowlan firmware!\n");
 		return 1;
 	}
 	return 0;
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c
index 383b86b..174743a 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c
@@ -48,232 +48,6 @@
 	return skb->priority;
 }
 
-/* mac80211's rate_idx is like this:
- *
- * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ
- *
- * B/G rate:
- * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC_RATE1M-->DESC_RATE54M ==> idx is 0-->11,
- *
- * N rate:
- * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15
- *
- * 5G band:rx_status->band == IEEE80211_BAND_5GHZ
- * A rate:
- * (rx_status->flag & RX_FLAG_HT) = 0,
- * DESC_RATE6M-->DESC_RATE54M ==> idx is 0-->7,
- *
- * N rate:
- * (rx_status->flag & RX_FLAG_HT) = 1,
- * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15
- */
-static int _rtl8821ae_rate_mapping(struct ieee80211_hw *hw,
-				   bool isht, bool isvht, u8 desc_rate)
-{
-	int rate_idx;
-
-	if (!isht) {
-		if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) {
-			switch (desc_rate) {
-			case DESC_RATE1M:
-				rate_idx = 0;
-				break;
-			case DESC_RATE2M:
-				rate_idx = 1;
-				break;
-			case DESC_RATE5_5M:
-				rate_idx = 2;
-				break;
-			case DESC_RATE11M:
-				rate_idx = 3;
-				break;
-			case DESC_RATE6M:
-				rate_idx = 4;
-				break;
-			case DESC_RATE9M:
-				rate_idx = 5;
-				break;
-			case DESC_RATE12M:
-				rate_idx = 6;
-				break;
-			case DESC_RATE18M:
-				rate_idx = 7;
-				break;
-			case DESC_RATE24M:
-				rate_idx = 8;
-				break;
-			case DESC_RATE36M:
-				rate_idx = 9;
-				break;
-			case DESC_RATE48M:
-				rate_idx = 10;
-				break;
-			case DESC_RATE54M:
-				rate_idx = 11;
-				break;
-			default:
-				rate_idx = 0;
-				break;
-			}
-		} else {
-			switch (desc_rate) {
-			case DESC_RATE6M:
-				rate_idx = 0;
-				break;
-			case DESC_RATE9M:
-				rate_idx = 1;
-				break;
-			case DESC_RATE12M:
-				rate_idx = 2;
-				break;
-			case DESC_RATE18M:
-				rate_idx = 3;
-				break;
-			case DESC_RATE24M:
-				rate_idx = 4;
-				break;
-			case DESC_RATE36M:
-				rate_idx = 5;
-				break;
-			case DESC_RATE48M:
-				rate_idx = 6;
-				break;
-			case DESC_RATE54M:
-				rate_idx = 7;
-				break;
-			default:
-				rate_idx = 0;
-				break;
-			}
-		}
-	} else {
-		switch (desc_rate) {
-		case DESC_RATEMCS0:
-			rate_idx = 0;
-			break;
-		case DESC_RATEMCS1:
-			rate_idx = 1;
-			break;
-		case DESC_RATEMCS2:
-			rate_idx = 2;
-			break;
-		case DESC_RATEMCS3:
-			rate_idx = 3;
-			break;
-		case DESC_RATEMCS4:
-			rate_idx = 4;
-			break;
-		case DESC_RATEMCS5:
-			rate_idx = 5;
-			break;
-		case DESC_RATEMCS6:
-			rate_idx = 6;
-			break;
-		case DESC_RATEMCS7:
-			rate_idx = 7;
-			break;
-		case DESC_RATEMCS8:
-			rate_idx = 8;
-			break;
-		case DESC_RATEMCS9:
-			rate_idx = 9;
-			break;
-		case DESC_RATEMCS10:
-			rate_idx = 10;
-			break;
-		case DESC_RATEMCS11:
-			rate_idx = 11;
-			break;
-		case DESC_RATEMCS12:
-			rate_idx = 12;
-			break;
-		case DESC_RATEMCS13:
-			rate_idx = 13;
-			break;
-		case DESC_RATEMCS14:
-			rate_idx = 14;
-			break;
-		case DESC_RATEMCS15:
-			rate_idx = 15;
-			break;
-		default:
-			rate_idx = 0;
-			break;
-		}
-	}
-
-	if (isvht) {
-		switch (desc_rate) {
-		case DESC_RATEVHT1SS_MCS0:
-			rate_idx = 0;
-			break;
-		case DESC_RATEVHT1SS_MCS1:
-			rate_idx = 1;
-			break;
-		case DESC_RATEVHT1SS_MCS2:
-			rate_idx = 2;
-			break;
-		case DESC_RATEVHT1SS_MCS3:
-			rate_idx = 3;
-			break;
-		case DESC_RATEVHT1SS_MCS4:
-			rate_idx = 4;
-			break;
-		case DESC_RATEVHT1SS_MCS5:
-			rate_idx = 5;
-			break;
-		case DESC_RATEVHT1SS_MCS6:
-			rate_idx = 6;
-			break;
-		case DESC_RATEVHT1SS_MCS7:
-			rate_idx = 7;
-			break;
-		case DESC_RATEVHT1SS_MCS8:
-			rate_idx = 8;
-			break;
-		case DESC_RATEVHT1SS_MCS9:
-			rate_idx = 9;
-			break;
-		case DESC_RATEVHT2SS_MCS0:
-			rate_idx = 0;
-			break;
-		case DESC_RATEVHT2SS_MCS1:
-			rate_idx = 1;
-			break;
-		case DESC_RATEVHT2SS_MCS2:
-			rate_idx = 2;
-			break;
-		case DESC_RATEVHT2SS_MCS3:
-			rate_idx = 3;
-			break;
-		case DESC_RATEVHT2SS_MCS4:
-			rate_idx = 4;
-			break;
-		case DESC_RATEVHT2SS_MCS5:
-			rate_idx = 5;
-			break;
-		case DESC_RATEVHT2SS_MCS6:
-			rate_idx = 6;
-			break;
-		case DESC_RATEVHT2SS_MCS7:
-			rate_idx = 7;
-			break;
-		case DESC_RATEVHT2SS_MCS8:
-			rate_idx = 8;
-			break;
-		case DESC_RATEVHT2SS_MCS9:
-			rate_idx = 9;
-			break;
-		default:
-			rate_idx = 0;
-			break;
-		}
-	}
-	return rate_idx;
-}
-
 static u16 odm_cfo(char value)
 {
 	int ret_val;
@@ -290,6 +64,20 @@
 	return ret_val;
 }
 
+static u8 _rtl8821ae_evm_dbm_jaguar(char value)
+{
+	char ret_val = value;
+
+	/* -33dB~0dB to 33dB ~ 0dB*/
+	if (ret_val == -128)
+		ret_val = 127;
+	else if (ret_val < 0)
+		ret_val = 0 - ret_val;
+
+	ret_val  = ret_val >> 1;
+	return ret_val;
+}
+
 static void query_rxphystatus(struct ieee80211_hw *hw,
 			      struct rtl_stats *pstatus, u8 *pdesc,
 			      struct rx_fwinfo_8821ae *p_drvinfo,
@@ -472,7 +260,7 @@
 
 		for (i = 0; i < max_spatial_stream; i++) {
 			evm = rtl_evm_db_to_percentage(p_phystrpt->rxevm[i]);
-			evmdbm = rtl_evm_dbm_jaguar(p_phystrpt->rxevm[i]);
+			evmdbm = _rtl8821ae_evm_dbm_jaguar(p_phystrpt->rxevm[i]);
 
 			if (bpacket_match_bssid) {
 				/* Fill value in RFD, Get the first
@@ -766,9 +554,9 @@
 	 * supported rates or MCS index if HT rates
 	 * are use (RX_FLAG_HT)
 	 */
-	rx_status->rate_idx =
-	  _rtl8821ae_rate_mapping(hw, status->is_ht,
-				  status->is_vht, status->rate);
+	rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht,
+						   status->is_vht,
+						   status->rate);
 
 	rx_status->mactime = status->timestamp_low;
 	if (phystatus) {
diff --git a/drivers/net/wireless/rtlwifi/stats.c b/drivers/net/wireless/rtlwifi/stats.c
index 2d0736a..d8b3069 100644
--- a/drivers/net/wireless/rtlwifi/stats.c
+++ b/drivers/net/wireless/rtlwifi/stats.c
@@ -39,15 +39,8 @@
 
 u8 rtl_evm_db_to_percentage(char value)
 {
-	char ret_val;
-	ret_val = value;
+	char ret_val = clamp(-value, 0, 33) * 3;
 
-	if (ret_val >= 0)
-		ret_val = 0;
-	if (ret_val <= -33)
-		ret_val = -33;
-	ret_val = 0 - ret_val;
-	ret_val *= 3;
 	if (ret_val == 99)
 		ret_val = 100;
 
@@ -55,21 +48,6 @@
 }
 EXPORT_SYMBOL(rtl_evm_db_to_percentage);
 
-u8 rtl_evm_dbm_jaguar(char value)
-{
-	char ret_val = value;
-
-	/* -33dB~0dB to 33dB ~ 0dB*/
-	if (ret_val == -128)
-		ret_val = 127;
-	else if (ret_val < 0)
-		ret_val = 0 - ret_val;
-
-	ret_val  = ret_val >> 1;
-	return ret_val;
-}
-EXPORT_SYMBOL(rtl_evm_dbm_jaguar);
-
 static long rtl_translate_todbm(struct ieee80211_hw *hw,
 			 u8 signal_strength_index)
 {
diff --git a/drivers/net/wireless/rtlwifi/stats.h b/drivers/net/wireless/rtlwifi/stats.h
index aa4eec8..2b57dff 100644
--- a/drivers/net/wireless/rtlwifi/stats.h
+++ b/drivers/net/wireless/rtlwifi/stats.h
@@ -35,7 +35,6 @@
 
 u8 rtl_query_rxpwrpercentage(char antpower);
 u8 rtl_evm_db_to_percentage(char value);
-u8 rtl_evm_dbm_jaguar(char value);
 long rtl_signal_scale_mapping(struct ieee80211_hw *hw, long currsig);
 void rtl_process_phyinfo(struct ieee80211_hw *hw, u8 *buffer,
 			 struct rtl_stats *pstatus);
diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c
index 46ee956..f0188c8 100644
--- a/drivers/net/wireless/rtlwifi/usb.c
+++ b/drivers/net/wireless/rtlwifi/usb.c
@@ -701,12 +701,18 @@
 
 static void _rtl_usb_cleanup_rx(struct ieee80211_hw *hw)
 {
+	struct rtl_priv *rtlpriv = rtl_priv(hw);
 	struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw));
 	struct urb *urb;
 
 	usb_kill_anchored_urbs(&rtlusb->rx_submitted);
 
 	tasklet_kill(&rtlusb->rx_work_tasklet);
+	cancel_work_sync(&rtlpriv->works.lps_change_work);
+
+	flush_workqueue(rtlpriv->works.rtl_wq);
+	destroy_workqueue(rtlpriv->works.rtl_wq);
+
 	skb_queue_purge(&rtlusb->rx_queue);
 
 	while ((urb = usb_get_from_anchor(&rtlusb->rx_cleanup_urbs))) {
@@ -794,8 +800,6 @@
 	struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw));
 	struct ieee80211_tx_info *txinfo;
 
-	SET_USB_STOP(rtlusb);
-
 	/* clean up rx stuff. */
 	_rtl_usb_cleanup_rx(hw);
 
@@ -834,7 +838,6 @@
 	cancel_work_sync(&rtlpriv->works.fill_h2c_cmd);
 	/* Enable software */
 	SET_USB_STOP(rtlusb);
-	rtl_usb_deinit(hw);
 	rtlpriv->cfg->ops->hw_disable(hw);
 }
 
@@ -1147,9 +1150,9 @@
 
 	if (unlikely(!rtlpriv))
 		return;
-
 	/* just in case driver is removed before firmware callback */
 	wait_for_completion(&rtlpriv->firmware_loading_complete);
+	clear_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
 	/*ieee80211_unregister_hw will call ops_stop */
 	if (rtlmac->mac80211_registered == 1) {
 		ieee80211_unregister_hw(hw);
diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h
index 6866dcf..5157291 100644
--- a/drivers/net/wireless/rtlwifi/wifi.h
+++ b/drivers/net/wireless/rtlwifi/wifi.h
@@ -331,10 +331,10 @@
 (IS_HARDWARE_TYPE_8723E(rtlhal) || IS_HARDWARE_TYPE_8723U(rtlhal))
 
 #define RX_HAL_IS_CCK_RATE(rxmcs)			\
-	((rxmcs) == DESC92_RATE1M ||			\
-	 (rxmcs) == DESC92_RATE2M ||			\
-	 (rxmcs) == DESC92_RATE5_5M ||			\
-	 (rxmcs) == DESC92_RATE11M)
+	((rxmcs) == DESC_RATE1M ||			\
+	 (rxmcs) == DESC_RATE2M ||			\
+	 (rxmcs) == DESC_RATE5_5M ||			\
+	 (rxmcs) == DESC_RATE11M)
 
 enum scan_operation_backup_opt {
 	SCAN_OPT_BACKUP = 0,
@@ -579,38 +579,59 @@
 };
 
 enum rtl_desc92_rate {
-	DESC92_RATE1M = 0x00,
-	DESC92_RATE2M = 0x01,
-	DESC92_RATE5_5M = 0x02,
-	DESC92_RATE11M = 0x03,
+	DESC_RATE1M = 0x00,
+	DESC_RATE2M = 0x01,
+	DESC_RATE5_5M = 0x02,
+	DESC_RATE11M = 0x03,
 
-	DESC92_RATE6M = 0x04,
-	DESC92_RATE9M = 0x05,
-	DESC92_RATE12M = 0x06,
-	DESC92_RATE18M = 0x07,
-	DESC92_RATE24M = 0x08,
-	DESC92_RATE36M = 0x09,
-	DESC92_RATE48M = 0x0a,
-	DESC92_RATE54M = 0x0b,
+	DESC_RATE6M = 0x04,
+	DESC_RATE9M = 0x05,
+	DESC_RATE12M = 0x06,
+	DESC_RATE18M = 0x07,
+	DESC_RATE24M = 0x08,
+	DESC_RATE36M = 0x09,
+	DESC_RATE48M = 0x0a,
+	DESC_RATE54M = 0x0b,
 
-	DESC92_RATEMCS0 = 0x0c,
-	DESC92_RATEMCS1 = 0x0d,
-	DESC92_RATEMCS2 = 0x0e,
-	DESC92_RATEMCS3 = 0x0f,
-	DESC92_RATEMCS4 = 0x10,
-	DESC92_RATEMCS5 = 0x11,
-	DESC92_RATEMCS6 = 0x12,
-	DESC92_RATEMCS7 = 0x13,
-	DESC92_RATEMCS8 = 0x14,
-	DESC92_RATEMCS9 = 0x15,
-	DESC92_RATEMCS10 = 0x16,
-	DESC92_RATEMCS11 = 0x17,
-	DESC92_RATEMCS12 = 0x18,
-	DESC92_RATEMCS13 = 0x19,
-	DESC92_RATEMCS14 = 0x1a,
-	DESC92_RATEMCS15 = 0x1b,
-	DESC92_RATEMCS15_SG = 0x1c,
-	DESC92_RATEMCS32 = 0x20,
+	DESC_RATEMCS0 = 0x0c,
+	DESC_RATEMCS1 = 0x0d,
+	DESC_RATEMCS2 = 0x0e,
+	DESC_RATEMCS3 = 0x0f,
+	DESC_RATEMCS4 = 0x10,
+	DESC_RATEMCS5 = 0x11,
+	DESC_RATEMCS6 = 0x12,
+	DESC_RATEMCS7 = 0x13,
+	DESC_RATEMCS8 = 0x14,
+	DESC_RATEMCS9 = 0x15,
+	DESC_RATEMCS10 = 0x16,
+	DESC_RATEMCS11 = 0x17,
+	DESC_RATEMCS12 = 0x18,
+	DESC_RATEMCS13 = 0x19,
+	DESC_RATEMCS14 = 0x1a,
+	DESC_RATEMCS15 = 0x1b,
+	DESC_RATEMCS15_SG = 0x1c,
+	DESC_RATEMCS32 = 0x20,
+
+	DESC_RATEVHT1SS_MCS0 = 0x2c,
+	DESC_RATEVHT1SS_MCS1 = 0x2d,
+	DESC_RATEVHT1SS_MCS2 = 0x2e,
+	DESC_RATEVHT1SS_MCS3 = 0x2f,
+	DESC_RATEVHT1SS_MCS4 = 0x30,
+	DESC_RATEVHT1SS_MCS5 = 0x31,
+	DESC_RATEVHT1SS_MCS6 = 0x32,
+	DESC_RATEVHT1SS_MCS7 = 0x33,
+	DESC_RATEVHT1SS_MCS8 = 0x34,
+	DESC_RATEVHT1SS_MCS9 = 0x35,
+	DESC_RATEVHT2SS_MCS0 = 0x36,
+	DESC_RATEVHT2SS_MCS1 = 0x37,
+	DESC_RATEVHT2SS_MCS2 = 0x38,
+	DESC_RATEVHT2SS_MCS3 = 0x39,
+	DESC_RATEVHT2SS_MCS4 = 0x3a,
+	DESC_RATEVHT2SS_MCS5 = 0x3b,
+	DESC_RATEVHT2SS_MCS6 = 0x3c,
+	DESC_RATEVHT2SS_MCS7 = 0x3d,
+	DESC_RATEVHT2SS_MCS8 = 0x3e,
+	DESC_RATEVHT2SS_MCS9 = 0x3f,
 };
 
 enum rtl_var_map {
@@ -2161,6 +2182,7 @@
 	void (*add_wowlan_pattern)(struct ieee80211_hw *hw,
 				   struct rtl_wow_pattern *rtl_pattern,
 				   u8 index);
+	u16 (*get_available_desc)(struct ieee80211_hw *hw, u8 q_idx);
 };
 
 struct rtl_intf_ops {
@@ -2242,6 +2264,7 @@
 	char *name;
 	char *fw_name;
 	char *alt_fw_name;
+	char *wowlan_fw_name;
 	struct rtl_hal_ops *ops;
 	struct rtl_mod_params *mod_params;
 	struct rtl_hal_usbint_cfg *usb_interface_cfg;
@@ -2390,8 +2413,6 @@
 	u8 pre_ccastate;
 	u8 cur_ccasate;
 	u8 large_fa_hit;
-	u8 dig_dynamic_min;
-	u8 dig_dynamic_min_1;
 	u8 forbidden_igi;
 	u8 dig_state;
 	u8 dig_highpwrstate;
@@ -2518,8 +2539,6 @@
 
 struct rtl_priv {
 	struct ieee80211_hw *hw;
-	/* Used to load a second firmware */
-	void (*rtl_fw_second_cb)(struct rtl_priv *rtlpriv);
 	struct completion firmware_loading_complete;
 	struct list_head list;
 	struct rtl_priv *buddy_priv;
diff --git a/drivers/net/wireless/ti/wilink_platform_data.c b/drivers/net/wireless/ti/wilink_platform_data.c
index a92bd3e..ea0e359 100644
--- a/drivers/net/wireless/ti/wilink_platform_data.c
+++ b/drivers/net/wireless/ti/wilink_platform_data.c
@@ -23,31 +23,6 @@
 #include <linux/err.h>
 #include <linux/wl12xx.h>
 
-static struct wl12xx_platform_data *wl12xx_platform_data;
-
-int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
-{
-	if (wl12xx_platform_data)
-		return -EBUSY;
-	if (!data)
-		return -EINVAL;
-
-	wl12xx_platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL);
-	if (!wl12xx_platform_data)
-		return -ENOMEM;
-
-	return 0;
-}
-
-struct wl12xx_platform_data *wl12xx_get_platform_data(void)
-{
-	if (!wl12xx_platform_data)
-		return ERR_PTR(-ENODEV);
-
-	return wl12xx_platform_data;
-}
-EXPORT_SYMBOL(wl12xx_get_platform_data);
-
 static struct wl1251_platform_data *wl1251_platform_data;
 
 int __init wl1251_set_platform_data(const struct wl1251_platform_data *data)
diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c
index 0b30a7b..5d54d16 100644
--- a/drivers/net/wireless/ti/wl1251/main.c
+++ b/drivers/net/wireless/ti/wl1251/main.c
@@ -468,7 +468,7 @@
 	wl1251_tx_flush(wl);
 	wl1251_power_off(wl);
 
-	memset(wl->bssid, 0, ETH_ALEN);
+	eth_zero_addr(wl->bssid);
 	wl->listen_int = 1;
 	wl->bss_type = MAX_BSS_TYPE;
 
@@ -500,6 +500,7 @@
 	int ret = 0;
 
 	vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+			     IEEE80211_VIF_SUPPORTS_UAPSD |
 			     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
 
 	wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
@@ -546,7 +547,7 @@
 	mutex_lock(&wl->mutex);
 	wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface");
 	wl->vif = NULL;
-	memset(wl->bssid, 0, ETH_ALEN);
+	eth_zero_addr(wl->bssid);
 	mutex_unlock(&wl->mutex);
 }
 
@@ -1480,9 +1481,7 @@
 	/* unit us */
 	/* FIXME: find a proper value */
 
-	wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
-		IEEE80211_HW_SUPPORTS_PS |
-		IEEE80211_HW_SUPPORTS_UAPSD;
+	wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS;
 
 	wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
 					 BIT(NL80211_IFTYPE_ADHOC);
@@ -1609,7 +1608,7 @@
 }
 EXPORT_SYMBOL_GPL(wl1251_free_hw);
 
-MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core");
+MODULE_DESCRIPTION("TI wl1251 Wireless LAN Driver Core");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");
 MODULE_FIRMWARE(WL1251_FW_NAME);
diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
index d6d0d6d..af0fe2e 100644
--- a/drivers/net/wireless/ti/wl12xx/main.c
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -24,8 +24,6 @@
 
 #include <linux/err.h>
 
-#include <linux/wl12xx.h>
-
 #include "../wlcore/wlcore.h"
 #include "../wlcore/debug.h"
 #include "../wlcore/io.h"
@@ -250,6 +248,7 @@
 		.keep_alive_interval         = 55000,
 		.max_listen_interval         = 20,
 		.sta_sleep_auth              = WL1271_PSM_ILLEGAL,
+		.suspend_rx_ba_activity      = 0,
 	},
 	.itrim = {
 		.enable = false,
@@ -1728,6 +1727,9 @@
 	.convert_hwaddr		= wl12xx_convert_hwaddr,
 	.lnk_high_prio		= wl12xx_lnk_high_prio,
 	.lnk_low_prio		= wl12xx_lnk_low_prio,
+	.interrupt_notify	= NULL,
+	.rx_ba_filter		= NULL,
+	.ap_sleep		= NULL,
 };
 
 static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
@@ -1766,11 +1768,44 @@
 	},
 };
 
+static const struct wl12xx_clock wl12xx_refclock_table[] = {
+	{ 19200000,	false,	WL12XX_REFCLOCK_19	},
+	{ 26000000,	false,	WL12XX_REFCLOCK_26	},
+	{ 26000000,	true,	WL12XX_REFCLOCK_26_XTAL	},
+	{ 38400000,	false,	WL12XX_REFCLOCK_38	},
+	{ 38400000,	true,	WL12XX_REFCLOCK_38_XTAL	},
+	{ 52000000,	false,	WL12XX_REFCLOCK_52	},
+	{ 0,		false,	0 }
+};
+
+static const struct wl12xx_clock wl12xx_tcxoclock_table[] = {
+	{ 16368000,	true,	WL12XX_TCXOCLOCK_16_368	},
+	{ 16800000,	true,	WL12XX_TCXOCLOCK_16_8	},
+	{ 19200000,	true,	WL12XX_TCXOCLOCK_19_2	},
+	{ 26000000,	true,	WL12XX_TCXOCLOCK_26	},
+	{ 32736000,	true,	WL12XX_TCXOCLOCK_32_736	},
+	{ 33600000,	true,	WL12XX_TCXOCLOCK_33_6	},
+	{ 38400000,	true,	WL12XX_TCXOCLOCK_38_4	},
+	{ 52000000,	true,	WL12XX_TCXOCLOCK_52	},
+	{ 0,		false,	0 }
+};
+
+static int wl12xx_get_clock_idx(const struct wl12xx_clock *table,
+				u32 freq, bool xtal)
+{
+	int i;
+
+	for (i = 0; table[i].freq != 0; i++)
+		if ((table[i].freq == freq) && (table[i].xtal == xtal))
+			return table[i].hw_idx;
+
+	return -EINVAL;
+}
+
 static int wl12xx_setup(struct wl1271 *wl)
 {
 	struct wl12xx_priv *priv = wl->priv;
 	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev);
-	struct wl12xx_platform_data *pdata = pdev_data->pdata;
 
 	BUILD_BUG_ON(WL12XX_MAX_LINKS > WLCORE_MAX_LINKS);
 	BUILD_BUG_ON(WL12XX_MAX_AP_STATIONS > WL12XX_MAX_LINKS);
@@ -1795,7 +1830,17 @@
 	wl12xx_conf_init(wl);
 
 	if (!fref_param) {
-		priv->ref_clock = pdata->board_ref_clock;
+		priv->ref_clock = wl12xx_get_clock_idx(wl12xx_refclock_table,
+						pdev_data->ref_clock_freq,
+						pdev_data->ref_clock_xtal);
+		if (priv->ref_clock < 0) {
+			wl1271_error("Invalid ref_clock frequency (%d Hz, %s)",
+				     pdev_data->ref_clock_freq,
+				     pdev_data->ref_clock_xtal ?
+				     "XTAL" : "not XTAL");
+
+			return priv->ref_clock;
+		}
 	} else {
 		if (!strcmp(fref_param, "19.2"))
 			priv->ref_clock = WL12XX_REFCLOCK_19;
@@ -1813,9 +1858,17 @@
 			wl1271_error("Invalid fref parameter %s", fref_param);
 	}
 
-	if (!tcxo_param) {
-		priv->tcxo_clock = pdata->board_tcxo_clock;
-	} else {
+	if (!tcxo_param && pdev_data->tcxo_clock_freq) {
+		priv->tcxo_clock = wl12xx_get_clock_idx(wl12xx_tcxoclock_table,
+						pdev_data->tcxo_clock_freq,
+						true);
+		if (priv->tcxo_clock < 0) {
+			wl1271_error("Invalid tcxo_clock frequency (%d Hz)",
+				     pdev_data->tcxo_clock_freq);
+
+			return priv->tcxo_clock;
+		}
+	} else if (tcxo_param) {
 		if (!strcmp(tcxo_param, "19.2"))
 			priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2;
 		else if (!strcmp(tcxo_param, "26"))
diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h
index 75c9265..5952e99 100644
--- a/drivers/net/wireless/ti/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h
@@ -82,6 +82,34 @@
 	struct wl127x_rx_mem_pool_addr *rx_mem_addr;
 };
 
+/* Reference clock values */
+enum {
+	WL12XX_REFCLOCK_19	= 0, /* 19.2 MHz */
+	WL12XX_REFCLOCK_26	= 1, /* 26 MHz */
+	WL12XX_REFCLOCK_38	= 2, /* 38.4 MHz */
+	WL12XX_REFCLOCK_52	= 3, /* 52 MHz */
+	WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */
+	WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */
+};
+
+/* TCXO clock values */
+enum {
+	WL12XX_TCXOCLOCK_19_2	= 0, /* 19.2MHz */
+	WL12XX_TCXOCLOCK_26	= 1, /* 26 MHz */
+	WL12XX_TCXOCLOCK_38_4	= 2, /* 38.4MHz */
+	WL12XX_TCXOCLOCK_52	= 3, /* 52 MHz */
+	WL12XX_TCXOCLOCK_16_368	= 4, /* 16.368 MHz */
+	WL12XX_TCXOCLOCK_32_736	= 5, /* 32.736 MHz */
+	WL12XX_TCXOCLOCK_16_8	= 6, /* 16.8 MHz */
+	WL12XX_TCXOCLOCK_33_6	= 7, /* 33.6 MHz */
+};
+
+struct wl12xx_clock {
+	u32	freq;
+	bool	xtal;
+	u8	hw_idx;
+};
+
 struct wl12xx_fw_packet_counters {
 	/* Cumulative counter of released packets per AC */
 	u8 tx_released_pkts[NUM_TX_QUEUES];
diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c
index a169bb5..67f2a0e 100644
--- a/drivers/net/wireless/ti/wl18xx/acx.c
+++ b/drivers/net/wireless/ti/wl18xx/acx.c
@@ -24,6 +24,7 @@
 #include "../wlcore/acx.h"
 
 #include "acx.h"
+#include "wl18xx.h"
 
 int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
 				  u32 sdio_blk_size, u32 extra_mem_blks,
@@ -194,3 +195,90 @@
 	kfree(acx);
 	return ret;
 }
+
+/*
+ * When the host is suspended, we don't want to get any fast-link/PSM
+ * notifications
+ */
+int wl18xx_acx_interrupt_notify_config(struct wl1271 *wl,
+				       bool action)
+{
+	struct wl18xx_acx_interrupt_notify *acx;
+	int ret = 0;
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->enable = action;
+	ret = wl1271_cmd_configure(wl, ACX_INTERRUPT_NOTIFY, acx, sizeof(*acx));
+	if (ret < 0) {
+		wl1271_warning("acx interrupt notify setting failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+/*
+ * When the host is suspended, we can configure the FW to disable RX BA
+ * notifications.
+ */
+int wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action)
+{
+	struct wl18xx_acx_rx_ba_filter *acx;
+	int ret = 0;
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->enable = (u32)action;
+	ret = wl1271_cmd_configure(wl, ACX_RX_BA_FILTER, acx, sizeof(*acx));
+	if (ret < 0) {
+		wl1271_warning("acx rx ba activity filter setting failed: %d",
+			       ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int wl18xx_acx_ap_sleep(struct wl1271 *wl)
+{
+	struct wl18xx_priv *priv = wl->priv;
+	struct acx_ap_sleep_cfg *acx;
+	struct conf_ap_sleep_settings *conf = &priv->conf.ap_sleep;
+	int ret;
+
+	wl1271_debug(DEBUG_ACX, "acx config ap sleep");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->idle_duty_cycle = conf->idle_duty_cycle;
+	acx->connected_duty_cycle = conf->connected_duty_cycle;
+	acx->max_stations_thresh = conf->max_stations_thresh;
+	acx->idle_conn_thresh = conf->idle_conn_thresh;
+
+	ret = wl1271_cmd_configure(wl, ACX_AP_SLEEP_CFG, acx, sizeof(*acx));
+	if (ret < 0) {
+		wl1271_warning("acx config ap-sleep failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h
index 0e636de..4afccd4 100644
--- a/drivers/net/wireless/ti/wl18xx/acx.h
+++ b/drivers/net/wireless/ti/wl18xx/acx.h
@@ -32,7 +32,10 @@
 	ACX_SIM_CONFIG			 = 0x0053,
 	ACX_CLEAR_STATISTICS		 = 0x0054,
 	ACX_AUTO_RX_STREAMING		 = 0x0055,
-	ACX_PEER_CAP			 = 0x0056
+	ACX_PEER_CAP			 = 0x0056,
+	ACX_INTERRUPT_NOTIFY		 = 0x0057,
+	ACX_RX_BA_FILTER		 = 0x0058,
+	ACX_AP_SLEEP_CFG                 = 0x0059
 };
 
 /* numbers of bits the length field takes (add 1 for the actual number) */
@@ -326,6 +329,44 @@
 	u8 padding;
 } __packed;
 
+/*
+ * ACX_INTERRUPT_NOTIFY
+ * enable/disable fast-link/PSM notification from FW
+ */
+struct wl18xx_acx_interrupt_notify {
+	struct acx_header header;
+	u32 enable;
+};
+
+/*
+ * ACX_RX_BA_FILTER
+ * enable/disable RX BA filtering in FW
+ */
+struct wl18xx_acx_rx_ba_filter {
+	struct acx_header header;
+	u32 enable;
+};
+
+struct acx_ap_sleep_cfg {
+	struct acx_header header;
+	/* Duty Cycle (20-80% of staying Awake) for IDLE AP
+	 * (0: disable)
+	 */
+	u8 idle_duty_cycle;
+	/* Duty Cycle (20-80% of staying Awake) for Connected AP
+	 * (0: disable)
+	 */
+	u8 connected_duty_cycle;
+	/* Maximum stations that are allowed to be connected to AP
+	 *  (255: no limit)
+	 */
+	u8 max_stations_thresh;
+	/* Timeout till enabling the Sleep Mechanism after data stops
+	 * [unit: 100 msec]
+	 */
+	u8 idle_conn_thresh;
+} __packed;
+
 int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
 				  u32 sdio_blk_size, u32 extra_mem_blks,
 				  u32 len_field_size);
@@ -336,5 +377,8 @@
 			    struct ieee80211_sta_ht_cap *ht_cap,
 			    bool allow_ht_operation,
 			    u32 rate_set, u8 hlid);
+int wl18xx_acx_interrupt_notify_config(struct wl1271 *wl, bool action);
+int wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action);
+int wl18xx_acx_ap_sleep(struct wl1271 *wl);
 
 #endif /* __WL18XX_ACX_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c
index 44f0b20..a8d176d 100644
--- a/drivers/net/wireless/ti/wl18xx/cmd.c
+++ b/drivers/net/wireless/ti/wl18xx/cmd.c
@@ -33,7 +33,8 @@
 	u32 supported_rates;
 	int ret;
 
-	wl1271_debug(DEBUG_ACX, "cmd channel switch");
+	wl1271_debug(DEBUG_ACX, "cmd channel switch (count=%d)",
+		     ch_switch->count);
 
 	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
 	if (!cmd) {
@@ -60,8 +61,12 @@
 		goto out_free;
 	}
 
-	supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
-			  wlcore_hw_sta_get_ap_rate_mask(wl, wlvif);
+	supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES;
+	if (wlvif->bss_type == BSS_TYPE_STA_BSS)
+		supported_rates |= wlcore_hw_sta_get_ap_rate_mask(wl, wlvif);
+	else
+		supported_rates |=
+			wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
 	if (wlvif->p2p)
 		supported_rates &= ~CONF_TX_CCK_RATES;
 	cmd->local_supported_rates = cpu_to_le32(supported_rates);
@@ -167,3 +172,85 @@
 out:
 	return ret;
 }
+
+int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start)
+{
+	struct wlcore_cmd_cac_start *cmd;
+	int ret = 0;
+
+	wl1271_debug(DEBUG_CMD, "cmd cac (channel %d) %s",
+		     wlvif->channel, start ? "start" : "stop");
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	cmd->role_id = wlvif->role_id;
+	cmd->channel = wlvif->channel;
+	if (wlvif->band == IEEE80211_BAND_5GHZ)
+		cmd->band = WLCORE_BAND_5GHZ;
+	cmd->bandwidth = wlcore_get_native_channel_type(wlvif->channel_type);
+
+	ret = wl1271_cmd_send(wl,
+			      start ? CMD_CAC_START : CMD_CAC_STOP,
+			      cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		wl1271_error("failed to send cac command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+	return ret;
+}
+
+int wl18xx_cmd_radar_detection_debug(struct wl1271 *wl, u8 channel)
+{
+	struct wl18xx_cmd_dfs_radar_debug *cmd;
+	int ret = 0;
+
+	wl1271_debug(DEBUG_CMD, "cmd radar detection debug (chan %d)",
+		     channel);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	cmd->channel = channel;
+
+	ret = wl1271_cmd_send(wl, CMD_DFS_RADAR_DETECTION_DEBUG,
+			      cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		wl1271_error("failed to send radar detection debug command");
+		goto out_free;
+	}
+
+out_free:
+	kfree(cmd);
+	return ret;
+}
+
+int wl18xx_cmd_dfs_master_restart(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+	struct wl18xx_cmd_dfs_master_restart *cmd;
+	int ret = 0;
+
+	wl1271_debug(DEBUG_CMD, "cmd dfs master restart (role %d)",
+		     wlvif->role_id);
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	cmd->role_id = wlvif->role_id;
+
+	ret = wl1271_cmd_send(wl, CMD_DFS_MASTER_RESTART,
+			      cmd, sizeof(*cmd), 0);
+	if (ret < 0) {
+		wl1271_error("failed to send dfs master restart command");
+		goto out_free;
+	}
+out_free:
+	kfree(cmd);
+	return ret;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/cmd.h b/drivers/net/wireless/ti/wl18xx/cmd.h
index 92499e2..7f9440a 100644
--- a/drivers/net/wireless/ti/wl18xx/cmd.h
+++ b/drivers/net/wireless/ti/wl18xx/cmd.h
@@ -59,6 +59,30 @@
 	u8 key[16];
 } __packed;
 
+struct wl18xx_cmd_dfs_radar_debug {
+	struct wl1271_cmd_header header;
+
+	u8 channel;
+	u8 padding[3];
+} __packed;
+
+struct wl18xx_cmd_dfs_master_restart {
+	struct wl1271_cmd_header header;
+
+	u8 role_id;
+	u8 padding[3];
+} __packed;
+
+/* cac_start and cac_stop share the same params */
+struct wlcore_cmd_cac_start {
+	struct wl1271_cmd_header header;
+
+	u8 role_id;
+	u8 channel;
+	u8 band;
+	u8 bandwidth;
+} __packed;
+
 int wl18xx_cmd_channel_switch(struct wl1271 *wl,
 			      struct wl12xx_vif *wlvif,
 			      struct ieee80211_channel_switch *ch_switch);
@@ -66,4 +90,7 @@
 int wl18xx_cmd_smart_config_stop(struct wl1271 *wl);
 int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
 					  u8 key_len, u8 *key);
+int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start);
+int wl18xx_cmd_radar_detection_debug(struct wl1271 *wl, u8 channel);
+int wl18xx_cmd_dfs_master_restart(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 #endif
diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h
index e34302e..71f1ec4 100644
--- a/drivers/net/wireless/ti/wl18xx/conf.h
+++ b/drivers/net/wireless/ti/wl18xx/conf.h
@@ -23,7 +23,7 @@
 #define __WL18XX_CONF_H__
 
 #define WL18XX_CONF_MAGIC	0x10e100ca
-#define WL18XX_CONF_VERSION	(WLCORE_CONF_VERSION | 0x0006)
+#define WL18XX_CONF_VERSION	(WLCORE_CONF_VERSION | 0x0007)
 #define WL18XX_CONF_MASK	0x0000ffff
 #define WL18XX_CONF_SIZE	(WLCORE_CONF_SIZE + \
 				 sizeof(struct wl18xx_priv_conf))
@@ -110,12 +110,33 @@
 	u8 mode;
 } __packed;
 
+struct conf_ap_sleep_settings {
+	/* Duty Cycle (20-80% of staying Awake) for IDLE AP
+	 * (0: disable)
+	 */
+	u8 idle_duty_cycle;
+	/* Duty Cycle (20-80% of staying Awake) for Connected AP
+	 * (0: disable)
+	 */
+	u8 connected_duty_cycle;
+	/* Maximum stations that are allowed to be connected to AP
+	 *  (255: no limit)
+	 */
+	u8 max_stations_thresh;
+	/* Timeout till enabling the Sleep Mechanism after data stops
+	 * [unit: 100 msec]
+	 */
+	u8 idle_conn_thresh;
+} __packed;
+
 struct wl18xx_priv_conf {
 	/* Module params structures */
 	struct wl18xx_ht_settings ht;
 
 	/* this structure is copied wholesale to FW */
 	struct wl18xx_mac_and_phy_params phy;
+
+	struct conf_ap_sleep_settings ap_sleep;
 } __packed;
 
 #endif /* __WL18XX_CONF_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c
index 7f1669c..5fbd223 100644
--- a/drivers/net/wireless/ti/wl18xx/debugfs.c
+++ b/drivers/net/wireless/ti/wl18xx/debugfs.c
@@ -22,9 +22,12 @@
 
 #include "../wlcore/debugfs.h"
 #include "../wlcore/wlcore.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/ps.h"
 
 #include "wl18xx.h"
 #include "acx.h"
+#include "cmd.h"
 #include "debugfs.h"
 
 #define WL18XX_DEBUGFS_FWSTATS_FILE(a, b, c) \
@@ -136,7 +139,7 @@
 WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, accum_arp_pend_requests, "%u");
 WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, max_arp_queue_dep, "%u");
 
-WL18XX_DEBUGFS_FWSTATS_FILE(rx_rate, rx_frames_per_rates, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(rx_rate, rx_frames_per_rates, 50);
 
 WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_vs_rate,
 				  AGGR_STATS_TX_AGG*AGGR_STATS_TX_RATE);
@@ -239,6 +242,45 @@
 	.llseek = default_llseek,
 };
 
+static ssize_t radar_detection_write(struct file *file,
+				     const char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct wl1271 *wl = file->private_data;
+	int ret;
+	u8 channel;
+
+	ret = kstrtou8_from_user(user_buf, count, 10, &channel);
+	if (ret < 0) {
+		wl1271_warning("illegal channel");
+		return -EINVAL;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	ret = wl1271_ps_elp_wakeup(wl);
+	if (ret < 0)
+		goto out;
+
+	ret = wl18xx_cmd_radar_detection_debug(wl, channel);
+	if (ret < 0)
+		count = ret;
+
+	wl1271_ps_elp_sleep(wl);
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations radar_detection_ops = {
+	.write = radar_detection_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
 int wl18xx_debugfs_add_files(struct wl1271 *wl,
 			     struct dentry *rootdir)
 {
@@ -390,6 +432,7 @@
 	DEBUGFS_FWSTATS_ADD(mem, fw_gen_free_mem_blks);
 
 	DEBUGFS_ADD(conf, moddir);
+	DEBUGFS_ADD(radar_detection, moddir);
 
 	return 0;
 
diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c
index eb1848e..548bb9e 100644
--- a/drivers/net/wireless/ti/wl18xx/event.c
+++ b/drivers/net/wireless/ti/wl18xx/event.c
@@ -47,6 +47,19 @@
 	return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
 }
 
+static const char *wl18xx_radar_type_decode(u8 radar_type)
+{
+	switch (radar_type) {
+	case RADAR_TYPE_REGULAR:
+		return "REGULAR";
+	case RADAR_TYPE_CHIRP:
+		return "CHIRP";
+	case RADAR_TYPE_NONE:
+	default:
+		return "N/A";
+	}
+}
+
 static int wlcore_smart_config_sync_event(struct wl1271 *wl, u8 sync_channel,
 					  u8 sync_band)
 {
@@ -64,7 +77,7 @@
 	wl1271_debug(DEBUG_EVENT,
 		     "SMART_CONFIG_SYNC_EVENT_ID, freq: %d (chan: %d band %d)",
 		     freq, sync_channel, sync_band);
-	skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, 20,
+	skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, NULL, 20,
 					  WLCORE_VENDOR_EVENT_SC_SYNC,
 					  GFP_KERNEL);
 
@@ -85,7 +98,7 @@
 	wl1271_debug(DEBUG_EVENT, "SMART_CONFIG_DECODE_EVENT_ID");
 	wl1271_dump_ascii(DEBUG_EVENT, "SSID:", ssid, ssid_len);
 
-	skb = cfg80211_vendor_event_alloc(wl->hw->wiphy,
+	skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, NULL,
 					  ssid_len + pwd_len + 20,
 					  WLCORE_VENDOR_EVENT_SC_DECODE,
 					  GFP_KERNEL);
@@ -115,6 +128,14 @@
 			wl18xx_scan_completed(wl, wl->scan_wlvif);
 	}
 
+	if (vector & RADAR_DETECTED_EVENT_ID) {
+		wl1271_info("radar event: channel %d type %s",
+			    mbox->radar_channel,
+			    wl18xx_radar_type_decode(mbox->radar_type));
+
+		ieee80211_radar_detected(wl->hw);
+	}
+
 	if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
 		wl1271_debug(DEBUG_EVENT,
 			     "PERIODIC_SCAN_REPORT_EVENT (results %d)",
diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h
index 0680312..266ee87 100644
--- a/drivers/net/wireless/ti/wl18xx/event.h
+++ b/drivers/net/wireless/ti/wl18xx/event.h
@@ -42,6 +42,12 @@
 	SMART_CONFIG_DECODE_EVENT_ID		 = BIT(23),
 };
 
+enum wl18xx_radar_types {
+	RADAR_TYPE_NONE,
+	RADAR_TYPE_REGULAR,
+	RADAR_TYPE_CHIRP
+};
+
 struct wl18xx_event_mailbox {
 	__le32 events_vector;
 
@@ -83,13 +89,19 @@
 	u8 sc_token_len;
 	u8 padding1;
 	u8 sc_ssid[32];
-	u8 sc_pwd[32];
+	u8 sc_pwd[64];
 	u8 sc_token[32];
 
 	/* smart config sync channel */
 	u8 sc_sync_channel;
 	u8 sc_sync_band;
 	u8 padding2[2];
+
+	/* radar detect */
+	u8 radar_channel;
+	u8 radar_type;
+
+	u8 padding3[2];
 } __packed;
 
 int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 8e56261..717c4f5 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -378,6 +378,7 @@
 		.keep_alive_interval         = 55000,
 		.max_listen_interval         = 20,
 		.sta_sleep_auth              = WL1271_PSM_ILLEGAL,
+		.suspend_rx_ba_activity      = 0,
 	},
 	.itrim = {
 		.enable = false,
@@ -567,6 +568,12 @@
 		.high_power_val_2nd		= 0xff,
 		.tx_rf_margin			= 1,
 	},
+	.ap_sleep = {               /* disabled by default */
+		.idle_duty_cycle        = 0,
+		.connected_duty_cycle   = 0,
+		.max_stations_thresh    = 0,
+		.idle_conn_thresh       = 0,
+	},
 };
 
 static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
@@ -648,7 +655,7 @@
 };
 
 /* TODO: maybe move to a new header file? */
-#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-3.bin"
+#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-4.bin"
 
 static int wl18xx_identify_chip(struct wl1271 *wl)
 {
@@ -983,6 +990,7 @@
 
 	wl->event_mask = BSS_LOSS_EVENT_ID |
 		SCAN_COMPLETE_EVENT_ID |
+		RADAR_DETECTED_EVENT_ID |
 		RSSI_SNR_TRIGGER_0_EVENT_ID |
 		PERIODIC_SCAN_COMPLETE_EVENT_ID |
 		PERIODIC_SCAN_REPORT_EVENT_ID |
@@ -1559,26 +1567,19 @@
 }
 
 static void wl18xx_sta_rc_update(struct wl1271 *wl,
-				 struct wl12xx_vif *wlvif,
-				 struct ieee80211_sta *sta,
-				 u32 changed)
+				 struct wl12xx_vif *wlvif)
 {
-	bool wide = sta->bandwidth >= IEEE80211_STA_RX_BW_40;
+	bool wide = wlvif->rc_update_bw >= IEEE80211_STA_RX_BW_40;
 
 	wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide);
 
-	if (!(changed & IEEE80211_RC_BW_CHANGED))
-		return;
-
-	mutex_lock(&wl->mutex);
-
 	/* sanity */
 	if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
-		goto out;
+		return;
 
 	/* ignore the change before association */
 	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
-		goto out;
+		return;
 
 	/*
 	 * If we started out as wide, we can change the operation mode. If we
@@ -1589,9 +1590,6 @@
 		wl18xx_acx_peer_ht_operation_mode(wl, wlvif->sta.hlid, wide);
 	else
 		ieee80211_connection_loss(wl12xx_wlvif_to_vif(wlvif));
-
-out:
-	mutex_unlock(&wl->mutex);
 }
 
 static int wl18xx_set_peer_cap(struct wl1271 *wl,
@@ -1703,6 +1701,11 @@
 	.smart_config_start = wl18xx_cmd_smart_config_start,
 	.smart_config_stop  = wl18xx_cmd_smart_config_stop,
 	.smart_config_set_group_key = wl18xx_cmd_smart_config_set_group_key,
+	.interrupt_notify = wl18xx_acx_interrupt_notify_config,
+	.rx_ba_filter	= wl18xx_acx_rx_ba_filter,
+	.ap_sleep	= wl18xx_acx_ap_sleep,
+	.set_cac	= wl18xx_cmd_set_cac,
+	.dfs_master_restart	= wl18xx_cmd_dfs_master_restart,
 };
 
 /* HT cap appropriate for wide channels in 2Ghz */
@@ -1796,6 +1799,10 @@
 		.limits = wl18xx_iface_ap_limits,
 		.n_limits = ARRAY_SIZE(wl18xx_iface_ap_limits),
 		.num_different_channels = 1,
+		.radar_detect_widths =	BIT(NL80211_CHAN_NO_HT) |
+					BIT(NL80211_CHAN_HT20) |
+					BIT(NL80211_CHAN_HT40MINUS) |
+					BIT(NL80211_CHAN_HT40PLUS),
 	}
 };
 
diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h
index 6a2b880..71e9e38 100644
--- a/drivers/net/wireless/ti/wl18xx/wl18xx.h
+++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h
@@ -26,10 +26,10 @@
 
 /* minimum FW required for driver */
 #define WL18XX_CHIP_VER		8
-#define WL18XX_IFTYPE_VER	8
+#define WL18XX_IFTYPE_VER	9
 #define WL18XX_MAJOR_VER	WLCORE_FW_VER_IGNORE
 #define WL18XX_SUBTYPE_VER	WLCORE_FW_VER_IGNORE
-#define WL18XX_MINOR_VER	13
+#define WL18XX_MINOR_VER	11
 
 #define WL18XX_CMD_MAX_SIZE          740
 
diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c
index b924cea..f28fa3b 100644
--- a/drivers/net/wireless/ti/wlcore/acx.c
+++ b/drivers/net/wireless/ti/wlcore/acx.c
@@ -1725,7 +1725,7 @@
 	acx->decrease_delta = conf->decrease_delta;
 	acx->quiet_time = conf->quiet_time;
 	acx->increase_time = conf->increase_time;
-	acx->window_size = acx->window_size;
+	acx->window_size = conf->window_size;
 
 	ret = wl1271_cmd_configure(wl, ACX_CONFIG_HANGOVER, acx,
 				   sizeof(*acx));
diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c
index 77752b0..19b7ec7 100644
--- a/drivers/net/wireless/ti/wlcore/boot.c
+++ b/drivers/net/wireless/ti/wlcore/boot.c
@@ -22,7 +22,6 @@
  */
 
 #include <linux/slab.h>
-#include <linux/wl12xx.h>
 #include <linux/export.h>
 
 #include "debug.h"
diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index b826619..68919f8 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -367,7 +367,7 @@
 	wl->links[*hlid].allocated_pkts = 0;
 	wl->links[*hlid].prev_freed_pkts = 0;
 	wl->links[*hlid].ba_bitmap = 0;
-	memset(wl->links[*hlid].addr, 0, ETH_ALEN);
+	eth_zero_addr(wl->links[*hlid].addr);
 
 	/*
 	 * At this point op_tx() will not add more packets to the queues. We
@@ -403,7 +403,7 @@
 	WARN_ON_ONCE(wl->active_link_count < 0);
 }
 
-static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
+u8 wlcore_get_native_channel_type(u8 nl_channel_type)
 {
 	switch (nl_channel_type) {
 	case NL80211_CHAN_NO_HT:
@@ -419,6 +419,7 @@
 		return WLCORE_CHAN_NO_HT;
 	}
 }
+EXPORT_SYMBOL_GPL(wlcore_get_native_channel_type);
 
 static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
 				     struct wl12xx_vif *wlvif,
@@ -1292,7 +1293,7 @@
 	hdr->frame_control = cpu_to_le16(fc);
 	memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN);
 	memcpy(hdr->addr2, vif->addr, ETH_ALEN);
-	memset(hdr->addr3, 0xff, ETH_ALEN);
+	eth_broadcast_addr(hdr->addr3);
 
 	ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_ARP_RSP,
 				      skb->data, skb->len, 0,
@@ -1686,9 +1687,7 @@
 {
 	struct wl12xx_cmd_regdomain_dfs_config *cmd = NULL;
 	int ret = 0, i, b, ch_bit_idx;
-	struct ieee80211_channel *channel;
 	u32 tmp_ch_bitmap[2];
-	u16 ch;
 	struct wiphy *wiphy = wl->hw->wiphy;
 	struct ieee80211_supported_band *band;
 	bool timeout = false;
@@ -1703,12 +1702,16 @@
 	for (b = IEEE80211_BAND_2GHZ; b <= IEEE80211_BAND_5GHZ; b++) {
 		band = wiphy->bands[b];
 		for (i = 0; i < band->n_channels; i++) {
-			channel = &band->channels[i];
-			ch = channel->hw_value;
+			struct ieee80211_channel *channel = &band->channels[i];
+			u16 ch = channel->hw_value;
+			u32 flags = channel->flags;
 
-			if (channel->flags & (IEEE80211_CHAN_DISABLED |
-					      IEEE80211_CHAN_RADAR |
-					      IEEE80211_CHAN_NO_IR))
+			if (flags & (IEEE80211_CHAN_DISABLED |
+				     IEEE80211_CHAN_NO_IR))
+				continue;
+
+			if ((flags & IEEE80211_CHAN_RADAR) &&
+			    channel->dfs_state != NL80211_DFS_AVAILABLE)
 				continue;
 
 			ch_bit_idx = wlcore_get_reg_conf_ch_idx(b, ch);
@@ -1733,6 +1736,7 @@
 
 	cmd->ch_bit_map1 = cpu_to_le32(tmp_ch_bitmap[0]);
 	cmd->ch_bit_map2 = cpu_to_le32(tmp_ch_bitmap[1]);
+	cmd->dfs_region = wl->dfs_region;
 
 	wl1271_debug(DEBUG_CMD,
 		     "cmd reg domain bitmap1: 0x%08x, bitmap2: 0x%08x",
diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h
index 453684a..e14cd40 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.h
+++ b/drivers/net/wireless/ti/wlcore/cmd.h
@@ -105,6 +105,7 @@
 void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid);
 int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
 					 u32 mask, bool *timeout);
+u8 wlcore_get_native_channel_type(u8 nl_channel_type);
 
 enum wl1271_commands {
 	CMD_INTERROGATE	= 1, /* use this to read information elements */
@@ -172,6 +173,11 @@
 	CMD_SMART_CONFIG_STOP		= 62,
 	CMD_SMART_CONFIG_SET_GROUP_KEY	= 63,
 
+	CMD_CAC_START			= 64,
+	CMD_CAC_STOP			= 65,
+	CMD_DFS_MASTER_RESTART		= 66,
+	CMD_DFS_RADAR_DETECTION_DEBUG	= 67,
+
 	MAX_COMMAND_ID = 0xFFFF,
 };
 
@@ -642,6 +648,8 @@
 
 	__le32 ch_bit_map1;
 	__le32 ch_bit_map2;
+	u8 dfs_region;
+	u8 padding[3];
 } __packed;
 
 struct wl12xx_cmd_config_fwlog {
diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h
index 40995c4..166add0 100644
--- a/drivers/net/wireless/ti/wlcore/conf.h
+++ b/drivers/net/wireless/ti/wlcore/conf.h
@@ -997,6 +997,11 @@
 	 * whether we can go to ELP.
 	 */
 	u8 sta_sleep_auth;
+
+	/*
+	 * Default RX BA Activity filter configuration
+	 */
+	u8 suspend_rx_ba_activity;
 } __packed;
 
 enum {
@@ -1347,7 +1352,7 @@
  * version, the two LSB are the lower driver's private conf
  * version.
  */
-#define WLCORE_CONF_VERSION	(0x0005 << 16)
+#define WLCORE_CONF_VERSION	(0x0006 << 16)
 #define WLCORE_CONF_MASK	0xffff0000
 #define WLCORE_CONF_SIZE	(sizeof(struct wlcore_conf_header) +	\
 				 sizeof(struct wlcore_conf))
diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index 0be21f6..eb43f94 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -502,7 +502,7 @@
 	DRIVER_STATE_PRINT_HEX(irq);
 	/* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */
 	DRIVER_STATE_PRINT_HEX(hw_pg_ver);
-	DRIVER_STATE_PRINT_HEX(platform_quirks);
+	DRIVER_STATE_PRINT_HEX(irq_flags);
 	DRIVER_STATE_PRINT_HEX(chip.id);
 	DRIVER_STATE_PRINT_STR(chip.fw_ver_str);
 	DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str);
@@ -929,17 +929,10 @@
 {
 	struct wl1271 *wl = file->private_data;
 	struct wl12xx_vif *wlvif;
-	char buf[10];
-	size_t len;
 	unsigned long value;
 	int ret;
 
-	len = min(count, sizeof(buf) - 1);
-	if (copy_from_user(buf, user_buf, len))
-		return -EFAULT;
-	buf[len] = '\0';
-
-	ret = kstrtoul(buf, 0, &value);
+	ret = kstrtoul_from_user(user_buf, count, 0, &value);
 	if (ret < 0) {
 		wl1271_warning("illegal value for beacon_filtering!");
 		return -EINVAL;
diff --git a/drivers/net/wireless/ti/wlcore/debugfs.h b/drivers/net/wireless/ti/wlcore/debugfs.h
index 0f2cfb0..bf14676 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.h
+++ b/drivers/net/wireless/ti/wlcore/debugfs.h
@@ -26,8 +26,8 @@
 
 #include "wlcore.h"
 
-int wl1271_format_buffer(char __user *userbuf, size_t count,
-			 loff_t *ppos, char *fmt, ...);
+__printf(4, 5) int wl1271_format_buffer(char __user *userbuf, size_t count,
+					loff_t *ppos, char *fmt, ...);
 
 int wl1271_debugfs_init(struct wl1271 *wl);
 void wl1271_debugfs_exit(struct wl1271 *wl);
diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c
index 5153640..c42e789 100644
--- a/drivers/net/wireless/ti/wlcore/event.c
+++ b/drivers/net/wireless/ti/wlcore/event.c
@@ -139,7 +139,7 @@
 	wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d",
 		     __func__, roles_bitmap, success);
 
-	wl12xx_for_each_wlvif_sta(wl, wlvif) {
+	wl12xx_for_each_wlvif(wl, wlvif) {
 		if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
 		    !test_bit(wlvif->role_id , &roles_bitmap))
 			continue;
@@ -150,8 +150,13 @@
 
 		vif = wl12xx_wlvif_to_vif(wlvif);
 
-		ieee80211_chswitch_done(vif, success);
-		cancel_delayed_work(&wlvif->channel_switch_work);
+		if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
+			ieee80211_chswitch_done(vif, success);
+			cancel_delayed_work(&wlvif->channel_switch_work);
+		} else {
+			set_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags);
+			ieee80211_csa_finish(vif);
+		}
 	}
 }
 EXPORT_SYMBOL_GPL(wlcore_event_channel_switch);
diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h
index aa9f82c..eec5693 100644
--- a/drivers/net/wireless/ti/wlcore/hw_ops.h
+++ b/drivers/net/wireless/ti/wlcore/hw_ops.h
@@ -211,11 +211,35 @@
 }
 
 static inline void
-wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-			struct ieee80211_sta *sta, u32 changed)
+wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
 	if (wl->ops->sta_rc_update)
-		wl->ops->sta_rc_update(wl, wlvif, sta, changed);
+		wl->ops->sta_rc_update(wl, wlvif);
+}
+
+static inline int
+wlcore_hw_interrupt_notify(struct wl1271 *wl, bool action)
+{
+	if (wl->ops->interrupt_notify)
+		return wl->ops->interrupt_notify(wl, action);
+	return 0;
+}
+
+static inline int
+wlcore_hw_rx_ba_filter(struct wl1271 *wl, bool action)
+{
+	if (wl->ops->rx_ba_filter)
+		return wl->ops->rx_ba_filter(wl, action);
+	return 0;
+}
+
+static inline int
+wlcore_hw_ap_sleep(struct wl1271 *wl)
+{
+	if (wl->ops->ap_sleep)
+		return wl->ops->ap_sleep(wl);
+
+	return 0;
 }
 
 static inline int
@@ -287,4 +311,22 @@
 
 	return wl->ops->smart_config_set_group_key(wl, group_id, key_len, key);
 }
+
+static inline int
+wlcore_hw_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start)
+{
+	if (!wl->ops->set_cac)
+		return -EINVAL;
+
+	return wl->ops->set_cac(wl, wlvif, start);
+}
+
+static inline int
+wlcore_hw_dfs_master_restart(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+	if (!wl->ops->dfs_master_restart)
+		return -EINVAL;
+
+	return wl->ops->dfs_master_restart(wl, wlvif);
+}
 #endif
diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c
index 199e941..5ca1fb1 100644
--- a/drivers/net/wireless/ti/wlcore/init.c
+++ b/drivers/net/wireless/ti/wlcore/init.c
@@ -392,6 +392,11 @@
 	if (ret < 0)
 		return ret;
 
+	/* configure AP sleep, if enabled */
+	ret = wlcore_hw_ap_sleep(wl);
+	if (ret < 0)
+		return ret;
+
 	return 0;
 }
 
@@ -567,8 +572,7 @@
 	/* consider all existing roles before configuring psm. */
 
 	if (wl->ap_count == 0 && is_ap) { /* first AP */
-		/* Configure for power always on */
-		ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
+		ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
 		if (ret < 0)
 			return ret;
 
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 6ad3fce..0be8079 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -25,8 +25,8 @@
 #include <linux/firmware.h>
 #include <linux/etherdevice.h>
 #include <linux/vmalloc.h>
-#include <linux/wl12xx.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
 
 #include "wlcore.h"
 #include "debug.h"
@@ -79,22 +79,12 @@
 static void wl1271_reg_notify(struct wiphy *wiphy,
 			      struct regulatory_request *request)
 {
-	struct ieee80211_supported_band *band;
-	struct ieee80211_channel *ch;
-	int i;
 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
 	struct wl1271 *wl = hw->priv;
 
-	band = wiphy->bands[IEEE80211_BAND_5GHZ];
-	for (i = 0; i < band->n_channels; i++) {
-		ch = &band->channels[i];
-		if (ch->flags & IEEE80211_CHAN_DISABLED)
-			continue;
-
-		if (ch->flags & IEEE80211_CHAN_RADAR)
-			ch->flags |= IEEE80211_CHAN_NO_IR;
-
-	}
+	/* copy the current dfs region */
+	if (request)
+		wl->dfs_region = request->dfs_region;
 
 	wlcore_regdomain_config(wl);
 }
@@ -226,6 +216,29 @@
 		msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout));
 }
 
+static void wlcore_rc_update_work(struct work_struct *work)
+{
+	int ret;
+	struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
+						rc_update_work);
+	struct wl1271 *wl = wlvif->wl;
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	ret = wl1271_ps_elp_wakeup(wl);
+	if (ret < 0)
+		goto out;
+
+	wlcore_hw_sta_rc_update(wl, wlvif);
+
+	wl1271_ps_elp_sleep(wl);
+out:
+	mutex_unlock(&wl->mutex);
+}
+
 static void wl12xx_tx_watchdog_work(struct work_struct *work)
 {
 	struct delayed_work *dwork;
@@ -525,7 +538,7 @@
 	 * In case edge triggered interrupt must be used, we cannot iterate
 	 * more than once without introducing race conditions with the hardirq.
 	 */
-	if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
+	if (wl->irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
 		loopcount = 1;
 
 	wl1271_debug(DEBUG_IRQ, "IRQ work");
@@ -1662,19 +1675,15 @@
 	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
-		goto out;
-
 	ret = wl1271_configure_wowlan(wl, wow);
 	if (ret < 0)
-		goto out_sleep;
+		goto out;
 
 	if ((wl->conf.conn.suspend_wake_up_event ==
 	     wl->conf.conn.wake_up_event) &&
 	    (wl->conf.conn.suspend_listen_interval ==
 	     wl->conf.conn.listen_interval))
-		goto out_sleep;
+		goto out;
 
 	ret = wl1271_acx_wake_up_conditions(wl, wlvif,
 				    wl->conf.conn.suspend_wake_up_event,
@@ -1682,29 +1691,28 @@
 
 	if (ret < 0)
 		wl1271_error("suspend: set wake up conditions failed: %d", ret);
-
-out_sleep:
-	wl1271_ps_elp_sleep(wl);
 out:
 	return ret;
 
 }
 
 static int wl1271_configure_suspend_ap(struct wl1271 *wl,
-				       struct wl12xx_vif *wlvif)
+					struct wl12xx_vif *wlvif,
+					struct cfg80211_wowlan *wow)
 {
 	int ret = 0;
 
 	if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
+	ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
 	if (ret < 0)
 		goto out;
 
-	ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
+	ret = wl1271_configure_wowlan(wl, wow);
+	if (ret < 0)
+		goto out;
 
-	wl1271_ps_elp_sleep(wl);
 out:
 	return ret;
 
@@ -1717,7 +1725,7 @@
 	if (wlvif->bss_type == BSS_TYPE_STA_BSS)
 		return wl1271_configure_suspend_sta(wl, wlvif, wow);
 	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
-		return wl1271_configure_suspend_ap(wl, wlvif);
+		return wl1271_configure_suspend_ap(wl, wlvif, wow);
 	return 0;
 }
 
@@ -1730,21 +1738,18 @@
 	if ((!is_ap) && (!is_sta))
 		return;
 
-	if (is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+	if ((is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) ||
+	    (is_ap && !test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)))
 		return;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
-		return;
+	wl1271_configure_wowlan(wl, NULL);
 
 	if (is_sta) {
-		wl1271_configure_wowlan(wl, NULL);
-
 		if ((wl->conf.conn.suspend_wake_up_event ==
 		     wl->conf.conn.wake_up_event) &&
 		    (wl->conf.conn.suspend_listen_interval ==
 		     wl->conf.conn.listen_interval))
-			goto out_sleep;
+			return;
 
 		ret = wl1271_acx_wake_up_conditions(wl, wlvif,
 				    wl->conf.conn.wake_up_event,
@@ -1757,9 +1762,6 @@
 	} else if (is_ap) {
 		ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
 	}
-
-out_sleep:
-	wl1271_ps_elp_sleep(wl);
 }
 
 static int wl1271_op_suspend(struct ieee80211_hw *hw,
@@ -1781,6 +1783,13 @@
 	wl1271_tx_flush(wl);
 
 	mutex_lock(&wl->mutex);
+
+	ret = wl1271_ps_elp_wakeup(wl);
+	if (ret < 0) {
+		mutex_unlock(&wl->mutex);
+		return ret;
+	}
+
 	wl->wow_enabled = true;
 	wl12xx_for_each_wlvif(wl, wlvif) {
 		ret = wl1271_configure_suspend(wl, wlvif, wow);
@@ -1790,7 +1799,27 @@
 			return ret;
 		}
 	}
+
+	/* disable fast link flow control notifications from FW */
+	ret = wlcore_hw_interrupt_notify(wl, false);
+	if (ret < 0)
+		goto out_sleep;
+
+	/* if filtering is enabled, configure the FW to drop all RX BA frames */
+	ret = wlcore_hw_rx_ba_filter(wl,
+				     !!wl->conf.conn.suspend_rx_ba_activity);
+	if (ret < 0)
+		goto out_sleep;
+
+out_sleep:
+	wl1271_ps_elp_sleep(wl);
 	mutex_unlock(&wl->mutex);
+
+	if (ret < 0) {
+		wl1271_warning("couldn't prepare device to suspend");
+		return ret;
+	}
+
 	/* flush any remaining work */
 	wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
 
@@ -1864,13 +1893,29 @@
 	if (pending_recovery) {
 		wl1271_warning("queuing forgotten recovery on resume");
 		ieee80211_queue_work(wl->hw, &wl->recovery_work);
-		goto out;
+		goto out_sleep;
 	}
 
+	ret = wl1271_ps_elp_wakeup(wl);
+	if (ret < 0)
+		goto out;
+
 	wl12xx_for_each_wlvif(wl, wlvif) {
 		wl1271_configure_resume(wl, wlvif);
 	}
 
+	ret = wlcore_hw_interrupt_notify(wl, true);
+	if (ret < 0)
+		goto out_sleep;
+
+	/* if filtering is enabled, configure the FW to drop all RX BA frames */
+	ret = wlcore_hw_rx_ba_filter(wl, false);
+	if (ret < 0)
+		goto out_sleep;
+
+out_sleep:
+	wl1271_ps_elp_sleep(wl);
+
 out:
 	wl->wow_enabled = false;
 
@@ -2279,6 +2324,7 @@
 		  wl1271_rx_streaming_enable_work);
 	INIT_WORK(&wlvif->rx_streaming_disable_work,
 		  wl1271_rx_streaming_disable_work);
+	INIT_WORK(&wlvif->rc_update_work, wlcore_rc_update_work);
 	INIT_DELAYED_WORK(&wlvif->channel_switch_work,
 			  wlcore_channel_switch_work);
 	INIT_DELAYED_WORK(&wlvif->connection_loss_work,
@@ -2508,6 +2554,7 @@
 	}
 
 	vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+			     IEEE80211_VIF_SUPPORTS_UAPSD |
 			     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
 
 	wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
@@ -2723,6 +2770,7 @@
 	del_timer_sync(&wlvif->rx_streaming_timer);
 	cancel_work_sync(&wlvif->rx_streaming_enable_work);
 	cancel_work_sync(&wlvif->rx_streaming_disable_work);
+	cancel_work_sync(&wlvif->rc_update_work);
 	cancel_delayed_work_sync(&wlvif->connection_loss_work);
 	cancel_delayed_work_sync(&wlvif->channel_switch_work);
 	cancel_delayed_work_sync(&wlvif->pending_auth_complete_work);
@@ -4072,8 +4120,14 @@
 		ret = wlcore_set_beacon_template(wl, vif, is_ap);
 		if (ret < 0)
 			goto out;
-	}
 
+		if (test_and_clear_bit(WLVIF_FLAG_BEACON_DISABLED,
+				       &wlvif->flags)) {
+			ret = wlcore_hw_dfs_master_restart(wl, wlvif);
+			if (ret < 0)
+				goto out;
+		}
+	}
 out:
 	if (ret != 0)
 		wl1271_error("beacon info change failed: %d", ret);
@@ -4574,10 +4628,46 @@
 				     struct ieee80211_chanctx_conf *ctx,
 				     u32 changed)
 {
+	struct wl1271 *wl = hw->priv;
+	struct wl12xx_vif *wlvif;
+	int ret;
+	int channel = ieee80211_frequency_to_channel(
+		ctx->def.chan->center_freq);
+
 	wl1271_debug(DEBUG_MAC80211,
 		     "mac80211 change chanctx %d (type %d) changed 0x%x",
-		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
-		     cfg80211_get_chandef_type(&ctx->def), changed);
+		     channel, cfg80211_get_chandef_type(&ctx->def), changed);
+
+	mutex_lock(&wl->mutex);
+
+	ret = wl1271_ps_elp_wakeup(wl);
+	if (ret < 0)
+		goto out;
+
+	wl12xx_for_each_wlvif(wl, wlvif) {
+		struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+
+		rcu_read_lock();
+		if (rcu_access_pointer(vif->chanctx_conf) != ctx) {
+			rcu_read_unlock();
+			continue;
+		}
+		rcu_read_unlock();
+
+		/* start radar if needed */
+		if (changed & IEEE80211_CHANCTX_CHANGE_RADAR &&
+		    wlvif->bss_type == BSS_TYPE_AP_BSS &&
+		    ctx->radar_enabled && !wlvif->radar_enabled &&
+		    ctx->def.chan->dfs_state == NL80211_DFS_USABLE) {
+			wl1271_debug(DEBUG_MAC80211, "Start radar detection");
+			wlcore_hw_set_cac(wl, wlvif, true);
+			wlvif->radar_enabled = true;
+		}
+	}
+
+	wl1271_ps_elp_sleep(wl);
+out:
+	mutex_unlock(&wl->mutex);
 }
 
 static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
@@ -4588,13 +4678,26 @@
 	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
 	int channel = ieee80211_frequency_to_channel(
 		ctx->def.chan->center_freq);
+	int ret = -EINVAL;
 
 	wl1271_debug(DEBUG_MAC80211,
-		     "mac80211 assign chanctx (role %d) %d (type %d)",
-		     wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def));
+		     "mac80211 assign chanctx (role %d) %d (type %d) (radar %d dfs_state %d)",
+		     wlvif->role_id, channel,
+		     cfg80211_get_chandef_type(&ctx->def),
+		     ctx->radar_enabled, ctx->def.chan->dfs_state);
 
 	mutex_lock(&wl->mutex);
 
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
+		goto out;
+
+	ret = wl1271_ps_elp_wakeup(wl);
+	if (ret < 0)
+		goto out;
+
 	wlvif->band = ctx->def.chan->band;
 	wlvif->channel = channel;
 	wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def);
@@ -4602,6 +4705,15 @@
 	/* update default rates according to the band */
 	wl1271_set_band_rate(wl, wlvif);
 
+	if (ctx->radar_enabled &&
+	    ctx->def.chan->dfs_state == NL80211_DFS_USABLE) {
+		wl1271_debug(DEBUG_MAC80211, "Start radar detection");
+		wlcore_hw_set_cac(wl, wlvif, true);
+		wlvif->radar_enabled = true;
+	}
+
+	wl1271_ps_elp_sleep(wl);
+out:
 	mutex_unlock(&wl->mutex);
 
 	return 0;
@@ -4613,6 +4725,7 @@
 {
 	struct wl1271 *wl = hw->priv;
 	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+	int ret;
 
 	wl1271_debug(DEBUG_MAC80211,
 		     "mac80211 unassign chanctx (role %d) %d (type %d)",
@@ -4621,6 +4734,99 @@
 		     cfg80211_get_chandef_type(&ctx->def));
 
 	wl1271_tx_flush(wl);
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON))
+		goto out;
+
+	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
+		goto out;
+
+	ret = wl1271_ps_elp_wakeup(wl);
+	if (ret < 0)
+		goto out;
+
+	if (wlvif->radar_enabled) {
+		wl1271_debug(DEBUG_MAC80211, "Stop radar detection");
+		wlcore_hw_set_cac(wl, wlvif, false);
+		wlvif->radar_enabled = false;
+	}
+
+	wl1271_ps_elp_sleep(wl);
+out:
+	mutex_unlock(&wl->mutex);
+}
+
+static int __wlcore_switch_vif_chan(struct wl1271 *wl,
+				    struct wl12xx_vif *wlvif,
+				    struct ieee80211_chanctx_conf *new_ctx)
+{
+	int channel = ieee80211_frequency_to_channel(
+		new_ctx->def.chan->center_freq);
+
+	wl1271_debug(DEBUG_MAC80211,
+		     "switch vif (role %d) %d -> %d chan_type: %d",
+		     wlvif->role_id, wlvif->channel, channel,
+		     cfg80211_get_chandef_type(&new_ctx->def));
+
+	if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS))
+		return 0;
+
+	WARN_ON(!test_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags));
+
+	if (wlvif->radar_enabled) {
+		wl1271_debug(DEBUG_MAC80211, "Stop radar detection");
+		wlcore_hw_set_cac(wl, wlvif, false);
+		wlvif->radar_enabled = false;
+	}
+
+	wlvif->band = new_ctx->def.chan->band;
+	wlvif->channel = channel;
+	wlvif->channel_type = cfg80211_get_chandef_type(&new_ctx->def);
+
+	/* start radar if needed */
+	if (new_ctx->radar_enabled) {
+		wl1271_debug(DEBUG_MAC80211, "Start radar detection");
+		wlcore_hw_set_cac(wl, wlvif, true);
+		wlvif->radar_enabled = true;
+	}
+
+	return 0;
+}
+
+static int
+wlcore_op_switch_vif_chanctx(struct ieee80211_hw *hw,
+			     struct ieee80211_vif_chanctx_switch *vifs,
+			     int n_vifs,
+			     enum ieee80211_chanctx_switch_mode mode)
+{
+	struct wl1271 *wl = hw->priv;
+	int i, ret;
+
+	wl1271_debug(DEBUG_MAC80211,
+		     "mac80211 switch chanctx n_vifs %d mode %d",
+		     n_vifs, mode);
+
+	mutex_lock(&wl->mutex);
+
+	ret = wl1271_ps_elp_wakeup(wl);
+	if (ret < 0)
+		goto out;
+
+	for (i = 0; i < n_vifs; i++) {
+		struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vifs[i].vif);
+
+		ret = __wlcore_switch_vif_chan(wl, wlvif, vifs[i].new_ctx);
+		if (ret)
+			goto out_sleep;
+	}
+out_sleep:
+	wl1271_ps_elp_sleep(wl);
+out:
+	mutex_unlock(&wl->mutex);
+
+	return 0;
 }
 
 static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
@@ -5228,6 +5434,83 @@
 	mutex_unlock(&wl->mutex);
 }
 
+static const void *wlcore_get_beacon_ie(struct wl1271 *wl,
+					struct wl12xx_vif *wlvif,
+					u8 eid)
+{
+	int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+	struct sk_buff *beacon =
+		ieee80211_beacon_get(wl->hw, wl12xx_wlvif_to_vif(wlvif));
+
+	if (!beacon)
+		return NULL;
+
+	return cfg80211_find_ie(eid,
+				beacon->data + ieoffset,
+				beacon->len - ieoffset);
+}
+
+static int wlcore_get_csa_count(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+				u8 *csa_count)
+{
+	const u8 *ie;
+	const struct ieee80211_channel_sw_ie *ie_csa;
+
+	ie = wlcore_get_beacon_ie(wl, wlvif, WLAN_EID_CHANNEL_SWITCH);
+	if (!ie)
+		return -EINVAL;
+
+	ie_csa = (struct ieee80211_channel_sw_ie *)&ie[2];
+	*csa_count = ie_csa->count;
+
+	return 0;
+}
+
+static void wlcore_op_channel_switch_beacon(struct ieee80211_hw *hw,
+					    struct ieee80211_vif *vif,
+					    struct cfg80211_chan_def *chandef)
+{
+	struct wl1271 *wl = hw->priv;
+	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+	struct ieee80211_channel_switch ch_switch = {
+		.block_tx = true,
+		.chandef = *chandef,
+	};
+	int ret;
+
+	wl1271_debug(DEBUG_MAC80211,
+		     "mac80211 channel switch beacon (role %d)",
+		     wlvif->role_id);
+
+	ret = wlcore_get_csa_count(wl, wlvif, &ch_switch.count);
+	if (ret < 0) {
+		wl1271_error("error getting beacon (for CSA counter)");
+		return;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	if (unlikely(wl->state != WLCORE_STATE_ON)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = wl1271_ps_elp_wakeup(wl);
+	if (ret < 0)
+		goto out;
+
+	ret = wl->ops->channel_switch(wl, wlvif, &ch_switch);
+	if (ret)
+		goto out_sleep;
+
+	set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
+
+out_sleep:
+	wl1271_ps_elp_sleep(wl);
+out:
+	mutex_unlock(&wl->mutex);
+}
+
 static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 			    u32 queues, bool drop)
 {
@@ -5370,19 +5653,26 @@
 				    u32 changed)
 {
 	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-	struct wl1271 *wl = hw->priv;
 
-	wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
+	wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update");
+
+	if (!(changed & IEEE80211_RC_BW_CHANGED))
+		return;
+
+	/* this callback is atomic, so schedule a new work */
+	wlvif->rc_update_bw = sta->bandwidth;
+	ieee80211_queue_work(hw, &wlvif->rc_update_work);
 }
 
-static int wlcore_op_get_rssi(struct ieee80211_hw *hw,
-			       struct ieee80211_vif *vif,
-			       struct ieee80211_sta *sta,
-			       s8 *rssi_dbm)
+static void wlcore_op_sta_statistics(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif,
+				     struct ieee80211_sta *sta,
+				     struct station_info *sinfo)
 {
 	struct wl1271 *wl = hw->priv;
 	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-	int ret = 0;
+	s8 rssi_dbm;
+	int ret;
 
 	wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi");
 
@@ -5395,17 +5685,18 @@
 	if (ret < 0)
 		goto out_sleep;
 
-	ret = wlcore_acx_average_rssi(wl, wlvif, rssi_dbm);
+	ret = wlcore_acx_average_rssi(wl, wlvif, &rssi_dbm);
 	if (ret < 0)
 		goto out_sleep;
 
+	sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+	sinfo->signal = rssi_dbm;
+
 out_sleep:
 	wl1271_ps_elp_sleep(wl);
 
 out:
 	mutex_unlock(&wl->mutex);
-
-	return ret;
 }
 
 static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
@@ -5596,6 +5887,7 @@
 	.set_bitrate_mask = wl12xx_set_bitrate_mask,
 	.set_default_unicast_key = wl1271_op_set_default_key_idx,
 	.channel_switch = wl12xx_op_channel_switch,
+	.channel_switch_beacon = wlcore_op_channel_switch_beacon,
 	.flush = wlcore_op_flush,
 	.remain_on_channel = wlcore_op_remain_on_channel,
 	.cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
@@ -5604,8 +5896,9 @@
 	.change_chanctx = wlcore_op_change_chanctx,
 	.assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
 	.unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
+	.switch_vif_chanctx = wlcore_op_switch_vif_chanctx,
 	.sta_rc_update = wlcore_op_sta_rc_update,
-	.get_rssi = wlcore_op_get_rssi,
+	.sta_statistics = wlcore_op_sta_statistics,
 	CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
 };
 
@@ -5776,7 +6069,6 @@
 	wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
 		IEEE80211_HW_SUPPORTS_PS |
 		IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
-		IEEE80211_HW_SUPPORTS_UAPSD |
 		IEEE80211_HW_HAS_RATE_CONTROL |
 		IEEE80211_HW_CONNECTION_MONITOR |
 		IEEE80211_HW_REPORTS_TX_ACK_STATUS |
@@ -5811,7 +6103,8 @@
 
 	wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
 				WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
-				WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+				WIPHY_FLAG_SUPPORTS_SCHED_SCAN |
+				WIPHY_FLAG_HAS_CHANNEL_SWITCH;
 
 	/* make sure all our channels fit in the scanned_ch bitmask */
 	BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
@@ -5956,7 +6249,6 @@
 	wl->ap_ps_map = 0;
 	wl->ap_fw_ps_map = 0;
 	wl->quirks = 0;
-	wl->platform_quirks = 0;
 	wl->system_hlid = WL12XX_SYSTEM_HLID;
 	wl->active_sta_count = 0;
 	wl->active_link_count = 0;
@@ -6097,8 +6389,8 @@
 	struct wl1271 *wl = context;
 	struct platform_device *pdev = wl->pdev;
 	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
-	struct wl12xx_platform_data *pdata = pdev_data->pdata;
-	unsigned long irqflags;
+	struct resource *res;
+
 	int ret;
 	irq_handler_t hardirq_fn = NULL;
 
@@ -6125,19 +6417,23 @@
 	/* adjust some runtime configuration parameters */
 	wlcore_adjust_conf(wl);
 
-	wl->irq = platform_get_irq(pdev, 0);
-	wl->platform_quirks = pdata->platform_quirks;
-	wl->if_ops = pdev_data->if_ops;
-
-	if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) {
-		irqflags = IRQF_TRIGGER_RISING;
-		hardirq_fn = wlcore_hardirq;
-	} else {
-		irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		wl1271_error("Could not get IRQ resource");
+		goto out_free_nvs;
 	}
 
+	wl->irq = res->start;
+	wl->irq_flags = res->flags & IRQF_TRIGGER_MASK;
+	wl->if_ops = pdev_data->if_ops;
+
+	if (wl->irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
+		hardirq_fn = wlcore_hardirq;
+	else
+		wl->irq_flags |= IRQF_ONESHOT;
+
 	ret = request_threaded_irq(wl->irq, hardirq_fn, wlcore_irq,
-				   irqflags, pdev->name, wl);
+				   wl->irq_flags, pdev->name, wl);
 	if (ret < 0) {
 		wl1271_error("request_irq() failed: %d", ret);
 		goto out_free_nvs;
@@ -6148,7 +6444,7 @@
 	if (!ret) {
 		wl->irq_wake_enabled = true;
 		device_init_wakeup(wl->dev, 1);
-		if (pdata->pwr_in_suspend)
+		if (pdev_data->pwr_in_suspend)
 			wl->hw->wiphy->wowlan = &wlcore_wowlan_support;
 	}
 #endif
diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c
index b52516e..4cd316e 100644
--- a/drivers/net/wireless/ti/wlcore/ps.c
+++ b/drivers/net/wireless/ti/wlcore/ps.c
@@ -56,9 +56,6 @@
 		goto out;
 
 	wl12xx_for_each_wlvif(wl, wlvif) {
-		if (wlvif->bss_type == BSS_TYPE_AP_BSS)
-			goto out;
-
 		if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) &&
 		    test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
 			goto out;
@@ -95,9 +92,6 @@
 		return;
 
 	wl12xx_for_each_wlvif(wl, wlvif) {
-		if (wlvif->bss_type == BSS_TYPE_AP_BSS)
-			return;
-
 		if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) &&
 		    test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
 			return;
@@ -108,6 +102,7 @@
 	ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
 				     msecs_to_jiffies(timeout));
 }
+EXPORT_SYMBOL_GPL(wl1271_ps_elp_sleep);
 
 int wl1271_ps_elp_wakeup(struct wl1271 *wl)
 {
@@ -175,6 +170,7 @@
 out:
 	return 0;
 }
+EXPORT_SYMBOL_GPL(wl1271_ps_elp_wakeup);
 
 int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 		       enum wl1271_cmd_ps_mode mode)
diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c
index d3dd7bf..ea7e07a 100644
--- a/drivers/net/wireless/ti/wlcore/sdio.c
+++ b/drivers/net/wireless/ti/wlcore/sdio.c
@@ -31,9 +31,10 @@
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
 #include <linux/gpio.h>
-#include <linux/wl12xx.h>
 #include <linux/pm_runtime.h>
 #include <linux/printk.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
 
 #include "wlcore.h"
 #include "wl12xx_80211.h"
@@ -214,6 +215,52 @@
 	.set_block_size = wl1271_sdio_set_block_size,
 };
 
+#ifdef CONFIG_OF
+static const struct of_device_id wlcore_sdio_of_match_table[] = {
+	{ .compatible = "ti,wl1271" },
+	{ .compatible = "ti,wl1273" },
+	{ .compatible = "ti,wl1281" },
+	{ .compatible = "ti,wl1283" },
+	{ .compatible = "ti,wl1801" },
+	{ .compatible = "ti,wl1805" },
+	{ .compatible = "ti,wl1807" },
+	{ .compatible = "ti,wl1831" },
+	{ .compatible = "ti,wl1835" },
+	{ .compatible = "ti,wl1837" },
+	{ }
+};
+
+static int wlcore_probe_of(struct device *dev, int *irq,
+			   struct wlcore_platdev_data *pdev_data)
+{
+	struct device_node *np = dev->of_node;
+
+	if (!np || !of_match_node(wlcore_sdio_of_match_table, np))
+		return -ENODATA;
+
+	*irq = irq_of_parse_and_map(np, 0);
+	if (!*irq) {
+		dev_err(dev, "No irq in platform data\n");
+		kfree(pdev_data);
+		return -EINVAL;
+	}
+
+	/* optional clock frequency params */
+	of_property_read_u32(np, "ref-clock-frequency",
+			     &pdev_data->ref_clock_freq);
+	of_property_read_u32(np, "tcxo-clock-frequency",
+			     &pdev_data->tcxo_clock_freq);
+
+	return 0;
+}
+#else
+static int wlcore_probe_of(struct device *dev, int *irq,
+			   struct wlcore_platdev_data *pdev_data)
+{
+	return -ENODATA;
+}
+#endif
+
 static int wl1271_probe(struct sdio_func *func,
 				  const struct sdio_device_id *id)
 {
@@ -222,6 +269,7 @@
 	struct resource res[1];
 	mmc_pm_flag_t mmcflags;
 	int ret = -ENOMEM;
+	int irq;
 	const char *chip_family;
 
 	/* We are only able to handle the wlan function */
@@ -245,19 +293,15 @@
 	/* 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);
-		dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
+	if (wlcore_probe_of(&func->dev, &irq, &pdev_data))
 		goto out_free_glue;
-	}
 
 	/* if sdio can keep power while host is suspended, enable wow */
 	mmcflags = sdio_get_host_pm_caps(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.pwr_in_suspend = true;
 
 	sdio_set_drvdata(func, glue);
 
@@ -286,8 +330,9 @@
 
 	memset(res, 0x00, sizeof(res));
 
-	res[0].start = pdev_data.pdata->irq;
-	res[0].flags = IORESOURCE_IRQ;
+	res[0].start = irq;
+	res[0].flags = IORESOURCE_IRQ |
+		       irqd_get_trigger_type(irq_get_irq_data(irq));
 	res[0].name = "irq";
 
 	ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res));
diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
index 69601f6..f1ac283 100644
--- a/drivers/net/wireless/ti/wlcore/spi.c
+++ b/drivers/net/wireless/ti/wlcore/spi.c
@@ -331,11 +331,7 @@
 
 	memset(&pdev_data, 0x00, sizeof(pdev_data));
 
-	pdev_data.pdata = dev_get_platdata(&spi->dev);
-	if (!pdev_data.pdata) {
-		dev_err(&spi->dev, "no platform data\n");
-		return -ENODEV;
-	}
+	/* TODO: add DT parsing when needed */
 
 	pdev_data.if_ops = &spi_ops;
 
diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.c b/drivers/net/wireless/ti/wlcore/vendor_cmd.c
index ad86a48..fd4e9ba 100644
--- a/drivers/net/wireless/ti/wlcore/vendor_cmd.c
+++ b/drivers/net/wireless/ti/wlcore/vendor_cmd.c
@@ -21,7 +21,7 @@
 struct nla_policy wlcore_vendor_attr_policy[NUM_WLCORE_VENDOR_ATTR] = {
 	[WLCORE_VENDOR_ATTR_FREQ]		= { .type = NLA_U32 },
 	[WLCORE_VENDOR_ATTR_GROUP_ID]		= { .type = NLA_U32 },
-	[WLCORE_VENDOR_ATTR_GROUP_KEY]		= { .type = NLA_U32,
+	[WLCORE_VENDOR_ATTR_GROUP_KEY]		= { .type = NLA_BINARY,
 						    .len = WLAN_MAX_KEY_LEN },
 };
 
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index df78cf1..7f363fa 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -106,8 +106,7 @@
 			      struct wl12xx_vif *wlvif,
 			      struct ieee80211_channel_switch *ch_switch);
 	u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len);
-	void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-			      struct ieee80211_sta *sta, u32 changed);
+	void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 	int (*set_peer_cap)(struct wl1271 *wl,
 			    struct ieee80211_sta_ht_cap *ht_cap,
 			    bool allow_ht_operation,
@@ -117,10 +116,16 @@
 			      struct wl1271_link *lnk);
 	bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
 			     struct wl1271_link *lnk);
+	int (*interrupt_notify)(struct wl1271 *wl, bool action);
+	int (*rx_ba_filter)(struct wl1271 *wl, bool action);
+	int (*ap_sleep)(struct wl1271 *wl);
 	int (*smart_config_start)(struct wl1271 *wl, u32 group_bitmap);
 	int (*smart_config_stop)(struct wl1271 *wl);
 	int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id,
 					  u8 key_len, u8 *key);
+	int (*set_cac)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+		       bool start);
+	int (*dfs_master_restart)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 };
 
 enum wlcore_partitions {
@@ -192,6 +197,8 @@
 
 	int irq;
 
+	int irq_flags;
+
 	spinlock_t wl_lock;
 
 	enum wlcore_state state;
@@ -399,9 +406,6 @@
 	/* Quirks of specific hardware revisions */
 	unsigned int quirks;
 
-	/* Platform limitations */
-	unsigned int platform_quirks;
-
 	/* number of currently active RX BA sessions */
 	int ba_rx_session_count;
 
@@ -460,6 +464,9 @@
 	/* HW HT (11n) capabilities */
 	struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS];
 
+	/* the current dfs region */
+	enum nl80211_dfs_regions dfs_region;
+
 	/* size of the private FW status data */
 	size_t fw_status_len;
 	size_t fw_status_priv_len;
diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h
index 0e52556..39efc6d 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h
@@ -201,8 +201,12 @@
 };
 
 struct wlcore_platdev_data {
-	struct wl12xx_platform_data *pdata;
 	struct wl1271_if_operations *if_ops;
+
+	bool ref_clock_xtal;	/* specify whether the clock is XTAL or not */
+	u32 ref_clock_freq;	/* in Hertz */
+	u32 tcxo_clock_freq;	/* in Hertz, tcxo is always XTAL */
+	bool pwr_in_suspend;
 };
 
 #define MAX_NUM_KEYS 14
@@ -251,6 +255,7 @@
 	WLVIF_FLAG_AP_PROBE_RESP_SET,
 	WLVIF_FLAG_IN_USE,
 	WLVIF_FLAG_ACTIVE,
+	WLVIF_FLAG_BEACON_DISABLED,
 };
 
 struct wl12xx_vif;
@@ -434,6 +439,8 @@
 
 	bool wmm_enabled;
 
+	bool radar_enabled;
+
 	/* Rx Streaming */
 	struct work_struct rx_streaming_enable_work;
 	struct work_struct rx_streaming_disable_work;
@@ -463,6 +470,10 @@
 	/* work for canceling ROC after pending auth reply */
 	struct delayed_work pending_auth_complete_work;
 
+	/* update rate conrol */
+	enum ieee80211_sta_rx_bandwidth rc_update_bw;
+	struct work_struct rc_update_work;
+
 	/*
 	 * total freed FW packets on the link.
 	 * For STA this holds the PN of the link to the AP.
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c
new file mode 100644
index 0000000..fdb0ed8
--- /dev/null
+++ b/drivers/net/wireless/zd1201.c
@@ -0,0 +1,1915 @@
+/*
+ *	Driver for ZyDAS zd1201 based wireless USB devices.
+ *
+ *	Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org)
+ *
+ *	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.
+ *
+ *	Parts of this driver have been derived from a wlan-ng version
+ *	modified by ZyDAS. They also made documentation available, thanks!
+ *	Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <net/cfg80211.h>
+#include <net/iw_handler.h>
+#include <linux/string.h>
+#include <linux/if_arp.h>
+#include <linux/firmware.h>
+#include "zd1201.h"
+
+static struct usb_device_id zd1201_table[] = {
+	{USB_DEVICE(0x0586, 0x3400)}, /* Peabird Wireless USB Adapter */
+	{USB_DEVICE(0x0ace, 0x1201)}, /* ZyDAS ZD1201 Wireless USB Adapter */
+	{USB_DEVICE(0x050d, 0x6051)}, /* Belkin F5D6051 usb  adapter */
+	{USB_DEVICE(0x0db0, 0x6823)}, /* MSI UB11B usb  adapter */
+	{USB_DEVICE(0x1044, 0x8004)}, /* Gigabyte GN-WLBZ101 */
+	{USB_DEVICE(0x1044, 0x8005)}, /* GIGABYTE GN-WLBZ201 usb adapter */
+	{}
+};
+
+static int ap;	/* Are we an AP or a normal station? */
+
+#define ZD1201_VERSION	"0.15"
+
+MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
+MODULE_DESCRIPTION("Driver for ZyDAS ZD1201 based USB Wireless adapters");
+MODULE_VERSION(ZD1201_VERSION);
+MODULE_LICENSE("GPL");
+module_param(ap, int, 0);
+MODULE_PARM_DESC(ap, "If non-zero Access Point firmware will be loaded");
+MODULE_DEVICE_TABLE(usb, zd1201_table);
+
+
+static int zd1201_fw_upload(struct usb_device *dev, int apfw)
+{
+	const struct firmware *fw_entry;
+	const char *data;
+	unsigned long len;
+	int err;
+	unsigned char ret;
+	char *buf;
+	char *fwfile;
+
+	if (apfw)
+		fwfile = "zd1201-ap.fw";
+	else
+		fwfile = "zd1201.fw";
+
+	err = request_firmware(&fw_entry, fwfile, &dev->dev);
+	if (err) {
+		dev_err(&dev->dev, "Failed to load %s firmware file!\n", fwfile);
+		dev_err(&dev->dev, "Make sure the hotplug firmware loader is installed.\n");
+		dev_err(&dev->dev, "Goto http://linux-lc100020.sourceforge.net for more info.\n");
+		return err;
+	}
+
+	data = fw_entry->data;
+        len = fw_entry->size;
+
+	buf = kmalloc(1024, GFP_ATOMIC);
+	if (!buf) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	
+	while (len > 0) {
+		int translen = (len > 1024) ? 1024 : len;
+		memcpy(buf, data, translen);
+
+		err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0,
+		    USB_DIR_OUT | 0x40, 0, 0, buf, translen,
+		    ZD1201_FW_TIMEOUT);
+		if (err < 0)
+			goto exit;
+
+		len -= translen;
+		data += translen;
+	}
+                                        
+	err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x2,
+	    USB_DIR_OUT | 0x40, 0, 0, NULL, 0, ZD1201_FW_TIMEOUT);
+	if (err < 0)
+		goto exit;
+
+	err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4,
+	    USB_DIR_IN | 0x40, 0, 0, buf, sizeof(ret), ZD1201_FW_TIMEOUT);
+	if (err < 0)
+		goto exit;
+
+	memcpy(&ret, buf, sizeof(ret));
+
+	if (ret & 0x80) {
+		err = -EIO;
+		goto exit;
+	}
+
+	err = 0;
+exit:
+	kfree(buf);
+	release_firmware(fw_entry);
+	return err;
+}
+
+MODULE_FIRMWARE("zd1201-ap.fw");
+MODULE_FIRMWARE("zd1201.fw");
+
+static void zd1201_usbfree(struct urb *urb)
+{
+	struct zd1201 *zd = urb->context;
+
+	switch(urb->status) {
+		case -EILSEQ:
+		case -ENODEV:
+		case -ETIME:
+		case -ENOENT:
+		case -EPIPE:
+		case -EOVERFLOW:
+		case -ESHUTDOWN:
+			dev_warn(&zd->usb->dev, "%s: urb failed: %d\n", 
+			    zd->dev->name, urb->status);
+	}
+
+	kfree(urb->transfer_buffer);
+	usb_free_urb(urb);
+}
+
+/* cmdreq message: 
+	u32 type
+	u16 cmd
+	u16 parm0
+	u16 parm1
+	u16 parm2
+	u8  pad[4]
+
+	total: 4 + 2 + 2 + 2 + 2 + 4 = 16
+*/
+static int zd1201_docmd(struct zd1201 *zd, int cmd, int parm0,
+			int parm1, int parm2)
+{
+	unsigned char *command;
+	int ret;
+	struct urb *urb;
+
+	command = kmalloc(16, GFP_ATOMIC);
+	if (!command)
+		return -ENOMEM;
+
+	*((__le32*)command) = cpu_to_le32(ZD1201_USB_CMDREQ);
+	*((__le16*)&command[4]) = cpu_to_le16(cmd);
+	*((__le16*)&command[6]) = cpu_to_le16(parm0);
+	*((__le16*)&command[8]) = cpu_to_le16(parm1);
+	*((__le16*)&command[10])= cpu_to_le16(parm2);
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		kfree(command);
+		return -ENOMEM;
+	}
+	usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2),
+			  command, 16, zd1201_usbfree, zd);
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret) {
+		kfree(command);
+		usb_free_urb(urb);
+	}
+
+	return ret;
+}
+
+/* Callback after sending out a packet */
+static void zd1201_usbtx(struct urb *urb)
+{
+	struct zd1201 *zd = urb->context;
+	netif_wake_queue(zd->dev);
+}
+
+/* Incoming data */
+static void zd1201_usbrx(struct urb *urb)
+{
+	struct zd1201 *zd = urb->context;
+	int free = 0;
+	unsigned char *data = urb->transfer_buffer;
+	struct sk_buff *skb;
+	unsigned char type;
+
+	if (!zd)
+		return;
+
+	switch(urb->status) {
+		case -EILSEQ:
+		case -ENODEV:
+		case -ETIME:
+		case -ENOENT:
+		case -EPIPE:
+		case -EOVERFLOW:
+		case -ESHUTDOWN:
+			dev_warn(&zd->usb->dev, "%s: rx urb failed: %d\n",
+			    zd->dev->name, urb->status);
+			free = 1;
+			goto exit;
+	}
+	
+	if (urb->status != 0 || urb->actual_length == 0)
+		goto resubmit;
+
+	type = data[0];
+	if (type == ZD1201_PACKET_EVENTSTAT || type == ZD1201_PACKET_RESOURCE) {
+		memcpy(zd->rxdata, data, urb->actual_length);
+		zd->rxlen = urb->actual_length;
+		zd->rxdatas = 1;
+		wake_up(&zd->rxdataq);
+	}
+	/* Info frame */
+	if (type == ZD1201_PACKET_INQUIRE) {
+		int i = 0;
+		unsigned short infotype, framelen, copylen;
+		framelen = le16_to_cpu(*(__le16*)&data[4]);
+		infotype = le16_to_cpu(*(__le16*)&data[6]);
+
+		if (infotype == ZD1201_INF_LINKSTATUS) {
+			short linkstatus;
+
+			linkstatus = le16_to_cpu(*(__le16*)&data[8]);
+			switch(linkstatus) {
+				case 1:
+					netif_carrier_on(zd->dev);
+					break;
+				case 2:
+					netif_carrier_off(zd->dev);
+					break;
+				case 3:
+					netif_carrier_off(zd->dev);
+					break;
+				case 4:
+					netif_carrier_on(zd->dev);
+					break;
+				default:
+					netif_carrier_off(zd->dev);
+			}
+			goto resubmit;
+		}
+		if (infotype == ZD1201_INF_ASSOCSTATUS) {
+			short status = le16_to_cpu(*(__le16*)(data+8));
+			int event;
+			union iwreq_data wrqu;
+
+			switch (status) {
+				case ZD1201_ASSOCSTATUS_STAASSOC:
+				case ZD1201_ASSOCSTATUS_REASSOC:
+					event = IWEVREGISTERED;
+					break;
+				case ZD1201_ASSOCSTATUS_DISASSOC:
+				case ZD1201_ASSOCSTATUS_ASSOCFAIL:
+				case ZD1201_ASSOCSTATUS_AUTHFAIL:
+				default:
+					event = IWEVEXPIRED;
+			}
+			memcpy(wrqu.addr.sa_data, data+10, ETH_ALEN);
+			wrqu.addr.sa_family = ARPHRD_ETHER;
+
+			/* Send event to user space */
+			wireless_send_event(zd->dev, event, &wrqu, NULL);
+
+			goto resubmit;
+		}
+		if (infotype == ZD1201_INF_AUTHREQ) {
+			union iwreq_data wrqu;
+
+			memcpy(wrqu.addr.sa_data, data+8, ETH_ALEN);
+			wrqu.addr.sa_family = ARPHRD_ETHER;
+			/* There isn't a event that trully fits this request.
+			   We assume that userspace will be smart enough to
+			   see a new station being expired and sends back a
+			   authstation ioctl to authorize it. */
+			wireless_send_event(zd->dev, IWEVEXPIRED, &wrqu, NULL);
+			goto resubmit;
+		}
+		/* Other infotypes are handled outside this handler */
+		zd->rxlen = 0;
+		while (i < urb->actual_length) {
+			copylen = le16_to_cpu(*(__le16*)&data[i+2]);
+			/* Sanity check, sometimes we get junk */
+			if (copylen+zd->rxlen > sizeof(zd->rxdata))
+				break;
+			memcpy(zd->rxdata+zd->rxlen, data+i+4, copylen);
+			zd->rxlen += copylen;
+			i += 64;
+		}
+		if (i >= urb->actual_length) {
+			zd->rxdatas = 1;
+			wake_up(&zd->rxdataq);
+		}
+		goto  resubmit;
+	}
+	/* Actual data */
+	if (data[urb->actual_length-1] == ZD1201_PACKET_RXDATA) {
+		int datalen = urb->actual_length-1;
+		unsigned short len, fc, seq;
+
+		len = ntohs(*(__be16 *)&data[datalen-2]);
+		if (len>datalen)
+			len=datalen;
+		fc = le16_to_cpu(*(__le16 *)&data[datalen-16]);
+		seq = le16_to_cpu(*(__le16 *)&data[datalen-24]);
+
+		if (zd->monitor) {
+			if (datalen < 24)
+				goto resubmit;
+			if (!(skb = dev_alloc_skb(datalen+24)))
+				goto resubmit;
+			
+			memcpy(skb_put(skb, 2), &data[datalen-16], 2);
+			memcpy(skb_put(skb, 2), &data[datalen-2], 2);
+			memcpy(skb_put(skb, 6), &data[datalen-14], 6);
+			memcpy(skb_put(skb, 6), &data[datalen-22], 6);
+			memcpy(skb_put(skb, 6), &data[datalen-8], 6);
+			memcpy(skb_put(skb, 2), &data[datalen-24], 2);
+			memcpy(skb_put(skb, len), data, len);
+			skb->protocol = eth_type_trans(skb, zd->dev);
+			zd->dev->stats.rx_packets++;
+			zd->dev->stats.rx_bytes += skb->len;
+			netif_rx(skb);
+			goto resubmit;
+		}
+			
+		if ((seq & IEEE80211_SCTL_FRAG) ||
+		    (fc & IEEE80211_FCTL_MOREFRAGS)) {
+			struct zd1201_frag *frag = NULL;
+			char *ptr;
+
+			if (datalen<14)
+				goto resubmit;
+			if ((seq & IEEE80211_SCTL_FRAG) == 0) {
+				frag = kmalloc(sizeof(*frag), GFP_ATOMIC);
+				if (!frag)
+					goto resubmit;
+				skb = dev_alloc_skb(IEEE80211_MAX_DATA_LEN +14+2);
+				if (!skb) {
+					kfree(frag);
+					goto resubmit;
+				}
+				frag->skb = skb;
+				frag->seq = seq & IEEE80211_SCTL_SEQ;
+				skb_reserve(skb, 2);
+				memcpy(skb_put(skb, 12), &data[datalen-14], 12);
+				memcpy(skb_put(skb, 2), &data[6], 2);
+				memcpy(skb_put(skb, len), data+8, len);
+				hlist_add_head(&frag->fnode, &zd->fraglist);
+				goto resubmit;
+			}
+			hlist_for_each_entry(frag, &zd->fraglist, fnode)
+				if (frag->seq == (seq&IEEE80211_SCTL_SEQ))
+					break;
+			if (!frag)
+				goto resubmit;
+			skb = frag->skb;
+			ptr = skb_put(skb, len);
+			if (ptr)
+				memcpy(ptr, data+8, len);
+			if (fc & IEEE80211_FCTL_MOREFRAGS)
+				goto resubmit;
+			hlist_del_init(&frag->fnode);
+			kfree(frag);
+		} else {
+			if (datalen<14)
+				goto resubmit;
+			skb = dev_alloc_skb(len + 14 + 2);
+			if (!skb)
+				goto resubmit;
+			skb_reserve(skb, 2);
+			memcpy(skb_put(skb, 12), &data[datalen-14], 12);
+			memcpy(skb_put(skb, 2), &data[6], 2);
+			memcpy(skb_put(skb, len), data+8, len);
+		}
+		skb->protocol = eth_type_trans(skb, zd->dev);
+		zd->dev->stats.rx_packets++;
+		zd->dev->stats.rx_bytes += skb->len;
+		netif_rx(skb);
+	}
+resubmit:
+	memset(data, 0, ZD1201_RXSIZE);
+
+	urb->status = 0;
+	urb->dev = zd->usb;
+	if(usb_submit_urb(urb, GFP_ATOMIC))
+		free = 1;
+
+exit:
+	if (free) {
+		zd->rxlen = 0;
+		zd->rxdatas = 1;
+		wake_up(&zd->rxdataq);
+		kfree(urb->transfer_buffer);
+	}
+}
+
+static int zd1201_getconfig(struct zd1201 *zd, int rid, void *riddata,
+	unsigned int riddatalen)
+{
+	int err;
+	int i = 0;
+	int code;
+	int rid_fid;
+	int length;
+	unsigned char *pdata;
+
+	zd->rxdatas = 0;
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_ACCESS, rid, 0, 0);
+	if (err)
+		return err;
+
+	wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+	if (!zd->rxlen)
+		return -EIO;
+
+	code = le16_to_cpu(*(__le16*)(&zd->rxdata[4]));
+	rid_fid = le16_to_cpu(*(__le16*)(&zd->rxdata[6]));
+	length = le16_to_cpu(*(__le16*)(&zd->rxdata[8]));
+	if (length > zd->rxlen)
+		length = zd->rxlen-6;
+
+	/* If access bit is not on, then error */
+	if ((code & ZD1201_ACCESSBIT) != ZD1201_ACCESSBIT || rid_fid != rid )
+		return -EINVAL;
+
+	/* Not enough buffer for allocating data */
+	if (riddatalen != (length - 4)) {
+		dev_dbg(&zd->usb->dev, "riddatalen mismatches, expected=%u, (packet=%u) length=%u, rid=0x%04X, rid_fid=0x%04X\n",
+		    riddatalen, zd->rxlen, length, rid, rid_fid);
+		return -ENODATA;
+	}
+
+	zd->rxdatas = 0;
+	/* Issue SetRxRid commnd */			
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_SETRXRID, rid, 0, length);
+	if (err)
+		return err;
+
+	/* Receive RID record from resource packets */
+	wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+	if (!zd->rxlen)
+		return -EIO;
+
+	if (zd->rxdata[zd->rxlen - 1] != ZD1201_PACKET_RESOURCE) {
+		dev_dbg(&zd->usb->dev, "Packet type mismatch: 0x%x not 0x3\n",
+		    zd->rxdata[zd->rxlen-1]);
+		return -EINVAL;
+	}
+
+	/* Set the data pointer and received data length */
+	pdata = zd->rxdata;
+	length = zd->rxlen;
+
+	do {
+		int actual_length;
+
+		actual_length = (length > 64) ? 64 : length;
+
+		if (pdata[0] != 0x3) {
+			dev_dbg(&zd->usb->dev, "Rx Resource packet type error: %02X\n",
+			    pdata[0]);
+			return -EINVAL;
+		}
+
+		if (actual_length != 64) {
+			/* Trim the last packet type byte */
+			actual_length--;
+		}
+
+		/* Skip the 4 bytes header (RID length and RID) */
+		if (i == 0) {
+			pdata += 8;
+			actual_length -= 8;
+		} else {
+			pdata += 4;
+			actual_length -= 4;
+		}
+		
+		memcpy(riddata, pdata, actual_length);
+		riddata += actual_length;
+		pdata += actual_length;
+		length -= 64;
+		i++;
+	} while (length > 0);
+
+	return 0;
+}
+
+/*
+ *	resreq:
+ *		byte	type
+ *		byte	sequence
+ *		u16	reserved
+ *		byte	data[12]
+ *	total: 16
+ */
+static int zd1201_setconfig(struct zd1201 *zd, int rid, void *buf, int len, int wait)
+{
+	int err;
+	unsigned char *request;
+	int reqlen;
+	char seq=0;
+	struct urb *urb;
+	gfp_t gfp_mask = wait ? GFP_NOIO : GFP_ATOMIC;
+
+	len += 4;			/* first 4 are for header */
+
+	zd->rxdatas = 0;
+	zd->rxlen = 0;
+	for (seq=0; len > 0; seq++) {
+		request = kmalloc(16, gfp_mask);
+		if (!request)
+			return -ENOMEM;
+		urb = usb_alloc_urb(0, gfp_mask);
+		if (!urb) {
+			kfree(request);
+			return -ENOMEM;
+		}
+		memset(request, 0, 16);
+		reqlen = len>12 ? 12 : len;
+		request[0] = ZD1201_USB_RESREQ;
+		request[1] = seq;
+		request[2] = 0;
+		request[3] = 0;
+		if (request[1] == 0) {
+			/* add header */
+			*(__le16*)&request[4] = cpu_to_le16((len-2+1)/2);
+			*(__le16*)&request[6] = cpu_to_le16(rid);
+			memcpy(request+8, buf, reqlen-4);
+			buf += reqlen-4;
+		} else {
+			memcpy(request+4, buf, reqlen);
+			buf += reqlen;
+		}
+
+		len -= reqlen;
+
+		usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb,
+		    zd->endp_out2), request, 16, zd1201_usbfree, zd);
+		err = usb_submit_urb(urb, gfp_mask);
+		if (err)
+			goto err;
+	}
+
+	request = kmalloc(16, gfp_mask);
+	if (!request)
+		return -ENOMEM;
+	urb = usb_alloc_urb(0, gfp_mask);
+	if (!urb) {
+		kfree(request);
+		return -ENOMEM;
+	}
+	*((__le32*)request) = cpu_to_le32(ZD1201_USB_CMDREQ);
+	*((__le16*)&request[4]) = 
+	    cpu_to_le16(ZD1201_CMDCODE_ACCESS|ZD1201_ACCESSBIT);
+	*((__le16*)&request[6]) = cpu_to_le16(rid);
+	*((__le16*)&request[8]) = cpu_to_le16(0);
+	*((__le16*)&request[10]) = cpu_to_le16(0);
+	usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2),
+	     request, 16, zd1201_usbfree, zd);
+	err = usb_submit_urb(urb, gfp_mask);
+	if (err)
+		goto err;
+	
+	if (wait) {
+		wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+		if (!zd->rxlen || le16_to_cpu(*(__le16*)&zd->rxdata[6]) != rid) {
+			dev_dbg(&zd->usb->dev, "wrong or no RID received\n");
+		}
+	}
+
+	return 0;
+err:
+	kfree(request);
+	usb_free_urb(urb);
+	return err;
+}
+
+static inline int zd1201_getconfig16(struct zd1201 *zd, int rid, short *val)
+{
+	int err;
+	__le16 zdval;
+
+	err = zd1201_getconfig(zd, rid, &zdval, sizeof(__le16));
+	if (err)
+		return err;
+	*val = le16_to_cpu(zdval);
+	return 0;
+}
+
+static inline int zd1201_setconfig16(struct zd1201 *zd, int rid, short val)
+{
+	__le16 zdval = cpu_to_le16(val);
+	return (zd1201_setconfig(zd, rid, &zdval, sizeof(__le16), 1));
+}
+
+static int zd1201_drvr_start(struct zd1201 *zd)
+{
+	int err, i;
+	short max;
+	__le16 zdmax;
+	unsigned char *buffer;
+
+	buffer = kzalloc(ZD1201_RXSIZE, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	usb_fill_bulk_urb(zd->rx_urb, zd->usb, 
+	    usb_rcvbulkpipe(zd->usb, zd->endp_in), buffer, ZD1201_RXSIZE,
+	    zd1201_usbrx, zd);
+
+	err = usb_submit_urb(zd->rx_urb, GFP_KERNEL);
+	if (err)
+		goto err_buffer;
+
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0);
+	if (err)
+		goto err_urb;
+
+	err = zd1201_getconfig(zd, ZD1201_RID_CNFMAXTXBUFFERNUMBER, &zdmax,
+	    sizeof(__le16));
+	if (err)
+		goto err_urb;
+
+	max = le16_to_cpu(zdmax);
+	for (i=0; i<max; i++) {
+		err = zd1201_docmd(zd, ZD1201_CMDCODE_ALLOC, 1514, 0, 0);
+		if (err)
+			goto err_urb;
+	}
+
+	return 0;
+
+err_urb:
+	usb_kill_urb(zd->rx_urb);
+	return err;
+err_buffer:
+	kfree(buffer);
+	return err;
+}
+
+/*	Magic alert: The firmware doesn't seem to like the MAC state being
+ *	toggled in promisc (aka monitor) mode.
+ *	(It works a number of times, but will halt eventually)
+ *	So we turn it of before disabling and on after enabling if needed.
+ */
+static int zd1201_enable(struct zd1201 *zd)
+{
+	int err;
+
+	if (zd->mac_enabled)
+		return 0;
+
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_ENABLE, 0, 0, 0);
+	if (!err)
+		zd->mac_enabled = 1;
+
+	if (zd->monitor)
+		err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 1);
+
+	return err;
+}
+
+static int zd1201_disable(struct zd1201 *zd)
+{
+	int err;
+
+	if (!zd->mac_enabled)
+		return 0;
+	if (zd->monitor) {
+		err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0);
+		if (err)
+			return err;
+	}
+
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_DISABLE, 0, 0, 0);
+	if (!err)
+		zd->mac_enabled = 0;
+	return err;
+}
+
+static int zd1201_mac_reset(struct zd1201 *zd)
+{
+	if (!zd->mac_enabled)
+		return 0;
+	zd1201_disable(zd);
+	return zd1201_enable(zd);
+}
+
+static int zd1201_join(struct zd1201 *zd, char *essid, int essidlen)
+{
+	int err, val;
+	char buf[IW_ESSID_MAX_SIZE+2];
+
+	err = zd1201_disable(zd);
+	if (err)
+		return err;
+
+	val = ZD1201_CNFAUTHENTICATION_OPENSYSTEM;
+	val |= ZD1201_CNFAUTHENTICATION_SHAREDKEY;
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, val);
+	if (err)
+		return err;
+
+	*(__le16 *)buf = cpu_to_le16(essidlen);
+	memcpy(buf+2, essid, essidlen);
+	if (!zd->ap) {	/* Normal station */
+		err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf,
+		    IW_ESSID_MAX_SIZE+2, 1);
+		if (err)
+			return err;
+	} else {	/* AP */
+		err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNSSID, buf,
+		    IW_ESSID_MAX_SIZE+2, 1);
+		if (err)
+			return err;
+	}
+
+	err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR, 
+	    zd->dev->dev_addr, zd->dev->addr_len, 1);
+	if (err)
+		return err;
+
+	err = zd1201_enable(zd);
+	if (err)
+		return err;
+
+	msleep(100);
+	return 0;
+}
+
+static int zd1201_net_open(struct net_device *dev)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+
+	/* Start MAC with wildcard if no essid set */
+	if (!zd->mac_enabled)
+		zd1201_join(zd, zd->essid, zd->essidlen);
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+static int zd1201_net_stop(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+	return 0;
+}
+
+/*
+	RFC 1042 encapsulates Ethernet frames in 802.11 frames
+	by prefixing them with 0xaa, 0xaa, 0x03) followed by a SNAP OID of 0
+	(0x00, 0x00, 0x00). Zd requires an additional padding, copy
+	of ethernet addresses, length of the standard RFC 1042 packet
+	and a command byte (which is nul for tx).
+	
+	tx frame (from Wlan NG):
+	RFC 1042:
+		llc		0xAA 0xAA 0x03 (802.2 LLC)
+		snap		0x00 0x00 0x00 (Ethernet encapsulated)
+		type		2 bytes, Ethernet type field
+		payload		(minus eth header)
+	Zydas specific:
+		padding		1B if (skb->len+8+1)%64==0
+		Eth MAC addr	12 bytes, Ethernet MAC addresses
+		length		2 bytes, RFC 1042 packet length 
+				(llc+snap+type+payload)
+		zd		1 null byte, zd1201 packet type
+ */
+static netdev_tx_t zd1201_hard_start_xmit(struct sk_buff *skb,
+						struct net_device *dev)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	unsigned char *txbuf = zd->txdata;
+	int txbuflen, pad = 0, err;
+	struct urb *urb = zd->tx_urb;
+
+	if (!zd->mac_enabled || zd->monitor) {
+		dev->stats.tx_dropped++;
+		kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+	netif_stop_queue(dev);
+
+	txbuflen = skb->len + 8 + 1;
+	if (txbuflen%64 == 0) {
+		pad = 1;
+		txbuflen++;
+	}
+	txbuf[0] = 0xAA;
+	txbuf[1] = 0xAA;
+	txbuf[2] = 0x03;
+	txbuf[3] = 0x00;	/* rfc1042 */
+	txbuf[4] = 0x00;
+	txbuf[5] = 0x00;
+
+	skb_copy_from_linear_data_offset(skb, 12, txbuf + 6, skb->len - 12);
+	if (pad)
+		txbuf[skb->len-12+6]=0;
+	skb_copy_from_linear_data(skb, txbuf + skb->len - 12 + 6 + pad, 12);
+	*(__be16*)&txbuf[skb->len+6+pad] = htons(skb->len-12+6);
+	txbuf[txbuflen-1] = 0;
+
+	usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out),
+	    txbuf, txbuflen, zd1201_usbtx, zd);
+
+	err = usb_submit_urb(zd->tx_urb, GFP_ATOMIC);
+	if (err) {
+		dev->stats.tx_errors++;
+		netif_start_queue(dev);
+	} else {
+		dev->stats.tx_packets++;
+		dev->stats.tx_bytes += skb->len;
+	}
+	kfree_skb(skb);
+
+	return NETDEV_TX_OK;
+}
+
+static void zd1201_tx_timeout(struct net_device *dev)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+
+	if (!zd)
+		return;
+	dev_warn(&zd->usb->dev, "%s: TX timeout, shooting down urb\n",
+	    dev->name);
+	usb_unlink_urb(zd->tx_urb);
+	dev->stats.tx_errors++;
+	/* Restart the timeout to quiet the watchdog: */
+	dev->trans_start = jiffies; /* prevent tx timeout */
+}
+
+static int zd1201_set_mac_address(struct net_device *dev, void *p)
+{
+	struct sockaddr *addr = p;
+	struct zd1201 *zd = netdev_priv(dev);
+	int err;
+
+	if (!zd)
+		return -ENODEV;
+
+	err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR, 
+	    addr->sa_data, dev->addr_len, 1);
+	if (err)
+		return err;
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+	return zd1201_mac_reset(zd);
+}
+
+static struct iw_statistics *zd1201_get_wireless_stats(struct net_device *dev)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+
+	return &zd->iwstats;
+}
+
+static void zd1201_set_multicast(struct net_device *dev)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	struct netdev_hw_addr *ha;
+	unsigned char reqbuf[ETH_ALEN*ZD1201_MAXMULTI];
+	int i;
+
+	if (netdev_mc_count(dev) > ZD1201_MAXMULTI)
+		return;
+
+	i = 0;
+	netdev_for_each_mc_addr(ha, dev)
+		memcpy(reqbuf + i++ * ETH_ALEN, ha->addr, ETH_ALEN);
+	zd1201_setconfig(zd, ZD1201_RID_CNFGROUPADDRESS, reqbuf,
+			 netdev_mc_count(dev) * ETH_ALEN, 0);
+}
+
+static int zd1201_config_commit(struct net_device *dev, 
+    struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+
+	return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_name(struct net_device *dev,
+    struct iw_request_info *info, char *name, char *extra)
+{
+	strcpy(name, "IEEE 802.11b");
+	return 0;
+}
+
+static int zd1201_set_freq(struct net_device *dev,
+    struct iw_request_info *info, struct iw_freq *freq, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short channel = 0;
+	int err;
+
+	if (freq->e == 0)
+		channel = freq->m;
+	else
+		channel = ieee80211_frequency_to_channel(freq->m);
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, channel);
+	if (err)
+		return err;
+
+	zd1201_mac_reset(zd);
+
+	return 0;
+}
+
+static int zd1201_get_freq(struct net_device *dev,
+    struct iw_request_info *info, struct iw_freq *freq, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short channel;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, &channel);
+	if (err)
+		return err;
+	freq->e = 0;
+	freq->m = channel;
+
+	return 0;
+}
+
+static int zd1201_set_mode(struct net_device *dev,
+    struct iw_request_info *info, __u32 *mode, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short porttype, monitor = 0;
+	unsigned char buffer[IW_ESSID_MAX_SIZE+2];
+	int err;
+
+	if (zd->ap) {
+		if (*mode != IW_MODE_MASTER)
+			return -EINVAL;
+		return 0;
+	}
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0);
+	if (err)
+		return err;
+	zd->dev->type = ARPHRD_ETHER;
+	switch(*mode) {
+		case IW_MODE_MONITOR:
+			monitor = 1;
+			zd->dev->type = ARPHRD_IEEE80211;
+			/* Make sure we are no longer associated with by
+			   setting an 'impossible' essid.
+			   (otherwise we mess up firmware)
+			 */
+			zd1201_join(zd, "\0-*#\0", 5);
+			/* Put port in pIBSS */
+		case 8: /* No pseudo-IBSS in wireless extensions (yet) */
+			porttype = ZD1201_PORTTYPE_PSEUDOIBSS;
+			break;
+		case IW_MODE_ADHOC:
+			porttype = ZD1201_PORTTYPE_IBSS;
+			break;
+		case IW_MODE_INFRA:
+			porttype = ZD1201_PORTTYPE_BSS;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype);
+	if (err)
+		return err;
+	if (zd->monitor && !monitor) {
+			zd1201_disable(zd);
+			*(__le16 *)buffer = cpu_to_le16(zd->essidlen);
+			memcpy(buffer+2, zd->essid, zd->essidlen);
+			err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID,
+			    buffer, IW_ESSID_MAX_SIZE+2, 1);
+			if (err)
+				return err;
+	}
+	zd->monitor = monitor;
+	/* If monitor mode is set we don't actually turn it on here since it
+	 * is done during mac reset anyway (see zd1201_mac_enable).
+	 */
+	zd1201_mac_reset(zd);
+
+	return 0;
+}
+
+static int zd1201_get_mode(struct net_device *dev,
+    struct iw_request_info *info, __u32 *mode, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short porttype;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFPORTTYPE, &porttype);
+	if (err)
+		return err;
+	switch(porttype) {
+		case ZD1201_PORTTYPE_IBSS:
+			*mode = IW_MODE_ADHOC;
+			break;
+		case ZD1201_PORTTYPE_BSS:
+			*mode = IW_MODE_INFRA;
+			break;
+		case ZD1201_PORTTYPE_WDS:
+			*mode = IW_MODE_REPEAT;
+			break;
+		case ZD1201_PORTTYPE_PSEUDOIBSS:
+			*mode = 8;/* No Pseudo-IBSS... */
+			break;
+		case ZD1201_PORTTYPE_AP:
+			*mode = IW_MODE_MASTER;
+			break;
+		default:
+			dev_dbg(&zd->usb->dev, "Unknown porttype: %d\n",
+			    porttype);
+			*mode = IW_MODE_AUTO;
+	}
+	if (zd->monitor)
+		*mode = IW_MODE_MONITOR;
+
+	return 0;
+}
+
+static int zd1201_get_range(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *wrq, char *extra)
+{
+	struct iw_range *range = (struct iw_range *)extra;
+
+	wrq->length = sizeof(struct iw_range);
+	memset(range, 0, sizeof(struct iw_range));
+	range->we_version_compiled = WIRELESS_EXT;
+	range->we_version_source = WIRELESS_EXT;
+
+	range->max_qual.qual = 128;
+	range->max_qual.level = 128;
+	range->max_qual.noise = 128;
+	range->max_qual.updated = 7;
+
+	range->encoding_size[0] = 5;
+	range->encoding_size[1] = 13;
+	range->num_encoding_sizes = 2;
+	range->max_encoding_tokens = ZD1201_NUMKEYS;
+
+	range->num_bitrates = 4;
+	range->bitrate[0] = 1000000;
+	range->bitrate[1] = 2000000;
+	range->bitrate[2] = 5500000;
+	range->bitrate[3] = 11000000;
+
+	range->min_rts = 0;
+	range->min_frag = ZD1201_FRAGMIN;
+	range->max_rts = ZD1201_RTSMAX;
+	range->min_frag = ZD1201_FRAGMAX;
+
+	return 0;
+}
+
+/*	Little bit of magic here: we only get the quality if we poll
+ *	for it, and we never get an actual request to trigger such
+ *	a poll. Therefore we 'assume' that the user will soon ask for
+ *	the stats after asking the bssid.
+ */
+static int zd1201_get_wap(struct net_device *dev,
+    struct iw_request_info *info, struct sockaddr *ap_addr, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	unsigned char buffer[6];
+
+	if (!zd1201_getconfig(zd, ZD1201_RID_COMMSQUALITY, buffer, 6)) {
+		/* Unfortunately the quality and noise reported is useless.
+		   they seem to be accumulators that increase until you
+		   read them, unless we poll on a fixed interval we can't
+		   use them
+		 */
+		/*zd->iwstats.qual.qual = le16_to_cpu(((__le16 *)buffer)[0]);*/
+		zd->iwstats.qual.level = le16_to_cpu(((__le16 *)buffer)[1]);
+		/*zd->iwstats.qual.noise = le16_to_cpu(((__le16 *)buffer)[2]);*/
+		zd->iwstats.qual.updated = 2;
+	}
+
+	return zd1201_getconfig(zd, ZD1201_RID_CURRENTBSSID, ap_addr->sa_data, 6);
+}
+
+static int zd1201_set_scan(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *srq, char *extra)
+{
+	/* We do everything in get_scan */
+	return 0;
+}
+
+static int zd1201_get_scan(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *srq, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	int err, i, j, enabled_save;
+	struct iw_event iwe;
+	char *cev = extra;
+	char *end_buf = extra + IW_SCAN_MAX_DATA;
+
+	/* No scanning in AP mode */
+	if (zd->ap)
+		return -EOPNOTSUPP;
+
+	/* Scan doesn't seem to work if disabled */
+	enabled_save = zd->mac_enabled;
+	zd1201_enable(zd);
+
+	zd->rxdatas = 0;
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_INQUIRE, 
+	     ZD1201_INQ_SCANRESULTS, 0, 0);
+	if (err)
+		return err;
+
+	wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+	if (!zd->rxlen)
+		return -EIO;
+
+	if (le16_to_cpu(*(__le16*)&zd->rxdata[2]) != ZD1201_INQ_SCANRESULTS)
+		return -EIO;
+
+	for(i=8; i<zd->rxlen; i+=62) {
+		iwe.cmd = SIOCGIWAP;
+		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+		memcpy(iwe.u.ap_addr.sa_data, zd->rxdata+i+6, 6);
+		cev = iwe_stream_add_event(info, cev, end_buf,
+					   &iwe, IW_EV_ADDR_LEN);
+
+		iwe.cmd = SIOCGIWESSID;
+		iwe.u.data.length = zd->rxdata[i+16];
+		iwe.u.data.flags = 1;
+		cev = iwe_stream_add_point(info, cev, end_buf,
+					   &iwe, zd->rxdata+i+18);
+
+		iwe.cmd = SIOCGIWMODE;
+		if (zd->rxdata[i+14]&0x01)
+			iwe.u.mode = IW_MODE_MASTER;
+		else
+			iwe.u.mode = IW_MODE_ADHOC;
+		cev = iwe_stream_add_event(info, cev, end_buf,
+					   &iwe, IW_EV_UINT_LEN);
+		
+		iwe.cmd = SIOCGIWFREQ;
+		iwe.u.freq.m = zd->rxdata[i+0];
+		iwe.u.freq.e = 0;
+		cev = iwe_stream_add_event(info, cev, end_buf,
+					   &iwe, IW_EV_FREQ_LEN);
+		
+		iwe.cmd = SIOCGIWRATE;
+		iwe.u.bitrate.fixed = 0;
+		iwe.u.bitrate.disabled = 0;
+		for (j=0; j<10; j++) if (zd->rxdata[i+50+j]) {
+			iwe.u.bitrate.value = (zd->rxdata[i+50+j]&0x7f)*500000;
+			cev = iwe_stream_add_event(info, cev, end_buf,
+						   &iwe, IW_EV_PARAM_LEN);
+		}
+		
+		iwe.cmd = SIOCGIWENCODE;
+		iwe.u.data.length = 0;
+		if (zd->rxdata[i+14]&0x10)
+			iwe.u.data.flags = IW_ENCODE_ENABLED;
+		else
+			iwe.u.data.flags = IW_ENCODE_DISABLED;
+		cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL);
+		
+		iwe.cmd = IWEVQUAL;
+		iwe.u.qual.qual = zd->rxdata[i+4];
+		iwe.u.qual.noise= zd->rxdata[i+2]/10-100;
+		iwe.u.qual.level = (256+zd->rxdata[i+4]*100)/255-100;
+		iwe.u.qual.updated = 7;
+		cev = iwe_stream_add_event(info, cev, end_buf,
+					   &iwe, IW_EV_QUAL_LEN);
+	}
+
+	if (!enabled_save)
+		zd1201_disable(zd);
+
+	srq->length = cev - extra;
+	srq->flags = 0;
+
+	return 0;
+}
+
+static int zd1201_set_essid(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+
+	if (data->length > IW_ESSID_MAX_SIZE)
+		return -EINVAL;
+	if (data->length < 1)
+		data->length = 1;
+	zd->essidlen = data->length;
+	memset(zd->essid, 0, IW_ESSID_MAX_SIZE+1);
+	memcpy(zd->essid, essid, data->length);
+	return zd1201_join(zd, zd->essid, zd->essidlen);
+}
+
+static int zd1201_get_essid(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+
+	memcpy(essid, zd->essid, zd->essidlen);
+	data->flags = 1;
+	data->length = zd->essidlen;
+
+	return 0;
+}
+
+static int zd1201_get_nick(struct net_device *dev, struct iw_request_info *info,
+    struct iw_point *data, char *nick)
+{
+	strcpy(nick, "zd1201");
+	data->flags = 1;
+	data->length = strlen(nick);
+	return 0;
+}
+
+static int zd1201_set_rate(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short rate;
+	int err;
+
+	switch (rrq->value) {
+		case 1000000:
+			rate = ZD1201_RATEB1;
+			break;
+		case 2000000:
+			rate = ZD1201_RATEB2;
+			break;
+		case 5500000:
+			rate = ZD1201_RATEB5;
+			break;
+		case 11000000:
+		default:
+			rate = ZD1201_RATEB11;
+			break;
+	}
+	if (!rrq->fixed) { /* Also enable all lower bitrates */
+		rate |= rate-1;
+	}
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL, rate);
+	if (err)
+		return err;
+
+	return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_rate(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short rate;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CURRENTTXRATE, &rate);
+	if (err)
+		return err;
+
+	switch(rate) {
+		case 1:
+			rrq->value = 1000000;
+			break;
+		case 2:
+			rrq->value = 2000000;
+			break;
+		case 5:
+			rrq->value = 5500000;
+			break;
+		case 11:
+			rrq->value = 11000000;
+			break;
+		default:
+			rrq->value = 0;
+	}
+	rrq->fixed = 0;
+	rrq->disabled = 0;
+
+	return 0;
+}
+
+static int zd1201_set_rts(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *rts, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	int err;
+	short val = rts->value;
+
+	if (rts->disabled || !rts->fixed)
+		val = ZD1201_RTSMAX;
+	if (val > ZD1201_RTSMAX)
+		return -EINVAL;
+	if (val < 0)
+		return -EINVAL;
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, val);
+	if (err)
+		return err;
+	return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_rts(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *rts, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short rtst;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, &rtst);
+	if (err)
+		return err;
+	rts->value = rtst;
+	rts->disabled = (rts->value == ZD1201_RTSMAX);
+	rts->fixed = 1;
+
+	return 0;
+}
+
+static int zd1201_set_frag(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *frag, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	int err;
+	short val = frag->value;
+
+	if (frag->disabled || !frag->fixed)
+		val = ZD1201_FRAGMAX;
+	if (val > ZD1201_FRAGMAX)
+		return -EINVAL;
+	if (val < ZD1201_FRAGMIN)
+		return -EINVAL;
+	if (val & 1)
+		return -EINVAL;
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, val);
+	if (err)
+		return err;
+	return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_frag(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *frag, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short fragt;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, &fragt);
+	if (err)
+		return err;
+	frag->value = fragt;
+	frag->disabled = (frag->value == ZD1201_FRAGMAX);
+	frag->fixed = 1;
+
+	return 0;
+}
+
+static int zd1201_set_retry(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	return 0;
+}
+
+static int zd1201_get_retry(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	return 0;
+}
+
+static int zd1201_set_encode(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *erq, char *key)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short i;
+	int err, rid;
+
+	if (erq->length > ZD1201_MAXKEYLEN)
+		return -EINVAL;
+
+	i = (erq->flags & IW_ENCODE_INDEX)-1;
+	if (i == -1) {
+		err = zd1201_getconfig16(zd,ZD1201_RID_CNFDEFAULTKEYID,&i);
+		if (err)
+			return err;
+	} else {
+		err = zd1201_setconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, i);
+		if (err)
+			return err;
+	}
+
+	if (i < 0 || i >= ZD1201_NUMKEYS)
+		return -EINVAL;
+
+	rid = ZD1201_RID_CNFDEFAULTKEY0 + i;
+	err = zd1201_setconfig(zd, rid, key, erq->length, 1);
+	if (err)
+		return err;
+	zd->encode_keylen[i] = erq->length;
+	memcpy(zd->encode_keys[i], key, erq->length);
+
+	i=0;
+	if (!(erq->flags & IW_ENCODE_DISABLED & IW_ENCODE_MODE)) {
+		i |= 0x01;
+		zd->encode_enabled = 1;
+	} else
+		zd->encode_enabled = 0;
+	if (erq->flags & IW_ENCODE_RESTRICTED & IW_ENCODE_MODE) {
+		i |= 0x02;
+		zd->encode_restricted = 1;
+	} else
+		zd->encode_restricted = 0;
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFWEBFLAGS, i);
+	if (err)
+		return err;
+
+	if (zd->encode_enabled)
+		i = ZD1201_CNFAUTHENTICATION_SHAREDKEY;
+	else
+		i = ZD1201_CNFAUTHENTICATION_OPENSYSTEM;
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, i);
+	if (err)
+		return err;
+
+	return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_encode(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *erq, char *key)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short i;
+	int err;
+
+	if (zd->encode_enabled)
+		erq->flags = IW_ENCODE_ENABLED;
+	else
+		erq->flags = IW_ENCODE_DISABLED;
+	if (zd->encode_restricted)
+		erq->flags |= IW_ENCODE_RESTRICTED;
+	else
+		erq->flags |= IW_ENCODE_OPEN;
+
+	i = (erq->flags & IW_ENCODE_INDEX) -1;
+	if (i == -1) {
+		err = zd1201_getconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, &i);
+		if (err)
+			return err;
+	}
+	if (i<0 || i>= ZD1201_NUMKEYS)
+		return -EINVAL;
+
+	erq->flags |= i+1;
+
+	erq->length = zd->encode_keylen[i];
+	memcpy(key, zd->encode_keys[i], erq->length);
+
+	return 0;
+}
+
+static int zd1201_set_power(struct net_device *dev, 
+    struct iw_request_info *info, struct iw_param *vwrq, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short enabled, duration, level;
+	int err;
+
+	enabled = vwrq->disabled ? 0 : 1;
+	if (enabled) {
+		if (vwrq->flags & IW_POWER_PERIOD) {
+			duration = vwrq->value;
+			err = zd1201_setconfig16(zd, 
+			    ZD1201_RID_CNFMAXSLEEPDURATION, duration);
+			if (err)
+				return err;
+			goto out;
+		}
+		if (vwrq->flags & IW_POWER_TIMEOUT) {
+			err = zd1201_getconfig16(zd, 
+			    ZD1201_RID_CNFMAXSLEEPDURATION, &duration);
+			if (err)
+				return err;
+			level = vwrq->value * 4 / duration;
+			if (level > 4)
+				level = 4;
+			if (level < 0)
+				level = 0;
+			err = zd1201_setconfig16(zd, ZD1201_RID_CNFPMEPS,
+			    level);
+			if (err)
+				return err;
+			goto out;
+		}
+		return -EINVAL;
+	}
+out:
+	return zd1201_setconfig16(zd, ZD1201_RID_CNFPMENABLED, enabled);
+}
+
+static int zd1201_get_power(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *vwrq, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short enabled, level, duration;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMENABLED, &enabled);
+	if (err)
+		return err;
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMEPS, &level);
+	if (err)
+		return err;
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXSLEEPDURATION, &duration);
+	if (err)
+		return err;
+	vwrq->disabled = enabled ? 0 : 1;
+	if (vwrq->flags & IW_POWER_TYPE) {
+		if (vwrq->flags & IW_POWER_PERIOD) {
+			vwrq->value = duration;
+			vwrq->flags = IW_POWER_PERIOD;
+		} else {
+			vwrq->value = duration * level / 4;
+			vwrq->flags = IW_POWER_TIMEOUT;
+		}
+	}
+	if (vwrq->flags & IW_POWER_MODE) {
+		if (enabled && level)
+			vwrq->flags = IW_POWER_UNICAST_R;
+		else
+			vwrq->flags = IW_POWER_ALL_R;
+	}
+
+	return 0;
+}
+
+
+static const iw_handler zd1201_iw_handler[] =
+{
+	(iw_handler) zd1201_config_commit,	/* SIOCSIWCOMMIT */
+	(iw_handler) zd1201_get_name,    	/* SIOCGIWNAME */
+	(iw_handler) NULL,			/* SIOCSIWNWID */
+	(iw_handler) NULL,			/* SIOCGIWNWID */
+	(iw_handler) zd1201_set_freq,		/* SIOCSIWFREQ */
+	(iw_handler) zd1201_get_freq,		/* SIOCGIWFREQ */
+	(iw_handler) zd1201_set_mode,		/* SIOCSIWMODE */
+	(iw_handler) zd1201_get_mode,		/* SIOCGIWMODE */
+	(iw_handler) NULL,                  	/* SIOCSIWSENS */
+	(iw_handler) NULL,           		/* SIOCGIWSENS */
+	(iw_handler) NULL,			/* SIOCSIWRANGE */
+	(iw_handler) zd1201_get_range,           /* SIOCGIWRANGE */
+	(iw_handler) NULL,			/* SIOCSIWPRIV */
+	(iw_handler) NULL,			/* SIOCGIWPRIV */
+	(iw_handler) NULL,			/* SIOCSIWSTATS */
+	(iw_handler) NULL,			/* SIOCGIWSTATS */
+	(iw_handler) NULL,			/* SIOCSIWSPY */
+	(iw_handler) NULL,			/* SIOCGIWSPY */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL/*zd1201_set_wap*/,		/* SIOCSIWAP */
+	(iw_handler) zd1201_get_wap,		/* SIOCGIWAP */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,       		/* SIOCGIWAPLIST */
+	(iw_handler) zd1201_set_scan,		/* SIOCSIWSCAN */
+	(iw_handler) zd1201_get_scan,		/* SIOCGIWSCAN */
+	(iw_handler) zd1201_set_essid,		/* SIOCSIWESSID */
+	(iw_handler) zd1201_get_essid,		/* SIOCGIWESSID */
+	(iw_handler) NULL,         		/* SIOCSIWNICKN */
+	(iw_handler) zd1201_get_nick, 		/* SIOCGIWNICKN */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) zd1201_set_rate,		/* SIOCSIWRATE */
+	(iw_handler) zd1201_get_rate,		/* SIOCGIWRATE */
+	(iw_handler) zd1201_set_rts,		/* SIOCSIWRTS */
+	(iw_handler) zd1201_get_rts,		/* SIOCGIWRTS */
+	(iw_handler) zd1201_set_frag,		/* SIOCSIWFRAG */
+	(iw_handler) zd1201_get_frag,		/* SIOCGIWFRAG */
+	(iw_handler) NULL,         		/* SIOCSIWTXPOW */
+	(iw_handler) NULL,          		/* SIOCGIWTXPOW */
+	(iw_handler) zd1201_set_retry,		/* SIOCSIWRETRY */
+	(iw_handler) zd1201_get_retry,		/* SIOCGIWRETRY */
+	(iw_handler) zd1201_set_encode,		/* SIOCSIWENCODE */
+	(iw_handler) zd1201_get_encode,		/* SIOCGIWENCODE */
+	(iw_handler) zd1201_set_power,		/* SIOCSIWPOWER */
+	(iw_handler) zd1201_get_power,		/* SIOCGIWPOWER */
+};
+
+static int zd1201_set_hostauth(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+
+	if (!zd->ap)
+		return -EOPNOTSUPP;
+
+	return zd1201_setconfig16(zd, ZD1201_RID_CNFHOSTAUTH, rrq->value);
+}
+
+static int zd1201_get_hostauth(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short hostauth;
+	int err;
+
+	if (!zd->ap)
+		return -EOPNOTSUPP;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFHOSTAUTH, &hostauth);
+	if (err)
+		return err;
+	rrq->value = hostauth;
+	rrq->fixed = 1;
+
+	return 0;
+}
+
+static int zd1201_auth_sta(struct net_device *dev,
+    struct iw_request_info *info, struct sockaddr *sta, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	unsigned char buffer[10];
+
+	if (!zd->ap)
+		return -EOPNOTSUPP;
+
+	memcpy(buffer, sta->sa_data, ETH_ALEN);
+	*(short*)(buffer+6) = 0;	/* 0==success, 1==failure */
+	*(short*)(buffer+8) = 0;
+
+	return zd1201_setconfig(zd, ZD1201_RID_AUTHENTICATESTA, buffer, 10, 1);
+}
+
+static int zd1201_set_maxassoc(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	int err;
+
+	if (!zd->ap)
+		return -EOPNOTSUPP;
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, rrq->value);
+	if (err)
+		return err;
+	return 0;
+}
+
+static int zd1201_get_maxassoc(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = netdev_priv(dev);
+	short maxassoc;
+	int err;
+
+	if (!zd->ap)
+		return -EOPNOTSUPP;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, &maxassoc);
+	if (err)
+		return err;
+	rrq->value = maxassoc;
+	rrq->fixed = 1;
+
+	return 0;
+}
+
+static const iw_handler zd1201_private_handler[] = {
+	(iw_handler) zd1201_set_hostauth,	/* ZD1201SIWHOSTAUTH */
+	(iw_handler) zd1201_get_hostauth,	/* ZD1201GIWHOSTAUTH */
+	(iw_handler) zd1201_auth_sta,		/* ZD1201SIWAUTHSTA */
+	(iw_handler) NULL,			/* nothing to get */
+	(iw_handler) zd1201_set_maxassoc,	/* ZD1201SIMAXASSOC */
+	(iw_handler) zd1201_get_maxassoc,	/* ZD1201GIMAXASSOC */
+};
+
+static const struct iw_priv_args zd1201_private_args[] = {
+	{ ZD1201SIWHOSTAUTH, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	    IW_PRIV_TYPE_NONE, "sethostauth" },
+	{ ZD1201GIWHOSTAUTH, IW_PRIV_TYPE_NONE,
+	    IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostauth" },
+	{ ZD1201SIWAUTHSTA, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1,
+	    IW_PRIV_TYPE_NONE, "authstation" },
+	{ ZD1201SIWMAXASSOC, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	    IW_PRIV_TYPE_NONE, "setmaxassoc" },
+	{ ZD1201GIWMAXASSOC, IW_PRIV_TYPE_NONE,
+	    IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmaxassoc" },
+};
+
+static const struct iw_handler_def zd1201_iw_handlers = {
+	.num_standard 		= ARRAY_SIZE(zd1201_iw_handler),
+	.num_private 		= ARRAY_SIZE(zd1201_private_handler),
+	.num_private_args 	= ARRAY_SIZE(zd1201_private_args),
+	.standard 		= (iw_handler *)zd1201_iw_handler,
+	.private 		= (iw_handler *)zd1201_private_handler,
+	.private_args 		= (struct iw_priv_args *) zd1201_private_args,
+	.get_wireless_stats	= zd1201_get_wireless_stats,
+};
+
+static const struct net_device_ops zd1201_netdev_ops = {
+	.ndo_open		= zd1201_net_open,
+	.ndo_stop		= zd1201_net_stop,
+	.ndo_start_xmit		= zd1201_hard_start_xmit,
+	.ndo_tx_timeout		= zd1201_tx_timeout,
+	.ndo_set_rx_mode	= zd1201_set_multicast,
+	.ndo_set_mac_address	= zd1201_set_mac_address,
+	.ndo_change_mtu		= eth_change_mtu,
+	.ndo_validate_addr	= eth_validate_addr,
+};
+
+static int zd1201_probe(struct usb_interface *interface,
+			const struct usb_device_id *id)
+{
+	struct zd1201 *zd;
+	struct net_device *dev;
+	struct usb_device *usb;
+	int err;
+	short porttype;
+	char buf[IW_ESSID_MAX_SIZE+2];
+
+	usb = interface_to_usbdev(interface);
+
+	dev = alloc_etherdev(sizeof(*zd));
+	if (!dev)
+		return -ENOMEM;
+	zd = netdev_priv(dev);
+	zd->dev = dev;
+
+	zd->ap = ap;
+	zd->usb = usb;
+	zd->removed = 0;
+	init_waitqueue_head(&zd->rxdataq);
+	INIT_HLIST_HEAD(&zd->fraglist);
+	
+	err = zd1201_fw_upload(usb, zd->ap);
+	if (err) {
+		dev_err(&usb->dev, "zd1201 firmware upload failed: %d\n", err);
+		goto err_zd;
+	}
+	
+	zd->endp_in = 1;
+	zd->endp_out = 1;
+	zd->endp_out2 = 2;
+	zd->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	zd->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!zd->rx_urb || !zd->tx_urb) {
+		err = -ENOMEM;
+		goto err_zd;
+	}
+
+	mdelay(100);
+	err = zd1201_drvr_start(zd);
+	if (err)
+		goto err_zd;
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXDATALEN, 2312);
+	if (err)
+		goto err_start;
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL,
+	    ZD1201_RATEB1 | ZD1201_RATEB2 | ZD1201_RATEB5 | ZD1201_RATEB11);
+	if (err)
+		goto err_start;
+
+	dev->netdev_ops = &zd1201_netdev_ops;
+	dev->wireless_handlers = &zd1201_iw_handlers;
+	dev->watchdog_timeo = ZD1201_TX_TIMEOUT;
+	strcpy(dev->name, "wlan%d");
+
+	err = zd1201_getconfig(zd, ZD1201_RID_CNFOWNMACADDR, 
+	    dev->dev_addr, dev->addr_len);
+	if (err)
+		goto err_start;
+
+	/* Set wildcard essid to match zd->essid */
+	*(__le16 *)buf = cpu_to_le16(0);
+	err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf,
+	    IW_ESSID_MAX_SIZE+2, 1);
+	if (err)
+		goto err_start;
+
+	if (zd->ap)
+		porttype = ZD1201_PORTTYPE_AP;
+	else
+		porttype = ZD1201_PORTTYPE_BSS;
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype);
+	if (err)
+		goto err_start;
+
+	SET_NETDEV_DEV(dev, &usb->dev);
+
+	err = register_netdev(dev);
+	if (err)
+		goto err_start;
+	dev_info(&usb->dev, "%s: ZD1201 USB Wireless interface\n",
+	    dev->name);
+
+	usb_set_intfdata(interface, zd);
+	zd1201_enable(zd);	/* zd1201 likes to startup enabled, */
+	zd1201_disable(zd);	/* interfering with all the wifis in range */
+	return 0;
+
+err_start:
+	/* Leave the device in reset state */
+	zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0);
+err_zd:
+	usb_free_urb(zd->tx_urb);
+	usb_free_urb(zd->rx_urb);
+	free_netdev(dev);
+	return err;
+}
+
+static void zd1201_disconnect(struct usb_interface *interface)
+{
+	struct zd1201 *zd = usb_get_intfdata(interface);
+	struct hlist_node *node2;
+	struct zd1201_frag *frag;
+
+	if (!zd)
+		return;
+	usb_set_intfdata(interface, NULL);
+
+	hlist_for_each_entry_safe(frag, node2, &zd->fraglist, fnode) {
+		hlist_del_init(&frag->fnode);
+		kfree_skb(frag->skb);
+		kfree(frag);
+	}
+
+	if (zd->tx_urb) {
+		usb_kill_urb(zd->tx_urb);
+		usb_free_urb(zd->tx_urb);
+	}
+	if (zd->rx_urb) {
+		usb_kill_urb(zd->rx_urb);
+		usb_free_urb(zd->rx_urb);
+	}
+
+	if (zd->dev) {
+		unregister_netdev(zd->dev);
+		free_netdev(zd->dev);
+	}
+}
+
+#ifdef CONFIG_PM
+
+static int zd1201_suspend(struct usb_interface *interface,
+			   pm_message_t message)
+{
+	struct zd1201 *zd = usb_get_intfdata(interface);
+
+	netif_device_detach(zd->dev);
+
+	zd->was_enabled = zd->mac_enabled;
+
+	if (zd->was_enabled)
+		return zd1201_disable(zd);
+	else
+		return 0;
+}
+
+static int zd1201_resume(struct usb_interface *interface)
+{
+	struct zd1201 *zd = usb_get_intfdata(interface);
+
+	if (!zd || !zd->dev)
+		return -ENODEV;
+
+	netif_device_attach(zd->dev);
+
+	if (zd->was_enabled)
+		return zd1201_enable(zd);
+	else
+		return 0;
+}
+
+#else
+
+#define zd1201_suspend NULL
+#define zd1201_resume  NULL
+
+#endif
+
+static struct usb_driver zd1201_usb = {
+	.name = "zd1201",
+	.probe = zd1201_probe,
+	.disconnect = zd1201_disconnect,
+	.id_table = zd1201_table,
+	.suspend = zd1201_suspend,
+	.resume = zd1201_resume,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0))
+	.disable_hub_initiated_lpm = 1,
+#endif
+};
+
+module_usb_driver(zd1201_usb);
diff --git a/drivers/net/wireless/zd1201.h b/drivers/net/wireless/zd1201.h
new file mode 100644
index 0000000..dd7ea1f
--- /dev/null
+++ b/drivers/net/wireless/zd1201.h
@@ -0,0 +1,147 @@
+/*
+ *	Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org)
+ *
+ *	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.
+ *
+ *	Parts of this driver have been derived from a wlan-ng version
+ *	modified by ZyDAS.
+ *	Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
+ */
+
+#ifndef _INCLUDE_ZD1201_H_
+#define _INCLUDE_ZD1201_H_
+
+#define ZD1201_NUMKEYS		4
+#define ZD1201_MAXKEYLEN	13
+#define ZD1201_MAXMULTI		16
+#define ZD1201_FRAGMAX		2500
+#define ZD1201_FRAGMIN		256
+#define ZD1201_RTSMAX		2500
+
+#define ZD1201_RXSIZE		3000
+
+struct zd1201 {
+	struct usb_device	*usb;
+	int			removed;
+	struct net_device	*dev;
+	struct iw_statistics	iwstats;
+
+	int			endp_in;
+	int			endp_out;
+	int			endp_out2;
+	struct urb		*rx_urb;
+	struct urb		*tx_urb;
+
+	unsigned char 		rxdata[ZD1201_RXSIZE];
+	int			rxlen;
+	wait_queue_head_t	rxdataq;
+	int			rxdatas;
+	struct hlist_head	fraglist;
+	unsigned char		txdata[ZD1201_RXSIZE];
+
+	int			ap;
+	char			essid[IW_ESSID_MAX_SIZE+1];
+	int			essidlen;
+	int			mac_enabled;
+	int			was_enabled;
+	int			monitor;
+	int			encode_enabled;
+	int			encode_restricted;
+	unsigned char		encode_keys[ZD1201_NUMKEYS][ZD1201_MAXKEYLEN];
+	int			encode_keylen[ZD1201_NUMKEYS];
+};
+
+struct zd1201_frag {
+	struct hlist_node	fnode;
+	int			seq;
+	struct sk_buff		*skb;
+};
+
+#define ZD1201SIWHOSTAUTH SIOCIWFIRSTPRIV
+#define ZD1201GIWHOSTAUTH ZD1201SIWHOSTAUTH+1
+#define ZD1201SIWAUTHSTA SIOCIWFIRSTPRIV+2
+#define ZD1201SIWMAXASSOC SIOCIWFIRSTPRIV+4
+#define ZD1201GIWMAXASSOC ZD1201SIWMAXASSOC+1
+
+#define ZD1201_FW_TIMEOUT	(1000)
+
+#define ZD1201_TX_TIMEOUT	(2000)
+
+#define ZD1201_USB_CMDREQ	0
+#define ZD1201_USB_RESREQ	1
+
+#define	ZD1201_CMDCODE_INIT	0x00
+#define ZD1201_CMDCODE_ENABLE	0x01
+#define ZD1201_CMDCODE_DISABLE	0x02
+#define ZD1201_CMDCODE_ALLOC	0x0a
+#define ZD1201_CMDCODE_INQUIRE	0x11
+#define ZD1201_CMDCODE_SETRXRID	0x17
+#define ZD1201_CMDCODE_ACCESS	0x21
+
+#define ZD1201_PACKET_EVENTSTAT	0x0
+#define ZD1201_PACKET_RXDATA	0x1
+#define ZD1201_PACKET_INQUIRE	0x2
+#define ZD1201_PACKET_RESOURCE	0x3
+
+#define ZD1201_ACCESSBIT	0x0100
+
+#define ZD1201_RID_CNFPORTTYPE		0xfc00
+#define ZD1201_RID_CNFOWNMACADDR	0xfc01
+#define ZD1201_RID_CNFDESIREDSSID	0xfc02
+#define ZD1201_RID_CNFOWNCHANNEL	0xfc03
+#define ZD1201_RID_CNFOWNSSID		0xfc04
+#define ZD1201_RID_CNFMAXDATALEN	0xfc07
+#define ZD1201_RID_CNFPMENABLED		0xfc09
+#define ZD1201_RID_CNFPMEPS		0xfc0a
+#define ZD1201_RID_CNFMAXSLEEPDURATION	0xfc0c
+#define ZD1201_RID_CNFDEFAULTKEYID	0xfc23
+#define ZD1201_RID_CNFDEFAULTKEY0	0xfc24
+#define ZD1201_RID_CNFDEFAULTKEY1	0xfc25
+#define ZD1201_RID_CNFDEFAULTKEY2	0xfc26
+#define ZD1201_RID_CNFDEFAULTKEY3	0xfc27
+#define ZD1201_RID_CNFWEBFLAGS		0xfc28
+#define ZD1201_RID_CNFAUTHENTICATION	0xfc2a
+#define ZD1201_RID_CNFMAXASSOCSTATIONS	0xfc2b
+#define ZD1201_RID_CNFHOSTAUTH		0xfc2e
+#define ZD1201_RID_CNFGROUPADDRESS	0xfc80
+#define ZD1201_RID_CNFFRAGTHRESHOLD	0xfc82
+#define ZD1201_RID_CNFRTSTHRESHOLD	0xfc83
+#define ZD1201_RID_TXRATECNTL		0xfc84
+#define ZD1201_RID_PROMISCUOUSMODE	0xfc85
+#define ZD1201_RID_CNFBASICRATES	0xfcb3
+#define ZD1201_RID_AUTHENTICATESTA	0xfce3
+#define ZD1201_RID_CURRENTBSSID		0xfd42
+#define ZD1201_RID_COMMSQUALITY		0xfd43
+#define ZD1201_RID_CURRENTTXRATE	0xfd44
+#define ZD1201_RID_CNFMAXTXBUFFERNUMBER	0xfda0
+#define ZD1201_RID_CURRENTCHANNEL	0xfdc1
+
+#define ZD1201_INQ_SCANRESULTS		0xf101
+
+#define ZD1201_INF_LINKSTATUS		0xf200
+#define ZD1201_INF_ASSOCSTATUS		0xf201
+#define ZD1201_INF_AUTHREQ		0xf202
+
+#define ZD1201_ASSOCSTATUS_STAASSOC	0x1
+#define ZD1201_ASSOCSTATUS_REASSOC	0x2
+#define ZD1201_ASSOCSTATUS_DISASSOC	0x3
+#define ZD1201_ASSOCSTATUS_ASSOCFAIL	0x4
+#define ZD1201_ASSOCSTATUS_AUTHFAIL	0x5
+
+#define ZD1201_PORTTYPE_IBSS		0
+#define ZD1201_PORTTYPE_BSS		1
+#define ZD1201_PORTTYPE_WDS		2
+#define ZD1201_PORTTYPE_PSEUDOIBSS	3
+#define ZD1201_PORTTYPE_AP		6
+
+#define ZD1201_RATEB1	1
+#define ZD1201_RATEB2	2
+#define ZD1201_RATEB5	4	/* 5.5 really, but 5 is shorter :) */
+#define ZD1201_RATEB11	8
+
+#define ZD1201_CNFAUTHENTICATION_OPENSYSTEM	0x0001
+#define ZD1201_CNFAUTHENTICATION_SHAREDKEY	0x0002
+
+#endif /* _INCLUDE_ZD1201_H_ */
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 5d8f3a9..19fcdc2 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -81,4 +81,5 @@
 source "drivers/nfc/nfcmrvl/Kconfig"
 source "drivers/nfc/st21nfca/Kconfig"
 source "drivers/nfc/st21nfcb/Kconfig"
+source "drivers/nfc/nxp-nci/Kconfig"
 endmenu
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index 2bf2c40..3e8e17e 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -13,5 +13,6 @@
 obj-$(CPTCFG_NFC_TRF7970A)	+= trf7970a.o
 obj-$(CPTCFG_NFC_ST21NFCA)  	+= st21nfca/
 obj-$(CPTCFG_NFC_ST21NFCB)	+= st21nfcb/
+obj-$(CPTCFG_NFC_NXP_NCI)	+= nxp-nci/
 
 ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c
index df85cd3..661e2c8 100644
--- a/drivers/nfc/microread/i2c.c
+++ b/drivers/nfc/microread/i2c.c
@@ -286,7 +286,7 @@
 	if (r < 0)
 		goto err_irq;
 
-	nfc_info(&client->dev, "Probed");
+	nfc_info(&client->dev, "Probed\n");
 
 	return 0;
 
diff --git a/drivers/nfc/microread/microread.c b/drivers/nfc/microread/microread.c
index 963a4a5..f454dc6 100644
--- a/drivers/nfc/microread/microread.c
+++ b/drivers/nfc/microread/microread.c
@@ -557,10 +557,11 @@
 		pr_err("Failed to handle discovered target err=%d\n", r);
 }
 
-static int microread_event_received(struct nfc_hci_dev *hdev, u8 gate,
+static int microread_event_received(struct nfc_hci_dev *hdev, u8 pipe,
 				     u8 event, struct sk_buff *skb)
 {
 	int r;
+	u8 gate = hdev->pipes[pipe].gate;
 	u8 mode;
 
 	pr_info("Microread received event 0x%x to gate 0x%x\n", event, gate);
diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c
index 85e8bcf..ad4933c 100644
--- a/drivers/nfc/nfcmrvl/main.c
+++ b/drivers/nfc/nfcmrvl/main.c
@@ -111,7 +111,7 @@
 
 	priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0);
 	if (!priv->ndev) {
-		nfc_err(dev, "nci_allocate_device failed");
+		nfc_err(dev, "nci_allocate_device failed\n");
 		rc = -ENOMEM;
 		goto error;
 	}
@@ -120,7 +120,7 @@
 
 	rc = nci_register_device(priv->ndev);
 	if (rc) {
-		nfc_err(dev, "nci_register_device failed %d", rc);
+		nfc_err(dev, "nci_register_device failed %d\n", rc);
 		nci_free_device(priv->ndev);
 		goto error;
 	}
diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c
index a717d1b..5e6b8f4 100644
--- a/drivers/nfc/nfcmrvl/usb.c
+++ b/drivers/nfc/nfcmrvl/usb.c
@@ -80,7 +80,7 @@
 	if (!urb->status) {
 		if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer,
 					   urb->actual_length) < 0)
-			nfc_err(&drv_data->udev->dev, "corrupted Rx packet");
+			nfc_err(&drv_data->udev->dev, "corrupted Rx packet\n");
 	}
 
 	if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
@@ -96,7 +96,7 @@
 		 */
 		if (err != -EPERM && err != -ENODEV)
 			nfc_err(&drv_data->udev->dev,
-				"urb %p failed to resubmit (%d)", urb, -err);
+				"urb %p failed to resubmit (%d)\n", urb, -err);
 		usb_unanchor_urb(urb);
 	}
 }
@@ -137,7 +137,7 @@
 	if (err) {
 		if (err != -EPERM && err != -ENODEV)
 			nfc_err(&drv_data->udev->dev,
-				"urb %p submission failed (%d)", urb, -err);
+				"urb %p submission failed (%d)\n", urb, -err);
 		usb_unanchor_urb(urb);
 	}
 
@@ -153,7 +153,7 @@
 	struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
 	struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
 
-	nfc_info(priv->dev, "urb %p status %d count %d",
+	nfc_info(priv->dev, "urb %p status %d count %d\n",
 		 urb, urb->status, urb->actual_length);
 
 	spin_lock(&drv_data->txlock);
@@ -253,7 +253,7 @@
 	if (err) {
 		if (err != -EPERM && err != -ENODEV)
 			nfc_err(&drv_data->udev->dev,
-				"urb %p submission failed (%d)", urb, -err);
+				"urb %p submission failed (%d)\n", urb, -err);
 		kfree(urb->setup_packet);
 		usb_unanchor_urb(urb);
 	} else {
@@ -293,7 +293,7 @@
 	int i;
 	struct usb_device *udev = interface_to_usbdev(intf);
 
-	nfc_info(&udev->dev, "intf %p id %p", intf, id);
+	nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
 
 	drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
 	if (!drv_data)
@@ -348,7 +348,7 @@
 	if (!drv_data)
 		return;
 
-	nfc_info(&drv_data->udev->dev, "intf %p", intf);
+	nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
 
 	nfcmrvl_nci_unregister_dev(drv_data->priv);
 
@@ -360,7 +360,7 @@
 {
 	struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
 
-	nfc_info(&drv_data->udev->dev, "intf %p", intf);
+	nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
 
 	if (drv_data->suspend_count++)
 		return 0;
@@ -401,7 +401,7 @@
 	struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
 	int err = 0;
 
-	nfc_info(&drv_data->udev->dev, "intf %p", intf);
+	nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
 
 	if (--drv_data->suspend_count)
 		return 0;
diff --git a/drivers/nfc/nxp-nci/Kconfig b/drivers/nfc/nxp-nci/Kconfig
new file mode 100644
index 0000000..08f0b50
--- /dev/null
+++ b/drivers/nfc/nxp-nci/Kconfig
@@ -0,0 +1,27 @@
+config NFC_NXP_NCI
+	tristate "NXP-NCI NFC driver"
+	depends on m
+	depends on NFC_NCI
+	default n
+	---help---
+	  Generic core driver for NXP NCI chips such as the NPC100
+	  or PN7150 families.
+	  This is a driver based on the NCI NFC kernel layers and
+	  will thus not work with NXP libnfc library.
+
+	  To compile this driver as a module, choose m here. The module will
+	  be called nxp_nci.
+	  Say N if unsure.
+
+config NFC_NXP_NCI_I2C
+	tristate "NXP-NCI I2C support"
+	depends on m
+	depends on NFC_NXP_NCI && I2C
+	---help---
+	  This module adds support for an I2C interface to the NXP NCI
+	  chips.
+	  Select this if your platform is using the I2C bus.
+
+	  To compile this driver as a module, choose m here. The module will
+	  be called nxp_nci_i2c.
+	  Say Y if unsure.
diff --git a/drivers/nfc/nxp-nci/Makefile b/drivers/nfc/nxp-nci/Makefile
new file mode 100644
index 0000000..89d602f
--- /dev/null
+++ b/drivers/nfc/nxp-nci/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for NXP-NCI NFC driver
+#
+
+nxp-nci-objs = core.o firmware.o
+nxp-nci_i2c-objs = i2c.o
+
+obj-$(CPTCFG_NFC_NXP_NCI) += nxp-nci.o
+obj-$(CPTCFG_NFC_NXP_NCI_I2C) += nxp-nci_i2c.o
+
+ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/nxp-nci/core.c b/drivers/nfc/nxp-nci/core.c
new file mode 100644
index 0000000..8979636
--- /dev/null
+++ b/drivers/nfc/nxp-nci/core.c
@@ -0,0 +1,186 @@
+/*
+ * Generic driver for NXP NCI NFC chips
+ *
+ * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/delay.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/platform_data/nxp-nci.h>
+
+#include <net/nfc/nci_core.h>
+
+#include "nxp-nci.h"
+
+#define NXP_NCI_HDR_LEN	4
+
+#define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
+			       NFC_PROTO_MIFARE_MASK | \
+			       NFC_PROTO_FELICA_MASK | \
+			       NFC_PROTO_ISO14443_MASK | \
+			       NFC_PROTO_ISO14443_B_MASK | \
+			       NFC_PROTO_NFC_DEP_MASK)
+
+static int nxp_nci_open(struct nci_dev *ndev)
+{
+	struct nxp_nci_info *info = nci_get_drvdata(ndev);
+	int r = 0;
+
+	mutex_lock(&info->info_lock);
+
+	if (info->mode != NXP_NCI_MODE_COLD) {
+		r = -EBUSY;
+		goto open_exit;
+	}
+
+	if (info->phy_ops->set_mode)
+		r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI);
+
+	info->mode = NXP_NCI_MODE_NCI;
+
+open_exit:
+	mutex_unlock(&info->info_lock);
+	return r;
+}
+
+static int nxp_nci_close(struct nci_dev *ndev)
+{
+	struct nxp_nci_info *info = nci_get_drvdata(ndev);
+	int r = 0;
+
+	mutex_lock(&info->info_lock);
+
+	if (info->phy_ops->set_mode)
+		r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+
+	info->mode = NXP_NCI_MODE_COLD;
+
+	mutex_unlock(&info->info_lock);
+	return r;
+}
+
+static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+	struct nxp_nci_info *info = nci_get_drvdata(ndev);
+	int r;
+
+	if (!info->phy_ops->write) {
+		r = -ENOTSUPP;
+		goto send_exit;
+	}
+
+	if (info->mode != NXP_NCI_MODE_NCI) {
+		r = -EINVAL;
+		goto send_exit;
+	}
+
+	r = info->phy_ops->write(info->phy_id, skb);
+	if (r < 0)
+		kfree_skb(skb);
+
+send_exit:
+	return r;
+}
+
+static struct nci_ops nxp_nci_ops = {
+	.open = nxp_nci_open,
+	.close = nxp_nci_close,
+	.send = nxp_nci_send,
+	.fw_download = nxp_nci_fw_download,
+};
+
+int nxp_nci_probe(void *phy_id, struct device *pdev,
+		  struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload,
+		  struct nci_dev **ndev)
+{
+	struct nxp_nci_info *info;
+	int r;
+
+	info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL);
+	if (!info) {
+		r = -ENOMEM;
+		goto probe_exit;
+	}
+
+	info->phy_id = phy_id;
+	info->pdev = pdev;
+	info->phy_ops = phy_ops;
+	info->max_payload = max_payload;
+	INIT_WORK(&info->fw_info.work, nxp_nci_fw_work);
+	init_completion(&info->fw_info.cmd_completion);
+	mutex_init(&info->info_lock);
+
+	if (info->phy_ops->set_mode) {
+		r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+		if (r < 0)
+			goto probe_exit;
+	}
+
+	info->mode = NXP_NCI_MODE_COLD;
+
+	info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS,
+					 NXP_NCI_HDR_LEN, 0);
+	if (!info->ndev) {
+		r = -ENOMEM;
+		goto probe_exit;
+	}
+
+	nci_set_parent_dev(info->ndev, pdev);
+	nci_set_drvdata(info->ndev, info);
+	r = nci_register_device(info->ndev);
+	if (r < 0)
+		goto probe_exit_free_nci;
+
+	*ndev = info->ndev;
+
+	goto probe_exit;
+
+probe_exit_free_nci:
+	nci_free_device(info->ndev);
+probe_exit:
+	return r;
+}
+EXPORT_SYMBOL(nxp_nci_probe);
+
+void nxp_nci_remove(struct nci_dev *ndev)
+{
+	struct nxp_nci_info *info = nci_get_drvdata(ndev);
+
+	if (info->mode == NXP_NCI_MODE_FW)
+		nxp_nci_fw_work_complete(info, -ESHUTDOWN);
+	cancel_work_sync(&info->fw_info.work);
+
+	mutex_lock(&info->info_lock);
+
+	if (info->phy_ops->set_mode)
+		info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+
+	nci_unregister_device(ndev);
+	nci_free_device(ndev);
+
+	mutex_unlock(&info->info_lock);
+}
+EXPORT_SYMBOL(nxp_nci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NXP NCI NFC driver");
+MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");
diff --git a/drivers/nfc/nxp-nci/firmware.c b/drivers/nfc/nxp-nci/firmware.c
new file mode 100644
index 0000000..5291797
--- /dev/null
+++ b/drivers/nfc/nxp-nci/firmware.c
@@ -0,0 +1,325 @@
+/*
+ * Generic driver for NXP NCI NFC chips
+ *
+ * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
+ *
+ * Author: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/completion.h>
+#include <linux/firmware.h>
+#include <linux/nfc.h>
+#include <linux/unaligned/access_ok.h>
+
+#include "nxp-nci.h"
+
+/* Crypto operations can take up to 30 seconds */
+#define NXP_NCI_FW_ANSWER_TIMEOUT	msecs_to_jiffies(30000)
+
+#define NXP_NCI_FW_CMD_RESET		0xF0
+#define NXP_NCI_FW_CMD_GETVERSION	0xF1
+#define NXP_NCI_FW_CMD_CHECKINTEGRITY	0xE0
+#define NXP_NCI_FW_CMD_WRITE		0xC0
+#define NXP_NCI_FW_CMD_READ		0xA2
+#define NXP_NCI_FW_CMD_GETSESSIONSTATE	0xF2
+#define NXP_NCI_FW_CMD_LOG		0xA7
+#define NXP_NCI_FW_CMD_FORCE		0xD0
+#define NXP_NCI_FW_CMD_GET_DIE_ID	0xF4
+
+#define NXP_NCI_FW_CHUNK_FLAG	0x0400
+
+#define NXP_NCI_FW_RESULT_OK				0x00
+#define NXP_NCI_FW_RESULT_INVALID_ADDR			0x01
+#define NXP_NCI_FW_RESULT_GENERIC_ERROR			0x02
+#define NXP_NCI_FW_RESULT_UNKNOWN_CMD			0x0B
+#define NXP_NCI_FW_RESULT_ABORTED_CMD			0x0C
+#define NXP_NCI_FW_RESULT_PLL_ERROR			0x0D
+#define NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR		0x1E
+#define NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR		0x1F
+#define NXP_NCI_FW_RESULT_MEM_BSY			0x20
+#define NXP_NCI_FW_RESULT_SIGNATURE_ERROR		0x21
+#define NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR	0x24
+#define NXP_NCI_FW_RESULT_PROTOCOL_ERROR		0x28
+#define NXP_NCI_FW_RESULT_SFWU_DEGRADED			0x2A
+#define NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK		0x2D
+#define NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK		0x2E
+#define NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5	0xC5
+
+void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result)
+{
+	struct nxp_nci_fw_info *fw_info = &info->fw_info;
+	int r;
+
+	if (info->phy_ops->set_mode) {
+		r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+		if (r < 0 && result == 0)
+			result = -r;
+	}
+
+	info->mode = NXP_NCI_MODE_COLD;
+
+	if (fw_info->fw) {
+		release_firmware(fw_info->fw);
+		fw_info->fw = NULL;
+	}
+
+	nfc_fw_download_done(info->ndev->nfc_dev, fw_info->name, (u32) -result);
+}
+
+/* crc_ccitt cannot be used since it is computed MSB first and not LSB first */
+static u16 nxp_nci_fw_crc(u8 const *buffer, size_t len)
+{
+	u16 crc = 0xffff;
+
+	while (len--) {
+		crc = ((crc >> 8) | (crc << 8)) ^ *buffer++;
+		crc ^= (crc & 0xff) >> 4;
+		crc ^= (crc & 0xff) << 12;
+		crc ^= (crc & 0xff) << 5;
+	}
+
+	return crc;
+}
+
+static int nxp_nci_fw_send_chunk(struct nxp_nci_info *info)
+{
+	struct nxp_nci_fw_info *fw_info = &info->fw_info;
+	u16 header, crc;
+	struct sk_buff *skb;
+	size_t chunk_len;
+	size_t remaining_len;
+	int r;
+
+	skb = nci_skb_alloc(info->ndev, info->max_payload, GFP_KERNEL);
+	if (!skb) {
+		r = -ENOMEM;
+		goto chunk_exit;
+	}
+
+	chunk_len = info->max_payload - NXP_NCI_FW_HDR_LEN - NXP_NCI_FW_CRC_LEN;
+	remaining_len = fw_info->frame_size - fw_info->written;
+
+	if (remaining_len > chunk_len) {
+		header = NXP_NCI_FW_CHUNK_FLAG;
+	} else {
+		chunk_len = remaining_len;
+		header = 0x0000;
+	}
+
+	header |= chunk_len & NXP_NCI_FW_FRAME_LEN_MASK;
+	put_unaligned_be16(header, skb_put(skb, NXP_NCI_FW_HDR_LEN));
+
+	memcpy(skb_put(skb, chunk_len), fw_info->data + fw_info->written,
+	       chunk_len);
+
+	crc = nxp_nci_fw_crc(skb->data, chunk_len + NXP_NCI_FW_HDR_LEN);
+	put_unaligned_be16(crc, skb_put(skb, NXP_NCI_FW_CRC_LEN));
+
+	r = info->phy_ops->write(info->phy_id, skb);
+	if (r >= 0)
+		r = chunk_len;
+
+	kfree_skb(skb);
+
+chunk_exit:
+	return r;
+}
+
+static int nxp_nci_fw_send(struct nxp_nci_info *info)
+{
+	struct nxp_nci_fw_info *fw_info = &info->fw_info;
+	long completion_rc;
+	int r;
+
+	reinit_completion(&fw_info->cmd_completion);
+
+	if (fw_info->written == 0) {
+		fw_info->frame_size = get_unaligned_be16(fw_info->data) &
+				      NXP_NCI_FW_FRAME_LEN_MASK;
+		fw_info->data += NXP_NCI_FW_HDR_LEN;
+		fw_info->size -= NXP_NCI_FW_HDR_LEN;
+	}
+
+	if (fw_info->frame_size > fw_info->size)
+		return -EMSGSIZE;
+
+	r = nxp_nci_fw_send_chunk(info);
+	if (r < 0)
+		return r;
+
+	fw_info->written += r;
+
+	if (*fw_info->data == NXP_NCI_FW_CMD_RESET) {
+		fw_info->cmd_result = 0;
+		if (fw_info->fw)
+			schedule_work(&fw_info->work);
+	} else {
+		completion_rc = wait_for_completion_interruptible_timeout(
+			&fw_info->cmd_completion, NXP_NCI_FW_ANSWER_TIMEOUT);
+		if (completion_rc == 0)
+			return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+void nxp_nci_fw_work(struct work_struct *work)
+{
+	struct nxp_nci_info *info;
+	struct nxp_nci_fw_info *fw_info;
+	int r;
+
+	fw_info = container_of(work, struct nxp_nci_fw_info, work);
+	info = container_of(fw_info, struct nxp_nci_info, fw_info);
+
+	mutex_lock(&info->info_lock);
+
+	r = fw_info->cmd_result;
+	if (r < 0)
+		goto exit_work;
+
+	if (fw_info->written == fw_info->frame_size) {
+		fw_info->data += fw_info->frame_size;
+		fw_info->size -= fw_info->frame_size;
+		fw_info->written = 0;
+	}
+
+	if (fw_info->size > 0)
+		r = nxp_nci_fw_send(info);
+
+exit_work:
+	if (r < 0 || fw_info->size == 0)
+		nxp_nci_fw_work_complete(info, r);
+	mutex_unlock(&info->info_lock);
+}
+
+int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name)
+{
+	struct nxp_nci_info *info = nci_get_drvdata(ndev);
+	struct nxp_nci_fw_info *fw_info = &info->fw_info;
+	int r;
+
+	mutex_lock(&info->info_lock);
+
+	if (!info->phy_ops->set_mode || !info->phy_ops->write) {
+		r = -ENOTSUPP;
+		goto fw_download_exit;
+	}
+
+	if (!firmware_name || firmware_name[0] == '\0') {
+		r = -EINVAL;
+		goto fw_download_exit;
+	}
+
+	strcpy(fw_info->name, firmware_name);
+
+	r = request_firmware(&fw_info->fw, firmware_name,
+			     ndev->nfc_dev->dev.parent);
+	if (r < 0)
+		goto fw_download_exit;
+
+	r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_FW);
+	if (r < 0) {
+		release_firmware(fw_info->fw);
+		goto fw_download_exit;
+	}
+
+	info->mode = NXP_NCI_MODE_FW;
+
+	fw_info->data = fw_info->fw->data;
+	fw_info->size = fw_info->fw->size;
+	fw_info->written = 0;
+	fw_info->frame_size = 0;
+	fw_info->cmd_result = 0;
+
+	schedule_work(&fw_info->work);
+
+fw_download_exit:
+	mutex_unlock(&info->info_lock);
+	return r;
+}
+
+static int nxp_nci_fw_read_status(u8 stat)
+{
+	switch (stat) {
+	case NXP_NCI_FW_RESULT_OK:
+		return 0;
+	case NXP_NCI_FW_RESULT_INVALID_ADDR:
+		return -EINVAL;
+	case NXP_NCI_FW_RESULT_UNKNOWN_CMD:
+		return -EINVAL;
+	case NXP_NCI_FW_RESULT_ABORTED_CMD:
+		return -EMSGSIZE;
+	case NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR:
+		return -EADDRNOTAVAIL;
+	case NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR:
+		return -ENOBUFS;
+	case NXP_NCI_FW_RESULT_MEM_BSY:
+		return -ENOKEY;
+	case NXP_NCI_FW_RESULT_SIGNATURE_ERROR:
+		return -EKEYREJECTED;
+	case NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR:
+		return -EALREADY;
+	case NXP_NCI_FW_RESULT_PROTOCOL_ERROR:
+		return -EPROTO;
+	case NXP_NCI_FW_RESULT_SFWU_DEGRADED:
+		return -EHWPOISON;
+	case NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK:
+		return 0;
+	case NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK:
+		return 0;
+	case NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5:
+		return -EINVAL;
+	default:
+		return -EIO;
+	}
+}
+
+static u16 nxp_nci_fw_check_crc(struct sk_buff *skb)
+{
+	u16 crc, frame_crc;
+	size_t len = skb->len - NXP_NCI_FW_CRC_LEN;
+
+	crc = nxp_nci_fw_crc(skb->data, len);
+	frame_crc = get_unaligned_be16(skb->data + len);
+
+	return (crc ^ frame_crc);
+}
+
+void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
+{
+	struct nxp_nci_info *info = nci_get_drvdata(ndev);
+	struct nxp_nci_fw_info *fw_info = &info->fw_info;
+
+	complete(&fw_info->cmd_completion);
+
+	if (skb) {
+		if (nxp_nci_fw_check_crc(skb) != 0x00)
+			fw_info->cmd_result = -EBADMSG;
+		else
+			fw_info->cmd_result = nxp_nci_fw_read_status(
+					*skb_pull(skb, NXP_NCI_FW_HDR_LEN));
+		kfree_skb(skb);
+	} else {
+		fw_info->cmd_result = -EIO;
+	}
+
+	if (fw_info->fw)
+		schedule_work(&fw_info->work);
+}
+EXPORT_SYMBOL(nxp_nci_fw_recv_frame);
diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c
new file mode 100644
index 0000000..17bd67d
--- /dev/null
+++ b/drivers/nfc/nxp-nci/i2c.c
@@ -0,0 +1,415 @@
+/*
+ * I2C link layer for the NXP NCI driver
+ *
+ * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/platform_data/nxp-nci.h>
+#include <linux/unaligned/access_ok.h>
+
+#include <net/nfc/nfc.h>
+
+#include "nxp-nci.h"
+
+#define NXP_NCI_I2C_DRIVER_NAME	"nxp-nci_i2c"
+
+#define NXP_NCI_I2C_MAX_PAYLOAD	32
+
+struct nxp_nci_i2c_phy {
+	struct i2c_client *i2c_dev;
+	struct nci_dev *ndev;
+
+	unsigned int gpio_en;
+	unsigned int gpio_fw;
+
+	int hard_fault; /*
+			 * < 0 if hardware error occurred (e.g. i2c err)
+			 * and prevents normal operation.
+			 */
+};
+
+static int nxp_nci_i2c_set_mode(void *phy_id,
+				    enum nxp_nci_mode mode)
+{
+	struct nxp_nci_i2c_phy *phy = (struct nxp_nci_i2c_phy *) phy_id;
+
+	gpio_set_value(phy->gpio_fw, (mode == NXP_NCI_MODE_FW) ? 1 : 0);
+	gpio_set_value(phy->gpio_en, (mode != NXP_NCI_MODE_COLD) ? 1 : 0);
+	usleep_range(10000, 15000);
+
+	if (mode == NXP_NCI_MODE_COLD)
+		phy->hard_fault = 0;
+
+	return 0;
+}
+
+static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+	int r;
+	struct nxp_nci_i2c_phy *phy = phy_id;
+	struct i2c_client *client = phy->i2c_dev;
+
+	if (phy->hard_fault != 0)
+		return phy->hard_fault;
+
+	r = i2c_master_send(client, skb->data, skb->len);
+	if (r == -EREMOTEIO) {
+		/* Retry, chip was in standby */
+		usleep_range(110000, 120000);
+		r = i2c_master_send(client, skb->data, skb->len);
+	}
+
+	if (r < 0) {
+		nfc_err(&client->dev, "Error %d on I2C send\n", r);
+	} else if (r != skb->len) {
+		nfc_err(&client->dev,
+			"Invalid length sent: %u (expected %u)\n",
+			r, skb->len);
+		r = -EREMOTEIO;
+	} else {
+		/* Success but return 0 and not number of bytes */
+		r = 0;
+	}
+
+	return r;
+}
+
+static struct nxp_nci_phy_ops i2c_phy_ops = {
+	.set_mode = nxp_nci_i2c_set_mode,
+	.write = nxp_nci_i2c_write,
+};
+
+static int nxp_nci_i2c_fw_read(struct nxp_nci_i2c_phy *phy,
+			       struct sk_buff **skb)
+{
+	struct i2c_client *client = phy->i2c_dev;
+	u16 header;
+	size_t frame_len;
+	int r;
+
+	r = i2c_master_recv(client, (u8 *) &header, NXP_NCI_FW_HDR_LEN);
+	if (r < 0) {
+		goto fw_read_exit;
+	} else if (r != NXP_NCI_FW_HDR_LEN) {
+		nfc_err(&client->dev, "Incorrect header length: %u\n", r);
+		r = -EBADMSG;
+		goto fw_read_exit;
+	}
+
+	frame_len = (get_unaligned_be16(&header) & NXP_NCI_FW_FRAME_LEN_MASK) +
+		    NXP_NCI_FW_CRC_LEN;
+
+	*skb = alloc_skb(NXP_NCI_FW_HDR_LEN + frame_len, GFP_KERNEL);
+	if (*skb == NULL) {
+		r = -ENOMEM;
+		goto fw_read_exit;
+	}
+
+	memcpy(skb_put(*skb, NXP_NCI_FW_HDR_LEN), &header, NXP_NCI_FW_HDR_LEN);
+
+	r = i2c_master_recv(client, skb_put(*skb, frame_len), frame_len);
+	if (r != frame_len) {
+		nfc_err(&client->dev,
+			"Invalid frame length: %u (expected %zu)\n",
+			r, frame_len);
+		r = -EBADMSG;
+		goto fw_read_exit_free_skb;
+	}
+
+	return 0;
+
+fw_read_exit_free_skb:
+	kfree_skb(*skb);
+fw_read_exit:
+	return r;
+}
+
+static int nxp_nci_i2c_nci_read(struct nxp_nci_i2c_phy *phy,
+				struct sk_buff **skb)
+{
+	struct nci_ctrl_hdr header; /* May actually be a data header */
+	struct i2c_client *client = phy->i2c_dev;
+	int r;
+
+	r = i2c_master_recv(client, (u8 *) &header, NCI_CTRL_HDR_SIZE);
+	if (r < 0) {
+		goto nci_read_exit;
+	} else if (r != NCI_CTRL_HDR_SIZE) {
+		nfc_err(&client->dev, "Incorrect header length: %u\n", r);
+		r = -EBADMSG;
+		goto nci_read_exit;
+	}
+
+	*skb = alloc_skb(NCI_CTRL_HDR_SIZE + header.plen, GFP_KERNEL);
+	if (*skb == NULL) {
+		r = -ENOMEM;
+		goto nci_read_exit;
+	}
+
+	memcpy(skb_put(*skb, NCI_CTRL_HDR_SIZE), (void *) &header,
+	       NCI_CTRL_HDR_SIZE);
+
+	r = i2c_master_recv(client, skb_put(*skb, header.plen), header.plen);
+	if (r != header.plen) {
+		nfc_err(&client->dev,
+			"Invalid frame payload length: %u (expected %u)\n",
+			r, header.plen);
+		r = -EBADMSG;
+		goto nci_read_exit_free_skb;
+	}
+
+	return 0;
+
+nci_read_exit_free_skb:
+	kfree_skb(*skb);
+nci_read_exit:
+	return r;
+}
+
+static irqreturn_t nxp_nci_i2c_irq_thread_fn(int irq, void *phy_id)
+{
+	struct nxp_nci_i2c_phy *phy = phy_id;
+	struct i2c_client *client;
+	struct nxp_nci_info *info;
+
+	struct sk_buff *skb = NULL;
+	int r = 0;
+
+	if (!phy || !phy->ndev)
+		goto exit_irq_none;
+
+	client = phy->i2c_dev;
+
+	if (!client || irq != client->irq)
+		goto exit_irq_none;
+
+	info = nci_get_drvdata(phy->ndev);
+
+	if (!info)
+		goto exit_irq_none;
+
+	mutex_lock(&info->info_lock);
+
+	if (phy->hard_fault != 0)
+		goto exit_irq_handled;
+
+	switch (info->mode) {
+	case NXP_NCI_MODE_NCI:
+		r = nxp_nci_i2c_nci_read(phy, &skb);
+		break;
+	case NXP_NCI_MODE_FW:
+		r = nxp_nci_i2c_fw_read(phy, &skb);
+		break;
+	case NXP_NCI_MODE_COLD:
+		r = -EREMOTEIO;
+		break;
+	}
+
+	if (r == -EREMOTEIO) {
+		phy->hard_fault = r;
+		skb = NULL;
+	} else if (r < 0) {
+		nfc_err(&client->dev, "Read failed with error %d\n", r);
+		goto exit_irq_handled;
+	}
+
+	switch (info->mode) {
+	case NXP_NCI_MODE_NCI:
+		nci_recv_frame(phy->ndev, skb);
+		break;
+	case NXP_NCI_MODE_FW:
+		nxp_nci_fw_recv_frame(phy->ndev, skb);
+		break;
+	case NXP_NCI_MODE_COLD:
+		break;
+	}
+
+exit_irq_handled:
+	mutex_unlock(&info->info_lock);
+	return IRQ_HANDLED;
+exit_irq_none:
+	WARN_ON_ONCE(1);
+	return IRQ_NONE;
+}
+
+#ifdef CONFIG_OF
+
+static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
+{
+	struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client);
+	struct device_node *pp;
+	int r;
+
+	pp = client->dev.of_node;
+	if (!pp)
+		return -ENODEV;
+
+	r = of_get_named_gpio(pp, "enable-gpios", 0);
+	if (r == -EPROBE_DEFER)
+		r = of_get_named_gpio(pp, "enable-gpios", 0);
+	if (r < 0) {
+		nfc_err(&client->dev, "Failed to get EN gpio, error: %d\n", r);
+		return r;
+	}
+	phy->gpio_en = r;
+
+	r = of_get_named_gpio(pp, "firmware-gpios", 0);
+	if (r == -EPROBE_DEFER)
+		r = of_get_named_gpio(pp, "firmware-gpios", 0);
+	if (r < 0) {
+		nfc_err(&client->dev, "Failed to get FW gpio, error: %d\n", r);
+		return r;
+	}
+	phy->gpio_fw = r;
+
+	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;
+	}
+	client->irq = r;
+
+	return 0;
+}
+
+#else
+
+static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
+{
+	return -ENODEV;
+}
+
+#endif
+
+static int nxp_nci_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct nxp_nci_i2c_phy *phy;
+	struct nxp_nci_nfc_platform_data *pdata;
+	int r;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
+		r = -ENODEV;
+		goto probe_exit;
+	}
+
+	phy = devm_kzalloc(&client->dev, sizeof(struct nxp_nci_i2c_phy),
+			   GFP_KERNEL);
+	if (!phy) {
+		r = -ENOMEM;
+		goto probe_exit;
+	}
+
+	phy->i2c_dev = client;
+	i2c_set_clientdata(client, phy);
+
+	pdata = client->dev.platform_data;
+
+	if (!pdata && client->dev.of_node) {
+		r = nxp_nci_i2c_parse_devtree(client);
+		if (r < 0) {
+			nfc_err(&client->dev, "Failed to get DT data\n");
+			goto probe_exit;
+		}
+	} else if (pdata) {
+		phy->gpio_en = pdata->gpio_en;
+		phy->gpio_fw = pdata->gpio_fw;
+		client->irq = pdata->irq;
+	} else {
+		nfc_err(&client->dev, "No platform data\n");
+		r = -EINVAL;
+		goto probe_exit;
+	}
+
+	r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en,
+				  GPIOF_OUT_INIT_LOW, "nxp_nci_en");
+	if (r < 0)
+		goto probe_exit;
+
+	r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw,
+				  GPIOF_OUT_INIT_LOW, "nxp_nci_fw");
+	if (r < 0)
+		goto probe_exit;
+
+	r = nxp_nci_probe(phy, &client->dev, &i2c_phy_ops,
+			  NXP_NCI_I2C_MAX_PAYLOAD, &phy->ndev);
+	if (r < 0)
+		goto probe_exit;
+
+	r = request_threaded_irq(client->irq, NULL,
+				 nxp_nci_i2c_irq_thread_fn,
+				 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+				 NXP_NCI_I2C_DRIVER_NAME, phy);
+	if (r < 0)
+		nfc_err(&client->dev, "Unable to register IRQ handler\n");
+
+probe_exit:
+	return r;
+}
+
+static int nxp_nci_i2c_remove(struct i2c_client *client)
+{
+	struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client);
+
+	nxp_nci_remove(phy->ndev);
+	free_irq(client->irq, phy);
+
+	return 0;
+}
+
+static struct i2c_device_id nxp_nci_i2c_id_table[] = {
+	{"nxp-nci_i2c", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, nxp_nci_i2c_id_table);
+
+static const struct of_device_id of_nxp_nci_i2c_match[] = {
+	{ .compatible = "nxp,nxp-nci-i2c", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_nxp_nci_i2c_match);
+
+static struct i2c_driver nxp_nci_i2c_driver = {
+	.driver = {
+		   .name = NXP_NCI_I2C_DRIVER_NAME,
+		   .owner  = THIS_MODULE,
+		   .of_match_table = of_match_ptr(of_nxp_nci_i2c_match),
+		  },
+	.probe = nxp_nci_i2c_probe,
+	.id_table = nxp_nci_i2c_id_table,
+	.remove = nxp_nci_i2c_remove,
+};
+
+module_i2c_driver(nxp_nci_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("I2C driver for NXP NCI NFC controllers");
+MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");
diff --git a/drivers/nfc/nxp-nci/nxp-nci.h b/drivers/nfc/nxp-nci/nxp-nci.h
new file mode 100644
index 0000000..f1fecc4
--- /dev/null
+++ b/drivers/nfc/nxp-nci/nxp-nci.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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_NXP_NCI_H_
+#define __LOCAL_NXP_NCI_H_
+
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <linux/nfc.h>
+#include <linux/platform_data/nxp-nci.h>
+
+#include <net/nfc/nci_core.h>
+
+#define NXP_NCI_FW_HDR_LEN	2
+#define NXP_NCI_FW_CRC_LEN	2
+
+#define NXP_NCI_FW_FRAME_LEN_MASK	0x03FF
+
+enum nxp_nci_mode {
+	NXP_NCI_MODE_COLD,
+	NXP_NCI_MODE_NCI,
+	NXP_NCI_MODE_FW
+};
+
+struct nxp_nci_phy_ops {
+	int (*set_mode)(void *id, enum nxp_nci_mode mode);
+	int (*write)(void *id, struct sk_buff *skb);
+};
+
+struct nxp_nci_fw_info {
+	char name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+	const struct firmware *fw;
+
+	size_t size;
+	size_t written;
+
+	const u8 *data;
+	size_t frame_size;
+
+	struct work_struct work;
+	struct completion cmd_completion;
+
+	int cmd_result;
+};
+
+struct nxp_nci_info {
+	struct nci_dev *ndev;
+	void *phy_id;
+	struct device *pdev;
+
+	enum nxp_nci_mode mode;
+
+	struct nxp_nci_phy_ops *phy_ops;
+	unsigned int max_payload;
+
+	struct mutex info_lock;
+
+	struct nxp_nci_fw_info fw_info;
+};
+
+int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name);
+void nxp_nci_fw_work(struct work_struct *work);
+void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
+void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result);
+
+int nxp_nci_probe(void *phy_id, struct device *pdev,
+		  struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload,
+		  struct nci_dev **ndev);
+void nxp_nci_remove(struct nci_dev *ndev);
+
+#endif /* __LOCAL_NXP_NCI_H_ */
diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c
index d46a700..a03e4eb 100644
--- a/drivers/nfc/pn533.c
+++ b/drivers/nfc/pn533.c
@@ -1820,7 +1820,7 @@
 	if (IS_ERR(resp)) {
 		rc = PTR_ERR(resp);
 
-		nfc_err(&dev->interface->dev, "RF setting error %d", rc);
+		nfc_err(&dev->interface->dev, "RF setting error %d\n", rc);
 
 		return rc;
 	}
@@ -2554,8 +2554,10 @@
 	}
 
 	skb = pn533_build_response(dev);
-	if (!skb)
+	if (!skb) {
+		rc = -ENOMEM;
 		goto error;
+	}
 
 	arg->cb(arg->cb_context, skb, 0);
 	kfree(arg);
diff --git a/drivers/nfc/pn544/Kconfig b/drivers/nfc/pn544/Kconfig
index 85d3c94..d6d6a4a 100644
--- a/drivers/nfc/pn544/Kconfig
+++ b/drivers/nfc/pn544/Kconfig
@@ -14,6 +14,7 @@
 	  Say N if unsure.
 
 config NFC_PN544_I2C
+	depends on !KERNEL_3_13
 	tristate "NFC PN544 i2c support"
 	depends on m
 	depends on NFC_PN544 && I2C && NFC_SHDLC
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c
index fc02e8d..6fd986f 100644
--- a/drivers/nfc/pn544/i2c.c
+++ b/drivers/nfc/pn544/i2c.c
@@ -24,11 +24,13 @@
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
 #include <linux/of_irq.h>
+#include <linux/acpi.h>
 #include <linux/miscdevice.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/nfc.h>
 #include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
 #include <linux/platform_data/pn544.h>
 #include <asm/unaligned.h>
 
@@ -41,6 +43,11 @@
 #define PN544_I2C_FRAME_HEADROOM 1
 #define PN544_I2C_FRAME_TAILROOM 2
 
+/* GPIO names */
+#define PN544_GPIO_NAME_IRQ "pn544_irq"
+#define PN544_GPIO_NAME_FW  "pn544_fw"
+#define PN544_GPIO_NAME_EN  "pn544_en"
+
 /* framing in HCI mode */
 #define PN544_HCI_I2C_LLC_LEN		1
 #define PN544_HCI_I2C_LLC_CRC		2
@@ -58,6 +65,13 @@
 
 MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table);
 
+static const struct acpi_device_id pn544_hci_i2c_acpi_match[] = {
+	{"NXP5440", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(acpi, pn544_hci_i2c_acpi_match);
+
 #define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c"
 
 /*
@@ -195,18 +209,19 @@
 	nfc_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n");
 
 	/* Disable fw download */
-	gpio_set_value(phy->gpio_fw, 0);
+	gpio_set_value_cansleep(phy->gpio_fw, 0);
 
 	for (polarity = 0; polarity < 2; polarity++) {
 		phy->en_polarity = polarity;
 		retry = 3;
 		while (retry--) {
 			/* power off */
-			gpio_set_value(phy->gpio_en, !phy->en_polarity);
+			gpio_set_value_cansleep(phy->gpio_en,
+						!phy->en_polarity);
 			usleep_range(10000, 15000);
 
 			/* power on */
-			gpio_set_value(phy->gpio_en, phy->en_polarity);
+			gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity);
 			usleep_range(10000, 15000);
 
 			/* send reset */
@@ -225,13 +240,14 @@
 		"Could not detect nfc_en polarity, fallback to active high\n");
 
 out:
-	gpio_set_value(phy->gpio_en, !phy->en_polarity);
+	gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity);
 }
 
 static void pn544_hci_i2c_enable_mode(struct pn544_i2c_phy *phy, int run_mode)
 {
-	gpio_set_value(phy->gpio_fw, run_mode == PN544_FW_MODE ? 1 : 0);
-	gpio_set_value(phy->gpio_en, phy->en_polarity);
+	gpio_set_value_cansleep(phy->gpio_fw,
+				run_mode == PN544_FW_MODE ? 1 : 0);
+	gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity);
 	usleep_range(10000, 15000);
 
 	phy->run_mode = run_mode;
@@ -254,14 +270,14 @@
 {
 	struct pn544_i2c_phy *phy = phy_id;
 
-	gpio_set_value(phy->gpio_fw, 0);
-	gpio_set_value(phy->gpio_en, !phy->en_polarity);
+	gpio_set_value_cansleep(phy->gpio_fw, 0);
+	gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity);
 	usleep_range(10000, 15000);
 
-	gpio_set_value(phy->gpio_en, phy->en_polarity);
+	gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity);
 	usleep_range(10000, 15000);
 
-	gpio_set_value(phy->gpio_en, !phy->en_polarity);
+	gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity);
 	usleep_range(10000, 15000);
 
 	phy->powered = 0;
@@ -859,6 +875,90 @@
 	}
 }
 
+static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client)
+{
+	struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
+	const struct acpi_device_id *id;
+	struct gpio_desc *gpiod_en, *gpiod_irq, *gpiod_fw;
+	struct device *dev;
+	int ret;
+
+	if (!client)
+		return -EINVAL;
+
+	dev = &client->dev;
+
+	/* Match the struct device against a given list of ACPI IDs */
+	id = acpi_match_device(dev->driver->acpi_match_table, dev);
+
+	if (!id)
+		return -ENODEV;
+
+	/* Get EN GPIO from ACPI */
+	gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1);
+	if (IS_ERR(gpiod_en)) {
+		nfc_err(dev,
+			"Unable to get EN GPIO\n");
+		return -ENODEV;
+	}
+
+	phy->gpio_en  = desc_to_gpio(gpiod_en);
+
+	/* Configuration EN GPIO */
+	ret = gpiod_direction_output(gpiod_en, 0);
+	if (ret) {
+		nfc_err(dev, "Fail EN pin direction\n");
+		return ret;
+	}
+
+	/* Get FW GPIO from ACPI */
+	gpiod_fw = devm_gpiod_get_index(dev, PN544_GPIO_NAME_FW, 2);
+	if (IS_ERR(gpiod_fw)) {
+		nfc_err(dev,
+			"Unable to get FW GPIO\n");
+		return -ENODEV;
+	}
+
+	phy->gpio_fw  = desc_to_gpio(gpiod_fw);
+
+	/* Configuration FW GPIO */
+	ret = gpiod_direction_output(gpiod_fw, 0);
+	if (ret) {
+		nfc_err(dev, "Fail FW pin direction\n");
+		return ret;
+	}
+
+	/* Get IRQ GPIO */
+	gpiod_irq = devm_gpiod_get_index(dev, PN544_GPIO_NAME_IRQ, 0);
+	if (IS_ERR(gpiod_irq)) {
+		nfc_err(dev,
+			"Unable to get IRQ GPIO\n");
+		return -ENODEV;
+	}
+
+	phy->gpio_irq = desc_to_gpio(gpiod_irq);
+
+	/* Configure IRQ GPIO */
+	ret = gpiod_direction_input(gpiod_irq);
+	if (ret) {
+		nfc_err(dev, "Fail IRQ pin direction\n");
+		return ret;
+	}
+
+	/* Map the pin to an IRQ */
+	ret = gpiod_to_irq(gpiod_irq);
+	if (ret < 0) {
+		nfc_err(dev, "Fail pin IRQ mapping\n");
+		return ret;
+	}
+
+	nfc_info(dev, "GPIO resource, no:%d irq:%d\n",
+		 desc_to_gpio(gpiod_irq), ret);
+	client->irq = ret;
+
+	return 0;
+}
+
 #ifdef CONFIG_OF
 
 static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
@@ -884,7 +984,7 @@
 	phy->gpio_en = ret;
 
 	/* Configuration of EN GPIO */
-	ret = gpio_request(phy->gpio_en, "pn544_en");
+	ret = gpio_request(phy->gpio_en, PN544_GPIO_NAME_EN);
 	if (ret) {
 		nfc_err(&client->dev, "Fail EN pin\n");
 		goto err_dt;
@@ -906,7 +1006,7 @@
 	phy->gpio_fw = ret;
 
 	/* Configuration of FW GPIO */
-	ret = gpio_request(phy->gpio_fw, "pn544_fw");
+	ret = gpio_request(phy->gpio_fw, PN544_GPIO_NAME_FW);
 	if (ret) {
 		nfc_err(&client->dev, "Fail FW pin\n");
 		goto err_gpio_en;
@@ -962,11 +1062,8 @@
 
 	phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy),
 			   GFP_KERNEL);
-	if (!phy) {
-		nfc_err(&client->dev,
-			"Cannot allocate memory for pn544 i2c phy.\n");
+	if (!phy)
 		return -ENOMEM;
-	}
 
 	INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work);
 	phy->fw_work_state = FW_WORK_STATE_IDLE;
@@ -1001,6 +1098,14 @@
 		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);
+	/* Using ACPI */
+	} else if (ACPI_HANDLE(&client->dev)) {
+		r = pn544_hci_i2c_acpi_request_resources(client);
+		if (r) {
+			nfc_err(&client->dev,
+				"Cannot get ACPI data\n");
+			return r;
+		}
 	} else {
 		nfc_err(&client->dev, "No platform data\n");
 		return -EINVAL;
@@ -1080,6 +1185,7 @@
 		   .name = PN544_HCI_I2C_DRIVER_NAME,
 		   .owner  = THIS_MODULE,
 		   .of_match_table = of_match_ptr(of_pn544_i2c_match),
+		   .acpi_match_table = ACPI_PTR(pn544_hci_i2c_acpi_match),
 		  },
 	.probe = pn544_hci_i2c_probe,
 	.id_table = pn544_hci_i2c_id_table,
diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c
index 9c8051d..12e819d 100644
--- a/drivers/nfc/pn544/pn544.c
+++ b/drivers/nfc/pn544/pn544.c
@@ -724,10 +724,11 @@
  * <= 0: driver handled the event, skb consumed
  *    1: driver does not handle the event, please do standard processing
  */
-static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event,
+static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
 				    struct sk_buff *skb)
 {
 	struct sk_buff *rgb_skb = NULL;
+	u8 gate = hdev->pipes[pipe].gate;
 	int r;
 
 	pr_debug("hci event %d\n", event);
diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c
index 4ac4d31..87d5099 100644
--- a/drivers/nfc/port100.c
+++ b/drivers/nfc/port100.c
@@ -604,11 +604,11 @@
 	case -ECONNRESET:
 	case -ENOENT:
 		nfc_err(&dev->interface->dev,
-			"The urb has been canceled (status %d)", urb->status);
+			"The urb has been canceled (status %d)\n", urb->status);
 		goto sched_wq;
 	case -ESHUTDOWN:
 	default:
-		nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+		nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
 			urb->status);
 		goto sched_wq;
 	}
@@ -616,7 +616,7 @@
 	in_frame = dev->in_urb->transfer_buffer;
 
 	if (!port100_rx_frame_is_valid(in_frame)) {
-		nfc_err(&dev->interface->dev, "Received an invalid frame");
+		nfc_err(&dev->interface->dev, "Received an invalid frame\n");
 		cmd->status = -EIO;
 		goto sched_wq;
 	}
@@ -626,7 +626,7 @@
 
 	if (!port100_rx_frame_is_cmd_response(dev, in_frame)) {
 		nfc_err(&dev->interface->dev,
-			"It's not the response to the last command");
+			"It's not the response to the last command\n");
 		cmd->status = -EIO;
 		goto sched_wq;
 	}
@@ -657,11 +657,11 @@
 	case -ECONNRESET:
 	case -ENOENT:
 		nfc_err(&dev->interface->dev,
-			"The urb has been stopped (status %d)", urb->status);
+			"The urb has been stopped (status %d)\n", urb->status);
 		goto sched_wq;
 	case -ESHUTDOWN:
 	default:
-		nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+		nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
 			urb->status);
 		goto sched_wq;
 	}
@@ -669,7 +669,7 @@
 	in_frame = dev->in_urb->transfer_buffer;
 
 	if (!port100_rx_frame_is_ack(in_frame)) {
-		nfc_err(&dev->interface->dev, "Received an invalid ack");
+		nfc_err(&dev->interface->dev, "Received an invalid ack\n");
 		cmd->status = -EIO;
 		goto sched_wq;
 	}
@@ -677,7 +677,7 @@
 	rc = port100_submit_urb_for_response(dev, GFP_ATOMIC);
 	if (rc) {
 		nfc_err(&dev->interface->dev,
-			"usb_submit_urb failed with result %d", rc);
+			"usb_submit_urb failed with result %d\n", rc);
 		cmd->status = rc;
 		goto sched_wq;
 	}
@@ -873,11 +873,11 @@
 	case -ECONNRESET:
 	case -ENOENT:
 		nfc_err(&dev->interface->dev,
-			"The urb has been stopped (status %d)", urb->status);
+			"The urb has been stopped (status %d)\n", urb->status);
 		break;
 	case -ESHUTDOWN:
 	default:
-		nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+		nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
 			urb->status);
 	}
 }
@@ -1094,7 +1094,7 @@
 
 	if (resp->len < 4) {
 		nfc_err(&dev->interface->dev,
-			"Invalid packet length received.\n");
+			"Invalid packet length received\n");
 		rc = -EIO;
 		goto error;
 	}
@@ -1250,7 +1250,7 @@
 		       PORT100_MDAA_TGT_WAS_ACTIVATED_MASK;
 		break;
 	default:
-		nfc_err(&dev->interface->dev, "Unknonwn command type.\n");
+		nfc_err(&dev->interface->dev, "Unknown command type\n");
 		return false;
 	}
 
@@ -1481,7 +1481,7 @@
 	cmd_type_mask = port100_get_command_type_mask(dev);
 	if (!cmd_type_mask) {
 		nfc_err(&interface->dev,
-			"Could not get supported command types.\n");
+			"Could not get supported command types\n");
 		rc = -ENODEV;
 		goto error;
 	}
@@ -1494,7 +1494,7 @@
 	rc = port100_set_command_type(dev, dev->cmd_type);
 	if (rc) {
 		nfc_err(&interface->dev,
-			"The device does not support command type %u.\n",
+			"The device does not support command type %u\n",
 			dev->cmd_type);
 		goto error;
 	}
@@ -1502,7 +1502,7 @@
 	fw_version = port100_get_firmware_version(dev);
 	if (!fw_version)
 		nfc_err(&interface->dev,
-			"Could not get device firmware version.\n");
+			"Could not get device firmware version\n");
 
 	nfc_info(&interface->dev,
 		 "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n",
@@ -1515,7 +1515,7 @@
 							   dev->skb_tailroom);
 	if (!dev->nfc_digital_dev) {
 		nfc_err(&interface->dev,
-			"Could not allocate nfc_digital_dev.\n");
+			"Could not allocate nfc_digital_dev\n");
 		rc = -ENOMEM;
 		goto error;
 	}
@@ -1526,7 +1526,7 @@
 	rc = nfc_digital_register_device(dev->nfc_digital_dev);
 	if (rc) {
 		nfc_err(&interface->dev,
-			"Could not register digital device.\n");
+			"Could not register digital device\n");
 		goto free_nfc_dev;
 	}
 
@@ -1562,7 +1562,7 @@
 
 	kfree(dev->cmd);
 
-	nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected");
+	nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected\n");
 }
 
 static struct usb_driver port100_driver = {
diff --git a/drivers/nfc/st21nfca/Makefile b/drivers/nfc/st21nfca/Makefile
index a7e2c4e..9410bc9 100644
--- a/drivers/nfc/st21nfca/Makefile
+++ b/drivers/nfc/st21nfca/Makefile
@@ -2,7 +2,7 @@
 # Makefile for ST21NFCA HCI based NFC driver
 #
 
-st21nfca_hci-objs = st21nfca.o st21nfca_dep.o
+st21nfca_hci-objs = st21nfca.o st21nfca_dep.o st21nfca_se.o
 obj-$(CPTCFG_NFC_ST21NFCA)     += st21nfca_hci.o
 
 st21nfca_i2c-objs  = i2c.o
diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c
index 0572208..a321439 100644
--- a/drivers/nfc/st21nfca/i2c.c
+++ b/drivers/nfc/st21nfca/i2c.c
@@ -74,6 +74,8 @@
 	unsigned int gpio_ena;
 	unsigned int irq_polarity;
 
+	struct st21nfca_se_status se_status;
+
 	struct sk_buff *pending_skb;
 	int current_read_len;
 	/*
@@ -537,6 +539,11 @@
 
 	phy->irq_polarity = irq_get_trigger_type(client->irq);
 
+	phy->se_status.is_ese_present =
+				of_property_read_bool(pp, "ese-present");
+	phy->se_status.is_uicc_present =
+				of_property_read_bool(pp, "uicc-present");
+
 	return 0;
 }
 #else
@@ -571,6 +578,9 @@
 		}
 	}
 
+	phy->se_status.is_ese_present = pdata->is_ese_present;
+	phy->se_status.is_uicc_present = pdata->is_uicc_present;
+
 	return 0;
 }
 
@@ -591,11 +601,8 @@
 
 	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");
+	if (!phy)
 		return -ENOMEM;
-	}
 
 	phy->i2c_dev = client;
 	phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL);
@@ -641,8 +648,11 @@
 	}
 
 	return st21nfca_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
-			       ST21NFCA_FRAME_HEADROOM, ST21NFCA_FRAME_TAILROOM,
-			       ST21NFCA_HCI_LLC_MAX_PAYLOAD, &phy->hdev);
+					ST21NFCA_FRAME_HEADROOM,
+					ST21NFCA_FRAME_TAILROOM,
+					ST21NFCA_HCI_LLC_MAX_PAYLOAD,
+					&phy->hdev,
+					&phy->se_status);
 }
 
 static int st21nfca_hci_i2c_remove(struct i2c_client *client)
@@ -661,6 +671,7 @@
 
 #ifdef CONFIG_OF
 static const struct of_device_id of_st21nfca_i2c_match[] = {
+	{ .compatible = "st,st21nfca-i2c", },
 	{ .compatible = "st,st21nfca_i2c", },
 	{}
 };
diff --git a/drivers/nfc/st21nfca/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c
index f2596c8..d251f72 100644
--- a/drivers/nfc/st21nfca/st21nfca.c
+++ b/drivers/nfc/st21nfca/st21nfca.c
@@ -23,6 +23,7 @@
 
 #include "st21nfca.h"
 #include "st21nfca_dep.h"
+#include "st21nfca_se.h"
 
 #define DRIVER_DESC "HCI NFC driver for ST21NFCA"
 
@@ -62,7 +63,6 @@
 #define ST21NFCA_RF_CARD_F_DATARATE		0x08
 #define ST21NFCA_RF_CARD_F_DATARATE_212_424	0x01
 
-#define ST21NFCA_DEVICE_MGNT_GATE		0x01
 #define ST21NFCA_DEVICE_MGNT_PIPE		0x02
 
 #define ST21NFCA_DM_GETINFO			0x13
@@ -78,6 +78,11 @@
 
 #define ST21NFCA_NFC_MODE			0x03	/* NFC_MODE parameter*/
 
+#define ST21NFCA_EVT_HOT_PLUG			0x03
+#define ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80)
+
+#define ST21NFCA_SE_TO_PIPES			2000
+
 static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES);
 
 static struct nfc_hci_gate st21nfca_gates[] = {
@@ -92,6 +97,10 @@
 	{ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE},
 	{ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
 	{ST21NFCA_RF_CARD_F_GATE, NFC_HCI_INVALID_PIPE},
+
+	/* Secure element pipes are created by secure element host */
+	{ST21NFCA_CONNECTIVITY_GATE, NFC_HCI_DO_NOT_CREATE_PIPE},
+	{ST21NFCA_APDU_READER_GATE, NFC_HCI_DO_NOT_CREATE_PIPE},
 };
 
 struct st21nfca_pipe_info {
@@ -118,18 +127,6 @@
 		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.
@@ -148,7 +145,8 @@
 	 * 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);
+				ST21NFCA_DEVICE_MGNT_GATE,
+				ST21NFCA_DEVICE_MGNT_PIPE);
 	if (r < 0)
 		goto free_info;
 
@@ -179,17 +177,28 @@
 		 * - destination gid (1byte)
 		 */
 		info = (struct st21nfca_pipe_info *) skb_pipe_info->data;
+		if (info->dst_gate_id == ST21NFCA_APDU_READER_GATE &&
+			info->src_host_id != ST21NFCA_ESE_HOST_ID) {
+			pr_err("Unexpected apdu_reader pipe on host %x\n",
+				info->src_host_id);
+			continue;
+		}
+
 		for (j = 0; (j < ARRAY_SIZE(st21nfca_gates)) &&
-			(st21nfca_gates[j].gate != info->dst_gate_id);
-			j++)
+			(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;
+							st21nfca_gates[j].pipe;
+			hdev->pipes[st21nfca_gates[j].pipe].gate =
+							st21nfca_gates[j].gate;
+			hdev->pipes[st21nfca_gates[j].pipe].dest_host =
+							info->src_host_id;
 		}
 	}
 
@@ -199,7 +208,7 @@
 	 */
 	if (skb_pipe_list->len + 3 < ARRAY_SIZE(st21nfca_gates)) {
 		for (i = skb_pipe_list->len + 3;
-				i < ARRAY_SIZE(st21nfca_gates); i++) {
+				i < ARRAY_SIZE(st21nfca_gates) - 2; i++) {
 			r = nfc_hci_connect_gate(hdev,
 					NFC_HCI_HOST_CONTROLLER_ID,
 					st21nfca_gates[i].gate,
@@ -212,7 +221,6 @@
 	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;
 }
@@ -257,16 +265,33 @@
 
 static int st21nfca_hci_ready(struct nfc_hci_dev *hdev)
 {
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
 	struct sk_buff *skb;
 
 	u8 param;
+	u8 white_list[2];
+	int wl_size = 0;
 	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;
+	if (info->se_status->is_ese_present &&
+		info->se_status->is_uicc_present) {
+		white_list[wl_size++] = NFC_HCI_UICC_HOST_ID;
+		white_list[wl_size++] = ST21NFCA_ESE_HOST_ID;
+	} else if (!info->se_status->is_ese_present &&
+			 info->se_status->is_uicc_present) {
+		white_list[wl_size++] = NFC_HCI_UICC_HOST_ID;
+	} else if (info->se_status->is_ese_present &&
+			!info->se_status->is_uicc_present) {
+		white_list[wl_size++] = ST21NFCA_ESE_HOST_ID;
+	}
+
+	if (wl_size) {
+		r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
+					NFC_HCI_ADMIN_WHITELIST,
+					(u8 *) &white_list, wl_size);
+		if (r < 0)
+			return r;
+	}
 
 	/* Set NFC_MODE in device management gate to enable */
 	r = nfc_hci_get_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
@@ -274,8 +299,9 @@
 	if (r < 0)
 		return r;
 
-	if (skb->data[0] == 0) {
-		kfree_skb(skb);
+	param = skb->data[0];
+	kfree_skb(skb);
+	if (param == 0) {
 		param = 1;
 
 		r = nfc_hci_set_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
@@ -417,9 +443,12 @@
 			r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
 					      ST21NFCA_RF_CARD_F_DATARATE,
 					      param, 1);
-			if (r < 0)
+			if (r < 0) {
+				kfree_skb(datarate_skb);
 				return r;
+			}
 		}
+		kfree_skb(datarate_skb);
 
 		/*
 		 * Configure sens_res
@@ -543,7 +572,7 @@
 	return r;
 }
 
-static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate,
+static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *uid,
 				       int *len)
 {
 	int r;
@@ -559,7 +588,7 @@
 		goto exit;
 	}
 
-	gate = uid_skb->data;
+	memcpy(uid, uid_skb->data, uid_skb->len);
 	*len = uid_skb->len;
 exit:
 	kfree_skb(uid_skb);
@@ -673,15 +702,15 @@
 						struct nfc_target *target)
 {
 	int r;
-	struct sk_buff *nfcid2_skb = NULL, *nfcid1_skb;
+	struct sk_buff *nfcid_skb = NULL;
 
 	if (gate == ST21NFCA_RF_READER_F_GATE) {
 		r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
-				ST21NFCA_RF_READER_F_NFCID2, &nfcid2_skb);
+				ST21NFCA_RF_READER_F_NFCID2, &nfcid_skb);
 		if (r < 0)
 			goto exit;
 
-		if (nfcid2_skb->len > NFC_SENSF_RES_MAXSIZE) {
+		if (nfcid_skb->len > NFC_SENSF_RES_MAXSIZE) {
 			r = -EPROTO;
 			goto exit;
 		}
@@ -693,11 +722,11 @@
 		 * - After the reception of SEL_RES with NFCIP-1 compliant bit
 		 * set for type A frame NFCID1 will be updated
 		 */
-		if (nfcid2_skb->len > 0) {
+		if (nfcid_skb->len > 0) {
 			/* P2P in type F */
-			memcpy(target->sensf_res, nfcid2_skb->data,
-				nfcid2_skb->len);
-			target->sensf_res_len = nfcid2_skb->len;
+			memcpy(target->sensf_res, nfcid_skb->data,
+				nfcid_skb->len);
+			target->sensf_res_len = nfcid_skb->len;
 			/* NFC Forum Digital Protocol Table 44 */
 			if (target->sensf_res[0] == 0x01 &&
 			    target->sensf_res[1] == 0xfe)
@@ -707,27 +736,28 @@
 				target->supported_protocols =
 							NFC_PROTO_FELICA_MASK;
 		} else {
+			kfree_skb(nfcid_skb);
 			/* P2P in type A */
 			r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
 					ST21NFCA_RF_READER_F_NFCID1,
-					&nfcid1_skb);
+					&nfcid_skb);
 			if (r < 0)
 				goto exit;
 
-			if (nfcid1_skb->len > NFC_NFCID1_MAXSIZE) {
+			if (nfcid_skb->len > NFC_NFCID1_MAXSIZE) {
 				r = -EPROTO;
 				goto exit;
 			}
-			memcpy(target->sensf_res, nfcid1_skb->data,
-				nfcid1_skb->len);
-			target->sensf_res_len = nfcid1_skb->len;
+			memcpy(target->sensf_res, nfcid_skb->data,
+				nfcid_skb->len);
+			target->sensf_res_len = nfcid_skb->len;
 			target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
 		}
 		target->hci_reader_gate = ST21NFCA_RF_READER_F_GATE;
 	}
 	r = 1;
 exit:
-	kfree_skb(nfcid2_skb);
+	kfree_skb(nfcid_skb);
 	return r;
 }
 
@@ -829,24 +859,82 @@
 	}
 }
 
+static void st21nfca_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
+				struct sk_buff *skb)
+{
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+	u8 gate = hdev->pipes[pipe].gate;
+
+	pr_debug("cmd: %x\n", cmd);
+
+	switch (cmd) {
+	case NFC_HCI_ANY_OPEN_PIPE:
+		if (gate != ST21NFCA_APDU_READER_GATE &&
+			hdev->pipes[pipe].dest_host != NFC_HCI_UICC_HOST_ID)
+			info->se_info.count_pipes++;
+
+		if (info->se_info.count_pipes == info->se_info.expected_pipes) {
+			del_timer_sync(&info->se_info.se_active_timer);
+			info->se_info.se_active = false;
+			info->se_info.count_pipes = 0;
+			complete(&info->se_info.req_completion);
+		}
+	break;
+	}
+}
+
+static int st21nfca_admin_event_received(struct nfc_hci_dev *hdev, u8 event,
+					struct sk_buff *skb)
+{
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+	pr_debug("admin event: %x\n", event);
+
+	switch (event) {
+	case ST21NFCA_EVT_HOT_PLUG:
+		if (info->se_info.se_active) {
+			if (!ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(skb)) {
+				del_timer_sync(&info->se_info.se_active_timer);
+				info->se_info.se_active = false;
+				complete(&info->se_info.req_completion);
+			} else {
+				mod_timer(&info->se_info.se_active_timer,
+					jiffies +
+					msecs_to_jiffies(ST21NFCA_SE_TO_PIPES));
+			}
+		}
+	break;
+	}
+	kfree_skb(skb);
+	return 0;
+}
+
 /*
  * Returns:
  * <= 0: driver handled the event, skb consumed
  *    1: driver does not handle the event, please do standard processing
  */
-static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
+static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe,
 				       u8 event, struct sk_buff *skb)
 {
+	u8 gate = hdev->pipes[pipe].gate;
+	u8 host = hdev->pipes[pipe].dest_host;
+
 	pr_debug("hci event: %d gate: %x\n", event, gate);
 
 	switch (gate) {
+	case NFC_HCI_ADMIN_GATE:
+		return st21nfca_admin_event_received(hdev, event, skb);
 	case ST21NFCA_RF_CARD_F_GATE:
 		return st21nfca_dep_event_received(hdev, event, skb);
+	case ST21NFCA_CONNECTIVITY_GATE:
+		return st21nfca_connectivity_event_received(hdev, host,
+							event, skb);
+	case ST21NFCA_APDU_READER_GATE:
+		return st21nfca_apdu_reader_event_received(hdev, event, skb);
 	default:
 		return 1;
 	}
-	kfree_skb(skb);
-	return 0;
 }
 
 static struct nfc_hci_ops st21nfca_hci_ops = {
@@ -865,11 +953,17 @@
 	.tm_send = st21nfca_hci_tm_send,
 	.check_presence = st21nfca_hci_check_presence,
 	.event_received = st21nfca_hci_event_received,
+	.cmd_received = st21nfca_hci_cmd_received,
+	.discover_se = st21nfca_hci_discover_se,
+	.enable_se = st21nfca_hci_enable_se,
+	.disable_se = st21nfca_hci_disable_se,
+	.se_io = st21nfca_hci_se_io,
 };
 
 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)
+		       int phy_payload, struct nfc_hci_dev **hdev,
+			   struct st21nfca_se_status *se_status)
 {
 	struct st21nfca_hci_info *info;
 	int r = 0;
@@ -929,6 +1023,8 @@
 		goto err_alloc_hdev;
 	}
 
+	info->se_status = se_status;
+
 	nfc_hci_set_clientdata(info->hdev, info);
 
 	r = nfc_hci_register_device(info->hdev);
@@ -937,6 +1033,7 @@
 
 	*hdev = info->hdev;
 	st21nfca_dep_init(info->hdev);
+	st21nfca_se_init(info->hdev);
 
 	return 0;
 
@@ -955,6 +1052,7 @@
 	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
 
 	st21nfca_dep_deinit(hdev);
+	st21nfca_se_deinit(hdev);
 	nfc_hci_unregister_device(hdev);
 	nfc_hci_free_device(hdev);
 	kfree(info);
diff --git a/drivers/nfc/st21nfca/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h
index 7c2a852..15a78d3 100644
--- a/drivers/nfc/st21nfca/st21nfca.h
+++ b/drivers/nfc/st21nfca/st21nfca.h
@@ -20,6 +20,7 @@
 #include <net/nfc/hci.h>
 
 #include "st21nfca_dep.h"
+#include "st21nfca_se.h"
 
 #define HCI_MODE 0
 
@@ -51,9 +52,15 @@
 
 #define ST21NFCA_NUM_DEVICES 256
 
+struct st21nfca_se_status {
+	bool is_ese_present;
+	bool is_uicc_present;
+};
+
 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);
+		       int phy_payload, struct nfc_hci_dev **hdev,
+			   struct st21nfca_se_status *se_status);
 void st21nfca_hci_remove(struct nfc_hci_dev *hdev);
 
 enum st21nfca_state {
@@ -66,6 +73,7 @@
 	void *phy_id;
 
 	struct nfc_hci_dev *hdev;
+	struct st21nfca_se_status *se_status;
 
 	enum st21nfca_state state;
 
@@ -76,13 +84,16 @@
 	void *async_cb_context;
 
 	struct st21nfca_dep_info dep_info;
+	struct st21nfca_se_info se_info;
 };
 
 /* Reader RF commands */
-#define ST21NFCA_WR_XCHG_DATA            0x10
+#define ST21NFCA_WR_XCHG_DATA           0x10
 
-#define ST21NFCA_RF_READER_F_GATE               0x14
-
-#define ST21NFCA_RF_CARD_F_GATE 0x24
+#define ST21NFCA_DEVICE_MGNT_GATE       0x01
+#define ST21NFCA_RF_READER_F_GATE       0x14
+#define ST21NFCA_RF_CARD_F_GATE			0x24
+#define ST21NFCA_APDU_READER_GATE		0xf0
+#define ST21NFCA_CONNECTIVITY_GATE		0x41
 
 #endif /* __LOCAL_ST21NFCA_H_ */
diff --git a/drivers/nfc/st21nfca/st21nfca_se.c b/drivers/nfc/st21nfca/st21nfca_se.c
new file mode 100644
index 0000000..3197e9b
--- /dev/null
+++ b/drivers/nfc/st21nfca/st21nfca_se.c
@@ -0,0 +1,420 @@
+/*
+ * 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 <net/nfc/hci.h>
+
+#include "st21nfca.h"
+#include "st21nfca_se.h"
+
+#define ST21NFCA_EVT_UICC_ACTIVATE		0x10
+#define ST21NFCA_EVT_UICC_DEACTIVATE	0x13
+#define ST21NFCA_EVT_SE_HARD_RESET		0x20
+#define ST21NFCA_EVT_SE_SOFT_RESET		0x11
+#define ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER	0x21
+#define ST21NFCA_EVT_SE_ACTIVATE		0x22
+#define ST21NFCA_EVT_SE_DEACTIVATE		0x23
+
+#define ST21NFCA_EVT_TRANSMIT_DATA		0x10
+#define ST21NFCA_EVT_WTX_REQUEST		0x11
+
+#define ST21NFCA_EVT_CONNECTIVITY		0x10
+#define ST21NFCA_EVT_TRANSACTION		0x12
+
+#define ST21NFCA_ESE_HOST_ID			0xc0
+
+#define ST21NFCA_SE_TO_HOT_PLUG			1000
+/* Connectivity pipe only */
+#define ST21NFCA_SE_COUNT_PIPE_UICC		0x01
+/* Connectivity + APDU Reader pipe */
+#define ST21NFCA_SE_COUNT_PIPE_EMBEDDED	0x02
+
+#define ST21NFCA_SE_MODE_OFF			0x00
+#define ST21NFCA_SE_MODE_ON				0x01
+
+#define ST21NFCA_PARAM_ATR				0x01
+#define ST21NFCA_ATR_DEFAULT_BWI		0x04
+
+/*
+ * WT = 2^BWI/10[s], convert into msecs and add a secure
+ * room by increasing by 2 this timeout
+ */
+#define ST21NFCA_BWI_TO_TIMEOUT(x)		((1 << x) * 200)
+#define ST21NFCA_ATR_GET_Y_FROM_TD(x)	(x >> 4)
+
+/* If TA is present bit 0 is set */
+#define ST21NFCA_ATR_TA_PRESENT(x) (x & 0x01)
+/* If TB is present bit 1 is set */
+#define ST21NFCA_ATR_TB_PRESENT(x) (x & 0x02)
+
+static u8 st21nfca_se_get_bwi(struct nfc_hci_dev *hdev)
+{
+	int i;
+	u8 td;
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+	/* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */
+	for (i = 1; i < ST21NFCA_ESE_MAX_LENGTH; i++) {
+		td = ST21NFCA_ATR_GET_Y_FROM_TD(info->se_info.atr[i]);
+		if (ST21NFCA_ATR_TA_PRESENT(td))
+			i++;
+		if (ST21NFCA_ATR_TB_PRESENT(td)) {
+			i++;
+			return info->se_info.atr[i] >> 4;
+		}
+	}
+	return ST21NFCA_ATR_DEFAULT_BWI;
+}
+
+static void st21nfca_se_get_atr(struct nfc_hci_dev *hdev)
+{
+	int r;
+	struct sk_buff *skb;
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+	r = nfc_hci_get_param(hdev, ST21NFCA_APDU_READER_GATE,
+			ST21NFCA_PARAM_ATR, &skb);
+	if (r < 0)
+		return;
+
+	if (skb->len <= ST21NFCA_ESE_MAX_LENGTH) {
+		memcpy(info->se_info.atr, skb->data, skb->len);
+		info->se_info.wt_timeout =
+			ST21NFCA_BWI_TO_TIMEOUT(st21nfca_se_get_bwi(hdev));
+	}
+	kfree_skb(skb);
+}
+
+static int st21nfca_hci_control_se(struct nfc_hci_dev *hdev, u32 se_idx,
+				u8 state)
+{
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+	int r;
+	struct sk_buff *sk_host_list;
+	u8 se_event, host_id;
+
+	switch (se_idx) {
+	case NFC_HCI_UICC_HOST_ID:
+		se_event = (state == ST21NFCA_SE_MODE_ON ?
+					ST21NFCA_EVT_UICC_ACTIVATE :
+					ST21NFCA_EVT_UICC_DEACTIVATE);
+
+		info->se_info.count_pipes = 0;
+		info->se_info.expected_pipes = ST21NFCA_SE_COUNT_PIPE_UICC;
+		break;
+	case ST21NFCA_ESE_HOST_ID:
+		se_event = (state == ST21NFCA_SE_MODE_ON ?
+					ST21NFCA_EVT_SE_ACTIVATE :
+					ST21NFCA_EVT_SE_DEACTIVATE);
+
+		info->se_info.count_pipes = 0;
+		info->se_info.expected_pipes = ST21NFCA_SE_COUNT_PIPE_EMBEDDED;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * Wait for an EVT_HOT_PLUG in order to
+	 * retrieve a relevant host list.
+	 */
+	reinit_completion(&info->se_info.req_completion);
+	r = nfc_hci_send_event(hdev, ST21NFCA_DEVICE_MGNT_GATE, se_event,
+			       NULL, 0);
+	if (r < 0)
+		return r;
+
+	mod_timer(&info->se_info.se_active_timer, jiffies +
+		msecs_to_jiffies(ST21NFCA_SE_TO_HOT_PLUG));
+	info->se_info.se_active = true;
+
+	/* Ignore return value and check in any case the host_list */
+	wait_for_completion_interruptible(&info->se_info.req_completion);
+
+	r = nfc_hci_get_param(hdev, NFC_HCI_ADMIN_GATE,
+			NFC_HCI_ADMIN_HOST_LIST,
+			&sk_host_list);
+	if (r < 0)
+		return r;
+
+	host_id = sk_host_list->data[sk_host_list->len - 1];
+	kfree_skb(sk_host_list);
+
+	if (state == ST21NFCA_SE_MODE_ON && host_id == se_idx)
+		return se_idx;
+	else if (state == ST21NFCA_SE_MODE_OFF && host_id != se_idx)
+		return se_idx;
+
+	return -1;
+}
+
+int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev)
+{
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+	int se_count = 0;
+
+	if (info->se_status->is_uicc_present) {
+		nfc_add_se(hdev->ndev, NFC_HCI_UICC_HOST_ID, NFC_SE_UICC);
+		se_count++;
+	}
+
+	if (info->se_status->is_ese_present) {
+		nfc_add_se(hdev->ndev, ST21NFCA_ESE_HOST_ID, NFC_SE_EMBEDDED);
+		se_count++;
+	}
+
+	return !se_count;
+}
+EXPORT_SYMBOL(st21nfca_hci_discover_se);
+
+int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx)
+{
+	int r;
+
+	/*
+	 * According to upper layer, se_idx == NFC_SE_UICC when
+	 * info->se_status->is_uicc_enable is true should never happen.
+	 * Same for eSE.
+	 */
+	r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_ON);
+
+	if (r == ST21NFCA_ESE_HOST_ID) {
+		st21nfca_se_get_atr(hdev);
+		r = nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE,
+				ST21NFCA_EVT_SE_SOFT_RESET, NULL, 0);
+		if (r < 0)
+			return r;
+	} else if (r < 0) {
+		/*
+		 * The activation tentative failed, the secure element
+		 * is not connected. Remove from the list.
+		 */
+		nfc_remove_se(hdev->ndev, se_idx);
+		return r;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(st21nfca_hci_enable_se);
+
+int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx)
+{
+	int r;
+
+	/*
+	 * According to upper layer, se_idx == NFC_SE_UICC when
+	 * info->se_status->is_uicc_enable is true should never happen
+	 * Same for eSE.
+	 */
+	r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_OFF);
+	if (r < 0)
+		return r;
+
+	return 0;
+}
+EXPORT_SYMBOL(st21nfca_hci_disable_se);
+
+int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx,
+			u8 *apdu, size_t apdu_length,
+			se_io_cb_t cb, void *cb_context)
+{
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+	pr_debug("se_io %x\n", se_idx);
+
+	switch (se_idx) {
+	case ST21NFCA_ESE_HOST_ID:
+		info->se_info.cb = cb;
+		info->se_info.cb_context = cb_context;
+		mod_timer(&info->se_info.bwi_timer, jiffies +
+			  msecs_to_jiffies(info->se_info.wt_timeout));
+		info->se_info.bwi_active = true;
+		return nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE,
+					ST21NFCA_EVT_TRANSMIT_DATA,
+					apdu, apdu_length);
+	default:
+		return -ENODEV;
+	}
+}
+EXPORT_SYMBOL(st21nfca_hci_se_io);
+
+static void st21nfca_se_wt_timeout(unsigned long data)
+{
+	/*
+	 * No answer from the secure element
+	 * within the defined timeout.
+	 * Let's send a reset request as recovery procedure.
+	 * According to the situation, we first try to send a software reset
+	 * to the secure element. If the next command is still not
+	 * answering in time, we send to the CLF a secure element hardware
+	 * reset request.
+	 */
+	/* hardware reset managed through VCC_UICC_OUT power supply */
+	u8 param = 0x01;
+	struct st21nfca_hci_info *info = (struct st21nfca_hci_info *) data;
+
+	pr_debug("\n");
+
+	info->se_info.bwi_active = false;
+
+	if (!info->se_info.xch_error) {
+		info->se_info.xch_error = true;
+		nfc_hci_send_event(info->hdev, ST21NFCA_APDU_READER_GATE,
+				ST21NFCA_EVT_SE_SOFT_RESET, NULL, 0);
+	} else {
+		info->se_info.xch_error = false;
+		nfc_hci_send_event(info->hdev, ST21NFCA_DEVICE_MGNT_GATE,
+				ST21NFCA_EVT_SE_HARD_RESET, &param, 1);
+	}
+	info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME);
+}
+
+static void st21nfca_se_activation_timeout(unsigned long data)
+{
+	struct st21nfca_hci_info *info = (struct st21nfca_hci_info *) data;
+
+	pr_debug("\n");
+
+	info->se_info.se_active = false;
+
+	complete(&info->se_info.req_completion);
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ *    1: driver does not handle the event, please do standard processing
+ */
+int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
+				u8 event, struct sk_buff *skb)
+{
+	int r = 0;
+	struct device *dev = &hdev->ndev->dev;
+	struct nfc_evt_transaction *transaction;
+
+	pr_debug("connectivity gate event: %x\n", event);
+
+	switch (event) {
+	case ST21NFCA_EVT_CONNECTIVITY:
+		break;
+	case ST21NFCA_EVT_TRANSACTION:
+		/*
+		 * According to specification etsi 102 622
+		 * 11.2.2.4 EVT_TRANSACTION Table 52
+		 * Description	Tag	Length
+		 * AID		81	5 to 16
+		 * PARAMETERS	82	0 to 255
+		 */
+		if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
+		    skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
+			return -EPROTO;
+
+		transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev,
+						   skb->len - 2, GFP_KERNEL);
+
+		transaction->aid_len = skb->data[1];
+		memcpy(transaction->aid, &skb->data[2],
+		       transaction->aid_len);
+
+		/* Check next byte is PARAMETERS tag (82) */
+		if (skb->data[transaction->aid_len + 2] !=
+		    NFC_EVT_TRANSACTION_PARAMS_TAG)
+			return -EPROTO;
+
+		transaction->params_len = skb->data[transaction->aid_len + 3];
+		memcpy(transaction->params, skb->data +
+		       transaction->aid_len + 4, transaction->params_len);
+
+		r = nfc_se_transaction(hdev->ndev, host, transaction);
+		break;
+	default:
+		return 1;
+	}
+	kfree_skb(skb);
+	return r;
+}
+EXPORT_SYMBOL(st21nfca_connectivity_event_received);
+
+int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev,
+					u8 event, struct sk_buff *skb)
+{
+	int r = 0;
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+	pr_debug("apdu reader gate event: %x\n", event);
+
+	switch (event) {
+	case ST21NFCA_EVT_TRANSMIT_DATA:
+		del_timer_sync(&info->se_info.bwi_timer);
+		info->se_info.bwi_active = false;
+		r = nfc_hci_send_event(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+				ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0);
+		if (r < 0)
+			goto exit;
+
+		info->se_info.cb(info->se_info.cb_context,
+			skb->data, skb->len, 0);
+		break;
+	case ST21NFCA_EVT_WTX_REQUEST:
+		mod_timer(&info->se_info.bwi_timer, jiffies +
+				msecs_to_jiffies(info->se_info.wt_timeout));
+		break;
+	}
+
+exit:
+	kfree_skb(skb);
+	return r;
+}
+EXPORT_SYMBOL(st21nfca_apdu_reader_event_received);
+
+void st21nfca_se_init(struct nfc_hci_dev *hdev)
+{
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+	init_completion(&info->se_info.req_completion);
+	/* initialize timers */
+	init_timer(&info->se_info.bwi_timer);
+	info->se_info.bwi_timer.data = (unsigned long)info;
+	info->se_info.bwi_timer.function = st21nfca_se_wt_timeout;
+	info->se_info.bwi_active = false;
+
+	init_timer(&info->se_info.se_active_timer);
+	info->se_info.se_active_timer.data = (unsigned long)info;
+	info->se_info.se_active_timer.function = st21nfca_se_activation_timeout;
+	info->se_info.se_active = false;
+
+	info->se_info.count_pipes = 0;
+	info->se_info.expected_pipes = 0;
+
+	info->se_info.xch_error = false;
+
+	info->se_info.wt_timeout =
+			ST21NFCA_BWI_TO_TIMEOUT(ST21NFCA_ATR_DEFAULT_BWI);
+}
+EXPORT_SYMBOL(st21nfca_se_init);
+
+void st21nfca_se_deinit(struct nfc_hci_dev *hdev)
+{
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+	if (info->se_info.bwi_active)
+		del_timer_sync(&info->se_info.bwi_timer);
+	if (info->se_info.se_active)
+		del_timer_sync(&info->se_info.se_active_timer);
+
+	info->se_info.bwi_active = false;
+	info->se_info.se_active = false;
+}
+EXPORT_SYMBOL(st21nfca_se_deinit);
diff --git a/drivers/nfc/st21nfca/st21nfca_se.h b/drivers/nfc/st21nfca/st21nfca_se.h
new file mode 100644
index 0000000..b172cfc
--- /dev/null
+++ b/drivers/nfc/st21nfca/st21nfca_se.h
@@ -0,0 +1,63 @@
+/*
+ * 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_SE_H
+#define __ST21NFCA_SE_H
+
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+
+/*
+ * ref ISO7816-3 chap 8.1. the initial character TS is followed by a
+ * sequence of at most 32 characters.
+ */
+#define ST21NFCA_ESE_MAX_LENGTH			33
+#define ST21NFCA_ESE_HOST_ID			0xc0
+
+struct st21nfca_se_info {
+	u8 atr[ST21NFCA_ESE_MAX_LENGTH];
+	struct completion req_completion;
+
+	struct timer_list bwi_timer;
+	int wt_timeout; /* in msecs */
+	bool bwi_active;
+
+	struct timer_list se_active_timer;
+	bool se_active;
+	int expected_pipes;
+	int count_pipes;
+
+	bool xch_error;
+
+	se_io_cb_t cb;
+	void *cb_context;
+};
+
+int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
+					u8 event, struct sk_buff *skb);
+int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev,
+					u8 event, struct sk_buff *skb);
+
+int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev);
+int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx);
+int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx);
+int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx,
+		u8 *apdu, size_t apdu_length,
+		se_io_cb_t cb, void *cb_context);
+
+void st21nfca_se_init(struct nfc_hci_dev *hdev);
+void st21nfca_se_deinit(struct nfc_hci_dev *hdev);
+#endif /* __ST21NFCA_SE_H */
diff --git a/drivers/nfc/st21nfcb/Makefile b/drivers/nfc/st21nfcb/Makefile
index 82b9ce5..b4b7ffd 100644
--- a/drivers/nfc/st21nfcb/Makefile
+++ b/drivers/nfc/st21nfcb/Makefile
@@ -2,7 +2,7 @@
 # Makefile for ST21NFCB NCI based NFC driver
 #
 
-st21nfcb_nci-objs = ndlc.o st21nfcb.o
+st21nfcb_nci-objs = ndlc.o st21nfcb.o st21nfcb_se.o
 obj-$(CPTCFG_NFC_ST21NFCB)     += st21nfcb_nci.o
 
 st21nfcb_i2c-objs = i2c.o
diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c
index 01ba865..76a4cad 100644
--- a/drivers/nfc/st21nfcb/i2c.c
+++ b/drivers/nfc/st21nfcb/i2c.c
@@ -109,7 +109,7 @@
 		return phy->ndlc->hard_fault;
 
 	r = i2c_master_send(client, skb->data, skb->len);
-	if (r == -EREMOTEIO) {  /* Retry, chip was in standby */
+	if (r < 0) {  /* Retry, chip was in standby */
 		usleep_range(1000, 4000);
 		r = i2c_master_send(client, skb->data, skb->len);
 	}
@@ -148,7 +148,7 @@
 	struct i2c_client *client = phy->i2c_dev;
 
 	r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE);
-	if (r == -EREMOTEIO) {  /* Retry, chip was in standby */
+	if (r < 0) {  /* Retry, chip was in standby */
 		usleep_range(1000, 4000);
 		r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE);
 	}
@@ -199,7 +199,7 @@
 	struct sk_buff *skb = NULL;
 	int r;
 
-	if (!phy || irq != phy->i2c_dev->irq) {
+	if (!phy || !phy->ndlc || irq != phy->i2c_dev->irq) {
 		WARN_ON_ONCE(1);
 		return IRQ_NONE;
 	}
@@ -313,11 +313,8 @@
 
 	phy = devm_kzalloc(&client->dev, sizeof(struct st21nfcb_i2c_phy),
 			   GFP_KERNEL);
-	if (!phy) {
-		nfc_err(&client->dev,
-			"Cannot allocate memory for st21nfcb i2c phy.\n");
+	if (!phy)
 		return -ENOMEM;
-	}
 
 	phy->i2c_dev = client;
 
@@ -343,18 +340,22 @@
 		return -ENODEV;
 	}
 
+	r = ndlc_probe(phy, &i2c_phy_ops, &client->dev,
+			ST21NFCB_FRAME_HEADROOM, ST21NFCB_FRAME_TAILROOM,
+			&phy->ndlc);
+	if (r < 0) {
+		nfc_err(&client->dev, "Unable to register ndlc layer\n");
+		return r;
+	}
+
 	r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
 				st21nfcb_nci_irq_thread_fn,
 				phy->irq_polarity | IRQF_ONESHOT,
 				ST21NFCB_NCI_DRIVER_NAME, phy);
-	if (r < 0) {
+	if (r < 0)
 		nfc_err(&client->dev, "Unable to register IRQ handler\n");
-		return r;
-	}
 
-	return ndlc_probe(phy, &i2c_phy_ops, &client->dev,
-			ST21NFCB_FRAME_HEADROOM, ST21NFCB_FRAME_TAILROOM,
-			&phy->ndlc);
+	return r;
 }
 
 static int st21nfcb_nci_i2c_remove(struct i2c_client *client)
@@ -373,6 +374,7 @@
 
 #ifdef CONFIG_OF
 static const struct of_device_id of_st21nfcb_i2c_match[] = {
+	{ .compatible = "st,st21nfcb-i2c", },
 	{ .compatible = "st,st21nfcb_i2c", },
 	{}
 };
diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st21nfcb/ndlc.c
index bac50e8..6014b58 100644
--- a/drivers/nfc/st21nfcb/ndlc.c
+++ b/drivers/nfc/st21nfcb/ndlc.c
@@ -138,7 +138,7 @@
 		default:
 			pr_err("UNKNOWN Packet Control Byte=%d\n", pcb);
 			kfree_skb(skb);
-			break;
+			continue;
 		}
 		skb_queue_head(&ndlc->send_q, skb);
 	}
@@ -256,10 +256,9 @@
 	struct llt_ndlc *ndlc;
 
 	ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL);
-	if (!ndlc) {
-		nfc_err(dev, "Cannot allocate memory for ndlc.\n");
+	if (!ndlc)
 		return -ENOMEM;
-	}
+
 	ndlc->ops = phy_ops;
 	ndlc->phy_id = phy_id;
 	ndlc->dev = dev;
@@ -297,6 +296,5 @@
 	skb_queue_purge(&ndlc->send_q);
 
 	st21nfcb_nci_remove(ndlc->ndev);
-	kfree(ndlc);
 }
 EXPORT_SYMBOL(ndlc_remove);
diff --git a/drivers/nfc/st21nfcb/st21nfcb.c b/drivers/nfc/st21nfcb/st21nfcb.c
index ea63d58..ca9871a 100644
--- a/drivers/nfc/st21nfcb/st21nfcb.c
+++ b/drivers/nfc/st21nfcb/st21nfcb.c
@@ -22,6 +22,7 @@
 #include <net/nfc/nci_core.h>
 
 #include "st21nfcb.h"
+#include "st21nfcb_se.h"
 
 #define DRIVER_DESC "NCI NFC driver for ST21NFCB"
 
@@ -78,6 +79,13 @@
 	.close = st21nfcb_nci_close,
 	.send = st21nfcb_nci_send,
 	.get_rfprotocol = st21nfcb_nci_get_rfprotocol,
+	.discover_se = st21nfcb_nci_discover_se,
+	.enable_se = st21nfcb_nci_enable_se,
+	.disable_se = st21nfcb_nci_disable_se,
+	.se_io = st21nfcb_nci_se_io,
+	.hci_load_session = st21nfcb_hci_load_session,
+	.hci_event_received = st21nfcb_hci_event_received,
+	.hci_cmd_received = st21nfcb_hci_cmd_received,
 };
 
 int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
@@ -114,9 +122,10 @@
 	if (r) {
 		pr_err("Cannot register nfc device to nci core\n");
 		nci_free_device(ndlc->ndev);
+		return r;
 	}
 
-	return r;
+	return st21nfcb_se_init(ndlc->ndev);
 }
 EXPORT_SYMBOL_GPL(st21nfcb_nci_probe);
 
diff --git a/drivers/nfc/st21nfcb/st21nfcb.h b/drivers/nfc/st21nfcb/st21nfcb.h
index ea58a56..5ef8a58 100644
--- a/drivers/nfc/st21nfcb/st21nfcb.h
+++ b/drivers/nfc/st21nfcb/st21nfcb.h
@@ -19,6 +19,7 @@
 #ifndef __LOCAL_ST21NFCB_H_
 #define __LOCAL_ST21NFCB_H_
 
+#include "st21nfcb_se.h"
 #include "ndlc.h"
 
 /* Define private flags: */
@@ -27,6 +28,7 @@
 struct st21nfcb_nci_info {
 	struct llt_ndlc *ndlc;
 	unsigned long flags;
+	struct st21nfcb_se_info se_info;
 };
 
 void st21nfcb_nci_remove(struct nci_dev *ndev);
diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.c b/drivers/nfc/st21nfcb/st21nfcb_se.c
new file mode 100644
index 0000000..24862a5
--- /dev/null
+++ b/drivers/nfc/st21nfcb/st21nfcb_se.c
@@ -0,0 +1,713 @@
+/*
+ * NCI 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 <linux/delay.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+
+#include "st21nfcb.h"
+#include "st21nfcb_se.h"
+
+struct st21nfcb_pipe_info {
+	u8 pipe_state;
+	u8 src_host_id;
+	u8 src_gate_id;
+	u8 dst_host_id;
+	u8 dst_gate_id;
+} __packed;
+
+/* Hosts */
+#define ST21NFCB_HOST_CONTROLLER_ID     0x00
+#define ST21NFCB_TERMINAL_HOST_ID       0x01
+#define ST21NFCB_UICC_HOST_ID           0x02
+#define ST21NFCB_ESE_HOST_ID            0xc0
+
+/* Gates */
+#define ST21NFCB_DEVICE_MGNT_GATE       0x01
+#define ST21NFCB_APDU_READER_GATE       0xf0
+#define ST21NFCB_CONNECTIVITY_GATE      0x41
+
+/* Pipes */
+#define ST21NFCB_DEVICE_MGNT_PIPE               0x02
+
+/* Connectivity pipe only */
+#define ST21NFCB_SE_COUNT_PIPE_UICC             0x01
+/* Connectivity + APDU Reader pipe */
+#define ST21NFCB_SE_COUNT_PIPE_EMBEDDED         0x02
+
+#define ST21NFCB_SE_TO_HOT_PLUG			1000 /* msecs */
+#define ST21NFCB_SE_TO_PIPES			2000
+
+#define ST21NFCB_EVT_HOT_PLUG_IS_INHIBITED(x)   (x->data[0] & 0x80)
+
+#define NCI_HCI_APDU_PARAM_ATR                     0x01
+#define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY       0x01
+#define NCI_HCI_ADMIN_PARAM_WHITELIST              0x03
+#define NCI_HCI_ADMIN_PARAM_HOST_LIST              0x04
+
+#define ST21NFCB_EVT_SE_HARD_RESET		0x20
+#define ST21NFCB_EVT_TRANSMIT_DATA		0x10
+#define ST21NFCB_EVT_WTX_REQUEST		0x11
+#define ST21NFCB_EVT_SE_SOFT_RESET		0x11
+#define ST21NFCB_EVT_SE_END_OF_APDU_TRANSFER	0x21
+#define ST21NFCB_EVT_HOT_PLUG			0x03
+
+#define ST21NFCB_SE_MODE_OFF                    0x00
+#define ST21NFCB_SE_MODE_ON                     0x01
+
+#define ST21NFCB_EVT_CONNECTIVITY       0x10
+#define ST21NFCB_EVT_TRANSACTION        0x12
+
+#define ST21NFCB_DM_GETINFO             0x13
+#define ST21NFCB_DM_GETINFO_PIPE_LIST   0x02
+#define ST21NFCB_DM_GETINFO_PIPE_INFO   0x01
+#define ST21NFCB_DM_PIPE_CREATED        0x02
+#define ST21NFCB_DM_PIPE_OPEN           0x04
+#define ST21NFCB_DM_RF_ACTIVE           0x80
+#define ST21NFCB_DM_DISCONNECT          0x30
+
+#define ST21NFCB_DM_IS_PIPE_OPEN(p) \
+	((p & 0x0f) == (ST21NFCB_DM_PIPE_CREATED | ST21NFCB_DM_PIPE_OPEN))
+
+#define ST21NFCB_ATR_DEFAULT_BWI        0x04
+
+/*
+ * WT = 2^BWI/10[s], convert into msecs and add a secure
+ * room by increasing by 2 this timeout
+ */
+#define ST21NFCB_BWI_TO_TIMEOUT(x)      ((1 << x) * 200)
+#define ST21NFCB_ATR_GET_Y_FROM_TD(x)   (x >> 4)
+
+/* If TA is present bit 0 is set */
+#define ST21NFCB_ATR_TA_PRESENT(x) (x & 0x01)
+/* If TB is present bit 1 is set */
+#define ST21NFCB_ATR_TB_PRESENT(x) (x & 0x02)
+
+#define ST21NFCB_NUM_DEVICES           256
+
+static DECLARE_BITMAP(dev_mask, ST21NFCB_NUM_DEVICES);
+
+/* Here are the mandatory pipe for st21nfcb */
+static struct nci_hci_gate st21nfcb_gates[] = {
+	{NCI_HCI_ADMIN_GATE, NCI_HCI_ADMIN_PIPE,
+					ST21NFCB_HOST_CONTROLLER_ID},
+	{NCI_HCI_LINK_MGMT_GATE, NCI_HCI_LINK_MGMT_PIPE,
+					ST21NFCB_HOST_CONTROLLER_ID},
+	{ST21NFCB_DEVICE_MGNT_GATE, ST21NFCB_DEVICE_MGNT_PIPE,
+					ST21NFCB_HOST_CONTROLLER_ID},
+
+	/* Secure element pipes are created by secure element host */
+	{ST21NFCB_CONNECTIVITY_GATE, NCI_HCI_DO_NOT_OPEN_PIPE,
+					ST21NFCB_HOST_CONTROLLER_ID},
+	{ST21NFCB_APDU_READER_GATE, NCI_HCI_DO_NOT_OPEN_PIPE,
+					ST21NFCB_HOST_CONTROLLER_ID},
+};
+
+static u8 st21nfcb_se_get_bwi(struct nci_dev *ndev)
+{
+	int i;
+	u8 td;
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+
+	/* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */
+	for (i = 1; i < ST21NFCB_ESE_MAX_LENGTH; i++) {
+		td = ST21NFCB_ATR_GET_Y_FROM_TD(info->se_info.atr[i]);
+		if (ST21NFCB_ATR_TA_PRESENT(td))
+			i++;
+		if (ST21NFCB_ATR_TB_PRESENT(td)) {
+			i++;
+			return info->se_info.atr[i] >> 4;
+		}
+	}
+	return ST21NFCB_ATR_DEFAULT_BWI;
+}
+
+static void st21nfcb_se_get_atr(struct nci_dev *ndev)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+	int r;
+	struct sk_buff *skb;
+
+	r = nci_hci_get_param(ndev, ST21NFCB_APDU_READER_GATE,
+				NCI_HCI_APDU_PARAM_ATR, &skb);
+	if (r < 0)
+		return;
+
+	if (skb->len <= ST21NFCB_ESE_MAX_LENGTH) {
+		memcpy(info->se_info.atr, skb->data, skb->len);
+
+		info->se_info.wt_timeout =
+			ST21NFCB_BWI_TO_TIMEOUT(st21nfcb_se_get_bwi(ndev));
+	}
+	kfree_skb(skb);
+}
+
+int st21nfcb_hci_load_session(struct nci_dev *ndev)
+{
+	int i, j, r;
+	struct sk_buff *skb_pipe_list, *skb_pipe_info;
+	struct st21nfcb_pipe_info *dm_pipe_info;
+	u8 pipe_list[] = { ST21NFCB_DM_GETINFO_PIPE_LIST,
+			ST21NFCB_TERMINAL_HOST_ID};
+	u8 pipe_info[] = { ST21NFCB_DM_GETINFO_PIPE_INFO,
+			ST21NFCB_TERMINAL_HOST_ID, 0};
+
+	/* On ST21NFCB device pipes number are dynamics
+	 * 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 ST21NFCB is to:
+	 * - get a pipe list for each host.
+	 * (eg: ST21NFCB_HOST_CONTROLLER_ID for now).
+	 * (TODO Later on UICC HOST and eSE HOST)
+	 * - get pipe information
+	 * - match retrieved pipe list in st21nfcb_gates
+	 * ST21NFCB_DEVICE_MGNT_GATE is a proprietary gate
+	 * with ST21NFCB_DEVICE_MGNT_PIPE.
+	 * Pipe can be closed and need to be open.
+	 */
+	r = nci_hci_connect_gate(ndev, ST21NFCB_HOST_CONTROLLER_ID,
+				ST21NFCB_DEVICE_MGNT_GATE,
+				ST21NFCB_DEVICE_MGNT_PIPE);
+	if (r < 0)
+		goto free_info;
+
+	/* Get pipe list */
+	r = nci_hci_send_cmd(ndev, ST21NFCB_DEVICE_MGNT_GATE,
+			ST21NFCB_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 = nci_hci_send_cmd(ndev, ST21NFCB_DEVICE_MGNT_GATE,
+					ST21NFCB_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)
+		 */
+		dm_pipe_info = (struct st21nfcb_pipe_info *)skb_pipe_info->data;
+		if (dm_pipe_info->dst_gate_id == ST21NFCB_APDU_READER_GATE &&
+		    dm_pipe_info->src_host_id != ST21NFCB_ESE_HOST_ID) {
+			pr_err("Unexpected apdu_reader pipe on host %x\n",
+			       dm_pipe_info->src_host_id);
+			continue;
+		}
+
+		for (j = 0; (j < ARRAY_SIZE(st21nfcb_gates)) &&
+		     (st21nfcb_gates[j].gate != dm_pipe_info->dst_gate_id); j++)
+			;
+
+		if (j < ARRAY_SIZE(st21nfcb_gates) &&
+		    st21nfcb_gates[j].gate == dm_pipe_info->dst_gate_id &&
+		    ST21NFCB_DM_IS_PIPE_OPEN(dm_pipe_info->pipe_state)) {
+			st21nfcb_gates[j].pipe = pipe_info[2];
+
+			ndev->hci_dev->gate2pipe[st21nfcb_gates[j].gate] =
+						st21nfcb_gates[j].pipe;
+			ndev->hci_dev->pipes[st21nfcb_gates[j].pipe].gate =
+						st21nfcb_gates[j].gate;
+			ndev->hci_dev->pipes[st21nfcb_gates[j].pipe].host =
+						dm_pipe_info->src_host_id;
+		}
+	}
+
+	memcpy(ndev->hci_dev->init_data.gates, st21nfcb_gates,
+	       sizeof(st21nfcb_gates));
+
+free_info:
+	kfree_skb(skb_pipe_info);
+	kfree_skb(skb_pipe_list);
+	return r;
+}
+EXPORT_SYMBOL_GPL(st21nfcb_hci_load_session);
+
+static void st21nfcb_hci_admin_event_received(struct nci_dev *ndev,
+					      u8 event, struct sk_buff *skb)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+
+	switch (event) {
+	case ST21NFCB_EVT_HOT_PLUG:
+		if (info->se_info.se_active) {
+			if (!ST21NFCB_EVT_HOT_PLUG_IS_INHIBITED(skb)) {
+				del_timer_sync(&info->se_info.se_active_timer);
+				info->se_info.se_active = false;
+				complete(&info->se_info.req_completion);
+			} else {
+				mod_timer(&info->se_info.se_active_timer,
+				      jiffies +
+				      msecs_to_jiffies(ST21NFCB_SE_TO_PIPES));
+			}
+		}
+	break;
+	}
+}
+
+static int st21nfcb_hci_apdu_reader_event_received(struct nci_dev *ndev,
+						   u8 event,
+						   struct sk_buff *skb)
+{
+	int r = 0;
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+
+	pr_debug("apdu reader gate event: %x\n", event);
+
+	switch (event) {
+	case ST21NFCB_EVT_TRANSMIT_DATA:
+		del_timer_sync(&info->se_info.bwi_timer);
+		info->se_info.bwi_active = false;
+		info->se_info.cb(info->se_info.cb_context,
+				 skb->data, skb->len, 0);
+	break;
+	case ST21NFCB_EVT_WTX_REQUEST:
+		mod_timer(&info->se_info.bwi_timer, jiffies +
+			  msecs_to_jiffies(info->se_info.wt_timeout));
+	break;
+	}
+
+	kfree_skb(skb);
+	return r;
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ *    1: driver does not handle the event, please do standard processing
+ */
+static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev,
+						u8 host, u8 event,
+						struct sk_buff *skb)
+{
+	int r = 0;
+	struct device *dev = &ndev->nfc_dev->dev;
+	struct nfc_evt_transaction *transaction;
+
+	pr_debug("connectivity gate event: %x\n", event);
+
+	switch (event) {
+	case ST21NFCB_EVT_CONNECTIVITY:
+
+	break;
+	case ST21NFCB_EVT_TRANSACTION:
+		/* According to specification etsi 102 622
+		 * 11.2.2.4 EVT_TRANSACTION Table 52
+		 * Description  Tag     Length
+		 * AID          81      5 to 16
+		 * PARAMETERS   82      0 to 255
+		 */
+		if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
+		    skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
+			return -EPROTO;
+
+		transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev,
+					    skb->len - 2, GFP_KERNEL);
+
+		transaction->aid_len = skb->data[1];
+		memcpy(transaction->aid, &skb->data[2], transaction->aid_len);
+
+		/* Check next byte is PARAMETERS tag (82) */
+		if (skb->data[transaction->aid_len + 2] !=
+		    NFC_EVT_TRANSACTION_PARAMS_TAG)
+			return -EPROTO;
+
+		transaction->params_len = skb->data[transaction->aid_len + 3];
+		memcpy(transaction->params, skb->data +
+		       transaction->aid_len + 4, transaction->params_len);
+
+		r = nfc_se_transaction(ndev->nfc_dev, host, transaction);
+		break;
+	default:
+		return 1;
+	}
+	kfree_skb(skb);
+	return r;
+}
+
+void st21nfcb_hci_event_received(struct nci_dev *ndev, u8 pipe,
+				 u8 event, struct sk_buff *skb)
+{
+	u8 gate = ndev->hci_dev->pipes[pipe].gate;
+	u8 host = ndev->hci_dev->pipes[pipe].host;
+
+	switch (gate) {
+	case NCI_HCI_ADMIN_GATE:
+		st21nfcb_hci_admin_event_received(ndev, event, skb);
+	break;
+	case ST21NFCB_APDU_READER_GATE:
+		st21nfcb_hci_apdu_reader_event_received(ndev, event, skb);
+	break;
+	case ST21NFCB_CONNECTIVITY_GATE:
+		st21nfcb_hci_connectivity_event_received(ndev, host, event,
+							 skb);
+	break;
+	}
+}
+EXPORT_SYMBOL_GPL(st21nfcb_hci_event_received);
+
+
+void st21nfcb_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
+			       struct sk_buff *skb)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+	u8 gate = ndev->hci_dev->pipes[pipe].gate;
+
+	pr_debug("cmd: %x\n", cmd);
+
+	switch (cmd) {
+	case NCI_HCI_ANY_OPEN_PIPE:
+		if (gate != ST21NFCB_APDU_READER_GATE &&
+		    ndev->hci_dev->pipes[pipe].host != ST21NFCB_UICC_HOST_ID)
+			ndev->hci_dev->count_pipes++;
+
+		if (ndev->hci_dev->count_pipes ==
+		    ndev->hci_dev->expected_pipes) {
+			del_timer_sync(&info->se_info.se_active_timer);
+			info->se_info.se_active = false;
+			ndev->hci_dev->count_pipes = 0;
+			complete(&info->se_info.req_completion);
+		}
+	break;
+	}
+}
+EXPORT_SYMBOL_GPL(st21nfcb_hci_cmd_received);
+
+/*
+ * Remarks: On some early st21nfcb firmware, nci_nfcee_mode_set(0)
+ * is rejected
+ */
+static int st21nfcb_nci_control_se(struct nci_dev *ndev, u8 se_idx,
+				   u8 state)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+	int r;
+	struct sk_buff *sk_host_list;
+	u8 host_id;
+
+	switch (se_idx) {
+	case ST21NFCB_UICC_HOST_ID:
+		ndev->hci_dev->count_pipes = 0;
+		ndev->hci_dev->expected_pipes = ST21NFCB_SE_COUNT_PIPE_UICC;
+		break;
+	case ST21NFCB_ESE_HOST_ID:
+		ndev->hci_dev->count_pipes = 0;
+		ndev->hci_dev->expected_pipes = ST21NFCB_SE_COUNT_PIPE_EMBEDDED;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * Wait for an EVT_HOT_PLUG in order to
+	 * retrieve a relevant host list.
+	 */
+	reinit_completion(&info->se_info.req_completion);
+	r = nci_nfcee_mode_set(ndev, se_idx, NCI_NFCEE_ENABLE);
+	if (r != NCI_STATUS_OK)
+		return r;
+
+	mod_timer(&info->se_info.se_active_timer, jiffies +
+		msecs_to_jiffies(ST21NFCB_SE_TO_HOT_PLUG));
+	info->se_info.se_active = true;
+
+	/* Ignore return value and check in any case the host_list */
+	wait_for_completion_interruptible(&info->se_info.req_completion);
+
+	/* There might be some "collision" after receiving a HOT_PLUG event
+	 * This may cause the CLF to not answer to the next hci command.
+	 * There is no possible synchronization to prevent this.
+	 * Adding a small delay is the only way to solve the issue.
+	 */
+	usleep_range(3000, 5000);
+
+	r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE,
+			NCI_HCI_ADMIN_PARAM_HOST_LIST, &sk_host_list);
+	if (r != NCI_HCI_ANY_OK)
+		return r;
+
+	host_id = sk_host_list->data[sk_host_list->len - 1];
+	kfree_skb(sk_host_list);
+	if (state == ST21NFCB_SE_MODE_ON && host_id == se_idx)
+		return se_idx;
+	else if (state == ST21NFCB_SE_MODE_OFF && host_id != se_idx)
+		return se_idx;
+
+	return -1;
+}
+
+int st21nfcb_nci_disable_se(struct nci_dev *ndev, u32 se_idx)
+{
+	int r;
+
+	pr_debug("st21nfcb_nci_disable_se\n");
+
+	if (se_idx == NFC_SE_EMBEDDED) {
+		r = nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE,
+				ST21NFCB_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0);
+		if (r < 0)
+			return r;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(st21nfcb_nci_disable_se);
+
+int st21nfcb_nci_enable_se(struct nci_dev *ndev, u32 se_idx)
+{
+	int r;
+
+	pr_debug("st21nfcb_nci_enable_se\n");
+
+	if (se_idx == ST21NFCB_HCI_HOST_ID_ESE) {
+		r = nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE,
+				ST21NFCB_EVT_SE_SOFT_RESET, NULL, 0);
+		if (r < 0)
+			return r;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(st21nfcb_nci_enable_se);
+
+static int st21nfcb_hci_network_init(struct nci_dev *ndev)
+{
+	struct core_conn_create_dest_spec_params *dest_params;
+	struct dest_spec_params spec_params;
+	struct nci_conn_info    *conn_info;
+	int r, dev_num;
+
+	dest_params =
+		kzalloc(sizeof(struct core_conn_create_dest_spec_params) +
+			sizeof(struct dest_spec_params), GFP_KERNEL);
+	if (dest_params == NULL) {
+		r = -ENOMEM;
+		goto exit;
+	}
+
+	dest_params->type = NCI_DESTINATION_SPECIFIC_PARAM_NFCEE_TYPE;
+	dest_params->length = sizeof(struct dest_spec_params);
+	spec_params.id = ndev->hci_dev->nfcee_id;
+	spec_params.protocol = NCI_NFCEE_INTERFACE_HCI_ACCESS;
+	memcpy(dest_params->value, &spec_params, sizeof(struct dest_spec_params));
+	r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCEE, 1,
+				 sizeof(struct core_conn_create_dest_spec_params) +
+				 sizeof(struct dest_spec_params),
+				 dest_params);
+	if (r != NCI_STATUS_OK)
+		goto free_dest_params;
+
+	conn_info = ndev->hci_dev->conn_info;
+	if (!conn_info)
+		goto free_dest_params;
+
+	memcpy(ndev->hci_dev->init_data.gates, st21nfcb_gates,
+	       sizeof(st21nfcb_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, ST21NFCB_NUM_DEVICES);
+	if (dev_num >= ST21NFCB_NUM_DEVICES) {
+		r = -ENODEV;
+		goto free_dest_params;
+	}
+
+	scnprintf(ndev->hci_dev->init_data.session_id,
+		  sizeof(ndev->hci_dev->init_data.session_id),
+		  "%s%2x", "ST21BH", dev_num);
+
+	r = nci_hci_dev_session_init(ndev);
+	if (r != NCI_HCI_ANY_OK)
+		goto free_dest_params;
+
+	r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id,
+			       NCI_NFCEE_ENABLE);
+	if (r != NCI_STATUS_OK)
+		goto free_dest_params;
+
+free_dest_params:
+	kfree(dest_params);
+
+exit:
+	return r;
+}
+
+int st21nfcb_nci_discover_se(struct nci_dev *ndev)
+{
+	u8 param[2];
+	int r;
+	int se_count = 0;
+
+	pr_debug("st21nfcb_nci_discover_se\n");
+
+	r = st21nfcb_hci_network_init(ndev);
+	if (r != 0)
+		return r;
+
+	param[0] = ST21NFCB_UICC_HOST_ID;
+	param[1] = ST21NFCB_HCI_HOST_ID_ESE;
+	r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE,
+				NCI_HCI_ADMIN_PARAM_WHITELIST,
+				param, sizeof(param));
+	if (r != NCI_HCI_ANY_OK)
+		return r;
+
+	r = st21nfcb_nci_control_se(ndev, ST21NFCB_UICC_HOST_ID,
+				ST21NFCB_SE_MODE_ON);
+	if (r == ST21NFCB_UICC_HOST_ID) {
+		nfc_add_se(ndev->nfc_dev, ST21NFCB_UICC_HOST_ID, NFC_SE_UICC);
+		se_count++;
+	}
+
+	/* Try to enable eSE in order to check availability */
+	r = st21nfcb_nci_control_se(ndev, ST21NFCB_HCI_HOST_ID_ESE,
+				ST21NFCB_SE_MODE_ON);
+	if (r == ST21NFCB_HCI_HOST_ID_ESE) {
+		nfc_add_se(ndev->nfc_dev, ST21NFCB_HCI_HOST_ID_ESE,
+			   NFC_SE_EMBEDDED);
+		se_count++;
+		st21nfcb_se_get_atr(ndev);
+	}
+
+	return !se_count;
+}
+EXPORT_SYMBOL_GPL(st21nfcb_nci_discover_se);
+
+int st21nfcb_nci_se_io(struct nci_dev *ndev, u32 se_idx,
+		       u8 *apdu, size_t apdu_length,
+		       se_io_cb_t cb, void *cb_context)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+
+	pr_debug("\n");
+
+	switch (se_idx) {
+	case ST21NFCB_HCI_HOST_ID_ESE:
+		info->se_info.cb = cb;
+		info->se_info.cb_context = cb_context;
+		mod_timer(&info->se_info.bwi_timer, jiffies +
+			  msecs_to_jiffies(info->se_info.wt_timeout));
+		info->se_info.bwi_active = true;
+		return nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE,
+					ST21NFCB_EVT_TRANSMIT_DATA, apdu,
+					apdu_length);
+	default:
+		return -ENODEV;
+	}
+}
+EXPORT_SYMBOL(st21nfcb_nci_se_io);
+
+static void st21nfcb_se_wt_timeout(unsigned long data)
+{
+	/*
+	 * No answer from the secure element
+	 * within the defined timeout.
+	 * Let's send a reset request as recovery procedure.
+	 * According to the situation, we first try to send a software reset
+	 * to the secure element. If the next command is still not
+	 * answering in time, we send to the CLF a secure element hardware
+	 * reset request.
+	 */
+	/* hardware reset managed through VCC_UICC_OUT power supply */
+	u8 param = 0x01;
+	struct st21nfcb_nci_info *info = (struct st21nfcb_nci_info *) data;
+
+	pr_debug("\n");
+
+	info->se_info.bwi_active = false;
+
+	if (!info->se_info.xch_error) {
+		info->se_info.xch_error = true;
+		nci_hci_send_event(info->ndlc->ndev, ST21NFCB_APDU_READER_GATE,
+				ST21NFCB_EVT_SE_SOFT_RESET, NULL, 0);
+	} else {
+		info->se_info.xch_error = false;
+		nci_hci_send_event(info->ndlc->ndev, ST21NFCB_DEVICE_MGNT_GATE,
+				ST21NFCB_EVT_SE_HARD_RESET, &param, 1);
+	}
+	info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME);
+}
+
+static void st21nfcb_se_activation_timeout(unsigned long data)
+{
+	struct st21nfcb_nci_info *info = (struct st21nfcb_nci_info *) data;
+
+	pr_debug("\n");
+
+	info->se_info.se_active = false;
+
+	complete(&info->se_info.req_completion);
+}
+
+int st21nfcb_se_init(struct nci_dev *ndev)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+
+	init_completion(&info->se_info.req_completion);
+	/* initialize timers */
+	init_timer(&info->se_info.bwi_timer);
+	info->se_info.bwi_timer.data = (unsigned long)info;
+	info->se_info.bwi_timer.function = st21nfcb_se_wt_timeout;
+	info->se_info.bwi_active = false;
+
+	init_timer(&info->se_info.se_active_timer);
+	info->se_info.se_active_timer.data = (unsigned long)info;
+	info->se_info.se_active_timer.function =
+			st21nfcb_se_activation_timeout;
+	info->se_info.se_active = false;
+
+	info->se_info.xch_error = false;
+
+	info->se_info.wt_timeout =
+		ST21NFCB_BWI_TO_TIMEOUT(ST21NFCB_ATR_DEFAULT_BWI);
+
+	return 0;
+}
+EXPORT_SYMBOL(st21nfcb_se_init);
+
+void st21nfcb_se_deinit(struct nci_dev *ndev)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+
+	if (info->se_info.bwi_active)
+		del_timer_sync(&info->se_info.bwi_timer);
+	if (info->se_info.se_active)
+		del_timer_sync(&info->se_info.se_active_timer);
+
+	info->se_info.se_active = false;
+	info->se_info.bwi_active = false;
+}
+EXPORT_SYMBOL(st21nfcb_se_deinit);
+
diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.h b/drivers/nfc/st21nfcb/st21nfcb_se.h
new file mode 100644
index 0000000..52a3238
--- /dev/null
+++ b/drivers/nfc/st21nfcb/st21nfcb_se.h
@@ -0,0 +1,61 @@
+/*
+ * NCI 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/>.
+ */
+#ifndef __LOCAL_ST21NFCB_SE_H_
+#define __LOCAL_ST21NFCB_SE_H_
+
+/*
+ * ref ISO7816-3 chap 8.1. the initial character TS is followed by a
+ * sequence of at most 32 characters.
+ */
+#define ST21NFCB_ESE_MAX_LENGTH		33
+#define ST21NFCB_HCI_HOST_ID_ESE	0xc0
+
+struct st21nfcb_se_info {
+	u8 atr[ST21NFCB_ESE_MAX_LENGTH];
+	struct completion req_completion;
+
+	struct timer_list bwi_timer;
+	int wt_timeout; /* in msecs */
+	bool bwi_active;
+
+	struct timer_list se_active_timer;
+	bool se_active;
+
+	bool xch_error;
+
+	se_io_cb_t cb;
+	void *cb_context;
+};
+
+int st21nfcb_se_init(struct nci_dev *ndev);
+void st21nfcb_se_deinit(struct nci_dev *ndev);
+
+int st21nfcb_nci_discover_se(struct nci_dev *ndev);
+int st21nfcb_nci_enable_se(struct nci_dev *ndev, u32 se_idx);
+int st21nfcb_nci_disable_se(struct nci_dev *ndev, u32 se_idx);
+int st21nfcb_nci_se_io(struct nci_dev *ndev, u32 se_idx,
+					u8 *apdu, size_t apdu_length,
+					se_io_cb_t cb, void *cb_context);
+int st21nfcb_hci_load_session(struct nci_dev *ndev);
+void st21nfcb_hci_event_received(struct nci_dev *ndev, u8 pipe,
+				 u8 event, struct sk_buff *skb);
+void st21nfcb_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
+			       struct sk_buff *skb);
+
+
+#endif /* __LOCAL_ST21NFCB_NCI_H_ */
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
index cc86fa1..7f85bd1 100644
--- a/drivers/ssb/Kconfig
+++ b/drivers/ssb/Kconfig
@@ -132,6 +132,7 @@
 	bool "SSB Broadcom MIPS core driver"
 	depends on SSB && MIPS
 	select SSB_SERIAL
+	select SSB_SFLASH
 	help
 	  Driver for the Sonics Silicon Backplane attached
 	  Broadcom MIPS core.
diff --git a/drivers/ssb/driver_chipcommon_pmu.c b/drivers/ssb/driver_chipcommon_pmu.c
index 1173a09..0942841 100644
--- a/drivers/ssb/driver_chipcommon_pmu.c
+++ b/drivers/ssb/driver_chipcommon_pmu.c
@@ -14,7 +14,7 @@
 #include <linux/delay.h>
 #include <linux/export.h>
 #ifdef CONFIG_BCM47XX
-#include <bcm47xx_nvram.h>
+#include <linux/bcm47xx_nvram.h>
 #endif
 
 #include "ssb_private.h"
diff --git a/drivers/ssb/driver_gige.c b/drivers/ssb/driver_gige.c
index 21f71a1..e973405 100644
--- a/drivers/ssb/driver_gige.c
+++ b/drivers/ssb/driver_gige.c
@@ -24,7 +24,7 @@
 
 static const struct ssb_device_id ssb_gige_tbl[] = {
 	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET_GBIT, SSB_ANY_REV),
-	SSB_DEVTABLE_END
+	{},
 };
 /* MODULE_DEVICE_TABLE(ssb, ssb_gige_tbl); */
 
diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c
index 7b986f9..f87efef 100644
--- a/drivers/ssb/driver_mipscore.c
+++ b/drivers/ssb/driver_mipscore.c
@@ -16,7 +16,7 @@
 #include <linux/serial_reg.h>
 #include <linux/time.h>
 #ifdef CONFIG_BCM47XX
-#include <bcm47xx_nvram.h>
+#include <linux/bcm47xx_nvram.h>
 #endif
 
 #include "ssb_private.h"
diff --git a/drivers/ssb/driver_pcicore.c b/drivers/ssb/driver_pcicore.c
index 0bbff3f..80cc71b 100644
--- a/drivers/ssb/driver_pcicore.c
+++ b/drivers/ssb/driver_pcicore.c
@@ -357,6 +357,15 @@
 	pcicore_write32(pc, SSB_PCICORE_SBTOPCI2,
 			SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA);
 
+	/*
+	 * Accessing PCI config without a proper delay after devices reset (not
+	 * GPIO reset) was causing reboots on WRT300N v1.0.
+	 * Tested delay 850 us lowered reboot chance to 50-80%, 1000 us fixed it
+	 * completely. Flushing all writes was also tested but with no luck.
+	 */
+	if (pc->dev->bus->chip_id == 0x4704)
+		usleep_range(1000, 2000);
+
 	/* Enable PCI bridge BAR0 prefetch and burst */
 	val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
 	ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2);
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index bec5e83..cecc1c9 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -90,25 +90,6 @@
 }
 #endif /* CPTCFG_SSB_PCMCIAHOST */
 
-#ifdef CPTCFG_SSB_SDIOHOST
-struct ssb_bus *ssb_sdio_func_to_bus(struct sdio_func *func)
-{
-	struct ssb_bus *bus;
-
-	ssb_buses_lock();
-	list_for_each_entry(bus, &buses, list) {
-		if (bus->bustype == SSB_BUSTYPE_SDIO &&
-		    bus->host_sdio == func)
-			goto found;
-	}
-	bus = NULL;
-found:
-	ssb_buses_unlock();
-
-	return bus;
-}
-#endif /* CPTCFG_SSB_SDIOHOST */
-
 int ssb_for_each_bus_call(unsigned long data,
 			  int (*func)(struct ssb_bus *bus, unsigned long data))
 {
@@ -1163,6 +1144,8 @@
 	case SSB_IDLOW_SSBREV_25:     /* TODO - find the proper REJECT bit */
 	case SSB_IDLOW_SSBREV_27:     /* same here */
 		return SSB_TMSLOW_REJECT;	/* this is a guess */
+	case SSB_IDLOW_SSBREV:
+		break;
 	default:
 		WARN(1, KERN_INFO "ssb: Backplane Revision 0x%.8X\n", rev);
 	}
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 29c3279..afadac0 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -245,7 +245,7 @@
 	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
 		dev_dbg(&desc->intf->dev,
 			"NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
-			dr->wIndex, dr->wLength);
+			le16_to_cpu(dr->wIndex), le16_to_cpu(dr->wLength));
 		break;
 
 	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
@@ -262,7 +262,9 @@
 		clear_bit(WDM_POLL_RUNNING, &desc->flags);
 		dev_err(&desc->intf->dev,
 			"unknown notification %d received: index %d len %d\n",
-			dr->bNotificationType, dr->wIndex, dr->wLength);
+			dr->bNotificationType,
+			le16_to_cpu(dr->wIndex),
+			le16_to_cpu(dr->wLength));
 		goto exit;
 	}
 
@@ -339,7 +341,7 @@
 	desc->werr = 0;
 	spin_unlock_irq(&desc->iuspin);
 	if (we < 0)
-		return -EIO;
+		return usb_translate_errors(we);
 
 	buf = kmalloc(count, GFP_KERNEL);
 	if (!buf) {
@@ -349,30 +351,25 @@
 
 	r = copy_from_user(buf, buffer, count);
 	if (r > 0) {
-		kfree(buf);
 		rv = -EFAULT;
-		goto outnl;
+		goto out_free_mem;
 	}
 
 	/* concurrent writes and disconnect */
 	r = mutex_lock_interruptible(&desc->wlock);
 	rv = -ERESTARTSYS;
-	if (r) {
-		kfree(buf);
-		goto outnl;
-	}
+	if (r)
+		goto out_free_mem;
 
 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
-		kfree(buf);
 		rv = -ENODEV;
-		goto outnp;
+		goto out_free_mem_lock;
 	}
 
 	r = usb_autopm_get_interface(desc->intf);
 	if (r < 0) {
-		kfree(buf);
 		rv = usb_translate_errors(r);
-		goto outnp;
+		goto out_free_mem_lock;
 	}
 
 	if (!(file->f_flags & O_NONBLOCK))
@@ -386,9 +383,8 @@
 		r = -EIO;
 
 	if (r < 0) {
-		kfree(buf);
 		rv = r;
-		goto out;
+		goto out_free_mem_pm;
 	}
 
 	req = desc->orq;
@@ -408,28 +404,35 @@
 			     USB_RECIP_INTERFACE);
 	req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
 	req->wValue = 0;
-	req->wIndex = desc->inum;
+	req->wIndex = desc->inum; /* already converted */
 	req->wLength = cpu_to_le16(count);
 	set_bit(WDM_IN_USE, &desc->flags);
 	desc->outbuf = buf;
 
 	rv = usb_submit_urb(desc->command, GFP_KERNEL);
 	if (rv < 0) {
-		kfree(buf);
 		desc->outbuf = NULL;
 		clear_bit(WDM_IN_USE, &desc->flags);
 		dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
 		rv = usb_translate_errors(rv);
+		goto out_free_mem_pm;
 	} else {
 		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
-			req->wIndex);
+			le16_to_cpu(req->wIndex));
 	}
-out:
+
 	usb_autopm_put_interface(desc->intf);
-outnp:
 	mutex_unlock(&desc->wlock);
 outnl:
 	return rv < 0 ? rv : count;
+
+out_free_mem_pm:
+	usb_autopm_put_interface(desc->intf);
+out_free_mem_lock:
+	mutex_unlock(&desc->wlock);
+out_free_mem:
+	kfree(buf);
+	return rv;
 }
 
 /*
@@ -519,9 +522,9 @@
 		spin_lock_irq(&desc->iuspin);
 
 		if (desc->rerr) { /* read completed, error happened */
+			rv = usb_translate_errors(desc->rerr);
 			desc->rerr = 0;
 			spin_unlock_irq(&desc->iuspin);
-			rv = -EIO;
 			goto err;
 		}
 		/*
@@ -820,7 +823,7 @@
 	desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
 	desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
 	desc->irq->wValue = 0;
-	desc->irq->wIndex = desc->inum;
+	desc->irq->wIndex = desc->inum; /* already converted */
 	desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
 
 	usb_fill_control_urb(
diff --git a/include/linux/backport-rhashtable.h b/include/linux/backport-rhashtable.h
new file mode 100644
index 0000000..e23d242
--- /dev/null
+++ b/include/linux/backport-rhashtable.h
@@ -0,0 +1,802 @@
+/*
+ * Resizable, Scalable, Concurrent Hash Table
+ *
+ * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
+ * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
+ *
+ * Code partially derived from nft_hash
+ * Rewritten with rehash code from br_multicast plus single list
+ * pointer as suggested by Josh Triplett
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_RHASHTABLE_H
+#define _LINUX_RHASHTABLE_H
+
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/jhash.h>
+#include <linux/list_nulls.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/rcupdate.h>
+
+/*
+ * The end of the chain is marked with a special nulls marks which has
+ * the following format:
+ *
+ * +-------+-----------------------------------------------------+-+
+ * | Base  |                      Hash                           |1|
+ * +-------+-----------------------------------------------------+-+
+ *
+ * Base (4 bits) : Reserved to distinguish between multiple tables.
+ *                 Specified via &struct rhashtable_params.nulls_base.
+ * Hash (27 bits): Full hash (unmasked) of first element added to bucket
+ * 1 (1 bit)     : Nulls marker (always set)
+ *
+ * The remaining bits of the next pointer remain unused for now.
+ */
+#define RHT_BASE_BITS		4
+#define RHT_HASH_BITS		27
+#define RHT_BASE_SHIFT		RHT_HASH_BITS
+
+/* Base bits plus 1 bit for nulls marker */
+#define RHT_HASH_RESERVED_SPACE	(RHT_BASE_BITS + 1)
+
+struct rhash_head {
+	struct rhash_head __rcu		*next;
+};
+
+/**
+ * struct bucket_table - Table of hash buckets
+ * @size: Number of hash buckets
+ * @rehash: Current bucket being rehashed
+ * @hash_rnd: Random seed to fold into hash
+ * @locks_mask: Mask to apply before accessing locks[]
+ * @locks: Array of spinlocks protecting individual buckets
+ * @walkers: List of active walkers
+ * @rcu: RCU structure for freeing the table
+ * @future_tbl: Table under construction during rehashing
+ * @buckets: size * hash buckets
+ */
+struct bucket_table {
+	unsigned int		size;
+	unsigned int		rehash;
+	u32			hash_rnd;
+	unsigned int		locks_mask;
+	spinlock_t		*locks;
+	struct list_head	walkers;
+	struct rcu_head		rcu;
+
+	struct bucket_table __rcu *future_tbl;
+
+	struct rhash_head __rcu	*buckets[] ____cacheline_aligned_in_smp;
+};
+
+/**
+ * struct rhashtable_compare_arg - Key for the function rhashtable_compare
+ * @ht: Hash table
+ * @key: Key to compare against
+ */
+struct rhashtable_compare_arg {
+	struct rhashtable *ht;
+	const void *key;
+};
+
+typedef u32 (*rht_hashfn_t)(const void *data, u32 len, u32 seed);
+typedef u32 (*rht_obj_hashfn_t)(const void *data, u32 len, u32 seed);
+typedef int (*rht_obj_cmpfn_t)(struct rhashtable_compare_arg *arg,
+			       const void *obj);
+
+struct rhashtable;
+
+/**
+ * struct rhashtable_params - Hash table construction parameters
+ * @nelem_hint: Hint on number of elements, should be 75% of desired size
+ * @key_len: Length of key
+ * @key_offset: Offset of key in struct to be hashed
+ * @head_offset: Offset of rhash_head in struct to be hashed
+ * @max_size: Maximum size while expanding
+ * @min_size: Minimum size while shrinking
+ * @nulls_base: Base value to generate nulls marker
+ * @insecure_elasticity: Set to true to disable chain length checks
+ * @automatic_shrinking: Enable automatic shrinking of tables
+ * @locks_mul: Number of bucket locks to allocate per cpu (default: 128)
+ * @hashfn: Hash function (default: jhash2 if !(key_len % 4), or jhash)
+ * @obj_hashfn: Function to hash object
+ * @obj_cmpfn: Function to compare key with object
+ */
+struct rhashtable_params {
+	size_t			nelem_hint;
+	size_t			key_len;
+	size_t			key_offset;
+	size_t			head_offset;
+	unsigned int		max_size;
+	unsigned int		min_size;
+	u32			nulls_base;
+	bool			insecure_elasticity;
+	bool			automatic_shrinking;
+	size_t			locks_mul;
+	rht_hashfn_t		hashfn;
+	rht_obj_hashfn_t	obj_hashfn;
+	rht_obj_cmpfn_t		obj_cmpfn;
+};
+
+/**
+ * struct rhashtable - Hash table handle
+ * @tbl: Bucket table
+ * @nelems: Number of elements in table
+ * @key_len: Key length for hashfn
+ * @elasticity: Maximum chain length before rehash
+ * @p: Configuration parameters
+ * @run_work: Deferred worker to expand/shrink asynchronously
+ * @mutex: Mutex to protect current/future table swapping
+ * @lock: Spin lock to protect walker list
+ */
+struct rhashtable {
+	struct bucket_table __rcu	*tbl;
+	atomic_t			nelems;
+	unsigned int			key_len;
+	unsigned int			elasticity;
+	struct rhashtable_params	p;
+	struct work_struct		run_work;
+	struct mutex                    mutex;
+	spinlock_t			lock;
+};
+
+/**
+ * struct rhashtable_walker - Hash table walker
+ * @list: List entry on list of walkers
+ * @tbl: The table that we were walking over
+ */
+struct rhashtable_walker {
+	struct list_head list;
+	struct bucket_table *tbl;
+};
+
+/**
+ * struct rhashtable_iter - Hash table iterator, fits into netlink cb
+ * @ht: Table to iterate through
+ * @p: Current pointer
+ * @walker: Associated rhashtable walker
+ * @slot: Current slot
+ * @skip: Number of entries to skip in slot
+ */
+struct rhashtable_iter {
+	struct rhashtable *ht;
+	struct rhash_head *p;
+	struct rhashtable_walker *walker;
+	unsigned int slot;
+	unsigned int skip;
+};
+
+static inline unsigned long rht_marker(const struct rhashtable *ht, u32 hash)
+{
+	return NULLS_MARKER(ht->p.nulls_base + hash);
+}
+
+#define INIT_RHT_NULLS_HEAD(ptr, ht, hash) \
+	((ptr) = (typeof(ptr)) rht_marker(ht, hash))
+
+static inline bool rht_is_a_nulls(const struct rhash_head *ptr)
+{
+	return ((unsigned long) ptr & 1);
+}
+
+static inline unsigned long rht_get_nulls_value(const struct rhash_head *ptr)
+{
+	return ((unsigned long) ptr) >> 1;
+}
+
+static inline void *rht_obj(const struct rhashtable *ht,
+			    const struct rhash_head *he)
+{
+	return (char *)he - ht->p.head_offset;
+}
+
+static inline unsigned int rht_bucket_index(const struct bucket_table *tbl,
+					    unsigned int hash)
+{
+	return (hash >> RHT_HASH_RESERVED_SPACE) & (tbl->size - 1);
+}
+
+static inline unsigned int rht_key_hashfn(
+	struct rhashtable *ht, const struct bucket_table *tbl,
+	const void *key, const struct rhashtable_params params)
+{
+	unsigned int hash;
+
+	/* params must be equal to ht->p if it isn't constant. */
+	if (!__builtin_constant_p(params.key_len))
+		hash = ht->p.hashfn(key, ht->key_len, tbl->hash_rnd);
+	else if (params.key_len) {
+		unsigned int key_len = params.key_len;
+
+		if (params.hashfn)
+			hash = params.hashfn(key, key_len, tbl->hash_rnd);
+		else if (key_len & (sizeof(u32) - 1))
+			hash = jhash(key, key_len, tbl->hash_rnd);
+		else
+			hash = jhash2(key, key_len / sizeof(u32),
+				      tbl->hash_rnd);
+	} else {
+		unsigned int key_len = ht->p.key_len;
+
+		if (params.hashfn)
+			hash = params.hashfn(key, key_len, tbl->hash_rnd);
+		else
+			hash = jhash(key, key_len, tbl->hash_rnd);
+	}
+
+	return rht_bucket_index(tbl, hash);
+}
+
+static inline unsigned int rht_head_hashfn(
+	struct rhashtable *ht, const struct bucket_table *tbl,
+	const struct rhash_head *he, const struct rhashtable_params params)
+{
+	const char *ptr = rht_obj(ht, he);
+
+	return likely(params.obj_hashfn) ?
+	       rht_bucket_index(tbl, params.obj_hashfn(ptr, params.key_len ?:
+							    ht->p.key_len,
+						       tbl->hash_rnd)) :
+	       rht_key_hashfn(ht, tbl, ptr + params.key_offset, params);
+}
+
+/**
+ * rht_grow_above_75 - returns true if nelems > 0.75 * table-size
+ * @ht:		hash table
+ * @tbl:	current table
+ */
+static inline bool rht_grow_above_75(const struct rhashtable *ht,
+				     const struct bucket_table *tbl)
+{
+	/* Expand table when exceeding 75% load */
+	return atomic_read(&ht->nelems) > (tbl->size / 4 * 3) &&
+	       (!ht->p.max_size || tbl->size < ht->p.max_size);
+}
+
+/**
+ * rht_shrink_below_30 - returns true if nelems < 0.3 * table-size
+ * @ht:		hash table
+ * @tbl:	current table
+ */
+static inline bool rht_shrink_below_30(const struct rhashtable *ht,
+				       const struct bucket_table *tbl)
+{
+	/* Shrink table beneath 30% load */
+	return atomic_read(&ht->nelems) < (tbl->size * 3 / 10) &&
+	       tbl->size > ht->p.min_size;
+}
+
+/**
+ * rht_grow_above_100 - returns true if nelems > table-size
+ * @ht:		hash table
+ * @tbl:	current table
+ */
+static inline bool rht_grow_above_100(const struct rhashtable *ht,
+				      const struct bucket_table *tbl)
+{
+	return atomic_read(&ht->nelems) > tbl->size;
+}
+
+/* The bucket lock is selected based on the hash and protects mutations
+ * on a group of hash buckets.
+ *
+ * A maximum of tbl->size/2 bucket locks is allocated. This ensures that
+ * a single lock always covers both buckets which may both contains
+ * entries which link to the same bucket of the old table during resizing.
+ * This allows to simplify the locking as locking the bucket in both
+ * tables during resize always guarantee protection.
+ *
+ * IMPORTANT: When holding the bucket lock of both the old and new table
+ * during expansions and shrinking, the old bucket lock must always be
+ * acquired first.
+ */
+static inline spinlock_t *rht_bucket_lock(const struct bucket_table *tbl,
+					  unsigned int hash)
+{
+	return &tbl->locks[hash & tbl->locks_mask];
+}
+
+#ifdef CONFIG_PROVE_LOCKING
+int lockdep_rht_mutex_is_held(struct rhashtable *ht);
+int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash);
+#else
+static inline int lockdep_rht_mutex_is_held(struct rhashtable *ht)
+{
+	return 1;
+}
+
+static inline int lockdep_rht_bucket_is_held(const struct bucket_table *tbl,
+					     u32 hash)
+{
+	return 1;
+}
+#endif /* CONFIG_PROVE_LOCKING */
+
+int rhashtable_init(struct rhashtable *ht,
+		    const struct rhashtable_params *params);
+
+int rhashtable_insert_slow(struct rhashtable *ht, const void *key,
+			   struct rhash_head *obj,
+			   struct bucket_table *old_tbl);
+int rhashtable_insert_rehash(struct rhashtable *ht);
+
+int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter);
+void rhashtable_walk_exit(struct rhashtable_iter *iter);
+int rhashtable_walk_start(struct rhashtable_iter *iter) __acquires(RCU);
+void *rhashtable_walk_next(struct rhashtable_iter *iter);
+void rhashtable_walk_stop(struct rhashtable_iter *iter) __releases(RCU);
+
+void rhashtable_free_and_destroy(struct rhashtable *ht,
+				 void (*free_fn)(void *ptr, void *arg),
+				 void *arg);
+void rhashtable_destroy(struct rhashtable *ht);
+
+#define rht_dereference(p, ht) \
+	rcu_dereference_protected(p, lockdep_rht_mutex_is_held(ht))
+
+#define rht_dereference_rcu(p, ht) \
+	rcu_dereference_check(p, lockdep_rht_mutex_is_held(ht))
+
+#define rht_dereference_bucket(p, tbl, hash) \
+	rcu_dereference_protected(p, lockdep_rht_bucket_is_held(tbl, hash))
+
+#define rht_dereference_bucket_rcu(p, tbl, hash) \
+	rcu_dereference_check(p, lockdep_rht_bucket_is_held(tbl, hash))
+
+#define rht_entry(tpos, pos, member) \
+	({ tpos = container_of(pos, typeof(*tpos), member); 1; })
+
+/**
+ * rht_for_each_continue - continue iterating over hash chain
+ * @pos:	the &struct rhash_head to use as a loop cursor.
+ * @head:	the previous &struct rhash_head to continue from
+ * @tbl:	the &struct bucket_table
+ * @hash:	the hash value / bucket index
+ */
+#define rht_for_each_continue(pos, head, tbl, hash) \
+	for (pos = rht_dereference_bucket(head, tbl, hash); \
+	     !rht_is_a_nulls(pos); \
+	     pos = rht_dereference_bucket((pos)->next, tbl, hash))
+
+/**
+ * rht_for_each - iterate over hash chain
+ * @pos:	the &struct rhash_head to use as a loop cursor.
+ * @tbl:	the &struct bucket_table
+ * @hash:	the hash value / bucket index
+ */
+#define rht_for_each(pos, tbl, hash) \
+	rht_for_each_continue(pos, (tbl)->buckets[hash], tbl, hash)
+
+/**
+ * rht_for_each_entry_continue - continue iterating over hash chain
+ * @tpos:	the type * to use as a loop cursor.
+ * @pos:	the &struct rhash_head to use as a loop cursor.
+ * @head:	the previous &struct rhash_head to continue from
+ * @tbl:	the &struct bucket_table
+ * @hash:	the hash value / bucket index
+ * @member:	name of the &struct rhash_head within the hashable struct.
+ */
+#define rht_for_each_entry_continue(tpos, pos, head, tbl, hash, member)	\
+	for (pos = rht_dereference_bucket(head, tbl, hash);		\
+	     (!rht_is_a_nulls(pos)) && rht_entry(tpos, pos, member);	\
+	     pos = rht_dereference_bucket((pos)->next, tbl, hash))
+
+/**
+ * rht_for_each_entry - iterate over hash chain of given type
+ * @tpos:	the type * to use as a loop cursor.
+ * @pos:	the &struct rhash_head to use as a loop cursor.
+ * @tbl:	the &struct bucket_table
+ * @hash:	the hash value / bucket index
+ * @member:	name of the &struct rhash_head within the hashable struct.
+ */
+#define rht_for_each_entry(tpos, pos, tbl, hash, member)		\
+	rht_for_each_entry_continue(tpos, pos, (tbl)->buckets[hash],	\
+				    tbl, hash, member)
+
+/**
+ * rht_for_each_entry_safe - safely iterate over hash chain of given type
+ * @tpos:	the type * to use as a loop cursor.
+ * @pos:	the &struct rhash_head to use as a loop cursor.
+ * @next:	the &struct rhash_head to use as next in loop cursor.
+ * @tbl:	the &struct bucket_table
+ * @hash:	the hash value / bucket index
+ * @member:	name of the &struct rhash_head within the hashable struct.
+ *
+ * This hash chain list-traversal primitive allows for the looped code to
+ * remove the loop cursor from the list.
+ */
+#define rht_for_each_entry_safe(tpos, pos, next, tbl, hash, member)	    \
+	for (pos = rht_dereference_bucket((tbl)->buckets[hash], tbl, hash), \
+	     next = !rht_is_a_nulls(pos) ?				    \
+		       rht_dereference_bucket(pos->next, tbl, hash) : NULL; \
+	     (!rht_is_a_nulls(pos)) && rht_entry(tpos, pos, member);	    \
+	     pos = next,						    \
+	     next = !rht_is_a_nulls(pos) ?				    \
+		       rht_dereference_bucket(pos->next, tbl, hash) : NULL)
+
+/**
+ * rht_for_each_rcu_continue - continue iterating over rcu hash chain
+ * @pos:	the &struct rhash_head to use as a loop cursor.
+ * @head:	the previous &struct rhash_head to continue from
+ * @tbl:	the &struct bucket_table
+ * @hash:	the hash value / bucket index
+ *
+ * This hash chain list-traversal primitive may safely run concurrently with
+ * the _rcu mutation primitives such as rhashtable_insert() as long as the
+ * traversal is guarded by rcu_read_lock().
+ */
+#define rht_for_each_rcu_continue(pos, head, tbl, hash)			\
+	for (({barrier(); }),						\
+	     pos = rht_dereference_bucket_rcu(head, tbl, hash);		\
+	     !rht_is_a_nulls(pos);					\
+	     pos = rcu_dereference_raw(pos->next))
+
+/**
+ * rht_for_each_rcu - iterate over rcu hash chain
+ * @pos:	the &struct rhash_head to use as a loop cursor.
+ * @tbl:	the &struct bucket_table
+ * @hash:	the hash value / bucket index
+ *
+ * This hash chain list-traversal primitive may safely run concurrently with
+ * the _rcu mutation primitives such as rhashtable_insert() as long as the
+ * traversal is guarded by rcu_read_lock().
+ */
+#define rht_for_each_rcu(pos, tbl, hash)				\
+	rht_for_each_rcu_continue(pos, (tbl)->buckets[hash], tbl, hash)
+
+/**
+ * rht_for_each_entry_rcu_continue - continue iterating over rcu hash chain
+ * @tpos:	the type * to use as a loop cursor.
+ * @pos:	the &struct rhash_head to use as a loop cursor.
+ * @head:	the previous &struct rhash_head to continue from
+ * @tbl:	the &struct bucket_table
+ * @hash:	the hash value / bucket index
+ * @member:	name of the &struct rhash_head within the hashable struct.
+ *
+ * This hash chain list-traversal primitive may safely run concurrently with
+ * the _rcu mutation primitives such as rhashtable_insert() as long as the
+ * traversal is guarded by rcu_read_lock().
+ */
+#define rht_for_each_entry_rcu_continue(tpos, pos, head, tbl, hash, member) \
+	for (({barrier(); }),						    \
+	     pos = rht_dereference_bucket_rcu(head, tbl, hash);		    \
+	     (!rht_is_a_nulls(pos)) && rht_entry(tpos, pos, member);	    \
+	     pos = rht_dereference_bucket_rcu(pos->next, tbl, hash))
+
+/**
+ * rht_for_each_entry_rcu - iterate over rcu hash chain of given type
+ * @tpos:	the type * to use as a loop cursor.
+ * @pos:	the &struct rhash_head to use as a loop cursor.
+ * @tbl:	the &struct bucket_table
+ * @hash:	the hash value / bucket index
+ * @member:	name of the &struct rhash_head within the hashable struct.
+ *
+ * This hash chain list-traversal primitive may safely run concurrently with
+ * the _rcu mutation primitives such as rhashtable_insert() as long as the
+ * traversal is guarded by rcu_read_lock().
+ */
+#define rht_for_each_entry_rcu(tpos, pos, tbl, hash, member)		\
+	rht_for_each_entry_rcu_continue(tpos, pos, (tbl)->buckets[hash],\
+					tbl, hash, member)
+
+static inline int rhashtable_compare(struct rhashtable_compare_arg *arg,
+				     const void *obj)
+{
+	struct rhashtable *ht = arg->ht;
+	const char *ptr = obj;
+
+	return memcmp(ptr + ht->p.key_offset, arg->key, ht->p.key_len);
+}
+
+/**
+ * rhashtable_lookup_fast - search hash table, inlined version
+ * @ht:		hash table
+ * @key:	the pointer to the key
+ * @params:	hash table parameters
+ *
+ * Computes the hash value for the key and traverses the bucket chain looking
+ * for a entry with an identical key. The first matching entry is returned.
+ *
+ * Returns the first entry on which the compare function returned true.
+ */
+static inline void *rhashtable_lookup_fast(
+	struct rhashtable *ht, const void *key,
+	const struct rhashtable_params params)
+{
+	struct rhashtable_compare_arg arg = {
+		.ht = ht,
+		.key = key,
+	};
+	const struct bucket_table *tbl;
+	struct rhash_head *he;
+	unsigned int hash;
+
+	rcu_read_lock();
+
+	tbl = rht_dereference_rcu(ht->tbl, ht);
+restart:
+	hash = rht_key_hashfn(ht, tbl, key, params);
+	rht_for_each_rcu(he, tbl, hash) {
+		if (params.obj_cmpfn ?
+		    params.obj_cmpfn(&arg, rht_obj(ht, he)) :
+		    rhashtable_compare(&arg, rht_obj(ht, he)))
+			continue;
+		rcu_read_unlock();
+		return rht_obj(ht, he);
+	}
+
+	/* Ensure we see any new tables. */
+	smp_rmb();
+
+	tbl = rht_dereference_rcu(tbl->future_tbl, ht);
+	if (unlikely(tbl))
+		goto restart;
+	rcu_read_unlock();
+
+	return NULL;
+}
+
+/* Internal function, please use rhashtable_insert_fast() instead */
+static inline int __rhashtable_insert_fast(
+	struct rhashtable *ht, const void *key, struct rhash_head *obj,
+	const struct rhashtable_params params)
+{
+	struct rhashtable_compare_arg arg = {
+		.ht = ht,
+		.key = key,
+	};
+	struct bucket_table *tbl, *new_tbl;
+	struct rhash_head *head;
+	spinlock_t *lock;
+	unsigned int elasticity;
+	unsigned int hash;
+	int err;
+
+restart:
+	rcu_read_lock();
+
+	tbl = rht_dereference_rcu(ht->tbl, ht);
+
+	/* All insertions must grab the oldest table containing
+	 * the hashed bucket that is yet to be rehashed.
+	 */
+	for (;;) {
+		hash = rht_head_hashfn(ht, tbl, obj, params);
+		lock = rht_bucket_lock(tbl, hash);
+		spin_lock_bh(lock);
+
+		if (tbl->rehash <= hash)
+			break;
+
+		spin_unlock_bh(lock);
+		tbl = rht_dereference_rcu(tbl->future_tbl, ht);
+	}
+
+	new_tbl = rht_dereference_rcu(tbl->future_tbl, ht);
+	if (unlikely(new_tbl)) {
+		err = rhashtable_insert_slow(ht, key, obj, new_tbl);
+		if (err == -EAGAIN)
+			goto slow_path;
+		goto out;
+	}
+
+	if (unlikely(rht_grow_above_100(ht, tbl))) {
+slow_path:
+		spin_unlock_bh(lock);
+		err = rhashtable_insert_rehash(ht);
+		rcu_read_unlock();
+		if (err)
+			return err;
+
+		goto restart;
+	}
+
+	err = -EEXIST;
+	elasticity = ht->elasticity;
+	rht_for_each(head, tbl, hash) {
+		if (key &&
+		    unlikely(!(params.obj_cmpfn ?
+			       params.obj_cmpfn(&arg, rht_obj(ht, head)) :
+			       rhashtable_compare(&arg, rht_obj(ht, head)))))
+			goto out;
+		if (!--elasticity)
+			goto slow_path;
+	}
+
+	err = 0;
+
+	head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash);
+
+	RCU_INIT_POINTER(obj->next, head);
+
+	rcu_assign_pointer(tbl->buckets[hash], obj);
+
+	atomic_inc(&ht->nelems);
+	if (rht_grow_above_75(ht, tbl))
+		schedule_work(&ht->run_work);
+
+out:
+	spin_unlock_bh(lock);
+	rcu_read_unlock();
+
+	return err;
+}
+
+/**
+ * rhashtable_insert_fast - insert object into hash table
+ * @ht:		hash table
+ * @obj:	pointer to hash head inside object
+ * @params:	hash table parameters
+ *
+ * Will take a per bucket spinlock to protect against mutual mutations
+ * on the same bucket. Multiple insertions may occur in parallel unless
+ * they map to the same bucket lock.
+ *
+ * It is safe to call this function from atomic context.
+ *
+ * Will trigger an automatic deferred table resizing if the size grows
+ * beyond the watermark indicated by grow_decision() which can be passed
+ * to rhashtable_init().
+ */
+static inline int rhashtable_insert_fast(
+	struct rhashtable *ht, struct rhash_head *obj,
+	const struct rhashtable_params params)
+{
+	return __rhashtable_insert_fast(ht, NULL, obj, params);
+}
+
+/**
+ * rhashtable_lookup_insert_fast - lookup and insert object into hash table
+ * @ht:		hash table
+ * @obj:	pointer to hash head inside object
+ * @params:	hash table parameters
+ *
+ * Locks down the bucket chain in both the old and new table if a resize
+ * is in progress to ensure that writers can't remove from the old table
+ * and can't insert to the new table during the atomic operation of search
+ * and insertion. Searches for duplicates in both the old and new table if
+ * a resize is in progress.
+ *
+ * This lookup function may only be used for fixed key hash table (key_len
+ * parameter set). It will BUG() if used inappropriately.
+ *
+ * It is safe to call this function from atomic context.
+ *
+ * Will trigger an automatic deferred table resizing if the size grows
+ * beyond the watermark indicated by grow_decision() which can be passed
+ * to rhashtable_init().
+ */
+static inline int rhashtable_lookup_insert_fast(
+	struct rhashtable *ht, struct rhash_head *obj,
+	const struct rhashtable_params params)
+{
+	const char *key = rht_obj(ht, obj);
+
+	BUG_ON(ht->p.obj_hashfn);
+
+	return __rhashtable_insert_fast(ht, key + ht->p.key_offset, obj,
+					params);
+}
+
+/**
+ * rhashtable_lookup_insert_key - search and insert object to hash table
+ *				  with explicit key
+ * @ht:		hash table
+ * @key:	key
+ * @obj:	pointer to hash head inside object
+ * @params:	hash table parameters
+ *
+ * Locks down the bucket chain in both the old and new table if a resize
+ * is in progress to ensure that writers can't remove from the old table
+ * and can't insert to the new table during the atomic operation of search
+ * and insertion. Searches for duplicates in both the old and new table if
+ * a resize is in progress.
+ *
+ * Lookups may occur in parallel with hashtable mutations and resizing.
+ *
+ * Will trigger an automatic deferred table resizing if the size grows
+ * beyond the watermark indicated by grow_decision() which can be passed
+ * to rhashtable_init().
+ *
+ * Returns zero on success.
+ */
+static inline int rhashtable_lookup_insert_key(
+	struct rhashtable *ht, const void *key, struct rhash_head *obj,
+	const struct rhashtable_params params)
+{
+	BUG_ON(!ht->p.obj_hashfn || !key);
+
+	return __rhashtable_insert_fast(ht, key, obj, params);
+}
+
+/* Internal function, please use rhashtable_remove_fast() instead */
+static inline int __rhashtable_remove_fast(
+	struct rhashtable *ht, struct bucket_table *tbl,
+	struct rhash_head *obj, const struct rhashtable_params params)
+{
+	struct rhash_head __rcu **pprev;
+	struct rhash_head *he;
+	spinlock_t * lock;
+	unsigned int hash;
+	int err = -ENOENT;
+
+	hash = rht_head_hashfn(ht, tbl, obj, params);
+	lock = rht_bucket_lock(tbl, hash);
+
+	spin_lock_bh(lock);
+
+	pprev = &tbl->buckets[hash];
+	rht_for_each(he, tbl, hash) {
+		if (he != obj) {
+			pprev = &he->next;
+			continue;
+		}
+
+		rcu_assign_pointer(*pprev, obj->next);
+		err = 0;
+		break;
+	}
+
+	spin_unlock_bh(lock);
+
+	return err;
+}
+
+/**
+ * rhashtable_remove_fast - remove object from hash table
+ * @ht:		hash table
+ * @obj:	pointer to hash head inside object
+ * @params:	hash table parameters
+ *
+ * Since the hash chain is single linked, the removal operation needs to
+ * walk the bucket chain upon removal. The removal operation is thus
+ * considerable slow if the hash table is not correctly sized.
+ *
+ * Will automatically shrink the table via rhashtable_expand() if the
+ * shrink_decision function specified at rhashtable_init() returns true.
+ *
+ * Returns zero on success, -ENOENT if the entry could not be found.
+ */
+static inline int rhashtable_remove_fast(
+	struct rhashtable *ht, struct rhash_head *obj,
+	const struct rhashtable_params params)
+{
+	struct bucket_table *tbl;
+	int err;
+
+	rcu_read_lock();
+
+	tbl = rht_dereference_rcu(ht->tbl, ht);
+
+	/* Because we have already taken (and released) the bucket
+	 * lock in old_tbl, if we find that future_tbl is not yet
+	 * visible then that guarantees the entry to still be in
+	 * the old tbl if it exists.
+	 */
+	while ((err = __rhashtable_remove_fast(ht, tbl, obj, params)) &&
+	       (tbl = rht_dereference_rcu(tbl->future_tbl, ht)))
+		;
+
+	if (err)
+		goto out;
+
+	atomic_dec(&ht->nelems);
+	if (unlikely(ht->p.automatic_shrinking &&
+		     rht_shrink_below_30(ht, tbl)))
+		schedule_work(&ht->run_work);
+
+out:
+	rcu_read_unlock();
+
+	return err;
+}
+
+#endif /* _LINUX_RHASHTABLE_H */
diff --git a/include/linux/bcm47xx_wdt.h b/include/linux/bcm47xx_wdt.h
index b708786..5582c21 100644
--- a/include/linux/bcm47xx_wdt.h
+++ b/include/linux/bcm47xx_wdt.h
@@ -16,6 +16,7 @@
 
 	struct watchdog_device wdd;
 	struct notifier_block notifier;
+	struct notifier_block restart_handler;
 
 	struct timer_list soft_timer;
 	atomic_t soft_ticks;
diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h
index 2ad74c6..24791e5 100644
--- a/include/linux/bcma/bcma.h
+++ b/include/linux/bcma/bcma.h
@@ -318,6 +318,7 @@
 	const struct bcma_host_ops *ops;
 
 	enum bcma_hosttype hosttype;
+	bool host_is_pcie2; /* Used for BCMA_HOSTTYPE_PCI only */
 	union {
 		/* Pointer to the PCI bus (only for BCMA_HOSTTYPE_PCI) */
 		struct pci_dev *host_pci;
@@ -433,6 +434,27 @@
 	return bcma_find_core_unit(bus, coreid, 0);
 }
 
+#ifdef CPTCFG_BCMA_HOST_PCI
+extern void bcma_host_pci_up(struct bcma_bus *bus);
+extern void bcma_host_pci_down(struct bcma_bus *bus);
+extern int bcma_host_pci_irq_ctl(struct bcma_bus *bus,
+				 struct bcma_device *core, bool enable);
+#else
+static inline void bcma_host_pci_up(struct bcma_bus *bus)
+{
+}
+static inline void bcma_host_pci_down(struct bcma_bus *bus)
+{
+}
+static inline int bcma_host_pci_irq_ctl(struct bcma_bus *bus,
+					struct bcma_device *core, bool enable)
+{
+	if (bus->hosttype == BCMA_HOSTTYPE_PCI)
+		return -ENOTSUPP;
+	return 0;
+}
+#endif
+
 extern bool bcma_core_is_enabled(struct bcma_device *core);
 extern void bcma_core_disable(struct bcma_device *core, u32 flags);
 extern int bcma_core_enable(struct bcma_device *core, u32 flags);
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index b151bb0..1f1d861 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -663,14 +663,6 @@
 #define bcma_cc_maskset32(cc, offset, mask, set) \
 	bcma_cc_write32(cc, offset, (bcma_cc_read32(cc, offset) & (mask)) | (set))
 
-extern void bcma_core_chipcommon_init(struct bcma_drv_cc *cc);
-extern void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc);
-
-extern void bcma_chipco_suspend(struct bcma_drv_cc *cc);
-extern void bcma_chipco_resume(struct bcma_drv_cc *cc);
-
-void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable);
-
 extern u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks);
 
 extern u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc);
@@ -690,9 +682,6 @@
 u32 bcma_chipco_gpio_pulldown(struct bcma_drv_cc *cc, u32 mask, u32 value);
 
 /* PMU support */
-extern void bcma_pmu_init(struct bcma_drv_cc *cc);
-extern void bcma_pmu_early_init(struct bcma_drv_cc *cc);
-
 extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset,
 				  u32 value);
 extern void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset,
diff --git a/include/linux/bcma/bcma_driver_gmac_cmn.h b/include/linux/bcma/bcma_driver_gmac_cmn.h
index 2837fc3..4354d4e 100644
--- a/include/linux/bcma/bcma_driver_gmac_cmn.h
+++ b/include/linux/bcma/bcma_driver_gmac_cmn.h
@@ -91,10 +91,4 @@
 #define gmac_cmn_write16(gc, offset, val)	bcma_write16((gc)->core, offset, val)
 #define gmac_cmn_write32(gc, offset, val)	bcma_write32((gc)->core, offset, val)
 
-#ifdef CPTCFG_BCMA_DRIVER_GMAC_CMN
-extern void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc);
-#else
-static inline void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc) { }
-#endif
-
 #endif /* LINUX_BCMA_DRIVER_GMAC_CMN_H_ */
diff --git a/include/linux/bcma/bcma_driver_mips.h b/include/linux/bcma/bcma_driver_mips.h
index 907c48c..8eea7f9 100644
--- a/include/linux/bcma/bcma_driver_mips.h
+++ b/include/linux/bcma/bcma_driver_mips.h
@@ -39,21 +39,6 @@
 	u8 early_setup_done:1;
 };
 
-#ifdef CPTCFG_BCMA_DRIVER_MIPS
-extern void bcma_core_mips_init(struct bcma_drv_mips *mcore);
-extern void bcma_core_mips_early_init(struct bcma_drv_mips *mcore);
-
-extern unsigned int bcma_core_mips_irq(struct bcma_device *dev);
-#else
-static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { }
-static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) { }
-
-static inline unsigned int bcma_core_mips_irq(struct bcma_device *dev)
-{
-	return 0;
-}
-#endif
-
 extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore);
 
 #endif /* LINUX_BCMA_DRIVER_MIPS_H_ */
diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h
index e4e9173..a43030e 100644
--- a/include/linux/bcma/bcma_driver_pci.h
+++ b/include/linux/bcma/bcma_driver_pci.h
@@ -223,6 +223,7 @@
 
 struct bcma_drv_pci {
 	struct bcma_device *core;
+	u8 early_setup_done:1;
 	u8 setup_done:1;
 	u8 hostmode:1;
 
@@ -237,12 +238,13 @@
 #define pcicore_write16(pc, offset, val)	bcma_write16((pc)->core, offset, val)
 #define pcicore_write32(pc, offset, val)	bcma_write32((pc)->core, offset, val)
 
-extern void bcma_core_pci_init(struct bcma_drv_pci *pc);
-extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc,
-				 struct bcma_device *core, bool enable);
-extern void bcma_core_pci_up(struct bcma_bus *bus);
-extern void bcma_core_pci_down(struct bcma_bus *bus);
+#ifdef CPTCFG_BCMA_DRIVER_PCI
 extern void bcma_core_pci_power_save(struct bcma_bus *bus, bool up);
+#else
+static inline void bcma_core_pci_power_save(struct bcma_bus *bus, bool up)
+{
+}
+#endif
 
 extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev);
 extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev);
diff --git a/include/linux/bcma/bcma_driver_pcie2.h b/include/linux/bcma/bcma_driver_pcie2.h
index 5988b05..31e6d17 100644
--- a/include/linux/bcma/bcma_driver_pcie2.h
+++ b/include/linux/bcma/bcma_driver_pcie2.h
@@ -143,6 +143,8 @@
 
 struct bcma_drv_pcie2 {
 	struct bcma_device *core;
+
+	u16 reqsize;
 };
 
 #define pcie2_read16(pcie2, offset)		bcma_read16((pcie2)->core, offset)
@@ -153,6 +155,4 @@
 #define pcie2_set32(pcie2, offset, set)		bcma_set32((pcie2)->core, offset, set)
 #define pcie2_mask32(pcie2, offset, mask)	bcma_mask32((pcie2)->core, offset, mask)
 
-void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2);
-
 #endif /* LINUX_BCMA_DRIVER_PCIE2_H_ */
diff --git a/include/linux/bcma/bcma_regs.h b/include/linux/bcma/bcma_regs.h
index e64ae7b..ebd5c1f 100644
--- a/include/linux/bcma/bcma_regs.h
+++ b/include/linux/bcma/bcma_regs.h
@@ -64,6 +64,8 @@
 #define  BCMA_PCI_GPIO_XTAL		0x40	/* PCI config space GPIO 14 for Xtal powerup */
 #define  BCMA_PCI_GPIO_PLL		0x80	/* PCI config space GPIO 15 for PLL powerdown */
 
+#define BCMA_PCIE2_BAR0_WIN2		0x70
+
 /* SiliconBackplane Address Map.
  * All regions may not exist on all chips.
  */
diff --git a/include/linux/bcma/bcma_soc.h b/include/linux/bcma/bcma_soc.h
index f24d245..1b5fc0c 100644
--- a/include/linux/bcma/bcma_soc.h
+++ b/include/linux/bcma/bcma_soc.h
@@ -5,8 +5,6 @@
 
 struct bcma_soc {
 	struct bcma_bus bus;
-	struct bcma_device core_cc;
-	struct bcma_device core_mips;
 };
 
 int __init bcma_host_soc_register(struct bcma_soc *soc);
diff --git a/include/linux/fixp-arith.h b/include/linux/fixp-arith.h
new file mode 100644
index 0000000..d4686fe
--- /dev/null
+++ b/include/linux/fixp-arith.h
@@ -0,0 +1,156 @@
+#ifndef _FIXP_ARITH_H
+#define _FIXP_ARITH_H
+
+#include <linux/math64.h>
+
+/*
+ * Simplistic fixed-point arithmetics.
+ * Hmm, I'm probably duplicating some code :(
+ *
+ * Copyright (c) 2002 Johann Deneux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so by
+ * e-mail - mail your message to <johann.deneux@gmail.com>
+ */
+
+#include <linux/types.h>
+
+static const s32 sin_table[] = {
+	0x00000000, 0x023be165, 0x04779632, 0x06b2f1d2, 0x08edc7b6, 0x0b27eb5c,
+	0x0d61304d, 0x0f996a26, 0x11d06c96, 0x14060b67, 0x163a1a7d, 0x186c6ddd,
+	0x1a9cd9ac, 0x1ccb3236, 0x1ef74bf2, 0x2120fb82, 0x234815ba, 0x256c6f9e,
+	0x278dde6e, 0x29ac379f, 0x2bc750e8, 0x2ddf003f, 0x2ff31bdd, 0x32037a44,
+	0x340ff241, 0x36185aee, 0x381c8bb5, 0x3a1c5c56, 0x3c17a4e7, 0x3e0e3ddb,
+	0x3fffffff, 0x41ecc483, 0x43d464fa, 0x45b6bb5d, 0x4793a20f, 0x496af3e1,
+	0x4b3c8c11, 0x4d084650, 0x4ecdfec6, 0x508d9210, 0x5246dd48, 0x53f9be04,
+	0x55a6125a, 0x574bb8e5, 0x58ea90c2, 0x5a827999, 0x5c135399, 0x5d9cff82,
+	0x5f1f5ea0, 0x609a52d1, 0x620dbe8a, 0x637984d3, 0x64dd894f, 0x6639b039,
+	0x678dde6d, 0x68d9f963, 0x6a1de735, 0x6b598ea1, 0x6c8cd70a, 0x6db7a879,
+	0x6ed9eba0, 0x6ff389de, 0x71046d3c, 0x720c8074, 0x730baeec, 0x7401e4bf,
+	0x74ef0ebb, 0x75d31a5f, 0x76adf5e5, 0x777f903b, 0x7847d908, 0x7906c0af,
+	0x79bc384c, 0x7a6831b8, 0x7b0a9f8c, 0x7ba3751c, 0x7c32a67c, 0x7cb82884,
+	0x7d33f0c8, 0x7da5f5a3, 0x7e0e2e31, 0x7e6c924f, 0x7ec11aa3, 0x7f0bc095,
+	0x7f4c7e52, 0x7f834ecf, 0x7fb02dc4, 0x7fd317b3, 0x7fec09e1, 0x7ffb025e,
+	0x7fffffff
+};
+
+/**
+ * __fixp_sin32() returns the sin of an angle in degrees
+ *
+ * @degrees: angle, in degrees, from 0 to 360.
+ *
+ * The returned value ranges from -0x7fffffff to +0x7fffffff.
+ */
+static inline s32 __fixp_sin32(int degrees)
+{
+	s32 ret;
+	bool negative = false;
+
+	if (degrees > 180) {
+		negative = true;
+		degrees -= 180;
+	}
+	if (degrees > 90)
+		degrees = 180 - degrees;
+
+	ret = sin_table[degrees];
+
+	return negative ? -ret : ret;
+}
+
+/**
+ * fixp_sin32() returns the sin of an angle in degrees
+ *
+ * @degrees: angle, in degrees. The angle can be positive or negative
+ *
+ * The returned value ranges from -0x7fffffff to +0x7fffffff.
+ */
+static inline s32 fixp_sin32(int degrees)
+{
+	degrees = (degrees % 360 + 360) % 360;
+
+	return __fixp_sin32(degrees);
+}
+
+/* cos(x) = sin(x + 90 degrees) */
+#define fixp_cos32(v) fixp_sin32((v) + 90)
+
+/*
+ * 16 bits variants
+ *
+ * The returned value ranges from -0x7fff to 0x7fff
+ */
+
+#define fixp_sin16(v) (fixp_sin32(v) >> 16)
+#define fixp_cos16(v) (fixp_cos32(v) >> 16)
+
+/**
+ * fixp_sin32_rad() - calculates the sin of an angle in radians
+ *
+ * @radians: angle, in radians
+ * @twopi: value to be used for 2*pi
+ *
+ * Provides a variant for the cases where just 360
+ * values is not enough. This function uses linear
+ * interpolation to a wider range of values given by
+ * twopi var.
+ *
+ * Experimental tests gave a maximum difference of
+ * 0.000038 between the value calculated by sin() and
+ * the one produced by this function, when twopi is
+ * equal to 360000. That seems to be enough precision
+ * for practical purposes.
+ *
+ * Please notice that two high numbers for twopi could cause
+ * overflows, so the routine will not allow values of twopi
+ * bigger than 1^18.
+ */
+static inline s32 fixp_sin32_rad(u32 radians, u32 twopi)
+{
+	int degrees;
+	s32 v1, v2, dx, dy;
+	s64 tmp;
+
+	/*
+	 * Avoid too large values for twopi, as we don't want overflows.
+	 */
+	BUG_ON(twopi > 1 << 18);
+
+	degrees = (radians * 360) / twopi;
+	tmp = radians - (degrees * twopi) / 360;
+
+	degrees = (degrees % 360 + 360) % 360;
+	v1 = __fixp_sin32(degrees);
+
+	v2 = fixp_sin32(degrees + 1);
+
+	dx = twopi / 360;
+	dy = v2 - v1;
+
+	tmp *= dy;
+
+	return v1 +  div_s64(tmp, dx);
+}
+
+/* cos(x) = sin(x + pi/2 radians) */
+
+#define fixp_cos32_rad(rad, twopi)	\
+	fixp_sin32_rad(rad + twopi / 4, twopi)
+
+#endif
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
new file mode 100644
index 0000000..e974420
--- /dev/null
+++ b/include/linux/hdmi.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE 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 __LINUX_HDMI_H_
+#define __LINUX_HDMI_H_
+
+#include <linux/types.h>
+#include <linux/device.h>
+
+enum hdmi_infoframe_type {
+	HDMI_INFOFRAME_TYPE_VENDOR = 0x81,
+	HDMI_INFOFRAME_TYPE_AVI = 0x82,
+	HDMI_INFOFRAME_TYPE_SPD = 0x83,
+	HDMI_INFOFRAME_TYPE_AUDIO = 0x84,
+};
+
+#define HDMI_IEEE_OUI 0x000c03
+#define HDMI_INFOFRAME_HEADER_SIZE  4
+#define HDMI_AVI_INFOFRAME_SIZE    13
+#define HDMI_SPD_INFOFRAME_SIZE    25
+#define HDMI_AUDIO_INFOFRAME_SIZE  10
+
+#define HDMI_INFOFRAME_SIZE(type)	\
+	(HDMI_INFOFRAME_HEADER_SIZE + HDMI_ ## type ## _INFOFRAME_SIZE)
+
+struct hdmi_any_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+};
+
+enum hdmi_colorspace {
+	HDMI_COLORSPACE_RGB,
+	HDMI_COLORSPACE_YUV422,
+	HDMI_COLORSPACE_YUV444,
+	HDMI_COLORSPACE_YUV420,
+	HDMI_COLORSPACE_RESERVED4,
+	HDMI_COLORSPACE_RESERVED5,
+	HDMI_COLORSPACE_RESERVED6,
+	HDMI_COLORSPACE_IDO_DEFINED,
+};
+
+enum hdmi_scan_mode {
+	HDMI_SCAN_MODE_NONE,
+	HDMI_SCAN_MODE_OVERSCAN,
+	HDMI_SCAN_MODE_UNDERSCAN,
+	HDMI_SCAN_MODE_RESERVED,
+};
+
+enum hdmi_colorimetry {
+	HDMI_COLORIMETRY_NONE,
+	HDMI_COLORIMETRY_ITU_601,
+	HDMI_COLORIMETRY_ITU_709,
+	HDMI_COLORIMETRY_EXTENDED,
+};
+
+enum hdmi_picture_aspect {
+	HDMI_PICTURE_ASPECT_NONE,
+	HDMI_PICTURE_ASPECT_4_3,
+	HDMI_PICTURE_ASPECT_16_9,
+	HDMI_PICTURE_ASPECT_RESERVED,
+};
+
+enum hdmi_active_aspect {
+	HDMI_ACTIVE_ASPECT_16_9_TOP = 2,
+	HDMI_ACTIVE_ASPECT_14_9_TOP = 3,
+	HDMI_ACTIVE_ASPECT_16_9_CENTER = 4,
+	HDMI_ACTIVE_ASPECT_PICTURE = 8,
+	HDMI_ACTIVE_ASPECT_4_3 = 9,
+	HDMI_ACTIVE_ASPECT_16_9 = 10,
+	HDMI_ACTIVE_ASPECT_14_9 = 11,
+	HDMI_ACTIVE_ASPECT_4_3_SP_14_9 = 13,
+	HDMI_ACTIVE_ASPECT_16_9_SP_14_9 = 14,
+	HDMI_ACTIVE_ASPECT_16_9_SP_4_3 = 15,
+};
+
+enum hdmi_extended_colorimetry {
+	HDMI_EXTENDED_COLORIMETRY_XV_YCC_601,
+	HDMI_EXTENDED_COLORIMETRY_XV_YCC_709,
+	HDMI_EXTENDED_COLORIMETRY_S_YCC_601,
+	HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601,
+	HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB,
+
+	/* The following EC values are only defined in CEA-861-F. */
+	HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM,
+	HDMI_EXTENDED_COLORIMETRY_BT2020,
+	HDMI_EXTENDED_COLORIMETRY_RESERVED,
+};
+
+enum hdmi_quantization_range {
+	HDMI_QUANTIZATION_RANGE_DEFAULT,
+	HDMI_QUANTIZATION_RANGE_LIMITED,
+	HDMI_QUANTIZATION_RANGE_FULL,
+	HDMI_QUANTIZATION_RANGE_RESERVED,
+};
+
+/* non-uniform picture scaling */
+enum hdmi_nups {
+	HDMI_NUPS_UNKNOWN,
+	HDMI_NUPS_HORIZONTAL,
+	HDMI_NUPS_VERTICAL,
+	HDMI_NUPS_BOTH,
+};
+
+enum hdmi_ycc_quantization_range {
+	HDMI_YCC_QUANTIZATION_RANGE_LIMITED,
+	HDMI_YCC_QUANTIZATION_RANGE_FULL,
+};
+
+enum hdmi_content_type {
+	HDMI_CONTENT_TYPE_GRAPHICS,
+	HDMI_CONTENT_TYPE_PHOTO,
+	HDMI_CONTENT_TYPE_CINEMA,
+	HDMI_CONTENT_TYPE_GAME,
+};
+
+struct hdmi_avi_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+	enum hdmi_colorspace colorspace;
+	enum hdmi_scan_mode scan_mode;
+	enum hdmi_colorimetry colorimetry;
+	enum hdmi_picture_aspect picture_aspect;
+	enum hdmi_active_aspect active_aspect;
+	bool itc;
+	enum hdmi_extended_colorimetry extended_colorimetry;
+	enum hdmi_quantization_range quantization_range;
+	enum hdmi_nups nups;
+	unsigned char video_code;
+	enum hdmi_ycc_quantization_range ycc_quantization_range;
+	enum hdmi_content_type content_type;
+	unsigned char pixel_repeat;
+	unsigned short top_bar;
+	unsigned short bottom_bar;
+	unsigned short left_bar;
+	unsigned short right_bar;
+};
+
+int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame);
+ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
+				size_t size);
+
+enum hdmi_spd_sdi {
+	HDMI_SPD_SDI_UNKNOWN,
+	HDMI_SPD_SDI_DSTB,
+	HDMI_SPD_SDI_DVDP,
+	HDMI_SPD_SDI_DVHS,
+	HDMI_SPD_SDI_HDDVR,
+	HDMI_SPD_SDI_DVC,
+	HDMI_SPD_SDI_DSC,
+	HDMI_SPD_SDI_VCD,
+	HDMI_SPD_SDI_GAME,
+	HDMI_SPD_SDI_PC,
+	HDMI_SPD_SDI_BD,
+	HDMI_SPD_SDI_SACD,
+	HDMI_SPD_SDI_HDDVD,
+	HDMI_SPD_SDI_PMP,
+};
+
+struct hdmi_spd_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+	char vendor[8];
+	char product[16];
+	enum hdmi_spd_sdi sdi;
+};
+
+int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame,
+			    const char *vendor, const char *product);
+ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer,
+				size_t size);
+
+enum hdmi_audio_coding_type {
+	HDMI_AUDIO_CODING_TYPE_STREAM,
+	HDMI_AUDIO_CODING_TYPE_PCM,
+	HDMI_AUDIO_CODING_TYPE_AC3,
+	HDMI_AUDIO_CODING_TYPE_MPEG1,
+	HDMI_AUDIO_CODING_TYPE_MP3,
+	HDMI_AUDIO_CODING_TYPE_MPEG2,
+	HDMI_AUDIO_CODING_TYPE_AAC_LC,
+	HDMI_AUDIO_CODING_TYPE_DTS,
+	HDMI_AUDIO_CODING_TYPE_ATRAC,
+	HDMI_AUDIO_CODING_TYPE_DSD,
+	HDMI_AUDIO_CODING_TYPE_EAC3,
+	HDMI_AUDIO_CODING_TYPE_DTS_HD,
+	HDMI_AUDIO_CODING_TYPE_MLP,
+	HDMI_AUDIO_CODING_TYPE_DST,
+	HDMI_AUDIO_CODING_TYPE_WMA_PRO,
+	HDMI_AUDIO_CODING_TYPE_CXT,
+};
+
+enum hdmi_audio_sample_size {
+	HDMI_AUDIO_SAMPLE_SIZE_STREAM,
+	HDMI_AUDIO_SAMPLE_SIZE_16,
+	HDMI_AUDIO_SAMPLE_SIZE_20,
+	HDMI_AUDIO_SAMPLE_SIZE_24,
+};
+
+enum hdmi_audio_sample_frequency {
+	HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_32000,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_44100,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_48000,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_88200,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_96000,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_176400,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_192000,
+};
+
+enum hdmi_audio_coding_type_ext {
+	/* Refer to Audio Coding Type (CT) field in Data Byte 1 */
+	HDMI_AUDIO_CODING_TYPE_EXT_CT,
+
+	/*
+	 * The next three CXT values are defined in CEA-861-E only.
+	 * They do not exist in older versions, and in CEA-861-F they are
+	 * defined as 'Not in use'.
+	 */
+	HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC,
+	HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2,
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND,
+
+	/* The following CXT values are only defined in CEA-861-F. */
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC,
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_V2,
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC,
+	HDMI_AUDIO_CODING_TYPE_EXT_DRA,
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_SURROUND,
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC_SURROUND = 10,
+};
+
+struct hdmi_audio_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+	unsigned char channels;
+	enum hdmi_audio_coding_type coding_type;
+	enum hdmi_audio_sample_size sample_size;
+	enum hdmi_audio_sample_frequency sample_frequency;
+	enum hdmi_audio_coding_type_ext coding_type_ext;
+	unsigned char channel_allocation;
+	unsigned char level_shift_value;
+	bool downmix_inhibit;
+
+};
+
+int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame);
+ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
+				  void *buffer, size_t size);
+
+enum hdmi_3d_structure {
+	HDMI_3D_STRUCTURE_INVALID = -1,
+	HDMI_3D_STRUCTURE_FRAME_PACKING = 0,
+	HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE,
+	HDMI_3D_STRUCTURE_LINE_ALTERNATIVE,
+	HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL,
+	HDMI_3D_STRUCTURE_L_DEPTH,
+	HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH,
+	HDMI_3D_STRUCTURE_TOP_AND_BOTTOM,
+	HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF = 8,
+};
+
+
+struct hdmi_vendor_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+	unsigned int oui;
+	u8 vic;
+	enum hdmi_3d_structure s3d_struct;
+	unsigned int s3d_ext_data;
+};
+
+int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame);
+ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
+				   void *buffer, size_t size);
+
+union hdmi_vendor_any_infoframe {
+	struct {
+		enum hdmi_infoframe_type type;
+		unsigned char version;
+		unsigned char length;
+		unsigned int oui;
+	} any;
+	struct hdmi_vendor_infoframe hdmi;
+};
+
+/**
+ * union hdmi_infoframe - overall union of all abstract infoframe representations
+ * @any: generic infoframe
+ * @avi: avi infoframe
+ * @spd: spd infoframe
+ * @vendor: union of all vendor infoframes
+ * @audio: audio infoframe
+ *
+ * This is used by the generic pack function. This works since all infoframes
+ * have the same header which also indicates which type of infoframe should be
+ * packed.
+ */
+union hdmi_infoframe {
+	struct hdmi_any_infoframe any;
+	struct hdmi_avi_infoframe avi;
+	struct hdmi_spd_infoframe spd;
+	union hdmi_vendor_any_infoframe vendor;
+	struct hdmi_audio_infoframe audio;
+};
+
+ssize_t
+hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size);
+int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer);
+void hdmi_infoframe_log(const char *level, struct device *dev,
+			union hdmi_infoframe *frame);
+
+#endif /* _DRM_HDMI_H */
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 4f4eea8..b9c7897 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1017,6 +1017,15 @@
 	u8 mic[8];
 } __packed;
 
+/* Management MIC information element (IEEE 802.11w) for GMAC and CMAC-256 */
+struct ieee80211_mmie_16 {
+	u8 element_id;
+	u8 length;
+	__le16 key_id;
+	u8 sequence_number[6];
+	u8 mic[16];
+} __packed;
+
 struct ieee80211_vendor_ie {
 	u8 element_id;
 	u8 len;
@@ -1994,9 +2003,15 @@
 	WLAN_KEY_LEN_WEP40 = 5,
 	WLAN_KEY_LEN_WEP104 = 13,
 	WLAN_KEY_LEN_CCMP = 16,
+	WLAN_KEY_LEN_CCMP_256 = 32,
 	WLAN_KEY_LEN_TKIP = 32,
 	WLAN_KEY_LEN_AES_CMAC = 16,
 	WLAN_KEY_LEN_SMS4 = 32,
+	WLAN_KEY_LEN_GCMP = 16,
+	WLAN_KEY_LEN_GCMP_256 = 32,
+	WLAN_KEY_LEN_BIP_CMAC_256 = 32,
+	WLAN_KEY_LEN_BIP_GMAC_128 = 16,
+	WLAN_KEY_LEN_BIP_GMAC_256 = 32,
 };
 
 #define IEEE80211_WEP_IV_LEN		4
@@ -2004,9 +2019,16 @@
 #define IEEE80211_CCMP_HDR_LEN		8
 #define IEEE80211_CCMP_MIC_LEN		8
 #define IEEE80211_CCMP_PN_LEN		6
+#define IEEE80211_CCMP_256_HDR_LEN	8
+#define IEEE80211_CCMP_256_MIC_LEN	16
+#define IEEE80211_CCMP_256_PN_LEN	6
 #define IEEE80211_TKIP_IV_LEN		8
 #define IEEE80211_TKIP_ICV_LEN		4
 #define IEEE80211_CMAC_PN_LEN		6
+#define IEEE80211_GMAC_PN_LEN		6
+#define IEEE80211_GCMP_HDR_LEN		8
+#define IEEE80211_GCMP_MIC_LEN		16
+#define IEEE80211_GCMP_PN_LEN		6
 
 /* Public action codes */
 enum ieee80211_pub_actioncode {
@@ -2230,6 +2252,11 @@
 #define WLAN_CIPHER_SUITE_WEP104	0x000FAC05
 #define WLAN_CIPHER_SUITE_AES_CMAC	0x000FAC06
 #define WLAN_CIPHER_SUITE_GCMP		0x000FAC08
+#define WLAN_CIPHER_SUITE_GCMP_256	0x000FAC09
+#define WLAN_CIPHER_SUITE_CCMP_256	0x000FAC0A
+#define WLAN_CIPHER_SUITE_BIP_GMAC_128	0x000FAC0B
+#define WLAN_CIPHER_SUITE_BIP_GMAC_256	0x000FAC0C
+#define WLAN_CIPHER_SUITE_BIP_CMAC_256	0x000FAC0D
 
 #define WLAN_CIPHER_SUITE_SMS4		0x00147201
 
diff --git a/include/linux/ieee802154.h b/include/linux/ieee802154.h
index 6e82d88..8872ca1 100644
--- a/include/linux/ieee802154.h
+++ b/include/linux/ieee802154.h
@@ -28,7 +28,9 @@
 #include <asm/byteorder.h>
 
 #define IEEE802154_MTU			127
-#define IEEE802154_MIN_PSDU_LEN		5
+#define IEEE802154_ACK_PSDU_LEN		5
+#define IEEE802154_MIN_PSDU_LEN		9
+#define IEEE802154_FCS_LEN		2
 
 #define IEEE802154_PAN_ID_BROADCAST	0xffff
 #define IEEE802154_ADDR_SHORT_BROADCAST	0xffff
@@ -38,6 +40,7 @@
 
 #define IEEE802154_LIFS_PERIOD		40
 #define IEEE802154_SIFS_PERIOD		12
+#define IEEE802154_MAX_SIFS_FRAME_SIZE	18
 
 #define IEEE802154_MAX_CHANNEL		26
 #define IEEE802154_MAX_PAGE		31
@@ -204,11 +207,18 @@
 
 /**
  * ieee802154_is_valid_psdu_len - check if psdu len is valid
+ * available lengths:
+ *	0-4	Reserved
+ *	5	MPDU (Acknowledgment)
+ *	6-8	Reserved
+ *	9-127	MPDU
+ *
  * @len: psdu len with (MHR + payload + MFR)
  */
 static inline bool ieee802154_is_valid_psdu_len(const u8 len)
 {
-	return (len >= IEEE802154_MIN_PSDU_LEN && len <= IEEE802154_MTU);
+	return (len == IEEE802154_ACK_PSDU_LEN ||
+		(len >= IEEE802154_MIN_PSDU_LEN && len <= IEEE802154_MTU));
 }
 
 /**
diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
index 0f01fe0..83430f2 100644
--- a/include/linux/mmc/sdio_ids.h
+++ b/include/linux/mmc/sdio_ids.h
@@ -24,13 +24,17 @@
  * Vendors and devices.  Sort key: vendor first, device next.
  */
 #define SDIO_VENDOR_ID_BROADCOM			0x02d0
-#define SDIO_DEVICE_ID_BROADCOM_43143		43143
+#define SDIO_DEVICE_ID_BROADCOM_43143		0xa887
 #define SDIO_DEVICE_ID_BROADCOM_43241		0x4324
 #define SDIO_DEVICE_ID_BROADCOM_4329		0x4329
 #define SDIO_DEVICE_ID_BROADCOM_4330		0x4330
 #define SDIO_DEVICE_ID_BROADCOM_4334		0x4334
+#define SDIO_DEVICE_ID_BROADCOM_43340		0xa94c
+#define SDIO_DEVICE_ID_BROADCOM_43341		0xa94d
 #define SDIO_DEVICE_ID_BROADCOM_4335_4339	0x4335
-#define SDIO_DEVICE_ID_BROADCOM_43362		43362
+#define SDIO_DEVICE_ID_BROADCOM_43362		0xa962
+#define SDIO_DEVICE_ID_BROADCOM_43430		0xa9a6
+#define SDIO_DEVICE_ID_BROADCOM_4345		0x4345
 #define SDIO_DEVICE_ID_BROADCOM_4354		0x4354
 
 #define SDIO_VENDOR_ID_INTEL			0x0089
diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h
index befef42..7bc92e0 100644
--- a/include/linux/of_graph.h
+++ b/include/linux/of_graph.h
@@ -14,6 +14,8 @@
 #ifndef __LINUX_OF_GRAPH_H
 #define __LINUX_OF_GRAPH_H
 
+#include <linux/types.h>
+
 /**
  * struct of_endpoint - the OF graph endpoint data structure
  * @port: identifier (value of reg property) of a port this endpoint belongs to
@@ -26,9 +28,21 @@
 	const struct device_node *local_node;
 };
 
+/**
+ * for_each_endpoint_of_node - iterate over every endpoint in a device node
+ * @parent: parent device node containing ports and endpoints
+ * @child: loop variable pointing to the current endpoint node
+ *
+ * When breaking out of the loop, of_node_put(child) has to be called manually.
+ */
+#define for_each_endpoint_of_node(parent, child) \
+	for (child = of_graph_get_next_endpoint(parent, NULL); child != NULL; \
+	     child = of_graph_get_next_endpoint(parent, child))
+
 #ifdef CONFIG_OF
 int of_graph_parse_endpoint(const struct device_node *node,
 				struct of_endpoint *endpoint);
+struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
 struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
 					struct device_node *previous);
 struct device_node *of_graph_get_remote_port_parent(
@@ -42,6 +56,12 @@
 	return -ENOSYS;
 }
 
+static inline struct device_node *of_graph_get_port_by_id(
+					struct device_node *node, u32 id)
+{
+	return NULL;
+}
+
 static inline struct device_node *of_graph_get_next_endpoint(
 					const struct device_node *parent,
 					struct device_node *previous)
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index e63c02a..38cff8f 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2315,6 +2315,8 @@
 #define PCI_VENDOR_ID_CENATEK		0x16CA
 #define PCI_DEVICE_ID_CENATEK_IDE	0x0001
 
+#define PCI_VENDOR_ID_SYNOPSYS		0x16c3
+
 #define PCI_VENDOR_ID_VITESSE		0x1725
 #define PCI_DEVICE_ID_VITESSE_VSC7174	0x7174
 
diff --git a/include/linux/platform_data/nxp-nci.h b/include/linux/platform_data/nxp-nci.h
new file mode 100644
index 0000000..d6ed286
--- /dev/null
+++ b/include/linux/platform_data/nxp-nci.h
@@ -0,0 +1,27 @@
+/*
+ * Generic platform data for the NXP NCI NFC chips.
+ *
+ * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * 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.
+ */
+
+#ifndef _NXP_NCI_H_
+#define _NXP_NCI_H_
+
+struct nxp_nci_nfc_platform_data {
+	unsigned int gpio_en;
+	unsigned int gpio_fw;
+	unsigned int irq;
+};
+
+#endif /* _NXP_NCI_H_ */
diff --git a/include/linux/platform_data/st21nfca.h b/include/linux/platform_data/st21nfca.h
index 5087fff..cc2bdaf 100644
--- a/include/linux/platform_data/st21nfca.h
+++ b/include/linux/platform_data/st21nfca.h
@@ -26,6 +26,8 @@
 struct st21nfca_nfc_platform_data {
 	unsigned int gpio_ena;
 	unsigned int irq_polarity;
+	bool is_ese_present;
+	bool is_uicc_present;
 };
 
 #endif /* _ST21NFCA_HCI_H_ */
diff --git a/include/linux/platform_data/st21nfcb.h b/include/linux/platform_data/st21nfcb.h
index c3b432f..b023373 100644
--- a/include/linux/platform_data/st21nfcb.h
+++ b/include/linux/platform_data/st21nfcb.h
@@ -19,8 +19,6 @@
 #ifndef _ST21NFCB_NCI_H_
 #define _ST21NFCB_NCI_H_
 
-#include <linux/i2c.h>
-
 #define ST21NFCB_NCI_DRIVER_NAME "st21nfcb_nci"
 
 struct st21nfcb_nfc_platform_data {
@@ -28,4 +26,4 @@
 	unsigned int irq_polarity;
 };
 
-#endif /* _ST21NFCA_HCI_H_ */
+#endif /* _ST21NFCB_NCI_H_ */
diff --git a/include/linux/platform_data/vsp1.h b/include/linux/platform_data/vsp1.h
deleted file mode 100644
index 63170e2..0000000
--- a/include/linux/platform_data/vsp1.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * vsp1.h  --  R-Car VSP1 Platform Data
- *
- * Copyright (C) 2013 Renesas Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-#ifndef __PLATFORM_VSP1_H__
-#define __PLATFORM_VSP1_H__
-
-#define VSP1_HAS_LIF		(1 << 0)
-#define VSP1_HAS_LUT		(1 << 1)
-#define VSP1_HAS_SRU		(1 << 2)
-
-struct vsp1_platform_data {
-	unsigned int features;
-	unsigned int rpf_count;
-	unsigned int uds_count;
-	unsigned int wpf_count;
-};
-
-#endif /* __PLATFORM_VSP1_H__ */
diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h
new file mode 100644
index 0000000..83fb924
--- /dev/null
+++ b/include/linux/rhashtable.h
@@ -0,0 +1,30 @@
+/* Automatically created during backport process */
+#ifndef CPTCFG_BPAUTO_RHASHTABLE
+#include_next <linux/rhashtable.h>
+#else
+#undef lockdep_rht_mutex_is_held
+#define lockdep_rht_mutex_is_held LINUX_BACKPORT(lockdep_rht_mutex_is_held)
+#undef lockdep_rht_bucket_is_held
+#define lockdep_rht_bucket_is_held LINUX_BACKPORT(lockdep_rht_bucket_is_held)
+#undef rhashtable_insert_rehash
+#define rhashtable_insert_rehash LINUX_BACKPORT(rhashtable_insert_rehash)
+#undef rhashtable_insert_slow
+#define rhashtable_insert_slow LINUX_BACKPORT(rhashtable_insert_slow)
+#undef rhashtable_walk_init
+#define rhashtable_walk_init LINUX_BACKPORT(rhashtable_walk_init)
+#undef rhashtable_walk_exit
+#define rhashtable_walk_exit LINUX_BACKPORT(rhashtable_walk_exit)
+#undef rhashtable_walk_start
+#define rhashtable_walk_start LINUX_BACKPORT(rhashtable_walk_start)
+#undef rhashtable_walk_next
+#define rhashtable_walk_next LINUX_BACKPORT(rhashtable_walk_next)
+#undef rhashtable_walk_stop
+#define rhashtable_walk_stop LINUX_BACKPORT(rhashtable_walk_stop)
+#undef rhashtable_init
+#define rhashtable_init LINUX_BACKPORT(rhashtable_init)
+#undef rhashtable_free_and_destroy
+#define rhashtable_free_and_destroy LINUX_BACKPORT(rhashtable_free_and_destroy)
+#undef rhashtable_destroy
+#define rhashtable_destroy LINUX_BACKPORT(rhashtable_destroy)
+#include <linux/backport-rhashtable.h>
+#endif /* CPTCFG_BPAUTO_RHASHTABLE */
diff --git a/include/linux/spi/at86rf230.h b/include/linux/spi/at86rf230.h
index b2b1afb..b63fe6f 100644
--- a/include/linux/spi/at86rf230.h
+++ b/include/linux/spi/at86rf230.h
@@ -12,10 +12,6 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Written by:
  * Dmitry Eremin-Solenikov <dmitry.baryshkov@siemens.com>
  */
@@ -26,6 +22,7 @@
 	int rstn;
 	int slp_tr;
 	int dig2;
+	u8 xtal_trim;
 };
 
 #endif
diff --git a/include/linux/spi/cc2520.h b/include/linux/spi/cc2520.h
index 85b8ee6..e741e8b 100644
--- a/include/linux/spi/cc2520.h
+++ b/include/linux/spi/cc2520.h
@@ -21,6 +21,7 @@
 	int sfd;
 	int reset;
 	int vreg;
+	bool amplified;
 };
 
 #endif
diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h
index f7b9100..c0f707a 100644
--- a/include/linux/ssb/ssb_regs.h
+++ b/include/linux/ssb/ssb_regs.h
@@ -173,6 +173,7 @@
 #define SSB_SPROMSIZE_BYTES_R123	(SSB_SPROMSIZE_WORDS_R123 * sizeof(u16))
 #define SSB_SPROMSIZE_BYTES_R4		(SSB_SPROMSIZE_WORDS_R4 * sizeof(u16))
 #define SSB_SPROMSIZE_WORDS_R10		230
+#define SSB_SPROMSIZE_WORDS_R11		234
 #define SSB_SPROM_BASE1			0x1000
 #define SSB_SPROM_BASE31		0x0800
 #define SSB_SPROM_REVISION		0x007E
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index d9a4905..6e0ce8c 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -227,9 +227,23 @@
 	struct urb		*urb;
 	struct usbnet		*dev;
 	enum skb_state		state;
-	size_t			length;
+	long			length;
+	unsigned long		packets;
 };
 
+/* Drivers that set FLAG_MULTI_PACKET must call this in their
+ * tx_fixup method before returning an skb.
+ */
+static inline void
+usbnet_set_skb_tx_stats(struct sk_buff *skb,
+			unsigned long packets, long bytes_delta)
+{
+	struct skb_data *entry = (struct skb_data *) skb->cb;
+
+	entry->packets = packets;
+	entry->length = bytes_delta;
+}
+
 extern int usbnet_open(struct net_device *net);
 extern int usbnet_stop(struct net_device *net);
 extern netdev_tx_t usbnet_start_xmit(struct sk_buff *skb,
diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h
index 54b7b62..4cadb3a 100644
--- a/include/linux/wl12xx.h
+++ b/include/linux/wl12xx.h
@@ -26,28 +26,6 @@
 
 #include <linux/err.h>
 
-/* Reference clock values */
-enum {
-	WL12XX_REFCLOCK_19	= 0, /* 19.2 MHz */
-	WL12XX_REFCLOCK_26	= 1, /* 26 MHz */
-	WL12XX_REFCLOCK_38	= 2, /* 38.4 MHz */
-	WL12XX_REFCLOCK_52	= 3, /* 52 MHz */
-	WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */
-	WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */
-};
-
-/* TCXO clock values */
-enum {
-	WL12XX_TCXOCLOCK_19_2	= 0, /* 19.2MHz */
-	WL12XX_TCXOCLOCK_26	= 1, /* 26 MHz */
-	WL12XX_TCXOCLOCK_38_4	= 2, /* 38.4MHz */
-	WL12XX_TCXOCLOCK_52	= 3, /* 52 MHz */
-	WL12XX_TCXOCLOCK_16_368	= 4, /* 16.368 MHz */
-	WL12XX_TCXOCLOCK_32_736	= 5, /* 32.736 MHz */
-	WL12XX_TCXOCLOCK_16_8	= 6, /* 16.8 MHz */
-	WL12XX_TCXOCLOCK_33_6	= 7, /* 33.6 MHz */
-};
-
 struct wl1251_platform_data {
 	int power_gpio;
 	/* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */
@@ -55,23 +33,8 @@
 	bool use_eeprom;
 };
 
-struct wl12xx_platform_data {
-	int irq;
-	int board_ref_clock;
-	int board_tcxo_clock;
-	unsigned long platform_quirks;
-	bool pwr_in_suspend;
-};
-
-/* Platform does not support level trigger interrupts */
-#define WL12XX_PLATFORM_QUIRK_EDGE_IRQ	BIT(0)
-
 #ifdef CPTCFG_WILINK_PLATFORM_DATA
 
-int wl12xx_set_platform_data(const struct wl12xx_platform_data *data);
-
-struct wl12xx_platform_data *wl12xx_get_platform_data(void);
-
 int wl1251_set_platform_data(const struct wl1251_platform_data *data);
 
 struct wl1251_platform_data *wl1251_get_platform_data(void);
@@ -79,18 +42,6 @@
 #else
 
 static inline
-int wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
-{
-	return -ENOSYS;
-}
-
-static inline
-struct wl12xx_platform_data *wl12xx_get_platform_data(void)
-{
-	return ERR_PTR(-ENODATA);
-}
-
-static inline
 int wl1251_set_platform_data(const struct wl1251_platform_data *data)
 {
 	return -ENOSYS;
diff --git a/include/media/adv7604.h b/include/media/adv7604.h
index aa1c447..9ecf353 100644
--- a/include/media/adv7604.h
+++ b/include/media/adv7604.h
@@ -47,16 +47,16 @@
 };
 
 /* Input Color Space (IO register 0x02, [7:4]) */
-enum adv7604_inp_color_space {
-	ADV7604_INP_COLOR_SPACE_LIM_RGB = 0,
-	ADV7604_INP_COLOR_SPACE_FULL_RGB = 1,
-	ADV7604_INP_COLOR_SPACE_LIM_YCbCr_601 = 2,
-	ADV7604_INP_COLOR_SPACE_LIM_YCbCr_709 = 3,
-	ADV7604_INP_COLOR_SPACE_XVYCC_601 = 4,
-	ADV7604_INP_COLOR_SPACE_XVYCC_709 = 5,
-	ADV7604_INP_COLOR_SPACE_FULL_YCbCr_601 = 6,
-	ADV7604_INP_COLOR_SPACE_FULL_YCbCr_709 = 7,
-	ADV7604_INP_COLOR_SPACE_AUTO = 0xf,
+enum adv76xx_inp_color_space {
+	ADV76XX_INP_COLOR_SPACE_LIM_RGB = 0,
+	ADV76XX_INP_COLOR_SPACE_FULL_RGB = 1,
+	ADV76XX_INP_COLOR_SPACE_LIM_YCbCr_601 = 2,
+	ADV76XX_INP_COLOR_SPACE_LIM_YCbCr_709 = 3,
+	ADV76XX_INP_COLOR_SPACE_XVYCC_601 = 4,
+	ADV76XX_INP_COLOR_SPACE_XVYCC_709 = 5,
+	ADV76XX_INP_COLOR_SPACE_FULL_YCbCr_601 = 6,
+	ADV76XX_INP_COLOR_SPACE_FULL_YCbCr_709 = 7,
+	ADV76XX_INP_COLOR_SPACE_AUTO = 0xf,
 };
 
 /* Select output format (IO register 0x03, [4:2]) */
@@ -66,38 +66,39 @@
 	ADV7604_OP_FORMAT_MODE2 = 0x08,
 };
 
-enum adv7604_drive_strength {
-	ADV7604_DR_STR_MEDIUM_LOW = 1,
-	ADV7604_DR_STR_MEDIUM_HIGH = 2,
-	ADV7604_DR_STR_HIGH = 3,
+enum adv76xx_drive_strength {
+	ADV76XX_DR_STR_MEDIUM_LOW = 1,
+	ADV76XX_DR_STR_MEDIUM_HIGH = 2,
+	ADV76XX_DR_STR_HIGH = 3,
 };
 
-enum adv7604_int1_config {
-	ADV7604_INT1_CONFIG_OPEN_DRAIN,
-	ADV7604_INT1_CONFIG_ACTIVE_LOW,
-	ADV7604_INT1_CONFIG_ACTIVE_HIGH,
-	ADV7604_INT1_CONFIG_DISABLED,
+/* INT1 Configuration (IO register 0x40, [1:0]) */
+enum adv76xx_int1_config {
+	ADV76XX_INT1_CONFIG_OPEN_DRAIN,
+	ADV76XX_INT1_CONFIG_ACTIVE_LOW,
+	ADV76XX_INT1_CONFIG_ACTIVE_HIGH,
+	ADV76XX_INT1_CONFIG_DISABLED,
 };
 
-enum adv7604_page {
-	ADV7604_PAGE_IO,
+enum adv76xx_page {
+	ADV76XX_PAGE_IO,
 	ADV7604_PAGE_AVLINK,
-	ADV7604_PAGE_CEC,
-	ADV7604_PAGE_INFOFRAME,
+	ADV76XX_PAGE_CEC,
+	ADV76XX_PAGE_INFOFRAME,
 	ADV7604_PAGE_ESDP,
 	ADV7604_PAGE_DPP,
-	ADV7604_PAGE_AFE,
-	ADV7604_PAGE_REP,
-	ADV7604_PAGE_EDID,
-	ADV7604_PAGE_HDMI,
-	ADV7604_PAGE_TEST,
-	ADV7604_PAGE_CP,
+	ADV76XX_PAGE_AFE,
+	ADV76XX_PAGE_REP,
+	ADV76XX_PAGE_EDID,
+	ADV76XX_PAGE_HDMI,
+	ADV76XX_PAGE_TEST,
+	ADV76XX_PAGE_CP,
 	ADV7604_PAGE_VDP,
-	ADV7604_PAGE_MAX,
+	ADV76XX_PAGE_MAX,
 };
 
 /* Platform dependent definition */
-struct adv7604_platform_data {
+struct adv76xx_platform_data {
 	/* DIS_PWRDNB: 1 if the PWRDNB pin is unused and unconnected */
 	unsigned disable_pwrdnb:1;
 
@@ -116,7 +117,7 @@
 	enum adv7604_op_format_mode_sel op_format_mode_sel;
 
 	/* Configuration of the INT1 pin */
-	enum adv7604_int1_config int1_config;
+	enum adv76xx_int1_config int1_config;
 
 	/* IO register 0x02 */
 	unsigned alt_gamma:1;
@@ -134,9 +135,9 @@
 	unsigned inv_llc_pol:1;
 
 	/* IO register 0x14 */
-	enum adv7604_drive_strength dr_str_data;
-	enum adv7604_drive_strength dr_str_clk;
-	enum adv7604_drive_strength dr_str_sync;
+	enum adv76xx_drive_strength dr_str_data;
+	enum adv76xx_drive_strength dr_str_clk;
+	enum adv76xx_drive_strength dr_str_sync;
 
 	/* IO register 0x30 */
 	unsigned output_bus_lsb_to_msb:1;
@@ -145,11 +146,11 @@
 	unsigned hdmi_free_run_mode;
 
 	/* i2c addresses: 0 == use default */
-	u8 i2c_addresses[ADV7604_PAGE_MAX];
+	u8 i2c_addresses[ADV76XX_PAGE_MAX];
 };
 
-enum adv7604_pad {
-	ADV7604_PAD_HDMI_PORT_A = 0,
+enum adv76xx_pad {
+	ADV76XX_PAD_HDMI_PORT_A = 0,
 	ADV7604_PAD_HDMI_PORT_B = 1,
 	ADV7604_PAD_HDMI_PORT_C = 2,
 	ADV7604_PAD_HDMI_PORT_D = 3,
@@ -158,7 +159,7 @@
 	/* The source pad is either 1 (ADV7611) or 6 (ADV7604) */
 	ADV7604_PAD_SOURCE = 6,
 	ADV7611_PAD_SOURCE = 1,
-	ADV7604_PAD_MAX = 7,
+	ADV76XX_PAD_MAX = 7,
 };
 
 #define V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE	(V4L2_CID_DV_CLASS_BASE + 0x1000)
@@ -166,7 +167,7 @@
 #define V4L2_CID_ADV_RX_FREE_RUN_COLOR		(V4L2_CID_DV_CLASS_BASE + 0x1002)
 
 /* notify events */
-#define ADV7604_HOTPLUG		1
-#define ADV7604_FMT_CHANGE	2
+#define ADV76XX_HOTPLUG		1
+#define ADV76XX_FMT_CHANGE	2
 
 #endif
diff --git a/include/media/atmel-isi.h b/include/media/atmel-isi.h
index c2e5703..6008b09 100644
--- a/include/media/atmel-isi.h
+++ b/include/media/atmel-isi.h
@@ -59,6 +59,10 @@
 #define		ISI_CFG1_FRATE_DIV_MASK		(7 << 8)
 #define ISI_CFG1_DISCR				(1 << 11)
 #define ISI_CFG1_FULL_MODE			(1 << 12)
+/* Definition for THMASK(ISI_V2) */
+#define		ISI_CFG1_THMASK_BEATS_4		(0 << 13)
+#define		ISI_CFG1_THMASK_BEATS_8		(1 << 13)
+#define		ISI_CFG1_THMASK_BEATS_16	(2 << 13)
 
 /* Bitfields in CFG2 */
 #define ISI_CFG2_GRAYSCALE			(1 << 13)
diff --git a/include/media/davinci/vpfe_capture.h b/include/media/davinci/vpfe_capture.h
index 288772e..28bcd71 100644
--- a/include/media/davinci/vpfe_capture.h
+++ b/include/media/davinci/vpfe_capture.h
@@ -102,7 +102,7 @@
 struct vpfe_device {
 	/* V4l2 specific parameters */
 	/* Identifies video device for this channel */
-	struct video_device *video_dev;
+	struct video_device video_dev;
 	/* sub devices */
 	struct v4l2_subdev **sd;
 	/* vpfe cfg */
diff --git a/include/media/media-entity.h b/include/media/media-entity.h
index e004591..0c003d8 100644
--- a/include/media/media-entity.h
+++ b/include/media/media-entity.h
@@ -44,6 +44,15 @@
 	unsigned long flags;		/* Pad flags (MEDIA_PAD_FL_*) */
 };
 
+/**
+ * struct media_entity_operations - Media entity operations
+ * @link_setup:		Notify the entity of link changes. The operation can
+ *			return an error, in which case link setup will be
+ *			cancelled. Optional.
+ * @link_validate:	Return whether a link is valid from the entity point of
+ *			view. The media_entity_pipeline_start() function
+ *			validates all links by calling this operation. Optional.
+ */
 struct media_entity_operations {
 	int (*link_setup)(struct media_entity *entity,
 			  const struct media_pad *local,
@@ -87,17 +96,7 @@
 		struct {
 			u32 major;
 			u32 minor;
-		} v4l;
-		struct {
-			u32 major;
-			u32 minor;
-		} fb;
-		struct {
-			u32 card;
-			u32 device;
-			u32 subdevice;
-		} alsa;
-		int dvb;
+		} dev;
 
 		/* Sub-device specifications */
 		/* Nothing needed yet */
diff --git a/include/media/mt9p031.h b/include/media/mt9p031.h
index b1e63f2..1ba3612 100644
--- a/include/media/mt9p031.h
+++ b/include/media/mt9p031.h
@@ -5,12 +5,10 @@
 
 /*
  * struct mt9p031_platform_data - MT9P031 platform data
- * @reset: Chip reset GPIO (set to -1 if not used)
  * @ext_freq: Input clock frequency
  * @target_freq: Pixel clock frequency
  */
 struct mt9p031_platform_data {
-	int reset;
 	int ext_freq;
 	int target_freq;
 };
diff --git a/include/media/omap3isp.h b/include/media/omap3isp.h
index 398279d..048f8f9 100644
--- a/include/media/omap3isp.h
+++ b/include/media/omap3isp.h
@@ -45,7 +45,7 @@
 };
 
 /**
- * struct isp_parallel_platform_data - Parallel interface platform data
+ * struct isp_parallel_cfg - Parallel interface configuration
  * @data_lane_shift: Data lane shifter
  *		ISP_LANE_SHIFT_0 - CAMEXT[13:0] -> CAM[13:0]
  *		ISP_LANE_SHIFT_2 - CAMEXT[13:2] -> CAM[11:0]
@@ -62,7 +62,7 @@
  * @data_pol: Data polarity
  *		0 - Normal, 1 - One's complement
  */
-struct isp_parallel_platform_data {
+struct isp_parallel_cfg {
 	unsigned int data_lane_shift:2;
 	unsigned int clk_pol:1;
 	unsigned int hs_pol:1;
@@ -105,7 +105,7 @@
 };
 
 /**
- * struct isp_ccp2_platform_data - CCP2 interface platform data
+ * struct isp_ccp2_cfg - CCP2 interface configuration
  * @strobe_clk_pol: Strobe/clock polarity
  *		0 - Non Inverted, 1 - Inverted
  * @crc: Enable the cyclic redundancy check
@@ -117,7 +117,7 @@
  *		ISP_CCP2_PHY_DATA_STROBE - Data/strobe physical layer
  * @vpclk_div: Video port output clock control
  */
-struct isp_ccp2_platform_data {
+struct isp_ccp2_cfg {
 	unsigned int strobe_clk_pol:1;
 	unsigned int crc:1;
 	unsigned int ccp2_mode:1;
@@ -127,39 +127,31 @@
 };
 
 /**
- * struct isp_csi2_platform_data - CSI2 interface platform data
+ * struct isp_csi2_cfg - CSI2 interface configuration
  * @crc: Enable the cyclic redundancy check
- * @vpclk_div: Video port output clock control
  */
-struct isp_csi2_platform_data {
+struct isp_csi2_cfg {
 	unsigned crc:1;
-	unsigned vpclk_div:2;
 	struct isp_csiphy_lanes_cfg lanecfg;
 };
 
-struct isp_subdev_i2c_board_info {
-	struct i2c_board_info *board_info;
-	int i2c_adapter_id;
-};
-
-struct isp_v4l2_subdevs_group {
-	struct isp_subdev_i2c_board_info *subdevs;
+struct isp_bus_cfg {
 	enum isp_interface_type interface;
 	union {
-		struct isp_parallel_platform_data parallel;
-		struct isp_ccp2_platform_data ccp2;
-		struct isp_csi2_platform_data csi2;
+		struct isp_parallel_cfg parallel;
+		struct isp_ccp2_cfg ccp2;
+		struct isp_csi2_cfg csi2;
 	} bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
 };
 
-struct isp_platform_xclk {
-	const char *dev_id;
-	const char *con_id;
+struct isp_platform_subdev {
+	struct i2c_board_info *board_info;
+	int i2c_adapter_id;
+	struct isp_bus_cfg *bus;
 };
 
 struct isp_platform_data {
-	struct isp_platform_xclk xclks[2];
-	struct isp_v4l2_subdevs_group *subdevs;
+	struct isp_platform_subdev *subdevs;
 	void (*set_constraints)(struct isp_device *isp, bool enable);
 };
 
diff --git a/include/media/ov2659.h b/include/media/ov2659.h
new file mode 100644
index 0000000..4216adc
--- /dev/null
+++ b/include/media/ov2659.h
@@ -0,0 +1,34 @@
+/*
+ * Omnivision OV2659 CMOS Image Sensor driver
+ *
+ * Copyright (C) 2015 Texas Instruments, Inc.
+ *
+ * Benoit Parrot <bparrot@ti.com>
+ * Lad, Prabhakar <prabhakar.csengg@gmail.com>
+ *
+ * This program is free software; you may 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * 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 OV2659_H
+#define OV2659_H
+
+/**
+ * struct ov2659_platform_data - ov2659 driver platform data
+ * @link_frequency: target pixel clock frequency
+ */
+struct ov2659_platform_data {
+	s64 link_frequency;
+};
+
+#endif /* OV2659_H */
diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h
index 944ecdf..92766f7 100644
--- a/include/media/saa7146_vv.h
+++ b/include/media/saa7146_vv.h
@@ -178,8 +178,8 @@
 };
 
 /* from saa7146_fops.c */
-int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, char *name, int type);
-int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev);
+int saa7146_register_device(struct video_device *vid, struct saa7146_dev *dev, char *name, int type);
+int saa7146_unregister_device(struct video_device *vid, struct saa7146_dev *dev);
 void saa7146_buffer_finish(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, int state);
 void saa7146_buffer_next(struct saa7146_dev *dev, struct saa7146_dmaqueue *q,int vbi);
 int saa7146_buffer_queue(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, struct saa7146_buf *buf);
diff --git a/include/media/smiapp.h b/include/media/smiapp.h
index 0b8f124..268a3cd 100644
--- a/include/media/smiapp.h
+++ b/include/media/smiapp.h
@@ -65,19 +65,19 @@
 	unsigned short i2c_addr_dfl;	/* Default i2c addr */
 	unsigned short i2c_addr_alt;	/* Alternate i2c addr */
 
-	unsigned int nvm_size;			/* bytes */
-	unsigned int ext_clk;			/* sensor external clk */
+	uint32_t nvm_size;		/* bytes */
+	uint32_t ext_clk;		/* sensor external clk */
 
 	unsigned int lanes;		/* Number of CSI-2 lanes */
-	u8 csi_signalling_mode;		/* SMIAPP_CSI_SIGNALLING_MODE_* */
-	const s64 *op_sys_clock;
+	uint32_t csi_signalling_mode;	/* SMIAPP_CSI_SIGNALLING_MODE_* */
+	uint64_t *op_sys_clock;
 
 	enum smiapp_module_board_orient module_board_orient;
 
 	struct smiapp_flash_strobe_parms *strobe_setup;
 
 	int (*set_xclk)(struct v4l2_subdev *sd, int hz);
-	int xshutdown;			/* gpio or SMIAPP_NO_XSHUTDOWN */
+	int32_t xshutdown;		/* gpio or SMIAPP_NO_XSHUTDOWN */
 };
 
 #endif /* __SMIAPP_H_  */
diff --git a/include/media/tea575x.h b/include/media/tea575x.h
index 2d4fa59..5d09657 100644
--- a/include/media/tea575x.h
+++ b/include/media/tea575x.h
@@ -71,6 +71,11 @@
 	int (*ext_init)(struct snd_tea575x *tea);
 };
 
+int snd_tea575x_enum_freq_bands(struct snd_tea575x *tea,
+					struct v4l2_frequency_band *band);
+int snd_tea575x_g_tuner(struct snd_tea575x *tea, struct v4l2_tuner *v);
+int snd_tea575x_s_hw_freq_seek(struct file *file, struct snd_tea575x *tea,
+				const struct v4l2_hw_freq_seek *a);
 int snd_tea575x_hw_init(struct snd_tea575x *tea);
 int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner);
 void snd_tea575x_exit(struct snd_tea575x *tea);
diff --git a/include/media/v4l2-clk.h b/include/media/v4l2-clk.h
index 0b36cc1..3ef6e3d 100644
--- a/include/media/v4l2-clk.h
+++ b/include/media/v4l2-clk.h
@@ -22,14 +22,15 @@
 struct module;
 struct device;
 
+struct clk;
 struct v4l2_clk {
 	struct list_head list;
 	const struct v4l2_clk_ops *ops;
 	const char *dev_id;
-	const char *id;
 	int enable;
 	struct mutex lock; /* Protect the enable count */
 	atomic_t use_count;
+	struct clk *clk;
 	void *priv;
 };
 
@@ -43,7 +44,7 @@
 
 struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops,
 				   const char *dev_name,
-				   const char *name, void *priv);
+				   void *priv);
 void v4l2_clk_unregister(struct v4l2_clk *clk);
 struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id);
 void v4l2_clk_put(struct v4l2_clk *clk);
@@ -55,14 +56,13 @@
 struct module;
 
 struct v4l2_clk *__v4l2_clk_register_fixed(const char *dev_id,
-		const char *id, unsigned long rate, struct module *owner);
+			unsigned long rate, struct module *owner);
 void v4l2_clk_unregister_fixed(struct v4l2_clk *clk);
 
 static inline struct v4l2_clk *v4l2_clk_register_fixed(const char *dev_id,
-							const char *id,
 							unsigned long rate)
 {
-	return __v4l2_clk_register_fixed(dev_id, id, rate, THIS_MODULE);
+	return __v4l2_clk_register_fixed(dev_id, rate, THIS_MODULE);
 }
 
 #define v4l2_clk_name_i2c(name, size, adap, client) snprintf(name, size, \
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index 5bb2ff8..5356898 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -65,7 +65,6 @@
 	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
 	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
 	unsigned int (*poll) (struct file *, struct poll_table_struct *);
-	long (*ioctl) (struct file *, unsigned int, unsigned long);
 	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
 #ifdef CONFIG_COMPAT
 	long (*compat_ioctl32) (struct file *, unsigned int, unsigned long);
@@ -124,7 +123,8 @@
 	spinlock_t		fh_lock; /* Lock for all v4l2_fhs */
 	struct list_head	fh_list; /* List of struct v4l2_fh */
 
-	int debug;			/* Activates debug level*/
+	/* Internal device debug flags, not for use by drivers */
+	int dev_debug;
 
 	/* Video standard vars */
 	v4l2_std_id tvnorms;		/* Supported tv norms */
diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
index d593ac9..be52eb4 100644
--- a/include/media/v4l2-device.h
+++ b/include/media/v4l2-device.h
@@ -58,8 +58,6 @@
 	struct v4l2_ctrl_handler *ctrl_handler;
 	/* Device's priority state */
 	struct v4l2_prio_state prio;
-	/* BKL replacement mutex. Temporary solution only. */
-	struct mutex ioctl_lock;
 	/* Keep track of the references to this struct. */
 	struct kref ref;
 	/* Release function that is called when the ref count goes to 0. */
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index a553a59..3c4511b 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -23,12 +23,6 @@
 	/* VIDIOC_QUERYCAP handler */
 	int (*vidioc_querycap)(struct file *file, void *fh, struct v4l2_capability *cap);
 
-	/* Priority handling */
-	int (*vidioc_g_priority)   (struct file *file, void *fh,
-				    enum v4l2_priority *p);
-	int (*vidioc_s_priority)   (struct file *file, void *fh,
-				    enum v4l2_priority p);
-
 	/* VIDIOC_ENUM_FMT handlers */
 	int (*vidioc_enum_fmt_vid_cap)     (struct file *file, void *fh,
 					    struct v4l2_fmtdesc *f);
@@ -291,9 +285,18 @@
 
 /* v4l debugging and diagnostics */
 
-/* Debug bitmask flags to be used on V4L2 */
-#define V4L2_DEBUG_IOCTL     0x01
-#define V4L2_DEBUG_IOCTL_ARG 0x02
+/* Device debug flags to be used with the video device debug attribute */
+
+/* Just log the ioctl name + error code */
+#define V4L2_DEV_DEBUG_IOCTL		0x01
+/* Log the ioctl name arguments + error code */
+#define V4L2_DEV_DEBUG_IOCTL_ARG	0x02
+/* Log the file operations open, release, mmap and get_unmapped_area */
+#define V4L2_DEV_DEBUG_FOP		0x04
+/* Log the read and write file operations and the VIDIOC_(D)QBUF ioctls */
+#define V4L2_DEV_DEBUG_STREAMING	0x08
+/* Log poll() */
+#define V4L2_DEV_DEBUG_POLL		0x10
 
 /*  Video standard functions  */
 extern const char *v4l2_norm_to_name(v4l2_std_id id);
diff --git a/include/media/v4l2-of.h b/include/media/v4l2-of.h
index 70fa7b7..f831c9c 100644
--- a/include/media/v4l2-of.h
+++ b/include/media/v4l2-of.h
@@ -29,12 +29,15 @@
  * @data_lanes: an array of physical data lane indexes
  * @clock_lane: physical lane index of the clock lane
  * @num_data_lanes: number of data lanes
+ * @lane_polarities: polarity of the lanes. The order is the same of
+ *		   the physical lanes.
  */
 struct v4l2_of_bus_mipi_csi2 {
 	unsigned int flags;
 	unsigned char data_lanes[4];
 	unsigned char clock_lane;
 	unsigned short num_data_lanes;
+	bool lane_polarities[5];
 };
 
 /**
@@ -66,9 +69,26 @@
 	struct list_head head;
 };
 
+/**
+ * struct v4l2_of_link - a link between two endpoints
+ * @local_node: pointer to device_node of this endpoint
+ * @local_port: identifier of the port this endpoint belongs to
+ * @remote_node: pointer to device_node of the remote endpoint
+ * @remote_port: identifier of the port the remote endpoint belongs to
+ */
+struct v4l2_of_link {
+	struct device_node *local_node;
+	unsigned int local_port;
+	struct device_node *remote_node;
+	unsigned int remote_port;
+};
+
 #ifdef CONFIG_OF
 int v4l2_of_parse_endpoint(const struct device_node *node,
 			   struct v4l2_of_endpoint *endpoint);
+int v4l2_of_parse_link(const struct device_node *node,
+		       struct v4l2_of_link *link);
+void v4l2_of_put_link(struct v4l2_of_link *link);
 #else /* CONFIG_OF */
 
 static inline int v4l2_of_parse_endpoint(const struct device_node *node,
@@ -77,6 +97,16 @@
 	return -ENOSYS;
 }
 
+static inline int v4l2_of_parse_link(const struct device_node *node,
+				     struct v4l2_of_link *link)
+{
+	return -ENOSYS;
+}
+
+static inline void v4l2_of_put_link(struct v4l2_of_link *link)
+{
+}
+
 #endif /* CONFIG_OF */
 
 #endif /* _V4L2_OF_H */
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 063a83e..6ade89c 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -332,8 +332,6 @@
 				struct v4l2_subdev_frame_interval *interval);
 	int (*s_frame_interval)(struct v4l2_subdev *sd,
 				struct v4l2_subdev_frame_interval *interval);
-	int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize);
-	int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival);
 	int (*s_dv_timings)(struct v4l2_subdev *sd,
 			struct v4l2_dv_timings *timings);
 	int (*g_dv_timings)(struct v4l2_subdev *sd,
@@ -342,8 +340,6 @@
 			struct v4l2_dv_timings *timings);
 	int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index,
 			     u32 *code);
-	int (*enum_mbus_fsizes)(struct v4l2_subdev *sd,
-			     struct v4l2_frmsizeenum *fsize);
 	int (*g_mbus_fmt)(struct v4l2_subdev *sd,
 			  struct v4l2_mbus_framefmt *fmt);
 	int (*try_mbus_fmt)(struct v4l2_subdev *sd,
@@ -484,6 +480,18 @@
 				struct v4l2_subdev_ir_parameters *params);
 };
 
+/*
+ * Used for storing subdev pad information. This structure only needs
+ * to be passed to the pad op if the 'which' field of the main argument
+ * is set to V4L2_SUBDEV_FORMAT_TRY. For V4L2_SUBDEV_FORMAT_ACTIVE it is
+ * safe to pass NULL.
+ */
+struct v4l2_subdev_pad_config {
+	struct v4l2_mbus_framefmt try_fmt;
+	struct v4l2_rect try_crop;
+	struct v4l2_rect try_compose;
+};
+
 /**
  * struct v4l2_subdev_pad_ops - v4l2-subdev pad level operations
  * @get_frame_desc: get the current low level media bus frame parameters.
@@ -491,25 +499,26 @@
  *                  may be adjusted by the subdev driver to device capabilities.
  */
 struct v4l2_subdev_pad_ops {
-	int (*enum_mbus_code)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+	int (*enum_mbus_code)(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_pad_config *cfg,
 			      struct v4l2_subdev_mbus_code_enum *code);
 	int (*enum_frame_size)(struct v4l2_subdev *sd,
-			       struct v4l2_subdev_fh *fh,
+			       struct v4l2_subdev_pad_config *cfg,
 			       struct v4l2_subdev_frame_size_enum *fse);
 	int (*enum_frame_interval)(struct v4l2_subdev *sd,
-				   struct v4l2_subdev_fh *fh,
+				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_interval_enum *fie);
-	int (*get_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+	int (*get_fmt)(struct v4l2_subdev *sd,
+		       struct v4l2_subdev_pad_config *cfg,
 		       struct v4l2_subdev_format *format);
-	int (*set_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+	int (*set_fmt)(struct v4l2_subdev *sd,
+		       struct v4l2_subdev_pad_config *cfg,
 		       struct v4l2_subdev_format *format);
-	int (*set_crop)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
-		       struct v4l2_subdev_crop *crop);
-	int (*get_crop)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
-		       struct v4l2_subdev_crop *crop);
-	int (*get_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+	int (*get_selection)(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_pad_config *cfg,
 			     struct v4l2_subdev_selection *sel);
-	int (*set_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+	int (*set_selection)(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_pad_config *cfg,
 			     struct v4l2_subdev_selection *sel);
 	int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
 	int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
@@ -631,11 +640,7 @@
 struct v4l2_subdev_fh {
 	struct v4l2_fh vfh;
 #if defined(CPTCFG_VIDEO_V4L2_SUBDEV_API)
-	struct {
-		struct v4l2_mbus_framefmt try_fmt;
-		struct v4l2_rect try_crop;
-		struct v4l2_rect try_compose;
-	} *pad;
+	struct v4l2_subdev_pad_config *pad;
 #endif
 };
 
@@ -645,17 +650,17 @@
 #if defined(CPTCFG_VIDEO_V4L2_SUBDEV_API)
 #define __V4L2_SUBDEV_MK_GET_TRY(rtype, fun_name, field_name)		\
 	static inline struct rtype *					\
-	v4l2_subdev_get_try_##fun_name(struct v4l2_subdev_fh *fh,	\
-				       unsigned int pad)		\
+	fun_name(struct v4l2_subdev *sd,				\
+		 struct v4l2_subdev_pad_config *cfg,			\
+		 unsigned int pad)					\
 	{								\
-		BUG_ON(pad >= vdev_to_v4l2_subdev(			\
-					fh->vfh.vdev)->entity.num_pads); \
-		return &fh->pad[pad].field_name;			\
+		BUG_ON(pad >= sd->entity.num_pads);			\
+		return &cfg[pad].field_name;				\
 	}
 
-__V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, format, try_fmt)
-__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, crop, try_crop)
-__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, compose, try_compose)
+__V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, v4l2_subdev_get_try_format, try_fmt)
+__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_crop, try_crop)
+__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_compose, try_compose)
 #endif
 
 extern const struct v4l2_file_operations v4l2_subdev_fops;
diff --git a/include/media/videobuf-dma-sg.h b/include/media/videobuf-dma-sg.h
index fb6fd4d..d8b2785 100644
--- a/include/media/videobuf-dma-sg.h
+++ b/include/media/videobuf-dma-sg.h
@@ -84,16 +84,8 @@
  * Despite the name, this is totally unrelated to videobuf, except that
  * videobuf-dma-sg uses the same API internally.
  */
-void videobuf_dma_init(struct videobuf_dmabuf *dma);
-int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
-			   unsigned long data, unsigned long size);
-int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
-			     int nr_pages);
-int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction,
-			      dma_addr_t addr, int nr_pages);
 int videobuf_dma_free(struct videobuf_dmabuf *dma);
 
-int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma);
 int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma);
 struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf);
 
diff --git a/include/media/videobuf-dvb.h b/include/media/videobuf-dvb.h
index d63965a..c3bfa47 100644
--- a/include/media/videobuf-dvb.h
+++ b/include/media/videobuf-dvb.h
@@ -56,9 +56,3 @@
 int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f, struct dvb_frontend *p);
 
 #endif			/* _VIDEOBUF_DVB_H_ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index e320202..c64c398 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -138,17 +138,6 @@
 };
 
 /**
- * enum vb2_fileio_flags - flags for selecting a mode of the file io emulator,
- * by default the 'streaming' style is used by the file io emulator
- * @VB2_FILEIO_READ_ONCE:	report EOF after reading the first buffer
- * @VB2_FILEIO_WRITE_IMMEDIATELY:	queue buffer after each write() call
- */
-enum vb2_fileio_flags {
-	VB2_FILEIO_READ_ONCE		= (1 << 0),
-	VB2_FILEIO_WRITE_IMMEDIATELY	= (1 << 1),
-};
-
-/**
  * enum vb2_buffer_state - current video buffer state
  * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control
  * @VB2_BUF_STATE_PREPARING:	buffer is being prepared in videobuf
@@ -350,7 +339,9 @@
  *
  * @type:	queue type (see V4L2_BUF_TYPE_* in linux/videodev2.h
  * @io_modes:	supported io methods (see vb2_io_modes enum)
- * @io_flags:	additional io flags (see vb2_fileio_flags enum)
+ * @fileio_read_once:		report EOF after reading the first buffer
+ * @fileio_write_immediately:	queue buffer after each write() call
+ * @allow_zero_bytesused:	allow bytesused == 0 to be passed to the driver
  * @lock:	pointer to a mutex that protects the vb2_queue struct. The
  *		driver can set this to a mutex to let the v4l2 core serialize
  *		the queuing ioctls. If the driver wants to handle locking
@@ -400,7 +391,10 @@
 struct vb2_queue {
 	enum v4l2_buf_type		type;
 	unsigned int			io_modes;
-	unsigned int			io_flags;
+	unsigned			fileio_read_once:1;
+	unsigned			fileio_write_immediately:1;
+	unsigned			allow_zero_bytesused:1;
+
 	struct mutex			*lock;
 	struct v4l2_fh			*owner;
 
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 58695ff..f73fcc4 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -245,10 +245,17 @@
 void bt_sock_unregister(int proto);
 void bt_sock_link(struct bt_sock_list *l, struct sock *s);
 void bt_sock_unlink(struct bt_sock_list *l, struct sock *s);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+int  bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+		     int flags);
+int  bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg,
+			    size_t len, int flags);
+#else
 int  bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 				struct msghdr *msg, size_t len, int flags);
 int  bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 			struct msghdr *msg, size_t len, int flags);
+#endif
 uint bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait);
 int  bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
 int  bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
@@ -269,29 +276,34 @@
 	__u16	reqseq;
 	__u16	txseq;
 	__u8	retries;
+	__le16  psm;
+	bdaddr_t bdaddr;
+	struct l2cap_chan *chan;
 };
 
 struct hci_dev;
 
-typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status);
+typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
+typedef void (*hci_req_complete_skb_t)(struct hci_dev *hdev, u8 status,
+				       u16 opcode, struct sk_buff *skb);
 
-struct hci_req_ctrl {
-	bool			start;
-	u8			event;
-	hci_req_complete_t	complete;
+struct req_ctrl {
+	bool start;
+	u8 event;
+	hci_req_complete_t complete;
+	hci_req_complete_skb_t complete_skb;
 };
 
 struct bt_skb_cb {
 	__u8 pkt_type;
-	__u8 incoming;
+	__u8 force_active;
 	__u16 opcode;
 	__u16 expect;
-	__u8 force_active;
-	struct l2cap_chan *chan;
-	struct l2cap_ctrl control;
-	struct hci_req_ctrl req;
-	bdaddr_t bdaddr;
-	__le16 psm;
+	__u8 incoming:1;
+	union {
+		struct l2cap_ctrl l2cap;
+		struct req_ctrl req;
+	};
 };
 #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
 
@@ -339,6 +351,11 @@
 
 int bt_to_errno(__u16 code);
 
+void hci_sock_set_flag(struct sock *sk, int nr);
+void hci_sock_clear_flag(struct sock *sk, int nr);
+int hci_sock_test_flag(struct sock *sk, int nr);
+unsigned short hci_sock_get_channel(struct sock *sk);
+
 int hci_sock_init(void);
 void hci_sock_cleanup(void);
 
@@ -358,6 +375,9 @@
 int sco_init(void);
 void sco_exit(void);
 
+int mgmt_init(void);
+void mgmt_exit(void);
+
 void bt_sock_reclassify_lock(struct sock *sk, int proto);
 
 #endif /* __BLUETOOTH_H */
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 40129b3..d95da83 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -102,6 +102,28 @@
 	 */
 	HCI_QUIRK_FIXUP_BUFFER_SIZE,
 
+	/* When this quirk is set, then a controller that does not
+	 * indicate support for Inquiry Result with RSSI is assumed to
+	 * support it anyway. Some early Bluetooth 1.2 controllers had
+	 * wrongly configured local features that will require forcing
+	 * them to enable this mode. Getting RSSI information with the
+	 * inquiry responses is preferred since it allows for a better
+	 * user expierence.
+	 *
+	 * This quirk must be set before hci_register_dev is called.
+	 */
+	HCI_QUIRK_FIXUP_INQUIRY_MODE,
+
+	/* When this quirk is set, then the HCI Read Local Supported
+	 * Commands command is not supported. In general Bluetooth 1.2
+	 * and later controllers should support this command. However
+	 * some controllers indicate Bluetooth 1.2 support, but do
+	 * not support this command.
+	 *
+	 * This quirk must be set before hci_register_dev is called.
+	 */
+	HCI_QUIRK_BROKEN_LOCAL_COMMANDS,
+
 	/* When this quirk is set, then no stored link key handling
 	 * is performed. This is mainly due to the fact that the
 	 * HCI Delete Stored Link Key command is advertised, but
@@ -138,6 +160,14 @@
 	 * during the hdev->setup vendor callback.
 	 */
 	HCI_QUIRK_STRICT_DUPLICATE_FILTER,
+
+	/* When this quirk is set, LE scan and BR/EDR inquiry is done
+	 * simultaneously, otherwise it's interleaved.
+	 *
+	 * This quirk can be set before hci_register_dev is called or
+	 * during the hdev->setup vendor callback.
+	 */
+	HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
 };
 
 /* HCI device flags */
@@ -157,14 +187,14 @@
 	HCI_RESET,
 };
 
-/* BR/EDR and/or LE controller flags: the flags defined here should represent
- * states configured via debugfs for debugging and testing purposes only.
- */
+/* HCI socket flags */
 enum {
-	HCI_DUT_MODE,
-	HCI_FORCE_SC,
-	HCI_FORCE_LESC,
-	HCI_FORCE_STATIC_ADDR,
+	HCI_SOCK_TRUSTED,
+	HCI_MGMT_INDEX_EVENTS,
+	HCI_MGMT_UNCONF_INDEX_EVENTS,
+	HCI_MGMT_EXT_INDEX_EVENTS,
+	HCI_MGMT_GENERIC_EVENTS,
+	HCI_MGMT_OOB_DATA_EVENTS,
 };
 
 /*
@@ -196,6 +226,8 @@
 	HCI_HS_ENABLED,
 	HCI_LE_ENABLED,
 	HCI_ADVERTISING,
+	HCI_ADVERTISING_CONNECTABLE,
+	HCI_ADVERTISING_INSTANCE,
 	HCI_CONNECTABLE,
 	HCI_DISCOVERABLE,
 	HCI_LIMITED_DISCOVERABLE,
@@ -204,13 +236,13 @@
 	HCI_FAST_CONNECTABLE,
 	HCI_BREDR_ENABLED,
 	HCI_LE_SCAN_INTERRUPTED,
-};
 
-/* A mask for the flags that are supposed to remain when a reset happens
- * or the HCI device is closed.
- */
-#define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ) | \
-			      BIT(HCI_FAST_CONNECTABLE) | BIT(HCI_LE_ADV))
+	HCI_DUT_MODE,
+	HCI_FORCE_BREDR_SMP,
+	HCI_FORCE_STATIC_ADDR,
+
+	__HCI_NUM_FLAGS,
+};
 
 /* HCI timeouts */
 #define HCI_DISCONN_TIMEOUT	msecs_to_jiffies(2000)	/* 2 seconds */
@@ -342,7 +374,9 @@
 /* LE features */
 #define HCI_LE_ENCRYPTION		0x01
 #define HCI_LE_CONN_PARAM_REQ_PROC	0x02
+#define HCI_LE_SLAVE_FEATURES		0x08
 #define HCI_LE_PING			0x10
+#define HCI_LE_DATA_LEN_EXT		0x20
 #define HCI_LE_EXT_SCAN_POLICY		0x80
 
 /* Connection modes */
@@ -430,9 +464,16 @@
 #define EIR_NAME_COMPLETE	0x09 /* complete local name */
 #define EIR_TX_POWER		0x0A /* transmit power level */
 #define EIR_CLASS_OF_DEV	0x0D /* Class of Device */
-#define EIR_SSP_HASH_C		0x0E /* Simple Pairing Hash C */
-#define EIR_SSP_RAND_R		0x0F /* Simple Pairing Randomizer R */
+#define EIR_SSP_HASH_C192	0x0E /* Simple Pairing Hash C-192 */
+#define EIR_SSP_RAND_R192	0x0F /* Simple Pairing Randomizer R-192 */
 #define EIR_DEVICE_ID		0x10 /* device ID */
+#define EIR_APPEARANCE		0x19 /* Device appearance */
+#define EIR_LE_BDADDR		0x1B /* LE Bluetooth device address */
+#define EIR_LE_ROLE		0x1C /* LE role */
+#define EIR_SSP_HASH_C256	0x1D /* Simple Pairing Hash C-256 */
+#define EIR_SSP_RAND_R256	0x1E /* Simple Pairing Rand R-256 */
+#define EIR_LE_SC_CONFIRM	0x22 /* LE SC Confirmation Value */
+#define EIR_LE_SC_RANDOM	0x23 /* LE SC Random Value */
 
 /* Low Energy Advertising Flags */
 #define LE_AD_LIMITED		0x01 /* Limited Discoverable */
@@ -833,11 +874,26 @@
 #define HCI_CONN_SETUP_AUTO_OFF	0x01
 #define HCI_CONN_SETUP_AUTO_ON	0x02
 
+#define HCI_OP_READ_STORED_LINK_KEY	0x0c0d
+struct hci_cp_read_stored_link_key {
+	bdaddr_t bdaddr;
+	__u8     read_all;
+} __packed;
+struct hci_rp_read_stored_link_key {
+	__u8     status;
+	__u8     max_keys;
+	__u8     num_keys;
+} __packed;
+
 #define HCI_OP_DELETE_STORED_LINK_KEY	0x0c12
 struct hci_cp_delete_stored_link_key {
 	bdaddr_t bdaddr;
 	__u8     delete_all;
 } __packed;
+struct hci_rp_delete_stored_link_key {
+	__u8     status;
+	__u8     num_keys;
+} __packed;
 
 #define HCI_MAX_NAME_LENGTH		248
 
@@ -1321,6 +1377,11 @@
 	__le16   max_ce_len;
 } __packed;
 
+#define HCI_OP_LE_READ_REMOTE_FEATURES	0x2016
+struct hci_cp_le_read_remote_features {
+	__le16	 handle;
+} __packed;
+
 #define HCI_OP_LE_START_ENC		0x2019
 struct hci_cp_le_start_enc {
 	__le16	handle;
@@ -1371,6 +1432,39 @@
 	__u8	reason;
 } __packed;
 
+#define HCI_OP_LE_SET_DATA_LEN		0x2022
+struct hci_cp_le_set_data_len {
+	__le16	handle;
+	__le16	tx_len;
+	__le16	tx_time;
+} __packed;
+struct hci_rp_le_set_data_len {
+	__u8	status;
+	__le16	handle;
+} __packed;
+
+#define HCI_OP_LE_READ_DEF_DATA_LEN	0x2023
+struct hci_rp_le_read_def_data_len {
+	__u8	status;
+	__le16	tx_len;
+	__le16	tx_time;
+} __packed;
+
+#define HCI_OP_LE_WRITE_DEF_DATA_LEN	0x2024
+struct hci_cp_le_write_def_data_len {
+	__le16	tx_len;
+	__le16	tx_time;
+} __packed;
+
+#define HCI_OP_LE_READ_MAX_DATA_LEN	0x202f
+struct hci_rp_le_read_max_data_len {
+	__u8	status;
+	__le16	tx_len;
+	__le16	tx_time;
+	__le16	rx_len;
+	__le16	rx_time;
+} __packed;
+
 /* ---- HCI Events ---- */
 #define HCI_EV_INQUIRY_COMPLETE		0x01
 
@@ -1780,6 +1874,13 @@
 	__le16   supervision_timeout;
 } __packed;
 
+#define HCI_EV_LE_REMOTE_FEAT_COMPLETE	0x04
+struct hci_ev_le_remote_feat_complete {
+	__u8     status;
+	__le16   handle;
+	__u8     features[8];
+} __packed;
+
 #define HCI_EV_LE_LTK_REQ		0x05
 struct hci_ev_le_ltk_req {
 	__le16	handle;
@@ -1796,6 +1897,15 @@
 	__le16 timeout;
 } __packed;
 
+#define HCI_EV_LE_DATA_LEN_CHANGE	0x07
+struct hci_ev_le_data_len_change {
+	__le16	handle;
+	__le16	tx_len;
+	__le16	tx_time;
+	__le16	rx_len;
+	__le16	rx_time;
+} __packed;
+
 #define HCI_EV_LE_DIRECT_ADV_REPORT	0x0B
 struct hci_ev_le_direct_adv_info {
 	__u8	 evt_type;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 3c78270..a056c2b 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -76,9 +76,12 @@
 	u8			last_adv_data[HCI_MAX_AD_LENGTH];
 	u8			last_adv_data_len;
 	bool			report_invalid_rssi;
+	bool			result_filtering;
 	s8			rssi;
 	u16			uuid_count;
 	u8			(*uuids)[16];
+	unsigned long		scan_start;
+	unsigned long		scan_duration;
 };
 
 struct hci_conn_hash {
@@ -106,7 +109,7 @@
 struct smp_csrk {
 	bdaddr_t bdaddr;
 	u8 bdaddr_type;
-	u8 master;
+	u8 type;
 	u8 val[16];
 };
 
@@ -145,12 +148,24 @@
 	struct list_head list;
 	bdaddr_t bdaddr;
 	u8 bdaddr_type;
+	u8 present;
 	u8 hash192[16];
 	u8 rand192[16];
 	u8 hash256[16];
 	u8 rand256[16];
 };
 
+struct adv_info {
+	struct delayed_work timeout_exp;
+	__u8	instance;
+	__u32	flags;
+	__u16	timeout;
+	__u16	adv_data_len;
+	__u8	adv_data[HCI_MAX_AD_LENGTH];
+	__u16	scan_rsp_len;
+	__u8	scan_rsp_data[HCI_MAX_AD_LENGTH];
+};
+
 #define HCI_MAX_SHORT_NAME_LENGTH	10
 
 /* Default LE RPA expiry time, 15 minutes */
@@ -170,7 +185,6 @@
 
 #define HCI_MAX_PAGES	3
 
-#define NUM_REASSEMBLY 4
 struct hci_dev {
 	struct list_head list;
 	struct mutex	lock;
@@ -205,6 +219,8 @@
 	__u16		lmp_subver;
 	__u16		voice_setting;
 	__u8		num_iac;
+	__u8		stored_max_keys;
+	__u8		stored_num_keys;
 	__u8		io_capability;
 	__s8		inq_tx_power;
 	__u16		page_scan_interval;
@@ -220,10 +236,17 @@
 	__u16		le_conn_max_interval;
 	__u16		le_conn_latency;
 	__u16		le_supv_timeout;
+	__u16		le_def_tx_len;
+	__u16		le_def_tx_time;
+	__u16		le_max_tx_len;
+	__u16		le_max_tx_time;
+	__u16		le_max_rx_len;
+	__u16		le_max_rx_time;
 	__u16		discov_interleaved_timeout;
 	__u16		conn_info_min_age;
 	__u16		conn_info_max_age;
 	__u8		ssp_debug_mode;
+	__u8		hw_error_code;
 	__u32		clock;
 
 	__u16		devid_source;
@@ -285,6 +308,7 @@
 
 	struct work_struct	power_on;
 	struct delayed_work	power_off;
+	struct work_struct	error_reset;
 
 	__u16			discov_timeout;
 	struct delayed_work	discov_off;
@@ -301,14 +325,13 @@
 	struct sk_buff_head	raw_q;
 	struct sk_buff_head	cmd_q;
 
-	struct sk_buff		*recv_evt;
 	struct sk_buff		*sent_cmd;
-	struct sk_buff		*reassembly[NUM_REASSEMBLY];
 
 	struct mutex		req_lock;
 	wait_queue_head_t	req_wait_q;
 	__u32			req_status;
 	__u32			req_result;
+	struct sk_buff		*req_skb;
 
 	void			*smp_data;
 	void			*smp_bredr_data;
@@ -339,10 +362,10 @@
 
 	struct rfkill		*rfkill;
 
-	unsigned long		dbg_flags;
-	unsigned long		dev_flags;
+	DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS);
 
 	struct delayed_work	le_scan_disable;
+	struct delayed_work	le_scan_restart;
 
 	__s8			adv_tx_power;
 	__u8			adv_data[HCI_MAX_AD_LENGTH];
@@ -350,6 +373,8 @@
 	__u8			scan_rsp_data[HCI_MAX_AD_LENGTH];
 	__u8			scan_rsp_data_len;
 
+	struct adv_info		adv_instance;
+
 	__u8			irk[16];
 	__u32			rpa_timeout;
 	struct delayed_work	rpa_expired;
@@ -359,8 +384,10 @@
 	int (*close)(struct hci_dev *hdev);
 	int (*flush)(struct hci_dev *hdev);
 	int (*setup)(struct hci_dev *hdev);
+	int (*shutdown)(struct hci_dev *hdev);
 	int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
 	void (*notify)(struct hci_dev *hdev, unsigned int evt);
+	void (*hw_error)(struct hci_dev *hdev, u8 code);
 	int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
 };
 
@@ -434,6 +461,7 @@
 	struct delayed_work le_conn_timeout;
 
 	struct device	dev;
+	struct dentry	*debugfs;
 
 	struct hci_dev	*hdev;
 	void		*l2cap_data;
@@ -482,19 +510,29 @@
 extern struct list_head hci_dev_list;
 extern struct list_head hci_cb_list;
 extern rwlock_t hci_dev_list_lock;
-extern rwlock_t hci_cb_list_lock;
+extern struct mutex hci_cb_list_lock;
+
+#define hci_dev_set_flag(hdev, nr)             set_bit((nr), (hdev)->dev_flags)
+#define hci_dev_clear_flag(hdev, nr)           clear_bit((nr), (hdev)->dev_flags)
+#define hci_dev_change_flag(hdev, nr)          change_bit((nr), (hdev)->dev_flags)
+#define hci_dev_test_flag(hdev, nr)            test_bit((nr), (hdev)->dev_flags)
+#define hci_dev_test_and_set_flag(hdev, nr)    test_and_set_bit((nr), (hdev)->dev_flags)
+#define hci_dev_test_and_clear_flag(hdev, nr)  test_and_clear_bit((nr), (hdev)->dev_flags)
+#define hci_dev_test_and_change_flag(hdev, nr) test_and_change_bit((nr), (hdev)->dev_flags)
+
+#define hci_dev_clear_volatile_flags(hdev)			\
+	do {							\
+		hci_dev_clear_flag(hdev, HCI_LE_SCAN);		\
+		hci_dev_clear_flag(hdev, HCI_LE_ADV);		\
+		hci_dev_clear_flag(hdev, HCI_PERIODIC_INQ);	\
+	} while (0)
 
 /* ----- HCI interface to upper protocols ----- */
 int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
-void l2cap_connect_cfm(struct hci_conn *hcon, u8 status);
 int l2cap_disconn_ind(struct hci_conn *hcon);
-void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason);
-int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt);
 int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags);
 
 int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
-void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
-void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
 int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
 
 /* ----- Inquiry cache ----- */
@@ -513,11 +551,19 @@
 
 static inline void hci_discovery_filter_clear(struct hci_dev *hdev)
 {
+	hdev->discovery.result_filtering = false;
 	hdev->discovery.report_invalid_rssi = true;
 	hdev->discovery.rssi = HCI_RSSI_INVALID;
 	hdev->discovery.uuid_count = 0;
 	kfree(hdev->discovery.uuids);
 	hdev->discovery.uuids = NULL;
+	hdev->discovery.scan_start = 0;
+	hdev->discovery.scan_duration = 0;
+}
+
+static inline void adv_info_init(struct hci_dev *hdev)
+{
+	memset(&hdev->adv_instance, 0, sizeof(struct adv_info));
 }
 
 bool hci_discovery_active(struct hci_dev *hdev);
@@ -566,7 +612,6 @@
 	HCI_CONN_SC_ENABLED,
 	HCI_CONN_AES_CCM,
 	HCI_CONN_POWER_SAVE,
-	HCI_CONN_REMOTE_OOB,
 	HCI_CONN_FLUSH_KEY,
 	HCI_CONN_ENCRYPT,
 	HCI_CONN_AUTH,
@@ -582,14 +627,14 @@
 static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
 {
 	struct hci_dev *hdev = conn->hdev;
-	return test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
+	return hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
 	       test_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
 }
 
 static inline bool hci_conn_sc_enabled(struct hci_conn *conn)
 {
 	struct hci_dev *hdev = conn->hdev;
-	return test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
+	return hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
 	       test_bit(HCI_CONN_SC_ENABLED, &conn->flags);
 }
 
@@ -772,7 +817,6 @@
 int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
 int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
 		      bool initiator);
-int hci_conn_change_link_key(struct hci_conn *conn);
 int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
 
 void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active);
@@ -920,8 +964,6 @@
 					       bdaddr_t *addr, u8 addr_type);
 struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
 					    bdaddr_t *addr, u8 addr_type);
-int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
-			u8 auto_connect);
 void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
 void hci_conn_params_clear_all(struct hci_dev *hdev);
 void hci_conn_params_clear_disabled(struct hci_dev *hdev);
@@ -930,8 +972,6 @@
 						  bdaddr_t *addr,
 						  u8 addr_type);
 
-void hci_update_background_scan(struct hci_dev *hdev);
-
 void hci_uuids_clear(struct hci_dev *hdev);
 
 void hci_link_keys_clear(struct hci_dev *hdev);
@@ -956,6 +996,8 @@
 void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type);
 void hci_smp_irks_clear(struct hci_dev *hdev);
 
+bool hci_bdaddr_is_paired(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
+
 void hci_remote_oob_data_clear(struct hci_dev *hdev);
 struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
 					  bdaddr_t *bdaddr, u8 bdaddr_type);
@@ -968,7 +1010,6 @@
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
 
 int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
-int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count);
 
 void hci_init_sysfs(struct hci_dev *hdev);
 void hci_conn_init_sysfs(struct hci_conn *conn);
@@ -1012,11 +1053,10 @@
 #define lmp_host_le_capable(dev)   (!!((dev)->features[1][0] & LMP_HOST_LE))
 #define lmp_host_le_br_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE_BREDR))
 
-#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
-				!test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
-#define bredr_sc_enabled(dev) ((lmp_sc_capable(dev) || \
-				test_bit(HCI_FORCE_SC, &(dev)->dbg_flags)) && \
-			       test_bit(HCI_SC_ENABLED, &(dev)->dev_flags))
+#define hdev_is_powered(dev)   (test_bit(HCI_UP, &(dev)->flags) && \
+				!hci_dev_test_flag(dev, HCI_AUTO_OFF))
+#define bredr_sc_enabled(dev)  (lmp_sc_capable(dev) && \
+				hci_dev_test_flag(dev, HCI_SC_ENABLED))
 
 /* ----- HCI protocols ----- */
 #define HCI_PROTO_DEFER             0x01
@@ -1038,28 +1078,6 @@
 	}
 }
 
-static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
-{
-	switch (conn->type) {
-	case ACL_LINK:
-	case LE_LINK:
-		l2cap_connect_cfm(conn, status);
-		break;
-
-	case SCO_LINK:
-	case ESCO_LINK:
-		sco_connect_cfm(conn, status);
-		break;
-
-	default:
-		BT_ERR("unknown link type %d", conn->type);
-		break;
-	}
-
-	if (conn->connect_cfm_cb)
-		conn->connect_cfm_cb(conn, status);
-}
-
 static inline int hci_proto_disconn_ind(struct hci_conn *conn)
 {
 	if (conn->type != ACL_LINK && conn->type != LE_LINK)
@@ -1068,91 +1086,69 @@
 	return l2cap_disconn_ind(conn);
 }
 
-static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason)
-{
-	switch (conn->type) {
-	case ACL_LINK:
-	case LE_LINK:
-		l2cap_disconn_cfm(conn, reason);
-		break;
-
-	case SCO_LINK:
-	case ESCO_LINK:
-		sco_disconn_cfm(conn, reason);
-		break;
-
-	/* L2CAP would be handled for BREDR chan */
-	case AMP_LINK:
-		break;
-
-	default:
-		BT_ERR("unknown link type %d", conn->type);
-		break;
-	}
-
-	if (conn->disconn_cfm_cb)
-		conn->disconn_cfm_cb(conn, reason);
-}
-
-static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
-{
-	__u8 encrypt;
-
-	if (conn->type != ACL_LINK && conn->type != LE_LINK)
-		return;
-
-	if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
-		return;
-
-	encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
-	l2cap_security_cfm(conn, status, encrypt);
-
-	if (conn->security_cfm_cb)
-		conn->security_cfm_cb(conn, status);
-}
-
-static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status,
-								__u8 encrypt)
-{
-	if (conn->type != ACL_LINK && conn->type != LE_LINK)
-		return;
-
-	l2cap_security_cfm(conn, status, encrypt);
-
-	if (conn->security_cfm_cb)
-		conn->security_cfm_cb(conn, status);
-}
-
 /* ----- HCI callbacks ----- */
 struct hci_cb {
 	struct list_head list;
 
 	char *name;
 
+	void (*connect_cfm)	(struct hci_conn *conn, __u8 status);
+	void (*disconn_cfm)	(struct hci_conn *conn, __u8 status);
 	void (*security_cfm)	(struct hci_conn *conn, __u8 status,
 								__u8 encrypt);
 	void (*key_change_cfm)	(struct hci_conn *conn, __u8 status);
 	void (*role_switch_cfm)	(struct hci_conn *conn, __u8 status, __u8 role);
 };
 
+static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)
+{
+	struct hci_cb *cb;
+
+	mutex_lock(&hci_cb_list_lock);
+	list_for_each_entry(cb, &hci_cb_list, list) {
+		if (cb->connect_cfm)
+			cb->connect_cfm(conn, status);
+	}
+	mutex_unlock(&hci_cb_list_lock);
+
+	if (conn->connect_cfm_cb)
+		conn->connect_cfm_cb(conn, status);
+}
+
+static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason)
+{
+	struct hci_cb *cb;
+
+	mutex_lock(&hci_cb_list_lock);
+	list_for_each_entry(cb, &hci_cb_list, list) {
+		if (cb->disconn_cfm)
+			cb->disconn_cfm(conn, reason);
+	}
+	mutex_unlock(&hci_cb_list_lock);
+
+	if (conn->disconn_cfm_cb)
+		conn->disconn_cfm_cb(conn, reason);
+}
+
 static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
 {
 	struct hci_cb *cb;
 	__u8 encrypt;
 
-	hci_proto_auth_cfm(conn, status);
-
 	if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
 		return;
 
 	encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
 
-	read_lock(&hci_cb_list_lock);
+	mutex_lock(&hci_cb_list_lock);
 	list_for_each_entry(cb, &hci_cb_list, list) {
 		if (cb->security_cfm)
 			cb->security_cfm(conn, status, encrypt);
 	}
-	read_unlock(&hci_cb_list_lock);
+	mutex_unlock(&hci_cb_list_lock);
+
+	if (conn->security_cfm_cb)
+		conn->security_cfm_cb(conn, status);
 }
 
 static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
@@ -1166,26 +1162,27 @@
 	if (conn->pending_sec_level > conn->sec_level)
 		conn->sec_level = conn->pending_sec_level;
 
-	hci_proto_encrypt_cfm(conn, status, encrypt);
-
-	read_lock(&hci_cb_list_lock);
+	mutex_lock(&hci_cb_list_lock);
 	list_for_each_entry(cb, &hci_cb_list, list) {
 		if (cb->security_cfm)
 			cb->security_cfm(conn, status, encrypt);
 	}
-	read_unlock(&hci_cb_list_lock);
+	mutex_unlock(&hci_cb_list_lock);
+
+	if (conn->security_cfm_cb)
+		conn->security_cfm_cb(conn, status);
 }
 
 static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
 {
 	struct hci_cb *cb;
 
-	read_lock(&hci_cb_list_lock);
+	mutex_lock(&hci_cb_list_lock);
 	list_for_each_entry(cb, &hci_cb_list, list) {
 		if (cb->key_change_cfm)
 			cb->key_change_cfm(conn, status);
 	}
-	read_unlock(&hci_cb_list_lock);
+	mutex_unlock(&hci_cb_list_lock);
 }
 
 static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
@@ -1193,12 +1190,12 @@
 {
 	struct hci_cb *cb;
 
-	read_lock(&hci_cb_list_lock);
+	mutex_lock(&hci_cb_list_lock);
 	list_for_each_entry(cb, &hci_cb_list, list) {
 		if (cb->role_switch_cfm)
 			cb->role_switch_cfm(conn, status, role);
 	}
-	read_unlock(&hci_cb_list_lock);
+	mutex_unlock(&hci_cb_list_lock);
 }
 
 static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
@@ -1284,30 +1281,6 @@
 int hci_register_cb(struct hci_cb *hcb);
 int hci_unregister_cb(struct hci_cb *hcb);
 
-struct hci_request {
-	struct hci_dev		*hdev;
-	struct sk_buff_head	cmd_q;
-
-	/* If something goes wrong when building the HCI request, the error
-	 * value is stored in this field.
-	 */
-	int			err;
-};
-
-void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
-int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
-void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
-		 const void *param);
-void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
-		    const void *param, u8 event);
-void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
-bool hci_req_pending(struct hci_dev *hdev);
-
-void hci_req_add_le_scan_disable(struct hci_request *req);
-void hci_req_add_le_passive_scan(struct hci_request *req);
-
-void hci_update_page_scan(struct hci_dev *hdev, struct hci_request *req);
-
 struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
 			       const void *param, u32 timeout);
 struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
@@ -1322,11 +1295,35 @@
 
 /* ----- HCI Sockets ----- */
 void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
-void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk);
+void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
+			 int flag, struct sock *skip_sk);
 void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb);
 
 void hci_sock_dev_event(struct hci_dev *hdev, int event);
 
+#define HCI_MGMT_VAR_LEN	BIT(0)
+#define HCI_MGMT_NO_HDEV	BIT(1)
+#define HCI_MGMT_UNTRUSTED	BIT(2)
+#define HCI_MGMT_UNCONFIGURED	BIT(3)
+
+struct hci_mgmt_handler {
+	int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
+		     u16 data_len);
+	size_t data_len;
+	unsigned long flags;
+};
+
+struct hci_mgmt_chan {
+	struct list_head list;
+	unsigned short channel;
+	size_t handler_count;
+	const struct hci_mgmt_handler *handlers;
+	void (*hdev_init) (struct sock *sk, struct hci_dev *hdev);
+};
+
+int hci_mgmt_chan_register(struct hci_mgmt_chan *c);
+void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c);
+
 /* Management interface */
 #define DISCOV_TYPE_BREDR		(BIT(BDADDR_BREDR))
 #define DISCOV_TYPE_LE			(BIT(BDADDR_LE_PUBLIC) | \
@@ -1344,8 +1341,8 @@
 #define DISCOV_INTERLEAVED_TIMEOUT	5120	/* msec */
 #define DISCOV_INTERLEAVED_INQUIRY_LEN	0x04
 #define DISCOV_BREDR_INQUIRY_LEN	0x08
+#define DISCOV_LE_RESTART_DELAY		msecs_to_jiffies(200)	/* msec */
 
-int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len);
 int mgmt_new_settings(struct hci_dev *hdev);
 void mgmt_index_added(struct hci_dev *hdev);
 void mgmt_index_removed(struct hci_dev *hdev);
@@ -1388,13 +1385,9 @@
 void mgmt_auth_failed(struct hci_conn *conn, u8 status);
 void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
 void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
-void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
 void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
 				    u8 status);
 void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
-void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
-				       u8 *rand192, u8 *hash256, u8 *rand256,
-				       u8 status);
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 		       u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
 		       u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
@@ -1417,8 +1410,6 @@
 void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
 							__u8 ltk[16]);
 
-int hci_update_random_address(struct hci_request *req, bool require_privacy,
-			      u8 *own_addr_type);
 void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
 			       u8 *bdaddr_type);
 
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index eee3ef5..ce1212f 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -248,6 +248,7 @@
 #define L2CAP_PSM_SDP		0x0001
 #define L2CAP_PSM_RFCOMM	0x0003
 #define L2CAP_PSM_3DSP		0x0021
+#define L2CAP_PSM_IPSP		0x0023 /* 6LoWPAN */
 
 /* channel identifier */
 #define L2CAP_CID_SIGNALING	0x0001
@@ -608,10 +609,12 @@
 	struct sk_buff		*(*alloc_skb) (struct l2cap_chan *chan,
 					       unsigned long hdr_len,
 					       unsigned long len, int nb);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
 	int			(*memcpy_fromiovec) (struct l2cap_chan *chan,
 						     unsigned char *kdata,
 						     struct iovec *iov,
 						     int len);
+#endif
 };
 
 struct l2cap_conn {
@@ -905,6 +908,7 @@
 	return 0;
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
 static inline int l2cap_chan_no_memcpy_fromiovec(struct l2cap_chan *chan,
 						 unsigned char *kdata,
 						 struct iovec *iov,
@@ -929,6 +933,7 @@
 
 	return 0;
 }
+#endif
 
 extern bool disable_ertm;
 
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 95c34d5..b831242 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -43,6 +43,8 @@
 #define MGMT_STATUS_CANCELLED		0x10
 #define MGMT_STATUS_INVALID_INDEX	0x11
 #define MGMT_STATUS_RFKILLED		0x12
+#define MGMT_STATUS_ALREADY_PAIRED	0x13
+#define MGMT_STATUS_PERMISSION_DENIED	0x14
 
 struct mgmt_hdr {
 	__le16	opcode;
@@ -98,6 +100,7 @@
 #define MGMT_SETTING_DEBUG_KEYS		0x00001000
 #define MGMT_SETTING_PRIVACY		0x00002000
 #define MGMT_SETTING_CONFIGURATION	0x00004000
+#define MGMT_SETTING_STATIC_ADDRESS	0x00008000
 
 #define MGMT_OP_READ_INFO		0x0004
 #define MGMT_READ_INFO_SIZE		0
@@ -301,10 +304,6 @@
 #define MGMT_OP_READ_LOCAL_OOB_DATA	0x0020
 #define MGMT_READ_LOCAL_OOB_DATA_SIZE	0
 struct mgmt_rp_read_local_oob_data {
-	__u8	hash[16];
-	__u8	rand[16];
-} __packed;
-struct mgmt_rp_read_local_oob_ext_data {
 	__u8	hash192[16];
 	__u8	rand192[16];
 	__u8	hash256[16];
@@ -507,6 +506,71 @@
 } __packed;
 #define MGMT_START_SERVICE_DISCOVERY_SIZE 4
 
+#define MGMT_OP_READ_LOCAL_OOB_EXT_DATA	0x003B
+struct mgmt_cp_read_local_oob_ext_data {
+	__u8 type;
+} __packed;
+#define MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE 1
+struct mgmt_rp_read_local_oob_ext_data {
+	__u8    type;
+	__le16	eir_len;
+	__u8	eir[0];
+} __packed;
+
+#define MGMT_OP_READ_EXT_INDEX_LIST	0x003C
+#define MGMT_READ_EXT_INDEX_LIST_SIZE	0
+struct mgmt_rp_read_ext_index_list {
+	__le16	num_controllers;
+	struct {
+		__le16 index;
+		__u8   type;
+		__u8   bus;
+	} entry[0];
+} __packed;
+
+#define MGMT_OP_READ_ADV_FEATURES	0x0003D
+#define MGMT_READ_ADV_FEATURES_SIZE	0
+struct mgmt_rp_read_adv_features {
+	__le32 supported_flags;
+	__u8   max_adv_data_len;
+	__u8   max_scan_rsp_len;
+	__u8   max_instances;
+	__u8   num_instances;
+	__u8   instance[0];
+} __packed;
+
+#define MGMT_OP_ADD_ADVERTISING		0x003E
+struct mgmt_cp_add_advertising {
+	__u8	instance;
+	__le32	flags;
+	__le16	duration;
+	__le16	timeout;
+	__u8	adv_data_len;
+	__u8	scan_rsp_len;
+	__u8	data[0];
+} __packed;
+#define MGMT_ADD_ADVERTISING_SIZE	11
+struct mgmt_rp_add_advertising {
+	__u8	instance;
+} __packed;
+
+#define MGMT_ADV_FLAG_CONNECTABLE	BIT(0)
+#define MGMT_ADV_FLAG_DISCOV		BIT(1)
+#define MGMT_ADV_FLAG_LIMITED_DISCOV	BIT(2)
+#define MGMT_ADV_FLAG_MANAGED_FLAGS	BIT(3)
+#define MGMT_ADV_FLAG_TX_POWER		BIT(4)
+#define MGMT_ADV_FLAG_APPEARANCE	BIT(5)
+#define MGMT_ADV_FLAG_LOCAL_NAME	BIT(6)
+
+#define MGMT_OP_REMOVE_ADVERTISING	0x003F
+struct mgmt_cp_remove_advertising {
+	__u8	instance;
+} __packed;
+#define MGMT_REMOVE_ADVERTISING_SIZE	1
+struct mgmt_rp_remove_advertising {
+	__u8	instance;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	__le16	opcode;
@@ -651,9 +715,14 @@
 	struct mgmt_irk_info irk;
 } __packed;
 
+#define MGMT_CSRK_LOCAL_UNAUTHENTICATED		0x00
+#define MGMT_CSRK_REMOTE_UNAUTHENTICATED	0x01
+#define MGMT_CSRK_LOCAL_AUTHENTICATED		0x02
+#define MGMT_CSRK_REMOTE_AUTHENTICATED		0x03
+
 struct mgmt_csrk_info {
 	struct mgmt_addr_info addr;
-	__u8 master;
+	__u8 type;
 	__u8 val[16];
 } __packed;
 
@@ -689,3 +758,29 @@
 #define MGMT_EV_UNCONF_INDEX_REMOVED	0x001e
 
 #define MGMT_EV_NEW_CONFIG_OPTIONS	0x001f
+
+struct mgmt_ev_ext_index {
+	__u8 type;
+	__u8 bus;
+} __packed;
+
+#define MGMT_EV_EXT_INDEX_ADDED		0x0020
+
+#define MGMT_EV_EXT_INDEX_REMOVED	0x0021
+
+#define MGMT_EV_LOCAL_OOB_DATA_UPDATED	0x0022
+struct mgmt_ev_local_oob_data_updated {
+	__u8    type;
+	__le16	eir_len;
+	__u8	eir[0];
+} __packed;
+
+#define MGMT_EV_ADVERTISING_ADDED	0x0023
+struct mgmt_ev_advertising_added {
+	__u8    instance;
+} __packed;
+
+#define MGMT_EV_ADVERTISING_REMOVED	0x0024
+struct mgmt_ev_advertising_removed {
+	__u8    instance;
+} __packed;
diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h
index 75db740..2c5c938 100644
--- a/include/net/bluetooth/rfcomm.h
+++ b/include/net/bluetooth/rfcomm.h
@@ -24,8 +24,6 @@
 #ifndef __RFCOMM_H
 #define __RFCOMM_H
 
-#define RFCOMM_PSM 3
-
 #define RFCOMM_CONN_TIMEOUT (HZ * 30)
 #define RFCOMM_DISC_TIMEOUT (HZ * 20)
 #define RFCOMM_AUTH_TIMEOUT (HZ * 25)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 66fc97a..9c56b05 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -215,6 +215,39 @@
 };
 
 /**
+ * enum ieee80211_bss_type - BSS type filter
+ *
+ * @IEEE80211_BSS_TYPE_ESS: Infrastructure BSS
+ * @IEEE80211_BSS_TYPE_PBSS: Personal BSS
+ * @IEEE80211_BSS_TYPE_IBSS: Independent BSS
+ * @IEEE80211_BSS_TYPE_MBSS: Mesh BSS
+ * @IEEE80211_BSS_TYPE_ANY: Wildcard value for matching any BSS type
+ */
+enum ieee80211_bss_type {
+	IEEE80211_BSS_TYPE_ESS,
+	IEEE80211_BSS_TYPE_PBSS,
+	IEEE80211_BSS_TYPE_IBSS,
+	IEEE80211_BSS_TYPE_MBSS,
+	IEEE80211_BSS_TYPE_ANY
+};
+
+/**
+ * enum ieee80211_privacy - BSS privacy filter
+ *
+ * @IEEE80211_PRIVACY_ON: privacy bit set
+ * @IEEE80211_PRIVACY_OFF: privacy bit clear
+ * @IEEE80211_PRIVACY_ANY: Wildcard value for matching any privacy setting
+ */
+enum ieee80211_privacy {
+	IEEE80211_PRIVACY_ON,
+	IEEE80211_PRIVACY_OFF,
+	IEEE80211_PRIVACY_ANY
+};
+
+#define IEEE80211_PRIVACY(x)	\
+	((x) ? IEEE80211_PRIVACY_ON : IEEE80211_PRIVACY_OFF)
+
+/**
  * struct ieee80211_rate - bitrate definition
  *
  * This structure describes a bitrate that an 802.11 PHY can
@@ -520,37 +553,41 @@
  *
  * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in
  * @SURVEY_INFO_IN_USE: channel is currently being used
- * @SURVEY_INFO_CHANNEL_TIME: channel active time (in ms) was filled in
- * @SURVEY_INFO_CHANNEL_TIME_BUSY: channel busy time was filled in
- * @SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: extension channel busy time was filled in
- * @SURVEY_INFO_CHANNEL_TIME_RX: channel receive time was filled in
- * @SURVEY_INFO_CHANNEL_TIME_TX: channel transmit time was filled in
+ * @SURVEY_INFO_TIME: active time (in ms) was filled in
+ * @SURVEY_INFO_TIME_BUSY: busy time was filled in
+ * @SURVEY_INFO_TIME_EXT_BUSY: extension channel busy time was filled in
+ * @SURVEY_INFO_TIME_RX: receive time was filled in
+ * @SURVEY_INFO_TIME_TX: transmit time was filled in
+ * @SURVEY_INFO_TIME_SCAN: scan time was filled in
  *
  * Used by the driver to indicate which info in &struct survey_info
  * it has filled in during the get_survey().
  */
 enum survey_info_flags {
-	SURVEY_INFO_NOISE_DBM = 1<<0,
-	SURVEY_INFO_IN_USE = 1<<1,
-	SURVEY_INFO_CHANNEL_TIME = 1<<2,
-	SURVEY_INFO_CHANNEL_TIME_BUSY = 1<<3,
-	SURVEY_INFO_CHANNEL_TIME_EXT_BUSY = 1<<4,
-	SURVEY_INFO_CHANNEL_TIME_RX = 1<<5,
-	SURVEY_INFO_CHANNEL_TIME_TX = 1<<6,
+	SURVEY_INFO_NOISE_DBM		= BIT(0),
+	SURVEY_INFO_IN_USE		= BIT(1),
+	SURVEY_INFO_TIME		= BIT(2),
+	SURVEY_INFO_TIME_BUSY		= BIT(3),
+	SURVEY_INFO_TIME_EXT_BUSY	= BIT(4),
+	SURVEY_INFO_TIME_RX		= BIT(5),
+	SURVEY_INFO_TIME_TX		= BIT(6),
+	SURVEY_INFO_TIME_SCAN		= BIT(7),
 };
 
 /**
  * struct survey_info - channel survey response
  *
- * @channel: the channel this survey record reports, mandatory
+ * @channel: the channel this survey record reports, may be %NULL for a single
+ *	record to report global statistics
  * @filled: bitflag of flags from &enum survey_info_flags
  * @noise: channel noise in dBm. This and all following fields are
  *	optional
- * @channel_time: amount of time in ms the radio spent on the channel
- * @channel_time_busy: amount of time the primary channel was sensed busy
- * @channel_time_ext_busy: amount of time the extension channel was sensed busy
- * @channel_time_rx: amount of time the radio spent receiving data
- * @channel_time_tx: amount of time the radio spent transmitting data
+ * @time: amount of time in ms the radio was turn on (on the channel)
+ * @time_busy: amount of time the primary channel was sensed busy
+ * @time_ext_busy: amount of time the extension channel was sensed busy
+ * @time_rx: amount of time the radio spent receiving data
+ * @time_tx: amount of time the radio spent transmitting data
+ * @time_scan: amount of time the radio spent for scanning
  *
  * Used by dump_survey() to report back per-channel survey information.
  *
@@ -559,11 +596,12 @@
  */
 struct survey_info {
 	struct ieee80211_channel *channel;
-	u64 channel_time;
-	u64 channel_time_busy;
-	u64 channel_time_ext_busy;
-	u64 channel_time_rx;
-	u64 channel_time_tx;
+	u64 time;
+	u64 time_busy;
+	u64 time_ext_busy;
+	u64 time_rx;
+	u64 time_tx;
+	u64 time_scan;
 	u32 filled;
 	s8 noise;
 };
@@ -861,75 +899,6 @@
 				  enum cfg80211_station_type statype);
 
 /**
- * enum station_info_flags - station information flags
- *
- * Used by the driver to indicate which info in &struct station_info
- * it has filled in during get_station() or dump_station().
- *
- * @STATION_INFO_INACTIVE_TIME: @inactive_time filled
- * @STATION_INFO_RX_BYTES: @rx_bytes filled
- * @STATION_INFO_TX_BYTES: @tx_bytes filled
- * @STATION_INFO_RX_BYTES64: @rx_bytes filled with 64-bit value
- * @STATION_INFO_TX_BYTES64: @tx_bytes filled with 64-bit value
- * @STATION_INFO_LLID: @llid filled
- * @STATION_INFO_PLID: @plid filled
- * @STATION_INFO_PLINK_STATE: @plink_state filled
- * @STATION_INFO_SIGNAL: @signal filled
- * @STATION_INFO_TX_BITRATE: @txrate fields are filled
- *	(tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
- * @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value
- * @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value
- * @STATION_INFO_TX_RETRIES: @tx_retries filled
- * @STATION_INFO_TX_FAILED: @tx_failed filled
- * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled
- * @STATION_INFO_SIGNAL_AVG: @signal_avg filled
- * @STATION_INFO_RX_BITRATE: @rxrate fields are filled
- * @STATION_INFO_BSS_PARAM: @bss_param filled
- * @STATION_INFO_CONNECTED_TIME: @connected_time filled
- * @STATION_INFO_ASSOC_REQ_IES: @assoc_req_ies filled
- * @STATION_INFO_STA_FLAGS: @sta_flags filled
- * @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled
- * @STATION_INFO_T_OFFSET: @t_offset filled
- * @STATION_INFO_LOCAL_PM: @local_pm filled
- * @STATION_INFO_PEER_PM: @peer_pm filled
- * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled
- * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled
- * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled
- * @STATION_INFO_EXPECTED_THROUGHPUT: @expected_throughput filled
- */
-enum station_info_flags {
-	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),
-};
-
-/**
  * enum station_info_rate_flags - bitrate info flags
  *
  * Used by the driver to indicate the specific rate transmission
@@ -937,22 +906,35 @@
  *
  * @RATE_INFO_FLAGS_MCS: mcs field filled with HT MCS
  * @RATE_INFO_FLAGS_VHT_MCS: mcs field filled with VHT MCS
- * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 MHz width transmission
- * @RATE_INFO_FLAGS_80_MHZ_WIDTH: 80 MHz width transmission
- * @RATE_INFO_FLAGS_80P80_MHZ_WIDTH: 80+80 MHz width transmission
- * @RATE_INFO_FLAGS_160_MHZ_WIDTH: 160 MHz width transmission
  * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval
  * @RATE_INFO_FLAGS_60G: 60GHz MCS
  */
 enum rate_info_flags {
 	RATE_INFO_FLAGS_MCS			= BIT(0),
 	RATE_INFO_FLAGS_VHT_MCS			= BIT(1),
-	RATE_INFO_FLAGS_40_MHZ_WIDTH		= BIT(2),
-	RATE_INFO_FLAGS_80_MHZ_WIDTH		= BIT(3),
-	RATE_INFO_FLAGS_80P80_MHZ_WIDTH		= BIT(4),
-	RATE_INFO_FLAGS_160_MHZ_WIDTH		= BIT(5),
-	RATE_INFO_FLAGS_SHORT_GI		= BIT(6),
-	RATE_INFO_FLAGS_60G			= BIT(7),
+	RATE_INFO_FLAGS_SHORT_GI		= BIT(2),
+	RATE_INFO_FLAGS_60G			= BIT(3),
+};
+
+/**
+ * enum rate_info_bw - rate bandwidth information
+ *
+ * Used by the driver to indicate the rate bandwidth.
+ *
+ * @RATE_INFO_BW_5: 5 MHz bandwidth
+ * @RATE_INFO_BW_10: 10 MHz bandwidth
+ * @RATE_INFO_BW_20: 20 MHz bandwidth
+ * @RATE_INFO_BW_40: 40 MHz bandwidth
+ * @RATE_INFO_BW_80: 80 MHz bandwidth
+ * @RATE_INFO_BW_160: 160 MHz bandwidth
+ */
+enum rate_info_bw {
+	RATE_INFO_BW_5,
+	RATE_INFO_BW_10,
+	RATE_INFO_BW_20,
+	RATE_INFO_BW_40,
+	RATE_INFO_BW_80,
+	RATE_INFO_BW_160,
 };
 
 /**
@@ -964,12 +946,14 @@
  * @mcs: mcs index if struct describes a 802.11n bitrate
  * @legacy: bitrate in 100kbit/s for 802.11abg
  * @nss: number of streams (VHT only)
+ * @bw: bandwidth (from &enum rate_info_bw)
  */
 struct rate_info {
 	u8 flags;
 	u8 mcs;
 	u16 legacy;
 	u8 nss;
+	u8 bw;
 };
 
 /**
@@ -1003,6 +987,24 @@
 	u16 beacon_interval;
 };
 
+/**
+ * struct cfg80211_tid_stats - per-TID statistics
+ * @filled: bitmap of flags using the bits of &enum nl80211_tid_stats to
+ *	indicate the relevant values in this struct are filled
+ * @rx_msdu: number of received MSDUs
+ * @tx_msdu: number of (attempted) transmitted MSDUs
+ * @tx_msdu_retries: number of retries (not counting the first) for
+ *	transmitted MSDUs
+ * @tx_msdu_failed: number of failed transmitted MSDUs
+ */
+struct cfg80211_tid_stats {
+	u32 filled;
+	u64 rx_msdu;
+	u64 tx_msdu;
+	u64 tx_msdu_retries;
+	u64 tx_msdu_failed;
+};
+
 #define IEEE80211_MAX_CHAINS	4
 
 /**
@@ -1010,11 +1012,12 @@
  *
  * Station information filled by driver for get_station() and dump_station.
  *
- * @filled: bitflag of flags from &enum station_info_flags
+ * @filled: bitflag of flags using the bits of &enum nl80211_sta_info to
+ *	indicate the relevant values in this struct for them
  * @connected_time: time(in secs) since a station is last connected
  * @inactive_time: time since last station activity (tx/rx) in milliseconds
- * @rx_bytes: bytes received from this station
- * @tx_bytes: bytes transmitted to this station
+ * @rx_bytes: bytes (size of MPDUs) received from this station
+ * @tx_bytes: bytes (size of MPDUs) transmitted to this station
  * @llid: mesh local link id
  * @plid: mesh peer link id
  * @plink_state: mesh peer link state
@@ -1027,10 +1030,10 @@
  * @chain_signal_avg: per-chain signal strength average in dBm
  * @txrate: current unicast bitrate from this station
  * @rxrate: current unicast bitrate to this station
- * @rx_packets: packets received from this station
- * @tx_packets: packets transmitted to this station
- * @tx_retries: cumulative retry counts
- * @tx_failed: number of failed transmissions (retries exceeded, no ACK)
+ * @rx_packets: packets (MSDUs & MMPDUs) received from this station
+ * @tx_packets: packets (MSDUs & MMPDUs) transmitted to this station
+ * @tx_retries: cumulative retry counts (MPDUs)
+ * @tx_failed: number of failed transmissions (MPDUs) (retries exceeded, no ACK)
  * @rx_dropped_misc:  Dropped for un-specified reason.
  * @bss_param: current BSS parameters
  * @generation: generation number for nl80211 dumps.
@@ -1050,6 +1053,11 @@
  * @nonpeer_pm: non-peer mesh STA power save mode
  * @expected_throughput: expected throughput in kbps (including 802.11 headers)
  *	towards this station.
+ * @rx_beacon: number of beacons received from this peer
+ * @rx_beacon_signal_avg: signal strength average (in dBm) for beacons received
+ *	from this peer
+ * @pertid: per-TID statistics, see &struct cfg80211_tid_stats, using the last
+ *	(IEEE80211_NUM_TIDS) index for MSDUs not encapsulated in QoS-MPDUs.
  */
 struct station_info {
 	u32 filled;
@@ -1090,10 +1098,9 @@
 
 	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.
-	 */
+	u64 rx_beacon;
+	u8 rx_beacon_signal_avg;
+	struct cfg80211_tid_stats pertid[IEEE80211_NUM_TIDS + 1];
 };
 
 /**
@@ -1516,6 +1523,13 @@
  * @mac_addr_mask: MAC address mask used with randomisation, bits that
  *	are 0 in the mask should be randomised, bits that are 1 should
  *	be taken from the @mac_addr
+ * @rcu_head: RCU callback used to free the struct
+ * @owner_nlportid: netlink portid of owner (if this should is a request
+ *	owned by a particular socket)
+ * @delay: delay in seconds to use before starting the first scan
+ *	cycle.  The driver may ignore this parameter and start
+ *	immediately (or at any other time), if this feature is not
+ *	supported.
  */
 struct cfg80211_sched_scan_request {
 	struct cfg80211_ssid *ssids;
@@ -1529,6 +1543,7 @@
 	struct cfg80211_match_set *match_sets;
 	int n_match_sets;
 	s32 min_rssi_thold;
+	u32 delay;
 
 	u8 mac_addr[ETH_ALEN] __aligned(2);
 	u8 mac_addr_mask[ETH_ALEN] __aligned(2);
@@ -1537,6 +1552,8 @@
 	struct wiphy *wiphy;
 	struct net_device *dev;
 	unsigned long scan_start;
+	struct rcu_head rcu_head;
+	u32 owner_nlportid;
 
 	/* keep last */
 	struct ieee80211_channel *channels[0];
@@ -2439,6 +2456,7 @@
 
 	struct wireless_dev * (*add_virtual_intf)(struct wiphy *wiphy,
 						  const char *name,
+						  unsigned char name_assign_type,
 						  enum nl80211_iftype type,
 						  u32 *flags,
 						  struct vif_params *params);
@@ -3011,6 +3029,8 @@
  * @regulatory_flags: wiphy regulatory flags, see
  *	&enum ieee80211_regulatory_flags
  * @features: features advertised to nl80211, see &enum nl80211_feature_flags.
+ * @ext_features: extended features advertised to nl80211, see
+ *	&enum nl80211_ext_feature_index.
  * @bss_priv_size: each BSS struct has private data allocated with it,
  *	this variable determines its size
  * @max_scan_ssids: maximum number of SSIDs the device can scan for in
@@ -3123,6 +3143,7 @@
 	u16 max_acl_mac_addrs;
 
 	u32 flags, regulatory_flags, features;
+	u8 ext_features[DIV_ROUND_UP(NUM_NL80211_EXT_FEATURES, 8)];
 
 	u32 ap_sme_capa;
 
@@ -3199,10 +3220,8 @@
 	const struct ieee80211_ht_cap *ht_capa_mod_mask;
 	const struct ieee80211_vht_cap *vht_capa_mod_mask;
 
-#ifdef CONFIG_NET_NS
 	/* the network namespace this phy lives in currently */
-	struct net *_net;
-#endif
+	possible_net_t _net;
 
 #ifdef CPTCFG_CFG80211_WEXT
 	const struct iw_handler_def *wext;
@@ -3224,12 +3243,12 @@
 
 static inline struct net *wiphy_net(struct wiphy *wiphy)
 {
-	return read_pnet(&wiphy->_net);
+	return possible_read_pnet(&wiphy->_net);
 }
 
 static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net)
 {
-	write_pnet(&wiphy->_net, net);
+	possible_write_pnet(&wiphy->_net, net);
 }
 
 /**
@@ -3811,6 +3830,34 @@
 int regulatory_hint(struct wiphy *wiphy, const char *alpha2);
 
 /**
+ * regulatory_set_wiphy_regd - set regdom info for self managed drivers
+ * @wiphy: the wireless device we want to process the regulatory domain on
+ * @rd: the regulatory domain informatoin to use for this wiphy
+ *
+ * Set the regulatory domain information for self-managed wiphys, only they
+ * may use this function. See %REGULATORY_WIPHY_SELF_MANAGED for more
+ * information.
+ *
+ * Return: 0 on success. -EINVAL, -EPERM
+ */
+int regulatory_set_wiphy_regd(struct wiphy *wiphy,
+			      struct ieee80211_regdomain *rd);
+
+/**
+ * regulatory_set_wiphy_regd_sync_rtnl - set regdom for self-managed drivers
+ * @wiphy: the wireless device we want to process the regulatory domain on
+ * @rd: the regulatory domain information to use for this wiphy
+ *
+ * This functions requires the RTNL to be held and applies the new regdomain
+ * synchronously to this wiphy. For more details see
+ * regulatory_set_wiphy_regd().
+ *
+ * Return: 0 on success. -EINVAL, -EPERM
+ */
+int regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy,
+					struct ieee80211_regdomain *rd);
+
+/**
  * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain
  * @wiphy: the wireless device we want to process the regulatory domain on
  * @regd: the custom regulatory domain to use for this wiphy
@@ -4000,14 +4047,16 @@
 				      struct ieee80211_channel *channel,
 				      const u8 *bssid,
 				      const u8 *ssid, size_t ssid_len,
-				      u16 capa_mask, u16 capa_val);
+				      enum ieee80211_bss_type bss_type,
+				      enum ieee80211_privacy);
 static inline struct cfg80211_bss *
 cfg80211_get_ibss(struct wiphy *wiphy,
 		  struct ieee80211_channel *channel,
 		  const u8 *ssid, size_t ssid_len)
 {
 	return cfg80211_get_bss(wiphy, channel, NULL, ssid, ssid_len,
-				WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
+				IEEE80211_BSS_TYPE_IBSS,
+				IEEE80211_PRIVACY_ANY);
 }
 
 /**
@@ -4248,6 +4297,7 @@
 					   int approxlen);
 
 struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
+					   struct wireless_dev *wdev,
 					   enum nl80211_commands cmd,
 					   enum nl80211_attrs attr,
 					   int vendor_event_idx,
@@ -4302,6 +4352,7 @@
 /**
  * cfg80211_vendor_event_alloc - allocate vendor-specific event skb
  * @wiphy: the wiphy
+ * @wdev: the wireless device
  * @event_idx: index of the vendor event in the wiphy's vendor_events
  * @approxlen: an upper bound of the length of the data that will
  *	be put into the skb
@@ -4310,16 +4361,20 @@
  * This function allocates and pre-fills an skb for an event on the
  * vendor-specific multicast group.
  *
+ * If wdev != NULL, both the ifindex and identifier of the specified
+ * wireless device are added to the event message before the vendor data
+ * attribute.
+ *
  * When done filling the skb, call cfg80211_vendor_event() with the
  * skb to send the event.
  *
  * Return: An allocated and pre-filled skb. %NULL if any errors happen.
  */
 static inline struct sk_buff *
-cfg80211_vendor_event_alloc(struct wiphy *wiphy, int approxlen,
-			    int event_idx, gfp_t gfp)
+cfg80211_vendor_event_alloc(struct wiphy *wiphy, struct wireless_dev *wdev,
+			     int approxlen, int event_idx, gfp_t gfp)
 {
-	return __cfg80211_alloc_event_skb(wiphy, NL80211_CMD_VENDOR,
+	return __cfg80211_alloc_event_skb(wiphy, wdev, NL80211_CMD_VENDOR,
 					  NL80211_ATTR_VENDOR_DATA,
 					  event_idx, approxlen, gfp);
 }
@@ -4420,7 +4475,7 @@
 static inline struct sk_buff *
 cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp)
 {
-	return __cfg80211_alloc_event_skb(wiphy, NL80211_CMD_TESTMODE,
+	return __cfg80211_alloc_event_skb(wiphy, NULL, NL80211_CMD_TESTMODE,
 					  NL80211_ATTR_TESTDATA, -1,
 					  approxlen, gfp);
 }
@@ -4568,13 +4623,27 @@
 		      struct station_info *sinfo, gfp_t gfp);
 
 /**
+ * cfg80211_del_sta_sinfo - notify userspace about deletion of a station
+ * @dev: the netdev
+ * @mac_addr: the station's address
+ * @sinfo: the station information/statistics
+ * @gfp: allocation flags
+ */
+void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr,
+			    struct station_info *sinfo, gfp_t gfp);
+
+/**
  * cfg80211_del_sta - notify userspace about deletion of a station
  *
  * @dev: the netdev
  * @mac_addr: the station's address
  * @gfp: allocation flags
  */
-void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp);
+static inline void cfg80211_del_sta(struct net_device *dev,
+				    const u8 *mac_addr, gfp_t gfp)
+{
+	cfg80211_del_sta_sinfo(dev, mac_addr, NULL, gfp);
+}
 
 /**
  * cfg80211_conn_failed - connection request failed notification
@@ -4836,6 +4905,17 @@
 bool ieee80211_operating_class_to_band(u8 operating_class,
 				       enum ieee80211_band *band);
 
+/**
+ * ieee80211_chandef_to_operating_class - convert chandef to operation class
+ *
+ * @chandef: the chandef to convert
+ * @op_class: a pointer to the resulting operating class
+ *
+ * Returns %true if the conversion was successful, %false otherwise.
+ */
+bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
+					  u8 *op_class);
+
 /*
  * cfg80211_tdls_oper_request - request userspace to perform TDLS operation
  * @dev: the device on which the operation is requested
@@ -4924,6 +5004,64 @@
 			  u8 *buf, unsigned int bufsize);
 
 /**
+ * ieee80211_ie_split_ric - split an IE buffer according to ordering (with RIC)
+ * @ies: the IE buffer
+ * @ielen: the length of the IE buffer
+ * @ids: an array with element IDs that are allowed before
+ *	the split
+ * @n_ids: the size of the element ID array
+ * @after_ric: array IE types that come after the RIC element
+ * @n_after_ric: size of the @after_ric array
+ * @offset: offset where to start splitting in the buffer
+ *
+ * This function splits an IE buffer by updating the @offset
+ * variable to point to the location where the buffer should be
+ * split.
+ *
+ * It assumes that the given IE buffer is well-formed, this
+ * has to be guaranteed by the caller!
+ *
+ * It also assumes that the IEs in the buffer are ordered
+ * correctly, if not the result of using this function will not
+ * be ordered correctly either, i.e. it does no reordering.
+ *
+ * The function returns the offset where the next part of the
+ * buffer starts, which may be @ielen if the entire (remainder)
+ * of the buffer should be used.
+ */
+size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
+			      const u8 *ids, int n_ids,
+			      const u8 *after_ric, int n_after_ric,
+			      size_t offset);
+
+/**
+ * ieee80211_ie_split - split an IE buffer according to ordering
+ * @ies: the IE buffer
+ * @ielen: the length of the IE buffer
+ * @ids: an array with element IDs that are allowed before
+ *	the split
+ * @n_ids: the size of the element ID array
+ * @offset: offset where to start splitting in the buffer
+ *
+ * This function splits an IE buffer by updating the @offset
+ * variable to point to the location where the buffer should be
+ * split.
+ *
+ * It assumes that the given IE buffer is well-formed, this
+ * has to be guaranteed by the caller!
+ *
+ * It also assumes that the IEs in the buffer are ordered
+ * correctly, if not the result of using this function will not
+ * be ordered correctly either, i.e. it does no reordering.
+ *
+ * The function returns the offset where the next part of the
+ * buffer starts, which may be @ielen if the entire (remainder)
+ * of the buffer should be used.
+ */
+size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+			  const u8 *ids, int n_ids, size_t offset);
+
+/**
  * cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN
  * @wdev: the wireless device reporting the wakeup
  * @wakeup: the wakeup report
@@ -5036,6 +5174,42 @@
  */
 void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy);
 
+/**
+ * wiphy_ext_feature_set - set the extended feature flag
+ *
+ * @wiphy: the wiphy to modify.
+ * @ftidx: extended feature bit index.
+ *
+ * The extended features are flagged in multiple bytes (see
+ * &struct wiphy.@ext_features)
+ */
+static inline void wiphy_ext_feature_set(struct wiphy *wiphy,
+					 enum nl80211_ext_feature_index ftidx)
+{
+	u8 *ft_byte;
+
+	ft_byte = &wiphy->ext_features[ftidx / 8];
+	*ft_byte |= BIT(ftidx % 8);
+}
+
+/**
+ * wiphy_ext_feature_isset - check the extended feature flag
+ *
+ * @wiphy: the wiphy to modify.
+ * @ftidx: extended feature bit index.
+ *
+ * The extended features are flagged in multiple bytes (see
+ * &struct wiphy.@ext_features)
+ */
+static inline bool
+wiphy_ext_feature_isset(struct wiphy *wiphy,
+			enum nl80211_ext_feature_index ftidx)
+{
+	u8 ft_byte;
+
+	ft_byte = wiphy->ext_features[ftidx / 8];
+	return (ft_byte & BIT(ftidx % 8)) != 0;
+}
 
 /* ethtool helper */
 void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h
index 7f713ac..eeda676 100644
--- a/include/net/cfg802154.h
+++ b/include/net/cfg802154.h
@@ -25,6 +25,7 @@
 #include <net/nl802154.h>
 
 struct wpan_phy;
+struct wpan_phy_cca;
 
 struct cfg802154_ops {
 	struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
@@ -39,6 +40,8 @@
 	int	(*del_virtual_intf)(struct wpan_phy *wpan_phy,
 				    struct wpan_dev *wpan_dev);
 	int	(*set_channel)(struct wpan_phy *wpan_phy, u8 page, u8 channel);
+	int	(*set_cca_mode)(struct wpan_phy *wpan_phy,
+				const struct wpan_phy_cca *cca);
 	int	(*set_pan_id)(struct wpan_phy *wpan_phy,
 			      struct wpan_dev *wpan_dev, __le16 pan_id);
 	int	(*set_short_addr)(struct wpan_phy *wpan_phy,
@@ -56,6 +59,11 @@
 				struct wpan_dev *wpan_dev, bool mode);
 };
 
+struct wpan_phy_cca {
+	enum nl802154_cca_modes mode;
+	enum nl802154_cca_opts opt;
+};
+
 struct wpan_phy {
 	struct mutex pib_lock;
 
@@ -76,7 +84,7 @@
 	u8 current_page;
 	u32 channels_supported[IEEE802154_MAX_PAGE + 1];
 	s8 transmit_power;
-	u8 cca_mode;
+	struct wpan_phy_cca cca;
 
 	__le64 perm_extended_addr;
 
diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h
index 83bb8a7..94a2970 100644
--- a/include/net/ieee802154_netdev.h
+++ b/include/net/ieee802154_netdev.h
@@ -28,6 +28,8 @@
 #include <linux/skbuff.h>
 #include <linux/ieee802154.h>
 
+#include <net/cfg802154.h>
+
 struct ieee802154_sechdr {
 #if defined(__LITTLE_ENDIAN_BITFIELD)
 	u8 level:3,
@@ -337,7 +339,7 @@
 	s8 frame_retries;
 
 	bool lbt;
-	u8 cca_mode;
+	struct wpan_phy_cca cca;
 	s32 cca_ed_level;
 };
 
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 96323df..427d2fc 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -84,6 +84,39 @@
  *
  */
 
+/**
+ * DOC: mac80211 software tx queueing
+ *
+ * mac80211 provides an optional intermediate queueing implementation designed
+ * to allow the driver to keep hardware queues short and provide some fairness
+ * between different stations/interfaces.
+ * In this model, the driver pulls data frames from the mac80211 queue instead
+ * of letting mac80211 push them via drv_tx().
+ * Other frames (e.g. control or management) are still pushed using drv_tx().
+ *
+ * Drivers indicate that they use this model by implementing the .wake_tx_queue
+ * driver operation.
+ *
+ * Intermediate queues (struct ieee80211_txq) are kept per-sta per-tid, with a
+ * single per-vif queue for multicast data frames.
+ *
+ * The driver is expected to initialize its private per-queue data for stations
+ * and interfaces in the .add_interface and .sta_add ops.
+ *
+ * The driver can't access the queue directly. To dequeue a frame, it calls
+ * ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a queue, it
+ * calls the .wake_tx_queue driver op.
+ *
+ * For AP powersave TIM handling, the driver only needs to indicate if it has
+ * buffered packets in the driver specific data structures by calling
+ * ieee80211_sta_set_buffered(). For frames buffered in the ieee80211_txq
+ * struct, mac80211 sets the appropriate TIM PVB bits and calls
+ * .release_buffered_frames().
+ * In that callback the driver is therefore expected to release its own
+ * buffered frames and afterwards also frames from the ieee80211_txq (obtained
+ * via the usual ieee80211_tx_dequeue).
+ */
+
 struct device;
 
 /**
@@ -301,17 +334,86 @@
 #define IEEE80211_BSS_ARP_ADDR_LIST_LEN 4
 
 /**
- * enum ieee80211_rssi_event - RSSI threshold event
- * An indicator for when RSSI goes below/above a certain threshold.
- * @RSSI_EVENT_HIGH: AP's rssi crossed the high threshold set by the driver.
- * @RSSI_EVENT_LOW: AP's rssi crossed the low threshold set by the driver.
+ * enum ieee80211_event_type - event to be notified to the low level driver
+ * @RSSI_EVENT: AP's rssi crossed the a threshold set by the driver.
+ * @MLME_EVENT: event related to MLME
  */
-enum ieee80211_rssi_event {
+enum ieee80211_event_type {
+	RSSI_EVENT,
+	MLME_EVENT,
+};
+
+/**
+ * enum ieee80211_rssi_event_data - relevant when event type is %RSSI_EVENT
+ * @RSSI_EVENT_HIGH: AP's rssi went below the threshold set by the driver.
+ * @RSSI_EVENT_LOW: AP's rssi went above the threshold set by the driver.
+ */
+enum ieee80211_rssi_event_data {
 	RSSI_EVENT_HIGH,
 	RSSI_EVENT_LOW,
 };
 
 /**
+ * enum ieee80211_rssi_event - data attached to an %RSSI_EVENT
+ * @data: See &enum ieee80211_rssi_event_data
+ */
+struct ieee80211_rssi_event {
+	enum ieee80211_rssi_event_data data;
+};
+
+/**
+ * enum ieee80211_mlme_event_data - relevant when event type is %MLME_EVENT
+ * @AUTH_EVENT: the MLME operation is authentication
+ * @ASSOC_EVENT: the MLME operation is association
+ * @DEAUTH_RX_EVENT: deauth received..
+ * @DEAUTH_TX_EVENT: deauth sent.
+ */
+enum ieee80211_mlme_event_data {
+	AUTH_EVENT,
+	ASSOC_EVENT,
+	DEAUTH_RX_EVENT,
+	DEAUTH_TX_EVENT,
+};
+
+/**
+ * enum ieee80211_mlme_event_status - relevant when event type is %MLME_EVENT
+ * @MLME_SUCCESS: the MLME operation completed successfully.
+ * @MLME_DENIED: the MLME operation was denied by the peer.
+ * @MLME_TIMEOUT: the MLME operation timed out.
+ */
+enum ieee80211_mlme_event_status {
+	MLME_SUCCESS,
+	MLME_DENIED,
+	MLME_TIMEOUT,
+};
+
+/**
+ * enum ieee80211_mlme_event - data attached to an %MLME_EVENT
+ * @data: See &enum ieee80211_mlme_event_data
+ * @status: See &enum ieee80211_mlme_event_status
+ * @reason: the reason code if applicable
+ */
+struct ieee80211_mlme_event {
+	enum ieee80211_mlme_event_data data;
+	enum ieee80211_mlme_event_status status;
+	u16 reason;
+};
+
+/**
+ * struct ieee80211_event - event to be sent to the driver
+ * @type The event itself. See &enum ieee80211_event_type.
+ * @rssi: relevant if &type is %RSSI_EVENT
+ * @mlme: relevant if &type is %AUTH_EVENT
+ */
+struct ieee80211_event {
+	enum ieee80211_event_type type;
+	union {
+		struct ieee80211_rssi_event rssi;
+		struct ieee80211_mlme_event mlme;
+	} u;
+};
+
+/**
  * struct ieee80211_bss_conf - holds the BSS's changing parameters
  *
  * This structure keeps information about a BSS (and an association
@@ -337,12 +439,15 @@
  *	HW flag %IEEE80211_HW_TIMING_BEACON_ONLY is set, then this can
  *	only come from a beacon, but might not become valid until after
  *	association when a beacon is received (which is notified with the
- *	%BSS_CHANGED_DTIM flag.)
+ *	%BSS_CHANGED_DTIM flag.). See also sync_dtim_count important notice.
  * @sync_device_ts: the device timestamp corresponding to the sync_tsf,
  *	the driver/device can use this to calculate synchronisation
- *	(see @sync_tsf)
+ *	(see @sync_tsf). See also sync_dtim_count important notice.
  * @sync_dtim_count: Only valid when %IEEE80211_HW_TIMING_BEACON_ONLY
  *	is requested, see @sync_tsf/@sync_device_ts.
+ *	IMPORTANT: These three sync_* parameters would possibly be out of sync
+ *	by the time the driver will use them. The synchronized view is currently
+ *	guaranteed only in certain callbacks.
  * @beacon_int: beacon interval
  * @assoc_capability: capabilities taken from assoc resp
  * @basic_rates: bitmap of basic rates, each bit stands for an
@@ -376,6 +481,12 @@
  * @ssid_len: Length of SSID given in @ssid.
  * @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode.
  * @txpower: TX power in dBm
+ * @txpower_type: TX power adjustment used to control per packet Transmit
+ *	Power Control (TPC) in lower driver for the current vif. In particular
+ *	TPC is enabled if value passed in %txpower_type is
+ *	NL80211_TX_POWER_LIMITED (allow using less than specified from
+ *	userspace), whereas TPC is disabled if %txpower_type is set to
+ *	NL80211_TX_POWER_FIXED (use value configured from userspace)
  * @p2p_noa_attr: P2P NoA attribute for P2P powersave
  */
 struct ieee80211_bss_conf {
@@ -411,6 +522,7 @@
 	size_t ssid_len;
 	bool hidden_ssid;
 	int txpower;
+	enum nl80211_tx_power_setting txpower_type;
 	struct ieee80211_p2p_noa_attr p2p_noa_attr;
 };
 
@@ -505,8 +617,11 @@
  * @IEEE80211_TX_CTL_DONTFRAG: Don't fragment this packet even if it
  *	would be fragmented by size (this is optional, only used for
  *	monitor injection).
- * @IEEE80211_TX_CTL_PS_RESPONSE: This frame is a response to a poll
- *	frame (PS-Poll or uAPSD).
+ * @IEEE80211_TX_STAT_NOACK_TRANSMITTED: A frame that was marked with
+ *	IEEE80211_TX_CTL_NO_ACK has been successfully transmitted without
+ *	any errors (like issues specific to the driver/HW).
+ *	This flag must not be set for frames that don't request no-ack
+ *	behaviour with IEEE80211_TX_CTL_NO_ACK.
  *
  * Note: If you have to add new flags to the enumeration, then don't
  *	 forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
@@ -542,7 +657,7 @@
 	IEEE80211_TX_STATUS_EOSP		= BIT(28),
 	IEEE80211_TX_CTL_USE_MINRATE		= BIT(29),
 	IEEE80211_TX_CTL_DONTFRAG		= BIT(30),
-	IEEE80211_TX_CTL_PS_RESPONSE		= BIT(31),
+	IEEE80211_TX_STAT_NOACK_TRANSMITTED	= BIT(31),
 };
 
 #define IEEE80211_TX_CTL_STBC_SHIFT		23
@@ -552,11 +667,14 @@
  *
  * @IEEE80211_TX_CTRL_PORT_CTRL_PROTO: this frame is a port control
  *	protocol frame (e.g. EAP)
+ * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
+ *	frame (PS-Poll or uAPSD).
  *
  * These flags are used in tx_info->control.flags.
  */
 enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTRL_PORT_CTRL_PROTO	= BIT(0),
+	IEEE80211_TX_CTRL_PS_RESPONSE		= BIT(1),
 };
 
 /*
@@ -925,15 +1043,13 @@
  * These flags are used with the @vht_flag member of
  *	&struct ieee80211_rx_status.
  * @RX_VHT_FLAG_80MHZ: 80 MHz was used
- * @RX_VHT_FLAG_80P80MHZ: 80+80 MHz was used
  * @RX_VHT_FLAG_160MHZ: 160 MHz was used
  * @RX_VHT_FLAG_BF: packet was beamformed
  */
 enum mac80211_rx_vht_flags {
 	RX_VHT_FLAG_80MHZ		= BIT(0),
-	RX_VHT_FLAG_80P80MHZ		= BIT(1),
-	RX_VHT_FLAG_160MHZ		= BIT(2),
-	RX_VHT_FLAG_BF			= BIT(3),
+	RX_VHT_FLAG_160MHZ		= BIT(1),
+	RX_VHT_FLAG_BF			= BIT(2),
 };
 
 /**
@@ -1181,10 +1297,15 @@
  *	monitoring on this virtual interface -- i.e. it can monitor
  *	connection quality related parameters, such as the RSSI level and
  *	provide notifications if configured trigger levels are reached.
+ * @IEEE80211_VIF_SUPPORTS_UAPSD: The device can do U-APSD for this
+ *	interface. This flag should be set during interface addition,
+ *	but may be set/cleared as late as authentication to an AP. It is
+ *	only valid for managed/station mode interfaces.
  */
 enum ieee80211_vif_flags {
 	IEEE80211_VIF_BEACON_FILTER		= BIT(0),
 	IEEE80211_VIF_SUPPORTS_CQM_RSSI		= BIT(1),
+	IEEE80211_VIF_SUPPORTS_UAPSD		= BIT(2),
 };
 
 /**
@@ -1218,6 +1339,7 @@
  *	monitor interface (if that is requested.)
  * @drv_priv: data area for driver use, will always be aligned to
  *	sizeof(void *).
+ * @txq: the multicast data TX queue (if driver uses the TXQ abstraction)
  */
 struct ieee80211_vif {
 	enum nl80211_iftype type;
@@ -1229,6 +1351,8 @@
 	u8 cab_queue;
 	u8 hw_queue[IEEE80211_NUM_ACS];
 
+	struct ieee80211_txq *txq;
+
 	struct ieee80211_chanctx_conf __rcu *chanctx_conf;
 
 	u32 driver_flags;
@@ -1263,6 +1387,19 @@
 struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev);
 
 /**
+ * ieee80211_vif_to_wdev - return a wdev struct from a vif
+ * @vif: the vif to get the wdev for
+ *
+ * This can be used by mac80211 drivers with direct cfg80211 APIs
+ * (like the vendor commands) that needs to get the wdev for a vif.
+ *
+ * Note that this function may return %NULL if the given wdev isn't
+ * associated with a vif that the driver knows about (e.g. monitor
+ * or AP_VLAN interfaces.)
+ */
+struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif);
+
+/**
  * enum ieee80211_key_flags - key flags
  *
  * These flags are used for communication about keys between the driver
@@ -1278,8 +1415,8 @@
  * @IEEE80211_KEY_FLAG_PAIRWISE: Set by mac80211, this flag indicates
  *	that the key is pairwise rather then a shared key.
  * @IEEE80211_KEY_FLAG_SW_MGMT_TX: This flag should be set by the driver for a
- *	CCMP key if it requires CCMP encryption of management frames (MFP) to
- *	be done in software.
+ *	CCMP/GCMP key if it requires CCMP/GCMP encryption of management frames
+ *	(MFP) to be done in software.
  * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver
  *	if space should be prepared for the IV, but the IV
  *	itself should not be generated. Do not set together with
@@ -1294,8 +1431,11 @@
  *	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
+ *	driver for a CCMP/GCMP key to indicate that is requires IV generation
  *	only for managment frames (MFP).
+ * @IEEE80211_KEY_FLAG_RESERVE_TAILROOM: This flag should be set by the
+ *	driver for a key to indicate that sufficient tailroom must always
+ *	be reserved for ICV or MIC, even when HW encryption is enabled.
  */
 enum ieee80211_key_flags {
 	IEEE80211_KEY_FLAG_GENERATE_IV_MGMT	= BIT(0),
@@ -1305,6 +1445,7 @@
 	IEEE80211_KEY_FLAG_SW_MGMT_TX		= BIT(4),
 	IEEE80211_KEY_FLAG_PUT_IV_SPACE		= BIT(5),
 	IEEE80211_KEY_FLAG_RX_MGMT		= BIT(6),
+	IEEE80211_KEY_FLAG_RESERVE_TAILROOM	= BIT(7),
 };
 
 /**
@@ -1452,7 +1593,8 @@
  * @supp_rates: Bitmap of supported rates (per band)
  * @ht_cap: HT capabilities of this STA; restricted to our own capabilities
  * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities
- * @wme: indicates whether the STA supports QoS/WME.
+ * @wme: indicates whether the STA supports QoS/WME (if local devices does,
+ *	otherwise always false)
  * @drv_priv: data area for driver use, will always be aligned to
  *	sizeof(void *), size is determined in hw information.
  * @uapsd_queues: bitmap of queues configured for uapsd. Only valid
@@ -1468,6 +1610,8 @@
  * @tdls: indicates whether the STA is a TDLS peer
  * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
  *	valid if the STA is a TDLS peer in the first place.
+ * @mfp: indicates whether the STA uses management frame protection or not.
+ * @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
  */
 struct ieee80211_sta {
 	u32 supp_rates[IEEE80211_NUM_BANDS];
@@ -1484,6 +1628,9 @@
 	struct ieee80211_sta_rates __rcu *rates;
 	bool tdls;
 	bool tdls_initiator;
+	bool mfp;
+
+	struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
 
 	/* must be last */
 	u8 drv_priv[0] __aligned(sizeof(void *));
@@ -1513,6 +1660,27 @@
 };
 
 /**
+ * struct ieee80211_txq - Software intermediate tx queue
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @sta: station table entry, %NULL for per-vif queue
+ * @tid: the TID for this queue (unused for per-vif queue)
+ * @ac: the AC for this queue
+ *
+ * The driver can obtain packets from this queue by calling
+ * ieee80211_tx_dequeue().
+ */
+struct ieee80211_txq {
+	struct ieee80211_vif *vif;
+	struct ieee80211_sta *sta;
+	u8 tid;
+	u8 ac;
+
+	/* must be last */
+	u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
+/**
  * enum ieee80211_hw_flags - hardware flags
  *
  * These flags are used to indicate hardware capabilities to
@@ -1583,11 +1751,6 @@
  * @IEEE80211_HW_MFP_CAPABLE:
  *	Hardware supports management frame protection (MFP, IEEE 802.11w).
  *
- * @IEEE80211_HW_SUPPORTS_UAPSD:
- *	Hardware supports Unscheduled Automatic Power Save Delivery
- *	(U-APSD) in managed mode. The mode is configured with
- *	conf_tx() operation.
- *
  * @IEEE80211_HW_REPORTS_TX_ACK_STATUS:
  *	Hardware can provide ack status reports of Tx frames to
  *	the stack.
@@ -1626,6 +1789,12 @@
  *	be created.  It is expected user-space will create vifs as
  *	desired (and thus have them named as desired).
  *
+ * @IEEE80211_HW_SW_CRYPTO_CONTROL: The driver wants to control which of the
+ *	crypto algorithms can be done in software - so don't automatically
+ *	try to fall back to it if hardware crypto fails, but do so only if
+ *	the driver returns 1. This also forces the driver to advertise its
+ *	supported cipher suites.
+ *
  * @IEEE80211_HW_QUEUE_CONTROL: The driver wants to control per-interface
  *	queue mapping in order to use different queues (not just one per AC)
  *	for different virtual interfaces. See the doc section on HW queue
@@ -1673,8 +1842,8 @@
 	IEEE80211_HW_MFP_CAPABLE			= 1<<13,
 	IEEE80211_HW_WANT_MONITOR_VIF			= 1<<14,
 	IEEE80211_HW_NO_AUTO_VIF			= 1<<15,
-	/* free slot */
-	IEEE80211_HW_SUPPORTS_UAPSD			= 1<<17,
+	IEEE80211_HW_SW_CRYPTO_CONTROL			= 1<<16,
+	/* free slots */
 	IEEE80211_HW_REPORTS_TX_ACK_STATUS		= 1<<18,
 	IEEE80211_HW_CONNECTION_MONITOR			= 1<<19,
 	IEEE80211_HW_QUEUE_CONTROL			= 1<<20,
@@ -1735,6 +1904,8 @@
  *	within &struct ieee80211_sta.
  * @chanctx_data_size: size (in bytes) of the drv_priv data area
  *	within &struct ieee80211_chanctx_conf.
+ * @txq_data_size: size (in bytes) of the drv_priv data area
+ *	within @struct ieee80211_txq.
  *
  * @max_rates: maximum number of alternate rate retry stages the hw
  *	can handle.
@@ -1783,6 +1954,9 @@
  * @n_cipher_schemes: a size of an array of cipher schemes definitions.
  * @cipher_schemes: a pointer to an array of cipher scheme definitions
  *	supported by HW.
+ *
+ * @txq_ac_max_pending: maximum number of frames per AC pending in all txq
+ *	entries for a vif.
  */
 struct ieee80211_hw {
 	struct ieee80211_conf conf;
@@ -1795,6 +1969,7 @@
 	int vif_data_size;
 	int sta_data_size;
 	int chanctx_data_size;
+	int txq_data_size;
 	u16 queues;
 	u16 max_listen_interval;
 	s8 max_signal;
@@ -1811,6 +1986,7 @@
 	u8 uapsd_max_sp_len;
 	u8 n_cipher_schemes;
 	const struct ieee80211_cipher_scheme *cipher_schemes;
+	int txq_ac_max_pending;
 };
 
 /**
@@ -1948,6 +2124,11 @@
  * added; if you return 0 then hw_key_idx must be assigned to the
  * hardware key index, you are free to use the full u8 range.
  *
+ * Note that in the case that the @IEEE80211_HW_SW_CRYPTO_CONTROL flag is
+ * set, mac80211 will not automatically fall back to software crypto if
+ * enabling hardware crypto failed. The set_key() call may also return the
+ * value 1 to permit this specific key/algorithm to be done in software.
+ *
  * When the cmd is %DISABLE_KEY then it must succeed.
  *
  * Note that it is permissible to not decrypt a frame even if a key
@@ -2026,7 +2207,7 @@
  * enabled whenever user has enabled powersave.
  *
  * Driver informs U-APSD client support by enabling
- * %IEEE80211_HW_SUPPORTS_UAPSD flag. The mode is configured through the
+ * %IEEE80211_VIF_SUPPORTS_UAPSD flag. The mode is configured through the
  * uapsd parameter in conf_tx() operation. Hardware needs to send the QoS
  * Nullfunc frames and stay awake until the service period has ended. To
  * utilize U-APSD, dynamic powersave is disabled for voip AC and all frames
@@ -2699,6 +2880,14 @@
  *	is only used if the configured rate control algorithm actually uses
  *	the new rate table API, and is therefore optional. Must be atomic.
  *
+ * @sta_statistics: Get statistics for this station. For example with beacon
+ *	filtering, the statistics kept by mac80211 might not be accurate, so
+ *	let the driver pre-fill the statistics. The driver can fill most of
+ *	the values (indicating which by setting the filled bitmap), but not
+ *	all of them make sense - see the source for which ones are possible.
+ *	Statistics that the driver doesn't fill will be filled by mac80211.
+ *	The callback can sleep.
+ *
  * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
  *	bursting) for a hardware TX queue.
  *	Returns a negative error code on failure.
@@ -2810,8 +2999,9 @@
  * @set_bitrate_mask: Set a mask of rates to be used for rate control selection
  *	when transmitting a frame. Currently only legacy rates are handled.
  *	The callback can sleep.
- * @rssi_callback: Notify driver when the average RSSI goes above/below
- *	thresholds that were registered previously. The callback can sleep.
+ * @event_callback: Notify driver about any event in mac80211. See
+ *	&enum ieee80211_event_type for the different types.
+ *	The callback can sleep.
  *
  * @release_buffered_frames: Release buffered frames according to the given
  *	parameters. In the case where the driver buffers some frames for
@@ -2859,9 +3049,6 @@
  * @get_et_strings:  Ethtool API to get a set of strings to describe stats
  *	and perhaps other supported types of ethtool data-sets.
  *
- * @get_rssi: Get current signal strength in dBm, the function is optional
- *	and can sleep.
- *
  * @mgd_prepare_tx: Prepare for transmitting a management frame for association
  *	before associated. In multi-channel scenarios, a virtual interface is
  *	bound to a channel before it is associated, but as it isn't associated
@@ -2962,6 +3149,8 @@
  *	response template is provided, together with the location of the
  *	switch-timing IE within the template. The skb can only be used within
  *	the function call.
+ *
+ * @wake_tx_queue: Called when new packets have been added to the queue.
  */
 struct ieee80211_ops {
 	void (*tx)(struct ieee80211_hw *hw,
@@ -3062,6 +3251,10 @@
 	void (*sta_rate_tbl_update)(struct ieee80211_hw *hw,
 				    struct ieee80211_vif *vif,
 				    struct ieee80211_sta *sta);
+	void (*sta_statistics)(struct ieee80211_hw *hw,
+			       struct ieee80211_vif *vif,
+			       struct ieee80211_sta *sta,
+			       struct station_info *sinfo);
 	int (*conf_tx)(struct ieee80211_hw *hw,
 		       struct ieee80211_vif *vif, u16 ac,
 		       const struct ieee80211_tx_queue_params *params);
@@ -3106,9 +3299,9 @@
 	bool (*tx_frames_pending)(struct ieee80211_hw *hw);
 	int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 				const struct cfg80211_bitrate_mask *mask);
-	void (*rssi_callback)(struct ieee80211_hw *hw,
-			      struct ieee80211_vif *vif,
-			      enum ieee80211_rssi_event rssi_event);
+	void (*event_callback)(struct ieee80211_hw *hw,
+			       struct ieee80211_vif *vif,
+			       const struct ieee80211_event *event);
 
 	void (*allow_buffered_frames)(struct ieee80211_hw *hw,
 				      struct ieee80211_sta *sta,
@@ -3129,8 +3322,6 @@
 	void	(*get_et_strings)(struct ieee80211_hw *hw,
 				  struct ieee80211_vif *vif,
 				  u32 sset, u8 *data);
-	int	(*get_rssi)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-			    struct ieee80211_sta *sta, s8 *rssi_dbm);
 
 	void	(*mgd_prepare_tx)(struct ieee80211_hw *hw,
 				  struct ieee80211_vif *vif);
@@ -3191,6 +3382,9 @@
 	void (*tdls_recv_channel_switch)(struct ieee80211_hw *hw,
 					 struct ieee80211_vif *vif,
 					 struct ieee80211_tdls_ch_sw_params *params);
+
+	void (*wake_tx_queue)(struct ieee80211_hw *hw,
+			      struct ieee80211_txq *txq);
 };
 
 /**
@@ -4065,6 +4259,10 @@
  *	reverse order than in packet)
  * @aes_cmac: PN data, most significant byte first (big endian,
  *	reverse order than in packet)
+ * @aes_gmac: PN data, most significant byte first (big endian,
+ *	reverse order than in packet)
+ * @gcmp: PN data, most significant byte first (big endian,
+ *	reverse order than in packet)
  */
 struct ieee80211_key_seq {
 	union {
@@ -4078,6 +4276,12 @@
 		struct {
 			u8 pn[6];
 		} aes_cmac;
+		struct {
+			u8 pn[6];
+		} aes_gmac;
+		struct {
+			u8 pn[6];
+		} gcmp;
 	};
 };
 
@@ -4102,7 +4306,7 @@
  * ieee80211_get_key_rx_seq - get key RX sequence counter
  *
  * @keyconf: the parameter passed with the set key
- * @tid: The TID, or -1 for the management frame value (CCMP only);
+ * @tid: The TID, or -1 for the management frame value (CCMP/GCMP only);
  *	the value on TID 0 is also used for non-QoS frames. For
  *	CMAC, only TID 0 is valid.
  * @seq: buffer to receive the sequence data
@@ -4138,7 +4342,7 @@
  * ieee80211_set_key_rx_seq - set key RX sequence counter
  *
  * @keyconf: the parameter passed with the set key
- * @tid: The TID, or -1 for the management frame value (CCMP only);
+ * @tid: The TID, or -1 for the management frame value (CCMP/GCMP only);
  *	the value on TID 0 is also used for non-QoS frames. For
  *	CMAC, only TID 0 is valid.
  * @seq: new sequence data
@@ -4300,13 +4504,33 @@
  *	haven't been re-added to the driver yet.
  * @IEEE80211_IFACE_ITER_RESUME_ALL: During resume, iterate over all
  *	interfaces, even if they haven't been re-added to the driver yet.
+ * @IEEE80211_IFACE_ITER_ACTIVE: Iterate only active interfaces (netdev is up).
  */
 enum ieee80211_interface_iteration_flags {
 	IEEE80211_IFACE_ITER_NORMAL	= 0,
 	IEEE80211_IFACE_ITER_RESUME_ALL	= BIT(0),
+	IEEE80211_IFACE_ITER_ACTIVE	= BIT(1),
 };
 
 /**
+ * ieee80211_iterate_interfaces - iterate interfaces
+ *
+ * This function iterates over the interfaces associated with a given
+ * hardware and calls the callback for them. This includes active as well as
+ * inactive interfaces. This function allows the iterator function to sleep.
+ * Will iterate over a new interface during add_interface().
+ *
+ * @hw: the hardware struct of which the interfaces should be iterated over
+ * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
+ * @iterator: the iterator function to call
+ * @data: first argument of the iterator function
+ */
+void ieee80211_iterate_interfaces(struct ieee80211_hw *hw, u32 iter_flags,
+				  void (*iterator)(void *data, u8 *mac,
+						   struct ieee80211_vif *vif),
+				  void *data);
+
+/**
  * ieee80211_iterate_active_interfaces - iterate active interfaces
  *
  * This function iterates over the interfaces associated with a given
@@ -4321,11 +4545,16 @@
  * @iterator: the iterator function to call
  * @data: first argument of the iterator function
  */
-void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw,
-					 u32 iter_flags,
-					 void (*iterator)(void *data, u8 *mac,
-						struct ieee80211_vif *vif),
-					 void *data);
+static inline void
+ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, u32 iter_flags,
+				    void (*iterator)(void *data, u8 *mac,
+						     struct ieee80211_vif *vif),
+				    void *data)
+{
+	ieee80211_iterate_interfaces(hw,
+				     iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
+				     iterator, data);
+}
 
 /**
  * ieee80211_iterate_active_interfaces_atomic - iterate active interfaces
@@ -5151,30 +5380,13 @@
 void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
 
 /**
- * ieee80211_ie_split - split an IE buffer according to ordering
+ * ieee80211_tx_dequeue - dequeue a packet from a software tx queue
  *
- * @ies: the IE buffer
- * @ielen: the length of the IE buffer
- * @ids: an array with element IDs that are allowed before
- *	the split
- * @n_ids: the size of the element ID array
- * @offset: offset where to start splitting in the buffer
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @txq: pointer obtained from station or virtual interface
  *
- * This function splits an IE buffer by updating the @offset
- * variable to point to the location where the buffer should be
- * split.
- *
- * It assumes that the given IE buffer is well-formed, this
- * has to be guaranteed by the caller!
- *
- * It also assumes that the IEs in the buffer are ordered
- * correctly, if not the result of using this function will not
- * be ordered correctly either, i.e. it does no reordering.
- *
- * The function returns the offset where the next part of the
- * buffer starts, which may be @ielen if the entire (remainder)
- * of the buffer should be used.
+ * Returns the skb if successful, %NULL if no frame was available.
  */
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
-			  const u8 *ids, int n_ids, size_t offset);
+struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
+				     struct ieee80211_txq *txq);
 #endif /* MAC80211_H */
diff --git a/include/net/mac802154.h b/include/net/mac802154.h
index c823d91..e18e7fd 100644
--- a/include/net/mac802154.h
+++ b/include/net/mac802154.h
@@ -19,6 +19,9 @@
 #include <net/af_ieee802154.h>
 #include <linux/ieee802154.h>
 #include <linux/skbuff.h>
+#include <linux/unaligned/memmove.h>
+
+#include <net/cfg802154.h>
 
 /* General MAC frame format:
  *  2 bytes: Frame Control
@@ -210,9 +213,10 @@
 	int		(*set_hw_addr_filt)(struct ieee802154_hw *hw,
 					    struct ieee802154_hw_addr_filt *filt,
 					    unsigned long changed);
-	int		(*set_txpower)(struct ieee802154_hw *hw, int db);
+	int		(*set_txpower)(struct ieee802154_hw *hw, s8 dbm);
 	int		(*set_lbt)(struct ieee802154_hw *hw, bool on);
-	int		(*set_cca_mode)(struct ieee802154_hw *hw, u8 mode);
+	int		(*set_cca_mode)(struct ieee802154_hw *hw,
+					const struct wpan_phy_cca *cca);
 	int		(*set_cca_ed_level)(struct ieee802154_hw *hw,
 					    s32 level);
 	int		(*set_csma_params)(struct ieee802154_hw *hw,
@@ -230,9 +234,7 @@
  */
 static inline void ieee802154_be64_to_le64(void *le64_dst, const void *be64_src)
 {
-	__le64 tmp = (__force __le64)swab64p(be64_src);
-
-	memcpy(le64_dst, &tmp, IEEE802154_EXTENDED_ADDR_LEN);
+	__put_unaligned_memmove64(swab64p(be64_src), le64_dst);
 }
 
 /**
@@ -242,12 +244,10 @@
  */
 static inline void ieee802154_le64_to_be64(void *be64_dst, const void *le64_src)
 {
-	__be64 tmp = (__force __be64)swab64p(le64_src);
-
-	memcpy(be64_dst, &tmp, IEEE802154_EXTENDED_ADDR_LEN);
+	__put_unaligned_memmove64(swab64p(le64_src), be64_dst);
 }
 
-/* Basic interface to register ieee802154 hwice */
+/* Basic interface to register ieee802154 device */
 struct ieee802154_hw *
 ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops);
 void ieee802154_free_hw(struct ieee802154_hw *hw);
diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h
index 14bd0e1..020a814 100644
--- a/include/net/nfc/hci.h
+++ b/include/net/nfc/hci.h
@@ -51,8 +51,10 @@
 	int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
 	int (*check_presence)(struct nfc_hci_dev *hdev,
 			      struct nfc_target *target);
-	int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
+	int (*event_received)(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
 			      struct sk_buff *skb);
+	void (*cmd_received)(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
+			    struct sk_buff *skb);
 	int (*fw_download)(struct nfc_hci_dev *hdev, const char *firmware_name);
 	int (*discover_se)(struct nfc_hci_dev *dev);
 	int (*enable_se)(struct nfc_hci_dev *dev, u32 se_idx);
@@ -63,8 +65,10 @@
 };
 
 /* Pipes */
-#define NFC_HCI_INVALID_PIPE	0x80
 #define NFC_HCI_DO_NOT_CREATE_PIPE	0x81
+#define NFC_HCI_INVALID_PIPE	0x80
+#define NFC_HCI_INVALID_GATE	0xFF
+#define NFC_HCI_INVALID_HOST	0x80
 #define NFC_HCI_LINK_MGMT_PIPE	0x00
 #define NFC_HCI_ADMIN_PIPE	0x01
 
@@ -73,7 +77,17 @@
 	u8 pipe;
 };
 
+struct nfc_hci_pipe {
+	u8 gate;
+	u8 dest_host;
+};
+
 #define NFC_HCI_MAX_CUSTOM_GATES	50
+/*
+ * According to specification 102 622 chapter 4.4 Pipes,
+ * the pipe identifier is 7 bits long.
+ */
+#define NFC_HCI_MAX_PIPES		127
 struct nfc_hci_init_data {
 	u8 gate_count;
 	struct nfc_hci_gate gates[NFC_HCI_MAX_CUSTOM_GATES];
@@ -125,6 +139,7 @@
 	void *clientdata;
 
 	u8 gate2pipe[NFC_HCI_MAX_GATES];
+	struct nfc_hci_pipe pipes[NFC_HCI_MAX_PIPES];
 
 	u8 sw_romlib;
 	u8 sw_patch;
@@ -167,6 +182,8 @@
 void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err);
 
 int nfc_hci_result_to_errno(u8 result);
+void nfc_hci_reset_pipes(struct nfc_hci_dev *dev);
+void nfc_hci_reset_pipes_per_host(struct nfc_hci_dev *hdev, u8 host);
 
 /* Host IDs */
 #define NFC_HCI_HOST_CONTROLLER_ID	0x00
@@ -219,6 +236,12 @@
 #define NFC_HCI_EVT_POST_DATA			0x02
 #define NFC_HCI_EVT_HOT_PLUG			0x03
 
+/* Generic commands */
+#define NFC_HCI_ANY_SET_PARAMETER	0x01
+#define NFC_HCI_ANY_GET_PARAMETER	0x02
+#define NFC_HCI_ANY_OPEN_PIPE		0x03
+#define NFC_HCI_ANY_CLOSE_PIPE		0x04
+
 /* Reader RF gates events */
 #define NFC_HCI_EVT_READER_REQUESTED	0x10
 #define NFC_HCI_EVT_END_OPERATION	0x11
@@ -249,8 +272,6 @@
 int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
 			   const u8 *param, size_t param_len,
 			   data_exchange_cb_t cb, void *cb_context);
-int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response,
-			  const u8 *param, size_t param_len);
 int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
 		       const u8 *param, size_t param_len);
 int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate);
diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h
index e7257a4..a2f2f3d 100644
--- a/include/net/nfc/nci.h
+++ b/include/net/nfc/nci.h
@@ -62,6 +62,25 @@
 #define NCI_STATUS_NFCEE_PROTOCOL_ERROR				0xc2
 #define NCI_STATUS_NFCEE_TIMEOUT_ERROR				0xc3
 
+/* NFCEE Interface/Protocols */
+#define NCI_NFCEE_INTERFACE_APDU           0x00
+#define NCI_NFCEE_INTERFACE_HCI_ACCESS     0x01
+#define NCI_NFCEE_INTERFACE_TYPE3_CMD_SET  0x02
+#define NCI_NFCEE_INTERFACE_TRANSPARENT        0x03
+
+/* Destination type */
+#define NCI_DESTINATION_NFCC_LOOPBACK      0x01
+#define NCI_DESTINATION_REMOTE_NFC_ENDPOINT    0x02
+#define NCI_DESTINATION_NFCEE              0x03
+
+/* Destination-specific parameters type */
+#define NCI_DESTINATION_SPECIFIC_PARAM_RF_TYPE     0x00
+#define NCI_DESTINATION_SPECIFIC_PARAM_NFCEE_TYPE  0x01
+
+/* NFCEE Discovery Action */
+#define NCI_NFCEE_DISCOVERY_ACTION_DISABLE			0x00
+#define NCI_NFCEE_DISCOVERY_ACTION_ENABLE			0x01
+
 /* NCI RF Technology and Mode */
 #define NCI_NFC_A_PASSIVE_POLL_MODE				0x00
 #define NCI_NFC_B_PASSIVE_POLL_MODE				0x01
@@ -224,6 +243,28 @@
 	struct	set_config_param param; /* support 1 param per cmd is enough */
 } __packed;
 
+#define NCI_OP_CORE_CONN_CREATE_CMD	nci_opcode_pack(NCI_GID_CORE, 0x04)
+#define DEST_SPEC_PARAMS_ID_INDEX	0
+#define DEST_SPEC_PARAMS_PROTOCOL_INDEX	1
+struct dest_spec_params {
+	__u8    id;
+	__u8    protocol;
+} __packed;
+
+struct core_conn_create_dest_spec_params {
+	__u8    type;
+	__u8    length;
+	__u8    value[0];
+} __packed;
+
+struct nci_core_conn_create_cmd {
+	__u8    destination_type;
+	__u8    number_destination_params;
+	struct core_conn_create_dest_spec_params params[0];
+} __packed;
+
+#define NCI_OP_CORE_CONN_CLOSE_CMD	nci_opcode_pack(NCI_GID_CORE, 0x05)
+
 #define NCI_OP_RF_DISCOVER_MAP_CMD	nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 struct disc_map_config {
 	__u8	rf_protocol;
@@ -260,6 +301,19 @@
 	__u8	type;
 } __packed;
 
+#define NCI_OP_NFCEE_DISCOVER_CMD nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x00)
+struct nci_nfcee_discover_cmd {
+	__u8	discovery_action;
+} __packed;
+
+#define NCI_OP_NFCEE_MODE_SET_CMD nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x01)
+#define NCI_NFCEE_DISABLE	0x00
+#define NCI_NFCEE_ENABLE	0x01
+struct nci_nfcee_mode_set_cmd {
+	__u8	nfcee_id;
+	__u8	nfcee_mode;
+} __packed;
+
 /* ----------------------- */
 /* ---- NCI Responses ---- */
 /* ----------------------- */
@@ -295,6 +349,16 @@
 	__u8	params_id[0];	/* variable size array */
 } __packed;
 
+#define NCI_OP_CORE_CONN_CREATE_RSP	nci_opcode_pack(NCI_GID_CORE, 0x04)
+struct nci_core_conn_create_rsp {
+	__u8	status;
+	__u8	max_ctrl_pkt_payload_len;
+	__u8    credits_cnt;
+	__u8	conn_id;
+} __packed;
+
+#define NCI_OP_CORE_CONN_CLOSE_RSP	nci_opcode_pack(NCI_GID_CORE, 0x05)
+
 #define NCI_OP_RF_DISCOVER_MAP_RSP	nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 
 #define NCI_OP_RF_DISCOVER_RSP		nci_opcode_pack(NCI_GID_RF_MGMT, 0x03)
@@ -303,6 +367,13 @@
 
 #define NCI_OP_RF_DEACTIVATE_RSP	nci_opcode_pack(NCI_GID_RF_MGMT, 0x06)
 
+#define NCI_OP_NFCEE_DISCOVER_RSP nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x00)
+struct nci_nfcee_discover_rsp {
+	__u8	status;
+	__u8	num_nfcee;
+} __packed;
+
+#define NCI_OP_NFCEE_MODE_SET_RSP nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x01)
 /* --------------------------- */
 /* ---- NCI Notifications ---- */
 /* --------------------------- */
@@ -430,4 +501,30 @@
 	__u8	reason;
 } __packed;
 
+#define NCI_OP_RF_NFCEE_ACTION_NTF	nci_opcode_pack(NCI_GID_RF_MGMT, 0x09)
+struct nci_rf_nfcee_action_ntf {
+	__u8 nfcee_id;
+	__u8 trigger;
+	__u8 supported_data_length;
+	__u8 supported_data[0];
+} __packed;
+
+#define NCI_OP_NFCEE_DISCOVER_NTF nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x00)
+struct nci_nfcee_supported_protocol {
+	__u8	num_protocol;
+	__u8	supported_protocol[0];
+} __packed;
+
+struct nci_nfcee_information_tlv {
+	__u8	num_tlv;
+	__u8	information_tlv[0];
+} __packed;
+
+struct nci_nfcee_discover_ntf {
+	__u8	nfcee_id;
+	__u8	nfcee_status;
+	struct nci_nfcee_supported_protocol supported_protocols;
+	struct nci_nfcee_information_tlv	information_tlv;
+} __packed;
+
 #endif /* __NCI_H */
diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 9e51bb4..d4dcc71 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -71,6 +71,7 @@
 	int   (*close)(struct nci_dev *ndev);
 	int   (*send)(struct nci_dev *ndev, struct sk_buff *skb);
 	int   (*setup)(struct nci_dev *ndev);
+	int   (*fw_download)(struct nci_dev *ndev, const char *firmware_name);
 	__u32 (*get_rfprotocol)(struct nci_dev *ndev, __u8 rf_protocol);
 	int   (*discover_se)(struct nci_dev *ndev);
 	int   (*disable_se)(struct nci_dev *ndev, u32 se_idx);
@@ -78,15 +79,111 @@
 	int   (*se_io)(struct nci_dev *ndev, u32 se_idx,
 				u8 *apdu, size_t apdu_length,
 				se_io_cb_t cb, void *cb_context);
+	int   (*hci_load_session)(struct nci_dev *ndev);
+	void  (*hci_event_received)(struct nci_dev *ndev, u8 pipe, u8 event,
+				    struct sk_buff *skb);
+	void  (*hci_cmd_received)(struct nci_dev *ndev, u8 pipe, u8 cmd,
+				  struct sk_buff *skb);
 };
 
 #define NCI_MAX_SUPPORTED_RF_INTERFACES		4
 #define NCI_MAX_DISCOVERED_TARGETS		10
+#define NCI_MAX_NUM_NFCEE   255
+#define NCI_MAX_CONN_ID		7
+
+struct nci_conn_info {
+	struct list_head list;
+	__u8	id; /* can be an RF Discovery ID or an NFCEE ID */
+	__u8	conn_id;
+	__u8	max_pkt_payload_len;
+
+	atomic_t credits_cnt;
+	__u8	 initial_num_credits;
+
+	data_exchange_cb_t	data_exchange_cb;
+	void *data_exchange_cb_context;
+
+	struct sk_buff *rx_skb;
+};
+
+#define NCI_INVALID_CONN_ID 0x80
+
+#define NCI_HCI_ANY_OPEN_PIPE      0x03
+
+/* Gates */
+#define NCI_HCI_ADMIN_GATE         0x00
+#define NCI_HCI_LINK_MGMT_GATE     0x06
+
+/* Pipes */
+#define NCI_HCI_LINK_MGMT_PIPE             0x00
+#define NCI_HCI_ADMIN_PIPE                 0x01
+
+/* Generic responses */
+#define NCI_HCI_ANY_OK                     0x00
+#define NCI_HCI_ANY_E_NOT_CONNECTED        0x01
+#define NCI_HCI_ANY_E_CMD_PAR_UNKNOWN      0x02
+#define NCI_HCI_ANY_E_NOK                  0x03
+#define NCI_HCI_ANY_E_PIPES_FULL           0x04
+#define NCI_HCI_ANY_E_REG_PAR_UNKNOWN      0x05
+#define NCI_HCI_ANY_E_PIPE_NOT_OPENED      0x06
+#define NCI_HCI_ANY_E_CMD_NOT_SUPPORTED    0x07
+#define NCI_HCI_ANY_E_INHIBITED            0x08
+#define NCI_HCI_ANY_E_TIMEOUT              0x09
+#define NCI_HCI_ANY_E_REG_ACCESS_DENIED    0x0a
+#define NCI_HCI_ANY_E_PIPE_ACCESS_DENIED   0x0b
+
+#define NCI_HCI_DO_NOT_OPEN_PIPE           0x81
+#define NCI_HCI_INVALID_PIPE               0x80
+#define NCI_HCI_INVALID_GATE               0xFF
+#define NCI_HCI_INVALID_HOST               0x80
+
+#define NCI_HCI_MAX_CUSTOM_GATES   50
+/*
+ * According to specification 102 622 chapter 4.4 Pipes,
+ * the pipe identifier is 7 bits long.
+ */
+#define NCI_HCI_MAX_PIPES          127
+
+struct nci_hci_gate {
+	u8 gate;
+	u8 pipe;
+	u8 dest_host;
+} __packed;
+
+struct nci_hci_pipe {
+	u8 gate;
+	u8 host;
+} __packed;
+
+struct nci_hci_init_data {
+	u8 gate_count;
+	struct nci_hci_gate gates[NCI_HCI_MAX_CUSTOM_GATES];
+	char session_id[9];
+};
+
+#define NCI_HCI_MAX_GATES          256
+
+struct nci_hci_dev {
+	u8 nfcee_id;
+	struct nci_dev *ndev;
+	struct nci_conn_info *conn_info;
+
+	struct nci_hci_init_data init_data;
+	struct nci_hci_pipe pipes[NCI_HCI_MAX_PIPES];
+	u8 gate2pipe[NCI_HCI_MAX_GATES];
+	int expected_pipes;
+	int count_pipes;
+
+	struct sk_buff_head rx_hcp_frags;
+	struct work_struct msg_rx_work;
+	struct sk_buff_head msg_rx_queue;
+};
 
 /* NCI Core structures */
 struct nci_dev {
 	struct nfc_dev		*nfc_dev;
 	struct nci_ops		*ops;
+	struct nci_hci_dev	*hci_dev;
 
 	int			tx_headroom;
 	int			tx_tailroom;
@@ -95,7 +192,10 @@
 	unsigned long		flags;
 
 	atomic_t		cmd_cnt;
-	atomic_t		credits_cnt;
+	__u8			cur_conn_id;
+
+	struct list_head	conn_info_list;
+	struct nci_conn_info	*rf_conn_info;
 
 	struct timer_list	cmd_timer;
 	struct timer_list	data_timer;
@@ -141,13 +241,10 @@
 	__u8			manufact_id;
 	__u32			manufact_specific_info;
 
-	/* received during NCI_OP_RF_INTF_ACTIVATED_NTF */
-	__u8			max_data_pkt_payload_size;
-	__u8			initial_num_credits;
+	/* Save RF Discovery ID or NFCEE ID under conn_create */
+	__u8			cur_id;
 
 	/* stored during nci_data_exchange */
-	data_exchange_cb_t	data_exchange_cb;
-	void			*data_exchange_cb_context;
 	struct sk_buff		*rx_data_reassembly;
 
 	/* stored during intf_activated_ntf */
@@ -163,9 +260,36 @@
 void nci_free_device(struct nci_dev *ndev);
 int nci_register_device(struct nci_dev *ndev);
 void nci_unregister_device(struct nci_dev *ndev);
+int nci_request(struct nci_dev *ndev,
+		void (*req)(struct nci_dev *ndev,
+			    unsigned long opt),
+		unsigned long opt, __u32 timeout);
 int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
 int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val);
 
+int nci_nfcee_discover(struct nci_dev *ndev, u8 action);
+int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode);
+int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
+			 u8 number_destination_params,
+			 size_t params_len,
+			 struct core_conn_create_dest_spec_params *params);
+int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id);
+
+struct nci_hci_dev *nci_hci_allocate(struct nci_dev *ndev);
+int nci_hci_send_event(struct nci_dev *ndev, u8 gate, u8 event,
+		       const u8 *param, size_t param_len);
+int nci_hci_send_cmd(struct nci_dev *ndev, u8 gate,
+		     u8 cmd, const u8 *param, size_t param_len,
+		     struct sk_buff **skb);
+int nci_hci_open_pipe(struct nci_dev *ndev, u8 pipe);
+int nci_hci_connect_gate(struct nci_dev *ndev, u8 dest_host,
+			 u8 dest_gate, u8 pipe);
+int nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx,
+		      const u8 *param, size_t param_len);
+int nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx,
+		      struct sk_buff **skb);
+int nci_hci_dev_session_init(struct nci_dev *ndev);
+
 static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev,
 					    unsigned int len,
 					    gfp_t how)
@@ -200,7 +324,9 @@
 int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload);
 int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb);
 void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
-				int err);
+				__u8 conn_id, int err);
+void nci_hci_data_received_cb(void *context, struct sk_buff *skb, int err);
+
 void nci_clear_target_list(struct nci_dev *ndev);
 
 /* ----- NCI requests ----- */
@@ -209,6 +335,8 @@
 #define NCI_REQ_CANCELED	2
 
 void nci_req_complete(struct nci_dev *ndev, int result);
+struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev,
+						   int conn_id);
 
 /* ----- NCI status code ----- */
 int nci_to_errno(__u8 code);
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index 12adb81..7ac029c 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -135,6 +135,31 @@
 	u16 state;
 };
 
+/**
+ * nfc_evt_transaction - A struct for NFC secure element event transaction.
+ *
+ * @aid: The application identifier triggering the event
+ *
+ * @aid_len: The application identifier length [5:16]
+ *
+ * @params: The application parameters transmitted during the transaction
+ *
+ * @params_len: The applications parameters length [0:255]
+ *
+ */
+#define NFC_MIN_AID_LENGTH	5
+#define	NFC_MAX_AID_LENGTH	16
+#define NFC_MAX_PARAMS_LENGTH	255
+
+#define NFC_EVT_TRANSACTION_AID_TAG	0x81
+#define NFC_EVT_TRANSACTION_PARAMS_TAG	0x82
+struct nfc_evt_transaction {
+	u32 aid_len;
+	u8 aid[NFC_MAX_AID_LENGTH];
+	u8 params_len;
+	u8 params[0];
+} __packed;
+
 struct nfc_genl_data {
 	u32 poll_req_portid;
 	struct mutex genl_data_mutex;
@@ -262,6 +287,8 @@
 
 void nfc_driver_failure(struct nfc_dev *dev, int err);
 
+int nfc_se_transaction(struct nfc_dev *dev, u8 se_idx,
+		       struct nfc_evt_transaction *evt_transaction);
 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);
diff --git a/include/net/nl802154.h b/include/net/nl802154.h
index 6dbd406..f8b5bc9 100644
--- a/include/net/nl802154.h
+++ b/include/net/nl802154.h
@@ -82,7 +82,7 @@
 	NL802154_ATTR_TX_POWER,
 
 	NL802154_ATTR_CCA_MODE,
-	NL802154_ATTR_CCA_MODE3_AND,
+	NL802154_ATTR_CCA_OPT,
 	NL802154_ATTR_CCA_ED_LEVEL,
 
 	NL802154_ATTR_MAX_FRAME_RETRIES,
@@ -119,4 +119,47 @@
 	NL802154_IFTYPE_MAX = NUM_NL802154_IFTYPES - 1
 };
 
+/**
+ * enum nl802154_cca_modes - cca modes
+ *
+ * @__NL802154_CCA_INVALID: cca mode number 0 is reserved
+ * @NL802154_CCA_ENERGY: Energy above threshold
+ * @NL802154_CCA_CARRIER: Carrier sense only
+ * @NL802154_CCA_ENERGY_CARRIER: Carrier sense with energy above threshold
+ * @NL802154_CCA_ALOHA: CCA shall always report an idle medium
+ * @NL802154_CCA_UWB_SHR: UWB preamble sense based on the SHR of a frame
+ * @NL802154_CCA_UWB_MULTIPEXED: UWB preamble sense based on the packet with
+ *	the multiplexed preamble
+ * @__NL802154_CCA_ATTR_AFTER_LAST: Internal
+ * @NL802154_CCA_ATTR_MAX: Maximum CCA attribute number
+ */
+enum nl802154_cca_modes {
+	__NL802154_CCA_INVALID,
+	NL802154_CCA_ENERGY,
+	NL802154_CCA_CARRIER,
+	NL802154_CCA_ENERGY_CARRIER,
+	NL802154_CCA_ALOHA,
+	NL802154_CCA_UWB_SHR,
+	NL802154_CCA_UWB_MULTIPEXED,
+
+	/* keep last */
+	__NL802154_CCA_ATTR_AFTER_LAST,
+	NL802154_CCA_ATTR_MAX = __NL802154_CCA_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl802154_cca_opts - additional options for cca modes
+ *
+ * @NL802154_CCA_OPT_ENERGY_CARRIER_OR: NL802154_CCA_ENERGY_CARRIER with OR
+ * @NL802154_CCA_OPT_ENERGY_CARRIER_AND: NL802154_CCA_ENERGY_CARRIER with AND
+ */
+enum nl802154_cca_opts {
+	NL802154_CCA_OPT_ENERGY_CARRIER_AND,
+	NL802154_CCA_OPT_ENERGY_CARRIER_OR,
+
+	/* keep last */
+	__NL802154_CCA_OPT_ATTR_AFTER_LAST,
+	NL802154_CCA_OPT_ATTR_MAX = __NL802154_CCA_OPT_ATTR_AFTER_LAST - 1
+};
+
 #endif /* __NL802154_H */
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index b776d72..ebc5a2e 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -147,6 +147,24 @@
  *	NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO,
  *	NL80211_IFTYPE_P2P_DEVICE. The flag will be set by default if a device
  *	includes any modes unsupported for enforcement checking.
+ * @REGULATORY_WIPHY_SELF_MANAGED: for devices that employ wiphy-specific
+ *	regdom management. These devices will ignore all regdom changes not
+ *	originating from their own wiphy.
+ *	A self-managed wiphys only employs regulatory information obtained from
+ *	the FW and driver and does not use other cfg80211 sources like
+ *	beacon-hints, country-code IEs and hints from other devices on the same
+ *	system. Conversely, a self-managed wiphy does not share its regulatory
+ *	hints with other devices in the system. If a system contains several
+ *	devices, one or more of which are self-managed, there might be
+ *	contradictory regulatory settings between them. Usage of flag is
+ *	generally discouraged. Only use it if the FW/driver is incompatible
+ *	with non-locally originated hints.
+ *	This flag is incompatible with the flags: %REGULATORY_CUSTOM_REG,
+ *	%REGULATORY_STRICT_REG, %REGULATORY_COUNTRY_IE_FOLLOW_POWER,
+ *	%REGULATORY_COUNTRY_IE_IGNORE and %REGULATORY_DISABLE_BEACON_HINTS.
+ *	Mixing any of the above flags with this flag will result in a failure
+ *	to register the wiphy. This flag implies
+ *	%REGULATORY_DISABLE_BEACON_HINTS and %REGULATORY_COUNTRY_IE_IGNORE.
  */
 enum ieee80211_regulatory_flags {
 	REGULATORY_CUSTOM_REG			= BIT(0),
@@ -156,6 +174,7 @@
 	REGULATORY_COUNTRY_IE_IGNORE		= BIT(4),
 	REGULATORY_ENABLE_RELAX_NO_IR           = BIT(5),
 	REGULATORY_IGNORE_STALE_KICKOFF         = BIT(6),
+	REGULATORY_WIPHY_SELF_MANAGED		= BIT(7),
 };
 
 struct ieee80211_freq_range {
diff --git a/include/trace/events/v4l2.h b/include/trace/events/v4l2.h
index b9bb1f2..2011217 100644
--- a/include/trace/events/v4l2.h
+++ b/include/trace/events/v4l2.h
@@ -6,33 +6,58 @@
 
 #include <linux/tracepoint.h>
 
-#define show_type(type)							       \
-	__print_symbolic(type,						       \
-		{ V4L2_BUF_TYPE_VIDEO_CAPTURE,	      "VIDEO_CAPTURE" },       \
-		{ V4L2_BUF_TYPE_VIDEO_OUTPUT,	      "VIDEO_OUTPUT" },	       \
-		{ V4L2_BUF_TYPE_VIDEO_OVERLAY,	      "VIDEO_OVERLAY" },       \
-		{ V4L2_BUF_TYPE_VBI_CAPTURE,	      "VBI_CAPTURE" },	       \
-		{ V4L2_BUF_TYPE_VBI_OUTPUT,	      "VBI_OUTPUT" },	       \
-		{ V4L2_BUF_TYPE_SLICED_VBI_CAPTURE,   "SLICED_VBI_CAPTURE" },  \
-		{ V4L2_BUF_TYPE_SLICED_VBI_OUTPUT,    "SLICED_VBI_OUTPUT" },   \
-		{ V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY, "VIDEO_OUTPUT_OVERLAY" },\
-		{ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, "VIDEO_CAPTURE_MPLANE" },\
-		{ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,  "VIDEO_OUTPUT_MPLANE" }, \
-		{ V4L2_BUF_TYPE_SDR_CAPTURE,          "SDR_CAPTURE" },         \
-		{ V4L2_BUF_TYPE_PRIVATE,	      "PRIVATE" })
+/* Enums require being exported to userspace, for user tool parsing */
+#undef EM
+#undef EMe
+#define EM(a, b)	TRACE_DEFINE_ENUM(a);
+#define EMe(a, b)	TRACE_DEFINE_ENUM(a);
+
+#define show_type(type)							\
+	__print_symbolic(type, SHOW_TYPE)
+
+#define SHOW_TYPE							\
+	EM( V4L2_BUF_TYPE_VIDEO_CAPTURE,	"VIDEO_CAPTURE" )	\
+	EM( V4L2_BUF_TYPE_VIDEO_OUTPUT,		"VIDEO_OUTPUT" )	\
+	EM( V4L2_BUF_TYPE_VIDEO_OVERLAY,	"VIDEO_OVERLAY" )	\
+	EM( V4L2_BUF_TYPE_VBI_CAPTURE,		"VBI_CAPTURE" )		\
+	EM( V4L2_BUF_TYPE_VBI_OUTPUT,		"VBI_OUTPUT" )		\
+	EM( V4L2_BUF_TYPE_SLICED_VBI_CAPTURE,   "SLICED_VBI_CAPTURE" )	\
+	EM( V4L2_BUF_TYPE_SLICED_VBI_OUTPUT,    "SLICED_VBI_OUTPUT" )	\
+	EM( V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY, "VIDEO_OUTPUT_OVERLAY" ) \
+	EM( V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, "VIDEO_CAPTURE_MPLANE" ) \
+	EM( V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,  "VIDEO_OUTPUT_MPLANE" )	\
+	EM( V4L2_BUF_TYPE_SDR_CAPTURE,          "SDR_CAPTURE" )		\
+	EMe(V4L2_BUF_TYPE_PRIVATE,		"PRIVATE" )
+
+SHOW_TYPE
 
 #define show_field(field)						\
-	__print_symbolic(field,						\
-		{ V4L2_FIELD_ANY,		"ANY" },		\
-		{ V4L2_FIELD_NONE,		"NONE" },		\
-		{ V4L2_FIELD_TOP,		"TOP" },		\
-		{ V4L2_FIELD_BOTTOM,		"BOTTOM" },		\
-		{ V4L2_FIELD_INTERLACED,	"INTERLACED" },		\
-		{ V4L2_FIELD_SEQ_TB,		"SEQ_TB" },		\
-		{ V4L2_FIELD_SEQ_BT,		"SEQ_BT" },		\
-		{ V4L2_FIELD_ALTERNATE,		"ALTERNATE" },		\
-		{ V4L2_FIELD_INTERLACED_TB,	"INTERLACED_TB" },      \
-		{ V4L2_FIELD_INTERLACED_BT,	"INTERLACED_BT" })
+	__print_symbolic(field, SHOW_FIELD)
+
+#define SHOW_FIELD							\
+	EM( V4L2_FIELD_ANY,		"ANY" )				\
+	EM( V4L2_FIELD_NONE,		"NONE" )			\
+	EM( V4L2_FIELD_TOP,		"TOP" )				\
+	EM( V4L2_FIELD_BOTTOM,		"BOTTOM" )			\
+	EM( V4L2_FIELD_INTERLACED,	"INTERLACED" )			\
+	EM( V4L2_FIELD_SEQ_TB,		"SEQ_TB" )			\
+	EM( V4L2_FIELD_SEQ_BT,		"SEQ_BT" )			\
+	EM( V4L2_FIELD_ALTERNATE,	"ALTERNATE" )			\
+	EM( V4L2_FIELD_INTERLACED_TB,	"INTERLACED_TB" )		\
+	EMe( V4L2_FIELD_INTERLACED_BT,	"INTERLACED_BT" )
+
+SHOW_FIELD
+
+/*
+ * Now redefine the EM() and EMe() macros to map the enums to the strings
+ * that will be printed in the output.
+ */
+#undef EM
+#undef EMe
+#define EM(a, b)	{a, b},
+#define EMe(a, b)	{a, b}
+
+/* V4L2_TC_TYPE_* are macros, not defines, they do not need processing */
 
 #define show_timecode_type(type)					\
 	__print_symbolic(type,						\
diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h
index 23b4090..190d491 100644
--- a/include/uapi/linux/media-bus-format.h
+++ b/include/uapi/linux/media-bus-format.h
@@ -33,22 +33,32 @@
 
 #define MEDIA_BUS_FMT_FIXED			0x0001
 
-/* RGB - next is	0x100e */
+/* RGB - next is	0x1018 */
+#define MEDIA_BUS_FMT_RGB444_1X12		0x1016
 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE	0x1001
 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE	0x1002
 #define MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE	0x1003
 #define MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE	0x1004
+#define MEDIA_BUS_FMT_RGB565_1X16		0x1017
 #define MEDIA_BUS_FMT_BGR565_2X8_BE		0x1005
 #define MEDIA_BUS_FMT_BGR565_2X8_LE		0x1006
 #define MEDIA_BUS_FMT_RGB565_2X8_BE		0x1007
 #define MEDIA_BUS_FMT_RGB565_2X8_LE		0x1008
 #define MEDIA_BUS_FMT_RGB666_1X18		0x1009
+#define MEDIA_BUS_FMT_RBG888_1X24		0x100e
+#define MEDIA_BUS_FMT_RGB666_1X24_CPADHI	0x1015
+#define MEDIA_BUS_FMT_RGB666_1X7X3_SPWG		0x1010
+#define MEDIA_BUS_FMT_BGR888_1X24		0x1013
+#define MEDIA_BUS_FMT_GBR888_1X24		0x1014
 #define MEDIA_BUS_FMT_RGB888_1X24		0x100a
 #define MEDIA_BUS_FMT_RGB888_2X12_BE		0x100b
 #define MEDIA_BUS_FMT_RGB888_2X12_LE		0x100c
+#define MEDIA_BUS_FMT_RGB888_1X7X4_SPWG		0x1011
+#define MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA	0x1012
 #define MEDIA_BUS_FMT_ARGB8888_1X32		0x100d
+#define MEDIA_BUS_FMT_RGB888_1X32_PADHI		0x100f
 
-/* YUV (including grey) - next is	0x2024 */
+/* YUV (including grey) - next is	0x2026 */
 #define MEDIA_BUS_FMT_Y8_1X8			0x2001
 #define MEDIA_BUS_FMT_UV8_1X8			0x2015
 #define MEDIA_BUS_FMT_UYVY8_1_5X8		0x2002
@@ -65,6 +75,10 @@
 #define MEDIA_BUS_FMT_YUYV10_2X10		0x200b
 #define MEDIA_BUS_FMT_YVYU10_2X10		0x200c
 #define MEDIA_BUS_FMT_Y12_1X12			0x2013
+#define MEDIA_BUS_FMT_UYVY12_2X12		0x201c
+#define MEDIA_BUS_FMT_VYUY12_2X12		0x201d
+#define MEDIA_BUS_FMT_YUYV12_2X12		0x201e
+#define MEDIA_BUS_FMT_YVYU12_2X12		0x201f
 #define MEDIA_BUS_FMT_UYVY8_1X16		0x200f
 #define MEDIA_BUS_FMT_VYUY8_1X16		0x2010
 #define MEDIA_BUS_FMT_YUYV8_1X16		0x2011
@@ -74,16 +88,14 @@
 #define MEDIA_BUS_FMT_VYUY10_1X20		0x201b
 #define MEDIA_BUS_FMT_YUYV10_1X20		0x200d
 #define MEDIA_BUS_FMT_YVYU10_1X20		0x200e
-#define MEDIA_BUS_FMT_YUV10_1X30		0x2016
-#define MEDIA_BUS_FMT_AYUV8_1X32		0x2017
-#define MEDIA_BUS_FMT_UYVY12_2X12		0x201c
-#define MEDIA_BUS_FMT_VYUY12_2X12		0x201d
-#define MEDIA_BUS_FMT_YUYV12_2X12		0x201e
-#define MEDIA_BUS_FMT_YVYU12_2X12		0x201f
+#define MEDIA_BUS_FMT_VUY8_1X24			0x2024
+#define MEDIA_BUS_FMT_YUV8_1X24			0x2025
 #define MEDIA_BUS_FMT_UYVY12_1X24		0x2020
 #define MEDIA_BUS_FMT_VYUY12_1X24		0x2021
 #define MEDIA_BUS_FMT_YUYV12_1X24		0x2022
 #define MEDIA_BUS_FMT_YVYU12_1X24		0x2023
+#define MEDIA_BUS_FMT_YUV10_1X30		0x2016
+#define MEDIA_BUS_FMT_AYUV8_1X32		0x2017
 
 /* Bayer - next is	0x3019 */
 #define MEDIA_BUS_FMT_SBGGR8_1X8		0x3001
diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
index d847c76..4e816be 100644
--- a/include/uapi/linux/media.h
+++ b/include/uapi/linux/media.h
@@ -50,7 +50,14 @@
 #define MEDIA_ENT_T_DEVNODE_V4L		(MEDIA_ENT_T_DEVNODE + 1)
 #define MEDIA_ENT_T_DEVNODE_FB		(MEDIA_ENT_T_DEVNODE + 2)
 #define MEDIA_ENT_T_DEVNODE_ALSA	(MEDIA_ENT_T_DEVNODE + 3)
-#define MEDIA_ENT_T_DEVNODE_DVB		(MEDIA_ENT_T_DEVNODE + 4)
+#define MEDIA_ENT_T_DEVNODE_DVB_FE	(MEDIA_ENT_T_DEVNODE + 4)
+#define MEDIA_ENT_T_DEVNODE_DVB_DEMUX	(MEDIA_ENT_T_DEVNODE + 5)
+#define MEDIA_ENT_T_DEVNODE_DVB_DVR	(MEDIA_ENT_T_DEVNODE + 6)
+#define MEDIA_ENT_T_DEVNODE_DVB_CA	(MEDIA_ENT_T_DEVNODE + 7)
+#define MEDIA_ENT_T_DEVNODE_DVB_NET	(MEDIA_ENT_T_DEVNODE + 8)
+
+/* Legacy symbol. Use it to avoid userspace compilation breakages */
+#define MEDIA_ENT_T_DEVNODE_DVB		MEDIA_ENT_T_DEVNODE_DVB_FE
 
 #define MEDIA_ENT_T_V4L2_SUBDEV		(2 << MEDIA_ENT_TYPE_SHIFT)
 #define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR	(MEDIA_ENT_T_V4L2_SUBDEV + 1)
@@ -59,6 +66,8 @@
 /* A converter of analogue video to its digital representation. */
 #define MEDIA_ENT_T_V4L2_SUBDEV_DECODER	(MEDIA_ENT_T_V4L2_SUBDEV + 4)
 
+#define MEDIA_ENT_T_V4L2_SUBDEV_TUNER	(MEDIA_ENT_T_V4L2_SUBDEV + 5)
+
 #define MEDIA_ENT_FL_DEFAULT		(1 << 0)
 
 struct media_entity_desc {
@@ -78,17 +87,48 @@
 		struct {
 			__u32 major;
 			__u32 minor;
-		} v4l;
-		struct {
-			__u32 major;
-			__u32 minor;
-		} fb;
+		} dev;
+
+#if 1
+		/*
+		 * TODO: this shouldn't have been added without
+		 * actual drivers that use this. When the first real driver
+		 * appears that sets this information, special attention
+		 * should be given whether this information is 1) enough, and
+		 * 2) can deal with udev rules that rename devices. The struct
+		 * dev would not be sufficient for this since that does not
+		 * contain the subdevice information. In addition, struct dev
+		 * can only refer to a single device, and not to multiple (e.g.
+		 * pcm and mixer devices).
+		 *
+		 * So for now mark this as a to do.
+		 */
 		struct {
 			__u32 card;
 			__u32 device;
 			__u32 subdevice;
 		} alsa;
+#endif
+
+#if 1
+		/*
+		 * DEPRECATED: previous node specifications. Kept just to
+		 * avoid breaking compilation, but media_entity_desc.dev
+		 * should be used instead. In particular, alsa and dvb
+		 * fields below are wrong: for all devnodes, there should
+		 * be just major/minor inside the struct, as this is enough
+		 * to represent any devnode, no matter what type.
+		 */
+		struct {
+			__u32 major;
+			__u32 minor;
+		} v4l;
+		struct {
+			__u32 major;
+			__u32 minor;
+		} fb;
 		int dvb;
+#endif
 
 		/* Sub-device specifications */
 		/* Nothing needed yet */
diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h
index 8119255..c1e2e63 100644
--- a/include/uapi/linux/nfc.h
+++ b/include/uapi/linux/nfc.h
@@ -183,6 +183,7 @@
 	NFC_ATTR_SE_APDU,
 	NFC_ATTR_TARGET_ISO15693_DSFID,
 	NFC_ATTR_TARGET_ISO15693_UID,
+	NFC_ATTR_SE_PARAMS,
 /* private: internal use only */
 	__NFC_ATTR_AFTER_LAST
 };
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index b37bd5a..241220c 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -25,10 +25,30 @@
  *
  */
 
+/*
+ * This header file defines the userspace API to the wireless stack. Please
+ * be careful not to break things - i.e. don't move anything around or so
+ * unless you can demonstrate that it breaks neither API nor ABI.
+ *
+ * Additions to the API should be accompanied by actual implementations in
+ * an upstream driver, so that example implementations exist in case there
+ * are ever concerns about the precise semantics of the API or changes are
+ * needed, and to ensure that code for dead (no longer implemented) API
+ * can actually be identified and removed.
+ * Nonetheless, semantics should also be documented carefully in this file.
+ */
+
 #include <linux/types.h>
 
 #define NL80211_GENL_NAME "nl80211"
 
+#define NL80211_MULTICAST_GROUP_CONFIG		"config"
+#define NL80211_MULTICAST_GROUP_SCAN		"scan"
+#define NL80211_MULTICAST_GROUP_REG		"regulatory"
+#define NL80211_MULTICAST_GROUP_MLME		"mlme"
+#define NL80211_MULTICAST_GROUP_VENDOR		"vendor"
+#define NL80211_MULTICAST_GROUP_TESTMODE	"testmode"
+
 /**
  * DOC: Station handling
  *
@@ -173,8 +193,8 @@
  *	%NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME.
  *
  * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration;
- *	either a dump request on a %NL80211_ATTR_WIPHY or a specific get
- *	on an %NL80211_ATTR_IFINDEX is supported.
+ *	either a dump request for all interfaces or a specific get with a
+ *	single %NL80211_ATTR_IFINDEX is supported.
  * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires
  *	%NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE.
  * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response
@@ -252,7 +272,18 @@
  *	%NL80211_ATTR_IFINDEX.
  *
  * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
- * 	regulatory domain.
+ *	regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device
+ *	has a private regulatory domain, it will be returned. Otherwise, the
+ *	global regdomain will be returned.
+ *	A device will have a private regulatory domain if it uses the
+ *	regulatory_hint() API. Even when a private regdomain is used the channel
+ *	information will still be mended according to further hints from
+ *	the regulatory core to help with compliance. A dump version of this API
+ *	is now available which will returns the global regdomain as well as
+ *	all private regdomains of present wiphys (for those that have it).
+ *	If a wiphy is self-managed (%NL80211_ATTR_WIPHY_SELF_MANAGED_REG), then
+ *	its private regdomain is the only valid one for it. The regulatory
+ *	core is not used to help with compliance in this case.
  * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
  *	after being queried by the kernel. CRDA replies by sending a regulatory
  *	domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
@@ -306,7 +337,9 @@
  *	if passed, define which channels should be scanned; if not
  *	passed, all channels allowed for the current regulatory domain
  *	are used.  Extra IEs can also be passed from the userspace by
- *	using the %NL80211_ATTR_IE attribute.
+ *	using the %NL80211_ATTR_IE attribute.  The first cycle of the
+ *	scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
+ *	is supplied.
  * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
  *	scheduled scan is not running. The caller may assume that as soon
  *	as the call returns, it is safe to start a new scheduled scan again.
@@ -774,6 +807,10 @@
  *	peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
  *	when this command completes.
  *
+ * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used
+ *	as an event to indicate changes for devices with wiphy-specific regdom
+ *	management.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -958,6 +995,8 @@
 	NL80211_CMD_TDLS_CHANNEL_SWITCH,
 	NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
 
+	NL80211_CMD_WIPHY_REG_CHANGE,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -1655,6 +1694,13 @@
  * @NL80211_ATTR_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.
+ *	If set during scheduled scan start then the new scan req will be
+ *	owned by the netlink socket that created it and the scheduled scan will
+ *	be stopped when the socket is closed.
+ *	If set during configuration of regulatory indoor operation then the
+ *	regulatory indoor configuration would be owned by the netlink socket
+ *	that configured the indoor setting, and the indoor operation would be
+ *	cleared when the socket is closed.
  *
  * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
  *	the TDLS link initiator.
@@ -1688,6 +1734,33 @@
  *
  * @NL80211_ATTR_MAC_MASK: MAC address mask
  *
+ * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device
+ *	is self-managing its regulatory information and any regulatory domain
+ *	obtained from it is coming from the device's wiphy and not the global
+ *	cfg80211 regdomain.
+ *
+ * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte
+ *	array. The feature flags are identified by their bit index (see &enum
+ *	nl80211_ext_feature_index). The bit index is ordered starting at the
+ *	least-significant bit of the first byte in the array, ie. bit index 0
+ *	is located at bit 0 of byte 0. bit index 25 would be located at bit 1
+ *	of byte 3 (u8 array).
+ *
+ * @NL80211_ATTR_SURVEY_RADIO_STATS: Request overall radio statistics to be
+ *	returned along with other survey data. If set, @NL80211_CMD_GET_SURVEY
+ *	may return a survey entry without a channel indicating global radio
+ *	statistics (only some values are valid and make sense.)
+ *	For devices that don't return such an entry even then, the information
+ *	should be contained in the result as the sum of the respective counters
+ *	over all channels.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
+ *	scheduled scan (or a WoWLAN net-detect scan) is started, u32
+ *	in seconds.
+
+ * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+ *      is operating in an indoor environment.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2045,6 +2118,18 @@
 
 	NL80211_ATTR_MAC_MASK,
 
+	NL80211_ATTR_WIPHY_SELF_MANAGED_REG,
+
+	NL80211_ATTR_EXT_FEATURES,
+
+	NL80211_ATTR_SURVEY_RADIO_STATS,
+
+	NL80211_ATTR_NETNS_FD,
+
+	NL80211_ATTR_SCHED_SCAN_DELAY,
+
+	NL80211_ATTR_REG_INDOOR,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -2085,7 +2170,7 @@
 
 #define NL80211_MAX_SUPP_RATES			32
 #define NL80211_MAX_SUPP_HT_RATES		77
-#define NL80211_MAX_SUPP_REG_RULES		32
+#define NL80211_MAX_SUPP_REG_RULES		64
 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY	0
 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY	16
 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY	24
@@ -2225,8 +2310,15 @@
  * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8)
  * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8)
  * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate
- * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate
+ * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: unused - 80+80 is treated the
+ *	same as 160 for purposes of the bitrates
  * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate
+ * @NL80211_RATE_INFO_10_MHZ_WIDTH: 10 MHz width - note that this is
+ *	a legacy rate and will be reported as the actual bitrate, i.e.
+ *	half the base (20 MHz) rate
+ * @NL80211_RATE_INFO_5_MHZ_WIDTH: 5 MHz width - note that this is
+ *	a legacy rate and will be reported as the actual bitrate, i.e.
+ *	a quarter of the base (20 MHz) rate
  * @__NL80211_RATE_INFO_AFTER_LAST: internal use
  */
 enum nl80211_rate_info {
@@ -2241,6 +2333,8 @@
 	NL80211_RATE_INFO_80_MHZ_WIDTH,
 	NL80211_RATE_INFO_80P80_MHZ_WIDTH,
 	NL80211_RATE_INFO_160_MHZ_WIDTH,
+	NL80211_RATE_INFO_10_MHZ_WIDTH,
+	NL80211_RATE_INFO_5_MHZ_WIDTH,
 
 	/* keep last */
 	__NL80211_RATE_INFO_AFTER_LAST,
@@ -2285,18 +2379,24 @@
  *
  * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved
  * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
- * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
- * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
- * @NL80211_STA_INFO_RX_BYTES64: total received bytes (u64, from this station)
- * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station)
+ * @NL80211_STA_INFO_RX_BYTES: total received bytes (MPDU length)
+ *	(u32, from this station)
+ * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (MPDU length)
+ *	(u32, to this station)
+ * @NL80211_STA_INFO_RX_BYTES64: total received bytes (MPDU length)
+ *	(u64, from this station)
+ * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (MPDU length)
+ *	(u64, to this station)
  * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
  * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
  * 	containing info as possible, see &enum nl80211_rate_info
- * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station)
- * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this
- *	station)
- * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station)
- * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station)
+ * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs)
+ *	(u32, from this station)
+ * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs)
+ *	(u32, to this station)
+ * @NL80211_STA_INFO_TX_RETRIES: total retries (MPDUs) (u32, to this station)
+ * @NL80211_STA_INFO_TX_FAILED: total failed packets (MPDUs)
+ *	(u32, to this station)
  * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm)
  * @NL80211_STA_INFO_LLID: the station's mesh LLID
  * @NL80211_STA_INFO_PLID: the station's mesh PLID
@@ -2320,6 +2420,16 @@
  *	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_RX_DROP_MISC: RX packets dropped for unspecified reasons
+ *	(u64)
+ * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64)
+ * @NL80211_STA_INFO_BEACON_SIGNAL_AVG: signal strength average
+ *	for beacons only (u8, dBm)
+ * @NL80211_STA_INFO_TID_STATS: per-TID statistics (see &enum nl80211_tid_stats)
+ *	This is a nested attribute where each the inner attribute number is the
+ *	TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames;
+ *	each one of those is again nested with &enum nl80211_tid_stats
+ *	attributes carrying the actual values.
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -2352,6 +2462,10 @@
 	NL80211_STA_INFO_CHAIN_SIGNAL,
 	NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
 	NL80211_STA_INFO_EXPECTED_THROUGHPUT,
+	NL80211_STA_INFO_RX_DROP_MISC,
+	NL80211_STA_INFO_BEACON_RX,
+	NL80211_STA_INFO_BEACON_SIGNAL_AVG,
+	NL80211_STA_INFO_TID_STATS,
 
 	/* keep last */
 	__NL80211_STA_INFO_AFTER_LAST,
@@ -2359,6 +2473,31 @@
 };
 
 /**
+ * enum nl80211_tid_stats - per TID statistics attributes
+ * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved
+ * @NL80211_TID_STATS_RX_MSDU: number of MSDUs received (u64)
+ * @NL80211_TID_STATS_TX_MSDU: number of MSDUs transmitted (or
+ *	attempted to transmit; u64)
+ * @NL80211_TID_STATS_TX_MSDU_RETRIES: number of retries for
+ *	transmitted MSDUs (not counting the first attempt; u64)
+ * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
+ *	MSDUs (u64)
+ * @NUM_NL80211_TID_STATS: number of attributes here
+ * @NL80211_TID_STATS_MAX: highest numbered attribute here
+ */
+enum nl80211_tid_stats {
+	__NL80211_TID_STATS_INVALID,
+	NL80211_TID_STATS_RX_MSDU,
+	NL80211_TID_STATS_TX_MSDU,
+	NL80211_TID_STATS_TX_MSDU_RETRIES,
+	NL80211_TID_STATS_TX_MSDU_FAILED,
+
+	/* keep last */
+	NUM_NL80211_TID_STATS,
+	NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1
+};
+
+/**
  * enum nl80211_mpath_flags - nl80211 mesh path flags
  *
  * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
@@ -2772,16 +2911,18 @@
  * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
  * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
  * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used
- * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio
- *	spent on this channel
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary
+ * @NL80211_SURVEY_INFO_TIME: amount of time (in ms) that the radio
+ *	was turned on (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_BUSY: amount of the time the primary
  *	channel was sensed busy (either due to activity or energy detect)
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension
+ * @NL80211_SURVEY_INFO_TIME_EXT_BUSY: amount of time the extension
  *	channel was sensed busy
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent
- *	receiving data
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent
- *	transmitting data
+ * @NL80211_SURVEY_INFO_TIME_RX: amount of time the radio spent
+ *	receiving data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent
+ *	transmitting data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan
+ *	(on this channel or globally)
  * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
  *	currently defined
  * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
@@ -2791,17 +2932,25 @@
 	NL80211_SURVEY_INFO_FREQUENCY,
 	NL80211_SURVEY_INFO_NOISE,
 	NL80211_SURVEY_INFO_IN_USE,
-	NL80211_SURVEY_INFO_CHANNEL_TIME,
-	NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
-	NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
-	NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
-	NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
+	NL80211_SURVEY_INFO_TIME,
+	NL80211_SURVEY_INFO_TIME_BUSY,
+	NL80211_SURVEY_INFO_TIME_EXT_BUSY,
+	NL80211_SURVEY_INFO_TIME_RX,
+	NL80211_SURVEY_INFO_TIME_TX,
+	NL80211_SURVEY_INFO_TIME_SCAN,
 
 	/* keep last */
 	__NL80211_SURVEY_INFO_AFTER_LAST,
 	NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1
 };
 
+/* keep old names for compatibility */
+#define NL80211_SURVEY_INFO_CHANNEL_TIME		NL80211_SURVEY_INFO_TIME
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY		NL80211_SURVEY_INFO_TIME_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY	NL80211_SURVEY_INFO_TIME_EXT_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_RX		NL80211_SURVEY_INFO_TIME_RX
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_TX		NL80211_SURVEY_INFO_TIME_TX
+
 /**
  * enum nl80211_mntr_flags - monitor configuration flags
  *
@@ -2966,7 +3115,8 @@
  *
  * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
  *	established peering with for longer than this time (in seconds), then
- *	remove it from the STA's list of peers.  Default is 30 minutes.
+ *	remove it from the STA's list of peers. You may set this to 0 to disable
+ *	the removal of the STA. Default is 30 minutes.
  *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
  */
@@ -3238,6 +3388,9 @@
 /**
  * enum nl80211_bss_status - BSS "status"
  * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS.
+ *	Note that this is no longer used since cfg80211 no longer
+ *	keeps track of whether or not authentication was done with
+ *	a given BSS.
  * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS.
  * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS.
  *
@@ -3565,6 +3718,8 @@
  * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put
  *	the chip into a special state -- works best with chips that have
  *	support for low-power operation already (flag)
+ *	Note that this mode is incompatible with all of the others, if
+ *	any others are even supported by the device.
  * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect
  *	is detected is implementation-specific (flag)
  * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed
@@ -3621,9 +3776,12 @@
  * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
  *	is detected.  This is a nested attribute that contains the
  *	same attributes used with @NL80211_CMD_START_SCHED_SCAN.  It
- *	specifies how the scan is performed (e.g. the interval and the
- *	channels to scan) as well as the scan results that will
- *	trigger a wake (i.e. the matchsets).
+ *	specifies how the scan is performed (e.g. the interval, the
+ *	channels to scan and the initial delay) as well as the scan
+ *	results that will trigger a wake (i.e. the matchsets).  This
+ *	attribute is also sent in a response to
+ *	@NL80211_CMD_GET_WIPHY, indicating the number of match sets
+ *	supported by the driver (u32).
  * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
  *	containing an array with information about what triggered the
  *	wake up.  If no elements are present in the array, it means
@@ -4194,6 +4352,21 @@
 };
 
 /**
+ * enum nl80211_ext_feature_index - bit index of extended features.
+ * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates.
+ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+enum nl80211_ext_feature_index {
+	NL80211_EXT_FEATURE_VHT_IBSS,
+
+	/* add new features before the definition below */
+	NUM_NL80211_EXT_FEATURES,
+	MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1
+};
+
+/**
  * enum nl80211_probe_resp_offload_support_attr - optional supported
  *	protocols for probe-response offloading by the driver/FW.
  *	To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute.
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index 4a1d0cc..efe3443 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -451,6 +451,10 @@
 #define  PCI_EXP_DEVCTL_AUX_PME	0x0400	/* Auxiliary Power PM Enable */
 #define  PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800  /* Enable No Snoop */
 #define  PCI_EXP_DEVCTL_READRQ	0x7000	/* Max_Read_Request_Size */
+#define  PCI_EXP_DEVCTL_READRQ_128B  0x0000 /* 128 Bytes */
+#define  PCI_EXP_DEVCTL_READRQ_256B  0x1000 /* 256 Bytes */
+#define  PCI_EXP_DEVCTL_READRQ_512B  0x2000 /* 512 Bytes */
+#define  PCI_EXP_DEVCTL_READRQ_1024B 0x3000 /* 1024 Bytes */
 #define  PCI_EXP_DEVCTL_BCR_FLR 0x8000  /* Bridge Configuration Retry / FLR */
 #define PCI_EXP_DEVSTA		10	/* Device Status */
 #define  PCI_EXP_DEVSTA_CED	0x0001	/* Correctable Error Detected */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 661f119..9f6e108 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -170,6 +170,10 @@
  * We reserve 16 controls for this driver. */
 #define V4L2_CID_USER_SAA7134_BASE		(V4L2_CID_USER_BASE + 0x1060)
 
+/* The base for the adv7180 driver controls.
+ * We reserve 16 controls for this driver. */
+#define V4L2_CID_USER_ADV7180_BASE		(V4L2_CID_USER_BASE + 0x1070)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
diff --git a/include/uapi/linux/v4l2-dv-timings.h b/include/uapi/linux/v4l2-dv-timings.h
index 6c8f159..c039f1d 100644
--- a/include/uapi/linux/v4l2-dv-timings.h
+++ b/include/uapi/linux/v4l2-dv-timings.h
@@ -48,14 +48,15 @@
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(720, 480, 1, 0, \
 		13500000, 19, 62, 57, 4, 3, 15, 4, 3, 16, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_HALF_LINE) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_HALF_LINE | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_720X480P59_94 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(720, 480, 0, 0, \
 		27000000, 16, 62, 60, 9, 6, 30, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, 0) \
+		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 /* Note: these are the nominal timings, for HDMI links this format is typically
@@ -64,14 +65,15 @@
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(720, 576, 1, 0, \
 		13500000, 12, 63, 69, 2, 3, 19, 2, 3, 20, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_HALF_LINE) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_HALF_LINE | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_720X576P50 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(720, 576, 0, 0, \
 		27000000, 12, 64, 68, 5, 5, 39, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, 0) \
+		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_1280X720P24 { \
@@ -88,7 +90,7 @@
 	V4L2_INIT_BT_TIMINGS(1280, 720, 0, \
 		V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
 		74250000, 2420, 40, 220, 5, 5, 20, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, 0) \
+		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_1280X720P30 { \
@@ -96,7 +98,8 @@
 	V4L2_INIT_BT_TIMINGS(1280, 720, 0, \
 		V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
 		74250000, 1760, 40, 220, 5, 5, 20, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_1280X720P50 { \
@@ -104,7 +107,7 @@
 	V4L2_INIT_BT_TIMINGS(1280, 720, 0, \
 		V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
 		74250000, 440, 40, 220, 5, 5, 20, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, 0) \
+		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_1280X720P60 { \
@@ -112,7 +115,8 @@
 	V4L2_INIT_BT_TIMINGS(1280, 720, 0, \
 		V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
 		74250000, 110, 40, 220, 5, 5, 20, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_1920X1080P24 { \
@@ -120,7 +124,8 @@
 	V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
 		V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
 		74250000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_1920X1080P25 { \
@@ -128,7 +133,7 @@
 	V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
 		V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
 		74250000, 528, 44, 148, 4, 5, 36, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, 0) \
+		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_1920X1080P30 { \
@@ -136,7 +141,8 @@
 	V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
 		V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
 		74250000, 88, 44, 148, 4, 5, 36, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_1920X1080I50 { \
@@ -144,7 +150,8 @@
 	V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
 		V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
 		74250000, 528, 44, 148, 2, 5, 15, 2, 5, 16, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_HALF_LINE) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_HALF_LINE | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_1920X1080P50 { \
@@ -152,7 +159,7 @@
 	V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
 		V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
 		148500000, 528, 44, 148, 4, 5, 36, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, 0) \
+		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_1920X1080I60 { \
@@ -161,7 +168,8 @@
 		V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
 		74250000, 88, 44, 148, 2, 5, 15, 2, 5, 16, \
 		V4L2_DV_BT_STD_CEA861, \
-		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_HALF_LINE) \
+		V4L2_DV_FL_CAN_REDUCE_FPS | \
+		V4L2_DV_FL_HALF_LINE | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_1920X1080P60 { \
@@ -170,77 +178,83 @@
 		V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
 		148500000, 88, 44, 148, 4, 5, 36, 0, 0, 0, \
 		V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CEA861, \
-		V4L2_DV_FL_CAN_REDUCE_FPS) \
+		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_3840X2160P24 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
 		297000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_3840X2160P25 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
 		297000000, 1056, 88, 296, 8, 10, 72, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, 0) \
+		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_3840X2160P30 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
 		297000000, 176, 88, 296, 8, 10, 72, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_3840X2160P50 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
 		594000000, 1056, 88, 296, 8, 10, 72, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, 0) \
+		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_3840X2160P60 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
 		594000000, 176, 88, 296, 8, 10, 72, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_4096X2160P24 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
 		297000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_4096X2160P25 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
 		297000000, 968, 88, 128, 8, 10, 72, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, 0) \
+		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_4096X2160P30 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
 		297000000, 88, 88, 128, 8, 10, 72, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_4096X2160P50 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
 		594000000, 968, 88, 128, 8, 10, 72, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, 0) \
+		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 #define V4L2_DV_BT_CEA_4096X2160P60 { \
 	.type = V4L2_DV_BT_656_1120, \
 	V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
 		594000000, 88, 88, 128, 8, 10, 72, 0, 0, 0, \
-		V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+		V4L2_DV_BT_STD_CEA861, \
+		V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_IS_CE_VIDEO) \
 }
 
 
diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h
index e0a7e3d..dbce2b5 100644
--- a/include/uapi/linux/v4l2-subdev.h
+++ b/include/uapi/linux/v4l2-subdev.h
@@ -69,12 +69,14 @@
  * @pad: pad number, as reported by the media API
  * @index: format index during enumeration
  * @code: format code (MEDIA_BUS_FMT_ definitions)
+ * @which: format type (from enum v4l2_subdev_format_whence)
  */
 struct v4l2_subdev_mbus_code_enum {
 	__u32 pad;
 	__u32 index;
 	__u32 code;
-	__u32 reserved[9];
+	__u32 which;
+	__u32 reserved[8];
 };
 
 /**
@@ -82,6 +84,7 @@
  * @pad: pad number, as reported by the media API
  * @index: format index during enumeration
  * @code: format code (MEDIA_BUS_FMT_ definitions)
+ * @which: format type (from enum v4l2_subdev_format_whence)
  */
 struct v4l2_subdev_frame_size_enum {
 	__u32 index;
@@ -91,7 +94,8 @@
 	__u32 max_width;
 	__u32 min_height;
 	__u32 max_height;
-	__u32 reserved[9];
+	__u32 which;
+	__u32 reserved[8];
 };
 
 /**
@@ -113,6 +117,7 @@
  * @width: frame width in pixels
  * @height: frame height in pixels
  * @interval: frame interval in seconds
+ * @which: format type (from enum v4l2_subdev_format_whence)
  */
 struct v4l2_subdev_frame_interval_enum {
 	__u32 index;
@@ -121,7 +126,8 @@
 	__u32 width;
 	__u32 height;
 	struct v4l2_fract interval;
-	__u32 reserved[9];
+	__u32 which;
+	__u32 reserved[8];
 };
 
 /**
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 7a26b65..a358724 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -268,9 +268,10 @@
 
 enum v4l2_quantization {
 	/*
-	 * The default for R'G'B' quantization is always full range. For
-	 * Y'CbCr the quantization is always limited range, except for
-	 * SYCC, XV601, XV709 or JPEG: those are full range.
+	 * The default for R'G'B' quantization is always full range, except
+	 * for the BT2020 colorspace. For Y'CbCr the quantization is always
+	 * limited range, except for COLORSPACE_JPEG, SYCC, XV601 or XV709:
+	 * those are full range.
 	 */
 	V4L2_QUANTIZATION_DEFAULT     = 0,
 	V4L2_QUANTIZATION_FULL_RANGE  = 1,
@@ -463,10 +464,11 @@
 #define V4L2_PIX_FMT_SGBRG10 v4l2_fourcc('G', 'B', '1', '0') /* 10  GBGB.. RGRG.. */
 #define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10  GRGR.. BGBG.. */
 #define V4L2_PIX_FMT_SRGGB10 v4l2_fourcc('R', 'G', '1', '0') /* 10  RGRG.. GBGB.. */
-#define V4L2_PIX_FMT_SBGGR12 v4l2_fourcc('B', 'G', '1', '2') /* 12  BGBG.. GRGR.. */
-#define V4L2_PIX_FMT_SGBRG12 v4l2_fourcc('G', 'B', '1', '2') /* 12  GBGB.. RGRG.. */
-#define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12  GRGR.. BGBG.. */
-#define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12  RGRG.. GBGB.. */
+	/* 10bit raw bayer packed, 5 bytes for every 4 pixels */
+#define V4L2_PIX_FMT_SBGGR10P v4l2_fourcc('p', 'B', 'A', 'A')
+#define V4L2_PIX_FMT_SGBRG10P v4l2_fourcc('p', 'G', 'A', 'A')
+#define V4L2_PIX_FMT_SGRBG10P v4l2_fourcc('p', 'g', 'A', 'A')
+#define V4L2_PIX_FMT_SRGGB10P v4l2_fourcc('p', 'R', 'A', 'A')
 	/* 10bit raw bayer a-law compressed to 8 bits */
 #define V4L2_PIX_FMT_SBGGR10ALAW8 v4l2_fourcc('a', 'B', 'A', '8')
 #define V4L2_PIX_FMT_SGBRG10ALAW8 v4l2_fourcc('a', 'G', 'A', '8')
@@ -477,10 +479,10 @@
 #define V4L2_PIX_FMT_SGBRG10DPCM8 v4l2_fourcc('b', 'G', 'A', '8')
 #define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0')
 #define V4L2_PIX_FMT_SRGGB10DPCM8 v4l2_fourcc('b', 'R', 'A', '8')
-	/*
-	 * 10bit raw bayer, expanded to 16 bits
-	 * xxxxrrrrrrrrrrxxxxgggggggggg xxxxggggggggggxxxxbbbbbbbbbb...
-	 */
+#define V4L2_PIX_FMT_SBGGR12 v4l2_fourcc('B', 'G', '1', '2') /* 12  BGBG.. GRGR.. */
+#define V4L2_PIX_FMT_SGBRG12 v4l2_fourcc('G', 'B', '1', '2') /* 12  GBGB.. RGRG.. */
+#define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12  GRGR.. BGBG.. */
+#define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12  RGRG.. GBGB.. */
 #define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16  BGBG.. GRGR.. */
 
 /* compressed formats */
@@ -1186,6 +1188,12 @@
    exactly the same number of half-lines. Whether half-lines can be detected
    or used depends on the hardware. */
 #define V4L2_DV_FL_HALF_LINE			(1 << 3)
+/* If set, then this is a Consumer Electronics (CE) video format. Such formats
+ * differ from other formats (commonly called IT formats) in that if RGB
+ * encoding is used then by default the RGB values use limited range (i.e.
+ * use the range 16-235) as opposed to 0-255. All formats defined in CEA-861
+ * except for the 640x480 format are CE formats. */
+#define V4L2_DV_FL_IS_CE_VIDEO			(1 << 4)
 
 /* A few useful defines to calculate the total blanking and frame sizes */
 #define V4L2_DV_BT_BLANKING_WIDTH(bt) \
@@ -1455,6 +1463,7 @@
 #define V4L2_CTRL_FLAG_WRITE_ONLY 	0x0040
 #define V4L2_CTRL_FLAG_VOLATILE		0x0080
 #define V4L2_CTRL_FLAG_HAS_PAYLOAD	0x0100
+#define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE	0x0200
 
 /*  Query flags, to be ORed with the control ID */
 #define V4L2_CTRL_FLAG_NEXT_CTRL	0x80000000
@@ -1840,8 +1849,8 @@
  */
 struct v4l2_plane_pix_format {
 	__u32		sizeimage;
-	__u16		bytesperline;
-	__u16		reserved[7];
+	__u32		bytesperline;
+	__u16		reserved[6];
 } __attribute__ ((packed));
 
 /**
diff --git a/net/6lowpan/Kconfig b/net/6lowpan/Kconfig
index cb540f5..6ce9d29 100644
--- a/net/6lowpan/Kconfig
+++ b/net/6lowpan/Kconfig
@@ -1,7 +1,70 @@
-config 6LOWPAN
+menuconfig 6LOWPAN
 	tristate "6LoWPAN Support"
 	depends on m
 	depends on IPV6
 	---help---
 	  This enables IPv6 over Low power Wireless Personal Area Network -
 	  "6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks.
+
+menuconfig 6LOWPAN_NHC
+	tristate "Next Header Compression Support"
+	depends on m
+	depends on 6LOWPAN
+	default y
+	---help---
+	  Support for next header compression.
+
+if 6LOWPAN_NHC
+
+config 6LOWPAN_NHC_DEST
+	tristate "Destination Options Header Support"
+	depends on m
+	default y
+	---help---
+	  6LoWPAN IPv6 Destination Options Header compression according to
+	  RFC6282.
+
+config 6LOWPAN_NHC_FRAGMENT
+	tristate "Fragment Header Support"
+	depends on m
+	default y
+	---help---
+	  6LoWPAN IPv6 Fragment Header compression according to RFC6282.
+
+config 6LOWPAN_NHC_HOP
+	tristate "Hop-by-Hop Options Header Support"
+	depends on m
+	default y
+	---help---
+	  6LoWPAN IPv6 Hop-by-Hop Options Header compression according to
+	  RFC6282.
+
+config 6LOWPAN_NHC_IPV6
+	tristate "IPv6 Header Support"
+	depends on m
+	default y
+	---help---
+	  6LoWPAN IPv6 Header compression according to RFC6282.
+
+config 6LOWPAN_NHC_MOBILITY
+	tristate "Mobility Header Support"
+	depends on m
+	default y
+	---help---
+	  6LoWPAN IPv6 Mobility Header compression according to RFC6282.
+
+config 6LOWPAN_NHC_ROUTING
+	tristate "Routing Header Support"
+	depends on m
+	default y
+	---help---
+	  6LoWPAN IPv6 Routing Header compression according to RFC6282.
+
+config 6LOWPAN_NHC_UDP
+	tristate "UDP Header Support"
+	depends on m
+	default y
+	---help---
+	  6LoWPAN IPv6 UDP Header compression according to RFC6282.
+
+endif
diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
index 0f2fd34..e35d2f8 100644
--- a/net/6lowpan/Makefile
+++ b/net/6lowpan/Makefile
@@ -1,3 +1,12 @@
-obj-$(CPTCFG_6LOWPAN) := 6lowpan.o
+obj-$(CPTCFG_6LOWPAN) += 6lowpan.o
 
-6lowpan-y := iphc.o
+6lowpan-y := iphc.o nhc.o
+
+#rfc6282 nhcs
+obj-$(CPTCFG_6LOWPAN_NHC_DEST) += nhc_dest.o
+obj-$(CPTCFG_6LOWPAN_NHC_FRAGMENT) += nhc_fragment.o
+obj-$(CPTCFG_6LOWPAN_NHC_HOP) += nhc_hop.o
+obj-$(CPTCFG_6LOWPAN_NHC_IPV6) += nhc_ipv6.o
+obj-$(CPTCFG_6LOWPAN_NHC_MOBILITY) += nhc_mobility.o
+obj-$(CPTCFG_6LOWPAN_NHC_ROUTING) += nhc_routing.o
+obj-$(CPTCFG_6LOWPAN_NHC_UDP) += nhc_udp.o
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index 32ffec6..94a375c 100644
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -54,6 +54,8 @@
 #include <net/ipv6.h>
 #include <net/af_ieee802154.h>
 
+#include "nhc.h"
+
 /* Uncompress address function for source and
  * destination address(non-multicast).
  *
@@ -224,77 +226,6 @@
 	return 0;
 }
 
-static int uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
-{
-	bool fail;
-	u8 tmp = 0, val = 0;
-
-	fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
-
-	if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
-		pr_debug("UDP header uncompression\n");
-		switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
-		case LOWPAN_NHC_UDP_CS_P_00:
-			fail |= lowpan_fetch_skb(skb, &uh->source,
-						 sizeof(uh->source));
-			fail |= lowpan_fetch_skb(skb, &uh->dest,
-						 sizeof(uh->dest));
-			break;
-		case LOWPAN_NHC_UDP_CS_P_01:
-			fail |= lowpan_fetch_skb(skb, &uh->source,
-						 sizeof(uh->source));
-			fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
-			uh->dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
-			break;
-		case LOWPAN_NHC_UDP_CS_P_10:
-			fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
-			uh->source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
-			fail |= lowpan_fetch_skb(skb, &uh->dest,
-						 sizeof(uh->dest));
-			break;
-		case LOWPAN_NHC_UDP_CS_P_11:
-			fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
-			uh->source = htons(LOWPAN_NHC_UDP_4BIT_PORT +
-					   (val >> 4));
-			uh->dest = htons(LOWPAN_NHC_UDP_4BIT_PORT +
-					 (val & 0x0f));
-			break;
-		default:
-			pr_debug("ERROR: unknown UDP format\n");
-			goto err;
-		}
-
-		pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
-			 ntohs(uh->source), ntohs(uh->dest));
-
-		/* checksum */
-		if (tmp & LOWPAN_NHC_UDP_CS_C) {
-			pr_debug_ratelimited("checksum elided currently not supported\n");
-			goto err;
-		} else {
-			fail |= lowpan_fetch_skb(skb, &uh->check,
-						 sizeof(uh->check));
-		}
-
-		/* UDP length needs to be infered from the lower layers
-		 * here, we obtain the hint from the remaining size of the
-		 * frame
-		 */
-		uh->len = htons(skb->len + sizeof(struct udphdr));
-		pr_debug("uncompressed UDP length: src = %d", ntohs(uh->len));
-	} else {
-		pr_debug("ERROR: unsupported NH format\n");
-		goto err;
-	}
-
-	if (fail)
-		goto err;
-
-	return 0;
-err:
-	return -EINVAL;
-}
-
 /* TTL uncompression values */
 static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
 
@@ -425,29 +356,11 @@
 			return -EINVAL;
 	}
 
-	/* UDP data uncompression */
+	/* Next header data uncompression */
 	if (iphc0 & LOWPAN_IPHC_NH_C) {
-		struct udphdr uh;
-		const int needed = sizeof(struct udphdr) + sizeof(hdr);
-
-		if (uncompress_udp_header(skb, &uh))
-			return -EINVAL;
-
-		/* replace the compressed UDP head by the uncompressed UDP
-		 * header
-		 */
-		err = skb_cow(skb, needed);
-		if (unlikely(err))
+		err = lowpan_nhc_do_uncompression(skb, dev, &hdr);
+		if (err < 0)
 			return err;
-
-		skb_push(skb, sizeof(struct udphdr));
-		skb_reset_transport_header(skb);
-		skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
-
-		raw_dump_table(__func__, "raw UDP header dump",
-			       (u8 *)&uh, sizeof(uh));
-
-		hdr.nexthdr = UIP_PROTO_UDP;
 	} else {
 		err = skb_cow(skb, sizeof(hdr));
 		if (unlikely(err))
@@ -500,71 +413,6 @@
 	return rol8(val, shift);
 }
 
-static void compress_udp_header(u8 **hc_ptr, struct sk_buff *skb)
-{
-	struct udphdr *uh;
-	u8 tmp;
-
-	/* In the case of RAW sockets the transport header is not set by
-	 * the ip6 stack so we must set it ourselves
-	 */
-	if (skb->transport_header == skb->network_header)
-		skb_set_transport_header(skb, sizeof(struct ipv6hdr));
-
-	uh = udp_hdr(skb);
-
-	if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
-	     LOWPAN_NHC_UDP_4BIT_PORT) &&
-	    ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
-	     LOWPAN_NHC_UDP_4BIT_PORT)) {
-		pr_debug("UDP header: both ports compression to 4 bits\n");
-		/* compression value */
-		tmp = LOWPAN_NHC_UDP_CS_P_11;
-		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
-		/* source and destination port */
-		tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
-		      ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
-		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
-	} else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
-			LOWPAN_NHC_UDP_8BIT_PORT) {
-		pr_debug("UDP header: remove 8 bits of dest\n");
-		/* compression value */
-		tmp = LOWPAN_NHC_UDP_CS_P_01;
-		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
-		/* source port */
-		lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
-		/* destination port */
-		tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
-		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
-	} else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
-			LOWPAN_NHC_UDP_8BIT_PORT) {
-		pr_debug("UDP header: remove 8 bits of source\n");
-		/* compression value */
-		tmp = LOWPAN_NHC_UDP_CS_P_10;
-		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
-		/* source port */
-		tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
-		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
-		/* destination port */
-		lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
-	} else {
-		pr_debug("UDP header: can't compress\n");
-		/* compression value */
-		tmp = LOWPAN_NHC_UDP_CS_P_00;
-		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
-		/* source port */
-		lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
-		/* destination port */
-		lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
-	}
-
-	/* checksum is always inline */
-	lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
-
-	/* skip the UDP header */
-	skb_pull(skb, sizeof(struct udphdr));
-}
-
 int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
 			   unsigned short type, const void *_daddr,
 			   const void *_saddr, unsigned int len)
@@ -572,7 +420,7 @@
 	u8 tmp, iphc0, iphc1, *hc_ptr;
 	struct ipv6hdr *hdr;
 	u8 head[100] = {};
-	int addr_type;
+	int ret, addr_type;
 
 	if (type != ETH_P_IPV6)
 		return -EINVAL;
@@ -649,13 +497,12 @@
 
 	/* NOTE: payload length is always compressed */
 
-	/* Next Header is compress if UDP */
-	if (hdr->nexthdr == UIP_PROTO_UDP)
-		iphc0 |= LOWPAN_IPHC_NH_C;
-
-	if ((iphc0 & LOWPAN_IPHC_NH_C) == 0)
-		lowpan_push_hc_data(&hc_ptr, &hdr->nexthdr,
-				    sizeof(hdr->nexthdr));
+	/* Check if we provide the nhc format for nexthdr and compression
+	 * functionality. If not nexthdr is handled inline and not compressed.
+	 */
+	ret = lowpan_nhc_check_compression(skb, hdr, &hc_ptr, &iphc0);
+	if (ret < 0)
+		return ret;
 
 	/* Hop limit
 	 * if 1:   compress, encoding is 01
@@ -741,9 +588,12 @@
 		}
 	}
 
-	/* UDP header compression */
-	if (hdr->nexthdr == UIP_PROTO_UDP)
-		compress_udp_header(&hc_ptr, skb);
+	/* next header compression */
+	if (iphc0 & LOWPAN_IPHC_NH_C) {
+		ret = lowpan_nhc_do_compression(skb, hdr, &hc_ptr);
+		if (ret < 0)
+			return ret;
+	}
 
 	head[0] = iphc0;
 	head[1] = iphc1;
@@ -761,4 +611,18 @@
 }
 EXPORT_SYMBOL_GPL(lowpan_header_compress);
 
+static int __init lowpan_module_init(void)
+{
+	request_module_nowait("nhc_dest");
+	request_module_nowait("nhc_fragment");
+	request_module_nowait("nhc_hop");
+	request_module_nowait("nhc_ipv6");
+	request_module_nowait("nhc_mobility");
+	request_module_nowait("nhc_routing");
+	request_module_nowait("nhc_udp");
+
+	return 0;
+}
+module_init(lowpan_module_init);
+
 MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc.c b/net/6lowpan/nhc.c
new file mode 100644
index 0000000..fd20fc5
--- /dev/null
+++ b/net/6lowpan/nhc.c
@@ -0,0 +1,241 @@
+/*
+ *	6LoWPAN next header compression
+ *
+ *
+ *	Authors:
+ *	Alexander Aring		<aar@pengutronix.de>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/netdevice.h>
+
+#include <net/ipv6.h>
+
+#include "nhc.h"
+
+static struct rb_root rb_root = RB_ROOT;
+static struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX];
+static DEFINE_SPINLOCK(lowpan_nhc_lock);
+
+static int lowpan_nhc_insert(struct lowpan_nhc *nhc)
+{
+	struct rb_node **new = &rb_root.rb_node, *parent = NULL;
+
+	/* Figure out where to put new node */
+	while (*new) {
+		struct lowpan_nhc *this = container_of(*new, struct lowpan_nhc,
+						       node);
+		int result, len_dif, len;
+
+		len_dif = nhc->idlen - this->idlen;
+
+		if (nhc->idlen < this->idlen)
+			len = nhc->idlen;
+		else
+			len = this->idlen;
+
+		result = memcmp(nhc->id, this->id, len);
+		if (!result)
+			result = len_dif;
+
+		parent = *new;
+		if (result < 0)
+			new = &((*new)->rb_left);
+		else if (result > 0)
+			new = &((*new)->rb_right);
+		else
+			return -EEXIST;
+	}
+
+	/* Add new node and rebalance tree. */
+	rb_link_node(&nhc->node, parent, new);
+	rb_insert_color(&nhc->node, &rb_root);
+
+	return 0;
+}
+
+static void lowpan_nhc_remove(struct lowpan_nhc *nhc)
+{
+	rb_erase(&nhc->node, &rb_root);
+}
+
+static struct lowpan_nhc *lowpan_nhc_by_nhcid(const struct sk_buff *skb)
+{
+	struct rb_node *node = rb_root.rb_node;
+	const u8 *nhcid_skb_ptr = skb->data;
+
+	while (node) {
+		struct lowpan_nhc *nhc = container_of(node, struct lowpan_nhc,
+						      node);
+		u8 nhcid_skb_ptr_masked[LOWPAN_NHC_MAX_ID_LEN];
+		int result, i;
+
+		if (nhcid_skb_ptr + nhc->idlen > skb->data + skb->len)
+			return NULL;
+
+		/* copy and mask afterwards the nhid value from skb */
+		memcpy(nhcid_skb_ptr_masked, nhcid_skb_ptr, nhc->idlen);
+		for (i = 0; i < nhc->idlen; i++)
+			nhcid_skb_ptr_masked[i] &= nhc->idmask[i];
+
+		result = memcmp(nhcid_skb_ptr_masked, nhc->id, nhc->idlen);
+		if (result < 0)
+			node = node->rb_left;
+		else if (result > 0)
+			node = node->rb_right;
+		else
+			return nhc;
+	}
+
+	return NULL;
+}
+
+int lowpan_nhc_check_compression(struct sk_buff *skb,
+				 const struct ipv6hdr *hdr, u8 **hc_ptr,
+				 u8 *iphc0)
+{
+	struct lowpan_nhc *nhc;
+
+	spin_lock_bh(&lowpan_nhc_lock);
+
+	nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
+	if (nhc && nhc->compress)
+		*iphc0 |= LOWPAN_IPHC_NH_C;
+	else
+		lowpan_push_hc_data(hc_ptr, &hdr->nexthdr,
+				    sizeof(hdr->nexthdr));
+
+	spin_unlock_bh(&lowpan_nhc_lock);
+
+	return 0;
+}
+
+int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
+			      u8 **hc_ptr)
+{
+	int ret;
+	struct lowpan_nhc *nhc;
+
+	spin_lock_bh(&lowpan_nhc_lock);
+
+	nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
+	/* check if the nhc module was removed in unlocked part.
+	 * TODO: this is a workaround we should prevent unloading
+	 * of nhc modules while unlocked part, this will always drop
+	 * the lowpan packet but it's very unlikely.
+	 *
+	 * Solution isn't easy because we need to decide at
+	 * lowpan_nhc_check_compression if we do a compression or not.
+	 * Because the inline data which is added to skb, we can't move this
+	 * handling.
+	 */
+	if (unlikely(!nhc || !nhc->compress)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* In the case of RAW sockets the transport header is not set by
+	 * the ip6 stack so we must set it ourselves
+	 */
+	if (skb->transport_header == skb->network_header)
+		skb_set_transport_header(skb, sizeof(struct ipv6hdr));
+
+	ret = nhc->compress(skb, hc_ptr);
+	if (ret < 0)
+		goto out;
+
+	/* skip the transport header */
+	skb_pull(skb, nhc->nexthdrlen);
+
+out:
+	spin_unlock_bh(&lowpan_nhc_lock);
+
+	return ret;
+}
+
+int lowpan_nhc_do_uncompression(struct sk_buff *skb, struct net_device *dev,
+				struct ipv6hdr *hdr)
+{
+	struct lowpan_nhc *nhc;
+	int ret;
+
+	spin_lock_bh(&lowpan_nhc_lock);
+
+	nhc = lowpan_nhc_by_nhcid(skb);
+	if (nhc) {
+		if (nhc->uncompress) {
+			ret = nhc->uncompress(skb, sizeof(struct ipv6hdr) +
+					      nhc->nexthdrlen);
+			if (ret < 0) {
+				spin_unlock_bh(&lowpan_nhc_lock);
+				return ret;
+			}
+		} else {
+			spin_unlock_bh(&lowpan_nhc_lock);
+			netdev_warn(dev, "received nhc id for %s which is not implemented.\n",
+				    nhc->name);
+			return -ENOTSUPP;
+		}
+	} else {
+		spin_unlock_bh(&lowpan_nhc_lock);
+		netdev_warn(dev, "received unknown nhc id which was not found.\n");
+		return -ENOENT;
+	}
+
+	hdr->nexthdr = nhc->nexthdr;
+	skb_reset_transport_header(skb);
+	raw_dump_table(__func__, "raw transport header dump",
+		       skb_transport_header(skb), nhc->nexthdrlen);
+
+	spin_unlock_bh(&lowpan_nhc_lock);
+
+	return 0;
+}
+
+int lowpan_nhc_add(struct lowpan_nhc *nhc)
+{
+	int ret;
+
+	if (!nhc->idlen || !nhc->idsetup)
+		return -EINVAL;
+
+	WARN_ONCE(nhc->idlen > LOWPAN_NHC_MAX_ID_LEN,
+		  "LOWPAN_NHC_MAX_ID_LEN should be updated to %zd.\n",
+		  nhc->idlen);
+
+	nhc->idsetup(nhc);
+
+	spin_lock_bh(&lowpan_nhc_lock);
+
+	if (lowpan_nexthdr_nhcs[nhc->nexthdr]) {
+		ret = -EEXIST;
+		goto out;
+	}
+
+	ret = lowpan_nhc_insert(nhc);
+	if (ret < 0)
+		goto out;
+
+	lowpan_nexthdr_nhcs[nhc->nexthdr] = nhc;
+out:
+	spin_unlock_bh(&lowpan_nhc_lock);
+	return ret;
+}
+EXPORT_SYMBOL(lowpan_nhc_add);
+
+void lowpan_nhc_del(struct lowpan_nhc *nhc)
+{
+	spin_lock_bh(&lowpan_nhc_lock);
+
+	lowpan_nhc_remove(nhc);
+	lowpan_nexthdr_nhcs[nhc->nexthdr] = NULL;
+
+	spin_unlock_bh(&lowpan_nhc_lock);
+
+	synchronize_net();
+}
+EXPORT_SYMBOL(lowpan_nhc_del);
diff --git a/net/6lowpan/nhc.h b/net/6lowpan/nhc.h
new file mode 100644
index 0000000..ed44938
--- /dev/null
+++ b/net/6lowpan/nhc.h
@@ -0,0 +1,146 @@
+#ifndef __6LOWPAN_NHC_H
+#define __6LOWPAN_NHC_H
+
+#include <linux/skbuff.h>
+#include <linux/rbtree.h>
+#include <linux/module.h>
+
+#include <net/6lowpan.h>
+#include <net/ipv6.h>
+
+#define LOWPAN_NHC_MAX_ID_LEN	1
+
+/**
+ * LOWPAN_NHC - helper macro to generate nh id fields and lowpan_nhc struct
+ *
+ * @__nhc: variable name of the lowpan_nhc struct.
+ * @_name: const char * of common header compression name.
+ * @_nexthdr: ipv6 nexthdr field for the header compression.
+ * @_nexthdrlen: ipv6 nexthdr len for the reserved space.
+ * @_idsetup: callback to setup id and mask values.
+ * @_idlen: len for the next header id and mask, should be always the same.
+ * @_uncompress: callback for uncompression call.
+ * @_compress: callback for compression call.
+ */
+#define LOWPAN_NHC(__nhc, _name, _nexthdr,	\
+		   _hdrlen, _idsetup, _idlen,	\
+		   _uncompress, _compress)	\
+static u8 __nhc##_val[_idlen];			\
+static u8 __nhc##_mask[_idlen];			\
+static struct lowpan_nhc __nhc = {		\
+	.name		= _name,		\
+	.nexthdr	= _nexthdr,		\
+	.nexthdrlen	= _hdrlen,		\
+	.id		= __nhc##_val,		\
+	.idmask		= __nhc##_mask,		\
+	.idlen		= _idlen,		\
+	.idsetup	= _idsetup,		\
+	.uncompress	= _uncompress,		\
+	.compress	= _compress,		\
+}
+
+#define module_lowpan_nhc(__nhc)		\
+static int __init __nhc##_init(void)		\
+{						\
+	return lowpan_nhc_add(&(__nhc));	\
+}						\
+module_init(__nhc##_init);			\
+static void __exit __nhc##_exit(void)		\
+{						\
+	lowpan_nhc_del(&(__nhc));		\
+}						\
+module_exit(__nhc##_exit);
+
+/**
+ * struct lowpan_nhc - hold 6lowpan next hdr compression ifnformation
+ *
+ * @node: holder for the rbtree.
+ * @name: name of the specific next header compression
+ * @nexthdr: next header value of the protocol which should be compressed.
+ * @nexthdrlen: ipv6 nexthdr len for the reserved space.
+ * @id: array for nhc id. Note this need to be in network byteorder.
+ * @mask: array for nhc id mask. Note this need to be in network byteorder.
+ * @len: the length of the next header id and mask.
+ * @setup: callback to setup fill the next header id value and mask.
+ * @compress: callback to do the header compression.
+ * @uncompress: callback to do the header uncompression.
+ */
+struct lowpan_nhc {
+	struct rb_node	node;
+	const char	*name;
+	const u8	nexthdr;
+	const size_t	nexthdrlen;
+	u8		*id;
+	u8		*idmask;
+	const size_t	idlen;
+
+	void		(*idsetup)(struct lowpan_nhc *nhc);
+	int		(*uncompress)(struct sk_buff *skb, size_t needed);
+	int		(*compress)(struct sk_buff *skb, u8 **hc_ptr);
+};
+
+/**
+ * lowpan_nhc_by_nexthdr - return the 6lowpan nhc by ipv6 nexthdr.
+ *
+ * @nexthdr: ipv6 nexthdr value.
+ */
+struct lowpan_nhc *lowpan_nhc_by_nexthdr(u8 nexthdr);
+
+/**
+ * lowpan_nhc_check_compression - checks if we support compression format. If
+ *	we support the nhc by nexthdr field, the 6LoWPAN iphc NHC bit will be
+ *	set. If we don't support nexthdr will be added as inline data to the
+ *	6LoWPAN header.
+ *
+ * @skb: skb of 6LoWPAN header to read nhc and replace header.
+ * @hdr: ipv6hdr to check the nexthdr value
+ * @hc_ptr: pointer for 6LoWPAN header which should increment at the end of
+ *	    replaced header.
+ * @iphc0: iphc0 pointer to set the 6LoWPAN NHC bit
+ */
+int lowpan_nhc_check_compression(struct sk_buff *skb,
+				 const struct ipv6hdr *hdr, u8 **hc_ptr,
+				 u8 *iphc0);
+
+/**
+ * lowpan_nhc_do_compression - calling compress callback for nhc
+ *
+ * @skb: skb of 6LoWPAN header to read nhc and replace header.
+ * @hdr: ipv6hdr to set the nexthdr value
+ * @hc_ptr: pointer for 6LoWPAN header which should increment at the end of
+ *	    replaced header.
+ */
+int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
+			      u8 **hc_ptr);
+
+/**
+ * lowpan_nhc_do_uncompression - calling uncompress callback for nhc
+ *
+ * @nhc: 6LoWPAN nhc context, get by lowpan_nhc_by_ functions.
+ * @skb: skb of 6LoWPAN header, skb->data should be pointed to nhc id value.
+ * @dev: netdevice for print logging information.
+ * @hdr: ipv6hdr for setting nexthdr value.
+ */
+int lowpan_nhc_do_uncompression(struct sk_buff *skb, struct net_device *dev,
+				struct ipv6hdr *hdr);
+
+/**
+ * lowpan_nhc_add - register a next header compression to framework
+ *
+ * @nhc: nhc which should be add.
+ */
+int lowpan_nhc_add(struct lowpan_nhc *nhc);
+
+/**
+ * lowpan_nhc_del - delete a next header compression from framework
+ *
+ * @nhc: nhc which should be delete.
+ */
+void lowpan_nhc_del(struct lowpan_nhc *nhc);
+
+/**
+ * lowpan_nhc_init - adding all default nhcs
+ */
+void lowpan_nhc_init(void);
+
+#endif /* __6LOWPAN_NHC_H */
diff --git a/net/6lowpan/nhc_dest.c b/net/6lowpan/nhc_dest.c
new file mode 100644
index 0000000..0b292c9
--- /dev/null
+++ b/net/6lowpan/nhc_dest.c
@@ -0,0 +1,28 @@
+/*
+ *	6LoWPAN IPv6 Destination Options Header compression according to
+ *	RFC6282
+ *
+ *	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 "nhc.h"
+
+#define LOWPAN_NHC_DEST_IDLEN	1
+#define LOWPAN_NHC_DEST_ID_0	0xe6
+#define LOWPAN_NHC_DEST_MASK_0	0xfe
+
+static void dest_nhid_setup(struct lowpan_nhc *nhc)
+{
+	nhc->id[0] = LOWPAN_NHC_DEST_ID_0;
+	nhc->idmask[0] = LOWPAN_NHC_DEST_MASK_0;
+}
+
+LOWPAN_NHC(nhc_dest, "RFC6282 Destination Options", NEXTHDR_DEST, 0,
+	   dest_nhid_setup, LOWPAN_NHC_DEST_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(nhc_dest);
+MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Destination Options compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_fragment.c b/net/6lowpan/nhc_fragment.c
new file mode 100644
index 0000000..473dbc5
--- /dev/null
+++ b/net/6lowpan/nhc_fragment.c
@@ -0,0 +1,27 @@
+/*
+ *	6LoWPAN IPv6 Fragment Header compression according to RFC6282
+ *
+ *	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 "nhc.h"
+
+#define LOWPAN_NHC_FRAGMENT_IDLEN	1
+#define LOWPAN_NHC_FRAGMENT_ID_0	0xe4
+#define LOWPAN_NHC_FRAGMENT_MASK_0	0xfe
+
+static void fragment_nhid_setup(struct lowpan_nhc *nhc)
+{
+	nhc->id[0] = LOWPAN_NHC_FRAGMENT_ID_0;
+	nhc->idmask[0] = LOWPAN_NHC_FRAGMENT_MASK_0;
+}
+
+LOWPAN_NHC(nhc_fragment, "RFC6282 Fragment", NEXTHDR_FRAGMENT, 0,
+	   fragment_nhid_setup, LOWPAN_NHC_FRAGMENT_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(nhc_fragment);
+MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Fragment compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_hop.c b/net/6lowpan/nhc_hop.c
new file mode 100644
index 0000000..1eb66be
--- /dev/null
+++ b/net/6lowpan/nhc_hop.c
@@ -0,0 +1,27 @@
+/*
+ *	6LoWPAN IPv6 Hop-by-Hop Options Header compression according to RFC6282
+ *
+ *	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 "nhc.h"
+
+#define LOWPAN_NHC_HOP_IDLEN	1
+#define LOWPAN_NHC_HOP_ID_0	0xe0
+#define LOWPAN_NHC_HOP_MASK_0	0xfe
+
+static void hop_nhid_setup(struct lowpan_nhc *nhc)
+{
+	nhc->id[0] = LOWPAN_NHC_HOP_ID_0;
+	nhc->idmask[0] = LOWPAN_NHC_HOP_MASK_0;
+}
+
+LOWPAN_NHC(nhc_hop, "RFC6282 Hop-by-Hop Options", NEXTHDR_HOP, 0,
+	   hop_nhid_setup, LOWPAN_NHC_HOP_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(nhc_hop);
+MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Hop-by-Hop Options compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_ipv6.c b/net/6lowpan/nhc_ipv6.c
new file mode 100644
index 0000000..2313d16
--- /dev/null
+++ b/net/6lowpan/nhc_ipv6.c
@@ -0,0 +1,27 @@
+/*
+ *	6LoWPAN IPv6 Header compression according to RFC6282
+ *
+ *	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 "nhc.h"
+
+#define LOWPAN_NHC_IPV6_IDLEN	1
+#define LOWPAN_NHC_IPV6_ID_0	0xee
+#define LOWPAN_NHC_IPV6_MASK_0	0xfe
+
+static void ipv6_nhid_setup(struct lowpan_nhc *nhc)
+{
+	nhc->id[0] = LOWPAN_NHC_IPV6_ID_0;
+	nhc->idmask[0] = LOWPAN_NHC_IPV6_MASK_0;
+}
+
+LOWPAN_NHC(nhc_ipv6, "RFC6282 IPv6", NEXTHDR_IPV6, 0, ipv6_nhid_setup,
+	   LOWPAN_NHC_IPV6_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(nhc_ipv6);
+MODULE_DESCRIPTION("6LoWPAN next header RFC6282 IPv6 compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_mobility.c b/net/6lowpan/nhc_mobility.c
new file mode 100644
index 0000000..60d3f38
--- /dev/null
+++ b/net/6lowpan/nhc_mobility.c
@@ -0,0 +1,27 @@
+/*
+ *	6LoWPAN IPv6 Mobility Header compression according to RFC6282
+ *
+ *	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 "nhc.h"
+
+#define LOWPAN_NHC_MOBILITY_IDLEN	1
+#define LOWPAN_NHC_MOBILITY_ID_0	0xe8
+#define LOWPAN_NHC_MOBILITY_MASK_0	0xfe
+
+static void mobility_nhid_setup(struct lowpan_nhc *nhc)
+{
+	nhc->id[0] = LOWPAN_NHC_MOBILITY_ID_0;
+	nhc->idmask[0] = LOWPAN_NHC_MOBILITY_MASK_0;
+}
+
+LOWPAN_NHC(nhc_mobility, "RFC6282 Mobility", NEXTHDR_MOBILITY, 0,
+	   mobility_nhid_setup, LOWPAN_NHC_MOBILITY_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(nhc_mobility);
+MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Mobility compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_routing.c b/net/6lowpan/nhc_routing.c
new file mode 100644
index 0000000..c393280
--- /dev/null
+++ b/net/6lowpan/nhc_routing.c
@@ -0,0 +1,27 @@
+/*
+ *	6LoWPAN IPv6 Routing Header compression according to RFC6282
+ *
+ *	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 "nhc.h"
+
+#define LOWPAN_NHC_ROUTING_IDLEN	1
+#define LOWPAN_NHC_ROUTING_ID_0		0xe2
+#define LOWPAN_NHC_ROUTING_MASK_0	0xfe
+
+static void routing_nhid_setup(struct lowpan_nhc *nhc)
+{
+	nhc->id[0] = LOWPAN_NHC_ROUTING_ID_0;
+	nhc->idmask[0] = LOWPAN_NHC_ROUTING_MASK_0;
+}
+
+LOWPAN_NHC(nhc_routing, "RFC6282 Routing", NEXTHDR_ROUTING, 0,
+	   routing_nhid_setup, LOWPAN_NHC_ROUTING_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(nhc_routing);
+MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Routing compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_udp.c b/net/6lowpan/nhc_udp.c
new file mode 100644
index 0000000..c6bcaeb
--- /dev/null
+++ b/net/6lowpan/nhc_udp.c
@@ -0,0 +1,157 @@
+/*
+ *	6LoWPAN IPv6 UDP compression according to RFC6282
+ *
+ *
+ *	Authors:
+ *	Alexander Aring	<aar@pengutronix.de>
+ *
+ *	Orignal written by:
+ *	Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ *	Jon Smirl <jonsmirl@gmail.com>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include "nhc.h"
+
+#define LOWPAN_NHC_UDP_IDLEN	1
+
+static int udp_uncompress(struct sk_buff *skb, size_t needed)
+{
+	u8 tmp = 0, val = 0;
+	struct udphdr uh;
+	bool fail;
+	int err;
+
+	fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
+
+	pr_debug("UDP header uncompression\n");
+	switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
+	case LOWPAN_NHC_UDP_CS_P_00:
+		fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
+		fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
+		break;
+	case LOWPAN_NHC_UDP_CS_P_01:
+		fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
+		fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
+		uh.dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
+		break;
+	case LOWPAN_NHC_UDP_CS_P_10:
+		fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
+		uh.source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
+		fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
+		break;
+	case LOWPAN_NHC_UDP_CS_P_11:
+		fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
+		uh.source = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val >> 4));
+		uh.dest = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val & 0x0f));
+		break;
+	default:
+		BUG();
+	}
+
+	pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
+		 ntohs(uh.source), ntohs(uh.dest));
+
+	/* checksum */
+	if (tmp & LOWPAN_NHC_UDP_CS_C) {
+		pr_debug_ratelimited("checksum elided currently not supported\n");
+		fail = true;
+	} else {
+		fail |= lowpan_fetch_skb(skb, &uh.check, sizeof(uh.check));
+	}
+
+	if (fail)
+		return -EINVAL;
+
+	/* UDP length needs to be infered from the lower layers
+	 * here, we obtain the hint from the remaining size of the
+	 * frame
+	 */
+	uh.len = htons(skb->len + sizeof(struct udphdr));
+	pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
+
+	/* replace the compressed UDP head by the uncompressed UDP
+	 * header
+	 */
+	err = skb_cow(skb, needed);
+	if (unlikely(err))
+		return err;
+
+	skb_push(skb, sizeof(struct udphdr));
+	skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
+
+	return 0;
+}
+
+static int udp_compress(struct sk_buff *skb, u8 **hc_ptr)
+{
+	const struct udphdr *uh = udp_hdr(skb);
+	u8 tmp;
+
+	if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+	     LOWPAN_NHC_UDP_4BIT_PORT) &&
+	    ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+	     LOWPAN_NHC_UDP_4BIT_PORT)) {
+		pr_debug("UDP header: both ports compression to 4 bits\n");
+		/* compression value */
+		tmp = LOWPAN_NHC_UDP_CS_P_11;
+		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+		/* source and destination port */
+		tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
+		      ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
+		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+	} else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+			LOWPAN_NHC_UDP_8BIT_PORT) {
+		pr_debug("UDP header: remove 8 bits of dest\n");
+		/* compression value */
+		tmp = LOWPAN_NHC_UDP_CS_P_01;
+		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+		/* source port */
+		lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
+		/* destination port */
+		tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
+		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+	} else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+			LOWPAN_NHC_UDP_8BIT_PORT) {
+		pr_debug("UDP header: remove 8 bits of source\n");
+		/* compression value */
+		tmp = LOWPAN_NHC_UDP_CS_P_10;
+		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+		/* source port */
+		tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
+		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+		/* destination port */
+		lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
+	} else {
+		pr_debug("UDP header: can't compress\n");
+		/* compression value */
+		tmp = LOWPAN_NHC_UDP_CS_P_00;
+		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+		/* source port */
+		lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
+		/* destination port */
+		lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
+	}
+
+	/* checksum is always inline */
+	lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
+
+	return 0;
+}
+
+static void udp_nhid_setup(struct lowpan_nhc *nhc)
+{
+	nhc->id[0] = LOWPAN_NHC_UDP_ID;
+	nhc->idmask[0] = LOWPAN_NHC_UDP_MASK;
+}
+
+LOWPAN_NHC(nhc_udp, "RFC6282 UDP", NEXTHDR_UDP, sizeof(struct udphdr),
+	   udp_nhid_setup, LOWPAN_NHC_UDP_IDLEN, udp_uncompress, udp_compress);
+
+module_lowpan_nhc(nhc_udp);
+MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression");
+MODULE_LICENSE("GPL");
diff --git a/net/Kconfig b/net/Kconfig
index ff9ffc1..44dd578 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -231,18 +231,18 @@
 source "net/switchdev/Kconfig"
 
 config RPS
-	boolean
+	bool
 	depends on SMP && SYSFS
 	default y
 
 config RFS_ACCEL
-	boolean
+	bool
 	depends on RPS
 	select CPU_RMAP
 	default y
 
 config XPS
-	boolean
+	bool
 	depends on SMP
 	default y
 
@@ -254,18 +254,18 @@
 	  a per-interface basis.
 
 config CGROUP_NET_CLASSID
-	boolean "Network classid cgroup"
+	bool "Network classid cgroup"
 	depends on CGROUPS
 	---help---
 	  Cgroup subsystem for use as general purpose socket classid marker that is
 	  being used in cls_cgroup and for netfilter matching.
 
 config NET_RX_BUSY_POLL
-	boolean
+	bool
 	default y
 
 config BQL
-	boolean
+	bool
 	depends on SYSFS
 	select DQL
 	default y
@@ -282,7 +282,7 @@
 	  this feature changing /proc/sys/net/core/bpf_jit_enable
 
 config NET_FLOW_LIMIT
-	boolean
+	bool
 	depends on RPS
 	default y
 	---help---
diff --git a/net/Makefile b/net/Makefile
index 76f0349..c0393b7 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -69,7 +69,7 @@
 obj-$(CPTCFG_NFC)		+= nfc/
 obj-$(CONFIG_OPENVSWITCH)	+= openvswitch/
 obj-$(CONFIG_VSOCKETS)	+= vmw_vsock/
-obj-$(CONFIG_NET_MPLS_GSO)	+= mpls/
+obj-$(CONFIG_MPLS)		+= mpls/
 obj-$(CONFIG_HSR)		+= hsr/
 ifneq ($(CONFIG_NET_SWITCHDEV),)
 obj-y				+= switchdev/
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 8ea2d68..6ee2471 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -32,7 +32,7 @@
 
 #define VERSION "0.1"
 
-static struct dentry *lowpan_psm_debugfs;
+static struct dentry *lowpan_enable_debugfs;
 static struct dentry *lowpan_control_debugfs;
 
 #define IFACE_NAME_TEMPLATE "bt%d"
@@ -56,11 +56,7 @@
 static LIST_HEAD(bt_6lowpan_devices);
 static DEFINE_SPINLOCK(devices_lock);
 
-/* If psm is set to 0 (default value), then 6lowpan is disabled.
- * Other values are used to indicate a Protocol Service Multiplexer
- * value for 6lowpan.
- */
-static u16 psm_6lowpan;
+static bool enable_6lowpan;
 
 /* We are listening incoming connections via this channel
  */
@@ -391,7 +387,6 @@
 
 drop:
 	dev->stats.rx_dropped++;
-	kfree_skb(skb);
 	return NET_RX_DROP;
 }
 
@@ -772,7 +767,7 @@
 	if (hcon->type != LE_LINK)
 		return false;
 
-	if (!psm_6lowpan)
+	if (!enable_6lowpan)
 		return false;
 
 	return true;
@@ -1060,7 +1055,9 @@
 	.suspend		= chan_suspend_cb,
 	.get_sndtimeo		= chan_get_sndtimeo_cb,
 	.alloc_skb		= chan_alloc_skb_cb,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
 	.memcpy_fromiovec	= l2cap_chan_no_memcpy_fromiovec,
+#endif
 
 	.teardown		= l2cap_chan_no_teardown,
 	.defer			= l2cap_chan_no_defer,
@@ -1097,7 +1094,7 @@
 	if (!pchan)
 		return -EINVAL;
 
-	err = l2cap_chan_connect(pchan, cpu_to_le16(psm_6lowpan), 0,
+	err = l2cap_chan_connect(pchan, cpu_to_le16(L2CAP_PSM_IPSP), 0,
 				 addr, dst_type);
 
 	BT_DBG("chan %p err %d", pchan, err);
@@ -1130,7 +1127,7 @@
 	struct l2cap_chan *pchan;
 	int err;
 
-	if (psm_6lowpan == 0)
+	if (!enable_6lowpan)
 		return NULL;
 
 	pchan = chan_get();
@@ -1142,10 +1139,9 @@
 
 	atomic_set(&pchan->nesting, L2CAP_NESTING_PARENT);
 
-	BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan,
-	       pchan->src_type);
+	BT_DBG("chan %p src type %d", pchan, pchan->src_type);
 
-	err = l2cap_add_psm(pchan, addr, cpu_to_le16(psm_6lowpan));
+	err = l2cap_add_psm(pchan, addr, cpu_to_le16(L2CAP_PSM_IPSP));
 	if (err) {
 		l2cap_chan_put(pchan);
 		BT_ERR("psm cannot be added err %d", err);
@@ -1231,22 +1227,23 @@
 	spin_unlock(&devices_lock);
 }
 
-struct set_psm {
+struct set_enable {
 	struct work_struct work;
-	u16 psm;
+	bool flag;
 };
 
-static void do_psm_set(struct work_struct *work)
+static void do_enable_set(struct work_struct *work)
 {
-	struct set_psm *set_psm = container_of(work, struct set_psm, work);
+	struct set_enable *set_enable = container_of(work,
+						     struct set_enable, work);
 
-	if (set_psm->psm == 0 || psm_6lowpan != set_psm->psm)
+	if (!set_enable->flag || enable_6lowpan != set_enable->flag)
 		/* Disconnect existing connections if 6lowpan is
-		 * disabled (psm = 0), or if psm changes.
+		 * disabled
 		 */
 		disconnect_all_peers();
 
-	psm_6lowpan = set_psm->psm;
+	enable_6lowpan = set_enable->flag;
 
 	if (listen_chan) {
 		l2cap_chan_close(listen_chan, 0);
@@ -1255,33 +1252,33 @@
 
 	listen_chan = bt_6lowpan_listen();
 
-	kfree(set_psm);
+	kfree(set_enable);
 }
 
-static int lowpan_psm_set(void *data, u64 val)
+static int lowpan_enable_set(void *data, u64 val)
 {
-	struct set_psm *set_psm;
+	struct set_enable *set_enable;
 
-	set_psm = kzalloc(sizeof(*set_psm), GFP_KERNEL);
-	if (!set_psm)
+	set_enable = kzalloc(sizeof(*set_enable), GFP_KERNEL);
+	if (!set_enable)
 		return -ENOMEM;
 
-	set_psm->psm = val;
-	INIT_WORK(&set_psm->work, do_psm_set);
+	set_enable->flag = !!val;
+	INIT_WORK(&set_enable->work, do_enable_set);
 
-	schedule_work(&set_psm->work);
+	schedule_work(&set_enable->work);
 
 	return 0;
 }
 
-static int lowpan_psm_get(void *data, u64 *val)
+static int lowpan_enable_get(void *data, u64 *val)
 {
-	*val = psm_6lowpan;
+	*val = enable_6lowpan;
 	return 0;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(lowpan_psm_fops, lowpan_psm_get,
-			lowpan_psm_set, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(lowpan_enable_fops, lowpan_enable_get,
+			lowpan_enable_set, "%llu\n");
 
 static ssize_t lowpan_control_write(struct file *fp,
 				    const char __user *user_buffer,
@@ -1451,9 +1448,9 @@
 
 static int __init bt_6lowpan_init(void)
 {
-	lowpan_psm_debugfs = debugfs_create_file("6lowpan_psm", 0644,
-						 bt_debugfs, NULL,
-						 &lowpan_psm_fops);
+	lowpan_enable_debugfs = debugfs_create_file("6lowpan_enable", 0644,
+						    bt_debugfs, NULL,
+						    &lowpan_enable_fops);
 	lowpan_control_debugfs = debugfs_create_file("6lowpan_control", 0644,
 						     bt_debugfs, NULL,
 						     &lowpan_control_fops);
@@ -1463,7 +1460,7 @@
 
 static void __exit bt_6lowpan_exit(void)
 {
-	debugfs_remove(lowpan_psm_debugfs);
+	debugfs_remove(lowpan_enable_debugfs);
 	debugfs_remove(lowpan_control_debugfs);
 
 	if (listen_chan) {
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index a68caf3..5f99800 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -66,4 +66,39 @@
 	help
 	  IPv6 compression over Bluetooth Low Energy.
 
+config BT_SELFTEST
+	bool "Bluetooth self testing support"
+	depends on BT && DEBUG_KERNEL
+	help
+	  Run self tests when initializing the Bluetooth subsystem.  This
+	  is a developer option and can cause significant delay when booting
+	  the system.
+
+	  When the Bluetooth subsystem is built as module, then the test
+	  cases are run first thing at module load time.  When the Bluetooth
+	  subsystem is compiled into the kernel image, then the test cases
+	  are run late in the initcall hierarchy.
+
+config BT_SELFTEST_ECDH
+	bool "ECDH test cases"
+	depends on BT_LE && BT_SELFTEST
+	help
+	  Run test cases for ECDH cryptographic functionality used by the
+	  Bluetooth Low Energy Secure Connections feature.
+
+config BT_SELFTEST_SMP
+	bool "SMP test cases"
+	depends on BT_LE && BT_SELFTEST
+	help
+	  Run test cases for SMP cryptographic functionality, including both
+	  legacy SMP as well as the Secure Connections features.
+
+config BT_DEBUGFS
+	bool "Export Bluetooth internals in debugfs"
+	depends on BT && DEBUG_FS
+	default y
+	help
+	  Provide extensive information about internal Bluetooth states
+	  in debugfs.
+
 source "drivers/bluetooth/Kconfig"
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index e3474dd..4f5f745 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -13,6 +13,9 @@
 
 bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
 	hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
-	a2mp.o amp.o ecc.o
+	a2mp.o amp.o ecc.o hci_request.o mgmt_util.o
+
+bluetooth-$(CPTCFG_BT_DEBUGFS) += hci_debugfs.o
+bluetooth-$(CPTCFG_BT_SELFTEST) += selftest.o
 
 subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 6fcb9bf..869037d 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -19,9 +19,11 @@
 #include "a2mp.h"
 #include "amp.h"
 
+#define A2MP_FEAT_EXT	0x8000
+
 /* Global AMP Manager list */
-LIST_HEAD(amp_mgr_list);
-DEFINE_MUTEX(amp_mgr_list_lock);
+static LIST_HEAD(amp_mgr_list);
+static DEFINE_MUTEX(amp_mgr_list_lock);
 
 /* A2MP build & send command helper functions */
 static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
@@ -43,7 +45,7 @@
 	return cmd;
 }
 
-void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data)
+static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data)
 {
 	struct l2cap_chan *chan = mgr->a2mp_chan;
 	struct a2mp_cmd *cmd;
@@ -72,7 +74,7 @@
 	kfree(cmd);
 }
 
-u8 __next_ident(struct amp_mgr *mgr)
+static u8 __next_ident(struct amp_mgr *mgr)
 {
 	if (++mgr->ident == 0)
 		mgr->ident = 1;
@@ -80,6 +82,23 @@
 	return mgr->ident;
 }
 
+static struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
+{
+	struct amp_mgr *mgr;
+
+	mutex_lock(&amp_mgr_list_lock);
+	list_for_each_entry(mgr, &amp_mgr_list, list) {
+		if (test_and_clear_bit(state, &mgr->state)) {
+			amp_mgr_get(mgr);
+			mutex_unlock(&amp_mgr_list_lock);
+			return mgr;
+		}
+	}
+	mutex_unlock(&amp_mgr_list_lock);
+
+	return NULL;
+}
+
 /* hci_dev_list shall be locked */
 static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl)
 {
@@ -724,7 +743,9 @@
 	.resume = l2cap_chan_no_resume,
 	.set_shutdown = l2cap_chan_no_set_shutdown,
 	.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
 	.memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
+#endif
 };
 
 static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
@@ -866,23 +887,6 @@
 	return mgr->a2mp_chan;
 }
 
-struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
-{
-	struct amp_mgr *mgr;
-
-	mutex_lock(&amp_mgr_list_lock);
-	list_for_each_entry(mgr, &amp_mgr_list, list) {
-		if (test_and_clear_bit(state, &mgr->state)) {
-			amp_mgr_get(mgr);
-			mutex_unlock(&amp_mgr_list_lock);
-			return mgr;
-		}
-	}
-	mutex_unlock(&amp_mgr_list_lock);
-
-	return NULL;
-}
-
 void a2mp_send_getinfo_rsp(struct hci_dev *hdev)
 {
 	struct amp_mgr *mgr;
diff --git a/net/bluetooth/a2mp.h b/net/bluetooth/a2mp.h
index 487b54c..296f665 100644
--- a/net/bluetooth/a2mp.h
+++ b/net/bluetooth/a2mp.h
@@ -17,8 +17,6 @@
 
 #include <net/bluetooth/l2cap.h>
 
-#define A2MP_FEAT_EXT	0x8000
-
 enum amp_mgr_state {
 	READ_LOC_AMP_INFO,
 	READ_LOC_AMP_ASSOC,
@@ -131,16 +129,10 @@
 #define A2MP_STATUS_PHYS_LINK_EXISTS		0x05
 #define A2MP_STATUS_SECURITY_VIOLATION		0x06
 
-extern struct list_head amp_mgr_list;
-extern struct mutex amp_mgr_list_lock;
-
 struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr);
 int amp_mgr_put(struct amp_mgr *mgr);
-u8 __next_ident(struct amp_mgr *mgr);
 struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
 				       struct sk_buff *skb);
-struct amp_mgr *amp_mgr_lookup_by_state(u8 state);
-void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data);
 void a2mp_discover_amp(struct l2cap_chan *chan);
 void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
 void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 012e3b0..1827f77 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -31,6 +31,8 @@
 #include <net/bluetooth/bluetooth.h>
 #include <linux/proc_fs.h>
 
+#include "selftest.h"
+
 #define VERSION "2.20"
 
 /* Bluetooth sockets */
@@ -208,8 +210,13 @@
 }
 EXPORT_SYMBOL(bt_accept_dequeue);
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+		    int flags)
+#else
 int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 				struct msghdr *msg, size_t len, int flags)
+#endif
 {
 	int noblock = flags & MSG_DONTWAIT;
 	struct sock *sk = sock->sk;
@@ -281,8 +288,13 @@
 	return timeo;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+int bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg,
+			   size_t size, int flags)
+#else
 int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 			       struct msghdr *msg, size_t size, int flags)
+#endif
 {
 	struct sock *sk = sock->sk;
 	int err = 0;
@@ -709,13 +721,16 @@
 
 static int __init bt_init(void)
 {
-	struct sk_buff *skb;
 	int err;
 
-	BUILD_BUG_ON(sizeof(struct bt_skb_cb) > sizeof(skb->cb));
+	sock_skb_cb_check_size(sizeof(struct bt_skb_cb));
 
 	BT_INFO("Core ver %s", VERSION);
 
+	err = bt_selftest();
+	if (err < 0)
+		return err;
+
 	bt_debugfs = debugfs_create_dir("bluetooth", NULL);
 
 	err = bt_sysfs_init();
@@ -744,6 +759,13 @@
 		goto sock_err;
 	}
 
+	err = mgmt_init();
+	if (err < 0) {
+		sco_exit();
+		l2cap_exit();
+		goto sock_err;
+	}
+
 	return 0;
 
 sock_err:
@@ -758,6 +780,8 @@
 
 static void __exit bt_exit(void)
 {
+	mgmt_exit();
+
 	sco_exit();
 
 	l2cap_exit();
diff --git a/net/bluetooth/bnep/bnep.h b/net/bluetooth/bnep/bnep.h
index 5a5b16f..40854c9 100644
--- a/net/bluetooth/bnep/bnep.h
+++ b/net/bluetooth/bnep/bnep.h
@@ -111,6 +111,10 @@
 #define BNEPCONNDEL	_IOW('B', 201, int)
 #define BNEPGETCONNLIST	_IOR('B', 210, int)
 #define BNEPGETCONNINFO	_IOR('B', 211, int)
+#define BNEPGETSUPPFEAT	_IOR('B', 212, int)
+
+#define BNEP_SETUP_RESPONSE	0
+#define BNEP_SETUP_RSP_SENT	10
 
 struct bnep_connadd_req {
 	int   sock;		/* Connected socket */
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 4a00a91..220c898 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -231,7 +231,14 @@
 		break;
 
 	case BNEP_SETUP_CONN_REQ:
-		err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED);
+		/* Successful response should be sent only once */
+		if (test_bit(BNEP_SETUP_RESPONSE, &s->flags) &&
+		    !test_and_set_bit(BNEP_SETUP_RSP_SENT, &s->flags))
+			err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
+					    BNEP_SUCCESS);
+		else
+			err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
+					    BNEP_CONN_NOT_ALLOWED);
 		break;
 
 	default: {
@@ -239,7 +246,7 @@
 			pkt[0] = BNEP_CONTROL;
 			pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
 			pkt[2] = cmd;
-			bnep_send(s, pkt, sizeof(pkt));
+			err = bnep_send(s, pkt, sizeof(pkt));
 		}
 		break;
 	}
@@ -292,30 +299,56 @@
 {
 	struct net_device *dev = s->dev;
 	struct sk_buff *nskb;
-	u8 type;
+	u8 type, ctrl_type;
 
 	dev->stats.rx_bytes += skb->len;
 
 	type = *(u8 *) skb->data;
 	skb_pull(skb, 1);
+	ctrl_type = *(u8 *)skb->data;
 
 	if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
 		goto badframe;
 
 	if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
-		bnep_rx_control(s, skb->data, skb->len);
-		kfree_skb(skb);
-		return 0;
+		if (bnep_rx_control(s, skb->data, skb->len) < 0) {
+			dev->stats.tx_errors++;
+			kfree_skb(skb);
+			return 0;
+		}
+
+		if (!(type & BNEP_EXT_HEADER)) {
+			kfree_skb(skb);
+			return 0;
+		}
+
+		/* Verify and pull ctrl message since it's already processed */
+		switch (ctrl_type) {
+		case BNEP_SETUP_CONN_REQ:
+			/* Pull: ctrl type (1 b), len (1 b), data (len bytes) */
+			if (!skb_pull(skb, 2 + *(u8 *)(skb->data + 1) * 2))
+				goto badframe;
+			break;
+		case BNEP_FILTER_MULTI_ADDR_SET:
+		case BNEP_FILTER_NET_TYPE_SET:
+			/* Pull: ctrl type (1 b), len (2 b), data (len bytes) */
+			if (!skb_pull(skb, 3 + *(u16 *)(skb->data + 1) * 2))
+				goto badframe;
+			break;
+		default:
+			kfree_skb(skb);
+			return 0;
+		}
+	} else {
+		skb_reset_mac_header(skb);
+
+		/* Verify and pull out header */
+		if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
+			goto badframe;
+
+		s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
 	}
 
-	skb_reset_mac_header(skb);
-
-	/* Verify and pull out header */
-	if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
-		goto badframe;
-
-	s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
-
 	if (type & BNEP_EXT_HEADER) {
 		if (bnep_rx_extension(s, skb) < 0)
 			goto badframe;
@@ -511,13 +544,12 @@
 
 static struct device *bnep_get_device(struct bnep_session *session)
 {
-	struct hci_conn *conn;
+	struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn;
 
-	conn = l2cap_pi(session->sock->sk)->chan->conn->hcon;
-	if (!conn)
+	if (!conn || !conn->hcon)
 		return NULL;
 
-	return &conn->dev;
+	return &conn->hcon->dev;
 }
 
 static struct device_type bnep_type = {
@@ -526,6 +558,7 @@
 
 int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
 {
+	u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
 	struct net_device *dev;
 	struct bnep_session *s, *ss;
 	u8 dst[ETH_ALEN], src[ETH_ALEN];
@@ -533,6 +566,12 @@
 
 	BT_DBG("");
 
+	if (!l2cap_is_socket(sock))
+		return -EBADFD;
+
+	if (req->flags & ~valid_flags)
+		return -EINVAL;
+
 	baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
 	baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
 
@@ -564,6 +603,7 @@
 	s->sock  = sock;
 	s->role  = req->role;
 	s->state = BT_CONNECTED;
+	s->flags = req->flags;
 
 	s->msg.msg_flags = MSG_NOSIGNAL;
 
@@ -609,11 +649,15 @@
 
 int bnep_del_connection(struct bnep_conndel_req *req)
 {
+	u32 valid_flags = 0;
 	struct bnep_session *s;
 	int  err = 0;
 
 	BT_DBG("");
 
+	if (req->flags & ~valid_flags)
+		return -EINVAL;
+
 	down_read(&bnep_session_sem);
 
 	s = __bnep_get_session(req->dst);
@@ -629,10 +673,12 @@
 
 static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
 {
+	u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
+
 	memset(ci, 0, sizeof(*ci));
 	memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
 	strcpy(ci->device, s->dev->name);
-	ci->flags = s->flags;
+	ci->flags = s->flags & valid_flags;
 	ci->state = s->state;
 	ci->role  = s->role;
 }
diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c
index 95e7c18..949034e 100644
--- a/net/bluetooth/bnep/netdev.c
+++ b/net/bluetooth/bnep/netdev.c
@@ -218,7 +218,7 @@
 void bnep_net_setup(struct net_device *dev)
 {
 
-	memset(dev->broadcast, 0xff, ETH_ALEN);
+	eth_broadcast_addr(dev->broadcast);
 	dev->addr_len = ETH_ALEN;
 
 	ether_setup(dev);
diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c
index 5f05129..bde2bdd 100644
--- a/net/bluetooth/bnep/sock.c
+++ b/net/bluetooth/bnep/sock.c
@@ -57,6 +57,7 @@
 	struct bnep_conninfo ci;
 	struct socket *nsock;
 	void __user *argp = (void __user *)arg;
+	__u32 supp_feat = BIT(BNEP_SETUP_RESPONSE);
 	int err;
 
 	BT_DBG("cmd %x arg %lx", cmd, arg);
@@ -120,6 +121,12 @@
 
 		return err;
 
+	case BNEPGETSUPPFEAT:
+		if (copy_to_user(argp, &supp_feat, sizeof(supp_feat)))
+			return -EFAULT;
+
+		return 0;
+
 	default:
 		return -EINVAL;
 	}
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
index 1ca8a87..b0c6c6a 100644
--- a/net/bluetooth/cmtp/capi.c
+++ b/net/bluetooth/cmtp/capi.c
@@ -253,8 +253,6 @@
 			if (skb->len < CAPI_MSG_BASELEN + 15)
 				break;
 
-			controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
-
 			if (!info && ctrl) {
 				int len = min_t(uint, CAPI_MANUFACTURER_LEN,
 						skb->data[CAPI_MSG_BASELEN + 14]);
@@ -270,8 +268,6 @@
 			if (skb->len < CAPI_MSG_BASELEN + 32)
 				break;
 
-			controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
-
 			if (!info && ctrl) {
 				ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
 				ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
@@ -285,8 +281,6 @@
 			if (skb->len < CAPI_MSG_BASELEN + 17)
 				break;
 
-			controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
-
 			if (!info && ctrl) {
 				int len = min_t(uint, CAPI_SERIAL_LEN,
 						skb->data[CAPI_MSG_BASELEN + 16]);
@@ -339,7 +333,7 @@
 		return;
 	}
 
-	if (session->flags & (1 << CMTP_LOOPBACK)) {
+	if (session->flags & BIT(CMTP_LOOPBACK)) {
 		kfree_skb(skb);
 		return;
 	}
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index 67fe5e8..298ed37 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -75,10 +75,11 @@
 
 static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
 {
+	u32 valid_flags = BIT(CMTP_LOOPBACK);
 	memset(ci, 0, sizeof(*ci));
 	bacpy(&ci->bdaddr, &session->bdaddr);
 
-	ci->flags = session->flags;
+	ci->flags = session->flags & valid_flags;
 	ci->state = session->state;
 
 	ci->num = session->num;
@@ -313,7 +314,7 @@
 
 	down_write(&cmtp_session_sem);
 
-	if (!(session->flags & (1 << CMTP_LOOPBACK)))
+	if (!(session->flags & BIT(CMTP_LOOPBACK)))
 		cmtp_detach_device(session);
 
 	fput(session->sock->file);
@@ -329,11 +330,18 @@
 
 int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
 {
+	u32 valid_flags = BIT(CMTP_LOOPBACK);
 	struct cmtp_session *session, *s;
 	int i, err;
 
 	BT_DBG("");
 
+	if (!l2cap_is_socket(sock))
+		return -EBADFD;
+
+	if (req->flags & ~valid_flags)
+		return -EINVAL;
+
 	session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL);
 	if (!session)
 		return -ENOMEM;
@@ -382,7 +390,7 @@
 		goto unlink;
 	}
 
-	if (!(session->flags & (1 << CMTP_LOOPBACK))) {
+	if (!(session->flags & BIT(CMTP_LOOPBACK))) {
 		err = cmtp_attach_device(session);
 		if (err < 0) {
 			atomic_inc(&session->terminate);
@@ -406,11 +414,15 @@
 
 int cmtp_del_connection(struct cmtp_conndel_req *req)
 {
+	u32 valid_flags = 0;
 	struct cmtp_session *session;
 	int err = 0;
 
 	BT_DBG("");
 
+	if (req->flags & ~valid_flags)
+		return -EINVAL;
+
 	down_read(&cmtp_session_sem);
 
 	session = __cmtp_get_session(&req->bdaddr);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index fe18825..ee5e598 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -25,11 +25,13 @@
 /* Bluetooth HCI connection handling. */
 
 #include <linux/export.h>
+#include <linux/debugfs.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
 
+#include "hci_request.h"
 #include "smp.h"
 #include "a2mp.h"
 
@@ -307,7 +309,7 @@
 		else
 			hci_add_sco(sco, conn->handle);
 	} else {
-		hci_proto_connect_cfm(sco, status);
+		hci_connect_cfm(sco, status);
 		hci_conn_del(sco);
 	}
 }
@@ -546,6 +548,8 @@
 
 	hci_conn_del_sysfs(conn);
 
+	debugfs_remove_recursive(conn->debugfs);
+
 	if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
 		hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
 
@@ -567,7 +571,7 @@
 
 	list_for_each_entry(d, &hci_dev_list, list) {
 		if (!test_bit(HCI_UP, &d->flags) ||
-		    test_bit(HCI_USER_CHANNEL, &d->dev_flags) ||
+		    hci_dev_test_flag(d, HCI_USER_CHANNEL) ||
 		    d->dev_type != HCI_BREDR)
 			continue;
 
@@ -614,7 +618,7 @@
 	mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type,
 			    status);
 
-	hci_proto_connect_cfm(conn, status);
+	hci_connect_cfm(conn, status);
 
 	hci_conn_del(conn);
 
@@ -629,7 +633,7 @@
 	mgmt_reenable_advertising(hdev);
 }
 
-static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
+static void create_le_conn_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
 	struct hci_conn *conn;
 
@@ -696,7 +700,7 @@
 	 * 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_LE_ADV, &hdev->dev_flags);
+	hci_dev_clear_flag(hdev, HCI_LE_ADV);
 
 	/* Set require_privacy to false so that the remote device has a
 	 * chance of identifying us.
@@ -729,6 +733,14 @@
 	struct hci_request req;
 	int err;
 
+	/* Let's make sure that le is enabled.*/
+	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
+		if (lmp_le_capable(hdev))
+			return ERR_PTR(-ECONNREFUSED);
+
+		return ERR_PTR(-EOPNOTSUPP);
+	}
+
 	/* 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
@@ -787,7 +799,7 @@
 	 * anyway have to disable it in order to start directed
 	 * advertising.
 	 */
-	if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
 		u8 enable = 0x00;
 		hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
 			    &enable);
@@ -798,7 +810,7 @@
 		/* If we're active scanning most controllers are unable
 		 * to initiate advertising. Simply reject the attempt.
 		 */
-		if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
+		if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
 		    hdev->le_scan_type == LE_SCAN_ACTIVE) {
 			skb_queue_purge(&req.cmd_q);
 			hci_conn_del(conn);
@@ -828,9 +840,9 @@
 	 * handler for scan disabling knows to set the correct discovery
 	 * state.
 	 */
-	if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
 		hci_req_add_le_scan_disable(&req);
-		set_bit(HCI_LE_SCAN_INTERRUPTED, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_LE_SCAN_INTERRUPTED);
 	}
 
 	hci_req_add_le_create_conn(&req, conn);
@@ -852,8 +864,12 @@
 {
 	struct hci_conn *acl;
 
-	if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
+		if (lmp_bredr_capable(hdev))
+			return ERR_PTR(-ECONNREFUSED);
+
 		return ERR_PTR(-EOPNOTSUPP);
+	}
 
 	acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
 	if (!acl) {
@@ -926,7 +942,7 @@
 	 * Connections is used and the link is encrypted with AES-CCM
 	 * using a P-256 authenticated combination key.
 	 */
-	if (test_bit(HCI_SC_ONLY, &conn->hdev->flags)) {
+	if (hci_dev_test_flag(conn->hdev, HCI_SC_ONLY)) {
 		if (!hci_conn_sc_enabled(conn) ||
 		    !test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
 		    conn->key_type != HCI_LK_AUTH_COMBINATION_P256)
@@ -1080,21 +1096,6 @@
 }
 EXPORT_SYMBOL(hci_conn_check_secure);
 
-/* Change link key */
-int hci_conn_change_link_key(struct hci_conn *conn)
-{
-	BT_DBG("hcon %p", conn);
-
-	if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
-		struct hci_cp_change_conn_link_key cp;
-		cp.handle = cpu_to_le16(conn->handle);
-		hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY,
-			     sizeof(cp), &cp);
-	}
-
-	return 0;
-}
-
 /* Switch role */
 int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
 {
@@ -1150,7 +1151,7 @@
 	list_for_each_entry_safe(c, n, &h->list, list) {
 		c->state = BT_CLOSED;
 
-		hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
+		hci_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
 		hci_conn_del(c);
 	}
 }
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 5dcacf9..476709b 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -37,6 +37,8 @@
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/mgmt.h>
 
+#include "hci_request.h"
+#include "hci_debugfs.h"
 #include "smp.h"
 
 static void hci_rx_work(struct work_struct *work);
@@ -49,7 +51,7 @@
 
 /* HCI callback list */
 LIST_HEAD(hci_cb_list);
-DEFINE_RWLOCK(hci_cb_list_lock);
+DEFINE_MUTEX(hci_cb_list_lock);
 
 /* HCI ID Numbering */
 static DEFINE_IDA(hci_index_ida);
@@ -78,7 +80,7 @@
 	struct hci_dev *hdev = file->private_data;
 	char buf[3];
 
-	buf[0] = test_bit(HCI_DUT_MODE, &hdev->dbg_flags) ? 'Y': 'N';
+	buf[0] = hci_dev_test_flag(hdev, HCI_DUT_MODE) ? 'Y': 'N';
 	buf[1] = '\n';
 	buf[2] = '\0';
 	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
@@ -104,7 +106,7 @@
 	if (strtobool(buf, &enable))
 		return -EINVAL;
 
-	if (enable == test_bit(HCI_DUT_MODE, &hdev->dbg_flags))
+	if (enable == hci_dev_test_flag(hdev, HCI_DUT_MODE))
 		return -EALREADY;
 
 	hci_req_lock(hdev);
@@ -125,7 +127,7 @@
 	if (err < 0)
 		return err;
 
-	change_bit(HCI_DUT_MODE, &hdev->dbg_flags);
+	hci_dev_change_flag(hdev, HCI_DUT_MODE);
 
 	return count;
 }
@@ -137,947 +139,18 @@
 	.llseek		= default_llseek,
 };
 
-static int features_show(struct seq_file *f, void *ptr)
-{
-	struct hci_dev *hdev = f->private;
-	u8 p;
-
-	hci_dev_lock(hdev);
-	for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) {
-		seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
-			   "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p,
-			   hdev->features[p][0], hdev->features[p][1],
-			   hdev->features[p][2], hdev->features[p][3],
-			   hdev->features[p][4], hdev->features[p][5],
-			   hdev->features[p][6], hdev->features[p][7]);
-	}
-	if (lmp_le_capable(hdev))
-		seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
-			   "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
-			   hdev->le_features[0], hdev->le_features[1],
-			   hdev->le_features[2], hdev->le_features[3],
-			   hdev->le_features[4], hdev->le_features[5],
-			   hdev->le_features[6], hdev->le_features[7]);
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int features_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, features_show, inode->i_private);
-}
-
-static const struct file_operations features_fops = {
-	.open		= features_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static int blacklist_show(struct seq_file *f, void *p)
-{
-	struct hci_dev *hdev = f->private;
-	struct bdaddr_list *b;
-
-	hci_dev_lock(hdev);
-	list_for_each_entry(b, &hdev->blacklist, list)
-		seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int blacklist_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, blacklist_show, inode->i_private);
-}
-
-static const struct file_operations blacklist_fops = {
-	.open		= blacklist_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static int uuids_show(struct seq_file *f, void *p)
-{
-	struct hci_dev *hdev = f->private;
-	struct bt_uuid *uuid;
-
-	hci_dev_lock(hdev);
-	list_for_each_entry(uuid, &hdev->uuids, list) {
-		u8 i, val[16];
-
-		/* The Bluetooth UUID values are stored in big endian,
-		 * but with reversed byte order. So convert them into
-		 * the right order for the %pUb modifier.
-		 */
-		for (i = 0; i < 16; i++)
-			val[i] = uuid->uuid[15 - i];
-
-		seq_printf(f, "%pUb\n", val);
-	}
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int uuids_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, uuids_show, inode->i_private);
-}
-
-static const struct file_operations uuids_fops = {
-	.open		= uuids_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static int inquiry_cache_show(struct seq_file *f, void *p)
-{
-	struct hci_dev *hdev = f->private;
-	struct discovery_state *cache = &hdev->discovery;
-	struct inquiry_entry *e;
-
-	hci_dev_lock(hdev);
-
-	list_for_each_entry(e, &cache->all, all) {
-		struct inquiry_data *data = &e->data;
-		seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
-			   &data->bdaddr,
-			   data->pscan_rep_mode, data->pscan_period_mode,
-			   data->pscan_mode, data->dev_class[2],
-			   data->dev_class[1], data->dev_class[0],
-			   __le16_to_cpu(data->clock_offset),
-			   data->rssi, data->ssp_mode, e->timestamp);
-	}
-
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int inquiry_cache_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, inquiry_cache_show, inode->i_private);
-}
-
-static const struct file_operations inquiry_cache_fops = {
-	.open		= inquiry_cache_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static int link_keys_show(struct seq_file *f, void *ptr)
-{
-	struct hci_dev *hdev = f->private;
-	struct link_key *key;
-
-	rcu_read_lock();
-	list_for_each_entry_rcu(key, &hdev->link_keys, list)
-		seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
-			   HCI_LINK_KEY_SIZE, key->val, key->pin_len);
-	rcu_read_unlock();
-
-	return 0;
-}
-
-static int link_keys_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, link_keys_show, inode->i_private);
-}
-
-static const struct file_operations link_keys_fops = {
-	.open		= link_keys_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static int dev_class_show(struct seq_file *f, void *ptr)
-{
-	struct hci_dev *hdev = f->private;
-
-	hci_dev_lock(hdev);
-	seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
-		   hdev->dev_class[1], hdev->dev_class[0]);
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int dev_class_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, dev_class_show, inode->i_private);
-}
-
-static const struct file_operations dev_class_fops = {
-	.open		= dev_class_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static int voice_setting_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->voice_setting;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get,
-			NULL, "0x%4.4llx\n");
-
-static int auto_accept_delay_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	hdev->auto_accept_delay = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int auto_accept_delay_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->auto_accept_delay;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
-			auto_accept_delay_set, "%llu\n");
-
-static ssize_t force_sc_support_read(struct file *file, char __user *user_buf,
-				     size_t count, loff_t *ppos)
-{
-	struct hci_dev *hdev = file->private_data;
-	char buf[3];
-
-	buf[0] = test_bit(HCI_FORCE_SC, &hdev->dbg_flags) ? 'Y': 'N';
-	buf[1] = '\n';
-	buf[2] = '\0';
-	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static ssize_t force_sc_support_write(struct file *file,
-				      const char __user *user_buf,
-				      size_t count, loff_t *ppos)
-{
-	struct hci_dev *hdev = file->private_data;
-	char buf[32];
-	size_t buf_size = min(count, (sizeof(buf)-1));
-	bool enable;
-
-	if (test_bit(HCI_UP, &hdev->flags))
-		return -EBUSY;
-
-	if (copy_from_user(buf, user_buf, buf_size))
-		return -EFAULT;
-
-	buf[buf_size] = '\0';
-	if (strtobool(buf, &enable))
-		return -EINVAL;
-
-	if (enable == test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
-		return -EALREADY;
-
-	change_bit(HCI_FORCE_SC, &hdev->dbg_flags);
-
-	return count;
-}
-
-static const struct file_operations force_sc_support_fops = {
-	.open		= simple_open,
-	.read		= force_sc_support_read,
-	.write		= force_sc_support_write,
-	.llseek		= default_llseek,
-};
-
-static ssize_t force_lesc_support_read(struct file *file, char __user *user_buf,
-				       size_t count, loff_t *ppos)
-{
-	struct hci_dev *hdev = file->private_data;
-	char buf[3];
-
-	buf[0] = test_bit(HCI_FORCE_LESC, &hdev->dbg_flags) ? 'Y': 'N';
-	buf[1] = '\n';
-	buf[2] = '\0';
-	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static ssize_t force_lesc_support_write(struct file *file,
-					const char __user *user_buf,
-					size_t count, loff_t *ppos)
-{
-	struct hci_dev *hdev = file->private_data;
-	char buf[32];
-	size_t buf_size = min(count, (sizeof(buf)-1));
-	bool enable;
-
-	if (copy_from_user(buf, user_buf, buf_size))
-		return -EFAULT;
-
-	buf[buf_size] = '\0';
-	if (strtobool(buf, &enable))
-		return -EINVAL;
-
-	if (enable == test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
-		return -EALREADY;
-
-	change_bit(HCI_FORCE_LESC, &hdev->dbg_flags);
-
-	return count;
-}
-
-static const struct file_operations force_lesc_support_fops = {
-	.open		= simple_open,
-	.read		= force_lesc_support_read,
-	.write		= force_lesc_support_write,
-	.llseek		= default_llseek,
-};
-
-static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
-				 size_t count, loff_t *ppos)
-{
-	struct hci_dev *hdev = file->private_data;
-	char buf[3];
-
-	buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N';
-	buf[1] = '\n';
-	buf[2] = '\0';
-	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static const struct file_operations sc_only_mode_fops = {
-	.open		= simple_open,
-	.read		= sc_only_mode_read,
-	.llseek		= default_llseek,
-};
-
-static int idle_timeout_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	if (val != 0 && (val < 500 || val > 3600000))
-		return -EINVAL;
-
-	hci_dev_lock(hdev);
-	hdev->idle_timeout = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int idle_timeout_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->idle_timeout;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
-			idle_timeout_set, "%llu\n");
-
-static int rpa_timeout_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	/* Require the RPA timeout to be at least 30 seconds and at most
-	 * 24 hours.
-	 */
-	if (val < 30 || val > (60 * 60 * 24))
-		return -EINVAL;
-
-	hci_dev_lock(hdev);
-	hdev->rpa_timeout = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int rpa_timeout_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->rpa_timeout;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(rpa_timeout_fops, rpa_timeout_get,
-			rpa_timeout_set, "%llu\n");
-
-static int sniff_min_interval_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
-		return -EINVAL;
-
-	hci_dev_lock(hdev);
-	hdev->sniff_min_interval = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int sniff_min_interval_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->sniff_min_interval;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get,
-			sniff_min_interval_set, "%llu\n");
-
-static int sniff_max_interval_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
-		return -EINVAL;
-
-	hci_dev_lock(hdev);
-	hdev->sniff_max_interval = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int sniff_max_interval_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->sniff_max_interval;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-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;
-	bdaddr_t addr;
-	u8 addr_type;
-
-	hci_dev_lock(hdev);
-
-	hci_copy_identity_address(hdev, &addr, &addr_type);
-
-	seq_printf(f, "%pMR (type %u) %*phN %pMR\n", &addr, addr_type,
-		   16, hdev->irk, &hdev->rpa);
-
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int identity_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, identity_show, inode->i_private);
-}
-
-static const struct file_operations identity_fops = {
-	.open		= identity_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static int random_address_show(struct seq_file *f, void *p)
-{
-	struct hci_dev *hdev = f->private;
-
-	hci_dev_lock(hdev);
-	seq_printf(f, "%pMR\n", &hdev->random_addr);
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int random_address_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, random_address_show, inode->i_private);
-}
-
-static const struct file_operations random_address_fops = {
-	.open		= random_address_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static int static_address_show(struct seq_file *f, void *p)
-{
-	struct hci_dev *hdev = f->private;
-
-	hci_dev_lock(hdev);
-	seq_printf(f, "%pMR\n", &hdev->static_addr);
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int static_address_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, static_address_show, inode->i_private);
-}
-
-static const struct file_operations static_address_fops = {
-	.open		= static_address_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static ssize_t force_static_address_read(struct file *file,
-					 char __user *user_buf,
-					 size_t count, loff_t *ppos)
-{
-	struct hci_dev *hdev = file->private_data;
-	char buf[3];
-
-	buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ? 'Y': 'N';
-	buf[1] = '\n';
-	buf[2] = '\0';
-	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static ssize_t force_static_address_write(struct file *file,
-					  const char __user *user_buf,
-					  size_t count, loff_t *ppos)
-{
-	struct hci_dev *hdev = file->private_data;
-	char buf[32];
-	size_t buf_size = min(count, (sizeof(buf)-1));
-	bool enable;
-
-	if (test_bit(HCI_UP, &hdev->flags))
-		return -EBUSY;
-
-	if (copy_from_user(buf, user_buf, buf_size))
-		return -EFAULT;
-
-	buf[buf_size] = '\0';
-	if (strtobool(buf, &enable))
-		return -EINVAL;
-
-	if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags))
-		return -EALREADY;
-
-	change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags);
-
-	return count;
-}
-
-static const struct file_operations force_static_address_fops = {
-	.open		= simple_open,
-	.read		= force_static_address_read,
-	.write		= force_static_address_write,
-	.llseek		= default_llseek,
-};
-
-static int white_list_show(struct seq_file *f, void *ptr)
-{
-	struct hci_dev *hdev = f->private;
-	struct bdaddr_list *b;
-
-	hci_dev_lock(hdev);
-	list_for_each_entry(b, &hdev->le_white_list, list)
-		seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int white_list_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, white_list_show, inode->i_private);
-}
-
-static const struct file_operations white_list_fops = {
-	.open		= white_list_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
-{
-	struct hci_dev *hdev = f->private;
-	struct smp_irk *irk;
-
-	rcu_read_lock();
-	list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
-		seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
-			   &irk->bdaddr, irk->addr_type,
-			   16, irk->val, &irk->rpa);
-	}
-	rcu_read_unlock();
-
-	return 0;
-}
-
-static int identity_resolving_keys_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, identity_resolving_keys_show,
-			   inode->i_private);
-}
-
-static const struct file_operations identity_resolving_keys_fops = {
-	.open		= identity_resolving_keys_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static int long_term_keys_show(struct seq_file *f, void *ptr)
-{
-	struct hci_dev *hdev = f->private;
-	struct smp_ltk *ltk;
-
-	rcu_read_lock();
-	list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list)
-		seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
-			   &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
-			   ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
-			   __le64_to_cpu(ltk->rand), 16, ltk->val);
-	rcu_read_unlock();
-
-	return 0;
-}
-
-static int long_term_keys_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, long_term_keys_show, inode->i_private);
-}
-
-static const struct file_operations long_term_keys_fops = {
-	.open		= long_term_keys_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
-static int conn_min_interval_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
-		return -EINVAL;
-
-	hci_dev_lock(hdev);
-	hdev->le_conn_min_interval = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int conn_min_interval_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->le_conn_min_interval;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get,
-			conn_min_interval_set, "%llu\n");
-
-static int conn_max_interval_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
-		return -EINVAL;
-
-	hci_dev_lock(hdev);
-	hdev->le_conn_max_interval = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int conn_max_interval_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->le_conn_max_interval;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
-			conn_max_interval_set, "%llu\n");
-
-static int conn_latency_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	if (val > 0x01f3)
-		return -EINVAL;
-
-	hci_dev_lock(hdev);
-	hdev->le_conn_latency = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int conn_latency_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->le_conn_latency;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(conn_latency_fops, conn_latency_get,
-			conn_latency_set, "%llu\n");
-
-static int supervision_timeout_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	if (val < 0x000a || val > 0x0c80)
-		return -EINVAL;
-
-	hci_dev_lock(hdev);
-	hdev->le_supv_timeout = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int supervision_timeout_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->le_supv_timeout;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(supervision_timeout_fops, supervision_timeout_get,
-			supervision_timeout_set, "%llu\n");
-
-static int adv_channel_map_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	if (val < 0x01 || val > 0x07)
-		return -EINVAL;
-
-	hci_dev_lock(hdev);
-	hdev->le_adv_channel_map = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int adv_channel_map_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->le_adv_channel_map;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get,
-			adv_channel_map_set, "%llu\n");
-
-static int adv_min_interval_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval)
-		return -EINVAL;
-
-	hci_dev_lock(hdev);
-	hdev->le_adv_min_interval = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int adv_min_interval_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->le_adv_min_interval;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(adv_min_interval_fops, adv_min_interval_get,
-			adv_min_interval_set, "%llu\n");
-
-static int adv_max_interval_set(void *data, u64 val)
-{
-	struct hci_dev *hdev = data;
-
-	if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval)
-		return -EINVAL;
-
-	hci_dev_lock(hdev);
-	hdev->le_adv_max_interval = val;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int adv_max_interval_get(void *data, u64 *val)
-{
-	struct hci_dev *hdev = data;
-
-	hci_dev_lock(hdev);
-	*val = hdev->le_adv_max_interval;
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get,
-			adv_max_interval_set, "%llu\n");
-
-static int device_list_show(struct seq_file *f, void *ptr)
-{
-	struct hci_dev *hdev = f->private;
-	struct hci_conn_params *p;
-	struct bdaddr_list *b;
-
-	hci_dev_lock(hdev);
-	list_for_each_entry(b, &hdev->whitelist, list)
-		seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
-	list_for_each_entry(p, &hdev->le_conn_params, list) {
-		seq_printf(f, "%pMR (type %u) %u\n", &p->addr, p->addr_type,
-			   p->auto_connect);
-	}
-	hci_dev_unlock(hdev);
-
-	return 0;
-}
-
-static int device_list_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, device_list_show, inode->i_private);
-}
-
-static const struct file_operations device_list_fops = {
-	.open		= device_list_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
 /* ---- HCI requests ---- */
 
-static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
+static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
+				  struct sk_buff *skb)
 {
 	BT_DBG("%s result 0x%2.2x", hdev->name, result);
 
 	if (hdev->req_status == HCI_REQ_PEND) {
 		hdev->req_result = result;
 		hdev->req_status = HCI_REQ_DONE;
+		if (skb)
+			hdev->req_skb = skb_get(skb);
 		wake_up_interruptible(&hdev->req_wait_q);
 	}
 }
@@ -1093,66 +166,12 @@
 	}
 }
 
-static struct sk_buff *hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
-					    u8 event)
-{
-	struct hci_ev_cmd_complete *ev;
-	struct hci_event_hdr *hdr;
-	struct sk_buff *skb;
-
-	hci_dev_lock(hdev);
-
-	skb = hdev->recv_evt;
-	hdev->recv_evt = NULL;
-
-	hci_dev_unlock(hdev);
-
-	if (!skb)
-		return ERR_PTR(-ENODATA);
-
-	if (skb->len < sizeof(*hdr)) {
-		BT_ERR("Too short HCI event");
-		goto failed;
-	}
-
-	hdr = (void *) skb->data;
-	skb_pull(skb, HCI_EVENT_HDR_SIZE);
-
-	if (event) {
-		if (hdr->evt != event)
-			goto failed;
-		return skb;
-	}
-
-	if (hdr->evt != HCI_EV_CMD_COMPLETE) {
-		BT_DBG("Last event is not cmd complete (0x%2.2x)", hdr->evt);
-		goto failed;
-	}
-
-	if (skb->len < sizeof(*ev)) {
-		BT_ERR("Too short cmd_complete event");
-		goto failed;
-	}
-
-	ev = (void *) skb->data;
-	skb_pull(skb, sizeof(*ev));
-
-	if (opcode == __le16_to_cpu(ev->opcode))
-		return skb;
-
-	BT_DBG("opcode doesn't match (0x%2.2x != 0x%2.2x)", opcode,
-	       __le16_to_cpu(ev->opcode));
-
-failed:
-	kfree_skb(skb);
-	return ERR_PTR(-ENODATA);
-}
-
 struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
 				  const void *param, u8 event, u32 timeout)
 {
 	DECLARE_WAITQUEUE(wait, current);
 	struct hci_request req;
+	struct sk_buff *skb;
 	int err = 0;
 
 	BT_DBG("%s", hdev->name);
@@ -1166,7 +185,7 @@
 	add_wait_queue(&hdev->req_wait_q, &wait);
 	set_current_state(TASK_INTERRUPTIBLE);
 
-	err = hci_req_run(&req, hci_req_sync_complete);
+	err = hci_req_run_skb(&req, hci_req_sync_complete);
 	if (err < 0) {
 		remove_wait_queue(&hdev->req_wait_q, &wait);
 		set_current_state(TASK_RUNNING);
@@ -1195,13 +214,20 @@
 	}
 
 	hdev->req_status = hdev->req_result = 0;
+	skb = hdev->req_skb;
+	hdev->req_skb = NULL;
 
 	BT_DBG("%s end: err %d", hdev->name, err);
 
-	if (err < 0)
+	if (err < 0) {
+		kfree_skb(skb);
 		return ERR_PTR(err);
+	}
 
-	return hci_get_cmd_complete(hdev, opcode, event);
+	if (!skb)
+		return ERR_PTR(-ENODATA);
+
+	return skb;
 }
 EXPORT_SYMBOL(__hci_cmd_sync_ev);
 
@@ -1233,7 +259,7 @@
 	add_wait_queue(&hdev->req_wait_q, &wait);
 	set_current_state(TASK_INTERRUPTIBLE);
 
-	err = hci_req_run(&req, hci_req_sync_complete);
+	err = hci_req_run_skb(&req, hci_req_sync_complete);
 	if (err < 0) {
 		hdev->req_status = 0;
 
@@ -1320,7 +346,7 @@
 	hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL);
 }
 
-static void amp_init(struct hci_request *req)
+static void amp_init1(struct hci_request *req)
 {
 	req->hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED;
 
@@ -1330,9 +356,6 @@
 	/* Read Local Supported Commands */
 	hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
 
-	/* Read Local Supported Features */
-	hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
-
 	/* Read Local AMP Info */
 	hci_req_add(req, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
 
@@ -1346,6 +369,16 @@
 	hci_req_add(req, HCI_OP_READ_LOCATION_DATA, 0, NULL);
 }
 
+static void amp_init2(struct hci_request *req)
+{
+	/* Read Local Supported Features. Not all AMP controllers
+	 * support this so it's placed conditionally in the second
+	 * stage init.
+	 */
+	if (req->hdev->commands[14] & 0x20)
+		hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
+}
+
 static void hci_init1_req(struct hci_request *req, unsigned long opt)
 {
 	struct hci_dev *hdev = req->hdev;
@@ -1362,7 +395,7 @@
 		break;
 
 	case HCI_AMP:
-		amp_init(req);
+		amp_init1(req);
 		break;
 
 	default:
@@ -1424,44 +457,7 @@
 
 	/* LE-only controllers have LE implicitly enabled */
 	if (!lmp_bredr_capable(hdev))
-		set_bit(HCI_LE_ENABLED, &hdev->dev_flags);
-}
-
-static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
-{
-	if (lmp_ext_inq_capable(hdev))
-		return 0x02;
-
-	if (lmp_inq_rssi_capable(hdev))
-		return 0x01;
-
-	if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 &&
-	    hdev->lmp_subver == 0x0757)
-		return 0x01;
-
-	if (hdev->manufacturer == 15) {
-		if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963)
-			return 0x01;
-		if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963)
-			return 0x01;
-		if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965)
-			return 0x01;
-	}
-
-	if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 &&
-	    hdev->lmp_subver == 0x1805)
-		return 0x01;
-
-	return 0x00;
-}
-
-static void hci_setup_inquiry_mode(struct hci_request *req)
-{
-	u8 mode;
-
-	mode = hci_get_inquiry_mode(req->hdev);
-
-	hci_req_add(req, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode);
+		hci_dev_set_flag(hdev, HCI_LE_ENABLED);
 }
 
 static void hci_setup_event_mask(struct hci_request *req)
@@ -1545,18 +541,27 @@
 {
 	struct hci_dev *hdev = req->hdev;
 
+	if (hdev->dev_type == HCI_AMP)
+		return amp_init2(req);
+
 	if (lmp_bredr_capable(hdev))
 		bredr_setup(req);
 	else
-		clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_BREDR_ENABLED);
 
 	if (lmp_le_capable(hdev))
 		le_setup(req);
 
-	/* AVM Berlin (31), aka "BlueFRITZ!", doesn't support the read
-	 * local supported commands HCI command.
+	/* All Bluetooth 1.2 and later controllers should support the
+	 * HCI command for reading the local supported commands.
+	 *
+	 * Unfortunately some controllers indicate Bluetooth 1.2 support,
+	 * but do not have support for this command. If that is the case,
+	 * the driver can quirk the behavior and skip reading the local
+	 * supported commands.
 	 */
-	if (hdev->manufacturer != 31 && hdev->hci_ver > BLUETOOTH_VER_1_1)
+	if (hdev->hci_ver > BLUETOOTH_VER_1_1 &&
+	    !test_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks))
 		hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
 
 	if (lmp_ssp_capable(hdev)) {
@@ -1568,8 +573,9 @@
 		 */
 		hdev->max_page = 0x01;
 
-		if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+		if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) {
 			u8 mode = 0x01;
+
 			hci_req_add(req, HCI_OP_WRITE_SSP_MODE,
 				    sizeof(mode), &mode);
 		} else {
@@ -1582,8 +588,18 @@
 		}
 	}
 
-	if (lmp_inq_rssi_capable(hdev))
-		hci_setup_inquiry_mode(req);
+	if (lmp_inq_rssi_capable(hdev) ||
+	    test_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks)) {
+		u8 mode;
+
+		/* If Extended Inquiry Result events are supported, then
+		 * they are clearly preferred over Inquiry Result with RSSI
+		 * events.
+		 */
+		mode = lmp_ext_inq_capable(hdev) ? 0x02 : 0x01;
+
+		hci_req_add(req, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode);
+	}
 
 	if (lmp_inq_tx_pwr_capable(hdev))
 		hci_req_add(req, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL);
@@ -1596,7 +612,7 @@
 			    sizeof(cp), &cp);
 	}
 
-	if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_LINK_SECURITY)) {
 		u8 enable = 1;
 		hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable),
 			    &enable);
@@ -1633,7 +649,7 @@
 
 	memset(&cp, 0, sizeof(cp));
 
-	if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
 		cp.le = 0x01;
 		cp.simul = 0x00;
 	}
@@ -1682,27 +698,12 @@
 
 	hci_setup_event_mask(req);
 
-	/* Some Broadcom based Bluetooth controllers do not support the
-	 * Delete Stored Link Key command. They are clearly indicating its
-	 * absence in the bit mask of supported commands.
-	 *
-	 * Check the supported commands and only if the the command is marked
-	 * as supported send it. If not supported assume that the controller
-	 * does not have actual support for stored link keys which makes this
-	 * command redundant anyway.
-	 *
-	 * Some controllers indicate that they support handling deleting
-	 * stored link keys, but they don't. The quirk lets a driver
-	 * just disable this command.
-	 */
-	if (hdev->commands[6] & 0x80 &&
-	    !test_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks)) {
-		struct hci_cp_delete_stored_link_key cp;
+	if (hdev->commands[6] & 0x20) {
+		struct hci_cp_read_stored_link_key cp;
 
 		bacpy(&cp.bdaddr, BDADDR_ANY);
-		cp.delete_all = 0x01;
-		hci_req_add(req, HCI_OP_DELETE_STORED_LINK_KEY,
-			    sizeof(cp), &cp);
+		cp.read_all = 0x01;
+		hci_req_add(req, HCI_OP_READ_STORED_LINK_KEY, sizeof(cp), &cp);
 	}
 
 	if (hdev->commands[5] & 0x10)
@@ -1735,6 +736,12 @@
 						 * Parameter Request
 						 */
 
+		/* If the controller supports the Data Length Extension
+		 * feature, enable the corresponding event.
+		 */
+		if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT)
+			events[0] |= 0x40;	/* LE Data Length Change */
+
 		/* If the controller supports Extended Scanner Filter
 		 * Policies, enable the correspondig event.
 		 */
@@ -1765,6 +772,14 @@
 			hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
 		}
 
+		if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT) {
+			/* Read LE Maximum Data Length */
+			hci_req_add(req, HCI_OP_LE_READ_MAX_DATA_LEN, 0, NULL);
+
+			/* Read LE Suggested Default Data Length */
+			hci_req_add(req, HCI_OP_LE_READ_DEF_DATA_LEN, 0, NULL);
+		}
+
 		hci_set_le_support(req);
 	}
 
@@ -1782,6 +797,29 @@
 {
 	struct hci_dev *hdev = req->hdev;
 
+	/* Some Broadcom based Bluetooth controllers do not support the
+	 * Delete Stored Link Key command. They are clearly indicating its
+	 * absence in the bit mask of supported commands.
+	 *
+	 * Check the supported commands and only if the the command is marked
+	 * as supported send it. If not supported assume that the controller
+	 * does not have actual support for stored link keys which makes this
+	 * command redundant anyway.
+	 *
+	 * Some controllers indicate that they support handling deleting
+	 * stored link keys, but they don't. The quirk lets a driver
+	 * just disable this command.
+	 */
+	if (hdev->commands[6] & 0x80 &&
+	    !test_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks)) {
+		struct hci_cp_delete_stored_link_key cp;
+
+		bacpy(&cp.bdaddr, BDADDR_ANY);
+		cp.delete_all = 0x01;
+		hci_req_add(req, HCI_OP_DELETE_STORED_LINK_KEY,
+			    sizeof(cp), &cp);
+	}
+
 	/* Set event mask page 2 if the HCI command for it is supported */
 	if (hdev->commands[22] & 0x04)
 		hci_set_event_mask_page_2(req);
@@ -1799,8 +837,10 @@
 		hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
 
 	/* Enable Secure Connections if supported and configured */
-	if (bredr_sc_enabled(hdev)) {
+	if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
+	    bredr_sc_enabled(hdev)) {
 		u8 support = 0x01;
+
 		hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
 			    sizeof(support), &support);
 	}
@@ -1817,22 +857,22 @@
 	/* The Device Under Test (DUT) mode is special and available for
 	 * all controller types. So just create it early on.
 	 */
-	if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_SETUP)) {
 		debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
 				    &dut_mode_fops);
 	}
 
-	/* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode
-	 * BR/EDR/LE type controllers. AMP controllers only need the
-	 * first stage init.
-	 */
-	if (hdev->dev_type != HCI_BREDR)
-		return 0;
-
 	err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT);
 	if (err < 0)
 		return err;
 
+	/* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode
+	 * BR/EDR/LE type controllers. AMP controllers only need the
+	 * first two stages of init.
+	 */
+	if (hdev->dev_type != HCI_BREDR)
+		return 0;
+
 	err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
 	if (err < 0)
 		return err;
@@ -1841,110 +881,29 @@
 	if (err < 0)
 		return err;
 
-	/* Only create debugfs entries during the initial setup
-	 * phase and not every time the controller gets powered on.
+	/* This function is only called when the controller is actually in
+	 * configured state. When the controller is marked as unconfigured,
+	 * this initialization procedure is not run.
+	 *
+	 * It means that it is possible that a controller runs through its
+	 * setup phase and then discovers missing settings. If that is the
+	 * case, then this function will not be called. It then will only
+	 * be called during the config phase.
+	 *
+	 * So only when in setup phase or config phase, create the debugfs
+	 * entries and register the SMP channels.
 	 */
-	if (!test_bit(HCI_SETUP, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
+	    !hci_dev_test_flag(hdev, HCI_CONFIG))
 		return 0;
 
-	debugfs_create_file("features", 0444, hdev->debugfs, hdev,
-			    &features_fops);
-	debugfs_create_u16("manufacturer", 0444, hdev->debugfs,
-			   &hdev->manufacturer);
-	debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
-	debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
-	debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
-			    &device_list_fops);
-	debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
-			    &blacklist_fops);
-	debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
+	hci_debugfs_create_common(hdev);
 
-	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))
+		hci_debugfs_create_bredr(hdev);
 
-	if (lmp_bredr_capable(hdev)) {
-		debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
-				    hdev, &inquiry_cache_fops);
-		debugfs_create_file("link_keys", 0400, hdev->debugfs,
-				    hdev, &link_keys_fops);
-		debugfs_create_file("dev_class", 0444, hdev->debugfs,
-				    hdev, &dev_class_fops);
-		debugfs_create_file("voice_setting", 0444, hdev->debugfs,
-				    hdev, &voice_setting_fops);
-	}
-
-	if (lmp_ssp_capable(hdev)) {
-		debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
-				    hdev, &auto_accept_delay_fops);
-		debugfs_create_file("force_sc_support", 0644, hdev->debugfs,
-				    hdev, &force_sc_support_fops);
-		debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
-				    hdev, &sc_only_mode_fops);
-		if (lmp_le_capable(hdev))
-			debugfs_create_file("force_lesc_support", 0644,
-					    hdev->debugfs, hdev,
-					    &force_lesc_support_fops);
-	}
-
-	if (lmp_sniff_capable(hdev)) {
-		debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
-				    hdev, &idle_timeout_fops);
-		debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs,
-				    hdev, &sniff_min_interval_fops);
-		debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs,
-				    hdev, &sniff_max_interval_fops);
-	}
-
-	if (lmp_le_capable(hdev)) {
-		debugfs_create_file("identity", 0400, hdev->debugfs,
-				    hdev, &identity_fops);
-		debugfs_create_file("rpa_timeout", 0644, hdev->debugfs,
-				    hdev, &rpa_timeout_fops);
-		debugfs_create_file("random_address", 0444, hdev->debugfs,
-				    hdev, &random_address_fops);
-		debugfs_create_file("static_address", 0444, hdev->debugfs,
-				    hdev, &static_address_fops);
-
-		/* For controllers with a public address, provide a debug
-		 * option to force the usage of the configured static
-		 * address. By default the public address is used.
-		 */
-		if (bacmp(&hdev->bdaddr, BDADDR_ANY))
-			debugfs_create_file("force_static_address", 0644,
-					    hdev->debugfs, hdev,
-					    &force_static_address_fops);
-
-		debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
-				  &hdev->le_white_list_size);
-		debugfs_create_file("white_list", 0444, hdev->debugfs, hdev,
-				    &white_list_fops);
-		debugfs_create_file("identity_resolving_keys", 0400,
-				    hdev->debugfs, hdev,
-				    &identity_resolving_keys_fops);
-		debugfs_create_file("long_term_keys", 0400, hdev->debugfs,
-				    hdev, &long_term_keys_fops);
-		debugfs_create_file("conn_min_interval", 0644, hdev->debugfs,
-				    hdev, &conn_min_interval_fops);
-		debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
-				    hdev, &conn_max_interval_fops);
-		debugfs_create_file("conn_latency", 0644, hdev->debugfs,
-				    hdev, &conn_latency_fops);
-		debugfs_create_file("supervision_timeout", 0644, hdev->debugfs,
-				    hdev, &supervision_timeout_fops);
-		debugfs_create_file("adv_channel_map", 0644, hdev->debugfs,
-				    hdev, &adv_channel_map_fops);
-		debugfs_create_file("adv_min_interval", 0644, hdev->debugfs,
-				    hdev, &adv_min_interval_fops);
-		debugfs_create_file("adv_max_interval", 0644, hdev->debugfs,
-				    hdev, &adv_max_interval_fops);
-		debugfs_create_u16("discov_interleaved_timeout", 0644,
-				   hdev->debugfs,
-				   &hdev->discov_interleaved_timeout);
-
-		smp_register(hdev);
-	}
+	if (lmp_le_capable(hdev))
+		hci_debugfs_create_le(hdev);
 
 	return 0;
 }
@@ -2297,12 +1256,12 @@
 	if (!hdev)
 		return -ENODEV;
 
-	if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
 		err = -EBUSY;
 		goto done;
 	}
 
-	if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
 		err = -EOPNOTSUPP;
 		goto done;
 	}
@@ -2312,7 +1271,7 @@
 		goto done;
 	}
 
-	if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+	if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
 		err = -EOPNOTSUPP;
 		goto done;
 	}
@@ -2384,17 +1343,17 @@
 
 	hci_req_lock(hdev);
 
-	if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
 		ret = -ENODEV;
 		goto done;
 	}
 
-	if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
-	    !test_bit(HCI_CONFIG, &hdev->dev_flags)) {
+	if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
+	    !hci_dev_test_flag(hdev, HCI_CONFIG)) {
 		/* Check for rfkill but allow the HCI setup stage to
 		 * proceed (which in itself doesn't cause any RF activity).
 		 */
-		if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) {
+		if (hci_dev_test_flag(hdev, HCI_RFKILLED)) {
 			ret = -ERFKILL;
 			goto done;
 		}
@@ -2411,7 +1370,7 @@
 		 * This check is only valid for BR/EDR controllers
 		 * since AMP controllers do not have an address.
 		 */
-		if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
+		if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
 		    hdev->dev_type == HCI_BREDR &&
 		    !bacmp(&hdev->bdaddr, BDADDR_ANY) &&
 		    !bacmp(&hdev->static_addr, BDADDR_ANY)) {
@@ -2433,7 +1392,7 @@
 	atomic_set(&hdev->cmd_cnt, 1);
 	set_bit(HCI_INIT, &hdev->flags);
 
-	if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_SETUP)) {
 		if (hdev->setup)
 			ret = hdev->setup(hdev);
 
@@ -2445,7 +1404,7 @@
 		 */
 		if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) ||
 		    test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks))
-			set_bit(HCI_UNCONFIGURED, &hdev->dev_flags);
+			hci_dev_set_flag(hdev, HCI_UNCONFIGURED);
 
 		/* For an unconfigured controller it is required to
 		 * read at least the version information provided by
@@ -2455,11 +1414,11 @@
 		 * also the original Bluetooth public device address
 		 * will be read using the Read BD Address command.
 		 */
-		if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+		if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
 			ret = __hci_unconf_init(hdev);
 	}
 
-	if (test_bit(HCI_CONFIG, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_CONFIG)) {
 		/* If public address change is configured, ensure that
 		 * the address gets programmed. If the driver does not
 		 * support changing the public address, fail the power
@@ -2473,8 +1432,8 @@
 	}
 
 	if (!ret) {
-		if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
-		    !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
+		if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
+		    !hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
 			ret = __hci_init(hdev);
 	}
 
@@ -2482,13 +1441,13 @@
 
 	if (!ret) {
 		hci_dev_hold(hdev);
-		set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
 		set_bit(HCI_UP, &hdev->flags);
 		hci_notify(hdev, HCI_DEV_UP);
-		if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
-		    !test_bit(HCI_CONFIG, &hdev->dev_flags) &&
-		    !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
-		    !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
+		if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
+		    !hci_dev_test_flag(hdev, HCI_CONFIG) &&
+		    !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
+		    !hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
 		    hdev->dev_type == HCI_BREDR) {
 			hci_dev_lock(hdev);
 			mgmt_powered(hdev, 1);
@@ -2540,8 +1499,8 @@
 	 * HCI_USER_CHANNEL will be set first before attempting to
 	 * open the device.
 	 */
-	if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
-	    !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
+	    !hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
 		err = -EOPNOTSUPP;
 		goto done;
 	}
@@ -2551,7 +1510,7 @@
 	 * particularly important if the setup procedure has not yet
 	 * completed.
 	 */
-	if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+	if (hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF))
 		cancel_delayed_work(&hdev->power_off);
 
 	/* After this call it is guaranteed that the setup procedure
@@ -2566,9 +1525,9 @@
 	 * is in use this bit will be cleared again and userspace has
 	 * to explicitly enable it.
 	 */
-	if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
-	    !test_bit(HCI_MGMT, &hdev->dev_flags))
-		set_bit(HCI_BONDABLE, &hdev->dev_flags);
+	if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
+	    !hci_dev_test_flag(hdev, HCI_MGMT))
+		hci_dev_set_flag(hdev, HCI_BONDABLE);
 
 	err = hci_dev_do_open(hdev);
 
@@ -2598,6 +1557,12 @@
 {
 	BT_DBG("%s %p", hdev->name, hdev);
 
+	if (!hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
+		/* Execute vendor specific shutdown routine */
+		if (hdev->shutdown)
+			hdev->shutdown(hdev);
+	}
+
 	cancel_delayed_work(&hdev->power_off);
 
 	hci_req_cancel(hdev, ENODEV);
@@ -2616,16 +1581,17 @@
 	if (hdev->discov_timeout > 0) {
 		cancel_delayed_work(&hdev->discov_off);
 		hdev->discov_timeout = 0;
-		clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
-		clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
+		hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
 	}
 
-	if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
+	if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE))
 		cancel_delayed_work(&hdev->service_cache);
 
 	cancel_delayed_work_sync(&hdev->le_scan_disable);
+	cancel_delayed_work_sync(&hdev->le_scan_restart);
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		cancel_delayed_work_sync(&hdev->rpa_expired);
 
 	/* Avoid potential lockdep warnings from the *_flush() calls by
@@ -2635,7 +1601,9 @@
 
 	hci_dev_lock(hdev);
 
-	if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
+	hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+
+	if (!hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
 		if (hdev->dev_type == HCI_BREDR)
 			mgmt_powered(hdev, 0);
 	}
@@ -2645,6 +1613,8 @@
 	hci_conn_hash_flush(hdev);
 	hci_dev_unlock(hdev);
 
+	smp_unregister(hdev);
+
 	hci_notify(hdev, HCI_DEV_DOWN);
 
 	if (hdev->flush)
@@ -2653,8 +1623,8 @@
 	/* Reset device */
 	skb_queue_purge(&hdev->cmd_q);
 	atomic_set(&hdev->cmd_cnt, 1);
-	if (!test_bit(HCI_AUTO_OFF, &hdev->dev_flags) &&
-	    !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
+	if (!hci_dev_test_flag(hdev, HCI_AUTO_OFF) &&
+	    !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
 	    test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) {
 		set_bit(HCI_INIT, &hdev->flags);
 		__hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
@@ -2676,16 +1646,13 @@
 		hdev->sent_cmd = NULL;
 	}
 
-	kfree_skb(hdev->recv_evt);
-	hdev->recv_evt = NULL;
-
 	/* After this point our queues are empty
 	 * and no tasks are scheduled. */
 	hdev->close(hdev);
 
 	/* Clear flags */
 	hdev->flags &= BIT(HCI_RAW);
-	hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
+	hci_dev_clear_volatile_flags(hdev);
 
 	/* Controller radio is available but is currently powered down */
 	hdev->amp_status = AMP_STATUS_POWERED_DOWN;
@@ -2709,12 +1676,12 @@
 	if (!hdev)
 		return -ENODEV;
 
-	if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
 		err = -EBUSY;
 		goto done;
 	}
 
-	if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+	if (hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF))
 		cancel_delayed_work(&hdev->power_off);
 
 	err = hci_dev_do_close(hdev);
@@ -2724,32 +1691,14 @@
 	return err;
 }
 
-int hci_dev_reset(__u16 dev)
+static int hci_dev_do_reset(struct hci_dev *hdev)
 {
-	struct hci_dev *hdev;
-	int ret = 0;
+	int ret;
 
-	hdev = hci_dev_get(dev);
-	if (!hdev)
-		return -ENODEV;
+	BT_DBG("%s %p", hdev->name, hdev);
 
 	hci_req_lock(hdev);
 
-	if (!test_bit(HCI_UP, &hdev->flags)) {
-		ret = -ENETDOWN;
-		goto done;
-	}
-
-	if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
-		ret = -EBUSY;
-		goto done;
-	}
-
-	if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
-		ret = -EOPNOTSUPP;
-		goto done;
-	}
-
 	/* Drop queues */
 	skb_queue_purge(&hdev->rx_q);
 	skb_queue_purge(&hdev->cmd_q);
@@ -2772,12 +1721,41 @@
 
 	ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
 
-done:
 	hci_req_unlock(hdev);
-	hci_dev_put(hdev);
 	return ret;
 }
 
+int hci_dev_reset(__u16 dev)
+{
+	struct hci_dev *hdev;
+	int err;
+
+	hdev = hci_dev_get(dev);
+	if (!hdev)
+		return -ENODEV;
+
+	if (!test_bit(HCI_UP, &hdev->flags)) {
+		err = -ENETDOWN;
+		goto done;
+	}
+
+	if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
+		err = -EBUSY;
+		goto done;
+	}
+
+	if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
+		err = -EOPNOTSUPP;
+		goto done;
+	}
+
+	err = hci_dev_do_reset(hdev);
+
+done:
+	hci_dev_put(hdev);
+	return err;
+}
+
 int hci_dev_reset_stat(__u16 dev)
 {
 	struct hci_dev *hdev;
@@ -2787,12 +1765,12 @@
 	if (!hdev)
 		return -ENODEV;
 
-	if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
 		ret = -EBUSY;
 		goto done;
 	}
 
-	if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
 		ret = -EOPNOTSUPP;
 		goto done;
 	}
@@ -2811,29 +1789,29 @@
 	BT_DBG("%s scan 0x%02x", hdev->name, scan);
 
 	if ((scan & SCAN_PAGE))
-		conn_changed = !test_and_set_bit(HCI_CONNECTABLE,
-						 &hdev->dev_flags);
+		conn_changed = !hci_dev_test_and_set_flag(hdev,
+							  HCI_CONNECTABLE);
 	else
-		conn_changed = test_and_clear_bit(HCI_CONNECTABLE,
-						  &hdev->dev_flags);
+		conn_changed = hci_dev_test_and_clear_flag(hdev,
+							   HCI_CONNECTABLE);
 
 	if ((scan & SCAN_INQUIRY)) {
-		discov_changed = !test_and_set_bit(HCI_DISCOVERABLE,
-						   &hdev->dev_flags);
+		discov_changed = !hci_dev_test_and_set_flag(hdev,
+							    HCI_DISCOVERABLE);
 	} else {
-		clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
-		discov_changed = test_and_clear_bit(HCI_DISCOVERABLE,
-						    &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
+		discov_changed = hci_dev_test_and_clear_flag(hdev,
+							     HCI_DISCOVERABLE);
 	}
 
-	if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_MGMT))
 		return;
 
 	if (conn_changed || discov_changed) {
 		/* In case this was disabled through mgmt */
-		set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_BREDR_ENABLED);
 
-		if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+		if (hci_dev_test_flag(hdev, HCI_LE_ENABLED))
 			mgmt_update_adv_data(hdev);
 
 		mgmt_new_settings(hdev);
@@ -2853,12 +1831,12 @@
 	if (!hdev)
 		return -ENODEV;
 
-	if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
 		err = -EBUSY;
 		goto done;
 	}
 
-	if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
 		err = -EOPNOTSUPP;
 		goto done;
 	}
@@ -2868,7 +1846,7 @@
 		goto done;
 	}
 
-	if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+	if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
 		err = -EOPNOTSUPP;
 		goto done;
 	}
@@ -2972,7 +1950,7 @@
 		 * is running, but in that case still indicate that the
 		 * device is actually down.
 		 */
-		if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+		if (hci_dev_test_flag(hdev, HCI_AUTO_OFF))
 			flags &= ~BIT(HCI_UP);
 
 		(dr + n)->dev_id  = hdev->id;
@@ -3010,7 +1988,7 @@
 	 * is running, but in that case still indicate that the
 	 * device is actually down.
 	 */
-	if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_AUTO_OFF))
 		flags = hdev->flags & ~BIT(HCI_UP);
 	else
 		flags = hdev->flags;
@@ -3053,16 +2031,16 @@
 
 	BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
 
-	if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
 		return -EBUSY;
 
 	if (blocked) {
-		set_bit(HCI_RFKILLED, &hdev->dev_flags);
-		if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
-		    !test_bit(HCI_CONFIG, &hdev->dev_flags))
+		hci_dev_set_flag(hdev, HCI_RFKILLED);
+		if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
+		    !hci_dev_test_flag(hdev, HCI_CONFIG))
 			hci_dev_do_close(hdev);
 	} else {
-		clear_bit(HCI_RFKILLED, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_RFKILLED);
 	}
 
 	return 0;
@@ -3091,23 +2069,23 @@
 	 * ignored and they need to be checked now. If they are still
 	 * valid, it is important to turn the device back off.
 	 */
-	if (test_bit(HCI_RFKILLED, &hdev->dev_flags) ||
-	    test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) ||
+	if (hci_dev_test_flag(hdev, HCI_RFKILLED) ||
+	    hci_dev_test_flag(hdev, HCI_UNCONFIGURED) ||
 	    (hdev->dev_type == HCI_BREDR &&
 	     !bacmp(&hdev->bdaddr, BDADDR_ANY) &&
 	     !bacmp(&hdev->static_addr, BDADDR_ANY))) {
-		clear_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_AUTO_OFF);
 		hci_dev_do_close(hdev);
-	} else if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
+	} else if (hci_dev_test_flag(hdev, HCI_AUTO_OFF)) {
 		queue_delayed_work(hdev->req_workqueue, &hdev->power_off,
 				   HCI_AUTO_OFF_TIMEOUT);
 	}
 
-	if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) {
+	if (hci_dev_test_and_clear_flag(hdev, HCI_SETUP)) {
 		/* For unconfigured devices, set the HCI_RAW flag
 		 * so that userspace can easily identify them.
 		 */
-		if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+		if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
 			set_bit(HCI_RAW, &hdev->flags);
 
 		/* For fully configured devices, this will send
@@ -3118,11 +2096,11 @@
 		 * and no event will be send.
 		 */
 		mgmt_index_added(hdev);
-	} else if (test_and_clear_bit(HCI_CONFIG, &hdev->dev_flags)) {
+	} else if (hci_dev_test_and_clear_flag(hdev, HCI_CONFIG)) {
 		/* When the controller is now configured, then it
 		 * is important to clear the HCI_RAW flag.
 		 */
-		if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+		if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
 			clear_bit(HCI_RAW, &hdev->flags);
 
 		/* Powering on the controller with HCI_CONFIG set only
@@ -3143,6 +2121,24 @@
 	hci_dev_do_close(hdev);
 }
 
+static void hci_error_reset(struct work_struct *work)
+{
+	struct hci_dev *hdev = container_of(work, struct hci_dev, error_reset);
+
+	BT_DBG("%s", hdev->name);
+
+	if (hdev->hw_error)
+		hdev->hw_error(hdev, hdev->hw_error_code);
+	else
+		BT_ERR("%s hardware error 0x%2.2x", hdev->name,
+		       hdev->hw_error_code);
+
+	if (hci_dev_do_close(hdev))
+		return;
+
+	hci_dev_do_open(hdev);
+}
+
 static void hci_discov_off(struct work_struct *work)
 {
 	struct hci_dev *hdev;
@@ -3473,6 +2469,42 @@
 	}
 }
 
+bool hci_bdaddr_is_paired(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
+{
+	struct smp_ltk *k;
+	struct smp_irk *irk;
+	u8 addr_type;
+
+	if (type == BDADDR_BREDR) {
+		if (hci_find_link_key(hdev, bdaddr))
+			return true;
+		return false;
+	}
+
+	/* Convert to HCI addr type which struct smp_ltk uses */
+	if (type == BDADDR_LE_PUBLIC)
+		addr_type = ADDR_LE_DEV_PUBLIC;
+	else
+		addr_type = ADDR_LE_DEV_RANDOM;
+
+	irk = hci_get_irk(hdev, bdaddr, addr_type);
+	if (irk) {
+		bdaddr = &irk->bdaddr;
+		addr_type = irk->addr_type;
+	}
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
+		if (k->bdaddr_type == addr_type && !bacmp(bdaddr, &k->bdaddr)) {
+			rcu_read_unlock();
+			return true;
+		}
+	}
+	rcu_read_unlock();
+
+	return false;
+}
+
 /* HCI command timer function */
 static void hci_cmd_timeout(struct work_struct *work)
 {
@@ -3555,9 +2587,15 @@
 	if (hash192 && rand192) {
 		memcpy(data->hash192, hash192, sizeof(data->hash192));
 		memcpy(data->rand192, rand192, sizeof(data->rand192));
+		if (hash256 && rand256)
+			data->present = 0x03;
 	} else {
 		memset(data->hash192, 0, sizeof(data->hash192));
 		memset(data->rand192, 0, sizeof(data->rand192));
+		if (hash256 && rand256)
+			data->present = 0x02;
+		else
+			data->present = 0x00;
 	}
 
 	if (hash256 && rand256) {
@@ -3566,6 +2604,8 @@
 	} else {
 		memset(data->hash256, 0, sizeof(data->hash256));
 		memset(data->rand256, 0, sizeof(data->rand256));
+		if (hash192 && rand192)
+			data->present = 0x01;
 	}
 
 	BT_DBG("%s for %pMR", hdev->name, bdaddr);
@@ -3659,23 +2699,6 @@
 	return NULL;
 }
 
-static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
-{
-	struct hci_conn *conn;
-
-	conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
-	if (!conn)
-		return false;
-
-	if (conn->dst_type != type)
-		return false;
-
-	if (conn->state != BT_CONNECTED)
-		return false;
-
-	return true;
-}
-
 /* This function requires the caller holds hdev->lock */
 struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
 						  bdaddr_t *addr, u8 addr_type)
@@ -3731,47 +2754,6 @@
 	return params;
 }
 
-/* This function requires the caller holds hdev->lock */
-int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
-			u8 auto_connect)
-{
-	struct hci_conn_params *params;
-
-	params = hci_conn_params_add(hdev, addr, addr_type);
-	if (!params)
-		return -EIO;
-
-	if (params->auto_connect == auto_connect)
-		return 0;
-
-	list_del_init(&params->action);
-
-	switch (auto_connect) {
-	case HCI_AUTO_CONN_DISABLED:
-	case HCI_AUTO_CONN_LINK_LOSS:
-		hci_update_background_scan(hdev);
-		break;
-	case HCI_AUTO_CONN_REPORT:
-		list_add(&params->action, &hdev->pend_le_reports);
-		hci_update_background_scan(hdev);
-		break;
-	case HCI_AUTO_CONN_DIRECT:
-	case HCI_AUTO_CONN_ALWAYS:
-		if (!is_connected(hdev, addr, addr_type)) {
-			list_add(&params->action, &hdev->pend_le_conns);
-			hci_update_background_scan(hdev);
-		}
-		break;
-	}
-
-	params->auto_connect = auto_connect;
-
-	BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type,
-	       auto_connect);
-
-	return 0;
-}
-
 static void hci_conn_params_free(struct hci_conn_params *params)
 {
 	if (params->conn) {
@@ -3828,7 +2810,7 @@
 	BT_DBG("All LE connection parameters were removed");
 }
 
-static void inquiry_complete(struct hci_dev *hdev, u8 status)
+static void inquiry_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
 	if (status) {
 		BT_ERR("Failed to start inquiry: status %d", status);
@@ -3840,11 +2822,11 @@
 	}
 }
 
-static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status)
+static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status,
+					  u16 opcode)
 {
 	/* General inquiry access code (GIAC) */
 	u8 lap[3] = { 0x33, 0x8b, 0x9e };
-	struct hci_request req;
 	struct hci_cp_inquiry cp;
 	int err;
 
@@ -3853,6 +2835,8 @@
 		return;
 	}
 
+	hdev->discovery.scan_start = 0;
+
 	switch (hdev->discovery.type) {
 	case DISCOV_TYPE_LE:
 		hci_dev_lock(hdev);
@@ -3861,21 +2845,37 @@
 		break;
 
 	case DISCOV_TYPE_INTERLEAVED:
-		hci_req_init(&req, hdev);
-
-		memset(&cp, 0, sizeof(cp));
-		memcpy(&cp.lap, lap, sizeof(cp.lap));
-		cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN;
-		hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp);
-
 		hci_dev_lock(hdev);
 
-		hci_inquiry_cache_flush(hdev);
+		if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
+			     &hdev->quirks)) {
+			/* If we were running LE only scan, change discovery
+			 * state. If we were running both LE and BR/EDR inquiry
+			 * simultaneously, and BR/EDR inquiry is already
+			 * finished, stop discovery, otherwise BR/EDR inquiry
+			 * will stop discovery when finished.
+			 */
+			if (!test_bit(HCI_INQUIRY, &hdev->flags))
+				hci_discovery_set_state(hdev,
+							DISCOVERY_STOPPED);
+		} else {
+			struct hci_request req;
 
-		err = hci_req_run(&req, inquiry_complete);
-		if (err) {
-			BT_ERR("Inquiry request failed: err %d", err);
-			hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+			hci_inquiry_cache_flush(hdev);
+
+			hci_req_init(&req, hdev);
+
+			memset(&cp, 0, sizeof(cp));
+			memcpy(&cp.lap, lap, sizeof(cp.lap));
+			cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN;
+			hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp);
+
+			err = hci_req_run(&req, inquiry_complete);
+			if (err) {
+				BT_ERR("Inquiry request failed: err %d", err);
+				hci_discovery_set_state(hdev,
+							DISCOVERY_STOPPED);
+			}
 		}
 
 		hci_dev_unlock(hdev);
@@ -3892,6 +2892,8 @@
 
 	BT_DBG("%s", hdev->name);
 
+	cancel_delayed_work_sync(&hdev->le_scan_restart);
+
 	hci_req_init(&req, hdev);
 
 	hci_req_add_le_scan_disable(&req);
@@ -3901,110 +2903,72 @@
 		BT_ERR("Disable LE scanning request failed: err %d", err);
 }
 
-static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
+static void le_scan_restart_work_complete(struct hci_dev *hdev, u8 status,
+					  u16 opcode)
 {
-	struct hci_dev *hdev = req->hdev;
+	unsigned long timeout, duration, scan_start, now;
 
-	/* If we're advertising or initiating an LE connection we can't
-	 * go ahead and change the random address at this time. This is
-	 * because the eventual initiator address used for the
-	 * subsequently created connection will be undefined (some
-	 * controllers use the new address and others the one we had
-	 * when the operation started).
-	 *
-	 * In this kind of scenario skip the update and let the random
-	 * address be updated at the next cycle.
-	 */
-	if (test_bit(HCI_LE_ADV, &hdev->dev_flags) ||
-	    hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) {
-		BT_DBG("Deferring random address update");
-		set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+	BT_DBG("%s", hdev->name);
+
+	if (status) {
+		BT_ERR("Failed to restart LE scan: status %d", status);
 		return;
 	}
 
-	hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa);
+	if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) ||
+	    !hdev->discovery.scan_start)
+		return;
+
+	/* When the scan was started, hdev->le_scan_disable has been queued
+	 * after duration from scan_start. During scan restart this job
+	 * has been canceled, and we need to queue it again after proper
+	 * timeout, to make sure that scan does not run indefinitely.
+	 */
+	duration = hdev->discovery.scan_duration;
+	scan_start = hdev->discovery.scan_start;
+	now = jiffies;
+	if (now - scan_start <= duration) {
+		int elapsed;
+
+		if (now >= scan_start)
+			elapsed = now - scan_start;
+		else
+			elapsed = ULONG_MAX - scan_start + now;
+
+		timeout = duration - elapsed;
+	} else {
+		timeout = 0;
+	}
+	queue_delayed_work(hdev->workqueue,
+			   &hdev->le_scan_disable, timeout);
 }
 
-int hci_update_random_address(struct hci_request *req, bool require_privacy,
-			      u8 *own_addr_type)
+static void le_scan_restart_work(struct work_struct *work)
 {
-	struct hci_dev *hdev = req->hdev;
+	struct hci_dev *hdev = container_of(work, struct hci_dev,
+					    le_scan_restart.work);
+	struct hci_request req;
+	struct hci_cp_le_set_scan_enable cp;
 	int err;
 
-	/* If privacy is enabled use a resolvable private address. If
-	 * current RPA has expired or there is something else than
-	 * the current RPA in use, then generate a new one.
-	 */
-	if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) {
-		int to;
+	BT_DBG("%s", hdev->name);
 
-		*own_addr_type = ADDR_LE_DEV_RANDOM;
+	/* If controller is not scanning we are done. */
+	if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
+		return;
 
-		if (!test_and_clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags) &&
-		    !bacmp(&hdev->random_addr, &hdev->rpa))
-			return 0;
+	hci_req_init(&req, hdev);
 
-		err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa);
-		if (err < 0) {
-			BT_ERR("%s failed to generate new RPA", hdev->name);
-			return err;
-		}
+	hci_req_add_le_scan_disable(&req);
 
-		set_random_addr(req, &hdev->rpa);
+	memset(&cp, 0, sizeof(cp));
+	cp.enable = LE_SCAN_ENABLE;
+	cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+	hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
 
-		to = msecs_to_jiffies(hdev->rpa_timeout * 1000);
-		queue_delayed_work(hdev->workqueue, &hdev->rpa_expired, to);
-
-		return 0;
-	}
-
-	/* In case of required privacy without resolvable private address,
-	 * use an non-resolvable private address. This is useful for active
-	 * scanning and non-connectable advertising.
-	 */
-	if (require_privacy) {
-		bdaddr_t nrpa;
-
-		while (true) {
-			/* The non-resolvable private address is generated
-			 * from random six bytes with the two most significant
-			 * bits cleared.
-			 */
-			get_random_bytes(&nrpa, 6);
-			nrpa.b[5] &= 0x3f;
-
-			/* The non-resolvable private address shall not be
-			 * equal to the public address.
-			 */
-			if (bacmp(&hdev->bdaddr, &nrpa))
-				break;
-		}
-
-		*own_addr_type = ADDR_LE_DEV_RANDOM;
-		set_random_addr(req, &nrpa);
-		return 0;
-	}
-
-	/* If forcing static address is in use or there is no public
-	 * address use the static address as random address (but skip
-	 * the HCI command if the current random address is already the
-	 * static one.
-	 */
-	if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ||
-	    !bacmp(&hdev->bdaddr, BDADDR_ANY)) {
-		*own_addr_type = ADDR_LE_DEV_RANDOM;
-		if (bacmp(&hdev->static_addr, &hdev->random_addr))
-			hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
-				    &hdev->static_addr);
-		return 0;
-	}
-
-	/* Neither privacy nor static address is being used so use a
-	 * public address.
-	 */
-	*own_addr_type = ADDR_LE_DEV_PUBLIC;
-
-	return 0;
+	err = hci_req_run(&req, le_scan_restart_work_complete);
+	if (err)
+		BT_ERR("Restart LE scan request failed: err %d", err);
 }
 
 /* Copy the Identity Address of the controller.
@@ -4015,12 +2979,18 @@
  *
  * For debugging purposes it is possible to force controllers with a
  * public address to use the static random address instead.
+ *
+ * In case BR/EDR has been disabled on a dual-mode controller and
+ * userspace has configured a static address, then that address
+ * becomes the identity address instead of the public BR/EDR address.
  */
 void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
 			       u8 *bdaddr_type)
 {
-	if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ||
-	    !bacmp(&hdev->bdaddr, BDADDR_ANY)) {
+	if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
+	    !bacmp(&hdev->bdaddr, BDADDR_ANY) ||
+	    (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
+	     bacmp(&hdev->static_addr, BDADDR_ANY))) {
 		bacpy(bdaddr, &hdev->static_addr);
 		*bdaddr_type = ADDR_LE_DEV_RANDOM;
 	} else {
@@ -4059,6 +3029,12 @@
 	hdev->le_conn_max_interval = 0x0038;
 	hdev->le_conn_latency = 0x0000;
 	hdev->le_supv_timeout = 0x002a;
+	hdev->le_def_tx_len = 0x001b;
+	hdev->le_def_tx_time = 0x0148;
+	hdev->le_max_tx_len = 0x001b;
+	hdev->le_max_tx_time = 0x0148;
+	hdev->le_max_rx_len = 0x001b;
+	hdev->le_max_rx_time = 0x0148;
 
 	hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT;
 	hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT;
@@ -4086,10 +3062,12 @@
 	INIT_WORK(&hdev->cmd_work, hci_cmd_work);
 	INIT_WORK(&hdev->tx_work, hci_tx_work);
 	INIT_WORK(&hdev->power_on, hci_power_on);
+	INIT_WORK(&hdev->error_reset, hci_error_reset);
 
 	INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
 	INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
 	INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
+	INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
 
 	skb_queue_head_init(&hdev->rx_q);
 	skb_queue_head_init(&hdev->cmd_q);
@@ -4101,6 +3079,7 @@
 
 	hci_init_sysfs(hdev);
 	discovery_init(hdev);
+	adv_info_init(hdev);
 
 	return hdev;
 }
@@ -4179,16 +3158,16 @@
 	}
 
 	if (hdev->rfkill && rfkill_blocked(hdev->rfkill))
-		set_bit(HCI_RFKILLED, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_RFKILLED);
 
-	set_bit(HCI_SETUP, &hdev->dev_flags);
-	set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+	hci_dev_set_flag(hdev, HCI_SETUP);
+	hci_dev_set_flag(hdev, HCI_AUTO_OFF);
 
 	if (hdev->dev_type == HCI_BREDR) {
 		/* Assume BR/EDR support until proven otherwise (such as
 		 * through reading supported features during init.
 		 */
-		set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_BREDR_ENABLED);
 	}
 
 	write_lock(&hci_dev_list_lock);
@@ -4199,7 +3178,7 @@
 	 * and should not be included in normal operation.
 	 */
 	if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
-		set_bit(HCI_UNCONFIGURED, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_UNCONFIGURED);
 
 	hci_notify(hdev, HCI_DEV_REG);
 	hci_dev_hold(hdev);
@@ -4221,11 +3200,11 @@
 /* Unregister HCI device */
 void hci_unregister_dev(struct hci_dev *hdev)
 {
-	int i, id;
+	int id;
 
 	BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
 
-	set_bit(HCI_UNREGISTER, &hdev->dev_flags);
+	hci_dev_set_flag(hdev, HCI_UNREGISTER);
 
 	id = hdev->id;
 
@@ -4235,14 +3214,11 @@
 
 	hci_dev_do_close(hdev);
 
-	for (i = 0; i < NUM_REASSEMBLY; i++)
-		kfree_skb(hdev->reassembly[i]);
-
 	cancel_work_sync(&hdev->power_on);
 
 	if (!test_bit(HCI_INIT, &hdev->flags) &&
-	    !test_bit(HCI_SETUP, &hdev->dev_flags) &&
-	    !test_bit(HCI_CONFIG, &hdev->dev_flags)) {
+	    !hci_dev_test_flag(hdev, HCI_SETUP) &&
+	    !hci_dev_test_flag(hdev, HCI_CONFIG)) {
 		hci_dev_lock(hdev);
 		mgmt_index_removed(hdev);
 		hci_dev_unlock(hdev);
@@ -4259,8 +3235,6 @@
 		rfkill_destroy(hdev->rfkill);
 	}
 
-	smp_unregister(hdev);
-
 	device_del(&hdev->dev);
 
 	debugfs_remove_recursive(hdev->debugfs);
@@ -4343,158 +3317,15 @@
 }
 EXPORT_SYMBOL(hci_recv_frame);
 
-static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
-			  int count, __u8 index)
-{
-	int len = 0;
-	int hlen = 0;
-	int remain = count;
-	struct sk_buff *skb;
-	struct bt_skb_cb *scb;
-
-	if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) ||
-	    index >= NUM_REASSEMBLY)
-		return -EILSEQ;
-
-	skb = hdev->reassembly[index];
-
-	if (!skb) {
-		switch (type) {
-		case HCI_ACLDATA_PKT:
-			len = HCI_MAX_FRAME_SIZE;
-			hlen = HCI_ACL_HDR_SIZE;
-			break;
-		case HCI_EVENT_PKT:
-			len = HCI_MAX_EVENT_SIZE;
-			hlen = HCI_EVENT_HDR_SIZE;
-			break;
-		case HCI_SCODATA_PKT:
-			len = HCI_MAX_SCO_SIZE;
-			hlen = HCI_SCO_HDR_SIZE;
-			break;
-		}
-
-		skb = bt_skb_alloc(len, GFP_ATOMIC);
-		if (!skb)
-			return -ENOMEM;
-
-		scb = (void *) skb->cb;
-		scb->expect = hlen;
-		scb->pkt_type = type;
-
-		hdev->reassembly[index] = skb;
-	}
-
-	while (count) {
-		scb = (void *) skb->cb;
-		len = min_t(uint, scb->expect, count);
-
-		memcpy(skb_put(skb, len), data, len);
-
-		count -= len;
-		data += len;
-		scb->expect -= len;
-		remain = count;
-
-		switch (type) {
-		case HCI_EVENT_PKT:
-			if (skb->len == HCI_EVENT_HDR_SIZE) {
-				struct hci_event_hdr *h = hci_event_hdr(skb);
-				scb->expect = h->plen;
-
-				if (skb_tailroom(skb) < scb->expect) {
-					kfree_skb(skb);
-					hdev->reassembly[index] = NULL;
-					return -ENOMEM;
-				}
-			}
-			break;
-
-		case HCI_ACLDATA_PKT:
-			if (skb->len  == HCI_ACL_HDR_SIZE) {
-				struct hci_acl_hdr *h = hci_acl_hdr(skb);
-				scb->expect = __le16_to_cpu(h->dlen);
-
-				if (skb_tailroom(skb) < scb->expect) {
-					kfree_skb(skb);
-					hdev->reassembly[index] = NULL;
-					return -ENOMEM;
-				}
-			}
-			break;
-
-		case HCI_SCODATA_PKT:
-			if (skb->len == HCI_SCO_HDR_SIZE) {
-				struct hci_sco_hdr *h = hci_sco_hdr(skb);
-				scb->expect = h->dlen;
-
-				if (skb_tailroom(skb) < scb->expect) {
-					kfree_skb(skb);
-					hdev->reassembly[index] = NULL;
-					return -ENOMEM;
-				}
-			}
-			break;
-		}
-
-		if (scb->expect == 0) {
-			/* Complete frame */
-
-			bt_cb(skb)->pkt_type = type;
-			hci_recv_frame(hdev, skb);
-
-			hdev->reassembly[index] = NULL;
-			return remain;
-		}
-	}
-
-	return remain;
-}
-
-#define STREAM_REASSEMBLY 0
-
-int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count)
-{
-	int type;
-	int rem = 0;
-
-	while (count) {
-		struct sk_buff *skb = hdev->reassembly[STREAM_REASSEMBLY];
-
-		if (!skb) {
-			struct { char type; } *pkt;
-
-			/* Start of the frame */
-			pkt = data;
-			type = pkt->type;
-
-			data++;
-			count--;
-		} else
-			type = bt_cb(skb)->pkt_type;
-
-		rem = hci_reassembly(hdev, type, data, count,
-				     STREAM_REASSEMBLY);
-		if (rem < 0)
-			return rem;
-
-		data += (count - rem);
-		count = rem;
-	}
-
-	return rem;
-}
-EXPORT_SYMBOL(hci_recv_stream_fragment);
-
 /* ---- Interface to upper protocols ---- */
 
 int hci_register_cb(struct hci_cb *cb)
 {
 	BT_DBG("%p name %s", cb, cb->name);
 
-	write_lock(&hci_cb_list_lock);
-	list_add(&cb->list, &hci_cb_list);
-	write_unlock(&hci_cb_list_lock);
+	mutex_lock(&hci_cb_list_lock);
+	list_add_tail(&cb->list, &hci_cb_list);
+	mutex_unlock(&hci_cb_list_lock);
 
 	return 0;
 }
@@ -4504,9 +3335,9 @@
 {
 	BT_DBG("%p name %s", cb, cb->name);
 
-	write_lock(&hci_cb_list_lock);
+	mutex_lock(&hci_cb_list_lock);
 	list_del(&cb->list);
-	write_unlock(&hci_cb_list_lock);
+	mutex_unlock(&hci_cb_list_lock);
 
 	return 0;
 }
@@ -4539,76 +3370,6 @@
 	}
 }
 
-void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
-{
-	skb_queue_head_init(&req->cmd_q);
-	req->hdev = hdev;
-	req->err = 0;
-}
-
-int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
-{
-	struct hci_dev *hdev = req->hdev;
-	struct sk_buff *skb;
-	unsigned long flags;
-
-	BT_DBG("length %u", skb_queue_len(&req->cmd_q));
-
-	/* If an error occurred during request building, remove all HCI
-	 * commands queued on the HCI request queue.
-	 */
-	if (req->err) {
-		skb_queue_purge(&req->cmd_q);
-		return req->err;
-	}
-
-	/* Do not allow empty requests */
-	if (skb_queue_empty(&req->cmd_q))
-		return -ENODATA;
-
-	skb = skb_peek_tail(&req->cmd_q);
-	bt_cb(skb)->req.complete = complete;
-
-	spin_lock_irqsave(&hdev->cmd_q.lock, flags);
-	skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
-	spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
-
-	queue_work(hdev->workqueue, &hdev->cmd_work);
-
-	return 0;
-}
-
-bool hci_req_pending(struct hci_dev *hdev)
-{
-	return (hdev->req_status == HCI_REQ_PEND);
-}
-
-static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode,
-				       u32 plen, const void *param)
-{
-	int len = HCI_COMMAND_HDR_SIZE + plen;
-	struct hci_command_hdr *hdr;
-	struct sk_buff *skb;
-
-	skb = bt_skb_alloc(len, GFP_ATOMIC);
-	if (!skb)
-		return NULL;
-
-	hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE);
-	hdr->opcode = cpu_to_le16(opcode);
-	hdr->plen   = plen;
-
-	if (plen)
-		memcpy(skb_put(skb, plen), param, plen);
-
-	BT_DBG("skb len %d", skb->len);
-
-	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
-	bt_cb(skb)->opcode = opcode;
-
-	return skb;
-}
-
 /* Send HCI command */
 int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
 		 const void *param)
@@ -4634,43 +3395,6 @@
 	return 0;
 }
 
-/* Queue a command to an asynchronous HCI request */
-void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
-		    const void *param, u8 event)
-{
-	struct hci_dev *hdev = req->hdev;
-	struct sk_buff *skb;
-
-	BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
-
-	/* If an error occurred during request building, there is no point in
-	 * queueing the HCI command. We can simply return.
-	 */
-	if (req->err)
-		return;
-
-	skb = hci_prepare_cmd(hdev, opcode, plen, param);
-	if (!skb) {
-		BT_ERR("%s no memory for command (opcode 0x%4.4x)",
-		       hdev->name, opcode);
-		req->err = -ENOMEM;
-		return;
-	}
-
-	if (skb_queue_empty(&req->cmd_q))
-		bt_cb(skb)->req.start = true;
-
-	bt_cb(skb)->req.event = event;
-
-	skb_queue_tail(&req->cmd_q, skb);
-}
-
-void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
-		 const void *param)
-{
-	hci_req_add_ev(req, opcode, plen, param, 0);
-}
-
 /* Get data from the previously sent command */
 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
 {
@@ -5020,7 +3744,7 @@
 
 static void __check_timeout(struct hci_dev *hdev, unsigned int cnt)
 {
-	if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+	if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
 		/* ACL tx timeout must be longer than maximum
 		 * link supervision timeout (40.9 seconds) */
 		if (!cnt && time_after(jiffies, hdev->acl_last_tx +
@@ -5203,7 +3927,7 @@
 	if (!hci_conn_num(hdev, LE_LINK))
 		return;
 
-	if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+	if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
 		/* LE tx timeout must be longer than maximum
 		 * link supervision timeout (40.9 seconds) */
 		if (!hdev->le_cnt && hdev->le_pkts &&
@@ -5251,7 +3975,7 @@
 	BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
 	       hdev->sco_cnt, hdev->le_cnt);
 
-	if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+	if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
 		/* Schedule queues and send stuff to HCI driver */
 		hci_sched_acl(hdev);
 		hci_sched_sco(hdev);
@@ -5366,9 +4090,10 @@
 	queue_work(hdev->workqueue, &hdev->cmd_work);
 }
 
-void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status)
+void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
+			  hci_req_complete_t *req_complete,
+			  hci_req_complete_skb_t *req_complete_skb)
 {
-	hci_req_complete_t req_complete = NULL;
 	struct sk_buff *skb;
 	unsigned long flags;
 
@@ -5400,18 +4125,14 @@
 	 * callback would be found in hdev->sent_cmd instead of the
 	 * command queue (hdev->cmd_q).
 	 */
-	if (hdev->sent_cmd) {
-		req_complete = bt_cb(hdev->sent_cmd)->req.complete;
+	if (bt_cb(hdev->sent_cmd)->req.complete) {
+		*req_complete = bt_cb(hdev->sent_cmd)->req.complete;
+		return;
+	}
 
-		if (req_complete) {
-			/* We must set the complete callback to NULL to
-			 * avoid calling the callback more than once if
-			 * this function gets called again.
-			 */
-			bt_cb(hdev->sent_cmd)->req.complete = NULL;
-
-			goto call_complete;
-		}
+	if (bt_cb(hdev->sent_cmd)->req.complete_skb) {
+		*req_complete_skb = bt_cb(hdev->sent_cmd)->req.complete_skb;
+		return;
 	}
 
 	/* Remove all pending commands belonging to this request */
@@ -5422,14 +4143,11 @@
 			break;
 		}
 
-		req_complete = bt_cb(skb)->req.complete;
+		*req_complete = bt_cb(skb)->req.complete;
+		*req_complete_skb = bt_cb(skb)->req.complete_skb;
 		kfree_skb(skb);
 	}
 	spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
-
-call_complete:
-	if (req_complete)
-		req_complete(hdev, status);
 }
 
 static void hci_rx_work(struct work_struct *work)
@@ -5448,7 +4166,7 @@
 			hci_send_to_sock(hdev, skb);
 		}
 
-		if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+		if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
 			kfree_skb(skb);
 			continue;
 		}
@@ -5518,302 +4236,3 @@
 		}
 	}
 }
-
-void hci_req_add_le_scan_disable(struct hci_request *req)
-{
-	struct hci_cp_le_set_scan_enable cp;
-
-	memset(&cp, 0, sizeof(cp));
-	cp.enable = LE_SCAN_DISABLE;
-	hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
-}
-
-static void add_to_white_list(struct hci_request *req,
-			      struct hci_conn_params *params)
-{
-	struct hci_cp_le_add_to_white_list cp;
-
-	cp.bdaddr_type = params->addr_type;
-	bacpy(&cp.bdaddr, &params->addr);
-
-	hci_req_add(req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(cp), &cp);
-}
-
-static u8 update_white_list(struct hci_request *req)
-{
-	struct hci_dev *hdev = req->hdev;
-	struct hci_conn_params *params;
-	struct bdaddr_list *b;
-	uint8_t white_list_entries = 0;
-
-	/* Go through the current white list programmed into the
-	 * controller one by one and check if that address is still
-	 * in the list of pending connections or list of devices to
-	 * report. If not present in either list, then queue the
-	 * command to remove it from the controller.
-	 */
-	list_for_each_entry(b, &hdev->le_white_list, list) {
-		struct hci_cp_le_del_from_white_list cp;
-
-		if (hci_pend_le_action_lookup(&hdev->pend_le_conns,
-					      &b->bdaddr, b->bdaddr_type) ||
-		    hci_pend_le_action_lookup(&hdev->pend_le_reports,
-					      &b->bdaddr, b->bdaddr_type)) {
-			white_list_entries++;
-			continue;
-		}
-
-		cp.bdaddr_type = b->bdaddr_type;
-		bacpy(&cp.bdaddr, &b->bdaddr);
-
-		hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST,
-			    sizeof(cp), &cp);
-	}
-
-	/* Since all no longer valid white list entries have been
-	 * removed, walk through the list of pending connections
-	 * and ensure that any new device gets programmed into
-	 * the controller.
-	 *
-	 * If the list of the devices is larger than the list of
-	 * available white list entries in the controller, then
-	 * just abort and return filer policy value to not use the
-	 * white list.
-	 */
-	list_for_each_entry(params, &hdev->pend_le_conns, action) {
-		if (hci_bdaddr_list_lookup(&hdev->le_white_list,
-					   &params->addr, params->addr_type))
-			continue;
-
-		if (white_list_entries >= hdev->le_white_list_size) {
-			/* Select filter policy to accept all advertising */
-			return 0x00;
-		}
-
-		if (hci_find_irk_by_addr(hdev, &params->addr,
-					 params->addr_type)) {
-			/* White list can not be used with RPAs */
-			return 0x00;
-		}
-
-		white_list_entries++;
-		add_to_white_list(req, params);
-	}
-
-	/* After adding all new pending connections, walk through
-	 * the list of pending reports and also add these to the
-	 * white list if there is still space.
-	 */
-	list_for_each_entry(params, &hdev->pend_le_reports, action) {
-		if (hci_bdaddr_list_lookup(&hdev->le_white_list,
-					   &params->addr, params->addr_type))
-			continue;
-
-		if (white_list_entries >= hdev->le_white_list_size) {
-			/* Select filter policy to accept all advertising */
-			return 0x00;
-		}
-
-		if (hci_find_irk_by_addr(hdev, &params->addr,
-					 params->addr_type)) {
-			/* White list can not be used with RPAs */
-			return 0x00;
-		}
-
-		white_list_entries++;
-		add_to_white_list(req, params);
-	}
-
-	/* Select filter policy to use white list */
-	return 0x01;
-}
-
-void hci_req_add_le_passive_scan(struct hci_request *req)
-{
-	struct hci_cp_le_set_scan_param param_cp;
-	struct hci_cp_le_set_scan_enable enable_cp;
-	struct hci_dev *hdev = req->hdev;
-	u8 own_addr_type;
-	u8 filter_policy;
-
-	/* Set require_privacy to false since no SCAN_REQ are send
-	 * during passive scanning. Not using an non-resolvable address
-	 * here is important so that peer devices using direct
-	 * advertising with our address will be correctly reported
-	 * by the controller.
-	 */
-	if (hci_update_random_address(req, false, &own_addr_type))
-		return;
-
-	/* Adding or removing entries from the white list must
-	 * happen before enabling scanning. The controller does
-	 * not allow white list modification while scanning.
-	 */
-	filter_policy = update_white_list(req);
-
-	/* When the controller is using random resolvable addresses and
-	 * with that having LE privacy enabled, then controllers with
-	 * Extended Scanner Filter Policies support can now enable support
-	 * for handling directed advertising.
-	 *
-	 * So instead of using filter polices 0x00 (no whitelist)
-	 * and 0x01 (whitelist enabled) use the new filter policies
-	 * 0x02 (no whitelist) and 0x03 (whitelist enabled).
-	 */
-	if (test_bit(HCI_PRIVACY, &hdev->dev_flags) &&
-	    (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY))
-		filter_policy |= 0x02;
-
-	memset(&param_cp, 0, sizeof(param_cp));
-	param_cp.type = LE_SCAN_PASSIVE;
-	param_cp.interval = cpu_to_le16(hdev->le_scan_interval);
-	param_cp.window = cpu_to_le16(hdev->le_scan_window);
-	param_cp.own_address_type = own_addr_type;
-	param_cp.filter_policy = filter_policy;
-	hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
-		    &param_cp);
-
-	memset(&enable_cp, 0, sizeof(enable_cp));
-	enable_cp.enable = LE_SCAN_ENABLE;
-	enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
-	hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
-		    &enable_cp);
-}
-
-static void update_background_scan_complete(struct hci_dev *hdev, u8 status)
-{
-	if (status)
-		BT_DBG("HCI request failed to update background scanning: "
-		       "status 0x%2.2x", status);
-}
-
-/* This function controls the background scanning based on hdev->pend_le_conns
- * list. If there are pending LE connection we start the background scanning,
- * otherwise we stop it.
- *
- * This function requires the caller holds hdev->lock.
- */
-void hci_update_background_scan(struct hci_dev *hdev)
-{
-	struct hci_request req;
-	struct hci_conn *conn;
-	int err;
-
-	if (!test_bit(HCI_UP, &hdev->flags) ||
-	    test_bit(HCI_INIT, &hdev->flags) ||
-	    test_bit(HCI_SETUP, &hdev->dev_flags) ||
-	    test_bit(HCI_CONFIG, &hdev->dev_flags) ||
-	    test_bit(HCI_AUTO_OFF, &hdev->dev_flags) ||
-	    test_bit(HCI_UNREGISTER, &hdev->dev_flags))
-		return;
-
-	/* No point in doing scanning if LE support hasn't been enabled */
-	if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
-		return;
-
-	/* If discovery is active don't interfere with it */
-	if (hdev->discovery.state != DISCOVERY_STOPPED)
-		return;
-
-	/* Reset RSSI and UUID filters when starting background scanning
-	 * since these filters are meant for service discovery only.
-	 *
-	 * The Start Discovery and Start Service Discovery operations
-	 * ensure to set proper values for RSSI threshold and UUID
-	 * filter list. So it is safe to just reset them here.
-	 */
-	hci_discovery_filter_clear(hdev);
-
-	hci_req_init(&req, hdev);
-
-	if (list_empty(&hdev->pend_le_conns) &&
-	    list_empty(&hdev->pend_le_reports)) {
-		/* If there is no pending LE connections or devices
-		 * to be scanned for, we should stop the background
-		 * scanning.
-		 */
-
-		/* If controller is not scanning we are done. */
-		if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags))
-			return;
-
-		hci_req_add_le_scan_disable(&req);
-
-		BT_DBG("%s stopping background scanning", hdev->name);
-	} else {
-		/* If there is at least one pending LE connection, we should
-		 * keep the background scan running.
-		 */
-
-		/* If controller is connecting, we should not start scanning
-		 * since some controllers are not able to scan and connect at
-		 * the same time.
-		 */
-		conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
-		if (conn)
-			return;
-
-		/* If controller is currently scanning, we stop it to ensure we
-		 * don't miss any advertising (due to duplicates filter).
-		 */
-		if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
-			hci_req_add_le_scan_disable(&req);
-
-		hci_req_add_le_passive_scan(&req);
-
-		BT_DBG("%s starting background scanning", hdev->name);
-	}
-
-	err = hci_req_run(&req, update_background_scan_complete);
-	if (err)
-		BT_ERR("Failed to run HCI request: err %d", err);
-}
-
-static bool disconnected_whitelist_entries(struct hci_dev *hdev)
-{
-	struct bdaddr_list *b;
-
-	list_for_each_entry(b, &hdev->whitelist, list) {
-		struct hci_conn *conn;
-
-		conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &b->bdaddr);
-		if (!conn)
-			return true;
-
-		if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
-			return true;
-	}
-
-	return false;
-}
-
-void hci_update_page_scan(struct hci_dev *hdev, struct hci_request *req)
-{
-	u8 scan;
-
-	if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
-		return;
-
-	if (!hdev_is_powered(hdev))
-		return;
-
-	if (mgmt_powering_down(hdev))
-		return;
-
-	if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) ||
-	    disconnected_whitelist_entries(hdev))
-		scan = SCAN_PAGE;
-	else
-		scan = SCAN_DISABLED;
-
-	if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE))
-		return;
-
-	if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
-		scan |= SCAN_INQUIRY;
-
-	if (req)
-		hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
-	else
-		hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
-}
diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
new file mode 100644
index 0000000..7db4220
--- /dev/null
+++ b/net/bluetooth/hci_debugfs.c
@@ -0,0 +1,1142 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL 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.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/debugfs.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_debugfs.h"
+
+#define DEFINE_QUIRK_ATTRIBUTE(__name, __quirk)				      \
+static ssize_t __name ## _read(struct file *file,			      \
+				char __user *user_buf,			      \
+				size_t count, loff_t *ppos)		      \
+{									      \
+	struct hci_dev *hdev = file->private_data;			      \
+	char buf[3];							      \
+									      \
+	buf[0] = test_bit(__quirk, &hdev->quirks) ? 'Y' : 'N';		      \
+	buf[1] = '\n';							      \
+	buf[2] = '\0';							      \
+	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);	      \
+}									      \
+									      \
+static ssize_t __name ## _write(struct file *file,			      \
+				 const char __user *user_buf,		      \
+				 size_t count, loff_t *ppos)		      \
+{									      \
+	struct hci_dev *hdev = file->private_data;			      \
+	char buf[32];							      \
+	size_t buf_size = min(count, (sizeof(buf) - 1));		      \
+	bool enable;							      \
+									      \
+	if (test_bit(HCI_UP, &hdev->flags))				      \
+		return -EBUSY;						      \
+									      \
+	if (copy_from_user(buf, user_buf, buf_size))			      \
+		return -EFAULT;						      \
+									      \
+	buf[buf_size] = '\0';						      \
+	if (strtobool(buf, &enable))					      \
+		return -EINVAL;						      \
+									      \
+	if (enable == test_bit(__quirk, &hdev->quirks))			      \
+		return -EALREADY;					      \
+									      \
+	change_bit(__quirk, &hdev->quirks);				      \
+									      \
+	return count;							      \
+}									      \
+									      \
+static const struct file_operations __name ## _fops = {			      \
+	.open		= simple_open,					      \
+	.read		= __name ## _read,				      \
+	.write		= __name ## _write,				      \
+	.llseek		= default_llseek,				      \
+}									      \
+
+static int features_show(struct seq_file *f, void *ptr)
+{
+	struct hci_dev *hdev = f->private;
+	u8 p;
+
+	hci_dev_lock(hdev);
+	for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) {
+		seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+			   "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p,
+			   hdev->features[p][0], hdev->features[p][1],
+			   hdev->features[p][2], hdev->features[p][3],
+			   hdev->features[p][4], hdev->features[p][5],
+			   hdev->features[p][6], hdev->features[p][7]);
+	}
+	if (lmp_le_capable(hdev))
+		seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+			   "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+			   hdev->le_features[0], hdev->le_features[1],
+			   hdev->le_features[2], hdev->le_features[3],
+			   hdev->le_features[4], hdev->le_features[5],
+			   hdev->le_features[6], hdev->le_features[7]);
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int features_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, features_show, inode->i_private);
+}
+
+static const struct file_operations features_fops = {
+	.open		= features_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int device_id_show(struct seq_file *f, void *ptr)
+{
+	struct hci_dev *hdev = f->private;
+
+	hci_dev_lock(hdev);
+	seq_printf(f, "%4.4x:%4.4x:%4.4x:%4.4x\n", hdev->devid_source,
+		  hdev->devid_vendor, hdev->devid_product, hdev->devid_version);
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int device_id_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, device_id_show, inode->i_private);
+}
+
+static const struct file_operations device_id_fops = {
+	.open		= device_id_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int device_list_show(struct seq_file *f, void *ptr)
+{
+	struct hci_dev *hdev = f->private;
+	struct hci_conn_params *p;
+	struct bdaddr_list *b;
+
+	hci_dev_lock(hdev);
+	list_for_each_entry(b, &hdev->whitelist, list)
+		seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+	list_for_each_entry(p, &hdev->le_conn_params, list) {
+		seq_printf(f, "%pMR (type %u) %u\n", &p->addr, p->addr_type,
+			   p->auto_connect);
+	}
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int device_list_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, device_list_show, inode->i_private);
+}
+
+static const struct file_operations device_list_fops = {
+	.open		= device_list_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int blacklist_show(struct seq_file *f, void *p)
+{
+	struct hci_dev *hdev = f->private;
+	struct bdaddr_list *b;
+
+	hci_dev_lock(hdev);
+	list_for_each_entry(b, &hdev->blacklist, list)
+		seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int blacklist_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, blacklist_show, inode->i_private);
+}
+
+static const struct file_operations blacklist_fops = {
+	.open		= blacklist_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int uuids_show(struct seq_file *f, void *p)
+{
+	struct hci_dev *hdev = f->private;
+	struct bt_uuid *uuid;
+
+	hci_dev_lock(hdev);
+	list_for_each_entry(uuid, &hdev->uuids, list) {
+		u8 i, val[16];
+
+		/* The Bluetooth UUID values are stored in big endian,
+		 * but with reversed byte order. So convert them into
+		 * the right order for the %pUb modifier.
+		 */
+		for (i = 0; i < 16; i++)
+			val[i] = uuid->uuid[15 - i];
+
+		seq_printf(f, "%pUb\n", val);
+	}
+	hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int uuids_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, uuids_show, inode->i_private);
+}
+
+static const struct file_operations uuids_fops = {
+	.open		= uuids_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int remote_oob_show(struct seq_file *f, void *ptr)
+{
+	struct hci_dev *hdev = f->private;
+	struct oob_data *data;
+
+	hci_dev_lock(hdev);
+	list_for_each_entry(data, &hdev->remote_oob_data, list) {
+		seq_printf(f, "%pMR (type %u) %u %*phN %*phN %*phN %*phN\n",
+			   &data->bdaddr, data->bdaddr_type, data->present,
+			   16, data->hash192, 16, data->rand192,
+			   16, data->hash256, 16, data->rand256);
+	}
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int remote_oob_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, remote_oob_show, inode->i_private);
+}
+
+static const struct file_operations remote_oob_fops = {
+	.open		= remote_oob_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+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 ssize_t use_debug_keys_read(struct file *file, char __user *user_buf,
+				   size_t count, loff_t *ppos)
+{
+	struct hci_dev *hdev = file->private_data;
+	char buf[3];
+
+	buf[0] = hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS) ? 'Y': 'N';
+	buf[1] = '\n';
+	buf[2] = '\0';
+	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations use_debug_keys_fops = {
+	.open		= simple_open,
+	.read		= use_debug_keys_read,
+	.llseek		= default_llseek,
+};
+
+static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
+				 size_t count, loff_t *ppos)
+{
+	struct hci_dev *hdev = file->private_data;
+	char buf[3];
+
+	buf[0] = hci_dev_test_flag(hdev, HCI_SC_ONLY) ? 'Y': 'N';
+	buf[1] = '\n';
+	buf[2] = '\0';
+	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations sc_only_mode_fops = {
+	.open		= simple_open,
+	.read		= sc_only_mode_read,
+	.llseek		= default_llseek,
+};
+
+void hci_debugfs_create_common(struct hci_dev *hdev)
+{
+	debugfs_create_file("features", 0444, hdev->debugfs, hdev,
+			    &features_fops);
+	debugfs_create_u16("manufacturer", 0444, hdev->debugfs,
+			   &hdev->manufacturer);
+	debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
+	debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
+	debugfs_create_u8("hardware_error", 0444, hdev->debugfs,
+			  &hdev->hw_error_code);
+	debugfs_create_file("device_id", 0444, hdev->debugfs, hdev,
+			    &device_id_fops);
+
+	debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
+			    &device_list_fops);
+	debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
+			    &blacklist_fops);
+	debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
+	debugfs_create_file("remote_oob", 0400, hdev->debugfs, hdev,
+			    &remote_oob_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_ssp_capable(hdev) || lmp_le_capable(hdev))
+		debugfs_create_file("use_debug_keys", 0444, hdev->debugfs,
+				    hdev, &use_debug_keys_fops);
+
+	if (lmp_sc_capable(hdev) || lmp_le_capable(hdev))
+		debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
+				    hdev, &sc_only_mode_fops);
+}
+
+static int inquiry_cache_show(struct seq_file *f, void *p)
+{
+	struct hci_dev *hdev = f->private;
+	struct discovery_state *cache = &hdev->discovery;
+	struct inquiry_entry *e;
+
+	hci_dev_lock(hdev);
+
+	list_for_each_entry(e, &cache->all, all) {
+		struct inquiry_data *data = &e->data;
+		seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
+			   &data->bdaddr,
+			   data->pscan_rep_mode, data->pscan_period_mode,
+			   data->pscan_mode, data->dev_class[2],
+			   data->dev_class[1], data->dev_class[0],
+			   __le16_to_cpu(data->clock_offset),
+			   data->rssi, data->ssp_mode, e->timestamp);
+	}
+
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int inquiry_cache_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, inquiry_cache_show, inode->i_private);
+}
+
+static const struct file_operations inquiry_cache_fops = {
+	.open		= inquiry_cache_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int link_keys_show(struct seq_file *f, void *ptr)
+{
+	struct hci_dev *hdev = f->private;
+	struct link_key *key;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(key, &hdev->link_keys, list)
+		seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
+			   HCI_LINK_KEY_SIZE, key->val, key->pin_len);
+	rcu_read_unlock();
+
+	return 0;
+}
+
+static int link_keys_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, link_keys_show, inode->i_private);
+}
+
+static const struct file_operations link_keys_fops = {
+	.open		= link_keys_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int dev_class_show(struct seq_file *f, void *ptr)
+{
+	struct hci_dev *hdev = f->private;
+
+	hci_dev_lock(hdev);
+	seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
+		   hdev->dev_class[1], hdev->dev_class[0]);
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int dev_class_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dev_class_show, inode->i_private);
+}
+
+static const struct file_operations dev_class_fops = {
+	.open		= dev_class_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int voice_setting_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->voice_setting;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get,
+			NULL, "0x%4.4llx\n");
+
+static ssize_t ssp_debug_mode_read(struct file *file, char __user *user_buf,
+				   size_t count, loff_t *ppos)
+{
+	struct hci_dev *hdev = file->private_data;
+	char buf[3];
+
+	buf[0] = hdev->ssp_debug_mode ? 'Y': 'N';
+	buf[1] = '\n';
+	buf[2] = '\0';
+	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations ssp_debug_mode_fops = {
+	.open		= simple_open,
+	.read		= ssp_debug_mode_read,
+	.llseek		= default_llseek,
+};
+
+static int auto_accept_delay_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	hdev->auto_accept_delay = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int auto_accept_delay_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->auto_accept_delay;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
+			auto_accept_delay_set, "%llu\n");
+
+static int idle_timeout_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	if (val != 0 && (val < 500 || val > 3600000))
+		return -EINVAL;
+
+	hci_dev_lock(hdev);
+	hdev->idle_timeout = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int idle_timeout_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->idle_timeout;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
+			idle_timeout_set, "%llu\n");
+
+static int sniff_min_interval_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
+		return -EINVAL;
+
+	hci_dev_lock(hdev);
+	hdev->sniff_min_interval = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int sniff_min_interval_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->sniff_min_interval;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get,
+			sniff_min_interval_set, "%llu\n");
+
+static int sniff_max_interval_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
+		return -EINVAL;
+
+	hci_dev_lock(hdev);
+	hdev->sniff_max_interval = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int sniff_max_interval_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->sniff_max_interval;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
+			sniff_max_interval_set, "%llu\n");
+
+void hci_debugfs_create_bredr(struct hci_dev *hdev)
+{
+	debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, hdev,
+			    &inquiry_cache_fops);
+	debugfs_create_file("link_keys", 0400, hdev->debugfs, hdev,
+			    &link_keys_fops);
+	debugfs_create_file("dev_class", 0444, hdev->debugfs, hdev,
+			    &dev_class_fops);
+	debugfs_create_file("voice_setting", 0444, hdev->debugfs, hdev,
+			    &voice_setting_fops);
+
+	if (lmp_ssp_capable(hdev)) {
+		debugfs_create_file("ssp_debug_mode", 0444, hdev->debugfs,
+				    hdev, &ssp_debug_mode_fops);
+		debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
+				    hdev, &auto_accept_delay_fops);
+	}
+
+	if (lmp_sniff_capable(hdev)) {
+		debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
+				    hdev, &idle_timeout_fops);
+		debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs,
+				    hdev, &sniff_min_interval_fops);
+		debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs,
+				    hdev, &sniff_max_interval_fops);
+	}
+}
+
+static int identity_show(struct seq_file *f, void *p)
+{
+	struct hci_dev *hdev = f->private;
+	bdaddr_t addr;
+	u8 addr_type;
+
+	hci_dev_lock(hdev);
+
+	hci_copy_identity_address(hdev, &addr, &addr_type);
+
+	seq_printf(f, "%pMR (type %u) %*phN %pMR\n", &addr, addr_type,
+		   16, hdev->irk, &hdev->rpa);
+
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int identity_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, identity_show, inode->i_private);
+}
+
+static const struct file_operations identity_fops = {
+	.open		= identity_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int rpa_timeout_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	/* Require the RPA timeout to be at least 30 seconds and at most
+	 * 24 hours.
+	 */
+	if (val < 30 || val > (60 * 60 * 24))
+		return -EINVAL;
+
+	hci_dev_lock(hdev);
+	hdev->rpa_timeout = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int rpa_timeout_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->rpa_timeout;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(rpa_timeout_fops, rpa_timeout_get,
+			rpa_timeout_set, "%llu\n");
+
+static int random_address_show(struct seq_file *f, void *p)
+{
+	struct hci_dev *hdev = f->private;
+
+	hci_dev_lock(hdev);
+	seq_printf(f, "%pMR\n", &hdev->random_addr);
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int random_address_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, random_address_show, inode->i_private);
+}
+
+static const struct file_operations random_address_fops = {
+	.open		= random_address_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int static_address_show(struct seq_file *f, void *p)
+{
+	struct hci_dev *hdev = f->private;
+
+	hci_dev_lock(hdev);
+	seq_printf(f, "%pMR\n", &hdev->static_addr);
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int static_address_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, static_address_show, inode->i_private);
+}
+
+static const struct file_operations static_address_fops = {
+	.open		= static_address_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static ssize_t force_static_address_read(struct file *file,
+					 char __user *user_buf,
+					 size_t count, loff_t *ppos)
+{
+	struct hci_dev *hdev = file->private_data;
+	char buf[3];
+
+	buf[0] = hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ? 'Y': 'N';
+	buf[1] = '\n';
+	buf[2] = '\0';
+	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_static_address_write(struct file *file,
+					  const char __user *user_buf,
+					  size_t count, loff_t *ppos)
+{
+	struct hci_dev *hdev = file->private_data;
+	char buf[32];
+	size_t buf_size = min(count, (sizeof(buf)-1));
+	bool enable;
+
+	if (test_bit(HCI_UP, &hdev->flags))
+		return -EBUSY;
+
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+
+	buf[buf_size] = '\0';
+	if (strtobool(buf, &enable))
+		return -EINVAL;
+
+	if (enable == hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR))
+		return -EALREADY;
+
+	hci_dev_change_flag(hdev, HCI_FORCE_STATIC_ADDR);
+
+	return count;
+}
+
+static const struct file_operations force_static_address_fops = {
+	.open		= simple_open,
+	.read		= force_static_address_read,
+	.write		= force_static_address_write,
+	.llseek		= default_llseek,
+};
+
+static int white_list_show(struct seq_file *f, void *ptr)
+{
+	struct hci_dev *hdev = f->private;
+	struct bdaddr_list *b;
+
+	hci_dev_lock(hdev);
+	list_for_each_entry(b, &hdev->le_white_list, list)
+		seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int white_list_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, white_list_show, inode->i_private);
+}
+
+static const struct file_operations white_list_fops = {
+	.open		= white_list_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
+{
+	struct hci_dev *hdev = f->private;
+	struct smp_irk *irk;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
+		seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
+			   &irk->bdaddr, irk->addr_type,
+			   16, irk->val, &irk->rpa);
+	}
+	rcu_read_unlock();
+
+	return 0;
+}
+
+static int identity_resolving_keys_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, identity_resolving_keys_show,
+			   inode->i_private);
+}
+
+static const struct file_operations identity_resolving_keys_fops = {
+	.open		= identity_resolving_keys_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int long_term_keys_show(struct seq_file *f, void *ptr)
+{
+	struct hci_dev *hdev = f->private;
+	struct smp_ltk *ltk;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list)
+		seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
+			   &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
+			   ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
+			   __le64_to_cpu(ltk->rand), 16, ltk->val);
+	rcu_read_unlock();
+
+	return 0;
+}
+
+static int long_term_keys_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, long_term_keys_show, inode->i_private);
+}
+
+static const struct file_operations long_term_keys_fops = {
+	.open		= long_term_keys_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int conn_min_interval_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
+		return -EINVAL;
+
+	hci_dev_lock(hdev);
+	hdev->le_conn_min_interval = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int conn_min_interval_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->le_conn_min_interval;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get,
+			conn_min_interval_set, "%llu\n");
+
+static int conn_max_interval_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
+		return -EINVAL;
+
+	hci_dev_lock(hdev);
+	hdev->le_conn_max_interval = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int conn_max_interval_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->le_conn_max_interval;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
+			conn_max_interval_set, "%llu\n");
+
+static int conn_latency_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	if (val > 0x01f3)
+		return -EINVAL;
+
+	hci_dev_lock(hdev);
+	hdev->le_conn_latency = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int conn_latency_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->le_conn_latency;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_latency_fops, conn_latency_get,
+			conn_latency_set, "%llu\n");
+
+static int supervision_timeout_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	if (val < 0x000a || val > 0x0c80)
+		return -EINVAL;
+
+	hci_dev_lock(hdev);
+	hdev->le_supv_timeout = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int supervision_timeout_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->le_supv_timeout;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(supervision_timeout_fops, supervision_timeout_get,
+			supervision_timeout_set, "%llu\n");
+
+static int adv_channel_map_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	if (val < 0x01 || val > 0x07)
+		return -EINVAL;
+
+	hci_dev_lock(hdev);
+	hdev->le_adv_channel_map = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int adv_channel_map_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->le_adv_channel_map;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get,
+			adv_channel_map_set, "%llu\n");
+
+static int adv_min_interval_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval)
+		return -EINVAL;
+
+	hci_dev_lock(hdev);
+	hdev->le_adv_min_interval = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int adv_min_interval_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->le_adv_min_interval;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(adv_min_interval_fops, adv_min_interval_get,
+			adv_min_interval_set, "%llu\n");
+
+static int adv_max_interval_set(void *data, u64 val)
+{
+	struct hci_dev *hdev = data;
+
+	if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval)
+		return -EINVAL;
+
+	hci_dev_lock(hdev);
+	hdev->le_adv_max_interval = val;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+static int adv_max_interval_get(void *data, u64 *val)
+{
+	struct hci_dev *hdev = data;
+
+	hci_dev_lock(hdev);
+	*val = hdev->le_adv_max_interval;
+	hci_dev_unlock(hdev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get,
+			adv_max_interval_set, "%llu\n");
+
+DEFINE_QUIRK_ATTRIBUTE(quirk_strict_duplicate_filter,
+		       HCI_QUIRK_STRICT_DUPLICATE_FILTER);
+DEFINE_QUIRK_ATTRIBUTE(quirk_simultaneous_discovery,
+		       HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
+
+void hci_debugfs_create_le(struct hci_dev *hdev)
+{
+	debugfs_create_file("identity", 0400, hdev->debugfs, hdev,
+			    &identity_fops);
+	debugfs_create_file("rpa_timeout", 0644, hdev->debugfs, hdev,
+			    &rpa_timeout_fops);
+	debugfs_create_file("random_address", 0444, hdev->debugfs, hdev,
+			    &random_address_fops);
+	debugfs_create_file("static_address", 0444, hdev->debugfs, hdev,
+			    &static_address_fops);
+
+	/* For controllers with a public address, provide a debug
+	 * option to force the usage of the configured static
+	 * address. By default the public address is used.
+	 */
+	if (bacmp(&hdev->bdaddr, BDADDR_ANY))
+		debugfs_create_file("force_static_address", 0644,
+				    hdev->debugfs, hdev,
+				    &force_static_address_fops);
+
+	debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
+			  &hdev->le_white_list_size);
+	debugfs_create_file("white_list", 0444, hdev->debugfs, hdev,
+			    &white_list_fops);
+	debugfs_create_file("identity_resolving_keys", 0400, hdev->debugfs,
+			    hdev, &identity_resolving_keys_fops);
+	debugfs_create_file("long_term_keys", 0400, hdev->debugfs, hdev,
+			    &long_term_keys_fops);
+	debugfs_create_file("conn_min_interval", 0644, hdev->debugfs, hdev,
+			    &conn_min_interval_fops);
+	debugfs_create_file("conn_max_interval", 0644, hdev->debugfs, hdev,
+			    &conn_max_interval_fops);
+	debugfs_create_file("conn_latency", 0644, hdev->debugfs, hdev,
+			    &conn_latency_fops);
+	debugfs_create_file("supervision_timeout", 0644, hdev->debugfs, hdev,
+			    &supervision_timeout_fops);
+	debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, hdev,
+			    &adv_channel_map_fops);
+	debugfs_create_file("adv_min_interval", 0644, hdev->debugfs, hdev,
+			    &adv_min_interval_fops);
+	debugfs_create_file("adv_max_interval", 0644, hdev->debugfs, hdev,
+			    &adv_max_interval_fops);
+	debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs,
+			   &hdev->discov_interleaved_timeout);
+
+	debugfs_create_file("quirk_strict_duplicate_filter", 0644,
+			    hdev->debugfs, hdev,
+			    &quirk_strict_duplicate_filter_fops);
+	debugfs_create_file("quirk_simultaneous_discovery", 0644,
+			    hdev->debugfs, hdev,
+			    &quirk_simultaneous_discovery_fops);
+}
+
+void hci_debugfs_create_conn(struct hci_conn *conn)
+{
+	struct hci_dev *hdev = conn->hdev;
+	char name[6];
+
+	if (IS_ERR_OR_NULL(hdev->debugfs))
+		return;
+
+	snprintf(name, sizeof(name), "%u", conn->handle);
+	conn->debugfs = debugfs_create_dir(name, hdev->debugfs);
+}
diff --git a/net/bluetooth/hci_debugfs.h b/net/bluetooth/hci_debugfs.h
new file mode 100644
index 0000000..30ff541
--- /dev/null
+++ b/net/bluetooth/hci_debugfs.h
@@ -0,0 +1,48 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL 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.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#if IS_ENABLED(CPTCFG_BT_DEBUGFS)
+
+void hci_debugfs_create_common(struct hci_dev *hdev);
+void hci_debugfs_create_bredr(struct hci_dev *hdev);
+void hci_debugfs_create_le(struct hci_dev *hdev);
+void hci_debugfs_create_conn(struct hci_conn *conn);
+
+#else
+
+static inline void hci_debugfs_create_common(struct hci_dev *hdev)
+{
+}
+
+static inline void hci_debugfs_create_bredr(struct hci_dev *hdev)
+{
+}
+
+static inline void hci_debugfs_create_le(struct hci_dev *hdev)
+{
+}
+
+static inline void hci_debugfs_create_conn(struct hci_conn *conn)
+{
+}
+
+#endif
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 39a5c8a..7b61be7 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -30,10 +30,15 @@
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/mgmt.h>
 
+#include "hci_request.h"
+#include "hci_debugfs.h"
 #include "a2mp.h"
 #include "amp.h"
 #include "smp.h"
 
+#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
+		 "\x00\x00\x00\x00\x00\x00\x00\x00"
+
 /* Handle HCI Event packets */
 
 static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
@@ -65,7 +70,7 @@
 	if (status)
 		return;
 
-	set_bit(HCI_PERIODIC_INQ, &hdev->dev_flags);
+	hci_dev_set_flag(hdev, HCI_PERIODIC_INQ);
 }
 
 static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb)
@@ -77,7 +82,7 @@
 	if (status)
 		return;
 
-	clear_bit(HCI_PERIODIC_INQ, &hdev->dev_flags);
+	hci_dev_clear_flag(hdev, HCI_PERIODIC_INQ);
 
 	hci_conn_check_pending(hdev);
 }
@@ -193,9 +198,10 @@
 		return;
 
 	/* Reset all non-persistent flags */
-	hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
+	hci_dev_clear_volatile_flags(hdev);
 
-	hdev->discovery.state = DISCOVERY_STOPPED;
+	hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+
 	hdev->inq_tx_power = HCI_TX_POWER_INVALID;
 	hdev->adv_tx_power = HCI_TX_POWER_INVALID;
 
@@ -212,6 +218,40 @@
 	hci_bdaddr_list_clear(&hdev->le_white_list);
 }
 
+static void hci_cc_read_stored_link_key(struct hci_dev *hdev,
+					struct sk_buff *skb)
+{
+	struct hci_rp_read_stored_link_key *rp = (void *)skb->data;
+	struct hci_cp_read_stored_link_key *sent;
+
+	BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+	sent = hci_sent_cmd_data(hdev, HCI_OP_READ_STORED_LINK_KEY);
+	if (!sent)
+		return;
+
+	if (!rp->status && sent->read_all == 0x01) {
+		hdev->stored_max_keys = rp->max_keys;
+		hdev->stored_num_keys = rp->num_keys;
+	}
+}
+
+static void hci_cc_delete_stored_link_key(struct hci_dev *hdev,
+					  struct sk_buff *skb)
+{
+	struct hci_rp_delete_stored_link_key *rp = (void *)skb->data;
+
+	BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+	if (rp->status)
+		return;
+
+	if (rp->num_keys <= hdev->stored_num_keys)
+		hdev->stored_num_keys -= rp->num_keys;
+	else
+		hdev->stored_num_keys = 0;
+}
+
 static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	__u8 status = *((__u8 *) skb->data);
@@ -225,7 +265,7 @@
 
 	hci_dev_lock(hdev);
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_set_local_name_complete(hdev, sent, status);
 	else if (!status)
 		memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
@@ -242,7 +282,8 @@
 	if (rp->status)
 		return;
 
-	if (test_bit(HCI_SETUP, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_SETUP) ||
+	    hci_dev_test_flag(hdev, HCI_CONFIG))
 		memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH);
 }
 
@@ -268,7 +309,7 @@
 			clear_bit(HCI_AUTH, &hdev->flags);
 	}
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_auth_enable_complete(hdev, status);
 
 	hci_dev_unlock(hdev);
@@ -363,7 +404,7 @@
 	if (status == 0)
 		memcpy(hdev->dev_class, sent, 3);
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_set_class_of_dev_complete(hdev, sent, status);
 
 	hci_dev_unlock(hdev);
@@ -456,13 +497,13 @@
 			hdev->features[1][0] &= ~LMP_HOST_SSP;
 	}
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_ssp_enable_complete(hdev, sent->mode, status);
 	else if (!status) {
 		if (sent->mode)
-			set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+			hci_dev_set_flag(hdev, HCI_SSP_ENABLED);
 		else
-			clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+			hci_dev_clear_flag(hdev, HCI_SSP_ENABLED);
 	}
 
 	hci_dev_unlock(hdev);
@@ -488,13 +529,11 @@
 			hdev->features[1][0] &= ~LMP_HOST_SC;
 	}
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
-		mgmt_sc_enable_complete(hdev, sent->support, status);
-	else if (!status) {
+	if (!hci_dev_test_flag(hdev, HCI_MGMT) && !status) {
 		if (sent->support)
-			set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+			hci_dev_set_flag(hdev, HCI_SC_ENABLED);
 		else
-			clear_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+			hci_dev_clear_flag(hdev, HCI_SC_ENABLED);
 	}
 
 	hci_dev_unlock(hdev);
@@ -509,7 +548,8 @@
 	if (rp->status)
 		return;
 
-	if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_SETUP) ||
+	    hci_dev_test_flag(hdev, HCI_CONFIG)) {
 		hdev->hci_ver = rp->hci_ver;
 		hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
 		hdev->lmp_ver = rp->lmp_ver;
@@ -528,7 +568,8 @@
 	if (rp->status)
 		return;
 
-	if (test_bit(HCI_SETUP, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_SETUP) ||
+	    hci_dev_test_flag(hdev, HCI_CONFIG))
 		memcpy(hdev->commands, rp->commands, sizeof(hdev->commands));
 }
 
@@ -650,7 +691,7 @@
 	if (test_bit(HCI_INIT, &hdev->flags))
 		bacpy(&hdev->bdaddr, &rp->bdaddr);
 
-	if (test_bit(HCI_SETUP, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_SETUP))
 		bacpy(&hdev->setup_addr, &rp->bdaddr);
 }
 
@@ -859,7 +900,7 @@
 
 	hci_dev_lock(hdev);
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status);
 
 	if (rp->status)
@@ -885,7 +926,7 @@
 
 	hci_dev_lock(hdev);
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr,
 						 rp->status);
 
@@ -944,7 +985,7 @@
 
 	hci_dev_lock(hdev);
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0,
 						 rp->status);
 
@@ -960,7 +1001,7 @@
 
 	hci_dev_lock(hdev);
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr,
 						     ACL_LINK, 0, rp->status);
 
@@ -975,7 +1016,7 @@
 
 	hci_dev_lock(hdev);
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, ACL_LINK,
 						 0, rp->status);
 
@@ -991,7 +1032,7 @@
 
 	hci_dev_lock(hdev);
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr,
 						     ACL_LINK, 0, rp->status);
 
@@ -1004,11 +1045,6 @@
 	struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
 
 	BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
-
-	hci_dev_lock(hdev);
-	mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL,
-					  rp->status);
-	hci_dev_unlock(hdev);
 }
 
 static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
@@ -1017,15 +1053,8 @@
 	struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data;
 
 	BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
-
-	hci_dev_lock(hdev);
-	mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192,
-					  rp->hash256, rp->rand256,
-					  rp->status);
-	hci_dev_unlock(hdev);
 }
 
-
 static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	__u8 status = *((__u8 *) skb->data);
@@ -1068,7 +1097,7 @@
 	if (*sent) {
 		struct hci_conn *conn;
 
-		set_bit(HCI_LE_ADV, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_LE_ADV);
 
 		conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
 		if (conn)
@@ -1076,7 +1105,7 @@
 					   &conn->le_conn_timeout,
 					   conn->conn_timeout);
 	} else {
-		clear_bit(HCI_LE_ADV, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_LE_ADV);
 	}
 
 	hci_dev_unlock(hdev);
@@ -1151,7 +1180,7 @@
 
 	switch (cp->enable) {
 	case LE_SCAN_ENABLE:
-		set_bit(HCI_LE_SCAN, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_LE_SCAN);
 		if (hdev->le_scan_type == LE_SCAN_ACTIVE)
 			clear_pending_adv_report(hdev);
 		break;
@@ -1176,7 +1205,7 @@
 		 */
 		cancel_delayed_work(&hdev->le_scan_disable);
 
-		clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_LE_SCAN);
 
 		/* The HCI_LE_SCAN_INTERRUPTED flag indicates that we
 		 * interrupted scanning due to a connect request. Mark
@@ -1185,10 +1214,9 @@
 		 * been disabled because of active scanning, so
 		 * re-enable it again if necessary.
 		 */
-		if (test_and_clear_bit(HCI_LE_SCAN_INTERRUPTED,
-				       &hdev->dev_flags))
+		if (hci_dev_test_and_clear_flag(hdev, HCI_LE_SCAN_INTERRUPTED))
 			hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-		else if (!test_bit(HCI_LE_ADV, &hdev->dev_flags) &&
+		else if (!hci_dev_test_flag(hdev, HCI_LE_ADV) &&
 			 hdev->discovery.state == DISCOVERY_FINDING)
 			mgmt_reenable_advertising(hdev);
 
@@ -1279,6 +1307,55 @@
 	memcpy(hdev->le_states, rp->le_states, 8);
 }
 
+static void hci_cc_le_read_def_data_len(struct hci_dev *hdev,
+					struct sk_buff *skb)
+{
+	struct hci_rp_le_read_def_data_len *rp = (void *) skb->data;
+
+	BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+	if (rp->status)
+		return;
+
+	hdev->le_def_tx_len = le16_to_cpu(rp->tx_len);
+	hdev->le_def_tx_time = le16_to_cpu(rp->tx_time);
+}
+
+static void hci_cc_le_write_def_data_len(struct hci_dev *hdev,
+					 struct sk_buff *skb)
+{
+	struct hci_cp_le_write_def_data_len *sent;
+	__u8 status = *((__u8 *) skb->data);
+
+	BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+	if (status)
+		return;
+
+	sent = hci_sent_cmd_data(hdev, HCI_OP_LE_WRITE_DEF_DATA_LEN);
+	if (!sent)
+		return;
+
+	hdev->le_def_tx_len = le16_to_cpu(sent->tx_len);
+	hdev->le_def_tx_time = le16_to_cpu(sent->tx_time);
+}
+
+static void hci_cc_le_read_max_data_len(struct hci_dev *hdev,
+					struct sk_buff *skb)
+{
+	struct hci_rp_le_read_max_data_len *rp = (void *) skb->data;
+
+	BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+	if (rp->status)
+		return;
+
+	hdev->le_max_tx_len = le16_to_cpu(rp->tx_len);
+	hdev->le_max_tx_time = le16_to_cpu(rp->tx_time);
+	hdev->le_max_rx_len = le16_to_cpu(rp->rx_len);
+	hdev->le_max_rx_time = le16_to_cpu(rp->rx_time);
+}
+
 static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
 					   struct sk_buff *skb)
 {
@@ -1298,11 +1375,11 @@
 
 	if (sent->le) {
 		hdev->features[1][0] |= LMP_HOST_LE;
-		set_bit(HCI_LE_ENABLED, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_LE_ENABLED);
 	} else {
 		hdev->features[1][0] &= ~LMP_HOST_LE;
-		clear_bit(HCI_LE_ENABLED, &hdev->dev_flags);
-		clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_LE_ENABLED);
+		hci_dev_clear_flag(hdev, HCI_ADVERTISING);
 	}
 
 	if (sent->simul)
@@ -1399,6 +1476,21 @@
 	hci_dev_unlock(hdev);
 }
 
+static void hci_cc_write_ssp_debug_mode(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	u8 status = *((u8 *) skb->data);
+	u8 *mode;
+
+	BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+	if (status)
+		return;
+
+	mode = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE);
+	if (mode)
+		hdev->ssp_debug_mode = *mode;
+}
+
 static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
 {
 	BT_DBG("%s status 0x%2.2x", hdev->name, status);
@@ -1432,7 +1524,7 @@
 		if (conn && conn->state == BT_CONNECT) {
 			if (status != 0x0c || conn->attempt > 2) {
 				conn->state = BT_CLOSED;
-				hci_proto_connect_cfm(conn, status);
+				hci_connect_cfm(conn, status);
 				hci_conn_del(conn);
 			} else
 				conn->state = BT_CONNECT2;
@@ -1476,7 +1568,7 @@
 		if (sco) {
 			sco->state = BT_CLOSED;
 
-			hci_proto_connect_cfm(sco, status);
+			hci_connect_cfm(sco, status);
 			hci_conn_del(sco);
 		}
 	}
@@ -1503,7 +1595,7 @@
 	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
 	if (conn) {
 		if (conn->state == BT_CONFIG) {
-			hci_proto_connect_cfm(conn, status);
+			hci_connect_cfm(conn, status);
 			hci_conn_drop(conn);
 		}
 	}
@@ -1530,7 +1622,7 @@
 	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
 	if (conn) {
 		if (conn->state == BT_CONFIG) {
-			hci_proto_connect_cfm(conn, status);
+			hci_connect_cfm(conn, status);
 			hci_conn_drop(conn);
 		}
 	}
@@ -1664,7 +1756,7 @@
 
 	conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		hci_check_pending_name(hdev, conn, &cp->bdaddr, NULL, 0);
 
 	if (!conn)
@@ -1706,7 +1798,7 @@
 	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
 	if (conn) {
 		if (conn->state == BT_CONFIG) {
-			hci_proto_connect_cfm(conn, status);
+			hci_connect_cfm(conn, status);
 			hci_conn_drop(conn);
 		}
 	}
@@ -1733,7 +1825,7 @@
 	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
 	if (conn) {
 		if (conn->state == BT_CONFIG) {
-			hci_proto_connect_cfm(conn, status);
+			hci_connect_cfm(conn, status);
 			hci_conn_drop(conn);
 		}
 	}
@@ -1768,7 +1860,7 @@
 		if (sco) {
 			sco->state = BT_CLOSED;
 
-			hci_proto_connect_cfm(sco, status);
+			hci_connect_cfm(sco, status);
 			hci_conn_del(sco);
 		}
 	}
@@ -1944,6 +2036,33 @@
 	hci_dev_unlock(hdev);
 }
 
+static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status)
+{
+	struct hci_cp_le_read_remote_features *cp;
+	struct hci_conn *conn;
+
+	BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+	if (!status)
+		return;
+
+	cp = hci_sent_cmd_data(hdev, HCI_OP_LE_READ_REMOTE_FEATURES);
+	if (!cp)
+		return;
+
+	hci_dev_lock(hdev);
+
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+	if (conn) {
+		if (conn->state == BT_CONFIG) {
+			hci_connect_cfm(conn, status);
+			hci_conn_drop(conn);
+		}
+	}
+
+	hci_dev_unlock(hdev);
+}
+
 static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
 {
 	struct hci_cp_le_start_enc *cp;
@@ -2013,7 +2132,7 @@
 	smp_mb__after_atomic(); /* wake_up_bit advises about this barrier */
 	wake_up_bit(&hdev->flags, HCI_INQUIRY);
 
-	if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_MGMT))
 		return;
 
 	hci_dev_lock(hdev);
@@ -2022,7 +2141,16 @@
 		goto unlock;
 
 	if (list_empty(&discov->resolve)) {
-		hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+		/* When BR/EDR inquiry is active and no LE scanning is in
+		 * progress, then change discovery state to indicate completion.
+		 *
+		 * When running LE scanning and BR/EDR inquiry simultaneously
+		 * and the LE scan already finished, then change the discovery
+		 * state to indicate completion.
+		 */
+		if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
+		    !test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
+			hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
 		goto unlock;
 	}
 
@@ -2031,7 +2159,16 @@
 		e->name_state = NAME_PENDING;
 		hci_discovery_set_state(hdev, DISCOVERY_RESOLVING);
 	} else {
-		hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+		/* When BR/EDR inquiry is active and no LE scanning is in
+		 * progress, then change discovery state to indicate completion.
+		 *
+		 * When running LE scanning and BR/EDR inquiry simultaneously
+		 * and the LE scan already finished, then change the discovery
+		 * state to indicate completion.
+		 */
+		if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
+		    !test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
+			hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
 	}
 
 unlock:
@@ -2049,7 +2186,7 @@
 	if (!num_rsp)
 		return;
 
-	if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_PERIODIC_INQ))
 		return;
 
 	hci_dev_lock(hdev);
@@ -2112,6 +2249,7 @@
 		} else
 			conn->state = BT_CONNECTED;
 
+		hci_debugfs_create_conn(conn);
 		hci_conn_add_sysfs(conn);
 
 		if (test_bit(HCI_AUTH, &hdev->flags))
@@ -2127,7 +2265,7 @@
 			hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
 				     sizeof(cp), &cp);
 
-			hci_update_page_scan(hdev, NULL);
+			hci_update_page_scan(hdev);
 		}
 
 		/* Set packet type for incoming connection */
@@ -2149,10 +2287,10 @@
 		hci_sco_setup(conn, ev->status);
 
 	if (ev->status) {
-		hci_proto_connect_cfm(conn, ev->status);
+		hci_connect_cfm(conn, ev->status);
 		hci_conn_del(conn);
 	} else if (ev->link_type != ACL_LINK)
-		hci_proto_connect_cfm(conn, ev->status);
+		hci_connect_cfm(conn, ev->status);
 
 unlock:
 	hci_dev_unlock(hdev);
@@ -2194,7 +2332,12 @@
 		return;
 	}
 
-	if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags) &&
+	/* Require HCI_CONNECTABLE or a whitelist entry to accept the
+	 * connection. These features are only touched through mgmt so
+	 * only do the checks if HCI_MGMT is set.
+	 */
+	if (hci_dev_test_flag(hdev, HCI_MGMT) &&
+	    !hci_dev_test_flag(hdev, HCI_CONNECTABLE) &&
 	    !hci_bdaddr_list_lookup(&hdev->whitelist, &ev->bdaddr,
 				    BDADDR_BREDR)) {
 		    hci_reject_conn(hdev, &ev->bdaddr);
@@ -2255,7 +2398,7 @@
 			     &cp);
 	} else {
 		conn->state = BT_CONNECT2;
-		hci_proto_connect_cfm(conn, 0);
+		hci_connect_cfm(conn, 0);
 	}
 }
 
@@ -2308,7 +2451,7 @@
 		if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
 			hci_remove_link_key(hdev, &conn->dst);
 
-		hci_update_page_scan(hdev, NULL);
+		hci_update_page_scan(hdev);
 	}
 
 	params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
@@ -2333,7 +2476,7 @@
 
 	type = conn->type;
 
-	hci_proto_disconn_cfm(conn, ev->reason);
+	hci_disconn_cfm(conn, ev->reason);
 	hci_conn_del(conn);
 
 	/* Re-enable advertising if necessary, since it might
@@ -2390,7 +2533,7 @@
 				     &cp);
 		} else {
 			conn->state = BT_CONNECTED;
-			hci_proto_connect_cfm(conn, ev->status);
+			hci_connect_cfm(conn, ev->status);
 			hci_conn_drop(conn);
 		}
 	} else {
@@ -2431,7 +2574,7 @@
 
 	conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
 
-	if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_MGMT))
 		goto check_auth;
 
 	if (ev->status == 0)
@@ -2497,7 +2640,7 @@
 	 * whenever the encryption procedure fails.
 	 */
 	if (ev->status && conn->type == LE_LINK)
-		set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
 
 	clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
 
@@ -2515,15 +2658,15 @@
 		 * connections that are not encrypted with AES-CCM
 		 * using a P-256 authenticated combination key.
 		 */
-		if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) &&
+		if (hci_dev_test_flag(hdev, HCI_SC_ONLY) &&
 		    (!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
 		     conn->key_type != HCI_LK_AUTH_COMBINATION_P256)) {
-			hci_proto_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE);
+			hci_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE);
 			hci_conn_drop(conn);
 			goto unlock;
 		}
 
-		hci_proto_connect_cfm(conn, ev->status);
+		hci_connect_cfm(conn, ev->status);
 		hci_conn_drop(conn);
 	} else
 		hci_encrypt_cfm(conn, ev->status, ev->encrypt);
@@ -2575,7 +2718,8 @@
 	if (conn->state != BT_CONFIG)
 		goto unlock;
 
-	if (!ev->status && lmp_ssp_capable(hdev) && lmp_ssp_capable(conn)) {
+	if (!ev->status && lmp_ext_feat_capable(hdev) &&
+	    lmp_ext_feat_capable(conn)) {
 		struct hci_cp_read_remote_ext_features cp;
 		cp.handle = ev->handle;
 		cp.page = 0x01;
@@ -2595,7 +2739,7 @@
 
 	if (!hci_outgoing_auth_needed(hdev, conn)) {
 		conn->state = BT_CONNECTED;
-		hci_proto_connect_cfm(conn, ev->status);
+		hci_connect_cfm(conn, ev->status);
 		hci_conn_drop(conn);
 	}
 
@@ -2603,17 +2747,19 @@
 	hci_dev_unlock(hdev);
 }
 
-static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
+				 u16 *opcode, u8 *status,
+				 hci_req_complete_t *req_complete,
+				 hci_req_complete_skb_t *req_complete_skb)
 {
 	struct hci_ev_cmd_complete *ev = (void *) skb->data;
-	u8 status = skb->data[sizeof(*ev)];
-	__u16 opcode;
+
+	*opcode = __le16_to_cpu(ev->opcode);
+	*status = skb->data[sizeof(*ev)];
 
 	skb_pull(skb, sizeof(*ev));
 
-	opcode = __le16_to_cpu(ev->opcode);
-
-	switch (opcode) {
+	switch (*opcode) {
 	case HCI_OP_INQUIRY_CANCEL:
 		hci_cc_inquiry_cancel(hdev, skb);
 		break;
@@ -2654,6 +2800,14 @@
 		hci_cc_reset(hdev, skb);
 		break;
 
+	case HCI_OP_READ_STORED_LINK_KEY:
+		hci_cc_read_stored_link_key(hdev, skb);
+		break;
+
+	case HCI_OP_DELETE_STORED_LINK_KEY:
+		hci_cc_delete_stored_link_key(hdev, skb);
+		break;
+
 	case HCI_OP_WRITE_LOCAL_NAME:
 		hci_cc_write_local_name(hdev, skb);
 		break;
@@ -2846,6 +3000,18 @@
 		hci_cc_le_read_supported_states(hdev, skb);
 		break;
 
+	case HCI_OP_LE_READ_DEF_DATA_LEN:
+		hci_cc_le_read_def_data_len(hdev, skb);
+		break;
+
+	case HCI_OP_LE_WRITE_DEF_DATA_LEN:
+		hci_cc_le_write_def_data_len(hdev, skb);
+		break;
+
+	case HCI_OP_LE_READ_MAX_DATA_LEN:
+		hci_cc_le_read_max_data_len(hdev, skb);
+		break;
+
 	case HCI_OP_WRITE_LE_HOST_SUPPORTED:
 		hci_cc_write_le_host_supported(hdev, skb);
 		break;
@@ -2866,33 +3032,41 @@
 		hci_cc_read_tx_power(hdev, skb);
 		break;
 
+	case HCI_OP_WRITE_SSP_DEBUG_MODE:
+		hci_cc_write_ssp_debug_mode(hdev, skb);
+		break;
+
 	default:
-		BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
+		BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode);
 		break;
 	}
 
-	if (opcode != HCI_OP_NOP)
+	if (*opcode != HCI_OP_NOP)
 		cancel_delayed_work(&hdev->cmd_timer);
 
-	hci_req_cmd_complete(hdev, opcode, status);
-
-	if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
+	if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
 		atomic_set(&hdev->cmd_cnt, 1);
-		if (!skb_queue_empty(&hdev->cmd_q))
-			queue_work(hdev->workqueue, &hdev->cmd_work);
-	}
+
+	hci_req_cmd_complete(hdev, *opcode, *status, req_complete,
+			     req_complete_skb);
+
+	if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q))
+		queue_work(hdev->workqueue, &hdev->cmd_work);
 }
 
-static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
+static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
+			       u16 *opcode, u8 *status,
+			       hci_req_complete_t *req_complete,
+			       hci_req_complete_skb_t *req_complete_skb)
 {
 	struct hci_ev_cmd_status *ev = (void *) skb->data;
-	__u16 opcode;
 
 	skb_pull(skb, sizeof(*ev));
 
-	opcode = __le16_to_cpu(ev->opcode);
+	*opcode = __le16_to_cpu(ev->opcode);
+	*status = ev->status;
 
-	switch (opcode) {
+	switch (*opcode) {
 	case HCI_OP_INQUIRY:
 		hci_cs_inquiry(hdev, ev->status);
 		break;
@@ -2957,34 +3131,47 @@
 		hci_cs_le_create_conn(hdev, ev->status);
 		break;
 
+	case HCI_OP_LE_READ_REMOTE_FEATURES:
+		hci_cs_le_read_remote_features(hdev, ev->status);
+		break;
+
 	case HCI_OP_LE_START_ENC:
 		hci_cs_le_start_enc(hdev, ev->status);
 		break;
 
 	default:
-		BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
+		BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode);
 		break;
 	}
 
-	if (opcode != HCI_OP_NOP)
+	if (*opcode != HCI_OP_NOP)
 		cancel_delayed_work(&hdev->cmd_timer);
 
+	if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
+		atomic_set(&hdev->cmd_cnt, 1);
+
+	/* Indicate request completion if the command failed. Also, if
+	 * we're not waiting for a special event and we get a success
+	 * command status we should try to flag the request as completed
+	 * (since for this kind of commands there will not be a command
+	 * complete event).
+	 */
 	if (ev->status ||
 	    (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event))
-		hci_req_cmd_complete(hdev, opcode, ev->status);
+		hci_req_cmd_complete(hdev, *opcode, ev->status, req_complete,
+				     req_complete_skb);
 
-	if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
-		atomic_set(&hdev->cmd_cnt, 1);
-		if (!skb_queue_empty(&hdev->cmd_q))
-			queue_work(hdev->workqueue, &hdev->cmd_work);
-	}
+	if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q))
+		queue_work(hdev->workqueue, &hdev->cmd_work);
 }
 
 static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_ev_hardware_error *ev = (void *) skb->data;
 
-	BT_ERR("%s hardware error 0x%2.2x", hdev->name, ev->code);
+	hdev->hw_error_code = ev->code;
+
+	queue_work(hdev->req_workqueue, &hdev->error_reset);
 }
 
 static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
@@ -3193,11 +3380,11 @@
 		hci_conn_drop(conn);
 	}
 
-	if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) &&
+	if (!hci_dev_test_flag(hdev, HCI_BONDABLE) &&
 	    !test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags)) {
 		hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
 			     sizeof(ev->bdaddr), &ev->bdaddr);
-	} else if (test_bit(HCI_MGMT, &hdev->dev_flags)) {
+	} else if (hci_dev_test_flag(hdev, HCI_MGMT)) {
 		u8 secure;
 
 		if (conn->pending_sec_level == BT_SECURITY_HIGH)
@@ -3253,7 +3440,7 @@
 
 	BT_DBG("%s", hdev->name);
 
-	if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_MGMT))
 		return;
 
 	hci_dev_lock(hdev);
@@ -3327,7 +3514,7 @@
 	set_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
 	conn_set_key(conn, ev->key_type, conn->pin_length);
 
-	if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_MGMT))
 		goto unlock;
 
 	key = hci_add_link_key(hdev, conn, &ev->bdaddr, ev->link_key,
@@ -3349,7 +3536,7 @@
 	 * store_hint being 0).
 	 */
 	if (key->type == HCI_LK_DEBUG_COMBINATION &&
-	    !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) {
+	    !hci_dev_test_flag(hdev, HCI_KEEP_DEBUG_KEYS)) {
 		list_del_rcu(&key->list);
 		kfree_rcu(key, rcu);
 		goto unlock;
@@ -3432,7 +3619,7 @@
 	if (!num_rsp)
 		return;
 
-	if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_PERIODIC_INQ))
 		return;
 
 	hci_dev_lock(hdev);
@@ -3541,7 +3728,7 @@
 
 	if (!hci_outgoing_auth_needed(hdev, conn)) {
 		conn->state = BT_CONNECTED;
-		hci_proto_connect_cfm(conn, ev->status);
+		hci_connect_cfm(conn, ev->status);
 		hci_conn_drop(conn);
 	}
 
@@ -3576,6 +3763,7 @@
 		conn->handle = __le16_to_cpu(ev->handle);
 		conn->state  = BT_CONNECTED;
 
+		hci_debugfs_create_conn(conn);
 		hci_conn_add_sysfs(conn);
 		break;
 
@@ -3599,7 +3787,7 @@
 		break;
 	}
 
-	hci_proto_connect_cfm(conn, ev->status);
+	hci_connect_cfm(conn, ev->status);
 	if (ev->status)
 		hci_conn_del(conn);
 
@@ -3637,7 +3825,7 @@
 	if (!num_rsp)
 		return;
 
-	if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_PERIODIC_INQ))
 		return;
 
 	hci_dev_lock(hdev);
@@ -3655,7 +3843,7 @@
 		data.rssi		= info->rssi;
 		data.ssp_mode		= 0x01;
 
-		if (test_bit(HCI_MGMT, &hdev->dev_flags))
+		if (hci_dev_test_flag(hdev, HCI_MGMT))
 			name_known = eir_has_data_type(info->data,
 						       sizeof(info->data),
 						       EIR_NAME_COMPLETE);
@@ -3710,7 +3898,7 @@
 		if (!ev->status)
 			conn->state = BT_CONNECTED;
 
-		hci_proto_connect_cfm(conn, ev->status);
+		hci_connect_cfm(conn, ev->status);
 		hci_conn_drop(conn);
 	} else {
 		hci_auth_cfm(conn, ev->status);
@@ -3742,6 +3930,48 @@
 	return (conn->remote_auth & ~0x01) | (conn->auth_type & 0x01);
 }
 
+static u8 bredr_oob_data_present(struct hci_conn *conn)
+{
+	struct hci_dev *hdev = conn->hdev;
+	struct oob_data *data;
+
+	data = hci_find_remote_oob_data(hdev, &conn->dst, BDADDR_BREDR);
+	if (!data)
+		return 0x00;
+
+	if (bredr_sc_enabled(hdev)) {
+		/* When Secure Connections is enabled, then just
+		 * return the present value stored with the OOB
+		 * data. The stored value contains the right present
+		 * information. However it can only be trusted when
+		 * not in Secure Connection Only mode.
+		 */
+		if (!hci_dev_test_flag(hdev, HCI_SC_ONLY))
+			return data->present;
+
+		/* When Secure Connections Only mode is enabled, then
+		 * the P-256 values are required. If they are not
+		 * available, then do not declare that OOB data is
+		 * present.
+		 */
+		if (!memcmp(data->rand256, ZERO_KEY, 16) ||
+		    !memcmp(data->hash256, ZERO_KEY, 16))
+			return 0x00;
+
+		return 0x02;
+	}
+
+	/* When Secure Connections is not enabled or actually
+	 * not supported by the hardware, then check that if
+	 * P-192 data values are present.
+	 */
+	if (!memcmp(data->rand192, ZERO_KEY, 16) ||
+	    !memcmp(data->hash192, ZERO_KEY, 16))
+		return 0x00;
+
+	return 0x01;
+}
+
 static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_ev_io_capa_request *ev = (void *) skb->data;
@@ -3757,13 +3987,13 @@
 
 	hci_conn_hold(conn);
 
-	if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_MGMT))
 		goto unlock;
 
 	/* Allow pairing if we're pairable, the initiators of the
 	 * pairing or if the remote is not requesting bonding.
 	 */
-	if (test_bit(HCI_BONDABLE, &hdev->dev_flags) ||
+	if (hci_dev_test_flag(hdev, HCI_BONDABLE) ||
 	    test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags) ||
 	    (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) {
 		struct hci_cp_io_capability_reply cp;
@@ -3789,16 +4019,11 @@
 		/* If we're not bondable, force one of the non-bondable
 		 * authentication requirement values.
 		 */
-		if (!test_bit(HCI_BONDABLE, &hdev->dev_flags))
+		if (!hci_dev_test_flag(hdev, HCI_BONDABLE))
 			conn->auth_type &= HCI_AT_NO_BONDING_MITM;
 
 		cp.authentication = conn->auth_type;
-
-		if (hci_find_remote_oob_data(hdev, &conn->dst, BDADDR_BREDR) &&
-		    (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)))
-			cp.oob_data = 0x01;
-		else
-			cp.oob_data = 0x00;
+		cp.oob_data = bredr_oob_data_present(conn);
 
 		hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY,
 			     sizeof(cp), &cp);
@@ -3831,8 +4056,6 @@
 
 	conn->remote_cap = ev->capability;
 	conn->remote_auth = ev->authentication;
-	if (ev->oob_data)
-		set_bit(HCI_CONN_REMOTE_OOB, &conn->flags);
 
 unlock:
 	hci_dev_unlock(hdev);
@@ -3849,7 +4072,7 @@
 
 	hci_dev_lock(hdev);
 
-	if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_MGMT))
 		goto unlock;
 
 	conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
@@ -3920,7 +4143,7 @@
 
 	BT_DBG("%s", hdev->name);
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0);
 }
 
@@ -3939,7 +4162,7 @@
 	conn->passkey_notify = __le32_to_cpu(ev->passkey);
 	conn->passkey_entered = 0;
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_user_passkey_notify(hdev, &conn->dst, conn->type,
 					 conn->dst_type, conn->passkey_notify,
 					 conn->passkey_entered);
@@ -3977,7 +4200,7 @@
 		return;
 	}
 
-	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_MGMT))
 		mgmt_user_passkey_notify(hdev, &conn->dst, conn->type,
 					 conn->dst_type, conn->passkey_notify,
 					 conn->passkey_entered);
@@ -4046,38 +4269,44 @@
 
 	hci_dev_lock(hdev);
 
-	if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_MGMT))
 		goto unlock;
 
 	data = hci_find_remote_oob_data(hdev, &ev->bdaddr, BDADDR_BREDR);
-	if (data) {
-		if (bredr_sc_enabled(hdev)) {
-			struct hci_cp_remote_oob_ext_data_reply cp;
-
-			bacpy(&cp.bdaddr, &ev->bdaddr);
-			memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
-			memcpy(cp.rand192, data->rand192, sizeof(cp.rand192));
-			memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
-			memcpy(cp.rand256, data->rand256, sizeof(cp.rand256));
-
-			hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
-				     sizeof(cp), &cp);
-		} else {
-			struct hci_cp_remote_oob_data_reply cp;
-
-			bacpy(&cp.bdaddr, &ev->bdaddr);
-			memcpy(cp.hash, data->hash192, sizeof(cp.hash));
-			memcpy(cp.rand, data->rand192, sizeof(cp.rand));
-
-			hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
-				     sizeof(cp), &cp);
-		}
-	} else {
+	if (!data) {
 		struct hci_cp_remote_oob_data_neg_reply cp;
 
 		bacpy(&cp.bdaddr, &ev->bdaddr);
 		hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY,
 			     sizeof(cp), &cp);
+		goto unlock;
+	}
+
+	if (bredr_sc_enabled(hdev)) {
+		struct hci_cp_remote_oob_ext_data_reply cp;
+
+		bacpy(&cp.bdaddr, &ev->bdaddr);
+		if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) {
+			memset(cp.hash192, 0, sizeof(cp.hash192));
+			memset(cp.rand192, 0, sizeof(cp.rand192));
+		} else {
+			memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
+			memcpy(cp.rand192, data->rand192, sizeof(cp.rand192));
+		}
+		memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
+		memcpy(cp.rand256, data->rand256, sizeof(cp.rand256));
+
+		hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
+			     sizeof(cp), &cp);
+	} else {
+		struct hci_cp_remote_oob_data_reply cp;
+
+		bacpy(&cp.bdaddr, &ev->bdaddr);
+		memcpy(cp.hash, data->hash192, sizeof(cp.hash));
+		memcpy(cp.rand, data->rand192, sizeof(cp.rand));
+
+		hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
+			     sizeof(cp), &cp);
 	}
 
 unlock:
@@ -4116,6 +4345,7 @@
 	hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
 	hci_conn_drop(hcon);
 
+	hci_debugfs_create_conn(hcon);
 	hci_conn_add_sysfs(hcon);
 
 	amp_physical_cfm(bredr_hcon, hcon);
@@ -4222,7 +4452,7 @@
 	/* All controllers implicitly stop advertising in the event of a
 	 * connection, so ensure that the state bit is cleared.
 	 */
-	clear_bit(HCI_LE_ADV, &hdev->dev_flags);
+	hci_dev_clear_flag(hdev, HCI_LE_ADV);
 
 	conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
 	if (!conn) {
@@ -4245,7 +4475,7 @@
 		if (conn->out) {
 			conn->resp_addr_type = ev->bdaddr_type;
 			bacpy(&conn->resp_addr, &ev->bdaddr);
-			if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) {
+			if (hci_dev_test_flag(hdev, HCI_PRIVACY)) {
 				conn->init_addr_type = ADDR_LE_DEV_RANDOM;
 				bacpy(&conn->init_addr, &hdev->rpa);
 			} else {
@@ -4316,15 +4546,42 @@
 
 	conn->sec_level = BT_SECURITY_LOW;
 	conn->handle = __le16_to_cpu(ev->handle);
-	conn->state = BT_CONNECTED;
+	conn->state = BT_CONFIG;
 
 	conn->le_conn_interval = le16_to_cpu(ev->interval);
 	conn->le_conn_latency = le16_to_cpu(ev->latency);
 	conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout);
 
+	hci_debugfs_create_conn(conn);
 	hci_conn_add_sysfs(conn);
 
-	hci_proto_connect_cfm(conn, ev->status);
+	if (!ev->status) {
+		/* The remote features procedure is defined for master
+		 * role only. So only in case of an initiated connection
+		 * request the remote features.
+		 *
+		 * If the local controller supports slave-initiated features
+		 * exchange, then requesting the remote features in slave
+		 * role is possible. Otherwise just transition into the
+		 * connected state without requesting the remote features.
+		 */
+		if (conn->out ||
+		    (hdev->le_features[0] & HCI_LE_SLAVE_FEATURES)) {
+			struct hci_cp_le_read_remote_features cp;
+
+			cp.handle = __cpu_to_le16(conn->handle);
+
+			hci_send_cmd(hdev, HCI_OP_LE_READ_REMOTE_FEATURES,
+				     sizeof(cp), &cp);
+
+			hci_conn_hold(conn);
+		} else {
+			conn->state = BT_CONNECTED;
+			hci_connect_cfm(conn, ev->status);
+		}
+	} else {
+		hci_connect_cfm(conn, ev->status);
+	}
 
 	params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
 					   conn->dst_type);
@@ -4470,7 +4727,7 @@
 		/* If the controller is not using resolvable random
 		 * addresses, then this report can be ignored.
 		 */
-		if (!test_bit(HCI_PRIVACY, &hdev->dev_flags))
+		if (!hci_dev_test_flag(hdev, HCI_PRIVACY))
 			return;
 
 		/* If the local IRK of the controller does not match
@@ -4626,6 +4883,48 @@
 	hci_dev_unlock(hdev);
 }
 
+static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev,
+					    struct sk_buff *skb)
+{
+	struct hci_ev_le_remote_feat_complete *ev = (void *)skb->data;
+	struct hci_conn *conn;
+
+	BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
+
+	hci_dev_lock(hdev);
+
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+	if (conn) {
+		if (!ev->status)
+			memcpy(conn->features[0], ev->features, 8);
+
+		if (conn->state == BT_CONFIG) {
+			__u8 status;
+
+			/* If the local controller supports slave-initiated
+			 * features exchange, but the remote controller does
+			 * not, then it is possible that the error code 0x1a
+			 * for unsupported remote feature gets returned.
+			 *
+			 * In this specific case, allow the connection to
+			 * transition into connected state and mark it as
+			 * successful.
+			 */
+			if ((hdev->le_features[0] & HCI_LE_SLAVE_FEATURES) &&
+			    !conn->out && ev->status == 0x1a)
+				status = 0x00;
+			else
+				status = ev->status;
+
+			conn->state = BT_CONNECTED;
+			hci_connect_cfm(conn, status);
+			hci_conn_drop(conn);
+		}
+	}
+
+	hci_dev_unlock(hdev);
+}
+
 static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_ev_le_ltk_req *ev = (void *) skb->data;
@@ -4799,6 +5098,10 @@
 		hci_le_adv_report_evt(hdev, skb);
 		break;
 
+	case HCI_EV_LE_REMOTE_FEAT_COMPLETE:
+		hci_le_remote_feat_complete_evt(hdev, skb);
+		break;
+
 	case HCI_EV_LE_LTK_REQ:
 		hci_le_ltk_request_evt(hdev, skb);
 		break;
@@ -4832,32 +5135,79 @@
 	amp_read_loc_assoc_final_data(hdev, hcon);
 }
 
+static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
+				 u8 event, struct sk_buff *skb)
+{
+	struct hci_ev_cmd_complete *ev;
+	struct hci_event_hdr *hdr;
+
+	if (!skb)
+		return false;
+
+	if (skb->len < sizeof(*hdr)) {
+		BT_ERR("Too short HCI event");
+		return false;
+	}
+
+	hdr = (void *) skb->data;
+	skb_pull(skb, HCI_EVENT_HDR_SIZE);
+
+	if (event) {
+		if (hdr->evt != event)
+			return false;
+		return true;
+	}
+
+	if (hdr->evt != HCI_EV_CMD_COMPLETE) {
+		BT_DBG("Last event is not cmd complete (0x%2.2x)", hdr->evt);
+		return false;
+	}
+
+	if (skb->len < sizeof(*ev)) {
+		BT_ERR("Too short cmd_complete event");
+		return false;
+	}
+
+	ev = (void *) skb->data;
+	skb_pull(skb, sizeof(*ev));
+
+	if (opcode != __le16_to_cpu(ev->opcode)) {
+		BT_DBG("opcode doesn't match (0x%2.2x != 0x%2.2x)", opcode,
+		       __le16_to_cpu(ev->opcode));
+		return false;
+	}
+
+	return true;
+}
+
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_event_hdr *hdr = (void *) skb->data;
-	__u8 event = hdr->evt;
-
-	hci_dev_lock(hdev);
-
-	/* Received events are (currently) only needed when a request is
-	 * ongoing so avoid unnecessary memory allocation.
-	 */
-	if (hci_req_pending(hdev)) {
-		kfree_skb(hdev->recv_evt);
-		hdev->recv_evt = skb_clone(skb, GFP_KERNEL);
-	}
-
-	hci_dev_unlock(hdev);
-
-	skb_pull(skb, HCI_EVENT_HDR_SIZE);
+	hci_req_complete_t req_complete = NULL;
+	hci_req_complete_skb_t req_complete_skb = NULL;
+	struct sk_buff *orig_skb = NULL;
+	u8 status = 0, event = hdr->evt, req_evt = 0;
+	u16 opcode = HCI_OP_NOP;
 
 	if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) {
 		struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
-		u16 opcode = __le16_to_cpu(cmd_hdr->opcode);
-
-		hci_req_cmd_complete(hdev, opcode, 0);
+		opcode = __le16_to_cpu(cmd_hdr->opcode);
+		hci_req_cmd_complete(hdev, opcode, status, &req_complete,
+				     &req_complete_skb);
+		req_evt = event;
 	}
 
+	/* If it looks like we might end up having to call
+	 * req_complete_skb, store a pristine copy of the skb since the
+	 * various handlers may modify the original one through
+	 * skb_pull() calls, etc.
+	 */
+	if (req_complete_skb || event == HCI_EV_CMD_STATUS ||
+	    event == HCI_EV_CMD_COMPLETE)
+		orig_skb = skb_clone(skb, GFP_KERNEL);
+
+	skb_pull(skb, HCI_EVENT_HDR_SIZE);
+
 	switch (event) {
 	case HCI_EV_INQUIRY_COMPLETE:
 		hci_inquiry_complete_evt(hdev, skb);
@@ -4900,11 +5250,13 @@
 		break;
 
 	case HCI_EV_CMD_COMPLETE:
-		hci_cmd_complete_evt(hdev, skb);
+		hci_cmd_complete_evt(hdev, skb, &opcode, &status,
+				     &req_complete, &req_complete_skb);
 		break;
 
 	case HCI_EV_CMD_STATUS:
-		hci_cmd_status_evt(hdev, skb);
+		hci_cmd_status_evt(hdev, skb, &opcode, &status, &req_complete,
+				   &req_complete_skb);
 		break;
 
 	case HCI_EV_HARDWARE_ERROR:
@@ -5036,6 +5388,17 @@
 		break;
 	}
 
+	if (req_complete) {
+		req_complete(hdev, status, opcode);
+	} else if (req_complete_skb) {
+		if (!hci_get_cmd_complete(hdev, opcode, req_evt, orig_skb)) {
+			kfree_skb(orig_skb);
+			orig_skb = NULL;
+		}
+		req_complete_skb(hdev, status, opcode, orig_skb);
+	}
+
+	kfree_skb(orig_skb);
 	kfree_skb(skb);
 	hdev->stat.evt_rx++;
 }
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
new file mode 100644
index 0000000..d6025d6
--- /dev/null
+++ b/net/bluetooth/hci_request.c
@@ -0,0 +1,568 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL 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.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "smp.h"
+#include "hci_request.h"
+
+void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
+{
+	skb_queue_head_init(&req->cmd_q);
+	req->hdev = hdev;
+	req->err = 0;
+}
+
+static int req_run(struct hci_request *req, hci_req_complete_t complete,
+		   hci_req_complete_skb_t complete_skb)
+{
+	struct hci_dev *hdev = req->hdev;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	BT_DBG("length %u", skb_queue_len(&req->cmd_q));
+
+	/* If an error occurred during request building, remove all HCI
+	 * commands queued on the HCI request queue.
+	 */
+	if (req->err) {
+		skb_queue_purge(&req->cmd_q);
+		return req->err;
+	}
+
+	/* Do not allow empty requests */
+	if (skb_queue_empty(&req->cmd_q))
+		return -ENODATA;
+
+	skb = skb_peek_tail(&req->cmd_q);
+	bt_cb(skb)->req.complete = complete;
+	bt_cb(skb)->req.complete_skb = complete_skb;
+
+	spin_lock_irqsave(&hdev->cmd_q.lock, flags);
+	skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
+	spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
+
+	queue_work(hdev->workqueue, &hdev->cmd_work);
+
+	return 0;
+}
+
+int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
+{
+	return req_run(req, complete, NULL);
+}
+
+int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete)
+{
+	return req_run(req, NULL, complete);
+}
+
+struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
+				const void *param)
+{
+	int len = HCI_COMMAND_HDR_SIZE + plen;
+	struct hci_command_hdr *hdr;
+	struct sk_buff *skb;
+
+	skb = bt_skb_alloc(len, GFP_ATOMIC);
+	if (!skb)
+		return NULL;
+
+	hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE);
+	hdr->opcode = cpu_to_le16(opcode);
+	hdr->plen   = plen;
+
+	if (plen)
+		memcpy(skb_put(skb, plen), param, plen);
+
+	BT_DBG("skb len %d", skb->len);
+
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	bt_cb(skb)->opcode = opcode;
+
+	return skb;
+}
+
+/* Queue a command to an asynchronous HCI request */
+void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
+		    const void *param, u8 event)
+{
+	struct hci_dev *hdev = req->hdev;
+	struct sk_buff *skb;
+
+	BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
+
+	/* If an error occurred during request building, there is no point in
+	 * queueing the HCI command. We can simply return.
+	 */
+	if (req->err)
+		return;
+
+	skb = hci_prepare_cmd(hdev, opcode, plen, param);
+	if (!skb) {
+		BT_ERR("%s no memory for command (opcode 0x%4.4x)",
+		       hdev->name, opcode);
+		req->err = -ENOMEM;
+		return;
+	}
+
+	if (skb_queue_empty(&req->cmd_q))
+		bt_cb(skb)->req.start = true;
+
+	bt_cb(skb)->req.event = event;
+
+	skb_queue_tail(&req->cmd_q, skb);
+}
+
+void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
+		 const void *param)
+{
+	hci_req_add_ev(req, opcode, plen, param, 0);
+}
+
+void hci_req_add_le_scan_disable(struct hci_request *req)
+{
+	struct hci_cp_le_set_scan_enable cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.enable = LE_SCAN_DISABLE;
+	hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+}
+
+static void add_to_white_list(struct hci_request *req,
+			      struct hci_conn_params *params)
+{
+	struct hci_cp_le_add_to_white_list cp;
+
+	cp.bdaddr_type = params->addr_type;
+	bacpy(&cp.bdaddr, &params->addr);
+
+	hci_req_add(req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(cp), &cp);
+}
+
+static u8 update_white_list(struct hci_request *req)
+{
+	struct hci_dev *hdev = req->hdev;
+	struct hci_conn_params *params;
+	struct bdaddr_list *b;
+	uint8_t white_list_entries = 0;
+
+	/* Go through the current white list programmed into the
+	 * controller one by one and check if that address is still
+	 * in the list of pending connections or list of devices to
+	 * report. If not present in either list, then queue the
+	 * command to remove it from the controller.
+	 */
+	list_for_each_entry(b, &hdev->le_white_list, list) {
+		struct hci_cp_le_del_from_white_list cp;
+
+		if (hci_pend_le_action_lookup(&hdev->pend_le_conns,
+					      &b->bdaddr, b->bdaddr_type) ||
+		    hci_pend_le_action_lookup(&hdev->pend_le_reports,
+					      &b->bdaddr, b->bdaddr_type)) {
+			white_list_entries++;
+			continue;
+		}
+
+		cp.bdaddr_type = b->bdaddr_type;
+		bacpy(&cp.bdaddr, &b->bdaddr);
+
+		hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST,
+			    sizeof(cp), &cp);
+	}
+
+	/* Since all no longer valid white list entries have been
+	 * removed, walk through the list of pending connections
+	 * and ensure that any new device gets programmed into
+	 * the controller.
+	 *
+	 * If the list of the devices is larger than the list of
+	 * available white list entries in the controller, then
+	 * just abort and return filer policy value to not use the
+	 * white list.
+	 */
+	list_for_each_entry(params, &hdev->pend_le_conns, action) {
+		if (hci_bdaddr_list_lookup(&hdev->le_white_list,
+					   &params->addr, params->addr_type))
+			continue;
+
+		if (white_list_entries >= hdev->le_white_list_size) {
+			/* Select filter policy to accept all advertising */
+			return 0x00;
+		}
+
+		if (hci_find_irk_by_addr(hdev, &params->addr,
+					 params->addr_type)) {
+			/* White list can not be used with RPAs */
+			return 0x00;
+		}
+
+		white_list_entries++;
+		add_to_white_list(req, params);
+	}
+
+	/* After adding all new pending connections, walk through
+	 * the list of pending reports and also add these to the
+	 * white list if there is still space.
+	 */
+	list_for_each_entry(params, &hdev->pend_le_reports, action) {
+		if (hci_bdaddr_list_lookup(&hdev->le_white_list,
+					   &params->addr, params->addr_type))
+			continue;
+
+		if (white_list_entries >= hdev->le_white_list_size) {
+			/* Select filter policy to accept all advertising */
+			return 0x00;
+		}
+
+		if (hci_find_irk_by_addr(hdev, &params->addr,
+					 params->addr_type)) {
+			/* White list can not be used with RPAs */
+			return 0x00;
+		}
+
+		white_list_entries++;
+		add_to_white_list(req, params);
+	}
+
+	/* Select filter policy to use white list */
+	return 0x01;
+}
+
+void hci_req_add_le_passive_scan(struct hci_request *req)
+{
+	struct hci_cp_le_set_scan_param param_cp;
+	struct hci_cp_le_set_scan_enable enable_cp;
+	struct hci_dev *hdev = req->hdev;
+	u8 own_addr_type;
+	u8 filter_policy;
+
+	/* Set require_privacy to false since no SCAN_REQ are send
+	 * during passive scanning. Not using an non-resolvable address
+	 * here is important so that peer devices using direct
+	 * advertising with our address will be correctly reported
+	 * by the controller.
+	 */
+	if (hci_update_random_address(req, false, &own_addr_type))
+		return;
+
+	/* Adding or removing entries from the white list must
+	 * happen before enabling scanning. The controller does
+	 * not allow white list modification while scanning.
+	 */
+	filter_policy = update_white_list(req);
+
+	/* When the controller is using random resolvable addresses and
+	 * with that having LE privacy enabled, then controllers with
+	 * Extended Scanner Filter Policies support can now enable support
+	 * for handling directed advertising.
+	 *
+	 * So instead of using filter polices 0x00 (no whitelist)
+	 * and 0x01 (whitelist enabled) use the new filter policies
+	 * 0x02 (no whitelist) and 0x03 (whitelist enabled).
+	 */
+	if (hci_dev_test_flag(hdev, HCI_PRIVACY) &&
+	    (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY))
+		filter_policy |= 0x02;
+
+	memset(&param_cp, 0, sizeof(param_cp));
+	param_cp.type = LE_SCAN_PASSIVE;
+	param_cp.interval = cpu_to_le16(hdev->le_scan_interval);
+	param_cp.window = cpu_to_le16(hdev->le_scan_window);
+	param_cp.own_address_type = own_addr_type;
+	param_cp.filter_policy = filter_policy;
+	hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
+		    &param_cp);
+
+	memset(&enable_cp, 0, sizeof(enable_cp));
+	enable_cp.enable = LE_SCAN_ENABLE;
+	enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+	hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
+		    &enable_cp);
+}
+
+static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
+{
+	struct hci_dev *hdev = req->hdev;
+
+	/* If we're advertising or initiating an LE connection we can't
+	 * go ahead and change the random address at this time. This is
+	 * because the eventual initiator address used for the
+	 * subsequently created connection will be undefined (some
+	 * controllers use the new address and others the one we had
+	 * when the operation started).
+	 *
+	 * In this kind of scenario skip the update and let the random
+	 * address be updated at the next cycle.
+	 */
+	if (hci_dev_test_flag(hdev, HCI_LE_ADV) ||
+	    hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) {
+		BT_DBG("Deferring random address update");
+		hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
+		return;
+	}
+
+	hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa);
+}
+
+int hci_update_random_address(struct hci_request *req, bool require_privacy,
+			      u8 *own_addr_type)
+{
+	struct hci_dev *hdev = req->hdev;
+	int err;
+
+	/* If privacy is enabled use a resolvable private address. If
+	 * current RPA has expired or there is something else than
+	 * the current RPA in use, then generate a new one.
+	 */
+	if (hci_dev_test_flag(hdev, HCI_PRIVACY)) {
+		int to;
+
+		*own_addr_type = ADDR_LE_DEV_RANDOM;
+
+		if (!hci_dev_test_and_clear_flag(hdev, HCI_RPA_EXPIRED) &&
+		    !bacmp(&hdev->random_addr, &hdev->rpa))
+			return 0;
+
+		err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa);
+		if (err < 0) {
+			BT_ERR("%s failed to generate new RPA", hdev->name);
+			return err;
+		}
+
+		set_random_addr(req, &hdev->rpa);
+
+		to = msecs_to_jiffies(hdev->rpa_timeout * 1000);
+		queue_delayed_work(hdev->workqueue, &hdev->rpa_expired, to);
+
+		return 0;
+	}
+
+	/* In case of required privacy without resolvable private address,
+	 * use an non-resolvable private address. This is useful for active
+	 * scanning and non-connectable advertising.
+	 */
+	if (require_privacy) {
+		bdaddr_t nrpa;
+
+		while (true) {
+			/* The non-resolvable private address is generated
+			 * from random six bytes with the two most significant
+			 * bits cleared.
+			 */
+			get_random_bytes(&nrpa, 6);
+			nrpa.b[5] &= 0x3f;
+
+			/* The non-resolvable private address shall not be
+			 * equal to the public address.
+			 */
+			if (bacmp(&hdev->bdaddr, &nrpa))
+				break;
+		}
+
+		*own_addr_type = ADDR_LE_DEV_RANDOM;
+		set_random_addr(req, &nrpa);
+		return 0;
+	}
+
+	/* If forcing static address is in use or there is no public
+	 * address use the static address as random address (but skip
+	 * the HCI command if the current random address is already the
+	 * static one.
+	 *
+	 * In case BR/EDR has been disabled on a dual-mode controller
+	 * and a static address has been configured, then use that
+	 * address instead of the public BR/EDR address.
+	 */
+	if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
+	    !bacmp(&hdev->bdaddr, BDADDR_ANY) ||
+	    (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
+	     bacmp(&hdev->static_addr, BDADDR_ANY))) {
+		*own_addr_type = ADDR_LE_DEV_RANDOM;
+		if (bacmp(&hdev->static_addr, &hdev->random_addr))
+			hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
+				    &hdev->static_addr);
+		return 0;
+	}
+
+	/* Neither privacy nor static address is being used so use a
+	 * public address.
+	 */
+	*own_addr_type = ADDR_LE_DEV_PUBLIC;
+
+	return 0;
+}
+
+static bool disconnected_whitelist_entries(struct hci_dev *hdev)
+{
+	struct bdaddr_list *b;
+
+	list_for_each_entry(b, &hdev->whitelist, list) {
+		struct hci_conn *conn;
+
+		conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &b->bdaddr);
+		if (!conn)
+			return true;
+
+		if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
+			return true;
+	}
+
+	return false;
+}
+
+void __hci_update_page_scan(struct hci_request *req)
+{
+	struct hci_dev *hdev = req->hdev;
+	u8 scan;
+
+	if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
+		return;
+
+	if (!hdev_is_powered(hdev))
+		return;
+
+	if (mgmt_powering_down(hdev))
+		return;
+
+	if (hci_dev_test_flag(hdev, HCI_CONNECTABLE) ||
+	    disconnected_whitelist_entries(hdev))
+		scan = SCAN_PAGE;
+	else
+		scan = SCAN_DISABLED;
+
+	if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE))
+		return;
+
+	if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
+		scan |= SCAN_INQUIRY;
+
+	hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+}
+
+void hci_update_page_scan(struct hci_dev *hdev)
+{
+	struct hci_request req;
+
+	hci_req_init(&req, hdev);
+	__hci_update_page_scan(&req);
+	hci_req_run(&req, NULL);
+}
+
+/* This function controls the background scanning based on hdev->pend_le_conns
+ * list. If there are pending LE connection we start the background scanning,
+ * otherwise we stop it.
+ *
+ * This function requires the caller holds hdev->lock.
+ */
+void __hci_update_background_scan(struct hci_request *req)
+{
+	struct hci_dev *hdev = req->hdev;
+	struct hci_conn *conn;
+
+	if (!test_bit(HCI_UP, &hdev->flags) ||
+	    test_bit(HCI_INIT, &hdev->flags) ||
+	    hci_dev_test_flag(hdev, HCI_SETUP) ||
+	    hci_dev_test_flag(hdev, HCI_CONFIG) ||
+	    hci_dev_test_flag(hdev, HCI_AUTO_OFF) ||
+	    hci_dev_test_flag(hdev, HCI_UNREGISTER))
+		return;
+
+	/* No point in doing scanning if LE support hasn't been enabled */
+	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+		return;
+
+	/* If discovery is active don't interfere with it */
+	if (hdev->discovery.state != DISCOVERY_STOPPED)
+		return;
+
+	/* Reset RSSI and UUID filters when starting background scanning
+	 * since these filters are meant for service discovery only.
+	 *
+	 * The Start Discovery and Start Service Discovery operations
+	 * ensure to set proper values for RSSI threshold and UUID
+	 * filter list. So it is safe to just reset them here.
+	 */
+	hci_discovery_filter_clear(hdev);
+
+	if (list_empty(&hdev->pend_le_conns) &&
+	    list_empty(&hdev->pend_le_reports)) {
+		/* If there is no pending LE connections or devices
+		 * to be scanned for, we should stop the background
+		 * scanning.
+		 */
+
+		/* If controller is not scanning we are done. */
+		if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
+			return;
+
+		hci_req_add_le_scan_disable(req);
+
+		BT_DBG("%s stopping background scanning", hdev->name);
+	} else {
+		/* If there is at least one pending LE connection, we should
+		 * keep the background scan running.
+		 */
+
+		/* If controller is connecting, we should not start scanning
+		 * since some controllers are not able to scan and connect at
+		 * the same time.
+		 */
+		conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+		if (conn)
+			return;
+
+		/* If controller is currently scanning, we stop it to ensure we
+		 * don't miss any advertising (due to duplicates filter).
+		 */
+		if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
+			hci_req_add_le_scan_disable(req);
+
+		hci_req_add_le_passive_scan(req);
+
+		BT_DBG("%s starting background scanning", hdev->name);
+	}
+}
+
+static void update_background_scan_complete(struct hci_dev *hdev, u8 status,
+					    u16 opcode)
+{
+	if (status)
+		BT_DBG("HCI request failed to update background scanning: "
+		       "status 0x%2.2x", status);
+}
+
+void hci_update_background_scan(struct hci_dev *hdev)
+{
+	int err;
+	struct hci_request req;
+
+	hci_req_init(&req, hdev);
+
+	__hci_update_background_scan(&req);
+
+	err = hci_req_run(&req, update_background_scan_complete);
+	if (err && err != -ENODATA)
+		BT_ERR("Failed to run HCI request: err %d", err);
+}
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
new file mode 100644
index 0000000..bf6df92
--- /dev/null
+++ b/net/bluetooth/hci_request.h
@@ -0,0 +1,57 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL 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.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
+struct hci_request {
+	struct hci_dev		*hdev;
+	struct sk_buff_head	cmd_q;
+
+	/* If something goes wrong when building the HCI request, the error
+	 * value is stored in this field.
+	 */
+	int			err;
+};
+
+void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
+int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
+int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete);
+void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
+		 const void *param);
+void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
+		    const void *param, u8 event);
+void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
+			  hci_req_complete_t *req_complete,
+			  hci_req_complete_skb_t *req_complete_skb);
+
+struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
+				const void *param);
+
+void hci_req_add_le_scan_disable(struct hci_request *req);
+void hci_req_add_le_passive_scan(struct hci_request *req);
+
+void hci_update_page_scan(struct hci_dev *hdev);
+void __hci_update_page_scan(struct hci_request *req);
+
+int hci_update_random_address(struct hci_request *req, bool require_privacy,
+			      u8 *own_addr_type);
+
+void hci_update_background_scan(struct hci_dev *hdev);
+void __hci_update_background_scan(struct hci_request *req);
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 2c245fd..a73e73d 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -30,6 +30,12 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/hci_mon.h>
+#include <net/bluetooth/mgmt.h>
+
+#include "mgmt_util.h"
+
+static LIST_HEAD(mgmt_chan_list);
+static DEFINE_MUTEX(mgmt_chan_list_lock);
 
 static atomic_t monitor_promisc = ATOMIC_INIT(0);
 
@@ -44,11 +50,32 @@
 	struct hci_filter filter;
 	__u32             cmsg_mask;
 	unsigned short    channel;
+	unsigned long     flags;
 };
 
-static inline int hci_test_bit(int nr, void *addr)
+void hci_sock_set_flag(struct sock *sk, int nr)
 {
-	return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
+	set_bit(nr, &hci_pi(sk)->flags);
+}
+
+void hci_sock_clear_flag(struct sock *sk, int nr)
+{
+	clear_bit(nr, &hci_pi(sk)->flags);
+}
+
+int hci_sock_test_flag(struct sock *sk, int nr)
+{
+	return test_bit(nr, &hci_pi(sk)->flags);
+}
+
+unsigned short hci_sock_get_channel(struct sock *sk)
+{
+	return hci_pi(sk)->channel;
+}
+
+static inline int hci_test_bit(int nr, const void *addr)
+{
+	return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
 }
 
 /* Security filter */
@@ -183,18 +210,23 @@
 	kfree_skb(skb_copy);
 }
 
-/* Send frame to control socket */
-void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk)
+/* Send frame to sockets with specific channel */
+void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
+			 int flag, struct sock *skip_sk)
 {
 	struct sock *sk;
 
-	BT_DBG("len %d", skb->len);
+	BT_DBG("channel %u len %d", channel, skb->len);
 
 	read_lock(&hci_sk_list.lock);
 
 	sk_for_each(sk, &hci_sk_list.head) {
 		struct sk_buff *nskb;
 
+		/* Ignore socket without the flag set */
+		if (!hci_sock_test_flag(sk, flag))
+			continue;
+
 		/* Skip the original socket */
 		if (sk == skip_sk)
 			continue;
@@ -202,7 +234,7 @@
 		if (sk->sk_state != BT_BOUND)
 			continue;
 
-		if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL)
+		if (hci_pi(sk)->channel != channel)
 			continue;
 
 		nskb = skb_clone(skb, GFP_ATOMIC);
@@ -219,8 +251,8 @@
 /* Send frame to monitor socket */
 void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
 {
-	struct sock *sk;
 	struct sk_buff *skb_copy = NULL;
+	struct hci_mon_hdr *hdr;
 	__le16 opcode;
 
 	if (!atomic_read(&monitor_promisc))
@@ -251,74 +283,22 @@
 		return;
 	}
 
-	read_lock(&hci_sk_list.lock);
+	/* Create a private copy with headroom */
+	skb_copy = __pskb_copy_fclone(skb, HCI_MON_HDR_SIZE, GFP_ATOMIC, true);
+	if (!skb_copy)
+		return;
 
-	sk_for_each(sk, &hci_sk_list.head) {
-		struct sk_buff *nskb;
+	/* Put header before the data */
+	hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE);
+	hdr->opcode = opcode;
+	hdr->index = cpu_to_le16(hdev->id);
+	hdr->len = cpu_to_le16(skb->len);
 
-		if (sk->sk_state != BT_BOUND)
-			continue;
-
-		if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR)
-			continue;
-
-		if (!skb_copy) {
-			struct hci_mon_hdr *hdr;
-
-			/* Create a private copy with headroom */
-			skb_copy = __pskb_copy_fclone(skb, HCI_MON_HDR_SIZE,
-						      GFP_ATOMIC, true);
-			if (!skb_copy)
-				continue;
-
-			/* Put header before the data */
-			hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE);
-			hdr->opcode = opcode;
-			hdr->index = cpu_to_le16(hdev->id);
-			hdr->len = cpu_to_le16(skb->len);
-		}
-
-		nskb = skb_clone(skb_copy, GFP_ATOMIC);
-		if (!nskb)
-			continue;
-
-		if (sock_queue_rcv_skb(sk, nskb))
-			kfree_skb(nskb);
-	}
-
-	read_unlock(&hci_sk_list.lock);
-
+	hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy,
+			    HCI_SOCK_TRUSTED, NULL);
 	kfree_skb(skb_copy);
 }
 
-static void send_monitor_event(struct sk_buff *skb)
-{
-	struct sock *sk;
-
-	BT_DBG("len %d", skb->len);
-
-	read_lock(&hci_sk_list.lock);
-
-	sk_for_each(sk, &hci_sk_list.head) {
-		struct sk_buff *nskb;
-
-		if (sk->sk_state != BT_BOUND)
-			continue;
-
-		if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR)
-			continue;
-
-		nskb = skb_clone(skb, GFP_ATOMIC);
-		if (!nskb)
-			continue;
-
-		if (sock_queue_rcv_skb(sk, nskb))
-			kfree_skb(nskb);
-	}
-
-	read_unlock(&hci_sk_list.lock);
-}
-
 static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
 {
 	struct hci_mon_hdr *hdr;
@@ -422,7 +402,8 @@
 
 		skb = create_monitor_event(hdev, event);
 		if (skb) {
-			send_monitor_event(skb);
+			hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
+					    HCI_SOCK_TRUSTED, NULL);
 			kfree_skb(skb);
 		}
 	}
@@ -453,6 +434,56 @@
 	}
 }
 
+static struct hci_mgmt_chan *__hci_mgmt_chan_find(unsigned short channel)
+{
+	struct hci_mgmt_chan *c;
+
+	list_for_each_entry(c, &mgmt_chan_list, list) {
+		if (c->channel == channel)
+			return c;
+	}
+
+	return NULL;
+}
+
+static struct hci_mgmt_chan *hci_mgmt_chan_find(unsigned short channel)
+{
+	struct hci_mgmt_chan *c;
+
+	mutex_lock(&mgmt_chan_list_lock);
+	c = __hci_mgmt_chan_find(channel);
+	mutex_unlock(&mgmt_chan_list_lock);
+
+	return c;
+}
+
+int hci_mgmt_chan_register(struct hci_mgmt_chan *c)
+{
+	if (c->channel < HCI_CHANNEL_CONTROL)
+		return -EINVAL;
+
+	mutex_lock(&mgmt_chan_list_lock);
+	if (__hci_mgmt_chan_find(c->channel)) {
+		mutex_unlock(&mgmt_chan_list_lock);
+		return -EALREADY;
+	}
+
+	list_add_tail(&c->list, &mgmt_chan_list);
+
+	mutex_unlock(&mgmt_chan_list_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(hci_mgmt_chan_register);
+
+void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c)
+{
+	mutex_lock(&mgmt_chan_list_lock);
+	list_del(&c->list);
+	mutex_unlock(&mgmt_chan_list_lock);
+}
+EXPORT_SYMBOL(hci_mgmt_chan_unregister);
+
 static int hci_sock_release(struct socket *sock)
 {
 	struct sock *sk = sock->sk;
@@ -473,7 +504,7 @@
 	if (hdev) {
 		if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
 			mgmt_index_added(hdev);
-			clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+			hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
 			hci_dev_close(hdev->id);
 		}
 
@@ -533,10 +564,10 @@
 	if (!hdev)
 		return -EBADFD;
 
-	if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
 		return -EBUSY;
 
-	if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
 		return -EOPNOTSUPP;
 
 	if (hdev->dev_type != HCI_BREDR)
@@ -712,14 +743,14 @@
 
 		if (test_bit(HCI_UP, &hdev->flags) ||
 		    test_bit(HCI_INIT, &hdev->flags) ||
-		    test_bit(HCI_SETUP, &hdev->dev_flags) ||
-		    test_bit(HCI_CONFIG, &hdev->dev_flags)) {
+		    hci_dev_test_flag(hdev, HCI_SETUP) ||
+		    hci_dev_test_flag(hdev, HCI_CONFIG)) {
 			err = -EBUSY;
 			hci_dev_put(hdev);
 			goto done;
 		}
 
-		if (test_and_set_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+		if (hci_dev_test_and_set_flag(hdev, HCI_USER_CHANNEL)) {
 			err = -EUSERS;
 			hci_dev_put(hdev);
 			goto done;
@@ -729,7 +760,7 @@
 
 		err = hci_dev_open(hdev->id);
 		if (err) {
-			clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+			hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
 			mgmt_index_added(hdev);
 			hci_dev_put(hdev);
 			goto done;
@@ -740,19 +771,6 @@
 		hci_pi(sk)->hdev = hdev;
 		break;
 
-	case HCI_CHANNEL_CONTROL:
-		if (haddr.hci_dev != HCI_DEV_NONE) {
-			err = -EINVAL;
-			goto done;
-		}
-
-		if (!capable(CAP_NET_ADMIN)) {
-			err = -EPERM;
-			goto done;
-		}
-
-		break;
-
 	case HCI_CHANNEL_MONITOR:
 		if (haddr.hci_dev != HCI_DEV_NONE) {
 			err = -EINVAL;
@@ -764,14 +782,51 @@
 			goto done;
 		}
 
+		/* The monitor interface is restricted to CAP_NET_RAW
+		 * capabilities and with that implicitly trusted.
+		 */
+		hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
+
 		send_monitor_replay(sk);
 
 		atomic_inc(&monitor_promisc);
 		break;
 
 	default:
-		err = -EINVAL;
-		goto done;
+		if (!hci_mgmt_chan_find(haddr.hci_channel)) {
+			err = -EINVAL;
+			goto done;
+		}
+
+		if (haddr.hci_dev != HCI_DEV_NONE) {
+			err = -EINVAL;
+			goto done;
+		}
+
+		/* Users with CAP_NET_ADMIN capabilities are allowed
+		 * access to all management commands and events. For
+		 * untrusted users the interface is restricted and
+		 * also only untrusted events are sent.
+		 */
+		if (capable(CAP_NET_ADMIN))
+			hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
+
+		/* At the moment the index and unconfigured index events
+		 * are enabled unconditionally. Setting them on each
+		 * socket when binding keeps this functionality. They
+		 * however might be cleared later and then sending of these
+		 * events will be disabled, but that is then intentional.
+		 *
+		 * This also enables generic events that are safe to be
+		 * received by untrusted users. Example for such events
+		 * are changes to settings, class of device, name etc.
+		 */
+		if (haddr.hci_channel == HCI_CHANNEL_CONTROL) {
+			hci_sock_set_flag(sk, HCI_MGMT_INDEX_EVENTS);
+			hci_sock_set_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS);
+			hci_sock_set_flag(sk, HCI_MGMT_GENERIC_EVENTS);
+		}
+		break;
 	}
 
 
@@ -851,8 +906,13 @@
 	}
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+			    int flags)
+#else
 static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 			    struct msghdr *msg, size_t len, int flags)
+#endif
 {
 	int noblock = flags & MSG_DONTWAIT;
 	struct sock *sk = sock->sk;
@@ -885,10 +945,13 @@
 		hci_sock_cmsg(sk, msg, skb);
 		break;
 	case HCI_CHANNEL_USER:
-	case HCI_CHANNEL_CONTROL:
 	case HCI_CHANNEL_MONITOR:
 		sock_recv_timestamp(msg, sk, skb);
 		break;
+	default:
+		if (hci_mgmt_chan_find(hci_pi(sk)->channel))
+			sock_recv_timestamp(msg, sk, skb);
+		break;
 	}
 
 	skb_free_datagram(sk, skb);
@@ -896,10 +959,127 @@
 	return err ? : copied;
 }
 
+static int hci_mgmt_cmd(struct hci_mgmt_chan *chan, struct sock *sk,
+			struct msghdr *msg, size_t msglen)
+{
+	void *buf;
+	u8 *cp;
+	struct mgmt_hdr *hdr;
+	u16 opcode, index, len;
+	struct hci_dev *hdev = NULL;
+	const struct hci_mgmt_handler *handler;
+	bool var_len, no_hdev;
+	int err;
+
+	BT_DBG("got %zu bytes", msglen);
+
+	if (msglen < sizeof(*hdr))
+		return -EINVAL;
+
+	buf = kmalloc(msglen, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (memcpy_from_msg(buf, msg, msglen)) {
+		err = -EFAULT;
+		goto done;
+	}
+
+	hdr = buf;
+	opcode = __le16_to_cpu(hdr->opcode);
+	index = __le16_to_cpu(hdr->index);
+	len = __le16_to_cpu(hdr->len);
+
+	if (len != msglen - sizeof(*hdr)) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	if (opcode >= chan->handler_count ||
+	    chan->handlers[opcode].func == NULL) {
+		BT_DBG("Unknown op %u", opcode);
+		err = mgmt_cmd_status(sk, index, opcode,
+				      MGMT_STATUS_UNKNOWN_COMMAND);
+		goto done;
+	}
+
+	handler = &chan->handlers[opcode];
+
+	if (!hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) &&
+	    !(handler->flags & HCI_MGMT_UNTRUSTED)) {
+		err = mgmt_cmd_status(sk, index, opcode,
+				      MGMT_STATUS_PERMISSION_DENIED);
+		goto done;
+	}
+
+	if (index != MGMT_INDEX_NONE) {
+		hdev = hci_dev_get(index);
+		if (!hdev) {
+			err = mgmt_cmd_status(sk, index, opcode,
+					      MGMT_STATUS_INVALID_INDEX);
+			goto done;
+		}
+
+		if (hci_dev_test_flag(hdev, HCI_SETUP) ||
+		    hci_dev_test_flag(hdev, HCI_CONFIG) ||
+		    hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
+			err = mgmt_cmd_status(sk, index, opcode,
+					      MGMT_STATUS_INVALID_INDEX);
+			goto done;
+		}
+
+		if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
+		    !(handler->flags & HCI_MGMT_UNCONFIGURED)) {
+			err = mgmt_cmd_status(sk, index, opcode,
+					      MGMT_STATUS_INVALID_INDEX);
+			goto done;
+		}
+	}
+
+	no_hdev = (handler->flags & HCI_MGMT_NO_HDEV);
+	if (no_hdev != !hdev) {
+		err = mgmt_cmd_status(sk, index, opcode,
+				      MGMT_STATUS_INVALID_INDEX);
+		goto done;
+	}
+
+	var_len = (handler->flags & HCI_MGMT_VAR_LEN);
+	if ((var_len && len < handler->data_len) ||
+	    (!var_len && len != handler->data_len)) {
+		err = mgmt_cmd_status(sk, index, opcode,
+				      MGMT_STATUS_INVALID_PARAMS);
+		goto done;
+	}
+
+	if (hdev && chan->hdev_init)
+		chan->hdev_init(sk, hdev);
+
+	cp = buf + sizeof(*hdr);
+
+	err = handler->func(sk, hdev, cp, len);
+	if (err < 0)
+		goto done;
+
+	err = msglen;
+
+done:
+	if (hdev)
+		hci_dev_put(hdev);
+
+	kfree(buf);
+	return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
+			    size_t len)
+#else
 static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 			    struct msghdr *msg, size_t len)
+#endif
 {
 	struct sock *sk = sock->sk;
+	struct hci_mgmt_chan *chan;
 	struct hci_dev *hdev;
 	struct sk_buff *skb;
 	int err;
@@ -921,14 +1101,18 @@
 	case HCI_CHANNEL_RAW:
 	case HCI_CHANNEL_USER:
 		break;
-	case HCI_CHANNEL_CONTROL:
-		err = mgmt_control(sk, msg, len);
-		goto done;
 	case HCI_CHANNEL_MONITOR:
 		err = -EOPNOTSUPP;
 		goto done;
 	default:
-		err = -EINVAL;
+		mutex_lock(&mgmt_chan_list_lock);
+		chan = __hci_mgmt_chan_find(hci_pi(sk)->channel);
+		if (chan)
+			err = hci_mgmt_cmd(chan, sk, msg, len);
+		else
+			err = -EINVAL;
+
+		mutex_unlock(&mgmt_chan_list_lock);
 		goto done;
 	}
 
@@ -1230,6 +1414,8 @@
 {
 	int err;
 
+	BUILD_BUG_ON(sizeof(struct sockaddr_hci) > sizeof(struct sockaddr));
+
 	err = proto_register(&hci_sk_proto, 0);
 	if (err < 0)
 		return err;
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 867f53e..517c8f6 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -70,10 +70,11 @@
 
 static void hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
 {
+	u32 valid_flags = 0;
 	memset(ci, 0, sizeof(*ci));
 	bacpy(&ci->bdaddr, &session->bdaddr);
 
-	ci->flags = session->flags;
+	ci->flags = session->flags & valid_flags;
 	ci->state = BT_CONNECTED;
 
 	if (session->input) {
@@ -911,7 +912,7 @@
 	kref_init(&session->ref);
 	atomic_set(&session->state, HIDP_SESSION_IDLING);
 	init_waitqueue_head(&session->state_queue);
-	session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
+	session->flags = req->flags & BIT(HIDP_BLUETOOTH_VENDOR_ID);
 
 	/* connection management */
 	bacpy(&session->bdaddr, bdaddr);
@@ -1316,15 +1317,21 @@
 			struct socket *ctrl_sock,
 			struct socket *intr_sock)
 {
+	u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG) |
+			  BIT(HIDP_BOOT_PROTOCOL_MODE);
 	struct hidp_session *session;
 	struct l2cap_conn *conn;
-	struct l2cap_chan *chan = l2cap_pi(ctrl_sock->sk)->chan;
+	struct l2cap_chan *chan;
 	int ret;
 
 	ret = hidp_verify_sockets(ctrl_sock, intr_sock);
 	if (ret)
 		return ret;
 
+	if (req->flags & ~valid_flags)
+		return -EINVAL;
+
+	chan = l2cap_pi(ctrl_sock->sk)->chan;
 	conn = NULL;
 	l2cap_chan_lock(chan);
 	if (chan->conn)
@@ -1354,13 +1361,17 @@
 
 int hidp_connection_del(struct hidp_conndel_req *req)
 {
+	u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG);
 	struct hidp_session *session;
 
+	if (req->flags & ~valid_flags)
+		return -EINVAL;
+
 	session = hidp_session_find(&req->bdaddr);
 	if (!session)
 		return -ENOENT;
 
-	if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG))
+	if (req->flags & BIT(HIDP_VIRTUAL_CABLE_UNPLUG))
 		hidp_send_ctrl_message(session,
 				       HIDP_TRANS_HID_CONTROL |
 				         HIDP_CTRL_VIRTUAL_CABLE_UNPLUG,
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 3c6bfb1..31a3cfc 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -63,10 +63,10 @@
 static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
 		     struct sk_buff_head *skbs, u8 event);
 
-static inline __u8 bdaddr_type(struct hci_conn *hcon, __u8 type)
+static inline u8 bdaddr_type(u8 link_type, u8 bdaddr_type)
 {
-	if (hcon->type == LE_LINK) {
-		if (type == ADDR_LE_DEV_PUBLIC)
+	if (link_type == LE_LINK) {
+		if (bdaddr_type == ADDR_LE_DEV_PUBLIC)
 			return BDADDR_LE_PUBLIC;
 		else
 			return BDADDR_LE_RANDOM;
@@ -75,6 +75,16 @@
 	return BDADDR_BREDR;
 }
 
+static inline u8 bdaddr_src_type(struct hci_conn *hcon)
+{
+	return bdaddr_type(hcon->type, hcon->src_type);
+}
+
+static inline u8 bdaddr_dst_type(struct hci_conn *hcon)
+{
+	return bdaddr_type(hcon->type, hcon->dst_type);
+}
+
 /* ---- L2CAP channels ---- */
 
 static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
@@ -282,7 +292,7 @@
 	struct sk_buff *skb;
 
 	skb_queue_walk(head, skb) {
-		if (bt_cb(skb)->control.txseq == seq)
+		if (bt_cb(skb)->l2cap.txseq == seq)
 			return skb;
 	}
 
@@ -646,7 +656,7 @@
 	list_for_each_entry(chan, &conn->chan_l, list) {
 		l2cap_chan_lock(chan);
 		bacpy(&chan->dst, &hcon->dst);
-		chan->dst_type = bdaddr_type(hcon, hcon->dst_type);
+		chan->dst_type = bdaddr_dst_type(hcon);
 		l2cap_chan_unlock(chan);
 	}
 
@@ -944,11 +954,11 @@
 {
 	if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
 		__unpack_extended_control(get_unaligned_le32(skb->data),
-					  &bt_cb(skb)->control);
+					  &bt_cb(skb)->l2cap);
 		skb_pull(skb, L2CAP_EXT_CTRL_SIZE);
 	} else {
 		__unpack_enhanced_control(get_unaligned_le16(skb->data),
-					  &bt_cb(skb)->control);
+					  &bt_cb(skb)->l2cap);
 		skb_pull(skb, L2CAP_ENH_CTRL_SIZE);
 	}
 }
@@ -1190,8 +1200,8 @@
 
 	chan->retry_count = 0;
 	skb_queue_walk(&chan->tx_q, skb) {
-		if (bt_cb(skb)->control.retries)
-			bt_cb(skb)->control.retries = 1;
+		if (bt_cb(skb)->l2cap.retries)
+			bt_cb(skb)->l2cap.retries = 1;
 		else
 			break;
 	}
@@ -1234,6 +1244,13 @@
 
 static void l2cap_chan_ready(struct l2cap_chan *chan)
 {
+	/* The channel may have already been flagged as connected in
+	 * case of receiving data before the L2CAP info req/rsp
+	 * procedure is complete.
+	 */
+	if (chan->state == BT_CONNECTED)
+		return;
+
 	/* This clears all conf flags, including CONF_NOT_COMPLETE */
 	chan->conf_state = 0;
 	__clear_chan_timer(chan);
@@ -1829,8 +1846,8 @@
 
 		skb = skb_dequeue(&chan->tx_q);
 
-		bt_cb(skb)->control.retries = 1;
-		control = &bt_cb(skb)->control;
+		bt_cb(skb)->l2cap.retries = 1;
+		control = &bt_cb(skb)->l2cap;
 
 		control->reqseq = 0;
 		control->txseq = chan->next_tx_seq;
@@ -1874,8 +1891,8 @@
 
 		skb = chan->tx_send_head;
 
-		bt_cb(skb)->control.retries = 1;
-		control = &bt_cb(skb)->control;
+		bt_cb(skb)->l2cap.retries = 1;
+		control = &bt_cb(skb)->l2cap;
 
 		if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
 			control->final = 1;
@@ -1946,11 +1963,11 @@
 			continue;
 		}
 
-		bt_cb(skb)->control.retries++;
-		control = bt_cb(skb)->control;
+		bt_cb(skb)->l2cap.retries++;
+		control = bt_cb(skb)->l2cap;
 
 		if (chan->max_tx != 0 &&
-		    bt_cb(skb)->control.retries > chan->max_tx) {
+		    bt_cb(skb)->l2cap.retries > chan->max_tx) {
 			BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
 			l2cap_send_disconn_req(chan, ECONNRESET);
 			l2cap_seq_list_clear(&chan->retrans_list);
@@ -2028,7 +2045,7 @@
 
 	if (chan->unacked_frames) {
 		skb_queue_walk(&chan->tx_q, skb) {
-			if (bt_cb(skb)->control.txseq == control->reqseq ||
+			if (bt_cb(skb)->l2cap.txseq == control->reqseq ||
 			    skb == chan->tx_send_head)
 				break;
 		}
@@ -2038,7 +2055,7 @@
 				break;
 
 			l2cap_seq_list_append(&chan->retrans_list,
-					      bt_cb(skb)->control.txseq);
+					      bt_cb(skb)->l2cap.txseq);
 		}
 
 		l2cap_ertm_resend(chan);
@@ -2260,8 +2277,8 @@
 		return ERR_PTR(err);
 	}
 
-	bt_cb(skb)->control.fcs = chan->fcs;
-	bt_cb(skb)->control.retries = 0;
+	bt_cb(skb)->l2cap.fcs = chan->fcs;
+	bt_cb(skb)->l2cap.retries = 0;
 	return skb;
 }
 
@@ -2314,7 +2331,7 @@
 			return PTR_ERR(skb);
 		}
 
-		bt_cb(skb)->control.sar = sar;
+		bt_cb(skb)->l2cap.sar = sar;
 		__skb_queue_tail(seg_queue, skb);
 
 		len -= pdu_len;
@@ -2849,7 +2866,7 @@
 			continue;
 
 		/* Don't send frame to the channel it came from */
-		if (bt_cb(skb)->chan == chan)
+		if (bt_cb(skb)->l2cap.chan == chan)
 			continue;
 
 		nskb = skb_clone(skb, GFP_KERNEL);
@@ -3800,8 +3817,8 @@
 
 	bacpy(&chan->src, &conn->hcon->src);
 	bacpy(&chan->dst, &conn->hcon->dst);
-	chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type);
-	chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type);
+	chan->src_type = bdaddr_src_type(conn->hcon);
+	chan->dst_type = bdaddr_dst_type(conn->hcon);
 	chan->psm  = psm;
 	chan->dcid = scid;
 	chan->local_amp_id = amp_id;
@@ -3893,7 +3910,7 @@
 		return -EPROTO;
 
 	hci_dev_lock(hdev);
-	if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
+	if (hci_dev_test_flag(hdev, HCI_MGMT) &&
 	    !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags))
 		mgmt_device_connected(hdev, hcon, 0, NULL, 0);
 	hci_dev_unlock(hdev);
@@ -5451,8 +5468,8 @@
 
 	bacpy(&chan->src, &conn->hcon->src);
 	bacpy(&chan->dst, &conn->hcon->dst);
-	chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type);
-	chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type);
+	chan->src_type = bdaddr_src_type(conn->hcon);
+	chan->dst_type = bdaddr_dst_type(conn->hcon);
 	chan->psm  = psm;
 	chan->dcid = scid;
 	chan->omtu = mtu;
@@ -5911,7 +5928,7 @@
 
 		skb_unlink(skb, &chan->srej_q);
 		chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
-		err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->control);
+		err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->l2cap);
 		if (err)
 			break;
 	}
@@ -5945,7 +5962,7 @@
 		return;
 	}
 
-	if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) {
+	if (chan->max_tx != 0 && bt_cb(skb)->l2cap.retries >= chan->max_tx) {
 		BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
 		l2cap_send_disconn_req(chan, ECONNRESET);
 		return;
@@ -5998,7 +6015,7 @@
 	skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq);
 
 	if (chan->max_tx && skb &&
-	    bt_cb(skb)->control.retries >= chan->max_tx) {
+	    bt_cb(skb)->l2cap.retries >= chan->max_tx) {
 		BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
 		l2cap_send_disconn_req(chan, ECONNRESET);
 		return;
@@ -6558,7 +6575,7 @@
 
 static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
 {
-	struct l2cap_ctrl *control = &bt_cb(skb)->control;
+	struct l2cap_ctrl *control = &bt_cb(skb)->l2cap;
 	u16 len;
 	u8 event;
 
@@ -6785,6 +6802,13 @@
 
 	BT_DBG("chan %p, len %d", chan, skb->len);
 
+	/* If we receive data on a fixed channel before the info req/rsp
+	 * procdure is done simply assume that the channel is supported
+	 * and mark it as ready.
+	 */
+	if (chan->chan_type == L2CAP_CHAN_FIXED)
+		l2cap_chan_ready(chan);
+
 	if (chan->state != BT_CONNECTED)
 		goto drop;
 
@@ -6850,8 +6874,8 @@
 		goto drop;
 
 	/* Store remote BD_ADDR and PSM for msg_name */
-	bacpy(&bt_cb(skb)->bdaddr, &hcon->dst);
-	bt_cb(skb)->psm = psm;
+	bacpy(&bt_cb(skb)->l2cap.bdaddr, &hcon->dst);
+	bt_cb(skb)->l2cap.psm = psm;
 
 	if (!chan->ops->recv(chan, skb)) {
 		l2cap_chan_put(chan);
@@ -6891,7 +6915,7 @@
 	 */
 	if (hcon->type == LE_LINK &&
 	    hci_bdaddr_list_lookup(&hcon->hdev->blacklist, &hcon->dst,
-				   bdaddr_type(hcon, hcon->dst_type))) {
+				   bdaddr_dst_type(hcon))) {
 		kfree_skb(skb);
 		return;
 	}
@@ -6973,12 +6997,12 @@
 	conn->local_fixed_chan = L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS;
 
 	if (hcon->type == ACL_LINK &&
-	    test_bit(HCI_HS_ENABLED, &hcon->hdev->dev_flags))
+	    hci_dev_test_flag(hcon->hdev, HCI_HS_ENABLED))
 		conn->local_fixed_chan |= L2CAP_FC_A2MP;
 
-	if (test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags) &&
+	if (hci_dev_test_flag(hcon->hdev, HCI_LE_ENABLED) &&
 	    (bredr_sc_enabled(hcon->hdev) ||
-	     test_bit(HCI_FORCE_LESC, &hcon->hdev->dbg_flags)))
+	     hci_dev_test_flag(hcon->hdev, HCI_FORCE_BREDR_SMP)))
 		conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR;
 
 	mutex_init(&conn->ident_lock);
@@ -7098,7 +7122,7 @@
 		else
 			dst_type = ADDR_LE_DEV_RANDOM;
 
-		if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+		if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
 			role = HCI_ROLE_SLAVE;
 		else
 			role = HCI_ROLE_MASTER;
@@ -7133,7 +7157,7 @@
 
 	/* Update source addr of the socket */
 	bacpy(&chan->src, &hcon->src);
-	chan->src_type = bdaddr_type(hcon, hcon->src_type);
+	chan->src_type = bdaddr_src_type(hcon);
 
 	__l2cap_chan_add(conn, chan);
 
@@ -7207,8 +7231,10 @@
  * global list (by passing NULL as first parameter).
  */
 static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
-						  bdaddr_t *src, u8 link_type)
+						  struct hci_conn *hcon)
 {
+	u8 src_type = bdaddr_src_type(hcon);
+
 	read_lock(&chan_list_lock);
 
 	if (c)
@@ -7221,11 +7247,9 @@
 			continue;
 		if (c->state != BT_LISTEN)
 			continue;
-		if (bacmp(&c->src, src) && bacmp(&c->src, BDADDR_ANY))
+		if (bacmp(&c->src, &hcon->src) && bacmp(&c->src, BDADDR_ANY))
 			continue;
-		if (link_type == ACL_LINK && c->src_type != BDADDR_BREDR)
-			continue;
-		if (link_type == LE_LINK && c->src_type == BDADDR_BREDR)
+		if (src_type != c->src_type)
 			continue;
 
 		l2cap_chan_hold(c);
@@ -7238,13 +7262,16 @@
 	return NULL;
 }
 
-void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
+static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
 {
 	struct hci_dev *hdev = hcon->hdev;
 	struct l2cap_conn *conn;
 	struct l2cap_chan *pchan;
 	u8 dst_type;
 
+	if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
+		return;
+
 	BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
 
 	if (status) {
@@ -7256,7 +7283,7 @@
 	if (!conn)
 		return;
 
-	dst_type = bdaddr_type(hcon, hcon->dst_type);
+	dst_type = bdaddr_dst_type(hcon);
 
 	/* If device is blocked, do not create channels for it */
 	if (hci_bdaddr_list_lookup(&hdev->blacklist, &hcon->dst, dst_type))
@@ -7267,7 +7294,7 @@
 	 * we left off, because the list lock would prevent calling the
 	 * potentially sleeping l2cap_chan_lock() function.
 	 */
-	pchan = l2cap_global_fixed_chan(NULL, &hdev->bdaddr, hcon->type);
+	pchan = l2cap_global_fixed_chan(NULL, hcon);
 	while (pchan) {
 		struct l2cap_chan *chan, *next;
 
@@ -7280,7 +7307,7 @@
 		if (chan) {
 			bacpy(&chan->src, &hcon->src);
 			bacpy(&chan->dst, &hcon->dst);
-			chan->src_type = bdaddr_type(hcon, hcon->src_type);
+			chan->src_type = bdaddr_src_type(hcon);
 			chan->dst_type = dst_type;
 
 			__l2cap_chan_add(conn, chan);
@@ -7288,8 +7315,7 @@
 
 		l2cap_chan_unlock(pchan);
 next:
-		next = l2cap_global_fixed_chan(pchan, &hdev->bdaddr,
-					       hcon->type);
+		next = l2cap_global_fixed_chan(pchan, hcon);
 		l2cap_chan_put(pchan);
 		pchan = next;
 	}
@@ -7308,8 +7334,11 @@
 	return conn->disc_reason;
 }
 
-void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
+static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
 {
+	if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
+		return;
+
 	BT_DBG("hcon %p reason %d", hcon, reason);
 
 	l2cap_conn_del(hcon, bt_to_errno(reason));
@@ -7332,13 +7361,13 @@
 	}
 }
 
-int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
+static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
 {
 	struct l2cap_conn *conn = hcon->l2cap_data;
 	struct l2cap_chan *chan;
 
 	if (!conn)
-		return 0;
+		return;
 
 	BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt);
 
@@ -7421,8 +7450,6 @@
 	}
 
 	mutex_unlock(&conn->chan_lock);
-
-	return 0;
 }
 
 int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
@@ -7530,6 +7557,13 @@
 	return 0;
 }
 
+static struct hci_cb l2cap_cb = {
+	.name		= "L2CAP",
+	.connect_cfm	= l2cap_connect_cfm,
+	.disconn_cfm	= l2cap_disconn_cfm,
+	.security_cfm	= l2cap_security_cfm,
+};
+
 static int l2cap_debugfs_show(struct seq_file *f, void *p)
 {
 	struct l2cap_chan *c;
@@ -7537,8 +7571,8 @@
 	read_lock(&chan_list_lock);
 
 	list_for_each_entry(c, &chan_list, global_l) {
-		seq_printf(f, "%pMR %pMR %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
-			   &c->src, &c->dst,
+		seq_printf(f, "%pMR (%u) %pMR (%u) %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
+			   &c->src, c->src_type, &c->dst, c->dst_type,
 			   c->state, __le16_to_cpu(c->psm),
 			   c->scid, c->dcid, c->imtu, c->omtu,
 			   c->sec_level, c->mode);
@@ -7571,6 +7605,8 @@
 	if (err < 0)
 		return err;
 
+	hci_register_cb(&l2cap_cb);
+
 	if (IS_ERR_OR_NULL(bt_debugfs))
 		return 0;
 
@@ -7588,6 +7624,7 @@
 void l2cap_exit(void)
 {
 	debugfs_remove(l2cap_debugfs);
+	hci_unregister_cb(&l2cap_cb);
 	l2cap_cleanup_sockets();
 }
 
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index b2ca901..2f306d4 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -302,7 +302,7 @@
 static int l2cap_sock_accept(struct socket *sock, struct socket *newsock,
 			     int flags)
 {
-	DECLARE_WAITQUEUE(wait, current);
+	DEFINE_WAIT_FUNC(wait, woken_wake_function);
 	struct sock *sk = sock->sk, *nsk;
 	long timeo;
 	int err = 0;
@@ -316,8 +316,6 @@
 	/* Wait for an incoming connection. (wake-one). */
 	add_wait_queue_exclusive(sk_sleep(sk), &wait);
 	while (1) {
-		set_current_state(TASK_INTERRUPTIBLE);
-
 		if (sk->sk_state != BT_LISTEN) {
 			err = -EBADFD;
 			break;
@@ -338,10 +336,11 @@
 		}
 
 		release_sock(sk);
-		timeo = schedule_timeout(timeo);
+
+		timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
+
 		lock_sock_nested(sk, L2CAP_NESTING_PARENT);
 	}
-	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(sk_sleep(sk), &wait);
 
 	if (err)
@@ -945,8 +944,13 @@
 	return err;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg,
+			      size_t len)
+#else
 static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 			      struct msghdr *msg, size_t len)
+#endif
 {
 	struct sock *sk = sock->sk;
 	struct l2cap_chan *chan = l2cap_pi(sk)->chan;
@@ -977,8 +981,13 @@
 	return err;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
+			      size_t len, int flags)
+#else
 static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 			      struct msghdr *msg, size_t len, int flags)
+#endif
 {
 	struct sock *sk = sock->sk;
 	struct l2cap_pinfo *pi = l2cap_pi(sk);
@@ -1005,9 +1014,17 @@
 	release_sock(sk);
 
 	if (sock->type == SOCK_STREAM)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+		err = bt_sock_stream_recvmsg(sock, msg, len, flags);
+#else
 		err = bt_sock_stream_recvmsg(iocb, sock, msg, len, flags);
+#endif
 	else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+		err = bt_sock_recvmsg(sock, msg, len, flags);
+#else
 		err = bt_sock_recvmsg(iocb, sock, msg, len, flags);
+#endif
 
 	if (pi->chan->mode != L2CAP_MODE_ERTM)
 		return err;
@@ -1335,17 +1352,19 @@
 
 	skb->priority = sk->sk_priority;
 
-	bt_cb(skb)->chan = chan;
+	bt_cb(skb)->l2cap.chan = chan;
 
 	return skb;
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
 static int l2cap_sock_memcpy_fromiovec_cb(struct l2cap_chan *chan,
 					  unsigned char *kdata,
 					  struct iovec *iov, int len)
 {
 	return memcpy_fromiovec(kdata, iov, len);
 }
+#endif
 
 static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
 {
@@ -1441,7 +1460,9 @@
 	.set_shutdown		= l2cap_sock_set_shutdown_cb,
 	.get_sndtimeo		= l2cap_sock_get_sndtimeo_cb,
 	.alloc_skb		= l2cap_sock_alloc_skb_cb,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
 	.memcpy_fromiovec	= l2cap_sock_memcpy_fromiovec_cb,
+#endif
 };
 
 static void l2cap_sock_destruct(struct sock *sk)
@@ -1467,8 +1488,8 @@
 
 	memset(la, 0, sizeof(struct sockaddr_l2));
 	la->l2_family = AF_BLUETOOTH;
-	la->l2_psm = bt_cb(skb)->psm;
-	bacpy(&la->l2_bdaddr, &bt_cb(skb)->bdaddr);
+	la->l2_psm = bt_cb(skb)->l2cap.psm;
+	bacpy(&la->l2_bdaddr, &bt_cb(skb)->l2cap.bdaddr);
 
 	*msg_namelen = sizeof(struct sockaddr_l2);
 }
@@ -1636,6 +1657,8 @@
 {
 	int err;
 
+	BUILD_BUG_ON(sizeof(struct sockaddr_l2) > sizeof(struct sockaddr));
+
 	err = proto_register(&l2cap_proto, 0);
 	if (err < 0)
 		return err;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 693ce8b..7fd87e7 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -29,13 +29,16 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci_sock.h>
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/mgmt.h>
 
+#include "hci_request.h"
 #include "smp.h"
+#include "mgmt_util.h"
 
 #define MGMT_VERSION	1
-#define MGMT_REVISION	8
+#define MGMT_REVISION	9
 
 static const u16 mgmt_commands[] = {
 	MGMT_OP_READ_INDEX_LIST,
@@ -94,6 +97,11 @@
 	MGMT_OP_SET_EXTERNAL_CONFIG,
 	MGMT_OP_SET_PUBLIC_ADDRESS,
 	MGMT_OP_START_SERVICE_DISCOVERY,
+	MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
+	MGMT_OP_READ_EXT_INDEX_LIST,
+	MGMT_OP_READ_ADV_FEATURES,
+	MGMT_OP_ADD_ADVERTISING,
+	MGMT_OP_REMOVE_ADVERTISING,
 };
 
 static const u16 mgmt_events[] = {
@@ -126,20 +134,38 @@
 	MGMT_EV_UNCONF_INDEX_ADDED,
 	MGMT_EV_UNCONF_INDEX_REMOVED,
 	MGMT_EV_NEW_CONFIG_OPTIONS,
+	MGMT_EV_EXT_INDEX_ADDED,
+	MGMT_EV_EXT_INDEX_REMOVED,
+	MGMT_EV_LOCAL_OOB_DATA_UPDATED,
+	MGMT_EV_ADVERTISING_ADDED,
+	MGMT_EV_ADVERTISING_REMOVED,
+};
+
+static const u16 mgmt_untrusted_commands[] = {
+	MGMT_OP_READ_INDEX_LIST,
+	MGMT_OP_READ_INFO,
+	MGMT_OP_READ_UNCONF_INDEX_LIST,
+	MGMT_OP_READ_CONFIG_INFO,
+	MGMT_OP_READ_EXT_INDEX_LIST,
+};
+
+static const u16 mgmt_untrusted_events[] = {
+	MGMT_EV_INDEX_ADDED,
+	MGMT_EV_INDEX_REMOVED,
+	MGMT_EV_NEW_SETTINGS,
+	MGMT_EV_CLASS_OF_DEV_CHANGED,
+	MGMT_EV_LOCAL_NAME_CHANGED,
+	MGMT_EV_UNCONF_INDEX_ADDED,
+	MGMT_EV_UNCONF_INDEX_REMOVED,
+	MGMT_EV_NEW_CONFIG_OPTIONS,
+	MGMT_EV_EXT_INDEX_ADDED,
+	MGMT_EV_EXT_INDEX_REMOVED,
 };
 
 #define CACHE_TIMEOUT	msecs_to_jiffies(2 * 1000)
 
-struct pending_cmd {
-	struct list_head list;
-	u16 opcode;
-	int index;
-	void *param;
-	size_t param_len;
-	struct sock *sk;
-	void *user_data;
-	void (*cmd_complete)(struct pending_cmd *cmd, u8 status);
-};
+#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
+		 "\x00\x00\x00\x00\x00\x00\x00\x00"
 
 /* HCI to MGMT error code conversion table */
 static u8 mgmt_status_table[] = {
@@ -214,98 +240,32 @@
 	return MGMT_STATUS_FAILED;
 }
 
-static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
+static int mgmt_index_event(u16 event, struct hci_dev *hdev, void *data,
+			    u16 len, int flag)
+{
+	return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len,
+			       flag, NULL);
+}
+
+static int mgmt_limited_event(u16 event, struct hci_dev *hdev, void *data,
+			      u16 len, int flag, struct sock *skip_sk)
+{
+	return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len,
+			       flag, skip_sk);
+}
+
+static int mgmt_generic_event(u16 event, struct hci_dev *hdev, void *data,
+			      u16 len, struct sock *skip_sk)
+{
+	return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len,
+			       HCI_MGMT_GENERIC_EVENTS, skip_sk);
+}
+
+static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len,
 		      struct sock *skip_sk)
 {
-	struct sk_buff *skb;
-	struct mgmt_hdr *hdr;
-
-	skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL);
-	if (!skb)
-		return -ENOMEM;
-
-	hdr = (void *) skb_put(skb, sizeof(*hdr));
-	hdr->opcode = cpu_to_le16(event);
-	if (hdev)
-		hdr->index = cpu_to_le16(hdev->id);
-	else
-		hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
-	hdr->len = cpu_to_le16(data_len);
-
-	if (data)
-		memcpy(skb_put(skb, data_len), data, data_len);
-
-	/* Time stamp */
-	__net_timestamp(skb);
-
-	hci_send_to_control(skb, skip_sk);
-	kfree_skb(skb);
-
-	return 0;
-}
-
-static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
-{
-	struct sk_buff *skb;
-	struct mgmt_hdr *hdr;
-	struct mgmt_ev_cmd_status *ev;
-	int err;
-
-	BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
-
-	skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL);
-	if (!skb)
-		return -ENOMEM;
-
-	hdr = (void *) skb_put(skb, sizeof(*hdr));
-
-	hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
-	hdr->index = cpu_to_le16(index);
-	hdr->len = cpu_to_le16(sizeof(*ev));
-
-	ev = (void *) skb_put(skb, sizeof(*ev));
-	ev->status = status;
-	ev->opcode = cpu_to_le16(cmd);
-
-	err = sock_queue_rcv_skb(sk, skb);
-	if (err < 0)
-		kfree_skb(skb);
-
-	return err;
-}
-
-static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
-			void *rp, size_t rp_len)
-{
-	struct sk_buff *skb;
-	struct mgmt_hdr *hdr;
-	struct mgmt_ev_cmd_complete *ev;
-	int err;
-
-	BT_DBG("sock %p", sk);
-
-	skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
-	if (!skb)
-		return -ENOMEM;
-
-	hdr = (void *) skb_put(skb, sizeof(*hdr));
-
-	hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
-	hdr->index = cpu_to_le16(index);
-	hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
-
-	ev = (void *) skb_put(skb, sizeof(*ev) + rp_len);
-	ev->opcode = cpu_to_le16(cmd);
-	ev->status = status;
-
-	if (rp)
-		memcpy(ev->data, rp, rp_len);
-
-	err = sock_queue_rcv_skb(sk, skb);
-	if (err < 0)
-		kfree_skb(skb);
-
-	return err;
+	return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len,
+			       HCI_SOCK_TRUSTED, skip_sk);
 }
 
 static int read_version(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -318,22 +278,28 @@
 	rp.version = MGMT_VERSION;
 	rp.revision = cpu_to_le16(MGMT_REVISION);
 
-	return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, &rp,
-			    sizeof(rp));
+	return mgmt_cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0,
+				 &rp, sizeof(rp));
 }
 
 static int read_commands(struct sock *sk, struct hci_dev *hdev, void *data,
 			 u16 data_len)
 {
 	struct mgmt_rp_read_commands *rp;
-	const u16 num_commands = ARRAY_SIZE(mgmt_commands);
-	const u16 num_events = ARRAY_SIZE(mgmt_events);
-	__le16 *opcode;
+	u16 num_commands, num_events;
 	size_t rp_size;
 	int i, err;
 
 	BT_DBG("sock %p", sk);
 
+	if (hci_sock_test_flag(sk, HCI_SOCK_TRUSTED)) {
+		num_commands = ARRAY_SIZE(mgmt_commands);
+		num_events = ARRAY_SIZE(mgmt_events);
+	} else {
+		num_commands = ARRAY_SIZE(mgmt_untrusted_commands);
+		num_events = ARRAY_SIZE(mgmt_untrusted_events);
+	}
+
 	rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16));
 
 	rp = kmalloc(rp_size, GFP_KERNEL);
@@ -343,14 +309,26 @@
 	rp->num_commands = cpu_to_le16(num_commands);
 	rp->num_events = cpu_to_le16(num_events);
 
-	for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++)
-		put_unaligned_le16(mgmt_commands[i], opcode);
+	if (hci_sock_test_flag(sk, HCI_SOCK_TRUSTED)) {
+		__le16 *opcode = rp->opcodes;
 
-	for (i = 0; i < num_events; i++, opcode++)
-		put_unaligned_le16(mgmt_events[i], opcode);
+		for (i = 0; i < num_commands; i++, opcode++)
+			put_unaligned_le16(mgmt_commands[i], opcode);
 
-	err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, rp,
-			   rp_size);
+		for (i = 0; i < num_events; i++, opcode++)
+			put_unaligned_le16(mgmt_events[i], opcode);
+	} else {
+		__le16 *opcode = rp->opcodes;
+
+		for (i = 0; i < num_commands; i++, opcode++)
+			put_unaligned_le16(mgmt_untrusted_commands[i], opcode);
+
+		for (i = 0; i < num_events; i++, opcode++)
+			put_unaligned_le16(mgmt_untrusted_events[i], opcode);
+	}
+
+	err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0,
+				rp, rp_size);
 	kfree(rp);
 
 	return err;
@@ -372,7 +350,7 @@
 	count = 0;
 	list_for_each_entry(d, &hci_dev_list, list) {
 		if (d->dev_type == HCI_BREDR &&
-		    !test_bit(HCI_UNCONFIGURED, &d->dev_flags))
+		    !hci_dev_test_flag(d, HCI_UNCONFIGURED))
 			count++;
 	}
 
@@ -385,9 +363,9 @@
 
 	count = 0;
 	list_for_each_entry(d, &hci_dev_list, list) {
-		if (test_bit(HCI_SETUP, &d->dev_flags) ||
-		    test_bit(HCI_CONFIG, &d->dev_flags) ||
-		    test_bit(HCI_USER_CHANNEL, &d->dev_flags))
+		if (hci_dev_test_flag(d, HCI_SETUP) ||
+		    hci_dev_test_flag(d, HCI_CONFIG) ||
+		    hci_dev_test_flag(d, HCI_USER_CHANNEL))
 			continue;
 
 		/* Devices marked as raw-only are neither configured
@@ -397,7 +375,7 @@
 			continue;
 
 		if (d->dev_type == HCI_BREDR &&
-		    !test_bit(HCI_UNCONFIGURED, &d->dev_flags)) {
+		    !hci_dev_test_flag(d, HCI_UNCONFIGURED)) {
 			rp->index[count++] = cpu_to_le16(d->id);
 			BT_DBG("Added hci%u", d->id);
 		}
@@ -408,8 +386,8 @@
 
 	read_unlock(&hci_dev_list_lock);
 
-	err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp,
-			   rp_len);
+	err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST,
+				0, rp, rp_len);
 
 	kfree(rp);
 
@@ -432,7 +410,7 @@
 	count = 0;
 	list_for_each_entry(d, &hci_dev_list, list) {
 		if (d->dev_type == HCI_BREDR &&
-		    test_bit(HCI_UNCONFIGURED, &d->dev_flags))
+		    hci_dev_test_flag(d, HCI_UNCONFIGURED))
 			count++;
 	}
 
@@ -445,9 +423,9 @@
 
 	count = 0;
 	list_for_each_entry(d, &hci_dev_list, list) {
-		if (test_bit(HCI_SETUP, &d->dev_flags) ||
-		    test_bit(HCI_CONFIG, &d->dev_flags) ||
-		    test_bit(HCI_USER_CHANNEL, &d->dev_flags))
+		if (hci_dev_test_flag(d, HCI_SETUP) ||
+		    hci_dev_test_flag(d, HCI_CONFIG) ||
+		    hci_dev_test_flag(d, HCI_USER_CHANNEL))
 			continue;
 
 		/* Devices marked as raw-only are neither configured
@@ -457,7 +435,7 @@
 			continue;
 
 		if (d->dev_type == HCI_BREDR &&
-		    test_bit(HCI_UNCONFIGURED, &d->dev_flags)) {
+		    hci_dev_test_flag(d, HCI_UNCONFIGURED)) {
 			rp->index[count++] = cpu_to_le16(d->id);
 			BT_DBG("Added hci%u", d->id);
 		}
@@ -468,8 +446,84 @@
 
 	read_unlock(&hci_dev_list_lock);
 
-	err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_UNCONF_INDEX_LIST,
-			   0, rp, rp_len);
+	err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE,
+				MGMT_OP_READ_UNCONF_INDEX_LIST, 0, rp, rp_len);
+
+	kfree(rp);
+
+	return err;
+}
+
+static int read_ext_index_list(struct sock *sk, struct hci_dev *hdev,
+			       void *data, u16 data_len)
+{
+	struct mgmt_rp_read_ext_index_list *rp;
+	struct hci_dev *d;
+	size_t rp_len;
+	u16 count;
+	int err;
+
+	BT_DBG("sock %p", sk);
+
+	read_lock(&hci_dev_list_lock);
+
+	count = 0;
+	list_for_each_entry(d, &hci_dev_list, list) {
+		if (d->dev_type == HCI_BREDR || d->dev_type == HCI_AMP)
+			count++;
+	}
+
+	rp_len = sizeof(*rp) + (sizeof(rp->entry[0]) * count);
+	rp = kmalloc(rp_len, GFP_ATOMIC);
+	if (!rp) {
+		read_unlock(&hci_dev_list_lock);
+		return -ENOMEM;
+	}
+
+	count = 0;
+	list_for_each_entry(d, &hci_dev_list, list) {
+		if (hci_dev_test_flag(d, HCI_SETUP) ||
+		    hci_dev_test_flag(d, HCI_CONFIG) ||
+		    hci_dev_test_flag(d, HCI_USER_CHANNEL))
+			continue;
+
+		/* Devices marked as raw-only are neither configured
+		 * nor unconfigured controllers.
+		 */
+		if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks))
+			continue;
+
+		if (d->dev_type == HCI_BREDR) {
+			if (hci_dev_test_flag(d, HCI_UNCONFIGURED))
+				rp->entry[count].type = 0x01;
+			else
+				rp->entry[count].type = 0x00;
+		} else if (d->dev_type == HCI_AMP) {
+			rp->entry[count].type = 0x02;
+		} else {
+			continue;
+		}
+
+		rp->entry[count].bus = d->bus;
+		rp->entry[count++].index = cpu_to_le16(d->id);
+		BT_DBG("Added hci%u", d->id);
+	}
+
+	rp->num_controllers = cpu_to_le16(count);
+	rp_len = sizeof(*rp) + (sizeof(rp->entry[0]) * count);
+
+	read_unlock(&hci_dev_list_lock);
+
+	/* If this command is called at least once, then all the
+	 * default index and unconfigured index events are disabled
+	 * and from now on only extended index events are used.
+	 */
+	hci_sock_set_flag(sk, HCI_MGMT_EXT_INDEX_EVENTS);
+	hci_sock_clear_flag(sk, HCI_MGMT_INDEX_EVENTS);
+	hci_sock_clear_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS);
+
+	err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE,
+				MGMT_OP_READ_EXT_INDEX_LIST, 0, rp, rp_len);
 
 	kfree(rp);
 
@@ -479,7 +533,7 @@
 static bool is_configured(struct hci_dev *hdev)
 {
 	if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) &&
-	    !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags))
+	    !hci_dev_test_flag(hdev, HCI_EXT_CONFIGURED))
 		return false;
 
 	if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) &&
@@ -494,7 +548,7 @@
 	u32 options = 0;
 
 	if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) &&
-	    !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags))
+	    !hci_dev_test_flag(hdev, HCI_EXT_CONFIGURED))
 		options |= MGMT_OPTION_EXTERNAL_CONFIG;
 
 	if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) &&
@@ -508,16 +562,16 @@
 {
 	__le32 options = get_missing_options(hdev);
 
-	return mgmt_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options,
-			  sizeof(options), skip);
+	return mgmt_generic_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options,
+				  sizeof(options), skip);
 }
 
 static int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
 {
 	__le32 options = get_missing_options(hdev);
 
-	return cmd_complete(sk, hdev->id, opcode, 0, &options,
-			    sizeof(options));
+	return mgmt_cmd_complete(sk, hdev->id, opcode, 0, &options,
+				 sizeof(options));
 }
 
 static int read_config_info(struct sock *sk, struct hci_dev *hdev,
@@ -544,8 +598,8 @@
 
 	hci_dev_unlock(hdev);
 
-	return cmd_complete(sk, hdev->id, MGMT_OP_READ_CONFIG_INFO, 0, &rp,
-			    sizeof(rp));
+	return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_CONFIG_INFO, 0,
+				 &rp, sizeof(rp));
 }
 
 static u32 get_supported_settings(struct hci_dev *hdev)
@@ -569,8 +623,7 @@
 			settings |= MGMT_SETTING_HS;
 		}
 
-		if (lmp_sc_capable(hdev) ||
-		    test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
+		if (lmp_sc_capable(hdev))
 			settings |= MGMT_SETTING_SECURE_CONN;
 	}
 
@@ -579,6 +632,7 @@
 		settings |= MGMT_SETTING_ADVERTISING;
 		settings |= MGMT_SETTING_SECURE_CONN;
 		settings |= MGMT_SETTING_PRIVACY;
+		settings |= MGMT_SETTING_STATIC_ADDRESS;
 	}
 
 	if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) ||
@@ -595,45 +649,64 @@
 	if (hdev_is_powered(hdev))
 		settings |= MGMT_SETTING_POWERED;
 
-	if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_CONNECTABLE))
 		settings |= MGMT_SETTING_CONNECTABLE;
 
-	if (test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE))
 		settings |= MGMT_SETTING_FAST_CONNECTABLE;
 
-	if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
 		settings |= MGMT_SETTING_DISCOVERABLE;
 
-	if (test_bit(HCI_BONDABLE, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_BONDABLE))
 		settings |= MGMT_SETTING_BONDABLE;
 
-	if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
 		settings |= MGMT_SETTING_BREDR;
 
-	if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_LE_ENABLED))
 		settings |= MGMT_SETTING_LE;
 
-	if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_LINK_SECURITY))
 		settings |= MGMT_SETTING_LINK_SECURITY;
 
-	if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED))
 		settings |= MGMT_SETTING_SSP;
 
-	if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_HS_ENABLED))
 		settings |= MGMT_SETTING_HS;
 
-	if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
 		settings |= MGMT_SETTING_ADVERTISING;
 
-	if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_SC_ENABLED))
 		settings |= MGMT_SETTING_SECURE_CONN;
 
-	if (test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_KEEP_DEBUG_KEYS))
 		settings |= MGMT_SETTING_DEBUG_KEYS;
 
-	if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_PRIVACY))
 		settings |= MGMT_SETTING_PRIVACY;
 
+	/* The current setting for static address has two purposes. The
+	 * first is to indicate if the static address will be used and
+	 * the second is to indicate if it is actually set.
+	 *
+	 * This means if the static address is not configured, this flag
+	 * will never be set. If the address is configured, then if the
+	 * address is actually used decides if the flag is set or not.
+	 *
+	 * For single mode LE only controllers and dual-mode controllers
+	 * with BR/EDR disabled, the existence of the static address will
+	 * be evaluated.
+	 */
+	if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
+	    !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) ||
+	    !bacmp(&hdev->bdaddr, BDADDR_ANY)) {
+		if (bacmp(&hdev->static_addr, BDADDR_ANY))
+			settings |= MGMT_SETTING_STATIC_ADDRESS;
+	}
+
 	return settings;
 }
 
@@ -747,35 +820,19 @@
 	return ptr;
 }
 
-static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
+static struct mgmt_pending_cmd *pending_find(u16 opcode, struct hci_dev *hdev)
 {
-	struct pending_cmd *cmd;
-
-	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
-		if (cmd->opcode == opcode)
-			return cmd;
-	}
-
-	return NULL;
+	return mgmt_pending_find(HCI_CHANNEL_CONTROL, opcode, hdev);
 }
 
-static struct pending_cmd *mgmt_pending_find_data(u16 opcode,
+static struct mgmt_pending_cmd *pending_find_data(u16 opcode,
 						  struct hci_dev *hdev,
 						  const void *data)
 {
-	struct pending_cmd *cmd;
-
-	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
-		if (cmd->user_data != data)
-			continue;
-		if (cmd->opcode == opcode)
-			return cmd;
-	}
-
-	return NULL;
+	return mgmt_pending_find_data(HCI_CHANNEL_CONTROL, opcode, hdev, data);
 }
 
-static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
+static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
 {
 	u8 ad_len = 0;
 	size_t name_len;
@@ -801,21 +858,36 @@
 	return ad_len;
 }
 
-static void update_scan_rsp_data(struct hci_request *req)
+static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
+{
+	/* TODO: Set the appropriate entries based on advertising instance flags
+	 * here once flags other than 0 are supported.
+	 */
+	memcpy(ptr, hdev->adv_instance.scan_rsp_data,
+	       hdev->adv_instance.scan_rsp_len);
+
+	return hdev->adv_instance.scan_rsp_len;
+}
+
+static void update_scan_rsp_data_for_instance(struct hci_request *req,
+					      u8 instance)
 {
 	struct hci_dev *hdev = req->hdev;
 	struct hci_cp_le_set_scan_rsp_data cp;
 	u8 len;
 
-	if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
 		return;
 
 	memset(&cp, 0, sizeof(cp));
 
-	len = create_scan_rsp_data(hdev, cp.data);
+	if (instance)
+		len = create_instance_scan_rsp_data(hdev, cp.data);
+	else
+		len = create_default_scan_rsp_data(hdev, cp.data);
 
 	if (hdev->scan_rsp_data_len == len &&
-	    memcmp(cp.data, hdev->scan_rsp_data, len) == 0)
+	    !memcmp(cp.data, hdev->scan_rsp_data, len))
 		return;
 
 	memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
@@ -826,14 +898,33 @@
 	hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp);
 }
 
+static void update_scan_rsp_data(struct hci_request *req)
+{
+	struct hci_dev *hdev = req->hdev;
+	u8 instance;
+
+	/* The "Set Advertising" setting supersedes the "Add Advertising"
+	 * setting. Here we set the scan response data based on which
+	 * setting was set. When neither apply, default to the global settings,
+	 * represented by instance "0".
+	 */
+	if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
+	    !hci_dev_test_flag(hdev, HCI_ADVERTISING))
+		instance = 0x01;
+	else
+		instance = 0x00;
+
+	update_scan_rsp_data_for_instance(req, instance);
+}
+
 static u8 get_adv_discov_flags(struct hci_dev *hdev)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	/* If there's a pending mgmt command the flags will not yet have
 	 * their final values, so check for this first.
 	 */
-	cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
+	cmd = pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
 	if (cmd) {
 		struct mgmt_mode *cp = cmd->param;
 		if (cp->val == 0x01)
@@ -841,39 +932,131 @@
 		else if (cp->val == 0x02)
 			return LE_AD_LIMITED;
 	} else {
-		if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+		if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
 			return LE_AD_LIMITED;
-		else if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+		else if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
 			return LE_AD_GENERAL;
 	}
 
 	return 0;
 }
 
-static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr)
+static u8 get_current_adv_instance(struct hci_dev *hdev)
 {
-	u8 ad_len = 0, flags = 0;
+	/* The "Set Advertising" setting supersedes the "Add Advertising"
+	 * setting. Here we set the advertising data based on which
+	 * setting was set. When neither apply, default to the global settings,
+	 * represented by instance "0".
+	 */
+	if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
+	    !hci_dev_test_flag(hdev, HCI_ADVERTISING))
+		return 0x01;
 
-	flags |= get_adv_discov_flags(hdev);
+	return 0x00;
+}
 
-	if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
-		flags |= LE_AD_NO_BREDR;
+static bool get_connectable(struct hci_dev *hdev)
+{
+	struct mgmt_pending_cmd *cmd;
 
-	if (flags) {
-		BT_DBG("adv flags 0x%02x", flags);
+	/* If there's a pending mgmt command the flag will not yet have
+	 * it's final value, so check for this first.
+	 */
+	cmd = pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
+	if (cmd) {
+		struct mgmt_mode *cp = cmd->param;
 
-		ptr[0] = 2;
-		ptr[1] = EIR_FLAGS;
-		ptr[2] = flags;
-
-		ad_len += 3;
-		ptr += 3;
+		return cp->val;
 	}
 
-	if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
-		ptr[0] = 2;
+	return hci_dev_test_flag(hdev, HCI_CONNECTABLE);
+}
+
+static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
+{
+	u32 flags;
+
+	if (instance > 0x01)
+		return 0;
+
+	if (instance == 0x01)
+		return hdev->adv_instance.flags;
+
+	/* Instance 0 always manages the "Tx Power" and "Flags" fields */
+	flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
+
+	/* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting corresponds
+	 * to the "connectable" instance flag.
+	 */
+	if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
+		flags |= MGMT_ADV_FLAG_CONNECTABLE;
+
+	return flags;
+}
+
+static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance)
+{
+	/* Ignore instance 0 and other unsupported instances */
+	if (instance != 0x01)
+		return 0;
+
+	/* TODO: Take into account the "appearance" and "local-name" flags here.
+	 * These are currently being ignored as they are not supported.
+	 */
+	return hdev->adv_instance.scan_rsp_len;
+}
+
+static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
+{
+	u8 ad_len = 0, flags = 0;
+	u32 instance_flags = get_adv_instance_flags(hdev, instance);
+
+	/* The Add Advertising command allows userspace to set both the general
+	 * and limited discoverable flags.
+	 */
+	if (instance_flags & MGMT_ADV_FLAG_DISCOV)
+		flags |= LE_AD_GENERAL;
+
+	if (instance_flags & MGMT_ADV_FLAG_LIMITED_DISCOV)
+		flags |= LE_AD_LIMITED;
+
+	if (flags || (instance_flags & MGMT_ADV_FLAG_MANAGED_FLAGS)) {
+		/* If a discovery flag wasn't provided, simply use the global
+		 * settings.
+		 */
+		if (!flags)
+			flags |= get_adv_discov_flags(hdev);
+
+		if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
+			flags |= LE_AD_NO_BREDR;
+
+		/* If flags would still be empty, then there is no need to
+		 * include the "Flags" AD field".
+		 */
+		if (flags) {
+			ptr[0] = 0x02;
+			ptr[1] = EIR_FLAGS;
+			ptr[2] = flags;
+
+			ad_len += 3;
+			ptr += 3;
+		}
+	}
+
+	if (instance) {
+		memcpy(ptr, hdev->adv_instance.adv_data,
+		       hdev->adv_instance.adv_data_len);
+
+		ad_len += hdev->adv_instance.adv_data_len;
+		ptr += hdev->adv_instance.adv_data_len;
+	}
+
+	/* Provide Tx Power only if we can provide a valid value for it */
+	if (hdev->adv_tx_power != HCI_TX_POWER_INVALID &&
+	    (instance_flags & MGMT_ADV_FLAG_TX_POWER)) {
+		ptr[0] = 0x02;
 		ptr[1] = EIR_TX_POWER;
-		ptr[2] = (u8) hdev->adv_tx_power;
+		ptr[2] = (u8)hdev->adv_tx_power;
 
 		ad_len += 3;
 		ptr += 3;
@@ -882,19 +1065,20 @@
 	return ad_len;
 }
 
-static void update_adv_data(struct hci_request *req)
+static void update_adv_data_for_instance(struct hci_request *req, u8 instance)
 {
 	struct hci_dev *hdev = req->hdev;
 	struct hci_cp_le_set_adv_data cp;
 	u8 len;
 
-	if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
 		return;
 
 	memset(&cp, 0, sizeof(cp));
 
-	len = create_adv_data(hdev, cp.data);
+	len = create_instance_adv_data(hdev, instance, cp.data);
 
+	/* There's nothing to do if the data hasn't changed */
 	if (hdev->adv_data_len == len &&
 	    memcmp(cp.data, hdev->adv_data, len) == 0)
 		return;
@@ -907,6 +1091,14 @@
 	hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
 }
 
+static void update_adv_data(struct hci_request *req)
+{
+	struct hci_dev *hdev = req->hdev;
+	u8 instance = get_current_adv_instance(hdev);
+
+	update_adv_data_for_instance(req, instance);
+}
+
 int mgmt_update_adv_data(struct hci_dev *hdev)
 {
 	struct hci_request req;
@@ -976,10 +1168,10 @@
 	if (!lmp_ext_inq_capable(hdev))
 		return;
 
-	if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED))
 		return;
 
-	if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
 		return;
 
 	memset(&cp, 0, sizeof(cp));
@@ -1015,17 +1207,17 @@
 	if (!hdev_is_powered(hdev))
 		return;
 
-	if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
 		return;
 
-	if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
 		return;
 
 	cod[0] = hdev->minor_class;
 	cod[1] = hdev->major_class;
 	cod[2] = get_service_classes(hdev);
 
-	if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
 		cod[1] |= 0x20;
 
 	if (memcmp(cod, hdev->dev_class, 3) == 0)
@@ -1034,22 +1226,6 @@
 	hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
 }
 
-static bool get_connectable(struct hci_dev *hdev)
-{
-	struct pending_cmd *cmd;
-
-	/* If there's a pending mgmt command the flag will not yet have
-	 * it's final value, so check for this first.
-	 */
-	cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
-	if (cmd) {
-		struct mgmt_mode *cp = cmd->param;
-		return cp->val;
-	}
-
-	return test_bit(HCI_CONNECTABLE, &hdev->dev_flags);
-}
-
 static void disable_advertising(struct hci_request *req)
 {
 	u8 enable = 0x00;
@@ -1063,11 +1239,13 @@
 	struct hci_cp_le_set_adv_param cp;
 	u8 own_addr_type, enable = 0x01;
 	bool connectable;
+	u8 instance;
+	u32 flags;
 
 	if (hci_conn_num(hdev, LE_LINK) > 0)
 		return;
 
-	if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_LE_ADV))
 		disable_advertising(req);
 
 	/* Clear the HCI_LE_ADV bit temporarily so that the
@@ -1075,9 +1253,16 @@
 	 * 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_LE_ADV, &hdev->dev_flags);
+	hci_dev_clear_flag(hdev, HCI_LE_ADV);
 
-	connectable = get_connectable(hdev);
+	instance = get_current_adv_instance(hdev);
+	flags = get_adv_instance_flags(hdev, instance);
+
+	/* If the "connectable" instance flag was not set, then choose between
+	 * ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
+	 */
+	connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
+		      get_connectable(hdev);
 
 	/* Set require_privacy to true only when non-connectable
 	 * advertising is used. In that case it is fine to use a
@@ -1089,7 +1274,14 @@
 	memset(&cp, 0, sizeof(cp));
 	cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval);
 	cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval);
-	cp.type = connectable ? LE_ADV_IND : LE_ADV_NONCONN_IND;
+
+	if (connectable)
+		cp.type = LE_ADV_IND;
+	else if (get_adv_instance_scan_rsp_len(hdev, instance))
+		cp.type = LE_ADV_SCAN_IND;
+	else
+		cp.type = LE_ADV_NONCONN_IND;
+
 	cp.own_address_type = own_addr_type;
 	cp.channel_map = hdev->le_adv_channel_map;
 
@@ -1104,7 +1296,7 @@
 					    service_cache.work);
 	struct hci_request req;
 
-	if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
+	if (!hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE))
 		return;
 
 	hci_req_init(&req, hdev);
@@ -1127,9 +1319,9 @@
 
 	BT_DBG("");
 
-	set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+	hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
 
-	if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_ADVERTISING))
 		return;
 
 	/* The generation of a new RPA and programming it into the
@@ -1142,7 +1334,7 @@
 
 static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev)
 {
-	if (test_and_set_bit(HCI_MGMT, &hdev->dev_flags))
+	if (hci_dev_test_and_set_flag(hdev, HCI_MGMT))
 		return;
 
 	INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
@@ -1153,7 +1345,7 @@
 	 * for mgmt we require user-space to explicitly enable
 	 * it
 	 */
-	clear_bit(HCI_BONDABLE, &hdev->dev_flags);
+	hci_dev_clear_flag(hdev, HCI_BONDABLE);
 }
 
 static int read_controller_info(struct sock *sk, struct hci_dev *hdev,
@@ -1182,76 +1374,19 @@
 
 	hci_dev_unlock(hdev);
 
-	return cmd_complete(sk, hdev->id, MGMT_OP_READ_INFO, 0, &rp,
-			    sizeof(rp));
-}
-
-static void mgmt_pending_free(struct pending_cmd *cmd)
-{
-	sock_put(cmd->sk);
-	kfree(cmd->param);
-	kfree(cmd);
-}
-
-static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
-					    struct hci_dev *hdev, void *data,
-					    u16 len)
-{
-	struct pending_cmd *cmd;
-
-	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-	if (!cmd)
-		return NULL;
-
-	cmd->opcode = opcode;
-	cmd->index = hdev->id;
-
-	cmd->param = kmemdup(data, len, GFP_KERNEL);
-	if (!cmd->param) {
-		kfree(cmd);
-		return NULL;
-	}
-
-	cmd->param_len = len;
-
-	cmd->sk = sk;
-	sock_hold(sk);
-
-	list_add(&cmd->list, &hdev->mgmt_pending);
-
-	return cmd;
-}
-
-static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
-				 void (*cb)(struct pending_cmd *cmd,
-					    void *data),
-				 void *data)
-{
-	struct pending_cmd *cmd, *tmp;
-
-	list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
-		if (opcode > 0 && cmd->opcode != opcode)
-			continue;
-
-		cb(cmd, data);
-	}
-}
-
-static void mgmt_pending_remove(struct pending_cmd *cmd)
-{
-	list_del(&cmd->list);
-	mgmt_pending_free(cmd);
+	return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_INFO, 0, &rp,
+				 sizeof(rp));
 }
 
 static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
 {
 	__le32 settings = cpu_to_le32(get_current_settings(hdev));
 
-	return cmd_complete(sk, hdev->id, opcode, 0, &settings,
-			    sizeof(settings));
+	return mgmt_cmd_complete(sk, hdev->id, opcode, 0, &settings,
+				 sizeof(settings));
 }
 
-static void clean_up_hci_complete(struct hci_dev *hdev, u8 status)
+static void clean_up_hci_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
 	BT_DBG("%s status 0x%02x", hdev->name, status);
 
@@ -1269,9 +1404,10 @@
 
 	switch (hdev->discovery.state) {
 	case DISCOVERY_FINDING:
-		if (test_bit(HCI_INQUIRY, &hdev->flags)) {
+		if (test_bit(HCI_INQUIRY, &hdev->flags))
 			hci_req_add(req, HCI_OP_INQUIRY_CANCEL, 0, NULL);
-		} else {
+
+		if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
 			cancel_delayed_work(&hdev->le_scan_disable);
 			hci_req_add_le_scan_disable(req);
 		}
@@ -1292,7 +1428,7 @@
 
 	default:
 		/* Passive scanning */
-		if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
+		if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
 			hci_req_add_le_scan_disable(req);
 			return true;
 		}
@@ -1303,6 +1439,49 @@
 	return false;
 }
 
+static void advertising_added(struct sock *sk, struct hci_dev *hdev,
+			      u8 instance)
+{
+	struct mgmt_ev_advertising_added ev;
+
+	ev.instance = instance;
+
+	mgmt_event(MGMT_EV_ADVERTISING_ADDED, hdev, &ev, sizeof(ev), sk);
+}
+
+static void advertising_removed(struct sock *sk, struct hci_dev *hdev,
+				u8 instance)
+{
+	struct mgmt_ev_advertising_removed ev;
+
+	ev.instance = instance;
+
+	mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk);
+}
+
+static void clear_adv_instance(struct hci_dev *hdev)
+{
+	struct hci_request req;
+
+	if (!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
+		return;
+
+	if (hdev->adv_instance.timeout)
+		cancel_delayed_work(&hdev->adv_instance.timeout_exp);
+
+	memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
+	advertising_removed(NULL, hdev, 1);
+	hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
+
+	if (!hdev_is_powered(hdev) ||
+	    hci_dev_test_flag(hdev, HCI_ADVERTISING))
+		return;
+
+	hci_req_init(&req, hdev);
+	disable_advertising(&req);
+	hci_req_run(&req, NULL);
+}
+
 static int clean_up_hci_state(struct hci_dev *hdev)
 {
 	struct hci_request req;
@@ -1318,7 +1497,10 @@
 		hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
 	}
 
-	if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
+	if (hdev->adv_instance.timeout)
+		clear_adv_instance(hdev);
+
+	if (hci_dev_test_flag(hdev, HCI_LE_ADV))
 		disable_advertising(&req);
 
 	discov_stopped = hci_stop_discovery(&req);
@@ -1366,24 +1548,24 @@
 		       u16 len)
 {
 	struct mgmt_mode *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	int err;
 
 	BT_DBG("request for %s", hdev->name);
 
 	if (cp->val != 0x00 && cp->val != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
-	if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
-				 MGMT_STATUS_BUSY);
+	if (pending_find(MGMT_OP_SET_POWERED, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
+				      MGMT_STATUS_BUSY);
 		goto failed;
 	}
 
-	if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
+	if (hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
 		cancel_delayed_work(&hdev->power_off);
 
 		if (cp->val) {
@@ -1430,11 +1612,10 @@
 
 static int new_settings(struct hci_dev *hdev, struct sock *skip)
 {
-	__le32 ev;
+	__le32 ev = cpu_to_le32(get_current_settings(hdev));
 
-	ev = cpu_to_le32(get_current_settings(hdev));
-
-	return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip);
+	return mgmt_generic_event(MGMT_EV_NEW_SETTINGS, hdev, &ev,
+				  sizeof(ev), skip);
 }
 
 int mgmt_new_settings(struct hci_dev *hdev)
@@ -1448,7 +1629,7 @@
 	u8 mgmt_status;
 };
 
-static void settings_rsp(struct pending_cmd *cmd, void *data)
+static void settings_rsp(struct mgmt_pending_cmd *cmd, void *data)
 {
 	struct cmd_lookup *match = data;
 
@@ -1464,15 +1645,15 @@
 	mgmt_pending_free(cmd);
 }
 
-static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
+static void cmd_status_rsp(struct mgmt_pending_cmd *cmd, void *data)
 {
 	u8 *status = data;
 
-	cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
+	mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
 	mgmt_pending_remove(cmd);
 }
 
-static void cmd_complete_rsp(struct pending_cmd *cmd, void *data)
+static void cmd_complete_rsp(struct mgmt_pending_cmd *cmd, void *data)
 {
 	if (cmd->cmd_complete) {
 		u8 *status = data;
@@ -1486,23 +1667,23 @@
 	cmd_status_rsp(cmd, data);
 }
 
-static void generic_cmd_complete(struct pending_cmd *cmd, u8 status)
+static int generic_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status)
 {
-	cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
-		     cmd->param_len);
+	return mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, status,
+				 cmd->param, cmd->param_len);
 }
 
-static void addr_cmd_complete(struct pending_cmd *cmd, u8 status)
+static int addr_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status)
 {
-	cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
-		     sizeof(struct mgmt_addr_info));
+	return mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, status,
+				 cmd->param, sizeof(struct mgmt_addr_info));
 }
 
 static u8 mgmt_bredr_support(struct hci_dev *hdev)
 {
 	if (!lmp_bredr_capable(hdev))
 		return MGMT_STATUS_NOT_SUPPORTED;
-	else if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+	else if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
 		return MGMT_STATUS_REJECTED;
 	else
 		return MGMT_STATUS_SUCCESS;
@@ -1512,15 +1693,16 @@
 {
 	if (!lmp_le_capable(hdev))
 		return MGMT_STATUS_NOT_SUPPORTED;
-	else if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+	else if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
 		return MGMT_STATUS_REJECTED;
 	else
 		return MGMT_STATUS_SUCCESS;
 }
 
-static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
+static void set_discoverable_complete(struct hci_dev *hdev, u8 status,
+				      u16 opcode)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct mgmt_mode *cp;
 	struct hci_request req;
 	bool changed;
@@ -1529,21 +1711,20 @@
 
 	hci_dev_lock(hdev);
 
-	cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
+	cmd = pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
 	if (!cmd)
 		goto unlock;
 
 	if (status) {
 		u8 mgmt_err = mgmt_status(status);
-		cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
-		clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+		mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+		hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
 		goto remove_cmd;
 	}
 
 	cp = cmd->param;
 	if (cp->val) {
-		changed = !test_and_set_bit(HCI_DISCOVERABLE,
-					    &hdev->dev_flags);
+		changed = !hci_dev_test_and_set_flag(hdev, HCI_DISCOVERABLE);
 
 		if (hdev->discov_timeout > 0) {
 			int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
@@ -1551,8 +1732,7 @@
 					   to);
 		}
 	} else {
-		changed = test_and_clear_bit(HCI_DISCOVERABLE,
-					     &hdev->dev_flags);
+		changed = hci_dev_test_and_clear_flag(hdev, HCI_DISCOVERABLE);
 	}
 
 	send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev);
@@ -1566,7 +1746,7 @@
 	 * entries.
 	 */
 	hci_req_init(&req, hdev);
-	hci_update_page_scan(hdev, &req);
+	__hci_update_page_scan(&req);
 	update_class(&req);
 	hci_req_run(&req, NULL);
 
@@ -1581,7 +1761,7 @@
 			    u16 len)
 {
 	struct mgmt_cp_set_discoverable *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	u16 timeout;
 	u8 scan;
@@ -1589,14 +1769,14 @@
 
 	BT_DBG("request for %s", hdev->name);
 
-	if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
-	    !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
-				  MGMT_STATUS_REJECTED);
+	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
+	    !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+				       MGMT_STATUS_REJECTED);
 
 	if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	timeout = __le16_to_cpu(cp->timeout);
 
@@ -1605,27 +1785,27 @@
 	 */
 	if ((cp->val == 0x00 && timeout > 0) ||
 	    (cp->val == 0x02 && timeout == 0))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
 	if (!hdev_is_powered(hdev) && timeout > 0) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
-				 MGMT_STATUS_NOT_POWERED);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+				      MGMT_STATUS_NOT_POWERED);
 		goto failed;
 	}
 
-	if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
-	    mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
-				 MGMT_STATUS_BUSY);
+	if (pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
+	    pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+				      MGMT_STATUS_BUSY);
 		goto failed;
 	}
 
-	if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
-				 MGMT_STATUS_REJECTED);
+	if (!hci_dev_test_flag(hdev, HCI_CONNECTABLE)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+				      MGMT_STATUS_REJECTED);
 		goto failed;
 	}
 
@@ -1636,8 +1816,8 @@
 		 * not a valid operation since it requires a timeout
 		 * and so no need to check HCI_LIMITED_DISCOVERABLE.
 		 */
-		if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
-			change_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+		if (!!cp->val != hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) {
+			hci_dev_change_flag(hdev, HCI_DISCOVERABLE);
 			changed = true;
 		}
 
@@ -1655,9 +1835,9 @@
 	 * value with the new value. And if only the timeout gets updated,
 	 * then no need for any HCI transactions.
 	 */
-	if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags) &&
-	    (cp->val == 0x02) == test_bit(HCI_LIMITED_DISCOVERABLE,
-					  &hdev->dev_flags)) {
+	if (!!cp->val == hci_dev_test_flag(hdev, HCI_DISCOVERABLE) &&
+	    (cp->val == 0x02) == hci_dev_test_flag(hdev,
+						   HCI_LIMITED_DISCOVERABLE)) {
 		cancel_delayed_work(&hdev->discov_off);
 		hdev->discov_timeout = timeout;
 
@@ -1686,16 +1866,16 @@
 
 	/* Limited discoverable mode */
 	if (cp->val == 0x02)
-		set_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_LIMITED_DISCOVERABLE);
 	else
-		clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
 
 	hci_req_init(&req, hdev);
 
 	/* The procedure for LE-only controllers is much simpler - just
 	 * update the advertising data.
 	 */
-	if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
 		goto update_ad;
 
 	scan = SCAN_PAGE;
@@ -1725,7 +1905,7 @@
 
 		scan |= SCAN_INQUIRY;
 	} else {
-		clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
 	}
 
 	hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
@@ -1748,7 +1928,7 @@
 	struct hci_cp_write_page_scan_activity acp;
 	u8 type;
 
-	if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
 		return;
 
 	if (hdev->hci_ver < BLUETOOTH_VER_1_2)
@@ -1777,9 +1957,10 @@
 		hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
 }
 
-static void set_connectable_complete(struct hci_dev *hdev, u8 status)
+static void set_connectable_complete(struct hci_dev *hdev, u8 status,
+				     u16 opcode)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct mgmt_mode *cp;
 	bool conn_changed, discov_changed;
 
@@ -1787,33 +1968,33 @@
 
 	hci_dev_lock(hdev);
 
-	cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
+	cmd = pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
 	if (!cmd)
 		goto unlock;
 
 	if (status) {
 		u8 mgmt_err = mgmt_status(status);
-		cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+		mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
 		goto remove_cmd;
 	}
 
 	cp = cmd->param;
 	if (cp->val) {
-		conn_changed = !test_and_set_bit(HCI_CONNECTABLE,
-						 &hdev->dev_flags);
+		conn_changed = !hci_dev_test_and_set_flag(hdev,
+							  HCI_CONNECTABLE);
 		discov_changed = false;
 	} else {
-		conn_changed = test_and_clear_bit(HCI_CONNECTABLE,
-						  &hdev->dev_flags);
-		discov_changed = test_and_clear_bit(HCI_DISCOVERABLE,
-						    &hdev->dev_flags);
+		conn_changed = hci_dev_test_and_clear_flag(hdev,
+							   HCI_CONNECTABLE);
+		discov_changed = hci_dev_test_and_clear_flag(hdev,
+							     HCI_DISCOVERABLE);
 	}
 
 	send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev);
 
 	if (conn_changed || discov_changed) {
 		new_settings(hdev, cmd->sk);
-		hci_update_page_scan(hdev, NULL);
+		hci_update_page_scan(hdev);
 		if (discov_changed)
 			mgmt_update_adv_data(hdev);
 		hci_update_background_scan(hdev);
@@ -1832,14 +2013,14 @@
 	bool changed = false;
 	int err;
 
-	if (!!val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+	if (!!val != hci_dev_test_flag(hdev, HCI_CONNECTABLE))
 		changed = true;
 
 	if (val) {
-		set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_CONNECTABLE);
 	} else {
-		clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
-		clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_CONNECTABLE);
+		hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
 	}
 
 	err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
@@ -1847,7 +2028,7 @@
 		return err;
 
 	if (changed) {
-		hci_update_page_scan(hdev, NULL);
+		hci_update_page_scan(hdev);
 		hci_update_background_scan(hdev);
 		return new_settings(hdev, sk);
 	}
@@ -1859,21 +2040,21 @@
 			   u16 len)
 {
 	struct mgmt_mode *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	u8 scan;
 	int err;
 
 	BT_DBG("request for %s", hdev->name);
 
-	if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
-	    !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
-				  MGMT_STATUS_REJECTED);
+	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
+	    !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
+				       MGMT_STATUS_REJECTED);
 
 	if (cp->val != 0x00 && cp->val != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
@@ -1882,10 +2063,10 @@
 		goto failed;
 	}
 
-	if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
-	    mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
-				 MGMT_STATUS_BUSY);
+	if (pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
+	    pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
+				      MGMT_STATUS_BUSY);
 		goto failed;
 	}
 
@@ -1901,10 +2082,10 @@
 	 * by-product of disabling connectable, we need to update the
 	 * advertising flags.
 	 */
-	if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+	if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
 		if (!cp->val) {
-			clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
-			clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+			hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
+			hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
 		}
 		update_adv_data(&req);
 	} else if (cp->val != test_bit(HCI_PSCAN, &hdev->flags)) {
@@ -1933,17 +2114,9 @@
 	}
 
 no_scan_update:
-	/* If we're going from non-connectable to connectable or
-	 * vice-versa when fast connectable is enabled ensure that fast
-	 * connectable gets disabled. write_fast_connectable won't do
-	 * anything if the page scan parameters are already what they
-	 * should be.
-	 */
-	if (cp->val || test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags))
-		write_fast_connectable(&req, false);
-
 	/* Update the advertising parameters if necessary */
-	if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+	    hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
 		enable_advertising(&req);
 
 	err = hci_req_run(&req, set_connectable_complete);
@@ -1970,15 +2143,15 @@
 	BT_DBG("request for %s", hdev->name);
 
 	if (cp->val != 0x00 && cp->val != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_BONDABLE,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BONDABLE,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
 	if (cp->val)
-		changed = !test_and_set_bit(HCI_BONDABLE, &hdev->dev_flags);
+		changed = !hci_dev_test_and_set_flag(hdev, HCI_BONDABLE);
 	else
-		changed = test_and_clear_bit(HCI_BONDABLE, &hdev->dev_flags);
+		changed = hci_dev_test_and_clear_flag(hdev, HCI_BONDABLE);
 
 	err = send_settings_rsp(sk, MGMT_OP_SET_BONDABLE, hdev);
 	if (err < 0)
@@ -1996,7 +2169,7 @@
 			     u16 len)
 {
 	struct mgmt_mode *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	u8 val, status;
 	int err;
 
@@ -2004,21 +2177,20 @@
 
 	status = mgmt_bredr_support(hdev);
 	if (status)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
-				  status);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
+				       status);
 
 	if (cp->val != 0x00 && cp->val != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
 	if (!hdev_is_powered(hdev)) {
 		bool changed = false;
 
-		if (!!cp->val != test_bit(HCI_LINK_SECURITY,
-					  &hdev->dev_flags)) {
-			change_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
+		if (!!cp->val != hci_dev_test_flag(hdev, HCI_LINK_SECURITY)) {
+			hci_dev_change_flag(hdev, HCI_LINK_SECURITY);
 			changed = true;
 		}
 
@@ -2032,9 +2204,9 @@
 		goto failed;
 	}
 
-	if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
-				 MGMT_STATUS_BUSY);
+	if (pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
+				      MGMT_STATUS_BUSY);
 		goto failed;
 	}
 
@@ -2065,7 +2237,7 @@
 static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 {
 	struct mgmt_mode *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	u8 status;
 	int err;
 
@@ -2073,15 +2245,15 @@
 
 	status = mgmt_bredr_support(hdev);
 	if (status)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, status);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, status);
 
 	if (!lmp_ssp_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	if (cp->val != 0x00 && cp->val != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
@@ -2089,16 +2261,16 @@
 		bool changed;
 
 		if (cp->val) {
-			changed = !test_and_set_bit(HCI_SSP_ENABLED,
-						    &hdev->dev_flags);
+			changed = !hci_dev_test_and_set_flag(hdev,
+							     HCI_SSP_ENABLED);
 		} else {
-			changed = test_and_clear_bit(HCI_SSP_ENABLED,
-						     &hdev->dev_flags);
+			changed = hci_dev_test_and_clear_flag(hdev,
+							      HCI_SSP_ENABLED);
 			if (!changed)
-				changed = test_and_clear_bit(HCI_HS_ENABLED,
-							     &hdev->dev_flags);
+				changed = hci_dev_test_and_clear_flag(hdev,
+								      HCI_HS_ENABLED);
 			else
-				clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+				hci_dev_clear_flag(hdev, HCI_HS_ENABLED);
 		}
 
 		err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
@@ -2111,14 +2283,13 @@
 		goto failed;
 	}
 
-	if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev) ||
-	    mgmt_pending_find(MGMT_OP_SET_HS, hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
-				 MGMT_STATUS_BUSY);
+	if (pending_find(MGMT_OP_SET_SSP, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
+				      MGMT_STATUS_BUSY);
 		goto failed;
 	}
 
-	if (!!cp->val == test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+	if (!!cp->val == hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) {
 		err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
 		goto failed;
 	}
@@ -2129,7 +2300,7 @@
 		goto failed;
 	}
 
-	if (!cp->val && test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags))
+	if (!cp->val && hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS))
 		hci_send_cmd(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE,
 			     sizeof(cp->val), &cp->val);
 
@@ -2155,32 +2326,38 @@
 
 	status = mgmt_bredr_support(hdev);
 	if (status)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, status);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, status);
 
 	if (!lmp_ssp_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
-	if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
-				  MGMT_STATUS_REJECTED);
+	if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED))
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+				       MGMT_STATUS_REJECTED);
 
 	if (cp->val != 0x00 && cp->val != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
+	if (pending_find(MGMT_OP_SET_SSP, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+				      MGMT_STATUS_BUSY);
+		goto unlock;
+	}
+
 	if (cp->val) {
-		changed = !test_and_set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+		changed = !hci_dev_test_and_set_flag(hdev, HCI_HS_ENABLED);
 	} else {
 		if (hdev_is_powered(hdev)) {
-			err = cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
-					 MGMT_STATUS_REJECTED);
+			err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+					      MGMT_STATUS_REJECTED);
 			goto unlock;
 		}
 
-		changed = test_and_clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+		changed = hci_dev_test_and_clear_flag(hdev, HCI_HS_ENABLED);
 	}
 
 	err = send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
@@ -2195,7 +2372,7 @@
 	return err;
 }
 
-static void le_enable_complete(struct hci_dev *hdev, u8 status)
+static void le_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
 	struct cmd_lookup match = { NULL, hdev };
 
@@ -2221,15 +2398,14 @@
 	 * has actually been enabled. During power on, the
 	 * update in powered_update_hci will take care of it.
 	 */
-	if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
 		struct hci_request req;
 
 		hci_req_init(&req, hdev);
 		update_adv_data(&req);
 		update_scan_rsp_data(&req);
+		__hci_update_background_scan(&req);
 		hci_req_run(&req, NULL);
-
-		hci_update_background_scan(hdev);
 	}
 
 unlock:
@@ -2240,7 +2416,7 @@
 {
 	struct mgmt_mode *cp = data;
 	struct hci_cp_write_le_host_supported hci_cp;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	int err;
 	u8 val, enabled;
@@ -2248,17 +2424,29 @@
 	BT_DBG("request for %s", hdev->name);
 
 	if (!lmp_le_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	if (cp->val != 0x00 && cp->val != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
+				       MGMT_STATUS_INVALID_PARAMS);
 
-	/* LE-only devices do not allow toggling LE on/off */
-	if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
-				  MGMT_STATUS_REJECTED);
+	/* Bluetooth single mode LE only controllers or dual-mode
+	 * controllers configured as LE only devices, do not allow
+	 * switching LE off. These have either LE enabled explicitly
+	 * or BR/EDR has been previously switched off.
+	 *
+	 * When trying to enable an already enabled LE, then gracefully
+	 * send a positive response. Trying to disable it however will
+	 * result into rejection.
+	 */
+	if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
+		if (cp->val == 0x01)
+			return send_settings_rsp(sk, MGMT_OP_SET_LE, hdev);
+
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
+				       MGMT_STATUS_REJECTED);
+	}
 
 	hci_dev_lock(hdev);
 
@@ -2268,13 +2456,13 @@
 	if (!hdev_is_powered(hdev) || val == enabled) {
 		bool changed = false;
 
-		if (val != test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
-			change_bit(HCI_LE_ENABLED, &hdev->dev_flags);
+		if (val != hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
+			hci_dev_change_flag(hdev, HCI_LE_ENABLED);
 			changed = true;
 		}
 
-		if (!val && test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
-			clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+		if (!val && hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
+			hci_dev_clear_flag(hdev, HCI_ADVERTISING);
 			changed = true;
 		}
 
@@ -2288,10 +2476,10 @@
 		goto unlock;
 	}
 
-	if (mgmt_pending_find(MGMT_OP_SET_LE, hdev) ||
-	    mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
-				 MGMT_STATUS_BUSY);
+	if (pending_find(MGMT_OP_SET_LE, hdev) ||
+	    pending_find(MGMT_OP_SET_ADVERTISING, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
+				      MGMT_STATUS_BUSY);
 		goto unlock;
 	}
 
@@ -2309,7 +2497,7 @@
 		hci_cp.le = val;
 		hci_cp.simul = 0x00;
 	} else {
-		if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
+		if (hci_dev_test_flag(hdev, HCI_LE_ADV))
 			disable_advertising(&req);
 	}
 
@@ -2333,7 +2521,7 @@
  */
 static bool pending_eir_or_class(struct hci_dev *hdev)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
 		switch (cmd->opcode) {
@@ -2369,16 +2557,16 @@
 
 static void mgmt_class_complete(struct hci_dev *hdev, u16 mgmt_op, u8 status)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	hci_dev_lock(hdev);
 
-	cmd = mgmt_pending_find(mgmt_op, hdev);
+	cmd = pending_find(mgmt_op, hdev);
 	if (!cmd)
 		goto unlock;
 
-	cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status),
-		     hdev->dev_class, 3);
+	mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
+			  mgmt_status(status), hdev->dev_class, 3);
 
 	mgmt_pending_remove(cmd);
 
@@ -2386,7 +2574,7 @@
 	hci_dev_unlock(hdev);
 }
 
-static void add_uuid_complete(struct hci_dev *hdev, u8 status)
+static void add_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
 	BT_DBG("status 0x%02x", status);
 
@@ -2396,7 +2584,7 @@
 static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 {
 	struct mgmt_cp_add_uuid *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	struct bt_uuid *uuid;
 	int err;
@@ -2406,8 +2594,8 @@
 	hci_dev_lock(hdev);
 
 	if (pending_eir_or_class(hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID,
-				 MGMT_STATUS_BUSY);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID,
+				      MGMT_STATUS_BUSY);
 		goto failed;
 	}
 
@@ -2433,8 +2621,8 @@
 		if (err != -ENODATA)
 			goto failed;
 
-		err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0,
-				   hdev->dev_class, 3);
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0,
+					hdev->dev_class, 3);
 		goto failed;
 	}
 
@@ -2456,7 +2644,7 @@
 	if (!hdev_is_powered(hdev))
 		return false;
 
-	if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
+	if (!hci_dev_test_and_set_flag(hdev, HCI_SERVICE_CACHE)) {
 		queue_delayed_work(hdev->workqueue, &hdev->service_cache,
 				   CACHE_TIMEOUT);
 		return true;
@@ -2465,7 +2653,7 @@
 	return false;
 }
 
-static void remove_uuid_complete(struct hci_dev *hdev, u8 status)
+static void remove_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
 	BT_DBG("status 0x%02x", status);
 
@@ -2476,7 +2664,7 @@
 		       u16 len)
 {
 	struct mgmt_cp_remove_uuid *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct bt_uuid *match, *tmp;
 	u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 	struct hci_request req;
@@ -2487,8 +2675,8 @@
 	hci_dev_lock(hdev);
 
 	if (pending_eir_or_class(hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID,
-				 MGMT_STATUS_BUSY);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID,
+				      MGMT_STATUS_BUSY);
 		goto unlock;
 	}
 
@@ -2496,8 +2684,9 @@
 		hci_uuids_clear(hdev);
 
 		if (enable_service_cache(hdev)) {
-			err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID,
-					   0, hdev->dev_class, 3);
+			err = mgmt_cmd_complete(sk, hdev->id,
+						MGMT_OP_REMOVE_UUID,
+						0, hdev->dev_class, 3);
 			goto unlock;
 		}
 
@@ -2516,8 +2705,8 @@
 	}
 
 	if (found == 0) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID,
-				 MGMT_STATUS_INVALID_PARAMS);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID,
+				      MGMT_STATUS_INVALID_PARAMS);
 		goto unlock;
 	}
 
@@ -2532,8 +2721,8 @@
 		if (err != -ENODATA)
 			goto unlock;
 
-		err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0,
-				   hdev->dev_class, 3);
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0,
+					hdev->dev_class, 3);
 		goto unlock;
 	}
 
@@ -2550,7 +2739,7 @@
 	return err;
 }
 
-static void set_class_complete(struct hci_dev *hdev, u8 status)
+static void set_class_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
 	BT_DBG("status 0x%02x", status);
 
@@ -2561,27 +2750,27 @@
 			 u16 len)
 {
 	struct mgmt_cp_set_dev_class *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	int err;
 
 	BT_DBG("request for %s", hdev->name);
 
 	if (!lmp_bredr_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	hci_dev_lock(hdev);
 
 	if (pending_eir_or_class(hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
-				 MGMT_STATUS_BUSY);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+				      MGMT_STATUS_BUSY);
 		goto unlock;
 	}
 
 	if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
-				 MGMT_STATUS_INVALID_PARAMS);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+				      MGMT_STATUS_INVALID_PARAMS);
 		goto unlock;
 	}
 
@@ -2589,14 +2778,14 @@
 	hdev->minor_class = cp->minor;
 
 	if (!hdev_is_powered(hdev)) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0,
-				   hdev->dev_class, 3);
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0,
+					hdev->dev_class, 3);
 		goto unlock;
 	}
 
 	hci_req_init(&req, hdev);
 
-	if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
+	if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) {
 		hci_dev_unlock(hdev);
 		cancel_delayed_work_sync(&hdev->service_cache);
 		hci_dev_lock(hdev);
@@ -2610,8 +2799,8 @@
 		if (err != -ENODATA)
 			goto unlock;
 
-		err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0,
-				   hdev->dev_class, 3);
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0,
+					hdev->dev_class, 3);
 		goto unlock;
 	}
 
@@ -2641,15 +2830,15 @@
 	BT_DBG("request for %s", hdev->name);
 
 	if (!lmp_bredr_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	key_count = __le16_to_cpu(cp->key_count);
 	if (key_count > max_key_count) {
 		BT_ERR("load_link_keys: too big key_count value %u",
 		       key_count);
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+				       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	expected_len = sizeof(*cp) + key_count *
@@ -2657,13 +2846,13 @@
 	if (expected_len != len) {
 		BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
 		       expected_len, len);
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+				       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	if (cp->debug_keys != 0x00 && cp->debug_keys != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys,
 	       key_count);
@@ -2672,8 +2861,9 @@
 		struct mgmt_link_key_info *key = &cp->keys[i];
 
 		if (key->addr.type != BDADDR_BREDR || key->type > 0x08)
-			return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
-					  MGMT_STATUS_INVALID_PARAMS);
+			return mgmt_cmd_status(sk, hdev->id,
+					       MGMT_OP_LOAD_LINK_KEYS,
+					       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	hci_dev_lock(hdev);
@@ -2681,11 +2871,10 @@
 	hci_link_keys_clear(hdev);
 
 	if (cp->debug_keys)
-		changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS,
-					    &hdev->dev_flags);
+		changed = !hci_dev_test_and_set_flag(hdev, HCI_KEEP_DEBUG_KEYS);
 	else
-		changed = test_and_clear_bit(HCI_KEEP_DEBUG_KEYS,
-					     &hdev->dev_flags);
+		changed = hci_dev_test_and_clear_flag(hdev,
+						      HCI_KEEP_DEBUG_KEYS);
 
 	if (changed)
 		new_settings(hdev, NULL);
@@ -2703,7 +2892,7 @@
 				 key->type, key->pin_len, NULL);
 	}
 
-	cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0);
+	mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0);
 
 	hci_dev_unlock(hdev);
 
@@ -2728,7 +2917,7 @@
 	struct mgmt_cp_unpair_device *cp = data;
 	struct mgmt_rp_unpair_device rp;
 	struct hci_cp_disconnect dc;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_conn *conn;
 	int err;
 
@@ -2737,20 +2926,21 @@
 	rp.addr.type = cp->addr.type;
 
 	if (!bdaddr_type_is_valid(cp->addr.type))
-		return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
-				    MGMT_STATUS_INVALID_PARAMS,
-				    &rp, sizeof(rp));
+		return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
+					 MGMT_STATUS_INVALID_PARAMS,
+					 &rp, sizeof(rp));
 
 	if (cp->disconnect != 0x00 && cp->disconnect != 0x01)
-		return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
-				    MGMT_STATUS_INVALID_PARAMS,
-				    &rp, sizeof(rp));
+		return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
+					 MGMT_STATUS_INVALID_PARAMS,
+					 &rp, sizeof(rp));
 
 	hci_dev_lock(hdev);
 
 	if (!hdev_is_powered(hdev)) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
-				   MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
+					MGMT_STATUS_NOT_POWERED, &rp,
+					sizeof(rp));
 		goto unlock;
 	}
 
@@ -2800,8 +2990,9 @@
 	}
 
 	if (err < 0) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
-				   MGMT_STATUS_NOT_PAIRED, &rp, sizeof(rp));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
+					MGMT_STATUS_NOT_PAIRED, &rp,
+					sizeof(rp));
 		goto unlock;
 	}
 
@@ -2809,8 +3000,8 @@
 	 * link is requested.
 	 */
 	if (!conn) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0,
-				   &rp, sizeof(rp));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0,
+					&rp, sizeof(rp));
 		device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
 		goto unlock;
 	}
@@ -2840,7 +3031,7 @@
 {
 	struct mgmt_cp_disconnect *cp = data;
 	struct mgmt_rp_disconnect rp;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_conn *conn;
 	int err;
 
@@ -2851,21 +3042,22 @@
 	rp.addr.type = cp->addr.type;
 
 	if (!bdaddr_type_is_valid(cp->addr.type))
-		return cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
-				    MGMT_STATUS_INVALID_PARAMS,
-				    &rp, sizeof(rp));
+		return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+					 MGMT_STATUS_INVALID_PARAMS,
+					 &rp, sizeof(rp));
 
 	hci_dev_lock(hdev);
 
 	if (!test_bit(HCI_UP, &hdev->flags)) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
-				   MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+					MGMT_STATUS_NOT_POWERED, &rp,
+					sizeof(rp));
 		goto failed;
 	}
 
-	if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
-				   MGMT_STATUS_BUSY, &rp, sizeof(rp));
+	if (pending_find(MGMT_OP_DISCONNECT, hdev)) {
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+					MGMT_STATUS_BUSY, &rp, sizeof(rp));
 		goto failed;
 	}
 
@@ -2876,8 +3068,9 @@
 		conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
 
 	if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
-				   MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+					MGMT_STATUS_NOT_CONNECTED, &rp,
+					sizeof(rp));
 		goto failed;
 	}
 
@@ -2931,8 +3124,8 @@
 	hci_dev_lock(hdev);
 
 	if (!hdev_is_powered(hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_GET_CONNECTIONS,
-				 MGMT_STATUS_NOT_POWERED);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_CONNECTIONS,
+				      MGMT_STATUS_NOT_POWERED);
 		goto unlock;
 	}
 
@@ -2965,8 +3158,8 @@
 	/* Recalculate length in case of filtered SCO connections, etc */
 	rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
 
-	err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, 0, rp,
-			   rp_len);
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, 0, rp,
+				rp_len);
 
 	kfree(rp);
 
@@ -2978,7 +3171,7 @@
 static int send_pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev,
 				   struct mgmt_cp_pin_code_neg_reply *cp)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	int err;
 
 	cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,
@@ -3000,7 +3193,7 @@
 	struct hci_conn *conn;
 	struct mgmt_cp_pin_code_reply *cp = data;
 	struct hci_cp_pin_code_reply reply;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	int err;
 
 	BT_DBG("");
@@ -3008,15 +3201,15 @@
 	hci_dev_lock(hdev);
 
 	if (!hdev_is_powered(hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
-				 MGMT_STATUS_NOT_POWERED);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+				      MGMT_STATUS_NOT_POWERED);
 		goto failed;
 	}
 
 	conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
 	if (!conn) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
-				 MGMT_STATUS_NOT_CONNECTED);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+				      MGMT_STATUS_NOT_CONNECTED);
 		goto failed;
 	}
 
@@ -3029,8 +3222,8 @@
 
 		err = send_pin_code_neg_reply(sk, hdev, &ncp);
 		if (err >= 0)
-			err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
-					 MGMT_STATUS_INVALID_PARAMS);
+			err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+					      MGMT_STATUS_INVALID_PARAMS);
 
 		goto failed;
 	}
@@ -3064,8 +3257,8 @@
 	BT_DBG("");
 
 	if (cp->io_capability > SMP_IO_KEYBOARD_DISPLAY)
-		return cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY,
-				    MGMT_STATUS_INVALID_PARAMS, NULL, 0);
+		return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY,
+					 MGMT_STATUS_INVALID_PARAMS, NULL, 0);
 
 	hci_dev_lock(hdev);
 
@@ -3076,14 +3269,14 @@
 
 	hci_dev_unlock(hdev);
 
-	return cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, 0, NULL,
-			    0);
+	return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, 0,
+				 NULL, 0);
 }
 
-static struct pending_cmd *find_pairing(struct hci_conn *conn)
+static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn)
 {
 	struct hci_dev *hdev = conn->hdev;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
 		if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
@@ -3098,16 +3291,17 @@
 	return NULL;
 }
 
-static void pairing_complete(struct pending_cmd *cmd, u8 status)
+static int pairing_complete(struct mgmt_pending_cmd *cmd, u8 status)
 {
 	struct mgmt_rp_pair_device rp;
 	struct hci_conn *conn = cmd->user_data;
+	int err;
 
 	bacpy(&rp.addr.bdaddr, &conn->dst);
 	rp.addr.type = link_to_bdaddr(conn->type, conn->dst_type);
 
-	cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status,
-		     &rp, sizeof(rp));
+	err = mgmt_cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE,
+				status, &rp, sizeof(rp));
 
 	/* So we don't get further callbacks for this connection */
 	conn->connect_cfm_cb = NULL;
@@ -3122,12 +3316,14 @@
 	clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
 
 	hci_conn_put(conn);
+
+	return err;
 }
 
 void mgmt_smp_complete(struct hci_conn *conn, bool complete)
 {
 	u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	cmd = find_pairing(conn);
 	if (cmd) {
@@ -3138,7 +3334,7 @@
 
 static void pairing_complete_cb(struct hci_conn *conn, u8 status)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	BT_DBG("status %u", status);
 
@@ -3154,7 +3350,7 @@
 
 static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	BT_DBG("status %u", status);
 
@@ -3176,7 +3372,7 @@
 {
 	struct mgmt_cp_pair_device *cp = data;
 	struct mgmt_rp_pair_device rp;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	u8 sec_level, auth_type;
 	struct hci_conn *conn;
 	int err;
@@ -3188,20 +3384,28 @@
 	rp.addr.type = cp->addr.type;
 
 	if (!bdaddr_type_is_valid(cp->addr.type))
-		return cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
-				    MGMT_STATUS_INVALID_PARAMS,
-				    &rp, sizeof(rp));
+		return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+					 MGMT_STATUS_INVALID_PARAMS,
+					 &rp, sizeof(rp));
 
 	if (cp->io_cap > SMP_IO_KEYBOARD_DISPLAY)
-		return cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
-				    MGMT_STATUS_INVALID_PARAMS,
-				    &rp, sizeof(rp));
+		return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+					 MGMT_STATUS_INVALID_PARAMS,
+					 &rp, sizeof(rp));
 
 	hci_dev_lock(hdev);
 
 	if (!hdev_is_powered(hdev)) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
-				   MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+					MGMT_STATUS_NOT_POWERED, &rp,
+					sizeof(rp));
+		goto unlock;
+	}
+
+	if (hci_bdaddr_is_paired(hdev, &cp->addr.bdaddr, cp->addr.type)) {
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+					MGMT_STATUS_ALREADY_PAIRED, &rp,
+					sizeof(rp));
 		goto unlock;
 	}
 
@@ -3242,19 +3446,22 @@
 
 		if (PTR_ERR(conn) == -EBUSY)
 			status = MGMT_STATUS_BUSY;
+		else if (PTR_ERR(conn) == -EOPNOTSUPP)
+			status = MGMT_STATUS_NOT_SUPPORTED;
+		else if (PTR_ERR(conn) == -ECONNREFUSED)
+			status = MGMT_STATUS_REJECTED;
 		else
 			status = MGMT_STATUS_CONNECT_FAILED;
 
-		err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
-				   status, &rp,
-				   sizeof(rp));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+					status, &rp, sizeof(rp));
 		goto unlock;
 	}
 
 	if (conn->connect_cfm_cb) {
 		hci_conn_drop(conn);
-		err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
-				   MGMT_STATUS_BUSY, &rp, sizeof(rp));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+					MGMT_STATUS_BUSY, &rp, sizeof(rp));
 		goto unlock;
 	}
 
@@ -3298,7 +3505,7 @@
 			      u16 len)
 {
 	struct mgmt_addr_info *addr = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_conn *conn;
 	int err;
 
@@ -3307,31 +3514,31 @@
 	hci_dev_lock(hdev);
 
 	if (!hdev_is_powered(hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
-				 MGMT_STATUS_NOT_POWERED);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
+				      MGMT_STATUS_NOT_POWERED);
 		goto unlock;
 	}
 
-	cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev);
+	cmd = pending_find(MGMT_OP_PAIR_DEVICE, hdev);
 	if (!cmd) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
-				 MGMT_STATUS_INVALID_PARAMS);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
+				      MGMT_STATUS_INVALID_PARAMS);
 		goto unlock;
 	}
 
 	conn = cmd->user_data;
 
 	if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
-				 MGMT_STATUS_INVALID_PARAMS);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
+				      MGMT_STATUS_INVALID_PARAMS);
 		goto unlock;
 	}
 
 	cmd->cmd_complete(cmd, MGMT_STATUS_CANCELLED);
 	mgmt_pending_remove(cmd);
 
-	err = cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0,
-			   addr, sizeof(*addr));
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0,
+				addr, sizeof(*addr));
 unlock:
 	hci_dev_unlock(hdev);
 	return err;
@@ -3341,16 +3548,16 @@
 			     struct mgmt_addr_info *addr, u16 mgmt_op,
 			     u16 hci_op, __le32 passkey)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_conn *conn;
 	int err;
 
 	hci_dev_lock(hdev);
 
 	if (!hdev_is_powered(hdev)) {
-		err = cmd_complete(sk, hdev->id, mgmt_op,
-				   MGMT_STATUS_NOT_POWERED, addr,
-				   sizeof(*addr));
+		err = mgmt_cmd_complete(sk, hdev->id, mgmt_op,
+					MGMT_STATUS_NOT_POWERED, addr,
+					sizeof(*addr));
 		goto done;
 	}
 
@@ -3360,22 +3567,22 @@
 		conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &addr->bdaddr);
 
 	if (!conn) {
-		err = cmd_complete(sk, hdev->id, mgmt_op,
-				   MGMT_STATUS_NOT_CONNECTED, addr,
-				   sizeof(*addr));
+		err = mgmt_cmd_complete(sk, hdev->id, mgmt_op,
+					MGMT_STATUS_NOT_CONNECTED, addr,
+					sizeof(*addr));
 		goto done;
 	}
 
 	if (addr->type == BDADDR_LE_PUBLIC || addr->type == BDADDR_LE_RANDOM) {
 		err = smp_user_confirm_reply(conn, mgmt_op, passkey);
 		if (!err)
-			err = cmd_complete(sk, hdev->id, mgmt_op,
-					   MGMT_STATUS_SUCCESS, addr,
-					   sizeof(*addr));
+			err = mgmt_cmd_complete(sk, hdev->id, mgmt_op,
+						MGMT_STATUS_SUCCESS, addr,
+						sizeof(*addr));
 		else
-			err = cmd_complete(sk, hdev->id, mgmt_op,
-					   MGMT_STATUS_FAILED, addr,
-					   sizeof(*addr));
+			err = mgmt_cmd_complete(sk, hdev->id, mgmt_op,
+						MGMT_STATUS_FAILED, addr,
+						sizeof(*addr));
 
 		goto done;
 	}
@@ -3427,8 +3634,8 @@
 	BT_DBG("");
 
 	if (len != sizeof(*cp))
-		return cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	return user_pairing_resp(sk, hdev, &cp->addr,
 				 MGMT_OP_USER_CONFIRM_REPLY,
@@ -3481,27 +3688,27 @@
 	hci_req_add(req, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp);
 }
 
-static void set_name_complete(struct hci_dev *hdev, u8 status)
+static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
 	struct mgmt_cp_set_local_name *cp;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	BT_DBG("status 0x%02x", status);
 
 	hci_dev_lock(hdev);
 
-	cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
+	cmd = pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
 	if (!cmd)
 		goto unlock;
 
 	cp = cmd->param;
 
 	if (status)
-		cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
-			   mgmt_status(status));
+		mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
+			        mgmt_status(status));
 	else
-		cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
-			     cp, sizeof(*cp));
+		mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
+				  cp, sizeof(*cp));
 
 	mgmt_pending_remove(cmd);
 
@@ -3513,7 +3720,7 @@
 			  u16 len)
 {
 	struct mgmt_cp_set_local_name *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	int err;
 
@@ -3527,8 +3734,8 @@
 	if (!memcmp(hdev->dev_name, cp->name, sizeof(hdev->dev_name)) &&
 	    !memcmp(hdev->short_name, cp->short_name,
 		    sizeof(hdev->short_name))) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
-				   data, len);
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
+					data, len);
 		goto failed;
 	}
 
@@ -3537,13 +3744,13 @@
 	if (!hdev_is_powered(hdev)) {
 		memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name));
 
-		err = cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
-				   data, len);
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
+					data, len);
 		if (err < 0)
 			goto failed;
 
-		err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, len,
-				 sk);
+		err = mgmt_generic_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev,
+					 data, len, sk);
 
 		goto failed;
 	}
@@ -3578,10 +3785,70 @@
 	return err;
 }
 
+static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
+				         u16 opcode, struct sk_buff *skb)
+{
+	struct mgmt_rp_read_local_oob_data mgmt_rp;
+	size_t rp_size = sizeof(mgmt_rp);
+	struct mgmt_pending_cmd *cmd;
+
+	BT_DBG("%s status %u", hdev->name, status);
+
+	cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
+	if (!cmd)
+		return;
+
+	if (status || !skb) {
+		mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+				status ? mgmt_status(status) : MGMT_STATUS_FAILED);
+		goto remove;
+	}
+
+	memset(&mgmt_rp, 0, sizeof(mgmt_rp));
+
+	if (opcode == HCI_OP_READ_LOCAL_OOB_DATA) {
+		struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
+
+		if (skb->len < sizeof(*rp)) {
+			mgmt_cmd_status(cmd->sk, hdev->id,
+					MGMT_OP_READ_LOCAL_OOB_DATA,
+					MGMT_STATUS_FAILED);
+			goto remove;
+		}
+
+		memcpy(mgmt_rp.hash192, rp->hash, sizeof(rp->hash));
+		memcpy(mgmt_rp.rand192, rp->rand, sizeof(rp->rand));
+
+		rp_size -= sizeof(mgmt_rp.hash256) + sizeof(mgmt_rp.rand256);
+	} else {
+		struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data;
+
+		if (skb->len < sizeof(*rp)) {
+			mgmt_cmd_status(cmd->sk, hdev->id,
+					MGMT_OP_READ_LOCAL_OOB_DATA,
+					MGMT_STATUS_FAILED);
+			goto remove;
+		}
+
+		memcpy(mgmt_rp.hash192, rp->hash192, sizeof(rp->hash192));
+		memcpy(mgmt_rp.rand192, rp->rand192, sizeof(rp->rand192));
+
+		memcpy(mgmt_rp.hash256, rp->hash256, sizeof(rp->hash256));
+		memcpy(mgmt_rp.rand256, rp->rand256, sizeof(rp->rand256));
+	}
+
+	mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+			  MGMT_STATUS_SUCCESS, &mgmt_rp, rp_size);
+
+remove:
+	mgmt_pending_remove(cmd);
+}
+
 static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
 			       void *data, u16 data_len)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
+	struct hci_request req;
 	int err;
 
 	BT_DBG("%s", hdev->name);
@@ -3589,20 +3856,20 @@
 	hci_dev_lock(hdev);
 
 	if (!hdev_is_powered(hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
-				 MGMT_STATUS_NOT_POWERED);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+				      MGMT_STATUS_NOT_POWERED);
 		goto unlock;
 	}
 
 	if (!lmp_ssp_capable(hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
-				 MGMT_STATUS_NOT_SUPPORTED);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+				      MGMT_STATUS_NOT_SUPPORTED);
 		goto unlock;
 	}
 
-	if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
-				 MGMT_STATUS_BUSY);
+	if (pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+				      MGMT_STATUS_BUSY);
 		goto unlock;
 	}
 
@@ -3612,12 +3879,14 @@
 		goto unlock;
 	}
 
-	if (bredr_sc_enabled(hdev))
-		err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA,
-				   0, NULL);
-	else
-		err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+	hci_req_init(&req, hdev);
 
+	if (bredr_sc_enabled(hdev))
+		hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL);
+	else
+		hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+
+	err = hci_req_run_skb(&req, read_local_oob_data_complete);
 	if (err < 0)
 		mgmt_pending_remove(cmd);
 
@@ -3629,10 +3898,17 @@
 static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
 			       void *data, u16 len)
 {
+	struct mgmt_addr_info *addr = data;
 	int err;
 
 	BT_DBG("%s ", hdev->name);
 
+	if (!bdaddr_type_is_valid(addr->type))
+		return mgmt_cmd_complete(sk, hdev->id,
+					 MGMT_OP_ADD_REMOTE_OOB_DATA,
+					 MGMT_STATUS_INVALID_PARAMS,
+					 addr, sizeof(*addr));
+
 	hci_dev_lock(hdev);
 
 	if (len == MGMT_ADD_REMOTE_OOB_DATA_SIZE) {
@@ -3640,10 +3916,10 @@
 		u8 status;
 
 		if (cp->addr.type != BDADDR_BREDR) {
-			err = cmd_complete(sk, hdev->id,
-					   MGMT_OP_ADD_REMOTE_OOB_DATA,
-					   MGMT_STATUS_INVALID_PARAMS,
-					   &cp->addr, sizeof(cp->addr));
+			err = mgmt_cmd_complete(sk, hdev->id,
+						MGMT_OP_ADD_REMOTE_OOB_DATA,
+						MGMT_STATUS_INVALID_PARAMS,
+						&cp->addr, sizeof(cp->addr));
 			goto unlock;
 		}
 
@@ -3655,43 +3931,70 @@
 		else
 			status = MGMT_STATUS_SUCCESS;
 
-		err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
-				   status, &cp->addr, sizeof(cp->addr));
+		err = mgmt_cmd_complete(sk, hdev->id,
+					MGMT_OP_ADD_REMOTE_OOB_DATA, status,
+					&cp->addr, sizeof(cp->addr));
 	} else if (len == MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE) {
 		struct mgmt_cp_add_remote_oob_ext_data *cp = data;
-		u8 *rand192, *hash192;
+		u8 *rand192, *hash192, *rand256, *hash256;
 		u8 status;
 
-		if (cp->addr.type != BDADDR_BREDR) {
-			err = cmd_complete(sk, hdev->id,
-					   MGMT_OP_ADD_REMOTE_OOB_DATA,
-					   MGMT_STATUS_INVALID_PARAMS,
-					   &cp->addr, sizeof(cp->addr));
-			goto unlock;
-		}
-
 		if (bdaddr_type_is_le(cp->addr.type)) {
+			/* Enforce zero-valued 192-bit parameters as
+			 * long as legacy SMP OOB isn't implemented.
+			 */
+			if (memcmp(cp->rand192, ZERO_KEY, 16) ||
+			    memcmp(cp->hash192, ZERO_KEY, 16)) {
+				err = mgmt_cmd_complete(sk, hdev->id,
+							MGMT_OP_ADD_REMOTE_OOB_DATA,
+							MGMT_STATUS_INVALID_PARAMS,
+							addr, sizeof(*addr));
+				goto unlock;
+			}
+
 			rand192 = NULL;
 			hash192 = NULL;
 		} else {
-			rand192 = cp->rand192;
-			hash192 = cp->hash192;
+			/* In case one of the P-192 values is set to zero,
+			 * then just disable OOB data for P-192.
+			 */
+			if (!memcmp(cp->rand192, ZERO_KEY, 16) ||
+			    !memcmp(cp->hash192, ZERO_KEY, 16)) {
+				rand192 = NULL;
+				hash192 = NULL;
+			} else {
+				rand192 = cp->rand192;
+				hash192 = cp->hash192;
+			}
+		}
+
+		/* In case one of the P-256 values is set to zero, then just
+		 * disable OOB data for P-256.
+		 */
+		if (!memcmp(cp->rand256, ZERO_KEY, 16) ||
+		    !memcmp(cp->hash256, ZERO_KEY, 16)) {
+			rand256 = NULL;
+			hash256 = NULL;
+		} else {
+			rand256 = cp->rand256;
+			hash256 = cp->hash256;
 		}
 
 		err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
 					      cp->addr.type, hash192, rand192,
-					      cp->hash256, cp->rand256);
+					      hash256, rand256);
 		if (err < 0)
 			status = MGMT_STATUS_FAILED;
 		else
 			status = MGMT_STATUS_SUCCESS;
 
-		err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
-				   status, &cp->addr, sizeof(cp->addr));
+		err = mgmt_cmd_complete(sk, hdev->id,
+					MGMT_OP_ADD_REMOTE_OOB_DATA,
+					status, &cp->addr, sizeof(cp->addr));
 	} else {
 		BT_ERR("add_remote_oob_data: invalid length of %u bytes", len);
-		err = cmd_status(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
-				 MGMT_STATUS_INVALID_PARAMS);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
+				      MGMT_STATUS_INVALID_PARAMS);
 	}
 
 unlock:
@@ -3709,9 +4012,10 @@
 	BT_DBG("%s", hdev->name);
 
 	if (cp->addr.type != BDADDR_BREDR)
-		return cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
-				    MGMT_STATUS_INVALID_PARAMS,
-				    &cp->addr, sizeof(cp->addr));
+		return mgmt_cmd_complete(sk, hdev->id,
+					 MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+					 MGMT_STATUS_INVALID_PARAMS,
+					 &cp->addr, sizeof(cp->addr));
 
 	hci_dev_lock(hdev);
 
@@ -3728,100 +4032,136 @@
 		status = MGMT_STATUS_SUCCESS;
 
 done:
-	err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
-			   status, &cp->addr, sizeof(cp->addr));
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+				status, &cp->addr, sizeof(cp->addr));
 
 	hci_dev_unlock(hdev);
 	return err;
 }
 
-static bool trigger_discovery(struct hci_request *req, u8 *status)
+static bool trigger_bredr_inquiry(struct hci_request *req, u8 *status)
+{
+	struct hci_dev *hdev = req->hdev;
+	struct hci_cp_inquiry cp;
+	/* General inquiry access code (GIAC) */
+	u8 lap[3] = { 0x33, 0x8b, 0x9e };
+
+	*status = mgmt_bredr_support(hdev);
+	if (*status)
+		return false;
+
+	if (hci_dev_test_flag(hdev, HCI_INQUIRY)) {
+		*status = MGMT_STATUS_BUSY;
+		return false;
+	}
+
+	hci_inquiry_cache_flush(hdev);
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.lap, lap, sizeof(cp.lap));
+	cp.length = DISCOV_BREDR_INQUIRY_LEN;
+
+	hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
+
+	return true;
+}
+
+static bool trigger_le_scan(struct hci_request *req, u16 interval, u8 *status)
 {
 	struct hci_dev *hdev = req->hdev;
 	struct hci_cp_le_set_scan_param param_cp;
 	struct hci_cp_le_set_scan_enable enable_cp;
-	struct hci_cp_inquiry inq_cp;
-	/* General inquiry access code (GIAC) */
-	u8 lap[3] = { 0x33, 0x8b, 0x9e };
 	u8 own_addr_type;
 	int err;
 
-	switch (hdev->discovery.type) {
-	case DISCOV_TYPE_BREDR:
-		*status = mgmt_bredr_support(hdev);
-		if (*status)
-			return false;
+	*status = mgmt_le_support(hdev);
+	if (*status)
+		return false;
 
-		if (test_bit(HCI_INQUIRY, &hdev->flags)) {
-			*status = MGMT_STATUS_BUSY;
+	if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
+		/* Don't let discovery abort an outgoing connection attempt
+		 * that's using directed advertising.
+		 */
+		if (hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) {
+			*status = MGMT_STATUS_REJECTED;
 			return false;
 		}
 
-		hci_inquiry_cache_flush(hdev);
+		disable_advertising(req);
+	}
 
-		memset(&inq_cp, 0, sizeof(inq_cp));
-		memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap));
-		inq_cp.length = DISCOV_BREDR_INQUIRY_LEN;
-		hci_req_add(req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp);
+	/* If controller is scanning, it means the background scanning is
+	 * running. Thus, we should temporarily stop it in order to set the
+	 * discovery scanning parameters.
+	 */
+	if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
+		hci_req_add_le_scan_disable(req);
+
+	/* All active scans will be done with either a resolvable private
+	 * address (when privacy feature has been enabled) or non-resolvable
+	 * private address.
+	 */
+	err = hci_update_random_address(req, true, &own_addr_type);
+	if (err < 0) {
+		*status = MGMT_STATUS_FAILED;
+		return false;
+	}
+
+	memset(&param_cp, 0, sizeof(param_cp));
+	param_cp.type = LE_SCAN_ACTIVE;
+	param_cp.interval = cpu_to_le16(interval);
+	param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
+	param_cp.own_address_type = own_addr_type;
+
+	hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
+		    &param_cp);
+
+	memset(&enable_cp, 0, sizeof(enable_cp));
+	enable_cp.enable = LE_SCAN_ENABLE;
+	enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+
+	hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
+		    &enable_cp);
+
+	return true;
+}
+
+static bool trigger_discovery(struct hci_request *req, u8 *status)
+{
+	struct hci_dev *hdev = req->hdev;
+
+	switch (hdev->discovery.type) {
+	case DISCOV_TYPE_BREDR:
+		if (!trigger_bredr_inquiry(req, status))
+			return false;
 		break;
 
-	case DISCOV_TYPE_LE:
 	case DISCOV_TYPE_INTERLEAVED:
-		*status = mgmt_le_support(hdev);
-		if (*status)
-			return false;
+		if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
+			     &hdev->quirks)) {
+			/* During simultaneous discovery, we double LE scan
+			 * interval. We must leave some time for the controller
+			 * to do BR/EDR inquiry.
+			 */
+			if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT * 2,
+					     status))
+				return false;
 
-		if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
-		    !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+			if (!trigger_bredr_inquiry(req, status))
+				return false;
+
+			return true;
+		}
+
+		if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
 			*status = MGMT_STATUS_NOT_SUPPORTED;
 			return false;
 		}
+		/* fall through */
 
-		if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) {
-			/* Don't let discovery abort an outgoing
-			 * connection attempt that's using directed
-			 * advertising.
-			 */
-			if (hci_conn_hash_lookup_state(hdev, LE_LINK,
-						       BT_CONNECT)) {
-				*status = MGMT_STATUS_REJECTED;
-				return false;
-			}
-
-			disable_advertising(req);
-		}
-
-		/* If controller is scanning, it means the background scanning
-		 * is running. Thus, we should temporarily stop it in order to
-		 * set the discovery scanning parameters.
-		 */
-		if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
-			hci_req_add_le_scan_disable(req);
-
-		memset(&param_cp, 0, sizeof(param_cp));
-
-		/* All active scans will be done with either a resolvable
-		 * private address (when privacy feature has been enabled)
-		 * or non-resolvable private address.
-		 */
-		err = hci_update_random_address(req, true, &own_addr_type);
-		if (err < 0) {
-			*status = MGMT_STATUS_FAILED;
+	case DISCOV_TYPE_LE:
+		if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT, status))
 			return false;
-		}
-
-		param_cp.type = LE_SCAN_ACTIVE;
-		param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
-		param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
-		param_cp.own_address_type = own_addr_type;
-		hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
-			    &param_cp);
-
-		memset(&enable_cp, 0, sizeof(enable_cp));
-		enable_cp.enable = LE_SCAN_ENABLE;
-		enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
-		hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
-			    &enable_cp);
 		break;
 
 	default:
@@ -3832,18 +4172,19 @@
 	return true;
 }
 
-static void start_discovery_complete(struct hci_dev *hdev, u8 status)
+static void start_discovery_complete(struct hci_dev *hdev, u8 status,
+				     u16 opcode)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	unsigned long timeout;
 
 	BT_DBG("status %d", status);
 
 	hci_dev_lock(hdev);
 
-	cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
+	cmd = pending_find(MGMT_OP_START_DISCOVERY, hdev);
 	if (!cmd)
-		cmd = mgmt_pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev);
+		cmd = pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev);
 
 	if (cmd) {
 		cmd->cmd_complete(cmd, mgmt_status(status));
@@ -3857,12 +4198,26 @@
 
 	hci_discovery_set_state(hdev, DISCOVERY_FINDING);
 
+	/* If the scan involves LE scan, pick proper timeout to schedule
+	 * hdev->le_scan_disable that will stop it.
+	 */
 	switch (hdev->discovery.type) {
 	case DISCOV_TYPE_LE:
 		timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
 		break;
 	case DISCOV_TYPE_INTERLEAVED:
-		timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
+		 /* When running simultaneous discovery, the LE scanning time
+		 * should occupy the whole discovery time sine BR/EDR inquiry
+		 * and LE scanning are scheduled by the controller.
+		 *
+		 * For interleaving discovery in comparison, BR/EDR inquiry
+		 * and LE scanning are done sequentially with separate
+		 * timeouts.
+		 */
+		if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
+			timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
+		else
+			timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
 		break;
 	case DISCOV_TYPE_BREDR:
 		timeout = 0;
@@ -3873,9 +4228,22 @@
 		break;
 	}
 
-	if (timeout)
+	if (timeout) {
+		/* When service discovery is used and the controller has
+		 * a strict duplicate filter, it is important to remember
+		 * the start and duration of the scan. This is required
+		 * for restarting scanning during the discovery phase.
+		 */
+		if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER,
+			     &hdev->quirks) &&
+		    hdev->discovery.result_filtering) {
+			hdev->discovery.scan_start = jiffies;
+			hdev->discovery.scan_duration = timeout;
+		}
+
 		queue_delayed_work(hdev->workqueue,
 				   &hdev->le_scan_disable, timeout);
+	}
 
 unlock:
 	hci_dev_unlock(hdev);
@@ -3885,7 +4253,7 @@
 			   void *data, u16 len)
 {
 	struct mgmt_cp_start_discovery *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	u8 status;
 	int err;
@@ -3895,17 +4263,17 @@
 	hci_dev_lock(hdev);
 
 	if (!hdev_is_powered(hdev)) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
-				   MGMT_STATUS_NOT_POWERED,
-				   &cp->type, sizeof(cp->type));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+					MGMT_STATUS_NOT_POWERED,
+					&cp->type, sizeof(cp->type));
 		goto failed;
 	}
 
 	if (hdev->discovery.state != DISCOVERY_STOPPED ||
-	    test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
-				   MGMT_STATUS_BUSY, &cp->type,
-				   sizeof(cp->type));
+	    hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) {
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+					MGMT_STATUS_BUSY, &cp->type,
+					sizeof(cp->type));
 		goto failed;
 	}
 
@@ -3928,8 +4296,8 @@
 	hci_req_init(&req, hdev);
 
 	if (!trigger_discovery(&req, &status)) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
-				   status, &cp->type, sizeof(cp->type));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+					status, &cp->type, sizeof(cp->type));
 		mgmt_pending_remove(cmd);
 		goto failed;
 	}
@@ -3947,16 +4315,18 @@
 	return err;
 }
 
-static void service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status)
+static int service_discovery_cmd_complete(struct mgmt_pending_cmd *cmd,
+					  u8 status)
 {
-	cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, 1);
+	return mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, status,
+				 cmd->param, 1);
 }
 
 static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
 				   void *data, u16 len)
 {
 	struct mgmt_cp_start_service_discovery *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16);
 	u16 uuid_count, expected_len;
@@ -3968,19 +4338,19 @@
 	hci_dev_lock(hdev);
 
 	if (!hdev_is_powered(hdev)) {
-		err = cmd_complete(sk, hdev->id,
-				   MGMT_OP_START_SERVICE_DISCOVERY,
-				   MGMT_STATUS_NOT_POWERED,
-				   &cp->type, sizeof(cp->type));
+		err = mgmt_cmd_complete(sk, hdev->id,
+					MGMT_OP_START_SERVICE_DISCOVERY,
+					MGMT_STATUS_NOT_POWERED,
+					&cp->type, sizeof(cp->type));
 		goto failed;
 	}
 
 	if (hdev->discovery.state != DISCOVERY_STOPPED ||
-	    test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
-		err = cmd_complete(sk, hdev->id,
-				   MGMT_OP_START_SERVICE_DISCOVERY,
-				   MGMT_STATUS_BUSY, &cp->type,
-				   sizeof(cp->type));
+	    hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) {
+		err = mgmt_cmd_complete(sk, hdev->id,
+					MGMT_OP_START_SERVICE_DISCOVERY,
+					MGMT_STATUS_BUSY, &cp->type,
+					sizeof(cp->type));
 		goto failed;
 	}
 
@@ -3988,10 +4358,10 @@
 	if (uuid_count > max_uuid_count) {
 		BT_ERR("service_discovery: too big uuid_count value %u",
 		       uuid_count);
-		err = cmd_complete(sk, hdev->id,
-				   MGMT_OP_START_SERVICE_DISCOVERY,
-				   MGMT_STATUS_INVALID_PARAMS, &cp->type,
-				   sizeof(cp->type));
+		err = mgmt_cmd_complete(sk, hdev->id,
+					MGMT_OP_START_SERVICE_DISCOVERY,
+					MGMT_STATUS_INVALID_PARAMS, &cp->type,
+					sizeof(cp->type));
 		goto failed;
 	}
 
@@ -3999,10 +4369,10 @@
 	if (expected_len != len) {
 		BT_ERR("service_discovery: expected %u bytes, got %u bytes",
 		       expected_len, len);
-		err = cmd_complete(sk, hdev->id,
-				   MGMT_OP_START_SERVICE_DISCOVERY,
-				   MGMT_STATUS_INVALID_PARAMS, &cp->type,
-				   sizeof(cp->type));
+		err = mgmt_cmd_complete(sk, hdev->id,
+					MGMT_OP_START_SERVICE_DISCOVERY,
+					MGMT_STATUS_INVALID_PARAMS, &cp->type,
+					sizeof(cp->type));
 		goto failed;
 	}
 
@@ -4020,6 +4390,7 @@
 	 */
 	hci_discovery_filter_clear(hdev);
 
+	hdev->discovery.result_filtering = true;
 	hdev->discovery.type = cp->type;
 	hdev->discovery.rssi = cp->rssi;
 	hdev->discovery.uuid_count = uuid_count;
@@ -4028,10 +4399,10 @@
 		hdev->discovery.uuids = kmemdup(cp->uuids, uuid_count * 16,
 						GFP_KERNEL);
 		if (!hdev->discovery.uuids) {
-			err = cmd_complete(sk, hdev->id,
-					   MGMT_OP_START_SERVICE_DISCOVERY,
-					   MGMT_STATUS_FAILED,
-					   &cp->type, sizeof(cp->type));
+			err = mgmt_cmd_complete(sk, hdev->id,
+						MGMT_OP_START_SERVICE_DISCOVERY,
+						MGMT_STATUS_FAILED,
+						&cp->type, sizeof(cp->type));
 			mgmt_pending_remove(cmd);
 			goto failed;
 		}
@@ -4040,9 +4411,9 @@
 	hci_req_init(&req, hdev);
 
 	if (!trigger_discovery(&req, &status)) {
-		err = cmd_complete(sk, hdev->id,
-				   MGMT_OP_START_SERVICE_DISCOVERY,
-				   status, &cp->type, sizeof(cp->type));
+		err = mgmt_cmd_complete(sk, hdev->id,
+					MGMT_OP_START_SERVICE_DISCOVERY,
+					status, &cp->type, sizeof(cp->type));
 		mgmt_pending_remove(cmd);
 		goto failed;
 	}
@@ -4060,15 +4431,15 @@
 	return err;
 }
 
-static void stop_discovery_complete(struct hci_dev *hdev, u8 status)
+static void stop_discovery_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	BT_DBG("status %d", status);
 
 	hci_dev_lock(hdev);
 
-	cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
+	cmd = pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
 	if (cmd) {
 		cmd->cmd_complete(cmd, mgmt_status(status));
 		mgmt_pending_remove(cmd);
@@ -4084,7 +4455,7 @@
 			  u16 len)
 {
 	struct mgmt_cp_stop_discovery *mgmt_cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	int err;
 
@@ -4093,16 +4464,16 @@
 	hci_dev_lock(hdev);
 
 	if (!hci_discovery_active(hdev)) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
-				   MGMT_STATUS_REJECTED, &mgmt_cp->type,
-				   sizeof(mgmt_cp->type));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
+					MGMT_STATUS_REJECTED, &mgmt_cp->type,
+					sizeof(mgmt_cp->type));
 		goto unlock;
 	}
 
 	if (hdev->discovery.type != mgmt_cp->type) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
-				   MGMT_STATUS_INVALID_PARAMS, &mgmt_cp->type,
-				   sizeof(mgmt_cp->type));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
+					MGMT_STATUS_INVALID_PARAMS,
+					&mgmt_cp->type, sizeof(mgmt_cp->type));
 		goto unlock;
 	}
 
@@ -4128,8 +4499,8 @@
 
 	/* If no HCI commands were sent we're done */
 	if (err == -ENODATA) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0,
-				   &mgmt_cp->type, sizeof(mgmt_cp->type));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0,
+					&mgmt_cp->type, sizeof(mgmt_cp->type));
 		hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
 	}
 
@@ -4150,17 +4521,17 @@
 	hci_dev_lock(hdev);
 
 	if (!hci_discovery_active(hdev)) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
-				   MGMT_STATUS_FAILED, &cp->addr,
-				   sizeof(cp->addr));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
+					MGMT_STATUS_FAILED, &cp->addr,
+					sizeof(cp->addr));
 		goto failed;
 	}
 
 	e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr);
 	if (!e) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
-				   MGMT_STATUS_INVALID_PARAMS, &cp->addr,
-				   sizeof(cp->addr));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
+					MGMT_STATUS_INVALID_PARAMS, &cp->addr,
+					sizeof(cp->addr));
 		goto failed;
 	}
 
@@ -4172,8 +4543,8 @@
 		hci_inquiry_cache_update_resolve(hdev, e);
 	}
 
-	err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME, 0, &cp->addr,
-			   sizeof(cp->addr));
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME, 0,
+				&cp->addr, sizeof(cp->addr));
 
 failed:
 	hci_dev_unlock(hdev);
@@ -4190,9 +4561,9 @@
 	BT_DBG("%s", hdev->name);
 
 	if (!bdaddr_type_is_valid(cp->addr.type))
-		return cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE,
-				    MGMT_STATUS_INVALID_PARAMS,
-				    &cp->addr, sizeof(cp->addr));
+		return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE,
+					 MGMT_STATUS_INVALID_PARAMS,
+					 &cp->addr, sizeof(cp->addr));
 
 	hci_dev_lock(hdev);
 
@@ -4208,8 +4579,8 @@
 	status = MGMT_STATUS_SUCCESS;
 
 done:
-	err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status,
-			   &cp->addr, sizeof(cp->addr));
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status,
+				&cp->addr, sizeof(cp->addr));
 
 	hci_dev_unlock(hdev);
 
@@ -4226,9 +4597,9 @@
 	BT_DBG("%s", hdev->name);
 
 	if (!bdaddr_type_is_valid(cp->addr.type))
-		return cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE,
-				    MGMT_STATUS_INVALID_PARAMS,
-				    &cp->addr, sizeof(cp->addr));
+		return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE,
+					 MGMT_STATUS_INVALID_PARAMS,
+					 &cp->addr, sizeof(cp->addr));
 
 	hci_dev_lock(hdev);
 
@@ -4244,8 +4615,8 @@
 	status = MGMT_STATUS_SUCCESS;
 
 done:
-	err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status,
-			   &cp->addr, sizeof(cp->addr));
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status,
+				&cp->addr, sizeof(cp->addr));
 
 	hci_dev_unlock(hdev);
 
@@ -4265,8 +4636,8 @@
 	source = __le16_to_cpu(cp->source);
 
 	if (source > 0x0002)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_DEVICE_ID,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEVICE_ID,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
@@ -4275,7 +4646,8 @@
 	hdev->devid_product = __le16_to_cpu(cp->product);
 	hdev->devid_version = __le16_to_cpu(cp->version);
 
-	err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEVICE_ID, 0, NULL, 0);
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEVICE_ID, 0,
+				NULL, 0);
 
 	hci_req_init(&req, hdev);
 	update_eir(&req);
@@ -4286,9 +4658,17 @@
 	return err;
 }
 
-static void set_advertising_complete(struct hci_dev *hdev, u8 status)
+static void enable_advertising_instance(struct hci_dev *hdev, u8 status,
+					u16 opcode)
+{
+	BT_DBG("status %d", status);
+}
+
+static void set_advertising_complete(struct hci_dev *hdev, u8 status,
+				     u16 opcode)
 {
 	struct cmd_lookup match = { NULL, hdev };
+	struct hci_request req;
 
 	hci_dev_lock(hdev);
 
@@ -4300,10 +4680,10 @@
 		goto unlock;
 	}
 
-	if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
-		set_bit(HCI_ADVERTISING, &hdev->dev_flags);
+	if (hci_dev_test_flag(hdev, HCI_LE_ADV))
+		hci_dev_set_flag(hdev, HCI_ADVERTISING);
 	else
-		clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_ADVERTISING);
 
 	mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp,
 			     &match);
@@ -4313,6 +4693,21 @@
 	if (match.sk)
 		sock_put(match.sk);
 
+	/* If "Set Advertising" was just disabled and instance advertising was
+	 * set up earlier, then enable the advertising instance.
+	 */
+	if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+	    !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
+		goto unlock;
+
+	hci_req_init(&req, hdev);
+
+	update_adv_data(&req);
+	enable_advertising(&req);
+
+	if (hci_req_run(&req, enable_advertising_instance) < 0)
+		BT_ERR("Failed to re-configure advertising");
+
 unlock:
 	hci_dev_unlock(hdev);
 }
@@ -4321,41 +4716,48 @@
 			   u16 len)
 {
 	struct mgmt_mode *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
-	u8 val, enabled, status;
+	u8 val, status;
 	int err;
 
 	BT_DBG("request for %s", hdev->name);
 
 	status = mgmt_le_support(hdev);
 	if (status)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
-				  status);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+				       status);
 
-	if (cp->val != 0x00 && cp->val != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
-				  MGMT_STATUS_INVALID_PARAMS);
+	if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
 	val = !!cp->val;
-	enabled = test_bit(HCI_ADVERTISING, &hdev->dev_flags);
 
 	/* The following conditions are ones which mean that we should
 	 * not do any HCI communication but directly send a mgmt
 	 * response to user space (after toggling the flag if
 	 * necessary).
 	 */
-	if (!hdev_is_powered(hdev) || val == enabled ||
+	if (!hdev_is_powered(hdev) ||
+	    (val == hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
+	     (cp->val == 0x02) == hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE)) ||
 	    hci_conn_num(hdev, LE_LINK) > 0 ||
-	    (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
+	    (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
 	     hdev->le_scan_type == LE_SCAN_ACTIVE)) {
-		bool changed = false;
+		bool changed;
 
-		if (val != test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
-			change_bit(HCI_ADVERTISING, &hdev->dev_flags);
-			changed = true;
+		if (cp->val) {
+			changed = !hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING);
+			if (cp->val == 0x02)
+				hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
+			else
+				hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
+		} else {
+			changed = hci_dev_test_and_clear_flag(hdev, HCI_ADVERTISING);
+			hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
 		}
 
 		err = send_settings_rsp(sk, MGMT_OP_SET_ADVERTISING, hdev);
@@ -4368,10 +4770,10 @@
 		goto unlock;
 	}
 
-	if (mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev) ||
-	    mgmt_pending_find(MGMT_OP_SET_LE, hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
-				 MGMT_STATUS_BUSY);
+	if (pending_find(MGMT_OP_SET_ADVERTISING, hdev) ||
+	    pending_find(MGMT_OP_SET_LE, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+				      MGMT_STATUS_BUSY);
 		goto unlock;
 	}
 
@@ -4383,10 +4785,19 @@
 
 	hci_req_init(&req, hdev);
 
-	if (val)
-		enable_advertising(&req);
+	if (cp->val == 0x02)
+		hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
 	else
+		hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
+
+	if (val) {
+		/* Switch to instance "0" for the Set Advertising setting. */
+		update_adv_data_for_instance(&req, 0);
+		update_scan_rsp_data_for_instance(&req, 0);
+		enable_advertising(&req);
+	} else {
 		disable_advertising(&req);
+	}
 
 	err = hci_req_run(&req, set_advertising_complete);
 	if (err < 0)
@@ -4406,34 +4817,38 @@
 	BT_DBG("%s", hdev->name);
 
 	if (!lmp_le_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	if (hdev_is_powered(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
-				  MGMT_STATUS_REJECTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
+				       MGMT_STATUS_REJECTED);
 
 	if (bacmp(&cp->bdaddr, BDADDR_ANY)) {
 		if (!bacmp(&cp->bdaddr, BDADDR_NONE))
-			return cmd_status(sk, hdev->id,
-					  MGMT_OP_SET_STATIC_ADDRESS,
-					  MGMT_STATUS_INVALID_PARAMS);
+			return mgmt_cmd_status(sk, hdev->id,
+					       MGMT_OP_SET_STATIC_ADDRESS,
+					       MGMT_STATUS_INVALID_PARAMS);
 
 		/* Two most significant bits shall be set */
 		if ((cp->bdaddr.b[5] & 0xc0) != 0xc0)
-			return cmd_status(sk, hdev->id,
-					  MGMT_OP_SET_STATIC_ADDRESS,
-					  MGMT_STATUS_INVALID_PARAMS);
+			return mgmt_cmd_status(sk, hdev->id,
+					       MGMT_OP_SET_STATIC_ADDRESS,
+					       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	hci_dev_lock(hdev);
 
 	bacpy(&hdev->static_addr, &cp->bdaddr);
 
-	err = cmd_complete(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS, 0, NULL, 0);
+	err = send_settings_rsp(sk, MGMT_OP_SET_STATIC_ADDRESS, hdev);
+	if (err < 0)
+		goto unlock;
 
+	err = new_settings(hdev, sk);
+
+unlock:
 	hci_dev_unlock(hdev);
-
 	return err;
 }
 
@@ -4447,36 +4862,37 @@
 	BT_DBG("%s", hdev->name);
 
 	if (!lmp_le_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	interval = __le16_to_cpu(cp->interval);
 
 	if (interval < 0x0004 || interval > 0x4000)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	window = __le16_to_cpu(cp->window);
 
 	if (window < 0x0004 || window > 0x4000)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	if (window > interval)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
 	hdev->le_scan_interval = interval;
 	hdev->le_scan_window = window;
 
-	err = cmd_complete(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 0, NULL, 0);
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 0,
+				NULL, 0);
 
 	/* If background scan is running, restart it so new parameters are
 	 * loaded.
 	 */
-	if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
+	if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
 	    hdev->discovery.state == DISCOVERY_STOPPED) {
 		struct hci_request req;
 
@@ -4493,28 +4909,29 @@
 	return err;
 }
 
-static void fast_connectable_complete(struct hci_dev *hdev, u8 status)
+static void fast_connectable_complete(struct hci_dev *hdev, u8 status,
+				      u16 opcode)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	BT_DBG("status 0x%02x", status);
 
 	hci_dev_lock(hdev);
 
-	cmd = mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev);
+	cmd = pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev);
 	if (!cmd)
 		goto unlock;
 
 	if (status) {
-		cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
-			   mgmt_status(status));
+		mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+			        mgmt_status(status));
 	} else {
 		struct mgmt_mode *cp = cmd->param;
 
 		if (cp->val)
-			set_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags);
+			hci_dev_set_flag(hdev, HCI_FAST_CONNECTABLE);
 		else
-			clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags);
+			hci_dev_clear_flag(hdev, HCI_FAST_CONNECTABLE);
 
 		send_settings_rsp(cmd->sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev);
 		new_settings(hdev, cmd->sk);
@@ -4530,43 +4947,43 @@
 				void *data, u16 len)
 {
 	struct mgmt_mode *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	int err;
 
 	BT_DBG("%s", hdev->name);
 
-	if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) ||
+	if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) ||
 	    hdev->hci_ver < BLUETOOTH_VER_1_2)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	if (cp->val != 0x00 && cp->val != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
-				  MGMT_STATUS_INVALID_PARAMS);
-
-	if (!hdev_is_powered(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
-				  MGMT_STATUS_NOT_POWERED);
-
-	if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
-				  MGMT_STATUS_REJECTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
-	if (mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
-				 MGMT_STATUS_BUSY);
+	if (pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+				      MGMT_STATUS_BUSY);
 		goto unlock;
 	}
 
-	if (!!cp->val == test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) {
+	if (!!cp->val == hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE)) {
 		err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE,
 					hdev);
 		goto unlock;
 	}
 
+	if (!hdev_is_powered(hdev)) {
+		hci_dev_change_flag(hdev, HCI_FAST_CONNECTABLE);
+		err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE,
+					hdev);
+		new_settings(hdev, sk);
+		goto unlock;
+	}
+
 	cmd = mgmt_pending_add(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev,
 			       data, len);
 	if (!cmd) {
@@ -4580,8 +4997,8 @@
 
 	err = hci_req_run(&req, fast_connectable_complete);
 	if (err < 0) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
-				 MGMT_STATUS_FAILED);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+				      MGMT_STATUS_FAILED);
 		mgmt_pending_remove(cmd);
 	}
 
@@ -4591,15 +5008,15 @@
 	return err;
 }
 
-static void set_bredr_complete(struct hci_dev *hdev, u8 status)
+static void set_bredr_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	BT_DBG("status 0x%02x", status);
 
 	hci_dev_lock(hdev);
 
-	cmd = mgmt_pending_find(MGMT_OP_SET_BREDR, hdev);
+	cmd = pending_find(MGMT_OP_SET_BREDR, hdev);
 	if (!cmd)
 		goto unlock;
 
@@ -4609,9 +5026,9 @@
 		/* We need to restore the flag if related HCI commands
 		 * failed.
 		 */
-		clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_BREDR_ENABLED);
 
-		cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+		mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
 	} else {
 		send_settings_rsp(cmd->sk, MGMT_OP_SET_BREDR, hdev);
 		new_settings(hdev, cmd->sk);
@@ -4626,41 +5043,41 @@
 static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 {
 	struct mgmt_mode *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	int err;
 
 	BT_DBG("request for %s", hdev->name);
 
 	if (!lmp_bredr_capable(hdev) || !lmp_le_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
-	if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
-				  MGMT_STATUS_REJECTED);
+	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+				       MGMT_STATUS_REJECTED);
 
 	if (cp->val != 0x00 && cp->val != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
-	if (cp->val == test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+	if (cp->val == hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
 		err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev);
 		goto unlock;
 	}
 
 	if (!hdev_is_powered(hdev)) {
 		if (!cp->val) {
-			clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
-			clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
-			clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
-			clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags);
-			clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+			hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
+			hci_dev_clear_flag(hdev, HCI_SSP_ENABLED);
+			hci_dev_clear_flag(hdev, HCI_LINK_SECURITY);
+			hci_dev_clear_flag(hdev, HCI_FAST_CONNECTABLE);
+			hci_dev_clear_flag(hdev, HCI_HS_ENABLED);
 		}
 
-		change_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+		hci_dev_change_flag(hdev, HCI_BREDR_ENABLED);
 
 		err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev);
 		if (err < 0)
@@ -4672,14 +5089,36 @@
 
 	/* Reject disabling when powered on */
 	if (!cp->val) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
-				 MGMT_STATUS_REJECTED);
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+				      MGMT_STATUS_REJECTED);
 		goto unlock;
+	} else {
+		/* When configuring a dual-mode controller to operate
+		 * with LE only and using a static address, then switching
+		 * BR/EDR back on is not allowed.
+		 *
+		 * Dual-mode controllers shall operate with the public
+		 * address as its identity address for BR/EDR and LE. So
+		 * reject the attempt to create an invalid configuration.
+		 *
+		 * The same restrictions applies when secure connections
+		 * has been enabled. For BR/EDR this is a controller feature
+		 * while for LE it is a host stack feature. This means that
+		 * switching BR/EDR back on when secure connections has been
+		 * enabled is not a supported transaction.
+		 */
+		if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
+		    (bacmp(&hdev->static_addr, BDADDR_ANY) ||
+		     hci_dev_test_flag(hdev, HCI_SC_ENABLED))) {
+			err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+					      MGMT_STATUS_REJECTED);
+			goto unlock;
+		}
 	}
 
-	if (mgmt_pending_find(MGMT_OP_SET_BREDR, hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
-				 MGMT_STATUS_BUSY);
+	if (pending_find(MGMT_OP_SET_BREDR, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+				      MGMT_STATUS_BUSY);
 		goto unlock;
 	}
 
@@ -4692,12 +5131,12 @@
 	/* We need to flip the bit already here so that update_adv_data
 	 * generates the correct flags.
 	 */
-	set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+	hci_dev_set_flag(hdev, HCI_BREDR_ENABLED);
 
 	hci_req_init(&req, hdev);
 
 	write_fast_connectable(&req, false);
-	hci_update_page_scan(hdev, &req);
+	__hci_update_page_scan(&req);
 
 	/* Since only the advertising data flags will change, there
 	 * is no need to update the scan response data.
@@ -4713,44 +5152,94 @@
 	return err;
 }
 
+static void sc_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+{
+	struct mgmt_pending_cmd *cmd;
+	struct mgmt_mode *cp;
+
+	BT_DBG("%s status %u", hdev->name, status);
+
+	hci_dev_lock(hdev);
+
+	cmd = pending_find(MGMT_OP_SET_SECURE_CONN, hdev);
+	if (!cmd)
+		goto unlock;
+
+	if (status) {
+		mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode,
+			        mgmt_status(status));
+		goto remove;
+	}
+
+	cp = cmd->param;
+
+	switch (cp->val) {
+	case 0x00:
+		hci_dev_clear_flag(hdev, HCI_SC_ENABLED);
+		hci_dev_clear_flag(hdev, HCI_SC_ONLY);
+		break;
+	case 0x01:
+		hci_dev_set_flag(hdev, HCI_SC_ENABLED);
+		hci_dev_clear_flag(hdev, HCI_SC_ONLY);
+		break;
+	case 0x02:
+		hci_dev_set_flag(hdev, HCI_SC_ENABLED);
+		hci_dev_set_flag(hdev, HCI_SC_ONLY);
+		break;
+	}
+
+	send_settings_rsp(cmd->sk, MGMT_OP_SET_SECURE_CONN, hdev);
+	new_settings(hdev, cmd->sk);
+
+remove:
+	mgmt_pending_remove(cmd);
+unlock:
+	hci_dev_unlock(hdev);
+}
+
 static int set_secure_conn(struct sock *sk, struct hci_dev *hdev,
 			   void *data, u16 len)
 {
 	struct mgmt_mode *cp = data;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
+	struct hci_request req;
 	u8 val;
 	int err;
 
 	BT_DBG("request for %s", hdev->name);
 
-	if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
-	    !lmp_sc_capable(hdev) && !test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
-				  MGMT_STATUS_NOT_SUPPORTED);
+	if (!lmp_sc_capable(hdev) &&
+	    !hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+				       MGMT_STATUS_NOT_SUPPORTED);
+
+	if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
+	    lmp_sc_capable(hdev) &&
+	    !hci_dev_test_flag(hdev, HCI_SSP_ENABLED))
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+				       MGMT_STATUS_REJECTED);
 
 	if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
 				  MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
-	if (!hdev_is_powered(hdev) ||
-	    (!lmp_sc_capable(hdev) &&
-	     !test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) ||
-	    !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+	if (!hdev_is_powered(hdev) || !lmp_sc_capable(hdev) ||
+	    !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
 		bool changed;
 
 		if (cp->val) {
-			changed = !test_and_set_bit(HCI_SC_ENABLED,
-						    &hdev->dev_flags);
+			changed = !hci_dev_test_and_set_flag(hdev,
+							     HCI_SC_ENABLED);
 			if (cp->val == 0x02)
-				set_bit(HCI_SC_ONLY, &hdev->dev_flags);
+				hci_dev_set_flag(hdev, HCI_SC_ONLY);
 			else
-				clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+				hci_dev_clear_flag(hdev, HCI_SC_ONLY);
 		} else {
-			changed = test_and_clear_bit(HCI_SC_ENABLED,
-						     &hdev->dev_flags);
-			clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+			changed = hci_dev_test_and_clear_flag(hdev,
+							      HCI_SC_ENABLED);
+			hci_dev_clear_flag(hdev, HCI_SC_ONLY);
 		}
 
 		err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev);
@@ -4763,16 +5252,16 @@
 		goto failed;
 	}
 
-	if (mgmt_pending_find(MGMT_OP_SET_SECURE_CONN, hdev)) {
-		err = cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
-				 MGMT_STATUS_BUSY);
+	if (pending_find(MGMT_OP_SET_SECURE_CONN, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+				      MGMT_STATUS_BUSY);
 		goto failed;
 	}
 
 	val = !!cp->val;
 
-	if (val == test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
-	    (cp->val == 0x02) == test_bit(HCI_SC_ONLY, &hdev->dev_flags)) {
+	if (val == hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
+	    (cp->val == 0x02) == hci_dev_test_flag(hdev, HCI_SC_ONLY)) {
 		err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev);
 		goto failed;
 	}
@@ -4783,17 +5272,14 @@
 		goto failed;
 	}
 
-	err = hci_send_cmd(hdev, HCI_OP_WRITE_SC_SUPPORT, 1, &val);
+	hci_req_init(&req, hdev);
+	hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT, 1, &val);
+	err = hci_req_run(&req, sc_enable_complete);
 	if (err < 0) {
 		mgmt_pending_remove(cmd);
 		goto failed;
 	}
 
-	if (cp->val == 0x02)
-		set_bit(HCI_SC_ONLY, &hdev->dev_flags);
-	else
-		clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
-
 failed:
 	hci_dev_unlock(hdev);
 	return err;
@@ -4809,27 +5295,26 @@
 	BT_DBG("request for %s", hdev->name);
 
 	if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_DEBUG_KEYS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEBUG_KEYS,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	hci_dev_lock(hdev);
 
 	if (cp->val)
-		changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS,
-					    &hdev->dev_flags);
+		changed = !hci_dev_test_and_set_flag(hdev, HCI_KEEP_DEBUG_KEYS);
 	else
-		changed = test_and_clear_bit(HCI_KEEP_DEBUG_KEYS,
-					     &hdev->dev_flags);
+		changed = hci_dev_test_and_clear_flag(hdev,
+						      HCI_KEEP_DEBUG_KEYS);
 
 	if (cp->val == 0x02)
-		use_changed = !test_and_set_bit(HCI_USE_DEBUG_KEYS,
-						&hdev->dev_flags);
+		use_changed = !hci_dev_test_and_set_flag(hdev,
+							 HCI_USE_DEBUG_KEYS);
 	else
-		use_changed = test_and_clear_bit(HCI_USE_DEBUG_KEYS,
-						 &hdev->dev_flags);
+		use_changed = hci_dev_test_and_clear_flag(hdev,
+							  HCI_USE_DEBUG_KEYS);
 
 	if (hdev_is_powered(hdev) && use_changed &&
-	    test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+	    hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) {
 		u8 mode = (cp->val == 0x02) ? 0x01 : 0x00;
 		hci_send_cmd(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE,
 			     sizeof(mode), &mode);
@@ -4857,32 +5342,32 @@
 	BT_DBG("request for %s", hdev->name);
 
 	if (!lmp_le_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	if (cp->privacy != 0x00 && cp->privacy != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	if (hdev_is_powered(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
-				  MGMT_STATUS_REJECTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
+				       MGMT_STATUS_REJECTED);
 
 	hci_dev_lock(hdev);
 
 	/* If user space supports this command it is also expected to
 	 * handle IRKs. Therefore, set the HCI_RPA_RESOLVING flag.
 	 */
-	set_bit(HCI_RPA_RESOLVING, &hdev->dev_flags);
+	hci_dev_set_flag(hdev, HCI_RPA_RESOLVING);
 
 	if (cp->privacy) {
-		changed = !test_and_set_bit(HCI_PRIVACY, &hdev->dev_flags);
+		changed = !hci_dev_test_and_set_flag(hdev, HCI_PRIVACY);
 		memcpy(hdev->irk, cp->irk, sizeof(hdev->irk));
-		set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
 	} else {
-		changed = test_and_clear_bit(HCI_PRIVACY, &hdev->dev_flags);
+		changed = hci_dev_test_and_clear_flag(hdev, HCI_PRIVACY);
 		memset(hdev->irk, 0, sizeof(hdev->irk));
-		clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_RPA_EXPIRED);
 	}
 
 	err = send_settings_rsp(sk, MGMT_OP_SET_PRIVACY, hdev);
@@ -4925,22 +5410,22 @@
 	BT_DBG("request for %s", hdev->name);
 
 	if (!lmp_le_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	irk_count = __le16_to_cpu(cp->irk_count);
 	if (irk_count > max_irk_count) {
 		BT_ERR("load_irks: too big irk_count value %u", irk_count);
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
+				       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	expected_len = sizeof(*cp) + irk_count * sizeof(struct mgmt_irk_info);
 	if (expected_len != len) {
 		BT_ERR("load_irks: expected %u bytes, got %u bytes",
 		       expected_len, len);
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
+				       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	BT_DBG("%s irk_count %u", hdev->name, irk_count);
@@ -4949,9 +5434,9 @@
 		struct mgmt_irk_info *key = &cp->irks[i];
 
 		if (!irk_is_valid(key))
-			return cmd_status(sk, hdev->id,
-					  MGMT_OP_LOAD_IRKS,
-					  MGMT_STATUS_INVALID_PARAMS);
+			return mgmt_cmd_status(sk, hdev->id,
+					       MGMT_OP_LOAD_IRKS,
+					       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	hci_dev_lock(hdev);
@@ -4971,9 +5456,9 @@
 			    BDADDR_ANY);
 	}
 
-	set_bit(HCI_RPA_RESOLVING, &hdev->dev_flags);
+	hci_dev_set_flag(hdev, HCI_RPA_RESOLVING);
 
-	err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_IRKS, 0, NULL, 0);
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_IRKS, 0, NULL, 0);
 
 	hci_dev_unlock(hdev);
 
@@ -5011,14 +5496,14 @@
 	BT_DBG("request for %s", hdev->name);
 
 	if (!lmp_le_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	key_count = __le16_to_cpu(cp->key_count);
 	if (key_count > max_key_count) {
 		BT_ERR("load_ltks: too big key_count value %u", key_count);
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
+				       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	expected_len = sizeof(*cp) + key_count *
@@ -5026,8 +5511,8 @@
 	if (expected_len != len) {
 		BT_ERR("load_keys: expected %u bytes, got %u bytes",
 		       expected_len, len);
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
+				       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	BT_DBG("%s key_count %u", hdev->name, key_count);
@@ -5036,9 +5521,9 @@
 		struct mgmt_ltk_info *key = &cp->keys[i];
 
 		if (!ltk_is_valid(key))
-			return cmd_status(sk, hdev->id,
-					  MGMT_OP_LOAD_LONG_TERM_KEYS,
-					  MGMT_STATUS_INVALID_PARAMS);
+			return mgmt_cmd_status(sk, hdev->id,
+					       MGMT_OP_LOAD_LONG_TERM_KEYS,
+					       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	hci_dev_lock(hdev);
@@ -5083,7 +5568,7 @@
 			    key->rand);
 	}
 
-	err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0,
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0,
 			   NULL, 0);
 
 	hci_dev_unlock(hdev);
@@ -5091,10 +5576,11 @@
 	return err;
 }
 
-static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status)
+static int conn_info_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status)
 {
 	struct hci_conn *conn = cmd->user_data;
 	struct mgmt_rp_get_conn_info rp;
+	int err;
 
 	memcpy(&rp.addr, cmd->param, sizeof(rp.addr));
 
@@ -5108,17 +5594,20 @@
 		rp.max_tx_power = HCI_TX_POWER_INVALID;
 	}
 
-	cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
-		     &rp, sizeof(rp));
+	err = mgmt_cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
+				status, &rp, sizeof(rp));
 
 	hci_conn_drop(conn);
 	hci_conn_put(conn);
+
+	return err;
 }
 
-static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status)
+static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status,
+				       u16 opcode)
 {
 	struct hci_cp_read_rssi *cp;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_conn *conn;
 	u16 handle;
 	u8 status;
@@ -5156,7 +5645,7 @@
 		goto unlock;
 	}
 
-	cmd = mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn);
+	cmd = pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn);
 	if (!cmd)
 		goto unlock;
 
@@ -5183,15 +5672,16 @@
 	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));
+		return mgmt_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));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+					MGMT_STATUS_NOT_POWERED, &rp,
+					sizeof(rp));
 		goto unlock;
 	}
 
@@ -5202,14 +5692,15 @@
 		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));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+					MGMT_STATUS_NOT_CONNECTED, &rp,
+					sizeof(rp));
 		goto unlock;
 	}
 
-	if (mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
-				   MGMT_STATUS_BUSY, &rp, sizeof(rp));
+	if (pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) {
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+					MGMT_STATUS_BUSY, &rp, sizeof(rp));
 		goto unlock;
 	}
 
@@ -5229,7 +5720,7 @@
 		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;
+		struct mgmt_pending_cmd *cmd;
 
 		hci_req_init(&req, hdev);
 		req_rssi_cp.handle = cpu_to_le16(conn->handle);
@@ -5277,8 +5768,8 @@
 		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));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+					MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
 	}
 
 unlock:
@@ -5286,11 +5777,12 @@
 	return err;
 }
 
-static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status)
+static int clock_info_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status)
 {
 	struct hci_conn *conn = cmd->user_data;
 	struct mgmt_rp_get_clock_info rp;
 	struct hci_dev *hdev;
+	int err;
 
 	memset(&rp, 0, sizeof(rp));
 	memcpy(&rp.addr, &cmd->param, sizeof(rp.addr));
@@ -5310,18 +5802,21 @@
 	}
 
 complete:
-	cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp, sizeof(rp));
+	err = mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp,
+				sizeof(rp));
 
 	if (conn) {
 		hci_conn_drop(conn);
 		hci_conn_put(conn);
 	}
+
+	return err;
 }
 
-static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
+static void get_clock_info_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
 	struct hci_cp_read_clock *hci_cp;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_conn *conn;
 
 	BT_DBG("%s status %u", hdev->name, status);
@@ -5339,7 +5834,7 @@
 		conn = NULL;
 	}
 
-	cmd = mgmt_pending_find_data(MGMT_OP_GET_CLOCK_INFO, hdev, conn);
+	cmd = pending_find_data(MGMT_OP_GET_CLOCK_INFO, hdev, conn);
 	if (!cmd)
 		goto unlock;
 
@@ -5356,7 +5851,7 @@
 	struct mgmt_cp_get_clock_info *cp = data;
 	struct mgmt_rp_get_clock_info rp;
 	struct hci_cp_read_clock hci_cp;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct hci_request req;
 	struct hci_conn *conn;
 	int err;
@@ -5368,15 +5863,16 @@
 	rp.addr.type = cp->addr.type;
 
 	if (cp->addr.type != BDADDR_BREDR)
-		return cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO,
-				    MGMT_STATUS_INVALID_PARAMS,
-				    &rp, sizeof(rp));
+		return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_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_CLOCK_INFO,
-				   MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO,
+					MGMT_STATUS_NOT_POWERED, &rp,
+					sizeof(rp));
 		goto unlock;
 	}
 
@@ -5384,10 +5880,10 @@
 		conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
 					       &cp->addr.bdaddr);
 		if (!conn || conn->state != BT_CONNECTED) {
-			err = cmd_complete(sk, hdev->id,
-					   MGMT_OP_GET_CLOCK_INFO,
-					   MGMT_STATUS_NOT_CONNECTED,
-					   &rp, sizeof(rp));
+			err = mgmt_cmd_complete(sk, hdev->id,
+						MGMT_OP_GET_CLOCK_INFO,
+						MGMT_STATUS_NOT_CONNECTED,
+						&rp, sizeof(rp));
 			goto unlock;
 		}
 	} else {
@@ -5425,6 +5921,65 @@
 	return err;
 }
 
+static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
+{
+	struct hci_conn *conn;
+
+	conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
+	if (!conn)
+		return false;
+
+	if (conn->dst_type != type)
+		return false;
+
+	if (conn->state != BT_CONNECTED)
+		return false;
+
+	return true;
+}
+
+/* This function requires the caller holds hdev->lock */
+static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr,
+			       u8 addr_type, u8 auto_connect)
+{
+	struct hci_dev *hdev = req->hdev;
+	struct hci_conn_params *params;
+
+	params = hci_conn_params_add(hdev, addr, addr_type);
+	if (!params)
+		return -EIO;
+
+	if (params->auto_connect == auto_connect)
+		return 0;
+
+	list_del_init(&params->action);
+
+	switch (auto_connect) {
+	case HCI_AUTO_CONN_DISABLED:
+	case HCI_AUTO_CONN_LINK_LOSS:
+		__hci_update_background_scan(req);
+		break;
+	case HCI_AUTO_CONN_REPORT:
+		list_add(&params->action, &hdev->pend_le_reports);
+		__hci_update_background_scan(req);
+		break;
+	case HCI_AUTO_CONN_DIRECT:
+	case HCI_AUTO_CONN_ALWAYS:
+		if (!is_connected(hdev, addr, addr_type)) {
+			list_add(&params->action, &hdev->pend_le_conns);
+			__hci_update_background_scan(req);
+		}
+		break;
+	}
+
+	params->auto_connect = auto_connect;
+
+	BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type,
+	       auto_connect);
+
+	return 0;
+}
+
 static void device_added(struct sock *sk, struct hci_dev *hdev,
 			 bdaddr_t *bdaddr, u8 type, u8 action)
 {
@@ -5437,10 +5992,31 @@
 	mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk);
 }
 
+static void add_device_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+{
+	struct mgmt_pending_cmd *cmd;
+
+	BT_DBG("status 0x%02x", status);
+
+	hci_dev_lock(hdev);
+
+	cmd = pending_find(MGMT_OP_ADD_DEVICE, hdev);
+	if (!cmd)
+		goto unlock;
+
+	cmd->cmd_complete(cmd, mgmt_status(status));
+	mgmt_pending_remove(cmd);
+
+unlock:
+	hci_dev_unlock(hdev);
+}
+
 static int add_device(struct sock *sk, struct hci_dev *hdev,
 		      void *data, u16 len)
 {
 	struct mgmt_cp_add_device *cp = data;
+	struct mgmt_pending_cmd *cmd;
+	struct hci_request req;
 	u8 auto_conn, addr_type;
 	int err;
 
@@ -5448,23 +6024,33 @@
 
 	if (!bdaddr_type_is_valid(cp->addr.type) ||
 	    !bacmp(&cp->addr.bdaddr, BDADDR_ANY))
-		return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
-				    MGMT_STATUS_INVALID_PARAMS,
-				    &cp->addr, sizeof(cp->addr));
+		return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+					 MGMT_STATUS_INVALID_PARAMS,
+					 &cp->addr, sizeof(cp->addr));
 
 	if (cp->action != 0x00 && cp->action != 0x01 && cp->action != 0x02)
-		return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
-				    MGMT_STATUS_INVALID_PARAMS,
-				    &cp->addr, sizeof(cp->addr));
+		return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+					 MGMT_STATUS_INVALID_PARAMS,
+					 &cp->addr, sizeof(cp->addr));
+
+	hci_req_init(&req, hdev);
 
 	hci_dev_lock(hdev);
 
+	cmd = mgmt_pending_add(sk, MGMT_OP_ADD_DEVICE, hdev, data, len);
+	if (!cmd) {
+		err = -ENOMEM;
+		goto unlock;
+	}
+
+	cmd->cmd_complete = addr_cmd_complete;
+
 	if (cp->addr.type == BDADDR_BREDR) {
 		/* Only incoming connections action is supported for now */
 		if (cp->action != 0x01) {
-			err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
-					   MGMT_STATUS_INVALID_PARAMS,
-					   &cp->addr, sizeof(cp->addr));
+			err = cmd->cmd_complete(cmd,
+						MGMT_STATUS_INVALID_PARAMS);
+			mgmt_pending_remove(cmd);
 			goto unlock;
 		}
 
@@ -5473,7 +6059,7 @@
 		if (err)
 			goto unlock;
 
-		hci_update_page_scan(hdev, NULL);
+		__hci_update_page_scan(&req);
 
 		goto added;
 	}
@@ -5493,19 +6079,25 @@
 	/* If the connection parameters don't exist for this device,
 	 * they will be created and configured with defaults.
 	 */
-	if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
+	if (hci_conn_params_set(&req, &cp->addr.bdaddr, addr_type,
 				auto_conn) < 0) {
-		err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
-				   MGMT_STATUS_FAILED,
-				   &cp->addr, sizeof(cp->addr));
+		err = cmd->cmd_complete(cmd, MGMT_STATUS_FAILED);
+		mgmt_pending_remove(cmd);
 		goto unlock;
 	}
 
 added:
 	device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action);
 
-	err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
-			   MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr));
+	err = hci_req_run(&req, add_device_complete);
+	if (err < 0) {
+		/* ENODATA means no HCI commands were needed (e.g. if
+		 * the adapter is powered off).
+		 */
+		if (err == -ENODATA)
+			err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
+		mgmt_pending_remove(cmd);
+	}
 
 unlock:
 	hci_dev_unlock(hdev);
@@ -5523,24 +6115,55 @@
 	mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk);
 }
 
+static void remove_device_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+{
+	struct mgmt_pending_cmd *cmd;
+
+	BT_DBG("status 0x%02x", status);
+
+	hci_dev_lock(hdev);
+
+	cmd = pending_find(MGMT_OP_REMOVE_DEVICE, hdev);
+	if (!cmd)
+		goto unlock;
+
+	cmd->cmd_complete(cmd, mgmt_status(status));
+	mgmt_pending_remove(cmd);
+
+unlock:
+	hci_dev_unlock(hdev);
+}
+
 static int remove_device(struct sock *sk, struct hci_dev *hdev,
 			 void *data, u16 len)
 {
 	struct mgmt_cp_remove_device *cp = data;
+	struct mgmt_pending_cmd *cmd;
+	struct hci_request req;
 	int err;
 
 	BT_DBG("%s", hdev->name);
 
+	hci_req_init(&req, hdev);
+
 	hci_dev_lock(hdev);
 
+	cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_DEVICE, hdev, data, len);
+	if (!cmd) {
+		err = -ENOMEM;
+		goto unlock;
+	}
+
+	cmd->cmd_complete = addr_cmd_complete;
+
 	if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
 		struct hci_conn_params *params;
 		u8 addr_type;
 
 		if (!bdaddr_type_is_valid(cp->addr.type)) {
-			err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
-					   MGMT_STATUS_INVALID_PARAMS,
-					   &cp->addr, sizeof(cp->addr));
+			err = cmd->cmd_complete(cmd,
+						MGMT_STATUS_INVALID_PARAMS);
+			mgmt_pending_remove(cmd);
 			goto unlock;
 		}
 
@@ -5549,14 +6172,13 @@
 						  &cp->addr.bdaddr,
 						  cp->addr.type);
 			if (err) {
-				err = cmd_complete(sk, hdev->id,
-						   MGMT_OP_REMOVE_DEVICE,
-						   MGMT_STATUS_INVALID_PARAMS,
-						   &cp->addr, sizeof(cp->addr));
+				err = cmd->cmd_complete(cmd,
+							MGMT_STATUS_INVALID_PARAMS);
+				mgmt_pending_remove(cmd);
 				goto unlock;
 			}
 
-			hci_update_page_scan(hdev, NULL);
+			__hci_update_page_scan(&req);
 
 			device_removed(sk, hdev, &cp->addr.bdaddr,
 				       cp->addr.type);
@@ -5571,23 +6193,23 @@
 		params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
 						addr_type);
 		if (!params) {
-			err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
-					   MGMT_STATUS_INVALID_PARAMS,
-					   &cp->addr, sizeof(cp->addr));
+			err = cmd->cmd_complete(cmd,
+						MGMT_STATUS_INVALID_PARAMS);
+			mgmt_pending_remove(cmd);
 			goto unlock;
 		}
 
 		if (params->auto_connect == HCI_AUTO_CONN_DISABLED) {
-			err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
-					   MGMT_STATUS_INVALID_PARAMS,
-					   &cp->addr, sizeof(cp->addr));
+			err = cmd->cmd_complete(cmd,
+						MGMT_STATUS_INVALID_PARAMS);
+			mgmt_pending_remove(cmd);
 			goto unlock;
 		}
 
 		list_del(&params->action);
 		list_del(&params->list);
 		kfree(params);
-		hci_update_background_scan(hdev);
+		__hci_update_background_scan(&req);
 
 		device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type);
 	} else {
@@ -5595,9 +6217,9 @@
 		struct bdaddr_list *b, *btmp;
 
 		if (cp->addr.type) {
-			err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
-					   MGMT_STATUS_INVALID_PARAMS,
-					   &cp->addr, sizeof(cp->addr));
+			err = cmd->cmd_complete(cmd,
+						MGMT_STATUS_INVALID_PARAMS);
+			mgmt_pending_remove(cmd);
 			goto unlock;
 		}
 
@@ -5607,7 +6229,7 @@
 			kfree(b);
 		}
 
-		hci_update_page_scan(hdev, NULL);
+		__hci_update_page_scan(&req);
 
 		list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
 			if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
@@ -5620,12 +6242,19 @@
 
 		BT_DBG("All LE connection parameters were removed");
 
-		hci_update_background_scan(hdev);
+		__hci_update_background_scan(&req);
 	}
 
 complete:
-	err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
-			   MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr));
+	err = hci_req_run(&req, remove_device_complete);
+	if (err < 0) {
+		/* ENODATA means no HCI commands were needed (e.g. if
+		 * the adapter is powered off).
+		 */
+		if (err == -ENODATA)
+			err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
+		mgmt_pending_remove(cmd);
+	}
 
 unlock:
 	hci_dev_unlock(hdev);
@@ -5642,15 +6271,15 @@
 	int i;
 
 	if (!lmp_le_capable(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	param_count = __le16_to_cpu(cp->param_count);
 	if (param_count > max_param_count) {
 		BT_ERR("load_conn_param: too big param_count value %u",
 		       param_count);
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM,
+				       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	expected_len = sizeof(*cp) + param_count *
@@ -5658,8 +6287,8 @@
 	if (expected_len != len) {
 		BT_ERR("load_conn_param: expected %u bytes, got %u bytes",
 		       expected_len, len);
-		return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM,
+				       MGMT_STATUS_INVALID_PARAMS);
 	}
 
 	BT_DBG("%s param_count %u", hdev->name, param_count);
@@ -5714,7 +6343,8 @@
 
 	hci_dev_unlock(hdev);
 
-	return cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 0, NULL, 0);
+	return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 0,
+				 NULL, 0);
 }
 
 static int set_external_config(struct sock *sk, struct hci_dev *hdev,
@@ -5727,25 +6357,23 @@
 	BT_DBG("%s", hdev->name);
 
 	if (hdev_is_powered(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
-				  MGMT_STATUS_REJECTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
+				       MGMT_STATUS_REJECTED);
 
 	if (cp->config != 0x00 && cp->config != 0x01)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
-				    MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
+				         MGMT_STATUS_INVALID_PARAMS);
 
 	if (!test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	hci_dev_lock(hdev);
 
 	if (cp->config)
-		changed = !test_and_set_bit(HCI_EXT_CONFIGURED,
-					    &hdev->dev_flags);
+		changed = !hci_dev_test_and_set_flag(hdev, HCI_EXT_CONFIGURED);
 	else
-		changed = test_and_clear_bit(HCI_EXT_CONFIGURED,
-					     &hdev->dev_flags);
+		changed = hci_dev_test_and_clear_flag(hdev, HCI_EXT_CONFIGURED);
 
 	err = send_options_rsp(sk, MGMT_OP_SET_EXTERNAL_CONFIG, hdev);
 	if (err < 0)
@@ -5756,12 +6384,12 @@
 
 	err = new_options(hdev, sk);
 
-	if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) == is_configured(hdev)) {
+	if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) == is_configured(hdev)) {
 		mgmt_index_removed(hdev);
 
-		if (test_and_change_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
-			set_bit(HCI_CONFIG, &hdev->dev_flags);
-			set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+		if (hci_dev_test_and_change_flag(hdev, HCI_UNCONFIGURED)) {
+			hci_dev_set_flag(hdev, HCI_CONFIG);
+			hci_dev_set_flag(hdev, HCI_AUTO_OFF);
 
 			queue_work(hdev->req_workqueue, &hdev->power_on);
 		} else {
@@ -5785,16 +6413,16 @@
 	BT_DBG("%s", hdev->name);
 
 	if (hdev_is_powered(hdev))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
-				  MGMT_STATUS_REJECTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+				       MGMT_STATUS_REJECTED);
 
 	if (!bacmp(&cp->bdaddr, BDADDR_ANY))
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
-				  MGMT_STATUS_INVALID_PARAMS);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+				       MGMT_STATUS_INVALID_PARAMS);
 
 	if (!hdev->set_bdaddr)
-		return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
-				  MGMT_STATUS_NOT_SUPPORTED);
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+				       MGMT_STATUS_NOT_SUPPORTED);
 
 	hci_dev_lock(hdev);
 
@@ -5808,16 +6436,16 @@
 	if (!changed)
 		goto unlock;
 
-	if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
 		err = new_options(hdev, sk);
 
 	if (is_configured(hdev)) {
 		mgmt_index_removed(hdev);
 
-		clear_bit(HCI_UNCONFIGURED, &hdev->dev_flags);
+		hci_dev_clear_flag(hdev, HCI_UNCONFIGURED);
 
-		set_bit(HCI_CONFIG, &hdev->dev_flags);
-		set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+		hci_dev_set_flag(hdev, HCI_CONFIG);
+		hci_dev_set_flag(hdev, HCI_AUTO_OFF);
 
 		queue_work(hdev->req_workqueue, &hdev->power_on);
 	}
@@ -5827,218 +6455,858 @@
 	return err;
 }
 
-static const struct mgmt_handler {
-	int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
-		     u16 data_len);
-	bool var_len;
-	size_t data_len;
-} mgmt_handlers[] = {
-	{ NULL }, /* 0x0000 (no command) */
-	{ read_version,           false, MGMT_READ_VERSION_SIZE },
-	{ read_commands,          false, MGMT_READ_COMMANDS_SIZE },
-	{ read_index_list,        false, MGMT_READ_INDEX_LIST_SIZE },
-	{ read_controller_info,   false, MGMT_READ_INFO_SIZE },
-	{ set_powered,            false, MGMT_SETTING_SIZE },
-	{ set_discoverable,       false, MGMT_SET_DISCOVERABLE_SIZE },
-	{ set_connectable,        false, MGMT_SETTING_SIZE },
-	{ set_fast_connectable,   false, MGMT_SETTING_SIZE },
-	{ set_bondable,           false, MGMT_SETTING_SIZE },
-	{ set_link_security,      false, MGMT_SETTING_SIZE },
-	{ set_ssp,                false, MGMT_SETTING_SIZE },
-	{ set_hs,                 false, MGMT_SETTING_SIZE },
-	{ set_le,                 false, MGMT_SETTING_SIZE },
-	{ set_dev_class,          false, MGMT_SET_DEV_CLASS_SIZE },
-	{ set_local_name,         false, MGMT_SET_LOCAL_NAME_SIZE },
-	{ add_uuid,               false, MGMT_ADD_UUID_SIZE },
-	{ remove_uuid,            false, MGMT_REMOVE_UUID_SIZE },
-	{ load_link_keys,         true,  MGMT_LOAD_LINK_KEYS_SIZE },
-	{ load_long_term_keys,    true,  MGMT_LOAD_LONG_TERM_KEYS_SIZE },
-	{ disconnect,             false, MGMT_DISCONNECT_SIZE },
-	{ get_connections,        false, MGMT_GET_CONNECTIONS_SIZE },
-	{ pin_code_reply,         false, MGMT_PIN_CODE_REPLY_SIZE },
-	{ pin_code_neg_reply,     false, MGMT_PIN_CODE_NEG_REPLY_SIZE },
-	{ set_io_capability,      false, MGMT_SET_IO_CAPABILITY_SIZE },
-	{ pair_device,            false, MGMT_PAIR_DEVICE_SIZE },
-	{ cancel_pair_device,     false, MGMT_CANCEL_PAIR_DEVICE_SIZE },
-	{ unpair_device,          false, MGMT_UNPAIR_DEVICE_SIZE },
-	{ user_confirm_reply,     false, MGMT_USER_CONFIRM_REPLY_SIZE },
-	{ user_confirm_neg_reply, false, MGMT_USER_CONFIRM_NEG_REPLY_SIZE },
-	{ user_passkey_reply,     false, MGMT_USER_PASSKEY_REPLY_SIZE },
-	{ user_passkey_neg_reply, false, MGMT_USER_PASSKEY_NEG_REPLY_SIZE },
-	{ read_local_oob_data,    false, MGMT_READ_LOCAL_OOB_DATA_SIZE },
-	{ add_remote_oob_data,    true,  MGMT_ADD_REMOTE_OOB_DATA_SIZE },
-	{ remove_remote_oob_data, false, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE },
-	{ start_discovery,        false, MGMT_START_DISCOVERY_SIZE },
-	{ stop_discovery,         false, MGMT_STOP_DISCOVERY_SIZE },
-	{ confirm_name,           false, MGMT_CONFIRM_NAME_SIZE },
-	{ block_device,           false, MGMT_BLOCK_DEVICE_SIZE },
-	{ unblock_device,         false, MGMT_UNBLOCK_DEVICE_SIZE },
-	{ set_device_id,          false, MGMT_SET_DEVICE_ID_SIZE },
-	{ set_advertising,        false, MGMT_SETTING_SIZE },
-	{ set_bredr,              false, MGMT_SETTING_SIZE },
-	{ set_static_address,     false, MGMT_SET_STATIC_ADDRESS_SIZE },
-	{ set_scan_params,        false, MGMT_SET_SCAN_PARAMS_SIZE },
-	{ set_secure_conn,        false, MGMT_SETTING_SIZE },
-	{ 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 },
-	{ get_clock_info,         false, MGMT_GET_CLOCK_INFO_SIZE },
-	{ add_device,             false, MGMT_ADD_DEVICE_SIZE },
-	{ remove_device,          false, MGMT_REMOVE_DEVICE_SIZE },
-	{ load_conn_param,        true,  MGMT_LOAD_CONN_PARAM_SIZE },
-	{ read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE },
-	{ read_config_info,       false, MGMT_READ_CONFIG_INFO_SIZE },
-	{ set_external_config,    false, MGMT_SET_EXTERNAL_CONFIG_SIZE },
-	{ set_public_address,     false, MGMT_SET_PUBLIC_ADDRESS_SIZE },
-	{ start_service_discovery,true,  MGMT_START_SERVICE_DISCOVERY_SIZE },
-};
-
-int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
+static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
+				  u8 data_len)
 {
-	void *buf;
-	u8 *cp;
-	struct mgmt_hdr *hdr;
-	u16 opcode, index, len;
-	struct hci_dev *hdev = NULL;
-	const struct mgmt_handler *handler;
+	eir[eir_len++] = sizeof(type) + data_len;
+	eir[eir_len++] = type;
+	memcpy(&eir[eir_len], data, data_len);
+	eir_len += data_len;
+
+	return eir_len;
+}
+
+static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status,
+					     u16 opcode, struct sk_buff *skb)
+{
+	const struct mgmt_cp_read_local_oob_ext_data *mgmt_cp;
+	struct mgmt_rp_read_local_oob_ext_data *mgmt_rp;
+	u8 *h192, *r192, *h256, *r256;
+	struct mgmt_pending_cmd *cmd;
+	u16 eir_len;
 	int err;
 
-	BT_DBG("got %zu bytes", msglen);
+	BT_DBG("%s status %u", hdev->name, status);
 
-	if (msglen < sizeof(*hdr))
-		return -EINVAL;
+	cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev);
+	if (!cmd)
+		return;
 
-	buf = kmalloc(msglen, GFP_KERNEL);
-	if (!buf)
+	mgmt_cp = cmd->param;
+
+	if (status) {
+		status = mgmt_status(status);
+		eir_len = 0;
+
+		h192 = NULL;
+		r192 = NULL;
+		h256 = NULL;
+		r256 = NULL;
+	} else if (opcode == HCI_OP_READ_LOCAL_OOB_DATA) {
+		struct hci_rp_read_local_oob_data *rp;
+
+		if (skb->len != sizeof(*rp)) {
+			status = MGMT_STATUS_FAILED;
+			eir_len = 0;
+		} else {
+			status = MGMT_STATUS_SUCCESS;
+			rp = (void *)skb->data;
+
+			eir_len = 5 + 18 + 18;
+			h192 = rp->hash;
+			r192 = rp->rand;
+			h256 = NULL;
+			r256 = NULL;
+		}
+	} else {
+		struct hci_rp_read_local_oob_ext_data *rp;
+
+		if (skb->len != sizeof(*rp)) {
+			status = MGMT_STATUS_FAILED;
+			eir_len = 0;
+		} else {
+			status = MGMT_STATUS_SUCCESS;
+			rp = (void *)skb->data;
+
+			if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) {
+				eir_len = 5 + 18 + 18;
+				h192 = NULL;
+				r192 = NULL;
+			} else {
+				eir_len = 5 + 18 + 18 + 18 + 18;
+				h192 = rp->hash192;
+				r192 = rp->rand192;
+			}
+
+			h256 = rp->hash256;
+			r256 = rp->rand256;
+		}
+	}
+
+	mgmt_rp = kmalloc(sizeof(*mgmt_rp) + eir_len, GFP_KERNEL);
+	if (!mgmt_rp)
+		goto done;
+
+	if (status)
+		goto send_rsp;
+
+	eir_len = eir_append_data(mgmt_rp->eir, 0, EIR_CLASS_OF_DEV,
+				  hdev->dev_class, 3);
+
+	if (h192 && r192) {
+		eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+					  EIR_SSP_HASH_C192, h192, 16);
+		eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+					  EIR_SSP_RAND_R192, r192, 16);
+	}
+
+	if (h256 && r256) {
+		eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+					  EIR_SSP_HASH_C256, h256, 16);
+		eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+					  EIR_SSP_RAND_R256, r256, 16);
+	}
+
+send_rsp:
+	mgmt_rp->type = mgmt_cp->type;
+	mgmt_rp->eir_len = cpu_to_le16(eir_len);
+
+	err = mgmt_cmd_complete(cmd->sk, hdev->id,
+				MGMT_OP_READ_LOCAL_OOB_EXT_DATA, status,
+				mgmt_rp, sizeof(*mgmt_rp) + eir_len);
+	if (err < 0 || status)
+		goto done;
+
+	hci_sock_set_flag(cmd->sk, HCI_MGMT_OOB_DATA_EVENTS);
+
+	err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev,
+				 mgmt_rp, sizeof(*mgmt_rp) + eir_len,
+				 HCI_MGMT_OOB_DATA_EVENTS, cmd->sk);
+done:
+	kfree(mgmt_rp);
+	mgmt_pending_remove(cmd);
+}
+
+static int read_local_ssp_oob_req(struct hci_dev *hdev, struct sock *sk,
+				  struct mgmt_cp_read_local_oob_ext_data *cp)
+{
+	struct mgmt_pending_cmd *cmd;
+	struct hci_request req;
+	int err;
+
+	cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev,
+			       cp, sizeof(*cp));
+	if (!cmd)
 		return -ENOMEM;
 
-	if (memcpy_from_msg(buf, msg, msglen)) {
-		err = -EFAULT;
-		goto done;
+	hci_req_init(&req, hdev);
+
+	if (bredr_sc_enabled(hdev))
+		hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL);
+	else
+		hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+
+	err = hci_req_run_skb(&req, read_local_oob_ext_data_complete);
+	if (err < 0) {
+		mgmt_pending_remove(cmd);
+		return err;
 	}
 
-	hdr = buf;
-	opcode = __le16_to_cpu(hdr->opcode);
-	index = __le16_to_cpu(hdr->index);
-	len = __le16_to_cpu(hdr->len);
+	return 0;
+}
 
-	if (len != msglen - sizeof(*hdr)) {
-		err = -EINVAL;
-		goto done;
+static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
+				   void *data, u16 data_len)
+{
+	struct mgmt_cp_read_local_oob_ext_data *cp = data;
+	struct mgmt_rp_read_local_oob_ext_data *rp;
+	size_t rp_len;
+	u16 eir_len;
+	u8 status, flags, role, addr[7], hash[16], rand[16];
+	int err;
+
+	BT_DBG("%s", hdev->name);
+
+	if (hdev_is_powered(hdev)) {
+		switch (cp->type) {
+		case BIT(BDADDR_BREDR):
+			status = mgmt_bredr_support(hdev);
+			if (status)
+				eir_len = 0;
+			else
+				eir_len = 5;
+			break;
+		case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
+			status = mgmt_le_support(hdev);
+			if (status)
+				eir_len = 0;
+			else
+				eir_len = 9 + 3 + 18 + 18 + 3;
+			break;
+		default:
+			status = MGMT_STATUS_INVALID_PARAMS;
+			eir_len = 0;
+			break;
+		}
+	} else {
+		status = MGMT_STATUS_NOT_POWERED;
+		eir_len = 0;
 	}
 
-	if (index != MGMT_INDEX_NONE) {
-		hdev = hci_dev_get(index);
-		if (!hdev) {
-			err = cmd_status(sk, index, opcode,
-					 MGMT_STATUS_INVALID_INDEX);
-			goto done;
+	rp_len = sizeof(*rp) + eir_len;
+	rp = kmalloc(rp_len, GFP_ATOMIC);
+	if (!rp)
+		return -ENOMEM;
+
+	if (status)
+		goto complete;
+
+	hci_dev_lock(hdev);
+
+	eir_len = 0;
+	switch (cp->type) {
+	case BIT(BDADDR_BREDR):
+		if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) {
+			err = read_local_ssp_oob_req(hdev, sk, cp);
+			hci_dev_unlock(hdev);
+			if (!err)
+				goto done;
+
+			status = MGMT_STATUS_FAILED;
+			goto complete;
+		} else {
+			eir_len = eir_append_data(rp->eir, eir_len,
+						  EIR_CLASS_OF_DEV,
+						  hdev->dev_class, 3);
+		}
+		break;
+	case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
+		if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
+		    smp_generate_oob(hdev, hash, rand) < 0) {
+			hci_dev_unlock(hdev);
+			status = MGMT_STATUS_FAILED;
+			goto complete;
 		}
 
-		if (test_bit(HCI_SETUP, &hdev->dev_flags) ||
-		    test_bit(HCI_CONFIG, &hdev->dev_flags) ||
-		    test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
-			err = cmd_status(sk, index, opcode,
-					 MGMT_STATUS_INVALID_INDEX);
-			goto done;
+		/* This should return the active RPA, but since the RPA
+		 * is only programmed on demand, it is really hard to fill
+		 * this in at the moment. For now disallow retrieving
+		 * local out-of-band data when privacy is in use.
+		 *
+		 * Returning the identity address will not help here since
+		 * pairing happens before the identity resolving key is
+		 * known and thus the connection establishment happens
+		 * based on the RPA and not the identity address.
+		 */
+		if (hci_dev_test_flag(hdev, HCI_PRIVACY)) {
+			hci_dev_unlock(hdev);
+			status = MGMT_STATUS_REJECTED;
+			goto complete;
 		}
 
-		if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
-		    opcode != MGMT_OP_READ_CONFIG_INFO &&
-		    opcode != MGMT_OP_SET_EXTERNAL_CONFIG &&
-		    opcode != MGMT_OP_SET_PUBLIC_ADDRESS) {
-			err = cmd_status(sk, index, opcode,
-					 MGMT_STATUS_INVALID_INDEX);
-			goto done;
+		if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
+		   !bacmp(&hdev->bdaddr, BDADDR_ANY) ||
+		   (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
+		    bacmp(&hdev->static_addr, BDADDR_ANY))) {
+			memcpy(addr, &hdev->static_addr, 6);
+			addr[6] = 0x01;
+		} else {
+			memcpy(addr, &hdev->bdaddr, 6);
+			addr[6] = 0x00;
 		}
+
+		eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_BDADDR,
+					  addr, sizeof(addr));
+
+		if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
+			role = 0x02;
+		else
+			role = 0x01;
+
+		eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_ROLE,
+					  &role, sizeof(role));
+
+		if (hci_dev_test_flag(hdev, HCI_SC_ENABLED)) {
+			eir_len = eir_append_data(rp->eir, eir_len,
+						  EIR_LE_SC_CONFIRM,
+						  hash, sizeof(hash));
+
+			eir_len = eir_append_data(rp->eir, eir_len,
+						  EIR_LE_SC_RANDOM,
+						  rand, sizeof(rand));
+		}
+
+		flags = get_adv_discov_flags(hdev);
+
+		if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
+			flags |= LE_AD_NO_BREDR;
+
+		eir_len = eir_append_data(rp->eir, eir_len, EIR_FLAGS,
+					  &flags, sizeof(flags));
+		break;
 	}
 
-	if (opcode >= ARRAY_SIZE(mgmt_handlers) ||
-	    mgmt_handlers[opcode].func == NULL) {
-		BT_DBG("Unknown op %u", opcode);
-		err = cmd_status(sk, index, opcode,
-				 MGMT_STATUS_UNKNOWN_COMMAND);
-		goto done;
-	}
+	hci_dev_unlock(hdev);
 
-	if (hdev && (opcode <= MGMT_OP_READ_INDEX_LIST ||
-		     opcode == MGMT_OP_READ_UNCONF_INDEX_LIST)) {
-		err = cmd_status(sk, index, opcode,
-				 MGMT_STATUS_INVALID_INDEX);
-		goto done;
-	}
+	hci_sock_set_flag(sk, HCI_MGMT_OOB_DATA_EVENTS);
 
-	if (!hdev && (opcode > MGMT_OP_READ_INDEX_LIST &&
-		      opcode != MGMT_OP_READ_UNCONF_INDEX_LIST)) {
-		err = cmd_status(sk, index, opcode,
-				 MGMT_STATUS_INVALID_INDEX);
-		goto done;
-	}
+	status = MGMT_STATUS_SUCCESS;
 
-	handler = &mgmt_handlers[opcode];
+complete:
+	rp->type = cp->type;
+	rp->eir_len = cpu_to_le16(eir_len);
 
-	if ((handler->var_len && len < handler->data_len) ||
-	    (!handler->var_len && len != handler->data_len)) {
-		err = cmd_status(sk, index, opcode,
-				 MGMT_STATUS_INVALID_PARAMS);
-		goto done;
-	}
-
-	if (hdev)
-		mgmt_init_hdev(sk, hdev);
-
-	cp = buf + sizeof(*hdr);
-
-	err = handler->func(sk, hdev, cp, len);
-	if (err < 0)
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
+				status, rp, sizeof(*rp) + eir_len);
+	if (err < 0 || status)
 		goto done;
 
-	err = msglen;
+	err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev,
+				 rp, sizeof(*rp) + eir_len,
+				 HCI_MGMT_OOB_DATA_EVENTS, sk);
 
 done:
-	if (hdev)
-		hci_dev_put(hdev);
+	kfree(rp);
 
-	kfree(buf);
 	return err;
 }
 
+static u32 get_supported_adv_flags(struct hci_dev *hdev)
+{
+	u32 flags = 0;
+
+	flags |= MGMT_ADV_FLAG_CONNECTABLE;
+	flags |= MGMT_ADV_FLAG_DISCOV;
+	flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
+	flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
+
+	if (hdev->adv_tx_power != HCI_TX_POWER_INVALID)
+		flags |= MGMT_ADV_FLAG_TX_POWER;
+
+	return flags;
+}
+
+static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
+			     void *data, u16 data_len)
+{
+	struct mgmt_rp_read_adv_features *rp;
+	size_t rp_len;
+	int err;
+	bool instance;
+	u32 supported_flags;
+
+	BT_DBG("%s", hdev->name);
+
+	if (!lmp_le_capable(hdev))
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_ADV_FEATURES,
+				       MGMT_STATUS_REJECTED);
+
+	hci_dev_lock(hdev);
+
+	rp_len = sizeof(*rp);
+
+	/* Currently only one instance is supported, so just add 1 to the
+	 * response length.
+	 */
+	instance = hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE);
+	if (instance)
+		rp_len++;
+
+	rp = kmalloc(rp_len, GFP_ATOMIC);
+	if (!rp) {
+		hci_dev_unlock(hdev);
+		return -ENOMEM;
+	}
+
+	supported_flags = get_supported_adv_flags(hdev);
+
+	rp->supported_flags = cpu_to_le32(supported_flags);
+	rp->max_adv_data_len = HCI_MAX_AD_LENGTH;
+	rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH;
+	rp->max_instances = 1;
+
+	/* Currently only one instance is supported, so simply return the
+	 * current instance number.
+	 */
+	if (instance) {
+		rp->num_instances = 1;
+		rp->instance[0] = 1;
+	} else {
+		rp->num_instances = 0;
+	}
+
+	hci_dev_unlock(hdev);
+
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_ADV_FEATURES,
+				MGMT_STATUS_SUCCESS, rp, rp_len);
+
+	kfree(rp);
+
+	return err;
+}
+
+static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
+			      u8 len, bool is_adv_data)
+{
+	u8 max_len = HCI_MAX_AD_LENGTH;
+	int i, cur_len;
+	bool flags_managed = false;
+	bool tx_power_managed = false;
+	u32 flags_params = MGMT_ADV_FLAG_DISCOV | MGMT_ADV_FLAG_LIMITED_DISCOV |
+			   MGMT_ADV_FLAG_MANAGED_FLAGS;
+
+	if (is_adv_data && (adv_flags & flags_params)) {
+		flags_managed = true;
+		max_len -= 3;
+	}
+
+	if (is_adv_data && (adv_flags & MGMT_ADV_FLAG_TX_POWER)) {
+		tx_power_managed = true;
+		max_len -= 3;
+	}
+
+	if (len > max_len)
+		return false;
+
+	/* Make sure that the data is correctly formatted. */
+	for (i = 0, cur_len = 0; i < len; i += (cur_len + 1)) {
+		cur_len = data[i];
+
+		if (flags_managed && data[i + 1] == EIR_FLAGS)
+			return false;
+
+		if (tx_power_managed && data[i + 1] == EIR_TX_POWER)
+			return false;
+
+		/* If the current field length would exceed the total data
+		 * length, then it's invalid.
+		 */
+		if (i + cur_len >= len)
+			return false;
+	}
+
+	return true;
+}
+
+static void add_advertising_complete(struct hci_dev *hdev, u8 status,
+				     u16 opcode)
+{
+	struct mgmt_pending_cmd *cmd;
+	struct mgmt_rp_add_advertising rp;
+
+	BT_DBG("status %d", status);
+
+	hci_dev_lock(hdev);
+
+	cmd = pending_find(MGMT_OP_ADD_ADVERTISING, hdev);
+
+	if (status) {
+		hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
+		memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
+		advertising_removed(cmd ? cmd->sk : NULL, hdev, 1);
+	}
+
+	if (!cmd)
+		goto unlock;
+
+	rp.instance = 0x01;
+
+	if (status)
+		mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode,
+				mgmt_status(status));
+	else
+		mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
+				  mgmt_status(status), &rp, sizeof(rp));
+
+	mgmt_pending_remove(cmd);
+
+unlock:
+	hci_dev_unlock(hdev);
+}
+
+static void adv_timeout_expired(struct work_struct *work)
+{
+	struct hci_dev *hdev = container_of(work, struct hci_dev,
+					    adv_instance.timeout_exp.work);
+
+	hdev->adv_instance.timeout = 0;
+
+	hci_dev_lock(hdev);
+	clear_adv_instance(hdev);
+	hci_dev_unlock(hdev);
+}
+
+static int add_advertising(struct sock *sk, struct hci_dev *hdev,
+			   void *data, u16 data_len)
+{
+	struct mgmt_cp_add_advertising *cp = data;
+	struct mgmt_rp_add_advertising rp;
+	u32 flags;
+	u32 supported_flags;
+	u8 status;
+	u16 timeout;
+	int err;
+	struct mgmt_pending_cmd *cmd;
+	struct hci_request req;
+
+	BT_DBG("%s", hdev->name);
+
+	status = mgmt_le_support(hdev);
+	if (status)
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
+				       status);
+
+	flags = __le32_to_cpu(cp->flags);
+	timeout = __le16_to_cpu(cp->timeout);
+
+	/* The current implementation only supports adding one instance and only
+	 * a subset of the specified flags.
+	 */
+	supported_flags = get_supported_adv_flags(hdev);
+	if (cp->instance != 0x01 || (flags & ~supported_flags))
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
+				       MGMT_STATUS_INVALID_PARAMS);
+
+	hci_dev_lock(hdev);
+
+	if (timeout && !hdev_is_powered(hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
+				      MGMT_STATUS_REJECTED);
+		goto unlock;
+	}
+
+	if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) ||
+	    pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) ||
+	    pending_find(MGMT_OP_SET_LE, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
+				      MGMT_STATUS_BUSY);
+		goto unlock;
+	}
+
+	if (!tlv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len, true) ||
+	    !tlv_data_is_valid(hdev, flags, cp->data + cp->adv_data_len,
+			       cp->scan_rsp_len, false)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
+				      MGMT_STATUS_INVALID_PARAMS);
+		goto unlock;
+	}
+
+	INIT_DELAYED_WORK(&hdev->adv_instance.timeout_exp, adv_timeout_expired);
+
+	hdev->adv_instance.flags = flags;
+	hdev->adv_instance.adv_data_len = cp->adv_data_len;
+	hdev->adv_instance.scan_rsp_len = cp->scan_rsp_len;
+
+	if (cp->adv_data_len)
+		memcpy(hdev->adv_instance.adv_data, cp->data, cp->adv_data_len);
+
+	if (cp->scan_rsp_len)
+		memcpy(hdev->adv_instance.scan_rsp_data,
+		       cp->data + cp->adv_data_len, cp->scan_rsp_len);
+
+	if (hdev->adv_instance.timeout)
+		cancel_delayed_work(&hdev->adv_instance.timeout_exp);
+
+	hdev->adv_instance.timeout = timeout;
+
+	if (timeout)
+		queue_delayed_work(hdev->workqueue,
+				   &hdev->adv_instance.timeout_exp,
+				   msecs_to_jiffies(timeout * 1000));
+
+	if (!hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING_INSTANCE))
+		advertising_added(sk, hdev, 1);
+
+	/* If the HCI_ADVERTISING flag is set or the device isn't powered then
+	 * we have no HCI communication to make. Simply return.
+	 */
+	if (!hdev_is_powered(hdev) ||
+	    hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
+		rp.instance = 0x01;
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
+					MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
+		goto unlock;
+	}
+
+	/* We're good to go, update advertising data, parameters, and start
+	 * advertising.
+	 */
+	cmd = mgmt_pending_add(sk, MGMT_OP_ADD_ADVERTISING, hdev, data,
+			       data_len);
+	if (!cmd) {
+		err = -ENOMEM;
+		goto unlock;
+	}
+
+	hci_req_init(&req, hdev);
+
+	update_adv_data(&req);
+	update_scan_rsp_data(&req);
+	enable_advertising(&req);
+
+	err = hci_req_run(&req, add_advertising_complete);
+	if (err < 0)
+		mgmt_pending_remove(cmd);
+
+unlock:
+	hci_dev_unlock(hdev);
+
+	return err;
+}
+
+static void remove_advertising_complete(struct hci_dev *hdev, u8 status,
+					u16 opcode)
+{
+	struct mgmt_pending_cmd *cmd;
+	struct mgmt_rp_remove_advertising rp;
+
+	BT_DBG("status %d", status);
+
+	hci_dev_lock(hdev);
+
+	/* A failure status here only means that we failed to disable
+	 * advertising. Otherwise, the advertising instance has been removed,
+	 * so report success.
+	 */
+	cmd = pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev);
+	if (!cmd)
+		goto unlock;
+
+	rp.instance = 1;
+
+	mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, MGMT_STATUS_SUCCESS,
+			  &rp, sizeof(rp));
+	mgmt_pending_remove(cmd);
+
+unlock:
+	hci_dev_unlock(hdev);
+}
+
+static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
+			      void *data, u16 data_len)
+{
+	struct mgmt_cp_remove_advertising *cp = data;
+	struct mgmt_rp_remove_advertising rp;
+	int err;
+	struct mgmt_pending_cmd *cmd;
+	struct hci_request req;
+
+	BT_DBG("%s", hdev->name);
+
+	/* The current implementation only allows modifying instance no 1. A
+	 * value of 0 indicates that all instances should be cleared.
+	 */
+	if (cp->instance > 1)
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING,
+				       MGMT_STATUS_INVALID_PARAMS);
+
+	hci_dev_lock(hdev);
+
+	if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) ||
+	    pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) ||
+	    pending_find(MGMT_OP_SET_LE, hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING,
+				      MGMT_STATUS_BUSY);
+		goto unlock;
+	}
+
+	if (!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING,
+				      MGMT_STATUS_INVALID_PARAMS);
+		goto unlock;
+	}
+
+	if (hdev->adv_instance.timeout)
+		cancel_delayed_work(&hdev->adv_instance.timeout_exp);
+
+	memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
+
+	advertising_removed(sk, hdev, 1);
+
+	hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
+
+	/* If the HCI_ADVERTISING flag is set or the device isn't powered then
+	 * we have no HCI communication to make. Simply return.
+	 */
+	if (!hdev_is_powered(hdev) ||
+	    hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
+		rp.instance = 1;
+		err = mgmt_cmd_complete(sk, hdev->id,
+					MGMT_OP_REMOVE_ADVERTISING,
+					MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
+		goto unlock;
+	}
+
+	cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_ADVERTISING, hdev, data,
+			       data_len);
+	if (!cmd) {
+		err = -ENOMEM;
+		goto unlock;
+	}
+
+	hci_req_init(&req, hdev);
+	disable_advertising(&req);
+
+	err = hci_req_run(&req, remove_advertising_complete);
+	if (err < 0)
+		mgmt_pending_remove(cmd);
+
+unlock:
+	hci_dev_unlock(hdev);
+
+	return err;
+}
+
+static const struct hci_mgmt_handler mgmt_handlers[] = {
+	{ NULL }, /* 0x0000 (no command) */
+	{ read_version,            MGMT_READ_VERSION_SIZE,
+						HCI_MGMT_NO_HDEV |
+						HCI_MGMT_UNTRUSTED },
+	{ read_commands,           MGMT_READ_COMMANDS_SIZE,
+						HCI_MGMT_NO_HDEV |
+						HCI_MGMT_UNTRUSTED },
+	{ read_index_list,         MGMT_READ_INDEX_LIST_SIZE,
+						HCI_MGMT_NO_HDEV |
+						HCI_MGMT_UNTRUSTED },
+	{ read_controller_info,    MGMT_READ_INFO_SIZE,
+						HCI_MGMT_UNTRUSTED },
+	{ set_powered,             MGMT_SETTING_SIZE },
+	{ set_discoverable,        MGMT_SET_DISCOVERABLE_SIZE },
+	{ set_connectable,         MGMT_SETTING_SIZE },
+	{ set_fast_connectable,    MGMT_SETTING_SIZE },
+	{ set_bondable,            MGMT_SETTING_SIZE },
+	{ set_link_security,       MGMT_SETTING_SIZE },
+	{ set_ssp,                 MGMT_SETTING_SIZE },
+	{ set_hs,                  MGMT_SETTING_SIZE },
+	{ set_le,                  MGMT_SETTING_SIZE },
+	{ set_dev_class,           MGMT_SET_DEV_CLASS_SIZE },
+	{ set_local_name,          MGMT_SET_LOCAL_NAME_SIZE },
+	{ add_uuid,                MGMT_ADD_UUID_SIZE },
+	{ remove_uuid,             MGMT_REMOVE_UUID_SIZE },
+	{ load_link_keys,          MGMT_LOAD_LINK_KEYS_SIZE,
+						HCI_MGMT_VAR_LEN },
+	{ load_long_term_keys,     MGMT_LOAD_LONG_TERM_KEYS_SIZE,
+						HCI_MGMT_VAR_LEN },
+	{ disconnect,              MGMT_DISCONNECT_SIZE },
+	{ get_connections,         MGMT_GET_CONNECTIONS_SIZE },
+	{ pin_code_reply,          MGMT_PIN_CODE_REPLY_SIZE },
+	{ pin_code_neg_reply,      MGMT_PIN_CODE_NEG_REPLY_SIZE },
+	{ set_io_capability,       MGMT_SET_IO_CAPABILITY_SIZE },
+	{ pair_device,             MGMT_PAIR_DEVICE_SIZE },
+	{ cancel_pair_device,      MGMT_CANCEL_PAIR_DEVICE_SIZE },
+	{ unpair_device,           MGMT_UNPAIR_DEVICE_SIZE },
+	{ user_confirm_reply,      MGMT_USER_CONFIRM_REPLY_SIZE },
+	{ user_confirm_neg_reply,  MGMT_USER_CONFIRM_NEG_REPLY_SIZE },
+	{ user_passkey_reply,      MGMT_USER_PASSKEY_REPLY_SIZE },
+	{ user_passkey_neg_reply,  MGMT_USER_PASSKEY_NEG_REPLY_SIZE },
+	{ read_local_oob_data,     MGMT_READ_LOCAL_OOB_DATA_SIZE },
+	{ add_remote_oob_data,     MGMT_ADD_REMOTE_OOB_DATA_SIZE,
+						HCI_MGMT_VAR_LEN },
+	{ remove_remote_oob_data,  MGMT_REMOVE_REMOTE_OOB_DATA_SIZE },
+	{ start_discovery,         MGMT_START_DISCOVERY_SIZE },
+	{ stop_discovery,          MGMT_STOP_DISCOVERY_SIZE },
+	{ confirm_name,            MGMT_CONFIRM_NAME_SIZE },
+	{ block_device,            MGMT_BLOCK_DEVICE_SIZE },
+	{ unblock_device,          MGMT_UNBLOCK_DEVICE_SIZE },
+	{ set_device_id,           MGMT_SET_DEVICE_ID_SIZE },
+	{ set_advertising,         MGMT_SETTING_SIZE },
+	{ set_bredr,               MGMT_SETTING_SIZE },
+	{ set_static_address,      MGMT_SET_STATIC_ADDRESS_SIZE },
+	{ set_scan_params,         MGMT_SET_SCAN_PARAMS_SIZE },
+	{ set_secure_conn,         MGMT_SETTING_SIZE },
+	{ set_debug_keys,          MGMT_SETTING_SIZE },
+	{ set_privacy,             MGMT_SET_PRIVACY_SIZE },
+	{ load_irks,               MGMT_LOAD_IRKS_SIZE,
+						HCI_MGMT_VAR_LEN },
+	{ get_conn_info,           MGMT_GET_CONN_INFO_SIZE },
+	{ get_clock_info,          MGMT_GET_CLOCK_INFO_SIZE },
+	{ add_device,              MGMT_ADD_DEVICE_SIZE },
+	{ remove_device,           MGMT_REMOVE_DEVICE_SIZE },
+	{ load_conn_param,         MGMT_LOAD_CONN_PARAM_SIZE,
+						HCI_MGMT_VAR_LEN },
+	{ read_unconf_index_list,  MGMT_READ_UNCONF_INDEX_LIST_SIZE,
+						HCI_MGMT_NO_HDEV |
+						HCI_MGMT_UNTRUSTED },
+	{ read_config_info,        MGMT_READ_CONFIG_INFO_SIZE,
+						HCI_MGMT_UNCONFIGURED |
+						HCI_MGMT_UNTRUSTED },
+	{ set_external_config,     MGMT_SET_EXTERNAL_CONFIG_SIZE,
+						HCI_MGMT_UNCONFIGURED },
+	{ set_public_address,      MGMT_SET_PUBLIC_ADDRESS_SIZE,
+						HCI_MGMT_UNCONFIGURED },
+	{ start_service_discovery, MGMT_START_SERVICE_DISCOVERY_SIZE,
+						HCI_MGMT_VAR_LEN },
+	{ read_local_oob_ext_data, MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE },
+	{ read_ext_index_list,     MGMT_READ_EXT_INDEX_LIST_SIZE,
+						HCI_MGMT_NO_HDEV |
+						HCI_MGMT_UNTRUSTED },
+	{ read_adv_features,       MGMT_READ_ADV_FEATURES_SIZE },
+	{ add_advertising,	   MGMT_ADD_ADVERTISING_SIZE,
+						HCI_MGMT_VAR_LEN },
+	{ remove_advertising,	   MGMT_REMOVE_ADVERTISING_SIZE },
+};
+
 void mgmt_index_added(struct hci_dev *hdev)
 {
-	if (hdev->dev_type != HCI_BREDR)
-		return;
+	struct mgmt_ev_ext_index ev;
 
 	if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
 		return;
 
-	if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
-		mgmt_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, NULL, 0, NULL);
-	else
-		mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
+	switch (hdev->dev_type) {
+	case HCI_BREDR:
+		if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
+			mgmt_index_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev,
+					 NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS);
+			ev.type = 0x01;
+		} else {
+			mgmt_index_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0,
+					 HCI_MGMT_INDEX_EVENTS);
+			ev.type = 0x00;
+		}
+		break;
+	case HCI_AMP:
+		ev.type = 0x02;
+		break;
+	default:
+		return;
+	}
+
+	ev.bus = hdev->bus;
+
+	mgmt_index_event(MGMT_EV_EXT_INDEX_ADDED, hdev, &ev, sizeof(ev),
+			 HCI_MGMT_EXT_INDEX_EVENTS);
 }
 
 void mgmt_index_removed(struct hci_dev *hdev)
 {
+	struct mgmt_ev_ext_index ev;
 	u8 status = MGMT_STATUS_INVALID_INDEX;
 
-	if (hdev->dev_type != HCI_BREDR)
-		return;
-
 	if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
 		return;
 
-	mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status);
+	switch (hdev->dev_type) {
+	case HCI_BREDR:
+		mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status);
 
-	if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
-		mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL);
-	else
-		mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
+		if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
+			mgmt_index_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev,
+					 NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS);
+			ev.type = 0x01;
+		} else {
+			mgmt_index_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0,
+					 HCI_MGMT_INDEX_EVENTS);
+			ev.type = 0x00;
+		}
+		break;
+	case HCI_AMP:
+		ev.type = 0x02;
+		break;
+	default:
+		return;
+	}
+
+	ev.bus = hdev->bus;
+
+	mgmt_index_event(MGMT_EV_EXT_INDEX_REMOVED, hdev, &ev, sizeof(ev),
+			 HCI_MGMT_EXT_INDEX_EVENTS);
 }
 
 /* This function requires the caller holds hdev->lock */
-static void restart_le_actions(struct hci_dev *hdev)
+static void restart_le_actions(struct hci_request *req)
 {
+	struct hci_dev *hdev = req->hdev;
 	struct hci_conn_params *p;
 
 	list_for_each_entry(p, &hdev->le_conn_params, list) {
@@ -6060,18 +7328,25 @@
 		}
 	}
 
-	hci_update_background_scan(hdev);
+	__hci_update_background_scan(req);
 }
 
-static void powered_complete(struct hci_dev *hdev, u8 status)
+static void powered_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
 	struct cmd_lookup match = { NULL, hdev };
 
 	BT_DBG("status 0x%02x", status);
 
-	hci_dev_lock(hdev);
+	if (!status) {
+		/* Register the available SMP channels (BR/EDR and LE) only
+		 * when successfully powering on the controller. This late
+		 * registration is required so that LE SMP can clearly
+		 * decide if the public address or static address is used.
+		 */
+		smp_register(hdev);
+	}
 
-	restart_le_actions(hdev);
+	hci_dev_lock(hdev);
 
 	mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
 
@@ -6090,19 +7365,21 @@
 
 	hci_req_init(&req, hdev);
 
-	if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
+	if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
 	    !lmp_host_ssp_capable(hdev)) {
-		u8 ssp = 1;
+		u8 mode = 0x01;
 
-		hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
+		hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
+
+		if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
+			u8 support = 0x01;
+
+			hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT,
+				    sizeof(support), &support);
+		}
 	}
 
-	if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
-		u8 sc = 0x01;
-		hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT, sizeof(sc), &sc);
-	}
-
-	if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+	if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
 	    lmp_bredr_capable(hdev)) {
 		struct hci_cp_write_le_host_supported cp;
 
@@ -6123,23 +7400,29 @@
 		 * advertising data. This also applies to the case
 		 * where BR/EDR was toggled during the AUTO_OFF phase.
 		 */
-		if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+		if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
 			update_adv_data(&req);
 			update_scan_rsp_data(&req);
 		}
 
-		if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+		if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+		    hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
 			enable_advertising(&req);
+
+		restart_le_actions(&req);
 	}
 
-	link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
+	link_sec = hci_dev_test_flag(hdev, HCI_LINK_SECURITY);
 	if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
 		hci_req_add(&req, HCI_OP_WRITE_AUTH_ENABLE,
 			    sizeof(link_sec), &link_sec);
 
 	if (lmp_bredr_capable(hdev)) {
-		write_fast_connectable(&req, false);
-		hci_update_page_scan(hdev, &req);
+		if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE))
+			write_fast_connectable(&req, true);
+		else
+			write_fast_connectable(&req, false);
+		__hci_update_page_scan(&req);
 		update_class(&req);
 		update_name(&req);
 		update_eir(&req);
@@ -6154,7 +7437,7 @@
 	u8 status, zero_cod[] = { 0, 0, 0 };
 	int err;
 
-	if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_MGMT))
 		return 0;
 
 	if (powered) {
@@ -6175,7 +7458,7 @@
 	 * been triggered, potentially causing misleading DISCONNECTED
 	 * status responses.
 	 */
-	if (test_bit(HCI_UNREGISTER, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
 		status = MGMT_STATUS_INVALID_INDEX;
 	else
 		status = MGMT_STATUS_NOT_POWERED;
@@ -6183,8 +7466,8 @@
 	mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status);
 
 	if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
-		mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
-			   zero_cod, sizeof(zero_cod), NULL);
+		mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
+				   zero_cod, sizeof(zero_cod), NULL);
 
 new_settings:
 	err = new_settings(hdev, match.sk);
@@ -6197,10 +7480,10 @@
 
 void mgmt_set_powered_failed(struct hci_dev *hdev, int err)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	u8 status;
 
-	cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
+	cmd = pending_find(MGMT_OP_SET_POWERED, hdev);
 	if (!cmd)
 		return;
 
@@ -6209,7 +7492,7 @@
 	else
 		status = MGMT_STATUS_FAILED;
 
-	cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status);
+	mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status);
 
 	mgmt_pending_remove(cmd);
 }
@@ -6225,17 +7508,23 @@
 	 * of a timeout triggered from general discoverable, it is
 	 * safe to unconditionally clear the flag.
 	 */
-	clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
-	clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+	hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
+	hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
 
 	hci_req_init(&req, hdev);
-	if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
 		u8 scan = SCAN_PAGE;
 		hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE,
 			    sizeof(scan), &scan);
 	}
 	update_class(&req);
-	update_adv_data(&req);
+
+	/* Advertising instances don't use the global discoverable setting, so
+	 * only update AD if advertising was enabled using Set Advertising.
+	 */
+	if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
+		update_adv_data(&req);
+
 	hci_req_run(&req, NULL);
 
 	hdev->discov_timeout = 0;
@@ -6373,7 +7662,7 @@
 
 	bacpy(&ev.key.addr.bdaddr, &csrk->bdaddr);
 	ev.key.addr.type = link_to_bdaddr(LE_LINK, csrk->bdaddr_type);
-	ev.key.master = csrk->master;
+	ev.key.type = csrk->type;
 	memcpy(ev.key.val, csrk->val, sizeof(csrk->val));
 
 	mgmt_event(MGMT_EV_NEW_CSRK, hdev, &ev, sizeof(ev), NULL);
@@ -6400,17 +7689,6 @@
 	mgmt_event(MGMT_EV_NEW_CONN_PARAM, hdev, &ev, sizeof(ev), NULL);
 }
 
-static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
-				  u8 data_len)
-{
-	eir[eir_len++] = sizeof(type) + data_len;
-	eir[eir_len++] = type;
-	memcpy(&eir[eir_len], data, data_len);
-	eir_len += data_len;
-
-	return eir_len;
-}
-
 void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
 			   u32 flags, u8 *name, u8 name_len)
 {
@@ -6448,7 +7726,7 @@
 		    sizeof(*ev) + eir_len, NULL);
 }
 
-static void disconnect_rsp(struct pending_cmd *cmd, void *data)
+static void disconnect_rsp(struct mgmt_pending_cmd *cmd, void *data)
 {
 	struct sock **sk = data;
 
@@ -6460,7 +7738,7 @@
 	mgmt_pending_remove(cmd);
 }
 
-static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
+static void unpair_device_rsp(struct mgmt_pending_cmd *cmd, void *data)
 {
 	struct hci_dev *hdev = data;
 	struct mgmt_cp_unpair_device *cp = cmd->param;
@@ -6473,10 +7751,10 @@
 
 bool mgmt_powering_down(struct hci_dev *hdev)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	struct mgmt_mode *cp;
 
-	cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
+	cmd = pending_find(MGMT_OP_SET_POWERED, hdev);
 	if (!cmd)
 		return false;
 
@@ -6528,12 +7806,12 @@
 {
 	u8 bdaddr_type = link_to_bdaddr(link_type, addr_type);
 	struct mgmt_cp_disconnect *cp;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
 			     hdev);
 
-	cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
+	cmd = pending_find(MGMT_OP_DISCONNECT, hdev);
 	if (!cmd)
 		return;
 
@@ -6583,9 +7861,9 @@
 void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
 				  u8 status)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
-	cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
+	cmd = pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
 	if (!cmd)
 		return;
 
@@ -6596,9 +7874,9 @@
 void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
 				      u8 status)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
-	cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
+	cmd = pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
 	if (!cmd)
 		return;
 
@@ -6641,9 +7919,9 @@
 				      u8 link_type, u8 addr_type, u8 status,
 				      u8 opcode)
 {
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
-	cmd = mgmt_pending_find(opcode, hdev);
+	cmd = pending_find(opcode, hdev);
 	if (!cmd)
 		return -ENOENT;
 
@@ -6702,7 +7980,7 @@
 void mgmt_auth_failed(struct hci_conn *conn, u8 hci_status)
 {
 	struct mgmt_ev_auth_failed ev;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 	u8 status = mgmt_status(hci_status);
 
 	bacpy(&ev.addr.bdaddr, &conn->dst);
@@ -6733,11 +8011,9 @@
 	}
 
 	if (test_bit(HCI_AUTH, &hdev->flags))
-		changed = !test_and_set_bit(HCI_LINK_SECURITY,
-					    &hdev->dev_flags);
+		changed = !hci_dev_test_and_set_flag(hdev, HCI_LINK_SECURITY);
 	else
-		changed = test_and_clear_bit(HCI_LINK_SECURITY,
-					     &hdev->dev_flags);
+		changed = hci_dev_test_and_clear_flag(hdev, HCI_LINK_SECURITY);
 
 	mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
 			     &match);
@@ -6773,9 +8049,9 @@
 	if (status) {
 		u8 mgmt_err = mgmt_status(status);
 
-		if (enable && test_and_clear_bit(HCI_SSP_ENABLED,
-						 &hdev->dev_flags)) {
-			clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+		if (enable && hci_dev_test_and_clear_flag(hdev,
+							  HCI_SSP_ENABLED)) {
+			hci_dev_clear_flag(hdev, HCI_HS_ENABLED);
 			new_settings(hdev, NULL);
 		}
 
@@ -6785,14 +8061,14 @@
 	}
 
 	if (enable) {
-		changed = !test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+		changed = !hci_dev_test_and_set_flag(hdev, HCI_SSP_ENABLED);
 	} else {
-		changed = test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+		changed = hci_dev_test_and_clear_flag(hdev, HCI_SSP_ENABLED);
 		if (!changed)
-			changed = test_and_clear_bit(HCI_HS_ENABLED,
-						     &hdev->dev_flags);
+			changed = hci_dev_test_and_clear_flag(hdev,
+							      HCI_HS_ENABLED);
 		else
-			clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+			hci_dev_clear_flag(hdev, HCI_HS_ENABLED);
 	}
 
 	mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
@@ -6805,8 +8081,8 @@
 
 	hci_req_init(&req, hdev);
 
-	if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
-		if (test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) {
+		if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS))
 			hci_req_add(&req, HCI_OP_WRITE_SSP_DEBUG_MODE,
 				    sizeof(enable), &enable);
 		update_eir(&req);
@@ -6817,44 +8093,7 @@
 	hci_req_run(&req, NULL);
 }
 
-void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
-{
-	struct cmd_lookup match = { NULL, hdev };
-	bool changed = false;
-
-	if (status) {
-		u8 mgmt_err = mgmt_status(status);
-
-		if (enable) {
-			if (test_and_clear_bit(HCI_SC_ENABLED,
-					       &hdev->dev_flags))
-				new_settings(hdev, NULL);
-			clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
-		}
-
-		mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev,
-				     cmd_status_rsp, &mgmt_err);
-		return;
-	}
-
-	if (enable) {
-		changed = !test_and_set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
-	} else {
-		changed = test_and_clear_bit(HCI_SC_ENABLED, &hdev->dev_flags);
-		clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
-	}
-
-	mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev,
-			     settings_rsp, &match);
-
-	if (changed)
-		new_settings(hdev, match.sk);
-
-	if (match.sk)
-		sock_put(match.sk);
-}
-
-static void sk_lookup(struct pending_cmd *cmd, void *data)
+static void sk_lookup(struct mgmt_pending_cmd *cmd, void *data)
 {
 	struct cmd_lookup *match = data;
 
@@ -6874,8 +8113,8 @@
 	mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match);
 
 	if (!status)
-		mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3,
-			   NULL);
+		mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
+				   dev_class, 3, NULL);
 
 	if (match.sk)
 		sock_put(match.sk);
@@ -6884,7 +8123,7 @@
 void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
 {
 	struct mgmt_cp_set_local_name ev;
-	struct pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd;
 
 	if (status)
 		return;
@@ -6893,62 +8132,19 @@
 	memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
 	memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH);
 
-	cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
+	cmd = pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
 	if (!cmd) {
 		memcpy(hdev->dev_name, name, sizeof(hdev->dev_name));
 
 		/* If this is a HCI command related to powering on the
 		 * HCI dev don't send any mgmt signals.
 		 */
-		if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
+		if (pending_find(MGMT_OP_SET_POWERED, hdev))
 			return;
 	}
 
-	mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
-		   cmd ? cmd->sk : NULL);
-}
-
-void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
-				       u8 *rand192, u8 *hash256, u8 *rand256,
-				       u8 status)
-{
-	struct pending_cmd *cmd;
-
-	BT_DBG("%s status %u", hdev->name, status);
-
-	cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
-	if (!cmd)
-		return;
-
-	if (status) {
-		cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
-			   mgmt_status(status));
-	} else {
-		if (bredr_sc_enabled(hdev) && hash256 && rand256) {
-			struct mgmt_rp_read_local_oob_ext_data rp;
-
-			memcpy(rp.hash192, hash192, sizeof(rp.hash192));
-			memcpy(rp.rand192, rand192, sizeof(rp.rand192));
-
-			memcpy(rp.hash256, hash256, sizeof(rp.hash256));
-			memcpy(rp.rand256, rand256, sizeof(rp.rand256));
-
-			cmd_complete(cmd->sk, hdev->id,
-				     MGMT_OP_READ_LOCAL_OOB_DATA, 0,
-				     &rp, sizeof(rp));
-		} else {
-			struct mgmt_rp_read_local_oob_data rp;
-
-			memcpy(rp.hash, hash192, sizeof(rp.hash));
-			memcpy(rp.rand, rand192, sizeof(rp.rand));
-
-			cmd_complete(cmd->sk, hdev->id,
-				     MGMT_OP_READ_LOCAL_OOB_DATA, 0,
-				     &rp, sizeof(rp));
-		}
-	}
-
-	mgmt_pending_remove(cmd);
+	mgmt_generic_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
+			   cmd ? cmd->sk : NULL);
 }
 
 static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16])
@@ -7018,14 +8214,73 @@
 	return false;
 }
 
+static void restart_le_scan(struct hci_dev *hdev)
+{
+	/* If controller is not scanning we are done. */
+	if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
+		return;
+
+	if (time_after(jiffies + DISCOV_LE_RESTART_DELAY,
+		       hdev->discovery.scan_start +
+		       hdev->discovery.scan_duration))
+		return;
+
+	queue_delayed_work(hdev->workqueue, &hdev->le_scan_restart,
+			   DISCOV_LE_RESTART_DELAY);
+}
+
+static bool is_filter_match(struct hci_dev *hdev, s8 rssi, u8 *eir,
+			    u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len)
+{
+	/* If a RSSI threshold has been specified, and
+	 * HCI_QUIRK_STRICT_DUPLICATE_FILTER is not set, then all results with
+	 * a RSSI smaller than the RSSI threshold will be dropped. If the quirk
+	 * is set, let it through for further processing, as we might need to
+	 * restart the scan.
+	 *
+	 * For BR/EDR devices (pre 1.2) providing no RSSI during inquiry,
+	 * the results are also dropped.
+	 */
+	if (hdev->discovery.rssi != HCI_RSSI_INVALID &&
+	    (rssi == HCI_RSSI_INVALID ||
+	    (rssi < hdev->discovery.rssi &&
+	     !test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks))))
+		return  false;
+
+	if (hdev->discovery.uuid_count != 0) {
+		/* If a list of UUIDs is provided in filter, results with no
+		 * matching UUID should be dropped.
+		 */
+		if (!eir_has_uuids(eir, eir_len, hdev->discovery.uuid_count,
+				   hdev->discovery.uuids) &&
+		    !eir_has_uuids(scan_rsp, scan_rsp_len,
+				   hdev->discovery.uuid_count,
+				   hdev->discovery.uuids))
+			return false;
+	}
+
+	/* If duplicate filtering does not report RSSI changes, then restart
+	 * scanning to ensure updated result with updated RSSI values.
+	 */
+	if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks)) {
+		restart_le_scan(hdev);
+
+		/* Validate RSSI value against the RSSI threshold once more. */
+		if (hdev->discovery.rssi != HCI_RSSI_INVALID &&
+		    rssi < hdev->discovery.rssi)
+			return false;
+	}
+
+	return true;
+}
+
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 		       u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
 		       u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len)
 {
 	char buf[512];
-	struct mgmt_ev_device_found *ev = (void *) buf;
+	struct mgmt_ev_device_found *ev = (void *)buf;
 	size_t ev_size;
-	bool match;
 
 	/* Don't send events for a non-kernel initiated discovery. With
 	 * LE one exception is if we have pend_le_reports > 0 in which
@@ -7038,17 +8293,12 @@
 			return;
 	}
 
-	/* When using service discovery with a RSSI threshold, then check
-	 * if such a RSSI threshold is specified. If a RSSI threshold has
-	 * been specified, then all results with a RSSI smaller than the
-	 * RSSI threshold will be dropped.
-	 *
-	 * For BR/EDR devices (pre 1.2) providing no RSSI during inquiry,
-	 * the results are also dropped.
-	 */
-	if (hdev->discovery.rssi != HCI_RSSI_INVALID &&
-	    (rssi < hdev->discovery.rssi || rssi == HCI_RSSI_INVALID))
-		return;
+	if (hdev->discovery.result_filtering) {
+		/* We are using service discovery */
+		if (!is_filter_match(hdev, rssi, eir, eir_len, scan_rsp,
+				     scan_rsp_len))
+			return;
+	}
 
 	/* Make sure that the buffer is big enough. The 5 extra bytes
 	 * are for the potential CoD field.
@@ -7066,7 +8316,8 @@
 	 * However when using service discovery, the value 127 will be
 	 * returned when the RSSI is not available.
 	 */
-	if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi)
+	if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi &&
+	    link_type == ACL_LINK)
 		rssi = 0;
 
 	bacpy(&ev->addr.bdaddr, bdaddr);
@@ -7074,63 +8325,17 @@
 	ev->rssi = rssi;
 	ev->flags = cpu_to_le32(flags);
 
-	if (eir_len > 0) {
-		/* When using service discovery and a list of UUID is
-		 * provided, results with no matching UUID should be
-		 * dropped. In case there is a match the result is
-		 * kept and checking possible scan response data
-		 * will be skipped.
-		 */
-		if (hdev->discovery.uuid_count > 0)
-			match = eir_has_uuids(eir, eir_len,
-					      hdev->discovery.uuid_count,
-					      hdev->discovery.uuids);
-		else
-			match = true;
-
-		if (!match && !scan_rsp_len)
-			return;
-
+	if (eir_len > 0)
 		/* Copy EIR or advertising data into event */
 		memcpy(ev->eir, eir, eir_len);
-	} else {
-		/* When using service discovery and a list of UUID is
-		 * provided, results with empty EIR or advertising data
-		 * should be dropped since they do not match any UUID.
-		 */
-		if (hdev->discovery.uuid_count > 0 && !scan_rsp_len)
-			return;
-
-		match = false;
-	}
 
 	if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
 		eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
 					  dev_class, 3);
 
-	if (scan_rsp_len > 0) {
-		/* When using service discovery and a list of UUID is
-		 * provided, results with no matching UUID should be
-		 * dropped if there is no previous match from the
-		 * advertising data.
-		 */
-		if (hdev->discovery.uuid_count > 0) {
-			if (!match && !eir_has_uuids(scan_rsp, scan_rsp_len,
-						     hdev->discovery.uuid_count,
-						     hdev->discovery.uuids))
-				return;
-		}
-
+	if (scan_rsp_len > 0)
 		/* Append scan response data to event */
 		memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len);
-	} else {
-		/* When using service discovery and a list of UUID is
-		 * provided, results with empty scan response and no
-		 * previous matched advertising data should be dropped.
-		 */
-		if (hdev->discovery.uuid_count > 0 && !match)
-			return;
-	}
 
 	ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
 	ev_size = sizeof(*ev) + eir_len + scan_rsp_len;
@@ -7174,7 +8379,7 @@
 	mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
 }
 
-static void adv_enable_complete(struct hci_dev *hdev, u8 status)
+static void adv_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
 	BT_DBG("%s status %u", hdev->name, status);
 }
@@ -7183,10 +8388,28 @@
 {
 	struct hci_request req;
 
-	if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
+	    !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
 		return;
 
 	hci_req_init(&req, hdev);
 	enable_advertising(&req);
 	hci_req_run(&req, adv_enable_complete);
 }
+
+static struct hci_mgmt_chan chan = {
+	.channel	= HCI_CHANNEL_CONTROL,
+	.handler_count	= ARRAY_SIZE(mgmt_handlers),
+	.handlers	= mgmt_handlers,
+	.hdev_init	= mgmt_init_hdev,
+};
+
+int mgmt_init(void)
+{
+	return hci_mgmt_chan_register(&chan);
+}
+
+void mgmt_exit(void)
+{
+	hci_mgmt_chan_unregister(&chan);
+}
diff --git a/net/bluetooth/mgmt_util.c b/net/bluetooth/mgmt_util.c
new file mode 100644
index 0000000..8c30c7e
--- /dev/null
+++ b/net/bluetooth/mgmt_util.c
@@ -0,0 +1,210 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+
+   Copyright (C) 2015  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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL 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.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/mgmt.h>
+
+#include "mgmt_util.h"
+
+int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
+		    void *data, u16 data_len, int flag, struct sock *skip_sk)
+{
+	struct sk_buff *skb;
+	struct mgmt_hdr *hdr;
+
+	skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	hdr = (void *) skb_put(skb, sizeof(*hdr));
+	hdr->opcode = cpu_to_le16(event);
+	if (hdev)
+		hdr->index = cpu_to_le16(hdev->id);
+	else
+		hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
+	hdr->len = cpu_to_le16(data_len);
+
+	if (data)
+		memcpy(skb_put(skb, data_len), data, data_len);
+
+	/* Time stamp */
+	__net_timestamp(skb);
+
+	hci_send_to_channel(channel, skb, flag, skip_sk);
+	kfree_skb(skb);
+
+	return 0;
+}
+
+int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
+{
+	struct sk_buff *skb;
+	struct mgmt_hdr *hdr;
+	struct mgmt_ev_cmd_status *ev;
+	int err;
+
+	BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
+
+	skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	hdr = (void *) skb_put(skb, sizeof(*hdr));
+
+	hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
+	hdr->index = cpu_to_le16(index);
+	hdr->len = cpu_to_le16(sizeof(*ev));
+
+	ev = (void *) skb_put(skb, sizeof(*ev));
+	ev->status = status;
+	ev->opcode = cpu_to_le16(cmd);
+
+	err = sock_queue_rcv_skb(sk, skb);
+	if (err < 0)
+		kfree_skb(skb);
+
+	return err;
+}
+
+int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
+		      void *rp, size_t rp_len)
+{
+	struct sk_buff *skb;
+	struct mgmt_hdr *hdr;
+	struct mgmt_ev_cmd_complete *ev;
+	int err;
+
+	BT_DBG("sock %p", sk);
+
+	skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	hdr = (void *) skb_put(skb, sizeof(*hdr));
+
+	hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
+	hdr->index = cpu_to_le16(index);
+	hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
+
+	ev = (void *) skb_put(skb, sizeof(*ev) + rp_len);
+	ev->opcode = cpu_to_le16(cmd);
+	ev->status = status;
+
+	if (rp)
+		memcpy(ev->data, rp, rp_len);
+
+	err = sock_queue_rcv_skb(sk, skb);
+	if (err < 0)
+		kfree_skb(skb);
+
+	return err;
+}
+
+struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
+					   struct hci_dev *hdev)
+{
+	struct mgmt_pending_cmd *cmd;
+
+	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+		if (hci_sock_get_channel(cmd->sk) != channel)
+			continue;
+		if (cmd->opcode == opcode)
+			return cmd;
+	}
+
+	return NULL;
+}
+
+struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel,
+						u16 opcode,
+						struct hci_dev *hdev,
+						const void *data)
+{
+	struct mgmt_pending_cmd *cmd;
+
+	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+		if (cmd->user_data != data)
+			continue;
+		if (cmd->opcode == opcode)
+			return cmd;
+	}
+
+	return NULL;
+}
+
+void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
+			  void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
+			  void *data)
+{
+	struct mgmt_pending_cmd *cmd, *tmp;
+
+	list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
+		if (opcode > 0 && cmd->opcode != opcode)
+			continue;
+
+		cb(cmd, data);
+	}
+}
+
+struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
+					  struct hci_dev *hdev,
+					  void *data, u16 len)
+{
+	struct mgmt_pending_cmd *cmd;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (!cmd)
+		return NULL;
+
+	cmd->opcode = opcode;
+	cmd->index = hdev->id;
+
+	cmd->param = kmemdup(data, len, GFP_KERNEL);
+	if (!cmd->param) {
+		kfree(cmd);
+		return NULL;
+	}
+
+	cmd->param_len = len;
+
+	cmd->sk = sk;
+	sock_hold(sk);
+
+	list_add(&cmd->list, &hdev->mgmt_pending);
+
+	return cmd;
+}
+
+void mgmt_pending_free(struct mgmt_pending_cmd *cmd)
+{
+	sock_put(cmd->sk);
+	kfree(cmd->param);
+	kfree(cmd);
+}
+
+void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
+{
+	list_del(&cmd->list);
+	mgmt_pending_free(cmd);
+}
diff --git a/net/bluetooth/mgmt_util.h b/net/bluetooth/mgmt_util.h
new file mode 100644
index 0000000..6559f18
--- /dev/null
+++ b/net/bluetooth/mgmt_util.h
@@ -0,0 +1,53 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2015  Intel Coropration
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL 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.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
+struct mgmt_pending_cmd {
+	struct list_head list;
+	u16 opcode;
+	int index;
+	void *param;
+	size_t param_len;
+	struct sock *sk;
+	void *user_data;
+	int (*cmd_complete)(struct mgmt_pending_cmd *cmd, u8 status);
+};
+
+int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
+		    void *data, u16 data_len, int flag, struct sock *skip_sk);
+int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status);
+int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
+		      void *rp, size_t rp_len);
+
+struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
+					   struct hci_dev *hdev);
+struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel,
+						u16 opcode,
+						struct hci_dev *hdev,
+						const void *data);
+void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
+			  void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
+			  void *data);
+struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
+					  struct hci_dev *hdev,
+					  void *data, u16 len);
+void mgmt_pending_free(struct mgmt_pending_cmd *cmd);
+void mgmt_pending_remove(struct mgmt_pending_cmd *cmd);
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 8b5b653..c340905 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -193,10 +193,12 @@
 	BT_DBG("%p", sk);
 	rfcomm_schedule();
 }
-
-static void backport_rfcomm_l2data_ready(struct sock *sk, int unused) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)
+static void backport_rfcomm_l2data_ready(struct sock *sk, int unused)
+{
 	rfcomm_l2data_ready(sk);
 }
+#endif
 
 static int rfcomm_l2sock_create(struct socket **sock)
 {
@@ -779,7 +781,7 @@
 
 	bacpy(&addr.l2_bdaddr, dst);
 	addr.l2_family = AF_BLUETOOTH;
-	addr.l2_psm    = cpu_to_le16(RFCOMM_PSM);
+	addr.l2_psm    = cpu_to_le16(L2CAP_PSM_RFCOMM);
 	addr.l2_cid    = 0;
 	addr.l2_bdaddr_type = BDADDR_BREDR;
 	*err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
@@ -2050,7 +2052,7 @@
 	/* Bind socket */
 	bacpy(&addr.l2_bdaddr, ba);
 	addr.l2_family = AF_BLUETOOTH;
-	addr.l2_psm    = cpu_to_le16(RFCOMM_PSM);
+	addr.l2_psm    = cpu_to_le16(L2CAP_PSM_RFCOMM);
 	addr.l2_cid    = 0;
 	addr.l2_bdaddr_type = BDADDR_BREDR;
 	err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index acaac1b..0eec473 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -476,7 +476,7 @@
 
 static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-	DECLARE_WAITQUEUE(wait, current);
+	DEFINE_WAIT_FUNC(wait, woken_wake_function);
 	struct sock *sk = sock->sk, *nsk;
 	long timeo;
 	int err = 0;
@@ -495,8 +495,6 @@
 	/* Wait for an incoming connection. (wake-one). */
 	add_wait_queue_exclusive(sk_sleep(sk), &wait);
 	while (1) {
-		set_current_state(TASK_INTERRUPTIBLE);
-
 		if (sk->sk_state != BT_LISTEN) {
 			err = -EBADFD;
 			break;
@@ -517,10 +515,11 @@
 		}
 
 		release_sock(sk);
-		timeo = schedule_timeout(timeo);
+
+		timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
+
 		lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
 	}
-	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(sk_sleep(sk), &wait);
 
 	if (err)
@@ -558,8 +557,13 @@
 	return 0;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg,
+			       size_t len)
+#else
 static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 			       struct msghdr *msg, size_t len)
+#endif
 {
 	struct sock *sk = sock->sk;
 	struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
@@ -624,8 +628,13 @@
 	return sent;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int rfcomm_sock_recvmsg(struct socket *sock, struct msghdr *msg,
+			       size_t size, int flags)
+#else
 static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 			       struct msghdr *msg, size_t size, int flags)
+#endif
 {
 	struct sock *sk = sock->sk;
 	struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
@@ -636,7 +645,11 @@
 		return 0;
 	}
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+	len = bt_sock_stream_recvmsg(sock, msg, size, flags);
+#else
 	len = bt_sock_stream_recvmsg(iocb, sock, msg, size, flags);
+#endif
 
 	lock_sock(sk);
 	if (!(flags & MSG_PEEK) && len > 0)
@@ -1066,6 +1079,8 @@
 {
 	int err;
 
+	BUILD_BUG_ON(sizeof(struct sockaddr_rc) > sizeof(struct sockaddr));
+
 	err = proto_register(&rfcomm_proto, 0);
 	if (err < 0)
 		return err;
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 73bc2c3..e47f08b 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -618,7 +618,7 @@
 
 static int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-	DECLARE_WAITQUEUE(wait, current);
+	DEFINE_WAIT_FUNC(wait, woken_wake_function);
 	struct sock *sk = sock->sk, *ch;
 	long timeo;
 	int err = 0;
@@ -632,8 +632,6 @@
 	/* Wait for an incoming connection. (wake-one). */
 	add_wait_queue_exclusive(sk_sleep(sk), &wait);
 	while (1) {
-		set_current_state(TASK_INTERRUPTIBLE);
-
 		if (sk->sk_state != BT_LISTEN) {
 			err = -EBADFD;
 			break;
@@ -654,10 +652,10 @@
 		}
 
 		release_sock(sk);
-		timeo = schedule_timeout(timeo);
+
+		timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
 		lock_sock(sk);
 	}
-	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(sk_sleep(sk), &wait);
 
 	if (err)
@@ -690,8 +688,13 @@
 	return 0;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg,
+			    size_t len)
+#else
 static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 			    struct msghdr *msg, size_t len)
+#endif
 {
 	struct sock *sk = sock->sk;
 	int err;
@@ -760,8 +763,13 @@
 	}
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int sco_sock_recvmsg(struct socket *sock, struct msghdr *msg,
+			    size_t len, int flags)
+#else
 static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 			    struct msghdr *msg, size_t len, int flags)
+#endif
 {
 	struct sock *sk = sock->sk;
 	struct sco_pinfo *pi = sco_pi(sk);
@@ -779,7 +787,11 @@
 
 	release_sock(sk);
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+	return bt_sock_recvmsg(sock, msg, len, flags);
+#else
 	return bt_sock_recvmsg(iocb, sock, msg, len, flags);
+#endif
 }
 
 static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
@@ -1089,9 +1101,13 @@
 	return lm;
 }
 
-void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
+static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
 {
+	if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
+		return;
+
 	BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
+
 	if (!status) {
 		struct sco_conn *conn;
 
@@ -1102,8 +1118,11 @@
 		sco_conn_del(hcon, bt_to_errno(status));
 }
 
-void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
+static void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
 {
+	if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
+		return;
+
 	BT_DBG("hcon %p reason %d", hcon, reason);
 
 	sco_conn_del(hcon, bt_to_errno(reason));
@@ -1128,6 +1147,12 @@
 	return 0;
 }
 
+static struct hci_cb sco_cb = {
+	.name		= "SCO",
+	.connect_cfm	= sco_connect_cfm,
+	.disconn_cfm	= sco_disconn_cfm,
+};
+
 static int sco_debugfs_show(struct seq_file *f, void *p)
 {
 	struct sock *sk;
@@ -1188,6 +1213,8 @@
 {
 	int err;
 
+	BUILD_BUG_ON(sizeof(struct sockaddr_sco) > sizeof(struct sockaddr));
+
 	err = proto_register(&sco_proto, 0);
 	if (err < 0)
 		return err;
@@ -1207,6 +1234,8 @@
 
 	BT_INFO("SCO socket layer initialized");
 
+	hci_register_cb(&sco_cb);
+
 	if (IS_ERR_OR_NULL(bt_debugfs))
 		return 0;
 
@@ -1220,12 +1249,14 @@
 	return err;
 }
 
-void __exit sco_exit(void)
+void sco_exit(void)
 {
 	bt_procfs_cleanup(&init_net, "sco");
 
 	debugfs_remove(sco_debugfs);
 
+	hci_unregister_cb(&sco_cb);
+
 	bt_sock_unregister(BTPROTO_SCO);
 
 	proto_unregister(&sco_proto);
diff --git a/net/bluetooth/selftest.c b/net/bluetooth/selftest.c
new file mode 100644
index 0000000..0773c00
--- /dev/null
+++ b/net/bluetooth/selftest.c
@@ -0,0 +1,271 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL 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.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/debugfs.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "ecc.h"
+#include "smp.h"
+#include "selftest.h"
+
+#if IS_ENABLED(CPTCFG_BT_SELFTEST_ECDH)
+
+static const u8 priv_a_1[32] __initconst = {
+	0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
+	0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
+	0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
+	0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
+};
+static const u8 priv_b_1[32] __initconst = {
+	0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
+	0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
+	0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
+	0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55,
+};
+static const u8 pub_a_1[64] __initconst = {
+	0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
+	0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
+	0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
+	0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
+
+	0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
+	0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
+	0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
+	0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
+};
+static const u8 pub_b_1[64] __initconst = {
+	0x90, 0xa1, 0xaa, 0x2f, 0xb2, 0x77, 0x90, 0x55,
+	0x9f, 0xa6, 0x15, 0x86, 0xfd, 0x8a, 0xb5, 0x47,
+	0x00, 0x4c, 0x9e, 0xf1, 0x84, 0x22, 0x59, 0x09,
+	0x96, 0x1d, 0xaf, 0x1f, 0xf0, 0xf0, 0xa1, 0x1e,
+
+	0x4a, 0x21, 0xb1, 0x15, 0xf9, 0xaf, 0x89, 0x5f,
+	0x76, 0x36, 0x8e, 0xe2, 0x30, 0x11, 0x2d, 0x47,
+	0x60, 0x51, 0xb8, 0x9a, 0x3a, 0x70, 0x56, 0x73,
+	0x37, 0xad, 0x9d, 0x42, 0x3e, 0xf3, 0x55, 0x4c,
+};
+static const u8 dhkey_1[32] __initconst = {
+	0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86,
+	0xf1, 0x66, 0xf8, 0xb4, 0x13, 0x6b, 0x79, 0x99,
+	0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
+	0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec,
+};
+
+static const u8 priv_a_2[32] __initconst = {
+	0x63, 0x76, 0x45, 0xd0, 0xf7, 0x73, 0xac, 0xb7,
+	0xff, 0xdd, 0x03, 0x72, 0xb9, 0x72, 0x85, 0xb4,
+	0x41, 0xb6, 0x5d, 0x0c, 0x5d, 0x54, 0x84, 0x60,
+	0x1a, 0xa3, 0x9a, 0x3c, 0x69, 0x16, 0xa5, 0x06,
+};
+static const u8 priv_b_2[32] __initconst = {
+	0xba, 0x30, 0x55, 0x50, 0x19, 0xa2, 0xca, 0xa3,
+	0xa5, 0x29, 0x08, 0xc6, 0xb5, 0x03, 0x88, 0x7e,
+	0x03, 0x2b, 0x50, 0x73, 0xd4, 0x2e, 0x50, 0x97,
+	0x64, 0xcd, 0x72, 0x0d, 0x67, 0xa0, 0x9a, 0x52,
+};
+static const u8 pub_a_2[64] __initconst = {
+	0xdd, 0x78, 0x5c, 0x74, 0x03, 0x9b, 0x7e, 0x98,
+	0xcb, 0x94, 0x87, 0x4a, 0xad, 0xfa, 0xf8, 0xd5,
+	0x43, 0x3e, 0x5c, 0xaf, 0xea, 0xb5, 0x4c, 0xf4,
+	0x9e, 0x80, 0x79, 0x57, 0x7b, 0xa4, 0x31, 0x2c,
+
+	0x4f, 0x5d, 0x71, 0x43, 0x77, 0x43, 0xf8, 0xea,
+	0xd4, 0x3e, 0xbd, 0x17, 0x91, 0x10, 0x21, 0xd0,
+	0x1f, 0x87, 0x43, 0x8e, 0x40, 0xe2, 0x52, 0xcd,
+	0xbe, 0xdf, 0x98, 0x38, 0x18, 0x12, 0x95, 0x91,
+};
+static const u8 pub_b_2[64] __initconst = {
+	0xcc, 0x00, 0x65, 0xe1, 0xf5, 0x6c, 0x0d, 0xcf,
+	0xec, 0x96, 0x47, 0x20, 0x66, 0xc9, 0xdb, 0x84,
+	0x81, 0x75, 0xa8, 0x4d, 0xc0, 0xdf, 0xc7, 0x9d,
+	0x1b, 0x3f, 0x3d, 0xf2, 0x3f, 0xe4, 0x65, 0xf4,
+
+	0x79, 0xb2, 0xec, 0xd8, 0xca, 0x55, 0xa1, 0xa8,
+	0x43, 0x4d, 0x6b, 0xca, 0x10, 0xb0, 0xc2, 0x01,
+	0xc2, 0x33, 0x4e, 0x16, 0x24, 0xc4, 0xef, 0xee,
+	0x99, 0xd8, 0xbb, 0xbc, 0x48, 0xd0, 0x01, 0x02,
+};
+static const u8 dhkey_2[32] __initconst = {
+	0x69, 0xeb, 0x21, 0x32, 0xf2, 0xc6, 0x05, 0x41,
+	0x60, 0x19, 0xcd, 0x5e, 0x94, 0xe1, 0xe6, 0x5f,
+	0x33, 0x07, 0xe3, 0x38, 0x4b, 0x68, 0xe5, 0x62,
+	0x3f, 0x88, 0x6d, 0x2f, 0x3a, 0x84, 0x85, 0xab,
+};
+
+static const u8 priv_a_3[32] __initconst = {
+	0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
+	0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
+	0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
+	0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
+};
+static const u8 pub_a_3[64] __initconst = {
+	0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
+	0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
+	0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
+	0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
+
+	0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
+	0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
+	0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
+	0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
+};
+static const u8 dhkey_3[32] __initconst = {
+	0x2d, 0xab, 0x00, 0x48, 0xcb, 0xb3, 0x7b, 0xda,
+	0x55, 0x7b, 0x8b, 0x72, 0xa8, 0x57, 0x87, 0xc3,
+	0x87, 0x27, 0x99, 0x32, 0xfc, 0x79, 0x5f, 0xae,
+	0x7c, 0x1c, 0xf9, 0x49, 0xe6, 0xd7, 0xaa, 0x70,
+};
+
+static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32],
+				   const u8 pub_a[64], const u8 pub_b[64],
+				   const u8 dhkey[32])
+{
+	u8 dhkey_a[32], dhkey_b[32];
+
+	ecdh_shared_secret(pub_b, priv_a, dhkey_a);
+	ecdh_shared_secret(pub_a, priv_b, dhkey_b);
+
+	if (memcmp(dhkey_a, dhkey, 32))
+		return -EINVAL;
+
+	if (memcmp(dhkey_b, dhkey, 32))
+		return -EINVAL;
+
+	return 0;
+}
+
+static char test_ecdh_buffer[32];
+
+static ssize_t test_ecdh_read(struct file *file, char __user *user_buf,
+			      size_t count, loff_t *ppos)
+{
+	return simple_read_from_buffer(user_buf, count, ppos, test_ecdh_buffer,
+				       strlen(test_ecdh_buffer));
+}
+
+static const struct file_operations test_ecdh_fops = {
+	.open		= simple_open,
+	.read		= test_ecdh_read,
+	.llseek		= default_llseek,
+};
+
+static int __init test_ecdh(void)
+{
+	ktime_t calltime, delta, rettime;
+	unsigned long long duration;
+	int err;
+
+	calltime = ktime_get();
+
+	err = test_ecdh_sample(priv_a_1, priv_b_1, pub_a_1, pub_b_1, dhkey_1);
+	if (err) {
+		BT_ERR("ECDH sample 1 failed");
+		goto done;
+	}
+
+	err = test_ecdh_sample(priv_a_2, priv_b_2, pub_a_2, pub_b_2, dhkey_2);
+	if (err) {
+		BT_ERR("ECDH sample 2 failed");
+		goto done;
+	}
+
+	err = test_ecdh_sample(priv_a_3, priv_a_3, pub_a_3, pub_a_3, dhkey_3);
+	if (err) {
+		BT_ERR("ECDH sample 3 failed");
+		goto done;
+	}
+
+	rettime = ktime_get();
+	delta = ktime_sub(rettime, calltime);
+	duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+
+	BT_INFO("ECDH test passed in %llu usecs", duration);
+
+done:
+	if (!err)
+		snprintf(test_ecdh_buffer, sizeof(test_ecdh_buffer),
+			 "PASS (%llu usecs)\n", duration);
+	else
+		snprintf(test_ecdh_buffer, sizeof(test_ecdh_buffer), "FAIL\n");
+
+	debugfs_create_file("selftest_ecdh", 0444, bt_debugfs, NULL,
+			    &test_ecdh_fops);
+
+	return err;
+}
+
+#else
+
+static inline int test_ecdh(void)
+{
+	return 0;
+}
+
+#endif
+
+static int __init run_selftest(void)
+{
+	int err;
+
+	BT_INFO("Starting self testing");
+
+	err = test_ecdh();
+	if (err)
+		goto done;
+
+	err = bt_selftest_smp();
+
+done:
+	BT_INFO("Finished self testing");
+
+	return err;
+}
+
+#if IS_MODULE(CPTCFG_BT)
+
+/* This is run when CPTCFG_BT_SELFTEST=y and CPTCFG_BT=m and is just a
+ * wrapper to allow running this at module init.
+ *
+ * If CPTCFG_BT_SELFTEST=n, then this code is not compiled at all.
+ */
+int __init bt_selftest(void)
+{
+	return run_selftest();
+}
+
+#else
+
+/* This is run when CPTCFG_BT_SELFTEST=y and CPTCFG_BT=y and is run
+ * via late_initcall() as last item in the initialization sequence.
+ *
+ * If CPTCFG_BT_SELFTEST=n, then this code is not compiled at all.
+ */
+static int __init bt_selftest_init(void)
+{
+	return run_selftest();
+}
+late_initcall(bt_selftest_init);
+
+#endif
diff --git a/net/bluetooth/selftest.h b/net/bluetooth/selftest.h
new file mode 100644
index 0000000..733c74d
--- /dev/null
+++ b/net/bluetooth/selftest.h
@@ -0,0 +1,45 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL 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.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#if IS_ENABLED(CPTCFG_BT_SELFTEST) && IS_MODULE(CPTCFG_BT)
+
+/* When CPTCFG_BT_SELFTEST=y and the CPTCFG_BT=m, then the self testing
+ * is run at module loading time.
+ */
+int bt_selftest(void);
+
+#else
+
+/* When CPTCFG_BT_SELFTEST=y and CPTCFG_BT=y, then the self testing
+ * is run via late_initcall() to make sure that subsys_initcall() of
+ * the Bluetooth subsystem and device_initcall() of the Crypto subsystem
+ * do not clash.
+ *
+ * When CPTCFG_BT_SELFTEST=n, then this turns into an empty call that
+ * has no impact.
+ */
+static inline int bt_selftest(void)
+{
+	return 0;
+}
+
+#endif
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index ef0de39..2f48da5 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -20,6 +20,7 @@
    SOFTWARE IS DISCLAIMED.
 */
 
+#include <linux/debugfs.h>
 #include <linux/crypto.h>
 #include <linux/scatterlist.h>
 #include <crypto/b128ops.h>
@@ -51,7 +52,7 @@
 
 #define SMP_TIMEOUT	msecs_to_jiffies(30000)
 
-#define AUTH_REQ_MASK(dev)	(test_bit(HCI_SC_ENABLED, &(dev)->dev_flags) ? \
+#define AUTH_REQ_MASK(dev)	(hci_dev_test_flag(dev, HCI_SC_ENABLED) ? \
 				 0x1f : 0x07)
 #define KEY_DIST_MASK		0x07
 
@@ -69,7 +70,19 @@
 	SMP_FLAG_DEBUG_KEY,
 	SMP_FLAG_WAIT_USER,
 	SMP_FLAG_DHKEY_PENDING,
-	SMP_FLAG_OOB,
+	SMP_FLAG_REMOTE_OOB,
+	SMP_FLAG_LOCAL_OOB,
+};
+
+struct smp_dev {
+	/* Secure Connections OOB data */
+	u8			local_pk[64];
+	u8			local_sk[32];
+	u8			local_rand[16];
+	bool			debug_key;
+
+	struct crypto_blkcipher	*tfm_aes;
+	struct crypto_hash	*tfm_cmac;
 };
 
 struct smp_chan {
@@ -83,7 +96,8 @@
 	u8		rrnd[16]; /* SMP Pairing Random (remote) */
 	u8		pcnf[16]; /* SMP Pairing Confirm */
 	u8		tk[16]; /* SMP Temporary Key */
-	u8		rr[16];
+	u8		rr[16]; /* Remote OOB ra/rb value */
+	u8		lr[16]; /* Local OOB ra/rb value */
 	u8		enc_key_size;
 	u8		remote_key_dist;
 	bdaddr_t	id_addr;
@@ -223,8 +237,9 @@
 	return err;
 }
 
-static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16],
-		  u8 a1[7], u8 a2[7], u8 mackey[16], u8 ltk[16])
+static int smp_f5(struct crypto_hash *tfm_cmac, const u8 w[32],
+		  const u8 n1[16], const u8 n2[16], const u8 a1[7],
+		  const u8 a2[7], u8 mackey[16], u8 ltk[16])
 {
 	/* The btle, salt and length "magic" values are as defined in
 	 * the SMP section of the Bluetooth core specification. In ASCII
@@ -276,7 +291,7 @@
 }
 
 static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16],
-		  const u8 n1[16], u8 n2[16], const u8 r[16],
+		  const u8 n1[16], const u8 n2[16], const u8 r[16],
 		  const u8 io_cap[3], const u8 a1[7], const u8 a2[7],
 		  u8 res[16])
 {
@@ -298,7 +313,7 @@
 	if (err)
 		return err;
 
-	BT_DBG("res %16phN", res);
+	SMP_DBG("res %16phN", res);
 
 	return err;
 }
@@ -476,18 +491,18 @@
 		     const bdaddr_t *bdaddr)
 {
 	struct l2cap_chan *chan = hdev->smp_data;
-	struct crypto_blkcipher *tfm;
+	struct smp_dev *smp;
 	u8 hash[3];
 	int err;
 
 	if (!chan || !chan->data)
 		return false;
 
-	tfm = chan->data;
+	smp = chan->data;
 
 	BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk);
 
-	err = smp_ah(tfm, irk, &bdaddr->b[3], hash);
+	err = smp_ah(smp->tfm_aes, irk, &bdaddr->b[3], hash);
 	if (err)
 		return false;
 
@@ -497,20 +512,20 @@
 int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa)
 {
 	struct l2cap_chan *chan = hdev->smp_data;
-	struct crypto_blkcipher *tfm;
+	struct smp_dev *smp;
 	int err;
 
 	if (!chan || !chan->data)
 		return -EOPNOTSUPP;
 
-	tfm = chan->data;
+	smp = chan->data;
 
 	get_random_bytes(&rpa->b[3], 3);
 
 	rpa->b[5] &= 0x3f;	/* Clear two most significant bits */
 	rpa->b[5] |= 0x40;	/* Set second most significant bit */
 
-	err = smp_ah(tfm, irk, &rpa->b[3], rpa->b);
+	err = smp_ah(smp->tfm_aes, irk, &rpa->b[3], rpa->b);
 	if (err < 0)
 		return err;
 
@@ -519,6 +534,53 @@
 	return 0;
 }
 
+int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16])
+{
+	struct l2cap_chan *chan = hdev->smp_data;
+	struct smp_dev *smp;
+	int err;
+
+	if (!chan || !chan->data)
+		return -EOPNOTSUPP;
+
+	smp = chan->data;
+
+	if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) {
+		BT_DBG("Using debug keys");
+		memcpy(smp->local_pk, debug_pk, 64);
+		memcpy(smp->local_sk, debug_sk, 32);
+		smp->debug_key = true;
+	} else {
+		while (true) {
+			/* Generate local key pair for Secure Connections */
+			if (!ecc_make_key(smp->local_pk, smp->local_sk))
+				return -EIO;
+
+			/* This is unlikely, but we need to check that
+			 * we didn't accidentially generate a debug key.
+			 */
+			if (memcmp(smp->local_sk, debug_sk, 32))
+				break;
+		}
+		smp->debug_key = false;
+	}
+
+	SMP_DBG("OOB Public Key X: %32phN", smp->local_pk);
+	SMP_DBG("OOB Public Key Y: %32phN", smp->local_pk + 32);
+	SMP_DBG("OOB Private Key:  %32phN", smp->local_sk);
+
+	get_random_bytes(smp->local_rand, 16);
+
+	err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->local_pk,
+		     smp->local_rand, 0, hash);
+	if (err < 0)
+		return err;
+
+	memcpy(rand, smp->local_rand, 16);
+
+	return 0;
+}
+
 static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
 {
 	struct l2cap_chan *chan = conn->smp;
@@ -592,7 +654,7 @@
 	struct hci_dev *hdev = hcon->hdev;
 	u8 local_dist = 0, remote_dist = 0, oob_flag = SMP_OOB_NOT_PRESENT;
 
-	if (test_bit(HCI_BONDABLE, &conn->hcon->hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_BONDABLE)) {
 		local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
 		remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
 		authreq |= SMP_AUTH_BONDING;
@@ -600,18 +662,18 @@
 		authreq &= ~SMP_AUTH_BONDING;
 	}
 
-	if (test_bit(HCI_RPA_RESOLVING, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_RPA_RESOLVING))
 		remote_dist |= SMP_DIST_ID_KEY;
 
-	if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_PRIVACY))
 		local_dist |= SMP_DIST_ID_KEY;
 
-	if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
+	if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
 	    (authreq & SMP_AUTH_SC)) {
 		struct oob_data *oob_data;
 		u8 bdaddr_type;
 
-		if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+		if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) {
 			local_dist |= SMP_DIST_LINK_KEY;
 			remote_dist |= SMP_DIST_LINK_KEY;
 		}
@@ -623,11 +685,13 @@
 
 		oob_data = hci_find_remote_oob_data(hdev, &hcon->dst,
 						    bdaddr_type);
-		if (oob_data) {
-			set_bit(SMP_FLAG_OOB, &smp->flags);
+		if (oob_data && oob_data->present) {
+			set_bit(SMP_FLAG_REMOTE_OOB, &smp->flags);
 			oob_flag = SMP_OOB_PRESENT;
 			memcpy(smp->rr, oob_data->rand256, 16);
 			memcpy(smp->pcnf, oob_data->hash256, 16);
+			SMP_DBG("OOB Remote Confirmation: %16phN", smp->pcnf);
+			SMP_DBG("OOB Remote Random: %16phN", smp->rr);
 		}
 
 	} else {
@@ -684,9 +748,9 @@
 	complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags);
 	mgmt_smp_complete(hcon, complete);
 
-	kfree(smp->csrk);
-	kfree(smp->slave_csrk);
-	kfree(smp->link_key);
+	kzfree(smp->csrk);
+	kzfree(smp->slave_csrk);
+	kzfree(smp->link_key);
 
 	crypto_free_blkcipher(smp->tfm_aes);
 	crypto_free_hash(smp->tfm_cmac);
@@ -695,7 +759,7 @@
 	 * support hasn't been explicitly enabled.
 	 */
 	if (smp->ltk && smp->ltk->type == SMP_LTK_P256_DEBUG &&
-	    !test_bit(HCI_KEEP_DEBUG_KEYS, &hcon->hdev->dev_flags)) {
+	    !hci_dev_test_flag(hcon->hdev, HCI_KEEP_DEBUG_KEYS)) {
 		list_del_rcu(&smp->ltk->list);
 		kfree_rcu(smp->ltk, rcu);
 		smp->ltk = NULL;
@@ -720,7 +784,7 @@
 	}
 
 	chan->data = NULL;
-	kfree(smp);
+	kzfree(smp);
 	hci_conn_drop(hcon);
 }
 
@@ -821,6 +885,12 @@
 		return 0;
 	}
 
+	/* If this function is used for SC -> legacy fallback we
+	 * can only recover the just-works case.
+	 */
+	if (test_bit(SMP_FLAG_SC, &smp->flags))
+		return -EINVAL;
+
 	/* Not Just Works/Confirm results in MITM Authentication */
 	if (smp->method != JUST_CFM) {
 		set_bit(SMP_FLAG_MITM_AUTH, &smp->flags);
@@ -1055,7 +1125,7 @@
 			/* Don't keep debug keys around if the relevant
 			 * flag is not set.
 			 */
-			if (!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags) &&
+			if (!hci_dev_test_flag(hdev, HCI_KEEP_DEBUG_KEYS) &&
 			    key->type == HCI_LK_DEBUG_COMBINATION) {
 				list_del_rcu(&key->list);
 				kfree_rcu(key, rcu);
@@ -1100,13 +1170,13 @@
 		return;
 
 	if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) {
-		kfree(smp->link_key);
+		kzfree(smp->link_key);
 		smp->link_key = NULL;
 		return;
 	}
 
 	if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) {
-		kfree(smp->link_key);
+		kzfree(smp->link_key);
 		smp->link_key = NULL;
 		return;
 	}
@@ -1255,7 +1325,10 @@
 
 		csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
 		if (csrk) {
-			csrk->master = 0x00;
+			if (hcon->sec_level > BT_SECURITY_MEDIUM)
+				csrk->type = MGMT_CSRK_LOCAL_AUTHENTICATED;
+			else
+				csrk->type = MGMT_CSRK_LOCAL_UNAUTHENTICATED;
 			memcpy(csrk->val, sign.csrk, sizeof(csrk->val));
 		}
 		smp->slave_csrk = csrk;
@@ -1300,7 +1373,7 @@
 	smp->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
 	if (IS_ERR(smp->tfm_aes)) {
 		BT_ERR("Unable to create ECB crypto context");
-		kfree(smp);
+		kzfree(smp);
 		return NULL;
 	}
 
@@ -1308,7 +1381,7 @@
 	if (IS_ERR(smp->tfm_cmac)) {
 		BT_ERR("Unable to create CMAC crypto context");
 		crypto_free_blkcipher(smp->tfm_aes);
-		kfree(smp);
+		kzfree(smp);
 		return NULL;
 	}
 
@@ -1604,15 +1677,15 @@
 	struct hci_dev *hdev = conn->hcon->hdev;
 	u8 local_dist = 0, remote_dist = 0;
 
-	if (test_bit(HCI_BONDABLE, &hdev->dev_flags)) {
+	if (hci_dev_test_flag(hdev, HCI_BONDABLE)) {
 		local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
 		remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
 	}
 
-	if (test_bit(HCI_RPA_RESOLVING, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_RPA_RESOLVING))
 		remote_dist |= SMP_DIST_ID_KEY;
 
-	if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
+	if (hci_dev_test_flag(hdev, HCI_PRIVACY))
 		local_dist |= SMP_DIST_ID_KEY;
 
 	if (!rsp) {
@@ -1664,22 +1737,29 @@
 	/* We didn't start the pairing, so match remote */
 	auth = req->auth_req & AUTH_REQ_MASK(hdev);
 
-	if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) &&
+	if (!hci_dev_test_flag(hdev, HCI_BONDABLE) &&
 	    (auth & SMP_AUTH_BONDING))
 		return SMP_PAIRING_NOTSUPP;
 
-	if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+	if (hci_dev_test_flag(hdev, HCI_SC_ONLY) && !(auth & SMP_AUTH_SC))
 		return SMP_AUTH_REQUIREMENTS;
 
 	smp->preq[0] = SMP_CMD_PAIRING_REQ;
 	memcpy(&smp->preq[1], req, sizeof(*req));
 	skb_pull(skb, sizeof(*req));
 
+	/* If the remote side's OOB flag is set it means it has
+	 * successfully received our local OOB data - therefore set the
+	 * flag to indicate that local OOB is in use.
+	 */
+	if (req->oob_flag == SMP_OOB_PRESENT)
+		set_bit(SMP_FLAG_LOCAL_OOB, &smp->flags);
+
 	/* SMP over BR/EDR requires special treatment */
 	if (conn->hcon->type == ACL_LINK) {
 		/* We must have a BR/EDR SC link */
 		if (!test_bit(HCI_CONN_AES_CCM, &conn->hcon->flags) &&
-		    !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+		    !hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP))
 			return SMP_CROSS_TRANSP_NOT_ALLOWED;
 
 		set_bit(SMP_FLAG_SC, &smp->flags);
@@ -1737,14 +1817,19 @@
 
 	clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
 
+	/* Strictly speaking we shouldn't allow Pairing Confirm for the
+	 * SC case, however some implementations incorrectly copy RFU auth
+	 * req bits from our security request, which may create a false
+	 * positive SC enablement.
+	 */
+	SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
 	if (test_bit(SMP_FLAG_SC, &smp->flags)) {
 		SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY);
 		/* Clear bits which are generated but not distributed */
 		smp->remote_key_dist &= ~SMP_SC_NO_DIST;
 		/* Wait for Public Key from Initiating Device */
 		return 0;
-	} else {
-		SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
 	}
 
 	/* Request setup of TK */
@@ -1761,7 +1846,26 @@
 
 	BT_DBG("");
 
-	if (test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) {
+	if (test_bit(SMP_FLAG_LOCAL_OOB, &smp->flags)) {
+		struct l2cap_chan *chan = hdev->smp_data;
+		struct smp_dev *smp_dev;
+
+		if (!chan || !chan->data)
+			return SMP_UNSPECIFIED;
+
+		smp_dev = chan->data;
+
+		memcpy(smp->local_pk, smp_dev->local_pk, 64);
+		memcpy(smp->local_sk, smp_dev->local_sk, 32);
+		memcpy(smp->lr, smp_dev->local_rand, 16);
+
+		if (smp_dev->debug_key)
+			set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
+
+		goto done;
+	}
+
+	if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) {
 		BT_DBG("Using debug keys");
 		memcpy(smp->local_pk, debug_pk, 64);
 		memcpy(smp->local_sk, debug_sk, 32);
@@ -1780,8 +1884,9 @@
 		}
 	}
 
+done:
 	SMP_DBG("Local Public Key X: %32phN", smp->local_pk);
-	SMP_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]);
+	SMP_DBG("Local Public Key Y: %32phN", smp->local_pk + 32);
 	SMP_DBG("Local Private Key:  %32phN", smp->local_sk);
 
 	smp_send_cmd(smp->conn, SMP_CMD_PUBLIC_KEY, 64, smp->local_pk);
@@ -1816,9 +1921,16 @@
 
 	auth = rsp->auth_req & AUTH_REQ_MASK(hdev);
 
-	if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+	if (hci_dev_test_flag(hdev, HCI_SC_ONLY) && !(auth & SMP_AUTH_SC))
 		return SMP_AUTH_REQUIREMENTS;
 
+	/* If the remote side's OOB flag is set it means it has
+	 * successfully received our local OOB data - therefore set the
+	 * flag to indicate that local OOB is in use.
+	 */
+	if (rsp->oob_flag == SMP_OOB_PRESENT)
+		set_bit(SMP_FLAG_LOCAL_OOB, &smp->flags);
+
 	smp->prsp[0] = SMP_CMD_PAIRING_RSP;
 	memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
 
@@ -1885,10 +1997,6 @@
 
 	BT_DBG("");
 
-	/* Public Key exchange must happen before any other steps */
-	if (!test_bit(SMP_FLAG_REMOTE_PK, &smp->flags))
-		return SMP_UNSPECIFIED;
-
 	if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
 		return sc_passkey_round(smp, SMP_CMD_PAIRING_CONFIRM);
 
@@ -1901,6 +2009,47 @@
 	return 0;
 }
 
+/* Work-around for some implementations that incorrectly copy RFU bits
+ * from our security request and thereby create the impression that
+ * we're doing SC when in fact the remote doesn't support it.
+ */
+static int fixup_sc_false_positive(struct smp_chan *smp)
+{
+	struct l2cap_conn *conn = smp->conn;
+	struct hci_conn *hcon = conn->hcon;
+	struct hci_dev *hdev = hcon->hdev;
+	struct smp_cmd_pairing *req, *rsp;
+	u8 auth;
+
+	/* The issue is only observed when we're in slave role */
+	if (hcon->out)
+		return SMP_UNSPECIFIED;
+
+	if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) {
+		BT_ERR("Refusing SMP SC -> legacy fallback in SC-only mode");
+		return SMP_UNSPECIFIED;
+	}
+
+	BT_ERR("Trying to fall back to legacy SMP");
+
+	req = (void *) &smp->preq[1];
+	rsp = (void *) &smp->prsp[1];
+
+	/* Rebuild key dist flags which may have been cleared for SC */
+	smp->remote_key_dist = (req->init_key_dist & rsp->resp_key_dist);
+
+	auth = req->auth_req & AUTH_REQ_MASK(hdev);
+
+	if (tk_request(conn, 0, auth, rsp->io_capability, req->io_capability)) {
+		BT_ERR("Failed to fall back to legacy SMP");
+		return SMP_UNSPECIFIED;
+	}
+
+	clear_bit(SMP_FLAG_SC, &smp->flags);
+
+	return 0;
+}
+
 static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 {
 	struct l2cap_chan *chan = conn->smp;
@@ -1914,8 +2063,19 @@
 	memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
 	skb_pull(skb, sizeof(smp->pcnf));
 
-	if (test_bit(SMP_FLAG_SC, &smp->flags))
-		return sc_check_confirm(smp);
+	if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+		int ret;
+
+		/* Public Key exchange must happen before any other steps */
+		if (test_bit(SMP_FLAG_REMOTE_PK, &smp->flags))
+			return sc_check_confirm(smp);
+
+		BT_ERR("Unexpected SMP Pairing Confirm");
+
+		ret = fixup_sc_false_positive(smp);
+		if (ret)
+			return ret;
+	}
 
 	if (conn->hcon->out) {
 		smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
@@ -1926,8 +2086,8 @@
 
 	if (test_bit(SMP_FLAG_TK_VALID, &smp->flags))
 		return smp_confirm(smp);
-	else
-		set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
+
+	set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
 
 	return 0;
 }
@@ -2086,7 +2246,7 @@
 
 	auth = rp->auth_req & AUTH_REQ_MASK(hdev);
 
-	if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+	if (hci_dev_test_flag(hdev, HCI_SC_ONLY) && !(auth & SMP_AUTH_SC))
 		return SMP_AUTH_REQUIREMENTS;
 
 	if (hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
@@ -2107,7 +2267,7 @@
 	if (!smp)
 		return SMP_UNSPECIFIED;
 
-	if (!test_bit(HCI_BONDABLE, &hcon->hdev->dev_flags) &&
+	if (!hci_dev_test_flag(hdev, HCI_BONDABLE) &&
 	    (auth & SMP_AUTH_BONDING))
 		return SMP_PAIRING_NOTSUPP;
 
@@ -2141,7 +2301,7 @@
 
 	chan = conn->smp;
 
-	if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
+	if (!hci_dev_test_flag(hcon->hdev, HCI_LE_ENABLED))
 		return 1;
 
 	if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
@@ -2170,7 +2330,7 @@
 
 	authreq = seclevel_to_authreq(sec_level);
 
-	if (test_bit(HCI_SC_ENABLED, &hcon->hdev->dev_flags))
+	if (hci_dev_test_flag(hcon->hdev, HCI_SC_ENABLED))
 		authreq |= SMP_AUTH_SC;
 
 	/* Require MITM if IO Capability allows or the security level
@@ -2308,8 +2468,12 @@
 	 * implementations are not known of and in order to not over
 	 * complicate our implementation, simply pretend that we never
 	 * received an IRK for such a device.
+	 *
+	 * The Identity Address must also be a Static Random or Public
+	 * Address, which hci_is_identity_address() checks for.
 	 */
-	if (!bacmp(&info->bdaddr, BDADDR_ANY)) {
+	if (!bacmp(&info->bdaddr, BDADDR_ANY) ||
+	    !hci_is_identity_address(&info->bdaddr, info->addr_type)) {
 		BT_ERR("Ignoring IRK with no identity address");
 		goto distribute;
 	}
@@ -2351,7 +2515,10 @@
 
 	csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
 	if (csrk) {
-		csrk->master = 0x01;
+		if (conn->hcon->sec_level > BT_SECURITY_MEDIUM)
+			csrk->type = MGMT_CSRK_REMOTE_AUTHENTICATED;
+		else
+			csrk->type = MGMT_CSRK_REMOTE_UNAUTHENTICATED;
 		memcpy(csrk->val, rp->csrk, sizeof(csrk->val));
 	}
 	smp->csrk = csrk;
@@ -2367,7 +2534,8 @@
 	struct smp_cmd_pairing *local, *remote;
 	u8 local_mitm, remote_mitm, local_io, remote_io, method;
 
-	if (test_bit(SMP_FLAG_OOB, &smp->flags))
+	if (test_bit(SMP_FLAG_REMOTE_OOB, &smp->flags) ||
+	    test_bit(SMP_FLAG_LOCAL_OOB, &smp->flags))
 		return REQ_OOB;
 
 	/* The preq/prsp contain the raw Pairing Request/Response PDUs
@@ -2421,6 +2589,16 @@
 
 	memcpy(smp->remote_pk, key, 64);
 
+	if (test_bit(SMP_FLAG_REMOTE_OOB, &smp->flags)) {
+		err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk,
+			     smp->rr, 0, cfm.confirm_val);
+		if (err)
+			return SMP_UNSPECIFIED;
+
+		if (memcmp(cfm.confirm_val, smp->pcnf, 16))
+			return SMP_CONFIRM_FAILED;
+	}
+
 	/* Non-initiating device sends its public key after receiving
 	 * the key from the initiating device.
 	 */
@@ -2431,7 +2609,7 @@
 	}
 
 	SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk);
-	SMP_DBG("Remote Public Key Y: %32phN", &smp->remote_pk[32]);
+	SMP_DBG("Remote Public Key Y: %32phN", smp->remote_pk + 32);
 
 	if (!ecdh_shared_secret(smp->remote_pk, smp->local_sk, smp->dhkey))
 		return SMP_UNSPECIFIED;
@@ -2469,14 +2647,6 @@
 	}
 
 	if (smp->method == REQ_OOB) {
-		err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk,
-			     smp->rr, 0, cfm.confirm_val);
-		if (err)
-			return SMP_UNSPECIFIED;
-
-		if (memcmp(cfm.confirm_val, smp->pcnf, 16))
-			return SMP_CONFIRM_FAILED;
-
 		if (hcon->out)
 			smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
 				     sizeof(smp->prnd), smp->prnd);
@@ -2549,6 +2719,8 @@
 
 	if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
 		put_unaligned_le32(hcon->passkey_notify, r);
+	else if (smp->method == REQ_OOB)
+		memcpy(r, smp->lr, 16);
 
 	err = smp_f6(smp->tfm_cmac, smp->mackey, smp->rrnd, smp->prnd, r,
 		     io_cap, remote_addr, local_addr, e);
@@ -2599,7 +2771,7 @@
 	if (skb->len < 1)
 		return -EILSEQ;
 
-	if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) {
+	if (!hci_dev_test_flag(hcon->hdev, HCI_LE_ENABLED)) {
 		reason = SMP_PAIRING_NOTSUPP;
 		goto done;
 	}
@@ -2737,16 +2909,16 @@
 		return;
 
 	/* Secure Connections support must be enabled */
-	if (!test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_SC_ENABLED))
 		return;
 
 	/* BR/EDR must use Secure Connections for SMP */
 	if (!test_bit(HCI_CONN_AES_CCM, &hcon->flags) &&
-	    !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+	    !hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP))
 		return;
 
 	/* If our LE support is not enabled don't do anything */
-	if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
 		return;
 
 	/* Don't bother if remote LE support is not enabled */
@@ -2850,7 +3022,7 @@
 		return ERR_PTR(-ENOMEM);
 
 	skb->priority = HCI_PRIO_MAX;
-	bt_cb(skb)->chan = chan;
+	bt_cb(skb)->l2cap.chan = chan;
 
 	return skb;
 }
@@ -2870,7 +3042,9 @@
 	.suspend		= l2cap_chan_no_suspend,
 	.set_shutdown		= l2cap_chan_no_set_shutdown,
 	.get_sndtimeo		= l2cap_chan_no_get_sndtimeo,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
 	.memcpy_fromiovec	= l2cap_chan_no_memcpy_fromiovec,
+#endif
 };
 
 static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
@@ -2919,43 +3093,76 @@
 	.resume			= l2cap_chan_no_resume,
 	.set_shutdown		= l2cap_chan_no_set_shutdown,
 	.get_sndtimeo		= l2cap_chan_no_get_sndtimeo,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
 	.memcpy_fromiovec	= l2cap_chan_no_memcpy_fromiovec,
+#endif
 };
 
 static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
 {
 	struct l2cap_chan *chan;
-	struct crypto_blkcipher	*tfm_aes;
+	struct smp_dev *smp;
+	struct crypto_blkcipher *tfm_aes;
+	struct crypto_hash *tfm_cmac;
 
 	if (cid == L2CAP_CID_SMP_BREDR) {
-		tfm_aes = NULL;
+		smp = NULL;
 		goto create_chan;
 	}
 
-	tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0);
+	smp = kzalloc(sizeof(*smp), GFP_KERNEL);
+	if (!smp)
+		return ERR_PTR(-ENOMEM);
+
+	tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
 	if (IS_ERR(tfm_aes)) {
-		BT_ERR("Unable to create crypto context");
+		BT_ERR("Unable to create ECB crypto context");
+		kzfree(smp);
 		return ERR_CAST(tfm_aes);
 	}
 
+	tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm_cmac)) {
+		BT_ERR("Unable to create CMAC crypto context");
+		crypto_free_blkcipher(tfm_aes);
+		kzfree(smp);
+		return ERR_CAST(tfm_cmac);
+	}
+
+	smp->tfm_aes = tfm_aes;
+	smp->tfm_cmac = tfm_cmac;
+
 create_chan:
 	chan = l2cap_chan_create();
 	if (!chan) {
-		crypto_free_blkcipher(tfm_aes);
+		if (smp) {
+			crypto_free_blkcipher(smp->tfm_aes);
+			crypto_free_hash(smp->tfm_cmac);
+			kzfree(smp);
+		}
 		return ERR_PTR(-ENOMEM);
 	}
 
-	chan->data = tfm_aes;
+	chan->data = smp;
 
 	l2cap_add_scid(chan, cid);
 
 	l2cap_chan_set_defaults(chan);
 
-	bacpy(&chan->src, &hdev->bdaddr);
-	if (cid == L2CAP_CID_SMP)
-		chan->src_type = BDADDR_LE_PUBLIC;
-	else
+	if (cid == L2CAP_CID_SMP) {
+		u8 bdaddr_type;
+
+		hci_copy_identity_address(hdev, &chan->src, &bdaddr_type);
+
+		if (bdaddr_type == ADDR_LE_DEV_PUBLIC)
+			chan->src_type = BDADDR_LE_PUBLIC;
+		else
+			chan->src_type = BDADDR_LE_RANDOM;
+	} else {
+		bacpy(&chan->src, &hdev->bdaddr);
 		chan->src_type = BDADDR_BREDR;
+	}
+
 	chan->state = BT_LISTEN;
 	chan->mode = L2CAP_MODE_BASIC;
 	chan->imtu = L2CAP_DEFAULT_MTU;
@@ -2969,34 +3176,125 @@
 
 static void smp_del_chan(struct l2cap_chan *chan)
 {
-	struct crypto_blkcipher	*tfm_aes;
+	struct smp_dev *smp;
 
 	BT_DBG("chan %p", chan);
 
-	tfm_aes = chan->data;
-	if (tfm_aes) {
+	smp = chan->data;
+	if (smp) {
 		chan->data = NULL;
-		crypto_free_blkcipher(tfm_aes);
+		if (smp->tfm_aes)
+			crypto_free_blkcipher(smp->tfm_aes);
+		if (smp->tfm_cmac)
+			crypto_free_hash(smp->tfm_cmac);
+		kzfree(smp);
 	}
 
 	l2cap_chan_put(chan);
 }
 
+static ssize_t force_bredr_smp_read(struct file *file,
+				    char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct hci_dev *hdev = file->private_data;
+	char buf[3];
+
+	buf[0] = hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP) ? 'Y': 'N';
+	buf[1] = '\n';
+	buf[2] = '\0';
+	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_bredr_smp_write(struct file *file,
+				     const char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct hci_dev *hdev = file->private_data;
+	char buf[32];
+	size_t buf_size = min(count, (sizeof(buf)-1));
+	bool enable;
+
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+
+	buf[buf_size] = '\0';
+	if (strtobool(buf, &enable))
+		return -EINVAL;
+
+	if (enable == hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP))
+		return -EALREADY;
+
+	if (enable) {
+		struct l2cap_chan *chan;
+
+		chan = smp_add_cid(hdev, L2CAP_CID_SMP_BREDR);
+		if (IS_ERR(chan))
+			return PTR_ERR(chan);
+
+		hdev->smp_bredr_data = chan;
+	} else {
+		struct l2cap_chan *chan;
+
+		chan = hdev->smp_bredr_data;
+		hdev->smp_bredr_data = NULL;
+		smp_del_chan(chan);
+	}
+
+	hci_dev_change_flag(hdev, HCI_FORCE_BREDR_SMP);
+
+	return count;
+}
+
+static const struct file_operations force_bredr_smp_fops = {
+	.open		= simple_open,
+	.read		= force_bredr_smp_read,
+	.write		= force_bredr_smp_write,
+	.llseek		= default_llseek,
+};
+
 int smp_register(struct hci_dev *hdev)
 {
 	struct l2cap_chan *chan;
 
 	BT_DBG("%s", hdev->name);
 
+	/* If the controller does not support Low Energy operation, then
+	 * there is also no need to register any SMP channel.
+	 */
+	if (!lmp_le_capable(hdev))
+		return 0;
+
+	if (WARN_ON(hdev->smp_data)) {
+		chan = hdev->smp_data;
+		hdev->smp_data = NULL;
+		smp_del_chan(chan);
+	}
+
 	chan = smp_add_cid(hdev, L2CAP_CID_SMP);
 	if (IS_ERR(chan))
 		return PTR_ERR(chan);
 
 	hdev->smp_data = chan;
 
-	if (!lmp_sc_capable(hdev) &&
-	    !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+	/* If the controller does not support BR/EDR Secure Connections
+	 * feature, then the BR/EDR SMP channel shall not be present.
+	 *
+	 * To test this with Bluetooth 4.0 controllers, create a debugfs
+	 * switch that allows forcing BR/EDR SMP support and accepting
+	 * cross-transport pairing on non-AES encrypted connections.
+	 */
+	if (!lmp_sc_capable(hdev)) {
+		debugfs_create_file("force_bredr_smp", 0644, hdev->debugfs,
+				    hdev, &force_bredr_smp_fops);
 		return 0;
+	}
+
+	if (WARN_ON(hdev->smp_bredr_data)) {
+		chan = hdev->smp_bredr_data;
+		hdev->smp_bredr_data = NULL;
+		smp_del_chan(chan);
+	}
 
 	chan = smp_add_cid(hdev, L2CAP_CID_SMP_BREDR);
 	if (IS_ERR(chan)) {
@@ -3028,3 +3326,356 @@
 		smp_del_chan(chan);
 	}
 }
+
+#if IS_ENABLED(CPTCFG_BT_SELFTEST_SMP)
+
+static int __init test_ah(struct crypto_blkcipher *tfm_aes)
+{
+	const u8 irk[16] = {
+			0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
+			0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
+	const u8 r[3] = { 0x94, 0x81, 0x70 };
+	const u8 exp[3] = { 0xaa, 0xfb, 0x0d };
+	u8 res[3];
+	int err;
+
+	err = smp_ah(tfm_aes, irk, r, res);
+	if (err)
+		return err;
+
+	if (memcmp(res, exp, 3))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int __init test_c1(struct crypto_blkcipher *tfm_aes)
+{
+	const u8 k[16] = {
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	const u8 r[16] = {
+			0xe0, 0x2e, 0x70, 0xc6, 0x4e, 0x27, 0x88, 0x63,
+			0x0e, 0x6f, 0xad, 0x56, 0x21, 0xd5, 0x83, 0x57 };
+	const u8 preq[7] = { 0x01, 0x01, 0x00, 0x00, 0x10, 0x07, 0x07 };
+	const u8 pres[7] = { 0x02, 0x03, 0x00, 0x00, 0x08, 0x00, 0x05 };
+	const u8 _iat = 0x01;
+	const u8 _rat = 0x00;
+	const bdaddr_t ra = { { 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1 } };
+	const bdaddr_t ia = { { 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1 } };
+	const u8 exp[16] = {
+			0x86, 0x3b, 0xf1, 0xbe, 0xc5, 0x4d, 0xa7, 0xd2,
+			0xea, 0x88, 0x89, 0x87, 0xef, 0x3f, 0x1e, 0x1e };
+	u8 res[16];
+	int err;
+
+	err = smp_c1(tfm_aes, k, r, preq, pres, _iat, &ia, _rat, &ra, res);
+	if (err)
+		return err;
+
+	if (memcmp(res, exp, 16))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int __init test_s1(struct crypto_blkcipher *tfm_aes)
+{
+	const u8 k[16] = {
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	const u8 r1[16] = {
+			0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 };
+	const u8 r2[16] = {
+			0x00, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99 };
+	const u8 exp[16] = {
+			0x62, 0xa0, 0x6d, 0x79, 0xae, 0x16, 0x42, 0x5b,
+			0x9b, 0xf4, 0xb0, 0xe8, 0xf0, 0xe1, 0x1f, 0x9a };
+	u8 res[16];
+	int err;
+
+	err = smp_s1(tfm_aes, k, r1, r2, res);
+	if (err)
+		return err;
+
+	if (memcmp(res, exp, 16))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int __init test_f4(struct crypto_hash *tfm_cmac)
+{
+	const u8 u[32] = {
+			0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
+			0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
+			0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
+			0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 };
+	const u8 v[32] = {
+			0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
+			0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
+			0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
+			0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 };
+	const u8 x[16] = {
+			0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
+			0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
+	const u8 z = 0x00;
+	const u8 exp[16] = {
+			0x2d, 0x87, 0x74, 0xa9, 0xbe, 0xa1, 0xed, 0xf1,
+			0x1c, 0xbd, 0xa9, 0x07, 0xf1, 0x16, 0xc9, 0xf2 };
+	u8 res[16];
+	int err;
+
+	err = smp_f4(tfm_cmac, u, v, x, z, res);
+	if (err)
+		return err;
+
+	if (memcmp(res, exp, 16))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int __init test_f5(struct crypto_hash *tfm_cmac)
+{
+	const u8 w[32] = {
+			0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86,
+			0xf1, 0x66, 0xf8, 0xb4, 0x13, 0x6b, 0x79, 0x99,
+			0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
+			0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
+	const u8 n1[16] = {
+			0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
+			0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
+	const u8 n2[16] = {
+			0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
+			0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
+	const u8 a1[7] = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56, 0x00 };
+	const u8 a2[7] = { 0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7, 0x00 };
+	const u8 exp_ltk[16] = {
+			0x38, 0x0a, 0x75, 0x94, 0xb5, 0x22, 0x05, 0x98,
+			0x23, 0xcd, 0xd7, 0x69, 0x11, 0x79, 0x86, 0x69 };
+	const u8 exp_mackey[16] = {
+			0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd,
+			0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1, 0x65, 0x29 };
+	u8 mackey[16], ltk[16];
+	int err;
+
+	err = smp_f5(tfm_cmac, w, n1, n2, a1, a2, mackey, ltk);
+	if (err)
+		return err;
+
+	if (memcmp(mackey, exp_mackey, 16))
+		return -EINVAL;
+
+	if (memcmp(ltk, exp_ltk, 16))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int __init test_f6(struct crypto_hash *tfm_cmac)
+{
+	const u8 w[16] = {
+			0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd,
+			0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1, 0x65, 0x29 };
+	const u8 n1[16] = {
+			0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
+			0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
+	const u8 n2[16] = {
+			0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
+			0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
+	const u8 r[16] = {
+			0xc8, 0x0f, 0x2d, 0x0c, 0xd2, 0x42, 0xda, 0x08,
+			0x54, 0xbb, 0x53, 0xb4, 0x3b, 0x34, 0xa3, 0x12 };
+	const u8 io_cap[3] = { 0x02, 0x01, 0x01 };
+	const u8 a1[7] = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56, 0x00 };
+	const u8 a2[7] = { 0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7, 0x00 };
+	const u8 exp[16] = {
+			0x61, 0x8f, 0x95, 0xda, 0x09, 0x0b, 0x6c, 0xd2,
+			0xc5, 0xe8, 0xd0, 0x9c, 0x98, 0x73, 0xc4, 0xe3 };
+	u8 res[16];
+	int err;
+
+	err = smp_f6(tfm_cmac, w, n1, n2, r, io_cap, a1, a2, res);
+	if (err)
+		return err;
+
+	if (memcmp(res, exp, 16))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int __init test_g2(struct crypto_hash *tfm_cmac)
+{
+	const u8 u[32] = {
+			0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
+			0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
+			0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
+			0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 };
+	const u8 v[32] = {
+			0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
+			0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
+			0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
+			0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 };
+	const u8 x[16] = {
+			0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
+			0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
+	const u8 y[16] = {
+			0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
+			0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
+	const u32 exp_val = 0x2f9ed5ba % 1000000;
+	u32 val;
+	int err;
+
+	err = smp_g2(tfm_cmac, u, v, x, y, &val);
+	if (err)
+		return err;
+
+	if (val != exp_val)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int __init test_h6(struct crypto_hash *tfm_cmac)
+{
+	const u8 w[16] = {
+			0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
+			0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
+	const u8 key_id[4] = { 0x72, 0x62, 0x65, 0x6c };
+	const u8 exp[16] = {
+			0x99, 0x63, 0xb1, 0x80, 0xe2, 0xa9, 0xd3, 0xe8,
+			0x1c, 0xc9, 0x6d, 0xe7, 0x02, 0xe1, 0x9a, 0x2d };
+	u8 res[16];
+	int err;
+
+	err = smp_h6(tfm_cmac, w, key_id, res);
+	if (err)
+		return err;
+
+	if (memcmp(res, exp, 16))
+		return -EINVAL;
+
+	return 0;
+}
+
+static char test_smp_buffer[32];
+
+static ssize_t test_smp_read(struct file *file, char __user *user_buf,
+			     size_t count, loff_t *ppos)
+{
+	return simple_read_from_buffer(user_buf, count, ppos, test_smp_buffer,
+				       strlen(test_smp_buffer));
+}
+
+static const struct file_operations test_smp_fops = {
+	.open		= simple_open,
+	.read		= test_smp_read,
+	.llseek		= default_llseek,
+};
+
+static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
+				struct crypto_hash *tfm_cmac)
+{
+	ktime_t calltime, delta, rettime;
+	unsigned long long duration;
+	int err;
+
+	calltime = ktime_get();
+
+	err = test_ah(tfm_aes);
+	if (err) {
+		BT_ERR("smp_ah test failed");
+		goto done;
+	}
+
+	err = test_c1(tfm_aes);
+	if (err) {
+		BT_ERR("smp_c1 test failed");
+		goto done;
+	}
+
+	err = test_s1(tfm_aes);
+	if (err) {
+		BT_ERR("smp_s1 test failed");
+		goto done;
+	}
+
+	err = test_f4(tfm_cmac);
+	if (err) {
+		BT_ERR("smp_f4 test failed");
+		goto done;
+	}
+
+	err = test_f5(tfm_cmac);
+	if (err) {
+		BT_ERR("smp_f5 test failed");
+		goto done;
+	}
+
+	err = test_f6(tfm_cmac);
+	if (err) {
+		BT_ERR("smp_f6 test failed");
+		goto done;
+	}
+
+	err = test_g2(tfm_cmac);
+	if (err) {
+		BT_ERR("smp_g2 test failed");
+		goto done;
+	}
+
+	err = test_h6(tfm_cmac);
+	if (err) {
+		BT_ERR("smp_h6 test failed");
+		goto done;
+	}
+
+	rettime = ktime_get();
+	delta = ktime_sub(rettime, calltime);
+	duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+
+	BT_INFO("SMP test passed in %llu usecs", duration);
+
+done:
+	if (!err)
+		snprintf(test_smp_buffer, sizeof(test_smp_buffer),
+			 "PASS (%llu usecs)\n", duration);
+	else
+		snprintf(test_smp_buffer, sizeof(test_smp_buffer), "FAIL\n");
+
+	debugfs_create_file("selftest_smp", 0444, bt_debugfs, NULL,
+			    &test_smp_fops);
+
+	return err;
+}
+
+int __init bt_selftest_smp(void)
+{
+	struct crypto_blkcipher *tfm_aes;
+	struct crypto_hash *tfm_cmac;
+	int err;
+
+	tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm_aes)) {
+		BT_ERR("Unable to create ECB crypto context");
+		return PTR_ERR(tfm_aes);
+	}
+
+	tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm_cmac)) {
+		BT_ERR("Unable to create CMAC crypto context");
+		crypto_free_blkcipher(tfm_aes);
+		return PTR_ERR(tfm_cmac);
+	}
+
+	err = run_selftests(tfm_aes, tfm_cmac);
+
+	crypto_free_hash(tfm_cmac);
+	crypto_free_blkcipher(tfm_aes);
+
+	return err;
+}
+
+#endif
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
index 3296bf4..af7b69b 100644
--- a/net/bluetooth/smp.h
+++ b/net/bluetooth/smp.h
@@ -188,8 +188,22 @@
 bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
 		     const bdaddr_t *bdaddr);
 int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa);
+int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]);
 
 int smp_register(struct hci_dev *hdev);
 void smp_unregister(struct hci_dev *hdev);
 
+#if IS_ENABLED(CPTCFG_BT_SELFTEST_SMP)
+
+int bt_selftest_smp(void);
+
+#else
+
+static inline int bt_selftest_smp(void)
+{
+	return 0;
+}
+
+#endif
+
 #endif /* __SMP_H */
diff --git a/net/ieee802154/6lowpan/6lowpan_i.h b/net/ieee802154/6lowpan/6lowpan_i.h
new file mode 100644
index 0000000..e50f69d
--- /dev/null
+++ b/net/ieee802154/6lowpan/6lowpan_i.h
@@ -0,0 +1,72 @@
+#ifndef __IEEE802154_6LOWPAN_I_H__
+#define __IEEE802154_6LOWPAN_I_H__
+
+#include <linux/list.h>
+
+#include <net/ieee802154_netdev.h>
+#include <net/inet_frag.h>
+
+struct lowpan_create_arg {
+	u16 tag;
+	u16 d_size;
+	const struct ieee802154_addr *src;
+	const struct ieee802154_addr *dst;
+};
+
+/* Equivalent of ipv4 struct ip
+ */
+struct lowpan_frag_queue {
+	struct inet_frag_queue	q;
+
+	u16			tag;
+	u16			d_size;
+	struct ieee802154_addr	saddr;
+	struct ieee802154_addr	daddr;
+};
+
+static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a)
+{
+	switch (a->mode) {
+	case IEEE802154_ADDR_LONG:
+		return (((__force u64)a->extended_addr) >> 32) ^
+			(((__force u64)a->extended_addr) & 0xffffffff);
+	case IEEE802154_ADDR_SHORT:
+		return (__force u32)(a->short_addr);
+	default:
+		return 0;
+	}
+}
+
+struct lowpan_dev_record {
+	struct net_device *ldev;
+	struct list_head list;
+};
+
+/* private device info */
+struct lowpan_dev_info {
+	struct net_device	*real_dev; /* real WPAN device ptr */
+	struct mutex		dev_list_mtx; /* mutex for list ops */
+	u16			fragment_tag;
+};
+
+static inline struct
+lowpan_dev_info *lowpan_dev_info(const struct net_device *dev)
+{
+	return netdev_priv(dev);
+}
+
+extern struct list_head lowpan_devices;
+
+int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type);
+void lowpan_net_frag_exit(void);
+int lowpan_net_frag_init(void);
+
+void lowpan_rx_init(void);
+void lowpan_rx_exit(void);
+
+int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
+			 unsigned short type, const void *_daddr,
+			 const void *_saddr, unsigned int len);
+netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev);
+
+#endif /* __IEEE802154_6LOWPAN_I_H__ */
diff --git a/net/ieee802154/6lowpan/Kconfig b/net/ieee802154/6lowpan/Kconfig
new file mode 100644
index 0000000..d6c413f
--- /dev/null
+++ b/net/ieee802154/6lowpan/Kconfig
@@ -0,0 +1,7 @@
+config IEEE802154_6LOWPAN
+	depends on !KERNEL_3_5
+	tristate "6lowpan support over IEEE 802.15.4"
+	depends on m
+	depends on 6LOWPAN
+	---help---
+	  IPv6 compression over IEEE 802.15.4.
diff --git a/net/ieee802154/6lowpan/Makefile b/net/ieee802154/6lowpan/Makefile
new file mode 100644
index 0000000..ff5f619
--- /dev/null
+++ b/net/ieee802154/6lowpan/Makefile
@@ -0,0 +1,3 @@
+obj-$(CPTCFG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o
+
+ieee802154_6lowpan-y := core.o rx.o reassembly.o tx.o
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
new file mode 100644
index 0000000..0ae5822
--- /dev/null
+++ b/net/ieee802154/6lowpan/core.c
@@ -0,0 +1,306 @@
+/* Copyright 2011, Siemens AG
+ * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ */
+
+/* Based on patches from Jon Smirl <jonsmirl@gmail.com>
+ * Copyright (c) 2011 Jon Smirl <jonsmirl@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.
+ */
+
+/* Jon's code is based on 6lowpan implementation for Contiki which is:
+ * Copyright (c) 2008, Swedish Institute of Computer Science.
+ * 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 name of the Institute 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 INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/ieee802154.h>
+
+#include <net/ipv6.h>
+
+#include "6lowpan_i.h"
+
+LIST_HEAD(lowpan_devices);
+static int lowpan_open_count;
+
+static __le16 lowpan_get_pan_id(const struct net_device *dev)
+{
+	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
+
+	return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev);
+}
+
+static __le16 lowpan_get_short_addr(const struct net_device *dev)
+{
+	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
+
+	return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev);
+}
+
+static u8 lowpan_get_dsn(const struct net_device *dev)
+{
+	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
+
+	return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev);
+}
+
+static struct header_ops lowpan_header_ops = {
+	.create	= lowpan_header_create,
+};
+
+static struct lock_class_key lowpan_tx_busylock;
+static struct lock_class_key lowpan_netdev_xmit_lock_key;
+
+static void lowpan_set_lockdep_class_one(struct net_device *dev,
+					 struct netdev_queue *txq,
+					 void *_unused)
+{
+	lockdep_set_class(&txq->_xmit_lock,
+			  &lowpan_netdev_xmit_lock_key);
+}
+
+static int lowpan_dev_init(struct net_device *dev)
+{
+	netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL);
+	dev->qdisc_tx_busylock = &lowpan_tx_busylock;
+	return 0;
+}
+
+static const struct net_device_ops lowpan_netdev_ops = {
+	.ndo_init		= lowpan_dev_init,
+	.ndo_start_xmit		= lowpan_xmit,
+};
+
+static struct ieee802154_mlme_ops lowpan_mlme = {
+	.get_pan_id = lowpan_get_pan_id,
+	.get_short_addr = lowpan_get_short_addr,
+	.get_dsn = lowpan_get_dsn,
+};
+
+static void lowpan_setup(struct net_device *dev)
+{
+	dev->addr_len		= IEEE802154_ADDR_LEN;
+	memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
+	dev->type		= ARPHRD_6LOWPAN;
+	/* Frame Control + Sequence Number + Address fields + Security Header */
+	dev->hard_header_len	= 2 + 1 + 20 + 14;
+	dev->needed_tailroom	= 2; /* FCS */
+	dev->mtu		= IPV6_MIN_MTU;
+	dev->tx_queue_len	= 0;
+	dev->flags		= IFF_BROADCAST | IFF_MULTICAST;
+	dev->watchdog_timeo	= 0;
+
+	dev->netdev_ops		= &lowpan_netdev_ops;
+	dev->header_ops		= &lowpan_header_ops;
+	dev->ml_priv		= &lowpan_mlme;
+	dev->destructor		= free_netdev;
+	dev->features		|= NETIF_F_NETNS_LOCAL;
+}
+
+static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+	if (tb[IFLA_ADDRESS]) {
+		if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN)
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int lowpan_newlink(struct net *src_net, struct net_device *dev,
+			  struct nlattr *tb[], struct nlattr *data[])
+{
+	struct net_device *real_dev;
+	struct lowpan_dev_record *entry;
+	int ret;
+
+	ASSERT_RTNL();
+
+	pr_debug("adding new link\n");
+
+	if (!tb[IFLA_LINK] ||
+	    !net_eq(dev_net(dev), &init_net))
+		return -EINVAL;
+	/* find and hold real wpan device */
+	real_dev = dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK]));
+	if (!real_dev)
+		return -ENODEV;
+	if (real_dev->type != ARPHRD_IEEE802154) {
+		dev_put(real_dev);
+		return -EINVAL;
+	}
+
+	lowpan_dev_info(dev)->real_dev = real_dev;
+	mutex_init(&lowpan_dev_info(dev)->dev_list_mtx);
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry) {
+		dev_put(real_dev);
+		lowpan_dev_info(dev)->real_dev = NULL;
+		return -ENOMEM;
+	}
+
+	entry->ldev = dev;
+
+	/* Set the lowpan hardware address to the wpan hardware address. */
+	memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN);
+
+	mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
+	INIT_LIST_HEAD(&entry->list);
+	list_add_tail(&entry->list, &lowpan_devices);
+	mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx);
+
+	ret = register_netdevice(dev);
+	if (ret >= 0) {
+		if (!lowpan_open_count)
+			lowpan_rx_init();
+		lowpan_open_count++;
+	}
+
+	return ret;
+}
+
+static void lowpan_dellink(struct net_device *dev, struct list_head *head)
+{
+	struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev);
+	struct net_device *real_dev = lowpan_dev->real_dev;
+	struct lowpan_dev_record *entry, *tmp;
+
+	ASSERT_RTNL();
+
+	lowpan_open_count--;
+	if (!lowpan_open_count)
+		lowpan_rx_exit();
+
+	mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
+	list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) {
+		if (entry->ldev == dev) {
+			list_del(&entry->list);
+			kfree(entry);
+		}
+	}
+	mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx);
+
+	mutex_destroy(&lowpan_dev_info(dev)->dev_list_mtx);
+
+	unregister_netdevice_queue(dev, head);
+
+	dev_put(real_dev);
+}
+
+static struct rtnl_link_ops lowpan_link_ops __read_mostly = {
+	.kind		= "lowpan",
+	.priv_size	= sizeof(struct lowpan_dev_info),
+	.setup		= lowpan_setup,
+	.newlink	= lowpan_newlink,
+	.dellink	= lowpan_dellink,
+	.validate	= lowpan_validate,
+};
+
+static inline int __init lowpan_netlink_init(void)
+{
+	return rtnl_link_register(&lowpan_link_ops);
+}
+
+static inline void lowpan_netlink_fini(void)
+{
+	rtnl_link_unregister(&lowpan_link_ops);
+}
+
+static int lowpan_device_event(struct notifier_block *unused,
+			       unsigned long event, void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	LIST_HEAD(del_list);
+	struct lowpan_dev_record *entry, *tmp;
+
+	if (dev->type != ARPHRD_IEEE802154)
+		goto out;
+
+	if (event == NETDEV_UNREGISTER) {
+		list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) {
+			if (lowpan_dev_info(entry->ldev)->real_dev == dev)
+				lowpan_dellink(entry->ldev, &del_list);
+		}
+
+		unregister_netdevice_many(&del_list);
+	}
+
+out:
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block lowpan_dev_notifier = {
+	.notifier_call = lowpan_device_event,
+};
+
+static int __init lowpan_init_module(void)
+{
+	int err = 0;
+
+	err = lowpan_net_frag_init();
+	if (err < 0)
+		goto out;
+
+	err = lowpan_netlink_init();
+	if (err < 0)
+		goto out_frag;
+
+	err = register_netdevice_notifier(&lowpan_dev_notifier);
+	if (err < 0)
+		goto out_pack;
+
+	return 0;
+
+out_pack:
+	lowpan_netlink_fini();
+out_frag:
+	lowpan_net_frag_exit();
+out:
+	return err;
+}
+
+static void __exit lowpan_cleanup_module(void)
+{
+	lowpan_netlink_fini();
+
+	lowpan_net_frag_exit();
+
+	unregister_netdevice_notifier(&lowpan_dev_notifier);
+}
+
+module_init(lowpan_init_module);
+module_exit(lowpan_cleanup_module);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("lowpan");
diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
similarity index 94%
rename from net/ieee802154/reassembly.c
rename to net/ieee802154/6lowpan/reassembly.c
index f133b62..f46e4d1 100644
--- a/net/ieee802154/reassembly.c
+++ b/net/ieee802154/6lowpan/reassembly.c
@@ -28,7 +28,7 @@
 #include <net/ipv6.h>
 #include <net/inet_frag.h>
 
-#include "reassembly.h"
+#include "6lowpan_i.h"
 
 static const char lowpan_frags_cache_name[] = "lowpan-frags";
 
@@ -89,9 +89,6 @@
 	fq->d_size = arg->d_size;
 	fq->saddr = *arg->src;
 	fq->daddr = *arg->dst;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
-	spin_lock_init(&fq->lru_lock);
-#endif
 }
 
 static void lowpan_frag_expire(unsigned long data)
@@ -100,11 +97,7 @@
 	struct net *net;
 
 	fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q);
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0))
 	net = container_of(fq->q.net, struct net, ieee802154_lowpan.frags);
-#else
-	net = &init_net;
-#endif
 
 	spin_lock(&fq->q.lock);
 
@@ -402,44 +395,24 @@
 static struct ctl_table lowpan_frags_ns_ctl_table[] = {
 	{
 		.procname	= "6lowpanfrag_high_thresh",
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0))
 		.data		= &init_net.ieee802154_lowpan.frags.high_thresh,
-#else
-		.data		= &ieee802154_lowpan.frags.high_thresh,
-#endif
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0))
 		.extra1		= &init_net.ieee802154_lowpan.frags.low_thresh
-#else
-		.extra1		= &ieee802154_lowpan.frags.low_thresh
-#endif
 	},
 	{
 		.procname	= "6lowpanfrag_low_thresh",
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0))
 		.data		= &init_net.ieee802154_lowpan.frags.low_thresh,
-#else
-		.data		= &ieee802154_lowpan.frags.low_thresh,
-#endif
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
 		.extra1		= &zero,
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0))
 		.extra2		= &init_net.ieee802154_lowpan.frags.high_thresh
-#else
-		.extra2		= &ieee802154_lowpan.frags.high_thresh
-#endif
 	},
 	{
 		.procname	= "6lowpanfrag_time",
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0))
 		.data		= &init_net.ieee802154_lowpan.frags.timeout,
-#else
-		.data		= &ieee802154_lowpan.frags.timeout,
-#endif
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_jiffies,
@@ -481,11 +454,9 @@
 		table[1].extra2 = &ieee802154_lowpan->frags.high_thresh;
 		table[2].data = &ieee802154_lowpan->frags.timeout;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
 		/* Don't export sysctls to unprivileged users */
 		if (net->user_ns != &init_user_ns)
 			table[0].procname = NULL;
-#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(3,8,0) */
 	}
 
 	hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table);
diff --git a/net/ieee802154/6lowpan/rx.c b/net/ieee802154/6lowpan/rx.c
new file mode 100644
index 0000000..4be1d28
--- /dev/null
+++ b/net/ieee802154/6lowpan/rx.c
@@ -0,0 +1,171 @@
+/* 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.
+ */
+
+#include <linux/if_arp.h>
+
+#include <net/6lowpan.h>
+#include <net/ieee802154_netdev.h>
+
+#include "6lowpan_i.h"
+
+static int lowpan_give_skb_to_devices(struct sk_buff *skb,
+				      struct net_device *dev)
+{
+	struct lowpan_dev_record *entry;
+	struct sk_buff *skb_cp;
+	int stat = NET_RX_SUCCESS;
+
+	skb->protocol = htons(ETH_P_IPV6);
+	skb->pkt_type = PACKET_HOST;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(entry, &lowpan_devices, list)
+		if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) {
+			skb_cp = skb_copy(skb, GFP_ATOMIC);
+			if (!skb_cp) {
+				kfree_skb(skb);
+				rcu_read_unlock();
+				return NET_RX_DROP;
+			}
+
+			skb_cp->dev = entry->ldev;
+			stat = netif_rx(skb_cp);
+			if (stat == NET_RX_DROP)
+				break;
+		}
+	rcu_read_unlock();
+
+	consume_skb(skb);
+
+	return stat;
+}
+
+static int
+iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
+{
+	u8 iphc0, iphc1;
+	struct ieee802154_addr_sa sa, da;
+	void *sap, *dap;
+
+	raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len);
+	/* at least two bytes will be used for the encoding */
+	if (skb->len < 2)
+		return -EINVAL;
+
+	if (lowpan_fetch_skb_u8(skb, &iphc0))
+		return -EINVAL;
+
+	if (lowpan_fetch_skb_u8(skb, &iphc1))
+		return -EINVAL;
+
+	ieee802154_addr_to_sa(&sa, &hdr->source);
+	ieee802154_addr_to_sa(&da, &hdr->dest);
+
+	if (sa.addr_type == IEEE802154_ADDR_SHORT)
+		sap = &sa.short_addr;
+	else
+		sap = &sa.hwaddr;
+
+	if (da.addr_type == IEEE802154_ADDR_SHORT)
+		dap = &da.short_addr;
+	else
+		dap = &da.hwaddr;
+
+	return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type,
+					IEEE802154_ADDR_LEN, dap, da.addr_type,
+					IEEE802154_ADDR_LEN, iphc0, iphc1);
+}
+
+static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
+		      struct packet_type *pt, struct net_device *orig_dev)
+{
+	struct ieee802154_hdr hdr;
+	int ret;
+
+	skb = skb_share_check(skb, GFP_ATOMIC);
+	if (!skb)
+		goto drop;
+
+	if (!netif_running(dev))
+		goto drop_skb;
+
+	if (skb->pkt_type == PACKET_OTHERHOST)
+		goto drop_skb;
+
+	if (dev->type != ARPHRD_IEEE802154)
+		goto drop_skb;
+
+	if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
+		goto drop_skb;
+
+	/* check that it's our buffer */
+	if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
+		/* Pull off the 1-byte of 6lowpan header. */
+		skb_pull(skb, 1);
+		return lowpan_give_skb_to_devices(skb, NULL);
+	} else {
+		switch (skb->data[0] & 0xe0) {
+		case LOWPAN_DISPATCH_IPHC:	/* ipv6 datagram */
+			ret = iphc_decompress(skb, &hdr);
+			if (ret < 0)
+				goto drop_skb;
+
+			return lowpan_give_skb_to_devices(skb, NULL);
+		case LOWPAN_DISPATCH_FRAG1:	/* first fragment header */
+			ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1);
+			if (ret == 1) {
+				ret = iphc_decompress(skb, &hdr);
+				if (ret < 0)
+					goto drop_skb;
+
+				return lowpan_give_skb_to_devices(skb, NULL);
+			} else if (ret == -1) {
+				return NET_RX_DROP;
+			} else {
+				return NET_RX_SUCCESS;
+			}
+		case LOWPAN_DISPATCH_FRAGN:	/* next fragments headers */
+			ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN);
+			if (ret == 1) {
+				ret = iphc_decompress(skb, &hdr);
+				if (ret < 0)
+					goto drop_skb;
+
+				return lowpan_give_skb_to_devices(skb, NULL);
+			} else if (ret == -1) {
+				return NET_RX_DROP;
+			} else {
+				return NET_RX_SUCCESS;
+			}
+		default:
+			break;
+		}
+	}
+
+drop_skb:
+	kfree_skb(skb);
+drop:
+	return NET_RX_DROP;
+}
+
+static struct packet_type lowpan_packet_type = {
+	.type = htons(ETH_P_IEEE802154),
+	.func = lowpan_rcv,
+};
+
+void lowpan_rx_init(void)
+{
+	dev_add_pack(&lowpan_packet_type);
+}
+
+void lowpan_rx_exit(void)
+{
+	dev_remove_pack(&lowpan_packet_type);
+}
diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c
new file mode 100644
index 0000000..2349070
--- /dev/null
+++ b/net/ieee802154/6lowpan/tx.c
@@ -0,0 +1,271 @@
+/* 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.
+ */
+
+#include <net/6lowpan.h>
+#include <net/ieee802154_netdev.h>
+
+#include "6lowpan_i.h"
+
+/* don't save pan id, it's intra pan */
+struct lowpan_addr {
+	u8 mode;
+	union {
+		/* IPv6 needs big endian here */
+		__be64 extended_addr;
+		__be16 short_addr;
+	} u;
+};
+
+struct lowpan_addr_info {
+	struct lowpan_addr daddr;
+	struct lowpan_addr saddr;
+};
+
+static inline struct
+lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
+{
+	WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info));
+	return (struct lowpan_addr_info *)(skb->data -
+			sizeof(struct lowpan_addr_info));
+}
+
+int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
+			 unsigned short type, const void *_daddr,
+			 const void *_saddr, unsigned int len)
+{
+	const u8 *saddr = _saddr;
+	const u8 *daddr = _daddr;
+	struct lowpan_addr_info *info;
+
+	/* TODO:
+	 * if this package isn't ipv6 one, where should it be routed?
+	 */
+	if (type != ETH_P_IPV6)
+		return 0;
+
+	if (!saddr)
+		saddr = dev->dev_addr;
+
+	raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8);
+	raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8);
+
+	info = lowpan_skb_priv(skb);
+
+	/* TODO: Currently we only support extended_addr */
+	info->daddr.mode = IEEE802154_ADDR_LONG;
+	memcpy(&info->daddr.u.extended_addr, daddr,
+	       sizeof(info->daddr.u.extended_addr));
+	info->saddr.mode = IEEE802154_ADDR_LONG;
+	memcpy(&info->saddr.u.extended_addr, saddr,
+	       sizeof(info->daddr.u.extended_addr));
+
+	return 0;
+}
+
+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 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);
+	}
+
+	return frag;
+}
+
+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;
+
+	raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen);
+
+	frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr);
+	if (IS_ERR(frag))
+		return -PTR_ERR(frag);
+
+	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__, " fragment dump", frag->data, frag->len);
+
+	return dev_queue_xmit(frag);
+}
+
+static int
+lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
+		       const struct ieee802154_hdr *wpan_hdr)
+{
+	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) -
+		     skb->mac_len;
+	frag_tag = htons(lowpan_dev_info(dev)->fragment_tag);
+	lowpan_dev_info(dev)->fragment_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));
+
+	payload_cap = ieee802154_max_payload(wpan_hdr);
+
+	frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE -
+			      skb_network_header_len(skb), 8);
+
+	skb_offset = skb_network_header_len(skb);
+	skb_unprocessed = skb->len - skb->mac_len - skb_offset;
+
+	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__, ntohs(frag_tag));
+		goto err;
+	}
+
+	frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1;
+	frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN;
+	frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8);
+
+	do {
+		dgram_offset += frag_len;
+		skb_offset += frag_len;
+		skb_unprocessed -= frag_len;
+		frag_len = min(frag_cap, skb_unprocessed);
+
+		frag_hdr[4] = dgram_offset >> 3;
+
+		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__, ntohs(frag_tag), skb_offset);
+			goto err;
+		}
+	} while (skb_unprocessed > frag_cap);
+
+	consume_skb(skb);
+	return NET_XMIT_SUCCESS;
+
+err:
+	kfree_skb(skb);
+	return rc;
+}
+
+static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ieee802154_addr sa, da;
+	struct ieee802154_mac_cb *cb = mac_cb_init(skb);
+	struct lowpan_addr_info info;
+	void *daddr, *saddr;
+
+	memcpy(&info, lowpan_skb_priv(skb), sizeof(info));
+
+	/* TODO: Currently we only support extended_addr */
+	daddr = &info.daddr.u.extended_addr;
+	saddr = &info.saddr.u.extended_addr;
+
+	lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len);
+
+	cb->type = IEEE802154_FC_TYPE_DATA;
+
+	/* prepare wpan address data */
+	sa.mode = IEEE802154_ADDR_LONG;
+	sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
+	sa.extended_addr = ieee802154_devaddr_from_raw(saddr);
+
+	/* intra-PAN communications */
+	da.pan_id = sa.pan_id;
+
+	/* if the destination address is the broadcast address, use the
+	 * corresponding short address
+	 */
+	if (lowpan_is_addr_broadcast((const u8 *)daddr)) {
+		da.mode = IEEE802154_ADDR_SHORT;
+		da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+		cb->ackreq = false;
+	} else {
+		da.mode = IEEE802154_ADDR_LONG;
+		da.extended_addr = ieee802154_devaddr_from_raw(daddr);
+		cb->ackreq = true;
+	}
+
+	return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
+			ETH_P_IPV6, (void *)&da, (void *)&sa, 0);
+}
+
+netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ieee802154_hdr wpan_hdr;
+	int max_single, ret;
+
+	pr_debug("package xmit\n");
+
+	/* We must take a copy of the skb before we modify/replace the ipv6
+	 * header as the header could be used elsewhere
+	 */
+	skb = skb_unshare(skb, GFP_ATOMIC);
+	if (!skb)
+		return NET_XMIT_DROP;
+
+	ret = lowpan_header(skb, dev);
+	if (ret < 0) {
+		kfree_skb(skb);
+		return NET_XMIT_DROP;
+	}
+
+	if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) {
+		kfree_skb(skb);
+		return NET_XMIT_DROP;
+	}
+
+	max_single = ieee802154_max_payload(&wpan_hdr);
+
+	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 rc < 0 ? NET_XMIT_DROP : rc;
+	}
+}
diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c
deleted file mode 100644
index dc34d84..0000000
--- a/net/ieee802154/6lowpan_rtnl.c
+++ /dev/null
@@ -1,733 +0,0 @@
-/* Copyright 2011, Siemens AG
- * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
- */
-
-/* Based on patches from Jon Smirl <jonsmirl@gmail.com>
- * Copyright (c) 2011 Jon Smirl <jonsmirl@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.
- */
-
-/* Jon's code is based on 6lowpan implementation for Contiki which is:
- * Copyright (c) 2008, Swedish Institute of Computer Science.
- * 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 name of the Institute 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 INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <linux/bitops.h>
-#include <linux/if_arp.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/netdevice.h>
-#include <linux/ieee802154.h>
-#include <net/af_ieee802154.h>
-#include <net/ieee802154_netdev.h>
-#include <net/6lowpan.h>
-#include <net/ipv6.h>
-
-#include "reassembly.h"
-
-static LIST_HEAD(lowpan_devices);
-static int lowpan_open_count;
-
-/* private device info */
-struct lowpan_dev_info {
-	struct net_device	*real_dev; /* real WPAN device ptr */
-	struct mutex		dev_list_mtx; /* mutex for list ops */
-	u16			fragment_tag;
-};
-
-struct lowpan_dev_record {
-	struct net_device *ldev;
-	struct list_head list;
-};
-
-/* don't save pan id, it's intra pan */
-struct lowpan_addr {
-	u8 mode;
-	union {
-		/* IPv6 needs big endian here */
-		__be64 extended_addr;
-		__be16 short_addr;
-	} u;
-};
-
-struct lowpan_addr_info {
-	struct lowpan_addr daddr;
-	struct lowpan_addr saddr;
-};
-
-static inline struct
-lowpan_dev_info *lowpan_dev_info(const struct net_device *dev)
-{
-	return netdev_priv(dev);
-}
-
-static inline struct
-lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
-{
-	WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct lowpan_addr_info));
-	return (struct lowpan_addr_info *)(skb->data -
-			sizeof(struct lowpan_addr_info));
-}
-
-static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
-				unsigned short type, const void *_daddr,
-				const void *_saddr, unsigned int len)
-{
-	const u8 *saddr = _saddr;
-	const u8 *daddr = _daddr;
-	struct lowpan_addr_info *info;
-
-	/* TODO:
-	 * if this package isn't ipv6 one, where should it be routed?
-	 */
-	if (type != ETH_P_IPV6)
-		return 0;
-
-	if (!saddr)
-		saddr = dev->dev_addr;
-
-	raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8);
-	raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8);
-
-	info = lowpan_skb_priv(skb);
-
-	/* TODO: Currently we only support extended_addr */
-	info->daddr.mode = IEEE802154_ADDR_LONG;
-	memcpy(&info->daddr.u.extended_addr, daddr,
-	       sizeof(info->daddr.u.extended_addr));
-	info->saddr.mode = IEEE802154_ADDR_LONG;
-	memcpy(&info->saddr.u.extended_addr, saddr,
-	       sizeof(info->daddr.u.extended_addr));
-
-	return 0;
-}
-
-static int lowpan_give_skb_to_devices(struct sk_buff *skb,
-				      struct net_device *dev)
-{
-	struct lowpan_dev_record *entry;
-	struct sk_buff *skb_cp;
-	int stat = NET_RX_SUCCESS;
-
-	skb->protocol = htons(ETH_P_IPV6);
-	skb->pkt_type = PACKET_HOST;
-
-	rcu_read_lock();
-	list_for_each_entry_rcu(entry, &lowpan_devices, list)
-		if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) {
-			skb_cp = skb_copy(skb, GFP_ATOMIC);
-			if (!skb_cp) {
-				kfree_skb(skb);
-				rcu_read_unlock();
-				return NET_RX_DROP;
-			}
-
-			skb_cp->dev = entry->ldev;
-			stat = netif_rx(skb_cp);
-			if (stat == NET_RX_DROP)
-				break;
-		}
-	rcu_read_unlock();
-
-	consume_skb(skb);
-
-	return stat;
-}
-
-static int
-iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
-{
-	u8 iphc0, iphc1;
-	struct ieee802154_addr_sa sa, da;
-	void *sap, *dap;
-
-	raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len);
-	/* at least two bytes will be used for the encoding */
-	if (skb->len < 2)
-		return -EINVAL;
-
-	if (lowpan_fetch_skb_u8(skb, &iphc0))
-		return -EINVAL;
-
-	if (lowpan_fetch_skb_u8(skb, &iphc1))
-		return -EINVAL;
-
-	ieee802154_addr_to_sa(&sa, &hdr->source);
-	ieee802154_addr_to_sa(&da, &hdr->dest);
-
-	if (sa.addr_type == IEEE802154_ADDR_SHORT)
-		sap = &sa.short_addr;
-	else
-		sap = &sa.hwaddr;
-
-	if (da.addr_type == IEEE802154_ADDR_SHORT)
-		dap = &da.short_addr;
-	else
-		dap = &da.hwaddr;
-
-	return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type,
-					IEEE802154_ADDR_LEN, dap, da.addr_type,
-					IEEE802154_ADDR_LEN, iphc0, iphc1);
-}
-
-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 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);
-	}
-
-	return frag;
-}
-
-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;
-
-	raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen);
-
-	frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr);
-	if (IS_ERR(frag))
-		return -PTR_ERR(frag);
-
-	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__, " fragment dump", frag->data, frag->len);
-
-	return dev_queue_xmit(frag);
-}
-
-static int
-lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
-		       const struct ieee802154_hdr *wpan_hdr)
-{
-	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) -
-		     skb->mac_len;
-	frag_tag = htons(lowpan_dev_info(dev)->fragment_tag);
-	lowpan_dev_info(dev)->fragment_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));
-
-	payload_cap = ieee802154_max_payload(wpan_hdr);
-
-	frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE -
-			      skb_network_header_len(skb), 8);
-
-	skb_offset = skb_network_header_len(skb);
-	skb_unprocessed = skb->len - skb->mac_len - skb_offset;
-
-	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__, ntohs(frag_tag));
-		goto err;
-	}
-
-	frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1;
-	frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN;
-	frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8);
-
-	do {
-		dgram_offset += frag_len;
-		skb_offset += frag_len;
-		skb_unprocessed -= frag_len;
-		frag_len = min(frag_cap, skb_unprocessed);
-
-		frag_hdr[4] = dgram_offset >> 3;
-
-		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__, ntohs(frag_tag), skb_offset);
-			goto err;
-		}
-	} while (skb_unprocessed > frag_cap);
-
-	consume_skb(skb);
-	return NET_XMIT_SUCCESS;
-
-err:
-	kfree_skb(skb);
-	return rc;
-}
-
-static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
-{
-	struct ieee802154_addr sa, da;
-	struct ieee802154_mac_cb *cb = mac_cb_init(skb);
-	struct lowpan_addr_info info;
-	void *daddr, *saddr;
-
-	memcpy(&info, lowpan_skb_priv(skb), sizeof(info));
-
-	/* TODO: Currently we only support extended_addr */
-	daddr = &info.daddr.u.extended_addr;
-	saddr = &info.saddr.u.extended_addr;
-
-	lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len);
-
-	cb->type = IEEE802154_FC_TYPE_DATA;
-
-	/* prepare wpan address data */
-	sa.mode = IEEE802154_ADDR_LONG;
-	sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
-	sa.extended_addr = ieee802154_devaddr_from_raw(saddr);
-
-	/* intra-PAN communications */
-	da.pan_id = sa.pan_id;
-
-	/* if the destination address is the broadcast address, use the
-	 * corresponding short address
-	 */
-	if (lowpan_is_addr_broadcast((const u8 *)daddr)) {
-		da.mode = IEEE802154_ADDR_SHORT;
-		da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
-		cb->ackreq = false;
-	} else {
-		da.mode = IEEE802154_ADDR_LONG;
-		da.extended_addr = ieee802154_devaddr_from_raw(daddr);
-		cb->ackreq = true;
-	}
-
-	return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
-			ETH_P_IPV6, (void *)&da, (void *)&sa, 0);
-}
-
-static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-	struct ieee802154_hdr wpan_hdr;
-	int max_single, ret;
-
-	pr_debug("package xmit\n");
-
-	/* We must take a copy of the skb before we modify/replace the ipv6
-	 * header as the header could be used elsewhere
-	 */
-	skb = skb_unshare(skb, GFP_ATOMIC);
-	if (!skb)
-		return NET_XMIT_DROP;
-
-	ret = lowpan_header(skb, dev);
-	if (ret < 0) {
-		kfree_skb(skb);
-		return NET_XMIT_DROP;
-	}
-
-	if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) {
-		kfree_skb(skb);
-		return NET_XMIT_DROP;
-	}
-
-	max_single = ieee802154_max_payload(&wpan_hdr);
-
-	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 rc < 0 ? NET_XMIT_DROP : rc;
-	}
-}
-
-static __le16 lowpan_get_pan_id(const struct net_device *dev)
-{
-	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
-
-	return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev);
-}
-
-static __le16 lowpan_get_short_addr(const struct net_device *dev)
-{
-	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
-
-	return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev);
-}
-
-static u8 lowpan_get_dsn(const struct net_device *dev)
-{
-	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
-
-	return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev);
-}
-
-static struct header_ops lowpan_header_ops = {
-	.create	= lowpan_header_create,
-};
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
-static struct lock_class_key lowpan_tx_busylock;
-#endif
-static struct lock_class_key lowpan_netdev_xmit_lock_key;
-
-static void lowpan_set_lockdep_class_one(struct net_device *dev,
-					 struct netdev_queue *txq,
-					 void *_unused)
-{
-	lockdep_set_class(&txq->_xmit_lock,
-			  &lowpan_netdev_xmit_lock_key);
-}
-
-static int lowpan_dev_init(struct net_device *dev)
-{
-	netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
-	dev->qdisc_tx_busylock = &lowpan_tx_busylock;
-#endif
-	return 0;
-}
-
-static const struct net_device_ops lowpan_netdev_ops = {
-	.ndo_init		= lowpan_dev_init,
-	.ndo_start_xmit		= lowpan_xmit,
-};
-
-static struct ieee802154_mlme_ops lowpan_mlme = {
-	.get_pan_id = lowpan_get_pan_id,
-	.get_short_addr = lowpan_get_short_addr,
-	.get_dsn = lowpan_get_dsn,
-};
-
-static void lowpan_setup(struct net_device *dev)
-{
-	dev->addr_len		= IEEE802154_ADDR_LEN;
-	memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
-	dev->type		= ARPHRD_IEEE802154;
-	/* Frame Control + Sequence Number + Address fields + Security Header */
-	dev->hard_header_len	= 2 + 1 + 20 + 14;
-	dev->needed_tailroom	= 2; /* FCS */
-	dev->mtu		= IPV6_MIN_MTU;
-	dev->tx_queue_len	= 0;
-	dev->flags		= IFF_BROADCAST | IFF_MULTICAST;
-	dev->watchdog_timeo	= 0;
-
-	dev->netdev_ops		= &lowpan_netdev_ops;
-	dev->header_ops		= &lowpan_header_ops;
-	dev->ml_priv		= &lowpan_mlme;
-	dev->destructor		= free_netdev;
-}
-
-static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[])
-{
-	if (tb[IFLA_ADDRESS]) {
-		if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN)
-			return -EINVAL;
-	}
-	return 0;
-}
-
-static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
-		      struct packet_type *pt, struct net_device *orig_dev)
-{
-	struct ieee802154_hdr hdr;
-	int ret;
-
-	skb = skb_share_check(skb, GFP_ATOMIC);
-	if (!skb)
-		goto drop;
-
-	if (!netif_running(dev))
-		goto drop_skb;
-
-	if (skb->pkt_type == PACKET_OTHERHOST)
-		goto drop_skb;
-
-	if (dev->type != ARPHRD_IEEE802154)
-		goto drop_skb;
-
-	if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
-		goto drop_skb;
-
-	/* check that it's our buffer */
-	if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
-		/* Pull off the 1-byte of 6lowpan header. */
-		skb_pull(skb, 1);
-		return lowpan_give_skb_to_devices(skb, NULL);
-	} else {
-		switch (skb->data[0] & 0xe0) {
-		case LOWPAN_DISPATCH_IPHC:	/* ipv6 datagram */
-			ret = iphc_decompress(skb, &hdr);
-			if (ret < 0)
-				goto drop_skb;
-
-			return lowpan_give_skb_to_devices(skb, NULL);
-		case LOWPAN_DISPATCH_FRAG1:	/* first fragment header */
-			ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1);
-			if (ret == 1) {
-				ret = iphc_decompress(skb, &hdr);
-				if (ret < 0)
-					goto drop_skb;
-
-				return lowpan_give_skb_to_devices(skb, NULL);
-			} else if (ret == -1) {
-				return NET_RX_DROP;
-			} else {
-				return NET_RX_SUCCESS;
-			}
-		case LOWPAN_DISPATCH_FRAGN:	/* next fragments headers */
-			ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN);
-			if (ret == 1) {
-				ret = iphc_decompress(skb, &hdr);
-				if (ret < 0)
-					goto drop_skb;
-
-				return lowpan_give_skb_to_devices(skb, NULL);
-			} else if (ret == -1) {
-				return NET_RX_DROP;
-			} else {
-				return NET_RX_SUCCESS;
-			}
-		default:
-			break;
-		}
-	}
-
-drop_skb:
-	kfree_skb(skb);
-drop:
-	return NET_RX_DROP;
-}
-
-static struct packet_type lowpan_packet_type = {
-	.type = htons(ETH_P_IEEE802154),
-	.func = lowpan_rcv,
-};
-
-static int lowpan_newlink(struct net *src_net, struct net_device *dev,
-			  struct nlattr *tb[], struct nlattr *data[])
-{
-	struct net_device *real_dev;
-	struct lowpan_dev_record *entry;
-	int ret;
-
-	ASSERT_RTNL();
-
-	pr_debug("adding new link\n");
-
-	if (!tb[IFLA_LINK])
-		return -EINVAL;
-	/* find and hold real wpan device */
-	real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
-	if (!real_dev)
-		return -ENODEV;
-	if (real_dev->type != ARPHRD_IEEE802154) {
-		dev_put(real_dev);
-		return -EINVAL;
-	}
-
-	lowpan_dev_info(dev)->real_dev = real_dev;
-	mutex_init(&lowpan_dev_info(dev)->dev_list_mtx);
-
-	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
-	if (!entry) {
-		dev_put(real_dev);
-		lowpan_dev_info(dev)->real_dev = NULL;
-		return -ENOMEM;
-	}
-
-	entry->ldev = dev;
-
-	/* Set the lowpan hardware address to the wpan hardware address. */
-	memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN);
-
-	mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
-	INIT_LIST_HEAD(&entry->list);
-	list_add_tail(&entry->list, &lowpan_devices);
-	mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx);
-
-	ret = register_netdevice(dev);
-	if (ret >= 0) {
-		if (!lowpan_open_count)
-			dev_add_pack(&lowpan_packet_type);
-		lowpan_open_count++;
-	}
-
-	return ret;
-}
-
-static void lowpan_dellink(struct net_device *dev, struct list_head *head)
-{
-	struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev);
-	struct net_device *real_dev = lowpan_dev->real_dev;
-	struct lowpan_dev_record *entry, *tmp;
-
-	ASSERT_RTNL();
-
-	lowpan_open_count--;
-	if (!lowpan_open_count)
-		dev_remove_pack(&lowpan_packet_type);
-
-	mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
-	list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) {
-		if (entry->ldev == dev) {
-			list_del(&entry->list);
-			kfree(entry);
-		}
-	}
-	mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx);
-
-	mutex_destroy(&lowpan_dev_info(dev)->dev_list_mtx);
-
-	unregister_netdevice_queue(dev, head);
-
-	dev_put(real_dev);
-}
-
-static struct rtnl_link_ops lowpan_link_ops __read_mostly = {
-	.kind		= "lowpan",
-	.priv_size	= sizeof(struct lowpan_dev_info),
-	.setup		= lowpan_setup,
-	.newlink	= lowpan_newlink,
-	.dellink	= lowpan_dellink,
-	.validate	= lowpan_validate,
-};
-
-static inline int __init lowpan_netlink_init(void)
-{
-	return rtnl_link_register(&lowpan_link_ops);
-}
-
-static inline void lowpan_netlink_fini(void)
-{
-	rtnl_link_unregister(&lowpan_link_ops);
-}
-
-static int lowpan_device_event(struct notifier_block *unused,
-			       unsigned long event, void *ptr)
-{
-	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
-	LIST_HEAD(del_list);
-	struct lowpan_dev_record *entry, *tmp;
-
-	if (dev->type != ARPHRD_IEEE802154)
-		goto out;
-
-	if (event == NETDEV_UNREGISTER) {
-		list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) {
-			if (lowpan_dev_info(entry->ldev)->real_dev == dev)
-				lowpan_dellink(entry->ldev, &del_list);
-		}
-
-		unregister_netdevice_many(&del_list);
-	}
-
-out:
-	return NOTIFY_DONE;
-}
-
-static struct notifier_block lowpan_dev_notifier = {
-	.notifier_call = lowpan_device_event,
-};
-
-static int __init lowpan_init_module(void)
-{
-	int err = 0;
-
-	err = lowpan_net_frag_init();
-	if (err < 0)
-		goto out;
-
-	err = lowpan_netlink_init();
-	if (err < 0)
-		goto out_frag;
-
-	err = register_netdevice_notifier(&lowpan_dev_notifier);
-	if (err < 0)
-		goto out_pack;
-
-	return 0;
-
-out_pack:
-	lowpan_netlink_fini();
-out_frag:
-	lowpan_net_frag_exit();
-out:
-	return err;
-}
-
-static void __exit lowpan_cleanup_module(void)
-{
-	lowpan_netlink_fini();
-
-	lowpan_net_frag_exit();
-
-	unregister_netdevice_notifier(&lowpan_dev_notifier);
-}
-
-module_init(lowpan_init_module);
-module_exit(lowpan_cleanup_module);
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_RTNL_LINK("lowpan");
diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig
index c509e93..c7dd1c4 100644
--- a/net/ieee802154/Kconfig
+++ b/net/ieee802154/Kconfig
@@ -1,4 +1,4 @@
-config IEEE802154
+menuconfig IEEE802154
 	depends on !KERNEL_3_19
 	tristate "IEEE Std 802.15.4 Low-Rate Wireless Personal Area Networks support"
 	depends on m
@@ -12,10 +12,17 @@
 	  Say Y here to compile LR-WPAN support into the kernel or say M to
 	  compile it as modules.
 
-config IEEE802154_6LOWPAN
-	depends on !KERNEL_3_5
-	tristate "6lowpan support over IEEE 802.15.4"
+if IEEE802154
+
+config IEEE802154_SOCKET
+	tristate "IEEE 802.15.4 socket interface"
 	depends on m
-	depends on IEEE802154 && 6LOWPAN
+	default y
 	---help---
-	  IPv6 compression over IEEE 802.15.4.
+	  Socket interface for IEEE 802.15.4. Contains DGRAM sockets interface
+	  for 802.15.4 dataframes. Also RAW socket interface to build MAC
+	  header from userspace.
+
+source "net/ieee802154/6lowpan/Kconfig"
+
+endif
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index 9e6d3d6..71d7ff2 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -1,9 +1,9 @@
-obj-$(CPTCFG_IEEE802154) += ieee802154.o af_802154.o
-obj-$(CPTCFG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o
+obj-$(CPTCFG_IEEE802154) += ieee802154.o
+obj-$(CPTCFG_IEEE802154_SOCKET) += ieee802154_socket.o
+obj-y += 6lowpan/
 
-ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o
 ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \
                 header_ops.o sysfs.o nl802154.o
-af_802154-y := af_ieee802154.o raw.o dgram.o
+ieee802154_socket-y := socket.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/ieee802154/af802154.h b/net/ieee802154/af802154.h
deleted file mode 100644
index 343b63e..0000000
--- a/net/ieee802154/af802154.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Internal interfaces for ieee 802.15.4 address family.
- *
- * Copyright 2007, 2008, 2009 Siemens AG
- *
- * 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:
- * Sergey Lapin <slapin@ossfans.org>
- * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
- */
-
-#ifndef AF802154_H
-#define AF802154_H
-
-struct sk_buff;
-struct net_device;
-struct ieee802154_addr;
-extern struct proto ieee802154_raw_prot;
-extern struct proto ieee802154_dgram_prot;
-void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb);
-int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb);
-struct net_device *ieee802154_get_dev(struct net *net,
-				      const struct ieee802154_addr *addr);
-
-#endif
diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c
deleted file mode 100644
index d0a1282..0000000
--- a/net/ieee802154/af_ieee802154.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * IEEE802154.4 socket interface
- *
- * Copyright 2007, 2008 Siemens AG
- *
- * 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:
- * Sergey Lapin <slapin@ossfans.org>
- * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
- */
-
-#include <linux/net.h>
-#include <linux/capability.h>
-#include <linux/module.h>
-#include <linux/if_arp.h>
-#include <linux/if.h>
-#include <linux/termios.h>	/* For TIOCOUTQ/INQ */
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <net/datalink.h>
-#include <net/psnap.h>
-#include <net/sock.h>
-#include <net/tcp_states.h>
-#include <net/route.h>
-
-#include <net/af_ieee802154.h>
-#include <net/ieee802154_netdev.h>
-
-#include "af802154.h"
-
-/* Utility function for families */
-struct net_device*
-ieee802154_get_dev(struct net *net, const struct ieee802154_addr *addr)
-{
-	struct net_device *dev = NULL;
-	struct net_device *tmp;
-	__le16 pan_id, short_addr;
-	u8 hwaddr[IEEE802154_ADDR_LEN];
-
-	switch (addr->mode) {
-	case IEEE802154_ADDR_LONG:
-		ieee802154_devaddr_to_raw(hwaddr, addr->extended_addr);
-		rcu_read_lock();
-		dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, hwaddr);
-		if (dev)
-			dev_hold(dev);
-		rcu_read_unlock();
-		break;
-	case IEEE802154_ADDR_SHORT:
-		if (addr->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST) ||
-		    addr->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
-		    addr->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST))
-			break;
-
-		rtnl_lock();
-
-		for_each_netdev(net, tmp) {
-			if (tmp->type != ARPHRD_IEEE802154)
-				continue;
-
-			pan_id = ieee802154_mlme_ops(tmp)->get_pan_id(tmp);
-			short_addr =
-				ieee802154_mlme_ops(tmp)->get_short_addr(tmp);
-
-			if (pan_id == addr->pan_id &&
-			    short_addr == addr->short_addr) {
-				dev = tmp;
-				dev_hold(dev);
-				break;
-			}
-		}
-
-		rtnl_unlock();
-		break;
-	default:
-		pr_warn("Unsupported ieee802154 address type: %d\n",
-			addr->mode);
-		break;
-	}
-
-	return dev;
-}
-
-static int ieee802154_sock_release(struct socket *sock)
-{
-	struct sock *sk = sock->sk;
-
-	if (sk) {
-		sock->sk = NULL;
-		sk->sk_prot->close(sk, 0);
-	}
-	return 0;
-}
-
-static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
-				   struct msghdr *msg, size_t len)
-{
-	struct sock *sk = sock->sk;
-
-	return sk->sk_prot->sendmsg(iocb, sk, msg, len);
-}
-
-static int ieee802154_sock_bind(struct socket *sock, struct sockaddr *uaddr,
-				int addr_len)
-{
-	struct sock *sk = sock->sk;
-
-	if (sk->sk_prot->bind)
-		return sk->sk_prot->bind(sk, uaddr, addr_len);
-
-	return sock_no_bind(sock, uaddr, addr_len);
-}
-
-static int ieee802154_sock_connect(struct socket *sock, struct sockaddr *uaddr,
-				   int addr_len, int flags)
-{
-	struct sock *sk = sock->sk;
-
-	if (addr_len < sizeof(uaddr->sa_family))
-		return -EINVAL;
-
-	if (uaddr->sa_family == AF_UNSPEC)
-		return sk->sk_prot->disconnect(sk, flags);
-
-	return sk->sk_prot->connect(sk, uaddr, addr_len);
-}
-
-static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
-				unsigned int cmd)
-{
-	struct ifreq ifr;
-	int ret = -ENOIOCTLCMD;
-	struct net_device *dev;
-
-	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
-		return -EFAULT;
-
-	ifr.ifr_name[IFNAMSIZ-1] = 0;
-
-	dev_load(sock_net(sk), ifr.ifr_name);
-	dev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
-
-	if (!dev)
-		return -ENODEV;
-
-	if (dev->type == ARPHRD_IEEE802154 && dev->netdev_ops->ndo_do_ioctl)
-		ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd);
-
-	if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
-		ret = -EFAULT;
-	dev_put(dev);
-
-	return ret;
-}
-
-static int ieee802154_sock_ioctl(struct socket *sock, unsigned int cmd,
-				 unsigned long arg)
-{
-	struct sock *sk = sock->sk;
-
-	switch (cmd) {
-	case SIOCGSTAMP:
-		return sock_get_timestamp(sk, (struct timeval __user *)arg);
-	case SIOCGSTAMPNS:
-		return sock_get_timestampns(sk, (struct timespec __user *)arg);
-	case SIOCGIFADDR:
-	case SIOCSIFADDR:
-		return ieee802154_dev_ioctl(sk, (struct ifreq __user *)arg,
-				cmd);
-	default:
-		if (!sk->sk_prot->ioctl)
-			return -ENOIOCTLCMD;
-		return sk->sk_prot->ioctl(sk, cmd, arg);
-	}
-}
-
-static const struct proto_ops ieee802154_raw_ops = {
-	.family		   = PF_IEEE802154,
-	.owner		   = THIS_MODULE,
-	.release	   = ieee802154_sock_release,
-	.bind		   = ieee802154_sock_bind,
-	.connect	   = ieee802154_sock_connect,
-	.socketpair	   = sock_no_socketpair,
-	.accept		   = sock_no_accept,
-	.getname	   = sock_no_getname,
-	.poll		   = datagram_poll,
-	.ioctl		   = ieee802154_sock_ioctl,
-	.listen		   = sock_no_listen,
-	.shutdown	   = sock_no_shutdown,
-	.setsockopt	   = sock_common_setsockopt,
-	.getsockopt	   = sock_common_getsockopt,
-	.sendmsg	   = ieee802154_sock_sendmsg,
-	.recvmsg	   = sock_common_recvmsg,
-	.mmap		   = sock_no_mmap,
-	.sendpage	   = sock_no_sendpage,
-#ifdef CONFIG_COMPAT
-	.compat_setsockopt = compat_sock_common_setsockopt,
-	.compat_getsockopt = compat_sock_common_getsockopt,
-#endif
-};
-
-static const struct proto_ops ieee802154_dgram_ops = {
-	.family		   = PF_IEEE802154,
-	.owner		   = THIS_MODULE,
-	.release	   = ieee802154_sock_release,
-	.bind		   = ieee802154_sock_bind,
-	.connect	   = ieee802154_sock_connect,
-	.socketpair	   = sock_no_socketpair,
-	.accept		   = sock_no_accept,
-	.getname	   = sock_no_getname,
-	.poll		   = datagram_poll,
-	.ioctl		   = ieee802154_sock_ioctl,
-	.listen		   = sock_no_listen,
-	.shutdown	   = sock_no_shutdown,
-	.setsockopt	   = sock_common_setsockopt,
-	.getsockopt	   = sock_common_getsockopt,
-	.sendmsg	   = ieee802154_sock_sendmsg,
-	.recvmsg	   = sock_common_recvmsg,
-	.mmap		   = sock_no_mmap,
-	.sendpage	   = sock_no_sendpage,
-#ifdef CONFIG_COMPAT
-	.compat_setsockopt = compat_sock_common_setsockopt,
-	.compat_getsockopt = compat_sock_common_getsockopt,
-#endif
-};
-
-/* Create a socket. Initialise the socket, blank the addresses
- * set the state.
- */
-static int ieee802154_create(struct net *net, struct socket *sock,
-			     int protocol, int kern)
-{
-	struct sock *sk;
-	int rc;
-	struct proto *proto;
-	const struct proto_ops *ops;
-
-	if (!net_eq(net, &init_net))
-		return -EAFNOSUPPORT;
-
-	switch (sock->type) {
-	case SOCK_RAW:
-		proto = &ieee802154_raw_prot;
-		ops = &ieee802154_raw_ops;
-		break;
-	case SOCK_DGRAM:
-		proto = &ieee802154_dgram_prot;
-		ops = &ieee802154_dgram_ops;
-		break;
-	default:
-		rc = -ESOCKTNOSUPPORT;
-		goto out;
-	}
-
-	rc = -ENOMEM;
-	sk = sk_alloc(net, PF_IEEE802154, GFP_KERNEL, proto);
-	if (!sk)
-		goto out;
-	rc = 0;
-
-	sock->ops = ops;
-
-	sock_init_data(sock, sk);
-	/* FIXME: sk->sk_destruct */
-	sk->sk_family = PF_IEEE802154;
-
-	/* Checksums on by default */
-	sock_set_flag(sk, SOCK_ZAPPED);
-
-	if (sk->sk_prot->hash)
-		sk->sk_prot->hash(sk);
-
-	if (sk->sk_prot->init) {
-		rc = sk->sk_prot->init(sk);
-		if (rc)
-			sk_common_release(sk);
-	}
-out:
-	return rc;
-}
-
-static const struct net_proto_family ieee802154_family_ops = {
-	.family		= PF_IEEE802154,
-	.create		= ieee802154_create,
-	.owner		= THIS_MODULE,
-};
-
-static int ieee802154_rcv(struct sk_buff *skb, struct net_device *dev,
-			  struct packet_type *pt, struct net_device *orig_dev)
-{
-	if (!netif_running(dev))
-		goto drop;
-	pr_debug("got frame, type %d, dev %p\n", dev->type, dev);
-#ifdef DEBUG
-	print_hex_dump_bytes("ieee802154_rcv ",
-			     DUMP_PREFIX_NONE, skb->data, skb->len);
-#endif
-
-	if (!net_eq(dev_net(dev), &init_net))
-		goto drop;
-
-	ieee802154_raw_deliver(dev, skb);
-
-	if (dev->type != ARPHRD_IEEE802154)
-		goto drop;
-
-	if (skb->pkt_type != PACKET_OTHERHOST)
-		return ieee802154_dgram_deliver(dev, skb);
-
-drop:
-	kfree_skb(skb);
-	return NET_RX_DROP;
-}
-
-static struct packet_type ieee802154_packet_type = {
-	.type = htons(ETH_P_IEEE802154),
-	.func = ieee802154_rcv,
-};
-
-static int __init af_ieee802154_init(void)
-{
-	int rc = -EINVAL;
-
-	rc = proto_register(&ieee802154_raw_prot, 1);
-	if (rc)
-		goto out;
-
-	rc = proto_register(&ieee802154_dgram_prot, 1);
-	if (rc)
-		goto err_dgram;
-
-	/* Tell SOCKET that we are alive */
-	rc = sock_register(&ieee802154_family_ops);
-	if (rc)
-		goto err_sock;
-	dev_add_pack(&ieee802154_packet_type);
-
-	rc = 0;
-	goto out;
-
-err_sock:
-	proto_unregister(&ieee802154_dgram_prot);
-err_dgram:
-	proto_unregister(&ieee802154_raw_prot);
-out:
-	return rc;
-}
-
-static void __exit af_ieee802154_remove(void)
-{
-	dev_remove_pack(&ieee802154_packet_type);
-	sock_unregister(PF_IEEE802154);
-	proto_unregister(&ieee802154_dgram_prot);
-	proto_unregister(&ieee802154_raw_prot);
-}
-
-module_init(af_ieee802154_init);
-module_exit(af_ieee802154_remove);
-
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_NETPROTO(PF_IEEE802154);
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
index 18bc7e7..2ee00e8 100644
--- a/net/ieee802154/core.c
+++ b/net/ieee802154/core.c
@@ -25,6 +25,9 @@
 #include "sysfs.h"
 #include "core.h"
 
+/* name for sysfs, %d is appended */
+#define PHY_NAME "phy"
+
 /* RCU-protected (and RTNL for writers) */
 LIST_HEAD(cfg802154_rdev_list);
 int cfg802154_rdev_list_generation;
@@ -122,7 +125,7 @@
 
 	INIT_LIST_HEAD(&rdev->wpan_dev_list);
 	device_initialize(&rdev->wpan_phy.dev);
-	dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx);
+	dev_set_name(&rdev->wpan_phy.dev, PHY_NAME "%d", rdev->wpan_phy_idx);
 
 	rdev->wpan_phy.dev.class = &wpan_phy_class;
 	rdev->wpan_phy.dev.platform_data = rdev;
@@ -225,6 +228,7 @@
 	switch (state) {
 		/* TODO NETDEV_DEVTYPE */
 	case NETDEV_REGISTER:
+		dev->features |= NETIF_F_NETNS_LOCAL;
 		wpan_dev->identifier = ++rdev->wpan_dev_id;
 		list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
 		rdev->devlist_generation++;
diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c
deleted file mode 100644
index a866820..0000000
--- a/net/ieee802154/dgram.c
+++ /dev/null
@@ -1,559 +0,0 @@
-/*
- * IEEE 802.15.4 dgram socket interface
- *
- * Copyright 2007, 2008 Siemens AG
- *
- * 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:
- * Sergey Lapin <slapin@ossfans.org>
- * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
- */
-
-#include <linux/capability.h>
-#include <linux/net.h>
-#include <linux/module.h>
-#include <linux/if_arp.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/ieee802154.h>
-#include <net/sock.h>
-#include <net/af_ieee802154.h>
-#include <net/ieee802154_netdev.h>
-
-#include <asm/ioctls.h>
-
-#include "af802154.h"
-
-static HLIST_HEAD(dgram_head);
-static DEFINE_RWLOCK(dgram_lock);
-
-struct dgram_sock {
-	struct sock sk;
-
-	struct ieee802154_addr src_addr;
-	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)
-{
-	return container_of(sk, struct dgram_sock, sk);
-}
-
-static void dgram_hash(struct sock *sk)
-{
-	write_lock_bh(&dgram_lock);
-	sk_add_node(sk, &dgram_head);
-	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
-	write_unlock_bh(&dgram_lock);
-}
-
-static void dgram_unhash(struct sock *sk)
-{
-	write_lock_bh(&dgram_lock);
-	if (sk_del_node_init(sk))
-		sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
-	write_unlock_bh(&dgram_lock);
-}
-
-static int dgram_init(struct sock *sk)
-{
-	struct dgram_sock *ro = dgram_sk(sk);
-
-	ro->want_ack = 1;
-	return 0;
-}
-
-static void dgram_close(struct sock *sk, long timeout)
-{
-	sk_common_release(sk);
-}
-
-static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
-{
-	struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
-	struct ieee802154_addr haddr;
-	struct dgram_sock *ro = dgram_sk(sk);
-	int err = -EINVAL;
-	struct net_device *dev;
-
-	lock_sock(sk);
-
-	ro->bound = 0;
-
-	if (len < sizeof(*addr))
-		goto out;
-
-	if (addr->family != AF_IEEE802154)
-		goto out;
-
-	ieee802154_addr_from_sa(&haddr, &addr->addr);
-	dev = ieee802154_get_dev(sock_net(sk), &haddr);
-	if (!dev) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	if (dev->type != ARPHRD_IEEE802154) {
-		err = -ENODEV;
-		goto out_put;
-	}
-
-	ro->src_addr = haddr;
-
-	ro->bound = 1;
-	err = 0;
-out_put:
-	dev_put(dev);
-out:
-	release_sock(sk);
-
-	return err;
-}
-
-static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
-{
-	switch (cmd) {
-	case SIOCOUTQ:
-	{
-		int amount = sk_wmem_alloc_get(sk);
-
-		return put_user(amount, (int __user *)arg);
-	}
-
-	case SIOCINQ:
-	{
-		struct sk_buff *skb;
-		unsigned long amount;
-
-		amount = 0;
-		spin_lock_bh(&sk->sk_receive_queue.lock);
-		skb = skb_peek(&sk->sk_receive_queue);
-		if (skb != NULL) {
-			/* We will only return the amount
-			 * of this packet since that is all
-			 * that will be read.
-			 */
-			amount = skb->len - ieee802154_hdr_length(skb);
-		}
-		spin_unlock_bh(&sk->sk_receive_queue.lock);
-		return put_user(amount, (int __user *)arg);
-	}
-	}
-
-	return -ENOIOCTLCMD;
-}
-
-/* FIXME: autobind */
-static int dgram_connect(struct sock *sk, struct sockaddr *uaddr,
-			 int len)
-{
-	struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
-	struct dgram_sock *ro = dgram_sk(sk);
-	int err = 0;
-
-	if (len < sizeof(*addr))
-		return -EINVAL;
-
-	if (addr->family != AF_IEEE802154)
-		return -EINVAL;
-
-	lock_sock(sk);
-
-	if (!ro->bound) {
-		err = -ENETUNREACH;
-		goto out;
-	}
-
-	ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr);
-	ro->connected = 1;
-
-out:
-	release_sock(sk);
-	return err;
-}
-
-static int dgram_disconnect(struct sock *sk, int flags)
-{
-	struct dgram_sock *ro = dgram_sk(sk);
-
-	lock_sock(sk);
-	ro->connected = 0;
-	release_sock(sk);
-
-	return 0;
-}
-
-static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
-			 struct msghdr *msg, size_t size)
-{
-	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;
-
-	if (msg->msg_flags & MSG_OOB) {
-		pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
-		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
-		dev = ieee802154_get_dev(sock_net(sk), &ro->src_addr);
-
-	if (!dev) {
-		pr_debug("no dev\n");
-		err = -ENXIO;
-		goto out;
-	}
-	mtu = dev->mtu;
-	pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
-
-	if (size > mtu) {
-		pr_debug("size = %Zu, mtu = %u\n", size, mtu);
-		err = -EMSGSIZE;
-		goto out_dev;
-	}
-
-	hlen = LL_RESERVED_SPACE(dev);
-	tlen = dev->needed_tailroom;
-	skb = sock_alloc_send_skb(sk, hlen + tlen + size,
-				  msg->msg_flags & MSG_DONTWAIT,
-				  &err);
-	if (!skb)
-		goto out_dev;
-
-	skb_reserve(skb, hlen);
-
-	skb_reset_network_header(skb);
-
-	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);
-
-		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;
-
-	err = memcpy_from_msg(skb_put(skb, size), msg, size);
-	if (err < 0)
-		goto out_skb;
-
-	skb->dev = dev;
-	skb->sk  = sk;
-	skb->protocol = htons(ETH_P_IEEE802154);
-
-	dev_put(dev);
-
-	err = dev_queue_xmit(skb);
-	if (err > 0)
-		err = net_xmit_errno(err);
-
-	return err ?: size;
-
-out_skb:
-	kfree_skb(skb);
-out_dev:
-	dev_put(dev);
-out:
-	return err;
-}
-
-static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,
-			 struct msghdr *msg, size_t len, int noblock,
-			 int flags, int *addr_len)
-{
-	size_t copied = 0;
-	int err = -EOPNOTSUPP;
-	struct sk_buff *skb;
-	DECLARE_SOCKADDR(struct sockaddr_ieee802154 *, saddr, msg->msg_name);
-
-	skb = skb_recv_datagram(sk, flags, noblock, &err);
-	if (!skb)
-		goto out;
-
-	copied = skb->len;
-	if (len < copied) {
-		msg->msg_flags |= MSG_TRUNC;
-		copied = len;
-	}
-
-	/* FIXME: skip headers if necessary ?! */
-	err = skb_copy_datagram_msg(skb, 0, msg, copied);
-	if (err)
-		goto done;
-
-	sock_recv_ts_and_drops(msg, sk, skb);
-
-	if (saddr) {
-		saddr->family = AF_IEEE802154;
-		ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source);
-		*addr_len = sizeof(*saddr);
-	}
-
-	if (flags & MSG_TRUNC)
-		copied = skb->len;
-done:
-	skb_free_datagram(sk, skb);
-out:
-	if (err)
-		return err;
-	return copied;
-}
-
-static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb)
-{
-	skb = skb_share_check(skb, GFP_ATOMIC);
-	if (!skb)
-		return NET_RX_DROP;
-
-	if (sock_queue_rcv_skb(sk, skb) < 0) {
-		kfree_skb(skb);
-		return NET_RX_DROP;
-	}
-
-	return NET_RX_SUCCESS;
-}
-
-static inline bool
-ieee802154_match_sock(__le64 hw_addr, __le16 pan_id, __le16 short_addr,
-		      struct dgram_sock *ro)
-{
-	if (!ro->bound)
-		return true;
-
-	if (ro->src_addr.mode == IEEE802154_ADDR_LONG &&
-	    hw_addr == ro->src_addr.extended_addr)
-		return true;
-
-	if (ro->src_addr.mode == IEEE802154_ADDR_SHORT &&
-	    pan_id == ro->src_addr.pan_id &&
-	    short_addr == ro->src_addr.short_addr)
-		return true;
-
-	return false;
-}
-
-int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
-{
-	struct sock *sk, *prev = NULL;
-	int ret = NET_RX_SUCCESS;
-	__le16 pan_id, short_addr;
-	__le64 hw_addr;
-
-	/* Data frame processing */
-	BUG_ON(dev->type != ARPHRD_IEEE802154);
-
-	pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
-	short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev);
-	hw_addr = ieee802154_devaddr_from_raw(dev->dev_addr);
-
-	read_lock(&dgram_lock);
-	sk_for_each(sk, &dgram_head) {
-		if (ieee802154_match_sock(hw_addr, pan_id, short_addr,
-					  dgram_sk(sk))) {
-			if (prev) {
-				struct sk_buff *clone;
-
-				clone = skb_clone(skb, GFP_ATOMIC);
-				if (clone)
-					dgram_rcv_skb(prev, clone);
-			}
-
-			prev = sk;
-		}
-	}
-
-	if (prev) {
-		dgram_rcv_skb(prev, skb);
-	} else {
-		kfree_skb(skb);
-		ret = NET_RX_DROP;
-	}
-	read_unlock(&dgram_lock);
-
-	return ret;
-}
-
-static int dgram_getsockopt(struct sock *sk, int level, int optname,
-			    char __user *optval, int __user *optlen)
-{
-	struct dgram_sock *ro = dgram_sk(sk);
-
-	int val, len;
-
-	if (level != SOL_IEEE802154)
-		return -EOPNOTSUPP;
-
-	if (get_user(len, optlen))
-		return -EFAULT;
-
-	len = min_t(unsigned int, len, sizeof(int));
-
-	switch (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;
-	}
-
-	if (put_user(len, optlen))
-		return -EFAULT;
-	if (copy_to_user(optval, &val, len))
-		return -EFAULT;
-	return 0;
-}
-
-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;
-
-	if (optlen < sizeof(int))
-		return -EINVAL;
-
-	if (get_user(val, (int __user *)optval))
-		return -EFAULT;
-
-	lock_sock(sk);
-
-	switch (optname) {
-	case WPAN_WANTACK:
-		ro->want_ack = !!val;
-		break;
-	case WPAN_SECURITY:
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
-		if (!ns_capable(net->user_ns, CAP_NET_ADMIN) &&
-		    !ns_capable(net->user_ns, CAP_NET_RAW)) {
-#else
-		if (!capable(CAP_NET_ADMIN) &&
-		    !capable(CAP_NET_RAW)) {
-#endif
-			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 LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
-		if (!ns_capable(net->user_ns, CAP_NET_ADMIN) &&
-		    !ns_capable(net->user_ns, CAP_NET_RAW)) {
-#else
-		if (!capable(CAP_NET_ADMIN) &&
-		    !capable(CAP_NET_RAW)) {
-#endif
-			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;
-	}
-
-	release_sock(sk);
-	return err;
-}
-
-struct proto ieee802154_dgram_prot = {
-	.name		= "IEEE-802.15.4-MAC",
-	.owner		= THIS_MODULE,
-	.obj_size	= sizeof(struct dgram_sock),
-	.init		= dgram_init,
-	.close		= dgram_close,
-	.bind		= dgram_bind,
-	.sendmsg	= dgram_sendmsg,
-	.recvmsg	= dgram_recvmsg,
-	.hash		= dgram_hash,
-	.unhash		= dgram_unhash,
-	.connect	= dgram_connect,
-	.disconnect	= dgram_disconnect,
-	.ioctl		= dgram_ioctl,
-	.getsockopt	= dgram_getsockopt,
-	.setsockopt	= dgram_setsockopt,
-};
-
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
index 78cc7cd..7b292fe 100644
--- a/net/ieee802154/netlink.c
+++ b/net/ieee802154/netlink.c
@@ -63,13 +63,9 @@
 	struct nlmsghdr *nlh = nlmsg_hdr(msg);
 	void *hdr = genlmsg_data(nlmsg_data(nlh));
 
-	if (genlmsg_end(msg, hdr) < 0)
-		goto out;
+	genlmsg_end(msg, hdr);
 
 	return genlmsg_multicast(&nl802154_family, msg, 0, group, GFP_ATOMIC);
-out:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,
@@ -96,13 +92,9 @@
 	struct nlmsghdr *nlh = nlmsg_hdr(msg);
 	void *hdr = genlmsg_data(nlmsg_data(nlh));
 
-	if (genlmsg_end(msg, hdr) < 0)
-		goto out;
+	genlmsg_end(msg, hdr);
 
 	return genlmsg_reply(msg, info);
-out:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 static __genl_const struct genl_ops ieee8021154_ops[] = {
diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c
index 25ff57e..585bf8f 100644
--- a/net/ieee802154/nl-mac.c
+++ b/net/ieee802154/nl-mac.c
@@ -76,7 +76,6 @@
 	nlmsg_free(msg);
 	return -ENOBUFS;
 }
-EXPORT_SYMBOL(ieee802154_nl_start_confirm);
 
 static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
 				    u32 seq, int flags, struct net_device *dev)
@@ -121,7 +120,7 @@
 			       params.transmit_power) ||
 		    nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, params.lbt) ||
 		    nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE,
-			       params.cca_mode) ||
+			       params.cca.mode) ||
 		    nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL,
 				params.cca_ed_level) ||
 		    nla_put_u8(msg, IEEE802154_ATTR_CSMA_RETRIES,
@@ -136,7 +135,8 @@
 	}
 
 	wpan_phy_put(phy);
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
 nla_put_failure:
 	wpan_phy_put(phy);
@@ -423,8 +423,7 @@
 	if (!msg)
 		goto out_dev;
 
-	rc = ieee802154_nl_fill_iface(msg, genl_info_snd_portid(info),
-				      info->snd_seq,
+	rc = ieee802154_nl_fill_iface(msg, genl_info_snd_portid(info), info->snd_seq,
 				      0, dev);
 	if (rc < 0)
 		goto out_free;
@@ -517,7 +516,7 @@
 		params.lbt = nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]);
 
 	if (info->attrs[IEEE802154_ATTR_CCA_MODE])
-		params.cca_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]);
+		params.cca.mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]);
 
 	if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL])
 		params.cca_ed_level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]);
diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c
index 2b8b724..97603e6 100644
--- a/net/ieee802154/nl-phy.c
+++ b/net/ieee802154/nl-phy.c
@@ -65,7 +65,8 @@
 		goto nla_put_failure;
 	mutex_unlock(&phy->pib_lock);
 	kfree(buf);
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
 nla_put_failure:
 	mutex_unlock(&phy->pib_lock);
@@ -102,8 +103,7 @@
 	if (!msg)
 		goto out_dev;
 
-	rc = ieee802154_nl_fill_phy(msg, genl_info_snd_portid(info),
-				    info->snd_seq,
+	rc = ieee802154_nl_fill_phy(msg, genl_info_snd_portid(info), info->snd_seq,
 				    0, phy);
 	if (rc < 0)
 		goto out_free;
diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c
index 47be111..1e55afb 100644
--- a/net/ieee802154/nl802154.c
+++ b/net/ieee802154/nl802154.c
@@ -26,10 +26,10 @@
 #include "rdev-ops.h"
 #include "core.h"
 
-static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+static int nl802154_pre_doit(__genl_const struct genl_ops *ops, struct sk_buff *skb,
 			     struct genl_info *info);
 
-static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+static void nl802154_post_doit(__genl_const struct genl_ops *ops, struct sk_buff *skb,
 			       struct genl_info *info);
 
 /* the netlink family */
@@ -49,7 +49,7 @@
 	NL802154_MCGRP_CONFIG,
 };
 
-static const struct genl_multicast_group nl802154_mcgrps[] = {
+static __genl_const struct genl_multicast_group nl802154_mcgrps[] = {
 	[NL802154_MCGRP_CONFIG] = { .name = "config", },
 };
 
@@ -209,7 +209,8 @@
 
 	[NL802154_ATTR_TX_POWER] = { .type = NLA_S8, },
 
-	[NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, },
+	[NL802154_ATTR_CCA_MODE] = { .type = NLA_U32, },
+	[NL802154_ATTR_CCA_OPT] = { .type = NLA_U32, },
 
 	[NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
 
@@ -290,16 +291,23 @@
 		goto nla_put_failure;
 
 	/* cca mode */
-	if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE,
-		       rdev->wpan_phy.cca_mode))
+	if (nla_put_u32(msg, NL802154_ATTR_CCA_MODE,
+			rdev->wpan_phy.cca.mode))
 		goto nla_put_failure;
 
+	if (rdev->wpan_phy.cca.mode == NL802154_CCA_ENERGY_CARRIER) {
+		if (nla_put_u32(msg, NL802154_ATTR_CCA_OPT,
+				rdev->wpan_phy.cca.opt))
+			goto nla_put_failure;
+	}
+
 	if (nla_put_s8(msg, NL802154_ATTR_TX_POWER,
 		       rdev->wpan_phy.transmit_power))
 		goto nla_put_failure;
 
 finish:
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
 nla_put_failure:
 	genlmsg_cancel(msg, hdr);
@@ -421,7 +429,8 @@
 		return -ENOMEM;
 
 	if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
-				   genl_info_snd_portid(info), info->snd_seq, 0) < 0) {
+				   genl_info_snd_portid(info), info->snd_seq,
+				   0) < 0) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
 	}
@@ -482,7 +491,8 @@
 	if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
 		goto nla_put_failure;
 
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
 nla_put_failure:
 	genlmsg_cancel(msg, hdr);
@@ -622,6 +632,31 @@
 	return rdev_set_channel(rdev, page, channel);
 }
 
+static int nl802154_set_cca_mode(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct wpan_phy_cca cca;
+
+	if (!info->attrs[NL802154_ATTR_CCA_MODE])
+		return -EINVAL;
+
+	cca.mode = nla_get_u32(info->attrs[NL802154_ATTR_CCA_MODE]);
+	/* checking 802.15.4 constraints */
+	if (cca.mode < NL802154_CCA_ENERGY || cca.mode > NL802154_CCA_ATTR_MAX)
+		return -EINVAL;
+
+	if (cca.mode == NL802154_CCA_ENERGY_CARRIER) {
+		if (!info->attrs[NL802154_ATTR_CCA_OPT])
+			return -EINVAL;
+
+		cca.opt = nla_get_u32(info->attrs[NL802154_ATTR_CCA_OPT]);
+		if (cca.opt > NL802154_CCA_OPT_ATTR_MAX)
+			return -EINVAL;
+	}
+
+	return rdev_set_cca_mode(rdev, &cca);
+}
+
 static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
@@ -770,7 +805,7 @@
 #define NL802154_FLAG_NEED_WPAN_DEV_UP	(NL802154_FLAG_NEED_WPAN_DEV |\
 					 NL802154_FLAG_CHECK_NETDEV_UP)
 
-static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+static int nl802154_pre_doit(__genl_const struct genl_ops *ops, struct sk_buff *skb,
 			     struct genl_info *info)
 {
 	struct cfg802154_registered_device *rdev;
@@ -832,7 +867,7 @@
 	return 0;
 }
 
-static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+static void nl802154_post_doit(__genl_const struct genl_ops *ops, struct sk_buff *skb,
 			       struct genl_info *info)
 {
 	if (info->user_ptr[1]) {
@@ -850,7 +885,7 @@
 		rtnl_unlock();
 }
 
-static const struct genl_ops nl802154_ops[] = {
+static __genl_const struct genl_ops nl802154_ops[] = {
 	{
 		.cmd = NL802154_CMD_GET_WPAN_PHY,
 		.doit = nl802154_get_wpan_phy,
@@ -895,6 +930,14 @@
 				  NL802154_FLAG_NEED_RTNL,
 	},
 	{
+		.cmd = NL802154_CMD_SET_CCA_MODE,
+		.doit = nl802154_set_cca_mode,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
 		.cmd = NL802154_CMD_SET_PAN_ID,
 		.doit = nl802154_set_pan_id,
 		.policy = nl802154_policy,
diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c
deleted file mode 100644
index 1674b11..0000000
--- a/net/ieee802154/raw.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Raw IEEE 802.15.4 sockets
- *
- * Copyright 2007, 2008 Siemens AG
- *
- * 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:
- * Sergey Lapin <slapin@ossfans.org>
- * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
- */
-
-#include <linux/net.h>
-#include <linux/module.h>
-#include <linux/if_arp.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <net/sock.h>
-#include <net/af_ieee802154.h>
-#include <net/ieee802154_netdev.h>
-
-#include "af802154.h"
-
-static HLIST_HEAD(raw_head);
-static DEFINE_RWLOCK(raw_lock);
-
-static void raw_hash(struct sock *sk)
-{
-	write_lock_bh(&raw_lock);
-	sk_add_node(sk, &raw_head);
-	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
-	write_unlock_bh(&raw_lock);
-}
-
-static void raw_unhash(struct sock *sk)
-{
-	write_lock_bh(&raw_lock);
-	if (sk_del_node_init(sk))
-		sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
-	write_unlock_bh(&raw_lock);
-}
-
-static void raw_close(struct sock *sk, long timeout)
-{
-	sk_common_release(sk);
-}
-
-static int raw_bind(struct sock *sk, struct sockaddr *_uaddr, int len)
-{
-	struct ieee802154_addr addr;
-	struct sockaddr_ieee802154 *uaddr = (struct sockaddr_ieee802154 *)_uaddr;
-	int err = 0;
-	struct net_device *dev = NULL;
-
-	if (len < sizeof(*uaddr))
-		return -EINVAL;
-
-	uaddr = (struct sockaddr_ieee802154 *)_uaddr;
-	if (uaddr->family != AF_IEEE802154)
-		return -EINVAL;
-
-	lock_sock(sk);
-
-	ieee802154_addr_from_sa(&addr, &uaddr->addr);
-	dev = ieee802154_get_dev(sock_net(sk), &addr);
-	if (!dev) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	if (dev->type != ARPHRD_IEEE802154) {
-		err = -ENODEV;
-		goto out_put;
-	}
-
-	sk->sk_bound_dev_if = dev->ifindex;
-	sk_dst_reset(sk);
-
-out_put:
-	dev_put(dev);
-out:
-	release_sock(sk);
-
-	return err;
-}
-
-static int raw_connect(struct sock *sk, struct sockaddr *uaddr,
-		       int addr_len)
-{
-	return -ENOTSUPP;
-}
-
-static int raw_disconnect(struct sock *sk, int flags)
-{
-	return 0;
-}
-
-static int raw_sendmsg(struct kiocb *iocb, struct sock *sk,
-		       struct msghdr *msg, size_t size)
-{
-	struct net_device *dev;
-	unsigned int mtu;
-	struct sk_buff *skb;
-	int hlen, tlen;
-	int err;
-
-	if (msg->msg_flags & MSG_OOB) {
-		pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
-		return -EOPNOTSUPP;
-	}
-
-	lock_sock(sk);
-	if (!sk->sk_bound_dev_if)
-		dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154);
-	else
-		dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if);
-	release_sock(sk);
-
-	if (!dev) {
-		pr_debug("no dev\n");
-		err = -ENXIO;
-		goto out;
-	}
-
-	mtu = dev->mtu;
-	pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
-
-	if (size > mtu) {
-		pr_debug("size = %Zu, mtu = %u\n", size, mtu);
-		err = -EINVAL;
-		goto out_dev;
-	}
-
-	hlen = LL_RESERVED_SPACE(dev);
-	tlen = dev->needed_tailroom;
-	skb = sock_alloc_send_skb(sk, hlen + tlen + size,
-				  msg->msg_flags & MSG_DONTWAIT, &err);
-	if (!skb)
-		goto out_dev;
-
-	skb_reserve(skb, hlen);
-
-	skb_reset_mac_header(skb);
-	skb_reset_network_header(skb);
-
-	err = memcpy_from_msg(skb_put(skb, size), msg, size);
-	if (err < 0)
-		goto out_skb;
-
-	skb->dev = dev;
-	skb->sk  = sk;
-	skb->protocol = htons(ETH_P_IEEE802154);
-
-	dev_put(dev);
-
-	err = dev_queue_xmit(skb);
-	if (err > 0)
-		err = net_xmit_errno(err);
-
-	return err ?: size;
-
-out_skb:
-	kfree_skb(skb);
-out_dev:
-	dev_put(dev);
-out:
-	return err;
-}
-
-static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
-		       size_t len, int noblock, int flags, int *addr_len)
-{
-	size_t copied = 0;
-	int err = -EOPNOTSUPP;
-	struct sk_buff *skb;
-
-	skb = skb_recv_datagram(sk, flags, noblock, &err);
-	if (!skb)
-		goto out;
-
-	copied = skb->len;
-	if (len < copied) {
-		msg->msg_flags |= MSG_TRUNC;
-		copied = len;
-	}
-
-	err = skb_copy_datagram_msg(skb, 0, msg, copied);
-	if (err)
-		goto done;
-
-	sock_recv_ts_and_drops(msg, sk, skb);
-
-	if (flags & MSG_TRUNC)
-		copied = skb->len;
-done:
-	skb_free_datagram(sk, skb);
-out:
-	if (err)
-		return err;
-	return copied;
-}
-
-static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb)
-{
-	skb = skb_share_check(skb, GFP_ATOMIC);
-	if (!skb)
-		return NET_RX_DROP;
-
-	if (sock_queue_rcv_skb(sk, skb) < 0) {
-		kfree_skb(skb);
-		return NET_RX_DROP;
-	}
-
-	return NET_RX_SUCCESS;
-}
-
-void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb)
-{
-	struct sock *sk;
-
-	read_lock(&raw_lock);
-	sk_for_each(sk, &raw_head) {
-		bh_lock_sock(sk);
-		if (!sk->sk_bound_dev_if ||
-		    sk->sk_bound_dev_if == dev->ifindex) {
-			struct sk_buff *clone;
-
-			clone = skb_clone(skb, GFP_ATOMIC);
-			if (clone)
-				raw_rcv_skb(sk, clone);
-		}
-		bh_unlock_sock(sk);
-	}
-	read_unlock(&raw_lock);
-}
-
-static int raw_getsockopt(struct sock *sk, int level, int optname,
-			  char __user *optval, int __user *optlen)
-{
-	return -EOPNOTSUPP;
-}
-
-static int raw_setsockopt(struct sock *sk, int level, int optname,
-			  char __user *optval, unsigned int optlen)
-{
-	return -EOPNOTSUPP;
-}
-
-struct proto ieee802154_raw_prot = {
-	.name		= "IEEE-802.15.4-RAW",
-	.owner		= THIS_MODULE,
-	.obj_size	= sizeof(struct sock),
-	.close		= raw_close,
-	.bind		= raw_bind,
-	.sendmsg	= raw_sendmsg,
-	.recvmsg	= raw_recvmsg,
-	.hash		= raw_hash,
-	.unhash		= raw_unhash,
-	.connect	= raw_connect,
-	.disconnect	= raw_disconnect,
-	.getsockopt	= raw_getsockopt,
-	.setsockopt	= raw_setsockopt,
-};
diff --git a/net/ieee802154/rdev-ops.h b/net/ieee802154/rdev-ops.h
index aff54fb..7c46732 100644
--- a/net/ieee802154/rdev-ops.h
+++ b/net/ieee802154/rdev-ops.h
@@ -42,6 +42,13 @@
 }
 
 static inline int
+rdev_set_cca_mode(struct cfg802154_registered_device *rdev,
+		  const struct wpan_phy_cca *cca)
+{
+	return rdev->ops->set_cca_mode(&rdev->wpan_phy, cca);
+}
+
+static inline int
 rdev_set_pan_id(struct cfg802154_registered_device *rdev,
 		struct wpan_dev *wpan_dev, __le16 pan_id)
 {
diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h
deleted file mode 100644
index 082e9d4..0000000
--- a/net/ieee802154/reassembly.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef __IEEE802154_6LOWPAN_REASSEMBLY_H__
-#define __IEEE802154_6LOWPAN_REASSEMBLY_H__
-
-#include <net/inet_frag.h>
-#include <linux/spinlock.h>
-
-struct lowpan_create_arg {
-	u16 tag;
-	u16 d_size;
-	const struct ieee802154_addr *src;
-	const struct ieee802154_addr *dst;
-};
-
-/* Equivalent of ipv4 struct ip
- */
-struct lowpan_frag_queue {
-	struct inet_frag_queue	q;
-
-	u16			tag;
-	u16			d_size;
-	struct ieee802154_addr	saddr;
-	struct ieee802154_addr	daddr;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
-	spinlock_t		lru_lock;
-#endif
-};
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
-/*
- * XXX: this is a *domain* specific inet_frag_lru_move backport,
- * note the added lowpan_ prefix, this requires a respective patch
- * which extends struct lowpan_frag_queue with an lru_lock and
- * initializes it. We add this helper here to reduce the backport.
- * There is no way to generalize the other changes in the patch.
- */
-#define inet_frag_lru_move LINUX_BACKPORT(lowpan_inet_frag_lru_move)
-static inline void inet_frag_lru_move(struct inet_frag_queue *q)
-{
-	struct lowpan_frag_queue *fq;
-
-	fq = container_of(q, struct lowpan_frag_queue, q);
-
-	spin_lock(&fq->lru_lock);
-	list_move_tail(&q->lru_list, &q->net->lru_list);
-	spin_unlock(&fq->lru_lock);
-}
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) */
-
-static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a)
-{
-	switch (a->mode) {
-	case IEEE802154_ADDR_LONG:
-		return (((__force u64)a->extended_addr) >> 32) ^
-			(((__force u64)a->extended_addr) & 0xffffffff);
-	case IEEE802154_ADDR_SHORT:
-		return (__force u32)(a->short_addr);
-	default:
-		return 0;
-	}
-}
-
-int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type);
-void lowpan_net_frag_exit(void);
-int lowpan_net_frag_init(void);
-
-#endif /* __IEEE802154_6LOWPAN_REASSEMBLY_H__ */
diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c
new file mode 100644
index 0000000..2f59d06
--- /dev/null
+++ b/net/ieee802154/socket.c
@@ -0,0 +1,1152 @@
+/*
+ * IEEE802154.4 socket interface
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * 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:
+ * Sergey Lapin <slapin@ossfans.org>
+ * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
+ */
+
+#include <linux/net.h>
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/if_arp.h>
+#include <linux/if.h>
+#include <linux/termios.h>	/* For TIOCOUTQ/INQ */
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <net/datalink.h>
+#include <net/psnap.h>
+#include <net/sock.h>
+#include <net/tcp_states.h>
+#include <net/route.h>
+
+#include <net/af_ieee802154.h>
+#include <net/ieee802154_netdev.h>
+
+/* Utility function for families */
+static struct net_device*
+ieee802154_get_dev(struct net *net, const struct ieee802154_addr *addr)
+{
+	struct net_device *dev = NULL;
+	struct net_device *tmp;
+	__le16 pan_id, short_addr;
+	u8 hwaddr[IEEE802154_ADDR_LEN];
+
+	switch (addr->mode) {
+	case IEEE802154_ADDR_LONG:
+		ieee802154_devaddr_to_raw(hwaddr, addr->extended_addr);
+		rcu_read_lock();
+		dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, hwaddr);
+		if (dev)
+			dev_hold(dev);
+		rcu_read_unlock();
+		break;
+	case IEEE802154_ADDR_SHORT:
+		if (addr->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST) ||
+		    addr->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
+		    addr->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST))
+			break;
+
+		rtnl_lock();
+
+		for_each_netdev(net, tmp) {
+			if (tmp->type != ARPHRD_IEEE802154)
+				continue;
+
+			pan_id = ieee802154_mlme_ops(tmp)->get_pan_id(tmp);
+			short_addr =
+				ieee802154_mlme_ops(tmp)->get_short_addr(tmp);
+
+			if (pan_id == addr->pan_id &&
+			    short_addr == addr->short_addr) {
+				dev = tmp;
+				dev_hold(dev);
+				break;
+			}
+		}
+
+		rtnl_unlock();
+		break;
+	default:
+		pr_warn("Unsupported ieee802154 address type: %d\n",
+			addr->mode);
+		break;
+	}
+
+	return dev;
+}
+
+static int ieee802154_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk) {
+		sock->sk = NULL;
+		sk->sk_prot->close(sk, 0);
+	}
+	return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int ieee802154_sock_sendmsg(struct socket *sock, struct msghdr *msg,
+				   size_t len)
+#else
+static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
+				   struct msghdr *msg, size_t len)
+#endif
+{
+	struct sock *sk = sock->sk;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+	return sk->sk_prot->sendmsg(sk, msg, len);
+#else
+	return sk->sk_prot->sendmsg(iocb, sk, msg, len);
+#endif
+}
+
+static int ieee802154_sock_bind(struct socket *sock, struct sockaddr *uaddr,
+				int addr_len)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk->sk_prot->bind)
+		return sk->sk_prot->bind(sk, uaddr, addr_len);
+
+	return sock_no_bind(sock, uaddr, addr_len);
+}
+
+static int ieee802154_sock_connect(struct socket *sock, struct sockaddr *uaddr,
+				   int addr_len, int flags)
+{
+	struct sock *sk = sock->sk;
+
+	if (addr_len < sizeof(uaddr->sa_family))
+		return -EINVAL;
+
+	if (uaddr->sa_family == AF_UNSPEC)
+		return sk->sk_prot->disconnect(sk, flags);
+
+	return sk->sk_prot->connect(sk, uaddr, addr_len);
+}
+
+static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
+				unsigned int cmd)
+{
+	struct ifreq ifr;
+	int ret = -ENOIOCTLCMD;
+	struct net_device *dev;
+
+	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+		return -EFAULT;
+
+	ifr.ifr_name[IFNAMSIZ-1] = 0;
+
+	dev_load(sock_net(sk), ifr.ifr_name);
+	dev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
+
+	if (!dev)
+		return -ENODEV;
+
+	if (dev->type == ARPHRD_IEEE802154 && dev->netdev_ops->ndo_do_ioctl)
+		ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd);
+
+	if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+		ret = -EFAULT;
+	dev_put(dev);
+
+	return ret;
+}
+
+static int ieee802154_sock_ioctl(struct socket *sock, unsigned int cmd,
+				 unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+
+	switch (cmd) {
+	case SIOCGSTAMP:
+		return sock_get_timestamp(sk, (struct timeval __user *)arg);
+	case SIOCGSTAMPNS:
+		return sock_get_timestampns(sk, (struct timespec __user *)arg);
+	case SIOCGIFADDR:
+	case SIOCSIFADDR:
+		return ieee802154_dev_ioctl(sk, (struct ifreq __user *)arg,
+				cmd);
+	default:
+		if (!sk->sk_prot->ioctl)
+			return -ENOIOCTLCMD;
+		return sk->sk_prot->ioctl(sk, cmd, arg);
+	}
+}
+
+/* RAW Sockets (802.15.4 created in userspace) */
+static HLIST_HEAD(raw_head);
+static DEFINE_RWLOCK(raw_lock);
+
+static void raw_hash(struct sock *sk)
+{
+	write_lock_bh(&raw_lock);
+	sk_add_node(sk, &raw_head);
+	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+	write_unlock_bh(&raw_lock);
+}
+
+static void raw_unhash(struct sock *sk)
+{
+	write_lock_bh(&raw_lock);
+	if (sk_del_node_init(sk))
+		sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+	write_unlock_bh(&raw_lock);
+}
+
+static void raw_close(struct sock *sk, long timeout)
+{
+	sk_common_release(sk);
+}
+
+static int raw_bind(struct sock *sk, struct sockaddr *_uaddr, int len)
+{
+	struct ieee802154_addr addr;
+	struct sockaddr_ieee802154 *uaddr = (struct sockaddr_ieee802154 *)_uaddr;
+	int err = 0;
+	struct net_device *dev = NULL;
+
+	if (len < sizeof(*uaddr))
+		return -EINVAL;
+
+	uaddr = (struct sockaddr_ieee802154 *)_uaddr;
+	if (uaddr->family != AF_IEEE802154)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	ieee802154_addr_from_sa(&addr, &uaddr->addr);
+	dev = ieee802154_get_dev(sock_net(sk), &addr);
+	if (!dev) {
+		err = -ENODEV;
+		goto out;
+	}
+
+	if (dev->type != ARPHRD_IEEE802154) {
+		err = -ENODEV;
+		goto out_put;
+	}
+
+	sk->sk_bound_dev_if = dev->ifindex;
+	sk_dst_reset(sk);
+
+out_put:
+	dev_put(dev);
+out:
+	release_sock(sk);
+
+	return err;
+}
+
+static int raw_connect(struct sock *sk, struct sockaddr *uaddr,
+		       int addr_len)
+{
+	return -ENOTSUPP;
+}
+
+static int raw_disconnect(struct sock *sk, int flags)
+{
+	return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
+#else
+static int raw_sendmsg(struct kiocb *iocb, struct sock *sk,
+		       struct msghdr *msg, size_t size)
+#endif
+{
+	struct net_device *dev;
+	unsigned int mtu;
+	struct sk_buff *skb;
+	int hlen, tlen;
+	int err;
+
+	if (msg->msg_flags & MSG_OOB) {
+		pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
+		return -EOPNOTSUPP;
+	}
+
+	lock_sock(sk);
+	if (!sk->sk_bound_dev_if)
+		dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154);
+	else
+		dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if);
+	release_sock(sk);
+
+	if (!dev) {
+		pr_debug("no dev\n");
+		err = -ENXIO;
+		goto out;
+	}
+
+	mtu = dev->mtu;
+	pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
+
+	if (size > mtu) {
+		pr_debug("size = %Zu, mtu = %u\n", size, mtu);
+		err = -EINVAL;
+		goto out_dev;
+	}
+
+	hlen = LL_RESERVED_SPACE(dev);
+	tlen = dev->needed_tailroom;
+	skb = sock_alloc_send_skb(sk, hlen + tlen + size,
+				  msg->msg_flags & MSG_DONTWAIT, &err);
+	if (!skb)
+		goto out_dev;
+
+	skb_reserve(skb, hlen);
+
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+
+	err = memcpy_from_msg(skb_put(skb, size), msg, size);
+	if (err < 0)
+		goto out_skb;
+
+	skb->dev = dev;
+	skb->sk  = sk;
+	skb->protocol = htons(ETH_P_IEEE802154);
+
+	dev_put(dev);
+
+	err = dev_queue_xmit(skb);
+	if (err > 0)
+		err = net_xmit_errno(err);
+
+	return err ?: size;
+
+out_skb:
+	kfree_skb(skb);
+out_dev:
+	dev_put(dev);
+out:
+	return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int raw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+		       int noblock, int flags, int *addr_len)
+#else
+static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+		       size_t len, int noblock, int flags, int *addr_len)
+#endif
+{
+	size_t copied = 0;
+	int err = -EOPNOTSUPP;
+	struct sk_buff *skb;
+
+	skb = skb_recv_datagram(sk, flags, noblock, &err);
+	if (!skb)
+		goto out;
+
+	copied = skb->len;
+	if (len < copied) {
+		msg->msg_flags |= MSG_TRUNC;
+		copied = len;
+	}
+
+	err = skb_copy_datagram_msg(skb, 0, msg, copied);
+	if (err)
+		goto done;
+
+	sock_recv_ts_and_drops(msg, sk, skb);
+
+	if (flags & MSG_TRUNC)
+		copied = skb->len;
+done:
+	skb_free_datagram(sk, skb);
+out:
+	if (err)
+		return err;
+	return copied;
+}
+
+static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+	skb = skb_share_check(skb, GFP_ATOMIC);
+	if (!skb)
+		return NET_RX_DROP;
+
+	if (sock_queue_rcv_skb(sk, skb) < 0) {
+		kfree_skb(skb);
+		return NET_RX_DROP;
+	}
+
+	return NET_RX_SUCCESS;
+}
+
+static void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb)
+{
+	struct sock *sk;
+
+	read_lock(&raw_lock);
+	sk_for_each(sk, &raw_head) {
+		bh_lock_sock(sk);
+		if (!sk->sk_bound_dev_if ||
+		    sk->sk_bound_dev_if == dev->ifindex) {
+			struct sk_buff *clone;
+
+			clone = skb_clone(skb, GFP_ATOMIC);
+			if (clone)
+				raw_rcv_skb(sk, clone);
+		}
+		bh_unlock_sock(sk);
+	}
+	read_unlock(&raw_lock);
+}
+
+static int raw_getsockopt(struct sock *sk, int level, int optname,
+			  char __user *optval, int __user *optlen)
+{
+	return -EOPNOTSUPP;
+}
+
+static int raw_setsockopt(struct sock *sk, int level, int optname,
+			  char __user *optval, unsigned int optlen)
+{
+	return -EOPNOTSUPP;
+}
+
+static struct proto ieee802154_raw_prot = {
+	.name		= "IEEE-802.15.4-RAW",
+	.owner		= THIS_MODULE,
+	.obj_size	= sizeof(struct sock),
+	.close		= raw_close,
+	.bind		= raw_bind,
+	.sendmsg	= raw_sendmsg,
+	.recvmsg	= raw_recvmsg,
+	.hash		= raw_hash,
+	.unhash		= raw_unhash,
+	.connect	= raw_connect,
+	.disconnect	= raw_disconnect,
+	.getsockopt	= raw_getsockopt,
+	.setsockopt	= raw_setsockopt,
+};
+
+static const struct proto_ops ieee802154_raw_ops = {
+	.family		   = PF_IEEE802154,
+	.owner		   = THIS_MODULE,
+	.release	   = ieee802154_sock_release,
+	.bind		   = ieee802154_sock_bind,
+	.connect	   = ieee802154_sock_connect,
+	.socketpair	   = sock_no_socketpair,
+	.accept		   = sock_no_accept,
+	.getname	   = sock_no_getname,
+	.poll		   = datagram_poll,
+	.ioctl		   = ieee802154_sock_ioctl,
+	.listen		   = sock_no_listen,
+	.shutdown	   = sock_no_shutdown,
+	.setsockopt	   = sock_common_setsockopt,
+	.getsockopt	   = sock_common_getsockopt,
+	.sendmsg	   = ieee802154_sock_sendmsg,
+	.recvmsg	   = sock_common_recvmsg,
+	.mmap		   = sock_no_mmap,
+	.sendpage	   = sock_no_sendpage,
+#ifdef CONFIG_COMPAT
+	.compat_setsockopt = compat_sock_common_setsockopt,
+	.compat_getsockopt = compat_sock_common_getsockopt,
+#endif
+};
+
+/* DGRAM Sockets (802.15.4 dataframes) */
+static HLIST_HEAD(dgram_head);
+static DEFINE_RWLOCK(dgram_lock);
+
+struct dgram_sock {
+	struct sock sk;
+
+	struct ieee802154_addr src_addr;
+	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)
+{
+	return container_of(sk, struct dgram_sock, sk);
+}
+
+static void dgram_hash(struct sock *sk)
+{
+	write_lock_bh(&dgram_lock);
+	sk_add_node(sk, &dgram_head);
+	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+	write_unlock_bh(&dgram_lock);
+}
+
+static void dgram_unhash(struct sock *sk)
+{
+	write_lock_bh(&dgram_lock);
+	if (sk_del_node_init(sk))
+		sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+	write_unlock_bh(&dgram_lock);
+}
+
+static int dgram_init(struct sock *sk)
+{
+	struct dgram_sock *ro = dgram_sk(sk);
+
+	ro->want_ack = 1;
+	return 0;
+}
+
+static void dgram_close(struct sock *sk, long timeout)
+{
+	sk_common_release(sk);
+}
+
+static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+	struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
+	struct ieee802154_addr haddr;
+	struct dgram_sock *ro = dgram_sk(sk);
+	int err = -EINVAL;
+	struct net_device *dev;
+
+	lock_sock(sk);
+
+	ro->bound = 0;
+
+	if (len < sizeof(*addr))
+		goto out;
+
+	if (addr->family != AF_IEEE802154)
+		goto out;
+
+	ieee802154_addr_from_sa(&haddr, &addr->addr);
+	dev = ieee802154_get_dev(sock_net(sk), &haddr);
+	if (!dev) {
+		err = -ENODEV;
+		goto out;
+	}
+
+	if (dev->type != ARPHRD_IEEE802154) {
+		err = -ENODEV;
+		goto out_put;
+	}
+
+	ro->src_addr = haddr;
+
+	ro->bound = 1;
+	err = 0;
+out_put:
+	dev_put(dev);
+out:
+	release_sock(sk);
+
+	return err;
+}
+
+static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+	switch (cmd) {
+	case SIOCOUTQ:
+	{
+		int amount = sk_wmem_alloc_get(sk);
+
+		return put_user(amount, (int __user *)arg);
+	}
+
+	case SIOCINQ:
+	{
+		struct sk_buff *skb;
+		unsigned long amount;
+
+		amount = 0;
+		spin_lock_bh(&sk->sk_receive_queue.lock);
+		skb = skb_peek(&sk->sk_receive_queue);
+		if (skb) {
+			/* We will only return the amount
+			 * of this packet since that is all
+			 * that will be read.
+			 */
+			amount = skb->len - ieee802154_hdr_length(skb);
+		}
+		spin_unlock_bh(&sk->sk_receive_queue.lock);
+		return put_user(amount, (int __user *)arg);
+	}
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+/* FIXME: autobind */
+static int dgram_connect(struct sock *sk, struct sockaddr *uaddr,
+			 int len)
+{
+	struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
+	struct dgram_sock *ro = dgram_sk(sk);
+	int err = 0;
+
+	if (len < sizeof(*addr))
+		return -EINVAL;
+
+	if (addr->family != AF_IEEE802154)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (!ro->bound) {
+		err = -ENETUNREACH;
+		goto out;
+	}
+
+	ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr);
+	ro->connected = 1;
+
+out:
+	release_sock(sk);
+	return err;
+}
+
+static int dgram_disconnect(struct sock *sk, int flags)
+{
+	struct dgram_sock *ro = dgram_sk(sk);
+
+	lock_sock(sk);
+	ro->connected = 0;
+	release_sock(sk);
+
+	return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
+#else
+static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
+			 struct msghdr *msg, size_t size)
+#endif
+{
+	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;
+
+	if (msg->msg_flags & MSG_OOB) {
+		pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
+		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
+		dev = ieee802154_get_dev(sock_net(sk), &ro->src_addr);
+
+	if (!dev) {
+		pr_debug("no dev\n");
+		err = -ENXIO;
+		goto out;
+	}
+	mtu = dev->mtu;
+	pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
+
+	if (size > mtu) {
+		pr_debug("size = %Zu, mtu = %u\n", size, mtu);
+		err = -EMSGSIZE;
+		goto out_dev;
+	}
+
+	hlen = LL_RESERVED_SPACE(dev);
+	tlen = dev->needed_tailroom;
+	skb = sock_alloc_send_skb(sk, hlen + tlen + size,
+				  msg->msg_flags & MSG_DONTWAIT,
+				  &err);
+	if (!skb)
+		goto out_dev;
+
+	skb_reserve(skb, hlen);
+
+	skb_reset_network_header(skb);
+
+	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);
+
+		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;
+
+	err = memcpy_from_msg(skb_put(skb, size), msg, size);
+	if (err < 0)
+		goto out_skb;
+
+	skb->dev = dev;
+	skb->sk  = sk;
+	skb->protocol = htons(ETH_P_IEEE802154);
+
+	dev_put(dev);
+
+	err = dev_queue_xmit(skb);
+	if (err > 0)
+		err = net_xmit_errno(err);
+
+	return err ?: size;
+
+out_skb:
+	kfree_skb(skb);
+out_dev:
+	dev_put(dev);
+out:
+	return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+			 int noblock, int flags, int *addr_len)
+#else
+static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,
+			 struct msghdr *msg, size_t len, int noblock,
+			 int flags, int *addr_len)
+#endif
+{
+	size_t copied = 0;
+	int err = -EOPNOTSUPP;
+	struct sk_buff *skb;
+	DECLARE_SOCKADDR(struct sockaddr_ieee802154 *, saddr, msg->msg_name);
+
+	skb = skb_recv_datagram(sk, flags, noblock, &err);
+	if (!skb)
+		goto out;
+
+	copied = skb->len;
+	if (len < copied) {
+		msg->msg_flags |= MSG_TRUNC;
+		copied = len;
+	}
+
+	/* FIXME: skip headers if necessary ?! */
+	err = skb_copy_datagram_msg(skb, 0, msg, copied);
+	if (err)
+		goto done;
+
+	sock_recv_ts_and_drops(msg, sk, skb);
+
+	if (saddr) {
+		saddr->family = AF_IEEE802154;
+		ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source);
+		*addr_len = sizeof(*saddr);
+	}
+
+	if (flags & MSG_TRUNC)
+		copied = skb->len;
+done:
+	skb_free_datagram(sk, skb);
+out:
+	if (err)
+		return err;
+	return copied;
+}
+
+static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+	skb = skb_share_check(skb, GFP_ATOMIC);
+	if (!skb)
+		return NET_RX_DROP;
+
+	if (sock_queue_rcv_skb(sk, skb) < 0) {
+		kfree_skb(skb);
+		return NET_RX_DROP;
+	}
+
+	return NET_RX_SUCCESS;
+}
+
+static inline bool
+ieee802154_match_sock(__le64 hw_addr, __le16 pan_id, __le16 short_addr,
+		      struct dgram_sock *ro)
+{
+	if (!ro->bound)
+		return true;
+
+	if (ro->src_addr.mode == IEEE802154_ADDR_LONG &&
+	    hw_addr == ro->src_addr.extended_addr)
+		return true;
+
+	if (ro->src_addr.mode == IEEE802154_ADDR_SHORT &&
+	    pan_id == ro->src_addr.pan_id &&
+	    short_addr == ro->src_addr.short_addr)
+		return true;
+
+	return false;
+}
+
+static int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
+{
+	struct sock *sk, *prev = NULL;
+	int ret = NET_RX_SUCCESS;
+	__le16 pan_id, short_addr;
+	__le64 hw_addr;
+
+	/* Data frame processing */
+	BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+	pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
+	short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev);
+	hw_addr = ieee802154_devaddr_from_raw(dev->dev_addr);
+
+	read_lock(&dgram_lock);
+	sk_for_each(sk, &dgram_head) {
+		if (ieee802154_match_sock(hw_addr, pan_id, short_addr,
+					  dgram_sk(sk))) {
+			if (prev) {
+				struct sk_buff *clone;
+
+				clone = skb_clone(skb, GFP_ATOMIC);
+				if (clone)
+					dgram_rcv_skb(prev, clone);
+			}
+
+			prev = sk;
+		}
+	}
+
+	if (prev) {
+		dgram_rcv_skb(prev, skb);
+	} else {
+		kfree_skb(skb);
+		ret = NET_RX_DROP;
+	}
+	read_unlock(&dgram_lock);
+
+	return ret;
+}
+
+static int dgram_getsockopt(struct sock *sk, int level, int optname,
+			    char __user *optval, int __user *optlen)
+{
+	struct dgram_sock *ro = dgram_sk(sk);
+
+	int val, len;
+
+	if (level != SOL_IEEE802154)
+		return -EOPNOTSUPP;
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	len = min_t(unsigned int, len, sizeof(int));
+
+	switch (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;
+	}
+
+	if (put_user(len, optlen))
+		return -EFAULT;
+	if (copy_to_user(optval, &val, len))
+		return -EFAULT;
+	return 0;
+}
+
+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;
+
+	if (optlen < sizeof(int))
+		return -EINVAL;
+
+	if (get_user(val, (int __user *)optval))
+		return -EFAULT;
+
+	lock_sock(sk);
+
+	switch (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;
+	}
+
+	release_sock(sk);
+	return err;
+}
+
+static struct proto ieee802154_dgram_prot = {
+	.name		= "IEEE-802.15.4-MAC",
+	.owner		= THIS_MODULE,
+	.obj_size	= sizeof(struct dgram_sock),
+	.init		= dgram_init,
+	.close		= dgram_close,
+	.bind		= dgram_bind,
+	.sendmsg	= dgram_sendmsg,
+	.recvmsg	= dgram_recvmsg,
+	.hash		= dgram_hash,
+	.unhash		= dgram_unhash,
+	.connect	= dgram_connect,
+	.disconnect	= dgram_disconnect,
+	.ioctl		= dgram_ioctl,
+	.getsockopt	= dgram_getsockopt,
+	.setsockopt	= dgram_setsockopt,
+};
+
+static const struct proto_ops ieee802154_dgram_ops = {
+	.family		   = PF_IEEE802154,
+	.owner		   = THIS_MODULE,
+	.release	   = ieee802154_sock_release,
+	.bind		   = ieee802154_sock_bind,
+	.connect	   = ieee802154_sock_connect,
+	.socketpair	   = sock_no_socketpair,
+	.accept		   = sock_no_accept,
+	.getname	   = sock_no_getname,
+	.poll		   = datagram_poll,
+	.ioctl		   = ieee802154_sock_ioctl,
+	.listen		   = sock_no_listen,
+	.shutdown	   = sock_no_shutdown,
+	.setsockopt	   = sock_common_setsockopt,
+	.getsockopt	   = sock_common_getsockopt,
+	.sendmsg	   = ieee802154_sock_sendmsg,
+	.recvmsg	   = sock_common_recvmsg,
+	.mmap		   = sock_no_mmap,
+	.sendpage	   = sock_no_sendpage,
+#ifdef CONFIG_COMPAT
+	.compat_setsockopt = compat_sock_common_setsockopt,
+	.compat_getsockopt = compat_sock_common_getsockopt,
+#endif
+};
+
+/* Create a socket. Initialise the socket, blank the addresses
+ * set the state.
+ */
+static int ieee802154_create(struct net *net, struct socket *sock,
+			     int protocol, int kern)
+{
+	struct sock *sk;
+	int rc;
+	struct proto *proto;
+	const struct proto_ops *ops;
+
+	if (!net_eq(net, &init_net))
+		return -EAFNOSUPPORT;
+
+	switch (sock->type) {
+	case SOCK_RAW:
+		proto = &ieee802154_raw_prot;
+		ops = &ieee802154_raw_ops;
+		break;
+	case SOCK_DGRAM:
+		proto = &ieee802154_dgram_prot;
+		ops = &ieee802154_dgram_ops;
+		break;
+	default:
+		rc = -ESOCKTNOSUPPORT;
+		goto out;
+	}
+
+	rc = -ENOMEM;
+	sk = sk_alloc(net, PF_IEEE802154, GFP_KERNEL, proto);
+	if (!sk)
+		goto out;
+	rc = 0;
+
+	sock->ops = ops;
+
+	sock_init_data(sock, sk);
+	/* FIXME: sk->sk_destruct */
+	sk->sk_family = PF_IEEE802154;
+
+	/* Checksums on by default */
+	sock_set_flag(sk, SOCK_ZAPPED);
+
+	if (sk->sk_prot->hash)
+		sk->sk_prot->hash(sk);
+
+	if (sk->sk_prot->init) {
+		rc = sk->sk_prot->init(sk);
+		if (rc)
+			sk_common_release(sk);
+	}
+out:
+	return rc;
+}
+
+static const struct net_proto_family ieee802154_family_ops = {
+	.family		= PF_IEEE802154,
+	.create		= ieee802154_create,
+	.owner		= THIS_MODULE,
+};
+
+static int ieee802154_rcv(struct sk_buff *skb, struct net_device *dev,
+			  struct packet_type *pt, struct net_device *orig_dev)
+{
+	if (!netif_running(dev))
+		goto drop;
+	pr_debug("got frame, type %d, dev %p\n", dev->type, dev);
+#ifdef DEBUG
+	print_hex_dump_bytes("ieee802154_rcv ",
+			     DUMP_PREFIX_NONE, skb->data, skb->len);
+#endif
+
+	if (!net_eq(dev_net(dev), &init_net))
+		goto drop;
+
+	ieee802154_raw_deliver(dev, skb);
+
+	if (dev->type != ARPHRD_IEEE802154)
+		goto drop;
+
+	if (skb->pkt_type != PACKET_OTHERHOST)
+		return ieee802154_dgram_deliver(dev, skb);
+
+drop:
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+static struct packet_type ieee802154_packet_type = {
+	.type = htons(ETH_P_IEEE802154),
+	.func = ieee802154_rcv,
+};
+
+static int __init af_ieee802154_init(void)
+{
+	int rc = -EINVAL;
+
+	rc = proto_register(&ieee802154_raw_prot, 1);
+	if (rc)
+		goto out;
+
+	rc = proto_register(&ieee802154_dgram_prot, 1);
+	if (rc)
+		goto err_dgram;
+
+	/* Tell SOCKET that we are alive */
+	rc = sock_register(&ieee802154_family_ops);
+	if (rc)
+		goto err_sock;
+	dev_add_pack(&ieee802154_packet_type);
+
+	rc = 0;
+	goto out;
+
+err_sock:
+	proto_unregister(&ieee802154_dgram_prot);
+err_dgram:
+	proto_unregister(&ieee802154_raw_prot);
+out:
+	return rc;
+}
+
+static void __exit af_ieee802154_remove(void)
+{
+	dev_remove_pack(&ieee802154_packet_type);
+	sock_unregister(PF_IEEE802154);
+	proto_unregister(&ieee802154_dgram_prot);
+	proto_unregister(&ieee802154_raw_prot);
+}
+
+module_init(af_ieee802154_init);
+module_exit(af_ieee802154_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_IEEE802154);
diff --git a/net/ieee802154/sysfs.c b/net/ieee802154/sysfs.c
index 2aec368..f9fa338 100644
--- a/net/ieee802154/sysfs.c
+++ b/net/ieee802154/sysfs.c
@@ -48,49 +48,6 @@
 }
 static DEVICE_ATTR_RO(name);
 
-#define MASTER_SHOW_COMPLEX(name, format_string, args...)		\
-static ssize_t name ## _show(struct device *dev,			\
-			    struct device_attribute *attr, char *buf)	\
-{									\
-	struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);	\
-	int ret;							\
-									\
-	mutex_lock(&phy->pib_lock);					\
-	ret = snprintf(buf, PAGE_SIZE, format_string "\n", args);	\
-	mutex_unlock(&phy->pib_lock);					\
-	return ret;							\
-}									\
-static DEVICE_ATTR_RO(name)
-
-#define MASTER_SHOW(field, format_string)				\
-	MASTER_SHOW_COMPLEX(field, format_string, phy->field)
-
-MASTER_SHOW(current_channel, "%d");
-MASTER_SHOW(current_page, "%d");
-MASTER_SHOW(transmit_power, "%d +- 1 dB");
-MASTER_SHOW(cca_mode, "%d");
-
-static ssize_t channels_supported_show(struct device *dev,
-				       struct device_attribute *attr,
-				       char *buf)
-{
-	struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
-	int ret;
-	int i, len = 0;
-
-	mutex_lock(&phy->pib_lock);
-	for (i = 0; i < 32; i++) {
-		ret = snprintf(buf + len, PAGE_SIZE - len,
-			       "%#09x\n", phy->channels_supported[i]);
-		if (ret < 0)
-			break;
-		len += ret;
-	}
-	mutex_unlock(&phy->pib_lock);
-	return len;
-}
-static DEVICE_ATTR_RO(channels_supported);
-
 static void wpan_phy_release(struct device *dev)
 {
 	struct cfg802154_registered_device *rdev = dev_to_rdev(dev);
@@ -101,12 +58,6 @@
 static struct attribute *pmib_attrs[] = {
 	&dev_attr_index.attr,
 	&dev_attr_name.attr,
-	/* below will be removed soon */
-	&dev_attr_current_channel.attr,
-	&dev_attr_current_page.attr,
-	&dev_attr_channels_supported.attr,
-	&dev_attr_transmit_power.attr,
-	&dev_attr_cca_mode.attr,
 	NULL,
 };
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 543f13a..d3bc609 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -6,6 +6,7 @@
 	depends on CRYPTO_ARC4
 	depends on CRYPTO_AES
 	select BPAUTO_CRYPTO_CCM
+	depends on CRYPTO_GCM
 	depends on CRC32
 	select BPAUTO_AVERAGE
 	---help---
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index c09aa25..91a0906 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -15,7 +15,9 @@
 	michael.o \
 	tkip.o \
 	aes_ccm.o \
+	aes_gcm.o \
 	aes_cmac.o \
+	aes_gmac.o \
 	cfg.o \
 	ethtool.o \
 	rx.o \
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c
index 09d9caa..70d53da 100644
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -11,16 +11,16 @@
 
 #include <linux/kernel.h>
 #include <linux/types.h>
-#include <linux/crypto.h>
 #include <linux/err.h>
-#include <crypto/aes.h>
+#include <crypto/aead.h>
 
 #include <net/mac80211.h>
 #include "key.h"
 #include "aes_ccm.h"
 
 void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
-			       u8 *data, size_t data_len, u8 *mic)
+			       u8 *data, size_t data_len, u8 *mic,
+			       size_t mic_len)
 {
 	struct scatterlist assoc, pt, ct[2];
 
@@ -35,7 +35,7 @@
 	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
 	sg_init_table(ct, 2);
 	sg_set_buf(&ct[0], data, data_len);
-	sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
+	sg_set_buf(&ct[1], mic, mic_len);
 
 	aead_request_set_tfm(aead_req, tfm);
 	aead_request_set_assoc(aead_req, &assoc, assoc.length);
@@ -45,7 +45,8 @@
 }
 
 int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
-			      u8 *data, size_t data_len, u8 *mic)
+			      u8 *data, size_t data_len, u8 *mic,
+			      size_t mic_len)
 {
 	struct scatterlist assoc, pt, ct[2];
 	char aead_req_data[sizeof(struct aead_request) +
@@ -62,17 +63,18 @@
 	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
 	sg_init_table(ct, 2);
 	sg_set_buf(&ct[0], data, data_len);
-	sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
+	sg_set_buf(&ct[1], mic, mic_len);
 
 	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);
+	aead_request_set_crypt(aead_req, ct, &pt, data_len + mic_len, b_0);
 
 	return crypto_aead_decrypt(aead_req);
 }
 
-struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
+						    size_t key_len,
+						    size_t mic_len)
 {
 	struct crypto_aead *tfm;
 	int err;
@@ -81,12 +83,16 @@
 	if (IS_ERR(tfm))
 		return tfm;
 
-	err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
-	if (!err)
-		err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN);
-	if (!err)
-		return tfm;
+	err = crypto_aead_setkey(tfm, key, key_len);
+	if (err)
+		goto free_aead;
+	err = crypto_aead_setauthsize(tfm, mic_len);
+	if (err)
+		goto free_aead;
 
+	return tfm;
+
+free_aead:
 	crypto_free_aead(tfm);
 	return ERR_PTR(err);
 }
diff --git a/net/mac80211/aes_ccm.h b/net/mac80211/aes_ccm.h
index 2c7ab19..6a73d1e 100644
--- a/net/mac80211/aes_ccm.h
+++ b/net/mac80211/aes_ccm.h
@@ -12,11 +12,15 @@
 
 #include <linux/crypto.h>
 
-struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]);
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
+						    size_t key_len,
+						    size_t mic_len);
 void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
-			       u8 *data, size_t data_len, u8 *mic);
+			       u8 *data, size_t data_len, u8 *mic,
+			       size_t mic_len);
 int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
-			      u8 *data, size_t data_len, u8 *mic);
+			      u8 *data, size_t data_len, u8 *mic,
+			      size_t mic_len);
 void ieee80211_aes_key_free(struct crypto_aead *tfm);
 
 #endif /* AES_CCM_H */
diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c
index 9b9009f..4192806 100644
--- a/net/mac80211/aes_cmac.c
+++ b/net/mac80211/aes_cmac.c
@@ -18,8 +18,8 @@
 #include "key.h"
 #include "aes_cmac.h"
 
-#define AES_CMAC_KEY_LEN 16
 #define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */
+#define CMAC_TLEN_256 16 /* CMAC TLen = 128 bits (16 octets) */
 #define AAD_LEN 20
 
 
@@ -35,9 +35,9 @@
 		pad[AES_BLOCK_SIZE - 1] ^= 0x87;
 }
 
-
-static void aes_128_cmac_vector(struct crypto_cipher *tfm, size_t num_elem,
-				const u8 *addr[], const size_t *len, u8 *mac)
+static void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem,
+			    const u8 *addr[], const size_t *len, u8 *mac,
+			    size_t mac_len)
 {
 	u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
 	const u8 *pos, *end;
@@ -88,7 +88,7 @@
 	for (i = 0; i < AES_BLOCK_SIZE; i++)
 		pad[i] ^= cbc[i];
 	crypto_cipher_encrypt_one(tfm, pad, pad);
-	memcpy(mac, pad, CMAC_TLEN);
+	memcpy(mac, pad, mac_len);
 }
 
 
@@ -107,17 +107,35 @@
 	addr[2] = zero;
 	len[2] = CMAC_TLEN;
 
-	aes_128_cmac_vector(tfm, 3, addr, len, mic);
+	aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN);
 }
 
+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad,
+			    const u8 *data, size_t data_len, u8 *mic)
+{
+	const u8 *addr[3];
+	size_t len[3];
+	u8 zero[CMAC_TLEN_256];
 
-struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[])
+	memset(zero, 0, CMAC_TLEN_256);
+	addr[0] = aad;
+	len[0] = AAD_LEN;
+	addr[1] = data;
+	len[1] = data_len - CMAC_TLEN_256;
+	addr[2] = zero;
+	len[2] = CMAC_TLEN_256;
+
+	aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN_256);
+}
+
+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[],
+						   size_t key_len)
 {
 	struct crypto_cipher *tfm;
 
 	tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
 	if (!IS_ERR(tfm))
-		crypto_cipher_setkey(tfm, key, AES_CMAC_KEY_LEN);
+		crypto_cipher_setkey(tfm, key, key_len);
 
 	return tfm;
 }
diff --git a/net/mac80211/aes_cmac.h b/net/mac80211/aes_cmac.h
index 0ce6487..3702041 100644
--- a/net/mac80211/aes_cmac.h
+++ b/net/mac80211/aes_cmac.h
@@ -11,9 +11,12 @@
 
 #include <linux/crypto.h>
 
-struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[]);
+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[],
+						   size_t key_len);
 void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,
 			const u8 *data, size_t data_len, u8 *mic);
+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad,
+			    const u8 *data, size_t data_len, u8 *mic);
 void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm);
 
 #endif /* AES_CMAC_H */
diff --git a/net/mac80211/aes_gcm.c b/net/mac80211/aes_gcm.c
new file mode 100644
index 0000000..b91c9d7
--- /dev/null
+++ b/net/mac80211/aes_gcm.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014-2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <crypto/aead.h>
+
+#include <net/mac80211.h>
+#include "key.h"
+#include "aes_gcm.h"
+
+void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+			       u8 *data, size_t data_len, u8 *mic)
+{
+	struct scatterlist assoc, pt, ct[2];
+
+	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));
+	sg_init_table(ct, 2);
+	sg_set_buf(&ct[0], data, data_len);
+	sg_set_buf(&ct[1], mic, IEEE80211_GCMP_MIC_LEN);
+
+	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, j_0);
+
+	crypto_aead_encrypt(aead_req);
+}
+
+int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+			      u8 *data, size_t data_len, u8 *mic)
+{
+	struct scatterlist assoc, pt, ct[2];
+	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;
+
+	if (data_len == 0)
+		return -EINVAL;
+
+	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));
+	sg_init_table(ct, 2);
+	sg_set_buf(&ct[0], data, data_len);
+	sg_set_buf(&ct[1], mic, IEEE80211_GCMP_MIC_LEN);
+
+	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_GCMP_MIC_LEN, j_0);
+
+	return crypto_aead_decrypt(aead_req);
+}
+
+struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
+							size_t key_len)
+{
+	struct crypto_aead *tfm;
+	int err;
+
+	tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm))
+		return tfm;
+
+	err = crypto_aead_setkey(tfm, key, key_len);
+	if (err)
+		goto free_aead;
+	err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN);
+	if (err)
+		goto free_aead;
+
+	return tfm;
+
+free_aead:
+	crypto_free_aead(tfm);
+	return ERR_PTR(err);
+}
+
+void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm)
+{
+	crypto_free_aead(tfm);
+}
diff --git a/net/mac80211/aes_gcm.h b/net/mac80211/aes_gcm.h
new file mode 100644
index 0000000..1347fda
--- /dev/null
+++ b/net/mac80211/aes_gcm.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014-2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef AES_GCM_H
+#define AES_GCM_H
+
+#include <linux/crypto.h>
+
+void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+			       u8 *data, size_t data_len, u8 *mic);
+int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+			      u8 *data, size_t data_len, u8 *mic);
+struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
+							size_t key_len);
+void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm);
+
+#endif /* AES_GCM_H */
diff --git a/net/mac80211/aes_gmac.c b/net/mac80211/aes_gmac.c
new file mode 100644
index 0000000..c34b06c
--- /dev/null
+++ b/net/mac80211/aes_gmac.c
@@ -0,0 +1,84 @@
+/*
+ * AES-GMAC for IEEE 802.11 BIP-GMAC-128 and BIP-GMAC-256
+ * Copyright 2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <crypto/aead.h>
+#include <crypto/aes.h>
+
+#include <net/mac80211.h>
+#include "key.h"
+#include "aes_gmac.h"
+
+#define GMAC_MIC_LEN 16
+#define GMAC_NONCE_LEN 12
+#define AAD_LEN 20
+
+int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
+		       const u8 *data, size_t data_len, u8 *mic)
+{
+	struct scatterlist sg[3], ct[1];
+	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;
+	u8 zero[GMAC_MIC_LEN], iv[AES_BLOCK_SIZE];
+
+	if (data_len < GMAC_MIC_LEN)
+		return -EINVAL;
+
+	memset(aead_req, 0, sizeof(aead_req_data));
+
+	memset(zero, 0, GMAC_MIC_LEN);
+	sg_init_table(sg, 3);
+	sg_set_buf(&sg[0], aad, AAD_LEN);
+	sg_set_buf(&sg[1], data, data_len - GMAC_MIC_LEN);
+	sg_set_buf(&sg[2], zero, GMAC_MIC_LEN);
+
+	memcpy(iv, nonce, GMAC_NONCE_LEN);
+	memset(iv + GMAC_NONCE_LEN, 0, sizeof(iv) - GMAC_NONCE_LEN);
+	iv[AES_BLOCK_SIZE - 1] = 0x01;
+
+	sg_init_table(ct, 1);
+	sg_set_buf(&ct[0], mic, GMAC_MIC_LEN);
+
+	aead_request_set_tfm(aead_req, tfm);
+	aead_request_set_assoc(aead_req, sg, AAD_LEN + data_len);
+	aead_request_set_crypt(aead_req, NULL, ct, 0, iv);
+
+	crypto_aead_encrypt(aead_req);
+
+	return 0;
+}
+
+struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
+						 size_t key_len)
+{
+	struct crypto_aead *tfm;
+	int err;
+
+	tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm))
+		return tfm;
+
+	err = crypto_aead_setkey(tfm, key, key_len);
+	if (!err)
+		err = crypto_aead_setauthsize(tfm, GMAC_MIC_LEN);
+	if (!err)
+		return tfm;
+
+	crypto_free_aead(tfm);
+	return ERR_PTR(err);
+}
+
+void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm)
+{
+	crypto_free_aead(tfm);
+}
diff --git a/net/mac80211/aes_gmac.h b/net/mac80211/aes_gmac.h
new file mode 100644
index 0000000..d328204
--- /dev/null
+++ b/net/mac80211/aes_gmac.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef AES_GMAC_H
+#define AES_GMAC_H
+
+#include <linux/crypto.h>
+
+struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
+						 size_t key_len);
+int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
+		       const u8 *data, size_t data_len, u8 *mic);
+void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm);
+
+#endif /* AES_GMAC_H */
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index a48bad4..5c564a6 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -49,8 +49,6 @@
 		container_of(h, struct tid_ampdu_rx, rcu_head);
 	int i;
 
-	del_timer_sync(&tid_rx->reorder_timer);
-
 	for (i = 0; i < tid_rx->buf_size; i++)
 		__skb_queue_purge(&tid_rx->reorder_buf[i]);
 	kfree(tid_rx->reorder_buf);
@@ -93,6 +91,12 @@
 
 	del_timer_sync(&tid_rx->session_timer);
 
+	/* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
+	spin_lock_bh(&tid_rx->reorder_lock);
+	tid_rx->removed = true;
+	spin_unlock_bh(&tid_rx->reorder_lock);
+	del_timer_sync(&tid_rx->reorder_timer);
+
 	call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
 }
 
@@ -234,6 +238,14 @@
 	int i, ret = -EOPNOTSUPP;
 	u16 status = WLAN_STATUS_REQUEST_DECLINED;
 
+	if (!sta->sta.ht_cap.ht_supported) {
+		ht_dbg(sta->sdata,
+		       "STA %pM erroneously requests BA session on tid %d w/o QoS\n",
+		       sta->sta.addr, tid);
+		/* send a response anyway, it's an error case if we get here */
+		goto end_no_lock;
+	}
+
 	if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
 		ht_dbg(sta->sdata,
 		       "Suspend in progress - Denying ADDBA request (%pM tid %d)\n",
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index a360c15..cce9d42 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -188,6 +188,43 @@
 	__release(agg_queue);
 }
 
+static void
+ieee80211_agg_stop_txq(struct sta_info *sta, int tid)
+{
+	struct ieee80211_txq *txq = sta->sta.txq[tid];
+	struct txq_info *txqi;
+
+	if (!txq)
+		return;
+
+	txqi = to_txq_info(txq);
+
+	/* Lock here to protect against further seqno updates on dequeue */
+	spin_lock_bh(&txqi->queue.lock);
+	set_bit(IEEE80211_TXQ_STOP, &txqi->flags);
+	spin_unlock_bh(&txqi->queue.lock);
+}
+
+static void
+ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable)
+{
+	struct ieee80211_txq *txq = sta->sta.txq[tid];
+	struct txq_info *txqi;
+
+	if (!txq)
+		return;
+
+	txqi = to_txq_info(txq);
+
+	if (enable)
+		set_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);
+	else
+		clear_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);
+
+	clear_bit(IEEE80211_TXQ_STOP, &txqi->flags);
+	drv_wake_tx_queue(sta->sdata->local, txqi);
+}
+
 /*
  * splice packets from the STA's pending to the local pending,
  * requires a call to ieee80211_agg_splice_finish later
@@ -247,6 +284,7 @@
 	ieee80211_assign_tid_tx(sta, tid, NULL);
 
 	ieee80211_agg_splice_finish(sta->sdata, tid);
+	ieee80211_agg_start_txq(sta, tid, false);
 
 	kfree_rcu(tid_tx, rcu_head);
 }
@@ -418,6 +456,8 @@
 	 */
 	clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);
 
+	ieee80211_agg_stop_txq(sta, tid);
+
 	/*
 	 * Make sure no packets are being processed. This ensures that
 	 * we have a valid starting sequence number and that in-flight
@@ -440,6 +480,8 @@
 		ieee80211_agg_splice_finish(sdata, tid);
 		spin_unlock_bh(&sta->lock);
 
+		ieee80211_agg_start_txq(sta, tid, false);
+
 		kfree_rcu(tid_tx, rcu_head);
 		return;
 	}
@@ -509,11 +551,14 @@
 	struct tid_ampdu_tx *tid_tx;
 	int ret = 0;
 
+	trace_api_start_tx_ba_session(pubsta, tid);
+
 	if (WARN(sta->reserved_tid == tid,
 		 "Requested to start BA session on reserved tid=%d", tid))
 		return -EINVAL;
 
-	trace_api_start_tx_ba_session(pubsta, tid);
+	if (!pubsta->ht_cap.ht_supported)
+		return -EINVAL;
 
 	if (WARN_ON_ONCE(!local->ops->ampdu_action))
 		return -EINVAL;
@@ -666,6 +711,8 @@
 	ieee80211_agg_splice_finish(sta->sdata, tid);
 
 	spin_unlock_bh(&sta->lock);
+
+	ieee80211_agg_start_txq(sta, tid, true);
 }
 
 void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)
@@ -793,6 +840,7 @@
 	struct ieee80211_local *local = sdata->local;
 	struct sta_info *sta;
 	struct tid_ampdu_tx *tid_tx;
+	bool send_delba = false;
 
 	trace_api_stop_tx_ba_cb(sdata, ra, tid);
 
@@ -824,13 +872,17 @@
 	}
 
 	if (tid_tx->stop_initiator == WLAN_BACK_INITIATOR && tid_tx->tx_stop)
-		ieee80211_send_delba(sta->sdata, ra, tid,
-			WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
+		send_delba = true;
 
 	ieee80211_remove_tid_tx(sta, tid);
 
  unlock_sta:
 	spin_unlock_bh(&sta->lock);
+
+	if (send_delba)
+		ieee80211_send_delba(sdata, ra, tid,
+			WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
+
 	mutex_unlock(&sta->ampdu_mlme.mtx);
  unlock:
 	mutex_unlock(&local->sta_mtx);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index dc6d29a..880981b 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -24,6 +24,7 @@
 
 static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
 						const char *name,
+						unsigned char name_assign_type,
 						enum nl80211_iftype type,
 						u32 *flags,
 						struct vif_params *params)
@@ -33,7 +34,7 @@
 	struct ieee80211_sub_if_data *sdata;
 	int err;
 
-	err = ieee80211_if_add(local, name, &wdev, type, params);
+	err = ieee80211_if_add(local, name, name_assign_type, &wdev, type, params);
 	if (err)
 		return ERR_PTR(err);
 
@@ -162,8 +163,13 @@
 			return -EINVAL;
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
 	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
 	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
 		break;
 	default:
 		cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);
@@ -348,6 +354,7 @@
 		params.seq_len = 6;
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
 		pn64 = atomic64_read(&key->u.ccmp.tx_pn);
 		seq[0] = pn64;
 		seq[1] = pn64 >> 8;
@@ -359,6 +366,7 @@
 		params.seq_len = 6;
 		break;
 	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 		pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
 		seq[0] = pn64;
 		seq[1] = pn64 >> 8;
@@ -369,6 +377,30 @@
 		params.seq = seq;
 		params.seq_len = 6;
 		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		pn64 = atomic64_read(&key->u.aes_gmac.tx_pn);
+		seq[0] = pn64;
+		seq[1] = pn64 >> 8;
+		seq[2] = pn64 >> 16;
+		seq[3] = pn64 >> 24;
+		seq[4] = pn64 >> 32;
+		seq[5] = pn64 >> 40;
+		params.seq = seq;
+		params.seq_len = 6;
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		pn64 = atomic64_read(&key->u.gcmp.tx_pn);
+		seq[0] = pn64;
+		seq[1] = pn64 >> 8;
+		seq[2] = pn64 >> 16;
+		seq[3] = pn64 >> 24;
+		seq[4] = pn64 >> 32;
+		seq[5] = pn64 >> 40;
+		params.seq = seq;
+		params.seq_len = 6;
+		break;
 	}
 
 	params.key = key->conf.key;
@@ -428,11 +460,13 @@
 		rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
 	}
 	if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
-		rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
-	if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
-		rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
-	if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
-		rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
+		rinfo->bw = RATE_INFO_BW_40;
+	else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+		rinfo->bw = RATE_INFO_BW_80;
+	else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+		rinfo->bw = RATE_INFO_BW_160;
+	else
+		rinfo->bw = RATE_INFO_BW_20;
 	if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
 		rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
 }
@@ -459,16 +493,21 @@
 		rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
 	}
 
-	if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
-		rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
 	if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
 		rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
-	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ)
-		rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
-	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80P80MHZ)
-		rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
-	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ)
-		rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
+
+	if (sta->last_rx_rate_flag & RX_FLAG_5MHZ)
+		rinfo->bw = RATE_INFO_BW_5;
+	else if (sta->last_rx_rate_flag & RX_FLAG_10MHZ)
+		rinfo->bw = RATE_INFO_BW_10;
+	else if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
+		rinfo->bw = RATE_INFO_BW_40;
+	else if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ)
+		rinfo->bw = RATE_INFO_BW_80;
+	else if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ)
+		rinfo->bw = RATE_INFO_BW_160;
+	else
+		rinfo->bw = RATE_INFO_BW_20;
 }
 
 static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
@@ -678,7 +717,8 @@
 		      BSS_CHANGED_BEACON_ENABLED |
 		      BSS_CHANGED_BEACON |
 		      BSS_CHANGED_SSID |
-		      BSS_CHANGED_P2P_PS;
+		      BSS_CHANGED_P2P_PS |
+		      BSS_CHANGED_TXPOWER;
 	int err;
 
 	old = sdata_dereference(sdata->u.ap.beacon, sdata);
@@ -938,6 +978,14 @@
 	if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
 	    set & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
 	    !test_sta_flag(sta, WLAN_STA_ASSOC)) {
+		/*
+		 * When peer becomes associated, init rate control as
+		 * well. Some drivers require rate control initialized
+		 * before drv_sta_state() is called.
+		 */
+		if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+			rate_control_rate_init(sta);
+
 		ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
 		if (ret)
 			return ret;
@@ -1011,6 +1059,10 @@
 		}
 	}
 
+	if (mask & BIT(NL80211_STA_FLAG_WME) &&
+	    local->hw.queues >= IEEE80211_NUM_ACS)
+		sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
+
 	/* auth flags will be set later for TDLS stations */
 	if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
 		ret = sta_apply_auth_flags(local, sta, mask, set);
@@ -1025,10 +1077,8 @@
 			clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
 	}
 
-	if (mask & BIT(NL80211_STA_FLAG_WME))
-		sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
-
 	if (mask & BIT(NL80211_STA_FLAG_MFP)) {
+		sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP));
 		if (set & BIT(NL80211_STA_FLAG_MFP))
 			set_sta_flag(sta, WLAN_STA_MFP);
 		else
@@ -1338,11 +1388,6 @@
 	if (err)
 		goto out_err;
 
-	/* When peer becomes authorized, init rate control as well */
-	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
-	    test_sta_flag(sta, WLAN_STA_AUTHORIZED))
-		rate_control_rate_init(sta);
-
 	mutex_unlock(&local->sta_mtx);
 
 	if ((sdata->vif.type == NL80211_IFTYPE_AP ||
@@ -1449,7 +1494,7 @@
 	if (next_hop_sta)
 		memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN);
 	else
-		memset(next_hop, 0, ETH_ALEN);
+		eth_zero_addr(next_hop);
 
 	memset(pinfo, 0, sizeof(*pinfo));
 
@@ -2102,6 +2147,8 @@
 {
 	struct ieee80211_local *local = wiphy_priv(wiphy);
 	struct ieee80211_sub_if_data *sdata;
+	enum nl80211_tx_power_setting txp_type = type;
+	bool update_txp_type = false;
 
 	if (wdev) {
 		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
@@ -2109,6 +2156,7 @@
 		switch (type) {
 		case NL80211_TX_POWER_AUTOMATIC:
 			sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
+			txp_type = NL80211_TX_POWER_LIMITED;
 			break;
 		case NL80211_TX_POWER_LIMITED:
 		case NL80211_TX_POWER_FIXED:
@@ -2118,7 +2166,12 @@
 			break;
 		}
 
-		ieee80211_recalc_txpower(sdata);
+		if (txp_type != sdata->vif.bss_conf.txpower_type) {
+			update_txp_type = true;
+			sdata->vif.bss_conf.txpower_type = txp_type;
+		}
+
+		ieee80211_recalc_txpower(sdata, update_txp_type);
 
 		return 0;
 	}
@@ -2126,6 +2179,7 @@
 	switch (type) {
 	case NL80211_TX_POWER_AUTOMATIC:
 		local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
+		txp_type = NL80211_TX_POWER_LIMITED;
 		break;
 	case NL80211_TX_POWER_LIMITED:
 	case NL80211_TX_POWER_FIXED:
@@ -2136,10 +2190,14 @@
 	}
 
 	mutex_lock(&local->iflist_mtx);
-	list_for_each_entry(sdata, &local->interfaces, list)
+	list_for_each_entry(sdata, &local->interfaces, list) {
 		sdata->user_power_level = local->user_power_level;
+		if (txp_type != sdata->vif.bss_conf.txpower_type)
+			update_txp_type = true;
+		sdata->vif.bss_conf.txpower_type = txp_type;
+	}
 	list_for_each_entry(sdata, &local->interfaces, list)
-		ieee80211_recalc_txpower(sdata);
+		ieee80211_recalc_txpower(sdata, update_txp_type);
 	mutex_unlock(&local->iflist_mtx);
 
 	return 0;
@@ -2221,7 +2279,6 @@
 {
 	struct sta_info *sta;
 	enum ieee80211_smps_mode old_req;
-	int i;
 
 	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
 		return -EINVAL;
@@ -2245,52 +2302,44 @@
 	}
 
 	ht_dbg(sdata,
-	       "SMSP %d requested in AP mode, sending Action frame to %d stations\n",
+	       "SMPS %d requested in AP mode, sending Action frame to %d stations\n",
 	       smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
 
 	mutex_lock(&sdata->local->sta_mtx);
-	for (i = 0; i < STA_HASH_SIZE; i++) {
-		for (sta = rcu_dereference_protected(sdata->local->sta_hash[i],
-				lockdep_is_held(&sdata->local->sta_mtx));
-		     sta;
-		     sta = rcu_dereference_protected(sta->hnext,
-				lockdep_is_held(&sdata->local->sta_mtx))) {
-			/*
-			 * Only stations associated to our AP and
-			 * associated VLANs
-			 */
-			if (sta->sdata->bss != &sdata->u.ap)
-				continue;
+	list_for_each_entry(sta, &sdata->local->sta_list, list) {
+		/*
+		 * Only stations associated to our AP and
+		 * associated VLANs
+		 */
+		if (sta->sdata->bss != &sdata->u.ap)
+			continue;
 
-			/* This station doesn't support MIMO - skip it */
-			if (sta_info_tx_streams(sta) == 1)
-				continue;
+		/* This station doesn't support MIMO - skip it */
+		if (sta_info_tx_streams(sta) == 1)
+			continue;
 
-			/*
-			 * Don't wake up a STA just to send the action frame
-			 * unless we are getting more restrictive.
-			 */
-			if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
-			    !ieee80211_smps_is_restrictive(sta->known_smps_mode,
-							   smps_mode)) {
-				ht_dbg(sdata,
-				       "Won't send SMPS to sleeping STA %pM\n",
-				       sta->sta.addr);
-				continue;
-			}
-
-			/*
-			 * If the STA is not authorized, wait until it gets
-			 * authorized and the action frame will be sent then.
-			 */
-			if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
-				continue;
-
-			ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
-			ieee80211_send_smps_action(sdata, smps_mode,
-						   sta->sta.addr,
-						   sdata->vif.bss_conf.bssid);
+		/*
+		 * Don't wake up a STA just to send the action frame
+		 * unless we are getting more restrictive.
+		 */
+		if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
+		    !ieee80211_smps_is_restrictive(sta->known_smps_mode,
+						   smps_mode)) {
+			ht_dbg(sdata, "Won't send SMPS to sleeping STA %pM\n",
+			       sta->sta.addr);
+			continue;
 		}
+
+		/*
+		 * If the STA is not authorized, wait until it gets
+		 * authorized and the action frame will be sent then.
+		 */
+		if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+			continue;
+
+		ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
+		ieee80211_send_smps_action(sdata, smps_mode, sta->sta.addr,
+					   sdata->vif.bss_conf.bssid);
 	}
 	mutex_unlock(&sdata->local->sta_mtx);
 
@@ -2556,7 +2605,7 @@
 
 	/* if there's one pending or we're scanning, queue this one */
 	if (!list_empty(&local->roc_list) ||
-	    local->scanning || local->radar_detect_enabled)
+	    local->scanning || ieee80211_is_radar_required(local))
 		goto out_check_combine;
 
 	/* if not HW assist, just queue & schedule work */
@@ -3529,7 +3578,7 @@
 		nullfunc->qos_ctrl = cpu_to_le16(7);
 
 	local_bh_disable();
-	ieee80211_xmit(sdata, skb);
+	ieee80211_xmit(sdata, sta, skb);
 	local_bh_enable();
 	rcu_read_unlock();
 
@@ -3664,7 +3713,7 @@
 		 * queues.
 		 */
 		synchronize_net();
-		ieee80211_flush_queues(local, sdata);
+		ieee80211_flush_queues(local, sdata, false);
 
 		/* restore the normal QoS parameters
 		 * (unconditionally to avoid races)
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index da1c12c..5bcd4e5 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -388,7 +388,7 @@
 	return NULL;
 }
 
-static bool ieee80211_is_radar_required(struct ieee80211_local *local)
+bool ieee80211_is_radar_required(struct ieee80211_local *local)
 {
 	struct ieee80211_sub_if_data *sdata;
 
@@ -406,6 +406,34 @@
 	return false;
 }
 
+static bool
+ieee80211_chanctx_radar_required(struct ieee80211_local *local,
+				 struct ieee80211_chanctx *ctx)
+{
+	struct ieee80211_chanctx_conf *conf = &ctx->conf;
+	struct ieee80211_sub_if_data *sdata;
+	bool required = false;
+
+	lockdep_assert_held(&local->chanctx_mtx);
+	lockdep_assert_held(&local->mtx);
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		if (!ieee80211_sdata_running(sdata))
+			continue;
+		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
+			continue;
+		if (!sdata->radar_required)
+			continue;
+
+		required = true;
+		break;
+	}
+	rcu_read_unlock();
+
+	return required;
+}
+
 static struct ieee80211_chanctx *
 ieee80211_alloc_chanctx(struct ieee80211_local *local,
 			const struct cfg80211_chan_def *chandef,
@@ -425,7 +453,7 @@
 	ctx->conf.rx_chains_static = 1;
 	ctx->conf.rx_chains_dynamic = 1;
 	ctx->mode = mode;
-	ctx->conf.radar_enabled = ieee80211_is_radar_required(local);
+	ctx->conf.radar_enabled = false;
 	ieee80211_recalc_chanctx_min_def(local, ctx);
 
 	return ctx;
@@ -567,16 +595,15 @@
 	bool radar_enabled;
 
 	lockdep_assert_held(&local->chanctx_mtx);
-	/* for setting local->radar_detect_enabled */
+	/* for ieee80211_is_radar_required */
 	lockdep_assert_held(&local->mtx);
 
-	radar_enabled = ieee80211_is_radar_required(local);
+	radar_enabled = ieee80211_chanctx_radar_required(local, chanctx);
 
 	if (radar_enabled == chanctx->conf.radar_enabled)
 		return;
 
 	chanctx->conf.radar_enabled = radar_enabled;
-	local->radar_detect_enabled = chanctx->conf.radar_enabled;
 
 	if (!local->use_chanctx) {
 		local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
@@ -628,7 +655,7 @@
 	}
 
 	if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
-		ieee80211_recalc_txpower(sdata);
+		ieee80211_recalc_txpower(sdata, false);
 		ieee80211_recalc_chanctx_min_def(local, new_ctx);
 	}
 
@@ -1360,7 +1387,7 @@
 				ieee80211_bss_info_change_notify(sdata,
 								 changed);
 
-			ieee80211_recalc_txpower(sdata);
+			ieee80211_recalc_txpower(sdata, false);
 		}
 
 		ieee80211_recalc_chanctx_chantype(local, ctx);
@@ -1481,6 +1508,8 @@
 	if (ieee80211_chanctx_refcount(local, ctx) == 0)
 		ieee80211_free_chanctx(local, ctx);
 
+	sdata->radar_required = false;
+
 	/* Unreserving may ready an in-place reservation. */
 	if (use_reserved_switch)
 		ieee80211_vif_use_reserved_switch(local);
@@ -1539,6 +1568,9 @@
 	ieee80211_recalc_smps_chanctx(local, ctx);
 	ieee80211_recalc_radar_chanctx(local, ctx);
  out:
+	if (ret)
+		sdata->radar_required = false;
+
 	mutex_unlock(&local->chanctx_mtx);
 	return ret;
 }
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index fc8e234..393d2a0 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -18,172 +18,6 @@
 
 #define DEBUGFS_FORMAT_BUFFER_SIZE 100
 
-#define TX_LATENCY_BIN_DELIMTER_C ','
-#define TX_LATENCY_BIN_DELIMTER_S ","
-#define TX_LATENCY_BINS_DISABLED "enable(bins disabled)\n"
-#define TX_LATENCY_DISABLED "disable\n"
-
-
-/*
- * Display if Tx latency statistics & bins are enabled/disabled
- */
-static ssize_t sta_tx_latency_stat_read(struct file *file,
-					char __user *userbuf,
-					size_t count, loff_t *ppos)
-{
-	struct ieee80211_local *local = file->private_data;
-	struct ieee80211_tx_latency_bin_ranges  *tx_latency;
-	char *buf;
-	int bufsz, i, ret;
-	int pos = 0;
-
-	rcu_read_lock();
-
-	tx_latency = rcu_dereference(local->tx_latency);
-
-	if (tx_latency && tx_latency->n_ranges) {
-		bufsz = tx_latency->n_ranges * 15;
-		buf = kzalloc(bufsz, GFP_ATOMIC);
-		if (!buf)
-			goto err;
-
-		for (i = 0; i < tx_latency->n_ranges; i++)
-			pos += scnprintf(buf + pos, bufsz - pos, "%d,",
-					 tx_latency->ranges[i]);
-		pos += scnprintf(buf + pos, bufsz - pos, "\n");
-	} else if (tx_latency) {
-		bufsz = sizeof(TX_LATENCY_BINS_DISABLED) + 1;
-		buf = kzalloc(bufsz, GFP_ATOMIC);
-		if (!buf)
-			goto err;
-
-		pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
-				 TX_LATENCY_BINS_DISABLED);
-	} else {
-		bufsz = sizeof(TX_LATENCY_DISABLED) + 1;
-		buf = kzalloc(bufsz, GFP_ATOMIC);
-		if (!buf)
-			goto err;
-
-		pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
-				 TX_LATENCY_DISABLED);
-	}
-
-	rcu_read_unlock();
-
-	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
-	kfree(buf);
-
-	return ret;
-err:
-	rcu_read_unlock();
-	return -ENOMEM;
-}
-
-/*
- * Receive input from user regarding Tx latency statistics
- * The input should indicate if Tx latency statistics and bins are
- * enabled/disabled.
- * If bins are enabled input should indicate the amount of different bins and
- * their ranges. Each bin will count how many Tx frames transmitted within the
- * appropriate latency.
- * Legal input is:
- * a) "enable(bins disabled)" - to enable only general statistics
- * b) "a,b,c,d,...z" - to enable general statistics and bins, where all are
- * numbers and a < b < c < d.. < z
- * c) "disable" - disable all statistics
- * NOTE: must configure Tx latency statistics bins before stations connected.
- */
-
-static ssize_t sta_tx_latency_stat_write(struct file *file,
-					 const char __user *userbuf,
-					 size_t count, loff_t *ppos)
-{
-	struct ieee80211_local *local = file->private_data;
-	char buf[128] = {};
-	char *bins = buf;
-	char *token;
-	int buf_size, i, alloc_size;
-	int prev_bin = 0;
-	int n_ranges = 0;
-	int ret = count;
-	struct ieee80211_tx_latency_bin_ranges  *tx_latency;
-
-	if (sizeof(buf) <= count)
-		return -EINVAL;
-	buf_size = count;
-	if (copy_from_user(buf, userbuf, buf_size))
-		return -EFAULT;
-
-	mutex_lock(&local->sta_mtx);
-
-	/* cannot change config once we have stations */
-	if (local->num_sta)
-		goto unlock;
-
-	tx_latency =
-		rcu_dereference_protected(local->tx_latency,
-					  lockdep_is_held(&local->sta_mtx));
-
-	/* disable Tx statistics */
-	if (!strcmp(buf, TX_LATENCY_DISABLED)) {
-		if (!tx_latency)
-			goto unlock;
-		RCU_INIT_POINTER(local->tx_latency, NULL);
-		synchronize_rcu();
-		kfree(tx_latency);
-		goto unlock;
-	}
-
-	/* Tx latency already enabled */
-	if (tx_latency)
-		goto unlock;
-
-	if (strcmp(TX_LATENCY_BINS_DISABLED, buf)) {
-		/* check how many bins and between what ranges user requested */
-		token = buf;
-		while (*token != '\0') {
-			if (*token == TX_LATENCY_BIN_DELIMTER_C)
-				n_ranges++;
-			token++;
-		}
-		n_ranges++;
-	}
-
-	alloc_size = sizeof(struct ieee80211_tx_latency_bin_ranges) +
-		     n_ranges * sizeof(u32);
-	tx_latency = kzalloc(alloc_size, GFP_ATOMIC);
-	if (!tx_latency) {
-		ret = -ENOMEM;
-		goto unlock;
-	}
-	tx_latency->n_ranges = n_ranges;
-	for (i = 0; i < n_ranges; i++) { /* setting bin ranges */
-		token = strsep(&bins, TX_LATENCY_BIN_DELIMTER_S);
-		sscanf(token, "%d", &tx_latency->ranges[i]);
-		/* bins values should be in ascending order */
-		if (prev_bin >= tx_latency->ranges[i]) {
-			ret = -EINVAL;
-			kfree(tx_latency);
-			goto unlock;
-		}
-		prev_bin = tx_latency->ranges[i];
-	}
-	rcu_assign_pointer(local->tx_latency, tx_latency);
-
-unlock:
-	mutex_unlock(&local->sta_mtx);
-
-	return ret;
-}
-
-static const struct file_operations stats_tx_latency_ops = {
-	.write = sta_tx_latency_stat_write,
-	.read = sta_tx_latency_stat_read,
-	.open = simple_open,
-	.llseek = generic_file_llseek,
-};
-
 int mac80211_format_buffer(char __user *userbuf, size_t count,
 				  loff_t *ppos, char *fmt, ...)
 {
@@ -303,8 +137,6 @@
 		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
 	if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE)
 		sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
-	if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
-		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
 	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
 		sf += scnprintf(buf + sf, mxln - sf,
 				"REPORTS_TX_ACK_STATUS\n");
@@ -442,8 +274,6 @@
 #ifdef CPTCFG_MAC80211_DEBUG_COUNTERS
 	DEBUGFS_STATS_ADD(tx_handlers_drop, local->tx_handlers_drop);
 	DEBUGFS_STATS_ADD(tx_handlers_queued, local->tx_handlers_queued);
-	DEBUGFS_STATS_ADD(tx_handlers_drop_unencrypted,
-		local->tx_handlers_drop_unencrypted);
 	DEBUGFS_STATS_ADD(tx_handlers_drop_fragment,
 		local->tx_handlers_drop_fragment);
 	DEBUGFS_STATS_ADD(tx_handlers_drop_wep,
@@ -477,6 +307,4 @@
 	DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);
 	DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);
 	DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount);
-
-	DEBUGFS_DEVSTATS_ADD(tx_latency);
 }
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 5523b94..71ac1b5 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -94,17 +94,33 @@
 				key->u.tkip.tx.iv16);
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
 		pn = atomic64_read(&key->u.ccmp.tx_pn);
 		len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
 				(u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
 				(u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
 		break;
 	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 		pn = atomic64_read(&key->u.aes_cmac.tx_pn);
 		len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
 				(u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
 				(u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
 		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		pn = atomic64_read(&key->u.aes_gmac.tx_pn);
+		len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
+				(u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
+				(u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		pn = atomic64_read(&key->u.gcmp.tx_pn);
+		len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
+				(u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
+				(u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
+		break;
 	default:
 		return 0;
 	}
@@ -134,6 +150,7 @@
 		len = p - buf;
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
 		for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
 			rpn = key->u.ccmp.rx_pn[i];
 			p += scnprintf(p, sizeof(buf)+buf-p,
@@ -144,6 +161,7 @@
 		len = p - buf;
 		break;
 	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 		rpn = key->u.aes_cmac.rx_pn;
 		p += scnprintf(p, sizeof(buf)+buf-p,
 			       "%02x%02x%02x%02x%02x%02x\n",
@@ -151,6 +169,26 @@
 			       rpn[3], rpn[4], rpn[5]);
 		len = p - buf;
 		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		rpn = key->u.aes_gmac.rx_pn;
+		p += scnprintf(p, sizeof(buf)+buf-p,
+			       "%02x%02x%02x%02x%02x%02x\n",
+			       rpn[0], rpn[1], rpn[2],
+			       rpn[3], rpn[4], rpn[5]);
+		len = p - buf;
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
+			rpn = key->u.gcmp.rx_pn[i];
+			p += scnprintf(p, sizeof(buf)+buf-p,
+				       "%02x%02x%02x%02x%02x%02x\n",
+				       rpn[0], rpn[1], rpn[2],
+				       rpn[3], rpn[4], rpn[5]);
+		}
+		len = p - buf;
+		break;
 	default:
 		return 0;
 	}
@@ -167,12 +205,23 @@
 
 	switch (key->conf.cipher) {
 	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
 		len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays);
 		break;
 	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 		len = scnprintf(buf, sizeof(buf), "%u\n",
 				key->u.aes_cmac.replays);
 		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		len = scnprintf(buf, sizeof(buf), "%u\n",
+				key->u.aes_gmac.replays);
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		len = scnprintf(buf, sizeof(buf), "%u\n", key->u.gcmp.replays);
+		break;
 	default:
 		return 0;
 	}
@@ -189,9 +238,15 @@
 
 	switch (key->conf.cipher) {
 	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 		len = scnprintf(buf, sizeof(buf), "%u\n",
 				key->u.aes_cmac.icverrors);
 		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		len = scnprintf(buf, sizeof(buf), "%u\n",
+				key->u.aes_gmac.icverrors);
+		break;
 	default:
 		return 0;
 	}
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index bb625e9..a68f48b 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -177,7 +177,6 @@
 	IEEE80211_IF_FILE_R(name)
 
 /* common attributes */
-IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
 IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ],
 		  HEX);
 IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ],
@@ -562,7 +561,6 @@
 
 static void add_common_files(struct ieee80211_sub_if_data *sdata)
 {
-	DEBUGFS_ADD(drop_unencrypted);
 	DEBUGFS_ADD(rc_rateidx_mask_2ghz);
 	DEBUGFS_ADD(rc_rateidx_mask_5ghz);
 	DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 94c7009..252859e 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -39,13 +39,6 @@
 	.llseek = generic_file_llseek,					\
 }
 
-#define STA_OPS_W(name)							\
-static const struct file_operations sta_ ##name## _ops = {		\
-	.write = sta_##name##_write,					\
-	.open = simple_open,						\
-	.llseek = generic_file_llseek,					\
-}
-
 #define STA_OPS_RW(name)						\
 static const struct file_operations sta_ ##name## _ops = {		\
 	.read = sta_##name##_read,					\
@@ -398,131 +391,6 @@
 }
 STA_OPS(last_rx_rate);
 
-static int
-sta_tx_latency_stat_header(struct ieee80211_tx_latency_bin_ranges *tx_latency,
-			   char *buf, int pos, int bufsz)
-{
-	int i;
-	int range_count = tx_latency->n_ranges;
-	u32 *bin_ranges = tx_latency->ranges;
-
-	pos += scnprintf(buf + pos, bufsz - pos,
-			  "Station\t\t\tTID\tMax\tAvg");
-	if (range_count) {
-		pos += scnprintf(buf + pos, bufsz - pos,
-				  "\t<=%d", bin_ranges[0]);
-		for (i = 0; i < range_count - 1; i++)
-			pos += scnprintf(buf + pos, bufsz - pos, "\t%d-%d",
-					  bin_ranges[i], bin_ranges[i+1]);
-		pos += scnprintf(buf + pos, bufsz - pos,
-				  "\t%d<", bin_ranges[range_count - 1]);
-	}
-
-	pos += scnprintf(buf + pos, bufsz - pos, "\n");
-
-	return pos;
-}
-
-static int
-sta_tx_latency_stat_table(struct ieee80211_tx_latency_bin_ranges *tx_lat_range,
-			  struct ieee80211_tx_latency_stat *tx_lat,
-			  char *buf, int pos, int bufsz, int tid)
-{
-	u32 avg = 0;
-	int j;
-	int bin_count = tx_lat->bin_count;
-
-	pos += scnprintf(buf + pos, bufsz - pos, "\t\t\t%d", tid);
-	/* make sure you don't divide in 0 */
-	if (tx_lat->counter)
-		avg = tx_lat->sum / tx_lat->counter;
-
-	pos += scnprintf(buf + pos, bufsz - pos, "\t%d\t%d",
-			  tx_lat->max, avg);
-
-	if (tx_lat_range->n_ranges && tx_lat->bins)
-		for (j = 0; j < bin_count; j++)
-			pos += scnprintf(buf + pos, bufsz - pos,
-					  "\t%d", tx_lat->bins[j]);
-	pos += scnprintf(buf + pos, bufsz - pos, "\n");
-
-	return pos;
-}
-
-/*
- * Output Tx latency statistics station && restart all statistics information
- */
-static ssize_t sta_tx_latency_stat_read(struct file *file,
-					char __user *userbuf,
-					size_t count, loff_t *ppos)
-{
-	struct sta_info *sta = file->private_data;
-	struct ieee80211_local *local = sta->local;
-	struct ieee80211_tx_latency_bin_ranges *tx_latency;
-	char *buf;
-	int bufsz, ret, i;
-	int pos = 0;
-
-	bufsz = 20 * IEEE80211_NUM_TIDS *
-		sizeof(struct ieee80211_tx_latency_stat);
-	buf = kzalloc(bufsz, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	rcu_read_lock();
-
-	tx_latency = rcu_dereference(local->tx_latency);
-
-	if (!sta->tx_lat) {
-		pos += scnprintf(buf + pos, bufsz - pos,
-				 "Tx latency statistics are not enabled\n");
-		goto unlock;
-	}
-
-	pos = sta_tx_latency_stat_header(tx_latency, buf, pos, bufsz);
-
-	pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", sta->sta.addr);
-	for (i = 0; i < IEEE80211_NUM_TIDS; i++)
-		pos = sta_tx_latency_stat_table(tx_latency, &sta->tx_lat[i],
-						buf, pos, bufsz, i);
-unlock:
-	rcu_read_unlock();
-
-	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
-	kfree(buf);
-
-	return ret;
-}
-STA_OPS(tx_latency_stat);
-
-static ssize_t sta_tx_latency_stat_reset_write(struct file *file,
-					       const char __user *userbuf,
-					       size_t count, loff_t *ppos)
-{
-	u32 *bins;
-	int bin_count;
-	struct sta_info *sta = file->private_data;
-	int i;
-
-	if (!sta->tx_lat)
-		return -EINVAL;
-
-	for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
-		bins = sta->tx_lat[i].bins;
-		bin_count = sta->tx_lat[i].bin_count;
-
-		sta->tx_lat[i].max = 0;
-		sta->tx_lat[i].sum = 0;
-		sta->tx_lat[i].counter = 0;
-
-		if (bin_count)
-			memset(bins, 0, bin_count * sizeof(u32));
-	}
-
-	return count;
-}
-STA_OPS_W(tx_latency_stat_reset);
-
 #define DEBUGFS_ADD(name) \
 	debugfs_create_file(#name, 0400, \
 		sta->debugfs.dir, sta, &sta_ ##name## _ops);
@@ -576,8 +444,6 @@
 	DEBUGFS_ADD(last_ack_signal);
 	DEBUGFS_ADD(current_tx_rate);
 	DEBUGFS_ADD(last_rx_rate);
-	DEBUGFS_ADD(tx_latency_stat);
-	DEBUGFS_ADD(tx_latency_stat_reset);
 
 	DEBUGFS_ADD_COUNTER(rx_packets, rx_packets);
 	DEBUGFS_ADD_COUNTER(tx_packets, tx_packets);
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 754c893..d8c93db 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -639,6 +639,21 @@
 	trace_drv_return_void(local);
 }
 
+static inline void drv_sta_statistics(struct ieee80211_local *local,
+				      struct ieee80211_sub_if_data *sdata,
+				      struct ieee80211_sta *sta,
+				      struct station_info *sinfo)
+{
+	sdata = get_bss_sdata(sdata);
+	if (!check_sdata_in_driver(sdata))
+		return;
+
+	trace_drv_sta_statistics(local, sdata, sta);
+	if (local->ops->sta_statistics)
+		local->ops->sta_statistics(&local->hw, &sdata->vif, sta, sinfo);
+	trace_drv_return_void(local);
+}
+
 static inline int drv_conf_tx(struct ieee80211_local *local,
 			      struct ieee80211_sub_if_data *sdata, u16 ac,
 			      const struct ieee80211_tx_queue_params *params)
@@ -926,13 +941,13 @@
 	trace_drv_return_void(local);
 }
 
-static inline void drv_rssi_callback(struct ieee80211_local *local,
-				     struct ieee80211_sub_if_data *sdata,
-				     const enum ieee80211_rssi_event event)
+static inline void drv_event_callback(struct ieee80211_local *local,
+				      struct ieee80211_sub_if_data *sdata,
+				      const struct ieee80211_event *event)
 {
-	trace_drv_rssi_callback(local, sdata, event);
-	if (local->ops->rssi_callback)
-		local->ops->rssi_callback(&local->hw, &sdata->vif, event);
+	trace_drv_event_callback(local, sdata, event);
+	if (local->ops->event_callback)
+		local->ops->event_callback(&local->hw, &sdata->vif, event);
 	trace_drv_return_void(local);
 }
 
@@ -966,21 +981,6 @@
 	trace_drv_return_void(local);
 }
 
-static inline int drv_get_rssi(struct ieee80211_local *local,
-				struct ieee80211_sub_if_data *sdata,
-				struct ieee80211_sta *sta,
-				s8 *rssi_dbm)
-{
-	int ret;
-
-	might_sleep();
-
-	ret = local->ops->get_rssi(&local->hw, &sdata->vif, sta, rssi_dbm);
-	trace_drv_get_rssi(local, sta, *rssi_dbm, ret);
-
-	return ret;
-}
-
 static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
 				      struct ieee80211_sub_if_data *sdata)
 {
@@ -1367,4 +1367,16 @@
 	trace_drv_return_void(local);
 }
 
+static inline void drv_wake_tx_queue(struct ieee80211_local *local,
+				     struct txq_info *txq)
+{
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif);
+
+	if (!check_sdata_in_driver(sdata))
+		return;
+
+	trace_drv_wake_tx_queue(local, sdata, txq);
+	local->ops->wake_tx_queue(&local->hw, &txq->txq);
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c
index ebfc809..52bcea6 100644
--- a/net/mac80211/ethtool.c
+++ b/net/mac80211/ethtool.c
@@ -117,16 +117,16 @@
 		data[i++] = sta->sta_state;
 
 
-		if (sinfo.filled & STATION_INFO_TX_BITRATE)
+		if (sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE))
 			data[i] = 100000 *
 				cfg80211_calculate_bitrate(&sinfo.txrate);
 		i++;
-		if (sinfo.filled & STATION_INFO_RX_BITRATE)
+		if (sinfo.filled & BIT(NL80211_STA_INFO_RX_BITRATE))
 			data[i] = 100000 *
 				cfg80211_calculate_bitrate(&sinfo.rxrate);
 		i++;
 
-		if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
+		if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))
 			data[i] = (u8)sinfo.signal_avg;
 		i++;
 	} else {
@@ -175,24 +175,24 @@
 		data[i++] = (u8)survey.noise;
 	else
 		data[i++] = -1LL;
-	if (survey.filled & SURVEY_INFO_CHANNEL_TIME)
-		data[i++] = survey.channel_time;
+	if (survey.filled & SURVEY_INFO_TIME)
+		data[i++] = survey.time;
 	else
 		data[i++] = -1LL;
-	if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
-		data[i++] = survey.channel_time_busy;
+	if (survey.filled & SURVEY_INFO_TIME_BUSY)
+		data[i++] = survey.time_busy;
 	else
 		data[i++] = -1LL;
-	if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
-		data[i++] = survey.channel_time_ext_busy;
+	if (survey.filled & SURVEY_INFO_TIME_EXT_BUSY)
+		data[i++] = survey.time_ext_busy;
 	else
 		data[i++] = -1LL;
-	if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX)
-		data[i++] = survey.channel_time_rx;
+	if (survey.filled & SURVEY_INFO_TIME_RX)
+		data[i++] = survey.time_rx;
 	else
 		data[i++] = -1LL;
-	if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX)
-		data[i++] = survey.channel_time_tx;
+	if (survey.filled & SURVEY_INFO_TIME_TX)
+		data[i++] = survey.time_tx;
 	else
 		data[i++] = -1LL;
 
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index ff630be..7a76ce6 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -252,8 +252,6 @@
 		break;
 	}
 
-	if (bw != sta->sta.bandwidth)
-		changed = true;
 	sta->sta.bandwidth = bw;
 
 	sta->cur_max_bandwidth =
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 509bc15..bfef1b2 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -188,6 +188,16 @@
 		 */
 		pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
 						 chandef, 0);
+
+		/* add VHT capability and information IEs */
+		if (chandef->width != NL80211_CHAN_WIDTH_20 &&
+		    chandef->width != NL80211_CHAN_WIDTH_40 &&
+		    sband->vht_cap.vht_supported) {
+			pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
+							 sband->vht_cap.cap);
+			pos = ieee80211_ie_build_vht_oper(pos, &sband->vht_cap,
+							  chandef);
+		}
 	}
 
 	if (local->hw.queues >= IEEE80211_NUM_ACS)
@@ -249,8 +259,6 @@
 	if (presp)
 		kfree_rcu(presp, rcu_head);
 
-	sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
-
 	/* make a copy of the chandef, it could be modified below. */
 	chandef = *req_chandef;
 	chan = chandef.chan;
@@ -417,6 +425,11 @@
 					NL80211_CHAN_WIDTH_20_NOHT);
 		chandef.width = sdata->u.ibss.chandef.width;
 		break;
+	case NL80211_CHAN_WIDTH_80:
+	case NL80211_CHAN_WIDTH_160:
+		chandef = sdata->u.ibss.chandef;
+		chandef.chan = cbss->channel;
+		break;
 	default:
 		/* fall back to 20 MHz for unsupported modes */
 		cfg80211_chandef_create(&chandef, cbss->channel,
@@ -470,22 +483,19 @@
 	struct beacon_data *presp, *old_presp;
 	struct cfg80211_bss *cbss;
 	const struct cfg80211_bss_ies *ies;
-	u16 capability;
+	u16 capability = 0;
 	u64 tsf;
 	int ret = 0;
 
 	sdata_assert_lock(sdata);
 
-	capability = WLAN_CAPABILITY_IBSS;
-
 	if (ifibss->privacy)
-		capability |= WLAN_CAPABILITY_PRIVACY;
+		capability = WLAN_CAPABILITY_PRIVACY;
 
 	cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan,
 				ifibss->bssid, ifibss->ssid,
-				ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
-				WLAN_CAPABILITY_PRIVACY,
-				capability);
+				ifibss->ssid_len, IEEE80211_BSS_TYPE_IBSS,
+				IEEE80211_PRIVACY(ifibss->privacy));
 
 	if (WARN_ON(!cbss)) {
 		ret = -EINVAL;
@@ -525,23 +535,17 @@
 	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 	struct cfg80211_bss *cbss;
 	int err, changed = 0;
-	u16 capability;
 
 	sdata_assert_lock(sdata);
 
 	/* update cfg80211 bss information with the new channel */
 	if (!is_zero_ether_addr(ifibss->bssid)) {
-		capability = WLAN_CAPABILITY_IBSS;
-
-		if (ifibss->privacy)
-			capability |= WLAN_CAPABILITY_PRIVACY;
-
 		cbss = cfg80211_get_bss(sdata->local->hw.wiphy,
 					ifibss->chandef.chan,
 					ifibss->bssid, ifibss->ssid,
-					ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
-					WLAN_CAPABILITY_PRIVACY,
-					capability);
+					ifibss->ssid_len,
+					IEEE80211_BSS_TYPE_IBSS,
+					IEEE80211_PRIVACY(ifibss->privacy));
 		/* XXX: should not really modify cfg80211 data */
 		if (cbss) {
 			cbss->channel = sdata->csa_chandef.chan;
@@ -682,19 +686,13 @@
 	struct cfg80211_bss *cbss;
 	struct beacon_data *presp;
 	struct sta_info *sta;
-	u16 capability;
 
 	if (!is_zero_ether_addr(ifibss->bssid)) {
-		capability = WLAN_CAPABILITY_IBSS;
-
-		if (ifibss->privacy)
-			capability |= WLAN_CAPABILITY_PRIVACY;
-
 		cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
 					ifibss->bssid, ifibss->ssid,
-					ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
-					WLAN_CAPABILITY_PRIVACY,
-					capability);
+					ifibss->ssid_len,
+					IEEE80211_BSS_TYPE_IBSS,
+					IEEE80211_PRIVACY(ifibss->privacy));
 
 		if (cbss) {
 			cfg80211_unlink_bss(local->hw.wiphy, cbss);
@@ -980,6 +978,121 @@
 			    mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0);
 }
 
+static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
+				      struct ieee80211_mgmt *mgmt, size_t len,
+				      struct ieee80211_rx_status *rx_status,
+				      struct ieee802_11_elems *elems,
+				      struct ieee80211_channel *channel)
+{
+	struct sta_info *sta;
+	enum ieee80211_band band = rx_status->band;
+	enum nl80211_bss_scan_width scan_width;
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+	bool rates_updated = false;
+	u32 supp_rates = 0;
+
+	if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
+		return;
+
+	if (!ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid))
+		return;
+
+	rcu_read_lock();
+	sta = sta_info_get(sdata, mgmt->sa);
+
+	if (elems->supp_rates) {
+		supp_rates = ieee80211_sta_get_rates(sdata, elems,
+						     band, NULL);
+		if (sta) {
+			u32 prev_rates;
+
+			prev_rates = sta->sta.supp_rates[band];
+			/* make sure mandatory rates are always added */
+			scan_width = NL80211_BSS_CHAN_WIDTH_20;
+			if (rx_status->flag & RX_FLAG_5MHZ)
+				scan_width = NL80211_BSS_CHAN_WIDTH_5;
+			if (rx_status->flag & RX_FLAG_10MHZ)
+				scan_width = NL80211_BSS_CHAN_WIDTH_10;
+
+			sta->sta.supp_rates[band] = supp_rates |
+				ieee80211_mandatory_rates(sband, scan_width);
+			if (sta->sta.supp_rates[band] != prev_rates) {
+				ibss_dbg(sdata,
+					 "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
+					 sta->sta.addr, prev_rates,
+					 sta->sta.supp_rates[band]);
+				rates_updated = true;
+			}
+		} else {
+			rcu_read_unlock();
+			sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
+						     mgmt->sa, supp_rates);
+		}
+	}
+
+	if (sta && elems->wmm_info && local->hw.queues >= IEEE80211_NUM_ACS)
+		sta->sta.wme = true;
+
+	if (sta && elems->ht_operation && elems->ht_cap_elem &&
+	    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+	    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 &&
+	    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) {
+		/* we both use HT */
+		struct ieee80211_ht_cap htcap_ie;
+		struct cfg80211_chan_def chandef;
+		enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth;
+
+		ieee80211_ht_oper_to_chandef(channel,
+					     elems->ht_operation,
+					     &chandef);
+
+		memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
+		rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+								   &htcap_ie,
+								   sta);
+
+		if (elems->vht_operation && elems->vht_cap_elem &&
+		    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20 &&
+		    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_40) {
+			/* we both use VHT */
+			struct ieee80211_vht_cap cap_ie;
+			struct ieee80211_sta_vht_cap cap = sta->sta.vht_cap;
+
+			ieee80211_vht_oper_to_chandef(channel,
+						      elems->vht_operation,
+						      &chandef);
+			memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie));
+			ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+							    &cap_ie, sta);
+			if (memcmp(&cap, &sta->sta.vht_cap, sizeof(cap)))
+				rates_updated |= true;
+		}
+
+		if (bw != sta->sta.bandwidth)
+			rates_updated |= true;
+
+		if (!cfg80211_chandef_compatible(&sdata->u.ibss.chandef,
+						 &chandef))
+			WARN_ON_ONCE(1);
+	}
+
+	if (sta && rates_updated) {
+		u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
+		u8 rx_nss = sta->sta.rx_nss;
+
+		/* Force rx_nss recalculation */
+		sta->sta.rx_nss = 0;
+		rate_control_rate_init(sta);
+		if (sta->sta.rx_nss != rx_nss)
+			changed |= IEEE80211_RC_NSS_CHANGED;
+
+		drv_sta_rc_update(local, sdata, &sta->sta, changed);
+	}
+
+	rcu_read_unlock();
+}
+
 static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
 				  struct ieee80211_mgmt *mgmt, size_t len,
 				  struct ieee80211_rx_status *rx_status,
@@ -988,94 +1101,16 @@
 	struct ieee80211_local *local = sdata->local;
 	struct cfg80211_bss *cbss;
 	struct ieee80211_bss *bss;
-	struct sta_info *sta;
 	struct ieee80211_channel *channel;
 	u64 beacon_timestamp, rx_timestamp;
 	u32 supp_rates = 0;
 	enum ieee80211_band band = rx_status->band;
-	enum nl80211_bss_scan_width scan_width;
-	struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
-	bool rates_updated = false;
 
 	channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
 	if (!channel)
 		return;
 
-	if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
-	    ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid)) {
-
-		rcu_read_lock();
-		sta = sta_info_get(sdata, mgmt->sa);
-
-		if (elems->supp_rates) {
-			supp_rates = ieee80211_sta_get_rates(sdata, elems,
-							     band, NULL);
-			if (sta) {
-				u32 prev_rates;
-
-				prev_rates = sta->sta.supp_rates[band];
-				/* make sure mandatory rates are always added */
-				scan_width = NL80211_BSS_CHAN_WIDTH_20;
-				if (rx_status->flag & RX_FLAG_5MHZ)
-					scan_width = NL80211_BSS_CHAN_WIDTH_5;
-				if (rx_status->flag & RX_FLAG_10MHZ)
-					scan_width = NL80211_BSS_CHAN_WIDTH_10;
-
-				sta->sta.supp_rates[band] = supp_rates |
-					ieee80211_mandatory_rates(sband,
-								  scan_width);
-				if (sta->sta.supp_rates[band] != prev_rates) {
-					ibss_dbg(sdata,
-						 "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
-						 sta->sta.addr, prev_rates,
-						 sta->sta.supp_rates[band]);
-					rates_updated = true;
-				}
-			} else {
-				rcu_read_unlock();
-				sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
-						mgmt->sa, supp_rates);
-			}
-		}
-
-		if (sta && elems->wmm_info)
-			sta->sta.wme = true;
-
-		if (sta && elems->ht_operation && elems->ht_cap_elem &&
-		    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
-		    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 &&
-		    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) {
-			/* we both use HT */
-			struct ieee80211_ht_cap htcap_ie;
-			struct cfg80211_chan_def chandef;
-
-			ieee80211_ht_oper_to_chandef(channel,
-						     elems->ht_operation,
-						     &chandef);
-
-			memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
-
-			/*
-			 * fall back to HT20 if we don't use or use
-			 * the other extension channel
-			 */
-			if (chandef.center_freq1 !=
-			    sdata->u.ibss.chandef.center_freq1)
-				htcap_ie.cap_info &=
-					cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
-
-			rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(
-						sdata, sband, &htcap_ie, sta);
-		}
-
-		if (sta && rates_updated) {
-			drv_sta_rc_update(local, sdata, &sta->sta,
-					  IEEE80211_RC_SUPP_RATES_CHANGED);
-			rate_control_rate_init(sta);
-		}
-
-		rcu_read_unlock();
-	}
+	ieee80211_update_sta_info(sdata, mgmt, len, rx_status, elems, channel);
 
 	bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
 					channel);
@@ -1266,7 +1301,7 @@
 
 	scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
 	ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
-				    NULL, scan_width);
+				    NULL, 0, scan_width);
 }
 
 static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -1297,14 +1332,82 @@
 
 	if (ifibss->privacy)
 		capability |= WLAN_CAPABILITY_PRIVACY;
-	else
-		sdata->drop_unencrypted = 0;
 
 	__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
 				  &ifibss->chandef, ifibss->basic_rates,
 				  capability, 0, true);
 }
 
+static unsigned ibss_setup_channels(struct wiphy *wiphy,
+				    struct ieee80211_channel **channels,
+				    unsigned int channels_max,
+				    u32 center_freq, u32 width)
+{
+	struct ieee80211_channel *chan = NULL;
+	unsigned int n_chan = 0;
+	u32 start_freq, end_freq, freq;
+
+	if (width <= 20) {
+		start_freq = center_freq;
+		end_freq = center_freq;
+	} else {
+		start_freq = center_freq - width / 2 + 10;
+		end_freq = center_freq + width / 2 - 10;
+	}
+
+	for (freq = start_freq; freq <= end_freq; freq += 20) {
+		chan = ieee80211_get_channel(wiphy, freq);
+		if (!chan)
+			continue;
+		if (n_chan >= channels_max)
+			return n_chan;
+
+		channels[n_chan] = chan;
+		n_chan++;
+	}
+
+	return n_chan;
+}
+
+static unsigned int
+ieee80211_ibss_setup_scan_channels(struct wiphy *wiphy,
+				   const struct cfg80211_chan_def *chandef,
+				   struct ieee80211_channel **channels,
+				   unsigned int channels_max)
+{
+	unsigned int n_chan = 0;
+	u32 width, cf1, cf2 = 0;
+
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_40:
+		width = 40;
+		break;
+	case NL80211_CHAN_WIDTH_80P80:
+		cf2 = chandef->center_freq2;
+		/* fall through */
+	case NL80211_CHAN_WIDTH_80:
+		width = 80;
+		break;
+	case NL80211_CHAN_WIDTH_160:
+		width = 160;
+		break;
+	default:
+		width = 20;
+		break;
+	}
+
+	cf1 = chandef->center_freq1;
+
+	n_chan = ibss_setup_channels(wiphy, channels, channels_max, cf1, width);
+
+	if (cf2)
+		n_chan += ibss_setup_channels(wiphy, &channels[n_chan],
+					      channels_max - n_chan, cf2,
+					      width);
+
+	return n_chan;
+}
+
 /*
  * This function is called with state == IEEE80211_IBSS_MLME_SEARCH
  */
@@ -1318,7 +1421,6 @@
 	const u8 *bssid = NULL;
 	enum nl80211_bss_scan_width scan_width;
 	int active_ibss;
-	u16 capability;
 
 	sdata_assert_lock(sdata);
 
@@ -1328,9 +1430,6 @@
 	if (active_ibss)
 		return;
 
-	capability = WLAN_CAPABILITY_IBSS;
-	if (ifibss->privacy)
-		capability |= WLAN_CAPABILITY_PRIVACY;
 	if (ifibss->fixed_bssid)
 		bssid = ifibss->bssid;
 	if (ifibss->fixed_channel)
@@ -1339,8 +1438,8 @@
 		bssid = ifibss->bssid;
 	cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid,
 				ifibss->ssid, ifibss->ssid_len,
-				WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_PRIVACY,
-				capability);
+				IEEE80211_BSS_TYPE_IBSS,
+				IEEE80211_PRIVACY(ifibss->privacy));
 
 	if (cbss) {
 		struct ieee80211_bss *bss;
@@ -1374,11 +1473,18 @@
 	/* Selected IBSS not found in current scan results - try to scan */
 	if (time_after(jiffies, ifibss->last_scan_completed +
 					IEEE80211_SCAN_INTERVAL)) {
+		struct ieee80211_channel *channels[8];
+		unsigned int num;
+
 		sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
 
+		num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
+							 &ifibss->chandef,
+							 channels,
+							 ARRAY_SIZE(channels));
 		scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
 		ieee80211_request_ibss_scan(sdata, ifibss->ssid,
-					    ifibss->ssid_len, chan,
+					    ifibss->ssid_len, channels, num,
 					    scan_width);
 	} else {
 		int interval = IEEE80211_SCAN_INTERVAL;
@@ -1735,7 +1841,7 @@
 
 	ieee80211_ibss_disconnect(sdata);
 	ifibss->ssid_len = 0;
-	memset(ifibss->bssid, 0, ETH_ALEN);
+	eth_zero_addr(ifibss->bssid);
 
 	/* remove beacon */
 	kfree(sdata->u.ibss.ie);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f08f4b9..2feb5da 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -26,6 +26,7 @@
 #include <linux/etherdevice.h>
 #include <linux/leds.h>
 #include <linux/idr.h>
+#include <linux/rhashtable.h>
 #include <net/ieee80211_radiotap.h>
 #include <net/cfg80211.h>
 #include <net/mac80211.h>
@@ -58,13 +59,24 @@
 #define IEEE80211_UNSET_POWER_LEVEL	INT_MIN
 
 /*
- * Some APs experience problems when working with U-APSD. Decrease the
- * probability of that happening by using legacy mode for all ACs but VO.
- * The AP that caused us trouble was a Cisco 4410N. It ignores our
- * setting, and always treats non-VO ACs as legacy.
+ * Some APs experience problems when working with U-APSD. Decreasing the
+ * probability of that happening by using legacy mode for all ACs but VO isn't
+ * enough.
+ *
+ * Cisco 4410N originally forced us to enable VO by default only because it
+ * treated non-VO ACs as legacy.
+ *
+ * However some APs (notably Netgear R7000) silently reclassify packets to
+ * different ACs. Since u-APSD ACs require trigger frames for frame retrieval
+ * clients would never see some frames (e.g. ARP responses) or would fetch them
+ * accidentally after a long time.
+ *
+ * It makes little sense to enable u-APSD queues by default because it needs
+ * userspace applications to be aware of it to actually take advantage of the
+ * possible additional powersavings. Implicitly depending on driver autotrigger
+ * frame support doesn't make much sense.
  */
-#define IEEE80211_DEFAULT_UAPSD_QUEUES \
-	IEEE80211_WMM_IE_STA_QOSINFO_AC_VO
+#define IEEE80211_DEFAULT_UAPSD_QUEUES 0
 
 #define IEEE80211_DEFAULT_MAX_SP_LEN		\
 	IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL
@@ -453,6 +465,7 @@
 	unsigned int flags;
 
 	bool csa_waiting_bcn;
+	bool csa_ignored_same_chan;
 
 	bool beacon_crc_valid;
 	u32 beacon_crc;
@@ -798,6 +811,19 @@
 	struct rcu_head rcu_head;
 };
 
+enum txq_info_flags {
+	IEEE80211_TXQ_STOP,
+	IEEE80211_TXQ_AMPDU,
+};
+
+struct txq_info {
+	struct sk_buff_head queue;
+	unsigned long flags;
+
+	/* keep last! */
+	struct ieee80211_txq txq;
+};
+
 struct ieee80211_sub_if_data {
 	struct list_head list;
 
@@ -818,8 +844,6 @@
 
 	unsigned long state;
 
-	int drop_unencrypted;
-
 	char name[IFNAMSIZ];
 
 	/* Fragment table for host-based reassembly */
@@ -842,6 +866,7 @@
 	bool control_port_no_encrypt;
 	int encrypt_headroom;
 
+	atomic_t txqs_len[IEEE80211_NUM_ACS];
 	struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
 	struct mac80211_qos_map __rcu *qos_map;
 
@@ -1030,24 +1055,6 @@
 };
 #endif
 
-/*
- * struct ieee80211_tx_latency_bin_ranges - Tx latency statistics bins ranges
- *
- * Measuring Tx latency statistics. Counts how many Tx frames transmitted in a
- * certain latency range (in Milliseconds). Each station that uses these
- * ranges will have bins to count the amount of frames received in that range.
- * The user can configure the ranges via debugfs.
- * If ranges is NULL then Tx latency statistics bins are disabled for all
- * stations.
- *
- * @n_ranges: number of ranges that are taken in account
- * @ranges: the ranges that the user requested or NULL if disabled.
- */
-struct ieee80211_tx_latency_bin_ranges {
-	int n_ranges;
-	u32 ranges[];
-};
-
 /**
  * mac80211 scan flags - currently active scan mode
  *
@@ -1168,8 +1175,6 @@
 	/* wowlan is enabled -- don't reconfig on resume */
 	bool wowlan;
 
-	/* DFS/radar detection is enabled */
-	bool radar_detect_enabled;
 	struct work_struct radar_detected_work;
 
 	/* number of RX chains the hardware has */
@@ -1197,16 +1202,10 @@
 	spinlock_t tim_lock;
 	unsigned long num_sta;
 	struct list_head sta_list;
-	struct sta_info __rcu *sta_hash[STA_HASH_SIZE];
+	struct rhashtable sta_hash;
 	struct timer_list sta_cleanup;
 	int sta_generation;
 
-	/*
-	 * Tx latency statistics parameters for all stations.
-	 * Can enable via debugfs (NULL when disabled).
-	 */
-	struct ieee80211_tx_latency_bin_ranges __rcu *tx_latency;
-
 	struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
 	struct tasklet_struct tx_pending_tasklet;
 
@@ -1291,7 +1290,6 @@
 	/* TX/RX handler statistics */
 	unsigned int tx_handlers_drop;
 	unsigned int tx_handlers_queued;
-	unsigned int tx_handlers_drop_unencrypted;
 	unsigned int tx_handlers_drop_fragment;
 	unsigned int tx_handlers_drop_wep;
 	unsigned int tx_handlers_drop_not_assoc;
@@ -1469,6 +1467,10 @@
 	return container_of(hw, struct ieee80211_local, hw);
 }
 
+static inline struct txq_info *to_txq_info(struct ieee80211_txq *txq)
+{
+	return container_of(txq, struct txq_info, txq);
+}
 
 static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
 {
@@ -1561,7 +1563,8 @@
 void ieee80211_scan_work(struct work_struct *work);
 int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
 				const u8 *ssid, u8 ssid_len,
-				struct ieee80211_channel *chan,
+				struct ieee80211_channel **channels,
+				unsigned int n_channels,
 				enum nl80211_bss_scan_width scan_width);
 int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
 			   struct cfg80211_scan_request *req);
@@ -1610,6 +1613,7 @@
 int ieee80211_iface_init(void);
 void ieee80211_iface_exit(void);
 int ieee80211_if_add(struct ieee80211_local *local, const char *name,
+		     unsigned char name_assign_type,
 		     struct wireless_dev **new_wdev, enum nl80211_iftype type,
 		     struct vif_params *params);
 int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
@@ -1626,7 +1630,8 @@
 void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
 
 bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
-void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+			      bool update_bss);
 
 static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
 {
@@ -1707,6 +1712,7 @@
 				    struct ieee80211_supported_band *sband,
 				    const struct ieee80211_vht_cap *vht_cap_ie,
 				    struct sta_info *sta);
+enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta);
 enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
 void ieee80211_sta_set_rx_nss(struct sta_info *sta);
 u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
@@ -1755,7 +1761,8 @@
 {
 	struct ieee80211_local *local = hw_to_local(hw);
 
-	WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
+	WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) &&
+	     !test_bit(SCAN_COMPLETED, &local->scanning),
 		"%s: resume with hardware scan still in progress\n",
 		wiphy_name(hw->wiphy));
 
@@ -1774,7 +1781,8 @@
 				     gfp_t gfp);
 void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
 			       bool bss_notify);
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
+		    struct sta_info *sta, struct sk_buff *skb);
 
 void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
 				 struct sk_buff *skb, int tid,
@@ -1884,11 +1892,44 @@
 void ieee80211_add_pending_skbs(struct ieee80211_local *local,
 				struct sk_buff_head *skbs);
 void ieee80211_flush_queues(struct ieee80211_local *local,
-			    struct ieee80211_sub_if_data *sdata);
+			    struct ieee80211_sub_if_data *sdata, bool drop);
 void __ieee80211_flush_queues(struct ieee80211_local *local,
 			      struct ieee80211_sub_if_data *sdata,
-			      unsigned int queues);
+			      unsigned int queues, bool drop);
 
+static inline bool ieee80211_can_run_worker(struct ieee80211_local *local)
+{
+	/*
+	 * If quiescing is set, we are racing with __ieee80211_suspend.
+	 * __ieee80211_suspend flushes the workers after setting quiescing,
+	 * and we check quiescing / suspended before enqueing new workers.
+	 * We should abort the worker to avoid the races below.
+	 */
+	if (local->quiescing)
+		return false;
+
+	/*
+	 * We might already be suspended if the following scenario occurs:
+	 * __ieee80211_suspend		Control path
+	 *
+	 *				if (local->quiescing)
+	 *					return;
+	 * local->quiescing = true;
+	 * flush_workqueue();
+	 *				queue_work(...);
+	 * local->suspended = true;
+	 * local->quiescing = false;
+	 *				worker starts running...
+	 */
+	if (local->suspended)
+		return false;
+
+	return true;
+}
+
+void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata,
+			     struct sta_info *sta,
+			     struct txq_info *txq, int tid);
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
 			 u16 transaction, u16 auth_alg, u16 status,
 			 const u8 *extra, size_t extra_len, const u8 *bssid,
@@ -1927,10 +1968,6 @@
 void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
 void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);
 
-size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
-			      const u8 *ids, int n_ids,
-			      const u8 *after_ric, int n_after_ric,
-			      size_t offset);
 size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
 u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
 			      u16 cap);
@@ -1939,6 +1976,8 @@
 			       u16 prot_mode);
 u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
 			       u32 cap);
+u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
+				const struct cfg80211_chan_def *chandef);
 int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
 			     const struct ieee80211_supported_band *sband,
 			     const u8 *srates, int srates_len, u32 *rates);
@@ -1954,6 +1993,9 @@
 void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
 				  const struct ieee80211_ht_operation *ht_oper,
 				  struct cfg80211_chan_def *chandef);
+void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan,
+				   const struct ieee80211_vht_operation *oper,
+				   struct cfg80211_chan_def *chandef);
 u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
 
 int __must_check
@@ -1984,6 +2026,7 @@
 				   struct ieee80211_chanctx *chanctx);
 void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
 				      struct ieee80211_chanctx *ctx);
+bool ieee80211_is_radar_required(struct ieee80211_local *local);
 
 void ieee80211_dfs_cac_timer(unsigned long data);
 void ieee80211_dfs_cac_timer_work(struct work_struct *work);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index f7c759a..7d1c5a9 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -73,9 +73,10 @@
 	return false;
 }
 
-void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+			      bool update_bss)
 {
-	if (__ieee80211_recalc_txpower(sdata))
+	if (__ieee80211_recalc_txpower(sdata) || update_bss)
 		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
 }
 
@@ -93,7 +94,7 @@
 	if (local->hw.conf.flags & IEEE80211_CONF_IDLE)
 		return 0;
 
-	ieee80211_flush_queues(local, NULL);
+	ieee80211_flush_queues(local, NULL, false);
 
 	local->hw.conf.flags |= IEEE80211_CONF_IDLE;
 	return IEEE80211_CONF_CHANGE_IDLE;
@@ -968,6 +969,13 @@
 	}
 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 
+	if (sdata->vif.txq) {
+		struct txq_info *txqi = to_txq_info(sdata->vif.txq);
+
+		ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+		atomic_set(&sdata->txqs_len[txqi->txq.ac], 0);
+	}
+
 	if (local->open_count == 0)
 		ieee80211_clear_tx_pending(local);
 
@@ -1189,12 +1197,7 @@
 	if (local->scanning)
 		return;
 
-	/*
-	 * ieee80211_queue_work() should have picked up most cases,
-	 * here we'll pick the rest.
-	 */
-	if (WARN(local->suspended,
-		 "interface work scheduled while going to suspend\n"))
+	if (!ieee80211_can_run_worker(local))
 		return;
 
 	/* first process frames */
@@ -1532,7 +1535,6 @@
 	}
 
 	/* reset some values that shouldn't be kept across type changes */
-	sdata->drop_unencrypted = 0;
 	if (type == NL80211_IFTYPE_STATION)
 		sdata->u.mgd.use_4addr = false;
 
@@ -1673,11 +1675,13 @@
 }
 
 int ieee80211_if_add(struct ieee80211_local *local, const char *name,
+		     unsigned char name_assign_type,
 		     struct wireless_dev **new_wdev, enum nl80211_iftype type,
 		     struct vif_params *params)
 {
 	struct net_device *ndev = NULL;
 	struct ieee80211_sub_if_data *sdata = NULL;
+	struct txq_info *txqi;
 	int ret, i;
 	int txqs = 1;
 
@@ -1697,11 +1701,19 @@
 		ieee80211_assign_perm_addr(local, wdev->address, type);
 		memcpy(sdata->vif.addr, wdev->address, ETH_ALEN);
 	} else {
+		int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size,
+				 sizeof(void *));
+		int txq_size = 0;
+
+		if (local->ops->wake_tx_queue)
+			txq_size += sizeof(struct txq_info) +
+				    local->hw.txq_data_size;
+
 		if (local->hw.queues >= IEEE80211_NUM_ACS)
 			txqs = IEEE80211_NUM_ACS;
 
-		ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size,
-					name, NET_NAME_UNKNOWN,
+		ndev = alloc_netdev_mqs(size + txq_size,
+					name, name_assign_type,
 					ieee80211_if_setup, txqs, 1);
 		if (!ndev)
 			return -ENOMEM;
@@ -1735,6 +1747,11 @@
 		memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN);
 		memcpy(sdata->name, ndev->name, IFNAMSIZ);
 
+		if (txq_size) {
+			txqi = netdev_priv(ndev) + size;
+			ieee80211_init_tx_queue(sdata, NULL, txqi, 0);
+		}
+
 		sdata->dev = ndev;
 	}
 
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 0bb7038..2291cd7 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -24,6 +24,8 @@
 #include "debugfs_key.h"
 #include "aes_ccm.h"
 #include "aes_cmac.h"
+#include "aes_gmac.h"
+#include "aes_gcm.h"
 
 
 /**
@@ -90,7 +92,7 @@
 {
 	struct ieee80211_sub_if_data *sdata;
 	struct sta_info *sta;
-	int ret;
+	int ret = -EOPNOTSUPP;
 
 	might_sleep();
 
@@ -140,7 +142,8 @@
 	if (!ret) {
 		key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
 
-		if (!(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC))
+		if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+		      (key->conf.flags & IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
 			sdata->crypto_tx_tailroom_needed_cnt--;
 
 		WARN_ON((key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
@@ -149,7 +152,7 @@
 		return 0;
 	}
 
-	if (ret != -ENOSPC && ret != -EOPNOTSUPP)
+	if (ret != -ENOSPC && ret != -EOPNOTSUPP && ret != 1)
 		sdata_err(sdata,
 			  "failed to set key (%d, %pM) to hardware (%d)\n",
 			  key->conf.keyidx,
@@ -161,8 +164,18 @@
 	case WLAN_CIPHER_SUITE_WEP104:
 	case WLAN_CIPHER_SUITE_TKIP:
 	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
 	case WLAN_CIPHER_SUITE_AES_CMAC:
-		/* all of these we can do in software */
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		/* all of these we can do in software - if driver can */
+		if (ret == 1)
+			return 0;
+		if (key->local->hw.flags & IEEE80211_HW_SW_CRYPTO_CONTROL)
+			return -EINVAL;
 		return 0;
 	default:
 		return -EINVAL;
@@ -188,7 +201,8 @@
 	sta = key->sta;
 	sdata = key->sdata;
 
-	if (!(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC))
+	if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+	      (key->conf.flags & IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
 		increment_tailroom_need_count(sdata);
 
 	ret = drv_set_key(key->local, DISABLE_KEY, sdata,
@@ -380,7 +394,26 @@
 		 * Initialize AES key state here as an optimization so that
 		 * it does not need to be initialized for every packet.
 		 */
-		key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data);
+		key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
+			key_data, key_len, IEEE80211_CCMP_MIC_LEN);
+		if (IS_ERR(key->u.ccmp.tfm)) {
+			err = PTR_ERR(key->u.ccmp.tfm);
+			kfree(key);
+			return ERR_PTR(err);
+		}
+		break;
+	case WLAN_CIPHER_SUITE_CCMP_256:
+		key->conf.iv_len = IEEE80211_CCMP_256_HDR_LEN;
+		key->conf.icv_len = IEEE80211_CCMP_256_MIC_LEN;
+		for (i = 0; seq && i < IEEE80211_NUM_TIDS + 1; i++)
+			for (j = 0; j < IEEE80211_CCMP_256_PN_LEN; j++)
+				key->u.ccmp.rx_pn[i][j] =
+					seq[IEEE80211_CCMP_256_PN_LEN - j - 1];
+		/* Initialize AES key state here as an optimization so that
+		 * it does not need to be initialized for every packet.
+		 */
+		key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
+			key_data, key_len, IEEE80211_CCMP_256_MIC_LEN);
 		if (IS_ERR(key->u.ccmp.tfm)) {
 			err = PTR_ERR(key->u.ccmp.tfm);
 			kfree(key);
@@ -388,8 +421,12 @@
 		}
 		break;
 	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 		key->conf.iv_len = 0;
-		key->conf.icv_len = sizeof(struct ieee80211_mmie);
+		if (cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+			key->conf.icv_len = sizeof(struct ieee80211_mmie);
+		else
+			key->conf.icv_len = sizeof(struct ieee80211_mmie_16);
 		if (seq)
 			for (j = 0; j < IEEE80211_CMAC_PN_LEN; j++)
 				key->u.aes_cmac.rx_pn[j] =
@@ -399,13 +436,51 @@
 		 * it does not need to be initialized for every packet.
 		 */
 		key->u.aes_cmac.tfm =
-			ieee80211_aes_cmac_key_setup(key_data);
+			ieee80211_aes_cmac_key_setup(key_data, key_len);
 		if (IS_ERR(key->u.aes_cmac.tfm)) {
 			err = PTR_ERR(key->u.aes_cmac.tfm);
 			kfree(key);
 			return ERR_PTR(err);
 		}
 		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		key->conf.iv_len = 0;
+		key->conf.icv_len = sizeof(struct ieee80211_mmie_16);
+		if (seq)
+			for (j = 0; j < IEEE80211_GMAC_PN_LEN; j++)
+				key->u.aes_gmac.rx_pn[j] =
+					seq[IEEE80211_GMAC_PN_LEN - j - 1];
+		/* Initialize AES key state here as an optimization so that
+		 * it does not need to be initialized for every packet.
+		 */
+		key->u.aes_gmac.tfm =
+			ieee80211_aes_gmac_key_setup(key_data, key_len);
+		if (IS_ERR(key->u.aes_gmac.tfm)) {
+			err = PTR_ERR(key->u.aes_gmac.tfm);
+			kfree(key);
+			return ERR_PTR(err);
+		}
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		key->conf.iv_len = IEEE80211_GCMP_HDR_LEN;
+		key->conf.icv_len = IEEE80211_GCMP_MIC_LEN;
+		for (i = 0; seq && i < IEEE80211_NUM_TIDS + 1; i++)
+			for (j = 0; j < IEEE80211_GCMP_PN_LEN; j++)
+				key->u.gcmp.rx_pn[i][j] =
+					seq[IEEE80211_GCMP_PN_LEN - j - 1];
+		/* Initialize AES key state here as an optimization so that
+		 * it does not need to be initialized for every packet.
+		 */
+		key->u.gcmp.tfm = ieee80211_aes_gcm_key_setup_encrypt(key_data,
+								      key_len);
+		if (IS_ERR(key->u.gcmp.tfm)) {
+			err = PTR_ERR(key->u.gcmp.tfm);
+			kfree(key);
+			return ERR_PTR(err);
+		}
+		break;
 	default:
 		if (cs) {
 			size_t len = (seq_len > MAX_PN_LEN) ?
@@ -417,6 +492,7 @@
 				for (j = 0; j < len; j++)
 					key->u.gen.rx_pn[i][j] =
 							seq[len - j - 1];
+			key->flags |= KEY_FLAG_CIPHER_SCHEME;
 		}
 	}
 	memcpy(key->conf.key, key_data, key_len);
@@ -427,10 +503,24 @@
 
 static void ieee80211_key_free_common(struct ieee80211_key *key)
 {
-	if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP)
+	switch (key->conf.cipher) {
+	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
 		ieee80211_aes_key_free(key->u.ccmp.tfm);
-	if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+		break;
+	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 		ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
+		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		ieee80211_aes_gmac_key_free(key->u.aes_gmac.tfm);
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		ieee80211_aes_gcm_key_free(key->u.gcmp.tfm);
+		break;
+	}
 	kzfree(key);
 }
 
@@ -737,6 +827,7 @@
 		seq->tkip.iv16 = key->u.tkip.tx.iv16;
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
 		pn64 = atomic64_read(&key->u.ccmp.tx_pn);
 		seq->ccmp.pn[5] = pn64;
 		seq->ccmp.pn[4] = pn64 >> 8;
@@ -746,6 +837,7 @@
 		seq->ccmp.pn[0] = pn64 >> 40;
 		break;
 	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 		pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
 		seq->ccmp.pn[5] = pn64;
 		seq->ccmp.pn[4] = pn64 >> 8;
@@ -754,6 +846,26 @@
 		seq->ccmp.pn[1] = pn64 >> 32;
 		seq->ccmp.pn[0] = pn64 >> 40;
 		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		pn64 = atomic64_read(&key->u.aes_gmac.tx_pn);
+		seq->ccmp.pn[5] = pn64;
+		seq->ccmp.pn[4] = pn64 >> 8;
+		seq->ccmp.pn[3] = pn64 >> 16;
+		seq->ccmp.pn[2] = pn64 >> 24;
+		seq->ccmp.pn[1] = pn64 >> 32;
+		seq->ccmp.pn[0] = pn64 >> 40;
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		pn64 = atomic64_read(&key->u.gcmp.tx_pn);
+		seq->gcmp.pn[5] = pn64;
+		seq->gcmp.pn[4] = pn64 >> 8;
+		seq->gcmp.pn[3] = pn64 >> 16;
+		seq->gcmp.pn[2] = pn64 >> 24;
+		seq->gcmp.pn[1] = pn64 >> 32;
+		seq->gcmp.pn[0] = pn64 >> 40;
+		break;
 	default:
 		WARN_ON(1);
 	}
@@ -776,6 +888,7 @@
 		seq->tkip.iv16 = key->u.tkip.rx[tid].iv16;
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
 		if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
 			return;
 		if (tid < 0)
@@ -785,11 +898,29 @@
 		memcpy(seq->ccmp.pn, pn, IEEE80211_CCMP_PN_LEN);
 		break;
 	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 		if (WARN_ON(tid != 0))
 			return;
 		pn = key->u.aes_cmac.rx_pn;
 		memcpy(seq->aes_cmac.pn, pn, IEEE80211_CMAC_PN_LEN);
 		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		if (WARN_ON(tid != 0))
+			return;
+		pn = key->u.aes_gmac.rx_pn;
+		memcpy(seq->aes_gmac.pn, pn, IEEE80211_GMAC_PN_LEN);
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
+			return;
+		if (tid < 0)
+			pn = key->u.gcmp.rx_pn[IEEE80211_NUM_TIDS];
+		else
+			pn = key->u.gcmp.rx_pn[tid];
+		memcpy(seq->gcmp.pn, pn, IEEE80211_GCMP_PN_LEN);
+		break;
 	}
 }
 EXPORT_SYMBOL(ieee80211_get_key_rx_seq);
@@ -808,6 +939,7 @@
 		key->u.tkip.tx.iv16 = seq->tkip.iv16;
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
 		pn64 = (u64)seq->ccmp.pn[5] |
 		       ((u64)seq->ccmp.pn[4] << 8) |
 		       ((u64)seq->ccmp.pn[3] << 16) |
@@ -817,6 +949,7 @@
 		atomic64_set(&key->u.ccmp.tx_pn, pn64);
 		break;
 	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 		pn64 = (u64)seq->aes_cmac.pn[5] |
 		       ((u64)seq->aes_cmac.pn[4] << 8) |
 		       ((u64)seq->aes_cmac.pn[3] << 16) |
@@ -825,6 +958,26 @@
 		       ((u64)seq->aes_cmac.pn[0] << 40);
 		atomic64_set(&key->u.aes_cmac.tx_pn, pn64);
 		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		pn64 = (u64)seq->aes_gmac.pn[5] |
+		       ((u64)seq->aes_gmac.pn[4] << 8) |
+		       ((u64)seq->aes_gmac.pn[3] << 16) |
+		       ((u64)seq->aes_gmac.pn[2] << 24) |
+		       ((u64)seq->aes_gmac.pn[1] << 32) |
+		       ((u64)seq->aes_gmac.pn[0] << 40);
+		atomic64_set(&key->u.aes_gmac.tx_pn, pn64);
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		pn64 = (u64)seq->gcmp.pn[5] |
+		       ((u64)seq->gcmp.pn[4] << 8) |
+		       ((u64)seq->gcmp.pn[3] << 16) |
+		       ((u64)seq->gcmp.pn[2] << 24) |
+		       ((u64)seq->gcmp.pn[1] << 32) |
+		       ((u64)seq->gcmp.pn[0] << 40);
+		atomic64_set(&key->u.gcmp.tx_pn, pn64);
+		break;
 	default:
 		WARN_ON(1);
 		break;
@@ -848,6 +1001,7 @@
 		key->u.tkip.rx[tid].iv16 = seq->tkip.iv16;
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
 		if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
 			return;
 		if (tid < 0)
@@ -857,11 +1011,29 @@
 		memcpy(pn, seq->ccmp.pn, IEEE80211_CCMP_PN_LEN);
 		break;
 	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 		if (WARN_ON(tid != 0))
 			return;
 		pn = key->u.aes_cmac.rx_pn;
 		memcpy(pn, seq->aes_cmac.pn, IEEE80211_CMAC_PN_LEN);
 		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		if (WARN_ON(tid != 0))
+			return;
+		pn = key->u.aes_gmac.rx_pn;
+		memcpy(pn, seq->aes_gmac.pn, IEEE80211_GMAC_PN_LEN);
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
+			return;
+		if (tid < 0)
+			pn = key->u.gcmp.rx_pn[IEEE80211_NUM_TIDS];
+		else
+			pn = key->u.gcmp.rx_pn[tid];
+		memcpy(pn, seq->gcmp.pn, IEEE80211_GCMP_PN_LEN);
+		break;
 	default:
 		WARN_ON(1);
 		break;
@@ -884,7 +1056,8 @@
 	if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
 		key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
 
-		if (!(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC))
+		if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+		      (key->conf.flags & IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
 			increment_tailroom_need_count(key->sdata);
 	}
 
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 721f8b3..a9810ea 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -30,10 +30,12 @@
  * @KEY_FLAG_UPLOADED_TO_HARDWARE: Indicates that this key is present
  *	in the hardware for TX crypto hardware acceleration.
  * @KEY_FLAG_TAINTED: Key is tainted and packets should be dropped.
+ * @KEY_FLAG_CIPHER_SCHEME: This key is for a hardware cipher scheme
  */
 enum ieee80211_internal_key_flags {
 	KEY_FLAG_UPLOADED_TO_HARDWARE	= BIT(0),
 	KEY_FLAG_TAINTED		= BIT(1),
+	KEY_FLAG_CIPHER_SCHEME		= BIT(2),
 };
 
 enum ieee80211_internal_tkip_state {
@@ -95,6 +97,24 @@
 			u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
 		} aes_cmac;
 		struct {
+			atomic64_t tx_pn;
+			u8 rx_pn[IEEE80211_GMAC_PN_LEN];
+			struct crypto_aead *tfm;
+			u32 replays; /* dot11RSNAStatsCMACReplays */
+			u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
+		} aes_gmac;
+		struct {
+			atomic64_t tx_pn;
+			/* Last received packet number. The first
+			 * IEEE80211_NUM_TIDS counters are used with Data
+			 * frames and the last counter is used with Robust
+			 * Management frames.
+			 */
+			u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_GCMP_PN_LEN];
+			struct crypto_aead *tfm;
+			u32 replays; /* dot11RSNAStatsGCMPReplays */
+		} gcmp;
+		struct {
 			/* generic cipher scheme */
 			u8 rx_pn[IEEE80211_NUM_TIDS + 1][MAX_PN_LEN];
 		} gen;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 8c5a3cf..dde23c2 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -557,6 +557,9 @@
 
 	local = wiphy_priv(wiphy);
 
+	if (sta_info_init(local))
+		goto err_free;
+
 	local->hw.wiphy = wiphy;
 
 	local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
@@ -629,8 +632,6 @@
 	spin_lock_init(&local->ack_status_lock);
 	idr_init(&local->ack_status_frames);
 
-	sta_info_init(local);
-
 	for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
 		skb_queue_head_init(&local->pending[i]);
 		atomic_set(&local->agg_queue_stop[i], 0);
@@ -650,6 +651,9 @@
 	ieee80211_roc_setup(local);
 
 	return &local->hw;
+ err_free:
+	wiphy_free(wiphy);
+	return NULL;
 }
 EXPORT_SYMBOL(ieee80211_alloc_hw_nm);
 
@@ -658,7 +662,6 @@
 	bool have_wep = !(IS_ERR(local->wep_tx_tfm) ||
 			  IS_ERR(local->wep_rx_tfm));
 	bool have_mfp = local->hw.flags & IEEE80211_HW_MFP_CAPABLE;
-	const struct ieee80211_cipher_scheme *cs = local->hw.cipher_schemes;
 	int n_suites = 0, r = 0, w = 0;
 	u32 *suites;
 	static const u32 cipher_suites[] = {
@@ -667,64 +670,29 @@
 		WLAN_CIPHER_SUITE_WEP104,
 		WLAN_CIPHER_SUITE_TKIP,
 		WLAN_CIPHER_SUITE_CCMP,
+		WLAN_CIPHER_SUITE_CCMP_256,
+		WLAN_CIPHER_SUITE_GCMP,
+		WLAN_CIPHER_SUITE_GCMP_256,
 
 		/* keep last -- depends on hw flags! */
-		WLAN_CIPHER_SUITE_AES_CMAC
+		WLAN_CIPHER_SUITE_AES_CMAC,
+		WLAN_CIPHER_SUITE_BIP_CMAC_256,
+		WLAN_CIPHER_SUITE_BIP_GMAC_128,
+		WLAN_CIPHER_SUITE_BIP_GMAC_256,
 	};
 
-	/* Driver specifies the ciphers, we have nothing to do... */
-	if (local->hw.wiphy->cipher_suites && have_wep)
-		return 0;
-
-	/* Set up cipher suites if driver relies on mac80211 cipher defs */
-	if (!local->hw.wiphy->cipher_suites && !cs) {
-		local->hw.wiphy->cipher_suites = cipher_suites;
-		local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
-
-		if (!have_mfp)
-			local->hw.wiphy->n_cipher_suites--;
-
-		if (!have_wep) {
-			local->hw.wiphy->cipher_suites += 2;
-			local->hw.wiphy->n_cipher_suites -= 2;
-		}
-
-		return 0;
-	}
-
-	if (!local->hw.wiphy->cipher_suites) {
-		/*
-		 * Driver specifies cipher schemes only
-		 * We start counting ciphers defined by schemes, TKIP and CCMP
+	if (local->hw.flags & IEEE80211_HW_SW_CRYPTO_CONTROL ||
+	    local->hw.wiphy->cipher_suites) {
+		/* If the driver advertises, or doesn't support SW crypto,
+		 * we only need to remove WEP if necessary.
 		 */
-		n_suites = local->hw.n_cipher_schemes + 2;
-
-		/* check if we have WEP40 and WEP104 */
 		if (have_wep)
-			n_suites += 2;
+			return 0;
 
-		/* check if we have AES_CMAC */
-		if (have_mfp)
-			n_suites++;
+		/* well if it has _no_ ciphers ... fine */
+		if (!local->hw.wiphy->n_cipher_suites)
+			return 0;
 
-		suites = kmalloc(sizeof(u32) * n_suites, GFP_KERNEL);
-		if (!suites)
-			return -ENOMEM;
-
-		suites[w++] = WLAN_CIPHER_SUITE_CCMP;
-		suites[w++] = WLAN_CIPHER_SUITE_TKIP;
-
-		if (have_wep) {
-			suites[w++] = WLAN_CIPHER_SUITE_WEP40;
-			suites[w++] = WLAN_CIPHER_SUITE_WEP104;
-		}
-
-		if (have_mfp)
-			suites[w++] = WLAN_CIPHER_SUITE_AES_CMAC;
-
-		for (r = 0; r < local->hw.n_cipher_schemes; r++)
-			suites[w++] = cs[r].cipher;
-	} else {
 		/* Driver provides cipher suites, but we need to exclude WEP */
 		suites = kmemdup(local->hw.wiphy->cipher_suites,
 				 sizeof(u32) * local->hw.wiphy->n_cipher_suites,
@@ -740,6 +708,71 @@
 				continue;
 			suites[w++] = suite;
 		}
+	} else if (!local->hw.cipher_schemes) {
+		/* If the driver doesn't have cipher schemes, there's nothing
+		 * else to do other than assign the (software supported and
+		 * perhaps offloaded) cipher suites.
+		 */
+		local->hw.wiphy->cipher_suites = cipher_suites;
+		local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+		if (!have_mfp)
+			local->hw.wiphy->n_cipher_suites -= 4;
+
+		if (!have_wep) {
+			local->hw.wiphy->cipher_suites += 2;
+			local->hw.wiphy->n_cipher_suites -= 2;
+		}
+
+		/* not dynamically allocated, so just return */
+		return 0;
+	} else {
+		const struct ieee80211_cipher_scheme *cs;
+
+		cs = local->hw.cipher_schemes;
+
+		/* Driver specifies cipher schemes only (but not cipher suites
+		 * including the schemes)
+		 *
+		 * We start counting ciphers defined by schemes, TKIP, CCMP,
+		 * CCMP-256, GCMP, and GCMP-256
+		 */
+		n_suites = local->hw.n_cipher_schemes + 5;
+
+		/* check if we have WEP40 and WEP104 */
+		if (have_wep)
+			n_suites += 2;
+
+		/* check if we have AES_CMAC, BIP-CMAC-256, BIP-GMAC-128,
+		 * BIP-GMAC-256
+		 */
+		if (have_mfp)
+			n_suites += 4;
+
+		suites = kmalloc(sizeof(u32) * n_suites, GFP_KERNEL);
+		if (!suites)
+			return -ENOMEM;
+
+		suites[w++] = WLAN_CIPHER_SUITE_CCMP;
+		suites[w++] = WLAN_CIPHER_SUITE_CCMP_256;
+		suites[w++] = WLAN_CIPHER_SUITE_TKIP;
+		suites[w++] = WLAN_CIPHER_SUITE_GCMP;
+		suites[w++] = WLAN_CIPHER_SUITE_GCMP_256;
+
+		if (have_wep) {
+			suites[w++] = WLAN_CIPHER_SUITE_WEP40;
+			suites[w++] = WLAN_CIPHER_SUITE_WEP104;
+		}
+
+		if (have_mfp) {
+			suites[w++] = WLAN_CIPHER_SUITE_AES_CMAC;
+			suites[w++] = WLAN_CIPHER_SUITE_BIP_CMAC_256;
+			suites[w++] = WLAN_CIPHER_SUITE_BIP_GMAC_128;
+			suites[w++] = WLAN_CIPHER_SUITE_BIP_GMAC_256;
+		}
+
+		for (r = 0; r < local->hw.n_cipher_schemes; r++)
+			suites[w++] = cs[r].cipher;
 	}
 
 	local->hw.wiphy->cipher_suites = suites;
@@ -916,10 +949,6 @@
 		}
 	}
 
-	WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
-	     && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
-	     "U-APSD not supported with HW_PS_NULLFUNC_STACK\n");
-
 	/*
 	 * Calculate scan IE length -- we need this to alloc
 	 * memory and to subtract from the driver limit. It
@@ -1010,6 +1039,9 @@
 
 	local->dynamic_ps_forced_timeout = -1;
 
+	if (!local->hw.txq_ac_max_pending)
+		local->hw.txq_ac_max_pending = 64;
+
 	result = ieee80211_wep_init(local);
 	if (result < 0)
 		wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",
@@ -1032,7 +1064,7 @@
 	/* add one default STA interface if supported */
 	if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
 	    !(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) {
-		result = ieee80211_if_add(local, "wlan%d", NULL,
+		result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
 					  NL80211_IFTYPE_STATION, NULL);
 		if (result)
 			wiphy_warn(local->hw.wiphy,
@@ -1045,10 +1077,8 @@
 		ieee80211_max_network_latency;
 	result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY,
 				     &local->network_latency_notifier);
-	if (result) {
-		rtnl_lock();
+	if (result)
 		goto fail_pm_qos;
-	}
 
 #ifdef CONFIG_INET
 	local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
@@ -1076,15 +1106,15 @@
  fail_ifa:
 	pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
 			       &local->network_latency_notifier);
-	rtnl_lock();
 #endif
  fail_pm_qos:
-	ieee80211_led_exit(local);
+	rtnl_lock();
+	rate_control_deinitialize(local);
 	ieee80211_remove_interfaces(local);
  fail_rate:
 	rtnl_unlock();
+	ieee80211_led_exit(local);
 	ieee80211_wep_free(local);
-	sta_info_stop(local);
 	destroy_workqueue(local->workqueue);
  fail_workqueue:
 	wiphy_unregister(local->hw.wiphy);
@@ -1150,7 +1180,6 @@
 
 	destroy_workqueue(local->workqueue);
 	wiphy_unregister(local->hw.wiphy);
-	sta_info_stop(local);
 	ieee80211_wep_free(local);
 	ieee80211_led_exit(local);
 	kfree(local->int_scan_req);
@@ -1178,7 +1207,7 @@
 		     ieee80211_free_ack_frame, NULL);
 	idr_destroy(&local->ack_status_frames);
 
-	kfree(rcu_access_pointer(local->tx_latency));
+	sta_info_stop(local);
 
 	wiphy_free(local->hw.wiphy);
 }
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 0c8b2a7..d468424 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -520,7 +520,7 @@
 	} else {
 		*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
 		/* RA TA DA SA */
-		memset(hdr->addr1, 0, ETH_ALEN);   /* RA is resolved later */
+		eth_zero_addr(hdr->addr1);   /* RA is resolved later */
 		memcpy(hdr->addr2, meshsa, ETH_ALEN);
 		memcpy(hdr->addr3, meshda, ETH_ALEN);
 		memcpy(hdr->addr4, meshsa, ETH_ALEN);
@@ -574,7 +574,8 @@
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 	u32 changed;
 
-	ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);
+	if (ifmsh->mshcfg.plink_timeout > 0)
+		ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);
 	mesh_path_expire(sdata);
 
 	changed = mesh_accept_plinks_update(sdata);
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index b488e18..60d737f 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -17,7 +17,7 @@
 #define PLINK_GET_PLID(p) (p + 4)
 
 #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
-				jiffies + HZ * t / 1000))
+				jiffies + msecs_to_jiffies(t)))
 
 enum plink_event {
 	PLINK_UNDEFINED,
@@ -382,6 +382,7 @@
 	enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
 	struct ieee80211_supported_band *sband;
 	u32 rates, basic_rates = 0, changed = 0;
+	enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth;
 
 	sband = local->hw.wiphy->bands[band];
 	rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
@@ -401,6 +402,9 @@
 					      elems->ht_cap_elem, sta))
 		changed |= IEEE80211_RC_BW_CHANGED;
 
+	if (bw != sta->sta.bandwidth)
+		changed |= IEEE80211_RC_BW_CHANGED;
+
 	/* HT peer is operating 20MHz-only */
 	if (elems->ht_operation &&
 	    !(elems->ht_operation->ht_param &
@@ -621,9 +625,9 @@
 				    sta->llid, sta->plid, reason);
 }
 
-static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
+static inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout)
 {
-	sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
+	sta->plink_timer.expires = jiffies + msecs_to_jiffies(timeout);
 	sta->plink_timer.data = (unsigned long) sta;
 	sta->plink_timer.function = mesh_plink_timer;
 	sta->plink_timeout = timeout;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 2c36c47..26053bf 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -157,14 +157,18 @@
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct cfg80211_chan_def vht_chandef;
+	struct ieee80211_sta_ht_cap sta_ht_cap;
 	u32 ht_cfreq, ret;
 
+	memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
+	ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
+
 	chandef->chan = channel;
 	chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
 	chandef->center_freq1 = channel->center_freq;
 	chandef->center_freq2 = 0;
 
-	if (!ht_cap || !ht_oper || !sband->ht_cap.ht_supported) {
+	if (!ht_cap || !ht_oper || !sta_ht_cap.ht_supported) {
 		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
 		goto out;
 	}
@@ -198,7 +202,7 @@
 	}
 
 	/* check 40 MHz support, if we have it */
-	if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+	if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
 		switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
 		case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
 			chandef->width = NL80211_CHAN_WIDTH_40;
@@ -1054,8 +1058,6 @@
 		sdata->csa_block_tx = false;
 	}
 
-	cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef);
-
 	sdata->vif.csa_active = false;
 	ifmgd->csa_waiting_bcn = false;
 
@@ -1067,6 +1069,8 @@
 				     &ifmgd->csa_connection_drop_work);
 		return;
 	}
+
+	cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef);
 }
 
 void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
@@ -1146,6 +1150,17 @@
 		return;
 	}
 
+	if (cfg80211_chandef_identical(&csa_ie.chandef,
+				       &sdata->vif.bss_conf.chandef)) {
+		if (ifmgd->csa_ignored_same_chan)
+			return;
+		sdata_info(sdata,
+			   "AP %pM tries to chanswitch to same channel, ignore\n",
+			   ifmgd->associated->bssid);
+		ifmgd->csa_ignored_same_chan = true;
+		return;
+	}
+
 	mutex_lock(&local->mtx);
 	mutex_lock(&local->chanctx_mtx);
 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
@@ -1153,11 +1168,7 @@
 	if (!conf) {
 		sdata_info(sdata,
 			   "no channel context assigned to vif?, disconnecting\n");
-		ieee80211_queue_work(&local->hw,
-				     &ifmgd->csa_connection_drop_work);
-		mutex_unlock(&local->chanctx_mtx);
-		mutex_unlock(&local->mtx);
-		return;
+		goto drop_connection;
 	}
 
 	chanctx = container_of(conf, struct ieee80211_chanctx, conf);
@@ -1166,11 +1177,7 @@
 	    !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
 		sdata_info(sdata,
 			   "driver doesn't support chan-switch with channel contexts\n");
-		ieee80211_queue_work(&local->hw,
-				     &ifmgd->csa_connection_drop_work);
-		mutex_unlock(&local->chanctx_mtx);
-		mutex_unlock(&local->mtx);
-		return;
+		goto drop_connection;
 	}
 
 	ch_switch.timestamp = timestamp;
@@ -1182,11 +1189,7 @@
 	if (drv_pre_channel_switch(sdata, &ch_switch)) {
 		sdata_info(sdata,
 			   "preparing for channel switch failed, disconnecting\n");
-		ieee80211_queue_work(&local->hw,
-				     &ifmgd->csa_connection_drop_work);
-		mutex_unlock(&local->chanctx_mtx);
-		mutex_unlock(&local->mtx);
-		return;
+		goto drop_connection;
 	}
 
 	res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
@@ -1195,17 +1198,14 @@
 		sdata_info(sdata,
 			   "failed to reserve channel context for channel switch, disconnecting (err=%d)\n",
 			   res);
-		ieee80211_queue_work(&local->hw,
-				     &ifmgd->csa_connection_drop_work);
-		mutex_unlock(&local->chanctx_mtx);
-		mutex_unlock(&local->mtx);
-		return;
+		goto drop_connection;
 	}
 	mutex_unlock(&local->chanctx_mtx);
 
 	sdata->vif.csa_active = true;
 	sdata->csa_chandef = csa_ie.chandef;
 	sdata->csa_block_tx = csa_ie.mode;
+	ifmgd->csa_ignored_same_chan = false;
 
 	if (sdata->csa_block_tx)
 		ieee80211_stop_vif_queues(local, sdata,
@@ -1228,6 +1228,11 @@
 		mod_timer(&ifmgd->chswitch_timer,
 			  TU_TO_EXP_TIME((csa_ie.count - 1) *
 					 cbss->beacon_interval));
+	return;
+ drop_connection:
+	ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work);
+	mutex_unlock(&local->chanctx_mtx);
+	mutex_unlock(&local->mtx);
 }
 
 static bool
@@ -1284,8 +1289,11 @@
 		country_ie_len -= 3;
 	}
 
-	if (have_chan_pwr)
+	if (have_chan_pwr && pwr_constr_elem)
 		*pwr_reduction = *pwr_constr_elem;
+	else
+		*pwr_reduction = 0;
+
 	return have_chan_pwr;
 }
 
@@ -1314,10 +1322,11 @@
 	int chan_pwr = 0, pwr_reduction_80211h = 0;
 	int pwr_level_cisco, pwr_level_80211h;
 	int new_ap_level;
+	__le16 capab = mgmt->u.probe_resp.capab_info;
 
-	if (country_ie && pwr_constr_ie &&
-	    mgmt->u.probe_resp.capab_info &
-		cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) {
+	if (country_ie &&
+	    (capab & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT) ||
+	     capab & cpu_to_le16(WLAN_CAPABILITY_RADIO_MEASURE))) {
 		has_80211h_pwr = ieee80211_find_80211h_pwr_constr(
 			sdata, channel, country_ie, country_ie_len,
 			pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h);
@@ -1339,15 +1348,15 @@
 	 */
 	if (has_80211h_pwr &&
 	    (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) {
-		sdata_info(sdata,
-			   "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
-			   pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
-			   sdata->u.mgd.bssid);
+		sdata_dbg(sdata,
+			  "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
+			  pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
+			  sdata->u.mgd.bssid);
 		new_ap_level = pwr_level_80211h;
 	} else {  /* has_cisco_pwr is always true here. */
-		sdata_info(sdata,
-			   "Limiting TX power to %d dBm as advertised by %pM\n",
-			   pwr_level_cisco, sdata->u.mgd.bssid);
+		sdata_dbg(sdata,
+			  "Limiting TX power to %d dBm as advertised by %pM\n",
+			  pwr_level_cisco, sdata->u.mgd.bssid);
 		new_ap_level = pwr_level_cisco;
 	}
 
@@ -1596,7 +1605,7 @@
 		} else {
 			ieee80211_send_nullfunc(local, sdata, 1);
 			/* Flush to get the tx status of nullfunc frame */
-			ieee80211_flush_queues(local, sdata);
+			ieee80211_flush_queues(local, sdata, false);
 		}
 	}
 
@@ -1613,9 +1622,6 @@
 {
 	struct ieee80211_local *local = (void *) data;
 
-	if (local->quiescing || local->suspended)
-		return;
-
 	ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
 }
 
@@ -1643,7 +1649,7 @@
 {
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	bool ret;
+	bool ret = false;
 	int ac;
 
 	if (local->hw.queues < IEEE80211_NUM_ACS)
@@ -2003,21 +2009,29 @@
 	/* disable per-vif ps */
 	ieee80211_recalc_ps_vif(sdata);
 
-	/* flush out any pending frame (e.g. DELBA) before deauth/disassoc */
+	/* make sure ongoing transmission finishes */
+	synchronize_net();
+
+	/*
+	 * drop any frame before deauth/disassoc, this can be data or
+	 * management frame. Since we are disconnecting, we should not
+	 * insist sending these frames which can take time and delay
+	 * the disconnection and possible the roaming.
+	 */
 	if (tx)
-		ieee80211_flush_queues(local, sdata);
+		ieee80211_flush_queues(local, sdata, true);
 
 	/* deauthenticate/disassociate now */
 	if (tx || frame_buf)
 		ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid, stype,
 					       reason, tx, frame_buf);
 
-	/* flush out frame */
+	/* flush out frame - make sure the deauth was actually sent */
 	if (tx)
-		ieee80211_flush_queues(local, sdata);
+		ieee80211_flush_queues(local, sdata, false);
 
 	/* clear bssid only after building the needed mgmt frames */
-	memset(ifmgd->bssid, 0, ETH_ALEN);
+	eth_zero_addr(ifmgd->bssid);
 
 	/* remove AP and TDLS peers */
 	sta_info_flush(sdata);
@@ -2074,6 +2088,7 @@
 
 	sdata->vif.csa_active = false;
 	ifmgd->csa_waiting_bcn = false;
+	ifmgd->csa_ignored_same_chan = false;
 	if (sdata->csa_block_tx) {
 		ieee80211_wake_vif_queues(local, sdata,
 					  IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -2231,7 +2246,7 @@
 		else
 			ssid_len = ssid[1];
 
-		ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
+		ieee80211_send_probe_req(sdata, sdata->vif.addr, dst,
 					 ssid + 2, ssid_len, NULL,
 					 0, (u32) -1, true, 0,
 					 ifmgd->associated->channel, false);
@@ -2343,6 +2358,24 @@
 }
 EXPORT_SYMBOL(ieee80211_ap_probereq_get);
 
+static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata,
+					const u8 *buf, size_t len, bool tx,
+					u16 reason)
+{
+	struct ieee80211_event event = {
+		.type = MLME_EVENT,
+		.u.mlme.data = tx ? DEAUTH_TX_EVENT : DEAUTH_RX_EVENT,
+		.u.mlme.reason = reason,
+	};
+
+	if (tx)
+		cfg80211_tx_mlme_mgmt(sdata->dev, buf, len);
+	else
+		cfg80211_rx_mlme_mgmt(sdata->dev, buf, len);
+
+	drv_event_callback(sdata->local, sdata, &event);
+}
+
 static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_local *local = sdata->local;
@@ -2368,8 +2401,9 @@
 	}
 	mutex_unlock(&local->mtx);
 
-	cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-			      IEEE80211_DEAUTH_FRAME_LEN);
+	ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+				    WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+
 	sdata_unlock(sdata);
 }
 
@@ -2440,9 +2474,15 @@
 	sdata_assert_lock(sdata);
 
 	if (!assoc) {
+		/*
+		 * we are not authenticated yet, the only timer that could be
+		 * running is the timeout for the authentication response which
+		 * which is not relevant anymore.
+		 */
+		del_timer_sync(&sdata->u.mgd.timer);
 		sta_info_destroy_addr(sdata, auth_data->bss->bssid);
 
-		memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
+		eth_zero_addr(sdata->u.mgd.bssid);
 		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
 		sdata->u.mgd.flags = 0;
 		mutex_lock(&sdata->local->mtx);
@@ -2487,6 +2527,10 @@
 	u8 bssid[ETH_ALEN];
 	u16 auth_alg, auth_transaction, status_code;
 	struct sta_info *sta;
+	struct ieee80211_event event = {
+		.type = MLME_EVENT,
+		.u.mlme.data = AUTH_EVENT,
+	};
 
 	sdata_assert_lock(sdata);
 
@@ -2519,6 +2563,9 @@
 			   mgmt->sa, status_code);
 		ieee80211_destroy_auth_data(sdata, false);
 		cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+		event.u.mlme.status = MLME_DENIED;
+		event.u.mlme.reason = status_code;
+		drv_event_callback(sdata->local, sdata, &event);
 		return;
 	}
 
@@ -2541,6 +2588,8 @@
 		return;
 	}
 
+	event.u.mlme.status = MLME_SUCCESS;
+	drv_event_callback(sdata->local, sdata, &event);
 	sdata_info(sdata, "authenticated\n");
 	ifmgd->auth_data->done = true;
 	ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
@@ -2659,7 +2708,7 @@
 
 	ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-	cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+	ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
 }
 
 
@@ -2685,7 +2734,7 @@
 
 	ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-	cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+	ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
 }
 
 static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
@@ -2747,9 +2796,15 @@
 	sdata_assert_lock(sdata);
 
 	if (!assoc) {
+		/*
+		 * we are not associated yet, the only timer that could be
+		 * running is the timeout for the association response which
+		 * which is not relevant anymore.
+		 */
+		del_timer_sync(&sdata->u.mgd.timer);
 		sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
 
-		memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
+		eth_zero_addr(sdata->u.mgd.bssid);
 		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
 		sdata->u.mgd.flags = 0;
 		mutex_lock(&sdata->local->mtx);
@@ -2941,10 +2996,14 @@
 
 	rate_control_rate_init(sta);
 
-	if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
+	if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
 		set_sta_flag(sta, WLAN_STA_MFP);
+		sta->sta.mfp = true;
+	} else {
+		sta->sta.mfp = false;
+	}
 
-	sta->sta.wme = elems.wmm_param;
+	sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;
 
 	err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
 	if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
@@ -3014,6 +3073,10 @@
 	u8 *pos;
 	bool reassoc;
 	struct cfg80211_bss *bss;
+	struct ieee80211_event event = {
+		.type = MLME_EVENT,
+		.u.mlme.data = ASSOC_EVENT,
+	};
 
 	sdata_assert_lock(sdata);
 
@@ -3065,6 +3128,9 @@
 		sdata_info(sdata, "%pM denied association (code=%d)\n",
 			   mgmt->sa, status_code);
 		ieee80211_destroy_assoc_data(sdata, false);
+		event.u.mlme.status = MLME_DENIED;
+		event.u.mlme.reason = status_code;
+		drv_event_callback(sdata->local, sdata, &event);
 	} else {
 		if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
 			/* oops -- internal error -- send timeout for now */
@@ -3072,6 +3138,8 @@
 			cfg80211_assoc_timeout(sdata->dev, bss);
 			return;
 		}
+		event.u.mlme.status = MLME_SUCCESS;
+		drv_event_callback(sdata->local, sdata, &event);
 		sdata_info(sdata, "associated\n");
 
 		/*
@@ -3176,7 +3244,8 @@
 	(1ULL << WLAN_EID_CHANNEL_SWITCH) |
 	(1ULL << WLAN_EID_PWR_CONSTRAINT) |
 	(1ULL << WLAN_EID_HT_CAPABILITY) |
-	(1ULL << WLAN_EID_HT_OPERATION);
+	(1ULL << WLAN_EID_HT_OPERATION) |
+	(1ULL << WLAN_EID_EXT_CHANSWITCH_ANN);
 
 static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 				     struct ieee80211_mgmt *mgmt, size_t len,
@@ -3273,6 +3342,9 @@
 	    ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
 		int sig = ifmgd->ave_beacon_signal;
 		int last_sig = ifmgd->last_ave_beacon_signal;
+		struct ieee80211_event event = {
+			.type = RSSI_EVENT,
+		};
 
 		/*
 		 * if signal crosses either of the boundaries, invoke callback
@@ -3281,12 +3353,14 @@
 		if (sig > ifmgd->rssi_max_thold &&
 		    (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
 			ifmgd->last_ave_beacon_signal = sig;
-			drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH);
+			event.u.rssi.data = RSSI_EVENT_HIGH;
+			drv_event_callback(local, sdata, &event);
 		} else if (sig < ifmgd->rssi_min_thold &&
 			   (last_sig >= ifmgd->rssi_max_thold ||
 			   last_sig == 0)) {
 			ifmgd->last_ave_beacon_signal = sig;
-			drv_rssi_callback(local, sdata, RSSI_EVENT_LOW);
+			event.u.rssi.data = RSSI_EVENT_LOW;
+			drv_event_callback(local, sdata, &event);
 		}
 	}
 
@@ -3391,6 +3465,26 @@
 	if (ifmgd->csa_waiting_bcn)
 		ieee80211_chswitch_post_beacon(sdata);
 
+	/*
+	 * Update beacon timing and dtim count on every beacon appearance. This
+	 * will allow the driver to use the most updated values. Do it before
+	 * comparing this one with last received beacon.
+	 * IMPORTANT: These parameters would possibly be out of sync by the time
+	 * the driver will use them. The synchronized view is currently
+	 * guaranteed only in certain callbacks.
+	 */
+	if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
+		sdata->vif.bss_conf.sync_tsf =
+			le64_to_cpu(mgmt->u.beacon.timestamp);
+		sdata->vif.bss_conf.sync_device_ts =
+			rx_status->device_timestamp;
+		if (elems.tim)
+			sdata->vif.bss_conf.sync_dtim_count =
+				elems.tim->dtim_count;
+		else
+			sdata->vif.bss_conf.sync_dtim_count = 0;
+	}
+
 	if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
 		return;
 	ifmgd->beacon_crc = ncrc;
@@ -3418,18 +3512,6 @@
 		else
 			bss_conf->dtim_period = 1;
 
-		if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
-			sdata->vif.bss_conf.sync_tsf =
-				le64_to_cpu(mgmt->u.beacon.timestamp);
-			sdata->vif.bss_conf.sync_device_ts =
-				rx_status->device_timestamp;
-			if (elems.tim)
-				sdata->vif.bss_conf.sync_dtim_count =
-					elems.tim->dtim_count;
-			else
-				sdata->vif.bss_conf.sync_dtim_count = 0;
-		}
-
 		changed |= BSS_CHANGED_BEACON_INFO;
 		ifmgd->have_beacon = true;
 
@@ -3460,8 +3542,9 @@
 		ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
 				       WLAN_REASON_DEAUTH_LEAVING,
 				       true, deauth_buf);
-		cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf,
-				      sizeof(deauth_buf));
+		ieee80211_report_disconnect(sdata, deauth_buf,
+					    sizeof(deauth_buf), true,
+					    WLAN_REASON_DEAUTH_LEAVING);
 		return;
 	}
 
@@ -3579,8 +3662,8 @@
 	ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
 			       tx, frame_buf);
 
-	cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-			      IEEE80211_DEAUTH_FRAME_LEN);
+	ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+				    reason);
 }
 
 static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
@@ -3774,12 +3857,18 @@
 			ieee80211_destroy_auth_data(sdata, false);
 		} else if (ieee80211_probe_auth(sdata)) {
 			u8 bssid[ETH_ALEN];
+			struct ieee80211_event event = {
+				.type = MLME_EVENT,
+				.u.mlme.data = AUTH_EVENT,
+				.u.mlme.status = MLME_TIMEOUT,
+			};
 
 			memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
 
 			ieee80211_destroy_auth_data(sdata, false);
 
 			cfg80211_auth_timeout(sdata->dev, bssid);
+			drv_event_callback(sdata->local, sdata, &event);
 		}
 	} else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
 		run_again(sdata, ifmgd->auth_data->timeout);
@@ -3789,9 +3878,15 @@
 		if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) ||
 		    ieee80211_do_assoc(sdata)) {
 			struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
+			struct ieee80211_event event = {
+				.type = MLME_EVENT,
+				.u.mlme.data = ASSOC_EVENT,
+				.u.mlme.status = MLME_TIMEOUT,
+			};
 
 			ieee80211_destroy_assoc_data(sdata, false);
 			cfg80211_assoc_timeout(sdata->dev, bss);
+			drv_event_callback(sdata->local, sdata, &event);
 		}
 	} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
 		run_again(sdata, ifmgd->assoc_data->timeout);
@@ -3863,12 +3958,8 @@
 {
 	struct ieee80211_sub_if_data *sdata =
 		(struct ieee80211_sub_if_data *) data;
-	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-	if (local->quiescing)
-		return;
-
 	if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
 		return;
 
@@ -3884,9 +3975,6 @@
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_local *local = sdata->local;
 
-	if (local->quiescing)
-		return;
-
 	if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
 		return;
 
@@ -3949,6 +4037,34 @@
 				      IEEE80211_DEAUTH_FRAME_LEN);
 	}
 
+	/* This is a bit of a hack - we should find a better and more generic
+	 * solution to this. Normally when suspending, cfg80211 will in fact
+	 * deauthenticate. However, it doesn't (and cannot) stop an ongoing
+	 * auth (not so important) or assoc (this is the problem) process.
+	 *
+	 * As a consequence, it can happen that we are in the process of both
+	 * associating and suspending, and receive an association response
+	 * after cfg80211 has checked if it needs to disconnect, but before
+	 * we actually set the flag to drop incoming frames. This will then
+	 * cause the workqueue flush to process the association response in
+	 * the suspend, resulting in a successful association just before it
+	 * tries to remove the interface from the driver, which now though
+	 * has a channel context assigned ... this results in issues.
+	 *
+	 * To work around this (for now) simply deauth here again if we're
+	 * now connected.
+	 */
+	if (ifmgd->associated && !sdata->local->wowlan) {
+		u8 bssid[ETH_ALEN];
+		struct cfg80211_deauth_request req = {
+			.reason_code = WLAN_REASON_DEAUTH_LEAVING,
+			.bssid = bssid,
+		};
+
+		memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
+		ieee80211_mgd_deauth(sdata, &req);
+	}
+
 	sdata_unlock(sdata);
 }
 
@@ -4197,9 +4313,13 @@
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_bss *bss = (void *)cbss->priv;
 	struct sta_info *new_sta = NULL;
-	bool have_sta = false;
+	struct ieee80211_supported_band *sband;
+	struct ieee80211_sta_ht_cap sta_ht_cap;
+	bool have_sta = false, is_override = false;
 	int err;
 
+	sband = local->hw.wiphy->bands[cbss->channel->band];
+
 	if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
 		return -EINVAL;
 
@@ -4214,25 +4334,32 @@
 		if (!new_sta)
 			return -ENOMEM;
 	}
+
+	memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
+	ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
+
+	is_override = (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) !=
+		      (sband->ht_cap.cap &
+		       IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+
+	if (new_sta || is_override) {
+		err = ieee80211_prep_channel(sdata, cbss);
+		if (err) {
+			if (new_sta)
+				sta_info_free(local, new_sta);
+			return -EINVAL;
+		}
+	}
+
 	if (new_sta) {
 		u32 rates = 0, basic_rates = 0;
 		bool have_higher_than_11mbit;
 		int min_rate = INT_MAX, min_rate_index = -1;
 		struct ieee80211_chanctx_conf *chanctx_conf;
-		struct ieee80211_supported_band *sband;
 		const struct cfg80211_bss_ies *ies;
-		int shift;
+		int shift = ieee80211_vif_get_shift(&sdata->vif);
 		u32 rate_flags;
 
-		sband = local->hw.wiphy->bands[cbss->channel->band];
-
-		err = ieee80211_prep_channel(sdata, cbss);
-		if (err) {
-			sta_info_free(local, new_sta);
-			return -EINVAL;
-		}
-		shift = ieee80211_vif_get_shift(&sdata->vif);
-
 		rcu_read_lock();
 		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 		if (WARN_ON(!chanctx_conf)) {
@@ -4326,6 +4453,10 @@
 	} else
 		WARN_ON_ONCE(!ether_addr_equal(ifmgd->bssid, cbss->bssid));
 
+	/* Cancel scan to ensure that nothing interferes with connection */
+	if (local->scanning)
+		ieee80211_scan_cancel(local);
+
 	return 0;
 }
 
@@ -4414,8 +4545,9 @@
 				       WLAN_REASON_UNSPECIFIED,
 				       false, frame_buf);
 
-		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-				      sizeof(frame_buf));
+		ieee80211_report_disconnect(sdata, frame_buf,
+					    sizeof(frame_buf), true,
+					    WLAN_REASON_UNSPECIFIED);
 	}
 
 	sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
@@ -4435,7 +4567,7 @@
 	return 0;
 
  err_clear:
-	memset(ifmgd->bssid, 0, ETH_ALEN);
+	eth_zero_addr(ifmgd->bssid);
 	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
 	ifmgd->auth_data = NULL;
  err_free:
@@ -4515,8 +4647,9 @@
 				       WLAN_REASON_UNSPECIFIED,
 				       false, frame_buf);
 
-		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-				      sizeof(frame_buf));
+		ieee80211_report_disconnect(sdata, frame_buf,
+					    sizeof(frame_buf), true,
+					    WLAN_REASON_UNSPECIFIED);
 	}
 
 	if (ifmgd->auth_data && !ifmgd->auth_data->done) {
@@ -4668,8 +4801,13 @@
 		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
 	rcu_read_unlock();
 
+	if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) &&
+		 (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
+	     "U-APSD not supported with HW_PS_NULLFUNC_STACK\n"))
+		sdata->vif.driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
+
 	if (bss->wmm_used && bss->uapsd_supported &&
-	    (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
+	    (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD)) {
 		assoc_data->uapsd = true;
 		ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
 	} else {
@@ -4773,7 +4911,7 @@
 
 	return 0;
  err_clear:
-	memset(ifmgd->bssid, 0, ETH_ALEN);
+	eth_zero_addr(ifmgd->bssid);
 	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
 	ifmgd->assoc_data = NULL;
  err_free:
@@ -4801,8 +4939,9 @@
 					       req->reason_code, tx,
 					       frame_buf);
 		ieee80211_destroy_auth_data(sdata, false);
-		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-				      IEEE80211_DEAUTH_FRAME_LEN);
+		ieee80211_report_disconnect(sdata, frame_buf,
+					    sizeof(frame_buf), true,
+					    req->reason_code);
 
 		return 0;
 	}
@@ -4816,8 +4955,9 @@
 
 		ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
 				       req->reason_code, tx, frame_buf);
-		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-				      IEEE80211_DEAUTH_FRAME_LEN);
+		ieee80211_report_disconnect(sdata, frame_buf,
+					    sizeof(frame_buf), true,
+					    req->reason_code);
 		return 0;
 	}
 
@@ -4849,8 +4989,8 @@
 			       req->reason_code, !req->local_state_change,
 			       frame_buf);
 
-	cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-			      IEEE80211_DEAUTH_FRAME_LEN);
+	ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+				    req->reason_code);
 
 	return 0;
 }
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index ff20b2e..683f0e3 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -121,7 +121,7 @@
 	ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
 					IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
 					false);
-	ieee80211_flush_queues(local, NULL);
+	ieee80211_flush_queues(local, NULL, false);
 
 	mutex_lock(&local->iflist_mtx);
 	list_for_each_entry(sdata, &local->interfaces, list) {
@@ -398,7 +398,7 @@
 		ieee80211_roc_notify_destroy(roc, !roc->abort);
 
 		if (started && !on_channel) {
-			ieee80211_flush_queues(local, NULL);
+			ieee80211_flush_queues(local, NULL, false);
 
 			local->tmp_channel = NULL;
 			ieee80211_hw_config(local, 0);
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 4c5192e..ac6ad62 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -41,7 +41,7 @@
 	/* flush out all packets */
 	synchronize_net();
 
-	ieee80211_flush_queues(local, NULL);
+	ieee80211_flush_queues(local, NULL, true);
 
 	local->quiescing = true;
 	/* make quiescing visible to timers everywhere */
@@ -59,9 +59,26 @@
 	cancel_work_sync(&local->dynamic_ps_enable_work);
 	del_timer_sync(&local->dynamic_ps_timer);
 
-	local->wowlan = wowlan && local->open_count;
+	local->wowlan = wowlan;
 	if (local->wowlan) {
-		int err = drv_suspend(local, wowlan);
+		int err;
+
+		/* Drivers don't expect to suspend while some operations like
+		 * authenticating or associating are in progress. It doesn't
+		 * make sense anyway to accept that, since the authentication
+		 * or association would never finish since the driver can't do
+		 * that on its own.
+		 * Thus, clean up in-progress auth/assoc first.
+		 */
+		list_for_each_entry(sdata, &local->interfaces, list) {
+			if (!ieee80211_sdata_running(sdata))
+				continue;
+			if (sdata->vif.type != NL80211_IFTYPE_STATION)
+				continue;
+			ieee80211_mgd_quiesce(sdata);
+		}
+
+		err = drv_suspend(local, wowlan);
 		if (err < 0) {
 			local->quiescing = false;
 			local->wowlan = false;
@@ -80,26 +97,19 @@
 			return err;
 		} else if (err > 0) {
 			WARN_ON(err != 1);
+			/* cfg80211 will call back into mac80211 to disconnect
+			 * all interfaces, allow that to proceed properly
+			 */
+			ieee80211_wake_queues_by_reason(hw,
+					IEEE80211_MAX_QUEUE_MAP,
+					IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+					false);
 			return err;
 		} else {
 			goto suspend;
 		}
 	}
 
-	/* tear down aggregation sessions and remove STAs */
-	mutex_lock(&local->sta_mtx);
-	list_for_each_entry(sta, &local->sta_list, list) {
-		if (sta->uploaded) {
-			enum ieee80211_sta_state state;
-
-			state = sta->sta_state;
-			for (; state > IEEE80211_STA_NOTEXIST; state--)
-				WARN_ON(drv_sta_state(local, sta->sdata, sta,
-						      state, state - 1));
-		}
-	}
-	mutex_unlock(&local->sta_mtx);
-
 	/* remove all interfaces that were created in the driver */
 	list_for_each_entry(sdata, &local->interfaces, list) {
 		if (!ieee80211_sdata_running(sdata))
@@ -111,6 +121,21 @@
 		case NL80211_IFTYPE_STATION:
 			ieee80211_mgd_quiesce(sdata);
 			break;
+		case NL80211_IFTYPE_WDS:
+			/* tear down aggregation sessions and remove STAs */
+			mutex_lock(&local->sta_mtx);
+			sta = sdata->u.wds.sta;
+			if (sta && sta->uploaded) {
+				enum ieee80211_sta_state state;
+
+				state = sta->sta_state;
+				for (; state > IEEE80211_STA_NOTEXIST; state--)
+					WARN_ON(drv_sta_state(local, sta->sdata,
+							      sta, state,
+							      state - 1));
+			}
+			mutex_unlock(&local->sta_mtx);
+			break;
 		default:
 			break;
 		}
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 217e379..0de98f9 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -69,14 +69,39 @@
 	return i;
 }
 
+/* return current EMWA throughput */
+int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma)
+{
+	int usecs;
+
+	usecs = mr->perfect_tx_time;
+	if (!usecs)
+		usecs = 1000000;
+
+	/* reset thr. below 10% success */
+	if (mr->stats.prob_ewma < MINSTREL_FRAC(10, 100))
+		return 0;
+
+	if (prob_ewma > MINSTREL_FRAC(90, 100))
+		return MINSTREL_TRUNC(100000 * (MINSTREL_FRAC(90, 100) / usecs));
+	else
+		return MINSTREL_TRUNC(100000 * (prob_ewma / usecs));
+}
+
 /* find & sort topmost throughput rates */
 static inline void
 minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list)
 {
 	int j = MAX_THR_RATES;
+	struct minstrel_rate_stats *tmp_mrs = &mi->r[j - 1].stats;
+	struct minstrel_rate_stats *cur_mrs = &mi->r[i].stats;
 
-	while (j > 0 && mi->r[i].stats.cur_tp > mi->r[tp_list[j - 1]].stats.cur_tp)
+	while (j > 0 && (minstrel_get_tp_avg(&mi->r[i], cur_mrs->prob_ewma) >
+	       minstrel_get_tp_avg(&mi->r[tp_list[j - 1]], tmp_mrs->prob_ewma))) {
 		j--;
+		tmp_mrs = &mi->r[tp_list[j - 1]].stats;
+	}
+
 	if (j < MAX_THR_RATES - 1)
 		memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1));
 	if (j < MAX_THR_RATES)
@@ -127,13 +152,47 @@
 	rate_control_set_rates(mp->hw, mi->sta, ratetbl);
 }
 
+/*
+* Recalculate statistics and counters of a given rate
+*/
+void
+minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs)
+{
+	if (unlikely(mrs->attempts > 0)) {
+		mrs->sample_skipped = 0;
+		mrs->cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
+		if (unlikely(!mrs->att_hist)) {
+			mrs->prob_ewma = mrs->cur_prob;
+		} else {
+			/* update exponential weighted moving variance */
+			mrs->prob_ewmsd = minstrel_ewmsd(mrs->prob_ewmsd,
+							 mrs->cur_prob,
+							 mrs->prob_ewma,
+							 EWMA_LEVEL);
+
+			/*update exponential weighted moving avarage */
+			mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma,
+						       mrs->cur_prob,
+						       EWMA_LEVEL);
+		}
+		mrs->att_hist += mrs->attempts;
+		mrs->succ_hist += mrs->success;
+	} else {
+		mrs->sample_skipped++;
+	}
+
+	mrs->last_success = mrs->success;
+	mrs->last_attempts = mrs->attempts;
+	mrs->success = 0;
+	mrs->attempts = 0;
+}
+
 static void
 minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
 {
 	u8 tmp_tp_rate[MAX_THR_RATES];
 	u8 tmp_prob_rate = 0;
-	u32 usecs;
-	int i;
+	int i, tmp_cur_tp, tmp_prob_tp;
 
 	for (i = 0; i < MAX_THR_RATES; i++)
 	    tmp_tp_rate[i] = 0;
@@ -141,38 +200,15 @@
 	for (i = 0; i < mi->n_rates; i++) {
 		struct minstrel_rate *mr = &mi->r[i];
 		struct minstrel_rate_stats *mrs = &mi->r[i].stats;
+		struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats;
 
-		usecs = mr->perfect_tx_time;
-		if (!usecs)
-			usecs = 1000000;
-
-		if (unlikely(mrs->attempts > 0)) {
-			mrs->sample_skipped = 0;
-			mrs->cur_prob = MINSTREL_FRAC(mrs->success,
-						      mrs->attempts);
-			mrs->succ_hist += mrs->success;
-			mrs->att_hist += mrs->attempts;
-			mrs->probability = minstrel_ewma(mrs->probability,
-							 mrs->cur_prob,
-							 EWMA_LEVEL);
-		} else
-			mrs->sample_skipped++;
-
-		mrs->last_success = mrs->success;
-		mrs->last_attempts = mrs->attempts;
-		mrs->success = 0;
-		mrs->attempts = 0;
-
-		/* Update throughput per rate, reset thr. below 10% success */
-		if (mrs->probability < MINSTREL_FRAC(10, 100))
-			mrs->cur_tp = 0;
-		else
-			mrs->cur_tp = mrs->probability * (1000000 / usecs);
+		/* Update statistics of success probability per rate */
+		minstrel_calc_rate_stats(mrs);
 
 		/* Sample less often below the 10% chance of success.
 		 * Sample less often above the 95% chance of success. */
-		if (mrs->probability > MINSTREL_FRAC(95, 100) ||
-		    mrs->probability < MINSTREL_FRAC(10, 100)) {
+		if (mrs->prob_ewma > MINSTREL_FRAC(95, 100) ||
+		    mrs->prob_ewma < MINSTREL_FRAC(10, 100)) {
 			mr->adjusted_retry_count = mrs->retry_count >> 1;
 			if (mr->adjusted_retry_count > 2)
 				mr->adjusted_retry_count = 2;
@@ -192,11 +228,14 @@
 		 * choose the maximum throughput rate as max_prob_rate
 		 * (2) if all success probabilities < 95%, the rate with
 		 * highest success probability is chosen as max_prob_rate */
-		if (mrs->probability >= MINSTREL_FRAC(95, 100)) {
-			if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp)
+		if (mrs->prob_ewma >= MINSTREL_FRAC(95, 100)) {
+			tmp_cur_tp = minstrel_get_tp_avg(mr, mrs->prob_ewma);
+			tmp_prob_tp = minstrel_get_tp_avg(&mi->r[tmp_prob_rate],
+							  tmp_mrs->prob_ewma);
+			if (tmp_cur_tp >= tmp_prob_tp)
 				tmp_prob_rate = i;
 		} else {
-			if (mrs->probability >= mi->r[tmp_prob_rate].stats.probability)
+			if (mrs->prob_ewma >= tmp_mrs->prob_ewma)
 				tmp_prob_rate = i;
 		}
 	}
@@ -215,7 +254,7 @@
 #endif
 
 	/* Reset update timer */
-	mi->stats_update = jiffies;
+	mi->last_stats_update = jiffies;
 
 	minstrel_update_rates(mp, mi);
 }
@@ -253,7 +292,7 @@
 	if (mi->sample_deferred > 0)
 		mi->sample_deferred--;
 
-	if (time_after(jiffies, mi->stats_update +
+	if (time_after(jiffies, mi->last_stats_update +
 				(mp->update_interval * HZ) / 1000))
 		minstrel_update_stats(mp, mi);
 }
@@ -263,12 +302,12 @@
 minstrel_get_retry_count(struct minstrel_rate *mr,
 			 struct ieee80211_tx_info *info)
 {
-	unsigned int retry = mr->adjusted_retry_count;
+	u8 retry = mr->adjusted_retry_count;
 
 	if (info->control.use_rts)
-		retry = max(2U, min(mr->stats.retry_count_rtscts, retry));
+		retry = max_t(u8, 2, min(mr->stats.retry_count_rtscts, retry));
 	else if (info->control.use_cts_prot)
-		retry = max(2U, min(mr->retry_count_cts, retry));
+		retry = max_t(u8, 2, min(mr->retry_count_cts, retry));
 	return retry;
 }
 
@@ -373,7 +412,7 @@
 		rate++;
 		mi->sample_deferred++;
 	} else {
-		if (!msr->sample_limit != 0)
+		if (!msr->sample_limit)
 			return;
 
 		mi->sample_packets++;
@@ -385,7 +424,7 @@
 	 * has a probability of >95%, we shouldn't be attempting
 	 * to use it, as this only wastes precious airtime */
 	if (!mrr_capable &&
-	   (mi->r[ndx].stats.probability > MINSTREL_FRAC(95, 100)))
+	   (mi->r[ndx].stats.prob_ewma > MINSTREL_FRAC(95, 100)))
 		return;
 
 	mi->prev_sample = true;
@@ -519,7 +558,7 @@
 	}
 
 	mi->n_rates = n;
-	mi->stats_update = jiffies;
+	mi->last_stats_update = jiffies;
 
 	init_sample_table(mi);
 	minstrel_update_rates(mp, mi);
@@ -553,7 +592,7 @@
 	if (!mi->sample_table)
 		goto error1;
 
-	mi->stats_update = jiffies;
+	mi->last_stats_update = jiffies;
 	return mi;
 
 error1:
@@ -663,12 +702,18 @@
 static u32 minstrel_get_expected_throughput(void *priv_sta)
 {
 	struct minstrel_sta_info *mi = priv_sta;
+	struct minstrel_rate_stats *tmp_mrs;
 	int idx = mi->max_tp_rate[0];
+	int tmp_cur_tp;
 
 	/* convert pkt per sec in kbps (1200 is the average pkt size used for
 	 * computing cur_tp
 	 */
-	return MINSTREL_TRUNC(mi->r[idx].stats.cur_tp) * 1200 * 8 / 1024;
+	tmp_mrs = &mi->r[idx].stats;
+	tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma);
+	tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024;
+
+	return tmp_cur_tp;
 }
 
 const struct rate_control_ops mac80211_minstrel = {
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
index 554ca0b..836e161 100644
--- a/net/mac80211/rc80211_minstrel.h
+++ b/net/mac80211/rc80211_minstrel.h
@@ -13,7 +13,6 @@
 #define EWMA_DIV	128
 #define SAMPLE_COLUMNS	10	/* number of columns in sample table */
 
-
 /* scaled fraction values */
 #define MINSTREL_SCALE  16
 #define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
@@ -24,30 +23,55 @@
 
 /*
  * Perform EWMA (Exponentially Weighted Moving Average) calculation
-  */
+ */
 static inline int
 minstrel_ewma(int old, int new, int weight)
 {
-	return (new * (EWMA_DIV - weight) + old * weight) / EWMA_DIV;
+	int diff, incr;
+
+	diff = new - old;
+	incr = (EWMA_DIV - weight) * diff / EWMA_DIV;
+
+	return old + incr;
+}
+
+/*
+ * Perform EWMSD (Exponentially Weighted Moving Standard Deviation) calculation
+ */
+static inline int
+minstrel_ewmsd(int old_ewmsd, int cur_prob, int prob_ewma, int weight)
+{
+	int diff, incr, tmp_var;
+
+	/* calculate exponential weighted moving variance */
+	diff = MINSTREL_TRUNC((cur_prob - prob_ewma) * 1000000);
+	incr = (EWMA_DIV - weight) * diff / EWMA_DIV;
+	tmp_var = old_ewmsd * old_ewmsd;
+	tmp_var = weight * (tmp_var + diff * incr / 1000000) / EWMA_DIV;
+
+	/* return standard deviation */
+	return (u16) int_sqrt(tmp_var);
 }
 
 struct minstrel_rate_stats {
 	/* current / last sampling period attempts/success counters */
-	unsigned int attempts, last_attempts;
-	unsigned int success, last_success;
+	u16 attempts, last_attempts;
+	u16 success, last_success;
 
 	/* total attempts/success counters */
 	u64 att_hist, succ_hist;
 
-	/* current throughput */
-	unsigned int cur_tp;
-
-	/* packet delivery probabilities */
-	unsigned int cur_prob, probability;
+	/* statistis of packet delivery probability
+	 *  cur_prob  - current prob within last update intervall
+	 *  prob_ewma - exponential weighted moving average of prob
+	 *  prob_ewmsd - exp. weighted moving standard deviation of prob */
+	unsigned int cur_prob;
+	unsigned int prob_ewma;
+	u16 prob_ewmsd;
 
 	/* maximum retry counts */
-	unsigned int retry_count;
-	unsigned int retry_count_rtscts;
+	u8 retry_count;
+	u8 retry_count_rtscts;
 
 	u8 sample_skipped;
 	bool retry_updated;
@@ -55,14 +79,15 @@
 
 struct minstrel_rate {
 	int bitrate;
-	int rix;
+
+	s8 rix;
+	u8 retry_count_cts;
+	u8 adjusted_retry_count;
 
 	unsigned int perfect_tx_time;
 	unsigned int ack_time;
 
 	int sample_limit;
-	unsigned int retry_count_cts;
-	unsigned int adjusted_retry_count;
 
 	struct minstrel_rate_stats stats;
 };
@@ -70,7 +95,7 @@
 struct minstrel_sta_info {
 	struct ieee80211_sta *sta;
 
-	unsigned long stats_update;
+	unsigned long last_stats_update;
 	unsigned int sp_ack_dur;
 	unsigned int rate_avg;
 
@@ -94,6 +119,7 @@
 
 #ifdef CPTCFG_MAC80211_DEBUGFS
 	struct dentry *dbg_stats;
+	struct dentry *dbg_stats_csv;
 #endif
 };
 
@@ -120,7 +146,6 @@
 	u32 fixed_rate_idx;
 	struct dentry *dbg_fixed_rate;
 #endif
-
 };
 
 struct minstrel_debugfs_info {
@@ -132,8 +157,13 @@
 void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
 void minstrel_remove_sta_debugfs(void *priv, void *priv_sta);
 
+/* Recalculate success probabilities and counters for a given rate using EWMA */
+void minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs);
+int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma);
+
 /* debugfs */
 int minstrel_stats_open(struct inode *inode, struct file *file);
+int minstrel_stats_csv_open(struct inode *inode, struct file *file);
 ssize_t minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos);
 int minstrel_stats_release(struct inode *inode, struct file *file);
 
diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c
index 2acab1b..1db5f7c 100644
--- a/net/mac80211/rc80211_minstrel_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_debugfs.c
@@ -54,59 +54,6 @@
 #include <net/mac80211.h>
 #include "rc80211_minstrel.h"
 
-int
-minstrel_stats_open(struct inode *inode, struct file *file)
-{
-	struct minstrel_sta_info *mi = inode->i_private;
-	struct minstrel_debugfs_info *ms;
-	unsigned int i, tp, prob, eprob;
-	char *p;
-
-	ms = kmalloc(2048, GFP_KERNEL);
-	if (!ms)
-		return -ENOMEM;
-
-	file->private_data = ms;
-	p = ms->buf;
-	p += sprintf(p, "rate          tpt eprob *prob"
-			"  *ok(*cum)        ok(      cum)\n");
-	for (i = 0; i < mi->n_rates; i++) {
-		struct minstrel_rate *mr = &mi->r[i];
-		struct minstrel_rate_stats *mrs = &mi->r[i].stats;
-
-		*(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' ';
-		*(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' ';
-		*(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' ';
-		*(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' ';
-		*(p++) = (i == mi->max_prob_rate) ? 'P' : ' ';
-		p += sprintf(p, "%3u%s", mr->bitrate / 2,
-				(mr->bitrate & 1 ? ".5" : "  "));
-
-		tp = MINSTREL_TRUNC(mrs->cur_tp / 10);
-		prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
-		eprob = MINSTREL_TRUNC(mrs->probability * 1000);
-
-		p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u"
-				" %4u(%4u) %9llu(%9llu)\n",
-				tp / 10, tp % 10,
-				eprob / 10, eprob % 10,
-				prob / 10, prob % 10,
-				mrs->last_success,
-				mrs->last_attempts,
-				(unsigned long long)mrs->succ_hist,
-				(unsigned long long)mrs->att_hist);
-	}
-	p += sprintf(p, "\nTotal packet count::    ideal %d      "
-			"lookaround %d\n\n",
-			mi->total_packets - mi->sample_packets,
-			mi->sample_packets);
-	ms->len = p - ms->buf;
-
-	WARN_ON(ms->len + sizeof(*ms) > 2048);
-
-	return 0;
-}
-
 ssize_t
 minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
 {
@@ -123,6 +70,73 @@
 	return 0;
 }
 
+int
+minstrel_stats_open(struct inode *inode, struct file *file)
+{
+	struct minstrel_sta_info *mi = inode->i_private;
+	struct minstrel_debugfs_info *ms;
+	unsigned int i, tp_max, tp_avg, prob, eprob;
+	char *p;
+
+	ms = kmalloc(2048, GFP_KERNEL);
+	if (!ms)
+		return -ENOMEM;
+
+	file->private_data = ms;
+	p = ms->buf;
+	p += sprintf(p, "\n");
+	p += sprintf(p, "best   __________rate_________    ______"
+			"statistics______    ________last_______    "
+			"______sum-of________\n");
+	p += sprintf(p, "rate  [name idx airtime max_tp]  [ ø(tp) ø(prob) "
+			"sd(prob)]  [prob.|retry|suc|att]  "
+			"[#success | #attempts]\n");
+
+	for (i = 0; i < mi->n_rates; i++) {
+		struct minstrel_rate *mr = &mi->r[i];
+		struct minstrel_rate_stats *mrs = &mi->r[i].stats;
+
+		*(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' ';
+		*(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' ';
+		*(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' ';
+		*(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' ';
+		*(p++) = (i == mi->max_prob_rate) ? 'P' : ' ';
+
+		p += sprintf(p, " %3u%s ", mr->bitrate / 2,
+				(mr->bitrate & 1 ? ".5" : "  "));
+		p += sprintf(p, "%3u  ", i);
+		p += sprintf(p, "%6u ", mr->perfect_tx_time);
+
+		tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
+		tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
+		prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
+		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+
+		p += sprintf(p, "%4u.%1u   %4u.%1u   %3u.%1u    %3u.%1u"
+				"     %3u.%1u %3u   %3u %-3u   "
+				"%9llu   %-9llu\n",
+				tp_max / 10, tp_max % 10,
+				tp_avg / 10, tp_avg % 10,
+				eprob / 10, eprob % 10,
+				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
+				prob / 10, prob % 10,
+				mrs->retry_count,
+				mrs->last_success,
+				mrs->last_attempts,
+				(unsigned long long)mrs->succ_hist,
+				(unsigned long long)mrs->att_hist);
+	}
+	p += sprintf(p, "\nTotal packet count::    ideal %d      "
+			"lookaround %d\n\n",
+			mi->total_packets - mi->sample_packets,
+			mi->sample_packets);
+	ms->len = p - ms->buf;
+
+	WARN_ON(ms->len + sizeof(*ms) > 2048);
+
+	return 0;
+}
+
 static const struct file_operations minstrel_stat_fops = {
 	.owner = THIS_MODULE,
 	.open = minstrel_stats_open,
@@ -131,6 +145,72 @@
 	.llseek = default_llseek,
 };
 
+int
+minstrel_stats_csv_open(struct inode *inode, struct file *file)
+{
+	struct minstrel_sta_info *mi = inode->i_private;
+	struct minstrel_debugfs_info *ms;
+	unsigned int i, tp_max, tp_avg, prob, eprob;
+	char *p;
+
+	ms = kmalloc(2048, GFP_KERNEL);
+	if (!ms)
+		return -ENOMEM;
+
+	file->private_data = ms;
+	p = ms->buf;
+
+	for (i = 0; i < mi->n_rates; i++) {
+		struct minstrel_rate *mr = &mi->r[i];
+		struct minstrel_rate_stats *mrs = &mi->r[i].stats;
+
+		p += sprintf(p, "%s" ,((i == mi->max_tp_rate[0]) ? "A" : ""));
+		p += sprintf(p, "%s" ,((i == mi->max_tp_rate[1]) ? "B" : ""));
+		p += sprintf(p, "%s" ,((i == mi->max_tp_rate[2]) ? "C" : ""));
+		p += sprintf(p, "%s" ,((i == mi->max_tp_rate[3]) ? "D" : ""));
+		p += sprintf(p, "%s" ,((i == mi->max_prob_rate) ? "P" : ""));
+
+		p += sprintf(p, ",%u%s", mr->bitrate / 2,
+				(mr->bitrate & 1 ? ".5," : ","));
+		p += sprintf(p, "%u,", i);
+		p += sprintf(p, "%u,",mr->perfect_tx_time);
+
+		tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
+		tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
+		prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
+		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+
+		p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u,"
+				"%llu,%llu,%d,%d\n",
+				tp_max / 10, tp_max % 10,
+				tp_avg / 10, tp_avg % 10,
+				eprob / 10, eprob % 10,
+				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
+				prob / 10, prob % 10,
+				mrs->retry_count,
+				mrs->last_success,
+				mrs->last_attempts,
+				(unsigned long long)mrs->succ_hist,
+				(unsigned long long)mrs->att_hist,
+				mi->total_packets - mi->sample_packets,
+				mi->sample_packets);
+
+	}
+	ms->len = p - ms->buf;
+
+	WARN_ON(ms->len + sizeof(*ms) > 2048);
+
+	return 0;
+}
+
+static const struct file_operations minstrel_stat_csv_fops = {
+	.owner = THIS_MODULE,
+	.open = minstrel_stats_csv_open,
+	.read = minstrel_stats_read,
+	.release = minstrel_stats_release,
+	.llseek = default_llseek,
+};
+
 void
 minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
 {
@@ -138,6 +218,9 @@
 
 	mi->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, mi,
 			&minstrel_stat_fops);
+
+	mi->dbg_stats_csv = debugfs_create_file("rc_stats_csv", S_IRUGO, dir,
+			mi, &minstrel_stat_csv_fops);
 }
 
 void
@@ -146,4 +229,6 @@
 	struct minstrel_sta_info *mi = priv_sta;
 
 	debugfs_remove(mi->dbg_stats);
+
+	debugfs_remove(mi->dbg_stats_csv);
 }
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index e8d8bec..8f0b9ab 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -17,10 +17,11 @@
 #include "rc80211_minstrel.h"
 #include "rc80211_minstrel_ht.h"
 
+#define AVG_AMPDU_SIZE	16
 #define AVG_PKT_SIZE	1200
 
 /* Number of bits for an average sized packet */
-#define MCS_NBITS (AVG_PKT_SIZE << 3)
+#define MCS_NBITS ((AVG_PKT_SIZE * AVG_AMPDU_SIZE) << 3)
 
 /* Number of symbols for a packet with (bps) bits per symbol */
 #define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps))
@@ -33,7 +34,8 @@
 	)
 
 /* Transmit duration for the raw data part of an average sized packet */
-#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps)))
+#define MCS_DURATION(streams, sgi, bps) \
+	(MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) / AVG_AMPDU_SIZE)
 
 #define BW_20			0
 #define BW_40			1
@@ -311,67 +313,35 @@
 	return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES];
 }
 
-
 /*
- * Recalculate success probabilities and counters for a rate using EWMA
+ * Return current throughput based on the average A-MPDU length, taking into
+ * account the expected number of retransmissions and their expected length
  */
-static void
-minstrel_calc_rate_ewma(struct minstrel_rate_stats *mr)
+int
+minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
+		       int prob_ewma)
 {
-	if (unlikely(mr->attempts > 0)) {
-		mr->sample_skipped = 0;
-		mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts);
-		if (!mr->att_hist)
-			mr->probability = mr->cur_prob;
-		else
-			mr->probability = minstrel_ewma(mr->probability,
-				mr->cur_prob, EWMA_LEVEL);
-		mr->att_hist += mr->attempts;
-		mr->succ_hist += mr->success;
-	} else {
-		mr->sample_skipped++;
-	}
-	mr->last_success = mr->success;
-	mr->last_attempts = mr->attempts;
-	mr->success = 0;
-	mr->attempts = 0;
-}
-
-/*
- * Calculate throughput based on the average A-MPDU length, taking into account
- * the expected number of retransmissions and their expected length
- */
-static void
-minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
-{
-	struct minstrel_rate_stats *mr;
 	unsigned int nsecs = 0;
-	unsigned int tp;
-	unsigned int prob;
 
-	mr = &mi->groups[group].rates[rate];
-	prob = mr->probability;
-
-	if (prob < MINSTREL_FRAC(1, 10)) {
-		mr->cur_tp = 0;
-		return;
-	}
-
-	/*
-	 * For the throughput calculation, limit the probability value to 90% to
-	 * account for collision related packet error rate fluctuation
-	 */
-	if (prob > MINSTREL_FRAC(9, 10))
-		prob = MINSTREL_FRAC(9, 10);
+	/* do not account throughput if sucess prob is below 10% */
+	if (prob_ewma < MINSTREL_FRAC(10, 100))
+		return 0;
 
 	if (group != MINSTREL_CCK_GROUP)
 		nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
 
 	nsecs += minstrel_mcs_groups[group].duration[rate];
 
-	/* prob is scaled - see MINSTREL_FRAC above */
-	tp = 1000000 * ((prob * 1000) / nsecs);
-	mr->cur_tp = MINSTREL_TRUNC(tp);
+	/*
+	 * For the throughput calculation, limit the probability value to 90% to
+	 * account for collision related packet error rate fluctuation
+	 * (prob is scaled - see MINSTREL_FRAC above)
+	 */
+	if (prob_ewma > MINSTREL_FRAC(90, 100))
+		return MINSTREL_TRUNC(100000 * ((MINSTREL_FRAC(90, 100) * 1000)
+								      / nsecs));
+	else
+		return MINSTREL_TRUNC(100000 * ((prob_ewma * 1000) / nsecs));
 }
 
 /*
@@ -385,22 +355,23 @@
 minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index,
 			       u16 *tp_list)
 {
-	int cur_group, cur_idx, cur_thr, cur_prob;
-	int tmp_group, tmp_idx, tmp_thr, tmp_prob;
+	int cur_group, cur_idx, cur_tp_avg, cur_prob;
+	int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob;
 	int j = MAX_THR_RATES;
 
 	cur_group = index / MCS_GROUP_RATES;
 	cur_idx = index  % MCS_GROUP_RATES;
-	cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp;
-	cur_prob = mi->groups[cur_group].rates[cur_idx].probability;
+	cur_prob = mi->groups[cur_group].rates[cur_idx].prob_ewma;
+	cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, cur_prob);
 
 	do {
 		tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
 		tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
-		tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
-		tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
-		if (cur_thr < tmp_thr ||
-		    (cur_thr == tmp_thr && cur_prob <= tmp_prob))
+		tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+		tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx,
+						    tmp_prob);
+		if (cur_tp_avg < tmp_tp_avg ||
+		    (cur_tp_avg == tmp_tp_avg && cur_prob <= tmp_prob))
 			break;
 		j--;
 	} while (j > 0);
@@ -420,16 +391,21 @@
 minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
 {
 	struct minstrel_mcs_group_data *mg;
-	struct minstrel_rate_stats *mr;
-	int tmp_group, tmp_idx, tmp_tp, tmp_prob, max_tp_group;
+	struct minstrel_rate_stats *mrs;
+	int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob;
+	int max_tp_group, cur_tp_avg, cur_group, cur_idx;
+	int max_gpr_group, max_gpr_idx;
+	int max_gpr_tp_avg, max_gpr_prob;
 
+	cur_group = index / MCS_GROUP_RATES;
+	cur_idx = index % MCS_GROUP_RATES;
 	mg = &mi->groups[index / MCS_GROUP_RATES];
-	mr = &mg->rates[index % MCS_GROUP_RATES];
+	mrs = &mg->rates[index % MCS_GROUP_RATES];
 
 	tmp_group = mi->max_prob_rate / MCS_GROUP_RATES;
 	tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES;
-	tmp_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
-	tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
+	tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+	tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
 
 	/* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from
 	 * MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */
@@ -438,15 +414,24 @@
 	    (max_tp_group != MINSTREL_CCK_GROUP))
 		return;
 
-	if (mr->probability > MINSTREL_FRAC(75, 100)) {
-		if (mr->cur_tp > tmp_tp)
+	if (mrs->prob_ewma > MINSTREL_FRAC(75, 100)) {
+		cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx,
+						    mrs->prob_ewma);
+		if (cur_tp_avg > tmp_tp_avg)
 			mi->max_prob_rate = index;
-		if (mr->cur_tp > mg->rates[mg->max_group_prob_rate].cur_tp)
+
+		max_gpr_group = mg->max_group_prob_rate / MCS_GROUP_RATES;
+		max_gpr_idx = mg->max_group_prob_rate %	MCS_GROUP_RATES;
+		max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_ewma;
+		max_gpr_tp_avg = minstrel_ht_get_tp_avg(mi, max_gpr_group,
+							max_gpr_idx,
+							max_gpr_prob);
+		if (cur_tp_avg > max_gpr_tp_avg)
 			mg->max_group_prob_rate = index;
 	} else {
-		if (mr->probability > tmp_prob)
+		if (mrs->prob_ewma > tmp_prob)
 			mi->max_prob_rate = index;
-		if (mr->probability > mg->rates[mg->max_group_prob_rate].probability)
+		if (mrs->prob_ewma > mg->rates[mg->max_group_prob_rate].prob_ewma)
 			mg->max_group_prob_rate = index;
 	}
 }
@@ -463,16 +448,18 @@
 				 u16 tmp_mcs_tp_rate[MAX_THR_RATES],
 				 u16 tmp_cck_tp_rate[MAX_THR_RATES])
 {
-	unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp;
+	unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp, tmp_prob;
 	int i;
 
 	tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES;
 	tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES;
-	tmp_cck_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
+	tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+	tmp_cck_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
 
 	tmp_group = tmp_mcs_tp_rate[0] / MCS_GROUP_RATES;
 	tmp_idx = tmp_mcs_tp_rate[0] % MCS_GROUP_RATES;
-	tmp_mcs_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
+	tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+	tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
 
 	if (tmp_cck_tp > tmp_mcs_tp) {
 		for(i = 0; i < MAX_THR_RATES; i++) {
@@ -491,8 +478,7 @@
 minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
 {
 	struct minstrel_mcs_group_data *mg;
-	struct minstrel_rate_stats *mr;
-	int tmp_max_streams, group;
+	int tmp_max_streams, group, tmp_idx, tmp_prob;
 	int tmp_tp = 0;
 
 	tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] /
@@ -501,11 +487,16 @@
 		mg = &mi->groups[group];
 		if (!mg->supported || group == MINSTREL_CCK_GROUP)
 			continue;
-		mr = minstrel_get_ratestats(mi, mg->max_group_prob_rate);
-		if (tmp_tp < mr->cur_tp &&
+
+		tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES;
+		tmp_prob = mi->groups[group].rates[tmp_idx].prob_ewma;
+
+		if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
 		   (minstrel_mcs_groups[group].streams < tmp_max_streams)) {
 				mi->max_prob_rate = mg->max_group_prob_rate;
-				tmp_tp = mr->cur_tp;
+				tmp_tp = minstrel_ht_get_tp_avg(mi, group,
+								tmp_idx,
+								tmp_prob);
 		}
 	}
 }
@@ -523,8 +514,8 @@
 minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
 {
 	struct minstrel_mcs_group_data *mg;
-	struct minstrel_rate_stats *mr;
-	int group, i, j;
+	struct minstrel_rate_stats *mrs;
+	int group, i, j, cur_prob;
 	u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
 	u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
 
@@ -563,12 +554,12 @@
 
 			index = MCS_GROUP_RATES * group + i;
 
-			mr = &mg->rates[i];
-			mr->retry_updated = false;
-			minstrel_calc_rate_ewma(mr);
-			minstrel_ht_calc_tp(mi, group, i);
+			mrs = &mg->rates[i];
+			mrs->retry_updated = false;
+			minstrel_calc_rate_stats(mrs);
+			cur_prob = mrs->prob_ewma;
 
-			if (!mr->cur_tp)
+			if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0)
 				continue;
 
 			/* Find max throughput rate set */
@@ -612,7 +603,7 @@
 #endif
 
 	/* Reset update timer */
-	mi->stats_update = jiffies;
+	mi->last_stats_update = jiffies;
 }
 
 static bool
@@ -635,7 +626,7 @@
 }
 
 static void
-minstrel_next_sample_idx(struct minstrel_ht_sta *mi)
+minstrel_set_next_sample_idx(struct minstrel_ht_sta *mi)
 {
 	struct minstrel_mcs_group_data *mg;
 
@@ -776,7 +767,8 @@
 		update = true;
 	}
 
-	if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) {
+	if (time_after(jiffies, mi->last_stats_update +
+				(mp->update_interval / 2 * HZ) / 1000)) {
 		update = true;
 		minstrel_ht_update_stats(mp, mi);
 	}
@@ -789,7 +781,7 @@
 minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
                          int index)
 {
-	struct minstrel_rate_stats *mr;
+	struct minstrel_rate_stats *mrs;
 	const struct mcs_group *group;
 	unsigned int tx_time, tx_time_rtscts, tx_time_data;
 	unsigned int cw = mp->cw_min;
@@ -798,16 +790,16 @@
 	unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len);
 	unsigned int overhead = 0, overhead_rtscts = 0;
 
-	mr = minstrel_get_ratestats(mi, index);
-	if (mr->probability < MINSTREL_FRAC(1, 10)) {
-		mr->retry_count = 1;
-		mr->retry_count_rtscts = 1;
+	mrs = minstrel_get_ratestats(mi, index);
+	if (mrs->prob_ewma < MINSTREL_FRAC(1, 10)) {
+		mrs->retry_count = 1;
+		mrs->retry_count_rtscts = 1;
 		return;
 	}
 
-	mr->retry_count = 2;
-	mr->retry_count_rtscts = 2;
-	mr->retry_updated = true;
+	mrs->retry_count = 2;
+	mrs->retry_count_rtscts = 2;
+	mrs->retry_updated = true;
 
 	group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
 	tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len / 1000;
@@ -838,9 +830,9 @@
 		tx_time_rtscts += ctime + overhead_rtscts + tx_time_data;
 
 		if (tx_time_rtscts < mp->segment_size)
-			mr->retry_count_rtscts++;
+			mrs->retry_count_rtscts++;
 	} while ((tx_time < mp->segment_size) &&
-	         (++mr->retry_count < mp->max_retry));
+	         (++mrs->retry_count < mp->max_retry));
 }
 
 
@@ -849,22 +841,22 @@
                      struct ieee80211_sta_rates *ratetbl, int offset, int index)
 {
 	const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
-	struct minstrel_rate_stats *mr;
+	struct minstrel_rate_stats *mrs;
 	u8 idx;
 	u16 flags = group->flags;
 
-	mr = minstrel_get_ratestats(mi, index);
-	if (!mr->retry_updated)
+	mrs = minstrel_get_ratestats(mi, index);
+	if (!mrs->retry_updated)
 		minstrel_calc_retransmit(mp, mi, index);
 
-	if (mr->probability < MINSTREL_FRAC(20, 100) || !mr->retry_count) {
+	if (mrs->prob_ewma < MINSTREL_FRAC(20, 100) || !mrs->retry_count) {
 		ratetbl->rate[offset].count = 2;
 		ratetbl->rate[offset].count_rts = 2;
 		ratetbl->rate[offset].count_cts = 2;
 	} else {
-		ratetbl->rate[offset].count = mr->retry_count;
-		ratetbl->rate[offset].count_cts = mr->retry_count;
-		ratetbl->rate[offset].count_rts = mr->retry_count_rtscts;
+		ratetbl->rate[offset].count = mrs->retry_count;
+		ratetbl->rate[offset].count_cts = mrs->retry_count;
+		ratetbl->rate[offset].count_rts = mrs->retry_count_rtscts;
 	}
 
 	if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP)
@@ -922,7 +914,7 @@
 static int
 minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
 {
-	struct minstrel_rate_stats *mr;
+	struct minstrel_rate_stats *mrs;
 	struct minstrel_mcs_group_data *mg;
 	unsigned int sample_dur, sample_group, cur_max_tp_streams;
 	int sample_idx = 0;
@@ -938,12 +930,12 @@
 	sample_group = mi->sample_group;
 	mg = &mi->groups[sample_group];
 	sample_idx = sample_table[mg->column][mg->index];
-	minstrel_next_sample_idx(mi);
+	minstrel_set_next_sample_idx(mi);
 
 	if (!(mg->supported & BIT(sample_idx)))
 		return -1;
 
-	mr = &mg->rates[sample_idx];
+	mrs = &mg->rates[sample_idx];
 	sample_idx += sample_group * MCS_GROUP_RATES;
 
 	/*
@@ -960,7 +952,7 @@
 	 * Do not sample if the probability is already higher than 95%
 	 * to avoid wasting airtime.
 	 */
-	if (mr->probability > MINSTREL_FRAC(95, 100))
+	if (mrs->prob_ewma > MINSTREL_FRAC(95, 100))
 		return -1;
 
 	/*
@@ -975,7 +967,7 @@
 	    (cur_max_tp_streams - 1 <
 	     minstrel_mcs_groups[sample_group].streams ||
 	     sample_dur >= minstrel_get_duration(mi->max_prob_rate))) {
-		if (mr->sample_skipped < 20)
+		if (mrs->sample_skipped < 20)
 			return -1;
 
 		if (mi->sample_slow++ > 2)
@@ -1129,7 +1121,7 @@
 	memset(mi, 0, sizeof(*mi));
 
 	mi->sta = sta;
-	mi->stats_update = jiffies;
+	mi->last_stats_update = jiffies;
 
 	ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0);
 	mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0);
@@ -1326,16 +1318,19 @@
 {
 	struct minstrel_ht_sta_priv *msp = priv_sta;
 	struct minstrel_ht_sta *mi = &msp->ht;
-	int i, j;
+	int i, j, prob, tp_avg;
 
 	if (!msp->is_ht)
 		return mac80211_minstrel.get_expected_throughput(priv_sta);
 
 	i = mi->max_tp_rate[0] / MCS_GROUP_RATES;
 	j = mi->max_tp_rate[0] % MCS_GROUP_RATES;
+	prob = mi->groups[i].rates[j].prob_ewma;
 
-	/* convert cur_tp from pkt per second in kbps */
-	return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024;
+	/* convert tp_avg from pkt per second in kbps */
+	tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * AVG_PKT_SIZE * 8 / 1024;
+
+	return tp_avg;
 }
 
 static const struct rate_control_ops mac80211_minstrel_ht = {
diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h
index a1f74f5..76b4bf0 100644
--- a/net/mac80211/rc80211_minstrel_ht.h
+++ b/net/mac80211/rc80211_minstrel_ht.h
@@ -78,7 +78,7 @@
 	u16 max_prob_rate;
 
 	/* time of last status update */
-	unsigned long stats_update;
+	unsigned long last_stats_update;
 
 	/* overhead time in usec for each frame */
 	unsigned int overhead;
@@ -112,6 +112,7 @@
 	};
 #ifdef CPTCFG_MAC80211_DEBUGFS
 	struct dentry *dbg_stats;
+	struct dentry *dbg_stats_csv;
 #endif
 	void *ratelist;
 	void *sample_table;
@@ -120,5 +121,7 @@
 
 void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
 void minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta);
+int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
+			   int prob_ewma);
 
 #endif
diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c
index 20c676b..6822ce0 100644
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
@@ -19,7 +19,7 @@
 minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
 {
 	const struct mcs_group *mg;
-	unsigned int j, tp, prob, eprob;
+	unsigned int j, tp_max, tp_avg, prob, eprob, tx_time;
 	char htmode = '2';
 	char gimode = 'L';
 	u32 gflags;
@@ -38,19 +38,26 @@
 		gimode = 'S';
 
 	for (j = 0; j < MCS_GROUP_RATES; j++) {
-		struct minstrel_rate_stats *mr = &mi->groups[i].rates[j];
+		struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
 		static const int bitrates[4] = { 10, 20, 55, 110 };
 		int idx = i * MCS_GROUP_RATES + j;
 
 		if (!(mi->groups[i].supported & BIT(j)))
 			continue;
 
-		if (gflags & IEEE80211_TX_RC_MCS)
-			p += sprintf(p, " HT%c0/%cGI ", htmode, gimode);
-		else if (gflags & IEEE80211_TX_RC_VHT_MCS)
-			p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode);
-		else
-			p += sprintf(p, " CCK/%cP   ", j < 4 ? 'L' : 'S');
+		if (gflags & IEEE80211_TX_RC_MCS) {
+			p += sprintf(p, "HT%c0  ", htmode);
+			p += sprintf(p, "%cGI  ", gimode);
+			p += sprintf(p, "%d  ", mg->streams);
+		} else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
+			p += sprintf(p, "VHT%c0 ", htmode);
+			p += sprintf(p, "%cGI ", gimode);
+			p += sprintf(p, "%d  ", mg->streams);
+		} else {
+			p += sprintf(p, "CCK    ");
+			p += sprintf(p, "%cP  ", j < 4 ? 'L' : 'S');
+			p += sprintf(p, "1 ");
+		}
 
 		*(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
 		*(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
@@ -59,29 +66,39 @@
 		*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
 
 		if (gflags & IEEE80211_TX_RC_MCS) {
-			p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j);
+			p += sprintf(p, "  MCS%-2u", (mg->streams - 1) * 8 + j);
 		} else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
-			p += sprintf(p, " MCS%-1u/%1u", j, mg->streams);
+			p += sprintf(p, "  MCS%-1u/%1u", j, mg->streams);
 		} else {
 			int r = bitrates[j % 4];
 
-			p += sprintf(p, " %2u.%1uM ", r / 10, r % 10);
+			p += sprintf(p, "   %2u.%1uM", r / 10, r % 10);
 		}
 
-		tp = mr->cur_tp / 10;
-		prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
-		eprob = MINSTREL_TRUNC(mr->probability * 1000);
+		p += sprintf(p, "  %3u  ", idx);
 
-		p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u "
-				"%3u %4u(%4u) %9llu(%9llu)\n",
-				tp / 10, tp % 10,
+		/* tx_time[rate(i)] in usec */
+		tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000);
+		p += sprintf(p, "%6u  ", tx_time);
+
+		tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
+		tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma);
+		prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
+		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+
+		p += sprintf(p, "%4u.%1u   %4u.%1u   %3u.%1u    %3u.%1u"
+				"     %3u.%1u %3u   %3u %-3u   "
+				"%9llu   %-9llu\n",
+				tp_max / 10, tp_max % 10,
+				tp_avg / 10, tp_avg % 10,
 				eprob / 10, eprob % 10,
+				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
 				prob / 10, prob % 10,
-				mr->retry_count,
-				mr->last_success,
-				mr->last_attempts,
-				(unsigned long long)mr->succ_hist,
-				(unsigned long long)mr->att_hist);
+				mrs->retry_count,
+				mrs->last_success,
+				mrs->last_attempts,
+				(unsigned long long)mrs->succ_hist,
+				(unsigned long long)mrs->att_hist);
 	}
 
 	return p;
@@ -94,8 +111,8 @@
 	struct minstrel_ht_sta *mi = &msp->ht;
 	struct minstrel_debugfs_info *ms;
 	unsigned int i;
-	char *p;
 	int ret;
+	char *p;
 
 	if (!msp->is_ht) {
 		inode->i_private = &msp->legacy;
@@ -110,8 +127,14 @@
 
 	file->private_data = ms;
 	p = ms->buf;
-	p += sprintf(p, " type           rate      tpt eprob *prob "
-			"ret  *ok(*cum)        ok(      cum)\n");
+
+	p += sprintf(p, "\n");
+	p += sprintf(p, "              best   ____________rate__________    "
+			"______statistics______    ________last_______    "
+			"______sum-of________\n");
+	p += sprintf(p, "mode guard #  rate  [name   idx airtime  max_tp]  "
+			"[ ø(tp) ø(prob) sd(prob)]  [prob.|retry|suc|att]  [#success | "
+			"#attempts]\n");
 
 	p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
 	for (i = 0; i < MINSTREL_CCK_GROUP; i++)
@@ -123,11 +146,10 @@
 			"lookaround %d\n",
 			max(0, (int) mi->total_packets - (int) mi->sample_packets),
 			mi->sample_packets);
-	p += sprintf(p, "Average A-MPDU length: %d.%d\n",
+	p += sprintf(p, "Average # of aggregated frames per A-MPDU: %d.%d\n",
 		MINSTREL_TRUNC(mi->avg_ampdu_len),
 		MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
 	ms->len = p - ms->buf;
-
 	WARN_ON(ms->len + sizeof(*ms) > 32768);
 
 	return nonseekable_open(inode, file);
@@ -141,6 +163,143 @@
 	.llseek = no_llseek,
 };
 
+static char *
+minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
+{
+	const struct mcs_group *mg;
+	unsigned int j, tp_max, tp_avg, prob, eprob, tx_time;
+	char htmode = '2';
+	char gimode = 'L';
+	u32 gflags;
+
+	if (!mi->groups[i].supported)
+		return p;
+
+	mg = &minstrel_mcs_groups[i];
+	gflags = mg->flags;
+
+	if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+		htmode = '4';
+	else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+		htmode = '8';
+	if (gflags & IEEE80211_TX_RC_SHORT_GI)
+		gimode = 'S';
+
+	for (j = 0; j < MCS_GROUP_RATES; j++) {
+		struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
+		static const int bitrates[4] = { 10, 20, 55, 110 };
+		int idx = i * MCS_GROUP_RATES + j;
+
+		if (!(mi->groups[i].supported & BIT(j)))
+			continue;
+
+		if (gflags & IEEE80211_TX_RC_MCS) {
+			p += sprintf(p, "HT%c0,", htmode);
+			p += sprintf(p, "%cGI,", gimode);
+			p += sprintf(p, "%d,", mg->streams);
+		} else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
+			p += sprintf(p, "VHT%c0,", htmode);
+			p += sprintf(p, "%cGI,", gimode);
+			p += sprintf(p, "%d,", mg->streams);
+		} else {
+			p += sprintf(p, "CCK,");
+			p += sprintf(p, "%cP,", j < 4 ? 'L' : 'S');
+			p += sprintf(p, "1,");
+		}
+
+		p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[0]) ? "A" : ""));
+		p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[1]) ? "B" : ""));
+		p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[2]) ? "C" : ""));
+		p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[3]) ? "D" : ""));
+		p += sprintf(p, "%s" ,((idx == mi->max_prob_rate) ? "P" : ""));
+
+		if (gflags & IEEE80211_TX_RC_MCS) {
+			p += sprintf(p, ",MCS%-2u,", (mg->streams - 1) * 8 + j);
+		} else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
+			p += sprintf(p, ",MCS%-1u/%1u,", j, mg->streams);
+		} else {
+			int r = bitrates[j % 4];
+			p += sprintf(p, ",%2u.%1uM,", r / 10, r % 10);
+		}
+
+		p += sprintf(p, "%u,", idx);
+		tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000);
+		p += sprintf(p, "%u,", tx_time);
+
+		tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
+		tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma);
+		prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
+		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+
+		p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,"
+				"%u,%llu,%llu,",
+				tp_max / 10, tp_max % 10,
+				tp_avg / 10, tp_avg % 10,
+				eprob / 10, eprob % 10,
+				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
+				prob / 10, prob % 10,
+				mrs->retry_count,
+				mrs->last_success,
+				mrs->last_attempts,
+				(unsigned long long)mrs->succ_hist,
+				(unsigned long long)mrs->att_hist);
+		p += sprintf(p, "%d,%d,%d.%d\n",
+				max(0, (int) mi->total_packets -
+				(int) mi->sample_packets),
+				mi->sample_packets,
+				MINSTREL_TRUNC(mi->avg_ampdu_len),
+				MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
+	}
+
+	return p;
+}
+
+static int
+minstrel_ht_stats_csv_open(struct inode *inode, struct file *file)
+{
+	struct minstrel_ht_sta_priv *msp = inode->i_private;
+	struct minstrel_ht_sta *mi = &msp->ht;
+	struct minstrel_debugfs_info *ms;
+	unsigned int i;
+	int ret;
+	char *p;
+
+	if (!msp->is_ht) {
+		inode->i_private = &msp->legacy;
+		ret = minstrel_stats_csv_open(inode, file);
+		inode->i_private = msp;
+		return ret;
+	}
+
+	ms = kmalloc(32768, GFP_KERNEL);
+
+	if (!ms)
+		return -ENOMEM;
+
+	file->private_data = ms;
+
+	p = ms->buf;
+
+	p = minstrel_ht_stats_csv_dump(mi, MINSTREL_CCK_GROUP, p);
+	for (i = 0; i < MINSTREL_CCK_GROUP; i++)
+		p = minstrel_ht_stats_csv_dump(mi, i, p);
+	for (i++; i < ARRAY_SIZE(mi->groups); i++)
+		p = minstrel_ht_stats_csv_dump(mi, i, p);
+
+	ms->len = p - ms->buf;
+	WARN_ON(ms->len + sizeof(*ms) > 32768);
+
+	return nonseekable_open(inode, file);
+}
+
+static const struct file_operations minstrel_ht_stat_csv_fops = {
+	.owner = THIS_MODULE,
+	.open = minstrel_ht_stats_csv_open,
+	.read = minstrel_stats_read,
+	.release = minstrel_stats_release,
+	.llseek = no_llseek,
+};
+
 void
 minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
 {
@@ -148,6 +307,8 @@
 
 	msp->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, msp,
 			&minstrel_ht_stat_fops);
+	msp->dbg_stats_csv = debugfs_create_file("rc_stats_csv", S_IRUGO,
+			     dir, msp, &minstrel_ht_stat_csv_fops);
 }
 
 void
@@ -156,4 +317,5 @@
 	struct minstrel_ht_sta_priv *msp = priv_sta;
 
 	debugfs_remove(msp->dbg_stats);
+	debugfs_remove(msp->dbg_stats_csv);
 }
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 2affc2a..cf144db 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -272,7 +272,7 @@
 	else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
 		channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
 	else if (rate)
-		channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
+		channel_flags |= IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ;
 	else
 		channel_flags |= IEEE80211_CHAN_2GHZ;
 	put_unaligned_le16(channel_flags, pos);
@@ -361,9 +361,6 @@
 		u16 known = local->hw.radiotap_vht_details;
 
 		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
-		/* known field - how to handle 80+80? */
-		if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)
-			known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
 		put_unaligned_le16(known, pos);
 		pos += 2;
 		/* flags */
@@ -378,8 +375,6 @@
 		/* bandwidth */
 		if (status->vht_flag & RX_VHT_FLAG_80MHZ)
 			*pos++ = 4;
-		else if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)
-			*pos++ = 0; /* marked not known above */
 		else if (status->vht_flag & RX_VHT_FLAG_160MHZ)
 			*pos++ = 11;
 		else if (status->flag & RX_FLAG_40MHZ)
@@ -652,6 +647,7 @@
 {
 	struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data;
 	struct ieee80211_mmie *mmie;
+	struct ieee80211_mmie_16 *mmie16;
 
 	if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))
 		return -1;
@@ -661,11 +657,18 @@
 
 	mmie = (struct ieee80211_mmie *)
 		(skb->data + skb->len - sizeof(*mmie));
-	if (mmie->element_id != WLAN_EID_MMIE ||
-	    mmie->length != sizeof(*mmie) - 2)
-		return -1;
+	if (mmie->element_id == WLAN_EID_MMIE &&
+	    mmie->length == sizeof(*mmie) - 2)
+		return le16_to_cpu(mmie->key_id);
 
-	return le16_to_cpu(mmie->key_id);
+	mmie16 = (struct ieee80211_mmie_16 *)
+		(skb->data + skb->len - sizeof(*mmie16));
+	if (skb->len >= 24 + sizeof(*mmie16) &&
+	    mmie16->element_id == WLAN_EID_MMIE &&
+	    mmie16->length == sizeof(*mmie16) - 2)
+		return le16_to_cpu(mmie16->key_id);
+
+	return -1;
 }
 
 static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs,
@@ -870,9 +873,10 @@
 
  set_release_timer:
 
-		mod_timer(&tid_agg_rx->reorder_timer,
-			  tid_agg_rx->reorder_time[j] + 1 +
-			  HT_RX_REORDER_BUF_TIMEOUT);
+		if (!tid_agg_rx->removed)
+			mod_timer(&tid_agg_rx->reorder_timer,
+				  tid_agg_rx->reorder_time[j] + 1 +
+				  HT_RX_REORDER_BUF_TIMEOUT);
 	} else {
 		del_timer(&tid_agg_rx->reorder_timer);
 	}
@@ -1181,6 +1185,7 @@
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
 	struct ieee80211_local *local = sdata->local;
 	struct ps_data *ps;
+	int tid;
 
 	if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
 	    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -1194,6 +1199,18 @@
 		drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta);
 	ps_dbg(sdata, "STA %pM aid %d enters power save mode\n",
 	       sta->sta.addr, sta->sta.aid);
+
+	if (!sta->sta.txq[0])
+		return;
+
+	for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
+		struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
+
+		if (!skb_queue_len(&txqi->queue))
+			set_bit(tid, &sta->txq_buffered_tids);
+		else
+			clear_bit(tid, &sta->txq_buffered_tids);
+	}
 }
 
 static void sta_ps_end(struct sta_info *sta)
@@ -1655,11 +1672,27 @@
 		result = ieee80211_crypto_tkip_decrypt(rx);
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
-		result = ieee80211_crypto_ccmp_decrypt(rx);
+		result = ieee80211_crypto_ccmp_decrypt(
+			rx, IEEE80211_CCMP_MIC_LEN);
+		break;
+	case WLAN_CIPHER_SUITE_CCMP_256:
+		result = ieee80211_crypto_ccmp_decrypt(
+			rx, IEEE80211_CCMP_256_MIC_LEN);
 		break;
 	case WLAN_CIPHER_SUITE_AES_CMAC:
 		result = ieee80211_crypto_aes_cmac_decrypt(rx);
 		break;
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+		result = ieee80211_crypto_aes_cmac_256_decrypt(rx);
+		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		result = ieee80211_crypto_aes_gmac_decrypt(rx);
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		result = ieee80211_crypto_gcmp_decrypt(rx);
+		break;
 	default:
 		result = ieee80211_crypto_hw_decrypt(rx);
 	}
@@ -1786,7 +1819,9 @@
 		/* This is the first fragment of a new frame. */
 		entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
 						 rx->seqno_idx, &(rx->skb));
-		if (rx->key && rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP &&
+		if (rx->key &&
+		    (rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP ||
+		     rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP_256) &&
 		    ieee80211_has_protected(fc)) {
 			int queue = rx->security_idx;
 			/* Store CCMP PN so that we can verify that the next
@@ -1815,7 +1850,9 @@
 		int i;
 		u8 pn[IEEE80211_CCMP_PN_LEN], *rpn;
 		int queue;
-		if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP)
+		if (!rx->key ||
+		    (rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP &&
+		     rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP_256))
 			return RX_DROP_UNUSABLE;
 		memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN);
 		for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) {
@@ -1889,8 +1926,7 @@
 	/* Drop unencrypted frames if key is set. */
 	if (unlikely(!ieee80211_has_protected(fc) &&
 		     !ieee80211_is_nullfunc(fc) &&
-		     ieee80211_is_data(fc) &&
-		     (rx->key || rx->sdata->drop_unencrypted)))
+		     ieee80211_is_data(fc) && rx->key))
 		return -EACCES;
 
 	return 0;
@@ -2020,6 +2056,9 @@
 	struct sta_info *dsta;
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
 
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += rx->skb->len;
+
 	skb = rx->skb;
 	xmit_skb = NULL;
 
@@ -2150,8 +2189,6 @@
 			dev_kfree_skb(rx->skb);
 			continue;
 		}
-		dev->stats.rx_packets++;
-		dev->stats.rx_bytes += rx->skb->len;
 
 		ieee80211_deliver_skb(rx);
 	}
@@ -2191,6 +2228,9 @@
 	hdr = (struct ieee80211_hdr *) skb->data;
 	mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
 
+	if (ieee80211_drop_unencrypted(rx, hdr->frame_control))
+		return RX_DROP_MONITOR;
+
 	/* frame is in RMC, don't forward */
 	if (ieee80211_is_data(hdr->frame_control) &&
 	    is_multicast_ether_addr(hdr->addr1) &&
@@ -2314,6 +2354,15 @@
 	if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
 		return RX_DROP_MONITOR;
 
+	if (rx->sta) {
+		/* The seqno index has the same property as needed
+		 * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
+		 * for non-QoS-data frames. Here we know it's a data
+		 * frame, so count MSDUs.
+		 */
+		rx->sta->rx_msdu[rx->seqno_idx]++;
+	}
+
 	/*
 	 * Send unexpected-4addr-frame event to hostapd. For older versions,
 	 * also drop the frame to cooked monitor interfaces.
@@ -2365,9 +2414,6 @@
 
 	rx->skb->dev = dev;
 
-	dev->stats.rx_packets++;
-	dev->stats.rx_bytes += rx->skb->len;
-
 	if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 &&
 	    !is_multicast_ether_addr(
 		    ((struct ethhdr *)rx->skb->data)->h_dest) &&
@@ -2598,7 +2644,7 @@
 		case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: {
 			struct ieee80211_supported_band *sband;
 			u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth;
-			enum ieee80211_sta_rx_bandwidth new_bw;
+			enum ieee80211_sta_rx_bandwidth max_bw, new_bw;
 
 			/* If it doesn't support 40 MHz it can't change ... */
 			if (!(rx->sta->sta.ht_cap.cap &
@@ -2606,13 +2652,18 @@
 				goto handled;
 
 			if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ)
-				new_bw = IEEE80211_STA_RX_BW_20;
+				max_bw = IEEE80211_STA_RX_BW_20;
 			else
-				new_bw = ieee80211_sta_cur_vht_bw(rx->sta);
+				max_bw = ieee80211_sta_cap_rx_bw(rx->sta);
+
+			/* set cur_max_bandwidth and recalc sta bw */
+			rx->sta->cur_max_bandwidth = max_bw;
+			new_bw = ieee80211_sta_cur_vht_bw(rx->sta);
 
 			if (rx->sta->sta.bandwidth == new_bw)
 				goto handled;
 
+			rx->sta->sta.bandwidth = new_bw;
 			sband = rx->local->hw.wiphy->bands[status->band];
 
 			rate_control_rate_update(local, sband, rx->sta,
@@ -3088,6 +3139,12 @@
 			goto rxh_next;  \
 	} while (0);
 
+	/* Lock here to avoid hitting all of the data used in the RX
+	 * path (e.g. key data, station data, ...) concurrently when
+	 * a frame is released from the reorder buffer due to timeout
+	 * from the timer, potentially concurrently with RX from the
+	 * driver.
+	 */
 	spin_lock_bh(&rx->local->rx_path_lock);
 
 	while ((skb = __skb_dequeue(frames))) {
@@ -3380,7 +3437,8 @@
 	__le16 fc;
 	struct ieee80211_rx_data rx;
 	struct ieee80211_sub_if_data *prev;
-	struct sta_info *sta, *tmp, *prev_sta;
+	struct sta_info *sta, *prev_sta;
+	struct rhash_head *tmp;
 	int err = 0;
 
 	fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
@@ -3415,9 +3473,13 @@
 		ieee80211_scan_rx(local, skb);
 
 	if (ieee80211_is_data(fc)) {
+		const struct bucket_table *tbl;
+
 		prev_sta = NULL;
 
-		for_each_sta_info(local, hdr->addr2, sta, tmp) {
+		tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
+
+		for_each_sta_info(local, tbl, hdr->addr2, sta, tmp) {
 			if (!prev_sta) {
 				prev_sta = sta;
 				continue;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index ae84267..7bb6a93 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -416,7 +416,7 @@
 	ieee80211_offchannel_stop_vifs(local);
 
 	/* ensure nullfunc is transmitted before leaving operating channel */
-	ieee80211_flush_queues(local, NULL);
+	ieee80211_flush_queues(local, NULL, false);
 
 	ieee80211_configure_filter(local);
 
@@ -432,7 +432,7 @@
 static bool ieee80211_can_scan(struct ieee80211_local *local,
 			       struct ieee80211_sub_if_data *sdata)
 {
-	if (local->radar_detect_enabled)
+	if (ieee80211_is_radar_required(local))
 		return false;
 
 	if (!list_empty(&local->roc_list))
@@ -505,7 +505,7 @@
 
 	lockdep_assert_held(&local->mtx);
 
-	if (local->scan_req)
+	if (local->scan_req || ieee80211_is_radar_required(local))
 		return -EBUSY;
 
 	if (!ieee80211_can_scan(local, sdata)) {
@@ -805,7 +805,7 @@
 	ieee80211_offchannel_stop_vifs(local);
 
 	if (local->ops->flush) {
-		ieee80211_flush_queues(local, NULL);
+		ieee80211_flush_queues(local, NULL, false);
 		*next_delay = 0;
 	} else
 		*next_delay = HZ / 10;
@@ -828,6 +828,11 @@
 
 	mutex_lock(&local->mtx);
 
+	if (!ieee80211_can_run_worker(local)) {
+		aborted = true;
+		goto out_complete;
+	}
+
 	sdata = rcu_dereference_protected(local->scan_sdata,
 					  lockdep_is_held(&local->mtx));
 	scan_req = rcu_dereference_protected(local->scan_req,
@@ -923,11 +928,12 @@
 
 int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
 				const u8 *ssid, u8 ssid_len,
-				struct ieee80211_channel *chan,
+				struct ieee80211_channel **channels,
+				unsigned int n_channels,
 				enum nl80211_bss_scan_width scan_width)
 {
 	struct ieee80211_local *local = sdata->local;
-	int ret = -EBUSY;
+	int ret = -EBUSY, i, n_ch = 0;
 	enum ieee80211_band band;
 
 	mutex_lock(&local->mtx);
@@ -937,9 +943,8 @@
 		goto unlock;
 
 	/* fill internal scan request */
-	if (!chan) {
-		int i, max_n;
-		int n_ch = 0;
+	if (!channels) {
+		int max_n;
 
 		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
 			if (!local->hw.wiphy->bands[band])
@@ -964,12 +969,19 @@
 
 		local->int_scan_req->n_channels = n_ch;
 	} else {
-		if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IR |
-						IEEE80211_CHAN_DISABLED)))
+		for (i = 0; i < n_channels; i++) {
+			if (channels[i]->flags & (IEEE80211_CHAN_NO_IR |
+						  IEEE80211_CHAN_DISABLED))
+				continue;
+
+			local->int_scan_req->channels[n_ch] = channels[i];
+			n_ch++;
+		}
+
+		if (WARN_ON_ONCE(n_ch == 0))
 			goto unlock;
 
-		local->int_scan_req->channels[0] = chan;
-		local->int_scan_req->n_channels = 1;
+		local->int_scan_req->n_channels = n_ch;
 	}
 
 	local->int_scan_req->ssids = &local->scan_ssid;
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index efeba56..06e6ac8 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -34,19 +34,15 @@
 	struct cfg80211_chan_def new_vht_chandef = {};
 	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
 	const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
-	const struct ieee80211_ht_operation *ht_oper;
 	int secondary_channel_offset = -1;
 
 	sec_chan_offs = elems->sec_chan_offs;
 	wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
-	ht_oper = elems->ht_operation;
 
 	if (sta_flags & (IEEE80211_STA_DISABLE_HT |
 			 IEEE80211_STA_DISABLE_40MHZ)) {
 		sec_chan_offs = NULL;
 		wide_bw_chansw_ie = NULL;
-		/* only used for bandwidth here */
-		ht_oper = NULL;
 	}
 
 	if (sta_flags & IEEE80211_STA_DISABLE_VHT)
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index ca80ce0..681274b 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -64,32 +64,20 @@
  * freed before they are done using it.
  */
 
+static const struct rhashtable_params sta_rht_params = {
+	.nelem_hint = 3, /* start small */
+	.head_offset = offsetof(struct sta_info, hash_node),
+	.key_offset = offsetof(struct sta_info, sta.addr),
+	.key_len = ETH_ALEN,
+	.hashfn = sta_addr_hash,
+};
+
 /* Caller must hold local->sta_mtx */
 static int sta_info_hash_del(struct ieee80211_local *local,
 			     struct sta_info *sta)
 {
-	struct sta_info *s;
-
-	s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)],
-				      lockdep_is_held(&local->sta_mtx));
-	if (!s)
-		return -ENOENT;
-	if (s == sta) {
-		rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)],
-				   s->hnext);
-		return 0;
-	}
-
-	while (rcu_access_pointer(s->hnext) &&
-	       rcu_access_pointer(s->hnext) != sta)
-		s = rcu_dereference_protected(s->hnext,
-					lockdep_is_held(&local->sta_mtx));
-	if (rcu_access_pointer(s->hnext)) {
-		rcu_assign_pointer(s->hnext, sta->hnext);
-		return 0;
-	}
-
-	return -ENOENT;
+	return rhashtable_remove_fast(&local->sta_hash, &sta->hash_node,
+				      sta_rht_params);
 }
 
 static void __cleanup_single_sta(struct sta_info *sta)
@@ -116,7 +104,16 @@
 		clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
 
 		atomic_dec(&ps->num_sta_ps);
-		sta_info_recalc_tim(sta);
+	}
+
+	if (sta->sta.txq[0]) {
+		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+			struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
+			int n = skb_queue_len(&txqi->queue);
+
+			ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+			atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]);
+		}
 	}
 
 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
@@ -160,18 +157,8 @@
 			      const u8 *addr)
 {
 	struct ieee80211_local *local = sdata->local;
-	struct sta_info *sta;
 
-	sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
-				    lockdep_is_held(&local->sta_mtx));
-	while (sta) {
-		if (sta->sdata == sdata &&
-		    ether_addr_equal(sta->sta.addr, addr))
-			break;
-		sta = rcu_dereference_check(sta->hnext,
-					    lockdep_is_held(&local->sta_mtx));
-	}
-	return sta;
+	return rhashtable_lookup_fast(&local->sta_hash, addr, sta_rht_params);
 }
 
 /*
@@ -183,18 +170,24 @@
 {
 	struct ieee80211_local *local = sdata->local;
 	struct sta_info *sta;
+	struct rhash_head *tmp;
+	const struct bucket_table *tbl;
 
-	sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
-				    lockdep_is_held(&local->sta_mtx));
-	while (sta) {
-		if ((sta->sdata == sdata ||
-		     (sta->sdata->bss && sta->sdata->bss == sdata->bss)) &&
-		    ether_addr_equal(sta->sta.addr, addr))
-			break;
-		sta = rcu_dereference_check(sta->hnext,
-					    lockdep_is_held(&local->sta_mtx));
+	rcu_read_lock();
+	tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
+
+	for_each_sta_info(local, tbl, addr, sta, tmp) {
+		if (sta->sdata == sdata ||
+		    (sta->sdata->bss && sta->sdata->bss == sdata->bss)) {
+			rcu_read_unlock();
+			/* this is safe as the caller must already hold
+			 * another rcu read section or the mutex
+			 */
+			return sta;
+		}
 	}
-	return sta;
+	rcu_read_unlock();
+	return NULL;
 }
 
 struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
@@ -230,19 +223,13 @@
  */
 void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
 {
-	int i;
-
 	if (sta->rate_ctrl)
 		rate_control_free_sta(sta);
 
-	if (sta->tx_lat) {
-		for (i = 0; i < IEEE80211_NUM_TIDS; i++)
-			kfree(sta->tx_lat[i].bins);
-		kfree(sta->tx_lat);
-	}
-
 	sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
 
+	if (sta->sta.txq[0])
+		kfree(to_txq_info(sta->sta.txq[0]));
 	kfree(rcu_dereference_raw(sta->sta.rates));
 	kfree(sta);
 }
@@ -251,9 +238,8 @@
 static void sta_info_hash_add(struct ieee80211_local *local,
 			      struct sta_info *sta)
 {
-	lockdep_assert_held(&local->sta_mtx);
-	sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)];
-	rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
+	rhashtable_insert_fast(&local->sta_hash, &sta->hash_node,
+			       sta_rht_params);
 }
 
 static void sta_deliver_ps_frames(struct work_struct *wk)
@@ -294,44 +280,15 @@
 				const u8 *addr, gfp_t gfp)
 {
 	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_hw *hw = &local->hw;
 	struct sta_info *sta;
 	struct timespec uptime;
-	struct ieee80211_tx_latency_bin_ranges *tx_latency;
 	int i;
 
-	sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
+	sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
 	if (!sta)
 		return NULL;
 
-	rcu_read_lock();
-	tx_latency = rcu_dereference(local->tx_latency);
-	/* init stations Tx latency statistics && TID bins */
-	if (tx_latency) {
-		sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS *
-				      sizeof(struct ieee80211_tx_latency_stat),
-				      GFP_ATOMIC);
-		if (!sta->tx_lat) {
-			rcu_read_unlock();
-			goto free;
-		}
-
-		if (tx_latency->n_ranges) {
-			for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
-				/* size of bins is size of the ranges +1 */
-				sta->tx_lat[i].bin_count =
-					tx_latency->n_ranges + 1;
-				sta->tx_lat[i].bins =
-					kcalloc(sta->tx_lat[i].bin_count,
-						sizeof(u32), GFP_ATOMIC);
-				if (!sta->tx_lat[i].bins) {
-					rcu_read_unlock();
-					goto free;
-				}
-			}
-		}
-	}
-	rcu_read_unlock();
-
 	spin_lock_init(&sta->lock);
 	spin_lock_init(&sta->ps_lock);
 	INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
@@ -360,8 +317,24 @@
 	for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
 		ewma_init(&sta->chain_signal_avg[i], 1024, 8);
 
+	if (local->ops->wake_tx_queue) {
+		void *txq_data;
+		int size = sizeof(struct txq_info) +
+			   ALIGN(hw->txq_data_size, sizeof(void *));
+
+		txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
+		if (!txq_data)
+			goto free;
+
+		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+			struct txq_info *txq = txq_data + i * size;
+
+			ieee80211_init_tx_queue(sdata, sta, txq, i);
+		}
+	}
+
 	if (sta_prepare_rate_control(local, sta, gfp))
-		goto free;
+		goto free_txq;
 
 	for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
 		/*
@@ -383,7 +356,7 @@
 	if (sdata->vif.type == NL80211_IFTYPE_AP ||
 	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
 		struct ieee80211_supported_band *sband =
-			local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+			hw->wiphy->bands[ieee80211_get_sdata_band(sdata)];
 		u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
 				IEEE80211_HT_CAP_SM_PS_SHIFT;
 		/*
@@ -406,14 +379,13 @@
 	}
 
 	sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
+
 	return sta;
 
+free_txq:
+	if (sta->sta.txq[0])
+		kfree(to_txq_info(sta->sta.txq[0]));
 free:
-	if (sta->tx_lat) {
-		for (i = 0; i < IEEE80211_NUM_TIDS; i++)
-			kfree(sta->tx_lat[i].bins);
-		kfree(sta->tx_lat);
-	}
 	kfree(sta);
 	return NULL;
 }
@@ -625,7 +597,7 @@
 	}
 }
 
-void sta_info_recalc_tim(struct sta_info *sta)
+static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
 {
 	struct ieee80211_local *local = sta->local;
 	struct ps_data *ps;
@@ -667,6 +639,9 @@
 	if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1)
 		ignore_for_tim = 0;
 
+	if (ignore_pending)
+		ignore_for_tim = BIT(IEEE80211_NUM_ACS) - 1;
+
 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
 		unsigned long tids;
 
@@ -682,6 +657,8 @@
 
 		indicate_tim |=
 			sta->driver_buffered_tids & tids;
+		indicate_tim |=
+			sta->txq_buffered_tids & tids;
 	}
 
  done:
@@ -695,7 +672,7 @@
 	else
 		__bss_tim_clear(ps->tim, id);
 
-	if (local->ops->set_tim) {
+	if (local->ops->set_tim && !WARN_ON(sta->dead)) {
 		local->tim_in_locked_section = true;
 		drv_set_tim(local, &sta->sta, indicate_tim);
 		local->tim_in_locked_section = false;
@@ -705,6 +682,11 @@
 	spin_unlock_bh(&local->tim_lock);
 }
 
+void sta_info_recalc_tim(struct sta_info *sta)
+{
+	__sta_info_recalc_tim(sta, false);
+}
+
 static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb)
 {
 	struct ieee80211_tx_info *info;
@@ -874,6 +856,7 @@
 {
 	struct ieee80211_local *local = sta->local;
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
+	struct station_info sinfo = {};
 	int ret;
 
 	/*
@@ -887,6 +870,9 @@
 	/* now keys can no longer be reached */
 	ieee80211_free_sta_keys(local, sta);
 
+	/* disable TIM bit - last chance to tell driver */
+	__sta_info_recalc_tim(sta, true);
+
 	sta->dead = true;
 
 	local->num_sta--;
@@ -908,7 +894,8 @@
 
 	sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr);
 
-	cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL);
+	sta_set_sinfo(sta, &sinfo);
+	cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
 
 	rate_control_remove_sta_debugfs(sta);
 	ieee80211_sta_debugfs_remove(sta);
@@ -980,19 +967,32 @@
 		  round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL));
 }
 
-void sta_info_init(struct ieee80211_local *local)
+u32 sta_addr_hash(const void *key, u32 length, u32 seed)
 {
+	return jhash(key, ETH_ALEN, seed);
+}
+
+int sta_info_init(struct ieee80211_local *local)
+{
+	int err;
+
+	err = rhashtable_init(&local->sta_hash, &sta_rht_params);
+	if (err)
+		return err;
+
 	spin_lock_init(&local->tim_lock);
 	mutex_init(&local->sta_mtx);
 	INIT_LIST_HEAD(&local->sta_list);
 
 	setup_timer(&local->sta_cleanup, sta_info_cleanup,
 		    (unsigned long)local);
+	return 0;
 }
 
 void sta_info_stop(struct ieee80211_local *local)
 {
 	del_timer_sync(&local->sta_cleanup);
+	rhashtable_destroy(&local->sta_hash);
 }
 
 
@@ -1056,16 +1056,21 @@
 }
 
 struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw,
-					       const u8 *addr,
-					       const u8 *localaddr)
+						   const u8 *addr,
+						   const u8 *localaddr)
 {
-	struct sta_info *sta, *nxt;
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct sta_info *sta;
+	struct rhash_head *tmp;
+	const struct bucket_table *tbl;
+
+	tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
 
 	/*
 	 * Just return a random station if localaddr is NULL
 	 * ... first in list.
 	 */
-	for_each_sta_info(hw_to_local(hw), addr, sta, nxt) {
+	for_each_sta_info(local, tbl, addr, sta, tmp) {
 		if (localaddr &&
 		    !ether_addr_equal(sta->sdata->vif.addr, localaddr))
 			continue;
@@ -1103,7 +1108,7 @@
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
 	struct ieee80211_local *local = sdata->local;
 	struct sk_buff_head pending;
-	int filtered = 0, buffered = 0, ac;
+	int filtered = 0, buffered = 0, ac, i;
 	unsigned long flags;
 	struct ps_data *ps;
 
@@ -1122,10 +1127,22 @@
 
 	BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1);
 	sta->driver_buffered_tids = 0;
+	sta->txq_buffered_tids = 0;
 
 	if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
 		drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta);
 
+	if (sta->sta.txq[0]) {
+		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+			struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
+
+			if (!skb_queue_len(&txqi->queue))
+				continue;
+
+			drv_wake_tx_queue(local, txqi);
+		}
+	}
+
 	skb_queue_head_init(&pending);
 
 	/* sync with ieee80211_tx_h_unicast_ps_buf */
@@ -1243,10 +1260,11 @@
 	 * ends the poll/service period.
 	 */
 	info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
-		       IEEE80211_TX_CTL_PS_RESPONSE |
 		       IEEE80211_TX_STATUS_EOSP |
 		       IEEE80211_TX_CTL_REQ_TX_STATUS;
 
+	info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE;
+
 	if (call_driver)
 		drv_allow_buffered_frames(local, sta, BIT(tid), 1,
 					  reason, false);
@@ -1262,7 +1280,7 @@
 	}
 
 	info->band = chanctx_conf->def.chan->band;
-	ieee80211_xmit(sdata, skb);
+	ieee80211_xmit(sdata, sta, skb);
 	rcu_read_unlock();
 }
 
@@ -1306,8 +1324,10 @@
 		/* if we already have frames from software, then we can't also
 		 * release from hardware queues
 		 */
-		if (skb_queue_empty(&frames))
+		if (skb_queue_empty(&frames)) {
 			driver_release_tids |= sta->driver_buffered_tids & tids;
+			driver_release_tids |= sta->txq_buffered_tids & tids;
+		}
 
 		if (driver_release_tids) {
 			/* If the driver has data on more than one TID then
@@ -1395,8 +1415,8 @@
 			 * STA may still remain is PS mode after this frame
 			 * exchange.
 			 */
-			info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
-				       IEEE80211_TX_CTL_PS_RESPONSE;
+			info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
+			info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE;
 
 			/*
 			 * Use MoreData flag to indicate whether there are
@@ -1478,6 +1498,9 @@
 
 		sta_info_recalc_tim(sta);
 	} else {
+		unsigned long tids = sta->txq_buffered_tids & driver_release_tids;
+		int tid;
+
 		/*
 		 * We need to release a frame that is buffered somewhere in the
 		 * driver ... it'll have to handle that.
@@ -1497,8 +1520,22 @@
 		 * that the TID(s) became empty before returning here from the
 		 * release function.
 		 * Either way, however, when the driver tells us that the TID(s)
-		 * became empty we'll do the TIM recalculation.
+		 * became empty or we find that a txq became empty, we'll do the
+		 * TIM recalculation.
 		 */
+
+		if (!sta->sta.txq[0])
+			return;
+
+		for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
+			struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
+
+			if (!(tids & BIT(tid)) || skb_queue_len(&txqi->queue))
+				continue;
+
+			sta_info_recalc_tim(sta);
+			break;
+		}
 	}
 }
 
@@ -1743,7 +1780,6 @@
 	struct ieee80211_local *local = sdata->local;
 	struct rate_control_ref *ref = NULL;
 	struct timespec uptime;
-	u64 packets = 0;
 	u32 thr = 0;
 	int i, ac;
 
@@ -1752,49 +1788,90 @@
 
 	sinfo->generation = sdata->local->sta_generation;
 
-	sinfo->filled = STATION_INFO_INACTIVE_TIME |
-			STATION_INFO_RX_BYTES64 |
-			STATION_INFO_TX_BYTES64 |
-			STATION_INFO_RX_PACKETS |
-			STATION_INFO_TX_PACKETS |
-			STATION_INFO_TX_RETRIES |
-			STATION_INFO_TX_FAILED |
-			STATION_INFO_TX_BITRATE |
-			STATION_INFO_RX_BITRATE |
-			STATION_INFO_RX_DROP_MISC |
-			STATION_INFO_BSS_PARAM |
-			STATION_INFO_CONNECTED_TIME |
-			STATION_INFO_STA_FLAGS |
-			STATION_INFO_BEACON_LOSS_COUNT;
+	/* do before driver, so beacon filtering drivers have a
+	 * chance to e.g. just add the number of filtered beacons
+	 * (or just modify the value entirely, of course)
+	 */
+	if (sdata->vif.type == NL80211_IFTYPE_STATION)
+		sinfo->rx_beacon = sdata->u.mgd.count_beacon_signal;
+
+	drv_sta_statistics(local, sdata, &sta->sta, sinfo);
+
+	sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME) |
+			 BIT(NL80211_STA_INFO_STA_FLAGS) |
+			 BIT(NL80211_STA_INFO_BSS_PARAM) |
+			 BIT(NL80211_STA_INFO_CONNECTED_TIME) |
+			 BIT(NL80211_STA_INFO_RX_DROP_MISC) |
+			 BIT(NL80211_STA_INFO_BEACON_LOSS);
 
 	ktime_get_ts(&uptime);
 	sinfo->connected_time = uptime.tv_sec - sta->last_connected;
-
 	sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
-	sinfo->tx_bytes = 0;
-	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-		sinfo->tx_bytes += sta->tx_bytes[ac];
-		packets += sta->tx_packets[ac];
+
+	if (!(sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES64) |
+			       BIT(NL80211_STA_INFO_TX_BYTES)))) {
+		sinfo->tx_bytes = 0;
+		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+			sinfo->tx_bytes += sta->tx_bytes[ac];
+		sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES64);
 	}
-	sinfo->tx_packets = packets;
-	sinfo->rx_bytes = sta->rx_bytes;
-	sinfo->rx_packets = sta->rx_packets;
-	sinfo->tx_retries = sta->tx_retry_count;
-	sinfo->tx_failed = sta->tx_retry_failed;
+
+	if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_PACKETS))) {
+		sinfo->tx_packets = 0;
+		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+			sinfo->tx_packets += sta->tx_packets[ac];
+		sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
+	}
+
+	if (!(sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES64) |
+			       BIT(NL80211_STA_INFO_RX_BYTES)))) {
+		sinfo->rx_bytes = sta->rx_bytes;
+		sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64);
+	}
+
+	if (!(sinfo->filled & BIT(NL80211_STA_INFO_RX_PACKETS))) {
+		sinfo->rx_packets = sta->rx_packets;
+		sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
+	}
+
+	if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_RETRIES))) {
+		sinfo->tx_retries = sta->tx_retry_count;
+		sinfo->filled |= BIT(NL80211_STA_INFO_TX_RETRIES);
+	}
+
+	if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_FAILED))) {
+		sinfo->tx_failed = sta->tx_retry_failed;
+		sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
+	}
+
 	sinfo->rx_dropped_misc = sta->rx_dropped;
 	sinfo->beacon_loss_count = sta->beacon_loss_count;
 
+	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+	    !(sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)) {
+		sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX) |
+				 BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
+		sinfo->rx_beacon_signal_avg = ieee80211_ave_rssi(&sdata->vif);
+	}
+
 	if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
 	    (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
-		sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
-		if (!local->ops->get_rssi ||
-		    drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
+		if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL))) {
 			sinfo->signal = (s8)sta->last_signal;
-		sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
+			sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+		}
+
+		if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) {
+			sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
+			sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
+		}
 	}
-	if (sta->chains) {
-		sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
-				 STATION_INFO_CHAIN_SIGNAL_AVG;
+
+	if (sta->chains &&
+	    !(sinfo->filled & (BIT(NL80211_STA_INFO_CHAIN_SIGNAL) |
+			       BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)))) {
+		sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL) |
+				 BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
 
 		sinfo->chains = sta->chains;
 		for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
@@ -1804,23 +1881,61 @@
 		}
 	}
 
-	sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
-	sta_set_rate_info_rx(sta, &sinfo->rxrate);
+	if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE))) {
+		sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
+		sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
+	}
+
+	if (!(sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE))) {
+		sta_set_rate_info_rx(sta, &sinfo->rxrate);
+		sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
+	}
+
+	sinfo->filled |= BIT(NL80211_STA_INFO_TID_STATS);
+	for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
+		struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i];
+
+		if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) {
+			tidstats->filled |= BIT(NL80211_TID_STATS_RX_MSDU);
+			tidstats->rx_msdu = sta->rx_msdu[i];
+		}
+
+		if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU))) {
+			tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU);
+			tidstats->tx_msdu = sta->tx_msdu[i];
+		}
+
+		if (!(tidstats->filled &
+				BIT(NL80211_TID_STATS_TX_MSDU_RETRIES)) &&
+		    local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
+			tidstats->filled |=
+				BIT(NL80211_TID_STATS_TX_MSDU_RETRIES);
+			tidstats->tx_msdu_retries = sta->tx_msdu_retries[i];
+		}
+
+		if (!(tidstats->filled &
+				BIT(NL80211_TID_STATS_TX_MSDU_FAILED)) &&
+		    local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
+			tidstats->filled |=
+				BIT(NL80211_TID_STATS_TX_MSDU_FAILED);
+			tidstats->tx_msdu_failed = sta->tx_msdu_failed[i];
+		}
+	}
 
 	if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CPTCFG_MAC80211_MESH
-		sinfo->filled |= STATION_INFO_LLID |
-				 STATION_INFO_PLID |
-				 STATION_INFO_PLINK_STATE |
-				 STATION_INFO_LOCAL_PM |
-				 STATION_INFO_PEER_PM |
-				 STATION_INFO_NONPEER_PM;
+		sinfo->filled |= BIT(NL80211_STA_INFO_LLID) |
+				 BIT(NL80211_STA_INFO_PLID) |
+				 BIT(NL80211_STA_INFO_PLINK_STATE) |
+				 BIT(NL80211_STA_INFO_LOCAL_PM) |
+				 BIT(NL80211_STA_INFO_PEER_PM) |
+				 BIT(NL80211_STA_INFO_NONPEER_PM);
 
 		sinfo->llid = sta->llid;
 		sinfo->plid = sta->plid;
 		sinfo->plink_state = sta->plink_state;
 		if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
-			sinfo->filled |= STATION_INFO_T_OFFSET;
+			sinfo->filled |= BIT(NL80211_STA_INFO_T_OFFSET);
 			sinfo->t_offset = sta->t_offset;
 		}
 		sinfo->local_pm = sta->local_pm;
@@ -1869,7 +1984,7 @@
 		thr = drv_get_expected_throughput(local, &sta->sta);
 
 	if (thr != 0) {
-		sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
+		sinfo->filled |= BIT(NL80211_STA_INFO_EXPECTED_THROUGHPUT);
 		sinfo->expected_throughput = thr;
 	}
 }
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 4370931..e8db2a7 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -16,6 +16,7 @@
 #include <linux/workqueue.h>
 #include <linux/average.h>
 #include <linux/etherdevice.h>
+#include <linux/rhashtable.h>
 #include "key.h"
 
 /**
@@ -175,6 +176,7 @@
  * @reorder_lock: serializes access to reorder buffer, see below.
  * @auto_seq: used for offloaded BA sessions to automatically pick head_seq_and
  *	and ssn.
+ * @removed: this session is removed (but might have been found due to RCU)
  *
  * This structure's lifetime is managed by RCU, assignments to
  * the array holding it must hold the aggregation mutex.
@@ -199,6 +201,7 @@
 	u16 timeout;
 	u8 dialog_token;
 	bool auto_seq;
+	bool removed;
 };
 
 /**
@@ -234,25 +237,6 @@
 	u8 dialog_token_allocator;
 };
 
-/*
- * struct ieee80211_tx_latency_stat - Tx latency statistics
- *
- * Measures TX latency and jitter for a station per TID.
- *
- * @max: worst case latency
- * @sum: sum of all latencies
- * @counter: amount of Tx frames sent from interface
- * @bins: each bin counts how many frames transmitted within a certain
- * latency range. when disabled it is NULL.
- * @bin_count: amount of bins.
- */
-struct ieee80211_tx_latency_stat {
-	u32 max;
-	u32 sum;
-	u32 counter;
-	u32 *bins;
-	u32 bin_count;
-};
 
 /* Value to indicate no TID reservation */
 #define IEEE80211_TID_UNRESERVED	0xff
@@ -265,7 +249,7 @@
  *
  * @list: global linked list entry
  * @free_list: list entry for keeping track of stations to free
- * @hnext: hash table linked list pointer
+ * @hash_node: hash node for rhashtable
  * @local: pointer to the global information
  * @sdata: virtual interface this station belongs to
  * @ptk: peer keys negotiated with this station, if any
@@ -293,6 +277,7 @@
  *	entered power saving state, these are also delivered to
  *	the station when it leaves powersave or polls for frames
  * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on
+ * @txq_buffered_tids: bitmap of TIDs that mac80211 has txq data buffered on
  * @rx_packets: Number of MSDUs received from this STA
  * @rx_bytes: Number of bytes received from this STA
  * @last_rx: time (in jiffies) when last frame was received from this STA
@@ -314,7 +299,6 @@
  * @tid_seq: per-TID sequence numbers for sending to this STA
  * @ampdu_mlme: A-MPDU state machine state
  * @timer_to_tid: identity mapping to ID timers
- * @tx_lat: Tx latency statistics
  * @llid: Local link ID
  * @plid: Peer link ID
  * @reason: Cancel reason on PLINK_HOLDING state
@@ -346,12 +330,20 @@
  * @cipher_scheme: optional cipher scheme for this station
  * @last_tdls_pkt_time: holds the time in jiffies of last TDLS pkt ACKed
  * @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED)
+ * @tx_msdu: MSDUs transmitted to this station, using IEEE80211_NUM_TID
+ *	entry for non-QoS frames
+ * @tx_msdu_retries: MSDU retries for transmissions to to this station,
+ *	using IEEE80211_NUM_TID entry for non-QoS frames
+ * @tx_msdu_failed: MSDU failures for transmissions to to this station,
+ *	using IEEE80211_NUM_TID entry for non-QoS frames
+ * @rx_msdu: MSDUs received from this station, using IEEE80211_NUM_TID
+ *	entry for non-QoS frames
  */
 struct sta_info {
 	/* General information, mostly static */
 	struct list_head list, free_list;
 	struct rcu_head rcu_head;
-	struct sta_info __rcu *hnext;
+	struct rhash_head hash_node;
 	struct ieee80211_local *local;
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
@@ -380,6 +372,7 @@
 	struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS];
 	struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS];
 	unsigned long driver_buffered_tids;
+	unsigned long txq_buffered_tids;
 
 	/* Updated from RX path only, no locking requirements */
 	unsigned long rx_packets;
@@ -416,6 +409,10 @@
 	u32 last_rx_rate_vht_flag;
 	u8 last_rx_rate_vht_nss;
 	u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
+	u64 tx_msdu[IEEE80211_NUM_TIDS + 1];
+	u64 tx_msdu_retries[IEEE80211_NUM_TIDS + 1];
+	u64 tx_msdu_failed[IEEE80211_NUM_TIDS + 1];
+	u64 rx_msdu[IEEE80211_NUM_TIDS + 1];
 
 	/*
 	 * Aggregation information, locked with lock.
@@ -423,8 +420,6 @@
 	struct sta_ampdu_mlme ampdu_mlme;
 	u8 timer_to_tid[IEEE80211_NUM_TIDS];
 
-	struct ieee80211_tx_latency_stat *tx_lat;
-
 #ifdef CPTCFG_MAC80211_MESH
 	/*
 	 * Mesh peer link attributes
@@ -545,10 +540,6 @@
 					 lockdep_is_held(&sta->ampdu_mlme.mtx));
 }
 
-#define STA_HASH_SIZE 256
-#define STA_HASH(sta) (sta[5])
-
-
 /* Maximum number of frames to buffer per power saving station per AC */
 #define STA_MAX_TX_BUFFER	64
 
@@ -569,26 +560,15 @@
 struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
 				  const u8 *addr);
 
-static inline
-void for_each_sta_info_type_check(struct ieee80211_local *local,
-				  const u8 *addr,
-				  struct sta_info *sta,
-				  struct sta_info *nxt)
-{
-}
+u32 sta_addr_hash(const void *key, u32 length, u32 seed);
 
-#define for_each_sta_info(local, _addr, _sta, nxt)			\
-	for (	/* initialise loop */					\
-		_sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\
-		nxt = _sta ? rcu_dereference(_sta->hnext) : NULL;	\
-		/* typecheck */						\
-		for_each_sta_info_type_check(local, (_addr), _sta, nxt),\
-		/* continue condition */				\
-		_sta;							\
-		/* advance loop */					\
-		_sta = nxt,						\
-		nxt = _sta ? rcu_dereference(_sta->hnext) : NULL	\
-	     )								\
+#define _sta_bucket_idx(_tbl, _a)					\
+	rht_bucket_index(_tbl, sta_addr_hash(_a, ETH_ALEN, (_tbl)->hash_rnd))
+
+#define for_each_sta_info(local, tbl, _addr, _sta, _tmp)		\
+	rht_for_each_entry_rcu(_sta, _tmp, tbl, 			\
+			       _sta_bucket_idx(tbl, _addr),		\
+			       hash_node)				\
 	/* compare address and run code only if it matches */		\
 	if (ether_addr_equal(_sta->sta.addr, (_addr)))
 
@@ -625,7 +605,7 @@
 
 void sta_info_recalc_tim(struct sta_info *sta);
 
-void sta_info_init(struct ieee80211_local *local);
+int sta_info_init(struct ieee80211_local *local);
 void sta_info_stop(struct ieee80211_local *local);
 
 /**
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index bb146f3..005fdbe 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -12,7 +12,6 @@
 
 #include <linux/export.h>
 #include <linux/etherdevice.h>
-#include <linux/time.h>
 #include <net/mac80211.h>
 #include <asm/unaligned.h>
 #include "ieee80211_i.h"
@@ -515,73 +514,6 @@
 }
 
 /*
- * Measure Tx frame completion and removal time for Tx latency statistics
- * calculation. A single Tx frame latency should be measured from when it
- * is entering the Kernel until we receive Tx complete confirmation indication
- * and remove the skb.
- */
-static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
-					    struct sk_buff *skb,
-					    struct sta_info *sta,
-					    struct ieee80211_hdr *hdr)
-{
-	u32 msrmnt;
-	u16 tid;
-	u8 *qc;
-	int i, bin_range_count;
-	u32 *bin_ranges;
-	__le16 fc;
-	struct ieee80211_tx_latency_stat *tx_lat;
-	struct ieee80211_tx_latency_bin_ranges *tx_latency;
-	ktime_t skb_arv = skb->tstamp;
-
-	tx_latency = rcu_dereference(local->tx_latency);
-
-	/* assert Tx latency stats are enabled & frame arrived when enabled */
-	if (!tx_latency || !ktime_to_ns(skb_arv))
-		return;
-
-	fc = hdr->frame_control;
-
-	if (!ieee80211_is_data(fc)) /* make sure it is a data frame */
-		return;
-
-	/* get frame tid */
-	if (ieee80211_is_data_qos(hdr->frame_control)) {
-		qc = ieee80211_get_qos_ctl(hdr);
-		tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
-	} else {
-		tid = 0;
-	}
-
-	tx_lat = &sta->tx_lat[tid];
-
-	/* Calculate the latency */
-	msrmnt = ktime_to_ms(ktime_sub(ktime_get(), skb_arv));
-
-	if (tx_lat->max < msrmnt) /* update stats */
-		tx_lat->max = msrmnt;
-	tx_lat->counter++;
-	tx_lat->sum += msrmnt;
-
-	if (!tx_lat->bins) /* bins not activated */
-		return;
-
-	/* count how many Tx frames transmitted with the appropriate latency */
-	bin_range_count = tx_latency->n_ranges;
-	bin_ranges = tx_latency->ranges;
-
-	for (i = 0; i < bin_range_count; i++) {
-		if (msrmnt <= bin_ranges[i]) {
-			tx_lat->bins[i]++;
-			break;
-		}
-	}
-	if (i == bin_range_count) /* msrmnt is bigger than the biggest range */
-		tx_lat->bins[i]++;
-}
-
-/*
  * Use a static threshold for now, best value to be determined
  * by testing ...
  * Should it depend on:
@@ -664,13 +596,15 @@
 	struct ieee80211_supported_band *sband;
 	int retry_count;
 	int rates_idx;
-	bool acked;
+	bool acked, noack_success;
 
 	rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
 
 	sband = hw->wiphy->bands[info->band];
 
 	acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
+	noack_success = !!(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED);
+
 	if (pubsta) {
 		struct sta_info *sta;
 
@@ -696,7 +630,7 @@
 		rate_control_tx_status_noskb(local, sband, sta, info);
 	}
 
-	if (acked) {
+	if (acked || noack_success) {
 		    local->dot11TransmittedFrameCount++;
 		    if (!pubsta)
 			    local->dot11MulticastTransmittedFrameCount++;
@@ -720,7 +654,8 @@
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_sub_if_data *sdata;
 	struct net_device *prev_dev = NULL;
-	struct sta_info *sta, *tmp;
+	struct sta_info *sta;
+	struct rhash_head *tmp;
 	int retry_count;
 	int rates_idx;
 	bool send_to_cooked;
@@ -728,6 +663,8 @@
 	struct ieee80211_bar *bar;
 	int rtap_len;
 	int shift = 0;
+	int tid = IEEE80211_NUM_TIDS;
+	const struct bucket_table *tbl;
 
 	rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
 
@@ -736,7 +673,9 @@
 	sband = local->hw.wiphy->bands[info->band];
 	fc = hdr->frame_control;
 
-	for_each_sta_info(local, hdr->addr1, sta, tmp) {
+	tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
+
+	for_each_sta_info(local, tbl, hdr->addr1, sta, tmp) {
 		/* skip wrong virtual interface */
 		if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
 			continue;
@@ -771,7 +710,7 @@
 
 		if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
 		    (ieee80211_is_data_qos(fc))) {
-			u16 tid, ssn;
+			u16 ssn;
 			u8 *qc;
 
 			qc = ieee80211_get_qos_ctl(hdr);
@@ -780,10 +719,14 @@
 						& IEEE80211_SCTL_SEQ);
 			ieee80211_send_bar(&sta->sdata->vif, hdr->addr1,
 					   tid, ssn);
+		} else if (ieee80211_is_data_qos(fc)) {
+			u8 *qc = ieee80211_get_qos_ctl(hdr);
+
+			tid = qc[0] & 0xf;
 		}
 
 		if (!acked && ieee80211_is_back_req(fc)) {
-			u16 tid, control;
+			u16 control;
 
 			/*
 			 * BAR failed, store the last SSN and retry sending
@@ -811,6 +754,12 @@
 			if (!acked)
 				sta->tx_retry_failed++;
 			sta->tx_retry_count += retry_count;
+
+			if (ieee80211_is_data_present(fc)) {
+				if (!acked)
+					sta->tx_msdu_failed[tid]++;
+				sta->tx_msdu_retries[tid] += retry_count;
+			}
 		}
 
 		rate_control_tx_status(local, sband, sta, skb);
@@ -840,12 +789,6 @@
 
 		if (acked)
 			sta->last_ack_signal = info->status.ack_signal;
-
-		/*
-		 * Measure frame removal for tx latency
-		 * statistics calculation
-		 */
-		ieee80211_tx_latency_end_msrmnt(local, skb, sta, hdr);
 	}
 
 	rcu_read_unlock();
@@ -856,10 +799,11 @@
 	 * Fragments are passed to low-level drivers as separate skbs, so these
 	 * are actually fragments, not frames. Update frame counters only for
 	 * the first fragment of the frame. */
-	if (info->flags & IEEE80211_TX_STAT_ACK) {
+	if ((info->flags & IEEE80211_TX_STAT_ACK) ||
+	    (info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED)) {
 		if (ieee80211_is_first_frag(hdr->seq_ctrl)) {
 			local->dot11TransmittedFrameCount++;
-			if (is_multicast_ether_addr(hdr->addr1))
+			if (is_multicast_ether_addr(ieee80211_get_DA(hdr)))
 				local->dot11MulticastTransmittedFrameCount++;
 			if (retry_count > 0)
 				local->dot11RetryCount++;
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 55ddd77..fff0d86 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -68,17 +68,24 @@
 		ch = ieee80211_get_channel(sdata->local->hw.wiphy, i);
 		if (ch) {
 			/* we will be active on the channel */
-			u32 flags = IEEE80211_CHAN_DISABLED |
-				    IEEE80211_CHAN_NO_IR;
 			cfg80211_chandef_create(&chandef, ch,
-						NL80211_CHAN_HT20);
-			if (cfg80211_chandef_usable(sdata->local->hw.wiphy,
-						    &chandef, flags)) {
+						NL80211_CHAN_NO_HT);
+			if (cfg80211_reg_can_beacon(sdata->local->hw.wiphy,
+						    &chandef,
+						    sdata->wdev.iftype)) {
 				ch_cnt++;
+				/*
+				 * check if the next channel is also part of
+				 * this allowed range
+				 */
 				continue;
 			}
 		}
 
+		/*
+		 * we've reached the end of a range, with allowed channels
+		 * found
+		 */
 		if (ch_cnt) {
 			u8 *pos = skb_put(skb, 2);
 			*pos++ = ieee80211_frequency_to_channel(subband_start);
@@ -89,6 +96,15 @@
 		}
 	}
 
+	/* all channels in the requested range are allowed - add them here */
+	if (ch_cnt) {
+		u8 *pos = skb_put(skb, 2);
+		*pos++ = ieee80211_frequency_to_channel(subband_start);
+		*pos++ = ch_cnt;
+
+		subband_cnt++;
+	}
+
 	return subband_cnt;
 }
 
@@ -120,6 +136,24 @@
 	*pos = 2 * subband_cnt;
 }
 
+static void ieee80211_tdls_add_oper_classes(struct ieee80211_sub_if_data *sdata,
+					    struct sk_buff *skb)
+{
+	u8 *pos;
+	u8 op_class;
+
+	if (!ieee80211_chandef_to_operating_class(&sdata->vif.bss_conf.chandef,
+						  &op_class))
+		return;
+
+	pos = skb_put(skb, 4);
+	*pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES;
+	*pos++ = 2; /* len */
+
+	*pos++ = op_class;
+	*pos++ = op_class; /* give current operating class as alternate too */
+}
+
 static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb)
 {
 	u8 *pos = (void *)skb_put(skb, 3);
@@ -177,6 +211,17 @@
 	memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
 }
 
+static void
+ieee80211_tdls_add_aid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+{
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+	u8 *pos = (void *)skb_put(skb, 4);
+
+	*pos++ = WLAN_EID_AID;
+	*pos++ = 2; /* len */
+	put_unaligned_le16(ifmgd->aid, pos);
+}
+
 /* translate numbering in the WMM parameter IE to the mac80211 notation */
 static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
 {
@@ -255,21 +300,11 @@
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_sta_ht_cap ht_cap;
+	struct ieee80211_sta_vht_cap vht_cap;
 	struct sta_info *sta = NULL;
 	size_t offset = 0, noffset;
 	u8 *pos;
 
-	rcu_read_lock();
-
-	/* we should have the peer STA if we're already responding */
-	if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
-		sta = sta_info_get(sdata, peer);
-		if (WARN_ON_ONCE(!sta)) {
-			rcu_read_unlock();
-			return;
-		}
-	}
-
 	ieee80211_add_srates_ie(sdata, skb, false, band);
 	ieee80211_add_ext_srates_ie(sdata, skb, false, band);
 	ieee80211_tdls_add_supp_channels(sdata, skb);
@@ -322,6 +357,19 @@
 		offset = noffset;
 	}
 
+	rcu_read_lock();
+
+	/* we should have the peer STA if we're already responding */
+	if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+		sta = sta_info_get(sdata, peer);
+		if (WARN_ON_ONCE(!sta)) {
+			rcu_read_unlock();
+			return;
+		}
+	}
+
+	ieee80211_tdls_add_oper_classes(sdata, skb);
+
 	/*
 	 * with TDLS we can switch channels, and HT-caps are not necessarily
 	 * the same on all bands. The specification limits the setup to a
@@ -329,35 +377,88 @@
 	 */
 	sband = local->hw.wiphy->bands[band];
 	memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+
 	if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
-	     action_code == WLAN_TDLS_SETUP_RESPONSE) &&
-	    ht_cap.ht_supported && (!sta || sta->sta.ht_cap.ht_supported)) {
-		if (action_code == WLAN_TDLS_SETUP_REQUEST) {
-			ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+	     action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) &&
+	    ht_cap.ht_supported) {
+		ieee80211_apply_htcap_overrides(sdata, &ht_cap);
 
-			/* disable SMPS in TDLS initiator */
-			ht_cap.cap |= (WLAN_HT_CAP_SM_PS_DISABLED
-				       << IEEE80211_HT_CAP_SM_PS_SHIFT);
-		} else {
-			/* disable SMPS in TDLS responder */
-			sta->sta.ht_cap.cap |=
-				(WLAN_HT_CAP_SM_PS_DISABLED
-				 << IEEE80211_HT_CAP_SM_PS_SHIFT);
+		/* disable SMPS in TDLS initiator */
+		ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
+				<< IEEE80211_HT_CAP_SM_PS_SHIFT;
 
-			/* the peer caps are already intersected with our own */
-			memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
-		}
+		pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+		ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
+	} else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
+		   ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
+		/* disable SMPS in TDLS responder */
+		sta->sta.ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
+					<< IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+		/* the peer caps are already intersected with our own */
+		memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
 
 		pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
 		ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
 	}
 
-	rcu_read_unlock();
-
 	if (ht_cap.ht_supported &&
 	    (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
 		ieee80211_tdls_add_bss_coex_ie(skb);
 
+	ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+
+	/* add any custom IEs that go before VHT capabilities */
+	if (extra_ies_len) {
+		static const u8 before_vht_cap[] = {
+			WLAN_EID_SUPP_RATES,
+			WLAN_EID_COUNTRY,
+			WLAN_EID_EXT_SUPP_RATES,
+			WLAN_EID_SUPPORTED_CHANNELS,
+			WLAN_EID_RSN,
+			WLAN_EID_EXT_CAPABILITY,
+			WLAN_EID_QOS_CAPA,
+			WLAN_EID_FAST_BSS_TRANSITION,
+			WLAN_EID_TIMEOUT_INTERVAL,
+			WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+			WLAN_EID_MULTI_BAND,
+		};
+		noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+					     before_vht_cap,
+					     ARRAY_SIZE(before_vht_cap),
+					     offset);
+		pos = skb_put(skb, noffset - offset);
+		memcpy(pos, extra_ies + offset, noffset - offset);
+		offset = noffset;
+	}
+
+	/* build the VHT-cap similarly to the HT-cap */
+	memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+	if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
+	     action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) &&
+	    vht_cap.vht_supported) {
+		ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);
+
+		/* the AID is present only when VHT is implemented */
+		if (action_code == WLAN_TDLS_SETUP_REQUEST)
+			ieee80211_tdls_add_aid(sdata, skb);
+
+		pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
+		ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
+	} else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
+		   vht_cap.vht_supported && sta->sta.vht_cap.vht_supported) {
+		/* the peer caps are already intersected with our own */
+		memcpy(&vht_cap, &sta->sta.vht_cap, sizeof(vht_cap));
+
+		/* the AID is present only when VHT is implemented */
+		ieee80211_tdls_add_aid(sdata, skb);
+
+		pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
+		ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
+	}
+
+	rcu_read_unlock();
+
 	/* add any remaining IEs */
 	if (extra_ies_len) {
 		noffset = extra_ies_len;
@@ -365,7 +466,6 @@
 		memcpy(pos, extra_ies + offset, noffset - offset);
 	}
 
-	ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
 }
 
 static void
@@ -378,6 +478,7 @@
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	size_t offset = 0, noffset;
 	struct sta_info *sta, *ap_sta;
+	enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
 	u8 *pos;
 
 	rcu_read_lock();
@@ -437,6 +538,21 @@
 		}
 	}
 
+	ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+
+	/* only include VHT-operation if not on the 2.4GHz band */
+	if (band != IEEE80211_BAND_2GHZ && !ap_sta->sta.vht_cap.vht_supported &&
+	    sta->sta.vht_cap.vht_supported) {
+		struct ieee80211_chanctx_conf *chanctx_conf =
+				rcu_dereference(sdata->vif.chanctx_conf);
+		if (!WARN_ON(!chanctx_conf)) {
+			pos = skb_put(skb, 2 +
+				      sizeof(struct ieee80211_vht_operation));
+			ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap,
+						    &chanctx_conf->def);
+		}
+	}
+
 	rcu_read_unlock();
 
 	/* add any remaining IEs */
@@ -445,8 +561,6 @@
 		pos = skb_put(skb, noffset - offset);
 		memcpy(pos, extra_ies + offset, noffset - offset);
 	}
-
-	ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
 }
 
 static void
@@ -692,8 +806,12 @@
 			       26 + /* max(WMM-info, WMM-param) */
 			       2 + max(sizeof(struct ieee80211_ht_cap),
 				       sizeof(struct ieee80211_ht_operation)) +
+			       2 + max(sizeof(struct ieee80211_vht_cap),
+				       sizeof(struct ieee80211_vht_operation)) +
 			       50 + /* supported channels */
 			       3 + /* 40/20 BSS coex */
+			       4 + /* AID */
+			       4 + /* oper classes */
 			       extra_ies_len +
 			       sizeof(struct ieee80211_tdls_lnkie));
 	if (!skb)
@@ -836,7 +954,6 @@
 	 */
 	if ((action_code == WLAN_TDLS_TEARDOWN) &&
 	    (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
-		struct sta_info *sta = NULL;
 		bool try_resend; /* Should we keep skb for possible resend */
 
 		/* If not sending directly to peer - no point in keeping skb */
@@ -892,7 +1009,7 @@
 	if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
 	    !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
 		ret = -EBUSY;
-		goto exit;
+		goto out_unlock;
 	}
 
 	/*
@@ -907,27 +1024,34 @@
 		if (!sta_info_get(sdata, peer)) {
 			rcu_read_unlock();
 			ret = -ENOLINK;
-			goto exit;
+			goto out_unlock;
 		}
 		rcu_read_unlock();
 	}
 
-	ieee80211_flush_queues(local, sdata);
+	ieee80211_flush_queues(local, sdata, false);
+	memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
+	mutex_unlock(&local->mtx);
 
+	/* we cannot take the mutex while preparing the setup packet */
 	ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
 					      dialog_token, status_code,
 					      peer_capability, initiator,
 					      extra_ies, extra_ies_len, 0,
 					      NULL);
-	if (ret < 0)
-		goto exit;
+	if (ret < 0) {
+		mutex_lock(&local->mtx);
+		eth_zero_addr(sdata->u.mgd.tdls_peer);
+		mutex_unlock(&local->mtx);
+		return ret;
+	}
 
-	memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
 	ieee80211_queue_delayed_work(&sdata->local->hw,
 				     &sdata->u.mgd.tdls_peer_del_work,
 				     TDLS_PEER_SETUP_TIMEOUT);
+	return 0;
 
-exit:
+out_unlock:
 	mutex_unlock(&local->mtx);
 	return ret;
 }
@@ -952,7 +1076,7 @@
 	 */
 	ieee80211_stop_vif_queues(local, sdata,
 				  IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
-	ieee80211_flush_queues(local, sdata);
+	ieee80211_flush_queues(local, sdata, false);
 
 	ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
 					      dialog_token, status_code,
@@ -1098,7 +1222,7 @@
 		 */
 		tasklet_kill(&local->tx_pending_tasklet);
 		/* flush a potentially queued teardown packet */
-		ieee80211_flush_queues(local, sdata);
+		ieee80211_flush_queues(local, sdata, false);
 
 		ret = sta_info_destroy_addr(sdata, peer);
 		break;
diff --git a/net/mac80211/trace.c b/net/mac80211/trace.c
index a25d60e..4c0e823 100644
--- a/net/mac80211/trace.c
+++ b/net/mac80211/trace.c
@@ -8,6 +8,7 @@
 #include "debug.h"
 #define CREATE_TRACE_POINTS
 #include "trace.h"
+#include "trace_msg.h"
 
 #ifdef CPTCFG_MAC80211_MESSAGE_TRACING
 void __sdata_info(const char *fmt, ...)
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 3991437..4c2e769 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -825,6 +825,13 @@
 	)
 );
 
+DEFINE_EVENT(sta_event, drv_sta_statistics,
+	TP_PROTO(struct ieee80211_local *local,
+		 struct ieee80211_sub_if_data *sdata,
+		 struct ieee80211_sta *sta),
+	TP_ARGS(local, sdata, sta)
+);
+
 DEFINE_EVENT(sta_event, drv_sta_add,
 	TP_PROTO(struct ieee80211_local *local,
 		 struct ieee80211_sub_if_data *sdata,
@@ -1249,28 +1256,28 @@
 		  LOCAL_PR_ARG, VIF_PR_ARG)
 );
 
-TRACE_EVENT(drv_rssi_callback,
+TRACE_EVENT(drv_event_callback,
 	TP_PROTO(struct ieee80211_local *local,
 		 struct ieee80211_sub_if_data *sdata,
-		 enum ieee80211_rssi_event rssi_event),
+		 const struct ieee80211_event *_event),
 
-	TP_ARGS(local, sdata, rssi_event),
+	TP_ARGS(local, sdata, _event),
 
 	TP_STRUCT__entry(
 		LOCAL_ENTRY
 		VIF_ENTRY
-		__field(u32, rssi_event)
+		__field(u32, type)
 	),
 
 	TP_fast_assign(
 		LOCAL_ASSIGN;
 		VIF_ASSIGN;
-		__entry->rssi_event = rssi_event;
+		__entry->type = _event->type;
 	),
 
 	TP_printk(
-		LOCAL_PR_FMT VIF_PR_FMT " rssi_event:%d",
-		LOCAL_PR_ARG, VIF_PR_ARG, __entry->rssi_event
+		LOCAL_PR_FMT VIF_PR_FMT " event:%d",
+		LOCAL_PR_ARG, VIF_PR_ARG, __entry->type
 	)
 );
 
@@ -1329,32 +1336,6 @@
 	TP_ARGS(local, sta, tids, num_frames, reason, more_data)
 );
 
-TRACE_EVENT(drv_get_rssi,
-	TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta,
-		 s8 rssi, int ret),
-
-	TP_ARGS(local, sta, rssi, ret),
-
-	TP_STRUCT__entry(
-		LOCAL_ENTRY
-		STA_ENTRY
-		__field(s8, rssi)
-		__field(int, ret)
-	),
-
-	TP_fast_assign(
-		LOCAL_ASSIGN;
-		STA_ASSIGN;
-		__entry->rssi = rssi;
-		__entry->ret = ret;
-	),
-
-	TP_printk(
-		LOCAL_PR_FMT STA_PR_FMT " rssi:%d ret:%d",
-		LOCAL_PR_ARG, STA_PR_ARG, __entry->rssi, __entry->ret
-	)
-);
-
 DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx,
 	TP_PROTO(struct ieee80211_local *local,
 		 struct ieee80211_sub_if_data *sdata),
@@ -2331,44 +2312,37 @@
 	)
 );
 
-#ifdef CPTCFG_MAC80211_MESSAGE_TRACING
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM mac80211_msg
+TRACE_EVENT(drv_wake_tx_queue,
+	TP_PROTO(struct ieee80211_local *local,
+		 struct ieee80211_sub_if_data *sdata,
+		 struct txq_info *txq),
 
-#define MAX_MSG_LEN	100
-
-DECLARE_EVENT_CLASS(mac80211_msg_event,
-	TP_PROTO(struct va_format *vaf),
-
-	TP_ARGS(vaf),
+	TP_ARGS(local, sdata, txq),
 
 	TP_STRUCT__entry(
-		__dynamic_array(char, msg, MAX_MSG_LEN)
+		LOCAL_ENTRY
+		VIF_ENTRY
+		STA_ENTRY
+		__field(u8, ac)
+		__field(u8, tid)
 	),
 
 	TP_fast_assign(
-		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
-				       MAX_MSG_LEN, vaf->fmt,
-				       *vaf->va) >= MAX_MSG_LEN);
+		struct ieee80211_sta *sta = txq->txq.sta;
+
+		LOCAL_ASSIGN;
+		VIF_ASSIGN;
+		STA_ASSIGN;
+		__entry->ac = txq->txq.ac;
+		__entry->tid = txq->txq.tid;
 	),
 
-	TP_printk("%s", __get_str(msg))
+	TP_printk(
+		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT " ac:%d tid:%d",
+		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ac, __entry->tid
+	)
 );
 
-DEFINE_EVENT(mac80211_msg_event, mac80211_info,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf)
-);
-DEFINE_EVENT(mac80211_msg_event, mac80211_dbg,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf)
-);
-DEFINE_EVENT(mac80211_msg_event, mac80211_err,
-	TP_PROTO(struct va_format *vaf),
-	TP_ARGS(vaf)
-);
-#endif
-
 #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH
diff --git a/net/mac80211/trace_msg.h b/net/mac80211/trace_msg.h
new file mode 100644
index 0000000..9c58126
--- /dev/null
+++ b/net/mac80211/trace_msg.h
@@ -0,0 +1,53 @@
+#ifdef CPTCFG_MAC80211_MESSAGE_TRACING
+
+#if !defined(__MAC80211_MSG_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __MAC80211_MSG_DRIVER_TRACE
+
+#include <linux/tracepoint.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mac80211_msg
+
+#define MAX_MSG_LEN	100
+
+DECLARE_EVENT_CLASS(mac80211_msg_event,
+	TP_PROTO(struct va_format *vaf),
+
+	TP_ARGS(vaf),
+
+	TP_STRUCT__entry(
+		__dynamic_array(char, msg, MAX_MSG_LEN)
+	),
+
+	TP_fast_assign(
+		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+				       MAX_MSG_LEN, vaf->fmt,
+				       *vaf->va) >= MAX_MSG_LEN);
+	),
+
+	TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(mac80211_msg_event, mac80211_info,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+DEFINE_EVENT(mac80211_msg_event, mac80211_dbg,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+DEFINE_EVENT(mac80211_msg_event, mac80211_err,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+#endif /* !__MAC80211_MSG_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace_msg
+#include <trace/define_trace.h>
+
+#endif
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 002f28d..d6bdd3f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -20,7 +20,6 @@
 #include <linux/bitmap.h>
 #include <linux/rcupdate.h>
 #include <linux/export.h>
-#include <linux/time.h>
 #include <net/net_namespace.h>
 #include <net/ieee80211_radiotap.h>
 #include <net/cfg80211.h>
@@ -566,6 +565,7 @@
 		if (tx->sdata->control_port_no_encrypt)
 			info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
 		info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
+		info->flags |= IEEE80211_TX_CTL_USE_MINRATE;
 	}
 
 	return TX_CONTINUE;
@@ -594,23 +594,8 @@
 	else if (!is_multicast_ether_addr(hdr->addr1) &&
 		 (key = rcu_dereference(tx->sdata->default_unicast_key)))
 		tx->key = key;
-	else if (info->flags & IEEE80211_TX_CTL_INJECTED)
+	else
 		tx->key = NULL;
-	else if (!tx->sdata->drop_unencrypted)
-		tx->key = NULL;
-	else if (tx->skb->protocol == tx->sdata->control_port_protocol)
-		tx->key = NULL;
-	else if (ieee80211_is_robust_mgmt_frame(tx->skb) &&
-		 !(ieee80211_is_action(hdr->frame_control) &&
-		   tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))
-		tx->key = NULL;
-	else if (ieee80211_is_mgmt(hdr->frame_control) &&
-		 !ieee80211_is_robust_mgmt_frame(tx->skb))
-		tx->key = NULL;
-	else {
-		I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
-		return TX_DROP;
-	}
 
 	if (tx->key) {
 		bool skip_hw = false;
@@ -626,6 +611,9 @@
 				tx->key = NULL;
 			break;
 		case WLAN_CIPHER_SUITE_CCMP:
+		case WLAN_CIPHER_SUITE_CCMP_256:
+		case WLAN_CIPHER_SUITE_GCMP:
+		case WLAN_CIPHER_SUITE_GCMP_256:
 			if (!ieee80211_is_data_present(hdr->frame_control) &&
 			    !ieee80211_use_mfp(hdr->frame_control, tx->sta,
 					       tx->skb))
@@ -636,6 +624,9 @@
 					ieee80211_is_mgmt(hdr->frame_control);
 			break;
 		case WLAN_CIPHER_SUITE_AES_CMAC:
+		case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+		case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+		case WLAN_CIPHER_SUITE_BIP_GMAC_256:
 			if (!ieee80211_is_mgmt(hdr->frame_control))
 				tx->key = NULL;
 			break;
@@ -776,12 +767,22 @@
 	return TX_CONTINUE;
 }
 
+static __le16 ieee80211_tx_next_seq(struct sta_info *sta, int tid)
+{
+	u16 *seq = &sta->tid_seq[tid];
+	__le16 ret = cpu_to_le16(*seq);
+
+	/* Increase the sequence number. */
+	*seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ;
+
+	return ret;
+}
+
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
-	u16 *seq;
 	u8 *qc;
 	int tid;
 
@@ -815,6 +816,8 @@
 		/* for pure STA mode without beacons, we can do it */
 		hdr->seq_ctrl = cpu_to_le16(tx->sdata->sequence_number);
 		tx->sdata->sequence_number += 0x10;
+		if (tx->sta)
+			tx->sta->tx_msdu[IEEE80211_NUM_TIDS]++;
 		return TX_CONTINUE;
 	}
 
@@ -830,12 +833,10 @@
 
 	qc = ieee80211_get_qos_ctl(hdr);
 	tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
-	seq = &tx->sta->tid_seq[tid];
+	tx->sta->tx_msdu[tid]++;
 
-	hdr->seq_ctrl = cpu_to_le16(*seq);
-
-	/* Increase the sequence number. */
-	*seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ;
+	if (!tx->sta->sta.txq[0])
+		hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
 
 	return TX_CONTINUE;
 }
@@ -1008,9 +1009,21 @@
 	case WLAN_CIPHER_SUITE_TKIP:
 		return ieee80211_crypto_tkip_encrypt(tx);
 	case WLAN_CIPHER_SUITE_CCMP:
-		return ieee80211_crypto_ccmp_encrypt(tx);
+		return ieee80211_crypto_ccmp_encrypt(
+			tx, IEEE80211_CCMP_MIC_LEN);
+	case WLAN_CIPHER_SUITE_CCMP_256:
+		return ieee80211_crypto_ccmp_encrypt(
+			tx, IEEE80211_CCMP_256_MIC_LEN);
 	case WLAN_CIPHER_SUITE_AES_CMAC:
 		return ieee80211_crypto_aes_cmac_encrypt(tx);
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+		return ieee80211_crypto_aes_cmac_256_encrypt(tx);
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		return ieee80211_crypto_aes_gmac_encrypt(tx);
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		return ieee80211_crypto_gcmp_encrypt(tx);
 	default:
 		return ieee80211_crypto_hw_encrypt(tx);
 	}
@@ -1064,7 +1077,7 @@
 		 * nothing -- this aggregation session is being started
 		 * but that might still fail with the driver
 		 */
-	} else {
+	} else if (!tx->sta->sta.txq[tid]) {
 		spin_lock(&tx->sta->lock);
 		/*
 		 * Need to re-check now, because we may get here
@@ -1115,11 +1128,13 @@
 
 /*
  * initialises @tx
+ * pass %NULL for the station if unknown, a valid pointer if known
+ * or an ERR_PTR() if the station is known not to exist
  */
 static ieee80211_tx_result
 ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
 		     struct ieee80211_tx_data *tx,
-		     struct sk_buff *skb)
+		     struct sta_info *sta, struct sk_buff *skb)
 {
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_hdr *hdr;
@@ -1142,17 +1157,22 @@
 
 	hdr = (struct ieee80211_hdr *) skb->data;
 
-	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
-		tx->sta = rcu_dereference(sdata->u.vlan.sta);
-		if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)
-			return TX_DROP;
-	} else if (info->flags & (IEEE80211_TX_CTL_INJECTED |
-				  IEEE80211_TX_INTFL_NL80211_FRAME_TX) ||
-		   tx->sdata->control_port_protocol == tx->skb->protocol) {
-		tx->sta = sta_info_get_bss(sdata, hdr->addr1);
+	if (likely(sta)) {
+		if (!IS_ERR(sta))
+			tx->sta = sta;
+	} else {
+		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+			tx->sta = rcu_dereference(sdata->u.vlan.sta);
+			if (!tx->sta && sdata->wdev.use_4addr)
+				return TX_DROP;
+		} else if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX |
+					  IEEE80211_TX_CTL_INJECTED) ||
+			   tx->sdata->control_port_protocol == tx->skb->protocol) {
+			tx->sta = sta_info_get_bss(sdata, hdr->addr1);
+		}
+		if (!tx->sta && !is_multicast_ether_addr(hdr->addr1))
+			tx->sta = sta_info_get(sdata, hdr->addr1);
 	}
-	if (!tx->sta)
-		tx->sta = sta_info_get(sdata, hdr->addr1);
 
 	if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
 	    !ieee80211_is_qos_nullfunc(hdr->frame_control) &&
@@ -1198,13 +1218,102 @@
 	return TX_CONTINUE;
 }
 
+static void ieee80211_drv_tx(struct ieee80211_local *local,
+			     struct ieee80211_vif *vif,
+			     struct ieee80211_sta *pubsta,
+			     struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_tx_control control = {
+		.sta = pubsta,
+	};
+	struct ieee80211_txq *txq = NULL;
+	struct txq_info *txqi;
+	u8 ac;
+
+	if (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE)
+		goto tx_normal;
+
+	if (!ieee80211_is_data(hdr->frame_control))
+		goto tx_normal;
+
+	if (pubsta) {
+		u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+
+		txq = pubsta->txq[tid];
+	} else if (vif) {
+		txq = vif->txq;
+	}
+
+	if (!txq)
+		goto tx_normal;
+
+	ac = txq->ac;
+	txqi = to_txq_info(txq);
+	atomic_inc(&sdata->txqs_len[ac]);
+	if (atomic_read(&sdata->txqs_len[ac]) >= local->hw.txq_ac_max_pending)
+		netif_stop_subqueue(sdata->dev, ac);
+
+	skb_queue_tail(&txqi->queue, skb);
+	drv_wake_tx_queue(local, txqi);
+
+	return;
+
+tx_normal:
+	drv_tx(local, &control, skb);
+}
+
+struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
+				     struct ieee80211_txq *txq)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
+	struct txq_info *txqi = container_of(txq, struct txq_info, txq);
+	struct ieee80211_hdr *hdr;
+	struct sk_buff *skb = NULL;
+	u8 ac = txq->ac;
+
+	spin_lock_bh(&txqi->queue.lock);
+
+	if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
+		goto out;
+
+	skb = __skb_dequeue(&txqi->queue);
+	if (!skb)
+		goto out;
+
+	atomic_dec(&sdata->txqs_len[ac]);
+	if (__netif_subqueue_stopped(sdata->dev, ac))
+		ieee80211_propagate_queue_wake(local, sdata->vif.hw_queue[ac]);
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+	if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+		struct sta_info *sta = container_of(txq->sta, struct sta_info,
+						    sta);
+		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+		hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid);
+		if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
+			info->flags |= IEEE80211_TX_CTL_AMPDU;
+		else
+			info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+	}
+
+out:
+	spin_unlock_bh(&txqi->queue.lock);
+
+	return skb;
+}
+EXPORT_SYMBOL(ieee80211_tx_dequeue);
+
 static bool ieee80211_tx_frags(struct ieee80211_local *local,
 			       struct ieee80211_vif *vif,
 			       struct ieee80211_sta *sta,
 			       struct sk_buff_head *skbs,
 			       bool txpending)
 {
-	struct ieee80211_tx_control control;
 	struct sk_buff *skb, *tmp;
 	unsigned long flags;
 
@@ -1262,10 +1371,9 @@
 		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 
 		info->control.vif = vif;
-		control.sta = sta;
 
 		__skb_unlink(skb, skbs);
-		drv_tx(local, &control, skb);
+		ieee80211_drv_tx(local, vif, sta, skb);
 	}
 
 	return true;
@@ -1400,8 +1508,9 @@
 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_tx_data tx;
+	struct sk_buff *skb2;
 
-	if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP)
+	if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP)
 		return false;
 
 	info->band = band;
@@ -1418,6 +1527,14 @@
 			*sta = NULL;
 	}
 
+	/* this function isn't suitable for fragmented data frames */
+	skb2 = __skb_dequeue(&tx.skbs);
+	if (WARN_ON(skb2 != skb || !skb_queue_empty(&tx.skbs))) {
+		ieee80211_free_txskb(hw, skb2);
+		ieee80211_purge_tx_queue(hw, &tx.skbs);
+		return false;
+	}
+
 	return true;
 }
 EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
@@ -1426,7 +1543,8 @@
  * Returns false if the frame couldn't be transmitted but was queued instead.
  */
 static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
-			 struct sk_buff *skb, bool txpending)
+			 struct sta_info *sta, struct sk_buff *skb,
+			 bool txpending)
 {
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_tx_data tx;
@@ -1442,7 +1560,7 @@
 
 	/* initialises tx */
 	led_len = skb->len;
-	res_prepare = ieee80211_tx_prepare(sdata, &tx, skb);
+	res_prepare = ieee80211_tx_prepare(sdata, &tx, sta, skb);
 
 	if (unlikely(res_prepare == TX_DROP)) {
 		ieee80211_free_txskb(&local->hw, skb);
@@ -1498,7 +1616,8 @@
 	return 0;
 }
 
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
+		    struct sta_info *sta, struct sk_buff *skb)
 {
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1533,7 +1652,7 @@
 	}
 
 	ieee80211_set_qos_hdr(sdata, skb);
-	ieee80211_tx(sdata, skb, false);
+	ieee80211_tx(sdata, sta, skb, false);
 }
 
 static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
@@ -1754,7 +1873,7 @@
 		goto fail_rcu;
 
 	info->band = chandef->chan->band;
-	ieee80211_xmit(sdata, skb);
+	ieee80211_xmit(sdata, NULL, skb);
 	rcu_read_unlock();
 
 	return NETDEV_TX_OK;
@@ -1766,21 +1885,89 @@
 	return NETDEV_TX_OK; /* meaning, we dealt with the skb */
 }
 
-/*
- * Measure Tx frame arrival time for Tx latency statistics calculation
- * A single Tx frame latency should be measured from when it is entering the
- * Kernel until we receive Tx complete confirmation indication and the skb is
- * freed.
- */
-static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
-					      struct sk_buff *skb)
+static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb)
 {
-	struct ieee80211_tx_latency_bin_ranges *tx_latency;
+	u16 ethertype = (skb->data[12] << 8) | skb->data[13];
 
-	tx_latency = rcu_dereference(local->tx_latency);
-	if (!tx_latency)
-		return;
-	skb->tstamp = ktime_get();
+	return ethertype == ETH_P_TDLS &&
+	       skb->len > 14 &&
+	       skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
+}
+
+static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
+				   struct sk_buff *skb,
+				   struct sta_info **sta_out)
+{
+	struct sta_info *sta;
+
+	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_AP_VLAN:
+		sta = rcu_dereference(sdata->u.vlan.sta);
+		if (sta) {
+			*sta_out = sta;
+			return 0;
+		} else if (sdata->wdev.use_4addr) {
+			return -ENOLINK;
+		}
+		/* fall through */
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_OCB:
+	case NL80211_IFTYPE_ADHOC:
+		if (is_multicast_ether_addr(skb->data)) {
+			*sta_out = ERR_PTR(-ENOENT);
+			return 0;
+		}
+		sta = sta_info_get_bss(sdata, skb->data);
+		break;
+	case NL80211_IFTYPE_WDS:
+		sta = sta_info_get(sdata, sdata->u.wds.remote_addr);
+		break;
+#ifdef CPTCFG_MAC80211_MESH
+	case NL80211_IFTYPE_MESH_POINT:
+		/* determined much later */
+		*sta_out = NULL;
+		return 0;
+#endif
+	case NL80211_IFTYPE_STATION:
+		if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
+			sta = sta_info_get(sdata, skb->data);
+			if (sta) {
+				bool tdls_peer, tdls_auth;
+
+				tdls_peer = test_sta_flag(sta,
+							  WLAN_STA_TDLS_PEER);
+				tdls_auth = test_sta_flag(sta,
+						WLAN_STA_TDLS_PEER_AUTH);
+
+				if (tdls_peer && tdls_auth) {
+					*sta_out = sta;
+					return 0;
+				}
+
+				/*
+				 * TDLS link during setup - throw out frames to
+				 * peer. Allow TDLS-setup frames to unauthorized
+				 * peers for the special case of a link teardown
+				 * after a TDLS sta is removed due to being
+				 * unreachable.
+				 */
+				if (tdls_peer && !tdls_auth &&
+				    !ieee80211_is_tdls_setup(skb))
+					return -EINVAL;
+			}
+
+		}
+
+		sta = sta_info_get(sdata, sdata->u.mgd.bssid);
+		if (!sta)
+			return -ENOLINK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*sta_out = sta ?: ERR_PTR(-ENOENT);
+	return 0;
 }
 
 /**
@@ -1802,7 +1989,8 @@
  * Returns: the (possibly reallocated) skb or an ERR_PTR() code
  */
 static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
-					   struct sk_buff *skb, u32 info_flags)
+					   struct sk_buff *skb, u32 info_flags,
+					   struct sta_info *sta)
 {
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_tx_info *info;
@@ -1815,9 +2003,8 @@
 	const u8 *encaps_data;
 	int encaps_len, skip_header_bytes;
 	int nh_pos, h_pos;
-	struct sta_info *sta = NULL;
-	bool wme_sta = false, authorized = false, tdls_auth = false;
-	bool tdls_peer = false, tdls_setup_frame = false;
+	bool wme_sta = false, authorized = false;
+	bool tdls_peer;
 	bool multicast;
 	u16 info_id = 0;
 	struct ieee80211_chanctx_conf *chanctx_conf;
@@ -1825,6 +2012,9 @@
 	enum ieee80211_band band;
 	int ret;
 
+	if (IS_ERR(sta))
+		sta = NULL;
+
 	/* convert Ethernet header to proper 802.11 header (based on
 	 * operation mode) */
 	ethertype = (skb->data[12] << 8) | skb->data[13];
@@ -1832,8 +2022,7 @@
 
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_AP_VLAN:
-		sta = rcu_dereference(sdata->u.vlan.sta);
-		if (sta) {
+		if (sdata->wdev.use_4addr) {
 			fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
 			/* RA TA DA SA */
 			memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN);
@@ -1852,7 +2041,7 @@
 			goto free;
 		}
 		band = chanctx_conf->def.chan->band;
-		if (sta)
+		if (sdata->wdev.use_4addr)
 			break;
 		/* fall through */
 	case NL80211_IFTYPE_AP:
@@ -1956,38 +2145,10 @@
 		break;
 #endif
 	case NL80211_IFTYPE_STATION:
-		if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
-			sta = sta_info_get(sdata, skb->data);
-			if (sta) {
-				authorized = test_sta_flag(sta,
-							WLAN_STA_AUTHORIZED);
-				wme_sta = sta->sta.wme;
-				tdls_peer = test_sta_flag(sta,
-							  WLAN_STA_TDLS_PEER);
-				tdls_auth = test_sta_flag(sta,
-						WLAN_STA_TDLS_PEER_AUTH);
-			}
+		/* we already did checks when looking up the RA STA */
+		tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER);
 
-			if (tdls_peer)
-				tdls_setup_frame =
-					ethertype == ETH_P_TDLS &&
-					skb->len > 14 &&
-					skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
-		}
-
-		/*
-		 * TDLS link during setup - throw out frames to peer. We allow
-		 * TDLS-setup frames to unauthorized peers for the special case
-		 * of a link teardown after a TDLS sta is removed due to being
-		 * unreachable.
-		 */
-		if (tdls_peer && !tdls_auth && !tdls_setup_frame) {
-			ret = -EINVAL;
-			goto free;
-		}
-
-		/* send direct packets to authorized TDLS peers */
-		if (tdls_peer && tdls_auth) {
+		if (tdls_peer) {
 			/* DA SA BSSID */
 			memcpy(hdr.addr1, skb->data, ETH_ALEN);
 			memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
@@ -2049,26 +2210,19 @@
 		goto free;
 	}
 
-	/*
-	 * There's no need to try to look up the destination
-	 * if it is a multicast address (which can only happen
-	 * in AP mode)
-	 */
 	multicast = is_multicast_ether_addr(hdr.addr1);
-	if (!multicast) {
-		sta = sta_info_get(sdata, hdr.addr1);
-		if (sta) {
-			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
-			wme_sta = sta->sta.wme;
-		}
+
+	/* sta is always NULL for mesh */
+	if (sta) {
+		authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+		wme_sta = sta->sta.wme;
+	} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+		/* For mesh, the use of the QoS header is mandatory */
+		wme_sta = true;
 	}
 
-	/* For mesh, the use of the QoS header is mandatory */
-	if (ieee80211_vif_is_mesh(&sdata->vif))
-		wme_sta = true;
-
-	/* receiver and we are QoS enabled, use a QoS type frame */
-	if (wme_sta && local->hw.queues >= IEEE80211_NUM_ACS) {
+	/* receiver does QoS (which also means we do) use it */
+	if (wme_sta) {
 		fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
 		hdrlen += 2;
 	}
@@ -2093,7 +2247,6 @@
 		goto free;
 	}
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
 	if (unlikely(!multicast && skb->sk &&
 		     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) {
 		struct sk_buff *ack_skb = skb_clone_sk(skb);
@@ -2115,7 +2268,6 @@
 			}
 		}
 	}
-#endif
 
 	/*
 	 * If the skb is shared we need to obtain our own copy.
@@ -2240,7 +2392,7 @@
 				  u32 info_flags)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	struct ieee80211_local *local = sdata->local;
+	struct sta_info *sta;
 
 	if (unlikely(skb->len < ETH_HLEN)) {
 		kfree_skb(skb);
@@ -2249,10 +2401,12 @@
 
 	rcu_read_lock();
 
-	/* Measure frame arrival for Tx latency statistics calculation */
-	ieee80211_tx_latency_start_msrmnt(local, skb);
+	if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+		kfree_skb(skb);
+		goto out;
+	}
 
-	skb = ieee80211_build_hdr(sdata, skb, info_flags);
+	skb = ieee80211_build_hdr(sdata, skb, info_flags, sta);
 	if (IS_ERR(skb))
 		goto out;
 
@@ -2260,7 +2414,7 @@
 	dev->stats.tx_bytes += skb->len;
 	dev->trans_start = jiffies;
 
-	ieee80211_xmit(sdata, skb);
+	ieee80211_xmit(sdata, sta, skb);
  out:
 	rcu_read_unlock();
 }
@@ -2288,10 +2442,17 @@
 		.local = sdata->local,
 		.sdata = sdata,
 	};
+	struct sta_info *sta;
 
 	rcu_read_lock();
 
-	skb = ieee80211_build_hdr(sdata, skb, info_flags);
+	if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+		kfree_skb(skb);
+		skb = ERR_PTR(-EINVAL);
+		goto out;
+	}
+
+	skb = ieee80211_build_hdr(sdata, skb, info_flags, sta);
 	if (IS_ERR(skb))
 		goto out;
 
@@ -2349,7 +2510,7 @@
 			return true;
 		}
 		info->band = chanctx_conf->def.chan->band;
-		result = ieee80211_tx(sdata, skb, true);
+		result = ieee80211_tx(sdata, NULL, skb, true);
 	} else {
 		struct sk_buff_head skbs;
 
@@ -3087,7 +3248,7 @@
 
 		if (sdata->vif.type == NL80211_IFTYPE_AP)
 			sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
-		if (!ieee80211_tx_prepare(sdata, &tx, skb))
+		if (!ieee80211_tx_prepare(sdata, &tx, NULL, skb))
 			break;
 		dev_kfree_skb_any(skb);
 	}
@@ -3154,7 +3315,7 @@
 	}
 
 	queues = BIT(sdata->vif.hw_queue[ieee802_1d_to_ac[tid]]);
-	__ieee80211_flush_queues(local, sdata, queues);
+	__ieee80211_flush_queues(local, sdata, queues, false);
 
 	sta->reserved_tid = tid;
 
@@ -3219,6 +3380,6 @@
 	 */
 	local_bh_disable();
 	IEEE80211_SKB_CB(skb)->band = band;
-	ieee80211_xmit(sdata, skb);
+	ieee80211_xmit(sdata, NULL, skb);
 	local_bh_enable();
 }
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 974ebe7..79412f1 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -308,6 +308,11 @@
 		for (ac = 0; ac < n_acs; ac++) {
 			int ac_queue = sdata->vif.hw_queue[ac];
 
+			if (local->ops->wake_tx_queue &&
+			    (atomic_read(&sdata->txqs_len[ac]) >
+			     local->hw.txq_ac_max_pending))
+				continue;
+
 			if (ac_queue == queue ||
 			    (sdata->vif.cab_queue == queue &&
 			     local->queue_stop_reasons[ac_queue] == 0 &&
@@ -578,7 +583,7 @@
 
 void __ieee80211_flush_queues(struct ieee80211_local *local,
 			      struct ieee80211_sub_if_data *sdata,
-			      unsigned int queues)
+			      unsigned int queues, bool drop)
 {
 	if (!local->ops->flush)
 		return;
@@ -594,7 +599,7 @@
 					IEEE80211_QUEUE_STOP_REASON_FLUSH,
 					false);
 
-	drv_flush(local, sdata, queues, false);
+	drv_flush(local, sdata, queues, drop);
 
 	ieee80211_wake_queues_by_reason(&local->hw, queues,
 					IEEE80211_QUEUE_STOP_REASON_FLUSH,
@@ -602,9 +607,9 @@
 }
 
 void ieee80211_flush_queues(struct ieee80211_local *local,
-			    struct ieee80211_sub_if_data *sdata)
+			    struct ieee80211_sub_if_data *sdata, bool drop)
 {
-	__ieee80211_flush_queues(local, sdata, 0);
+	__ieee80211_flush_queues(local, sdata, 0, drop);
 }
 
 void ieee80211_stop_vif_queues(struct ieee80211_local *local,
@@ -625,13 +630,14 @@
 					reason, true);
 }
 
-static void __iterate_active_interfaces(struct ieee80211_local *local,
-					u32 iter_flags,
-					void (*iterator)(void *data, u8 *mac,
-						struct ieee80211_vif *vif),
-					void *data)
+static void __iterate_interfaces(struct ieee80211_local *local,
+				 u32 iter_flags,
+				 void (*iterator)(void *data, u8 *mac,
+						  struct ieee80211_vif *vif),
+				 void *data)
 {
 	struct ieee80211_sub_if_data *sdata;
+	bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE;
 
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 		switch (sdata->vif.type) {
@@ -645,9 +651,9 @@
 			break;
 		}
 		if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
-		    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+		    active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
 			continue;
-		if (ieee80211_sdata_running(sdata))
+		if (ieee80211_sdata_running(sdata) || !active_only)
 			iterator(data, sdata->vif.addr,
 				 &sdata->vif);
 	}
@@ -656,12 +662,12 @@
 				      lockdep_is_held(&local->iflist_mtx) ||
 				      lockdep_rtnl_is_held());
 	if (sdata &&
-	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
+	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
 	     sdata->flags & IEEE80211_SDATA_IN_DRIVER))
 		iterator(data, sdata->vif.addr, &sdata->vif);
 }
 
-void ieee80211_iterate_active_interfaces(
+void ieee80211_iterate_interfaces(
 	struct ieee80211_hw *hw, u32 iter_flags,
 	void (*iterator)(void *data, u8 *mac,
 			 struct ieee80211_vif *vif),
@@ -670,10 +676,10 @@
 	struct ieee80211_local *local = hw_to_local(hw);
 
 	mutex_lock(&local->iflist_mtx);
-	__iterate_active_interfaces(local, iter_flags, iterator, data);
+	__iterate_interfaces(local, iter_flags, iterator, data);
 	mutex_unlock(&local->iflist_mtx);
 }
-EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
+EXPORT_SYMBOL_GPL(ieee80211_iterate_interfaces);
 
 void ieee80211_iterate_active_interfaces_atomic(
 	struct ieee80211_hw *hw, u32 iter_flags,
@@ -684,7 +690,8 @@
 	struct ieee80211_local *local = hw_to_local(hw);
 
 	rcu_read_lock();
-	__iterate_active_interfaces(local, iter_flags, iterator, data);
+	__iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
+			     iterator, data);
 	rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
@@ -699,7 +706,8 @@
 
 	ASSERT_RTNL();
 
-	__iterate_active_interfaces(local, iter_flags, iterator, data);
+	__iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
+			     iterator, data);
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
 
@@ -742,18 +750,33 @@
 }
 EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
 
+struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
+{
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+	if (!ieee80211_sdata_running(sdata) ||
+	    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+		return NULL;
+
+	return &sdata->wdev;
+}
+EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev);
+
 /*
  * Nothing should have been stuffed into the workqueue during
- * the suspend->resume cycle. If this WARN is seen then there
- * is a bug with either the driver suspend or something in
- * mac80211 stuffing into the workqueue which we haven't yet
- * cleared during mac80211's suspend cycle.
+ * the suspend->resume cycle. Since we can't check each caller
+ * of this function if we are already quiescing / suspended,
+ * check here and don't WARN since this can actually happen when
+ * the rx path (for example) is racing against __ieee80211_suspend
+ * and suspending / quiescing was set after the rx path checked
+ * them.
  */
 static bool ieee80211_can_queue_work(struct ieee80211_local *local)
 {
-	if (WARN(local->suspended && !local->resuming,
-		 "queueing ieee80211 work while going to suspend\n"))
+	if (local->quiescing || (local->suspended && !local->resuming)) {
+		pr_warn("queueing ieee80211 work while going to suspend\n");
 		return false;
+	}
 
 	return true;
 }
@@ -1470,10 +1493,12 @@
 
 	/* Check if any channel in this sband supports at least 80 MHz */
 	for (i = 0; i < sband->n_channels; i++) {
-		if (!(sband->channels[i].flags & IEEE80211_CHAN_NO_80MHZ)) {
-			have_80mhz = true;
-			break;
-		}
+		if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
+						IEEE80211_CHAN_NO_80MHZ))
+			continue;
+
+		have_80mhz = true;
+		break;
 	}
 
 	if (sband->vht_cap.vht_supported && have_80mhz) {
@@ -1735,6 +1760,10 @@
 	struct cfg80211_sched_scan_request *sched_scan_req;
 	bool sched_scan_stopped = false;
 
+	/* nothing to do if HW shouldn't run */
+	if (!local->open_count)
+		goto wake_up;
+
 #ifdef CONFIG_PM
 	if (local->suspended)
 		local->resuming = true;
@@ -1756,9 +1785,6 @@
 		reconfig_due_to_wowlan = true;
 	}
 #endif
-	/* everything else happens only if HW was up & running */
-	if (!local->open_count)
-		goto wake_up;
 
 	/*
 	 * Upon resume hardware can sometimes be goofy due to
@@ -1805,8 +1831,25 @@
 	list_for_each_entry(sdata, &local->interfaces, list) {
 		if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
 		    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
-		    ieee80211_sdata_running(sdata))
+		    ieee80211_sdata_running(sdata)) {
 			res = drv_add_interface(local, sdata);
+			if (WARN_ON(res))
+				break;
+		}
+	}
+
+	/* If adding any of the interfaces failed above, roll back and
+	 * report failure.
+	 */
+	if (res) {
+		list_for_each_entry_continue_reverse(sdata, &local->interfaces,
+						     list)
+			if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+			    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+			    ieee80211_sdata_running(sdata))
+				drv_remove_interface(local, sdata);
+		ieee80211_handle_reconfig_failure(local);
+		return res;
 	}
 
 	/* add channel contexts */
@@ -2042,7 +2085,7 @@
 	 * If this is for hw restart things are still running.
 	 * We may want to change that later, however.
 	 */
-	if (!local->suspended || reconfig_due_to_wowlan)
+	if (local->open_count && (!local->suspended || reconfig_due_to_wowlan))
 		drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
 
 	if (!local->suspended)
@@ -2054,7 +2097,19 @@
 	mb();
 	local->resuming = false;
 
-	if (!reconfig_due_to_wowlan)
+	/* It's possible that we don't handle the scan completion in
+	 * time during suspend, so if it's still marked as completed
+	 * here, queue the work and flush it to clean things up.
+	 * Instead of calling the worker function directly here, we
+	 * really queue it to avoid potential races with other flows
+	 * scheduling the same work.
+	 */
+	if (test_bit(SCAN_COMPLETED, &local->scanning)) {
+		ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0);
+		flush_delayed_work(&local->scan_work);
+	}
+
+	if (local->open_count && !reconfig_due_to_wowlan)
 		drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
 
 	list_for_each_entry(sdata, &local->interfaces, list) {
@@ -2139,46 +2194,6 @@
 	mutex_unlock(&local->chanctx_mtx);
 }
 
-static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
-{
-	int i;
-
-	for (i = 0; i < n_ids; i++)
-		if (ids[i] == id)
-			return true;
-	return false;
-}
-
-size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
-			      const u8 *ids, int n_ids,
-			      const u8 *after_ric, int n_after_ric,
-			      size_t offset)
-{
-	size_t pos = offset;
-
-	while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
-		if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
-			pos += 2 + ies[pos + 1];
-
-			while (pos < ielen &&
-			       !ieee80211_id_in_list(after_ric, n_after_ric,
-						     ies[pos]))
-				pos += 2 + ies[pos + 1];
-		} else {
-			pos += 2 + ies[pos + 1];
-		}
-	}
-
-	return pos;
-}
-
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
-			  const u8 *ids, int n_ids, size_t offset)
-{
-	return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
-}
-EXPORT_SYMBOL(ieee80211_ie_split);
-
 size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
 {
 	size_t pos = offset;
@@ -2326,6 +2341,41 @@
 	return pos + sizeof(struct ieee80211_ht_operation);
 }
 
+u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
+				const struct cfg80211_chan_def *chandef)
+{
+	struct ieee80211_vht_operation *vht_oper;
+
+	*pos++ = WLAN_EID_VHT_OPERATION;
+	*pos++ = sizeof(struct ieee80211_vht_operation);
+	vht_oper = (struct ieee80211_vht_operation *)pos;
+	vht_oper->center_freq_seg1_idx = ieee80211_frequency_to_channel(
+							chandef->center_freq1);
+	if (chandef->center_freq2)
+		vht_oper->center_freq_seg2_idx =
+			ieee80211_frequency_to_channel(chandef->center_freq2);
+
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_160:
+		vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ;
+		break;
+	case NL80211_CHAN_WIDTH_80P80:
+		vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ;
+		break;
+	case NL80211_CHAN_WIDTH_80:
+		vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
+		break;
+	default:
+		vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
+		break;
+	}
+
+	/* don't require special VHT peer rates */
+	vht_oper->basic_mcs_set = cpu_to_le16(0xffff);
+
+	return pos + sizeof(struct ieee80211_vht_operation);
+}
+
 void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
 				  const struct ieee80211_ht_operation *ht_oper,
 				  struct cfg80211_chan_def *chandef)
@@ -2355,6 +2405,39 @@
 	cfg80211_chandef_create(chandef, control_chan, channel_type);
 }
 
+void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan,
+				   const struct ieee80211_vht_operation *oper,
+				   struct cfg80211_chan_def *chandef)
+{
+	if (!oper)
+		return;
+
+	chandef->chan = control_chan;
+
+	switch (oper->chan_width) {
+	case IEEE80211_VHT_CHANWIDTH_USE_HT:
+		break;
+	case IEEE80211_VHT_CHANWIDTH_80MHZ:
+		chandef->width = NL80211_CHAN_WIDTH_80;
+		break;
+	case IEEE80211_VHT_CHANWIDTH_160MHZ:
+		chandef->width = NL80211_CHAN_WIDTH_160;
+		break;
+	case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+		chandef->width = NL80211_CHAN_WIDTH_80P80;
+		break;
+	default:
+		break;
+	}
+
+	chandef->center_freq1 =
+		ieee80211_channel_to_frequency(oper->center_freq_seg1_idx,
+					       control_chan->band);
+	chandef->center_freq2 =
+		ieee80211_channel_to_frequency(oper->center_freq_seg2_idx,
+					       control_chan->band);
+}
+
 int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
 			     const struct ieee80211_supported_band *sband,
 			     const u8 *srates, int srates_len, u32 *rates)
@@ -2538,7 +2621,9 @@
 		ri.mcs = status->rate_idx;
 		ri.flags |= RATE_INFO_FLAGS_MCS;
 		if (status->flag & RX_FLAG_40MHZ)
-			ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+			ri.bw = RATE_INFO_BW_40;
+		else
+			ri.bw = RATE_INFO_BW_20;
 		if (status->flag & RX_FLAG_SHORT_GI)
 			ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
 	} else if (status->flag & RX_FLAG_VHT) {
@@ -2546,13 +2631,13 @@
 		ri.mcs = status->rate_idx;
 		ri.nss = status->vht_nss;
 		if (status->flag & RX_FLAG_40MHZ)
-			ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
-		if (status->vht_flag & RX_VHT_FLAG_80MHZ)
-			ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
-		if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)
-			ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
-		if (status->vht_flag & RX_VHT_FLAG_160MHZ)
-			ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
+			ri.bw = RATE_INFO_BW_40;
+		else if (status->vht_flag & RX_VHT_FLAG_80MHZ)
+			ri.bw = RATE_INFO_BW_80;
+		else if (status->vht_flag & RX_VHT_FLAG_160MHZ)
+			ri.bw = RATE_INFO_BW_160;
+		else
+			ri.bw = RATE_INFO_BW_20;
 		if (status->flag & RX_FLAG_SHORT_GI)
 			ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
 	} else {
@@ -2560,10 +2645,15 @@
 		int shift = 0;
 		int bitrate;
 
-		if (status->flag & RX_FLAG_10MHZ)
+		if (status->flag & RX_FLAG_10MHZ) {
 			shift = 1;
-		if (status->flag & RX_FLAG_5MHZ)
+			ri.bw = RATE_INFO_BW_10;
+		} else if (status->flag & RX_FLAG_5MHZ) {
 			shift = 2;
+			ri.bw = RATE_INFO_BW_5;
+		} else {
+			ri.bw = RATE_INFO_BW_20;
+		}
 
 		sband = local->hw.wiphy->bands[status->band];
 		bitrate = sband->bitrates[status->rate_idx].bitrate;
@@ -3153,7 +3243,7 @@
 		wdev_iter = &sdata_iter->wdev;
 
 		if (sdata_iter == sdata ||
-		    rcu_access_pointer(sdata_iter->vif.chanctx_conf) == NULL ||
+		    !ieee80211_sdata_running(sdata_iter) ||
 		    local->hw.wiphy->software_iftypes & BIT(wdev_iter->iftype))
 			continue;
 
@@ -3227,3 +3317,20 @@
 
 	return buf;
 }
+
+void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata,
+			     struct sta_info *sta,
+			     struct txq_info *txqi, int tid)
+{
+	skb_queue_head_init(&txqi->queue);
+	txqi->txq.vif = &sdata->vif;
+
+	if (sta) {
+		txqi->txq.sta = &sta->sta;
+		sta->sta.txq[tid] = &txqi->txq;
+		txqi->txq.ac = ieee802_1d_to_ac[tid & 7];
+	} else {
+		sdata->vif.txq = &txqi->txq;
+		txqi->txq.ac = IEEE80211_AC_BE;
+	}
+}
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index bc9e8fc..80694d5 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -129,10 +129,6 @@
 	if (!vht_cap_ie || !sband->vht_cap.vht_supported)
 		return;
 
-	/* don't support VHT for TDLS peers for now */
-	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
-		return;
-
 	/*
 	 * A VHT STA must support 40 MHz, but if we verify that here
 	 * then we break a few things - some APs (e.g. Netgear R6300v2
@@ -269,51 +265,54 @@
 	sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
 }
 
+enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta)
+{
+	struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap;
+	u32 cap_width;
+
+	if (!vht_cap->vht_supported)
+		return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
+				IEEE80211_STA_RX_BW_40 :
+				IEEE80211_STA_RX_BW_20;
+
+	cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+
+	if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
+	    cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
+		return IEEE80211_STA_RX_BW_160;
+
+	return IEEE80211_STA_RX_BW_80;
+}
+
+static enum ieee80211_sta_rx_bandwidth
+ieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width)
+{
+	switch (width) {
+	case NL80211_CHAN_WIDTH_20_NOHT:
+	case NL80211_CHAN_WIDTH_20:
+		return IEEE80211_STA_RX_BW_20;
+	case NL80211_CHAN_WIDTH_40:
+		return IEEE80211_STA_RX_BW_40;
+	case NL80211_CHAN_WIDTH_80:
+		return IEEE80211_STA_RX_BW_80;
+	case NL80211_CHAN_WIDTH_160:
+	case NL80211_CHAN_WIDTH_80P80:
+		return IEEE80211_STA_RX_BW_160;
+	default:
+		WARN_ON_ONCE(1);
+		return IEEE80211_STA_RX_BW_20;
+	}
+}
+
 enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
 {
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
-	u32 cap = sta->sta.vht_cap.cap;
 	enum ieee80211_sta_rx_bandwidth bw;
 
-	if (!sta->sta.vht_cap.vht_supported) {
-		bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
-				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
-		goto check_max;
-	}
+	bw = ieee80211_chan_width_to_rx_bw(sdata->vif.bss_conf.chandef.width);
+	bw = min(bw, ieee80211_sta_cap_rx_bw(sta));
+	bw = min(bw, sta->cur_max_bandwidth);
 
-	switch (sdata->vif.bss_conf.chandef.width) {
-	default:
-		WARN_ON_ONCE(1);
-		/* fall through */
-	case NL80211_CHAN_WIDTH_20_NOHT:
-	case NL80211_CHAN_WIDTH_20:
-		bw = IEEE80211_STA_RX_BW_20;
-		break;
-	case NL80211_CHAN_WIDTH_40:
-		bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
-				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
-		break;
-	case NL80211_CHAN_WIDTH_160:
-		if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
-				IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) {
-			bw = IEEE80211_STA_RX_BW_160;
-			break;
-		}
-		/* fall through */
-	case NL80211_CHAN_WIDTH_80P80:
-		if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
-				IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
-			bw = IEEE80211_STA_RX_BW_160;
-			break;
-		}
-		/* fall through */
-	case NL80211_CHAN_WIDTH_80:
-		bw = IEEE80211_STA_RX_BW_80;
-	}
-
- check_max:
-	if (bw > sta->cur_max_bandwidth)
-		bw = sta->cur_max_bandwidth;
 	return bw;
 }
 
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 12398fd..9d63d93 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -22,6 +22,8 @@
 #include "tkip.h"
 #include "aes_ccm.h"
 #include "aes_cmac.h"
+#include "aes_gmac.h"
+#include "aes_gcm.h"
 #include "wpa.h"
 
 ieee80211_tx_result
@@ -393,7 +395,8 @@
 }
 
 
-static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
+static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb,
+			    unsigned int mic_len)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	struct ieee80211_key *key = tx->key;
@@ -424,7 +427,7 @@
 	if (info->control.hw_key)
 		tail = 0;
 	else
-		tail = IEEE80211_CCMP_MIC_LEN;
+		tail = mic_len;
 
 	if (WARN_ON(skb_tailroom(skb) < tail ||
 		    skb_headroom(skb) < IEEE80211_CCMP_HDR_LEN))
@@ -459,21 +462,22 @@
 	pos += IEEE80211_CCMP_HDR_LEN;
 	ccmp_special_blocks(skb, pn, b_0, aad);
 	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
-				  skb_put(skb, IEEE80211_CCMP_MIC_LEN));
+				  skb_put(skb, mic_len), mic_len);
 
 	return 0;
 }
 
 
 ieee80211_tx_result
-ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
+ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx,
+			      unsigned int mic_len)
 {
 	struct sk_buff *skb;
 
 	ieee80211_tx_set_protected(tx);
 
 	skb_queue_walk(&tx->skbs, skb) {
-		if (ccmp_encrypt_skb(tx, skb) < 0)
+		if (ccmp_encrypt_skb(tx, skb, mic_len) < 0)
 			return TX_DROP;
 	}
 
@@ -482,7 +486,8 @@
 
 
 ieee80211_rx_result
-ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
+ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
+			      unsigned int mic_len)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
 	int hdrlen;
@@ -499,8 +504,7 @@
 	    !ieee80211_is_robust_mgmt_frame(skb))
 		return RX_CONTINUE;
 
-	data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN -
-		   IEEE80211_CCMP_MIC_LEN;
+	data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len;
 	if (!rx->sta || data_len < 0)
 		return RX_DROP_UNUSABLE;
 
@@ -531,14 +535,14 @@
 			    key->u.ccmp.tfm, b_0, aad,
 			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
 			    data_len,
-			    skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))
+			    skb->data + skb->len - mic_len, mic_len))
 			return RX_DROP_UNUSABLE;
 	}
 
 	memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
 
 	/* Remove CCMP header and MIC */
-	if (pskb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN))
+	if (pskb_trim(skb, skb->len - mic_len))
 		return RX_DROP_UNUSABLE;
 	memmove(skb->data + IEEE80211_CCMP_HDR_LEN, skb->data, hdrlen);
 	skb_pull(skb, IEEE80211_CCMP_HDR_LEN);
@@ -546,6 +550,229 @@
 	return RX_CONTINUE;
 }
 
+static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad)
+{
+	__le16 mask_fc;
+	u8 qos_tid;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+	memcpy(j_0, hdr->addr2, ETH_ALEN);
+	memcpy(&j_0[ETH_ALEN], pn, IEEE80211_GCMP_PN_LEN);
+	j_0[13] = 0;
+	j_0[14] = 0;
+	j_0[AES_BLOCK_SIZE - 1] = 0x01;
+
+	/* AAD (extra authenticate-only data) / masked 802.11 header
+	 * FC | A1 | A2 | A3 | SC | [A4] | [QC]
+	 */
+	put_unaligned_be16(ieee80211_hdrlen(hdr->frame_control) - 2, &aad[0]);
+	/* Mask FC: zero subtype b4 b5 b6 (if not mgmt)
+	 * Retry, PwrMgt, MoreData; set Protected
+	 */
+	mask_fc = hdr->frame_control;
+	mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY |
+				IEEE80211_FCTL_PM | IEEE80211_FCTL_MOREDATA);
+	if (!ieee80211_is_mgmt(hdr->frame_control))
+		mask_fc &= ~cpu_to_le16(0x0070);
+	mask_fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+
+	put_unaligned(mask_fc, (__le16 *)&aad[2]);
+	memcpy(&aad[4], &hdr->addr1, 3 * ETH_ALEN);
+
+	/* Mask Seq#, leave Frag# */
+	aad[22] = *((u8 *)&hdr->seq_ctrl) & 0x0f;
+	aad[23] = 0;
+
+	if (ieee80211_is_data_qos(hdr->frame_control))
+		qos_tid = *ieee80211_get_qos_ctl(hdr) &
+			IEEE80211_QOS_CTL_TID_MASK;
+	else
+		qos_tid = 0;
+
+	if (ieee80211_has_a4(hdr->frame_control)) {
+		memcpy(&aad[24], hdr->addr4, ETH_ALEN);
+		aad[30] = qos_tid;
+		aad[31] = 0;
+	} else {
+		memset(&aad[24], 0, ETH_ALEN + IEEE80211_QOS_CTL_LEN);
+		aad[24] = qos_tid;
+	}
+}
+
+static inline void gcmp_pn2hdr(u8 *hdr, const u8 *pn, int key_id)
+{
+	hdr[0] = pn[5];
+	hdr[1] = pn[4];
+	hdr[2] = 0;
+	hdr[3] = 0x20 | (key_id << 6);
+	hdr[4] = pn[3];
+	hdr[5] = pn[2];
+	hdr[6] = pn[1];
+	hdr[7] = pn[0];
+}
+
+static inline void gcmp_hdr2pn(u8 *pn, const u8 *hdr)
+{
+	pn[0] = hdr[7];
+	pn[1] = hdr[6];
+	pn[2] = hdr[5];
+	pn[3] = hdr[4];
+	pn[4] = hdr[1];
+	pn[5] = hdr[0];
+}
+
+static int gcmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_key *key = tx->key;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int hdrlen, len, tail;
+	u8 *pos;
+	u8 pn[6];
+	u64 pn64;
+	u8 aad[2 * AES_BLOCK_SIZE];
+	u8 j_0[AES_BLOCK_SIZE];
+
+	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_GENERATE_IV_MGMT) &&
+	      ieee80211_is_mgmt(hdr->frame_control))) {
+		/* hwaccel has no need for preallocated room for GCMP
+		 * header or MIC fields
+		 */
+		return 0;
+	}
+
+	hdrlen = ieee80211_hdrlen(hdr->frame_control);
+	len = skb->len - hdrlen;
+
+	if (info->control.hw_key)
+		tail = 0;
+	else
+		tail = IEEE80211_GCMP_MIC_LEN;
+
+	if (WARN_ON(skb_tailroom(skb) < tail ||
+		    skb_headroom(skb) < IEEE80211_GCMP_HDR_LEN))
+		return -1;
+
+	pos = skb_push(skb, IEEE80211_GCMP_HDR_LEN);
+	memmove(pos, pos + IEEE80211_GCMP_HDR_LEN, hdrlen);
+	skb_set_network_header(skb, skb_network_offset(skb) +
+				    IEEE80211_GCMP_HDR_LEN);
+
+	/* the HW only needs room for the IV, but not the actual IV */
+	if (info->control.hw_key &&
+	    (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
+		return 0;
+
+	hdr = (struct ieee80211_hdr *)pos;
+	pos += hdrlen;
+
+	pn64 = atomic64_inc_return(&key->u.gcmp.tx_pn);
+
+	pn[5] = pn64;
+	pn[4] = pn64 >> 8;
+	pn[3] = pn64 >> 16;
+	pn[2] = pn64 >> 24;
+	pn[1] = pn64 >> 32;
+	pn[0] = pn64 >> 40;
+
+	gcmp_pn2hdr(pos, pn, key->conf.keyidx);
+
+	/* hwaccel - with software GCMP header */
+	if (info->control.hw_key)
+		return 0;
+
+	pos += IEEE80211_GCMP_HDR_LEN;
+	gcmp_special_blocks(skb, pn, j_0, aad);
+	ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
+				  skb_put(skb, IEEE80211_GCMP_MIC_LEN));
+
+	return 0;
+}
+
+ieee80211_tx_result
+ieee80211_crypto_gcmp_encrypt(struct ieee80211_tx_data *tx)
+{
+	struct sk_buff *skb;
+
+	ieee80211_tx_set_protected(tx);
+
+	skb_queue_walk(&tx->skbs, skb) {
+		if (gcmp_encrypt_skb(tx, skb) < 0)
+			return TX_DROP;
+	}
+
+	return TX_CONTINUE;
+}
+
+ieee80211_rx_result
+ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+	int hdrlen;
+	struct ieee80211_key *key = rx->key;
+	struct sk_buff *skb = rx->skb;
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+	u8 pn[IEEE80211_GCMP_PN_LEN];
+	int data_len;
+	int queue;
+
+	hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+	if (!ieee80211_is_data(hdr->frame_control) &&
+	    !ieee80211_is_robust_mgmt_frame(skb))
+		return RX_CONTINUE;
+
+	data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN -
+		   IEEE80211_GCMP_MIC_LEN;
+	if (!rx->sta || data_len < 0)
+		return RX_DROP_UNUSABLE;
+
+	if (status->flag & RX_FLAG_DECRYPTED) {
+		if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_GCMP_HDR_LEN))
+			return RX_DROP_UNUSABLE;
+	} else {
+		if (skb_linearize(rx->skb))
+			return RX_DROP_UNUSABLE;
+	}
+
+	gcmp_hdr2pn(pn, skb->data + hdrlen);
+
+	queue = rx->security_idx;
+
+	if (memcmp(pn, key->u.gcmp.rx_pn[queue], IEEE80211_GCMP_PN_LEN) <= 0) {
+		key->u.gcmp.replays++;
+		return RX_DROP_UNUSABLE;
+	}
+
+	if (!(status->flag & RX_FLAG_DECRYPTED)) {
+		u8 aad[2 * AES_BLOCK_SIZE];
+		u8 j_0[AES_BLOCK_SIZE];
+		/* hardware didn't decrypt/verify MIC */
+		gcmp_special_blocks(skb, pn, j_0, aad);
+
+		if (ieee80211_aes_gcm_decrypt(
+			    key->u.gcmp.tfm, j_0, aad,
+			    skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
+			    data_len,
+			    skb->data + skb->len - IEEE80211_GCMP_MIC_LEN))
+			return RX_DROP_UNUSABLE;
+	}
+
+	memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN);
+
+	/* Remove GCMP header and MIC */
+	if (pskb_trim(skb, skb->len - IEEE80211_GCMP_MIC_LEN))
+		return RX_DROP_UNUSABLE;
+	memmove(skb->data + IEEE80211_GCMP_HDR_LEN, skb->data, hdrlen);
+	skb_pull(skb, IEEE80211_GCMP_HDR_LEN);
+
+	return RX_CONTINUE;
+}
+
 static ieee80211_tx_result
 ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
 			    struct sk_buff *skb)
@@ -553,9 +780,8 @@
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct ieee80211_key *key = tx->key;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-	const struct ieee80211_cipher_scheme *cs = key->sta->cipher_scheme;
 	int hdrlen;
-	u8 *pos;
+	u8 *pos, iv_len = key->conf.iv_len;
 
 	if (info->control.hw_key &&
 	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
@@ -563,14 +789,14 @@
 		return TX_CONTINUE;
 	}
 
-	if (unlikely(skb_headroom(skb) < cs->hdr_len &&
-		     pskb_expand_head(skb, cs->hdr_len, 0, GFP_ATOMIC)))
+	if (unlikely(skb_headroom(skb) < iv_len &&
+		     pskb_expand_head(skb, iv_len, 0, GFP_ATOMIC)))
 		return TX_DROP;
 
 	hdrlen = ieee80211_hdrlen(hdr->frame_control);
 
-	pos = skb_push(skb, cs->hdr_len);
-	memmove(pos, pos + cs->hdr_len, hdrlen);
+	pos = skb_push(skb, iv_len);
+	memmove(pos, pos + iv_len, hdrlen);
 
 	return TX_CONTINUE;
 }
@@ -729,6 +955,48 @@
 	return TX_CONTINUE;
 }
 
+ieee80211_tx_result
+ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data *tx)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct ieee80211_key *key = tx->key;
+	struct ieee80211_mmie_16 *mmie;
+	u8 aad[20];
+	u64 pn64;
+
+	if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
+		return TX_DROP;
+
+	skb = skb_peek(&tx->skbs);
+
+	info = IEEE80211_SKB_CB(skb);
+
+	if (info->control.hw_key)
+		return TX_CONTINUE;
+
+	if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
+		return TX_DROP;
+
+	mmie = (struct ieee80211_mmie_16 *)skb_put(skb, sizeof(*mmie));
+	mmie->element_id = WLAN_EID_MMIE;
+	mmie->length = sizeof(*mmie) - 2;
+	mmie->key_id = cpu_to_le16(key->conf.keyidx);
+
+	/* PN = PN + 1 */
+	pn64 = atomic64_inc_return(&key->u.aes_cmac.tx_pn);
+
+	bip_ipn_set64(mmie->sequence_number, pn64);
+
+	bip_aad(skb, aad);
+
+	/* MIC = AES-256-CMAC(IGTK, AAD || Management Frame Body || MMIE, 128)
+	 */
+	ieee80211_aes_cmac_256(key->u.aes_cmac.tfm, aad,
+			       skb->data + 24, skb->len - 24, mmie->mic);
+
+	return TX_CONTINUE;
+}
 
 ieee80211_rx_result
 ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
@@ -780,6 +1048,160 @@
 	return RX_CONTINUE;
 }
 
+ieee80211_rx_result
+ieee80211_crypto_aes_cmac_256_decrypt(struct ieee80211_rx_data *rx)
+{
+	struct sk_buff *skb = rx->skb;
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+	struct ieee80211_key *key = rx->key;
+	struct ieee80211_mmie_16 *mmie;
+	u8 aad[20], mic[16], ipn[6];
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+	if (!ieee80211_is_mgmt(hdr->frame_control))
+		return RX_CONTINUE;
+
+	/* management frames are already linear */
+
+	if (skb->len < 24 + sizeof(*mmie))
+		return RX_DROP_UNUSABLE;
+
+	mmie = (struct ieee80211_mmie_16 *)
+		(skb->data + skb->len - sizeof(*mmie));
+	if (mmie->element_id != WLAN_EID_MMIE ||
+	    mmie->length != sizeof(*mmie) - 2)
+		return RX_DROP_UNUSABLE; /* Invalid MMIE */
+
+	bip_ipn_swap(ipn, mmie->sequence_number);
+
+	if (memcmp(ipn, key->u.aes_cmac.rx_pn, 6) <= 0) {
+		key->u.aes_cmac.replays++;
+		return RX_DROP_UNUSABLE;
+	}
+
+	if (!(status->flag & RX_FLAG_DECRYPTED)) {
+		/* hardware didn't decrypt/verify MIC */
+		bip_aad(skb, aad);
+		ieee80211_aes_cmac_256(key->u.aes_cmac.tfm, aad,
+				       skb->data + 24, skb->len - 24, mic);
+		if (memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) {
+			key->u.aes_cmac.icverrors++;
+			return RX_DROP_UNUSABLE;
+		}
+	}
+
+	memcpy(key->u.aes_cmac.rx_pn, ipn, 6);
+
+	/* Remove MMIE */
+	skb_trim(skb, skb->len - sizeof(*mmie));
+
+	return RX_CONTINUE;
+}
+
+ieee80211_tx_result
+ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct ieee80211_key *key = tx->key;
+	struct ieee80211_mmie_16 *mmie;
+	struct ieee80211_hdr *hdr;
+	u8 aad[20];
+	u64 pn64;
+	u8 nonce[12];
+
+	if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
+		return TX_DROP;
+
+	skb = skb_peek(&tx->skbs);
+
+	info = IEEE80211_SKB_CB(skb);
+
+	if (info->control.hw_key)
+		return TX_CONTINUE;
+
+	if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
+		return TX_DROP;
+
+	mmie = (struct ieee80211_mmie_16 *)skb_put(skb, sizeof(*mmie));
+	mmie->element_id = WLAN_EID_MMIE;
+	mmie->length = sizeof(*mmie) - 2;
+	mmie->key_id = cpu_to_le16(key->conf.keyidx);
+
+	/* PN = PN + 1 */
+	pn64 = atomic64_inc_return(&key->u.aes_gmac.tx_pn);
+
+	bip_ipn_set64(mmie->sequence_number, pn64);
+
+	bip_aad(skb, aad);
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+	memcpy(nonce, hdr->addr2, ETH_ALEN);
+	bip_ipn_swap(nonce + ETH_ALEN, mmie->sequence_number);
+
+	/* MIC = AES-GMAC(IGTK, AAD || Management Frame Body || MMIE, 128) */
+	if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
+			       skb->data + 24, skb->len - 24, mmie->mic) < 0)
+		return TX_DROP;
+
+	return TX_CONTINUE;
+}
+
+ieee80211_rx_result
+ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx)
+{
+	struct sk_buff *skb = rx->skb;
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+	struct ieee80211_key *key = rx->key;
+	struct ieee80211_mmie_16 *mmie;
+	u8 aad[20], mic[16], ipn[6], nonce[12];
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+	if (!ieee80211_is_mgmt(hdr->frame_control))
+		return RX_CONTINUE;
+
+	/* management frames are already linear */
+
+	if (skb->len < 24 + sizeof(*mmie))
+		return RX_DROP_UNUSABLE;
+
+	mmie = (struct ieee80211_mmie_16 *)
+		(skb->data + skb->len - sizeof(*mmie));
+	if (mmie->element_id != WLAN_EID_MMIE ||
+	    mmie->length != sizeof(*mmie) - 2)
+		return RX_DROP_UNUSABLE; /* Invalid MMIE */
+
+	bip_ipn_swap(ipn, mmie->sequence_number);
+
+	if (memcmp(ipn, key->u.aes_gmac.rx_pn, 6) <= 0) {
+		key->u.aes_gmac.replays++;
+		return RX_DROP_UNUSABLE;
+	}
+
+	if (!(status->flag & RX_FLAG_DECRYPTED)) {
+		/* hardware didn't decrypt/verify MIC */
+		bip_aad(skb, aad);
+
+		memcpy(nonce, hdr->addr2, ETH_ALEN);
+		memcpy(nonce + ETH_ALEN, ipn, 6);
+
+		if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
+				       skb->data + 24, skb->len - 24,
+				       mic) < 0 ||
+		    memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) {
+			key->u.aes_gmac.icverrors++;
+			return RX_DROP_UNUSABLE;
+		}
+	}
+
+	memcpy(key->u.aes_gmac.rx_pn, ipn, 6);
+
+	/* Remove MMIE */
+	skb_trim(skb, skb->len - sizeof(*mmie));
+
+	return RX_CONTINUE;
+}
+
 ieee80211_tx_result
 ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
 {
@@ -794,7 +1216,7 @@
 		if (!info->control.hw_key)
 			return TX_DROP;
 
-		if (tx->key->sta->cipher_scheme) {
+		if (tx->key->flags & KEY_FLAG_CIPHER_SCHEME) {
 			res = ieee80211_crypto_cs_encrypt(tx, skb);
 			if (res != TX_CONTINUE)
 				return res;
diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h
index 62e5a12..d98011e 100644
--- a/net/mac80211/wpa.h
+++ b/net/mac80211/wpa.h
@@ -24,17 +24,32 @@
 ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx);
 
 ieee80211_tx_result
-ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx,
+			      unsigned int mic_len);
 ieee80211_rx_result
-ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx);
+ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
+			      unsigned int mic_len);
 
 ieee80211_tx_result
 ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_tx_result
+ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data *tx);
 ieee80211_rx_result
 ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx);
+ieee80211_rx_result
+ieee80211_crypto_aes_cmac_256_decrypt(struct ieee80211_rx_data *rx);
+ieee80211_tx_result
+ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_rx_result
+ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx);
 ieee80211_tx_result
 ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx);
 ieee80211_rx_result
 ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx);
 
+ieee80211_tx_result
+ieee80211_crypto_gcmp_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_rx_result
+ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx);
+
 #endif /* WPA_H */
diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c
index c035708..5d9f68c 100644
--- a/net/mac802154/cfg.c
+++ b/net/mac802154/cfg.c
@@ -51,10 +51,7 @@
 	struct net_device *err;
 
 	err = ieee802154_if_add(local, name, type, extended_addr);
-	if (IS_ERR(err))
-		return PTR_ERR(err);
-
-	return 0;
+	return PTR_ERR_OR_ZERO(err);
 }
 
 static int
@@ -87,6 +84,26 @@
 }
 
 static int
+ieee802154_set_cca_mode(struct wpan_phy *wpan_phy,
+			const struct wpan_phy_cca *cca)
+{
+	struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+	int ret;
+
+	ASSERT_RTNL();
+
+	/* check if phy support this setting */
+	if (!(local->hw.flags & IEEE802154_HW_CCA_MODE))
+		return -EOPNOTSUPP;
+
+	ret = drv_set_cca_mode(local, cca);
+	if (!ret)
+		wpan_phy->cca = *cca;
+
+	return ret;
+}
+
+static int
 ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
 		      __le16 pan_id)
 {
@@ -201,6 +218,7 @@
 	.add_virtual_intf = ieee802154_add_iface,
 	.del_virtual_intf = ieee802154_del_iface,
 	.set_channel = ieee802154_set_channel,
+	.set_cca_mode = ieee802154_set_cca_mode,
 	.set_pan_id = ieee802154_set_pan_id,
 	.set_short_addr = ieee802154_set_short_addr,
 	.set_backoff_exponent = ieee802154_set_backoff_exponent,
diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h
index f21e864..a053335 100644
--- a/net/mac802154/driver-ops.h
+++ b/net/mac802154/driver-ops.h
@@ -1,4 +1,4 @@
-#ifndef __MAC802154_DRVIER_OPS
+#ifndef __MAC802154_DRIVER_OPS
 #define __MAC802154_DRIVER_OPS
 
 #include <linux/types.h>
@@ -70,7 +70,8 @@
 	return local->ops->set_txpower(&local->hw, dbm);
 }
 
-static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode)
+static inline int drv_set_cca_mode(struct ieee802154_local *local,
+				   const struct wpan_phy_cca *cca)
 {
 	might_sleep();
 
@@ -79,7 +80,7 @@
 		return -EOPNOTSUPP;
 	}
 
-	return local->ops->set_cca_mode(&local->hw, cca_mode);
+	return local->ops->set_cca_mode(&local->hw, cca);
 }
 
 static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode)
@@ -219,4 +220,4 @@
 	return local->ops->set_promiscuous_mode(&local->hw, on);
 }
 
-#endif /* __MAC802154_DRVIER_OPS */
+#endif /* __MAC802154_DRIVER_OPS */
diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c
index 9ae8930..38b56f9 100644
--- a/net/mac802154/iface.c
+++ b/net/mac802154/iface.c
@@ -137,25 +137,11 @@
 static int mac802154_slave_open(struct net_device *dev)
 {
 	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
-	struct ieee802154_sub_if_data *subif;
 	struct ieee802154_local *local = sdata->local;
 	int res = 0;
 
 	ASSERT_RTNL();
 
-	if (sdata->vif.type == NL802154_IFTYPE_NODE) {
-		mutex_lock(&sdata->local->iflist_mtx);
-		list_for_each_entry(subif, &sdata->local->interfaces, list) {
-			if (subif != sdata &&
-			    subif->vif.type == sdata->vif.type &&
-			    ieee802154_sdata_running(subif)) {
-				mutex_unlock(&sdata->local->iflist_mtx);
-				return -EBUSY;
-			}
-		}
-		mutex_unlock(&sdata->local->iflist_mtx);
-	}
-
 	set_bit(SDATA_STATE_RUNNING, &sdata->state);
 
 	if (!local->open_count) {
@@ -175,6 +161,80 @@
 	return res;
 }
 
+static int
+ieee802154_check_mac_settings(struct ieee802154_local *local,
+			      struct wpan_dev *wpan_dev,
+			      struct wpan_dev *nwpan_dev)
+{
+	ASSERT_RTNL();
+
+	if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
+		if (wpan_dev->promiscuous_mode != nwpan_dev->promiscuous_mode)
+			return -EBUSY;
+	}
+
+	if (local->hw.flags & IEEE802154_HW_AFILT) {
+		if (wpan_dev->pan_id != nwpan_dev->pan_id ||
+		    wpan_dev->short_addr != nwpan_dev->short_addr ||
+		    wpan_dev->extended_addr != nwpan_dev->extended_addr)
+			return -EBUSY;
+	}
+
+	if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
+		if (wpan_dev->min_be != nwpan_dev->min_be ||
+		    wpan_dev->max_be != nwpan_dev->max_be ||
+		    wpan_dev->csma_retries != nwpan_dev->csma_retries)
+			return -EBUSY;
+	}
+
+	if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
+		if (wpan_dev->frame_retries != nwpan_dev->frame_retries)
+			return -EBUSY;
+	}
+
+	if (local->hw.flags & IEEE802154_HW_LBT) {
+		if (wpan_dev->lbt != nwpan_dev->lbt)
+			return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int
+ieee802154_check_concurrent_iface(struct ieee802154_sub_if_data *sdata,
+				  enum nl802154_iftype iftype)
+{
+	struct ieee802154_local *local = sdata->local;
+	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+	struct ieee802154_sub_if_data *nsdata;
+
+	/* we hold the RTNL here so can safely walk the list */
+	list_for_each_entry(nsdata, &local->interfaces, list) {
+		if (nsdata != sdata && ieee802154_sdata_running(nsdata)) {
+			int ret;
+
+			/* TODO currently we don't support multiple node types
+			 * we need to run skb_clone at rx path. Check if there
+			 * exist really an use case if we need to support
+			 * multiple node types at the same time.
+			 */
+			if (sdata->vif.type == NL802154_IFTYPE_NODE &&
+			    nsdata->vif.type == NL802154_IFTYPE_NODE)
+				return -EBUSY;
+
+			/* check all phy mac sublayer settings are the same.
+			 * We have only one phy, different values makes trouble.
+			 */
+			ret = ieee802154_check_mac_settings(local, wpan_dev,
+							    &nsdata->wpan_dev);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
 static int mac802154_wpan_open(struct net_device *dev)
 {
 	int rc;
@@ -183,6 +243,10 @@
 	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
 	struct wpan_phy *phy = sdata->local->phy;
 
+	rc = ieee802154_check_concurrent_iface(sdata, sdata->vif.type);
+	if (rc < 0)
+		return rc;
+
 	rc = mac802154_slave_open(dev);
 	if (rc < 0)
 		return rc;
diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c
index dcf7395..3ccf1e9 100644
--- a/net/mac802154/llsec.c
+++ b/net/mac802154/llsec.c
@@ -17,8 +17,9 @@
 #include <linux/err.h>
 #include <linux/bug.h>
 #include <linux/completion.h>
+#include <linux/crypto.h>
 #include <linux/ieee802154.h>
-#include <crypto/algapi.h>
+#include <crypto/aead.h>
 
 #include "ieee802154_i.h"
 #include "llsec.h"
diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
index 6aacb18..bdccb4e 100644
--- a/net/mac802154/mac_cmd.c
+++ b/net/mac802154/mac_cmd.c
@@ -81,7 +81,7 @@
 
 	/* PHY */
 	wpan_dev->wpan_phy->transmit_power = params->transmit_power;
-	wpan_dev->wpan_phy->cca_mode = params->cca_mode;
+	wpan_dev->wpan_phy->cca = params->cca;
 	wpan_dev->wpan_phy->cca_ed_level = params->cca_ed_level;
 
 	/* MAC */
@@ -98,7 +98,7 @@
 	}
 
 	if (local->hw.flags & IEEE802154_HW_CCA_MODE) {
-		ret = drv_set_cca_mode(local, params->cca_mode);
+		ret = drv_set_cca_mode(local, &params->cca);
 		if (ret < 0)
 			return ret;
 	}
@@ -122,7 +122,7 @@
 
 	/* PHY */
 	params->transmit_power = wpan_dev->wpan_phy->transmit_power;
-	params->cca_mode = wpan_dev->wpan_phy->cca_mode;
+	params->cca = wpan_dev->wpan_phy->cca;
 	params->cca_ed_level = wpan_dev->wpan_phy->cca_ed_level;
 
 	/* MAC */
diff --git a/net/mac802154/util.c b/net/mac802154/util.c
index 5fc9790..150bf80 100644
--- a/net/mac802154/util.c
+++ b/net/mac802154/util.c
@@ -65,8 +65,19 @@
 {
 	if (ifs_handling) {
 		struct ieee802154_local *local = hw_to_local(hw);
+		u8 max_sifs_size;
 
-		if (skb->len > 18)
+		/* If transceiver sets CRC on his own we need to use lifs
+		 * threshold len above 16 otherwise 18, because it's not
+		 * part of skb->len.
+		 */
+		if (hw->flags & IEEE802154_HW_TX_OMIT_CKSUM)
+			max_sifs_size = IEEE802154_MAX_SIFS_FRAME_SIZE -
+					IEEE802154_FCS_LEN;
+		else
+			max_sifs_size = IEEE802154_MAX_SIFS_FRAME_SIZE;
+
+		if (skb->len > max_sifs_size)
 			hrtimer_start(&local->ifs_timer,
 				      ktime_set(0, hw->phy->lifs_period * NSEC_PER_USEC),
 				      HRTIMER_MODE_REL);
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 819b877..cff3f16 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -555,7 +555,6 @@
 
 int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
 {
-
 	struct nfc_se *se;
 	int rc;
 
@@ -605,7 +604,6 @@
 
 int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
 {
-
 	struct nfc_se *se;
 	int rc;
 
@@ -934,6 +932,27 @@
 }
 EXPORT_SYMBOL(nfc_remove_se);
 
+int nfc_se_transaction(struct nfc_dev *dev, u8 se_idx,
+		       struct nfc_evt_transaction *evt_transaction)
+{
+	int rc;
+
+	pr_debug("transaction: %x\n", se_idx);
+
+	device_lock(&dev->dev);
+
+	if (!evt_transaction) {
+		rc = -EPROTO;
+		goto out;
+	}
+
+	rc = nfc_genl_se_transaction(dev, se_idx, evt_transaction);
+out:
+	device_unlock(&dev->dev);
+	return rc;
+}
+EXPORT_SYMBOL(nfc_se_transaction);
+
 static void nfc_release(struct device *d)
 {
 	struct nfc_dev *dev = to_nfc_dev(d);
diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c
index 91df487..844673c 100644
--- a/net/nfc/hci/command.c
+++ b/net/nfc/hci/command.c
@@ -116,23 +116,6 @@
 }
 EXPORT_SYMBOL(nfc_hci_send_event);
 
-int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response,
-			  const u8 *param, size_t param_len)
-{
-	u8 pipe;
-
-	pr_debug("\n");
-
-	pipe = hdev->gate2pipe[gate];
-	if (pipe == NFC_HCI_INVALID_PIPE)
-		return -EADDRNOTAVAIL;
-
-	return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_RESPONSE,
-				      response, param, param_len, NULL, NULL,
-				      0);
-}
-EXPORT_SYMBOL(nfc_hci_send_response);
-
 /*
  * Execute an hci command sent to gate.
  * skb will contain response data if success. skb can be NULL if you are not
@@ -331,7 +314,7 @@
 	if (r < 0)
 		return r;
 
-	memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
+	nfc_hci_reset_pipes(hdev);
 
 	return 0;
 }
@@ -345,7 +328,7 @@
 
 	pr_debug("\n");
 
-	if (hdev->gate2pipe[dest_gate] == NFC_HCI_DO_NOT_CREATE_PIPE)
+	if (pipe == NFC_HCI_DO_NOT_CREATE_PIPE)
 		return 0;
 
 	if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE)
@@ -380,6 +363,8 @@
 		return r;
 	}
 
+	hdev->pipes[pipe].gate = dest_gate;
+	hdev->pipes[pipe].dest_host = dest_host;
 	hdev->gate2pipe[dest_gate] = pipe;
 
 	return 0;
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index ef50e77..6e061da 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -46,6 +46,32 @@
 }
 EXPORT_SYMBOL(nfc_hci_result_to_errno);
 
+void nfc_hci_reset_pipes(struct nfc_hci_dev *hdev)
+{
+	int i = 0;
+
+	for (i = 0; i < NFC_HCI_MAX_PIPES; i++) {
+		hdev->pipes[i].gate = NFC_HCI_INVALID_GATE;
+		hdev->pipes[i].dest_host = NFC_HCI_INVALID_HOST;
+	}
+	memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
+}
+EXPORT_SYMBOL(nfc_hci_reset_pipes);
+
+void nfc_hci_reset_pipes_per_host(struct nfc_hci_dev *hdev, u8 host)
+{
+	int i = 0;
+
+	for (i = 0; i < NFC_HCI_MAX_PIPES; i++) {
+		if (hdev->pipes[i].dest_host != host)
+			continue;
+
+		hdev->pipes[i].gate = NFC_HCI_INVALID_GATE;
+		hdev->pipes[i].dest_host = NFC_HCI_INVALID_HOST;
+	}
+}
+EXPORT_SYMBOL(nfc_hci_reset_pipes_per_host);
+
 static void nfc_hci_msg_tx_work(struct work_struct *work)
 {
 	struct nfc_hci_dev *hdev = container_of(work, struct nfc_hci_dev,
@@ -167,48 +193,69 @@
 void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
 			  struct sk_buff *skb)
 {
-	int r = 0;
-	u8 gate = nfc_hci_pipe2gate(hdev, pipe);
-	u8 local_gate, new_pipe;
-	u8 gate_opened = 0x00;
+	u8 gate = hdev->pipes[pipe].gate;
+	u8 status = NFC_HCI_ANY_OK;
+	struct hci_create_pipe_resp *create_info;
+	struct hci_delete_pipe_noti *delete_info;
+	struct hci_all_pipe_cleared_noti *cleared_info;
 
 	pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd);
 
 	switch (cmd) {
 	case NFC_HCI_ADM_NOTIFY_PIPE_CREATED:
 		if (skb->len != 5) {
-			r = -EPROTO;
-			break;
+			status = NFC_HCI_ANY_E_NOK;
+			goto exit;
 		}
+		create_info = (struct hci_create_pipe_resp *)skb->data;
 
-		local_gate = skb->data[3];
-		new_pipe = skb->data[4];
-		nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
-
-		/* save the new created pipe and bind with local gate,
+		/* Save the new created pipe and bind with local gate,
 		 * the description for skb->data[3] is destination gate id
 		 * but since we received this cmd from host controller, we
 		 * are the destination and it is our local gate
 		 */
-		hdev->gate2pipe[local_gate] = new_pipe;
+		hdev->gate2pipe[create_info->dest_gate] = create_info->pipe;
+		hdev->pipes[create_info->pipe].gate = create_info->dest_gate;
+		hdev->pipes[create_info->pipe].dest_host =
+							create_info->src_host;
 		break;
 	case NFC_HCI_ANY_OPEN_PIPE:
-		/* if the pipe is already created, we allow remote host to
-		 * open it
-		 */
-		if (gate != 0xff)
-			nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK,
-					      &gate_opened, 1);
+		if (gate == NFC_HCI_INVALID_GATE) {
+			status = NFC_HCI_ANY_E_NOK;
+			goto exit;
+		}
+		break;
+	case NFC_HCI_ADM_NOTIFY_PIPE_DELETED:
+		if (skb->len != 1) {
+			status = NFC_HCI_ANY_E_NOK;
+			goto exit;
+		}
+		delete_info = (struct hci_delete_pipe_noti *)skb->data;
+
+		hdev->pipes[delete_info->pipe].gate = NFC_HCI_INVALID_GATE;
+		hdev->pipes[delete_info->pipe].dest_host = NFC_HCI_INVALID_HOST;
 		break;
 	case NFC_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED:
-		nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
+		if (skb->len != 1) {
+			status = NFC_HCI_ANY_E_NOK;
+			goto exit;
+		}
+		cleared_info = (struct hci_all_pipe_cleared_noti *)skb->data;
+
+		nfc_hci_reset_pipes_per_host(hdev, cleared_info->host);
 		break;
 	default:
 		pr_info("Discarded unknown cmd %x to gate %x\n", cmd, gate);
-		r = -EINVAL;
 		break;
 	}
 
+	if (hdev->ops->cmd_received)
+		hdev->ops->cmd_received(hdev, pipe, cmd, skb);
+
+exit:
+	nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_RESPONSE,
+			       status, NULL, 0, NULL, NULL, 0);
+
 	kfree_skb(skb);
 }
 
@@ -330,15 +377,15 @@
 			    struct sk_buff *skb)
 {
 	int r = 0;
-	u8 gate = nfc_hci_pipe2gate(hdev, pipe);
+	u8 gate = hdev->pipes[pipe].gate;
 
-	if (gate == 0xff) {
+	if (gate == NFC_HCI_INVALID_GATE) {
 		pr_err("Discarded event %x to unopened pipe %x\n", event, pipe);
 		goto exit;
 	}
 
 	if (hdev->ops->event_received) {
-		r = hdev->ops->event_received(hdev, gate, event, skb);
+		r = hdev->ops->event_received(hdev, pipe, event, skb);
 		if (r <= 0)
 			goto exit_noskb;
 	}
@@ -573,7 +620,7 @@
 	if (hdev->ops->close)
 		hdev->ops->close(hdev);
 
-	memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
+	nfc_hci_reset_pipes(hdev);
 
 	return 0;
 }
@@ -932,7 +979,7 @@
 
 	nfc_set_drvdata(hdev->ndev, hdev);
 
-	memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
+	nfc_hci_reset_pipes(hdev);
 
 	hdev->quirks = quirks;
 
diff --git a/net/nfc/hci/hci.h b/net/nfc/hci/hci.h
index c3d2e2c..ab4c8e8 100644
--- a/net/nfc/hci/hci.h
+++ b/net/nfc/hci/hci.h
@@ -65,6 +65,14 @@
 	u8 pipe;
 } __packed;
 
+struct hci_delete_pipe_noti {
+	u8 pipe;
+} __packed;
+
+struct hci_all_pipe_cleared_noti {
+	u8 host;
+} __packed;
+
 #define NFC_HCI_FRAGMENT	0x7f
 
 #define HCP_HEADER(type, instr) ((((type) & 0x03) << 6) | ((instr) & 0x3f))
@@ -77,8 +85,6 @@
 			   data_exchange_cb_t cb, void *cb_context,
 			   unsigned long completion_delay);
 
-u8 nfc_hci_pipe2gate(struct nfc_hci_dev *hdev, u8 pipe);
-
 void nfc_hci_hcp_message_rx(struct nfc_hci_dev *hdev, u8 pipe, u8 type,
 			    u8 instruction, struct sk_buff *skb);
 
diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c
index e9de151..1fe725d 100644
--- a/net/nfc/hci/hcp.c
+++ b/net/nfc/hci/hcp.c
@@ -124,17 +124,6 @@
 	return err;
 }
 
-u8 nfc_hci_pipe2gate(struct nfc_hci_dev *hdev, u8 pipe)
-{
-	int gate;
-
-	for (gate = 0; gate < NFC_HCI_MAX_GATES; gate++)
-		if (hdev->gate2pipe[gate] == pipe)
-			return gate;
-
-	return 0xff;
-}
-
 /*
  * Receive hcp message for pipe, with type and cmd.
  * skb contains optional message data only.
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index e181e29..03bbe8c 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -750,8 +750,13 @@
 	return ret;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int llcp_sock_sendmsg(struct socket *sock, struct msghdr *msg,
+			     size_t len)
+#else
 static int llcp_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 			     struct msghdr *msg, size_t len)
+#endif
 {
 	struct sock *sk = sock->sk;
 	struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
@@ -793,8 +798,13 @@
 	return nfc_llcp_send_i_frame(llcp_sock, msg, len);
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int llcp_sock_recvmsg(struct socket *sock, struct msghdr *msg,
+			     size_t len, int flags)
+#else
 static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 			     struct msghdr *msg, size_t len, int flags)
+#endif
 {
 	int noblock = flags & MSG_DONTWAIT;
 	struct sock *sk = sock->sk;
diff --git a/net/nfc/nci/Makefile b/net/nfc/nci/Makefile
index 268baec..c23bb75 100644
--- a/net/nfc/nci/Makefile
+++ b/net/nfc/nci/Makefile
@@ -4,6 +4,6 @@
 
 obj-$(CPTCFG_NFC_NCI) += nci.o
 
-nci-objs := core.o data.o lib.o ntf.o rsp.o
+nci-objs := core.o data.o lib.o ntf.o rsp.o hci.o
 
 nci-$(CPTCFG_NFC_NCI_SPI) += spi.o
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 51feb5e..49ff321 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -41,10 +41,28 @@
 #include <net/nfc/nci_core.h>
 #include <linux/nfc.h>
 
+struct core_conn_create_data {
+	int length;
+	struct nci_core_conn_create_cmd *cmd;
+};
+
 static void nci_cmd_work(struct work_struct *work);
 static void nci_rx_work(struct work_struct *work);
 static void nci_tx_work(struct work_struct *work);
 
+struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev,
+						   int conn_id)
+{
+	struct nci_conn_info *conn_info;
+
+	list_for_each_entry(conn_info, &ndev->conn_info_list, list) {
+		if (conn_info->conn_id == conn_id)
+			return conn_info;
+	}
+
+	return NULL;
+}
+
 /* ---- NCI requests ---- */
 
 void nci_req_complete(struct nci_dev *ndev, int result)
@@ -109,10 +127,10 @@
 	return rc;
 }
 
-static inline int nci_request(struct nci_dev *ndev,
-			      void (*req)(struct nci_dev *ndev,
-					  unsigned long opt),
-			      unsigned long opt, __u32 timeout)
+inline int nci_request(struct nci_dev *ndev,
+		       void (*req)(struct nci_dev *ndev,
+				   unsigned long opt),
+		       unsigned long opt, __u32 timeout)
 {
 	int rc;
 
@@ -456,6 +474,95 @@
 }
 EXPORT_SYMBOL(nci_set_config);
 
+static void nci_nfcee_discover_req(struct nci_dev *ndev, unsigned long opt)
+{
+	struct nci_nfcee_discover_cmd cmd;
+	__u8 action = opt;
+
+	cmd.discovery_action = action;
+
+	nci_send_cmd(ndev, NCI_OP_NFCEE_DISCOVER_CMD, 1, &cmd);
+}
+
+int nci_nfcee_discover(struct nci_dev *ndev, u8 action)
+{
+	return nci_request(ndev, nci_nfcee_discover_req, action,
+				msecs_to_jiffies(NCI_CMD_TIMEOUT));
+}
+EXPORT_SYMBOL(nci_nfcee_discover);
+
+static void nci_nfcee_mode_set_req(struct nci_dev *ndev, unsigned long opt)
+{
+	struct nci_nfcee_mode_set_cmd *cmd =
+					(struct nci_nfcee_mode_set_cmd *)opt;
+
+	nci_send_cmd(ndev, NCI_OP_NFCEE_MODE_SET_CMD,
+		     sizeof(struct nci_nfcee_mode_set_cmd), cmd);
+}
+
+int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode)
+{
+	struct nci_nfcee_mode_set_cmd cmd;
+
+	cmd.nfcee_id = nfcee_id;
+	cmd.nfcee_mode = nfcee_mode;
+
+	return nci_request(ndev, nci_nfcee_mode_set_req, (unsigned long)&cmd,
+			   msecs_to_jiffies(NCI_CMD_TIMEOUT));
+}
+EXPORT_SYMBOL(nci_nfcee_mode_set);
+
+static void nci_core_conn_create_req(struct nci_dev *ndev, unsigned long opt)
+{
+	struct core_conn_create_data *data =
+					(struct core_conn_create_data *)opt;
+
+	nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, data->length, data->cmd);
+}
+
+int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
+			 u8 number_destination_params,
+			 size_t params_len,
+			 struct core_conn_create_dest_spec_params *params)
+{
+	int r;
+	struct nci_core_conn_create_cmd *cmd;
+	struct core_conn_create_data data;
+
+	data.length = params_len + sizeof(struct nci_core_conn_create_cmd);
+	cmd = kzalloc(data.length, GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	cmd->destination_type = destination_type;
+	cmd->number_destination_params = number_destination_params;
+	memcpy(cmd->params, params, params_len);
+
+	data.cmd = cmd;
+	ndev->cur_id = params->value[DEST_SPEC_PARAMS_ID_INDEX];
+
+	r = __nci_request(ndev, nci_core_conn_create_req,
+			  (unsigned long)&data,
+			  msecs_to_jiffies(NCI_CMD_TIMEOUT));
+	kfree(cmd);
+	return r;
+}
+EXPORT_SYMBOL(nci_core_conn_create);
+
+static void nci_core_conn_close_req(struct nci_dev *ndev, unsigned long opt)
+{
+	__u8 conn_id = opt;
+
+	nci_send_cmd(ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 1, &conn_id);
+}
+
+int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id)
+{
+	return nci_request(ndev, nci_core_conn_close_req, conn_id,
+				msecs_to_jiffies(NCI_CMD_TIMEOUT));
+}
+EXPORT_SYMBOL(nci_core_conn_close);
+
 static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
 {
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
@@ -712,6 +819,11 @@
 {
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
 	int rc;
+	struct nci_conn_info    *conn_info;
+
+	conn_info = ndev->rf_conn_info;
+	if (!conn_info)
+		return -EPROTO;
 
 	pr_debug("target_idx %d, len %d\n", target->idx, skb->len);
 
@@ -724,8 +836,8 @@
 		return -EBUSY;
 
 	/* store cb and context to be used on receiving data */
-	ndev->data_exchange_cb = cb;
-	ndev->data_exchange_cb_context = cb_context;
+	conn_info->data_exchange_cb = cb;
+	conn_info->data_exchange_cb_context = cb_context;
 
 	rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
 	if (rc)
@@ -768,10 +880,16 @@
 
 static int nci_discover_se(struct nfc_dev *nfc_dev)
 {
+	int r;
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
 
-	if (ndev->ops->discover_se)
+	if (ndev->ops->discover_se) {
+		r = nci_nfcee_discover(ndev, NCI_NFCEE_DISCOVERY_ACTION_ENABLE);
+		if (r != NCI_STATUS_OK)
+			return -EPROTO;
+
 		return ndev->ops->discover_se(ndev);
+	}
 
 	return 0;
 }
@@ -789,6 +907,16 @@
 	return 0;
 }
 
+static int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name)
+{
+	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+	if (!ndev->ops->fw_download)
+		return -ENOTSUPP;
+
+	return ndev->ops->fw_download(ndev, firmware_name);
+}
+
 static struct nfc_ops nci_nfc_ops = {
 	.dev_up = nci_dev_up,
 	.dev_down = nci_dev_down,
@@ -804,10 +932,10 @@
 	.disable_se = nci_disable_se,
 	.discover_se = nci_discover_se,
 	.se_io = nci_se_io,
+	.fw_download = nci_fw_download,
 };
 
 /* ---- Interface to NCI drivers ---- */
-
 /**
  * nci_allocate_device - allocate a new nci device
  *
@@ -842,13 +970,20 @@
 					    tx_headroom + NCI_DATA_HDR_SIZE,
 					    tx_tailroom);
 	if (!ndev->nfc_dev)
-		goto free_exit;
+		goto free_nci;
+
+	ndev->hci_dev = nci_hci_allocate(ndev);
+	if (!ndev->hci_dev)
+		goto free_nfc;
 
 	nfc_set_drvdata(ndev->nfc_dev, ndev);
 
 	return ndev;
 
-free_exit:
+free_nfc:
+	kfree(ndev->nfc_dev);
+
+free_nci:
 	kfree(ndev);
 	return NULL;
 }
@@ -913,6 +1048,7 @@
 		    (unsigned long) ndev);
 
 	mutex_init(&ndev->req_lock);
+	INIT_LIST_HEAD(&ndev->conn_info_list);
 
 	rc = nfc_register_device(ndev->nfc_dev);
 	if (rc)
@@ -938,12 +1074,19 @@
  */
 void nci_unregister_device(struct nci_dev *ndev)
 {
+	struct nci_conn_info    *conn_info, *n;
+
 	nci_close_device(ndev);
 
 	destroy_workqueue(ndev->cmd_wq);
 	destroy_workqueue(ndev->rx_wq);
 	destroy_workqueue(ndev->tx_wq);
 
+	list_for_each_entry_safe(conn_info, n, &ndev->conn_info_list, list) {
+		list_del(&conn_info->list);
+		/* conn_info is allocated with devm_kzalloc */
+	}
+
 	nfc_unregister_device(ndev->nfc_dev);
 }
 EXPORT_SYMBOL(nci_unregister_device);
@@ -1027,20 +1170,25 @@
 static void nci_tx_work(struct work_struct *work)
 {
 	struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work);
+	struct nci_conn_info    *conn_info;
 	struct sk_buff *skb;
 
-	pr_debug("credits_cnt %d\n", atomic_read(&ndev->credits_cnt));
+	conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id);
+	if (!conn_info)
+		return;
+
+	pr_debug("credits_cnt %d\n", atomic_read(&conn_info->credits_cnt));
 
 	/* Send queued tx data */
-	while (atomic_read(&ndev->credits_cnt)) {
+	while (atomic_read(&conn_info->credits_cnt)) {
 		skb = skb_dequeue(&ndev->tx_q);
 		if (!skb)
 			return;
 
 		/* Check if data flow control is used */
-		if (atomic_read(&ndev->credits_cnt) !=
+		if (atomic_read(&conn_info->credits_cnt) !=
 		    NCI_DATA_FLOW_CONTROL_NOT_USED)
-			atomic_dec(&ndev->credits_cnt);
+			atomic_dec(&conn_info->credits_cnt);
 
 		pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n",
 			 nci_pbf(skb->data),
@@ -1092,7 +1240,9 @@
 	if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) {
 		/* complete the data exchange transaction, if exists */
 		if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
-			nci_data_exchange_complete(ndev, NULL, -ETIMEDOUT);
+			nci_data_exchange_complete(ndev, NULL,
+						   ndev->cur_conn_id,
+						   -ETIMEDOUT);
 
 		clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
 	}
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index a2de2a8..566466d 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -36,10 +36,20 @@
 
 /* Complete data exchange transaction and forward skb to nfc core */
 void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
-				int err)
+				__u8 conn_id, int err)
 {
-	data_exchange_cb_t cb = ndev->data_exchange_cb;
-	void *cb_context = ndev->data_exchange_cb_context;
+	struct nci_conn_info    *conn_info;
+	data_exchange_cb_t cb;
+	void *cb_context;
+
+	conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
+	if (!conn_info) {
+		kfree_skb(skb);
+		goto exit;
+	}
+
+	cb = conn_info->data_exchange_cb;
+	cb_context = conn_info->data_exchange_cb_context;
 
 	pr_debug("len %d, err %d\n", skb ? skb->len : 0, err);
 
@@ -48,9 +58,6 @@
 	clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
 
 	if (cb) {
-		ndev->data_exchange_cb = NULL;
-		ndev->data_exchange_cb_context = NULL;
-
 		/* forward skb to nfc core */
 		cb(cb_context, skb, err);
 	} else if (skb) {
@@ -60,6 +67,7 @@
 		kfree_skb(skb);
 	}
 
+exit:
 	clear_bit(NCI_DATA_EXCHANGE, &ndev->flags);
 }
 
@@ -85,6 +93,7 @@
 static int nci_queue_tx_data_frags(struct nci_dev *ndev,
 				   __u8 conn_id,
 				   struct sk_buff *skb) {
+	struct nci_conn_info    *conn_info;
 	int total_len = skb->len;
 	unsigned char *data = skb->data;
 	unsigned long flags;
@@ -95,11 +104,17 @@
 
 	pr_debug("conn_id 0x%x, total_len %d\n", conn_id, total_len);
 
+	conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
+	if (!conn_info) {
+		rc = -EPROTO;
+		goto free_exit;
+	}
+
 	__skb_queue_head_init(&frags_q);
 
 	while (total_len) {
 		frag_len =
-			min_t(int, total_len, ndev->max_data_pkt_payload_size);
+			min_t(int, total_len, conn_info->max_pkt_payload_len);
 
 		skb_frag = nci_skb_alloc(ndev,
 					 (NCI_DATA_HDR_SIZE + frag_len),
@@ -151,12 +166,19 @@
 /* Send NCI data */
 int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb)
 {
+	struct nci_conn_info    *conn_info;
 	int rc = 0;
 
 	pr_debug("conn_id 0x%x, plen %d\n", conn_id, skb->len);
 
+	conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
+	if (!conn_info) {
+		rc = -EPROTO;
+		goto free_exit;
+	}
+
 	/* check if the packet need to be fragmented */
-	if (skb->len <= ndev->max_data_pkt_payload_size) {
+	if (skb->len <= conn_info->max_pkt_payload_len) {
 		/* no need to fragment packet */
 		nci_push_data_hdr(ndev, conn_id, skb, NCI_PBF_LAST);
 
@@ -170,6 +192,7 @@
 		}
 	}
 
+	ndev->cur_conn_id = conn_id;
 	queue_work(ndev->tx_wq, &ndev->tx_work);
 
 	goto exit;
@@ -185,7 +208,7 @@
 
 static void nci_add_rx_data_frag(struct nci_dev *ndev,
 				 struct sk_buff *skb,
-				 __u8 pbf, __u8 status)
+				 __u8 pbf, __u8 conn_id, __u8 status)
 {
 	int reassembly_len;
 	int err = 0;
@@ -229,16 +252,13 @@
 	}
 
 exit:
-	if (ndev->nfc_dev->rf_mode == NFC_RF_INITIATOR) {
-		nci_data_exchange_complete(ndev, skb, err);
-	} else if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) {
+	if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) {
 		/* Data received in Target mode, forward to nfc core */
 		err = nfc_tm_data_received(ndev->nfc_dev, skb);
 		if (err)
 			pr_err("unable to handle received data\n");
 	} else {
-		pr_err("rf mode unknown\n");
-		kfree_skb(skb);
+		nci_data_exchange_complete(ndev, skb, conn_id, err);
 	}
 }
 
@@ -247,6 +267,8 @@
 {
 	__u8 pbf = nci_pbf(skb->data);
 	__u8 status = 0;
+	__u8 conn_id = nci_conn_id(skb->data);
+	struct nci_conn_info    *conn_info;
 
 	pr_debug("len %d\n", skb->len);
 
@@ -255,6 +277,10 @@
 		 nci_conn_id(skb->data),
 		 nci_plen(skb->data));
 
+	conn_info = nci_get_conn_info_by_conn_id(ndev, nci_conn_id(skb->data));
+	if (!conn_info)
+		return;
+
 	/* strip the nci data header */
 	skb_pull(skb, NCI_DATA_HDR_SIZE);
 
@@ -268,5 +294,5 @@
 		skb_trim(skb, (skb->len - 1));
 	}
 
-	nci_add_rx_data_frag(ndev, skb, pbf, nci_to_errno(status));
+	nci_add_rx_data_frag(ndev, skb, pbf, conn_id, nci_to_errno(status));
 }
diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c
new file mode 100644
index 0000000..ed54ec5
--- /dev/null
+++ b/net/nfc/nci/hci.c
@@ -0,0 +1,694 @@
+/*
+ *  The NFC Controller Interface is the communication protocol between an
+ *  NFC Controller (NFCC) and a Device Host (DH).
+ *  This is the HCI over NCI implementation, as specified in the 10.2
+ *  section of the NCI 1.1 specification.
+ *
+ *  Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation
+ *
+ *  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/skbuff.h>
+
+#include "../nfc.h"
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include <linux/nfc.h>
+
+struct nci_data {
+	u8              conn_id;
+	u8              pipe;
+	u8              cmd;
+	const u8        *data;
+	u32             data_len;
+} __packed;
+
+struct nci_hci_create_pipe_params {
+	u8 src_gate;
+	u8 dest_host;
+	u8 dest_gate;
+} __packed;
+
+struct nci_hci_create_pipe_resp {
+	u8 src_host;
+	u8 src_gate;
+	u8 dest_host;
+	u8 dest_gate;
+	u8 pipe;
+} __packed;
+
+struct nci_hci_delete_pipe_noti {
+	u8 pipe;
+} __packed;
+
+struct nci_hci_all_pipe_cleared_noti {
+	u8 host;
+} __packed;
+
+struct nci_hcp_message {
+	u8 header;      /* type -cmd,evt,rsp- + instruction */
+	u8 data[];
+} __packed;
+
+struct nci_hcp_packet {
+	u8 header;      /* cbit+pipe */
+	struct nci_hcp_message message;
+} __packed;
+
+#define NCI_HCI_ANY_SET_PARAMETER  0x01
+#define NCI_HCI_ANY_GET_PARAMETER  0x02
+#define NCI_HCI_ANY_CLOSE_PIPE     0x04
+
+#define NCI_HFP_NO_CHAINING        0x80
+
+#define NCI_NFCEE_ID_HCI                0x80
+
+#define NCI_EVT_HOT_PLUG           0x03
+
+#define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY       0x01
+
+/* HCP headers */
+#define NCI_HCI_HCP_PACKET_HEADER_LEN      1
+#define NCI_HCI_HCP_MESSAGE_HEADER_LEN     1
+#define NCI_HCI_HCP_HEADER_LEN             2
+
+/* HCP types */
+#define NCI_HCI_HCP_COMMAND        0x00
+#define NCI_HCI_HCP_EVENT          0x01
+#define NCI_HCI_HCP_RESPONSE       0x02
+
+#define NCI_HCI_ADM_NOTIFY_PIPE_CREATED     0x12
+#define NCI_HCI_ADM_NOTIFY_PIPE_DELETED     0x13
+#define NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED 0x15
+
+#define NCI_HCI_FRAGMENT           0x7f
+#define NCI_HCP_HEADER(type, instr) ((((type) & 0x03) << 6) |\
+				      ((instr) & 0x3f))
+
+#define NCI_HCP_MSG_GET_TYPE(header) ((header & 0xc0) >> 6)
+#define NCI_HCP_MSG_GET_CMD(header)  (header & 0x3f)
+#define NCI_HCP_MSG_GET_PIPE(header) (header & 0x7f)
+
+/* HCI core */
+static void nci_hci_reset_pipes(struct nci_hci_dev *hdev)
+{
+	int i;
+
+	for (i = 0; i < NCI_HCI_MAX_PIPES; i++) {
+		hdev->pipes[i].gate = NCI_HCI_INVALID_GATE;
+		hdev->pipes[i].host = NCI_HCI_INVALID_HOST;
+	}
+	memset(hdev->gate2pipe, NCI_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
+}
+
+static void nci_hci_reset_pipes_per_host(struct nci_dev *ndev, u8 host)
+{
+	int i;
+
+	for (i = 0; i < NCI_HCI_MAX_PIPES; i++) {
+		if (ndev->hci_dev->pipes[i].host == host) {
+			ndev->hci_dev->pipes[i].gate = NCI_HCI_INVALID_GATE;
+			ndev->hci_dev->pipes[i].host = NCI_HCI_INVALID_HOST;
+		}
+	}
+}
+
+/* Fragment HCI data over NCI packet.
+ * NFC Forum NCI 10.2.2 Data Exchange:
+ * The payload of the Data Packets sent on the Logical Connection SHALL be
+ * valid HCP packets, as defined within [ETSI_102622]. Each Data Packet SHALL
+ * contain a single HCP packet. NCI Segmentation and Reassembly SHALL NOT be
+ * applied to Data Messages in either direction. The HCI fragmentation mechanism
+ * is used if required.
+ */
+static int nci_hci_send_data(struct nci_dev *ndev, u8 pipe,
+			     const u8 data_type, const u8 *data,
+			     size_t data_len)
+{
+	struct nci_conn_info    *conn_info;
+	struct sk_buff *skb;
+	int len, i, r;
+	u8 cb = pipe;
+
+	conn_info = ndev->hci_dev->conn_info;
+	if (!conn_info)
+		return -EPROTO;
+
+	skb = nci_skb_alloc(ndev, 2 + conn_info->max_pkt_payload_len +
+			    NCI_DATA_HDR_SIZE, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_reserve(skb, 2 + NCI_DATA_HDR_SIZE);
+	*skb_push(skb, 1) = data_type;
+
+	i = 0;
+	len = conn_info->max_pkt_payload_len;
+
+	do {
+		/* If last packet add NCI_HFP_NO_CHAINING */
+		if (i + conn_info->max_pkt_payload_len -
+		    (skb->len + 1) >= data_len) {
+			cb |= NCI_HFP_NO_CHAINING;
+			len = data_len - i;
+		} else {
+			len = conn_info->max_pkt_payload_len - skb->len - 1;
+		}
+
+		*skb_push(skb, 1) = cb;
+
+		if (len > 0)
+			memcpy(skb_put(skb, len), data + i, len);
+
+		r = nci_send_data(ndev, conn_info->conn_id, skb);
+		if (r < 0)
+			return r;
+
+		i += len;
+		if (i < data_len) {
+			skb_trim(skb, 0);
+			skb_pull(skb, len);
+		}
+	} while (i < data_len);
+
+	return i;
+}
+
+static void nci_hci_send_data_req(struct nci_dev *ndev, unsigned long opt)
+{
+	struct nci_data *data = (struct nci_data *)opt;
+
+	nci_hci_send_data(ndev, data->pipe, data->cmd,
+			  data->data, data->data_len);
+}
+
+int nci_hci_send_event(struct nci_dev *ndev, u8 gate, u8 event,
+		       const u8 *param, size_t param_len)
+{
+	u8 pipe = ndev->hci_dev->gate2pipe[gate];
+
+	if (pipe == NCI_HCI_INVALID_PIPE)
+		return -EADDRNOTAVAIL;
+
+	return nci_hci_send_data(ndev, pipe,
+			NCI_HCP_HEADER(NCI_HCI_HCP_EVENT, event),
+			param, param_len);
+}
+EXPORT_SYMBOL(nci_hci_send_event);
+
+int nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd,
+		     const u8 *param, size_t param_len,
+		     struct sk_buff **skb)
+{
+	struct nci_conn_info    *conn_info;
+	struct nci_data data;
+	int r;
+	u8 pipe = ndev->hci_dev->gate2pipe[gate];
+
+	if (pipe == NCI_HCI_INVALID_PIPE)
+		return -EADDRNOTAVAIL;
+
+	conn_info = ndev->hci_dev->conn_info;
+	if (!conn_info)
+		return -EPROTO;
+
+	data.conn_id = conn_info->conn_id;
+	data.pipe = pipe;
+	data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, cmd);
+	data.data = param;
+	data.data_len = param_len;
+
+	r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data,
+			msecs_to_jiffies(NCI_DATA_TIMEOUT));
+
+	if (r == NCI_STATUS_OK)
+		*skb = conn_info->rx_skb;
+
+	return r;
+}
+EXPORT_SYMBOL(nci_hci_send_cmd);
+
+static void nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
+				   u8 event, struct sk_buff *skb)
+{
+	if (ndev->ops->hci_event_received)
+		ndev->ops->hci_event_received(ndev, pipe, event, skb);
+}
+
+static void nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe,
+				 u8 cmd, struct sk_buff *skb)
+{
+	u8 gate = ndev->hci_dev->pipes[pipe].gate;
+	u8 status = NCI_HCI_ANY_OK | ~NCI_HCI_FRAGMENT;
+	u8 dest_gate, new_pipe;
+	struct nci_hci_create_pipe_resp *create_info;
+	struct nci_hci_delete_pipe_noti *delete_info;
+	struct nci_hci_all_pipe_cleared_noti *cleared_info;
+
+	pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd);
+
+	switch (cmd) {
+	case NCI_HCI_ADM_NOTIFY_PIPE_CREATED:
+		if (skb->len != 5) {
+			status = NCI_HCI_ANY_E_NOK;
+			goto exit;
+		}
+		create_info = (struct nci_hci_create_pipe_resp *)skb->data;
+		dest_gate = create_info->dest_gate;
+		new_pipe = create_info->pipe;
+
+		/* Save the new created pipe and bind with local gate,
+		 * the description for skb->data[3] is destination gate id
+		 * but since we received this cmd from host controller, we
+		 * are the destination and it is our local gate
+		 */
+		ndev->hci_dev->gate2pipe[dest_gate] = new_pipe;
+		ndev->hci_dev->pipes[new_pipe].gate = dest_gate;
+		ndev->hci_dev->pipes[new_pipe].host =
+						create_info->src_host;
+		break;
+	case NCI_HCI_ANY_OPEN_PIPE:
+		/* If the pipe is not created report an error */
+		if (gate == NCI_HCI_INVALID_GATE) {
+			status = NCI_HCI_ANY_E_NOK;
+			goto exit;
+		}
+		break;
+	case NCI_HCI_ADM_NOTIFY_PIPE_DELETED:
+		if (skb->len != 1) {
+			status = NCI_HCI_ANY_E_NOK;
+			goto exit;
+		}
+		delete_info = (struct nci_hci_delete_pipe_noti *)skb->data;
+
+		ndev->hci_dev->pipes[delete_info->pipe].gate =
+						NCI_HCI_INVALID_GATE;
+		ndev->hci_dev->pipes[delete_info->pipe].host =
+						NCI_HCI_INVALID_HOST;
+		break;
+	case NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED:
+		if (skb->len != 1) {
+			status = NCI_HCI_ANY_E_NOK;
+			goto exit;
+		}
+
+		cleared_info =
+			(struct nci_hci_all_pipe_cleared_noti *)skb->data;
+		nci_hci_reset_pipes_per_host(ndev, cleared_info->host);
+		break;
+	default:
+		pr_debug("Discarded unknown cmd %x to gate %x\n", cmd, gate);
+		break;
+	}
+
+	if (ndev->ops->hci_cmd_received)
+		ndev->ops->hci_cmd_received(ndev, pipe, cmd, skb);
+
+exit:
+	nci_hci_send_data(ndev, pipe, status, NULL, 0);
+
+	kfree_skb(skb);
+}
+
+static void nci_hci_resp_received(struct nci_dev *ndev, u8 pipe,
+				  u8 result, struct sk_buff *skb)
+{
+	struct nci_conn_info    *conn_info;
+	u8 status = result;
+
+	if (result != NCI_HCI_ANY_OK)
+		goto exit;
+
+	conn_info = ndev->hci_dev->conn_info;
+	if (!conn_info) {
+		status = NCI_STATUS_REJECTED;
+		goto exit;
+	}
+
+	conn_info->rx_skb = skb;
+
+exit:
+	nci_req_complete(ndev, status);
+}
+
+/* Receive hcp message for pipe, with type and cmd.
+ * skb contains optional message data only.
+ */
+static void nci_hci_hcp_message_rx(struct nci_dev *ndev, u8 pipe,
+				   u8 type, u8 instruction, struct sk_buff *skb)
+{
+	switch (type) {
+	case NCI_HCI_HCP_RESPONSE:
+		nci_hci_resp_received(ndev, pipe, instruction, skb);
+		break;
+	case NCI_HCI_HCP_COMMAND:
+		nci_hci_cmd_received(ndev, pipe, instruction, skb);
+		break;
+	case NCI_HCI_HCP_EVENT:
+		nci_hci_event_received(ndev, pipe, instruction, skb);
+		break;
+	default:
+		pr_err("UNKNOWN MSG Type %d, instruction=%d\n",
+		       type, instruction);
+		kfree_skb(skb);
+		break;
+	}
+
+	nci_req_complete(ndev, 0);
+}
+
+static void nci_hci_msg_rx_work(struct work_struct *work)
+{
+	struct nci_hci_dev *hdev =
+		container_of(work, struct nci_hci_dev, msg_rx_work);
+	struct sk_buff *skb;
+	struct nci_hcp_message *message;
+	u8 pipe, type, instruction;
+
+	while ((skb = skb_dequeue(&hdev->msg_rx_queue)) != NULL) {
+		pipe = skb->data[0];
+		skb_pull(skb, NCI_HCI_HCP_PACKET_HEADER_LEN);
+		message = (struct nci_hcp_message *)skb->data;
+		type = NCI_HCP_MSG_GET_TYPE(message->header);
+		instruction = NCI_HCP_MSG_GET_CMD(message->header);
+		skb_pull(skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN);
+
+		nci_hci_hcp_message_rx(hdev->ndev, pipe,
+				       type, instruction, skb);
+	}
+}
+
+void nci_hci_data_received_cb(void *context,
+			      struct sk_buff *skb, int err)
+{
+	struct nci_dev *ndev = (struct nci_dev *)context;
+	struct nci_hcp_packet *packet;
+	u8 pipe, type, instruction;
+	struct sk_buff *hcp_skb;
+	struct sk_buff *frag_skb;
+	int msg_len;
+
+	pr_debug("\n");
+
+	if (err) {
+		nci_req_complete(ndev, err);
+		return;
+	}
+
+	packet = (struct nci_hcp_packet *)skb->data;
+	if ((packet->header & ~NCI_HCI_FRAGMENT) == 0) {
+		skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb);
+		return;
+	}
+
+	/* it's the last fragment. Does it need re-aggregation? */
+	if (skb_queue_len(&ndev->hci_dev->rx_hcp_frags)) {
+		pipe = packet->header & NCI_HCI_FRAGMENT;
+		skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb);
+
+		msg_len = 0;
+		skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) {
+			msg_len += (frag_skb->len -
+				    NCI_HCI_HCP_PACKET_HEADER_LEN);
+		}
+
+		hcp_skb = nfc_alloc_recv_skb(NCI_HCI_HCP_PACKET_HEADER_LEN +
+					     msg_len, GFP_KERNEL);
+		if (!hcp_skb) {
+			nci_req_complete(ndev, -ENOMEM);
+			return;
+		}
+
+		*skb_put(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN) = pipe;
+
+		skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) {
+		       msg_len = frag_skb->len - NCI_HCI_HCP_PACKET_HEADER_LEN;
+			memcpy(skb_put(hcp_skb, msg_len), frag_skb->data +
+			       NCI_HCI_HCP_PACKET_HEADER_LEN, msg_len);
+		}
+
+		skb_queue_purge(&ndev->hci_dev->rx_hcp_frags);
+	} else {
+		packet->header &= NCI_HCI_FRAGMENT;
+		hcp_skb = skb;
+	}
+
+	/* if this is a response, dispatch immediately to
+	 * unblock waiting cmd context. Otherwise, enqueue to dispatch
+	 * in separate context where handler can also execute command.
+	 */
+	packet = (struct nci_hcp_packet *)hcp_skb->data;
+	type = NCI_HCP_MSG_GET_TYPE(packet->message.header);
+	if (type == NCI_HCI_HCP_RESPONSE) {
+		pipe = packet->header;
+		instruction = NCI_HCP_MSG_GET_CMD(packet->message.header);
+		skb_pull(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN +
+			 NCI_HCI_HCP_MESSAGE_HEADER_LEN);
+		nci_hci_hcp_message_rx(ndev, pipe, type, instruction, hcp_skb);
+	} else {
+		skb_queue_tail(&ndev->hci_dev->msg_rx_queue, hcp_skb);
+		schedule_work(&ndev->hci_dev->msg_rx_work);
+	}
+}
+
+int nci_hci_open_pipe(struct nci_dev *ndev, u8 pipe)
+{
+	struct nci_data data;
+	struct nci_conn_info    *conn_info;
+
+	conn_info = ndev->hci_dev->conn_info;
+	if (!conn_info)
+		return -EPROTO;
+
+	data.conn_id = conn_info->conn_id;
+	data.pipe = pipe;
+	data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND,
+				       NCI_HCI_ANY_OPEN_PIPE);
+	data.data = NULL;
+	data.data_len = 0;
+
+	return nci_request(ndev, nci_hci_send_data_req,
+			(unsigned long)&data,
+			msecs_to_jiffies(NCI_DATA_TIMEOUT));
+}
+EXPORT_SYMBOL(nci_hci_open_pipe);
+
+int nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx,
+		      const u8 *param, size_t param_len)
+{
+	struct nci_conn_info *conn_info;
+	struct nci_data data;
+	int r;
+	u8 *tmp;
+	u8 pipe = ndev->hci_dev->gate2pipe[gate];
+
+	pr_debug("idx=%d to gate %d\n", idx, gate);
+
+	if (pipe == NCI_HCI_INVALID_PIPE)
+		return -EADDRNOTAVAIL;
+
+	conn_info = ndev->hci_dev->conn_info;
+	if (!conn_info)
+		return -EPROTO;
+
+	tmp = kmalloc(1 + param_len, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	*tmp = idx;
+	memcpy(tmp + 1, param, param_len);
+
+	data.conn_id = conn_info->conn_id;
+	data.pipe = pipe;
+	data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND,
+				       NCI_HCI_ANY_SET_PARAMETER);
+	data.data = tmp;
+	data.data_len = param_len + 1;
+
+	r = nci_request(ndev, nci_hci_send_data_req,
+			(unsigned long)&data,
+			msecs_to_jiffies(NCI_DATA_TIMEOUT));
+
+	kfree(tmp);
+	return r;
+}
+EXPORT_SYMBOL(nci_hci_set_param);
+
+int nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx,
+		      struct sk_buff **skb)
+{
+	struct nci_conn_info    *conn_info;
+	struct nci_data data;
+	int r;
+	u8 pipe = ndev->hci_dev->gate2pipe[gate];
+
+	pr_debug("idx=%d to gate %d\n", idx, gate);
+
+	if (pipe == NCI_HCI_INVALID_PIPE)
+		return -EADDRNOTAVAIL;
+
+	conn_info = ndev->hci_dev->conn_info;
+	if (!conn_info)
+		return -EPROTO;
+
+	data.conn_id = conn_info->conn_id;
+	data.pipe = pipe;
+	data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND,
+				  NCI_HCI_ANY_GET_PARAMETER);
+	data.data = &idx;
+	data.data_len = 1;
+
+	r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data,
+			msecs_to_jiffies(NCI_DATA_TIMEOUT));
+
+	if (r == NCI_STATUS_OK)
+		*skb = conn_info->rx_skb;
+
+	return r;
+}
+EXPORT_SYMBOL(nci_hci_get_param);
+
+int nci_hci_connect_gate(struct nci_dev *ndev,
+			 u8 dest_host, u8 dest_gate, u8 pipe)
+{
+	int r;
+
+	if (pipe == NCI_HCI_DO_NOT_OPEN_PIPE)
+		return 0;
+
+	if (ndev->hci_dev->gate2pipe[dest_gate] != NCI_HCI_INVALID_PIPE)
+		return -EADDRINUSE;
+
+	if (pipe != NCI_HCI_INVALID_PIPE)
+		goto open_pipe;
+
+	switch (dest_gate) {
+	case NCI_HCI_LINK_MGMT_GATE:
+		pipe = NCI_HCI_LINK_MGMT_PIPE;
+	break;
+	case NCI_HCI_ADMIN_GATE:
+		pipe = NCI_HCI_ADMIN_PIPE;
+	break;
+	}
+
+open_pipe:
+	r = nci_hci_open_pipe(ndev, pipe);
+	if (r < 0)
+		return r;
+
+	ndev->hci_dev->pipes[pipe].gate = dest_gate;
+	ndev->hci_dev->pipes[pipe].host = dest_host;
+	ndev->hci_dev->gate2pipe[dest_gate] = pipe;
+
+	return 0;
+}
+EXPORT_SYMBOL(nci_hci_connect_gate);
+
+static int nci_hci_dev_connect_gates(struct nci_dev *ndev,
+				     u8 gate_count,
+				     struct nci_hci_gate *gates)
+{
+	int r;
+
+	while (gate_count--) {
+		r = nci_hci_connect_gate(ndev, gates->dest_host,
+					 gates->gate, gates->pipe);
+		if (r < 0)
+			return r;
+		gates++;
+	}
+
+	return 0;
+}
+
+int nci_hci_dev_session_init(struct nci_dev *ndev)
+{
+	struct nci_conn_info    *conn_info;
+	struct sk_buff *skb;
+	int r;
+
+	ndev->hci_dev->count_pipes = 0;
+	ndev->hci_dev->expected_pipes = 0;
+
+	conn_info = ndev->hci_dev->conn_info;
+	if (!conn_info)
+		return -EPROTO;
+
+	conn_info->data_exchange_cb = nci_hci_data_received_cb;
+	conn_info->data_exchange_cb_context = ndev;
+
+	nci_hci_reset_pipes(ndev->hci_dev);
+
+	if (ndev->hci_dev->init_data.gates[0].gate != NCI_HCI_ADMIN_GATE)
+		return -EPROTO;
+
+	r = nci_hci_connect_gate(ndev,
+				 ndev->hci_dev->init_data.gates[0].dest_host,
+				 ndev->hci_dev->init_data.gates[0].gate,
+				 ndev->hci_dev->init_data.gates[0].pipe);
+	if (r < 0)
+		goto exit;
+
+	r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE,
+			      NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY, &skb);
+	if (r < 0)
+		goto exit;
+
+	if (skb->len &&
+	    skb->len == strlen(ndev->hci_dev->init_data.session_id) &&
+	    memcmp(ndev->hci_dev->init_data.session_id,
+		   skb->data, skb->len) == 0 &&
+	    ndev->ops->hci_load_session) {
+		/* Restore gate<->pipe table from some proprietary location. */
+		r = ndev->ops->hci_load_session(ndev);
+		if (r < 0)
+			goto exit;
+	} else {
+		r = nci_hci_dev_connect_gates(ndev,
+					      ndev->hci_dev->init_data.gate_count,
+					      ndev->hci_dev->init_data.gates);
+		if (r < 0)
+			goto exit;
+
+		r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE,
+				      NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY,
+				      ndev->hci_dev->init_data.session_id,
+				      strlen(ndev->hci_dev->init_data.session_id));
+	}
+	if (r == 0)
+		goto exit;
+
+exit:
+	kfree_skb(skb);
+
+	return r;
+}
+EXPORT_SYMBOL(nci_hci_dev_session_init);
+
+struct nci_hci_dev *nci_hci_allocate(struct nci_dev *ndev)
+{
+	struct nci_hci_dev *hdev;
+
+	hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
+	if (!hdev)
+		return NULL;
+
+	skb_queue_head_init(&hdev->rx_hcp_frags);
+	INIT_WORK(&hdev->msg_rx_work, nci_hci_msg_rx_work);
+	skb_queue_head_init(&hdev->msg_rx_queue);
+	hdev->ndev = ndev;
+
+	return hdev;
+}
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 22e453c..3218071 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -43,6 +43,7 @@
 					     struct sk_buff *skb)
 {
 	struct nci_core_conn_credit_ntf *ntf = (void *) skb->data;
+	struct nci_conn_info	*conn_info;
 	int i;
 
 	pr_debug("num_entries %d\n", ntf->num_entries);
@@ -59,11 +60,13 @@
 			 i, ntf->conn_entries[i].conn_id,
 			 ntf->conn_entries[i].credits);
 
-		if (ntf->conn_entries[i].conn_id == NCI_STATIC_RF_CONN_ID) {
-			/* found static rf connection */
-			atomic_add(ntf->conn_entries[i].credits,
-				   &ndev->credits_cnt);
-		}
+		conn_info = nci_get_conn_info_by_conn_id(ndev,
+							 ntf->conn_entries[i].conn_id);
+		if (!conn_info)
+			return;
+
+		atomic_add(ntf->conn_entries[i].credits,
+			   &conn_info->credits_cnt);
 	}
 
 	/* trigger the next tx */
@@ -96,7 +99,7 @@
 
 	/* complete the data exchange transaction, if exists */
 	if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
-		nci_data_exchange_complete(ndev, NULL, -EIO);
+		nci_data_exchange_complete(ndev, NULL, ntf->conn_id, -EIO);
 }
 
 static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev,
@@ -513,6 +516,7 @@
 static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
 					     struct sk_buff *skb)
 {
+	struct nci_conn_info    *conn_info;
 	struct nci_rf_intf_activated_ntf ntf;
 	__u8 *data = skb->data;
 	int err = NCI_STATUS_OK;
@@ -537,6 +541,13 @@
 	pr_debug("rf_tech_specific_params_len %d\n",
 		 ntf.rf_tech_specific_params_len);
 
+	/* If this contains a value of 0x00 (NFCEE Direct RF
+	 * Interface) then all following parameters SHALL contain a
+	 * value of 0 and SHALL be ignored.
+	 */
+	if (ntf.rf_interface == NCI_RF_INTERFACE_NFCEE_DIRECT)
+		goto listen;
+
 	if (ntf.rf_tech_specific_params_len > 0) {
 		switch (ntf.activation_rf_tech_and_mode) {
 		case NCI_NFC_A_PASSIVE_POLL_MODE:
@@ -614,11 +625,16 @@
 
 exit:
 	if (err == NCI_STATUS_OK) {
-		ndev->max_data_pkt_payload_size = ntf.max_data_pkt_payload_size;
-		ndev->initial_num_credits = ntf.initial_num_credits;
+		conn_info = ndev->rf_conn_info;
+		if (!conn_info)
+			return;
+
+		conn_info->max_pkt_payload_len = ntf.max_data_pkt_payload_size;
+		conn_info->initial_num_credits = ntf.initial_num_credits;
 
 		/* set the available credits to initial value */
-		atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
+		atomic_set(&conn_info->credits_cnt,
+			   conn_info->initial_num_credits);
 
 		/* store general bytes to be reported later in dep_link_up */
 		if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) {
@@ -643,6 +659,7 @@
 			nci_req_complete(ndev, err);
 		}
 	} else {
+listen:
 		/* Listen mode */
 		atomic_set(&ndev->state, NCI_LISTEN_ACTIVE);
 		if (err == NCI_STATUS_OK &&
@@ -661,10 +678,15 @@
 static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev,
 					 struct sk_buff *skb)
 {
+	struct nci_conn_info    *conn_info;
 	struct nci_rf_deactivate_ntf *ntf = (void *) skb->data;
 
 	pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason);
 
+	conn_info = ndev->rf_conn_info;
+	if (!conn_info)
+		return;
+
 	/* drop tx data queue */
 	skb_queue_purge(&ndev->tx_q);
 
@@ -676,7 +698,8 @@
 
 	/* complete the data exchange transaction, if exists */
 	if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
-		nci_data_exchange_complete(ndev, NULL, -EIO);
+		nci_data_exchange_complete(ndev, NULL, NCI_STATIC_RF_CONN_ID,
+					   -EIO);
 
 	switch (ntf->type) {
 	case NCI_DEACTIVATE_TYPE_IDLE_MODE:
@@ -696,6 +719,32 @@
 	nci_req_complete(ndev, NCI_STATUS_OK);
 }
 
+static void nci_nfcee_discover_ntf_packet(struct nci_dev *ndev,
+					  struct sk_buff *skb)
+{
+	u8 status = NCI_STATUS_OK;
+	struct nci_nfcee_discover_ntf   *nfcee_ntf =
+				(struct nci_nfcee_discover_ntf *)skb->data;
+
+	pr_debug("\n");
+
+	/* NFCForum NCI 9.2.1 HCI Network Specific Handling
+	 * If the NFCC supports the HCI Network, it SHALL return one,
+	 * and only one, NFCEE_DISCOVER_NTF with a Protocol type of
+	 * “HCI Access”, even if the HCI Network contains multiple NFCEEs.
+	 */
+	ndev->hci_dev->nfcee_id = nfcee_ntf->nfcee_id;
+	ndev->cur_id = nfcee_ntf->nfcee_id;
+
+	nci_req_complete(ndev, status);
+}
+
+static void nci_nfcee_action_ntf_packet(struct nci_dev *ndev,
+					struct sk_buff *skb)
+{
+	pr_debug("\n");
+}
+
 void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
 	__u16 ntf_opcode = nci_opcode(skb->data);
@@ -734,6 +783,14 @@
 		nci_rf_deactivate_ntf_packet(ndev, skb);
 		break;
 
+	case NCI_OP_NFCEE_DISCOVER_NTF:
+		nci_nfcee_discover_ntf_packet(ndev, skb);
+		break;
+
+	case NCI_OP_RF_NFCEE_ACTION_NTF:
+		nci_nfcee_action_ntf_packet(ndev, skb);
+		break;
+
 	default:
 		pr_err("unknown ntf opcode 0x%x\n", ntf_opcode);
 		break;
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index 041de51..02486bc 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -140,13 +140,31 @@
 
 static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
+	struct nci_conn_info    *conn_info;
 	__u8 status = skb->data[0];
 
 	pr_debug("status 0x%x\n", status);
 
-	if (status == NCI_STATUS_OK)
+	if (status == NCI_STATUS_OK) {
 		atomic_set(&ndev->state, NCI_DISCOVERY);
 
+		conn_info = ndev->rf_conn_info;
+		if (!conn_info) {
+			conn_info = devm_kzalloc(&ndev->nfc_dev->dev,
+						 sizeof(struct nci_conn_info),
+						 GFP_KERNEL);
+			if (!conn_info) {
+				status = NCI_STATUS_REJECTED;
+				goto exit;
+			}
+			conn_info->conn_id = NCI_STATIC_RF_CONN_ID;
+			INIT_LIST_HEAD(&conn_info->list);
+			list_add(&conn_info->list, &ndev->conn_info_list);
+			ndev->rf_conn_info = conn_info;
+		}
+	}
+
+exit:
 	nci_req_complete(ndev, status);
 }
 
@@ -178,6 +196,90 @@
 	}
 }
 
+static void nci_nfcee_discover_rsp_packet(struct nci_dev *ndev,
+					  struct sk_buff *skb)
+{
+	struct nci_nfcee_discover_rsp *discover_rsp;
+
+	if (skb->len != 2) {
+		nci_req_complete(ndev, NCI_STATUS_NFCEE_PROTOCOL_ERROR);
+		return;
+	}
+
+	discover_rsp = (struct nci_nfcee_discover_rsp *)skb->data;
+
+	if (discover_rsp->status != NCI_STATUS_OK ||
+	    discover_rsp->num_nfcee == 0)
+		nci_req_complete(ndev, discover_rsp->status);
+}
+
+static void nci_nfcee_mode_set_rsp_packet(struct nci_dev *ndev,
+					  struct sk_buff *skb)
+{
+	__u8 status = skb->data[0];
+
+	pr_debug("status 0x%x\n", status);
+	nci_req_complete(ndev, status);
+}
+
+static void nci_core_conn_create_rsp_packet(struct nci_dev *ndev,
+					    struct sk_buff *skb)
+{
+	__u8 status = skb->data[0];
+	struct nci_conn_info *conn_info;
+	struct nci_core_conn_create_rsp *rsp;
+
+	pr_debug("status 0x%x\n", status);
+
+	if (status == NCI_STATUS_OK) {
+		rsp = (struct nci_core_conn_create_rsp *)skb->data;
+
+		conn_info = devm_kzalloc(&ndev->nfc_dev->dev,
+					 sizeof(*conn_info), GFP_KERNEL);
+		if (!conn_info) {
+			status = NCI_STATUS_REJECTED;
+			goto exit;
+		}
+
+		conn_info->id = ndev->cur_id;
+		conn_info->conn_id = rsp->conn_id;
+
+		/* Note: data_exchange_cb and data_exchange_cb_context need to
+		 * be specify out of nci_core_conn_create_rsp_packet
+		 */
+
+		INIT_LIST_HEAD(&conn_info->list);
+		list_add(&conn_info->list, &ndev->conn_info_list);
+
+		if (ndev->cur_id == ndev->hci_dev->nfcee_id)
+			ndev->hci_dev->conn_info = conn_info;
+
+		conn_info->conn_id = rsp->conn_id;
+		conn_info->max_pkt_payload_len = rsp->max_ctrl_pkt_payload_len;
+		atomic_set(&conn_info->credits_cnt, rsp->credits_cnt);
+	}
+
+exit:
+	nci_req_complete(ndev, status);
+}
+
+static void nci_core_conn_close_rsp_packet(struct nci_dev *ndev,
+					   struct sk_buff *skb)
+{
+	struct nci_conn_info *conn_info;
+	__u8 status = skb->data[0];
+
+	pr_debug("status 0x%x\n", status);
+	if (status == NCI_STATUS_OK) {
+		conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_id);
+		if (conn_info) {
+			list_del(&conn_info->list);
+			devm_kfree(&ndev->nfc_dev->dev, conn_info);
+		}
+	}
+	nci_req_complete(ndev, status);
+}
+
 void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
 	__u16 rsp_opcode = nci_opcode(skb->data);
@@ -207,6 +309,14 @@
 		nci_core_set_config_rsp_packet(ndev, skb);
 		break;
 
+	case NCI_OP_CORE_CONN_CREATE_RSP:
+		nci_core_conn_create_rsp_packet(ndev, skb);
+		break;
+
+	case NCI_OP_CORE_CONN_CLOSE_RSP:
+		nci_core_conn_close_rsp_packet(ndev, skb);
+		break;
+
 	case NCI_OP_RF_DISCOVER_MAP_RSP:
 		nci_rf_disc_map_rsp_packet(ndev, skb);
 		break;
@@ -223,6 +333,14 @@
 		nci_rf_deactivate_rsp_packet(ndev, skb);
 		break;
 
+	case NCI_OP_NFCEE_DISCOVER_RSP:
+		nci_nfcee_discover_rsp_packet(ndev, skb);
+		break;
+
+	case NCI_OP_NFCEE_MODE_SET_RSP:
+		nci_nfcee_mode_set_rsp_packet(ndev, skb);
+		break;
+
 	default:
 		pr_err("unknown rsp opcode 0x%x\n", rsp_opcode);
 		break;
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 99c7065..3214bfe 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -69,8 +69,7 @@
 {
 	void *hdr;
 
-	hdr = genlmsg_put(msg, NETLINK_CB_PORTID(cb->skb),
-			  cb->nlh->nlmsg_seq,
+	hdr = genlmsg_put(msg, NETLINK_CB_PORTID(cb->skb), cb->nlh->nlmsg_seq,
 			  &nfc_genl_family, flags, NFC_CMD_GET_TARGET);
 	if (!hdr)
 		return -EMSGSIZE;
@@ -103,7 +102,8 @@
 			goto nla_put_failure;
 	}
 
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
 nla_put_failure:
 	genlmsg_cancel(msg, hdr);
@@ -497,6 +497,53 @@
 	return -EMSGSIZE;
 }
 
+int nfc_genl_se_transaction(struct nfc_dev *dev, u8 se_idx,
+			    struct nfc_evt_transaction *evt_transaction)
+{
+	struct nfc_se *se;
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+			  NFC_EVENT_SE_TRANSACTION);
+	if (!hdr)
+		goto free_msg;
+
+	se = nfc_find_se(dev, se_idx);
+	if (!se)
+		goto free_msg;
+
+	if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
+	    nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) ||
+	    nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type) ||
+	    nla_put(msg, NFC_ATTR_SE_AID, evt_transaction->aid_len,
+		    evt_transaction->aid) ||
+	    nla_put(msg, NFC_ATTR_SE_PARAMS, evt_transaction->params_len,
+		    evt_transaction->params))
+		goto nla_put_failure;
+
+	/* evt_transaction is no more used */
+	devm_kfree(&dev->dev, evt_transaction);
+
+	genlmsg_end(msg, hdr);
+
+	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
+
+	return 0;
+
+nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+free_msg:
+	/* evt_transaction is no more used */
+	devm_kfree(&dev->dev, evt_transaction);
+	nlmsg_free(msg);
+	return -EMSGSIZE;
+}
+
 static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
 				u32 portid, u32 seq,
 				struct netlink_callback *cb,
@@ -519,7 +566,8 @@
 	    nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
 		goto nla_put_failure;
 
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
 nla_put_failure:
 	genlmsg_cancel(msg, hdr);
@@ -553,8 +601,7 @@
 	while (dev) {
 		int rc;
 
-		rc = nfc_genl_send_device(skb, dev,
-					  NETLINK_CB_PORTID(cb->skb),
+		rc = nfc_genl_send_device(skb, dev, NETLINK_CB_PORTID(cb->skb),
 					  cb->nlh->nlmsg_seq, cb, NLM_F_MULTI);
 		if (rc < 0)
 			break;
@@ -673,8 +720,7 @@
 		goto out_putdev;
 	}
 
-	rc = nfc_genl_send_device(msg, dev, genl_info_snd_portid(info),
-				  info->snd_seq,
+	rc = nfc_genl_send_device(msg, dev, genl_info_snd_portid(info), info->snd_seq,
 				  NULL, 0);
 	if (rc < 0)
 		goto out_free;
@@ -911,7 +957,8 @@
 	    nla_put_u16(msg, NFC_ATTR_LLC_PARAM_MIUX, be16_to_cpu(local->miux)))
 		goto nla_put_failure;
 
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
 nla_put_failure:
 
@@ -1251,8 +1298,7 @@
 		    nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type))
 			goto nla_put_failure;
 
-		if (genlmsg_end(msg, hdr) < 0)
-			goto nla_put_failure;
+		genlmsg_end(msg, hdr);
 	}
 
 	return 0;
@@ -1539,7 +1585,7 @@
 
 struct urelease_work {
 	struct	work_struct w;
-	int	portid;
+	u32	portid;
 };
 
 static void nfc_urelease_event_work(struct work_struct *work)
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index 88d6006..a8ce80b 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -100,6 +100,8 @@
 
 int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type);
 int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx);
+int nfc_genl_se_transaction(struct nfc_dev *dev, u8 se_idx,
+			    struct nfc_evt_transaction *evt_transaction);
 
 struct nfc_dev *nfc_get_device(unsigned int idx);
 
diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c
index 373e138..a01e342 100644
--- a/net/nfc/rawsock.c
+++ b/net/nfc/rawsock.c
@@ -211,8 +211,12 @@
 	}
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int rawsock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
+#else
 static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock,
 			   struct msghdr *msg, size_t len)
+#endif
 {
 	struct sock *sk = sock->sk;
 	struct nfc_dev *dev = nfc_rawsock(sk)->dev;
@@ -248,8 +252,13 @@
 	return len;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+static int rawsock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+			   int flags)
+#else
 static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock,
 			   struct msghdr *msg, size_t len, int flags)
+#endif
 {
 	int noblock = flags & MSG_DONTWAIT;
 	struct sock *sk = sock->sk;
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index bc4e435..14fdadd 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -158,13 +158,21 @@
 	  Most distributions have a CRDA package.  So if unsure, say N.
 
 config CFG80211_WEXT
-	bool
+	bool "cfg80211 wireless extensions compatibility" if !CFG80211_WEXT_EXPORT
 	depends on CFG80211
 	depends on WEXT_CORE
+	default y if CFG80211_WEXT_EXPORT
 	help
 	  Enable this option if you need old userspace for wireless
 	  extensions with cfg80211-based drivers.
 
+config CFG80211_WEXT_EXPORT
+	bool
+	depends on CFG80211
+	help
+	  Drivers should select this option if they require cfg80211's
+	  wext compatibility symbols to be exported.
+
 config LIB80211
 	tristate
 	depends on m
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 073bfe4..865304a 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -320,6 +320,20 @@
 	rtnl_unlock();
 }
 
+static void cfg80211_sched_scan_stop_wk(struct work_struct *work)
+{
+	struct cfg80211_registered_device *rdev;
+
+	rdev = container_of(work, struct cfg80211_registered_device,
+			   sched_scan_stop_wk);
+
+	rtnl_lock();
+
+	__cfg80211_stop_sched_scan(rdev, false);
+
+	rtnl_unlock();
+}
+
 /* exported functions */
 
 struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
@@ -413,6 +427,7 @@
 	INIT_LIST_HEAD(&rdev->destroy_list);
 	spin_lock_init(&rdev->destroy_list_lock);
 	INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
+	INIT_WORK(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk);
 
 #ifdef CPTCFG_CFG80211_DEFAULT_PS
 	rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -567,6 +582,14 @@
 				       BIT(NL80211_IFTYPE_MONITOR)))
 		wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
 
+	if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) &&
+		    (wiphy->regulatory_flags &
+					(REGULATORY_CUSTOM_REG |
+					 REGULATORY_STRICT_REG |
+					 REGULATORY_COUNTRY_IE_FOLLOW_POWER |
+					 REGULATORY_COUNTRY_IE_IGNORE))))
+		return -EINVAL;
+
 	if (WARN_ON(wiphy->coalesce &&
 		    (!wiphy->coalesce->n_rules ||
 		     !wiphy->coalesce->n_patterns) &&
@@ -785,6 +808,7 @@
 	flush_work(&rdev->event_work);
 	cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
 	flush_work(&rdev->destroy_work);
+	flush_work(&rdev->sched_scan_stop_wk);
 
 #ifdef CONFIG_PM
 	if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
@@ -865,6 +889,7 @@
 		      struct wireless_dev *wdev)
 {
 	struct net_device *dev = wdev->netdev;
+	struct cfg80211_sched_scan_request *sched_scan_req;
 
 	ASSERT_RTNL();
 	ASSERT_WDEV_LOCK(wdev);
@@ -875,7 +900,8 @@
 		break;
 	case NL80211_IFTYPE_P2P_CLIENT:
 	case NL80211_IFTYPE_STATION:
-		if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev)
+		sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
+		if (sched_scan_req && dev == sched_scan_req->dev)
 			__cfg80211_stop_sched_scan(rdev, false);
 
 #ifdef CPTCFG_CFG80211_WEXT
@@ -950,6 +976,7 @@
 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	struct cfg80211_registered_device *rdev;
+	struct cfg80211_sched_scan_request *sched_scan_req;
 
 	if (!wdev)
 		return NOTIFY_DONE;
@@ -1023,8 +1050,9 @@
 			___cfg80211_scan_done(rdev, false);
 		}
 
-		if (WARN_ON(rdev->sched_scan_req &&
-			    rdev->sched_scan_req->dev == wdev->netdev)) {
+		sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
+		if (WARN_ON(sched_scan_req &&
+			    sched_scan_req->dev == wdev->netdev)) {
 			__cfg80211_stop_sched_scan(rdev, false);
 		}
 
@@ -1187,6 +1215,7 @@
 	regulatory_exit();
 out_fail_reg:
 	debugfs_remove(ieee80211_debugfs_dir);
+	nl80211_exit();
 out_fail_nl80211:
 	unregister_netdevice_notifier(&cfg80211_netdev_notifier);
 out_fail_notifier:
diff --git a/net/wireless/core.h b/net/wireless/core.h
index df247e3..cbe5f34 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -36,6 +36,13 @@
 	 * the country on the country IE changed. */
 	char country_ie_alpha2[2];
 
+	/*
+	 * the driver requests the regulatory core to set this regulatory
+	 * domain as the wiphy's. Only used for %REGULATORY_WIPHY_SELF_MANAGED
+	 * devices using the regulatory_set_wiphy_regd() API
+	 */
+	const struct ieee80211_regdomain *requested_regd;
+
 	/* If a Country IE has been received this tells us the environment
 	 * which its telling us its in. This defaults to ENVIRON_ANY */
 	enum environment_cap env;
@@ -63,7 +70,7 @@
 	u32 bss_generation;
 	struct cfg80211_scan_request *scan_req; /* protected by RTNL */
 	struct sk_buff *scan_msg;
-	struct cfg80211_sched_scan_request *sched_scan_req;
+	struct cfg80211_sched_scan_request __rcu *sched_scan_req;
 	unsigned long suspend_at;
 	struct work_struct scan_done_wk;
 	struct work_struct sched_scan_results_wk;
@@ -84,6 +91,8 @@
 	struct list_head destroy_list;
 	struct work_struct destroy_work;
 
+	struct work_struct sched_scan_stop_wk;
+
 	/* 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);
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 49a70c9..bb4008c 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -30,7 +30,7 @@
 		return;
 
 	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
-			       WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
+			       IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
 
 	if (WARN_ON(!bss))
 		return;
@@ -533,7 +533,7 @@
 	else if (wdev->wext.ibss.bssid)
 		memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN);
 	else
-		memset(ap_addr->sa_data, 0, ETH_ALEN);
+		eth_zero_addr(ap_addr->sa_data);
 
 	wdev_unlock(wdev);
 
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 9b18d93..9d2f246 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -229,7 +229,8 @@
 		return -EALREADY;
 
 	req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
-				   WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+				   IEEE80211_BSS_TYPE_ESS,
+				   IEEE80211_PRIVACY_ANY);
 	if (!req.bss)
 		return -ENOENT;
 
@@ -296,7 +297,8 @@
 				   rdev->wiphy.vht_capa_mod_mask);
 
 	req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
-				    WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+				    IEEE80211_BSS_TYPE_ESS,
+				    IEEE80211_PRIVACY_ANY);
 	if (!req->bss)
 		return -ENOENT;
 
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index b800567..85c4aa4 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -59,13 +59,13 @@
 };
 
 static __genl_const struct genl_multicast_group nl80211_mcgrps[] = {
-	[NL80211_MCGRP_CONFIG] = { .name = "config", },
-	[NL80211_MCGRP_SCAN] = { .name = "scan", },
-	[NL80211_MCGRP_REGULATORY] = { .name = "regulatory", },
-	[NL80211_MCGRP_MLME] = { .name = "mlme", },
-	[NL80211_MCGRP_VENDOR] = { .name = "vendor", },
+	[NL80211_MCGRP_CONFIG] = { .name = NL80211_MULTICAST_GROUP_CONFIG },
+	[NL80211_MCGRP_SCAN] = { .name = NL80211_MULTICAST_GROUP_SCAN },
+	[NL80211_MCGRP_REGULATORY] = { .name = NL80211_MULTICAST_GROUP_REG },
+	[NL80211_MCGRP_MLME] = { .name = NL80211_MULTICAST_GROUP_MLME },
+	[NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR },
 #ifdef CPTCFG_NL80211_TESTMODE
-	[NL80211_MCGRP_TESTMODE] = { .name = "testmode", }
+	[NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE }
 #endif
 };
 
@@ -396,6 +396,10 @@
 	[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
 	[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
 	[NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
+	[NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
+	[NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 },
+	[NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
+	[NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -1087,10 +1091,13 @@
 			return -ENOBUFS;
 	}
 
-	if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
+	if ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_NET_DETECT) &&
+	    nla_put_u32(msg, NL80211_WOWLAN_TRIG_NET_DETECT,
+			rdev->wiphy.wowlan->max_nd_match_sets))
 		return -ENOBUFS;
 
-	/* TODO: send wowlan net detect */
+	if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
+		return -ENOBUFS;
 
 	nla_nest_end(msg, nl_wowlan);
 
@@ -1701,12 +1708,22 @@
 			       rdev->wiphy.max_num_csa_counters))
 			goto nla_put_failure;
 
+		if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+		    nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+			goto nla_put_failure;
+
+		if (nla_put(msg, NL80211_ATTR_EXT_FEATURES,
+			    sizeof(rdev->wiphy.ext_features),
+			    rdev->wiphy.ext_features))
+			goto nla_put_failure;
+
 		/* done */
 		state->split_start = 0;
 		break;
 	}
  finish:
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
  nla_put_failure:
 	genlmsg_cancel(msg, hdr);
@@ -2391,7 +2408,8 @@
 			goto nla_put_failure;
 	}
 
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
  nla_put_failure:
 	genlmsg_cancel(msg, hdr);
@@ -2637,10 +2655,6 @@
 			return err;
 	}
 
-	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-	if (!msg)
-		return -ENOMEM;
-
 	err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
 				  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
 				  &flags);
@@ -2649,9 +2663,14 @@
 	    !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
 		return -EOPNOTSUPP;
 
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
 	wdev = rdev_add_virtual_intf(rdev,
 				nla_data(info->attrs[NL80211_ATTR_IFNAME]),
-				type, err ? NULL : &flags, &params);
+				NET_NAME_USER, type, err ? NULL : &flags,
+				&params);
 	if (WARN_ON(!wdev)) {
 		nlmsg_free(msg);
 		return -EPROTO;
@@ -2856,12 +2875,14 @@
 	if (!rdev->ops->get_key)
 		return -EOPNOTSUPP;
 
+	if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
+		return -ENOENT;
+
 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 	if (!msg)
 		return -ENOMEM;
 
-	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
-			     0,
+	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0,
 			     NL80211_CMD_NEW_KEY);
 	if (!hdr)
 		goto nla_put_failure;
@@ -2876,10 +2897,6 @@
 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
 		goto nla_put_failure;
 
-	if (pairwise && mac_addr &&
-	    !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
-		return -ENOENT;
-
 	err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie,
 			   get_key_callback);
 
@@ -3050,7 +3067,7 @@
 	wdev_lock(dev->ieee80211_ptr);
 	err = nl80211_key_allowed(dev->ieee80211_ptr);
 
-	if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
+	if (key.type == NL80211_KEYTYPE_GROUP && mac_addr &&
 	    !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
 		err = -ENOENT;
 
@@ -3566,6 +3583,7 @@
 	struct nlattr *rate;
 	u32 bitrate;
 	u16 bitrate_compat;
+	enum nl80211_attrs rate_flg;
 
 	rate = nla_nest_start(msg, attr);
 	if (!rate)
@@ -3582,12 +3600,36 @@
 	    nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat))
 		return false;
 
+	switch (info->bw) {
+	case RATE_INFO_BW_5:
+		rate_flg = NL80211_RATE_INFO_5_MHZ_WIDTH;
+		break;
+	case RATE_INFO_BW_10:
+		rate_flg = NL80211_RATE_INFO_10_MHZ_WIDTH;
+		break;
+	default:
+		WARN_ON(1);
+		/* fall through */
+	case RATE_INFO_BW_20:
+		rate_flg = 0;
+		break;
+	case RATE_INFO_BW_40:
+		rate_flg = NL80211_RATE_INFO_40_MHZ_WIDTH;
+		break;
+	case RATE_INFO_BW_80:
+		rate_flg = NL80211_RATE_INFO_80_MHZ_WIDTH;
+		break;
+	case RATE_INFO_BW_160:
+		rate_flg = NL80211_RATE_INFO_160_MHZ_WIDTH;
+		break;
+	}
+
+	if (rate_flg && nla_put_flag(msg, rate_flg))
+		return false;
+
 	if (info->flags & RATE_INFO_FLAGS_MCS) {
 		if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))
 			return false;
-		if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
-		    nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
-			return false;
 		if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
 		    nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
 			return false;
@@ -3596,18 +3638,6 @@
 			return false;
 		if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))
 			return false;
-		if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
-		    nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
-			return false;
-		if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH &&
-		    nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH))
-			return false;
-		if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH &&
-		    nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH))
-			return false;
-		if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH &&
-		    nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH))
-			return false;
 		if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
 		    nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
 			return false;
@@ -3643,8 +3673,8 @@
 	return true;
 }
 
-static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
-				int flags,
+static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
+				u32 seq, int flags,
 				struct cfg80211_registered_device *rdev,
 				struct net_device *dev,
 				const u8 *mac_addr, struct station_info *sinfo)
@@ -3652,7 +3682,7 @@
 	void *hdr;
 	struct nlattr *sinfoattr, *bss_param;
 
-	hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
+	hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
 	if (!hdr)
 		return -1;
 
@@ -3664,115 +3694,77 @@
 	sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
 	if (!sinfoattr)
 		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_CONNECTED_TIME) &&
-	    nla_put_u32(msg, NL80211_STA_INFO_CONNECTED_TIME,
-			sinfo->connected_time))
-		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_INACTIVE_TIME) &&
-	    nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME,
-			sinfo->inactive_time))
-		goto nla_put_failure;
-	if ((sinfo->filled & (STATION_INFO_RX_BYTES |
-			      STATION_INFO_RX_BYTES64)) &&
+
+#define PUT_SINFO(attr, memb, type) do {				\
+	if (sinfo->filled & BIT(NL80211_STA_INFO_ ## attr) &&		\
+	    nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr,		\
+			     sinfo->memb))				\
+		goto nla_put_failure;					\
+	} while (0)
+
+	PUT_SINFO(CONNECTED_TIME, connected_time, u32);
+	PUT_SINFO(INACTIVE_TIME, inactive_time, u32);
+
+	if (sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES) |
+			     BIT(NL80211_STA_INFO_RX_BYTES64)) &&
 	    nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
 			(u32)sinfo->rx_bytes))
 		goto nla_put_failure;
-	if ((sinfo->filled & (STATION_INFO_TX_BYTES |
-			      STATION_INFO_TX_BYTES64)) &&
+
+	if (sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES) |
+			     BIT(NL80211_STA_INFO_TX_BYTES64)) &&
 	    nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
 			(u32)sinfo->tx_bytes))
 		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_RX_BYTES64) &&
-	    nla_put_u64(msg, NL80211_STA_INFO_RX_BYTES64,
-			sinfo->rx_bytes))
-		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_TX_BYTES64) &&
-	    nla_put_u64(msg, NL80211_STA_INFO_TX_BYTES64,
-			sinfo->tx_bytes))
-		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_LLID) &&
-	    nla_put_u16(msg, NL80211_STA_INFO_LLID, sinfo->llid))
-		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_PLID) &&
-	    nla_put_u16(msg, NL80211_STA_INFO_PLID, sinfo->plid))
-		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_PLINK_STATE) &&
-	    nla_put_u8(msg, NL80211_STA_INFO_PLINK_STATE,
-		       sinfo->plink_state))
-		goto nla_put_failure;
+
+	PUT_SINFO(RX_BYTES64, rx_bytes, u64);
+	PUT_SINFO(TX_BYTES64, tx_bytes, u64);
+	PUT_SINFO(LLID, llid, u16);
+	PUT_SINFO(PLID, plid, u16);
+	PUT_SINFO(PLINK_STATE, plink_state, u8);
+
 	switch (rdev->wiphy.signal_type) {
 	case CFG80211_SIGNAL_TYPE_MBM:
-		if ((sinfo->filled & STATION_INFO_SIGNAL) &&
-		    nla_put_u8(msg, NL80211_STA_INFO_SIGNAL,
-			       sinfo->signal))
-			goto nla_put_failure;
-		if ((sinfo->filled & STATION_INFO_SIGNAL_AVG) &&
-		    nla_put_u8(msg, NL80211_STA_INFO_SIGNAL_AVG,
-			       sinfo->signal_avg))
-			goto nla_put_failure;
+		PUT_SINFO(SIGNAL, signal, u8);
+		PUT_SINFO(SIGNAL_AVG, signal_avg, u8);
 		break;
 	default:
 		break;
 	}
-	if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) {
+	if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL)) {
 		if (!nl80211_put_signal(msg, sinfo->chains,
 					sinfo->chain_signal,
 					NL80211_STA_INFO_CHAIN_SIGNAL))
 			goto nla_put_failure;
 	}
-	if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) {
+	if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) {
 		if (!nl80211_put_signal(msg, sinfo->chains,
 					sinfo->chain_signal_avg,
 					NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
 			goto nla_put_failure;
 	}
-	if (sinfo->filled & STATION_INFO_TX_BITRATE) {
+	if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE)) {
 		if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
 					  NL80211_STA_INFO_TX_BITRATE))
 			goto nla_put_failure;
 	}
-	if (sinfo->filled & STATION_INFO_RX_BITRATE) {
+	if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE)) {
 		if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
 					  NL80211_STA_INFO_RX_BITRATE))
 			goto nla_put_failure;
 	}
-	if ((sinfo->filled & STATION_INFO_RX_PACKETS) &&
-	    nla_put_u32(msg, NL80211_STA_INFO_RX_PACKETS,
-			sinfo->rx_packets))
-		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_TX_PACKETS) &&
-	    nla_put_u32(msg, NL80211_STA_INFO_TX_PACKETS,
-			sinfo->tx_packets))
-		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_TX_RETRIES) &&
-	    nla_put_u32(msg, NL80211_STA_INFO_TX_RETRIES,
-			sinfo->tx_retries))
-		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_TX_FAILED) &&
-	    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))
-		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_LOCAL_PM) &&
-	    nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM,
-			sinfo->local_pm))
-		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_PEER_PM) &&
-	    nla_put_u32(msg, NL80211_STA_INFO_PEER_PM,
-			sinfo->peer_pm))
-		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_NONPEER_PM) &&
-	    nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM,
-			sinfo->nonpeer_pm))
-		goto nla_put_failure;
-	if (sinfo->filled & STATION_INFO_BSS_PARAM) {
+
+	PUT_SINFO(RX_PACKETS, rx_packets, u32);
+	PUT_SINFO(TX_PACKETS, tx_packets, u32);
+	PUT_SINFO(TX_RETRIES, tx_retries, u32);
+	PUT_SINFO(TX_FAILED, tx_failed, u32);
+	PUT_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32);
+	PUT_SINFO(BEACON_LOSS, beacon_loss_count, u32);
+	PUT_SINFO(LOCAL_PM, local_pm, u32);
+	PUT_SINFO(PEER_PM, peer_pm, u32);
+	PUT_SINFO(NONPEER_PM, nonpeer_pm, u32);
+
+	if (sinfo->filled & BIT(NL80211_STA_INFO_BSS_PARAM)) {
 		bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
 		if (!bss_param)
 			goto nla_put_failure;
@@ -3791,23 +3783,68 @@
 
 		nla_nest_end(msg, bss_param);
 	}
-	if ((sinfo->filled & STATION_INFO_STA_FLAGS) &&
+	if ((sinfo->filled & BIT(NL80211_STA_INFO_STA_FLAGS)) &&
 	    nla_put(msg, NL80211_STA_INFO_STA_FLAGS,
 		    sizeof(struct nl80211_sta_flag_update),
 		    &sinfo->sta_flags))
 		goto nla_put_failure;
-	if ((sinfo->filled & STATION_INFO_T_OFFSET) &&
-		nla_put_u64(msg, NL80211_STA_INFO_T_OFFSET,
-			    sinfo->t_offset))
-		goto nla_put_failure;
+
+	PUT_SINFO(T_OFFSET, t_offset, u64);
+	PUT_SINFO(RX_DROP_MISC, rx_dropped_misc, u64);
+	PUT_SINFO(BEACON_RX, rx_beacon, u64);
+	PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8);
+
+#undef PUT_SINFO
+
+	if (sinfo->filled & BIT(NL80211_STA_INFO_TID_STATS)) {
+		struct nlattr *tidsattr;
+		int tid;
+
+		tidsattr = nla_nest_start(msg, NL80211_STA_INFO_TID_STATS);
+		if (!tidsattr)
+			goto nla_put_failure;
+
+		for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) {
+			struct cfg80211_tid_stats *tidstats;
+			struct nlattr *tidattr;
+
+			tidstats = &sinfo->pertid[tid];
+
+			if (!tidstats->filled)
+				continue;
+
+			tidattr = nla_nest_start(msg, tid + 1);
+			if (!tidattr)
+				goto nla_put_failure;
+
+#define PUT_TIDVAL(attr, memb, type) do {				\
+	if (tidstats->filled & BIT(NL80211_TID_STATS_ ## attr) &&	\
+	    nla_put_ ## type(msg, NL80211_TID_STATS_ ## attr,		\
+			     tidstats->memb))				\
+		goto nla_put_failure;					\
+	} while (0)
+
+			PUT_TIDVAL(RX_MSDU, rx_msdu, u64);
+			PUT_TIDVAL(TX_MSDU, tx_msdu, u64);
+			PUT_TIDVAL(TX_MSDU_RETRIES, tx_msdu_retries, u64);
+			PUT_TIDVAL(TX_MSDU_FAILED, tx_msdu_failed, u64);
+
+#undef PUT_TIDVAL
+			nla_nest_end(msg, tidattr);
+		}
+
+		nla_nest_end(msg, tidsattr);
+	}
+
 	nla_nest_end(msg, sinfoattr);
 
-	if ((sinfo->filled & STATION_INFO_ASSOC_REQ_IES) &&
+	if (sinfo->assoc_req_ies_len &&
 	    nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
 		    sinfo->assoc_req_ies))
 		goto nla_put_failure;
 
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
  nla_put_failure:
 	genlmsg_cancel(msg, hdr);
@@ -3847,7 +3884,7 @@
 		if (err)
 			goto out_err;
 
-		if (nl80211_send_station(skb,
+		if (nl80211_send_station(skb, NL80211_CMD_NEW_STATION,
 				NETLINK_CB_PORTID(cb->skb),
 				cb->nlh->nlmsg_seq, NLM_F_MULTI,
 				rdev, wdev->netdev, mac_addr,
@@ -3894,7 +3931,8 @@
 	if (!msg)
 		return -ENOMEM;
 
-	if (nl80211_send_station(msg, genl_info_snd_portid(info), info->snd_seq, 0,
+	if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
+				 genl_info_snd_portid(info), info->snd_seq, 0,
 				 rdev, dev, mac_addr, &sinfo) < 0) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
@@ -4364,6 +4402,16 @@
 	if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
 		return -EINVAL;
 
+	/* HT/VHT requires QoS, but if we don't have that just ignore HT/VHT
+	 * as userspace might just pass through the capabilities from the IEs
+	 * directly, rather than enforcing this restriction and returning an
+	 * error in this case.
+	 */
+	if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) {
+		params.ht_capa = NULL;
+		params.vht_capa = NULL;
+	}
+
 	/* When you run into this, adjust the code below for the new flag */
 	BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
 
@@ -4536,7 +4584,8 @@
 
 	nla_nest_end(msg, pinfoattr);
 
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
  nla_put_failure:
 	genlmsg_cancel(msg, hdr);
@@ -4921,7 +4970,10 @@
 static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
 	char *data = NULL;
+	bool is_indoor;
 	enum nl80211_user_reg_hint_type user_reg_hint_type;
+	u32 owner_nlportid;
+
 
 	/*
 	 * You should only get this when cfg80211 hasn't yet initialized
@@ -4947,7 +4999,15 @@
 		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();
+		if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
+			owner_nlportid = genl_info_snd_portid(info);
+			is_indoor = !!info->attrs[NL80211_ATTR_REG_INDOOR];
+		} else {
+			owner_nlportid = 0;
+			is_indoor = true;
+		}
+
+		return regulatory_hint_indoor(is_indoor, owner_nlportid);
 	default:
 		return -EINVAL;
 	}
@@ -4986,8 +5046,7 @@
 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 	if (!msg)
 		return -ENOMEM;
-	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
-			     0,
+	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0,
 			     NL80211_CMD_GET_MESH_CONFIG);
 	if (!hdr)
 		goto out;
@@ -5229,7 +5288,7 @@
 	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
 				  0, 65535, mask,
 				  NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
-	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 1, 0xffffffff,
+	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 0, 0xffffffff,
 				  mask, NL80211_MESHCONF_PLINK_TIMEOUT,
 				  nla_get_u32);
 	if (mask_out)
@@ -5331,43 +5390,20 @@
 	return err;
 }
 
-static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom,
+			      struct sk_buff *msg)
 {
-	const struct ieee80211_regdomain *regdom;
-	struct sk_buff *msg;
-	void *hdr = NULL;
 	struct nlattr *nl_reg_rules;
 	unsigned int i;
 
-	if (!cfg80211_regdomain)
-		return -EINVAL;
-
-	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-	if (!msg)
-		return -ENOBUFS;
-
-	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
-			     0,
-			     NL80211_CMD_GET_REG);
-	if (!hdr)
-		goto put_failure;
-
-	if (reg_last_request_cell_base() &&
-	    nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
-			NL80211_USER_REG_HINT_CELL_BASE))
-		goto nla_put_failure;
-
-	rcu_read_lock();
-	regdom = rcu_dereference(cfg80211_regdomain);
-
 	if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
 	    (regdom->dfs_region &&
 	     nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
-		goto nla_put_failure_rcu;
+		goto nla_put_failure;
 
 	nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
 	if (!nl_reg_rules)
-		goto nla_put_failure_rcu;
+		goto nla_put_failure;
 
 	for (i = 0; i < regdom->n_reg_rules; i++) {
 		struct nlattr *nl_reg_rule;
@@ -5382,7 +5418,7 @@
 
 		nl_reg_rule = nla_nest_start(msg, i);
 		if (!nl_reg_rule)
-			goto nla_put_failure_rcu;
+			goto nla_put_failure;
 
 		max_bandwidth_khz = freq_range->max_bandwidth_khz;
 		if (!max_bandwidth_khz)
@@ -5403,13 +5439,74 @@
 				power_rule->max_eirp) ||
 		    nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME,
 				reg_rule->dfs_cac_ms))
-			goto nla_put_failure_rcu;
+			goto nla_put_failure;
 
 		nla_nest_end(msg, nl_reg_rule);
 	}
-	rcu_read_unlock();
 
 	nla_nest_end(msg, nl_reg_rules);
+	return 0;
+
+nla_put_failure:
+	return -EMSGSIZE;
+}
+
+static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
+{
+	const struct ieee80211_regdomain *regdom = NULL;
+	struct cfg80211_registered_device *rdev;
+	struct wiphy *wiphy = NULL;
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOBUFS;
+
+	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0,
+			     NL80211_CMD_GET_REG);
+	if (!hdr)
+		goto put_failure;
+
+	if (info->attrs[NL80211_ATTR_WIPHY]) {
+		bool self_managed;
+
+		rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
+		if (IS_ERR(rdev)) {
+			nlmsg_free(msg);
+			return PTR_ERR(rdev);
+		}
+
+		wiphy = &rdev->wiphy;
+		self_managed = wiphy->regulatory_flags &
+			       REGULATORY_WIPHY_SELF_MANAGED;
+		regdom = get_wiphy_regdom(wiphy);
+
+		/* a self-managed-reg device must have a private regdom */
+		if (WARN_ON(!regdom && self_managed)) {
+			nlmsg_free(msg);
+			return -EINVAL;
+		}
+
+		if (regdom &&
+		    nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
+			goto nla_put_failure;
+	}
+
+	if (!wiphy && reg_last_request_cell_base() &&
+	    nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
+			NL80211_USER_REG_HINT_CELL_BASE))
+		goto nla_put_failure;
+
+	rcu_read_lock();
+
+	if (!regdom)
+		regdom = rcu_dereference(cfg80211_regdomain);
+
+	if (nl80211_put_regdom(regdom, msg))
+		goto nla_put_failure_rcu;
+
+	rcu_read_unlock();
 
 	genlmsg_end(msg, hdr);
 	return genlmsg_reply(msg, info);
@@ -5423,6 +5520,84 @@
 	return -EMSGSIZE;
 }
 
+static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb,
+			       u32 seq, int flags, struct wiphy *wiphy,
+			       const struct ieee80211_regdomain *regdom)
+{
+	void *hdr = nl80211hdr_put(msg, NETLINK_CB_PORTID(cb->skb), seq, flags,
+				   NL80211_CMD_GET_REG);
+
+	if (!hdr)
+		return -1;
+
+	genl_dump_check_consistent(cb, hdr, &nl80211_fam);
+
+	if (nl80211_put_regdom(regdom, msg))
+		goto nla_put_failure;
+
+	if (!wiphy && reg_last_request_cell_base() &&
+	    nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
+			NL80211_USER_REG_HINT_CELL_BASE))
+		goto nla_put_failure;
+
+	if (wiphy &&
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
+		goto nla_put_failure;
+
+	if (wiphy && wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+	    nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+		goto nla_put_failure;
+
+	genlmsg_end(msg, hdr);
+	return 0;
+
+nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	return -EMSGSIZE;
+}
+
+static int nl80211_get_reg_dump(struct sk_buff *skb,
+				struct netlink_callback *cb)
+{
+	const struct ieee80211_regdomain *regdom = NULL;
+	struct cfg80211_registered_device *rdev;
+	int err, reg_idx, start = cb->args[2];
+
+	rtnl_lock();
+
+	if (cfg80211_regdomain && start == 0) {
+		err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
+					  NLM_F_MULTI, NULL,
+					  rtnl_dereference(cfg80211_regdomain));
+		if (err < 0)
+			goto out_err;
+	}
+
+	/* the global regdom is idx 0 */
+	reg_idx = 1;
+	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+		regdom = get_wiphy_regdom(&rdev->wiphy);
+		if (!regdom)
+			continue;
+
+		if (++reg_idx <= start)
+			continue;
+
+		err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
+					  NLM_F_MULTI, &rdev->wiphy, regdom);
+		if (err < 0) {
+			reg_idx--;
+			break;
+		}
+	}
+
+	cb->args[2] = reg_idx;
+	err = skb->len;
+out_err:
+	rtnl_unlock();
+	return err;
+}
+
 static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
 	struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
@@ -5491,7 +5666,7 @@
 		}
 	}
 
-	r = set_regdom(rd);
+	r = set_regdom(rd, REGD_SOURCE_CRDA);
 	/* set_regdom took ownership */
 	rd = NULL;
 
@@ -5531,8 +5706,8 @@
 	int i;
 
 	if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) {
-		memset(mac_addr, 0, ETH_ALEN);
-		memset(mac_addr_mask, 0, ETH_ALEN);
+		eth_zero_addr(mac_addr);
+		eth_zero_addr(mac_addr_mask);
 		mac_addr[0] = 0x2;
 		mac_addr_mask[0] = 0x3;
 
@@ -5628,7 +5803,7 @@
 		request->ssids = (void *)&request->channels[n_channels];
 	request->n_ssids = n_ssids;
 	if (ie_len) {
-		if (request->ssids)
+		if (n_ssids)
 			request->ie = (void *)(request->ssids + n_ssids);
 		else
 			request->ie = (void *)(request->channels + n_channels);
@@ -5684,7 +5859,7 @@
 	request->n_channels = i;
 
 	i = 0;
-	if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
+	if (n_ssids) {
 		nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
 			if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
 				err = -EINVAL;
@@ -5882,7 +6057,7 @@
 		request->ssids = (void *)&request->channels[n_channels];
 	request->n_ssids = n_ssids;
 	if (ie_len) {
-		if (request->ssids)
+		if (n_ssids)
 			request->ie = (void *)(request->ssids + n_ssids);
 		else
 			request->ie = (void *)(request->channels + n_channels);
@@ -5891,7 +6066,7 @@
 	if (n_match_sets) {
 		if (request->ie)
 			request->match_sets = (void *)(request->ie + ie_len);
-		else if (request->ssids)
+		else if (n_ssids)
 			request->match_sets =
 				(void *)(request->ssids + n_ssids);
 		else
@@ -5950,7 +6125,7 @@
 	request->n_channels = i;
 
 	i = 0;
-	if (attrs[NL80211_ATTR_SCAN_SSIDS]) {
+	if (n_ssids) {
 		nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
 				    tmp) {
 			if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
@@ -6058,6 +6233,10 @@
 		}
 	}
 
+	if (attrs[NL80211_ATTR_SCHED_SCAN_DELAY])
+		request->delay =
+			nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_DELAY]);
+
 	request->interval = interval;
 	request->scan_start = jiffies;
 
@@ -6074,6 +6253,7 @@
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
 	struct net_device *dev = info->user_ptr[1];
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_sched_scan_request *sched_scan_req;
 	int err;
 
 	if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
@@ -6083,27 +6263,32 @@
 	if (rdev->sched_scan_req)
 		return -EINPROGRESS;
 
-	rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
-							info->attrs);
-	err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
+	sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
+						  info->attrs);
+
+	err = PTR_ERR_OR_ZERO(sched_scan_req);
 	if (err)
 		goto out_err;
 
-	err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
+	err = rdev_sched_scan_start(rdev, dev, sched_scan_req);
 	if (err)
 		goto out_free;
 
-	rdev->sched_scan_req->dev = dev;
-	rdev->sched_scan_req->wiphy = &rdev->wiphy;
+	sched_scan_req->dev = dev;
+	sched_scan_req->wiphy = &rdev->wiphy;
+
+	if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
+		sched_scan_req->owner_nlportid = genl_info_snd_portid(info);
+
+	rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req);
 
 	nl80211_send_sched_scan(rdev, dev,
 				NL80211_CMD_START_SCHED_SCAN);
 	return 0;
 
 out_free:
-	kfree(rdev->sched_scan_req);
+	kfree(sched_scan_req);
 out_err:
-	rdev->sched_scan_req = NULL;
 	return err;
 }
 
@@ -6438,7 +6623,8 @@
 
 	nla_nest_end(msg, bss);
 
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
  fail_unlock_rcu:
 	rcu_read_unlock();
@@ -6488,12 +6674,17 @@
 }
 
 static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
-				int flags, struct net_device *dev,
-				struct survey_info *survey)
+			       int flags, struct net_device *dev,
+			       bool allow_radio_stats,
+			       struct survey_info *survey)
 {
 	void *hdr;
 	struct nlattr *infoattr;
 
+	/* skip radio stats if userspace didn't request them */
+	if (!survey->channel && !allow_radio_stats)
+		return 0;
+
 	hdr = nl80211hdr_put(msg, portid, seq, flags,
 			     NL80211_CMD_NEW_SURVEY_RESULTS);
 	if (!hdr)
@@ -6506,7 +6697,8 @@
 	if (!infoattr)
 		goto nla_put_failure;
 
-	if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
+	if (survey->channel &&
+	    nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
 			survey->channel->center_freq))
 		goto nla_put_failure;
 
@@ -6516,49 +6708,57 @@
 	if ((survey->filled & SURVEY_INFO_IN_USE) &&
 	    nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE))
 		goto nla_put_failure;
-	if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) &&
-	    nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
-			survey->channel_time))
+	if ((survey->filled & SURVEY_INFO_TIME) &&
+	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME,
+			survey->time))
 		goto nla_put_failure;
-	if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) &&
-	    nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
-			survey->channel_time_busy))
+	if ((survey->filled & SURVEY_INFO_TIME_BUSY) &&
+	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_BUSY,
+			survey->time_busy))
 		goto nla_put_failure;
-	if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) &&
-	    nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
-			survey->channel_time_ext_busy))
+	if ((survey->filled & SURVEY_INFO_TIME_EXT_BUSY) &&
+	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_EXT_BUSY,
+			survey->time_ext_busy))
 		goto nla_put_failure;
-	if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) &&
-	    nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
-			survey->channel_time_rx))
+	if ((survey->filled & SURVEY_INFO_TIME_RX) &&
+	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_RX,
+			survey->time_rx))
 		goto nla_put_failure;
-	if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) &&
-	    nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
-			survey->channel_time_tx))
+	if ((survey->filled & SURVEY_INFO_TIME_TX) &&
+	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_TX,
+			survey->time_tx))
+		goto nla_put_failure;
+	if ((survey->filled & SURVEY_INFO_TIME_SCAN) &&
+	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_SCAN,
+			survey->time_scan))
 		goto nla_put_failure;
 
 	nla_nest_end(msg, infoattr);
 
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
  nla_put_failure:
 	genlmsg_cancel(msg, hdr);
 	return -EMSGSIZE;
 }
 
-static int nl80211_dump_survey(struct sk_buff *skb,
-			struct netlink_callback *cb)
+static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct survey_info survey;
 	struct cfg80211_registered_device *rdev;
 	struct wireless_dev *wdev;
 	int survey_idx = cb->args[2];
 	int res;
+	bool radio_stats;
 
 	res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
 	if (res)
 		return res;
 
+	/* prepare_wdev_dump parsed the attributes */
+	radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS];
+
 	if (!wdev->netdev) {
 		res = -EINVAL;
 		goto out_err;
@@ -6576,13 +6776,9 @@
 		if (res)
 			goto out_err;
 
-		/* Survey without a channel doesn't make sense */
-		if (!survey.channel) {
-			res = -EINVAL;
-			goto out;
-		}
-
-		if (survey.channel->flags & IEEE80211_CHAN_DISABLED) {
+		/* don't send disabled channels, but do send non-channel data */
+		if (survey.channel &&
+		    survey.channel->flags & IEEE80211_CHAN_DISABLED) {
 			survey_idx++;
 			continue;
 		}
@@ -6590,7 +6786,7 @@
 		if (nl80211_send_survey(skb,
 				NETLINK_CB_PORTID(cb->skb),
 				cb->nlh->nlmsg_seq, NLM_F_MULTI,
-				wdev->netdev, &survey) < 0)
+				wdev->netdev, radio_stats, &survey) < 0)
 			goto out;
 		survey_idx++;
 	}
@@ -7094,8 +7290,18 @@
 		break;
 	case NL80211_CHAN_WIDTH_20:
 	case NL80211_CHAN_WIDTH_40:
-		if (rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)
-			break;
+		if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
+			return -EINVAL;
+		break;
+	case NL80211_CHAN_WIDTH_80:
+	case NL80211_CHAN_WIDTH_80P80:
+	case NL80211_CHAN_WIDTH_160:
+		if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
+			return -EINVAL;
+		if (!wiphy_ext_feature_isset(&rdev->wiphy,
+					     NL80211_EXT_FEATURE_VHT_IBSS))
+			return -EINVAL;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -7208,8 +7414,8 @@
 
 static struct sk_buff *
 __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
-			    int approxlen, u32 portid, u32 seq,
-			    enum nl80211_commands cmd,
+			    struct wireless_dev *wdev, int approxlen,
+			    u32 portid, u32 seq, enum nl80211_commands cmd,
 			    enum nl80211_attrs attr,
 			    const struct nl80211_vendor_cmd_info *info,
 			    gfp_t gfp)
@@ -7240,6 +7446,16 @@
 			goto nla_put_failure;
 	}
 
+	if (wdev) {
+		if (nla_put_u64(skb, NL80211_ATTR_WDEV,
+				wdev_id(wdev)))
+			goto nla_put_failure;
+		if (wdev->netdev &&
+		    nla_put_u32(skb, NL80211_ATTR_IFINDEX,
+				wdev->netdev->ifindex))
+			goto nla_put_failure;
+	}
+
 	data = nla_nest_start(skb, attr);
 
 	((void **)skb->cb)[0] = rdev;
@@ -7254,6 +7470,7 @@
 }
 
 struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
+					   struct wireless_dev *wdev,
 					   enum nl80211_commands cmd,
 					   enum nl80211_attrs attr,
 					   int vendor_event_idx,
@@ -7279,7 +7496,7 @@
 		return NULL;
 	}
 
-	return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0,
+	return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, 0, 0,
 					   cmd, attr, info, gfp);
 }
 EXPORT_SYMBOL(__cfg80211_alloc_event_skb);
@@ -7603,14 +7820,19 @@
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
 	struct net *net;
 	int err;
-	u32 pid;
 
-	if (!info->attrs[NL80211_ATTR_PID])
+	if (info->attrs[NL80211_ATTR_PID]) {
+		u32 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
+
+		net = get_net_ns_by_pid(pid);
+	} else if (info->attrs[NL80211_ATTR_NETNS_FD]) {
+		u32 fd = nla_get_u32(info->attrs[NL80211_ATTR_NETNS_FD]);
+
+		net = get_net_ns_by_fd(fd);
+	} else {
 		return -EINVAL;
+	}
 
-	pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
-
-	net = get_net_ns_by_pid(pid);
 	if (IS_ERR(net))
 		return PTR_ERR(net);
 
@@ -7776,8 +7998,7 @@
 	if (!msg)
 		return -ENOMEM;
 
-	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
-			     0,
+	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0,
 			     NL80211_CMD_REMAIN_ON_CHANNEL);
 	if (!hdr) {
 		err = -ENOBUFS;
@@ -8086,8 +8307,7 @@
 	if (!rdev->ops->mgmt_tx)
 		return -EOPNOTSUPP;
 
-	return cfg80211_mlme_register_mgmt(wdev, genl_info_snd_portid(info),
-					   frame_type,
+	return cfg80211_mlme_register_mgmt(wdev, genl_info_snd_portid(info), frame_type,
 			nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
 			nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
 }
@@ -8302,8 +8522,7 @@
 	if (!msg)
 		return -ENOMEM;
 
-	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
-			     0,
+	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0,
 			     NL80211_CMD_GET_POWER_SAVE);
 	if (!hdr) {
 		err = -ENOBUFS;
@@ -8579,8 +8798,8 @@
 	if (!nl_tcp)
 		return -ENOBUFS;
 
-	if (nla_put_be32(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) ||
-	    nla_put_be32(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) ||
+	if (nla_put_in_addr(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) ||
+	    nla_put_in_addr(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) ||
 	    nla_put(msg, NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, tcp->dst_mac) ||
 	    nla_put_u16(msg, NL80211_WOWLAN_TCP_SRC_PORT, tcp->src_port) ||
 	    nla_put_u16(msg, NL80211_WOWLAN_TCP_DST_PORT, tcp->dst_port) ||
@@ -8610,6 +8829,51 @@
 	return 0;
 }
 
+static int nl80211_send_wowlan_nd(struct sk_buff *msg,
+				  struct cfg80211_sched_scan_request *req)
+{
+	struct nlattr *nd, *freqs, *matches, *match;
+	int i;
+
+	if (!req)
+		return 0;
+
+	nd = nla_nest_start(msg, NL80211_WOWLAN_TRIG_NET_DETECT);
+	if (!nd)
+		return -ENOBUFS;
+
+	if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, req->interval))
+		return -ENOBUFS;
+
+	if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY, req->delay))
+		return -ENOBUFS;
+
+	freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
+	if (!freqs)
+		return -ENOBUFS;
+
+	for (i = 0; i < req->n_channels; i++)
+		nla_put_u32(msg, i, req->channels[i]->center_freq);
+
+	nla_nest_end(msg, freqs);
+
+	if (req->n_match_sets) {
+		matches = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
+		for (i = 0; i < req->n_match_sets; i++) {
+			match = nla_nest_start(msg, i);
+			nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+				req->match_sets[i].ssid.ssid_len,
+				req->match_sets[i].ssid.ssid);
+			nla_nest_end(msg, match);
+		}
+		nla_nest_end(msg, matches);
+	}
+
+	nla_nest_end(msg, nd);
+
+	return 0;
+}
+
 static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -8632,8 +8896,7 @@
 	if (!msg)
 		return -ENOMEM;
 
-	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
-			     0,
+	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0,
 			     NL80211_CMD_GET_WOWLAN);
 	if (!hdr)
 		goto nla_put_failure;
@@ -8668,6 +8931,11 @@
 					    rdev->wiphy.wowlan_config->tcp))
 			goto nla_put_failure;
 
+		if (nl80211_send_wowlan_nd(
+			    msg,
+			    rdev->wiphy.wowlan_config->nd_config))
+			goto nla_put_failure;
+
 		nla_nest_end(msg, nl_wowlan);
 	}
 
@@ -8765,8 +9033,8 @@
 	cfg = kzalloc(size, GFP_KERNEL);
 	if (!cfg)
 		return -ENOMEM;
-	cfg->src = nla_get_be32(tb[NL80211_WOWLAN_TCP_SRC_IPV4]);
-	cfg->dst = nla_get_be32(tb[NL80211_WOWLAN_TCP_DST_IPV4]);
+	cfg->src = nla_get_in_addr(tb[NL80211_WOWLAN_TCP_SRC_IPV4]);
+	cfg->dst = nla_get_in_addr(tb[NL80211_WOWLAN_TCP_DST_IPV4]);
 	memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]),
 	       ETH_ALEN);
 	if (tb[NL80211_WOWLAN_TCP_SRC_PORT])
@@ -8866,6 +9134,7 @@
 	const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
 	int err, i;
 	bool prev_enabled = rdev->wiphy.wowlan_config;
+	bool regular = false;
 
 	if (!wowlan)
 		return -EOPNOTSUPP;
@@ -8893,12 +9162,14 @@
 		if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
 			return -EINVAL;
 		new_triggers.disconnect = true;
+		regular = true;
 	}
 
 	if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
 		if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
 			return -EINVAL;
 		new_triggers.magic_pkt = true;
+		regular = true;
 	}
 
 	if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
@@ -8908,24 +9179,28 @@
 		if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
 			return -EINVAL;
 		new_triggers.gtk_rekey_failure = true;
+		regular = true;
 	}
 
 	if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
 		if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
 			return -EINVAL;
 		new_triggers.eap_identity_req = true;
+		regular = true;
 	}
 
 	if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
 		if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
 			return -EINVAL;
 		new_triggers.four_way_handshake = true;
+		regular = true;
 	}
 
 	if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
 		if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
 			return -EINVAL;
 		new_triggers.rfkill_release = true;
+		regular = true;
 	}
 
 	if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
@@ -8934,6 +9209,8 @@
 		int rem, pat_len, mask_len, pkt_offset;
 		struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
 
+		regular = true;
+
 		nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
 				    rem)
 			n_patterns++;
@@ -8995,6 +9272,7 @@
 	}
 
 	if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) {
+		regular = true;
 		err = nl80211_parse_wowlan_tcp(
 			rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION],
 			&new_triggers);
@@ -9003,6 +9281,7 @@
 	}
 
 	if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) {
+		regular = true;
 		err = nl80211_parse_wowlan_nd(
 			rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT],
 			&new_triggers);
@@ -9010,6 +9289,17 @@
 			goto error;
 	}
 
+	/* The 'any' trigger means the device continues operating more or less
+	 * as in its normal operation mode and wakes up the host on most of the
+	 * normal interrupts (like packet RX, ...)
+	 * It therefore makes little sense to combine with the more constrained
+	 * wakeup trigger modes.
+	 */
+	if (new_triggers.any && regular) {
+		err = -EINVAL;
+		goto error;
+	}
+
 	ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
 	if (!ntrig) {
 		err = -ENOMEM;
@@ -9104,8 +9394,7 @@
 	if (!msg)
 		return -ENOMEM;
 
-	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
-			     0,
+	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0,
 			     NL80211_CMD_GET_COALESCE);
 	if (!hdr)
 		goto nla_put_failure;
@@ -9388,8 +9677,7 @@
 	if (!msg)
 		return -ENOMEM;
 
-	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
-			     0,
+	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0,
 			     NL80211_CMD_PROBE_CLIENT);
 	if (!hdr) {
 		err = -ENOBUFS;
@@ -9504,8 +9792,7 @@
 	if (!msg)
 		return -ENOMEM;
 
-	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
-			     0,
+	hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0,
 			     NL80211_CMD_GET_PROTOCOL_FEATURES);
 	if (!hdr)
 		goto nla_put_failure;
@@ -9681,7 +9968,7 @@
 	if (WARN_ON(!rdev->cur_cmd_info))
 		return NULL;
 
-	return __cfg80211_alloc_vendor_skb(rdev, approxlen,
+	return __cfg80211_alloc_vendor_skb(rdev, NULL, approxlen,
 					   genl_info_snd_portid(rdev->cur_cmd_info),
 					   rdev->cur_cmd_info->snd_seq,
 					   cmd, attr, NULL, GFP_KERNEL);
@@ -10240,7 +10527,8 @@
 	},
 	{
 		.cmd = NL80211_CMD_GET_REG,
-		.doit = nl80211_get_reg,
+		.doit = nl80211_get_reg_do,
+		.dumpit = nl80211_get_reg_dump,
 		.policy = nl80211_policy,
 		.internal_flags = NL80211_FLAG_NEED_RTNL,
 		/* can be retrieved by unprivileged users */
@@ -10839,7 +11127,8 @@
 	/* ignore errors and send incomplete event anyway */
 	nl80211_add_scan_req(msg, rdev);
 
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
  nla_put_failure:
 	genlmsg_cancel(msg, hdr);
@@ -10862,7 +11151,8 @@
 	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
 		goto nla_put_failure;
 
-	return genlmsg_end(msg, hdr);
+	genlmsg_end(msg, hdr);
+	return 0;
 
  nla_put_failure:
 	genlmsg_cancel(msg, hdr);
@@ -10954,25 +11244,9 @@
 				NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
-/*
- * This can happen on global regulatory changes or device specific settings
- * based on custom world regulatory domains.
- */
-void nl80211_send_reg_change_event(struct regulatory_request *request)
+static bool nl80211_reg_change_event_fill(struct sk_buff *msg,
+					  struct regulatory_request *request)
 {
-	struct sk_buff *msg;
-	void *hdr;
-
-	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-	if (!msg)
-		return;
-
-	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
-	if (!hdr) {
-		nlmsg_free(msg);
-		return;
-	}
-
 	/* Userspace can always count this one always being set */
 	if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
 		goto nla_put_failure;
@@ -10998,8 +11272,46 @@
 			goto nla_put_failure;
 	}
 
-	if (request->wiphy_idx != WIPHY_IDX_INVALID &&
-	    nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
+	if (request->wiphy_idx != WIPHY_IDX_INVALID) {
+		struct wiphy *wiphy = wiphy_idx_to_wiphy(request->wiphy_idx);
+
+		if (wiphy &&
+		    nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
+			goto nla_put_failure;
+
+		if (wiphy &&
+		    wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+		    nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+			goto nla_put_failure;
+	}
+
+	return true;
+
+nla_put_failure:
+	return false;
+}
+
+/*
+ * This can happen on global regulatory changes or device specific settings
+ * based on custom regulatory domains.
+ */
+void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
+				     struct regulatory_request *request)
+{
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return;
+
+	hdr = nl80211hdr_put(msg, 0, 0, 0, cmd_id);
+	if (!hdr) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	if (nl80211_reg_change_event_fill(msg, request) == false)
 		goto nla_put_failure;
 
 	genlmsg_end(msg, hdr);
@@ -11538,7 +11850,7 @@
 	if (!msg)
 		return;
 
-	if (nl80211_send_station(msg, 0, 0, 0,
+	if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, 0, 0, 0,
 				 rdev, dev, mac_addr, sinfo) < 0) {
 		nlmsg_free(msg);
 		return;
@@ -11549,12 +11861,16 @@
 }
 EXPORT_SYMBOL(cfg80211_new_sta);
 
-void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
+void cfg80211_del_sta_sinfo(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_rdev(wiphy);
 	struct sk_buff *msg;
-	void *hdr;
+	struct station_info empty_sinfo = {};
+
+	if (!sinfo)
+		sinfo = &empty_sinfo;
 
 	trace_cfg80211_del_sta(dev, mac_addr);
 
@@ -11562,27 +11878,16 @@
 	if (!msg)
 		return;
 
-	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
-	if (!hdr) {
+	if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0,
+				 rdev, dev, mac_addr, sinfo) < 0) {
 		nlmsg_free(msg);
 		return;
 	}
 
-	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
-	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
-		goto nla_put_failure;
-
-	genlmsg_end(msg, hdr);
-
 	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
 				NL80211_MCGRP_MLME, gfp);
-	return;
-
- nla_put_failure:
-	genlmsg_cancel(msg, hdr);
-	nlmsg_free(msg);
 }
-EXPORT_SYMBOL(cfg80211_del_sta);
+EXPORT_SYMBOL(cfg80211_del_sta_sinfo);
 
 void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
 			  enum nl80211_connect_failed_reason reason,
@@ -12295,9 +12600,7 @@
 			}
 
 			for (j = 0; j < match->n_channels; j++) {
-				if (nla_put_u32(msg,
-						NL80211_ATTR_WIPHY_FREQ,
-						match->channels[j])) {
+				if (nla_put_u32(msg, j, match->channels[j])) {
 					nla_nest_cancel(msg, nl_freqs);
 					nla_nest_cancel(msg, nl_match);
 					goto out;
@@ -12486,6 +12789,13 @@
 
 	list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
 		bool schedule_destroy_work = false;
+		bool schedule_scan_stop = false;
+		struct cfg80211_sched_scan_request *sched_scan_req =
+			rcu_dereference(rdev->sched_scan_req);
+
+		if (sched_scan_req && netlink_notify_portid(notify) &&
+		    sched_scan_req->owner_nlportid == netlink_notify_portid(notify))
+			schedule_scan_stop = true;
 
 		list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) {
 			cfg80211_mlme_unregister_socket(wdev,
@@ -12517,11 +12827,22 @@
 				spin_unlock(&rdev->destroy_list_lock);
 				schedule_work(&rdev->destroy_work);
 			}
+		} else if (schedule_scan_stop) {
+			sched_scan_req->owner_nlportid = 0;
+
+			if (rdev->ops->sched_scan_stop &&
+			    rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
+				schedule_work(&rdev->sched_scan_stop_wk);
 		}
 	}
 
 	rcu_read_unlock();
 
+	/*
+	 * It is possible that the user space process that is controlling the
+	 * indoor setting disappeared, so notify the regulatory core.
+	 */
+	regulatory_netlink_notify(netlink_notify_portid(notify));
 	return NOTIFY_OK;
 }
 
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 7ad70d6..84d4edf 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -17,7 +17,21 @@
 			     struct net_device *netdev, u32 cmd);
 void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
 				     struct net_device *netdev);
-void nl80211_send_reg_change_event(struct regulatory_request *request);
+void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
+				     struct regulatory_request *request);
+
+static inline void
+nl80211_send_reg_change_event(struct regulatory_request *request)
+{
+	nl80211_common_reg_change_event(NL80211_CMD_REG_CHANGE, request);
+}
+
+static inline void
+nl80211_send_wiphy_reg_change_event(struct regulatory_request *request)
+{
+	nl80211_common_reg_change_event(NL80211_CMD_WIPHY_REG_CHANGE, request);
+}
+
 void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
 			  struct net_device *netdev,
 			  const u8 *buf, size_t len, gfp_t gfp);
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 0336264..0c0da07 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -35,13 +35,14 @@
 
 static inline struct wireless_dev
 *rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name,
+		       unsigned char name_assign_type,
 		       enum nl80211_iftype type, u32 *flags,
 		       struct vif_params *params)
 {
 	struct wireless_dev *ret;
 	trace_rdev_add_virtual_intf(&rdev->wiphy, name, type);
-	ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, type, flags,
-					  params);
+	ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, name_assign_type,
+					  type, flags, params);
 	trace_rdev_return_wdev(&rdev->wiphy, ret);
 	return ret;
 }
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index ec2e3b5..8b59af8 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -82,17 +82,12 @@
  *	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 = {
@@ -109,7 +104,7 @@
  * protected by RTNL (and can be accessed with RCU protection)
  */
 static struct regulatory_request __rcu *last_request =
-	(void __rcu *)&core_request_world;
+	(void __force __rcu *)&core_request_world;
 
 /* To trigger userspace events */
 static struct platform_device *reg_pdev;
@@ -133,16 +128,24 @@
  * 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 spinlock_t reg_indoor_lock;
+
+/* Used to track the userspace process controlling the indoor setting */
+static u32 reg_is_indoor_portid;
+
+/* Max number of consecutive attempts to communicate with CRDA  */
+#define REG_MAX_CRDA_TIMEOUTS 10
+
+static u32 reg_crda_timeouts;
 
 static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
 {
 	return rtnl_dereference(cfg80211_regdomain);
 }
 
-static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
+const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
 {
 	return rtnl_dereference(wiphy->regd);
 }
@@ -228,7 +231,7 @@
 
 /* We keep a static world regulatory domain in case of the absence of CRDA */
 static const struct ieee80211_regdomain world_regdom = {
-	.n_reg_rules = 6,
+	.n_reg_rules = 8,
 	.alpha2 =  "00",
 	.reg_rules = {
 		/* IEEE 802.11b/g, channels 1..11 */
@@ -487,7 +490,7 @@
 	mutex_unlock(&reg_regdb_search_mutex);
 
 	if (!IS_ERR_OR_NULL(regdom))
-		set_regdom(regdom);
+		set_regdom(regdom, REGD_SOURCE_INTERNAL_DB);
 
 	rtnl_unlock();
 }
@@ -537,15 +540,20 @@
 	snprintf(country, sizeof(country), "COUNTRY=%c%c",
 		 alpha2[0], alpha2[1]);
 
+	/* query internal regulatory database (if it exists) */
+	reg_regdb_query(alpha2);
+
+	if (reg_crda_timeouts > REG_MAX_CRDA_TIMEOUTS) {
+		pr_info("Exceeded CRDA call max attempts. Not calling CRDA\n");
+		return -EINVAL;
+	}
+
 	if (!is_world_regdom((char *) alpha2))
 		pr_info("Calling CRDA for country: %c%c\n",
 			alpha2[0], alpha2[1]);
 	else
 		pr_info("Calling CRDA to update world regulatory domain\n");
 
-	/* query internal regulatory database (if it exists) */
-	reg_regdb_query(alpha2);
-
 	return kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, env);
 }
 
@@ -554,6 +562,9 @@
 {
 	if (call_crda(request->alpha2))
 		return REG_REQ_IGNORE;
+
+	queue_delayed_work(system_power_efficient_wq,
+			   &reg_timeout, msecs_to_jiffies(3142));
 	return REG_REQ_OK;
 }
 
@@ -1248,13 +1259,6 @@
 	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());
@@ -1307,6 +1311,9 @@
 {
 	struct regulatory_request *lr = get_last_request();
 
+	if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+		return true;
+
 	if (!lr) {
 		REG_DBG_PRINT("Ignoring regulatory request set by %s "
 			      "since last_request is not set\n",
@@ -1530,45 +1537,40 @@
 
 static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
 {
-	struct ieee80211_channel *ch;
 	struct cfg80211_chan_def chandef;
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-	bool ret = true;
+	enum nl80211_iftype iftype;
 
 	wdev_lock(wdev);
+	iftype = wdev->iftype;
 
+	/* make sure the interface is active */
 	if (!wdev->netdev || !netif_running(wdev->netdev))
-		goto out;
+		goto wdev_inactive_unlock;
 
-	switch (wdev->iftype) {
+	switch (iftype) {
 	case NL80211_IFTYPE_AP:
 	case NL80211_IFTYPE_P2P_GO:
 		if (!wdev->beacon_interval)
-			goto out;
-
-		ret = cfg80211_reg_can_beacon(wiphy,
-					      &wdev->chandef, wdev->iftype);
+			goto wdev_inactive_unlock;
+		chandef = wdev->chandef;
 		break;
 	case NL80211_IFTYPE_ADHOC:
 		if (!wdev->ssid_len)
-			goto out;
-
-		ret = cfg80211_reg_can_beacon(wiphy,
-					      &wdev->chandef, wdev->iftype);
+			goto wdev_inactive_unlock;
+		chandef = wdev->chandef;
 		break;
 	case NL80211_IFTYPE_STATION:
 	case NL80211_IFTYPE_P2P_CLIENT:
 		if (!wdev->current_bss ||
 		    !wdev->current_bss->pub.channel)
-			goto out;
+			goto wdev_inactive_unlock;
 
-		ch = wdev->current_bss->pub.channel;
-		if (rdev->ops->get_channel &&
-		    !rdev_get_channel(rdev, wdev, &chandef))
-			ret = cfg80211_chandef_usable(wiphy, &chandef,
-						      IEEE80211_CHAN_DISABLED);
-		else
-			ret = !(ch->flags & IEEE80211_CHAN_DISABLED);
+		if (!rdev->ops->get_channel ||
+		    rdev_get_channel(rdev, wdev, &chandef))
+			cfg80211_chandef_create(&chandef,
+						wdev->current_bss->pub.channel,
+						NL80211_CHAN_NO_HT);
 		break;
 	case NL80211_IFTYPE_MONITOR:
 	case NL80211_IFTYPE_AP_VLAN:
@@ -1581,9 +1583,26 @@
 		break;
 	}
 
-out:
 	wdev_unlock(wdev);
-	return ret;
+
+	switch (iftype) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_P2P_GO:
+	case NL80211_IFTYPE_ADHOC:
+		return cfg80211_reg_can_beacon(wiphy, &chandef, iftype);
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_P2P_CLIENT:
+		return cfg80211_chandef_usable(wiphy, &chandef,
+					       IEEE80211_CHAN_DISABLED);
+	default:
+		break;
+	}
+
+	return true;
+
+wdev_inactive_unlock:
+	wdev_unlock(wdev);
+	return true;
 }
 
 static void reg_leave_invalid_chans(struct wiphy *wiphy)
@@ -1683,8 +1702,12 @@
 	if (IS_ERR(reg_rule)) {
 		REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
 			      chan->center_freq);
-		chan->orig_flags |= IEEE80211_CHAN_DISABLED;
-		chan->flags = chan->orig_flags;
+		if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
+			chan->flags |= IEEE80211_CHAN_DISABLED;
+		} else {
+			chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+			chan->flags = chan->orig_flags;
+		}
 		return;
 	}
 
@@ -1709,7 +1732,13 @@
 	chan->dfs_state = NL80211_DFS_USABLE;
 
 	chan->beacon_found = false;
-	chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
+
+	if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+		chan->flags = chan->orig_flags | bw_flags |
+			      map_regdom_flags(reg_rule->flags);
+	else
+		chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
+
 	chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
 	chan->max_reg_power = chan->max_power =
 		(int) MBM_TO_DBM(power_rule->max_eirp);
@@ -1775,8 +1804,7 @@
 		need_more_processing = true;
 	spin_unlock(&reg_requests_lock);
 
-	if (lr->initiator == NL80211_REGDOM_SET_BY_USER)
-		cancel_delayed_work(&reg_timeout);
+	cancel_delayed_work(&reg_timeout);
 
 	if (need_more_processing)
 		schedule_work(&reg_work);
@@ -1808,11 +1836,6 @@
 {
 	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);
 
@@ -1860,8 +1883,7 @@
 
 	treatment = __reg_process_hint_user(user_request);
 	if (treatment == REG_REQ_IGNORE ||
-	    treatment == REG_REQ_ALREADY_SET ||
-	    treatment == REG_REQ_USER_HINT_HANDLED) {
+	    treatment == REG_REQ_ALREADY_SET) {
 		reg_free_request(user_request);
 		return treatment;
 	}
@@ -1922,7 +1944,6 @@
 	case REG_REQ_OK:
 		break;
 	case REG_REQ_IGNORE:
-	case REG_REQ_USER_HINT_HANDLED:
 		reg_free_request(driver_request);
 		return treatment;
 	case REG_REQ_INTERSECT:
@@ -2022,7 +2043,6 @@
 	case REG_REQ_OK:
 		break;
 	case REG_REQ_IGNORE:
-	case REG_REQ_USER_HINT_HANDLED:
 		/* fall through */
 	case REG_REQ_ALREADY_SET:
 		reg_free_request(country_ie_request);
@@ -2061,11 +2081,8 @@
 	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_USER_HINT_HANDLED)
+		    treatment == REG_REQ_ALREADY_SET)
 			return;
-		queue_delayed_work(system_power_efficient_wq,
-				   &reg_timeout, msecs_to_jiffies(3142));
 		return;
 	case NL80211_REGDOM_SET_BY_DRIVER:
 		if (!wiphy)
@@ -2095,6 +2112,26 @@
 	reg_free_request(reg_request);
 }
 
+static bool reg_only_self_managed_wiphys(void)
+{
+	struct cfg80211_registered_device *rdev;
+	struct wiphy *wiphy;
+	bool self_managed_found = false;
+
+	ASSERT_RTNL();
+
+	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+		wiphy = &rdev->wiphy;
+		if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+			self_managed_found = true;
+		else
+			return false;
+	}
+
+	/* make sure at least one self-managed wiphy exists */
+	return self_managed_found;
+}
+
 /*
  * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_*
  * Regulatory hints come on a first come first serve basis and we
@@ -2126,7 +2163,19 @@
 
 	spin_unlock(&reg_requests_lock);
 
+	if (reg_only_self_managed_wiphys()) {
+		reg_free_request(reg_request);
+		return;
+	}
+
 	reg_process_hint(reg_request);
+
+	lr = get_last_request();
+
+	spin_lock(&reg_requests_lock);
+	if (!list_empty(&reg_requests_list) && lr && lr->processed)
+		schedule_work(&reg_work);
+	spin_unlock(&reg_requests_lock);
 }
 
 /* Processes beacon hints -- this has nothing to do with country IEs */
@@ -2153,11 +2202,52 @@
 	spin_unlock_bh(&reg_pending_beacons_lock);
 }
 
+static void reg_process_self_managed_hints(void)
+{
+	struct cfg80211_registered_device *rdev;
+	struct wiphy *wiphy;
+	const struct ieee80211_regdomain *tmp;
+	const struct ieee80211_regdomain *regd;
+	enum ieee80211_band band;
+	struct regulatory_request request = {};
+
+	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+		wiphy = &rdev->wiphy;
+
+		spin_lock(&reg_requests_lock);
+		regd = rdev->requested_regd;
+		rdev->requested_regd = NULL;
+		spin_unlock(&reg_requests_lock);
+
+		if (regd == NULL)
+			continue;
+
+		tmp = get_wiphy_regdom(wiphy);
+		rcu_assign_pointer(wiphy->regd, regd);
+		rcu_free_regdom(tmp);
+
+		for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+			handle_band_custom(wiphy, wiphy->bands[band], regd);
+
+		reg_process_ht_flags(wiphy);
+
+		request.wiphy_idx = get_wiphy_idx(wiphy);
+		request.alpha2[0] = regd->alpha2[0];
+		request.alpha2[1] = regd->alpha2[1];
+		request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
+
+		nl80211_send_wiphy_reg_change_event(&request);
+	}
+
+	reg_check_channels();
+}
+
 static void reg_todo(struct work_struct *work)
 {
 	rtnl_lock();
 	reg_process_pending_hints();
 	reg_process_pending_beacon_hints();
+	reg_process_self_managed_hints();
 	rtnl_unlock();
 }
 
@@ -2213,27 +2303,58 @@
 	request->initiator = NL80211_REGDOM_SET_BY_USER;
 	request->user_reg_hint_type = user_reg_hint_type;
 
+	/* Allow calling CRDA again */
+	reg_crda_timeouts = 0;
+
 	queue_regulatory_request(request);
 
 	return 0;
 }
 
-int regulatory_hint_indoor_user(void)
+int regulatory_hint_indoor(bool is_indoor, u32 portid)
 {
-	struct regulatory_request *request;
+	spin_lock(&reg_indoor_lock);
 
-	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
-	if (!request)
-		return -ENOMEM;
+	/* It is possible that more than one user space process is trying to
+	 * configure the indoor setting. To handle such cases, clear the indoor
+	 * setting in case that some process does not think that the device
+	 * is operating in an indoor environment. In addition, if a user space
+	 * process indicates that it is controlling the indoor setting, save its
+	 * portid, i.e., make it the owner.
+	 */
+	reg_is_indoor = is_indoor;
+	if (reg_is_indoor) {
+		if (!reg_is_indoor_portid)
+			reg_is_indoor_portid = portid;
+	} else {
+		reg_is_indoor_portid = 0;
+	}
 
-	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);
+	spin_unlock(&reg_indoor_lock);
+
+	if (!is_indoor)
+		reg_check_channels();
 
 	return 0;
 }
 
+void regulatory_netlink_notify(u32 portid)
+{
+	spin_lock(&reg_indoor_lock);
+
+	if (reg_is_indoor_portid != portid) {
+		spin_unlock(&reg_indoor_lock);
+		return;
+	}
+
+	reg_is_indoor = false;
+	reg_is_indoor_portid = 0;
+
+	spin_unlock(&reg_indoor_lock);
+
+	reg_check_channels();
+}
+
 /* Driver hints */
 int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 {
@@ -2254,6 +2375,9 @@
 	request->alpha2[1] = alpha2[1];
 	request->initiator = NL80211_REGDOM_SET_BY_DRIVER;
 
+	/* Allow calling CRDA again */
+	reg_crda_timeouts = 0;
+
 	queue_regulatory_request(request);
 
 	return 0;
@@ -2307,6 +2431,9 @@
 	request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE;
 	request->country_ie_env = env;
 
+	/* Allow calling CRDA again */
+	reg_crda_timeouts = 0;
+
 	queue_regulatory_request(request);
 	request = NULL;
 out:
@@ -2395,13 +2522,22 @@
 	char alpha2[2];
 	char world_alpha2[2];
 	struct reg_beacon *reg_beacon, *btmp;
-	struct regulatory_request *reg_request, *tmp;
 	LIST_HEAD(tmp_reg_req_list);
 	struct cfg80211_registered_device *rdev;
 
 	ASSERT_RTNL();
 
-	reg_is_indoor = false;
+	/*
+	 * Clear the indoor setting in case that it is not controlled by user
+	 * space, as otherwise there is no guarantee that the device is still
+	 * operating in an indoor environment.
+	 */
+	spin_lock(&reg_indoor_lock);
+	if (reg_is_indoor && !reg_is_indoor_portid) {
+		reg_is_indoor = false;
+		reg_check_channels();
+	}
+	spin_unlock(&reg_indoor_lock);
 
 	reset_regdomains(true, &world_regdom);
 	restore_alpha2(alpha2, reset_user);
@@ -2413,11 +2549,7 @@
 	 * settings.
 	 */
 	spin_lock(&reg_requests_lock);
-	list_for_each_entry_safe(reg_request, tmp, &reg_requests_list, list) {
-		if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER)
-			continue;
-		list_move_tail(&reg_request->list, &tmp_reg_req_list);
-	}
+	list_splice_tail_init(&reg_requests_list, &tmp_reg_req_list);
 	spin_unlock(&reg_requests_lock);
 
 	/* Clear beacon hints */
@@ -2438,6 +2570,8 @@
 	world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
 
 	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+		if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+			continue;
 		if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
 			restore_custom_reg_settings(&rdev->wiphy);
 	}
@@ -2778,7 +2912,8 @@
  * multiple drivers can be ironed out later. Caller must've already
  * kmalloc'd the rd structure.
  */
-int set_regdom(const struct ieee80211_regdomain *rd)
+int set_regdom(const struct ieee80211_regdomain *rd,
+	       enum ieee80211_regd_source regd_src)
 {
 	struct regulatory_request *lr;
 	bool user_reset = false;
@@ -2789,6 +2924,9 @@
 		return -EINVAL;
 	}
 
+	if (regd_src == REGD_SOURCE_CRDA)
+		reg_crda_timeouts = 0;
+
 	lr = get_last_request();
 
 	/* Note that this doesn't update the wiphys, this is done below */
@@ -2841,10 +2979,79 @@
 	return 0;
 }
 
+static int __regulatory_set_wiphy_regd(struct wiphy *wiphy,
+				       struct ieee80211_regdomain *rd)
+{
+	const struct ieee80211_regdomain *regd;
+	const struct ieee80211_regdomain *prev_regd;
+	struct cfg80211_registered_device *rdev;
+
+	if (WARN_ON(!wiphy || !rd))
+		return -EINVAL;
+
+	if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED),
+		 "wiphy should have REGULATORY_WIPHY_SELF_MANAGED\n"))
+		return -EPERM;
+
+	if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected\n")) {
+		print_regdomain_info(rd);
+		return -EINVAL;
+	}
+
+	regd = reg_copy_regd(rd);
+	if (IS_ERR(regd))
+		return PTR_ERR(regd);
+
+	rdev = wiphy_to_rdev(wiphy);
+
+	spin_lock(&reg_requests_lock);
+	prev_regd = rdev->requested_regd;
+	rdev->requested_regd = regd;
+	spin_unlock(&reg_requests_lock);
+
+	kfree(prev_regd);
+	return 0;
+}
+
+int regulatory_set_wiphy_regd(struct wiphy *wiphy,
+			      struct ieee80211_regdomain *rd)
+{
+	int ret = __regulatory_set_wiphy_regd(wiphy, rd);
+
+	if (ret)
+		return ret;
+
+	schedule_work(&reg_work);
+	return 0;
+}
+EXPORT_SYMBOL(regulatory_set_wiphy_regd);
+
+int regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy,
+					struct ieee80211_regdomain *rd)
+{
+	int ret;
+
+	ASSERT_RTNL();
+
+	ret = __regulatory_set_wiphy_regd(wiphy, rd);
+	if (ret)
+		return ret;
+
+	/* process the request immediately */
+	reg_process_self_managed_hints();
+	return 0;
+}
+EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync_rtnl);
+
 void wiphy_regulatory_register(struct wiphy *wiphy)
 {
 	struct regulatory_request *lr;
 
+	/* self-managed devices ignore external hints */
+	if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+		wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
+					   REGULATORY_COUNTRY_IE_IGNORE;
+
 	if (!reg_dev_ignore_cell_hint(wiphy))
 		reg_num_devs_support_basehint++;
 
@@ -2879,6 +3086,7 @@
 {
 	REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
 	rtnl_lock();
+	reg_crda_timeouts++;
 	restore_regulatory_settings(true);
 	rtnl_unlock();
 }
@@ -2927,6 +3135,7 @@
 
 	spin_lock_init(&reg_requests_lock);
 	spin_lock_init(&reg_pending_beacons_lock);
+	spin_lock_init(&reg_indoor_lock);
 
 	reg_regdb_size_check();
 
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 5e48031..9f495d7 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -16,6 +16,11 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+enum ieee80211_regd_source {
+	REGD_SOURCE_INTERNAL_DB,
+	REGD_SOURCE_CRDA,
+};
+
 extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
 
 bool reg_is_valid_request(const char *alpha2);
@@ -25,7 +30,20 @@
 
 int regulatory_hint_user(const char *alpha2,
 			 enum nl80211_user_reg_hint_type user_reg_hint_type);
-int regulatory_hint_indoor_user(void);
+
+/**
+ * regulatory_hint_indoor - hint operation in indoor env. or not
+ * @is_indoor: if true indicates that user space thinks that the
+ * device is operating in an indoor environment.
+ * @portid: the netlink port ID on which the hint was given.
+ */
+int regulatory_hint_indoor(bool is_indoor, u32 portid);
+
+/**
+ * regulatory_netlink_notify - notify on released netlink socket
+ * @portid: the netlink socket port ID
+ */
+void regulatory_netlink_notify(u32 portid);
 
 void wiphy_regulatory_register(struct wiphy *wiphy);
 void wiphy_regulatory_deregister(struct wiphy *wiphy);
@@ -33,11 +51,14 @@
 int __init regulatory_init(void);
 void regulatory_exit(void);
 
-int set_regdom(const struct ieee80211_regdomain *rd);
+int set_regdom(const struct ieee80211_regdomain *rd,
+	       enum ieee80211_regd_source regd_src);
+
 unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
 				   const struct ieee80211_reg_rule *rule);
 
 bool reg_last_request_cell_base(void);
+const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy);
 
 /**
  * regulatory_hint_found_beacon - hints a beacon was found on a channel
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index f3ac474..6add3f8 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -257,7 +257,7 @@
 
 	rtnl_lock();
 
-	request = rdev->sched_scan_req;
+	request = rtnl_dereference(rdev->sched_scan_req);
 
 	/* we don't have sched_scan_req anymore if the scan is stopping */
 	if (request) {
@@ -279,7 +279,8 @@
 {
 	trace_cfg80211_sched_scan_results(wiphy);
 	/* ignore if we're not scanning */
-	if (wiphy_to_rdev(wiphy)->sched_scan_req)
+
+	if (rcu_access_pointer(wiphy_to_rdev(wiphy)->sched_scan_req))
 		queue_work(cfg80211_wq,
 			   &wiphy_to_rdev(wiphy)->sched_scan_results_wk);
 }
@@ -308,6 +309,7 @@
 int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
 			       bool driver_initiated)
 {
+	struct cfg80211_sched_scan_request *sched_scan_req;
 	struct net_device *dev;
 
 	ASSERT_RTNL();
@@ -315,7 +317,8 @@
 	if (!rdev->sched_scan_req)
 		return -ENOENT;
 
-	dev = rdev->sched_scan_req->dev;
+	sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
+	dev = sched_scan_req->dev;
 
 	if (!driver_initiated) {
 		int err = rdev_sched_scan_stop(rdev, dev);
@@ -325,8 +328,8 @@
 
 	nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED);
 
-	kfree(rdev->sched_scan_req);
-	rdev->sched_scan_req = NULL;
+	RCU_INIT_POINTER(rdev->sched_scan_req, NULL);
+	kfree_rcu(sched_scan_req, rcu_head);
 
 	return 0;
 }
@@ -528,24 +531,78 @@
 	}
 }
 
+static bool cfg80211_bss_type_match(u16 capability,
+				    enum ieee80211_band band,
+				    enum ieee80211_bss_type bss_type)
+{
+	bool ret = true;
+	u16 mask, val;
+
+	if (bss_type == IEEE80211_BSS_TYPE_ANY)
+		return ret;
+
+	if (band == IEEE80211_BAND_60GHZ) {
+		mask = WLAN_CAPABILITY_DMG_TYPE_MASK;
+		switch (bss_type) {
+		case IEEE80211_BSS_TYPE_ESS:
+			val = WLAN_CAPABILITY_DMG_TYPE_AP;
+			break;
+		case IEEE80211_BSS_TYPE_PBSS:
+			val = WLAN_CAPABILITY_DMG_TYPE_PBSS;
+			break;
+		case IEEE80211_BSS_TYPE_IBSS:
+			val = WLAN_CAPABILITY_DMG_TYPE_IBSS;
+			break;
+		default:
+			return false;
+		}
+	} else {
+		mask = WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS;
+		switch (bss_type) {
+		case IEEE80211_BSS_TYPE_ESS:
+			val = WLAN_CAPABILITY_ESS;
+			break;
+		case IEEE80211_BSS_TYPE_IBSS:
+			val = WLAN_CAPABILITY_IBSS;
+			break;
+		case IEEE80211_BSS_TYPE_MBSS:
+			val = 0;
+			break;
+		default:
+			return false;
+		}
+	}
+
+	ret = ((capability & mask) == val);
+	return ret;
+}
+
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
 				      struct ieee80211_channel *channel,
 				      const u8 *bssid,
 				      const u8 *ssid, size_t ssid_len,
-				      u16 capa_mask, u16 capa_val)
+				      enum ieee80211_bss_type bss_type,
+				      enum ieee80211_privacy privacy)
 {
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 	struct cfg80211_internal_bss *bss, *res = NULL;
 	unsigned long now = jiffies;
+	int bss_privacy;
 
-	trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, capa_mask,
-			       capa_val);
+	trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, bss_type,
+			       privacy);
 
 	spin_lock_bh(&rdev->bss_lock);
 
 	list_for_each_entry(bss, &rdev->bss_list, list) {
-		if ((bss->pub.capability & capa_mask) != capa_val)
+		if (!cfg80211_bss_type_match(bss->pub.capability,
+					     bss->pub.channel->band, bss_type))
+			continue;
+
+		bss_privacy = (bss->pub.capability & WLAN_CAPABILITY_PRIVACY);
+		if ((privacy == IEEE80211_PRIVACY_ON && !bss_privacy) ||
+		    (privacy == IEEE80211_PRIVACY_OFF && bss_privacy))
 			continue;
 		if (channel && bss->pub.channel != channel)
 			continue;
@@ -893,6 +950,7 @@
 	struct cfg80211_bss_ies *ies;
 	struct ieee80211_channel *channel;
 	struct cfg80211_internal_bss tmp = {}, *res;
+	int bss_type;
 	bool signal_valid;
 
 	if (WARN_ON(!wiphy))
@@ -947,8 +1005,15 @@
 	if (!res)
 		return NULL;
 
-	if (res->pub.capability & WLAN_CAPABILITY_ESS)
-		regulatory_hint_found_beacon(wiphy, channel, gfp);
+	if (channel->band == IEEE80211_BAND_60GHZ) {
+		bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
+		if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
+		    bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
+			regulatory_hint_found_beacon(wiphy, channel, gfp);
+	} else {
+		if (res->pub.capability & WLAN_CAPABILITY_ESS)
+			regulatory_hint_found_beacon(wiphy, channel, gfp);
+	}
 
 	trace_cfg80211_return_bss(&res->pub);
 	/* cfg80211_bss_update gives us a referenced result */
@@ -970,6 +1035,7 @@
 	bool signal_valid;
 	size_t ielen = len - offsetof(struct ieee80211_mgmt,
 				      u.probe_resp.variable);
+	int bss_type;
 
 	BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
 			offsetof(struct ieee80211_mgmt, u.beacon.variable));
@@ -1022,8 +1088,15 @@
 	if (!res)
 		return NULL;
 
-	if (res->pub.capability & WLAN_CAPABILITY_ESS)
-		regulatory_hint_found_beacon(wiphy, channel, gfp);
+	if (channel->band == IEEE80211_BAND_60GHZ) {
+		bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
+		if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
+		    bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
+			regulatory_hint_found_beacon(wiphy, channel, gfp);
+	} else {
+		if (res->pub.capability & WLAN_CAPABILITY_ESS)
+			regulatory_hint_found_beacon(wiphy, channel, gfp);
+	}
 
 	trace_cfg80211_return_bss(&res->pub);
 	/* cfg80211_bss_update gives us a referenced result */
@@ -1234,17 +1307,17 @@
 	kfree(creq);
 	return err;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
+EXPORT_WEXT_HANDLER(cfg80211_wext_siwscan);
 
-static void ieee80211_scan_add_ies(struct iw_request_info *info,
-				   const struct cfg80211_bss_ies *ies,
-				   char **current_ev, char *end_buf)
+static char *ieee80211_scan_add_ies(struct iw_request_info *info,
+				    const struct cfg80211_bss_ies *ies,
+				    char *current_ev, char *end_buf)
 {
 	const u8 *pos, *end, *next;
 	struct iw_event iwe;
 
 	if (!ies)
-		return;
+		return current_ev;
 
 	/*
 	 * If needed, fragment the IEs buffer (at IE boundaries) into short
@@ -1261,10 +1334,11 @@
 		memset(&iwe, 0, sizeof(iwe));
 		iwe.cmd = IWEVGENIE;
 		iwe.u.data.length = next - pos;
-		*current_ev = iwe_stream_add_point(info, *current_ev,
-						   end_buf, &iwe,
-						   (void *)pos);
-
+		current_ev = iwe_stream_add_point_check(info, current_ev,
+							end_buf, &iwe,
+							(void *)pos);
+		if (IS_ERR(current_ev))
+			return current_ev;
 		pos = next;
 	}
 
@@ -1272,10 +1346,14 @@
 		memset(&iwe, 0, sizeof(iwe));
 		iwe.cmd = IWEVGENIE;
 		iwe.u.data.length = end - pos;
-		*current_ev = iwe_stream_add_point(info, *current_ev,
-						   end_buf, &iwe,
-						   (void *)pos);
+		current_ev = iwe_stream_add_point_check(info, current_ev,
+							end_buf, &iwe,
+							(void *)pos);
+		if (IS_ERR(current_ev))
+			return current_ev;
 	}
+
+	return current_ev;
 }
 
 static char *
@@ -1286,7 +1364,8 @@
 	const struct cfg80211_bss_ies *ies;
 	struct iw_event iwe;
 	const u8 *ie;
-	u8 *buf, *cfg, *p;
+	u8 buf[50];
+	u8 *cfg, *p, *tmp;
 	int rem, i, sig;
 	bool ismesh = false;
 
@@ -1294,22 +1373,28 @@
 	iwe.cmd = SIOCGIWAP;
 	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
 	memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
-	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-					  IW_EV_ADDR_LEN);
+	current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+						IW_EV_ADDR_LEN);
+	if (IS_ERR(current_ev))
+		return current_ev;
 
 	memset(&iwe, 0, sizeof(iwe));
 	iwe.cmd = SIOCGIWFREQ;
 	iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
 	iwe.u.freq.e = 0;
-	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-					  IW_EV_FREQ_LEN);
+	current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+						IW_EV_FREQ_LEN);
+	if (IS_ERR(current_ev))
+		return current_ev;
 
 	memset(&iwe, 0, sizeof(iwe));
 	iwe.cmd = SIOCGIWFREQ;
 	iwe.u.freq.m = bss->pub.channel->center_freq;
 	iwe.u.freq.e = 6;
-	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-					  IW_EV_FREQ_LEN);
+	current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+						IW_EV_FREQ_LEN);
+	if (IS_ERR(current_ev))
+		return current_ev;
 
 	if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
 		memset(&iwe, 0, sizeof(iwe));
@@ -1338,8 +1423,11 @@
 			/* not reached */
 			break;
 		}
-		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
-						  &iwe, IW_EV_QUAL_LEN);
+		current_ev = iwe_stream_add_event_check(info, current_ev,
+							end_buf, &iwe,
+							IW_EV_QUAL_LEN);
+		if (IS_ERR(current_ev))
+			return current_ev;
 	}
 
 	memset(&iwe, 0, sizeof(iwe));
@@ -1349,8 +1437,10 @@
 	else
 		iwe.u.data.flags = IW_ENCODE_DISABLED;
 	iwe.u.data.length = 0;
-	current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-					  &iwe, "");
+	current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
+						&iwe, "");
+	if (IS_ERR(current_ev))
+		return current_ev;
 
 	rcu_read_lock();
 	ies = rcu_dereference(bss->pub.ies);
@@ -1368,66 +1458,91 @@
 			iwe.cmd = SIOCGIWESSID;
 			iwe.u.data.length = ie[1];
 			iwe.u.data.flags = 1;
-			current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-							  &iwe, (u8 *)ie + 2);
+			current_ev = iwe_stream_add_point_check(info,
+								current_ev,
+								end_buf, &iwe,
+								(u8 *)ie + 2);
+			if (IS_ERR(current_ev))
+				goto unlock;
 			break;
 		case WLAN_EID_MESH_ID:
 			memset(&iwe, 0, sizeof(iwe));
 			iwe.cmd = SIOCGIWESSID;
 			iwe.u.data.length = ie[1];
 			iwe.u.data.flags = 1;
-			current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-							  &iwe, (u8 *)ie + 2);
+			current_ev = iwe_stream_add_point_check(info,
+								current_ev,
+								end_buf, &iwe,
+								(u8 *)ie + 2);
+			if (IS_ERR(current_ev))
+				goto unlock;
 			break;
 		case WLAN_EID_MESH_CONFIG:
 			ismesh = true;
 			if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
 				break;
-			buf = kmalloc(50, GFP_ATOMIC);
-			if (!buf)
-				break;
 			cfg = (u8 *)ie + 2;
 			memset(&iwe, 0, sizeof(iwe));
 			iwe.cmd = IWEVCUSTOM;
 			sprintf(buf, "Mesh Network Path Selection Protocol ID: "
 				"0x%02X", cfg[0]);
 			iwe.u.data.length = strlen(buf);
-			current_ev = iwe_stream_add_point(info, current_ev,
-							  end_buf,
-							  &iwe, buf);
+			current_ev = iwe_stream_add_point_check(info,
+								current_ev,
+								end_buf,
+								&iwe, buf);
+			if (IS_ERR(current_ev))
+				goto unlock;
 			sprintf(buf, "Path Selection Metric ID: 0x%02X",
 				cfg[1]);
 			iwe.u.data.length = strlen(buf);
-			current_ev = iwe_stream_add_point(info, current_ev,
-							  end_buf,
-							  &iwe, buf);
+			current_ev = iwe_stream_add_point_check(info,
+								current_ev,
+								end_buf,
+								&iwe, buf);
+			if (IS_ERR(current_ev))
+				goto unlock;
 			sprintf(buf, "Congestion Control Mode ID: 0x%02X",
 				cfg[2]);
 			iwe.u.data.length = strlen(buf);
-			current_ev = iwe_stream_add_point(info, current_ev,
-							  end_buf,
-							  &iwe, buf);
+			current_ev = iwe_stream_add_point_check(info,
+								current_ev,
+								end_buf,
+								&iwe, buf);
+			if (IS_ERR(current_ev))
+				goto unlock;
 			sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
 			iwe.u.data.length = strlen(buf);
-			current_ev = iwe_stream_add_point(info, current_ev,
-							  end_buf,
-							  &iwe, buf);
+			current_ev = iwe_stream_add_point_check(info,
+								current_ev,
+								end_buf,
+								&iwe, buf);
+			if (IS_ERR(current_ev))
+				goto unlock;
 			sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
 			iwe.u.data.length = strlen(buf);
-			current_ev = iwe_stream_add_point(info, current_ev,
-							  end_buf,
-							  &iwe, buf);
+			current_ev = iwe_stream_add_point_check(info,
+								current_ev,
+								end_buf,
+								&iwe, buf);
+			if (IS_ERR(current_ev))
+				goto unlock;
 			sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
 			iwe.u.data.length = strlen(buf);
-			current_ev = iwe_stream_add_point(info, current_ev,
-							  end_buf,
-							  &iwe, buf);
+			current_ev = iwe_stream_add_point_check(info,
+								current_ev,
+								end_buf,
+								&iwe, buf);
+			if (IS_ERR(current_ev))
+				goto unlock;
 			sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
 			iwe.u.data.length = strlen(buf);
-			current_ev = iwe_stream_add_point(info, current_ev,
-							  end_buf,
-							  &iwe, buf);
-			kfree(buf);
+			current_ev = iwe_stream_add_point_check(info,
+								current_ev,
+								end_buf,
+								&iwe, buf);
+			if (IS_ERR(current_ev))
+				goto unlock;
 			break;
 		case WLAN_EID_SUPP_RATES:
 		case WLAN_EID_EXT_SUPP_RATES:
@@ -1442,8 +1557,14 @@
 			for (i = 0; i < ie[1]; i++) {
 				iwe.u.bitrate.value =
 					((ie[i + 2] & 0x7f) * 500000);
+				tmp = p;
 				p = iwe_stream_add_value(info, current_ev, p,
-						end_buf, &iwe, IW_EV_PARAM_LEN);
+							 end_buf, &iwe,
+							 IW_EV_PARAM_LEN);
+				if (p == tmp) {
+					current_ev = ERR_PTR(-E2BIG);
+					goto unlock;
+				}
 			}
 			current_ev = p;
 			break;
@@ -1462,31 +1583,35 @@
 			iwe.u.mode = IW_MODE_MASTER;
 		else
 			iwe.u.mode = IW_MODE_ADHOC;
-		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
-						  &iwe, IW_EV_UINT_LEN);
+		current_ev = iwe_stream_add_event_check(info, current_ev,
+							end_buf, &iwe,
+							IW_EV_UINT_LEN);
+		if (IS_ERR(current_ev))
+			goto unlock;
 	}
 
-	buf = kmalloc(31, GFP_ATOMIC);
-	if (buf) {
-		memset(&iwe, 0, sizeof(iwe));
-		iwe.cmd = IWEVCUSTOM;
-		sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
-		iwe.u.data.length = strlen(buf);
-		current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-						  &iwe, buf);
-		memset(&iwe, 0, sizeof(iwe));
-		iwe.cmd = IWEVCUSTOM;
-		sprintf(buf, " Last beacon: %ums ago",
-			elapsed_jiffies_msecs(bss->ts));
-		iwe.u.data.length = strlen(buf);
-		current_ev = iwe_stream_add_point(info, current_ev,
-						  end_buf, &iwe, buf);
-		kfree(buf);
-	}
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = IWEVCUSTOM;
+	sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
+	iwe.u.data.length = strlen(buf);
+	current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
+						&iwe, buf);
+	if (IS_ERR(current_ev))
+		goto unlock;
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = IWEVCUSTOM;
+	sprintf(buf, " Last beacon: %ums ago",
+		elapsed_jiffies_msecs(bss->ts));
+	iwe.u.data.length = strlen(buf);
+	current_ev = iwe_stream_add_point_check(info, current_ev,
+						end_buf, &iwe, buf);
+	if (IS_ERR(current_ev))
+		goto unlock;
 
-	ieee80211_scan_add_ies(info, ies, &current_ev, end_buf);
+	current_ev = ieee80211_scan_add_ies(info, ies, current_ev, end_buf);
+
+ unlock:
 	rcu_read_unlock();
-
 	return current_ev;
 }
 
@@ -1498,19 +1623,27 @@
 	char *current_ev = buf;
 	char *end_buf = buf + len;
 	struct cfg80211_internal_bss *bss;
+	int err = 0;
 
 	spin_lock_bh(&rdev->bss_lock);
 	cfg80211_bss_expire(rdev);
 
 	list_for_each_entry(bss, &rdev->bss_list, list) {
 		if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
-			spin_unlock_bh(&rdev->bss_lock);
-			return -E2BIG;
+			err = -E2BIG;
+			break;
 		}
 		current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
 					   current_ev, end_buf);
+		if (IS_ERR(current_ev)) {
+			err = PTR_ERR(current_ev);
+			break;
+		}
 	}
 	spin_unlock_bh(&rdev->bss_lock);
+
+	if (err)
+		return err;
 	return current_ev - buf;
 }
 
@@ -1542,5 +1675,5 @@
 
 	return res;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan);
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwscan);
 #endif
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 969c09f..2fb88cb 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -42,7 +42,7 @@
 		CFG80211_CONN_CONNECTED,
 	} state;
 	u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
-	u8 *ie;
+	const u8 *ie;
 	size_t ie_len;
 	bool auto_auth, prev_bssid_valid;
 };
@@ -257,19 +257,15 @@
 {
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 	struct cfg80211_bss *bss;
-	u16 capa = WLAN_CAPABILITY_ESS;
 
 	ASSERT_WDEV_LOCK(wdev);
 
-	if (wdev->conn->params.privacy)
-		capa |= WLAN_CAPABILITY_PRIVACY;
-
 	bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel,
 			       wdev->conn->params.bssid,
 			       wdev->conn->params.ssid,
 			       wdev->conn->params.ssid_len,
-			       WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
-			       capa);
+			       IEEE80211_BSS_TYPE_ESS,
+			       IEEE80211_PRIVACY(wdev->conn->params.privacy));
 	if (!bss)
 		return NULL;
 
@@ -427,6 +423,62 @@
 	schedule_work(&rdev->conn_work);
 }
 
+static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev,
+				     const u8 *ies, size_t ies_len,
+				     const u8 **out_ies, size_t *out_ies_len)
+{
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+	u8 *buf;
+	size_t offs;
+
+	if (!rdev->wiphy.extended_capabilities_len ||
+	    (ies && cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, ies, ies_len))) {
+		*out_ies = kmemdup(ies, ies_len, GFP_KERNEL);
+		if (!*out_ies)
+			return -ENOMEM;
+		*out_ies_len = ies_len;
+		return 0;
+	}
+
+	buf = kmalloc(ies_len + rdev->wiphy.extended_capabilities_len + 2,
+		      GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (ies_len) {
+		static const u8 before_extcapa[] = {
+			/* not listing IEs expected to be created by driver */
+			WLAN_EID_RSN,
+			WLAN_EID_QOS_CAPA,
+			WLAN_EID_RRM_ENABLED_CAPABILITIES,
+			WLAN_EID_MOBILITY_DOMAIN,
+			WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+			WLAN_EID_BSS_COEX_2040,
+		};
+
+		offs = ieee80211_ie_split(ies, ies_len, before_extcapa,
+					  ARRAY_SIZE(before_extcapa), 0);
+		memcpy(buf, ies, offs);
+		/* leave a whole for extended capabilities IE */
+		memcpy(buf + offs + rdev->wiphy.extended_capabilities_len + 2,
+		       ies + offs, ies_len - offs);
+	} else {
+		offs = 0;
+	}
+
+	/* place extended capabilities IE (with only driver capabilities) */
+	buf[offs] = WLAN_EID_EXT_CAPABILITY;
+	buf[offs + 1] = rdev->wiphy.extended_capabilities_len;
+	memcpy(buf + offs + 2,
+	       rdev->wiphy.extended_capabilities,
+	       rdev->wiphy.extended_capabilities_len);
+
+	*out_ies = buf;
+	*out_ies_len = ies_len + rdev->wiphy.extended_capabilities_len + 2;
+
+	return 0;
+}
+
 static int cfg80211_sme_connect(struct wireless_dev *wdev,
 				struct cfg80211_connect_params *connect,
 				const u8 *prev_bssid)
@@ -457,16 +509,14 @@
 		memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
 	}
 
-	if (connect->ie) {
-		wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
-					GFP_KERNEL);
-		wdev->conn->params.ie = wdev->conn->ie;
-		if (!wdev->conn->ie) {
-			kfree(wdev->conn);
-			wdev->conn = NULL;
-			return -ENOMEM;
-		}
+	if (cfg80211_sme_get_conn_ies(wdev, connect->ie, connect->ie_len,
+				      &wdev->conn->ie,
+				      &wdev->conn->params.ie_len)) {
+		kfree(wdev->conn);
+		wdev->conn = NULL;
+		return -ENOMEM;
 	}
+	wdev->conn->params.ie = wdev->conn->ie;
 
 	if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
 		wdev->conn->auto_auth = true;
@@ -637,8 +687,8 @@
 		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,
-				       WLAN_CAPABILITY_ESS);
+				       IEEE80211_BSS_TYPE_ESS,
+				       IEEE80211_PRIVACY_ANY);
 		if (bss)
 			cfg80211_hold_bss(bss_from_pub(bss));
 	}
@@ -795,8 +845,8 @@
 	struct cfg80211_bss *bss;
 
 	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
-			       wdev->ssid_len, WLAN_CAPABILITY_ESS,
-			       WLAN_CAPABILITY_ESS);
+			       wdev->ssid_len,
+			       IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY);
 	if (WARN_ON(!bss))
 		return;
 
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index d9d146d..4ed6b3a 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -7,6 +7,7 @@
 #include <linux/tracepoint.h>
 
 #include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
 #include <net/cfg80211.h>
 #include "core.h"
 
@@ -15,7 +16,7 @@
 	if (given_mac)						     \
 		memcpy(__entry->entry_mac, given_mac, ETH_ALEN);     \
 	else							     \
-		memset(__entry->entry_mac, 0, ETH_ALEN);	     \
+		eth_zero_addr(__entry->entry_mac);		     \
 	} while (0)
 #define MAC_PR_FMT "%pM"
 #define MAC_PR_ARG(entry_mac) (__entry->entry_mac)
@@ -627,6 +628,7 @@
 		__field(u8, plink_state)
 		__field(u8, uapsd_queues)
 		__array(u8, ht_capa, (int)sizeof(struct ieee80211_ht_cap))
+		__array(char, vlan, IFNAMSIZ)
 	),
 	TP_fast_assign(
 		WIPHY_ASSIGN;
@@ -644,16 +646,19 @@
 		if (params->ht_capa)
 			memcpy(__entry->ht_capa, params->ht_capa,
 			       sizeof(struct ieee80211_ht_cap));
+		memset(__entry->vlan, 0, sizeof(__entry->vlan));
+		if (params->vlan)
+			memcpy(__entry->vlan, params->vlan->name, IFNAMSIZ);
 	),
 	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
 		  ", station flags mask: %u, station flags set: %u, "
 		  "station modify mask: %u, listen interval: %d, aid: %u, "
-		  "plink action: %u, plink state: %u, uapsd queues: %u",
+		  "plink action: %u, plink state: %u, uapsd queues: %u, vlan:%s",
 		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
 		  __entry->sta_flags_mask, __entry->sta_flags_set,
 		  __entry->sta_modify_mask, __entry->listen_interval,
 		  __entry->aid, __entry->plink_action, __entry->plink_state,
-		  __entry->uapsd_queues)
+		  __entry->uapsd_queues, __entry->vlan)
 );
 
 DEFINE_EVENT(station_add_change, rdev_add_station,
@@ -1077,7 +1082,7 @@
 		if (req->bss)
 			MAC_ASSIGN(bssid, req->bss->bssid);
 		else
-			memset(__entry->bssid, 0, ETH_ALEN);
+			eth_zero_addr(__entry->bssid);
 		__entry->auth_type = req->auth_type;
 	),
 	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", auth type: %d, bssid: " MAC_PR_FMT,
@@ -1103,7 +1108,7 @@
 		if (req->bss)
 			MAC_ASSIGN(bssid, req->bss->bssid);
 		else
-			memset(__entry->bssid, 0, ETH_ALEN);
+			eth_zero_addr(__entry->bssid);
 		MAC_ASSIGN(prev_bssid, req->prev_bssid);
 		__entry->use_mfp = req->use_mfp;
 		__entry->flags = req->flags;
@@ -1153,7 +1158,7 @@
 		if (req->bss)
 			MAC_ASSIGN(bssid, req->bss->bssid);
 		else
-			memset(__entry->bssid, 0, ETH_ALEN);
+			eth_zero_addr(__entry->bssid);
 		__entry->reason_code = req->reason_code;
 		__entry->local_state_change = req->local_state_change;
 	),
@@ -1604,11 +1609,12 @@
 		WIPHY_ENTRY
 		CHAN_ENTRY
 		__field(int, ret)
-		__field(u64, channel_time)
-		__field(u64, channel_time_busy)
-		__field(u64, channel_time_ext_busy)
-		__field(u64, channel_time_rx)
-		__field(u64, channel_time_tx)
+		__field(u64, time)
+		__field(u64, time_busy)
+		__field(u64, time_ext_busy)
+		__field(u64, time_rx)
+		__field(u64, time_tx)
+		__field(u64, time_scan)
 		__field(u32, filled)
 		__field(s8, noise)
 	),
@@ -1616,22 +1622,24 @@
 		WIPHY_ASSIGN;
 		CHAN_ASSIGN(info->channel);
 		__entry->ret = ret;
-		__entry->channel_time = info->channel_time;
-		__entry->channel_time_busy = info->channel_time_busy;
-		__entry->channel_time_ext_busy = info->channel_time_ext_busy;
-		__entry->channel_time_rx = info->channel_time_rx;
-		__entry->channel_time_tx = info->channel_time_tx;
+		__entry->time = info->time;
+		__entry->time_busy = info->time_busy;
+		__entry->time_ext_busy = info->time_ext_busy;
+		__entry->time_rx = info->time_rx;
+		__entry->time_tx = info->time_tx;
+		__entry->time_scan = info->time_scan;
 		__entry->filled = info->filled;
 		__entry->noise = info->noise;
 	),
 	TP_printk(WIPHY_PR_FMT ", returned: %d, " CHAN_PR_FMT
 		  ", channel time: %llu, channel time busy: %llu, "
 		  "channel time extension busy: %llu, channel time rx: %llu, "
-		  "channel time tx: %llu, filled: %u, noise: %d",
+		  "channel time tx: %llu, scan time: %llu, filled: %u, noise: %d",
 		  WIPHY_PR_ARG, __entry->ret, CHAN_PR_ARG,
-		  __entry->channel_time, __entry->channel_time_busy,
-		  __entry->channel_time_ext_busy, __entry->channel_time_rx,
-		  __entry->channel_time_tx, __entry->filled, __entry->noise)
+		  __entry->time, __entry->time_busy,
+		  __entry->time_ext_busy, __entry->time_rx,
+		  __entry->time_tx, __entry->time_scan,
+		  __entry->filled, __entry->noise)
 );
 
 TRACE_EVENT(rdev_tdls_oper,
@@ -2633,28 +2641,30 @@
 TRACE_EVENT(cfg80211_get_bss,
 	TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
 		 const u8 *bssid, const u8 *ssid, size_t ssid_len,
-		 u16 capa_mask, u16 capa_val),
-	TP_ARGS(wiphy, channel, bssid, ssid, ssid_len, capa_mask, capa_val),
+		 enum ieee80211_bss_type bss_type,
+		 enum ieee80211_privacy privacy),
+	TP_ARGS(wiphy, channel, bssid, ssid, ssid_len, bss_type, privacy),
 	TP_STRUCT__entry(
 		WIPHY_ENTRY
 		CHAN_ENTRY
 		MAC_ENTRY(bssid)
 		__dynamic_array(u8, ssid, ssid_len)
-		__field(u16, capa_mask)
-		__field(u16, capa_val)
+		__field(enum ieee80211_bss_type, bss_type)
+		__field(enum ieee80211_privacy, privacy)
 	),
 	TP_fast_assign(
 		WIPHY_ASSIGN;
 		CHAN_ASSIGN(channel);
 		MAC_ASSIGN(bssid, bssid);
 		memcpy(__get_dynamic_array(ssid), ssid, ssid_len);
-		__entry->capa_mask = capa_mask;
-		__entry->capa_val = capa_val;
+		__entry->bss_type = bss_type;
+		__entry->privacy = privacy;
 	),
-	TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT ", " MAC_PR_FMT ", buf: %#.2x, "
-		  "capa_mask: %d, capa_val: %u", WIPHY_PR_ARG, CHAN_PR_ARG,
-		  MAC_PR_ARG(bssid), ((u8 *)__get_dynamic_array(ssid))[0],
-		  __entry->capa_mask, __entry->capa_val)
+	TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT ", " MAC_PR_FMT
+		  ", buf: %#.2x, bss_type: %d, privacy: %d",
+		  WIPHY_PR_ARG, CHAN_PR_ARG, MAC_PR_ARG(bssid),
+		  ((u8 *)__get_dynamic_array(ssid))[0], __entry->bss_type,
+		  __entry->privacy)
 );
 
 TRACE_EVENT(cfg80211_inform_bss_width_frame,
diff --git a/net/wireless/util.c b/net/wireless/util.c
index d0ac795..70051ab 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -227,18 +227,32 @@
 	if (pairwise && !mac_addr)
 		return -EINVAL;
 
-	/*
-	 * Disallow pairwise keys with non-zero index unless it's WEP
-	 * or a vendor specific cipher (because current deployments use
-	 * pairwise WEP keys with non-zero indices and for vendor specific
-	 * ciphers this should be validated in the driver or hardware level
-	 * - but 802.11i clearly specifies to use zero)
-	 */
-	if (pairwise && key_idx &&
-	    ((params->cipher == WLAN_CIPHER_SUITE_TKIP) ||
-	     (params->cipher == WLAN_CIPHER_SUITE_CCMP) ||
-	     (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC)))
-		return -EINVAL;
+	switch (params->cipher) {
+	case WLAN_CIPHER_SUITE_TKIP:
+	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_CCMP_256:
+	case WLAN_CIPHER_SUITE_GCMP:
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		/* Disallow pairwise keys with non-zero index unless it's WEP
+		 * or a vendor specific cipher (because current deployments use
+		 * pairwise WEP keys with non-zero indices and for vendor
+		 * specific ciphers this should be validated in the driver or
+		 * hardware level - but 802.11i clearly specifies to use zero)
+		 */
+		if (pairwise && key_idx)
+			return -EINVAL;
+		break;
+	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		/* Disallow BIP (group-only) cipher as pairwise cipher */
+		if (pairwise)
+			return -EINVAL;
+		break;
+	default:
+		break;
+	}
 
 	switch (params->cipher) {
 	case WLAN_CIPHER_SUITE_WEP40:
@@ -253,6 +267,18 @@
 		if (params->key_len != WLAN_KEY_LEN_CCMP)
 			return -EINVAL;
 		break;
+	case WLAN_CIPHER_SUITE_CCMP_256:
+		if (params->key_len != WLAN_KEY_LEN_CCMP_256)
+			return -EINVAL;
+		break;
+	case WLAN_CIPHER_SUITE_GCMP:
+		if (params->key_len != WLAN_KEY_LEN_GCMP)
+			return -EINVAL;
+		break;
+	case WLAN_CIPHER_SUITE_GCMP_256:
+		if (params->key_len != WLAN_KEY_LEN_GCMP_256)
+			return -EINVAL;
+		break;
 	case WLAN_CIPHER_SUITE_WEP104:
 		if (params->key_len != WLAN_KEY_LEN_WEP104)
 			return -EINVAL;
@@ -261,6 +287,18 @@
 		if (params->key_len != WLAN_KEY_LEN_AES_CMAC)
 			return -EINVAL;
 		break;
+	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+		if (params->key_len != WLAN_KEY_LEN_BIP_CMAC_256)
+			return -EINVAL;
+		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+		if (params->key_len != WLAN_KEY_LEN_BIP_GMAC_128)
+			return -EINVAL;
+		break;
+	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		if (params->key_len != WLAN_KEY_LEN_BIP_GMAC_256)
+			return -EINVAL;
+		break;
 	default:
 		/*
 		 * We don't know anything about this algorithm,
@@ -280,7 +318,13 @@
 			return -EINVAL;
 		case WLAN_CIPHER_SUITE_TKIP:
 		case WLAN_CIPHER_SUITE_CCMP:
+		case WLAN_CIPHER_SUITE_CCMP_256:
+		case WLAN_CIPHER_SUITE_GCMP:
+		case WLAN_CIPHER_SUITE_GCMP_256:
 		case WLAN_CIPHER_SUITE_AES_CMAC:
+		case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+		case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+		case WLAN_CIPHER_SUITE_BIP_GMAC_256:
 			if (params->seq_len != 6)
 				return -EINVAL;
 			break;
@@ -308,6 +352,12 @@
 		goto out;
 	}
 
+	if (ieee80211_is_mgmt(fc)) {
+		if (ieee80211_has_order(fc))
+			hdrlen += IEEE80211_HT_CTL_LEN;
+		goto out;
+	}
+
 	if (ieee80211_is_ctl(fc)) {
 		/*
 		 * ACK and CTS are 10 bytes, all others 16. To see how
@@ -708,8 +758,8 @@
 	if (skb->priority >= 256 && skb->priority <= 263)
 		return skb->priority - 256;
 
-	if (vlan_tx_tag_present(skb)) {
-		vlan_priority = (vlan_tx_tag_get(skb) & VLAN_PRIO_MASK)
+	if (skb_vlan_tag_present(skb)) {
+		vlan_priority = (skb_vlan_tag_get(skb) & VLAN_PRIO_MASK)
 			>> VLAN_PRIO_SHIFT;
 		if (vlan_priority > 0)
 			return vlan_priority;
@@ -1073,10 +1123,24 @@
 	if (WARN_ON_ONCE(rate->mcs > 9))
 		return 0;
 
-	idx = rate->flags & (RATE_INFO_FLAGS_160_MHZ_WIDTH |
-			     RATE_INFO_FLAGS_80P80_MHZ_WIDTH) ? 3 :
-		  rate->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH ? 2 :
-		  rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH ? 1 : 0;
+	switch (rate->bw) {
+	case RATE_INFO_BW_160:
+		idx = 3;
+		break;
+	case RATE_INFO_BW_80:
+		idx = 2;
+		break;
+	case RATE_INFO_BW_40:
+		idx = 1;
+		break;
+	case RATE_INFO_BW_5:
+	case RATE_INFO_BW_10:
+	default:
+		WARN_ON(1);
+		/* fall through */
+	case RATE_INFO_BW_20:
+		idx = 0;
+	}
 
 	bitrate = base[idx][rate->mcs];
 	bitrate *= rate->nss;
@@ -1107,8 +1171,7 @@
 	modulation = rate->mcs & 7;
 	streams = (rate->mcs >> 3) + 1;
 
-	bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
-			13500000 : 6500000;
+	bitrate = (rate->bw == RATE_INFO_BW_40) ? 13500000 : 6500000;
 
 	if (modulation < 4)
 		bitrate *= (modulation + 1);
@@ -1227,12 +1290,54 @@
 }
 EXPORT_SYMBOL(cfg80211_get_p2p_attr);
 
+static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
+{
+	int i;
+
+	for (i = 0; i < n_ids; i++)
+		if (ids[i] == id)
+			return true;
+	return false;
+}
+
+size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
+			      const u8 *ids, int n_ids,
+			      const u8 *after_ric, int n_after_ric,
+			      size_t offset)
+{
+	size_t pos = offset;
+
+	while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
+		if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
+			pos += 2 + ies[pos + 1];
+
+			while (pos < ielen &&
+			       !ieee80211_id_in_list(after_ric, n_after_ric,
+						     ies[pos]))
+				pos += 2 + ies[pos + 1];
+		} else {
+			pos += 2 + ies[pos + 1];
+		}
+	}
+
+	return pos;
+}
+EXPORT_SYMBOL(ieee80211_ie_split_ric);
+
+size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+			  const u8 *ids, int n_ids, size_t offset)
+{
+	return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
+}
+EXPORT_SYMBOL(ieee80211_ie_split);
+
 bool ieee80211_operating_class_to_band(u8 operating_class,
 				       enum ieee80211_band *band)
 {
 	switch (operating_class) {
 	case 112:
 	case 115 ... 127:
+	case 128 ... 130:
 		*band = IEEE80211_BAND_5GHZ;
 		return true;
 	case 81:
@@ -1250,6 +1355,135 @@
 }
 EXPORT_SYMBOL(ieee80211_operating_class_to_band);
 
+bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
+					  u8 *op_class)
+{
+	u8 vht_opclass;
+	u16 freq = chandef->center_freq1;
+
+	if (freq >= 2412 && freq <= 2472) {
+		if (chandef->width > NL80211_CHAN_WIDTH_40)
+			return false;
+
+		/* 2.407 GHz, channels 1..13 */
+		if (chandef->width == NL80211_CHAN_WIDTH_40) {
+			if (freq > chandef->chan->center_freq)
+				*op_class = 83; /* HT40+ */
+			else
+				*op_class = 84; /* HT40- */
+		} else {
+			*op_class = 81;
+		}
+
+		return true;
+	}
+
+	if (freq == 2484) {
+		if (chandef->width > NL80211_CHAN_WIDTH_40)
+			return false;
+
+		*op_class = 82; /* channel 14 */
+		return true;
+	}
+
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_80:
+		vht_opclass = 128;
+		break;
+	case NL80211_CHAN_WIDTH_160:
+		vht_opclass = 129;
+		break;
+	case NL80211_CHAN_WIDTH_80P80:
+		vht_opclass = 130;
+		break;
+	case NL80211_CHAN_WIDTH_10:
+	case NL80211_CHAN_WIDTH_5:
+		return false; /* unsupported for now */
+	default:
+		vht_opclass = 0;
+		break;
+	}
+
+	/* 5 GHz, channels 36..48 */
+	if (freq >= 5180 && freq <= 5240) {
+		if (vht_opclass) {
+			*op_class = vht_opclass;
+		} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+			if (freq > chandef->chan->center_freq)
+				*op_class = 116;
+			else
+				*op_class = 117;
+		} else {
+			*op_class = 115;
+		}
+
+		return true;
+	}
+
+	/* 5 GHz, channels 52..64 */
+	if (freq >= 5260 && freq <= 5320) {
+		if (vht_opclass) {
+			*op_class = vht_opclass;
+		} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+			if (freq > chandef->chan->center_freq)
+				*op_class = 119;
+			else
+				*op_class = 120;
+		} else {
+			*op_class = 118;
+		}
+
+		return true;
+	}
+
+	/* 5 GHz, channels 100..144 */
+	if (freq >= 5500 && freq <= 5720) {
+		if (vht_opclass) {
+			*op_class = vht_opclass;
+		} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+			if (freq > chandef->chan->center_freq)
+				*op_class = 122;
+			else
+				*op_class = 123;
+		} else {
+			*op_class = 121;
+		}
+
+		return true;
+	}
+
+	/* 5 GHz, channels 149..169 */
+	if (freq >= 5745 && freq <= 5845) {
+		if (vht_opclass) {
+			*op_class = vht_opclass;
+		} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+			if (freq > chandef->chan->center_freq)
+				*op_class = 126;
+			else
+				*op_class = 127;
+		} else if (freq <= 5805) {
+			*op_class = 124;
+		} else {
+			*op_class = 125;
+		}
+
+		return true;
+	}
+
+	/* 56.16 GHz, channel 1..4 */
+	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
+		if (chandef->width >= NL80211_CHAN_WIDTH_40)
+			return false;
+
+		*op_class = 180;
+		return true;
+	}
+
+	/* not supported yet */
+	return false;
+}
+EXPORT_SYMBOL(ieee80211_chandef_to_operating_class);
+
 int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
 				 u32 beacon_int)
 {
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 0f47948..fff1bef 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -63,7 +63,7 @@
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_giwname);
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwname);
 
 int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
 			  u32 *mode, char *extra)
@@ -99,7 +99,7 @@
 
 	return cfg80211_change_iface(rdev, dev, type, NULL, &vifparams);
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_siwmode);
+EXPORT_WEXT_HANDLER(cfg80211_wext_siwmode);
 
 int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
 			  u32 *mode, char *extra)
@@ -134,7 +134,7 @@
 	}
 	return 0;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_giwmode);
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwmode);
 
 
 int cfg80211_wext_giwrange(struct net_device *dev,
@@ -248,7 +248,7 @@
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange);
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwrange);
 
 
 /**
@@ -303,7 +303,7 @@
 
 	return err;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_siwrts);
+EXPORT_WEXT_HANDLER(cfg80211_wext_siwrts);
 
 int cfg80211_wext_giwrts(struct net_device *dev,
 			 struct iw_request_info *info,
@@ -317,7 +317,7 @@
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_giwrts);
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwrts);
 
 int cfg80211_wext_siwfrag(struct net_device *dev,
 			  struct iw_request_info *info,
@@ -343,7 +343,7 @@
 
 	return err;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_siwfrag);
+EXPORT_WEXT_HANDLER(cfg80211_wext_siwfrag);
 
 int cfg80211_wext_giwfrag(struct net_device *dev,
 			  struct iw_request_info *info,
@@ -357,7 +357,7 @@
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_giwfrag);
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwfrag);
 
 static int cfg80211_wext_siwretry(struct net_device *dev,
 				  struct iw_request_info *info,
@@ -427,7 +427,7 @@
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry);
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwretry);
 
 static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
 				     struct net_device *dev, bool pairwise,
@@ -1300,7 +1300,7 @@
 	if (err)
 		return err;
 
-	if (!(sinfo.filled & STATION_INFO_TX_BITRATE))
+	if (!(sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE)))
 		return -EOPNOTSUPP;
 
 	rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate);
@@ -1340,7 +1340,7 @@
 
 	switch (rdev->wiphy.signal_type) {
 	case CFG80211_SIGNAL_TYPE_MBM:
-		if (sinfo.filled & STATION_INFO_SIGNAL) {
+		if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL)) {
 			int sig = sinfo.signal;
 			wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;
 			wstats.qual.updated |= IW_QUAL_QUAL_UPDATED;
@@ -1354,7 +1354,7 @@
 			break;
 		}
 	case CFG80211_SIGNAL_TYPE_UNSPEC:
-		if (sinfo.filled & STATION_INFO_SIGNAL) {
+		if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL)) {
 			wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;
 			wstats.qual.updated |= IW_QUAL_QUAL_UPDATED;
 			wstats.qual.level = sinfo.signal;
@@ -1367,9 +1367,9 @@
 	}
 
 	wstats.qual.updated |= IW_QUAL_NOISE_INVALID;
-	if (sinfo.filled & STATION_INFO_RX_DROP_MISC)
+	if (sinfo.filled & BIT(NL80211_STA_INFO_RX_DROP_MISC))
 		wstats.discard.misc = sinfo.rx_dropped_misc;
-	if (sinfo.filled & STATION_INFO_TX_FAILED)
+	if (sinfo.filled & BIT(NL80211_STA_INFO_TX_FAILED))
 		wstats.discard.retries = sinfo.tx_failed;
 
 	return &wstats;
diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h
index ebcacca..046a3d3 100644
--- a/net/wireless/wext-compat.h
+++ b/net/wireless/wext-compat.h
@@ -4,6 +4,12 @@
 #include <net/iw_handler.h>
 #include <linux/wireless.h>
 
+#ifdef CPTCFG_CFG80211_WEXT_EXPORT
+#define EXPORT_WEXT_HANDLER(h) EXPORT_SYMBOL_GPL(h)
+#else
+#define EXPORT_WEXT_HANDLER(h)
+#endif /* CPTCFG_CFG80211_WEXT_EXPORT */
+
 int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
 			       struct iw_request_info *info,
 			       struct iw_freq *freq, char *extra);
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index 368611c..a4e8af3 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -322,7 +322,7 @@
 	if (wdev->current_bss)
 		memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
 	else
-		memset(ap_addr->sa_data, 0, ETH_ALEN);
+		eth_zero_addr(ap_addr->sa_data);
 	wdev_unlock(wdev);
 
 	return 0;
diff --git a/versions b/versions
index 012034c..bbfb370 100644
--- a/versions
+++ b/versions
@@ -1,3 +1,3 @@
-BACKPORTS_VERSION="v3.19-rc1-1-0-g74aaf28"
-BACKPORTED_KERNEL_VERSION="v3.19-rc1-0-g97bf6af"
+BACKPORTS_VERSION="backports-20150424-0-g29d880c"
+BACKPORTED_KERNEL_VERSION="next-20150424-0-ga3da0fb6"
 BACKPORTED_KERNEL_NAME="Linux"