From 84a3e668fc852afea25aadf8ee6f04a661fae280 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Fri, 24 Apr 2015 10:53:11 +0000
Subject: [PATCH] mac80211: backport brcmfmac to support multiple devices NVRAM
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

SVN-Revision: 45577
---
 ...tic-superset-of-channels-for-wiphy-b.patch | 300 +++++++++++++++
 ...wiphy-band-information-upon-updating.patch |  29 ++
 ...ac-add-description-for-feature-flags.patch |  24 ++
 ...e-scheduled-scan-support-conditional.patch |  51 +++
 ...d-support-for-BCM4324-rev-B5-chipset.patch |  43 +++
 ...ess-interrupt-regardless-sdiod-state.patch |  27 ++
 ...brcmfmac-fix-sdio-suspend-and-resume.patch |  68 ++++
 ...-add-support-for-BCM4358-PCIe-device.patch |  77 ++++
 ...-add-additional-43602-pcie-device-id.patch |  30 ++
 ...port-for-multiple-PCIE-devices-in-nv.patch | 351 ++++++++++++++++++
 10 files changed, 1000 insertions(+)
 create mode 100644 package/kernel/mac80211/patches/354-brcmfmac-use-static-superset-of-channels-for-wiphy-b.patch
 create mode 100644 package/kernel/mac80211/patches/355-brcmfmac-update-wiphy-band-information-upon-updating.patch
 create mode 100644 package/kernel/mac80211/patches/356-brcmfmac-add-description-for-feature-flags.patch
 create mode 100644 package/kernel/mac80211/patches/357-brcmfmac-make-scheduled-scan-support-conditional.patch
 create mode 100644 package/kernel/mac80211/patches/358-brcmfmac-add-support-for-BCM4324-rev-B5-chipset.patch
 create mode 100644 package/kernel/mac80211/patches/359-brcmfmac-process-interrupt-regardless-sdiod-state.patch
 create mode 100644 package/kernel/mac80211/patches/360-brcmfmac-fix-sdio-suspend-and-resume.patch
 create mode 100644 package/kernel/mac80211/patches/361-brcmfmac-add-support-for-BCM4358-PCIe-device.patch
 create mode 100644 package/kernel/mac80211/patches/362-brcmfmac-add-additional-43602-pcie-device-id.patch
 create mode 100644 package/kernel/mac80211/patches/363-brcmfmac-Add-support-for-multiple-PCIE-devices-in-nv.patch

diff --git a/package/kernel/mac80211/patches/354-brcmfmac-use-static-superset-of-channels-for-wiphy-b.patch b/package/kernel/mac80211/patches/354-brcmfmac-use-static-superset-of-channels-for-wiphy-b.patch
new file mode 100644
index 0000000000..6e461f617f
--- /dev/null
+++ b/package/kernel/mac80211/patches/354-brcmfmac-use-static-superset-of-channels-for-wiphy-b.patch
@@ -0,0 +1,300 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Tue, 14 Apr 2015 20:10:24 +0200
+Subject: [PATCH] brcmfmac: use static superset of channels for wiphy
+ bands
+
+The driver was constructing a list of channels per wiphy band
+by querying the device. This list is not what the hardware is
+able to do as it is already filtered by the country setting in
+the device. As user-space may change the country this would
+require updating the channel list which is not recommended [1].
+This patch introduces a superset of channels. The individual
+channels are disabled appropriately by querying the device.
+
+[1] http://mid.gmane.org/1426706320.3001.21.camel@sipsolutions.net
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -129,13 +129,47 @@ static struct ieee80211_rate __wl_rates[
+ 	RATETAB_ENT(BRCM_RATE_54M, 0),
+ };
+ 
+-#define wl_a_rates		(__wl_rates + 4)
+-#define wl_a_rates_size	8
+ #define wl_g_rates		(__wl_rates + 0)
+-#define wl_g_rates_size	12
++#define wl_g_rates_size		ARRAY_SIZE(__wl_rates)
++#define wl_a_rates		(__wl_rates + 4)
++#define wl_a_rates_size		(wl_g_rates_size - 4)
++
++#define CHAN2G(_channel, _freq) {				\
++	.band			= IEEE80211_BAND_2GHZ,		\
++	.center_freq		= (_freq),			\
++	.hw_value		= (_channel),			\
++	.flags			= IEEE80211_CHAN_DISABLED,	\
++	.max_antenna_gain	= 0,				\
++	.max_power		= 30,				\
++}
++
++#define CHAN5G(_channel) {					\
++	.band			= IEEE80211_BAND_5GHZ,		\
++	.center_freq		= 5000 + (5 * (_channel)),	\
++	.hw_value		= (_channel),			\
++	.flags			= IEEE80211_CHAN_DISABLED,	\
++	.max_antenna_gain	= 0,				\
++	.max_power		= 30,				\
++}
++
++static struct ieee80211_channel __wl_2ghz_channels[] = {
++	CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
++	CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
++	CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
++	CHAN2G(13, 2472), CHAN2G(14, 2484)
++};
++
++static struct ieee80211_channel __wl_5ghz_channels[] = {
++	CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
++	CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
++	CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
++	CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
++	CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
++	CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
++};
+ 
+ /* Band templates duplicated per wiphy. The channel info
+- * is filled in after querying the device.
++ * above is added to the band during setup.
+  */
+ static const struct ieee80211_supported_band __wl_band_2ghz = {
+ 	.band = IEEE80211_BAND_2GHZ,
+@@ -143,7 +177,7 @@ static const struct ieee80211_supported_
+ 	.n_bitrates = wl_g_rates_size,
+ };
+ 
+-static const struct ieee80211_supported_band __wl_band_5ghz_a = {
++static const struct ieee80211_supported_band __wl_band_5ghz = {
+ 	.band = IEEE80211_BAND_5GHZ,
+ 	.bitrates = wl_a_rates,
+ 	.n_bitrates = wl_a_rates_size,
+@@ -5252,40 +5286,6 @@ dongle_scantime_out:
+ 	return err;
+ }
+ 
+-/* Filter the list of channels received from firmware counting only
+- * the 20MHz channels. The wiphy band data only needs those which get
+- * flagged to indicate if they can take part in higher bandwidth.
+- */
+-static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg,
+-				       struct brcmf_chanspec_list *chlist,
+-				       u32 chcnt[])
+-{
+-	u32 total = le32_to_cpu(chlist->count);
+-	struct brcmu_chan ch;
+-	int i;
+-
+-	for (i = 0; i < total; i++) {
+-		ch.chspec = (u16)le32_to_cpu(chlist->element[i]);
+-		cfg->d11inf.decchspec(&ch);
+-
+-		/* Firmware gives a ordered list. We skip non-20MHz
+-		 * channels is 2G. For 5G we can abort upon reaching
+-		 * a non-20MHz channel in the list.
+-		 */
+-		if (ch.bw != BRCMU_CHAN_BW_20) {
+-			if (ch.band == BRCMU_CHAN_BAND_5G)
+-				break;
+-			else
+-				continue;
+-		}
+-
+-		if (ch.band == BRCMU_CHAN_BAND_2G)
+-			chcnt[0] += 1;
+-		else if (ch.band == BRCMU_CHAN_BAND_5G)
+-			chcnt[1] += 1;
+-	}
+-}
+-
+ static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
+ 					   struct brcmu_chan *ch)
+ {
+@@ -5321,7 +5321,6 @@ static int brcmf_construct_chaninfo(stru
+ 	u32 i, j;
+ 	u32 total;
+ 	u32 chaninfo;
+-	u32 chcnt[2] = { 0, 0 };
+ 	u32 index;
+ 
+ 	pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+@@ -5338,42 +5337,15 @@ static int brcmf_construct_chaninfo(stru
+ 		goto fail_pbuf;
+ 	}
+ 
+-	brcmf_count_20mhz_channels(cfg, list, chcnt);
+ 	wiphy = cfg_to_wiphy(cfg);
+-	if (chcnt[0]) {
+-		band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
+-			       GFP_KERNEL);
+-		if (band == NULL) {
+-			err = -ENOMEM;
+-			goto fail_pbuf;
+-		}
+-		band->channels = kcalloc(chcnt[0], sizeof(*channel),
+-					 GFP_KERNEL);
+-		if (band->channels == NULL) {
+-			kfree(band);
+-			err = -ENOMEM;
+-			goto fail_pbuf;
+-		}
+-		band->n_channels = 0;
+-		wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+-	}
+-	if (chcnt[1]) {
+-		band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a),
+-			       GFP_KERNEL);
+-		if (band == NULL) {
+-			err = -ENOMEM;
+-			goto fail_band2g;
+-		}
+-		band->channels = kcalloc(chcnt[1], sizeof(*channel),
+-					 GFP_KERNEL);
+-		if (band->channels == NULL) {
+-			kfree(band);
+-			err = -ENOMEM;
+-			goto fail_band2g;
+-		}
+-		band->n_channels = 0;
+-		wiphy->bands[IEEE80211_BAND_5GHZ] = band;
+-	}
++	band = wiphy->bands[IEEE80211_BAND_2GHZ];
++	if (band)
++		for (i = 0; i < band->n_channels; i++)
++			band->channels[i].flags = IEEE80211_CHAN_DISABLED;
++	band = wiphy->bands[IEEE80211_BAND_5GHZ];
++	if (band)
++		for (i = 0; i < band->n_channels; i++)
++			band->channels[i].flags = IEEE80211_CHAN_DISABLED;
+ 
+ 	total = le32_to_cpu(list->count);
+ 	for (i = 0; i < total; i++) {
+@@ -5388,6 +5360,8 @@ static int brcmf_construct_chaninfo(stru
+ 			brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
+ 			continue;
+ 		}
++		if (!band)
++			continue;
+ 		if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
+ 		    ch.bw == BRCMU_CHAN_BW_40)
+ 			continue;
+@@ -5415,9 +5389,9 @@ static int brcmf_construct_chaninfo(stru
+ 		} else if (ch.bw == BRCMU_CHAN_BW_40) {
+ 			brcmf_update_bw40_channel_flag(&channel[index], &ch);
+ 		} else {
+-			/* disable other bandwidths for now as mentioned
+-			 * order assure they are enabled for subsequent
+-			 * chanspecs.
++			/* enable the channel and disable other bandwidths
++			 * for now as mentioned order assure they are enabled
++			 * for subsequent chanspecs.
+ 			 */
+ 			channel[index].flags = IEEE80211_CHAN_NO_HT40 |
+ 					       IEEE80211_CHAN_NO_80MHZ;
+@@ -5436,16 +5410,8 @@ static int brcmf_construct_chaninfo(stru
+ 						IEEE80211_CHAN_NO_IR;
+ 			}
+ 		}
+-		if (index == band->n_channels)
+-			band->n_channels++;
+ 	}
+-	kfree(pbuf);
+-	return 0;
+ 
+-fail_band2g:
+-	kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
+-	kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
+-	wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
+ fail_pbuf:
+ 	kfree(pbuf);
+ 	return err;
+@@ -5778,7 +5744,12 @@ static void brcmf_wiphy_wowl_params(stru
+ 
+ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
+ {
++	struct ieee80211_supported_band *band;
+ 	struct ieee80211_iface_combination ifc_combo;
++	__le32 bandlist[3];
++	u32 n_bands;
++	int err, i;
++
+ 	wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
+ 	wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+ 	wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
+@@ -5820,7 +5791,52 @@ static int brcmf_setup_wiphy(struct wiph
+ 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
+ 		brcmf_wiphy_wowl_params(wiphy);
+ 
+-	return brcmf_setup_wiphybands(wiphy);
++	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
++				     sizeof(bandlist));
++	if (err) {
++		brcmf_err("could not obtain band info: err=%d\n", err);
++		return err;
++	}
++	/* first entry in bandlist is number of bands */
++	n_bands = le32_to_cpu(bandlist[0]);
++	for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
++		if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
++			band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
++				       GFP_KERNEL);
++			if (!band)
++				return -ENOMEM;
++
++			band->channels = kmemdup(&__wl_2ghz_channels,
++						 sizeof(__wl_2ghz_channels),
++						 GFP_KERNEL);
++			if (!band->channels) {
++				kfree(band);
++				return -ENOMEM;
++			}
++
++			band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
++			wiphy->bands[IEEE80211_BAND_2GHZ] = band;
++		}
++		if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
++			band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
++				       GFP_KERNEL);
++			if (!band)
++				return -ENOMEM;
++
++			band->channels = kmemdup(&__wl_5ghz_channels,
++						 sizeof(__wl_5ghz_channels),
++						 GFP_KERNEL);
++			if (!band->channels) {
++				kfree(band);
++				return -ENOMEM;
++			}
++
++			band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
++			wiphy->bands[IEEE80211_BAND_5GHZ] = band;
++		}
++	}
++	err = brcmf_setup_wiphybands(wiphy);
++	return err;
+ }
+ 
+ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
+@@ -6011,6 +6027,9 @@ static void brcmf_cfg80211_reg_notifier(
+ 
+ static void brcmf_free_wiphy(struct wiphy *wiphy)
+ {
++	if (!wiphy)
++		return;
++
+ 	kfree(wiphy->iface_combinations);
+ 	if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+ 		kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
diff --git a/package/kernel/mac80211/patches/355-brcmfmac-update-wiphy-band-information-upon-updating.patch b/package/kernel/mac80211/patches/355-brcmfmac-update-wiphy-band-information-upon-updating.patch
new file mode 100644
index 0000000000..a0c22eb411
--- /dev/null
+++ b/package/kernel/mac80211/patches/355-brcmfmac-update-wiphy-band-information-upon-updating.patch
@@ -0,0 +1,29 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Tue, 14 Apr 2015 20:10:25 +0200
+Subject: [PATCH] brcmfmac: update wiphy band information upon updating
+ regulatory domain
+
+When change the country code the available channels may change. So
+the wiphy bands should be updated accordingly.
+
+Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -6022,7 +6022,11 @@ static void brcmf_cfg80211_reg_notifier(
+ 	memset(&ccreq, 0, sizeof(ccreq));
+ 	ccreq.rev = cpu_to_le32(-1);
+ 	memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
+-	brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
++	if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
++		brcmf_err("firmware rejected country setting\n");
++		return;
++	}
++	brcmf_setup_wiphybands(wiphy);
+ }
+ 
+ static void brcmf_free_wiphy(struct wiphy *wiphy)
diff --git a/package/kernel/mac80211/patches/356-brcmfmac-add-description-for-feature-flags.patch b/package/kernel/mac80211/patches/356-brcmfmac-add-description-for-feature-flags.patch
new file mode 100644
index 0000000000..193f507d0d
--- /dev/null
+++ b/package/kernel/mac80211/patches/356-brcmfmac-add-description-for-feature-flags.patch
@@ -0,0 +1,24 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Tue, 14 Apr 2015 20:10:26 +0200
+Subject: [PATCH] brcmfmac: add description for feature flags
+
+Some feature flags were not described in the header file. Adding
+the description.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
+@@ -19,7 +19,9 @@
+ /*
+  * Features:
+  *
++ * MBSS: multiple BSSID support (eg. guest network in AP mode).
+  * MCHAN: multi-channel for concurrent P2P.
++ * WOWL: Wake-On-WLAN.
+  */
+ #define BRCMF_FEAT_LIST \
+ 	BRCMF_FEAT_DEF(MBSS) \
diff --git a/package/kernel/mac80211/patches/357-brcmfmac-make-scheduled-scan-support-conditional.patch b/package/kernel/mac80211/patches/357-brcmfmac-make-scheduled-scan-support-conditional.patch
new file mode 100644
index 0000000000..42330b474e
--- /dev/null
+++ b/package/kernel/mac80211/patches/357-brcmfmac-make-scheduled-scan-support-conditional.patch
@@ -0,0 +1,51 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Tue, 14 Apr 2015 20:10:27 +0200
+Subject: [PATCH] brcmfmac: make scheduled scan support conditional
+
+The scheduled scan support depends on firmware supporting the PNO
+feature. This feature is optional so add a feature flag for this
+in the driver and announce scheduled scan support accordingly.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -5782,7 +5782,8 @@ static int brcmf_setup_wiphy(struct wiph
+ 		wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
+ 	wiphy->mgmt_stypes = brcmf_txrx_stypes;
+ 	wiphy->max_remain_on_channel_duration = 5000;
+-	brcmf_wiphy_pno_params(wiphy);
++	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
++		brcmf_wiphy_pno_params(wiphy);
+ 
+ 	/* vendor commands/events support */
+ 	wiphy->vendor_commands = brcmf_vendor_cmds;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
+@@ -124,6 +124,7 @@ void brcmf_feat_attach(struct brcmf_pub
+ 	struct brcmf_if *ifp = drvr->iflist[0];
+ 
+ 	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
++	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 (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID)
+--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
+@@ -21,11 +21,13 @@
+  *
+  * MBSS: multiple BSSID support (eg. guest network in AP mode).
+  * MCHAN: multi-channel for concurrent P2P.
++ * PNO: preferred network offload.
+  * WOWL: Wake-On-WLAN.
+  */
+ #define BRCMF_FEAT_LIST \
+ 	BRCMF_FEAT_DEF(MBSS) \
+ 	BRCMF_FEAT_DEF(MCHAN) \
++	BRCMF_FEAT_DEF(PNO) \
+ 	BRCMF_FEAT_DEF(WOWL)
+ /*
+  * Quirks:
diff --git a/package/kernel/mac80211/patches/358-brcmfmac-add-support-for-BCM4324-rev-B5-chipset.patch b/package/kernel/mac80211/patches/358-brcmfmac-add-support-for-BCM4324-rev-B5-chipset.patch
new file mode 100644
index 0000000000..b859d46894
--- /dev/null
+++ b/package/kernel/mac80211/patches/358-brcmfmac-add-support-for-BCM4324-rev-B5-chipset.patch
@@ -0,0 +1,43 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Tue, 14 Apr 2015 20:10:28 +0200
+Subject: [PATCH] brcmfmac: add support for BCM4324 rev B5 chipset
+
+This patch adds support for the BCM4324 B5 revision. This device
+is similar to BCM43241 from driver and firmware perspective. It
+is known to be used in Lenovo Thinkpad Tablet devices.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+@@ -601,6 +601,8 @@ static const struct sdiod_drive_str sdio
+ #define BCM43241B0_NVRAM_NAME		"brcm/brcmfmac43241b0-sdio.txt"
+ #define BCM43241B4_FIRMWARE_NAME	"brcm/brcmfmac43241b4-sdio.bin"
+ #define BCM43241B4_NVRAM_NAME		"brcm/brcmfmac43241b4-sdio.txt"
++#define BCM43241B5_FIRMWARE_NAME	"brcm/brcmfmac43241b5-sdio.bin"
++#define BCM43241B5_NVRAM_NAME		"brcm/brcmfmac43241b5-sdio.txt"
+ #define BCM4329_FIRMWARE_NAME		"brcm/brcmfmac4329-sdio.bin"
+ #define BCM4329_NVRAM_NAME		"brcm/brcmfmac4329-sdio.txt"
+ #define BCM4330_FIRMWARE_NAME		"brcm/brcmfmac4330-sdio.bin"
+@@ -628,6 +630,8 @@ MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME
+ MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME);
+ MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME);
+ MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME);
++MODULE_FIRMWARE(BCM43241B5_FIRMWARE_NAME);
++MODULE_FIRMWARE(BCM43241B5_NVRAM_NAME);
+ MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME);
+ MODULE_FIRMWARE(BCM4329_NVRAM_NAME);
+ MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME);
+@@ -667,7 +671,8 @@ enum brcmf_firmware_type {
+ static const struct brcmf_firmware_names brcmf_fwname_data[] = {
+ 	{ BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) },
+ 	{ BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) },
+-	{ BRCM_CC_43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
++	{ BRCM_CC_43241_CHIP_ID, 0x00000020, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
++	{ BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43241B5) },
+ 	{ BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
+ 	{ BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
+ 	{ BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
diff --git a/package/kernel/mac80211/patches/359-brcmfmac-process-interrupt-regardless-sdiod-state.patch b/package/kernel/mac80211/patches/359-brcmfmac-process-interrupt-regardless-sdiod-state.patch
new file mode 100644
index 0000000000..d420308383
--- /dev/null
+++ b/package/kernel/mac80211/patches/359-brcmfmac-process-interrupt-regardless-sdiod-state.patch
@@ -0,0 +1,27 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Tue, 14 Apr 2015 20:10:29 +0200
+Subject: [PATCH] brcmfmac: process interrupt regardless sdiod state
+
+When the sdio bus state is not ready to process we abort the
+interrupt service routine. This is not wanted as it keeps the
+interrupt source active. Better clear the interrupt source.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+@@ -3555,10 +3555,6 @@ void brcmf_sdio_isr(struct brcmf_sdio *b
+ 		return;
+ 	}
+ 
+-	if (bus->sdiodev->state != BRCMF_SDIOD_DATA) {
+-		brcmf_err("bus is down. we have nothing to do\n");
+-		return;
+-	}
+ 	/* Count the interrupt call */
+ 	bus->sdcnt.intrcount++;
+ 	if (in_interrupt())
diff --git a/package/kernel/mac80211/patches/360-brcmfmac-fix-sdio-suspend-and-resume.patch b/package/kernel/mac80211/patches/360-brcmfmac-fix-sdio-suspend-and-resume.patch
new file mode 100644
index 0000000000..ac5584ec3c
--- /dev/null
+++ b/package/kernel/mac80211/patches/360-brcmfmac-fix-sdio-suspend-and-resume.patch
@@ -0,0 +1,68 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Tue, 14 Apr 2015 20:10:30 +0200
+Subject: [PATCH] brcmfmac: fix sdio suspend and resume
+
+commit 330b4e4be937 ("brcmfmac: Add wowl support for SDIO devices.")
+changed the behaviour by removing the MMC_PM_KEEP_POWER flag for
+non-wowl scenario, which needs to be restored. Another necessary
+change is to mark the card as being non-removable. With this in place
+the suspend resume test passes successfully doing:
+
+ # echo devices > /sys/power/pm_test
+ # echo mem > /sys/power/state
+
+Note that power may still be switched off when system is going
+in S3 state.
+
+Reported-by: Fu, Zhonghui <<zhonghui.fu@linux.intel.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+@@ -1011,6 +1011,14 @@ static int brcmf_sdiod_remove(struct brc
+ 	return 0;
+ }
+ 
++static void brcmf_sdiod_host_fixup(struct mmc_host *host)
++{
++	/* runtime-pm powers off the device */
++	pm_runtime_forbid(host->parent);
++	/* avoid removal detection upon resume */
++	host->caps |= MMC_CAP_NONREMOVABLE;
++}
++
+ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
+ {
+ 	struct sdio_func *func;
+@@ -1076,7 +1084,7 @@ static int brcmf_sdiod_probe(struct brcm
+ 		ret = -ENODEV;
+ 		goto out;
+ 	}
+-	pm_runtime_forbid(host->parent);
++	brcmf_sdiod_host_fixup(host);
+ out:
+ 	if (ret)
+ 		brcmf_sdiod_remove(sdiodev);
+@@ -1246,15 +1254,15 @@ static int brcmf_ops_sdio_suspend(struct
+ 	brcmf_sdiod_freezer_on(sdiodev);
+ 	brcmf_sdio_wd_timer(sdiodev->bus, 0);
+ 
++	sdio_flags = MMC_PM_KEEP_POWER;
+ 	if (sdiodev->wowl_enabled) {
+-		sdio_flags = MMC_PM_KEEP_POWER;
+ 		if (sdiodev->pdata->oob_irq_supported)
+ 			enable_irq_wake(sdiodev->pdata->oob_irq_nr);
+ 		else
+-			sdio_flags = MMC_PM_WAKE_SDIO_IRQ;
+-		if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags))
+-			brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
++			sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
+ 	}
++	if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags))
++		brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
+ 	return 0;
+ }
+ 
diff --git a/package/kernel/mac80211/patches/361-brcmfmac-add-support-for-BCM4358-PCIe-device.patch b/package/kernel/mac80211/patches/361-brcmfmac-add-support-for-BCM4358-PCIe-device.patch
new file mode 100644
index 0000000000..a521b65d0f
--- /dev/null
+++ b/package/kernel/mac80211/patches/361-brcmfmac-add-support-for-BCM4358-PCIe-device.patch
@@ -0,0 +1,77 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Tue, 14 Apr 2015 20:10:31 +0200
+Subject: [PATCH] brcmfmac: add support for BCM4358 PCIe device
+
+This patch adds support for the BCM4358 2x2 11ac device.
+
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
+@@ -649,6 +649,7 @@ static u32 brcmf_chip_tcm_rambase(struct
+ 	case BRCM_CC_43567_CHIP_ID:
+ 	case BRCM_CC_43569_CHIP_ID:
+ 	case BRCM_CC_43570_CHIP_ID:
++	case BRCM_CC_4358_CHIP_ID:
+ 	case BRCM_CC_43602_CHIP_ID:
+ 		return 0x180000;
+ 	default:
+--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+@@ -51,6 +51,8 @@ enum brcmf_pcie_state {
+ #define BRCMF_PCIE_4356_NVRAM_NAME		"brcm/brcmfmac4356-pcie.txt"
+ #define BRCMF_PCIE_43570_FW_NAME		"brcm/brcmfmac43570-pcie.bin"
+ #define BRCMF_PCIE_43570_NVRAM_NAME		"brcm/brcmfmac43570-pcie.txt"
++#define BRCMF_PCIE_4358_FW_NAME			"brcm/brcmfmac4358-pcie.bin"
++#define BRCMF_PCIE_4358_NVRAM_NAME		"brcm/brcmfmac4358-pcie.txt"
+ 
+ #define BRCMF_PCIE_FW_UP_TIMEOUT		2000 /* msec */
+ 
+@@ -189,6 +191,8 @@ MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME)
+ MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME);
+ MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME);
+ MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME);
++MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME);
++MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME);
+ 
+ 
+ struct brcmf_pcie_console {
+@@ -1333,6 +1337,10 @@ static int brcmf_pcie_get_fwnames(struct
+ 		fw_name = BRCMF_PCIE_43570_FW_NAME;
+ 		nvram_name = BRCMF_PCIE_43570_NVRAM_NAME;
+ 		break;
++	case BRCM_CC_4358_CHIP_ID:
++		fw_name = BRCMF_PCIE_4358_FW_NAME;
++		nvram_name = BRCMF_PCIE_4358_NVRAM_NAME;
++		break;
+ 	default:
+ 		brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip);
+ 		return -ENODEV;
+@@ -1850,6 +1858,7 @@ static struct pci_device_id brcmf_pcie_d
+ 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID),
+ 	BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
+ 	BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
++	BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID),
+ 	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID),
+ 	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID),
+ 	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID),
+--- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
++++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
+@@ -45,6 +45,7 @@
+ #define BRCM_CC_43567_CHIP_ID		43567
+ #define BRCM_CC_43569_CHIP_ID		43569
+ #define BRCM_CC_43570_CHIP_ID		43570
++#define BRCM_CC_4358_CHIP_ID		0x4358
+ #define BRCM_CC_43602_CHIP_ID		43602
+ 
+ /* USB Device IDs */
+@@ -59,6 +60,7 @@
+ #define BRCM_PCIE_4356_DEVICE_ID	0x43ec
+ #define BRCM_PCIE_43567_DEVICE_ID	0x43d3
+ #define BRCM_PCIE_43570_DEVICE_ID	0x43d9
++#define BRCM_PCIE_4358_DEVICE_ID	0x43e9
+ #define BRCM_PCIE_43602_DEVICE_ID	0x43ba
+ #define BRCM_PCIE_43602_2G_DEVICE_ID	0x43bb
+ #define BRCM_PCIE_43602_5G_DEVICE_ID	0x43bc
diff --git a/package/kernel/mac80211/patches/362-brcmfmac-add-additional-43602-pcie-device-id.patch b/package/kernel/mac80211/patches/362-brcmfmac-add-additional-43602-pcie-device-id.patch
new file mode 100644
index 0000000000..bcbb984d42
--- /dev/null
+++ b/package/kernel/mac80211/patches/362-brcmfmac-add-additional-43602-pcie-device-id.patch
@@ -0,0 +1,30 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Tue, 14 Apr 2015 20:10:32 +0200
+Subject: [PATCH] brcmfmac: add additional 43602 pcie device id.
+
+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>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+@@ -1862,6 +1862,7 @@ static struct pci_device_id brcmf_pcie_d
+ 	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID),
+ 	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID),
+ 	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID),
++	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID),
+ 	{ /* end: all zeroes */ }
+ };
+ 
+--- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
++++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
+@@ -64,6 +64,7 @@
+ #define BRCM_PCIE_43602_DEVICE_ID	0x43ba
+ #define BRCM_PCIE_43602_2G_DEVICE_ID	0x43bb
+ #define BRCM_PCIE_43602_5G_DEVICE_ID	0x43bc
++#define BRCM_PCIE_43602_RAW_DEVICE_ID	43602
+ 
+ /* brcmsmac IDs */
+ #define BCM4313_D11N2G_ID	0x4727	/* 4313 802.11n 2.4G device */
diff --git a/package/kernel/mac80211/patches/363-brcmfmac-Add-support-for-multiple-PCIE-devices-in-nv.patch b/package/kernel/mac80211/patches/363-brcmfmac-Add-support-for-multiple-PCIE-devices-in-nv.patch
new file mode 100644
index 0000000000..5f9bbeaf5c
--- /dev/null
+++ b/package/kernel/mac80211/patches/363-brcmfmac-Add-support-for-multiple-PCIE-devices-in-nv.patch
@@ -0,0 +1,351 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Tue, 14 Apr 2015 20:10:33 +0200
+Subject: [PATCH] brcmfmac: Add support for multiple PCIE devices in
+ nvram.
+
+With PCIE it is possible to support multiple devices with the
+same device type. They all load the same nvram file. In order to
+support this the nvram can specify which part of the nvram is
+for which pcie device. This patch adds support for these new
+types of nvram files.
+
+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: Daniel (Deognyoun) Kim <dekim@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
+@@ -23,6 +23,10 @@
+ #include "debug.h"
+ #include "firmware.h"
+ 
++#define BRCMF_FW_MAX_NVRAM_SIZE			64000
++#define BRCMF_FW_NVRAM_DEVPATH_LEN		19	/* devpath0=pcie/1/4/ */
++#define BRCMF_FW_NVRAM_PCIEDEV_LEN		9	/* pcie/1/4/ */
++
+ char brcmf_firmware_path[BRCMF_FW_PATH_LEN];
+ module_param_string(firmware_path, brcmf_firmware_path,
+ 		    BRCMF_FW_PATH_LEN, 0440);
+@@ -46,6 +50,8 @@ enum nvram_parser_state {
+  * @column: current column in line.
+  * @pos: byte offset in input buffer.
+  * @entry: start position of key,value entry.
++ * @multi_dev_v1: detect pcie multi device v1 (compressed).
++ * @multi_dev_v2: detect pcie multi device v2.
+  */
+ struct nvram_parser {
+ 	enum nvram_parser_state state;
+@@ -56,6 +62,8 @@ struct nvram_parser {
+ 	u32 column;
+ 	u32 pos;
+ 	u32 entry;
++	bool multi_dev_v1;
++	bool multi_dev_v2;
+ };
+ 
+ static bool is_nvram_char(char c)
+@@ -108,6 +116,10 @@ static enum nvram_parser_state brcmf_nvr
+ 			st = COMMENT;
+ 		else
+ 			st = VALUE;
++		if (strncmp(&nvp->fwnv->data[nvp->entry], "devpath", 7) == 0)
++			nvp->multi_dev_v1 = true;
++		if (strncmp(&nvp->fwnv->data[nvp->entry], "pcie/", 5) == 0)
++			nvp->multi_dev_v2 = true;
+ 	} else if (!is_nvram_char(c)) {
+ 		brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
+ 			  nvp->line, nvp->column);
+@@ -133,6 +145,8 @@ brcmf_nvram_handle_value(struct nvram_pa
+ 		ekv = (u8 *)&nvp->fwnv->data[nvp->pos];
+ 		skv = (u8 *)&nvp->fwnv->data[nvp->entry];
+ 		cplen = ekv - skv;
++		if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
++			return END;
+ 		/* copy to output buffer */
+ 		memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
+ 		nvp->nvram_len += cplen;
+@@ -180,10 +194,18 @@ static enum nvram_parser_state
+ static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
+ 				   const struct firmware *nv)
+ {
++	size_t size;
++
+ 	memset(nvp, 0, sizeof(*nvp));
+ 	nvp->fwnv = nv;
++	/* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
++	if (nv->size > BRCMF_FW_MAX_NVRAM_SIZE)
++		size = BRCMF_FW_MAX_NVRAM_SIZE;
++	else
++		size = nv->size;
+ 	/* Alloc for extra 0 byte + roundup by 4 + length field */
+-	nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL);
++	size += 1 + 3 + sizeof(u32);
++	nvp->nvram = kzalloc(size, GFP_KERNEL);
+ 	if (!nvp->nvram)
+ 		return -ENOMEM;
+ 
+@@ -192,12 +214,136 @@ static int brcmf_init_nvram_parser(struc
+ 	return 0;
+ }
+ 
++/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple
++ * devices. Strip it down for one device, use domain_nr/bus_nr to determine
++ * which data is to be returned. v1 is the version where nvram is stored
++ * compressed and "devpath" maps to index for valid entries.
++ */
++static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
++				    u16 bus_nr)
++{
++	u32 i, j;
++	bool found;
++	u8 *nvram;
++	u8 id;
++
++	nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
++	if (!nvram)
++		goto fail;
++
++	/* min length: devpath0=pcie/1/4/ + 0:x=y */
++	if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6)
++		goto fail;
++
++	/* First search for the devpathX and see if it is the configuration
++	 * for domain_nr/bus_nr. Search complete nvp
++	 */
++	found = false;
++	i = 0;
++	while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) {
++		/* Format: devpathX=pcie/Y/Z/
++		 * Y = domain_nr, Z = bus_nr, X = virtual ID
++		 */
++		if ((strncmp(&nvp->nvram[i], "devpath", 7) == 0) &&
++		    (strncmp(&nvp->nvram[i + 8], "=pcie/", 6) == 0)) {
++			if (((nvp->nvram[i + 14] - '0') == domain_nr) &&
++			    ((nvp->nvram[i + 16] - '0') == bus_nr)) {
++				id = nvp->nvram[i + 7] - '0';
++				found = true;
++				break;
++			}
++		}
++		while (nvp->nvram[i] != 0)
++			i++;
++		i++;
++	}
++	if (!found)
++		goto fail;
++
++	/* Now copy all valid entries, release old nvram and assign new one */
++	i = 0;
++	j = 0;
++	while (i < nvp->nvram_len) {
++		if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) {
++			i += 2;
++			while (nvp->nvram[i] != 0) {
++				nvram[j] = nvp->nvram[i];
++				i++;
++				j++;
++			}
++			nvram[j] = 0;
++			j++;
++		}
++		while (nvp->nvram[i] != 0)
++			i++;
++		i++;
++	}
++	kfree(nvp->nvram);
++	nvp->nvram = nvram;
++	nvp->nvram_len = j;
++	return;
++
++fail:
++	kfree(nvram);
++	nvp->nvram_len = 0;
++}
++
++/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple
++ * devices. Strip it down for one device, use domain_nr/bus_nr to determine
++ * which data is to be returned. v2 is the version where nvram is stored
++ * uncompressed, all relevant valid entries are identified by
++ * pcie/domain_nr/bus_nr:
++ */
++static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr,
++				    u16 bus_nr)
++{
++	u32 i, j;
++	u8 *nvram;
++
++	nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
++	if (!nvram)
++		goto fail;
++
++	/* Copy all valid entries, release old nvram and assign new one.
++	 * Valid entries are of type pcie/X/Y/ where X = domain_nr and
++	 * Y = bus_nr.
++	 */
++	i = 0;
++	j = 0;
++	while (i < nvp->nvram_len - BRCMF_FW_NVRAM_PCIEDEV_LEN) {
++		if ((strncmp(&nvp->nvram[i], "pcie/", 5) == 0) &&
++		    (nvp->nvram[i + 6] == '/') && (nvp->nvram[i + 8] == '/') &&
++		    ((nvp->nvram[i + 5] - '0') == domain_nr) &&
++		    ((nvp->nvram[i + 7] - '0') == bus_nr)) {
++			i += BRCMF_FW_NVRAM_PCIEDEV_LEN;
++			while (nvp->nvram[i] != 0) {
++				nvram[j] = nvp->nvram[i];
++				i++;
++				j++;
++			}
++			nvram[j] = 0;
++			j++;
++		}
++		while (nvp->nvram[i] != 0)
++			i++;
++		i++;
++	}
++	kfree(nvp->nvram);
++	nvp->nvram = nvram;
++	nvp->nvram_len = j;
++	return;
++fail:
++	kfree(nvram);
++	nvp->nvram_len = 0;
++}
++
+ /* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
+  * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
+  * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
+  * End of buffer is completed with token identifying length of buffer.
+  */
+-static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length)
++static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length,
++				  u16 domain_nr, u16 bus_nr)
+ {
+ 	struct nvram_parser nvp;
+ 	u32 pad;
+@@ -212,6 +358,16 @@ static void *brcmf_fw_nvram_strip(const
+ 		if (nvp.state == END)
+ 			break;
+ 	}
++	if (nvp.multi_dev_v1)
++		brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr);
++	else if (nvp.multi_dev_v2)
++		brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr);
++
++	if (nvp.nvram_len == 0) {
++		kfree(nvp.nvram);
++		return NULL;
++	}
++
+ 	pad = nvp.nvram_len;
+ 	*new_length = roundup(nvp.nvram_len + 1, 4);
+ 	while (pad != *new_length) {
+@@ -239,6 +395,8 @@ struct brcmf_fw {
+ 	u16 flags;
+ 	const struct firmware *code;
+ 	const char *nvram_name;
++	u16 domain_nr;
++	u16 bus_nr;
+ 	void (*done)(struct device *dev, const struct firmware *fw,
+ 		     void *nvram_image, u32 nvram_len);
+ };
+@@ -254,7 +412,8 @@ static void brcmf_fw_request_nvram_done(
+ 		goto fail;
+ 
+ 	if (fw) {
+-		nvram = brcmf_fw_nvram_strip(fw, &nvram_length);
++		nvram = brcmf_fw_nvram_strip(fw, &nvram_length,
++					     fwctx->domain_nr, fwctx->bus_nr);
+ 		release_firmware(fw);
+ 		if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+ 			goto fail;
+@@ -309,11 +468,12 @@ fail:
+ 	kfree(fwctx);
+ }
+ 
+-int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+-			   const char *code, const char *nvram,
+-			   void (*fw_cb)(struct device *dev,
+-					 const struct firmware *fw,
+-					 void *nvram_image, u32 nvram_len))
++int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
++				const char *code, const char *nvram,
++				void (*fw_cb)(struct device *dev,
++					      const struct firmware *fw,
++					      void *nvram_image, u32 nvram_len),
++				u16 domain_nr, u16 bus_nr)
+ {
+ 	struct brcmf_fw *fwctx;
+ 
+@@ -333,8 +493,21 @@ int brcmf_fw_get_firmwares(struct device
+ 	fwctx->done = fw_cb;
+ 	if (flags & BRCMF_FW_REQUEST_NVRAM)
+ 		fwctx->nvram_name = nvram;
++	fwctx->domain_nr = domain_nr;
++	fwctx->bus_nr = bus_nr;
+ 
+ 	return request_firmware_nowait(THIS_MODULE, true, code, dev,
+ 				       GFP_KERNEL, fwctx,
+ 				       brcmf_fw_request_code_done);
+ }
++
++int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
++			   const char *code, const char *nvram,
++			   void (*fw_cb)(struct device *dev,
++					 const struct firmware *fw,
++					 void *nvram_image, u32 nvram_len))
++{
++	return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0,
++					   0);
++}
++
+--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
+@@ -32,6 +32,12 @@ void brcmf_fw_nvram_free(void *nvram);
+  * fails it will not use the callback, but call device_release_driver()
+  * instead which will call the driver .remove() callback.
+  */
++int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
++				const char *code, const char *nvram,
++				void (*fw_cb)(struct device *dev,
++					      const struct firmware *fw,
++					      void *nvram_image, u32 nvram_len),
++				u16 domain_nr, u16 bus_nr);
+ int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+ 			   const char *code, const char *nvram,
+ 			   void (*fw_cb)(struct device *dev,
+--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+@@ -1649,8 +1649,13 @@ brcmf_pcie_probe(struct pci_dev *pdev, c
+ 	struct brcmf_pciedev_info *devinfo;
+ 	struct brcmf_pciedev *pcie_bus_dev;
+ 	struct brcmf_bus *bus;
++	u16 domain_nr;
++	u16 bus_nr;
+ 
+-	brcmf_dbg(PCIE, "Enter %x:%x\n", pdev->vendor, pdev->device);
++	domain_nr = pci_domain_nr(pdev->bus) + 1;
++	bus_nr = pdev->bus->number;
++	brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device,
++		  domain_nr, bus_nr);
+ 
+ 	ret = -ENOMEM;
+ 	devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
+@@ -1699,10 +1704,10 @@ brcmf_pcie_probe(struct pci_dev *pdev, c
+ 	if (ret)
+ 		goto fail_bus;
+ 
+-	ret = brcmf_fw_get_firmwares(bus->dev, BRCMF_FW_REQUEST_NVRAM |
+-					       BRCMF_FW_REQ_NV_OPTIONAL,
+-				     devinfo->fw_name, devinfo->nvram_name,
+-				     brcmf_pcie_setup);
++	ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM |
++						    BRCMF_FW_REQ_NV_OPTIONAL,
++					  devinfo->fw_name, devinfo->nvram_name,
++					  brcmf_pcie_setup, domain_nr, bus_nr);
+ 	if (ret == 0)
+ 		return 0;
+ fail_bus:
-- 
GitLab