From f5317ed5d2e06765982b44fb4f42b80686790b37 Mon Sep 17 00:00:00 2001
From: =?utf8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Mon, 7 Mar 2016 22:37:09 +0000
Subject: [PATCH] mac80211: backport brcmfmac patchset with driver setting
 concept
MIME-Version: 1.0
Content-Type: text/plain; charset=utf8
Content-Transfer-Encoding: 8bit

This prepares brcmfmac for better country handling and fixes BCM4360
support which was always failing with:
[   13.249195] brcmfmac: brcmf_pcie_download_fw_nvram: FW failed to initialize

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>

SVN-Revision: 48959
---
 ...function-name-for-brcmf_cfg80211_wai.patch |  99 +++
 ...-brcmfmac-Limit-memory-allocs-to-64K.patch | 127 +++
 ...or-wowl-support-before-enumerating-f.patch |  29 +
 ...re-country-code-using-device-specifi.patch | 214 +++++
 ...Add-length-checks-on-firmware-events.patch | 283 +++++++
 ...ghbor-discovery-offload-ip-address-t.patch | 333 ++++++++
 ...heck-return-for-ARP-ip-setting-iovar.patch |  38 +
 ...ice-memsize-config-from-fw-if-define.patch |  73 ++
 ...1-window-size-as-provided-by-pci-sub.patch |  58 ++
 ...add-support-for-the-PCIE-4366c0-chip.patch |  34 +
 ...11-brcmfmac-remove-pcie-gen1-support.patch | 221 ++++++
 ...cmfmac-increase-timeout-for-tx-eapol.patch |  30 +
 ...-move-module-init-and-exit-to-common.patch | 135 ++++
 ...dd-wowl-gtk-rekeying-offload-support.patch | 260 +++++++
 ...atform-data-retrieval-code-to-common.patch | 385 +++++++++
 ...P-and-ND-offload-enabled-during-WOWL.patch |  69 ++
 ...brcmfmac-switch-to-new-platform-data.patch | 734 ++++++++++++++++++
 ...-platform-data-and-module-paramaters.patch | 607 +++++++++++++++
 ...fmac-integrate-add_keyext-in-add_key.patch | 227 ++++++
 ....11w-management-frame-protection-sup.patch | 509 ++++++++++++
 ...-register-wiphy-s-during-module_init.patch |   4 +-
 ...und-bug-with-some-inconsistent-BSSes.patch |   2 +-
 22 files changed, 4468 insertions(+), 3 deletions(-)
 create mode 100644 package/kernel/mac80211/patches/344-0001-brcmfmac-change-function-name-for-brcmf_cfg80211_wai.patch
 create mode 100644 package/kernel/mac80211/patches/344-0002-brcmfmac-Limit-memory-allocs-to-64K.patch
 create mode 100644 package/kernel/mac80211/patches/344-0003-brcmfmac-check-for-wowl-support-before-enumerating-f.patch
 create mode 100644 package/kernel/mac80211/patches/344-0004-brcmfmac-Configure-country-code-using-device-specifi.patch
 create mode 100644 package/kernel/mac80211/patches/344-0005-brcmfmac-Add-length-checks-on-firmware-events.patch
 create mode 100644 package/kernel/mac80211/patches/344-0006-brcmfmac-add-neighbor-discovery-offload-ip-address-t.patch
 create mode 100644 package/kernel/mac80211/patches/344-0007-brcmfmac-check-return-for-ARP-ip-setting-iovar.patch
 create mode 100644 package/kernel/mac80211/patches/344-0008-brcmfmac-use-device-memsize-config-from-fw-if-define.patch
 create mode 100644 package/kernel/mac80211/patches/344-0009-brcmfmac-use-bar1-window-size-as-provided-by-pci-sub.patch
 create mode 100644 package/kernel/mac80211/patches/344-0010-brcmfmac-add-support-for-the-PCIE-4366c0-chip.patch
 create mode 100644 package/kernel/mac80211/patches/344-0011-brcmfmac-remove-pcie-gen1-support.patch
 create mode 100644 package/kernel/mac80211/patches/344-0012-brcmfmac-increase-timeout-for-tx-eapol.patch
 create mode 100644 package/kernel/mac80211/patches/344-0013-brcmfmac-move-module-init-and-exit-to-common.patch
 create mode 100644 package/kernel/mac80211/patches/344-0014-brcmfmac-add-wowl-gtk-rekeying-offload-support.patch
 create mode 100644 package/kernel/mac80211/patches/344-0015-brcmfmac-move-platform-data-retrieval-code-to-common.patch
 create mode 100644 package/kernel/mac80211/patches/344-0016-brcmfmac-keep-ARP-and-ND-offload-enabled-during-WOWL.patch
 create mode 100644 package/kernel/mac80211/patches/344-0017-brcmfmac-switch-to-new-platform-data.patch
 create mode 100644 package/kernel/mac80211/patches/344-0018-brcmfmac-merge-platform-data-and-module-paramaters.patch
 create mode 100644 package/kernel/mac80211/patches/344-0019-brcmfmac-integrate-add_keyext-in-add_key.patch
 create mode 100644 package/kernel/mac80211/patches/344-0020-brcmfmac-add-802.11w-management-frame-protection-sup.patch

diff --git a/package/kernel/mac80211/patches/344-0001-brcmfmac-change-function-name-for-brcmf_cfg80211_wai.patch b/package/kernel/mac80211/patches/344-0001-brcmfmac-change-function-name-for-brcmf_cfg80211_wai.patch
new file mode 100644
index 0000000000..e3427de732
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0001-brcmfmac-change-function-name-for-brcmf_cfg80211_wai.patch
@@ -0,0 +1,99 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:50 +0100
+Subject: [PATCH] brcmfmac: change function name for
+ brcmf_cfg80211_wait_vif_event_timeout()
+
+Dropping the '_timeout' from the function name as the fact that a timeout
+value is passed makes it obvious a timeout is used. Also helps to keep code
+lines a bit shorter and easier to stick to 80 char boundary.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -564,8 +564,8 @@ struct wireless_dev *brcmf_ap_add_vif(st
+ 	}
+ 
+ 	/* wait for firmware event */
+-	err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
+-						    BRCMF_VIF_EVENT_TIMEOUT);
++	err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
++					    BRCMF_VIF_EVENT_TIMEOUT);
+ 	brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ 	if (!err) {
+ 		brcmf_err("timeout occurred\n");
+@@ -6395,8 +6395,9 @@ bool brcmf_cfg80211_vif_event_armed(stru
+ 
+ 	return armed;
+ }
+-int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
+-					  u8 action, ulong timeout)
++
++int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
++				  u8 action, ulong timeout)
+ {
+ 	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+ 
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+@@ -402,8 +402,8 @@ bool brcmf_get_vif_state_any(struct brcm
+ void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
+ 				  struct brcmf_cfg80211_vif *vif);
+ bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg);
+-int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
+-					  u8 action, ulong timeout);
++int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
++				  u8 action, ulong timeout);
+ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
+ 				struct brcmf_if *ifp, bool aborted,
+ 				bool fw_abort);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+@@ -1988,8 +1988,8 @@ int brcmf_p2p_ifchange(struct brcmf_cfg8
+ 		brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ 		return err;
+ 	}
+-	err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_CHANGE,
+-						    BRCMF_VIF_EVENT_TIMEOUT);
++	err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_CHANGE,
++					    BRCMF_VIF_EVENT_TIMEOUT);
+ 	brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ 	if (!err)  {
+ 		brcmf_err("No BRCMF_E_IF_CHANGE event received\n");
+@@ -2090,8 +2090,8 @@ static struct wireless_dev *brcmf_p2p_cr
+ 	}
+ 
+ 	/* wait for firmware event */
+-	err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD,
+-						    BRCMF_VIF_EVENT_TIMEOUT);
++	err = brcmf_cfg80211_wait_vif_event(p2p->cfg, BRCMF_E_IF_ADD,
++					    BRCMF_VIF_EVENT_TIMEOUT);
+ 	brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
+ 	brcmf_fweh_p2pdev_setup(pri_ifp, false);
+ 	if (!err) {
+@@ -2180,8 +2180,8 @@ struct wireless_dev *brcmf_p2p_add_vif(s
+ 	}
+ 
+ 	/* wait for firmware event */
+-	err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
+-						    BRCMF_VIF_EVENT_TIMEOUT);
++	err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
++					    BRCMF_VIF_EVENT_TIMEOUT);
+ 	brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ 	if (!err) {
+ 		brcmf_err("timeout occurred\n");
+@@ -2274,8 +2274,8 @@ int brcmf_p2p_del_vif(struct wiphy *wiph
+ 	}
+ 	if (!err) {
+ 		/* wait for firmware event */
+-		err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL,
+-							BRCMF_VIF_EVENT_TIMEOUT);
++		err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL,
++						    BRCMF_VIF_EVENT_TIMEOUT);
+ 		if (!err)
+ 			err = -EIO;
+ 		else
diff --git a/package/kernel/mac80211/patches/344-0002-brcmfmac-Limit-memory-allocs-to-64K.patch b/package/kernel/mac80211/patches/344-0002-brcmfmac-Limit-memory-allocs-to-64K.patch
new file mode 100644
index 0000000000..9c336f774f
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0002-brcmfmac-Limit-memory-allocs-to-64K.patch
@@ -0,0 +1,127 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:51 +0100
+Subject: [PATCH] brcmfmac: Limit memory allocs to <64K
+
+Some systems have problems with allocating memory allocation larger
+then 64K. Often on unload/load or suspend/resume a failure is
+reported: Could not allocate wiphy device. This patch makes the
+escan intermediate storage buf dynamically allocated, and smaller
+than 64K.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -1125,7 +1125,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy
+ 
+ 	/* Arm scan timeout timer */
+ 	mod_timer(&cfg->escan_timeout, jiffies +
+-			WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
++			BRCMF_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
+ 
+ 	return 0;
+ 
+@@ -3020,7 +3020,7 @@ brcmf_cfg80211_escan_handler(struct brcm
+ 
+ 		list = (struct brcmf_scan_results *)
+ 				cfg->escan_info.escan_buf;
+-		if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
++		if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) {
+ 			brcmf_err("Buffer is too small: ignoring\n");
+ 			goto exit;
+ 		}
+@@ -3033,8 +3033,8 @@ brcmf_cfg80211_escan_handler(struct brcm
+ 							  bss_info_le))
+ 				goto exit;
+ 		}
+-		memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
+-			bss_info_le, bi_length);
++		memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le,
++		       bi_length);
+ 		list->version = le32_to_cpu(bss_info_le->version);
+ 		list->buflen += bi_length;
+ 		list->count++;
+@@ -5402,14 +5402,14 @@ static void brcmf_deinit_priv_mem(struct
+ {
+ 	kfree(cfg->conf);
+ 	cfg->conf = NULL;
+-	kfree(cfg->escan_ioctl_buf);
+-	cfg->escan_ioctl_buf = NULL;
+ 	kfree(cfg->extra_buf);
+ 	cfg->extra_buf = NULL;
+ 	kfree(cfg->wowl.nd);
+ 	cfg->wowl.nd = NULL;
+ 	kfree(cfg->wowl.nd_info);
+ 	cfg->wowl.nd_info = NULL;
++	kfree(cfg->escan_info.escan_buf);
++	cfg->escan_info.escan_buf = NULL;
+ }
+ 
+ static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
+@@ -5417,9 +5417,6 @@ static s32 brcmf_init_priv_mem(struct br
+ 	cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
+ 	if (!cfg->conf)
+ 		goto init_priv_mem_out;
+-	cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+-	if (!cfg->escan_ioctl_buf)
+-		goto init_priv_mem_out;
+ 	cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
+ 	if (!cfg->extra_buf)
+ 		goto init_priv_mem_out;
+@@ -5431,6 +5428,9 @@ static s32 brcmf_init_priv_mem(struct br
+ 				    GFP_KERNEL);
+ 	if (!cfg->wowl.nd_info)
+ 		goto init_priv_mem_out;
++	cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL);
++	if (!cfg->escan_info.escan_buf)
++		goto init_priv_mem_out;
+ 
+ 	return 0;
+ 
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+@@ -28,8 +28,11 @@
+ #define WL_ROAM_TRIGGER_LEVEL		-75
+ #define WL_ROAM_DELTA			20
+ 
+-#define WL_ESCAN_BUF_SIZE		(1024 * 64)
+-#define WL_ESCAN_TIMER_INTERVAL_MS	10000 /* E-Scan timeout */
++/* Keep BRCMF_ESCAN_BUF_SIZE below 64K (65536). Allocing over 64K can be
++ * problematic on some systems and should be avoided.
++ */
++#define BRCMF_ESCAN_BUF_SIZE		65000
++#define BRCMF_ESCAN_TIMER_INTERVAL_MS	10000	/* E-Scan timeout */
+ 
+ #define WL_ESCAN_ACTION_START		1
+ #define WL_ESCAN_ACTION_CONTINUE	2
+@@ -205,7 +208,7 @@ enum wl_escan_state {
+ 
+ struct escan_info {
+ 	u32 escan_state;
+-	u8 escan_buf[WL_ESCAN_BUF_SIZE];
++	u8 *escan_buf;
+ 	struct wiphy *wiphy;
+ 	struct brcmf_if *ifp;
+ 	s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
+@@ -278,7 +281,6 @@ struct brcmf_cfg80211_wowl {
+  * @escan_info: escan information.
+  * @escan_timeout: Timer for catch scan timeout.
+  * @escan_timeout_work: scan timeout worker.
+- * @escan_ioctl_buf: dongle command buffer for escan commands.
+  * @vif_list: linked list of vif instances.
+  * @vif_cnt: number of vif instances.
+  * @vif_event: vif event signalling.
+@@ -309,7 +311,6 @@ struct brcmf_cfg80211_info {
+ 	struct escan_info escan_info;
+ 	struct timer_list escan_timeout;
+ 	struct work_struct escan_timeout_work;
+-	u8 *escan_ioctl_buf;
+ 	struct list_head vif_list;
+ 	struct brcmf_cfg80211_vif_event vif_event;
+ 	struct completion vif_disabled;
diff --git a/package/kernel/mac80211/patches/344-0003-brcmfmac-check-for-wowl-support-before-enumerating-f.patch b/package/kernel/mac80211/patches/344-0003-brcmfmac-check-for-wowl-support-before-enumerating-f.patch
new file mode 100644
index 0000000000..ee3d9f37a8
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0003-brcmfmac-check-for-wowl-support-before-enumerating-f.patch
@@ -0,0 +1,29 @@
+From: Franky Lin <frankyl@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:52 +0100
+Subject: [PATCH] brcmfmac: check for wowl support before enumerating feature
+ flag
+
+In some cases wiphy->wowlan could be NULL if firmware doesn't have the
+support. Driver should check for support before walking down the feature
+flags.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Franky Lin <frankyl@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -6594,7 +6594,8 @@ struct brcmf_cfg80211_info *brcmf_cfg802
+ 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
+ 		wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
+ #ifdef CONFIG_PM
+-		if (wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
++		if (wiphy->wowlan &&
++		    wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
+ 			wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
+ #endif
+ 	}
diff --git a/package/kernel/mac80211/patches/344-0004-brcmfmac-Configure-country-code-using-device-specifi.patch b/package/kernel/mac80211/patches/344-0004-brcmfmac-Configure-country-code-using-device-specifi.patch
new file mode 100644
index 0000000000..c52cac87b9
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0004-brcmfmac-Configure-country-code-using-device-specifi.patch
@@ -0,0 +1,214 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:53 +0100
+Subject: [PATCH] brcmfmac: Configure country code using device specific
+ settings
+
+Country code configuration in a device is a device specific
+operation. For this the country code as specified by reg notifier
+(iso3166 alpha2) needs to be translated to a device specific
+country locale and revision number. This patch adds this
+translation and puts a placeholder in the device specific settings
+where the translation table can be stored. Additional patches will
+be needed to read these tables from for example device platform
+data.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -6405,28 +6405,85 @@ int brcmf_cfg80211_wait_vif_event(struct
+ 				  vif_event_equals(event, action), timeout);
+ }
+ 
++static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
++					struct brcmf_fil_country_le *ccreq)
++{
++	struct cc_translate *country_codes;
++	struct cc_entry *cc;
++	s32 found_index;
++	int i;
++
++	country_codes = drvr->settings->country_codes;
++	if (!country_codes) {
++		brcmf_dbg(TRACE, "No country codes configured for device\n");
++		return -EINVAL;
++	}
++
++	if ((alpha2[0] == ccreq->country_abbrev[0]) &&
++	    (alpha2[1] == ccreq->country_abbrev[1])) {
++		brcmf_dbg(TRACE, "Country code already set\n");
++		return -EAGAIN;
++	}
++
++	found_index = -1;
++	for (i = 0; i < country_codes->table_size; i++) {
++		cc = &country_codes->table[i];
++		if ((cc->iso3166[0] == '\0') && (found_index == -1))
++			found_index = i;
++		if ((cc->iso3166[0] == alpha2[0]) &&
++		    (cc->iso3166[1] == alpha2[1])) {
++			found_index = i;
++			break;
++		}
++	}
++	if (found_index == -1) {
++		brcmf_dbg(TRACE, "No country code match found\n");
++		return -EINVAL;
++	}
++	memset(ccreq, 0, sizeof(*ccreq));
++	ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev);
++	memcpy(ccreq->ccode, country_codes->table[found_index].cc,
++	       BRCMF_COUNTRY_BUF_SZ);
++	ccreq->country_abbrev[0] = alpha2[0];
++	ccreq->country_abbrev[1] = alpha2[1];
++	ccreq->country_abbrev[2] = 0;
++
++	return 0;
++}
++
+ 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;
++	s32 err;
+ 	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");
++			brcmf_err("not a ISO3166 code (0x%02x 0x%02x)\n",
++				  req->alpha2[0], req->alpha2[1]);
+ 			return;
+ 		}
+-	memset(&ccreq, 0, sizeof(ccreq));
+-	ccreq.rev = cpu_to_le32(-1);
+-	memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
+-	if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
+-		brcmf_err("firmware rejected country setting\n");
++
++	brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator,
++		  req->alpha2[0], req->alpha2[1]);
++
++	err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));
++	if (err) {
++		brcmf_err("Country code iovar returned err = %d\n", err);
++		return;
++	}
++
++	err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq);
++	if (err)
++		return;
++
++	err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
++	if (err) {
++		brcmf_err("Firmware rejected country setting\n");
+ 		return;
+ 	}
+ 	brcmf_setup_wiphybands(wiphy);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+@@ -230,10 +230,8 @@ void brcmf_mp_attach(void)
+ int brcmf_mp_device_attach(struct brcmf_pub *drvr)
+ {
+ 	drvr->settings = kzalloc(sizeof(*drvr->settings), GFP_ATOMIC);
+-	if (!drvr->settings) {
+-		brcmf_err("Failed to alloca storage space for settings\n");
++	if (!drvr->settings)
+ 		return -ENOMEM;
+-	}
+ 
+ 	drvr->settings->sdiod_txglomsz = brcmf_sdiod_txglomsz;
+ 	drvr->settings->p2p_enable = !!brcmf_p2p_enable;
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
+@@ -15,6 +15,8 @@
+ #ifndef BRCMFMAC_COMMON_H
+ #define BRCMFMAC_COMMON_H
+ 
++#include "fwil_types.h"
++
+ extern const u8 ALLFFMAC[ETH_ALEN];
+ 
+ #define BRCMF_FW_ALTPATH_LEN			256
+@@ -39,6 +41,33 @@ struct brcmf_mp_global_t {
+ extern struct brcmf_mp_global_t brcmf_mp_global;
+ 
+ /**
++ * struct cc_entry - Struct for translating user space country code (iso3166) to
++ *		     firmware country code and revision.
++ *
++ * @iso3166: iso3166 alpha 2 country code string.
++ * @cc: firmware country code string.
++ * @rev: firmware country code revision.
++ */
++struct cc_entry {
++	char	iso3166[BRCMF_COUNTRY_BUF_SZ];
++	char	cc[BRCMF_COUNTRY_BUF_SZ];
++	s32	rev;
++};
++
++/**
++ * struct cc_translate - Struct for translating country codes as set by user
++ *			 space to a country code and rev which can be used by
++ *			 firmware.
++ *
++ * @table_size: number of entries in table (> 0)
++ * @table: dynamic array of 1 or more elements with translation information.
++ */
++struct cc_translate {
++	int	table_size;
++	struct cc_entry table[0];
++};
++
++/**
+  * struct brcmf_mp_device - Device module paramaters.
+  *
+  * @sdiod_txglomsz: SDIO txglom size.
+@@ -47,6 +76,7 @@ extern struct brcmf_mp_global_t brcmf_mp
+  * @feature_disable: Feature_disable bitmask.
+  * @fcmode: FWS flow control.
+  * @roamoff: Firmware roaming off?
++ * @country_codes: If available, pointer to struct for translating country codes
+  */
+ struct brcmf_mp_device {
+ 	int	sdiod_txglomsz;
+@@ -56,6 +86,7 @@ struct brcmf_mp_device {
+ 	int	fcmode;
+ 	bool	roamoff;
+ 	bool	ignore_probe_fail;
++	struct cc_translate *country_codes;
+ };
+ 
+ void brcmf_mp_attach(void);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+@@ -134,6 +134,8 @@
+ #define BRCMF_PFN_MAC_OUI_ONLY		BIT(0)
+ #define BRCMF_PFN_SET_MAC_UNASSOC	BIT(1)
+ 
++#define BRCMF_MCSSET_LEN		16
++
+ /* join preference types for join_pref iovar */
+ enum brcmf_join_pref_types {
+ 	BRCMF_JOIN_PREF_RSSI = 1,
+@@ -279,7 +281,7 @@ struct brcmf_bss_info_le {
+ 	__le32 reserved32[1];	/* Reserved for expansion of BSS properties */
+ 	u8 flags;		/* flags */
+ 	u8 reserved[3];	/* Reserved for expansion of BSS properties */
+-	u8 basic_mcs[MCSSET_LEN];	/* 802.11N BSS required MCS set */
++	u8 basic_mcs[BRCMF_MCSSET_LEN];	/* 802.11N BSS required MCS set */
+ 
+ 	__le16 ie_offset;	/* offset at which IEs start, from beginning */
+ 	__le32 ie_length;	/* byte length of Information Elements */
diff --git a/package/kernel/mac80211/patches/344-0005-brcmfmac-Add-length-checks-on-firmware-events.patch b/package/kernel/mac80211/patches/344-0005-brcmfmac-Add-length-checks-on-firmware-events.patch
new file mode 100644
index 0000000000..3e2e3503b6
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0005-brcmfmac-Add-length-checks-on-firmware-events.patch
@@ -0,0 +1,283 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:54 +0100
+Subject: [PATCH] brcmfmac: Add length checks on firmware events
+
+Add additional length checks on firmware events to create more
+robust code.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Lei Zhang <leizh@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -3092,6 +3092,11 @@ brcmf_notify_sched_scan_results(struct b
+ 
+ 	brcmf_dbg(SCAN, "Enter\n");
+ 
++	if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
++		brcmf_dbg(SCAN, "Event data to small. Ignore\n");
++		return 0;
++	}
++
+ 	if (e->event_code == BRCMF_E_PFN_NET_LOST) {
+ 		brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
+ 		return 0;
+@@ -3415,6 +3420,11 @@ brcmf_wowl_nd_results(struct brcmf_if *i
+ 
+ 	brcmf_dbg(SCAN, "Enter\n");
+ 
++	if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
++		brcmf_dbg(SCAN, "Event data to small. Ignore\n");
++		return 0;
++	}
++
+ 	pfn_result = (struct brcmf_pno_scanresults_le *)data;
+ 
+ 	if (e->event_code == BRCMF_E_PFN_NET_LOST) {
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
+@@ -26,50 +26,6 @@
+ #include "fwil.h"
+ 
+ /**
+- * struct brcm_ethhdr - broadcom specific ether header.
+- *
+- * @subtype: subtype for this packet.
+- * @length: TODO: length of appended data.
+- * @version: version indication.
+- * @oui: OUI of this packet.
+- * @usr_subtype: subtype for this OUI.
+- */
+-struct brcm_ethhdr {
+-	__be16 subtype;
+-	__be16 length;
+-	u8 version;
+-	u8 oui[3];
+-	__be16 usr_subtype;
+-} __packed;
+-
+-struct brcmf_event_msg_be {
+-	__be16 version;
+-	__be16 flags;
+-	__be32 event_type;
+-	__be32 status;
+-	__be32 reason;
+-	__be32 auth_type;
+-	__be32 datalen;
+-	u8 addr[ETH_ALEN];
+-	char ifname[IFNAMSIZ];
+-	u8 ifidx;
+-	u8 bsscfgidx;
+-} __packed;
+-
+-/**
+- * struct brcmf_event - contents of broadcom event packet.
+- *
+- * @eth: standard ether header.
+- * @hdr: broadcom specific ether header.
+- * @msg: common part of the actual event message.
+- */
+-struct brcmf_event {
+-	struct ethhdr eth;
+-	struct brcm_ethhdr hdr;
+-	struct brcmf_event_msg_be msg;
+-} __packed;
+-
+-/**
+  * struct brcmf_fweh_queue_item - event item on event queue.
+  *
+  * @q: list element for queuing.
+@@ -85,6 +41,7 @@ struct brcmf_fweh_queue_item {
+ 	u8 ifidx;
+ 	u8 ifaddr[ETH_ALEN];
+ 	struct brcmf_event_msg_be emsg;
++	u32 datalen;
+ 	u8 data[0];
+ };
+ 
+@@ -294,6 +251,11 @@ static void brcmf_fweh_event_worker(stru
+ 		brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,
+ 				   min_t(u32, emsg.datalen, 64),
+ 				   "event payload, len=%d\n", emsg.datalen);
++		if (emsg.datalen > event->datalen) {
++			brcmf_err("event invalid length header=%d, msg=%d\n",
++				  event->datalen, emsg.datalen);
++			goto event_free;
++		}
+ 
+ 		/* special handling of interface event */
+ 		if (event->code == BRCMF_E_IF) {
+@@ -439,7 +401,8 @@ int brcmf_fweh_activate_events(struct br
+  * dispatch the event to a registered handler (using worker).
+  */
+ void brcmf_fweh_process_event(struct brcmf_pub *drvr,
+-			      struct brcmf_event *event_packet)
++			      struct brcmf_event *event_packet,
++			      u32 packet_len)
+ {
+ 	enum brcmf_fweh_event_code code;
+ 	struct brcmf_fweh_info *fweh = &drvr->fweh;
+@@ -459,6 +422,9 @@ void brcmf_fweh_process_event(struct brc
+ 	if (code != BRCMF_E_IF && !fweh->evt_handler[code])
+ 		return;
+ 
++	if (datalen > BRCMF_DCMD_MAXLEN)
++		return;
++
+ 	if (in_interrupt())
+ 		alloc_flag = GFP_ATOMIC;
+ 
+@@ -472,6 +438,7 @@ void brcmf_fweh_process_event(struct brc
+ 	/* use memcpy to get aligned event message */
+ 	memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
+ 	memcpy(event->data, data, datalen);
++	event->datalen = datalen;
+ 	memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
+ 
+ 	brcmf_fweh_queue_event(fweh, event);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
+@@ -27,7 +27,6 @@
+ struct brcmf_pub;
+ struct brcmf_if;
+ struct brcmf_cfg80211_info;
+-struct brcmf_event;
+ 
+ /* list of firmware events */
+ #define BRCMF_FWEH_EVENT_ENUM_DEFLIST \
+@@ -180,13 +179,55 @@ enum brcmf_fweh_event_code {
+ /**
+  * definitions for event packet validation.
+  */
+-#define BRCMF_EVENT_OUI_OFFSET		19
+-#define BRCM_OUI			"\x00\x10\x18"
+-#define DOT11_OUI_LEN			3
+-#define BCMILCP_BCM_SUBTYPE_EVENT	1
++#define BRCM_OUI				"\x00\x10\x18"
++#define BCMILCP_BCM_SUBTYPE_EVENT		1
+ 
+ 
+ /**
++ * struct brcm_ethhdr - broadcom specific ether header.
++ *
++ * @subtype: subtype for this packet.
++ * @length: TODO: length of appended data.
++ * @version: version indication.
++ * @oui: OUI of this packet.
++ * @usr_subtype: subtype for this OUI.
++ */
++struct brcm_ethhdr {
++	__be16 subtype;
++	__be16 length;
++	u8 version;
++	u8 oui[3];
++	__be16 usr_subtype;
++} __packed;
++
++struct brcmf_event_msg_be {
++	__be16 version;
++	__be16 flags;
++	__be32 event_type;
++	__be32 status;
++	__be32 reason;
++	__be32 auth_type;
++	__be32 datalen;
++	u8 addr[ETH_ALEN];
++	char ifname[IFNAMSIZ];
++	u8 ifidx;
++	u8 bsscfgidx;
++} __packed;
++
++/**
++ * struct brcmf_event - contents of broadcom event packet.
++ *
++ * @eth: standard ether header.
++ * @hdr: broadcom specific ether header.
++ * @msg: common part of the actual event message.
++ */
++struct brcmf_event {
++	struct ethhdr eth;
++	struct brcm_ethhdr hdr;
++	struct brcmf_event_msg_be msg;
++} __packed;
++
++/**
+  * struct brcmf_event_msg - firmware event message.
+  *
+  * @version: version information.
+@@ -256,34 +297,35 @@ void brcmf_fweh_unregister(struct brcmf_
+ 			   enum brcmf_fweh_event_code code);
+ int brcmf_fweh_activate_events(struct brcmf_if *ifp);
+ void brcmf_fweh_process_event(struct brcmf_pub *drvr,
+-			      struct brcmf_event *event_packet);
++			      struct brcmf_event *event_packet,
++			      u32 packet_len);
+ void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing);
+ 
+ static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr,
+ 					  struct sk_buff *skb)
+ {
+ 	struct brcmf_event *event_packet;
+-	u8 *data;
+ 	u16 usr_stype;
+ 
+ 	/* only process events when protocol matches */
+ 	if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL))
+ 		return;
+ 
++	if ((skb->len + ETH_HLEN) < sizeof(*event_packet))
++		return;
++
+ 	/* check for BRCM oui match */
+ 	event_packet = (struct brcmf_event *)skb_mac_header(skb);
+-	data = (u8 *)event_packet;
+-	data += BRCMF_EVENT_OUI_OFFSET;
+-	if (memcmp(BRCM_OUI, data, DOT11_OUI_LEN))
++	if (memcmp(BRCM_OUI, &event_packet->hdr.oui[0],
++		   sizeof(event_packet->hdr.oui)))
+ 		return;
+ 
+ 	/* final match on usr_subtype */
+-	data += DOT11_OUI_LEN;
+-	usr_stype = get_unaligned_be16(data);
++	usr_stype = get_unaligned_be16(&event_packet->hdr.usr_subtype);
+ 	if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT)
+ 		return;
+ 
+-	brcmf_fweh_process_event(drvr, event_packet);
++	brcmf_fweh_process_event(drvr, event_packet, skb->len + ETH_HLEN);
+ }
+ 
+ #endif /* FWEH_H_ */
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+@@ -1361,6 +1361,11 @@ int brcmf_p2p_notify_action_frame_rx(str
+ 	u16 mgmt_type;
+ 	u8 action;
+ 
++	if (e->datalen < sizeof(*rxframe)) {
++		brcmf_dbg(SCAN, "Event data to small. Ignore\n");
++		return 0;
++	}
++
+ 	ch.chspec = be16_to_cpu(rxframe->chanspec);
+ 	cfg->d11inf.decchspec(&ch);
+ 	/* Check if wpa_supplicant has registered for this frame */
+@@ -1858,6 +1863,11 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probere
+ 	brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code,
+ 		  e->reason);
+ 
++	if (e->datalen < sizeof(*rxframe)) {
++		brcmf_dbg(SCAN, "Event data to small. Ignore\n");
++		return 0;
++	}
++
+ 	ch.chspec = be16_to_cpu(rxframe->chanspec);
+ 	cfg->d11inf.decchspec(&ch);
+ 
diff --git a/package/kernel/mac80211/patches/344-0006-brcmfmac-add-neighbor-discovery-offload-ip-address-t.patch b/package/kernel/mac80211/patches/344-0006-brcmfmac-add-neighbor-discovery-offload-ip-address-t.patch
new file mode 100644
index 0000000000..888ad5b050
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0006-brcmfmac-add-neighbor-discovery-offload-ip-address-t.patch
@@ -0,0 +1,333 @@
+From: Franky Lin <frankyl@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:55 +0100
+Subject: [PATCH] brcmfmac: add neighbor discovery offload ip address table
+ configuration
+
+Configure ipv6 address for neighbor discovery offload ip table in
+firmware obtained through ipv6 address notification callback.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Franky Lin <frankyl@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -456,7 +456,7 @@ send_key_to_dongle(struct brcmf_if *ifp,
+ }
+ 
+ static s32
+-brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
++brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable)
+ {
+ 	s32 err;
+ 	u32 mode;
+@@ -484,6 +484,15 @@ brcmf_configure_arp_offload(struct brcmf
+ 				  enable, mode);
+ 	}
+ 
++	err = brcmf_fil_iovar_int_set(ifp, "ndoe", enable);
++	if (err) {
++		brcmf_dbg(TRACE, "failed to configure (%d) ND offload err = %d\n",
++			  enable, err);
++		err = 0;
++	} else
++		brcmf_dbg(TRACE, "successfully configured (%d) ND offload to 0x%x\n",
++			  enable, mode);
++
+ 	return err;
+ }
+ 
+@@ -3543,7 +3552,7 @@ static s32 brcmf_cfg80211_resume(struct
+ 		brcmf_report_wowl_wakeind(wiphy, ifp);
+ 		brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
+ 		brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
+-		brcmf_configure_arp_offload(ifp, true);
++		brcmf_configure_arp_nd_offload(ifp, true);
+ 		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
+ 				      cfg->wowl.pre_pmmode);
+ 		cfg->wowl.active = false;
+@@ -3567,7 +3576,7 @@ static void brcmf_configure_wowl(struct
+ 
+ 	brcmf_dbg(TRACE, "Suspend, wowl config.\n");
+ 
+-	brcmf_configure_arp_offload(ifp, false);
++	brcmf_configure_arp_nd_offload(ifp, false);
+ 	brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
+ 	brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
+ 
+@@ -4336,7 +4345,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wi
+ 
+ 	if (!mbss) {
+ 		brcmf_set_mpc(ifp, 0);
+-		brcmf_configure_arp_offload(ifp, false);
++		brcmf_configure_arp_nd_offload(ifp, false);
+ 	}
+ 
+ 	/* find the RSN_IE */
+@@ -4482,7 +4491,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wi
+ exit:
+ 	if ((err) && (!mbss)) {
+ 		brcmf_set_mpc(ifp, 1);
+-		brcmf_configure_arp_offload(ifp, true);
++		brcmf_configure_arp_nd_offload(ifp, true);
+ 	}
+ 	return err;
+ }
+@@ -4540,7 +4549,7 @@ static int brcmf_cfg80211_stop_ap(struct
+ 			brcmf_err("bss_enable config failed %d\n", err);
+ 	}
+ 	brcmf_set_mpc(ifp, 1);
+-	brcmf_configure_arp_offload(ifp, true);
++	brcmf_configure_arp_nd_offload(ifp, true);
+ 	clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
+ 	brcmf_net_setcarrier(ifp, false);
+ 
+@@ -6287,7 +6296,7 @@ static s32 brcmf_config_dongle(struct br
+ 	if (err)
+ 		goto default_conf_out;
+ 
+-	brcmf_configure_arp_offload(ifp, true);
++	brcmf_configure_arp_nd_offload(ifp, true);
+ 
+ 	cfg->dongle_up = true;
+ default_conf_out:
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -20,6 +20,8 @@
+ #include <linux/inetdevice.h>
+ #include <net/cfg80211.h>
+ #include <net/rtnetlink.h>
++#include <net/addrconf.h>
++#include <net/ipv6.h>
+ #include <brcmu_utils.h>
+ #include <brcmu_wifi.h>
+ 
+@@ -172,6 +174,35 @@ _brcmf_set_mac_address(struct work_struc
+ 	}
+ }
+ 
++#if IS_ENABLED(CONFIG_IPV6)
++static void _brcmf_update_ndtable(struct work_struct *work)
++{
++	struct brcmf_if *ifp;
++	int i, ret;
++
++	ifp = container_of(work, struct brcmf_if, ndoffload_work);
++
++	/* clear the table in firmware */
++	ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip_clear", NULL, 0);
++	if (ret) {
++		brcmf_dbg(TRACE, "fail to clear nd ip table err:%d\n", ret);
++		return;
++	}
++
++	for (i = 0; i < ifp->ipv6addr_idx; i++) {
++		ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip",
++					       &ifp->ipv6_addr_tbl[i],
++					       sizeof(struct in6_addr));
++		if (ret)
++			brcmf_err("add nd ip err %d\n", ret);
++	}
++}
++#else
++static void _brcmf_update_ndtable(struct work_struct *work)
++{
++}
++#endif
++
+ static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
+ {
+ 	struct brcmf_if *ifp = netdev_priv(ndev);
+@@ -685,6 +716,7 @@ int brcmf_net_attach(struct brcmf_if *if
+ 
+ 	INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address);
+ 	INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);
++	INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable);
+ 
+ 	if (rtnl_locked)
+ 		err = register_netdevice(ndev);
+@@ -884,6 +916,7 @@ static void brcmf_del_if(struct brcmf_pu
+ 		if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
+ 			cancel_work_sync(&ifp->setmacaddr_work);
+ 			cancel_work_sync(&ifp->multicast_work);
++			cancel_work_sync(&ifp->ndoffload_work);
+ 		}
+ 		brcmf_net_detach(ifp->ndev);
+ 	} else {
+@@ -1025,6 +1058,56 @@ static int brcmf_inetaddr_changed(struct
+ }
+ #endif
+ 
++#if IS_ENABLED(CONFIG_IPV6)
++static int brcmf_inet6addr_changed(struct notifier_block *nb,
++				   unsigned long action, void *data)
++{
++	struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub,
++					      inet6addr_notifier);
++	struct inet6_ifaddr *ifa = data;
++	struct brcmf_if *ifp;
++	int i;
++	struct in6_addr *table;
++
++	/* Only handle primary interface */
++	ifp = drvr->iflist[0];
++	if (!ifp)
++		return NOTIFY_DONE;
++	if (ifp->ndev != ifa->idev->dev)
++		return NOTIFY_DONE;
++
++	table = ifp->ipv6_addr_tbl;
++	for (i = 0; i < NDOL_MAX_ENTRIES; i++)
++		if (ipv6_addr_equal(&ifa->addr, &table[i]))
++			break;
++
++	switch (action) {
++	case NETDEV_UP:
++		if (i == NDOL_MAX_ENTRIES) {
++			if (ifp->ipv6addr_idx < NDOL_MAX_ENTRIES) {
++				table[ifp->ipv6addr_idx++] = ifa->addr;
++			} else {
++				for (i = 0; i < NDOL_MAX_ENTRIES - 1; i++)
++					table[i] = table[i + 1];
++				table[NDOL_MAX_ENTRIES - 1] = ifa->addr;
++			}
++		}
++		break;
++	case NETDEV_DOWN:
++		if (i < NDOL_MAX_ENTRIES)
++			for (; i < ifp->ipv6addr_idx; i++)
++				table[i] = table[i + 1];
++		break;
++	default:
++		break;
++	}
++
++	schedule_work(&ifp->ndoffload_work);
++
++	return NOTIFY_OK;
++}
++#endif
++
+ int brcmf_attach(struct device *dev)
+ {
+ 	struct brcmf_pub *drvr = NULL;
+@@ -1164,30 +1247,41 @@ int brcmf_bus_start(struct device *dev)
+ #ifdef CONFIG_INET
+ 	drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed;
+ 	ret = register_inetaddr_notifier(&drvr->inetaddr_notifier);
++	if (ret)
++		goto fail;
++
++#if IS_ENABLED(CONFIG_IPV6)
++	drvr->inet6addr_notifier.notifier_call = brcmf_inet6addr_changed;
++	ret = register_inet6addr_notifier(&drvr->inet6addr_notifier);
++	if (ret) {
++		unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
++		goto fail;
++	}
+ #endif
++#endif /* CONFIG_INET */
++
++	return 0;
+ 
+ fail:
+-	if (ret < 0) {
+-		brcmf_err("failed: %d\n", ret);
+-		if (drvr->config) {
+-			brcmf_cfg80211_detach(drvr->config);
+-			drvr->config = NULL;
+-		}
+-		if (drvr->fws) {
+-			brcmf_fws_del_interface(ifp);
+-			brcmf_fws_deinit(drvr);
+-		}
+-		if (ifp)
+-			brcmf_net_detach(ifp->ndev);
+-		if (p2p_ifp)
+-			brcmf_net_detach(p2p_ifp->ndev);
+-		drvr->iflist[0] = NULL;
+-		drvr->iflist[1] = NULL;
+-		if (brcmf_ignoring_probe_fail(drvr))
+-			ret = 0;
+-		return ret;
++	brcmf_err("failed: %d\n", ret);
++	if (drvr->config) {
++		brcmf_cfg80211_detach(drvr->config);
++		drvr->config = NULL;
++	}
++	if (drvr->fws) {
++		brcmf_fws_del_interface(ifp);
++		brcmf_fws_deinit(drvr);
+ 	}
+-	return 0;
++	if (ifp)
++		brcmf_net_detach(ifp->ndev);
++	if (p2p_ifp)
++		brcmf_net_detach(p2p_ifp->ndev);
++	drvr->iflist[0] = NULL;
++	drvr->iflist[1] = NULL;
++	if (brcmf_ignoring_probe_fail(drvr))
++		ret = 0;
++
++	return ret;
+ }
+ 
+ void brcmf_bus_add_txhdrlen(struct device *dev, uint len)
+@@ -1237,6 +1331,10 @@ void brcmf_detach(struct device *dev)
+ 	unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
+ #endif
+ 
++#if IS_ENABLED(CONFIG_IPV6)
++	unregister_inet6addr_notifier(&drvr->inet6addr_notifier);
++#endif
++
+ 	/* stop firmware event handling */
+ 	brcmf_fweh_detach(drvr);
+ 	if (drvr->config)
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+@@ -48,6 +48,8 @@
+  */
+ #define BRCMF_DRIVER_FIRMWARE_VERSION_LEN	32
+ 
++#define NDOL_MAX_ENTRIES	8
++
+ /**
+  * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
+  *
+@@ -143,6 +145,7 @@ struct brcmf_pub {
+ #endif
+ 
+ 	struct notifier_block inetaddr_notifier;
++	struct notifier_block inet6addr_notifier;
+ 	struct brcmf_mp_device *settings;
+ };
+ 
+@@ -175,6 +178,7 @@ enum brcmf_netif_stop_reason {
+  * @stats: interface specific network statistics.
+  * @setmacaddr_work: worker object for setting mac address.
+  * @multicast_work: worker object for multicast provisioning.
++ * @ndoffload_work: worker object for neighbor discovery offload configuration.
+  * @fws_desc: interface specific firmware-signalling descriptor.
+  * @ifidx: interface index in device firmware.
+  * @bsscfgidx: index of bss associated with this interface.
+@@ -191,6 +195,7 @@ struct brcmf_if {
+ 	struct net_device_stats stats;
+ 	struct work_struct setmacaddr_work;
+ 	struct work_struct multicast_work;
++	struct work_struct ndoffload_work;
+ 	struct brcmf_fws_mac_descriptor *fws_desc;
+ 	int ifidx;
+ 	s32 bsscfgidx;
+@@ -199,6 +204,8 @@ struct brcmf_if {
+ 	spinlock_t netif_stop_lock;
+ 	atomic_t pend_8021x_cnt;
+ 	wait_queue_head_t pend_8021x_wait;
++	struct in6_addr ipv6_addr_tbl[NDOL_MAX_ENTRIES];
++	u8 ipv6addr_idx;
+ };
+ 
+ struct brcmf_skb_reorder_data {
diff --git a/package/kernel/mac80211/patches/344-0007-brcmfmac-check-return-for-ARP-ip-setting-iovar.patch b/package/kernel/mac80211/patches/344-0007-brcmfmac-check-return-for-ARP-ip-setting-iovar.patch
new file mode 100644
index 0000000000..68de8ed2a2
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0007-brcmfmac-check-return-for-ARP-ip-setting-iovar.patch
@@ -0,0 +1,38 @@
+From: Franky Lin <frankyl@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:56 +0100
+Subject: [PATCH] brcmfmac: check return for ARP ip setting iovar
+
+The return value of iovar set function should be saved and checked.
+
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Franky Lin <frankyl@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -1039,14 +1039,14 @@ static int brcmf_inetaddr_changed(struct
+ 				return NOTIFY_OK;
+ 			}
+ 			for (i = 0; i < ARPOL_MAX_ENTRIES; i++) {
+-				if (addr_table[i] != 0) {
+-					brcmf_fil_iovar_data_set(ifp,
+-						"arp_hostip", &addr_table[i],
+-						sizeof(addr_table[i]));
+-					if (ret)
+-						brcmf_err("add arp ip err %d\n",
+-							  ret);
+-				}
++				if (addr_table[i] == 0)
++					continue;
++				ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip",
++							       &addr_table[i],
++							       sizeof(addr_table[i]));
++				if (ret)
++					brcmf_err("add arp ip err %d\n",
++						  ret);
+ 			}
+ 		}
+ 		break;
diff --git a/package/kernel/mac80211/patches/344-0008-brcmfmac-use-device-memsize-config-from-fw-if-define.patch b/package/kernel/mac80211/patches/344-0008-brcmfmac-use-device-memsize-config-from-fw-if-define.patch
new file mode 100644
index 0000000000..3de0f64d56
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0008-brcmfmac-use-device-memsize-config-from-fw-if-define.patch
@@ -0,0 +1,73 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:57 +0100
+Subject: [PATCH] brcmfmac: use device memsize config from fw if defined
+
+Newer type pcie devices have memory which get shared between fw and
+hw. The division of this memory is done firmware compile time. As a
+result the ramsize as used by driver needs to be adjusted for this.
+This is done by reading the memory size from the firmware.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+@@ -207,6 +207,10 @@ static struct brcmf_firmware_mapping brc
+ #define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG	0x4F4
+ #define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB	3
+ 
++/* Magic number at a magic location to find RAM size */
++#define BRCMF_RAMSIZE_MAGIC			0x534d4152	/* SMAR */
++#define BRCMF_RAMSIZE_OFFSET			0x6c
++
+ 
+ struct brcmf_pcie_console {
+ 	u32 base_addr;
+@@ -1412,6 +1416,28 @@ static const struct brcmf_bus_ops brcmf_
+ };
+ 
+ 
++static void
++brcmf_pcie_adjust_ramsize(struct brcmf_pciedev_info *devinfo, u8 *data,
++			  u32 data_len)
++{
++	__le32 *field;
++	u32 newsize;
++
++	if (data_len < BRCMF_RAMSIZE_OFFSET + 8)
++		return;
++
++	field = (__le32 *)&data[BRCMF_RAMSIZE_OFFSET];
++	if (le32_to_cpup(field) != BRCMF_RAMSIZE_MAGIC)
++		return;
++	field++;
++	newsize = le32_to_cpup(field);
++
++	brcmf_dbg(PCIE, "Found ramsize info in FW, adjusting to 0x%x\n",
++		  newsize);
++	devinfo->ci->ramsize = newsize;
++}
++
++
+ static int
+ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
+ 			       u32 sharedram_addr)
+@@ -1694,6 +1720,13 @@ static void brcmf_pcie_setup(struct devi
+ 
+ 	brcmf_pcie_attach(devinfo);
+ 
++	/* Some of the firmwares have the size of the memory of the device
++	 * defined inside the firmware. This is because part of the memory in
++	 * the device is shared and the devision is determined by FW. Parse
++	 * the firmware and adjust the chip memory size now.
++	 */
++	brcmf_pcie_adjust_ramsize(devinfo, (u8 *)fw->data, fw->size);
++
+ 	ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len);
+ 	if (ret)
+ 		goto fail;
diff --git a/package/kernel/mac80211/patches/344-0009-brcmfmac-use-bar1-window-size-as-provided-by-pci-sub.patch b/package/kernel/mac80211/patches/344-0009-brcmfmac-use-bar1-window-size-as-provided-by-pci-sub.patch
new file mode 100644
index 0000000000..ca03ffe4b0
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0009-brcmfmac-use-bar1-window-size-as-provided-by-pci-sub.patch
@@ -0,0 +1,58 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:58 +0100
+Subject: [PATCH] brcmfmac: use bar1 window size as provided by pci subsystem
+
+The PCIE bar1 window size is specified by chip. Currently the
+ioremap of bar1 was using a define which always matched the size
+of bar1, but newer chips can have a different bar1 sizes. With
+this patch the ioremap will be called with the by chip provided
+window size.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+@@ -72,7 +72,6 @@ static struct brcmf_firmware_mapping brc
+ 
+ #define BRCMF_PCIE_FW_UP_TIMEOUT		2000 /* msec */
+ 
+-#define BRCMF_PCIE_TCM_MAP_SIZE			(4096 * 1024)
+ #define BRCMF_PCIE_REG_MAP_SIZE			(32 * 1024)
+ 
+ /* backplane addres space accessed by BAR0 */
+@@ -252,7 +251,6 @@ struct brcmf_pciedev_info {
+ 	char nvram_name[BRCMF_FW_NAME_LEN];
+ 	void __iomem *regs;
+ 	void __iomem *tcm;
+-	u32 tcm_size;
+ 	u32 ram_base;
+ 	u32 ram_size;
+ 	struct brcmf_chip *ci;
+@@ -1592,8 +1590,7 @@ static int brcmf_pcie_get_resource(struc
+ 	}
+ 
+ 	devinfo->regs = ioremap_nocache(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE);
+-	devinfo->tcm = ioremap_nocache(bar1_addr, BRCMF_PCIE_TCM_MAP_SIZE);
+-	devinfo->tcm_size = BRCMF_PCIE_TCM_MAP_SIZE;
++	devinfo->tcm = ioremap_nocache(bar1_addr, bar1_size);
+ 
+ 	if (!devinfo->regs || !devinfo->tcm) {
+ 		brcmf_err("ioremap() failed (%p,%p)\n", devinfo->regs,
+@@ -1602,8 +1599,9 @@ static int brcmf_pcie_get_resource(struc
+ 	}
+ 	brcmf_dbg(PCIE, "Phys addr : reg space = %p base addr %#016llx\n",
+ 		  devinfo->regs, (unsigned long long)bar0_addr);
+-	brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx\n",
+-		  devinfo->tcm, (unsigned long long)bar1_addr);
++	brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx size 0x%x\n",
++		  devinfo->tcm, (unsigned long long)bar1_addr,
++		  (unsigned int)bar1_size);
+ 
+ 	return 0;
+ }
diff --git a/package/kernel/mac80211/patches/344-0010-brcmfmac-add-support-for-the-PCIE-4366c0-chip.patch b/package/kernel/mac80211/patches/344-0010-brcmfmac-add-support-for-the-PCIE-4366c0-chip.patch
new file mode 100644
index 0000000000..e4a8f305cf
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0010-brcmfmac-add-support-for-the-PCIE-4366c0-chip.patch
@@ -0,0 +1,34 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:59 +0100
+Subject: [PATCH] brcmfmac: add support for the PCIE 4366c0 chip
+
+A newer version of the 4366 PCIE chip has been released. Add
+support for this version of the chip.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+@@ -53,6 +53,7 @@ BRCMF_FW_NVRAM_DEF(4358, "brcmfmac4358-p
+ BRCMF_FW_NVRAM_DEF(4359, "brcmfmac4359-pcie.bin", "brcmfmac4359-pcie.txt");
+ BRCMF_FW_NVRAM_DEF(4365B, "brcmfmac4365b-pcie.bin", "brcmfmac4365b-pcie.txt");
+ BRCMF_FW_NVRAM_DEF(4366B, "brcmfmac4366b-pcie.bin", "brcmfmac4366b-pcie.txt");
++BRCMF_FW_NVRAM_DEF(4366C, "brcmfmac4366c-pcie.bin", "brcmfmac4366c-pcie.txt");
+ BRCMF_FW_NVRAM_DEF(4371, "brcmfmac4371-pcie.bin", "brcmfmac4371-pcie.txt");
+ 
+ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
+@@ -66,7 +67,8 @@ static struct brcmf_firmware_mapping brc
+ 	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4358_CHIP_ID, 0xFFFFFFFF, 4358),
+ 	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359),
+ 	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4365_CHIP_ID, 0xFFFFFFFF, 4365B),
+-	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFFF, 4366B),
++	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0x0000000F, 4366B),
++	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFF0, 4366C),
+ 	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371),
+ };
+ 
diff --git a/package/kernel/mac80211/patches/344-0011-brcmfmac-remove-pcie-gen1-support.patch b/package/kernel/mac80211/patches/344-0011-brcmfmac-remove-pcie-gen1-support.patch
new file mode 100644
index 0000000000..f99f6dbf07
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0011-brcmfmac-remove-pcie-gen1-support.patch
@@ -0,0 +1,221 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:27:00 +0100
+Subject: [PATCH] brcmfmac: remove pcie gen1 support
+
+The PCIE bus driver supports older gen1 (v1) chips, but there is no
+actual device which is using this older pcie core which is supported
+by brcmfmac. Remove all gen1 related code.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+@@ -100,9 +100,6 @@ static struct brcmf_firmware_mapping brc
+ #define BRCMF_PCIE_PCIE2REG_CONFIGDATA		0x124
+ #define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX		0x140
+ 
+-#define BRCMF_PCIE_GENREV1			1
+-#define BRCMF_PCIE_GENREV2			2
+-
+ #define BRCMF_PCIE2_INTA			0x01
+ #define BRCMF_PCIE2_INTB			0x02
+ 
+@@ -257,9 +254,7 @@ struct brcmf_pciedev_info {
+ 	u32 ram_size;
+ 	struct brcmf_chip *ci;
+ 	u32 coreid;
+-	u32 generic_corerev;
+ 	struct brcmf_pcie_shared_info shared;
+-	void (*ringbell)(struct brcmf_pciedev_info *devinfo);
+ 	wait_queue_head_t mbdata_resp_wait;
+ 	bool mbdata_completed;
+ 	bool irq_allocated;
+@@ -746,68 +741,22 @@ static void brcmf_pcie_bus_console_read(
+ }
+ 
+ 
+-static __used void brcmf_pcie_ringbell_v1(struct brcmf_pciedev_info *devinfo)
+-{
+-	u32 reg_value;
+-
+-	brcmf_dbg(PCIE, "RING !\n");
+-	reg_value = brcmf_pcie_read_reg32(devinfo,
+-					  BRCMF_PCIE_PCIE2REG_MAILBOXINT);
+-	reg_value |= BRCMF_PCIE2_INTB;
+-	brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
+-			       reg_value);
+-}
+-
+-
+-static void brcmf_pcie_ringbell_v2(struct brcmf_pciedev_info *devinfo)
+-{
+-	brcmf_dbg(PCIE, "RING !\n");
+-	/* Any arbitrary value will do, lets use 1 */
+-	brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1);
+-}
+-
+-
+ static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo)
+ {
+-	if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1)
+-		pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK,
+-				       0);
+-	else
+-		brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
+-				       0);
++	brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, 0);
+ }
+ 
+ 
+ static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo)
+ {
+-	if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1)
+-		pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK,
+-				       BRCMF_PCIE_INT_DEF);
+-	else
+-		brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
+-				       BRCMF_PCIE_MB_INT_D2H_DB |
+-				       BRCMF_PCIE_MB_INT_FN0_0 |
+-				       BRCMF_PCIE_MB_INT_FN0_1);
++	brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
++			       BRCMF_PCIE_MB_INT_D2H_DB |
++			       BRCMF_PCIE_MB_INT_FN0_0 |
++			       BRCMF_PCIE_MB_INT_FN0_1);
+ }
+ 
+ 
+-static irqreturn_t brcmf_pcie_quick_check_isr_v1(int irq, void *arg)
+-{
+-	struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
+-	u32 status;
+-
+-	status = 0;
+-	pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
+-	if (status) {
+-		brcmf_pcie_intr_disable(devinfo);
+-		brcmf_dbg(PCIE, "Enter\n");
+-		return IRQ_WAKE_THREAD;
+-	}
+-	return IRQ_NONE;
+-}
+-
+-
+-static irqreturn_t brcmf_pcie_quick_check_isr_v2(int irq, void *arg)
++static irqreturn_t brcmf_pcie_quick_check_isr(int irq, void *arg)
+ {
+ 	struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
+ 
+@@ -820,29 +769,7 @@ static irqreturn_t brcmf_pcie_quick_chec
+ }
+ 
+ 
+-static irqreturn_t brcmf_pcie_isr_thread_v1(int irq, void *arg)
+-{
+-	struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
+-	const struct pci_dev *pdev = devinfo->pdev;
+-	u32 status;
+-
+-	devinfo->in_irq = true;
+-	status = 0;
+-	pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
+-	brcmf_dbg(PCIE, "Enter %x\n", status);
+-	if (status) {
+-		pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status);
+-		if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
+-			brcmf_proto_msgbuf_rx_trigger(&devinfo->pdev->dev);
+-	}
+-	if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
+-		brcmf_pcie_intr_enable(devinfo);
+-	devinfo->in_irq = false;
+-	return IRQ_HANDLED;
+-}
+-
+-
+-static irqreturn_t brcmf_pcie_isr_thread_v2(int irq, void *arg)
++static irqreturn_t brcmf_pcie_isr_thread(int irq, void *arg)
+ {
+ 	struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
+ 	u32 status;
+@@ -879,28 +806,14 @@ static int brcmf_pcie_request_irq(struct
+ 	brcmf_pcie_intr_disable(devinfo);
+ 
+ 	brcmf_dbg(PCIE, "Enter\n");
+-	/* is it a v1 or v2 implementation */
++
+ 	pci_enable_msi(pdev);
+-	if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) {
+-		if (request_threaded_irq(pdev->irq,
+-					 brcmf_pcie_quick_check_isr_v1,
+-					 brcmf_pcie_isr_thread_v1,
+-					 IRQF_SHARED, "brcmf_pcie_intr",
+-					 devinfo)) {
+-			pci_disable_msi(pdev);
+-			brcmf_err("Failed to request IRQ %d\n", pdev->irq);
+-			return -EIO;
+-		}
+-	} else {
+-		if (request_threaded_irq(pdev->irq,
+-					 brcmf_pcie_quick_check_isr_v2,
+-					 brcmf_pcie_isr_thread_v2,
+-					 IRQF_SHARED, "brcmf_pcie_intr",
+-					 devinfo)) {
+-			pci_disable_msi(pdev);
+-			brcmf_err("Failed to request IRQ %d\n", pdev->irq);
+-			return -EIO;
+-		}
++	if (request_threaded_irq(pdev->irq, brcmf_pcie_quick_check_isr,
++				 brcmf_pcie_isr_thread, IRQF_SHARED,
++				 "brcmf_pcie_intr", devinfo)) {
++		pci_disable_msi(pdev);
++		brcmf_err("Failed to request IRQ %d\n", pdev->irq);
++		return -EIO;
+ 	}
+ 	devinfo->irq_allocated = true;
+ 	return 0;
+@@ -931,16 +844,9 @@ static void brcmf_pcie_release_irq(struc
+ 	if (devinfo->in_irq)
+ 		brcmf_err("Still in IRQ (processing) !!!\n");
+ 
+-	if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) {
+-		status = 0;
+-		pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
+-		pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status);
+-	} else {
+-		status = brcmf_pcie_read_reg32(devinfo,
+-					       BRCMF_PCIE_PCIE2REG_MAILBOXINT);
+-		brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
+-				       status);
+-	}
++	status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
++	brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, status);
++
+ 	devinfo->irq_allocated = false;
+ }
+ 
+@@ -989,7 +895,9 @@ static int brcmf_pcie_ring_mb_ring_bell(
+ 	if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
+ 		return -EIO;
+ 
+-	devinfo->ringbell(devinfo);
++	brcmf_dbg(PCIE, "RING !\n");
++	/* Any arbitrary value will do, lets use 1 */
++	brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1);
+ 
+ 	return 0;
+ }
+@@ -1503,9 +1411,6 @@ static int brcmf_pcie_download_fw_nvram(
+ 	u32 address;
+ 	u32 resetintr;
+ 
+-	devinfo->ringbell = brcmf_pcie_ringbell_v2;
+-	devinfo->generic_corerev = BRCMF_PCIE_GENREV2;
+-
+ 	brcmf_dbg(PCIE, "Halt ARM.\n");
+ 	err = brcmf_pcie_enter_download_state(devinfo);
+ 	if (err)
diff --git a/package/kernel/mac80211/patches/344-0012-brcmfmac-increase-timeout-for-tx-eapol.patch b/package/kernel/mac80211/patches/344-0012-brcmfmac-increase-timeout-for-tx-eapol.patch
new file mode 100644
index 0000000000..4adfc2dc64
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0012-brcmfmac-increase-timeout-for-tx-eapol.patch
@@ -0,0 +1,30 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:27:01 +0100
+Subject: [PATCH] brcmfmac: increase timeout for tx eapol
+
+When keys get set and updated this has to happen after eapol got
+transmitted (without key or old key) before the key can be updated.
+To make sure the order of sending eapol and configuring key is done
+correctly a timeout for tx of eapol is applied. This timeout is set
+to 50 msec, which is not always enough. Especially in AP mode and
+key updates the timeout may need to be much longer because client(s)
+can be in powersave. Increase the timeout from 50 to 950 msec.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -42,7 +42,7 @@ MODULE_AUTHOR("Broadcom Corporation");
+ MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
+ MODULE_LICENSE("Dual BSD/GPL");
+ 
+-#define MAX_WAIT_FOR_8021X_TX			msecs_to_jiffies(50)
++#define MAX_WAIT_FOR_8021X_TX			msecs_to_jiffies(950)
+ 
+ /* AMPDU rx reordering definitions */
+ #define BRCMF_RXREORDER_FLOWID_OFFSET		0
diff --git a/package/kernel/mac80211/patches/344-0013-brcmfmac-move-module-init-and-exit-to-common.patch b/package/kernel/mac80211/patches/344-0013-brcmfmac-move-module-init-and-exit-to-common.patch
new file mode 100644
index 0000000000..bd62781188
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0013-brcmfmac-move-module-init-and-exit-to-common.patch
@@ -0,0 +1,135 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:27:02 +0100
+Subject: [PATCH] brcmfmac: move module init and exit to common
+
+In preparation of module parameters for all devices the module init
+and exit routines are moved to the common file.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+@@ -28,6 +28,10 @@
+ #include "tracepoint.h"
+ #include "common.h"
+ 
++MODULE_AUTHOR("Broadcom Corporation");
++MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
++MODULE_LICENSE("Dual BSD/GPL");
++
+ const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ 
+ #define BRCMF_DEFAULT_SCAN_CHANNEL_TIME	40
+@@ -221,7 +225,7 @@ void __brcmf_dbg(u32 level, const char *
+ }
+ #endif
+ 
+-void brcmf_mp_attach(void)
++static void brcmf_mp_attach(void)
+ {
+ 	strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path,
+ 		BRCMF_FW_ALTPATH_LEN);
+@@ -249,3 +253,33 @@ void brcmf_mp_device_detach(struct brcmf
+ 	kfree(drvr->settings);
+ }
+ 
++static int __init brcmfmac_module_init(void)
++{
++	int err;
++
++	/* Initialize debug system first */
++	brcmf_debugfs_init();
++
++#ifdef CPTCFG_BRCMFMAC_SDIO
++	brcmf_sdio_init();
++#endif
++	/* Initialize global module paramaters */
++	brcmf_mp_attach();
++
++	/* Continue the initialization by registering the different busses */
++	err = brcmf_core_init();
++	if (err)
++		brcmf_debugfs_exit();
++
++	return err;
++}
++
++static void __exit brcmfmac_module_exit(void)
++{
++	brcmf_core_exit();
++	brcmf_debugfs_exit();
++}
++
++module_init(brcmfmac_module_init);
++module_exit(brcmfmac_module_exit);
++
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
+@@ -89,7 +89,6 @@ struct brcmf_mp_device {
+ 	struct cc_translate *country_codes;
+ };
+ 
+-void brcmf_mp_attach(void);
+ int brcmf_mp_device_attach(struct brcmf_pub *drvr);
+ void brcmf_mp_device_detach(struct brcmf_pub *drvr);
+ #ifdef DEBUG
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -38,10 +38,6 @@
+ #include "pcie.h"
+ #include "common.h"
+ 
+-MODULE_AUTHOR("Broadcom Corporation");
+-MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
+-MODULE_LICENSE("Dual BSD/GPL");
+-
+ #define MAX_WAIT_FOR_8021X_TX			msecs_to_jiffies(950)
+ 
+ /* AMPDU rx reordering definitions */
+@@ -1422,19 +1418,15 @@ static void brcmf_driver_register(struct
+ }
+ static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register);
+ 
+-static int __init brcmfmac_module_init(void)
++int __init brcmf_core_init(void)
+ {
+-	brcmf_debugfs_init();
+-#ifdef CPTCFG_BRCMFMAC_SDIO
+-	brcmf_sdio_init();
+-#endif
+ 	if (!schedule_work(&brcmf_driver_work))
+ 		return -EBUSY;
+ 
+ 	return 0;
+ }
+ 
+-static void __exit brcmfmac_module_exit(void)
++void __exit brcmf_core_exit(void)
+ {
+ 	cancel_work_sync(&brcmf_driver_work);
+ 
+@@ -1447,8 +1439,5 @@ static void __exit brcmfmac_module_exit(
+ #ifdef CPTCFG_BRCMFMAC_PCIE
+ 	brcmf_pcie_exit();
+ #endif
+-	brcmf_debugfs_exit();
+ }
+ 
+-module_init(brcmfmac_module_init);
+-module_exit(brcmfmac_module_exit);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+@@ -227,5 +227,7 @@ void brcmf_txflowblock_if(struct brcmf_i
+ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
+ void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
+ void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
++int __init brcmf_core_init(void);
++void __exit brcmf_core_exit(void);
+ 
+ #endif /* BRCMFMAC_CORE_H */
diff --git a/package/kernel/mac80211/patches/344-0014-brcmfmac-add-wowl-gtk-rekeying-offload-support.patch b/package/kernel/mac80211/patches/344-0014-brcmfmac-add-wowl-gtk-rekeying-offload-support.patch
new file mode 100644
index 0000000000..577ca8ed28
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0014-brcmfmac-add-wowl-gtk-rekeying-offload-support.patch
@@ -0,0 +1,260 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:27:03 +0100
+Subject: [PATCH] brcmfmac: add wowl gtk rekeying offload support
+
+This patch adds support for gtk rekeying offload and for gtk
+rekeying failure during wowl mode.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -3526,6 +3526,10 @@ static void brcmf_report_wowl_wakeind(st
+ 			else
+ 				wakeup_data.net_detect = cfg->wowl.nd_info;
+ 		}
++		if (wakeind & BRCMF_WOWL_GTK_FAILURE) {
++			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n");
++			wakeup_data.gtk_rekey_failure = true;
++		}
+ 	} else {
+ 		wakeup = NULL;
+ 	}
+@@ -3607,6 +3611,8 @@ static void brcmf_configure_wowl(struct
+ 		brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
+ 				    brcmf_wowl_nd_results);
+ 	}
++	if (wowl->gtk_rekey_failure)
++		wowl_config |= BRCMF_WOWL_GTK_FAILURE;
+ 	if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
+ 		wowl_config |= BRCMF_WOWL_UNASSOC;
+ 
+@@ -4874,7 +4880,32 @@ static int brcmf_cfg80211_tdls_oper(stru
+ 	return ret;
+ }
+ 
+-static struct cfg80211_ops wl_cfg80211_ops = {
++#ifdef CONFIG_PM
++static int
++brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,
++			      struct cfg80211_gtk_rekey_data *gtk)
++{
++	struct brcmf_if *ifp = netdev_priv(ndev);
++	struct brcmf_gtk_keyinfo_le gtk_le;
++	int ret;
++
++	brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
++
++	memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck));
++	memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek));
++	memcpy(gtk_le.replay_counter, gtk->replay_ctr,
++	       sizeof(gtk_le.replay_counter));
++
++	ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", &gtk_le,
++				       sizeof(gtk_le));
++	if (ret < 0)
++		brcmf_err("gtk_key_info iovar failed: ret=%d\n", ret);
++
++	return ret;
++}
++#endif
++
++static struct cfg80211_ops brcmf_cfg80211_ops = {
+ 	.add_virtual_intf = brcmf_cfg80211_add_iface,
+ 	.del_virtual_intf = brcmf_cfg80211_del_iface,
+ 	.change_virtual_intf = brcmf_cfg80211_change_iface,
+@@ -6139,19 +6170,18 @@ static void brcmf_wiphy_wowl_params(stru
+ {
+ #ifdef CONFIG_PM
+ 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+-	s32 err;
+-	u32 wowl_cap;
+ 
+ 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
+-		err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
+-		if (!err) {
+-			if (wowl_cap & BRCMF_WOWL_PFN_FOUND) {
+-				brcmf_wowlan_support.flags |=
+-							WIPHY_WOWLAN_NET_DETECT;
+-				init_waitqueue_head(&cfg->wowl.nd_data_wait);
+-			}
++		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) {
++			brcmf_wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
++			init_waitqueue_head(&cfg->wowl.nd_data_wait);
+ 		}
+ 	}
++	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) {
++		brcmf_wowlan_support.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY;
++		brcmf_wowlan_support.flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE;
++	}
++
+ 	wiphy->wowlan = &brcmf_wowlan_support;
+ #endif
+ }
+@@ -6538,6 +6568,7 @@ struct brcmf_cfg80211_info *brcmf_cfg802
+ 	struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
+ 	struct brcmf_cfg80211_info *cfg;
+ 	struct wiphy *wiphy;
++	struct cfg80211_ops *ops;
+ 	struct brcmf_cfg80211_vif *vif;
+ 	struct brcmf_if *ifp;
+ 	s32 err = 0;
+@@ -6549,8 +6580,17 @@ struct brcmf_cfg80211_info *brcmf_cfg802
+ 		return NULL;
+ 	}
+ 
++	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
++	if (!ops)
++		return NULL;
++
++	memcpy(ops, &brcmf_cfg80211_ops, sizeof(*ops));
+ 	ifp = netdev_priv(ndev);
+-	wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
++#ifdef CONFIG_PM
++	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
++		ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
++#endif
++	wiphy = wiphy_new(ops, sizeof(struct brcmf_cfg80211_info));
+ 	if (!wiphy) {
+ 		brcmf_err("Could not allocate wiphy device\n");
+ 		return NULL;
+@@ -6560,6 +6600,7 @@ struct brcmf_cfg80211_info *brcmf_cfg802
+ 
+ 	cfg = wiphy_priv(wiphy);
+ 	cfg->wiphy = wiphy;
++	cfg->ops = ops;
+ 	cfg->pub = drvr;
+ 	init_vif_event(&cfg->vif_event);
+ 	INIT_LIST_HEAD(&cfg->vif_list);
+@@ -6686,6 +6727,7 @@ priv_out:
+ 	ifp->vif = NULL;
+ wiphy_out:
+ 	brcmf_free_wiphy(wiphy);
++	kfree(ops);
+ 	return NULL;
+ }
+ 
+@@ -6696,6 +6738,7 @@ void brcmf_cfg80211_detach(struct brcmf_
+ 
+ 	brcmf_btcoex_detach(cfg);
+ 	wiphy_unregister(cfg->wiphy);
++	kfree(cfg->ops);
+ 	wl_deinit_priv(cfg);
+ 	brcmf_free_wiphy(cfg->wiphy);
+ }
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+@@ -256,6 +256,7 @@ struct brcmf_cfg80211_wowl {
+  * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface
+  *
+  * @wiphy: wiphy object for cfg80211 interface.
++ * @ops: pointer to copy of ops as registered with wiphy object.
+  * @conf: dongle configuration.
+  * @p2p: peer-to-peer specific information.
+  * @btcoex: Bluetooth coexistence information.
+@@ -288,6 +289,7 @@ struct brcmf_cfg80211_wowl {
+  */
+ struct brcmf_cfg80211_info {
+ 	struct wiphy *wiphy;
++	struct cfg80211_ops *ops;
+ 	struct brcmf_cfg80211_conf *conf;
+ 	struct brcmf_p2p_info p2p;
+ 	struct brcmf_btcoex_info *btcoex;
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+@@ -136,6 +136,7 @@ void brcmf_feat_attach(struct brcmf_pub
+ {
+ 	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
+ 	struct brcmf_pno_macaddr_le pfn_mac;
++	u32 wowl_cap;
+ 	s32 err;
+ 
+ 	brcmf_feat_firmware_capabilities(ifp);
+@@ -143,6 +144,17 @@ void brcmf_feat_attach(struct brcmf_pub
+ 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
+ 	if (drvr->bus_if->wowl_supported)
+ 		brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
++	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) {
++		err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
++		if (!err) {
++			if (wowl_cap & BRCMF_WOWL_PFN_FOUND)
++				ifp->drvr->feat_flags |=
++					BIT(BRCMF_FEAT_WOWL_ND);
++			if (wowl_cap & BRCMF_WOWL_GTK_FAILURE)
++				ifp->drvr->feat_flags |=
++					BIT(BRCMF_FEAT_WOWL_GTK);
++		}
++	}
+ 	/* MBSS does not work for 43362 */
+ 	if (drvr->bus_if->chip == BRCM_CC_43362_CHIP_ID)
+ 		ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
+@@ -27,6 +27,8 @@
+  * RSDB: Real Simultaneous Dual Band
+  * TDLS: Tunneled Direct Link Setup
+  * SCAN_RANDOM_MAC: Random MAC during (net detect) scheduled scan.
++ * WOWL_ND: WOWL net detect (PNO)
++ * WOWL_GTK: (WOWL) GTK rekeying offload
+  */
+ #define BRCMF_FEAT_LIST \
+ 	BRCMF_FEAT_DEF(MBSS) \
+@@ -36,7 +38,9 @@
+ 	BRCMF_FEAT_DEF(P2P) \
+ 	BRCMF_FEAT_DEF(RSDB) \
+ 	BRCMF_FEAT_DEF(TDLS) \
+-	BRCMF_FEAT_DEF(SCAN_RANDOM_MAC)
++	BRCMF_FEAT_DEF(SCAN_RANDOM_MAC) \
++	BRCMF_FEAT_DEF(WOWL_ND) \
++	BRCMF_FEAT_DEF(WOWL_GTK)
+ 
+ /*
+  * Quirks:
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+@@ -111,7 +111,9 @@
+ /* Wakeup if received matched secured pattern: */
+ #define BRCMF_WOWL_SECURE		(1 << 25)
+ /* Wakeup on finding preferred network */
+-#define BRCMF_WOWL_PFN_FOUND		(1 << 26)
++#define BRCMF_WOWL_PFN_FOUND		(1 << 27)
++/* Wakeup on receiving pairwise key EAP packets: */
++#define WIPHY_WOWL_EAP_PK		(1 << 28)
+ /* Link Down indication in WoWL mode: */
+ #define BRCMF_WOWL_LINKDOWN		(1 << 31)
+ 
+@@ -136,6 +138,10 @@
+ 
+ #define BRCMF_MCSSET_LEN		16
+ 
++#define BRCMF_RSN_KCK_LENGTH		16
++#define BRCMF_RSN_KEK_LENGTH		16
++#define BRCMF_RSN_REPLAY_LEN		8
++
+ /* join preference types for join_pref iovar */
+ enum brcmf_join_pref_types {
+ 	BRCMF_JOIN_PREF_RSSI = 1,
+@@ -789,4 +795,17 @@ struct brcmf_pktcnt_le {
+ 	__le32 rx_ocast_good_pkt;
+ };
+ 
++/**
++ * struct brcmf_gtk_keyinfo_le - GTP rekey data
++ *
++ * @kck: key confirmation key.
++ * @kek: key encryption key.
++ * @replay_counter: replay counter.
++ */
++struct brcmf_gtk_keyinfo_le {
++	u8 kck[BRCMF_RSN_KCK_LENGTH];
++	u8 kek[BRCMF_RSN_KEK_LENGTH];
++	u8 replay_counter[BRCMF_RSN_REPLAY_LEN];
++};
++
+ #endif /* FWIL_TYPES_H_ */
diff --git a/package/kernel/mac80211/patches/344-0015-brcmfmac-move-platform-data-retrieval-code-to-common.patch b/package/kernel/mac80211/patches/344-0015-brcmfmac-move-platform-data-retrieval-code-to-common.patch
new file mode 100644
index 0000000000..2685238925
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0015-brcmfmac-move-platform-data-retrieval-code-to-common.patch
@@ -0,0 +1,385 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:27:04 +0100
+Subject: [PATCH] brcmfmac: move platform data retrieval code to common
+
+In preparation of module parameters for all devices the module
+platform data retrieval is moved from sdio to common. It is still
+only used for sdio devices.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+@@ -27,8 +27,6 @@
+ #include <linux/mmc/sdio_func.h>
+ #include <linux/mmc/card.h>
+ #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>
+@@ -46,7 +44,6 @@
+ #include "bus.h"
+ #include "debug.h"
+ #include "sdio.h"
+-#include "of.h"
+ #include "core.h"
+ #include "common.h"
+ 
+@@ -106,18 +103,18 @@ static void brcmf_sdiod_dummy_irqhandler
+ 
+ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
+ {
++	struct brcmfmac_sdio_platform_data *pdata;
+ 	int ret = 0;
+ 	u8 data;
+ 	u32 addr, gpiocontrol;
+ 	unsigned long flags;
+ 
+-	if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) {
++	pdata = sdiodev->pdata;
++	if ((pdata) && (pdata->oob_irq_supported)) {
+ 		brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n",
+-			  sdiodev->pdata->oob_irq_nr);
+-		ret = request_irq(sdiodev->pdata->oob_irq_nr,
+-				  brcmf_sdiod_oob_irqhandler,
+-				  sdiodev->pdata->oob_irq_flags,
+-				  "brcmf_oob_intr",
++			  pdata->oob_irq_nr);
++		ret = request_irq(pdata->oob_irq_nr, brcmf_sdiod_oob_irqhandler,
++				  pdata->oob_irq_flags, "brcmf_oob_intr",
+ 				  &sdiodev->func[1]->dev);
+ 		if (ret != 0) {
+ 			brcmf_err("request_irq failed %d\n", ret);
+@@ -129,7 +126,7 @@ int brcmf_sdiod_intr_register(struct brc
+ 		sdiodev->irq_en = true;
+ 		spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags);
+ 
+-		ret = enable_irq_wake(sdiodev->pdata->oob_irq_nr);
++		ret = enable_irq_wake(pdata->oob_irq_nr);
+ 		if (ret != 0) {
+ 			brcmf_err("enable_irq_wake failed %d\n", ret);
+ 			return ret;
+@@ -158,7 +155,7 @@ int brcmf_sdiod_intr_register(struct brc
+ 
+ 		/* redirect, configure and enable io for interrupt signal */
+ 		data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
+-		if (sdiodev->pdata->oob_irq_flags & IRQF_TRIGGER_HIGH)
++		if (pdata->oob_irq_flags & IRQF_TRIGGER_HIGH)
+ 			data |= SDIO_SEPINT_ACT_HI;
+ 		brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret);
+ 
+@@ -176,9 +173,12 @@ int brcmf_sdiod_intr_register(struct brc
+ 
+ int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
+ {
++	struct brcmfmac_sdio_platform_data *pdata;
++
+ 	brcmf_dbg(SDIO, "Entering\n");
+ 
+-	if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) {
++	pdata = sdiodev->pdata;
++	if ((pdata) && (pdata->oob_irq_supported)) {
+ 		sdio_claim_host(sdiodev->func[1]);
+ 		brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
+ 		brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
+@@ -187,11 +187,10 @@ int brcmf_sdiod_intr_unregister(struct b
+ 		if (sdiodev->oob_irq_requested) {
+ 			sdiodev->oob_irq_requested = false;
+ 			if (sdiodev->irq_wake) {
+-				disable_irq_wake(sdiodev->pdata->oob_irq_nr);
++				disable_irq_wake(pdata->oob_irq_nr);
+ 				sdiodev->irq_wake = false;
+ 			}
+-			free_irq(sdiodev->pdata->oob_irq_nr,
+-				 &sdiodev->func[1]->dev);
++			free_irq(pdata->oob_irq_nr, &sdiodev->func[1]->dev);
+ 			sdiodev->irq_en = false;
+ 		}
+ 	} else {
+@@ -1103,8 +1102,6 @@ static const struct sdio_device_id brcmf
+ };
+ MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
+ 
+-static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata;
+-
+ 
+ static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev,
+ 						  int val)
+@@ -1167,10 +1164,7 @@ static int brcmf_ops_sdio_probe(struct s
+ 	dev_set_drvdata(&func->dev, bus_if);
+ 	dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
+ 	sdiodev->dev = &sdiodev->func[1]->dev;
+-	sdiodev->pdata = brcmfmac_sdio_pdata;
+-
+-	if (!sdiodev->pdata)
+-		brcmf_of_probe(sdiodev);
++	sdiodev->pdata = brcmf_get_module_param(sdiodev->dev);
+ 
+ #ifdef CONFIG_PM_SLEEP
+ 	/* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ
+@@ -1296,7 +1290,7 @@ static const struct dev_pm_ops brcmf_sdi
+ static struct sdio_driver brcmf_sdmmc_driver = {
+ 	.probe = brcmf_ops_sdio_probe,
+ 	.remove = brcmf_ops_sdio_remove,
+-	.name = BRCMFMAC_SDIO_PDATA_NAME,
++	.name = KBUILD_MODNAME,
+ 	.id_table = brcmf_sdmmc_ids,
+ 	.drv = {
+ 		.owner = THIS_MODULE,
+@@ -1306,37 +1300,6 @@ static struct sdio_driver brcmf_sdmmc_dr
+ 	},
+ };
+ 
+-static int __init brcmf_sdio_pd_probe(struct platform_device *pdev)
+-{
+-	brcmf_dbg(SDIO, "Enter\n");
+-
+-	brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev);
+-
+-	if (brcmfmac_sdio_pdata->power_on)
+-		brcmfmac_sdio_pdata->power_on();
+-
+-	return 0;
+-}
+-
+-static int brcmf_sdio_pd_remove(struct platform_device *pdev)
+-{
+-	brcmf_dbg(SDIO, "Enter\n");
+-
+-	if (brcmfmac_sdio_pdata->power_off)
+-		brcmfmac_sdio_pdata->power_off();
+-
+-	sdio_unregister_driver(&brcmf_sdmmc_driver);
+-
+-	return 0;
+-}
+-
+-static struct platform_driver brcmf_sdio_pd = {
+-	.remove		= brcmf_sdio_pd_remove,
+-	.driver		= {
+-		.name	= BRCMFMAC_SDIO_PDATA_NAME,
+-	}
+-};
+-
+ void brcmf_sdio_register(void)
+ {
+ 	int ret;
+@@ -1350,19 +1313,6 @@ void brcmf_sdio_exit(void)
+ {
+ 	brcmf_dbg(SDIO, "Enter\n");
+ 
+-	if (brcmfmac_sdio_pdata)
+-		platform_driver_unregister(&brcmf_sdio_pd);
+-	else
+-		sdio_unregister_driver(&brcmf_sdmmc_driver);
++	sdio_unregister_driver(&brcmf_sdmmc_driver);
+ }
+ 
+-void __init brcmf_sdio_init(void)
+-{
+-	int ret;
+-
+-	brcmf_dbg(SDIO, "Enter\n");
+-
+-	ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe);
+-	if (ret == -ENODEV)
+-		brcmf_dbg(SDIO, "No platform data available.\n");
+-}
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+@@ -27,6 +27,7 @@
+ #include "fwil_types.h"
+ #include "tracepoint.h"
+ #include "common.h"
++#include "of.h"
+ 
+ MODULE_AUTHOR("Broadcom Corporation");
+ MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
+@@ -79,6 +80,7 @@ module_param_named(ignore_probe_fail, br
+ MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging");
+ #endif
+ 
++static struct brcmfmac_sdio_platform_data *brcmfmac_pdata;
+ struct brcmf_mp_global_t brcmf_mp_global;
+ 
+ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
+@@ -231,6 +233,13 @@ static void brcmf_mp_attach(void)
+ 		BRCMF_FW_ALTPATH_LEN);
+ }
+ 
++struct brcmfmac_sdio_platform_data *brcmf_get_module_param(struct device *dev)
++{
++	if (!brcmfmac_pdata)
++		brcmf_of_probe(dev, &brcmfmac_pdata);
++	return brcmfmac_pdata;
++}
++
+ int brcmf_mp_device_attach(struct brcmf_pub *drvr)
+ {
+ 	drvr->settings = kzalloc(sizeof(*drvr->settings), GFP_ATOMIC);
+@@ -253,6 +262,35 @@ void brcmf_mp_device_detach(struct brcmf
+ 	kfree(drvr->settings);
+ }
+ 
++static int __init brcmf_common_pd_probe(struct platform_device *pdev)
++{
++	brcmf_dbg(INFO, "Enter\n");
++
++	brcmfmac_pdata = dev_get_platdata(&pdev->dev);
++
++	if (brcmfmac_pdata->power_on)
++		brcmfmac_pdata->power_on();
++
++	return 0;
++}
++
++static int brcmf_common_pd_remove(struct platform_device *pdev)
++{
++	brcmf_dbg(INFO, "Enter\n");
++
++	if (brcmfmac_pdata->power_off)
++		brcmfmac_pdata->power_off();
++
++	return 0;
++}
++
++static struct platform_driver brcmf_pd = {
++	.remove		= brcmf_common_pd_remove,
++	.driver		= {
++		.name	= BRCMFMAC_SDIO_PDATA_NAME,
++	}
++};
++
+ static int __init brcmfmac_module_init(void)
+ {
+ 	int err;
+@@ -260,16 +298,21 @@ static int __init brcmfmac_module_init(v
+ 	/* Initialize debug system first */
+ 	brcmf_debugfs_init();
+ 
+-#ifdef CPTCFG_BRCMFMAC_SDIO
+-	brcmf_sdio_init();
+-#endif
++	/* Get the platform data (if available) for our devices */
++	err = platform_driver_probe(&brcmf_pd, brcmf_common_pd_probe);
++	if (err == -ENODEV)
++		brcmf_dbg(INFO, "No platform data available.\n");
++
+ 	/* Initialize global module paramaters */
+ 	brcmf_mp_attach();
+ 
+ 	/* Continue the initialization by registering the different busses */
+ 	err = brcmf_core_init();
+-	if (err)
++	if (err) {
+ 		brcmf_debugfs_exit();
++		if (brcmfmac_pdata)
++			platform_driver_unregister(&brcmf_pd);
++	}
+ 
+ 	return err;
+ }
+@@ -277,6 +320,8 @@ static int __init brcmfmac_module_init(v
+ static void __exit brcmfmac_module_exit(void)
+ {
+ 	brcmf_core_exit();
++	if (brcmfmac_pdata)
++		platform_driver_unregister(&brcmf_pd);
+ 	brcmf_debugfs_exit();
+ }
+ 
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
+@@ -15,6 +15,8 @@
+ #ifndef BRCMFMAC_COMMON_H
+ #define BRCMFMAC_COMMON_H
+ 
++#include <linux/platform_device.h>
++#include <linux/platform_data/brcmfmac-sdio.h>
+ #include "fwil_types.h"
+ 
+ extern const u8 ALLFFMAC[ETH_ALEN];
+@@ -89,6 +91,7 @@ struct brcmf_mp_device {
+ 	struct cc_translate *country_codes;
+ };
+ 
++struct brcmfmac_sdio_platform_data *brcmf_get_module_param(struct device *dev);
+ int brcmf_mp_device_attach(struct brcmf_pub *drvr);
+ void brcmf_mp_device_detach(struct brcmf_pub *drvr);
+ #ifdef DEBUG
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
+@@ -16,17 +16,16 @@
+ #include <linux/init.h>
+ #include <linux/of.h>
+ #include <linux/of_irq.h>
+-#include <linux/mmc/card.h>
+-#include <linux/platform_data/brcmfmac-sdio.h>
+-#include <linux/mmc/sdio_func.h>
+ 
+ #include <defs.h>
+ #include "debug.h"
+-#include "sdio.h"
++#include "core.h"
++#include "common.h"
++#include "of.h"
+ 
+-void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev)
++void
++brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_platform_data **sdio)
+ {
+-	struct device *dev = sdiodev->dev;
+ 	struct device_node *np = dev->of_node;
+ 	int irq;
+ 	u32 irqf;
+@@ -35,12 +34,12 @@ void brcmf_of_probe(struct brcmf_sdio_de
+ 	if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac"))
+ 		return;
+ 
+-	sdiodev->pdata = devm_kzalloc(dev, sizeof(*sdiodev->pdata), GFP_KERNEL);
+-	if (!sdiodev->pdata)
++	*sdio = devm_kzalloc(dev, sizeof(*sdio), GFP_KERNEL);
++	if (!(*sdio))
+ 		return;
+ 
+ 	if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0)
+-		sdiodev->pdata->drive_strength = val;
++		(*sdio)->drive_strength = val;
+ 
+ 	/* make sure there are interrupts defined in the node */
+ 	if (!of_find_property(np, "interrupts", NULL))
+@@ -53,7 +52,7 @@ void brcmf_of_probe(struct brcmf_sdio_de
+ 	}
+ 	irqf = irqd_get_trigger_type(irq_get_irq_data(irq));
+ 
+-	sdiodev->pdata->oob_irq_supported = true;
+-	sdiodev->pdata->oob_irq_nr = irq;
+-	sdiodev->pdata->oob_irq_flags = irqf;
++	(*sdio)->oob_irq_supported = true;
++	(*sdio)->oob_irq_nr = irq;
++	(*sdio)->oob_irq_flags = irqf;
+ }
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
+@@ -14,9 +14,11 @@
+  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  */
+ #ifdef CONFIG_OF
+-void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev);
++void
++brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_platform_data **sdio);
+ #else
+-static void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev)
++static void brcmf_of_probe(struct device *dev,
++			   struct brcmfmac_sdio_platform_data **sdio)
+ {
+ }
+ #endif /* CONFIG_OF */
diff --git a/package/kernel/mac80211/patches/344-0016-brcmfmac-keep-ARP-and-ND-offload-enabled-during-WOWL.patch b/package/kernel/mac80211/patches/344-0016-brcmfmac-keep-ARP-and-ND-offload-enabled-during-WOWL.patch
new file mode 100644
index 0000000000..4e789cfc5a
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0016-brcmfmac-keep-ARP-and-ND-offload-enabled-during-WOWL.patch
@@ -0,0 +1,69 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:27:05 +0100
+Subject: [PATCH] brcmfmac: keep ARP and ND offload enabled during WOWL
+
+Currently ARP and ND (IPv6 Neigbor Discovery) offload get disabled
+on entering suspend. However when firmwares support the wowl_cap
+iovar then these offload routines can be kept enabled as they
+will work during WOWL as well.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -3556,7 +3556,8 @@ static s32 brcmf_cfg80211_resume(struct
+ 		brcmf_report_wowl_wakeind(wiphy, ifp);
+ 		brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
+ 		brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
+-		brcmf_configure_arp_nd_offload(ifp, true);
++		if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
++			brcmf_configure_arp_nd_offload(ifp, true);
+ 		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
+ 				      cfg->wowl.pre_pmmode);
+ 		cfg->wowl.active = false;
+@@ -3580,7 +3581,8 @@ static void brcmf_configure_wowl(struct
+ 
+ 	brcmf_dbg(TRACE, "Suspend, wowl config.\n");
+ 
+-	brcmf_configure_arp_nd_offload(ifp, false);
++	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
++		brcmf_configure_arp_nd_offload(ifp, false);
+ 	brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
+ 	brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
+ 
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+@@ -147,6 +147,7 @@ void brcmf_feat_attach(struct brcmf_pub
+ 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) {
+ 		err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
+ 		if (!err) {
++			ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_WOWL_ARP_ND);
+ 			if (wowl_cap & BRCMF_WOWL_PFN_FOUND)
+ 				ifp->drvr->feat_flags |=
+ 					BIT(BRCMF_FEAT_WOWL_ND);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
+@@ -29,6 +29,7 @@
+  * SCAN_RANDOM_MAC: Random MAC during (net detect) scheduled scan.
+  * WOWL_ND: WOWL net detect (PNO)
+  * WOWL_GTK: (WOWL) GTK rekeying offload
++ * WOWL_ARP_ND: ARP and Neighbor Discovery offload support during WOWL.
+  */
+ #define BRCMF_FEAT_LIST \
+ 	BRCMF_FEAT_DEF(MBSS) \
+@@ -40,7 +41,8 @@
+ 	BRCMF_FEAT_DEF(TDLS) \
+ 	BRCMF_FEAT_DEF(SCAN_RANDOM_MAC) \
+ 	BRCMF_FEAT_DEF(WOWL_ND) \
+-	BRCMF_FEAT_DEF(WOWL_GTK)
++	BRCMF_FEAT_DEF(WOWL_GTK) \
++	BRCMF_FEAT_DEF(WOWL_ARP_ND)
+ 
+ /*
+  * Quirks:
diff --git a/package/kernel/mac80211/patches/344-0017-brcmfmac-switch-to-new-platform-data.patch b/package/kernel/mac80211/patches/344-0017-brcmfmac-switch-to-new-platform-data.patch
new file mode 100644
index 0000000000..37b68552cc
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0017-brcmfmac-switch-to-new-platform-data.patch
@@ -0,0 +1,734 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:27:07 +0100
+Subject: [PATCH] brcmfmac: switch to new platform data
+
+Platform data is only available for sdio. With this patch a new
+platform data structure is being used which allows for platform
+data for any device and configurable per device. This patch only
+switches to the new structure and adds support for SDIO devices.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+@@ -103,7 +103,7 @@ static void brcmf_sdiod_dummy_irqhandler
+ 
+ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
+ {
+-	struct brcmfmac_sdio_platform_data *pdata;
++	struct brcmfmac_sdio_pd *pdata;
+ 	int ret = 0;
+ 	u8 data;
+ 	u32 addr, gpiocontrol;
+@@ -173,7 +173,7 @@ int brcmf_sdiod_intr_register(struct brc
+ 
+ int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
+ {
+-	struct brcmfmac_sdio_platform_data *pdata;
++	struct brcmfmac_sdio_pd *pdata;
+ 
+ 	brcmf_dbg(SDIO, "Entering\n");
+ 
+@@ -1164,17 +1164,6 @@ static int brcmf_ops_sdio_probe(struct s
+ 	dev_set_drvdata(&func->dev, bus_if);
+ 	dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
+ 	sdiodev->dev = &sdiodev->func[1]->dev;
+-	sdiodev->pdata = brcmf_get_module_param(sdiodev->dev);
+-
+-#ifdef CONFIG_PM_SLEEP
+-	/* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ
+-	 * is true or when platform data OOB irq is true).
+-	 */
+-	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 && sdiodev->pdata->oob_irq_supported)))
+-		bus_if->wowl_supported = true;
+-#endif
+ 
+ 	brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN);
+ 
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -6459,8 +6459,8 @@ int brcmf_cfg80211_wait_vif_event(struct
+ static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
+ 					struct brcmf_fil_country_le *ccreq)
+ {
+-	struct cc_translate *country_codes;
+-	struct cc_entry *cc;
++	struct brcmfmac_pd_cc *country_codes;
++	struct brcmfmac_pd_cc_entry *cc;
+ 	s32 found_index;
+ 	int i;
+ 
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+@@ -80,7 +80,7 @@ module_param_named(ignore_probe_fail, br
+ MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging");
+ #endif
+ 
+-static struct brcmfmac_sdio_platform_data *brcmfmac_pdata;
++static struct brcmfmac_platform_data *brcmfmac_pdata;
+ struct brcmf_mp_global_t brcmf_mp_global;
+ 
+ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
+@@ -229,15 +229,46 @@ void __brcmf_dbg(u32 level, const char *
+ 
+ static void brcmf_mp_attach(void)
+ {
++	/* If module param firmware path is set then this will always be used,
++	 * if not set then if available use the platform data version. To make
++	 * sure it gets initialized at all, always copy the module param version
++	 */
+ 	strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path,
+ 		BRCMF_FW_ALTPATH_LEN);
++	if ((brcmfmac_pdata) && (brcmfmac_pdata->fw_alternative_path) &&
++	    (brcmf_mp_global.firmware_path[0] == '\0')) {
++		strlcpy(brcmf_mp_global.firmware_path,
++			brcmfmac_pdata->fw_alternative_path,
++			BRCMF_FW_ALTPATH_LEN);
++	}
+ }
+ 
+-struct brcmfmac_sdio_platform_data *brcmf_get_module_param(struct device *dev)
+-{
+-	if (!brcmfmac_pdata)
+-		brcmf_of_probe(dev, &brcmfmac_pdata);
+-	return brcmfmac_pdata;
++struct brcmfmac_sdio_pd *brcmf_get_module_param(struct device *dev,
++						enum brcmf_bus_type bus_type,
++						u32 chip, u32 chiprev)
++{
++	struct brcmfmac_sdio_pd *pdata;
++	struct brcmfmac_pd_device *device_pd;
++	int i;
++
++	if (brcmfmac_pdata) {
++		for (i = 0; i < brcmfmac_pdata->device_count; i++) {
++			device_pd = &brcmfmac_pdata->devices[i];
++			if ((device_pd->bus_type == bus_type) &&
++			    (device_pd->id == chip) &&
++			    ((device_pd->rev == chiprev) ||
++			     (device_pd->rev == -1))) {
++				brcmf_dbg(INFO, "Platform data for device found\n");
++				if (device_pd->bus_type == BRCMF_BUSTYPE_SDIO)
++					return &device_pd->bus.sdio;
++				break;
++			}
++		}
++	}
++	pdata = NULL;
++	brcmf_of_probe(dev, &pdata);
++
++	return pdata;
+ }
+ 
+ int brcmf_mp_device_attach(struct brcmf_pub *drvr)
+@@ -287,7 +318,7 @@ static int brcmf_common_pd_remove(struct
+ static struct platform_driver brcmf_pd = {
+ 	.remove		= brcmf_common_pd_remove,
+ 	.driver		= {
+-		.name	= BRCMFMAC_SDIO_PDATA_NAME,
++		.name	= BRCMFMAC_PDATA_NAME,
+ 	}
+ };
+ 
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
+@@ -16,7 +16,7 @@
+ #define BRCMFMAC_COMMON_H
+ 
+ #include <linux/platform_device.h>
+-#include <linux/platform_data/brcmfmac-sdio.h>
++#include <linux/platform_data/brcmfmac.h>
+ #include "fwil_types.h"
+ 
+ extern const u8 ALLFFMAC[ETH_ALEN];
+@@ -43,33 +43,6 @@ struct brcmf_mp_global_t {
+ extern struct brcmf_mp_global_t brcmf_mp_global;
+ 
+ /**
+- * struct cc_entry - Struct for translating user space country code (iso3166) to
+- *		     firmware country code and revision.
+- *
+- * @iso3166: iso3166 alpha 2 country code string.
+- * @cc: firmware country code string.
+- * @rev: firmware country code revision.
+- */
+-struct cc_entry {
+-	char	iso3166[BRCMF_COUNTRY_BUF_SZ];
+-	char	cc[BRCMF_COUNTRY_BUF_SZ];
+-	s32	rev;
+-};
+-
+-/**
+- * struct cc_translate - Struct for translating country codes as set by user
+- *			 space to a country code and rev which can be used by
+- *			 firmware.
+- *
+- * @table_size: number of entries in table (> 0)
+- * @table: dynamic array of 1 or more elements with translation information.
+- */
+-struct cc_translate {
+-	int	table_size;
+-	struct cc_entry table[0];
+-};
+-
+-/**
+  * struct brcmf_mp_device - Device module paramaters.
+  *
+  * @sdiod_txglomsz: SDIO txglom size.
+@@ -88,10 +61,12 @@ struct brcmf_mp_device {
+ 	int	fcmode;
+ 	bool	roamoff;
+ 	bool	ignore_probe_fail;
+-	struct cc_translate *country_codes;
++	struct brcmfmac_pd_cc *country_codes;
+ };
+ 
+-struct brcmfmac_sdio_platform_data *brcmf_get_module_param(struct device *dev);
++struct brcmfmac_sdio_pd *brcmf_get_module_param(struct device *dev,
++						enum brcmf_bus_type bus_type,
++						u32 chip, u32 chiprev);
+ int brcmf_mp_device_attach(struct brcmf_pub *drvr);
+ void brcmf_mp_device_detach(struct brcmf_pub *drvr);
+ #ifdef DEBUG
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
+@@ -23,8 +23,7 @@
+ #include "common.h"
+ #include "of.h"
+ 
+-void
+-brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_platform_data **sdio)
++void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd **sdio)
+ {
+ 	struct device_node *np = dev->of_node;
+ 	int irq;
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
+@@ -15,10 +15,9 @@
+  */
+ #ifdef CONFIG_OF
+ void
+-brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_platform_data **sdio);
++brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd **sdio);
+ #else
+-static void brcmf_of_probe(struct device *dev,
+-			   struct brcmfmac_sdio_platform_data **sdio)
++static void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd **sdio)
+ {
+ }
+ #endif /* CONFIG_OF */
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+@@ -33,8 +33,6 @@
+ #include <linux/bcma/bcma.h>
+ #include <linux/debugfs.h>
+ #include <linux/vmalloc.h>
+-#include <linux/platform_data/brcmfmac-sdio.h>
+-#include <linux/moduleparam.h>
+ #include <asm/unaligned.h>
+ #include <defs.h>
+ #include <brcmu_wifi.h>
+@@ -44,6 +42,8 @@
+ #include "sdio.h"
+ #include "chip.h"
+ #include "firmware.h"
++#include "core.h"
++#include "common.h"
+ 
+ #define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2500)
+ #define CTL_DONE_TIMEOUT	msecs_to_jiffies(2500)
+@@ -3775,26 +3775,28 @@ static const struct brcmf_buscore_ops br
+ static bool
+ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
+ {
++	struct brcmf_sdio_dev *sdiodev;
+ 	u8 clkctl = 0;
+ 	int err = 0;
+ 	int reg_addr;
+ 	u32 reg_val;
+ 	u32 drivestrength;
+ 
+-	sdio_claim_host(bus->sdiodev->func[1]);
++	sdiodev = bus->sdiodev;
++	sdio_claim_host(sdiodev->func[1]);
+ 
+ 	pr_debug("F1 signature read @0x18000000=0x%4x\n",
+-		 brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL));
++		 brcmf_sdiod_regrl(sdiodev, SI_ENUM_BASE, NULL));
+ 
+ 	/*
+ 	 * Force PLL off until brcmf_chip_attach()
+ 	 * programs PLL control regs
+ 	 */
+ 
+-	brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
++	brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ 			  BRCMF_INIT_CLKCTL1, &err);
+ 	if (!err)
+-		clkctl = brcmf_sdiod_regrb(bus->sdiodev,
++		clkctl = brcmf_sdiod_regrb(sdiodev,
+ 					   SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ 
+ 	if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {
+@@ -3803,50 +3805,77 @@ brcmf_sdio_probe_attach(struct brcmf_sdi
+ 		goto fail;
+ 	}
+ 
+-	bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops);
++	bus->ci = brcmf_chip_attach(sdiodev, &brcmf_sdio_buscore_ops);
+ 	if (IS_ERR(bus->ci)) {
+ 		brcmf_err("brcmf_chip_attach failed!\n");
+ 		bus->ci = NULL;
+ 		goto fail;
+ 	}
++	sdiodev->pdata = brcmf_get_module_param(sdiodev->dev,
++						   BRCMF_BUSTYPE_SDIO,
++						   bus->ci->chip,
++						   bus->ci->chiprev);
++	/* platform specific configuration:
++	 *   alignments must be at least 4 bytes for ADMA
++	 */
++	bus->head_align = ALIGNMENT;
++	bus->sgentry_align = ALIGNMENT;
++	if (sdiodev->pdata) {
++		if (sdiodev->pdata->sd_head_align > ALIGNMENT)
++			bus->head_align = sdiodev->pdata->sd_head_align;
++		if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT)
++			bus->sgentry_align = sdiodev->pdata->sd_sgentry_align;
++	}
++	/* allocate scatter-gather table. sg support
++	 * will be disabled upon allocation failure.
++	 */
++	brcmf_sdiod_sgtable_alloc(sdiodev);
++
++#ifdef CONFIG_PM_SLEEP
++	/* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ
++	 * is true or when platform data OOB irq is true).
++	 */
++	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 && sdiodev->pdata->oob_irq_supported)))
++		sdiodev->bus_if->wowl_supported = true;
++#endif
+ 
+ 	if (brcmf_sdio_kso_init(bus)) {
+ 		brcmf_err("error enabling KSO\n");
+ 		goto fail;
+ 	}
+ 
+-	if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength))
+-		drivestrength = bus->sdiodev->pdata->drive_strength;
++	if ((sdiodev->pdata) && (sdiodev->pdata->drive_strength))
++		drivestrength = sdiodev->pdata->drive_strength;
+ 	else
+ 		drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
+-	brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);
++	brcmf_sdio_drivestrengthinit(sdiodev, bus->ci, drivestrength);
+ 
+ 	/* 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);
++	reg_val = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, &err);
+ 	if (err)
+ 		goto fail;
+ 
+ 	reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET;
+ 
+-	brcmf_sdiod_regwb(bus->sdiodev,
+-			  SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
++	brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
+ 	if (err)
+ 		goto fail;
+ 
+ 	/* set PMUControl so a backplane reset does PMU state reload */
+ 	reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, pmucontrol);
+-	reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err);
++	reg_val = brcmf_sdiod_regrl(sdiodev, reg_addr, &err);
+ 	if (err)
+ 		goto fail;
+ 
+ 	reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT);
+ 
+-	brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err);
++	brcmf_sdiod_regwl(sdiodev, reg_addr, reg_val, &err);
+ 	if (err)
+ 		goto fail;
+ 
+-	sdio_release_host(bus->sdiodev->func[1]);
++	sdio_release_host(sdiodev->func[1]);
+ 
+ 	brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
+ 
+@@ -3867,7 +3896,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdi
+ 	return true;
+ 
+ fail:
+-	sdio_release_host(bus->sdiodev->func[1]);
++	sdio_release_host(sdiodev->func[1]);
+ 	return false;
+ }
+ 
+@@ -4045,18 +4074,6 @@ struct brcmf_sdio *brcmf_sdio_probe(stru
+ 	bus->txminmax = BRCMF_TXMINMAX;
+ 	bus->tx_seq = SDPCM_SEQ_WRAP - 1;
+ 
+-	/* platform specific configuration:
+-	 *   alignments must be at least 4 bytes for ADMA
+-	 */
+-	bus->head_align = ALIGNMENT;
+-	bus->sgentry_align = ALIGNMENT;
+-	if (sdiodev->pdata) {
+-		if (sdiodev->pdata->sd_head_align > ALIGNMENT)
+-			bus->head_align = sdiodev->pdata->sd_head_align;
+-		if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT)
+-			bus->sgentry_align = sdiodev->pdata->sd_sgentry_align;
+-	}
+-
+ 	/* single-threaded workqueue */
+ 	wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM,
+ 				     dev_name(&sdiodev->func[1]->dev));
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
+@@ -184,7 +184,7 @@ struct brcmf_sdio_dev {
+ 	struct brcmf_sdio *bus;
+ 	struct device *dev;
+ 	struct brcmf_bus *bus_if;
+-	struct brcmfmac_sdio_platform_data *pdata;
++	struct brcmfmac_sdio_pd *pdata;
+ 	bool oob_irq_requested;
+ 	bool irq_en;			/* irq enable flags */
+ 	spinlock_t irq_en_lock;
+--- a/include/linux/platform_data/brcmfmac-sdio.h
++++ /dev/null
+@@ -1,135 +0,0 @@
+-/*
+- * Copyright (c) 2013 Broadcom Corporation
+- *
+- * Permission to use, copy, modify, and/or distribute this software for any
+- * purpose with or without fee is hereby granted, provided that the above
+- * copyright notice and this permission notice appear in all copies.
+- *
+- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+- */
+-
+-#ifndef _LINUX_BRCMFMAC_PLATFORM_H
+-#define _LINUX_BRCMFMAC_PLATFORM_H
+-
+-/*
+- * Platform specific driver functions and data. Through the platform specific
+- * device data functions can be provided to help the brcmfmac driver to
+- * operate with the device in combination with the used platform.
+- *
+- * Use the platform data in the following (similar) way:
+- *
+- *
+-#include <brcmfmac_platform.h>
+-
+-
+-static void brcmfmac_power_on(void)
+-{
+-}
+-
+-static void brcmfmac_power_off(void)
+-{
+-}
+-
+-static void brcmfmac_reset(void)
+-{
+-}
+-
+-static struct brcmfmac_sdio_platform_data brcmfmac_sdio_pdata = {
+-	.power_on		= brcmfmac_power_on,
+-	.power_off		= brcmfmac_power_off,
+-	.reset			= brcmfmac_reset
+-};
+-
+-static struct platform_device brcmfmac_device = {
+-	.name			= BRCMFMAC_SDIO_PDATA_NAME,
+-	.id			= PLATFORM_DEVID_NONE,
+-	.dev.platform_data	= &brcmfmac_sdio_pdata
+-};
+-
+-void __init brcmfmac_init_pdata(void)
+-{
+-	brcmfmac_sdio_pdata.oob_irq_supported = true;
+-	brcmfmac_sdio_pdata.oob_irq_nr = gpio_to_irq(GPIO_BRCMF_SDIO_OOB);
+-	brcmfmac_sdio_pdata.oob_irq_flags = IORESOURCE_IRQ |
+-					    IORESOURCE_IRQ_HIGHLEVEL;
+-	platform_device_register(&brcmfmac_device);
+-}
+- *
+- *
+- * Note: the brcmfmac can be loaded as module or be statically built-in into
+- * the kernel. If built-in then do note that it uses module_init (and
+- * module_exit) routines which equal device_initcall. So if you intend to
+- * create a module with the platform specific data for the brcmfmac and have
+- * it built-in to the kernel then use a higher initcall then device_initcall
+- * (see init.h). If this is not done then brcmfmac will load without problems
+- * but will not pickup the platform data.
+- *
+- * When the driver does not "detect" platform driver data then it will continue
+- * without reporting anything and just assume there is no data needed. Which is
+- * probably true for most platforms.
+- *
+- * Explanation of the platform_data fields:
+- *
+- * drive_strength: is the preferred drive_strength to be used for the SDIO
+- * pins. If 0 then a default value will be used. This is the target drive
+- * strength, the exact drive strength which will be used depends on the
+- * capabilities of the device.
+- *
+- * oob_irq_supported: does the board have support for OOB interrupts. SDIO
+- * in-band interrupts are relatively slow and for having less overhead on
+- * interrupt processing an out of band interrupt can be used. If the HW
+- * supports this then enable this by setting this field to true and configure
+- * the oob related fields.
+- *
+- * oob_irq_nr, oob_irq_flags: the OOB interrupt information. The values are
+- * used for registering the irq using request_irq function.
+- *
+- * broken_sg_support: flag for broken sg list support of SDIO host controller.
+- * Set this to true if the SDIO host controller has higher align requirement
+- * than 32 bytes for each scatterlist item.
+- *
+- * sd_head_align: alignment requirement for start of data buffer
+- *
+- * sd_sgentry_align: length alignment requirement for each sg entry
+- *
+- * power_on: This function is called by the brcmfmac when the module gets
+- * loaded. This can be particularly useful for low power devices. The platform
+- * spcific routine may for example decide to power up the complete device.
+- * If there is no use-case for this function then provide NULL.
+- *
+- * power_off: This function is called by the brcmfmac when the module gets
+- * unloaded. At this point the device can be powered down or otherwise be reset.
+- * So if an actual power_off is not supported but reset is then reset the device
+- * when this function gets called. This can be particularly useful for low power
+- * devices. If there is no use-case for this function (either power-down or
+- * reset) then provide NULL.
+- *
+- * reset: This function can get called if the device communication broke down.
+- * This functionality is particularly useful in case of SDIO type devices. It is
+- * possible to reset a dongle via sdio data interface, but it requires that
+- * this is fully functional. This function is chip/module specific and this
+- * function should return only after the complete reset has completed.
+- */
+-
+-#define BRCMFMAC_SDIO_PDATA_NAME	"brcmfmac_sdio"
+-
+-struct brcmfmac_sdio_platform_data {
+-	unsigned int drive_strength;
+-	bool oob_irq_supported;
+-	unsigned int oob_irq_nr;
+-	unsigned long oob_irq_flags;
+-	bool broken_sg_support;
+-	unsigned short sd_head_align;
+-	unsigned short sd_sgentry_align;
+-	void (*power_on)(void);
+-	void (*power_off)(void);
+-	void (*reset)(void);
+-};
+-
+-#endif /* _LINUX_BRCMFMAC_PLATFORM_H */
+--- /dev/null
++++ b/include/linux/platform_data/brcmfmac.h
+@@ -0,0 +1,185 @@
++/*
++ * Copyright (c) 201 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 _LINUX_BRCMFMAC_PLATFORM_H
++#define _LINUX_BRCMFMAC_PLATFORM_H
++
++
++#define BRCMFMAC_PDATA_NAME		"brcmfmac"
++
++#define BRCMFMAC_COUNTRY_BUF_SZ		4
++
++
++/*
++ * Platform specific driver functions and data. Through the platform specific
++ * device data functions and data can be provided to help the brcmfmac driver to
++ * operate with the device in combination with the used platform.
++ */
++
++
++/**
++ * Note: the brcmfmac can be loaded as module or be statically built-in into
++ * the kernel. If built-in then do note that it uses module_init (and
++ * module_exit) routines which equal device_initcall. So if you intend to
++ * create a module with the platform specific data for the brcmfmac and have
++ * it built-in to the kernel then use a higher initcall then device_initcall
++ * (see init.h). If this is not done then brcmfmac will load without problems
++ * but will not pickup the platform data.
++ *
++ * When the driver does not "detect" platform driver data then it will continue
++ * without reporting anything and just assume there is no data needed. Which is
++ * probably true for most platforms.
++ */
++
++/**
++ * enum brcmf_bus_type - Bus type identifier. Currently SDIO, USB and PCIE are
++ *			 supported.
++ */
++enum brcmf_bus_type {
++	BRCMF_BUSTYPE_SDIO,
++	BRCMF_BUSTYPE_USB,
++	BRCMF_BUSTYPE_PCIE
++};
++
++
++/**
++ * struct brcmfmac_sdio_pd - SDIO Device specific platform data.
++ *
++ * @txglomsz:		SDIO txglom size. Use 0 if default of driver is to be
++ *			used.
++ * @drive_strength:	is the preferred drive_strength to be used for the SDIO
++ *			pins. If 0 then a default value will be used. This is
++ *			the target drive strength, the exact drive strength
++ *			which will be used depends on the capabilities of the
++ *			device.
++ * @oob_irq_supported:	does the board have support for OOB interrupts. SDIO
++ *			in-band interrupts are relatively slow and for having
++ *			less overhead on interrupt processing an out of band
++ *			interrupt can be used. If the HW supports this then
++ *			enable this by setting this field to true and configure
++ *			the oob related fields.
++ * @oob_irq_nr,
++ * @oob_irq_flags:	the OOB interrupt information. The values are used for
++ *			registering the irq using request_irq function.
++ * @broken_sg_support:	flag for broken sg list support of SDIO host controller.
++ *			Set this to true if the SDIO host controller has higher
++ *			align requirement than 32 bytes for each scatterlist
++ *			item.
++ * @sd_head_align:	alignment requirement for start of data buffer.
++ * @sd_sgentry_align:	length alignment requirement for each sg entry.
++ * @reset:		This function can get called if the device communication
++ *			broke down. This functionality is particularly useful in
++ *			case of SDIO type devices. It is possible to reset a
++ *			dongle via sdio data interface, but it requires that
++ *			this is fully functional. This function is chip/module
++ *			specific and this function should return only after the
++ *			complete reset has completed.
++ */
++struct brcmfmac_sdio_pd {
++	int		txglomsz;
++	unsigned int	drive_strength;
++	bool		oob_irq_supported;
++	unsigned int	oob_irq_nr;
++	unsigned long	oob_irq_flags;
++	bool		broken_sg_support;
++	unsigned short	sd_head_align;
++	unsigned short	sd_sgentry_align;
++	void		(*reset)(void);
++};
++
++/**
++ * struct brcmfmac_pd_cc_entry - Struct for translating user space country code
++ *				 (iso3166) to firmware country code and
++ *				 revision.
++ *
++ * @iso3166:	iso3166 alpha 2 country code string.
++ * @cc:		firmware country code string.
++ * @rev:	firmware country code revision.
++ */
++struct brcmfmac_pd_cc_entry {
++	char	iso3166[BRCMFMAC_COUNTRY_BUF_SZ];
++	char	cc[BRCMFMAC_COUNTRY_BUF_SZ];
++	s32	rev;
++};
++
++/**
++ * struct brcmfmac_pd_cc - Struct for translating country codes as set by user
++ *			   space to a country code and rev which can be used by
++ *			   firmware.
++ *
++ * @table_size:	number of entries in table (> 0)
++ * @table:	array of 1 or more elements with translation information.
++ */
++struct brcmfmac_pd_cc {
++	int				table_size;
++	struct brcmfmac_pd_cc_entry	table[0];
++};
++
++/**
++ * struct brcmfmac_pd_device - Device specific platform data. (id/rev/bus_type)
++ *			       is the unique identifier of the device.
++ *
++ * @id:			ID of the device for which this data is. In case of SDIO
++ *			or PCIE this is the chipid as identified by chip.c In
++ *			case of USB this is the chipid as identified by the
++ *			device query.
++ * @rev:		chip revision, see id.
++ * @bus_type:		The type of bus. Some chipid/rev exist for different bus
++ *			types. Each bus type has its own set of settings.
++ * @feature_disable:	Bitmask of features to disable (override), See feature.c
++ *			in brcmfmac for details.
++ * @country_codes:	If available, pointer to struct for translating country
++ *			codes.
++ * @bus:		Bus specific (union) device settings. Currently only
++ *			SDIO.
++ */
++struct brcmfmac_pd_device {
++	unsigned int		id;
++	unsigned int		rev;
++	enum brcmf_bus_type	bus_type;
++	unsigned int		feature_disable;
++	struct brcmfmac_pd_cc	*country_codes;
++	union {
++		struct brcmfmac_sdio_pd sdio;
++	} bus;
++};
++
++/**
++ * struct brcmfmac_platform_data - BRCMFMAC specific platform data.
++ *
++ * @power_on:	This function is called by the brcmfmac driver when the module
++ *		gets loaded. This can be particularly useful for low power
++ *		devices. The platform spcific routine may for example decide to
++ *		power up the complete device. If there is no use-case for this
++ *		function then provide NULL.
++ * @power_off:	This function is called by the brcmfmac when the module gets
++ *		unloaded. At this point the devices can be powered down or
++ *		otherwise be reset. So if an actual power_off is not supported
++ *		but reset is supported by the devices then reset the devices
++ *		when this function gets called. This can be particularly useful
++ *		for low power devices. If there is no use-case for this
++ *		function then provide NULL.
++ */
++struct brcmfmac_platform_data {
++	void	(*power_on)(void);
++	void	(*power_off)(void);
++	char	*fw_alternative_path;
++	int	device_count;
++	struct brcmfmac_pd_device devices[0];
++};
++
++
++#endif /* _LINUX_BRCMFMAC_PLATFORM_H */
diff --git a/package/kernel/mac80211/patches/344-0018-brcmfmac-merge-platform-data-and-module-paramaters.patch b/package/kernel/mac80211/patches/344-0018-brcmfmac-merge-platform-data-and-module-paramaters.patch
new file mode 100644
index 0000000000..34341d7f18
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0018-brcmfmac-merge-platform-data-and-module-paramaters.patch
@@ -0,0 +1,607 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:27:08 +0100
+Subject: [PATCH] brcmfmac: merge platform data and module paramaters
+
+Merge module parameters and platform data in one struct. This is the
+last step to move to the new platform data per device. Now parameters
+of platform data will be merged with module parameters per device.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+@@ -109,8 +109,8 @@ int brcmf_sdiod_intr_register(struct brc
+ 	u32 addr, gpiocontrol;
+ 	unsigned long flags;
+ 
+-	pdata = sdiodev->pdata;
+-	if ((pdata) && (pdata->oob_irq_supported)) {
++	pdata = &sdiodev->settings->bus.sdio;
++	if (pdata->oob_irq_supported) {
+ 		brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n",
+ 			  pdata->oob_irq_nr);
+ 		ret = request_irq(pdata->oob_irq_nr, brcmf_sdiod_oob_irqhandler,
+@@ -177,8 +177,8 @@ int brcmf_sdiod_intr_unregister(struct b
+ 
+ 	brcmf_dbg(SDIO, "Entering\n");
+ 
+-	pdata = sdiodev->pdata;
+-	if ((pdata) && (pdata->oob_irq_supported)) {
++	pdata = &sdiodev->settings->bus.sdio;
++	if (pdata->oob_irq_supported) {
+ 		sdio_claim_host(sdiodev->func[1]);
+ 		brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
+ 		brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
+@@ -522,7 +522,7 @@ static int brcmf_sdiod_sglist_rw(struct
+ 	target_list = pktlist;
+ 	/* for host with broken sg support, prepare a page aligned list */
+ 	__skb_queue_head_init(&local_list);
+-	if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) {
++	if (!write && sdiodev->settings->bus.sdio.broken_sg_support) {
+ 		req_sz = 0;
+ 		skb_queue_walk(pktlist, pkt_next)
+ 			req_sz += pkt_next->len;
+@@ -629,7 +629,7 @@ static int brcmf_sdiod_sglist_rw(struct
+ 		}
+ 	}
+ 
+-	if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) {
++	if (!write && sdiodev->settings->bus.sdio.broken_sg_support) {
+ 		local_pkt_next = local_list.next;
+ 		orig_offset = 0;
+ 		skb_queue_walk(pktlist, pkt_next) {
+@@ -900,7 +900,7 @@ void brcmf_sdiod_sgtable_alloc(struct br
+ 		return;
+ 
+ 	nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE,
+-		      sdiodev->bus_if->drvr->settings->sdiod_txglomsz);
++		      sdiodev->settings->bus.sdio.txglomsz);
+ 	nents += (nents >> 4) + 1;
+ 
+ 	WARN_ON(nents > sdiodev->max_segment_count);
+@@ -912,7 +912,7 @@ void brcmf_sdiod_sgtable_alloc(struct br
+ 		sdiodev->sg_support = false;
+ 	}
+ 
+-	sdiodev->txglomsz = sdiodev->bus_if->drvr->settings->sdiod_txglomsz;
++	sdiodev->txglomsz = sdiodev->settings->bus.sdio.txglomsz;
+ }
+ 
+ #ifdef CONFIG_PM_SLEEP
+@@ -1246,8 +1246,8 @@ static int brcmf_ops_sdio_suspend(struct
+ 
+ 	sdio_flags = MMC_PM_KEEP_POWER;
+ 	if (sdiodev->wowl_enabled) {
+-		if (sdiodev->pdata->oob_irq_supported)
+-			enable_irq_wake(sdiodev->pdata->oob_irq_nr);
++		if (sdiodev->settings->bus.sdio.oob_irq_supported)
++			enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
+ 		else
+ 			sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
+ 	}
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+@@ -43,6 +43,8 @@ enum brcmf_bus_protocol_type {
+ 	BRCMF_PROTO_MSGBUF
+ };
+ 
++struct brcmf_mp_device;
++
+ struct brcmf_bus_dcmd {
+ 	char *name;
+ 	char *param;
+@@ -217,7 +219,7 @@ bool brcmf_c_prec_enq(struct device *dev
+ void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp);
+ 
+ /* Indication from bus module regarding presence/insertion of dongle. */
+-int brcmf_attach(struct device *dev);
++int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings);
+ /* Indication from bus module regarding removal/absence of dongle */
+ void brcmf_detach(struct device *dev);
+ /* Indication from bus module that dongle should be reset */
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+@@ -243,14 +243,35 @@ static void brcmf_mp_attach(void)
+ 	}
+ }
+ 
+-struct brcmfmac_sdio_pd *brcmf_get_module_param(struct device *dev,
+-						enum brcmf_bus_type bus_type,
+-						u32 chip, u32 chiprev)
++struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
++					       enum brcmf_bus_type bus_type,
++					       u32 chip, u32 chiprev)
+ {
+-	struct brcmfmac_sdio_pd *pdata;
++	struct brcmf_mp_device *settings;
+ 	struct brcmfmac_pd_device *device_pd;
++	bool found;
+ 	int i;
+ 
++	brcmf_dbg(INFO, "Enter, bus=%d, chip=%d, rev=%d\n", bus_type, chip,
++		  chiprev);
++	settings = kzalloc(sizeof(*settings), GFP_ATOMIC);
++	if (!settings)
++		return NULL;
++
++	/* start by using the module paramaters */
++	settings->p2p_enable = !!brcmf_p2p_enable;
++	settings->feature_disable = brcmf_feature_disable;
++	settings->fcmode = brcmf_fcmode;
++	settings->roamoff = !!brcmf_roamoff;
++#ifdef DEBUG
++	settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;
++#endif
++
++	if (bus_type == BRCMF_BUSTYPE_SDIO)
++		settings->bus.sdio.txglomsz = brcmf_sdiod_txglomsz;
++
++	/* See if there is any device specific platform data configured */
++	found = false;
+ 	if (brcmfmac_pdata) {
+ 		for (i = 0; i < brcmfmac_pdata->device_count; i++) {
+ 			device_pd = &brcmfmac_pdata->devices[i];
+@@ -259,38 +280,29 @@ struct brcmfmac_sdio_pd *brcmf_get_modul
+ 			    ((device_pd->rev == chiprev) ||
+ 			     (device_pd->rev == -1))) {
+ 				brcmf_dbg(INFO, "Platform data for device found\n");
++				settings->country_codes =
++						device_pd->country_codes;
+ 				if (device_pd->bus_type == BRCMF_BUSTYPE_SDIO)
+-					return &device_pd->bus.sdio;
++					memcpy(&settings->bus.sdio,
++					       &device_pd->bus.sdio,
++					       sizeof(settings->bus.sdio));
++				found = true;
+ 				break;
+ 			}
+ 		}
+ 	}
+-	pdata = NULL;
+-	brcmf_of_probe(dev, &pdata);
+-
+-	return pdata;
+-}
+-
+-int brcmf_mp_device_attach(struct brcmf_pub *drvr)
+-{
+-	drvr->settings = kzalloc(sizeof(*drvr->settings), GFP_ATOMIC);
+-	if (!drvr->settings)
+-		return -ENOMEM;
+-
+-	drvr->settings->sdiod_txglomsz = brcmf_sdiod_txglomsz;
+-	drvr->settings->p2p_enable = !!brcmf_p2p_enable;
+-	drvr->settings->feature_disable = brcmf_feature_disable;
+-	drvr->settings->fcmode = brcmf_fcmode;
+-	drvr->settings->roamoff = !!brcmf_roamoff;
+-#ifdef DEBUG
+-	drvr->settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;
+-#endif
+-	return 0;
++	if ((bus_type == BRCMF_BUSTYPE_SDIO) && (!found)) {
++		/* No platform data for this device. In case of SDIO try OF
++		 * (Open Firwmare) Device Tree.
++		 */
++		brcmf_of_probe(dev, &settings->bus.sdio);
++	}
++	return settings;
+ }
+ 
+-void brcmf_mp_device_detach(struct brcmf_pub *drvr)
++void brcmf_release_module_param(struct brcmf_mp_device *module_param)
+ {
+-	kfree(drvr->settings);
++	kfree(module_param);
+ }
+ 
+ static int __init brcmf_common_pd_probe(struct platform_device *pdev)
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
+@@ -45,41 +45,30 @@ extern struct brcmf_mp_global_t brcmf_mp
+ /**
+  * struct brcmf_mp_device - Device module paramaters.
+  *
+- * @sdiod_txglomsz: SDIO txglom size.
+- * @joinboost_5g_rssi: 5g rssi booost for preferred join selection.
+  * @p2p_enable: Legacy P2P0 enable (old wpa_supplicant).
+  * @feature_disable: Feature_disable bitmask.
+  * @fcmode: FWS flow control.
+  * @roamoff: Firmware roaming off?
++ * @ignore_probe_fail: Ignore probe failure.
+  * @country_codes: If available, pointer to struct for translating country codes
++ * @bus: Bus specific platform data. Only SDIO at the mmoment.
+  */
+ struct brcmf_mp_device {
+-	int	sdiod_txglomsz;
+-	int	joinboost_5g_rssi;
+-	bool	p2p_enable;
+-	int	feature_disable;
+-	int	fcmode;
+-	bool	roamoff;
+-	bool	ignore_probe_fail;
++	bool		p2p_enable;
++	unsigned int	feature_disable;
++	int		fcmode;
++	bool		roamoff;
++	bool		ignore_probe_fail;
+ 	struct brcmfmac_pd_cc *country_codes;
++	union {
++		struct brcmfmac_sdio_pd sdio;
++	} bus;
+ };
+ 
+-struct brcmfmac_sdio_pd *brcmf_get_module_param(struct device *dev,
+-						enum brcmf_bus_type bus_type,
+-						u32 chip, u32 chiprev);
+-int brcmf_mp_device_attach(struct brcmf_pub *drvr);
+-void brcmf_mp_device_detach(struct brcmf_pub *drvr);
+-#ifdef DEBUG
+-static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr)
+-{
+-	return drvr->settings->ignore_probe_fail;
+-}
+-#else
+-static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr)
+-{
+-	return false;
+-}
+-#endif
++struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
++					       enum brcmf_bus_type bus_type,
++					       u32 chip, u32 chiprev);
++void brcmf_release_module_param(struct brcmf_mp_device *module_param);
+ 
+ /* Sets dongle media info (drv_version, mac address). */
+ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -1104,7 +1104,7 @@ static int brcmf_inet6addr_changed(struc
+ }
+ #endif
+ 
+-int brcmf_attach(struct device *dev)
++int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings)
+ {
+ 	struct brcmf_pub *drvr = NULL;
+ 	int ret = 0;
+@@ -1126,10 +1126,7 @@ int brcmf_attach(struct device *dev)
+ 	drvr->hdrlen = 0;
+ 	drvr->bus_if = dev_get_drvdata(dev);
+ 	drvr->bus_if->drvr = drvr;
+-
+-	/* Initialize device specific settings */
+-	if (brcmf_mp_device_attach(drvr))
+-		goto fail;
++	drvr->settings = settings;
+ 
+ 	/* attach debug facilities */
+ 	brcmf_debug_attach(drvr);
+@@ -1274,7 +1271,7 @@ fail:
+ 		brcmf_net_detach(p2p_ifp->ndev);
+ 	drvr->iflist[0] = NULL;
+ 	drvr->iflist[1] = NULL;
+-	if (brcmf_ignoring_probe_fail(drvr))
++	if (drvr->settings->ignore_probe_fail)
+ 		ret = 0;
+ 
+ 	return ret;
+@@ -1350,8 +1347,6 @@ void brcmf_detach(struct device *dev)
+ 
+ 	brcmf_proto_detach(drvr);
+ 
+-	brcmf_mp_device_detach(drvr);
+-
+ 	brcmf_debug_detach(drvr);
+ 	bus_if->drvr = NULL;
+ 	kfree(drvr);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
+@@ -23,7 +23,7 @@
+ #include "common.h"
+ #include "of.h"
+ 
+-void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd **sdio)
++void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio)
+ {
+ 	struct device_node *np = dev->of_node;
+ 	int irq;
+@@ -33,12 +33,8 @@ void brcmf_of_probe(struct device *dev,
+ 	if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac"))
+ 		return;
+ 
+-	*sdio = devm_kzalloc(dev, sizeof(*sdio), GFP_KERNEL);
+-	if (!(*sdio))
+-		return;
+-
+ 	if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0)
+-		(*sdio)->drive_strength = val;
++		sdio->drive_strength = val;
+ 
+ 	/* make sure there are interrupts defined in the node */
+ 	if (!of_find_property(np, "interrupts", NULL))
+@@ -51,7 +47,7 @@ void brcmf_of_probe(struct device *dev,
+ 	}
+ 	irqf = irqd_get_trigger_type(irq_get_irq_data(irq));
+ 
+-	(*sdio)->oob_irq_supported = true;
+-	(*sdio)->oob_irq_nr = irq;
+-	(*sdio)->oob_irq_flags = irqf;
++	sdio->oob_irq_supported = true;
++	sdio->oob_irq_nr = irq;
++	sdio->oob_irq_flags = irqf;
+ }
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
+@@ -14,10 +14,9 @@
+  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  */
+ #ifdef CONFIG_OF
+-void
+-brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd **sdio);
++void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio);
+ #else
+-static void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd **sdio)
++static void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio)
+ {
+ }
+ #endif /* CONFIG_OF */
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+@@ -37,6 +37,8 @@
+ #include "pcie.h"
+ #include "firmware.h"
+ #include "chip.h"
++#include "core.h"
++#include "common.h"
+ 
+ 
+ enum brcmf_pcie_state {
+@@ -266,6 +268,7 @@ struct brcmf_pciedev_info {
+ 	u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset);
+ 	void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ 			  u16 value);
++	struct brcmf_mp_device *settings;
+ };
+ 
+ struct brcmf_pcie_ringbuf {
+@@ -1525,16 +1528,16 @@ static void brcmf_pcie_release_resource(
+ }
+ 
+ 
+-static int brcmf_pcie_attach_bus(struct device *dev)
++static int brcmf_pcie_attach_bus(struct brcmf_pciedev_info *devinfo)
+ {
+ 	int ret;
+ 
+ 	/* Attach to the common driver interface */
+-	ret = brcmf_attach(dev);
++	ret = brcmf_attach(&devinfo->pdev->dev, devinfo->settings);
+ 	if (ret) {
+ 		brcmf_err("brcmf_attach failed\n");
+ 	} else {
+-		ret = brcmf_bus_start(dev);
++		ret = brcmf_bus_start(&devinfo->pdev->dev);
+ 		if (ret)
+ 			brcmf_err("dongle is not responding\n");
+ 	}
+@@ -1672,7 +1675,7 @@ static void brcmf_pcie_setup(struct devi
+ 	init_waitqueue_head(&devinfo->mbdata_resp_wait);
+ 
+ 	brcmf_pcie_intr_enable(devinfo);
+-	if (brcmf_pcie_attach_bus(bus->dev) == 0)
++	if (brcmf_pcie_attach_bus(devinfo) == 0)
+ 		return;
+ 
+ 	brcmf_pcie_bus_console_read(devinfo);
+@@ -1716,6 +1719,15 @@ brcmf_pcie_probe(struct pci_dev *pdev, c
+ 		goto fail;
+ 	}
+ 
++	devinfo->settings = brcmf_get_module_param(&devinfo->pdev->dev,
++						   BRCMF_BUSTYPE_PCIE,
++						   devinfo->ci->chip,
++						   devinfo->ci->chiprev);
++	if (!devinfo->settings) {
++		ret = -ENOMEM;
++		goto fail;
++	}
++
+ 	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+ 	if (!bus) {
+ 		ret = -ENOMEM;
+@@ -1760,6 +1772,8 @@ fail:
+ 	brcmf_pcie_release_resource(devinfo);
+ 	if (devinfo->ci)
+ 		brcmf_chip_detach(devinfo->ci);
++	if (devinfo->settings)
++		brcmf_release_module_param(devinfo->settings);
+ 	kfree(pcie_bus_dev);
+ 	kfree(devinfo);
+ 	return ret;
+@@ -1799,6 +1813,8 @@ brcmf_pcie_remove(struct pci_dev *pdev)
+ 
+ 	if (devinfo->ci)
+ 		brcmf_chip_detach(devinfo->ci);
++	if (devinfo->settings)
++		brcmf_release_module_param(devinfo->settings);
+ 
+ 	kfree(devinfo);
+ 	dev_set_drvdata(&pdev->dev, NULL);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+@@ -2442,15 +2442,17 @@ static void brcmf_sdio_bus_stop(struct d
+ 
+ static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus)
+ {
++	struct brcmf_sdio_dev *sdiodev;
+ 	unsigned long flags;
+ 
+-	if (bus->sdiodev->oob_irq_requested) {
+-		spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags);
+-		if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) {
+-			enable_irq(bus->sdiodev->pdata->oob_irq_nr);
+-			bus->sdiodev->irq_en = true;
++	sdiodev = bus->sdiodev;
++	if (sdiodev->oob_irq_requested) {
++		spin_lock_irqsave(&sdiodev->irq_en_lock, flags);
++		if (!sdiodev->irq_en && !atomic_read(&bus->ipend)) {
++			enable_irq(sdiodev->settings->bus.sdio.oob_irq_nr);
++			sdiodev->irq_en = true;
+ 		}
+-		spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags);
++		spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags);
+ 	}
+ }
+ 
+@@ -3394,9 +3396,7 @@ static int brcmf_sdio_bus_preinit(struct
+ 					   sizeof(u32));
+ 	} else {
+ 		/* otherwise, set txglomalign */
+-		value = 4;
+-		if (sdiodev->pdata)
+-			value = sdiodev->pdata->sd_sgentry_align;
++		value = sdiodev->settings->bus.sdio.sd_sgentry_align;
+ 		/* SDIO ADMA requires at least 32 bit alignment */
+ 		value = max_t(u32, value, 4);
+ 		err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value,
+@@ -3811,21 +3811,25 @@ brcmf_sdio_probe_attach(struct brcmf_sdi
+ 		bus->ci = NULL;
+ 		goto fail;
+ 	}
+-	sdiodev->pdata = brcmf_get_module_param(sdiodev->dev,
++	sdiodev->settings = brcmf_get_module_param(sdiodev->dev,
+ 						   BRCMF_BUSTYPE_SDIO,
+ 						   bus->ci->chip,
+ 						   bus->ci->chiprev);
++	if (!sdiodev->settings) {
++		brcmf_err("Failed to get device parameters\n");
++		goto fail;
++	}
+ 	/* platform specific configuration:
+ 	 *   alignments must be at least 4 bytes for ADMA
+ 	 */
+ 	bus->head_align = ALIGNMENT;
+ 	bus->sgentry_align = ALIGNMENT;
+-	if (sdiodev->pdata) {
+-		if (sdiodev->pdata->sd_head_align > ALIGNMENT)
+-			bus->head_align = sdiodev->pdata->sd_head_align;
+-		if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT)
+-			bus->sgentry_align = sdiodev->pdata->sd_sgentry_align;
+-	}
++	if (sdiodev->settings->bus.sdio.sd_head_align > ALIGNMENT)
++		bus->head_align = sdiodev->settings->bus.sdio.sd_head_align;
++	if (sdiodev->settings->bus.sdio.sd_sgentry_align > ALIGNMENT)
++		bus->sgentry_align =
++				sdiodev->settings->bus.sdio.sd_sgentry_align;
++
+ 	/* allocate scatter-gather table. sg support
+ 	 * will be disabled upon allocation failure.
+ 	 */
+@@ -3837,7 +3841,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdi
+ 	 */
+ 	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 && sdiodev->pdata->oob_irq_supported)))
++	     (sdiodev->settings->bus.sdio.oob_irq_supported)))
+ 		sdiodev->bus_if->wowl_supported = true;
+ #endif
+ 
+@@ -3846,8 +3850,8 @@ brcmf_sdio_probe_attach(struct brcmf_sdi
+ 		goto fail;
+ 	}
+ 
+-	if ((sdiodev->pdata) && (sdiodev->pdata->drive_strength))
+-		drivestrength = sdiodev->pdata->drive_strength;
++	if (sdiodev->settings->bus.sdio.drive_strength)
++		drivestrength = sdiodev->settings->bus.sdio.drive_strength;
+ 	else
+ 		drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
+ 	brcmf_sdio_drivestrengthinit(sdiodev, bus->ci, drivestrength);
+@@ -4124,7 +4128,7 @@ struct brcmf_sdio *brcmf_sdio_probe(stru
+ 	bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
+ 
+ 	/* Attach to the common layer, reserve hdr space */
+-	ret = brcmf_attach(bus->sdiodev->dev);
++	ret = brcmf_attach(bus->sdiodev->dev, bus->sdiodev->settings);
+ 	if (ret != 0) {
+ 		brcmf_err("brcmf_attach failed\n");
+ 		goto fail;
+@@ -4228,6 +4232,8 @@ void brcmf_sdio_remove(struct brcmf_sdio
+ 			}
+ 			brcmf_chip_detach(bus->ci);
+ 		}
++		if (bus->sdiodev->settings)
++			brcmf_release_module_param(bus->sdiodev->settings);
+ 
+ 		kfree(bus->rxbuf);
+ 		kfree(bus->hdrbuf);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
+@@ -184,7 +184,7 @@ struct brcmf_sdio_dev {
+ 	struct brcmf_sdio *bus;
+ 	struct device *dev;
+ 	struct brcmf_bus *bus_if;
+-	struct brcmfmac_sdio_pd *pdata;
++	struct brcmf_mp_device *settings;
+ 	bool oob_irq_requested;
+ 	bool irq_en;			/* irq enable flags */
+ 	spinlock_t irq_en_lock;
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+@@ -27,6 +27,8 @@
+ #include "debug.h"
+ #include "firmware.h"
+ #include "usb.h"
++#include "core.h"
++#include "common.h"
+ 
+ 
+ #define IOCTL_RESP_TIMEOUT		msecs_to_jiffies(2000)
+@@ -171,6 +173,7 @@ struct brcmf_usbdev_info {
+ 	struct urb *bulk_urb; /* used for FW download */
+ 
+ 	bool wowl_enabled;
++	struct brcmf_mp_device *settings;
+ };
+ 
+ static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
+@@ -1027,6 +1030,9 @@ static void brcmf_usb_detach(struct brcm
+ 
+ 	kfree(devinfo->tx_reqs);
+ 	kfree(devinfo->rx_reqs);
++
++	if (devinfo->settings)
++		brcmf_release_module_param(devinfo->settings);
+ }
+ 
+ 
+@@ -1136,7 +1142,7 @@ static int brcmf_usb_bus_setup(struct br
+ 	int ret;
+ 
+ 	/* Attach to the common driver interface */
+-	ret = brcmf_attach(devinfo->dev);
++	ret = brcmf_attach(devinfo->dev, devinfo->settings);
+ 	if (ret) {
+ 		brcmf_err("brcmf_attach failed\n");
+ 		return ret;
+@@ -1223,6 +1229,14 @@ static int brcmf_usb_probe_cb(struct brc
+ 	bus->wowl_supported = true;
+ #endif
+ 
++	devinfo->settings = brcmf_get_module_param(bus->dev, BRCMF_BUSTYPE_USB,
++						   bus_pub->devid,
++						   bus_pub->chiprev);
++	if (!devinfo->settings) {
++		ret = -ENOMEM;
++		goto fail;
++	}
++
+ 	if (!brcmf_usb_dlneeded(devinfo)) {
+ 		ret = brcmf_usb_bus_setup(devinfo);
+ 		if (ret)
diff --git a/package/kernel/mac80211/patches/344-0019-brcmfmac-integrate-add_keyext-in-add_key.patch b/package/kernel/mac80211/patches/344-0019-brcmfmac-integrate-add_keyext-in-add_key.patch
new file mode 100644
index 0000000000..eb680fccfc
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0019-brcmfmac-integrate-add_keyext-in-add_key.patch
@@ -0,0 +1,227 @@
+From: Hante Meuleman <hante.meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:27:09 +0100
+Subject: [PATCH] brcmfmac: integrate add_keyext in add_key
+
+brcmf_add_keyext is called when a key is configured for a specific
+mac address. This function is very similar to the calling function
+brcmf_add_key. Integrate this function and also use existing del_key
+function in case key is to be cleared.
+
+Reviewed-by: Arend Van Spriel <arend.van@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <franky.lin@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Signed-off-by: Hante Meuleman <hante.meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -2073,84 +2073,34 @@ done:
+ }
+ 
+ static s32
+-brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
+-	      u8 key_idx, const u8 *mac_addr, struct key_params *params)
++brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
++		       u8 key_idx, bool pairwise, const u8 *mac_addr)
+ {
+ 	struct brcmf_if *ifp = netdev_priv(ndev);
+ 	struct brcmf_wsec_key key;
+ 	s32 err = 0;
+-	u8 keybuf[8];
++
++	brcmf_dbg(TRACE, "Enter\n");
++	if (!check_vif_up(ifp->vif))
++		return -EIO;
++
++	if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
++		/* we ignore this key index in this case */
++		return -EINVAL;
++	}
+ 
+ 	memset(&key, 0, sizeof(key));
+-	key.index = (u32) key_idx;
+-	/* Instead of bcast for ea address for default wep keys,
+-		 driver needs it to be Null */
+-	if (!is_multicast_ether_addr(mac_addr))
+-		memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
+-	key.len = (u32) params->key_len;
+-	/* check for key index change */
+-	if (key.len == 0) {
+-		/* key delete */
+-		err = send_key_to_dongle(ifp, &key);
+-		if (err)
+-			brcmf_err("key delete error (%d)\n", err);
+-	} else {
+-		if (key.len > sizeof(key.data)) {
+-			brcmf_err("Invalid key length (%d)\n", key.len);
+-			return -EINVAL;
+-		}
+ 
+-		brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
+-		memcpy(key.data, params->key, key.len);
++	key.index = (u32)key_idx;
++	key.flags = BRCMF_PRIMARY_KEY;
++	key.algo = CRYPTO_ALGO_OFF;
+ 
+-		if (!brcmf_is_apmode(ifp->vif) &&
+-		    (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
+-			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));
+-		}
++	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
+ 
+-		/* if IW_ENCODE_EXT_RX_SEQ_VALID set */
+-		if (params->seq && params->seq_len == 6) {
+-			/* rx iv */
+-			u8 *ivptr;
+-			ivptr = (u8 *) params->seq;
+-			key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
+-			    (ivptr[3] << 8) | ivptr[2];
+-			key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
+-			key.iv_initialized = true;
+-		}
++	/* Set the new key/index */
++	err = send_key_to_dongle(ifp, &key);
+ 
+-		switch (params->cipher) {
+-		case WLAN_CIPHER_SUITE_WEP40:
+-			key.algo = CRYPTO_ALGO_WEP1;
+-			brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
+-			break;
+-		case WLAN_CIPHER_SUITE_WEP104:
+-			key.algo = CRYPTO_ALGO_WEP128;
+-			brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
+-			break;
+-		case WLAN_CIPHER_SUITE_TKIP:
+-			key.algo = CRYPTO_ALGO_TKIP;
+-			brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
+-			break;
+-		case WLAN_CIPHER_SUITE_AES_CMAC:
+-			key.algo = CRYPTO_ALGO_AES_CCM;
+-			brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
+-			break;
+-		case WLAN_CIPHER_SUITE_CCMP:
+-			key.algo = CRYPTO_ALGO_AES_CCM;
+-			brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
+-			break;
+-		default:
+-			brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
+-			return -EINVAL;
+-		}
+-		err = send_key_to_dongle(ifp, &key);
+-		if (err)
+-			brcmf_err("wsec_key error (%d)\n", err);
+-	}
++	brcmf_dbg(TRACE, "Exit\n");
+ 	return err;
+ }
+ 
+@@ -2163,8 +2113,9 @@ brcmf_cfg80211_add_key(struct wiphy *wip
+ 	struct brcmf_wsec_key *key;
+ 	s32 val;
+ 	s32 wsec;
+-	s32 err = 0;
++	s32 err;
+ 	u8 keybuf[8];
++	bool ext_key;
+ 
+ 	brcmf_dbg(TRACE, "Enter\n");
+ 	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
+@@ -2177,27 +2128,32 @@ brcmf_cfg80211_add_key(struct wiphy *wip
+ 		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);
+-	}
+-
+-	key = &ifp->vif->profile.key[key_idx];
+-	memset(key, 0, sizeof(*key));
++	if (params->key_len == 0)
++		return brcmf_cfg80211_del_key(wiphy, ndev, key_idx, pairwise,
++					      mac_addr);
+ 
+ 	if (params->key_len > sizeof(key->data)) {
+ 		brcmf_err("Too long key length (%u)\n", params->key_len);
+-		err = -EINVAL;
+-		goto done;
++		return -EINVAL;
++	}
++
++	ext_key = false;
++	if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
++	    (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
++		brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr);
++		ext_key = true;
+ 	}
++
++	key = &ifp->vif->profile.key[key_idx];
++	memset(key, 0, sizeof(*key));
++	if ((ext_key) && (!is_multicast_ether_addr(mac_addr)))
++		memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN);
+ 	key->len = params->key_len;
+ 	key->index = key_idx;
+-
+ 	memcpy(key->data, params->key, key->len);
++	if (!ext_key)
++		key->flags = BRCMF_PRIMARY_KEY;
+ 
+-	key->flags = BRCMF_PRIMARY_KEY;
+ 	switch (params->cipher) {
+ 	case WLAN_CIPHER_SUITE_WEP40:
+ 		key->algo = CRYPTO_ALGO_WEP1;
+@@ -2237,7 +2193,7 @@ brcmf_cfg80211_add_key(struct wiphy *wip
+ 	}
+ 
+ 	err = send_key_to_dongle(ifp, key);
+-	if (err)
++	if (ext_key || err)
+ 		goto done;
+ 
+ 	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
+@@ -2256,38 +2212,6 @@ done:
+ 	brcmf_dbg(TRACE, "Exit\n");
+ 	return err;
+ }
+-
+-static s32
+-brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+-		    u8 key_idx, bool pairwise, const u8 *mac_addr)
+-{
+-	struct brcmf_if *ifp = netdev_priv(ndev);
+-	struct brcmf_wsec_key key;
+-	s32 err = 0;
+-
+-	brcmf_dbg(TRACE, "Enter\n");
+-	if (!check_vif_up(ifp->vif))
+-		return -EIO;
+-
+-	if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
+-		/* we ignore this key index in this case */
+-		return -EINVAL;
+-	}
+-
+-	memset(&key, 0, sizeof(key));
+-
+-	key.index = (u32) key_idx;
+-	key.flags = BRCMF_PRIMARY_KEY;
+-	key.algo = CRYPTO_ALGO_OFF;
+-
+-	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
+-
+-	/* Set the new key/index */
+-	err = send_key_to_dongle(ifp, &key);
+-
+-	brcmf_dbg(TRACE, "Exit\n");
+-	return err;
+-}
+ 
+ static s32
+ brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
diff --git a/package/kernel/mac80211/patches/344-0020-brcmfmac-add-802.11w-management-frame-protection-sup.patch b/package/kernel/mac80211/patches/344-0020-brcmfmac-add-802.11w-management-frame-protection-sup.patch
new file mode 100644
index 0000000000..c20d40c049
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0020-brcmfmac-add-802.11w-management-frame-protection-sup.patch
@@ -0,0 +1,509 @@
+From: Hante Meuleman <hante.meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:27:10 +0100
+Subject: [PATCH] brcmfmac: add 802.11w management frame protection support
+
+Add full support for both AP and STA for management frame protection.
+
+Reviewed-by: Arend Van Spriel <arend.van@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <franky.lin@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Signed-off-by: Hante Meuleman <hante.meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -72,8 +72,13 @@
+ #define RSN_AKM_NONE			0	/* None (IBSS) */
+ #define RSN_AKM_UNSPECIFIED		1	/* Over 802.1x */
+ #define RSN_AKM_PSK			2	/* Pre-shared Key */
++#define RSN_AKM_SHA256_1X		5	/* SHA256, 802.1X */
++#define RSN_AKM_SHA256_PSK		6	/* SHA256, Pre-shared Key */
+ #define RSN_CAP_LEN			2	/* Length of RSN capabilities */
+-#define RSN_CAP_PTK_REPLAY_CNTR_MASK	0x000C
++#define RSN_CAP_PTK_REPLAY_CNTR_MASK	(BIT(2) | BIT(3))
++#define RSN_CAP_MFPR_MASK		BIT(6)
++#define RSN_CAP_MFPC_MASK		BIT(7)
++#define RSN_PMKID_COUNT_LEN		2
+ 
+ #define VNDR_IE_CMD_LEN			4	/* length of the set command
+ 						 * string :"add", "del" (+ NUL)
+@@ -211,12 +216,19 @@ static const struct ieee80211_regdomain
+ 		REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
+ };
+ 
+-static const u32 __wl_cipher_suites[] = {
++/* Note: brcmf_cipher_suites is an array of int defining which cipher suites
++ * are supported. A pointer to this array and the number of entries is passed
++ * on to upper layers. AES_CMAC defines whether or not the driver supports MFP.
++ * So the cipher suite AES_CMAC has to be the last one in the array, and when
++ * device does not support MFP then the number of suites will be decreased by 1
++ */
++static const u32 brcmf_cipher_suites[] = {
+ 	WLAN_CIPHER_SUITE_WEP40,
+ 	WLAN_CIPHER_SUITE_WEP104,
+ 	WLAN_CIPHER_SUITE_TKIP,
+ 	WLAN_CIPHER_SUITE_CCMP,
+-	WLAN_CIPHER_SUITE_AES_CMAC,
++	/* Keep as last entry: */
++	WLAN_CIPHER_SUITE_AES_CMAC
+ };
+ 
+ /* Vendor specific ie. id = 221, oui and type defines exact ie */
+@@ -1533,7 +1545,7 @@ static s32 brcmf_set_auth_type(struct ne
+ 
+ static s32
+ brcmf_set_wsec_mode(struct net_device *ndev,
+-		     struct cfg80211_connect_params *sme, bool mfp)
++		    struct cfg80211_connect_params *sme)
+ {
+ 	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
+ 	struct brcmf_cfg80211_security *sec;
+@@ -1592,10 +1604,7 @@ brcmf_set_wsec_mode(struct net_device *n
+ 	    sme->privacy)
+ 		pval = AES_ENABLED;
+ 
+-	if (mfp)
+-		wsec = pval | gval | MFP_CAPABLE;
+-	else
+-		wsec = pval | gval;
++	wsec = pval | gval;
+ 	err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
+ 	if (err) {
+ 		brcmf_err("error (%d)\n", err);
+@@ -1612,56 +1621,100 @@ brcmf_set_wsec_mode(struct net_device *n
+ static s32
+ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
+ {
+-	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
+-	struct brcmf_cfg80211_security *sec;
+-	s32 val = 0;
+-	s32 err = 0;
++	struct brcmf_if *ifp = netdev_priv(ndev);
++	s32 val;
++	s32 err;
++	const struct brcmf_tlv *rsn_ie;
++	const u8 *ie;
++	u32 ie_len;
++	u32 offset;
++	u16 rsn_cap;
++	u32 mfp;
++	u16 count;
+ 
+-	if (sme->crypto.n_akm_suites) {
+-		err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
+-					       "wpa_auth", &val);
+-		if (err) {
+-			brcmf_err("could not get wpa_auth (%d)\n", err);
+-			return err;
++	if (!sme->crypto.n_akm_suites)
++		return 0;
++
++	err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "wpa_auth", &val);
++	if (err) {
++		brcmf_err("could not get wpa_auth (%d)\n", err);
++		return err;
++	}
++	if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
++		switch (sme->crypto.akm_suites[0]) {
++		case WLAN_AKM_SUITE_8021X:
++			val = WPA_AUTH_UNSPECIFIED;
++			break;
++		case WLAN_AKM_SUITE_PSK:
++			val = WPA_AUTH_PSK;
++			break;
++		default:
++			brcmf_err("invalid cipher group (%d)\n",
++				  sme->crypto.cipher_group);
++			return -EINVAL;
+ 		}
+-		if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
+-			switch (sme->crypto.akm_suites[0]) {
+-			case WLAN_AKM_SUITE_8021X:
+-				val = WPA_AUTH_UNSPECIFIED;
+-				break;
+-			case WLAN_AKM_SUITE_PSK:
+-				val = WPA_AUTH_PSK;
+-				break;
+-			default:
+-				brcmf_err("invalid cipher group (%d)\n",
+-					  sme->crypto.cipher_group);
+-				return -EINVAL;
+-			}
+-		} else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+-			switch (sme->crypto.akm_suites[0]) {
+-			case WLAN_AKM_SUITE_8021X:
+-				val = WPA2_AUTH_UNSPECIFIED;
+-				break;
+-			case WLAN_AKM_SUITE_PSK:
+-				val = WPA2_AUTH_PSK;
+-				break;
+-			default:
+-				brcmf_err("invalid cipher group (%d)\n",
+-					  sme->crypto.cipher_group);
+-				return -EINVAL;
+-			}
++	} else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
++		switch (sme->crypto.akm_suites[0]) {
++		case WLAN_AKM_SUITE_8021X:
++			val = WPA2_AUTH_UNSPECIFIED;
++			break;
++		case WLAN_AKM_SUITE_8021X_SHA256:
++			val = WPA2_AUTH_1X_SHA256;
++			break;
++		case WLAN_AKM_SUITE_PSK_SHA256:
++			val = WPA2_AUTH_PSK_SHA256;
++			break;
++		case WLAN_AKM_SUITE_PSK:
++			val = WPA2_AUTH_PSK;
++			break;
++		default:
++			brcmf_err("invalid cipher group (%d)\n",
++				  sme->crypto.cipher_group);
++			return -EINVAL;
+ 		}
++	}
+ 
+-		brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
+-		err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
+-					       "wpa_auth", val);
+-		if (err) {
+-			brcmf_err("could not set wpa_auth (%d)\n", err);
+-			return err;
+-		}
++	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
++		goto skip_mfp_config;
++	/* The MFP mode (1 or 2) needs to be determined, parse IEs. The
++	 * IE will not be verified, just a quick search for MFP config
++	 */
++	rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len,
++				  WLAN_EID_RSN);
++	if (!rsn_ie)
++		goto skip_mfp_config;
++	ie = (const u8 *)rsn_ie;
++	ie_len = rsn_ie->len + TLV_HDR_LEN;
++	/* Skip unicast suite */
++	offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN;
++	if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
++		goto skip_mfp_config;
++	/* Skip multicast suite */
++	count = ie[offset] + (ie[offset + 1] << 8);
++	offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
++	if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
++		goto skip_mfp_config;
++	/* Skip auth key management suite(s) */
++	count = ie[offset] + (ie[offset + 1] << 8);
++	offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
++	if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len)
++		goto skip_mfp_config;
++	/* Ready to read capabilities */
++	mfp = BRCMF_MFP_NONE;
++	rsn_cap = ie[offset] + (ie[offset + 1] << 8);
++	if (rsn_cap & RSN_CAP_MFPR_MASK)
++		mfp = BRCMF_MFP_REQUIRED;
++	else if (rsn_cap & RSN_CAP_MFPC_MASK)
++		mfp = BRCMF_MFP_CAPABLE;
++	brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);
++
++skip_mfp_config:
++	brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
++	err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
++	if (err) {
++		brcmf_err("could not set wpa_auth (%d)\n", err);
++		return err;
+ 	}
+-	sec = &profile->sec;
+-	sec->wpa_auth = sme->crypto.akm_suites[0];
+ 
+ 	return err;
+ }
+@@ -1827,7 +1880,7 @@ brcmf_cfg80211_connect(struct wiphy *wip
+ 		goto done;
+ 	}
+ 
+-	err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
++	err = brcmf_set_wsec_mode(ndev, sme);
+ 	if (err) {
+ 		brcmf_err("wl_set_set_cipher failed (%d)\n", err);
+ 		goto done;
+@@ -2077,10 +2130,12 @@ brcmf_cfg80211_del_key(struct wiphy *wip
+ 		       u8 key_idx, bool pairwise, const u8 *mac_addr)
+ {
+ 	struct brcmf_if *ifp = netdev_priv(ndev);
+-	struct brcmf_wsec_key key;
+-	s32 err = 0;
++	struct brcmf_wsec_key *key;
++	s32 err;
+ 
+ 	brcmf_dbg(TRACE, "Enter\n");
++	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
++
+ 	if (!check_vif_up(ifp->vif))
+ 		return -EIO;
+ 
+@@ -2089,16 +2144,19 @@ brcmf_cfg80211_del_key(struct wiphy *wip
+ 		return -EINVAL;
+ 	}
+ 
+-	memset(&key, 0, sizeof(key));
++	key = &ifp->vif->profile.key[key_idx];
+ 
+-	key.index = (u32)key_idx;
+-	key.flags = BRCMF_PRIMARY_KEY;
+-	key.algo = CRYPTO_ALGO_OFF;
++	if (key->algo == CRYPTO_ALGO_OFF) {
++		brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n");
++		return -EINVAL;
++	}
+ 
+-	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
++	memset(key, 0, sizeof(*key));
++	key->index = (u32)key_idx;
++	key->flags = BRCMF_PRIMARY_KEY;
+ 
+-	/* Set the new key/index */
+-	err = send_key_to_dongle(ifp, &key);
++	/* Clear the key/index */
++	err = send_key_to_dongle(ifp, key);
+ 
+ 	brcmf_dbg(TRACE, "Exit\n");
+ 	return err;
+@@ -2106,8 +2164,8 @@ brcmf_cfg80211_del_key(struct wiphy *wip
+ 
+ static s32
+ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+-		    u8 key_idx, bool pairwise, const u8 *mac_addr,
+-		    struct key_params *params)
++		       u8 key_idx, bool pairwise, const u8 *mac_addr,
++		       struct key_params *params)
+ {
+ 	struct brcmf_if *ifp = netdev_priv(ndev);
+ 	struct brcmf_wsec_key *key;
+@@ -2214,9 +2272,10 @@ done:
+ }
+ 
+ static s32
+-brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
+-		    u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
+-		    void (*callback) (void *cookie, struct key_params * params))
++brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx,
++		       bool pairwise, const u8 *mac_addr, void *cookie,
++		       void (*callback)(void *cookie,
++					struct key_params *params))
+ {
+ 	struct key_params params;
+ 	struct brcmf_if *ifp = netdev_priv(ndev);
+@@ -2268,8 +2327,15 @@ done:
+ 
+ static s32
+ brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
+-				    struct net_device *ndev, u8 key_idx)
++				       struct net_device *ndev, u8 key_idx)
+ {
++	struct brcmf_if *ifp = netdev_priv(ndev);
++
++	brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx);
++
++	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
++		return 0;
++
+ 	brcmf_dbg(INFO, "Not supported\n");
+ 
+ 	return -EOPNOTSUPP;
+@@ -3769,7 +3835,7 @@ brcmf_configure_wpaie(struct brcmf_if *i
+ 	u32 auth = 0; /* d11 open authentication */
+ 	u16 count;
+ 	s32 err = 0;
+-	s32 len = 0;
++	s32 len;
+ 	u32 i;
+ 	u32 wsec;
+ 	u32 pval = 0;
+@@ -3779,6 +3845,7 @@ brcmf_configure_wpaie(struct brcmf_if *i
+ 	u8 *data;
+ 	u16 rsn_cap;
+ 	u32 wme_bss_disable;
++	u32 mfp;
+ 
+ 	brcmf_dbg(TRACE, "Enter\n");
+ 	if (wpa_ie == NULL)
+@@ -3893,19 +3960,53 @@ brcmf_configure_wpaie(struct brcmf_if *i
+ 			is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
+ 				    (wpa_auth |= WPA_AUTH_PSK);
+ 			break;
++		case RSN_AKM_SHA256_PSK:
++			brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n");
++			wpa_auth |= WPA2_AUTH_PSK_SHA256;
++			break;
++		case RSN_AKM_SHA256_1X:
++			brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n");
++			wpa_auth |= WPA2_AUTH_1X_SHA256;
++			break;
+ 		default:
+ 			brcmf_err("Ivalid key mgmt info\n");
+ 		}
+ 		offset++;
+ 	}
+ 
++	mfp = BRCMF_MFP_NONE;
+ 	if (is_rsn_ie) {
+ 		wme_bss_disable = 1;
+ 		if ((offset + RSN_CAP_LEN) <= len) {
+ 			rsn_cap = data[offset] + (data[offset + 1] << 8);
+ 			if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
+ 				wme_bss_disable = 0;
++			if (rsn_cap & RSN_CAP_MFPR_MASK) {
++				brcmf_dbg(TRACE, "MFP Required\n");
++				mfp = BRCMF_MFP_REQUIRED;
++				/* Firmware only supports mfp required in
++				 * combination with WPA2_AUTH_PSK_SHA256 or
++				 * WPA2_AUTH_1X_SHA256.
++				 */
++				if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 |
++						  WPA2_AUTH_1X_SHA256))) {
++					err = -EINVAL;
++					goto exit;
++				}
++				/* Firmware has requirement that WPA2_AUTH_PSK/
++				 * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI
++				 * is to be included in the rsn ie.
++				 */
++				if (wpa_auth & WPA2_AUTH_PSK_SHA256)
++					wpa_auth |= WPA2_AUTH_PSK;
++				else if (wpa_auth & WPA2_AUTH_1X_SHA256)
++					wpa_auth |= WPA2_AUTH_UNSPECIFIED;
++			} else if (rsn_cap & RSN_CAP_MFPC_MASK) {
++				brcmf_dbg(TRACE, "MFP Capable\n");
++				mfp = BRCMF_MFP_CAPABLE;
++			}
+ 		}
++		offset += RSN_CAP_LEN;
+ 		/* set wme_bss_disable to sync RSN Capabilities */
+ 		err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
+ 					       wme_bss_disable);
+@@ -3913,6 +4014,21 @@ brcmf_configure_wpaie(struct brcmf_if *i
+ 			brcmf_err("wme_bss_disable error %d\n", err);
+ 			goto exit;
+ 		}
++
++		/* Skip PMKID cnt as it is know to be 0 for AP. */
++		offset += RSN_PMKID_COUNT_LEN;
++
++		/* See if there is BIP wpa suite left for MFP */
++		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) &&
++		    ((offset + WPA_IE_MIN_OUI_LEN) <= len)) {
++			err = brcmf_fil_bsscfg_data_set(ifp, "bip",
++							&data[offset],
++							WPA_IE_MIN_OUI_LEN);
++			if (err < 0) {
++				brcmf_err("bip error %d\n", err);
++				goto exit;
++			}
++		}
+ 	}
+ 	/* FOR WPS , set SES_OW_ENABLED */
+ 	wsec = (pval | gval | SES_OW_ENABLED);
+@@ -3929,6 +4045,16 @@ brcmf_configure_wpaie(struct brcmf_if *i
+ 		brcmf_err("wsec error %d\n", err);
+ 		goto exit;
+ 	}
++	/* Configure MFP, this needs to go after wsec otherwise the wsec command
++	 * will overwrite the values set by MFP
++	 */
++	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) {
++		err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp);
++		if (err < 0) {
++			brcmf_err("mfp error %d\n", err);
++			goto exit;
++		}
++	}
+ 	/* set upper-layer auth */
+ 	err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
+ 	if (err < 0) {
+@@ -6149,8 +6275,10 @@ static int brcmf_setup_wiphy(struct wiph
+ 	wiphy->n_addresses = i;
+ 
+ 	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+-	wiphy->cipher_suites = __wl_cipher_suites;
+-	wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
++	wiphy->cipher_suites = brcmf_cipher_suites;
++	wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites);
++	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
++		wiphy->n_cipher_suites--;
+ 	wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
+ 			WIPHY_FLAG_OFFCHAN_TX |
+ 			WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+@@ -72,7 +72,7 @@
+ 
+ #define BRCMF_VNDR_IE_P2PAF_SHIFT	12
+ 
+-#define BRCMF_MAX_DEFAULT_KEYS		4
++#define BRCMF_MAX_DEFAULT_KEYS		6
+ 
+ /* beacon loss timeout defaults */
+ #define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON	2
+@@ -107,7 +107,6 @@ struct brcmf_cfg80211_security {
+ 	u32 auth_type;
+ 	u32 cipher_pairwise;
+ 	u32 cipher_group;
+-	u32 wpa_auth;
+ };
+ 
+ /**
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+@@ -161,6 +161,7 @@ void brcmf_feat_attach(struct brcmf_pub
+ 		ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS);
+ 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode");
+ 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable");
++	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp");
+ 
+ 	pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
+ 	err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac,
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
+@@ -30,6 +30,7 @@
+  * WOWL_ND: WOWL net detect (PNO)
+  * WOWL_GTK: (WOWL) GTK rekeying offload
+  * WOWL_ARP_ND: ARP and Neighbor Discovery offload support during WOWL.
++ * MFP: 802.11w Management Frame Protection.
+  */
+ #define BRCMF_FEAT_LIST \
+ 	BRCMF_FEAT_DEF(MBSS) \
+@@ -42,7 +43,8 @@
+ 	BRCMF_FEAT_DEF(SCAN_RANDOM_MAC) \
+ 	BRCMF_FEAT_DEF(WOWL_ND) \
+ 	BRCMF_FEAT_DEF(WOWL_GTK) \
+-	BRCMF_FEAT_DEF(WOWL_ARP_ND)
++	BRCMF_FEAT_DEF(WOWL_ARP_ND) \
++	BRCMF_FEAT_DEF(MFP)
+ 
+ /*
+  * Quirks:
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+@@ -142,6 +142,10 @@
+ #define BRCMF_RSN_KEK_LENGTH		16
+ #define BRCMF_RSN_REPLAY_LEN		8
+ 
++#define BRCMF_MFP_NONE			0
++#define BRCMF_MFP_CAPABLE		1
++#define BRCMF_MFP_REQUIRED		2
++
+ /* join preference types for join_pref iovar */
+ enum brcmf_join_pref_types {
+ 	BRCMF_JOIN_PREF_RSSI = 1,
+--- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
++++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
+@@ -236,6 +236,8 @@ static inline bool ac_bitmap_tst(u8 bitm
+ #define WPA2_AUTH_RESERVED3	0x0200
+ #define WPA2_AUTH_RESERVED4	0x0400
+ #define WPA2_AUTH_RESERVED5	0x0800
++#define WPA2_AUTH_1X_SHA256	0x1000  /* 1X with SHA256 key derivation */
++#define WPA2_AUTH_PSK_SHA256	0x8000	/* PSK with SHA256 key derivation */
+ 
+ #define DOT11_DEFAULT_RTS_LEN		2347
+ #define DOT11_DEFAULT_FRAG_LEN		2346
diff --git a/package/kernel/mac80211/patches/861-brcmfmac-register-wiphy-s-during-module_init.patch b/package/kernel/mac80211/patches/861-brcmfmac-register-wiphy-s-during-module_init.patch
index 95d93ae1f6..7803733fda 100644
--- a/package/kernel/mac80211/patches/861-brcmfmac-register-wiphy-s-during-module_init.patch
+++ b/package/kernel/mac80211/patches/861-brcmfmac-register-wiphy-s-during-module_init.patch
@@ -13,8 +13,8 @@ Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
 
 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
-@@ -1332,6 +1332,7 @@ static int __init brcmfmac_module_init(v
- #endif
+@@ -1417,6 +1417,7 @@ int __init brcmf_core_init(void)
+ {
  	if (!schedule_work(&brcmf_driver_work))
  		return -EBUSY;
 +	flush_work(&brcmf_driver_work);
diff --git a/package/kernel/mac80211/patches/862-brcmfmac-workaround-bug-with-some-inconsistent-BSSes.patch b/package/kernel/mac80211/patches/862-brcmfmac-workaround-bug-with-some-inconsistent-BSSes.patch
index 4295b4bd33..ac0f704538 100644
--- a/package/kernel/mac80211/patches/862-brcmfmac-workaround-bug-with-some-inconsistent-BSSes.patch
+++ b/package/kernel/mac80211/patches/862-brcmfmac-workaround-bug-with-some-inconsistent-BSSes.patch
@@ -10,7 +10,7 @@ Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
 
 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
-@@ -615,9 +615,37 @@ static struct wireless_dev *brcmf_cfg802
+@@ -636,9 +636,37 @@ static struct wireless_dev *brcmf_cfg802
  						     u32 *flags,
  						     struct vif_params *params)
  {
-- 
2.30.2