From 5704dcddfb1cfa9b24824a53e088929c4ef6e9e5 Mon Sep 17 00:00:00 2001
From: Shayne Chen <shayne.chen@mediatek.com>
Date: Thu, 16 Mar 2023 12:38:39 +0800
Subject: [PATCH 07/70] sync backport patches/brcm

---
 drivers/net/wireless/broadcom/b43/Kconfig     |  2 +-
 drivers/net/wireless/broadcom/b43/Makefile    |  2 +-
 drivers/net/wireless/broadcom/b43/b43.h       |  3 +
 drivers/net/wireless/broadcom/b43/dma.h       |  2 +-
 drivers/net/wireless/broadcom/b43/main.c      | 82 ++++++++++++++++---
 drivers/net/wireless/broadcom/b43/pio.h       | 34 +++++++-
 .../net/wireless/broadcom/brcm80211/Kconfig   |  2 +-
 .../broadcom/brcm80211/brcmfmac/cfg80211.c    | 32 ++++++++
 .../broadcom/brcm80211/brcmfmac/common.c      |  4 +
 .../broadcom/brcm80211/brcmfmac/firmware.c    | 14 ++++
 .../wireless/broadcom/brcm80211/brcmfmac/of.c | 68 +++++++++++++++
 .../wireless/broadcom/brcm80211/brcmfmac/of.h |  7 ++
 .../broadcom/brcm80211/brcmfmac/sdio.c        | 45 +++++++++-
 .../broadcom/brcm80211/brcmsmac/channel.c     | 19 ++---
 14 files changed, 285 insertions(+), 31 deletions(-)

diff --git a/drivers/net/wireless/broadcom/b43/Kconfig b/drivers/net/wireless/broadcom/b43/Kconfig
index 84cbe38..5f008b4 100644
--- a/drivers/net/wireless/broadcom/b43/Kconfig
+++ b/drivers/net/wireless/broadcom/b43/Kconfig
@@ -100,7 +100,7 @@ config B43_BCMA_PIO
 	default y
 
 config B43_PIO
-	bool
+	bool "Broadcom 43xx PIO support"
 	depends on B43 && B43_SSB
 	depends on SSB_BLOCKIO
 	default y
diff --git a/drivers/net/wireless/broadcom/b43/Makefile b/drivers/net/wireless/broadcom/b43/Makefile
index f20b692..48589b5 100644
--- a/drivers/net/wireless/broadcom/b43/Makefile
+++ b/drivers/net/wireless/broadcom/b43/Makefile
@@ -18,7 +18,7 @@ b43-$(CPTCFG_B43_PHY_AC)	+= phy_ac.o
 b43-y				+= sysfs.o
 b43-y				+= xmit.o
 b43-y				+= dma.o
-b43-y				+= pio.o
+b43-$(CPTCFG_B43_PIO)		+= pio.o
 b43-y				+= rfkill.o
 b43-y				+= ppr.o
 b43-$(CPTCFG_B43_LEDS)		+= leds.o
diff --git a/drivers/net/wireless/broadcom/b43/b43.h b/drivers/net/wireless/broadcom/b43/b43.h
index 872ebd7..745736c 100644
--- a/drivers/net/wireless/broadcom/b43/b43.h
+++ b/drivers/net/wireless/broadcom/b43/b43.h
@@ -840,6 +840,9 @@ struct b43_wldev {
 	bool qos_enabled;		/* TRUE, if QoS is used. */
 	bool hwcrypto_enabled;		/* TRUE, if HW crypto acceleration is enabled. */
 	bool use_pio;			/* TRUE if next init should use PIO */
+	int gpiomask;			/* GPIO LED mask as a module parameter */
+	int rx_antenna;			/* Used RX antenna (B43_ANTENNAxxx) */
+	int tx_antenna;			/* Used TX antenna (B43_ANTENNAxxx) */
 
 	/* PHY/Radio device. */
 	struct b43_phy phy;
diff --git a/drivers/net/wireless/broadcom/b43/dma.h b/drivers/net/wireless/broadcom/b43/dma.h
index dfebc64..ec72414 100644
--- a/drivers/net/wireless/broadcom/b43/dma.h
+++ b/drivers/net/wireless/broadcom/b43/dma.h
@@ -170,7 +170,7 @@ struct b43_dmadesc_generic {
 
 /* DMA engine tuning knobs */
 #define B43_TXRING_SLOTS		256
-#define B43_RXRING_SLOTS		256
+#define B43_RXRING_SLOTS		32
 #define B43_DMA0_RX_FW598_BUFSIZE	(B43_DMA0_RX_FW598_FO + IEEE80211_MAX_FRAME_LEN)
 #define B43_DMA0_RX_FW351_BUFSIZE	(B43_DMA0_RX_FW351_FO + IEEE80211_MAX_FRAME_LEN)
 
diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
index 2b5a292..7865412 100644
--- a/drivers/net/wireless/broadcom/b43/main.c
+++ b/drivers/net/wireless/broadcom/b43/main.c
@@ -72,6 +72,11 @@ MODULE_FIRMWARE("b43/ucode40.fw");
 MODULE_FIRMWARE("b43/ucode42.fw");
 MODULE_FIRMWARE("b43/ucode9.fw");
 
+static int modparam_gpiomask = 0x000F;
+module_param_named(gpiomask, modparam_gpiomask, int, 0444);
+MODULE_PARM_DESC(gpiomask,
+         "GPIO mask for LED control (default 0x000F)");
+
 static int modparam_bad_frames_preempt;
 module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444);
 MODULE_PARM_DESC(bad_frames_preempt,
@@ -109,7 +114,7 @@ static int b43_modparam_pio;
 module_param_named(pio, b43_modparam_pio, int, 0644);
 MODULE_PARM_DESC(pio, "Use PIO accesses by default: 0=DMA, 1=PIO");
 
-static int modparam_allhwsupport = !IS_ENABLED(CPTCFG_BRCMSMAC);
+static int modparam_allhwsupport = 1;
 module_param_named(allhwsupport, modparam_allhwsupport, int, 0444);
 MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if overlaps with the brcmsmac driver)");
 
@@ -1638,7 +1643,7 @@ static void b43_write_beacon_template(struct b43_wldev *dev,
 				  len, ram_offset, shm_size_offset, rate);
 
 	/* Write the PHY TX control parameters. */
-	antenna = B43_ANTENNA_DEFAULT;
+	antenna = dev->tx_antenna;
 	antenna = b43_antenna_to_phyctl(antenna);
 	ctl = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL);
 	/* We can't send beacons with short preamble. Would get PHY errors. */
@@ -1996,10 +2001,12 @@ static void b43_do_interrupt_thread(struct b43_wldev *dev)
 			dma_reason[0], dma_reason[1],
 			dma_reason[2], dma_reason[3],
 			dma_reason[4], dma_reason[5]);
+#ifdef CPTCFG_B43_PIO
 		b43err(dev->wl, "This device does not support DMA "
 			       "on your system. It will now be switched to PIO.\n");
 		/* Fall back to PIO transfers if we get fatal DMA errors! */
 		dev->use_pio = true;
+#endif
 		b43_controller_restart(dev, "DMA error");
 		return;
 	}
@@ -2869,16 +2876,24 @@ static int b43_gpio_init(struct b43_wldev *dev)
 	u32 mask, set;
 
 	b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0);
-	b43_maskset16(dev, B43_MMIO_GPIO_MASK, ~0, 0xF);
+	b43_maskset16(dev, B43_MMIO_GPIO_MASK, ~0, modparam_gpiomask);
 
 	mask = 0x0000001F;
-	set = 0x0000000F;
+	set = modparam_gpiomask;
 	if (dev->dev->chip_id == 0x4301) {
 		mask |= 0x0060;
 		set |= 0x0060;
 	} else if (dev->dev->chip_id == 0x5354) {
 		/* Don't allow overtaking buttons GPIOs */
 		set &= 0x2; /* 0x2 is LED GPIO on BCM5354 */
+	} else if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 || 
+		   dev->dev->chip_id == BCMA_CHIP_ID_BCM47162 ||
+		   dev->dev->chip_id == BCMA_CHIP_ID_BCM5356 ||
+		   dev->dev->chip_id == BCMA_CHIP_ID_BCM5357 ||
+		   dev->dev->chip_id == BCMA_CHIP_ID_BCM53572) {
+		/* just use gpio 0 and 1 for 2.4 GHz wifi led */
+		set &= 0x3;
+		mask &= 0x3;
 	}
 
 	if (0 /* FIXME: conditional unknown */ ) {
@@ -3277,8 +3292,8 @@ static int b43_chip_init(struct b43_wldev *dev)
 
 	/* Select the antennae */
 	if (phy->ops->set_rx_antenna)
-		phy->ops->set_rx_antenna(dev, B43_ANTENNA_DEFAULT);
-	b43_mgmtframe_txantenna(dev, B43_ANTENNA_DEFAULT);
+		phy->ops->set_rx_antenna(dev, dev->rx_antenna);
+	b43_mgmtframe_txantenna(dev, dev->tx_antenna);
 
 	if (phy->type == B43_PHYTYPE_B) {
 		value16 = b43_read16(dev, 0x005E);
@@ -3979,7 +3994,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
 	struct b43_wldev *dev = wl->current_dev;
 	struct b43_phy *phy = &dev->phy;
 	struct ieee80211_conf *conf = &hw->conf;
-	int antenna;
 	int err = 0;
 
 	mutex_lock(&wl->mutex);
@@ -4022,11 +4036,9 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
 	}
 
 	/* Antennas for RX and management frame TX. */
-	antenna = B43_ANTENNA_DEFAULT;
-	b43_mgmtframe_txantenna(dev, antenna);
-	antenna = B43_ANTENNA_DEFAULT;
+	b43_mgmtframe_txantenna(dev, dev->tx_antenna);
 	if (phy->ops->set_rx_antenna)
-		phy->ops->set_rx_antenna(dev, antenna);
+		phy->ops->set_rx_antenna(dev, dev->rx_antenna);
 
 	if (wl->radio_enabled != phy->radio_on) {
 		if (wl->radio_enabled) {
@@ -5169,6 +5181,47 @@ static int b43_op_get_survey(struct ieee80211_hw *hw, int idx,
 	return 0;
 }
 
+static int b43_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+{
+	struct b43_wl *wl = hw_to_b43_wl(hw);
+	struct b43_wldev *dev = wl->current_dev;
+
+	if (tx_ant == 1 && rx_ant == 1) {
+		dev->tx_antenna = B43_ANTENNA0;
+		dev->rx_antenna = B43_ANTENNA0;
+	}
+	else if (tx_ant == 2 && rx_ant == 2) {
+		dev->tx_antenna = B43_ANTENNA1;
+		dev->rx_antenna = B43_ANTENNA1;
+	}
+	else if ((tx_ant & 3) == 3 && (rx_ant & 3) == 3) {
+		dev->tx_antenna = B43_ANTENNA_DEFAULT;
+		dev->rx_antenna = B43_ANTENNA_DEFAULT;
+	}
+	else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static int b43_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
+{
+	struct b43_wl *wl = hw_to_b43_wl(hw);
+	struct b43_wldev *dev = wl->current_dev;
+
+	switch (dev->tx_antenna) {
+	case B43_ANTENNA0:
+		*tx_ant = 1; *rx_ant = 1; break;
+	case B43_ANTENNA1:
+		*tx_ant = 2; *rx_ant = 2; break;
+	case B43_ANTENNA_DEFAULT:
+		*tx_ant = 3; *rx_ant = 3; break;
+	}
+	return 0;
+}
+
 static const struct ieee80211_ops b43_hw_ops = {
 	.tx			= b43_op_tx,
 	.wake_tx_queue		= ieee80211_handle_wake_tx_queue,
@@ -5191,6 +5244,8 @@ static const struct ieee80211_ops b43_hw_ops = {
 	.sw_scan_complete	= b43_op_sw_scan_complete_notifier,
 	.get_survey		= b43_op_get_survey,
 	.rfkill_poll		= b43_rfkill_poll,
+	.set_antenna		= b43_op_set_antenna,
+	.get_antenna		= b43_op_get_antenna,
 };
 
 /* Hard-reset the chip. Do not call this directly.
@@ -5492,6 +5547,8 @@ static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl)
 	if (!wldev)
 		goto out;
 
+	wldev->rx_antenna = B43_ANTENNA_DEFAULT;
+	wldev->tx_antenna = B43_ANTENNA_DEFAULT;
 	wldev->use_pio = b43_modparam_pio;
 	wldev->dev = dev;
 	wldev->wl = wl;
@@ -5583,6 +5640,9 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev)
 
 	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
 
+	hw->wiphy->available_antennas_rx = 0x3;
+	hw->wiphy->available_antennas_tx = 0x3;
+
 	wl->hw_registered = false;
 	hw->max_rates = 2;
 	SET_IEEE80211_DEV(hw, dev->dev);
diff --git a/drivers/net/wireless/broadcom/b43/pio.h b/drivers/net/wireless/broadcom/b43/pio.h
index ffbfec6..a3742b7 100644
--- a/drivers/net/wireless/broadcom/b43/pio.h
+++ b/drivers/net/wireless/broadcom/b43/pio.h
@@ -151,7 +151,7 @@ static inline void b43_piorx_write32(struct b43_pio_rxqueue *q,
 	b43_write32(q->dev, q->mmio_base + offset, value);
 }
 
-
+#ifdef CPTCFG_B43_PIO
 int b43_pio_init(struct b43_wldev *dev);
 void b43_pio_free(struct b43_wldev *dev);
 
@@ -162,5 +162,37 @@ void b43_pio_rx(struct b43_pio_rxqueue *q);
 
 void b43_pio_tx_suspend(struct b43_wldev *dev);
 void b43_pio_tx_resume(struct b43_wldev *dev);
+#else
+static inline int b43_pio_init(struct b43_wldev *dev)
+{
+	return 0;
+}
+
+static inline void b43_pio_free(struct b43_wldev *dev)
+{
+}
+
+static inline int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb)
+{
+	return 0;
+}
+
+static inline void b43_pio_handle_txstatus(struct b43_wldev *dev,
+					   const struct b43_txstatus *status)
+{
+}
+
+static inline void b43_pio_rx(struct b43_pio_rxqueue *q)
+{
+}
+
+static inline void b43_pio_tx_suspend(struct b43_wldev *dev)
+{
+}
+
+static inline void b43_pio_tx_resume(struct b43_wldev *dev)
+{
+}
+#endif /* CPTCFG_B43_PIO */
 
 #endif /* B43_PIO_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig
index b2d97b8..597a4c3 100644
--- a/drivers/net/wireless/broadcom/brcm80211/Kconfig
+++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config BRCMUTIL
-	tristate
+	tristate "Broadcom 802.11 driver utility functions"
 	depends on m
 
 config BRCMSMAC
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index a9690ec..af218ff 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -979,8 +979,36 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 	struct brcmf_pub *drvr = cfg->pub;
 	struct wireless_dev *wdev;
+	struct net_device *dev;
 	int err;
 
+	/*
+	 * There is a bug with in-firmware BSS management. When adding virtual
+	 * interface brcmfmac first tells firmware to create new BSS and then
+	 * it creates new struct net_device.
+	 *
+	 * If creating/registering netdev(ice) fails, BSS remains in some bugged
+	 * state. It conflicts with existing BSSes by overtaking their auth
+	 * requests.
+	 *
+	 * It results in one BSS (addresss X) sending beacons and another BSS
+	 * (address Y) replying to authentication requests. This makes interface
+	 * unusable as AP.
+	 *
+	 * To workaround this bug we may try to guess if register_netdev(ice)
+	 * will fail. The most obvious case is using interface name that already
+	 * exists. This is actually quite likely with brcmfmac & some user space
+	 * scripts as brcmfmac doesn't allow deleting virtual interfaces.
+	 * So this bug can be triggered even by something trivial like:
+	 * iw dev wlan0 delete
+	 * iw phy phy0 interface add wlan0 type __ap
+	 */
+	dev = dev_get_by_name(&init_net, name);
+	if (dev) {
+		dev_put(dev);
+		return ERR_PTR(-ENFILE);
+	}
+
 	brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
 	err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
 	if (err) {
@@ -3224,6 +3252,10 @@ brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
 	 * preference in cfg struct to apply this to
 	 * FW later while initializing the dongle
 	 */
+#if defined(CONFIG_ARCH_BCM2835)
+	brcmf_dbg(INFO, "power management disabled\n");
+	enabled = false;
+#endif
 	cfg->pwr_save = enabled;
 	if (!check_vif_up(ifp->vif)) {
 
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
index 051e256..c07b8bc 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -59,7 +59,11 @@ static int brcmf_fcmode;
 module_param_named(fcmode, brcmf_fcmode, int, 0);
 MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control");
 
+#if defined(CONFIG_ARCH_BCM2835)
+static int brcmf_roamoff = 1;
+#else
 static int brcmf_roamoff;
+#endif
 module_param_named(roamoff, brcmf_roamoff, int, 0400);
 MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine");
 
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
index 09d2f2d..6a2e518 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
@@ -459,6 +459,7 @@ struct brcmf_fw {
 	u32 curpos;
 	unsigned int board_index;
 	void (*done)(struct device *dev, int err, struct brcmf_fw_request *req);
+	struct completion *completion;
 };
 
 #ifdef CONFIG_EFI
@@ -686,6 +687,8 @@ static void brcmf_fw_request_done(const struct firmware *fw, void *ctx)
 		fwctx->req = NULL;
 	}
 	fwctx->done(fwctx->dev, ret, fwctx->req);
+	if (fwctx->completion)
+		complete(fwctx->completion);
 	kfree(fwctx);
 }
 
@@ -751,6 +754,8 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
 {
 	struct brcmf_fw_item *first = &req->items[0];
 	struct brcmf_fw *fwctx;
+	struct completion completion;
+	unsigned long time_left;
 	char *alt_path = NULL;
 	int ret;
 
@@ -768,6 +773,9 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
 	fwctx->dev = dev;
 	fwctx->req = req;
 	fwctx->done = fw_cb;
+ 
+	init_completion(&completion);
+	fwctx->completion = &completion;
 
 	/* First try alternative board-specific path if any */
 	if (fwctx->req->board_types[0])
@@ -787,6 +795,12 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
 	if (ret < 0)
 		brcmf_fw_request_done(NULL, fwctx);
 
+
+	time_left = wait_for_completion_timeout(&completion,
+						msecs_to_jiffies(5000));
+	if (!time_left && fwctx)
+		fwctx->completion = NULL;
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
index fdd0c9a..453ce68 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
@@ -11,6 +11,7 @@
 #include "debug.h"
 #include "core.h"
 #include "common.h"
+#include "firmware.h"
 #include "of.h"
 
 static int brcmf_of_get_country_codes(struct device *dev,
@@ -65,6 +66,36 @@ static int brcmf_of_get_country_codes(struct device *dev,
 	return 0;
 }
 
+/* TODO: FIXME: Use DT */
+static void brcmf_of_probe_cc(struct device *dev,
+			      struct brcmf_mp_device *settings)
+{
+	static struct brcmfmac_pd_cc_entry netgear_r8000_cc_ent[] = {
+		{ "JP", "JP", 78 },
+		{ "US", "Q2", 86 },
+	};
+	struct brcmfmac_pd_cc_entry *cc_ent = NULL;
+	int table_size = 0;
+
+	if (of_machine_is_compatible("netgear,r8000")) {
+		cc_ent = netgear_r8000_cc_ent;
+		table_size = ARRAY_SIZE(netgear_r8000_cc_ent);
+	}
+
+	if (cc_ent && table_size) {
+		struct brcmfmac_pd_cc *cc;
+		size_t memsize;
+
+		memsize = table_size * sizeof(struct brcmfmac_pd_cc_entry);
+		cc = devm_kzalloc(dev, sizeof(*cc) + memsize, GFP_KERNEL);
+		if (!cc)
+			return;
+		cc->table_size = table_size;
+		memcpy(cc->table, cc_ent, memsize);
+		settings->country_codes = cc;
+	}
+}
+
 void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
 		    struct brcmf_mp_device *settings)
 {
@@ -106,6 +137,8 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
 		of_node_put(root);
 	}
 
+	brcmf_of_probe_cc(dev, settings);
+
 	if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac"))
 		return;
 
@@ -136,3 +169,38 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
 	sdio->oob_irq_nr = irq;
 	sdio->oob_irq_flags = irqf;
 }
+
+struct brcmf_firmware_mapping *
+brcmf_of_fwnames(struct device *dev, u32 *fwname_count)
+{
+	struct device_node *np = dev->of_node;
+	struct brcmf_firmware_mapping *fwnames;
+	struct device_node *map_np, *fw_np;
+	int of_count;
+	int count = 0;
+
+	map_np = of_get_child_by_name(np, "firmwares");
+	of_count = of_get_child_count(map_np);
+	if (!of_count)
+		return NULL;
+
+	fwnames = devm_kcalloc(dev, of_count,
+			       sizeof(struct brcmf_firmware_mapping),
+			       GFP_KERNEL);
+
+	for_each_child_of_node(map_np, fw_np)
+	{
+		struct brcmf_firmware_mapping *cur = &fwnames[count];
+
+		if (of_property_read_u32(fw_np, "chipid", &cur->chipid) ||
+		    of_property_read_u32(fw_np, "revmask", &cur->revmask))
+			continue;
+		cur->fw_base = of_get_property(fw_np, "fw_base", NULL);
+		if (cur->fw_base)
+			count++;
+	}
+
+	*fwname_count = count;
+
+	return count ? fwnames : NULL;
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
index 10bf522..5b39a39 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
@@ -5,9 +5,16 @@
 #ifdef CONFIG_OF
 void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
 		    struct brcmf_mp_device *settings);
+struct brcmf_firmware_mapping *
+brcmf_of_fwnames(struct device *dev, u32 *map_count);
 #else
 static void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
 			   struct brcmf_mp_device *settings)
 {
 }
+static struct brcmf_firmware_mapping *
+brcmf_of_fwnames(struct device *dev, u32 *map_count)
+{
+	return NULL;
+}
 #endif /* CONFIG_OF */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 6b38d9d..7448261 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -35,6 +35,7 @@
 #include "core.h"
 #include "common.h"
 #include "bcdc.h"
+#include "of.h"
 
 #define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2500)
 #define CTL_DONE_TIMEOUT	msecs_to_jiffies(2500)
@@ -632,7 +633,7 @@ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-sdio.*.txt");
 /* per-board firmware binaries */
 MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-sdio.*.bin");
 
-static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
+static const struct brcmf_firmware_mapping sdio_fwnames[] = {
 	BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
 	BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0),
 	BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4),
@@ -660,6 +661,9 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
 	BRCMF_FW_ENTRY(CY_CC_43752_CHIP_ID, 0xFFFFFFFF, 43752)
 };
 
+static const struct brcmf_firmware_mapping *brcmf_sdio_fwnames = sdio_fwnames;
+static u32 brcmf_sdio_fwnames_count = ARRAY_SIZE(sdio_fwnames);
+
 #define TXCTL_CREDITS	2
 
 static void pkt_align(struct sk_buff *p, int len, int align)
@@ -4201,6 +4205,9 @@ static const struct brcmf_bus_ops brcmf_sdio_bus_ops = {
 #define BRCMF_SDIO_FW_NVRAM	1
 #define BRCMF_SDIO_FW_CLM	2
 
+static struct brcmf_fw_request *
+brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus);
+
 static void brcmf_sdio_firmware_callback(struct device *dev, int err,
 					 struct brcmf_fw_request *fwreq)
 {
@@ -4216,6 +4223,22 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
 
 	brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err);
 
+	if (err && brcmf_sdio_fwnames != sdio_fwnames) {
+		/* Try again with the standard firmware names */
+		brcmf_sdio_fwnames = sdio_fwnames;
+		brcmf_sdio_fwnames_count = ARRAY_SIZE(sdio_fwnames);
+		kfree(fwreq);
+		fwreq = brcmf_sdio_prepare_fw_request(bus);
+		if (!fwreq) {
+			err = -ENOMEM;
+			goto fail;
+		}
+		err = brcmf_fw_get_firmwares(dev, fwreq,
+					     brcmf_sdio_firmware_callback);
+		if (!err)
+			return;
+	}
+
 	if (err)
 		goto fail;
 
@@ -4426,7 +4449,7 @@ brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus)
 
 	fwreq = brcmf_fw_alloc_request(bus->ci->chip, bus->ci->chiprev,
 				       brcmf_sdio_fwnames,
-				       ARRAY_SIZE(brcmf_sdio_fwnames),
+				       brcmf_sdio_fwnames_count,
 				       fwnames, ARRAY_SIZE(fwnames));
 	if (!fwreq)
 		return NULL;
@@ -4446,6 +4469,9 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
 	struct brcmf_sdio *bus;
 	struct workqueue_struct *wq;
 	struct brcmf_fw_request *fwreq;
+	struct brcmf_firmware_mapping *of_fwnames, *fwnames = NULL;
+	const int fwname_size = sizeof(struct brcmf_firmware_mapping);
+	u32 of_fw_count;
 
 	brcmf_dbg(TRACE, "Enter\n");
 
@@ -4528,6 +4554,21 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
 
 	brcmf_dbg(INFO, "completed!!\n");
 
+	of_fwnames = brcmf_of_fwnames(sdiodev->dev, &of_fw_count);
+	if (of_fwnames)
+		fwnames = devm_kcalloc(sdiodev->dev,
+				       of_fw_count + brcmf_sdio_fwnames_count,
+				       fwname_size, GFP_KERNEL);
+
+	if (fwnames) {
+		/* The array is scanned in order, so overrides come first */
+		memcpy(fwnames, of_fwnames, of_fw_count * fwname_size);
+		memcpy(fwnames + of_fw_count, sdio_fwnames,
+		       brcmf_sdio_fwnames_count * fwname_size);
+		brcmf_sdio_fwnames = fwnames;
+		brcmf_sdio_fwnames_count += of_fw_count;
+	}
+
 	fwreq = brcmf_sdio_prepare_fw_request(bus);
 	if (!fwreq) {
 		ret = -ENOMEM;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
index 5a6d9c8..dbf197f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
@@ -58,19 +58,12 @@
 				 (((c) < 149) ? 3 : 4))))
 
 #define BRCM_2GHZ_2412_2462	REG_RULE(2412-10, 2462+10, 40, 0, 19, 0)
-#define BRCM_2GHZ_2467_2472	REG_RULE(2467-10, 2472+10, 20, 0, 19, \
-					 NL80211_RRF_NO_IR)
-
-#define BRCM_5GHZ_5180_5240	REG_RULE(5180-10, 5240+10, 40, 0, 21, \
-					 NL80211_RRF_NO_IR)
-#define BRCM_5GHZ_5260_5320	REG_RULE(5260-10, 5320+10, 40, 0, 21, \
-					 NL80211_RRF_DFS | \
-					 NL80211_RRF_NO_IR)
-#define BRCM_5GHZ_5500_5700	REG_RULE(5500-10, 5700+10, 40, 0, 21, \
-					 NL80211_RRF_DFS | \
-					 NL80211_RRF_NO_IR)
-#define BRCM_5GHZ_5745_5825	REG_RULE(5745-10, 5825+10, 40, 0, 21, \
-					 NL80211_RRF_NO_IR)
+#define BRCM_2GHZ_2467_2472	REG_RULE(2467-10, 2472+10, 20, 0, 19, 0)
+
+#define BRCM_5GHZ_5180_5240	REG_RULE(5180-10, 5240+10, 40, 0, 21, 0)
+#define BRCM_5GHZ_5260_5320	REG_RULE(5260-10, 5320+10, 40, 0, 21, 0)
+#define BRCM_5GHZ_5500_5700	REG_RULE(5500-10, 5700+10, 40, 0, 21, 0)
+#define BRCM_5GHZ_5745_5825	REG_RULE(5745-10, 5825+10, 40, 0, 21, 0)
 
 static const struct ieee80211_regdomain brcms_regdom_x2 = {
 	.n_reg_rules = 6,
-- 
2.39.2

