summaryrefslogtreecommitdiff
path: root/drivers/net/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c4
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c32
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h10
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c264
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h41
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c73
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h1
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c199
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h9
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c183
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h11
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c11
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h44
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c156
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c14
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c27
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c38
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c5
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c366
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h62
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c33
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c6
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h1
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h4
27 files changed, 1463 insertions, 139 deletions
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index a4f635820f35..1c183cd26e94 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1198,7 +1198,7 @@ static int ath10k_core_fetch_board_data_api_n(struct ath10k *ar,
out:
if (!ar->normal_mode_fw.board_data || !ar->normal_mode_fw.board_len) {
- ath10k_err(ar,
+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
"failed to fetch board data for %s from %s/%s\n",
boardname, ar->hw_params.fw.dir, filename);
ret = -ENODATA;
@@ -2301,7 +2301,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
ret = ath10k_core_fetch_firmware_files(ar);
if (ret) {
- ath10k_err(ar, "could not fetch firmware files (%d)\n", ret);
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "could not fetch firmware files (%d)\n", ret);
goto err_power_down;
}
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index f9e409caca68..03a84c6fb0c6 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -1790,6 +1790,10 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
ath10k_pci_safe_chip_reset(ar);
ath10k_pci_flush(ar);
+ ath10k_pci_sleep_sync(ar);
+
+ napi_synchronize(&ar->napi);
+ napi_disable(&ar->napi);
spin_lock_irqsave(&ar_pci->ps_lock, flags);
WARN_ON(ar_pci->ps_wake_refcount > 0);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
index dd6e27513cc1..6e0a31b2b1cb 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
@@ -47,6 +47,7 @@
#include "sdio.h"
#include "core.h"
#include "common.h"
+#include "cfg80211.h"
#define SDIOH_API_ACCESS_RETRY_LIMIT 2
@@ -54,6 +55,7 @@
#define SDIO_FUNC1_BLOCKSIZE 64
#define SDIO_FUNC2_BLOCKSIZE 512
+#define SDIO_4373_FUNC2_BLOCKSIZE 256
/* Maximum milliseconds to wait for F2 to come up */
#define SDIO_WAIT_F2RDY 3000
@@ -1038,6 +1040,7 @@ static void brcmf_sdiod_host_fixup(struct mmc_host *host)
static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
{
int ret = 0;
+ unsigned int f2_blksz = SDIO_FUNC2_BLOCKSIZE;
sdiodev->num_funcs = 2;
@@ -1049,11 +1052,18 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
sdio_release_host(sdiodev->func[1]);
goto out;
}
- ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE);
+
+ if (sdiodev->func[0]->device == SDIO_DEVICE_ID_CYPRESS_4373) {
+ f2_blksz = SDIO_4373_FUNC2_BLOCKSIZE;
+ }
+
+ ret = sdio_set_block_size(sdiodev->func[2], f2_blksz);
if (ret) {
brcmf_err("Failed to set F2 blocksize\n");
sdio_release_host(sdiodev->func[1]);
goto out;
+ } else {
+ brcmf_dbg(SDIO, "set F2 blocksize to %d\n", f2_blksz);
}
/* increase F2 timeout */
@@ -1101,12 +1111,14 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43364),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4339),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43428),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43012),
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
@@ -1173,6 +1185,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
dev_set_drvdata(&func->dev, bus_if);
dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
sdiodev->dev = &sdiodev->func[1]->dev;
+ dev_set_drvdata(&sdiodev->func[2]->dev, bus_if);
brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN);
@@ -1189,6 +1202,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
fail:
dev_set_drvdata(&func->dev, NULL);
dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
+ dev_set_drvdata(&sdiodev->func[2]->dev, NULL);
kfree(sdiodev->func[0]);
kfree(sdiodev);
kfree(bus_if);
@@ -1245,14 +1259,26 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
struct brcmf_bus *bus_if;
struct brcmf_sdio_dev *sdiodev;
mmc_pm_flag_t sdio_flags;
+ struct brcmf_cfg80211_info *config;
+ int retry = BRCMF_PM_WAIT_MAXRETRY;
func = container_of(dev, struct sdio_func, dev);
+ bus_if = dev_get_drvdata(dev);
+ config = bus_if->drvr->config;
+
brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
+
+ while (retry &&
+ config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) {
+ usleep_range(10000, 20000);
+ retry--;
+ }
+ if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING)
+ brcmf_err("timed out wait for cfg80211 suspended\n");
+
if (func->num != SDIO_FUNC_1)
return 0;
-
- bus_if = dev_get_drvdata(dev);
sdiodev = bus_if->bus_priv.sdio;
brcmf_sdiod_freezer_on(sdiodev);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
index 163ddc49f951..0b76a615708e 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
@@ -71,6 +71,7 @@ struct brcmf_bus_dcmd {
* @wowl_config: specify if dongle is configured for wowl when going to suspend
* @get_ramsize: obtain size of device memory.
* @get_memdump: obtain device memory dump in provided buffer.
+ * @get_fwname: obtain firmware name.
*
* This structure provides an abstract interface towards the
* bus specific driver. For control messages to common driver
@@ -87,6 +88,8 @@ struct brcmf_bus_ops {
void (*wowl_config)(struct device *dev, bool enabled);
size_t (*get_ramsize)(struct device *dev);
int (*get_memdump)(struct device *dev, void *data, size_t len);
+ int (*get_fwname)(struct device *dev, uint chip, uint chiprev,
+ unsigned char *fw_name);
};
@@ -224,6 +227,13 @@ int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len)
return bus->ops->get_memdump(bus->dev, data, len);
}
+static inline
+int brcmf_bus_get_fwname(struct brcmf_bus *bus, uint chip, uint chiprev,
+ unsigned char *fw_name)
+{
+ return bus->ops->get_fwname(bus->dev, chip, chiprev, fw_name);
+}
+
/*
* interface functions from common layer
*/
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 04fa66ed99a0..76ad2b182358 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -1760,6 +1760,14 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
case WLAN_AKM_SUITE_PSK:
val = WPA2_AUTH_PSK;
break;
+ case WLAN_AKM_SUITE_FT_8021X:
+ val = WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT;
+ if (sme->want_1x)
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
+ break;
+ case WLAN_AKM_SUITE_FT_PSK:
+ val = WPA2_AUTH_PSK | WPA2_AUTH_FT;
+ break;
default:
brcmf_err("invalid cipher group (%d)\n",
sme->crypto.cipher_group);
@@ -3615,7 +3623,7 @@ static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
sizeof(wake_ind_le));
if (err) {
- brcmf_err("Get wowl_wakeind failed, err = %d\n", err);
+ brcmf_dbg(INFO, "cannet get wowl_wakeind, err = %d\n", err);
return;
}
@@ -3683,10 +3691,24 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct net_device *ndev = cfg_to_ndev(cfg);
struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_bus *bus_if = drvr->bus_if;
+ struct brcmf_cfg80211_info *config = drvr->config;
+ int retry = BRCMF_PM_WAIT_MAXRETRY;
brcmf_dbg(TRACE, "Enter\n");
+ config->pm_state = BRCMF_CFG80211_PM_STATE_RESUMING;
+
if (cfg->wowl.active) {
+ /* wait for bus resumed */
+ while (retry && bus_if->state != BRCMF_BUS_UP) {
+ usleep_range(10000, 20000);
+ retry--;
+ }
+ if (!retry && bus_if->state != BRCMF_BUS_UP)
+ brcmf_err("timed out wait for bus resume\n");
+
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);
@@ -3702,7 +3724,12 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
brcmf_notify_sched_scan_results);
cfg->wowl.nd_enabled = false;
}
+
+ /* disable packet filters */
+ brcmf_pktfilter_enable(ifp->ndev, false);
+
}
+ config->pm_state = BRCMF_CFG80211_PM_STATE_RESUMED;
return 0;
}
@@ -3760,6 +3787,9 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
brcmf_bus_wowl_config(cfg->pub->bus_if, true);
cfg->wowl.active = true;
+
+ /* enable packet filters */
+ brcmf_pktfilter_enable(ifp->ndev, true);
}
static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
@@ -3769,9 +3799,12 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
struct net_device *ndev = cfg_to_ndev(cfg);
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_cfg80211_vif *vif;
+ struct brcmf_cfg80211_info *config = ifp->drvr->config;
brcmf_dbg(TRACE, "Enter\n");
+ config->pm_state = BRCMF_CFG80211_PM_STATE_SUSPENDING;
+
/* if the primary net_device is not READY there is nothing
* we can do but pray resume goes smoothly.
*/
@@ -3786,7 +3819,8 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
brcmf_abort_scanning(cfg);
- if (wowl == NULL) {
+ if (!wowl || !test_bit(BRCMF_VIF_STATUS_CONNECTED,
+ &ifp->vif->sme_state)) {
brcmf_bus_wowl_config(cfg->pub->bus_if, false);
list_for_each_entry(vif, &cfg->vif_list, list) {
if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
@@ -3806,14 +3840,19 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
brcmf_set_mpc(ifp, 1);
} else {
- /* Configure WOWL paramaters */
- brcmf_configure_wowl(cfg, ifp, wowl);
+ /* Configure WOWL parameters */
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
+ brcmf_configure_wowl(cfg, ifp, wowl);
}
exit:
- brcmf_dbg(TRACE, "Exit\n");
+ /* set cfg80211 pm state to cfg80211 suspended state */
+ config->pm_state = BRCMF_CFG80211_PM_STATE_SUSPENDED;
+
/* clear any scanning activity */
cfg->scan_status = 0;
+
+ brcmf_dbg(TRACE, "Exit\n");
return 0;
}
@@ -4622,9 +4661,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
}
}
- if ((dev_role == NL80211_IFTYPE_AP) &&
- ((ifp->ifidx == 0) ||
- !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
+ if ((dev_role == NL80211_IFTYPE_AP) && (ifp->ifidx == 0)) {
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
if (err < 0) {
brcmf_err("BRCMF_C_DOWN error %d\n", err);
@@ -5255,6 +5292,26 @@ static int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev,
return brcmf_set_pmk(ifp, NULL, 0);
}
+static int
+brcmf_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev,
+ struct bss_parameters *params)
+{
+ struct brcmf_if *ifp;
+ int ret = 0;
+ u32 ap_isolate;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ ifp = netdev_priv(dev);
+ if (params->ap_isolate >= 0) {
+ ap_isolate = (u32)params->ap_isolate;
+ ret = brcmf_fil_iovar_int_set(ifp, "ap_isolate", ap_isolate);
+ if (ret < 0)
+ brcmf_err("ap_isolate iovar failed: ret=%d\n", ret);
+ }
+
+ return ret;
+}
+
static struct cfg80211_ops brcmf_cfg80211_ops = {
.add_virtual_intf = brcmf_cfg80211_add_iface,
.del_virtual_intf = brcmf_cfg80211_del_iface,
@@ -5300,6 +5357,7 @@ static struct cfg80211_ops brcmf_cfg80211_ops = {
.update_connect_params = brcmf_cfg80211_update_conn_params,
.set_pmk = brcmf_cfg80211_set_pmk,
.del_pmk = brcmf_cfg80211_del_pmk,
+ .change_bss = brcmf_cfg80211_change_bss,
};
struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
@@ -5434,11 +5492,124 @@ static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
conn_info->resp_ie_len = 0;
}
+u8 brcmf_map_prio_to_prec(void *config, u8 prio)
+{
+ struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
+
+ if (!cfg)
+ return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
+ (prio ^ 2) : prio;
+
+ /* For those AC(s) with ACM flag set to 1, convert its 4-level priority
+ * to an 8-level precedence which is the same as BE's */
+ if (prio > PRIO_8021D_EE &&
+ cfg->ac_priority[prio] == cfg->ac_priority[PRIO_8021D_BE])
+ return cfg->ac_priority[prio] * 2;
+
+ /* Conversion of 4-level priority to 8-level precedence */
+ if (prio == PRIO_8021D_BE || prio == PRIO_8021D_BK ||
+ prio == PRIO_8021D_CL || prio == PRIO_8021D_VO)
+ return cfg->ac_priority[prio] * 2;
+ else
+ return cfg->ac_priority[prio] * 2 + 1;
+}
+
+u8 brcmf_map_prio_to_aci(void *config, u8 prio)
+{
+ /* Prio here refers to the 802.1d priority in range of 0 to 7.
+ * ACI here refers to the WLAN AC Index in range of 0 to 3.
+ * This function will return ACI corresponding to input prio.
+ */
+ struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
+
+ if (cfg)
+ return cfg->ac_priority[prio];
+
+ return prio;
+}
+
+static void brcmf_wifi_prioritize_acparams(const
+ struct brcmf_cfg80211_edcf_acparam *acp, u8 *priority)
+{
+ u8 aci;
+ u8 aifsn;
+ u8 ecwmin;
+ u8 ecwmax;
+ u8 acm;
+ u8 ranking_basis[EDCF_AC_COUNT];
+ u8 aci_prio[EDCF_AC_COUNT]; /* AC_BE, AC_BK, AC_VI, AC_VO */
+ u8 index;
+
+ for (aci = 0; aci < EDCF_AC_COUNT; aci++, acp++) {
+ aifsn = acp->ACI & EDCF_AIFSN_MASK;
+ acm = (acp->ACI & EDCF_ACM_MASK) ? 1 : 0;
+ ecwmin = acp->ECW & EDCF_ECWMIN_MASK;
+ ecwmax = (acp->ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;
+ brcmf_dbg(CONN, "ACI %d aifsn %d acm %d ecwmin %d ecwmax %d\n",
+ aci, aifsn, acm, ecwmin, ecwmax);
+ /* Default AC_VO will be the lowest ranking value */
+ ranking_basis[aci] = aifsn + ecwmin + ecwmax;
+ /* Initialise priority starting at 0 (AC_BE) */
+ aci_prio[aci] = 0;
+
+ /* If ACM is set, STA can't use this AC as per 802.11.
+ * Change the ranking to BE
+ */
+ if (aci != AC_BE && aci != AC_BK && acm == 1)
+ ranking_basis[aci] = ranking_basis[AC_BE];
+ }
+
+ /* Ranking method which works for AC priority
+ * swapping when values for cwmin, cwmax and aifsn are varied
+ * Compare each aci_prio against each other aci_prio
+ */
+ for (aci = 0; aci < EDCF_AC_COUNT; aci++) {
+ for (index = 0; index < EDCF_AC_COUNT; index++) {
+ if (index != aci) {
+ /* Smaller ranking value has higher priority,
+ * so increment priority for each ACI which has
+ * a higher ranking value
+ */
+ if (ranking_basis[aci] < ranking_basis[index])
+ aci_prio[aci]++;
+ }
+ }
+ }
+
+ /* By now, aci_prio[] will be in range of 0 to 3.
+ * Use ACI prio to get the new priority value for
+ * each 802.1d traffic type, in this range.
+ */
+
+ /* 802.1d 0,3 maps to BE */
+ priority[0] = aci_prio[AC_BE];
+ priority[3] = aci_prio[AC_BE];
+
+ /* 802.1d 1,2 maps to BK */
+ priority[1] = aci_prio[AC_BK];
+ priority[2] = aci_prio[AC_BK];
+
+ /* 802.1d 4,5 maps to VO */
+ priority[4] = aci_prio[AC_VI];
+ priority[5] = aci_prio[AC_VI];
+
+ /* 802.1d 6,7 maps to VO */
+ priority[6] = aci_prio[AC_VO];
+ priority[7] = aci_prio[AC_VO];
+
+ brcmf_dbg(CONN, "Adj prio BE 0->%d, BK 1->%d, BK 2->%d, BE 3->%d\n",
+ priority[0], priority[1], priority[2], priority[3]);
+
+ brcmf_dbg(CONN, "Adj prio VI 4->%d, VI 5->%d, VO 6->%d, VO 7->%d\n",
+ priority[4], priority[5], priority[6], priority[7]);
+}
+
static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
struct brcmf_if *ifp)
{
struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
+ struct brcmf_cfg80211_edcf_acparam edcf_acparam_info[EDCF_AC_COUNT];
u32 req_len;
u32 resp_len;
s32 err = 0;
@@ -5487,6 +5658,17 @@ static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
GFP_KERNEL);
if (!conn_info->resp_ie)
conn_info->resp_ie_len = 0;
+
+ err = brcmf_fil_iovar_data_get(ifp, "wme_ac_sta",
+ edcf_acparam_info,
+ sizeof(edcf_acparam_info));
+ if (err) {
+ brcmf_err("could not get wme_ac_sta (%d)\n", err);
+ return err;
+ }
+
+ brcmf_wifi_prioritize_acparams(edcf_acparam_info,
+ cfg->ac_priority);
} else {
conn_info->resp_ie_len = 0;
conn_info->resp_ie = NULL;
@@ -5797,6 +5979,25 @@ static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
conf->retry_long = (u32)-1;
}
+static void brcmf_init_wmm_prio(u8 *priority)
+{
+ /* Initialize AC priority array to default
+ * 802.1d priority as per following table:
+ * 802.1d prio 0,3 maps to BE
+ * 802.1d prio 1,2 maps to BK
+ * 802.1d prio 4,5 maps to VI
+ * 802.1d prio 6,7 maps to VO
+ */
+ priority[0] = AC_BE;
+ priority[3] = AC_BE;
+ priority[1] = AC_BK;
+ priority[2] = AC_BK;
+ priority[4] = AC_VI;
+ priority[5] = AC_VI;
+ priority[6] = AC_VO;
+ priority[7] = AC_VO;
+}
+
static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
{
brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
@@ -5892,6 +6093,7 @@ static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
mutex_init(&cfg->usr_sync);
brcmf_init_escan(cfg);
brcmf_init_conf(cfg->conf);
+ brcmf_init_wmm_prio(cfg->ac_priority);
init_completion(&cfg->vif_disabled);
return err;
}
@@ -6408,6 +6610,16 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)
}
};
@@ -6553,6 +6765,7 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
#ifdef CONFIG_PM
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct wiphy_wowlan_support *wowl;
+ struct cfg80211_wowlan *brcmf_wowlan_config = NULL;
wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support),
GFP_KERNEL);
@@ -6575,6 +6788,27 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
}
wiphy->wowlan = wowl;
+
+ /* wowlan_config structure report for kernels */
+ brcmf_wowlan_config = kzalloc(sizeof(*brcmf_wowlan_config),
+ GFP_KERNEL);
+ if (brcmf_wowlan_config) {
+ brcmf_wowlan_config->any = false;
+ brcmf_wowlan_config->disconnect = true;
+ brcmf_wowlan_config->eap_identity_req = true;
+ brcmf_wowlan_config->four_way_handshake = true;
+ brcmf_wowlan_config->rfkill_release = false;
+ brcmf_wowlan_config->patterns = NULL;
+ brcmf_wowlan_config->n_patterns = 0;
+ brcmf_wowlan_config->tcp = NULL;
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
+ brcmf_wowlan_config->gtk_rekey_failure = true;
+ else
+ brcmf_wowlan_config->gtk_rekey_failure = false;
+ } else {
+ brcmf_err("Can not allocate memory for brcm_wowlan_config\n");
+ }
+ wiphy->wowlan_config = brcmf_wowlan_config;
#endif
}
@@ -6626,6 +6860,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
wiphy->flags |= WIPHY_FLAG_NETNS_OK |
WIPHY_FLAG_PS_ON_BY_DEFAULT |
+ WIPHY_FLAG_HAVE_AP_SME |
WIPHY_FLAG_OFFCHAN_TX |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
@@ -6648,8 +6883,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
wiphy->vendor_commands = brcmf_vendor_cmds;
wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
- brcmf_wiphy_wowl_params(wiphy, ifp);
+ brcmf_wiphy_wowl_params(wiphy, ifp);
err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
sizeof(bandlist));
if (err) {
@@ -6706,6 +6940,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
struct wireless_dev *wdev;
struct brcmf_if *ifp;
s32 power_mode;
+ s32 eap_restrict;
s32 err = 0;
if (cfg->dongle_up)
@@ -6730,6 +6965,14 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
err = brcmf_dongle_roam(ifp);
if (err)
goto default_conf_out;
+
+ eap_restrict = ifp->drvr->settings->eap_restrict;
+ if (eap_restrict) {
+ err = brcmf_fil_iovar_int_set(ifp, "eap_restrict",
+ eap_restrict);
+ if (err)
+ brcmf_info("eap_restrict error (%d)\n", err);
+ }
err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
NULL);
if (err)
@@ -7018,6 +7261,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
cfg->wiphy = wiphy;
cfg->ops = ops;
cfg->pub = drvr;
+ cfg->pm_state = BRCMF_CFG80211_PM_STATE_RESUMED;
init_vif_event(&cfg->vif_event);
INIT_LIST_HEAD(&cfg->vif_list);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index 7b2835e5e434..00faae6e50ab 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -34,6 +34,23 @@
#define WL_ROAM_TRIGGER_LEVEL -75
#define WL_ROAM_DELTA 20
+/* WME Access Category Indices (ACIs) */
+#define AC_BE 0 /* Best Effort */
+#define AC_BK 1 /* Background */
+#define AC_VI 2 /* Video */
+#define AC_VO 3 /* Voice */
+#define EDCF_AC_COUNT 4
+#define MAX_8021D_PRIO 8
+
+#define EDCF_ACI_MASK 0x60
+#define EDCF_ACI_SHIFT 5
+#define EDCF_ACM_MASK 0x10
+#define EDCF_ECWMIN_MASK 0x0f
+#define EDCF_ECWMAX_SHIFT 4
+#define EDCF_AIFSN_MASK 0x0f
+#define EDCF_AIFSN_MAX 15
+#define EDCF_ECWMAX_MASK 0xf0
+
/* Keep BRCMF_ESCAN_BUF_SIZE below 64K (65536). Allocing over 64K can be
* problematic on some systems and should be avoided.
*/
@@ -86,6 +103,15 @@
#define BRCMF_VIF_EVENT_TIMEOUT msecs_to_jiffies(1500)
+#define BRCMF_PM_WAIT_MAXRETRY 100
+
+/* cfg80211 wowlan definitions */
+#define WL_WOWLAN_MAX_PATTERNS 8
+#define WL_WOWLAN_MIN_PATTERN_LEN 1
+#define WL_WOWLAN_MAX_PATTERN_LEN 255
+#define WL_WOWLAN_PKT_FILTER_ID_FIRST 201
+#define WL_WOWLAN_PKT_FILTER_ID_LAST (WL_WOWLAN_PKT_FILTER_ID_FIRST + \
+ WL_WOWLAN_MAX_PATTERNS - 1)
/**
* enum brcmf_scan_status - scan engine status
*
@@ -156,6 +182,13 @@ enum brcmf_vif_status {
BRCMF_VIF_STATUS_ASSOC_SUCCESS,
};
+enum brcmf_cfg80211_pm_state {
+ BRCMF_CFG80211_PM_STATE_RESUMED,
+ BRCMF_CFG80211_PM_STATE_RESUMING,
+ BRCMF_CFG80211_PM_STATE_SUSPENDED,
+ BRCMF_CFG80211_PM_STATE_SUSPENDING,
+};
+
/**
* struct vif_saved_ie - holds saved IEs for a virtual interface.
*
@@ -214,6 +247,12 @@ struct brcmf_cfg80211_assoc_ielen_le {
__le32 resp_len;
};
+struct brcmf_cfg80211_edcf_acparam {
+ u8 ACI;
+ u8 ECW;
+ u16 TXOP; /* stored in network order (ls octet first) */
+};
+
/* dongle escan state */
enum wl_escan_state {
WL_ESCAN_STATE_IDLE,
@@ -335,6 +374,8 @@ struct brcmf_cfg80211_info {
struct brcmf_assoclist_le assoclist;
struct brcmf_cfg80211_wowl wowl;
struct brcmf_pno_info *pno;
+ u8 ac_priority[MAX_8021D_PRIO];
+ u8 pm_state;
};
/**
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
index c5d1a1cbf601..a62acf76f258 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
@@ -165,6 +165,7 @@ struct sbconfig {
#define SRCI_LSS_MASK 0x00f00000
#define SRCI_LSS_SHIFT 20
#define SRCI_SRNB_MASK 0xf0
+#define SRCI_SRNB_MASK_EXT 0x100
#define SRCI_SRNB_SHIFT 4
#define SRCI_SRBSZ_MASK 0xf
#define SRCI_SRBSZ_SHIFT 0
@@ -443,11 +444,25 @@ static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
{
struct brcmf_chip_priv *ci;
int count;
+ struct brcmf_core *d11core2 = NULL;
+ struct brcmf_core_priv *d11priv2 = NULL;
ci = core->chip;
+ /* special handle two D11 cores reset */
+ if (core->pub.id == BCMA_CORE_80211) {
+ d11core2 = brcmf_chip_get_d11core(&ci->pub, 1);
+ if (d11core2) {
+ brcmf_dbg(INFO, "found two d11 cores, reset both\n");
+ d11priv2 = container_of(d11core2, struct brcmf_core_priv,
+ pub);
+ }
+ }
+
/* must disable first to work for arbitrary current core state */
brcmf_chip_ai_coredisable(core, prereset, reset);
+ if (d11priv2)
+ brcmf_chip_ai_coredisable(d11priv2, prereset, reset);
count = 0;
while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) &
@@ -459,9 +474,30 @@ static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
usleep_range(40, 60);
}
+ if (d11priv2) {
+ count = 0;
+ while (ci->ops->read32(ci->ctx,
+ d11priv2->wrapbase + BCMA_RESET_CTL) &
+ BCMA_RESET_CTL_RESET) {
+ ci->ops->write32(ci->ctx,
+ d11priv2->wrapbase + BCMA_RESET_CTL,
+ 0);
+ count++;
+ if (count > 50)
+ break;
+ usleep_range(40, 60);
+ }
+ }
+
ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
postreset | BCMA_IOCTL_CLK);
ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+
+ if (d11priv2) {
+ ci->ops->write32(ci->ctx, d11priv2->wrapbase + BCMA_IOCTL,
+ postreset | BCMA_IOCTL_CLK);
+ ci->ops->read32(ci->ctx, d11priv2->wrapbase + BCMA_IOCTL);
+ }
}
static char *brcmf_chip_name(uint chipid, char *buf, uint len)
@@ -592,7 +628,13 @@ static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize,
if (lss != 0)
*ramsize += (1 << ((lss - 1) + SR_BSZ_BASE));
} else {
- nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+ /* length of SRAM Banks increased for corerev greater than 23 */
+ if (sr->pub.rev >= 23) {
+ nb = (coreinfo & (SRCI_SRNB_MASK | SRCI_SRNB_MASK_EXT))
+ >> SRCI_SRNB_SHIFT;
+ } else {
+ nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+ }
for (i = 0; i < nb; i++) {
retent = brcmf_chip_socram_banksize(sr, i, &banksize);
*ramsize += banksize;
@@ -1112,6 +1154,21 @@ void brcmf_chip_detach(struct brcmf_chip *pub)
kfree(chip);
}
+struct brcmf_core *brcmf_chip_get_d11core(struct brcmf_chip *pub, u8 unit)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core_priv *core;
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ list_for_each_entry(core, &chip->cores, list) {
+ if (core->pub.id == BCMA_CORE_80211) {
+ if (unit-- == 0)
+ return &core->pub;
+ }
+ }
+ return NULL;
+}
+
struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid)
{
struct brcmf_chip_priv *chip;
@@ -1322,7 +1379,7 @@ bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec)
bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
{
- u32 base, addr, reg, pmu_cc3_mask = ~0;
+ u32 base, addr, reg, sr_eng_en, pmu_cc3_mask = ~0;
struct brcmf_chip_priv *chip;
struct brcmf_core *pmu = brcmf_chip_get_pmu(pub);
@@ -1338,6 +1395,7 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
switch (pub->chip) {
case BRCM_CC_4354_CHIP_ID:
case BRCM_CC_4356_CHIP_ID:
+ case BRCM_CC_4345_CHIP_ID:
/* explicitly check SR engine enable bit */
pmu_cc3_mask = BIT(2);
/* fall-through */
@@ -1354,6 +1412,17 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
addr = CORE_CC_REG(base, sr_control1);
reg = chip->ops->read32(chip->ctx, addr);
return reg != 0;
+ case CY_CC_4373_CHIP_ID:
+ /* explicitly check SR engine enable bit */
+ sr_eng_en = BIT(0);
+ addr = CORE_CC_REG(base, sr_control0);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return (reg & sr_eng_en) != 0;
+ case CY_CC_43012_CHIP_ID:
+ addr = CORE_CC_REG(pmu->base, retention_ctl);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
+ PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
default:
addr = CORE_CC_REG(pmu->base, pmucapabilities_ext);
reg = chip->ops->read32(chip->ctx, addr);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
index dd0ec3eba6a9..fe80270d856c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
@@ -86,6 +86,7 @@ void brcmf_chip_detach(struct brcmf_chip *chip);
struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid);
struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip);
struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub);
+struct brcmf_core *brcmf_chip_get_d11core(struct brcmf_chip *pub, u8 unit);
bool brcmf_chip_iscoreup(struct brcmf_core *core);
void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset);
void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset,
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
index 7a2b49587b4d..7ca24d5e1b5b 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -18,6 +18,7 @@
#include <linux/string.h>
#include <linux/netdevice.h>
#include <linux/module.h>
+#include <linux/firmware.h>
#include <brcmu_wifi.h>
#include <brcmu_utils.h>
#include "core.h"
@@ -28,6 +29,7 @@
#include "tracepoint.h"
#include "common.h"
#include "of.h"
+#include "firmware.h"
MODULE_AUTHOR("Broadcom Corporation");
MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
@@ -73,6 +75,14 @@ static int brcmf_roamoff;
module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine");
+static int brcmf_eap_restrict;
+module_param_named(eap_restrict, brcmf_eap_restrict, int, 0400);
+MODULE_PARM_DESC(eap_restrict, "Block non-802.1X frames until auth finished");
+
+static int brcmf_sdio_wq_highpri;
+module_param_named(sdio_wq_highpri, brcmf_sdio_wq_highpri, int, 0);
+MODULE_PARM_DESC(sdio_wq_highpri, "SDIO workqueue is set to high priority");
+
#ifdef DEBUG
/* always succeed brcmf_bus_started() */
static int brcmf_ignore_probe_fail;
@@ -104,16 +114,141 @@ void brcmf_c_set_joinpref_default(struct brcmf_if *ifp)
brcmf_err("Set join_pref error (%d)\n", err);
}
+static int brcmf_c_download(struct brcmf_if *ifp, u16 flag,
+ struct brcmf_dload_data_le *dload_buf,
+ u32 len)
+{
+ s32 err;
+
+ flag |= (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT);
+ dload_buf->flag = cpu_to_le16(flag);
+ dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM);
+ dload_buf->len = cpu_to_le32(len);
+ dload_buf->crc = cpu_to_le32(0);
+ len = sizeof(*dload_buf) + len - 1;
+
+ err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, len);
+
+ return err;
+}
+
+static int brcmf_c_get_clm_name(struct brcmf_if *ifp, u8 *clm_name)
+{
+ struct brcmf_bus *bus = ifp->drvr->bus_if;
+ struct brcmf_rev_info *ri = &ifp->drvr->revinfo;
+ u8 fw_name[BRCMF_FW_NAME_LEN];
+ u8 *ptr;
+ size_t len;
+ s32 err;
+
+ memset(fw_name, 0, BRCMF_FW_NAME_LEN);
+ err = brcmf_bus_get_fwname(bus, ri->chipnum, ri->chiprev, fw_name);
+ if (err) {
+ brcmf_err("get firmware name failed (%d)\n", err);
+ goto done;
+ }
+
+ /* generate CLM blob file name */
+ ptr = strrchr(fw_name, '.');
+ if (!ptr) {
+ err = -ENOENT;
+ goto done;
+ }
+
+ len = ptr - fw_name + 1;
+ if (len + strlen(".clm_blob") > BRCMF_FW_NAME_LEN) {
+ err = -E2BIG;
+ } else {
+ strlcpy(clm_name, fw_name, len);
+ strlcat(clm_name, ".clm_blob", BRCMF_FW_NAME_LEN);
+ }
+done:
+ return err;
+}
+
+static int brcmf_c_process_clm_blob(struct brcmf_if *ifp)
+{
+ struct device *dev = ifp->drvr->bus_if->dev;
+ struct brcmf_dload_data_le *chunk_buf;
+ const struct firmware *clm = NULL;
+ u8 clm_name[BRCMF_FW_NAME_LEN];
+ u32 chunk_len;
+ u32 datalen;
+ u32 cumulative_len;
+ u16 dl_flag = DL_BEGIN;
+ u32 status;
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ memset(clm_name, 0, BRCMF_FW_NAME_LEN);
+ err = brcmf_c_get_clm_name(ifp, clm_name);
+ if (err) {
+ brcmf_err("get CLM blob file name failed (%d)\n", err);
+ return err;
+ }
+
+ err = request_firmware(&clm, clm_name, dev);
+ if (err) {
+ brcmf_info("no clm_blob available(err=%d), device may have limited channels available\n",
+ err);
+ return 0;
+ }
+
+ chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN - 1, GFP_KERNEL);
+ if (!chunk_buf) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ datalen = clm->size;
+ cumulative_len = 0;
+ do {
+ if (datalen > MAX_CHUNK_LEN) {
+ chunk_len = MAX_CHUNK_LEN;
+ } else {
+ chunk_len = datalen;
+ dl_flag |= DL_END;
+ }
+ memcpy(chunk_buf->data, clm->data + cumulative_len, chunk_len);
+
+ err = brcmf_c_download(ifp, dl_flag, chunk_buf, chunk_len);
+
+ dl_flag &= ~DL_BEGIN;
+
+ cumulative_len += chunk_len;
+ datalen -= chunk_len;
+ } while ((datalen > 0) && (err == 0));
+
+ if (err) {
+ brcmf_err("clmload (%zu byte file) failed (%d); ",
+ clm->size, err);
+ /* Retrieve clmload_status and print */
+ err = brcmf_fil_iovar_int_get(ifp, "clmload_status", &status);
+ if (err)
+ brcmf_err("get clmload_status failed (%d)\n", err);
+ else
+ brcmf_dbg(INFO, "clmload_status=%d\n", status);
+ err = -EIO;
+ }
+
+ kfree(chunk_buf);
+done:
+ release_firmware(clm);
+ return err;
+}
+
int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
{
s8 eventmask[BRCMF_EVENTING_MASK_LEN];
u8 buf[BRCMF_DCMD_SMLEN];
struct brcmf_rev_info_le revinfo;
struct brcmf_rev_info *ri;
+ char *clmver;
char *ptr;
s32 err;
- /* retreive mac address */
+ /* retrieve mac addresses */
err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
sizeof(ifp->mac_addr));
if (err < 0) {
@@ -148,6 +283,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
}
ri->result = err;
+ /* Do any CLM downloading */
+ err = brcmf_c_process_clm_blob(ifp);
+ if (err < 0) {
+ brcmf_err("download CLM blob file failed, %d\n", err);
+ goto done;
+ }
+
/* query for 'ver' to get version info from firmware */
memset(buf, 0, sizeof(buf));
strcpy(buf, "ver");
@@ -167,6 +309,26 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
ptr = strrchr(buf, ' ') + 1;
strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver));
+ /* Query for 'clmver' to get CLM version info from firmware */
+ memset(buf, 0, sizeof(buf));
+ err = brcmf_fil_iovar_data_get(ifp, "clmver", buf, sizeof(buf));
+ if (err) {
+ brcmf_dbg(TRACE, "retrieving clmver failed, %d\n", err);
+ } else {
+ clmver = (char *)buf;
+ /* store CLM version for adding it to revinfo debugfs file */
+ memcpy(ifp->drvr->clmver, clmver, sizeof(ifp->drvr->clmver));
+
+ /* Replace all newline/linefeed characters with space
+ * character
+ */
+ ptr = clmver;
+ while ((ptr = strnchr(ptr, '\n', sizeof(buf))) != NULL)
+ *ptr = ' ';
+
+ brcmf_dbg(INFO, "CLM version = %s\n", clmver);
+ }
+
/* set mpc */
err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
if (err) {
@@ -212,6 +374,12 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
/* Enable tx beamforming, errors can be ignored (not supported) */
(void)brcmf_fil_iovar_int_set(ifp, "txbf", 1);
+ /* add unicast packet filter */
+ err = brcmf_pktfilter_add_remove(ifp->ndev,
+ BRCMF_UNICAST_FILTER_NUM, true);
+ if (err)
+ brcmf_info("Add unicast filter error (%d)\n", err);
+
/* do bus specific preinit here */
err = brcmf_bus_preinit(ifp->drvr->bus_if);
done:
@@ -282,11 +450,13 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
if (!settings)
return NULL;
- /* start by using the module paramaters */
+ /* start by using the module parameters */
settings->p2p_enable = !!brcmf_p2p_enable;
settings->feature_disable = brcmf_feature_disable;
settings->fcmode = brcmf_fcmode;
settings->roamoff = !!brcmf_roamoff;
+ settings->eap_restrict = !!brcmf_eap_restrict;
+ settings->sdio_wq_highpri = !!brcmf_sdio_wq_highpri;
#ifdef DEBUG
settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;
#endif
@@ -297,6 +467,7 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
/* See if there is any device specific platform data configured */
found = false;
if (brcmfmac_pdata) {
+ pinctrl_pm_select_default_state(brcmfmac_pdata->dev);
for (i = 0; i < brcmfmac_pdata->device_count; i++) {
device_pd = &brcmfmac_pdata->devices[i];
if ((device_pd->bus_type == bus_type) &&
@@ -329,10 +500,30 @@ void brcmf_release_module_param(struct brcmf_mp_device *module_param)
static int __init brcmf_common_pd_probe(struct platform_device *pdev)
{
+ int err;
+ struct brcmfmac_platform_data pdata = {
+ .power_on = NULL,
+ .power_off = NULL,
+ .fw_alternative_path = NULL,
+ .device_count = 0,
+ };
+
brcmf_dbg(INFO, "Enter\n");
brcmfmac_pdata = dev_get_platdata(&pdev->dev);
+ if (!brcmfmac_pdata) {
+ err = platform_device_add_data(pdev, &pdata,
+ sizeof(pdata));
+ if (err)
+ brcmf_err("platform data allocation failed\n");
+ brcmfmac_pdata = dev_get_platdata(&pdev->dev);
+ pinctrl_pm_select_idle_state(&pdev->dev);
+ }
+
+ if (!brcmfmac_pdata)
+ return 0;
+ brcmfmac_pdata->dev = &pdev->dev;
if (brcmfmac_pdata->power_on)
brcmfmac_pdata->power_on();
@@ -343,7 +534,7 @@ static int brcmf_common_pd_remove(struct platform_device *pdev)
{
brcmf_dbg(INFO, "Enter\n");
- if (brcmfmac_pdata->power_off)
+ if (brcmfmac_pdata && brcmfmac_pdata->power_off)
brcmfmac_pdata->power_off();
return 0;
@@ -368,7 +559,7 @@ static int __init brcmfmac_module_init(void)
if (err == -ENODEV)
brcmf_dbg(INFO, "No platform data available.\n");
- /* Initialize global module paramaters */
+ /* Initialize global module parameters */
brcmf_mp_attach();
/* Continue the initialization by registering the different busses */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
index a62f8e70b320..f415f208db40 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
@@ -49,6 +49,8 @@ extern struct brcmf_mp_global_t brcmf_mp_global;
* @feature_disable: Feature_disable bitmask.
* @fcmode: FWS flow control.
* @roamoff: Firmware roaming off?
+ * @eap_restrict: Not allow data tx/rx until 802.1X auth succeeds
+ * @sdio_wq_highpri: Tasks submitted to SDIO workqueue will run immediately.
* @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.
@@ -58,6 +60,9 @@ struct brcmf_mp_device {
unsigned int feature_disable;
int fcmode;
bool roamoff;
+ bool eap_restrict;
+ int sdio_dpc_prio;
+ bool sdio_wq_highpri;
bool ignore_probe_fail;
struct brcmfmac_pd_cc *country_codes;
union {
@@ -75,4 +80,8 @@ 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);
+u8 brcmf_map_prio_to_prec(void *cfg, u8 prio);
+
+u8 brcmf_map_prio_to_aci(void *cfg, u8 prio);
+
#endif /* BRCMFMAC_COMMON_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index bfc0e37b7f34..df3061467c57 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -371,6 +371,11 @@ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
struct ethhdr *eh;
u16 type;
+ if (!ifp) {
+ brcmu_pkt_buf_free_skb(txp);
+ return;
+ }
+
eh = (struct ethhdr *)(txp->data);
type = ntohs(eh->h_proto);
@@ -414,8 +419,6 @@ static int brcmf_netdev_stop(struct net_device *ndev)
brcmf_cfg80211_down(ndev);
- brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear", NULL, 0);
-
brcmf_net_setcarrier(ifp, false);
return 0;
@@ -955,6 +958,8 @@ static int brcmf_revinfo_read(struct seq_file *s, void *data)
seq_printf(s, "anarev: %u\n", ri->anarev);
seq_printf(s, "nvramrev: %08x\n", ri->nvramrev);
+ seq_printf(s, "clmver: %s\n", bus_if->drvr->clmver);
+
return 0;
}
@@ -1201,3 +1206,177 @@ void __exit brcmf_core_exit(void)
#endif
}
+int
+brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num, bool add)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_pkt_filter_le *pkt_filter;
+ int filter_fixed_len = offsetof(struct brcmf_pkt_filter_le, u);
+ int pattern_fixed_len = offsetof(struct brcmf_pkt_filter_pattern_le,
+ mask_and_pattern);
+ u16 mask_and_pattern[MAX_PKTFILTER_PATTERN_SIZE];
+ int buflen = 0;
+ int ret = 0;
+
+ brcmf_dbg(INFO, "%s packet filter number %d\n",
+ (add ? "add" : "remove"), filter_num);
+
+ pkt_filter = kzalloc(sizeof(*pkt_filter) +
+ (MAX_PKTFILTER_PATTERN_SIZE * 2), GFP_ATOMIC);
+ if (!pkt_filter)
+ return -ENOMEM;
+
+ switch (filter_num) {
+ case BRCMF_UNICAST_FILTER_NUM:
+ pkt_filter->id = 100;
+ pkt_filter->type = 0;
+ pkt_filter->negate_match = 0;
+ pkt_filter->u.pattern.offset = 0;
+ pkt_filter->u.pattern.size_bytes = 1;
+ mask_and_pattern[0] = 0x0001;
+ break;
+ case BRCMF_BROADCAST_FILTER_NUM:
+ //filter_pattern = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF";
+ pkt_filter->id = 101;
+ pkt_filter->type = 0;
+ pkt_filter->negate_match = 0;
+ pkt_filter->u.pattern.offset = 0;
+ pkt_filter->u.pattern.size_bytes = 6;
+ mask_and_pattern[0] = 0xFFFF;
+ mask_and_pattern[1] = 0xFFFF;
+ mask_and_pattern[2] = 0xFFFF;
+ mask_and_pattern[3] = 0xFFFF;
+ mask_and_pattern[4] = 0xFFFF;
+ mask_and_pattern[5] = 0xFFFF;
+ break;
+ case BRCMF_MULTICAST4_FILTER_NUM:
+ //filter_pattern = "102 0 0 0 0xFFFFFF 0x01005E";
+ pkt_filter->id = 102;
+ pkt_filter->type = 0;
+ pkt_filter->negate_match = 0;
+ pkt_filter->u.pattern.offset = 0;
+ pkt_filter->u.pattern.size_bytes = 3;
+ mask_and_pattern[0] = 0xFFFF;
+ mask_and_pattern[1] = 0x01FF;
+ mask_and_pattern[2] = 0x5E00;
+ break;
+ case BRCMF_MULTICAST6_FILTER_NUM:
+ //filter_pattern = "103 0 0 0 0xFFFF 0x3333";
+ pkt_filter->id = 103;
+ pkt_filter->type = 0;
+ pkt_filter->negate_match = 0;
+ pkt_filter->u.pattern.offset = 0;
+ pkt_filter->u.pattern.size_bytes = 2;
+ mask_and_pattern[0] = 0xFFFF;
+ mask_and_pattern[1] = 0x3333;
+ break;
+ case BRCMF_MDNS_FILTER_NUM:
+ //filter_pattern = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB";
+ pkt_filter->id = 104;
+ pkt_filter->type = 0;
+ pkt_filter->negate_match = 0;
+ pkt_filter->u.pattern.offset = 0;
+ pkt_filter->u.pattern.size_bytes = 6;
+ mask_and_pattern[0] = 0xFFFF;
+ mask_and_pattern[1] = 0xFFFF;
+ mask_and_pattern[2] = 0xFFFF;
+ mask_and_pattern[3] = 0x0001;
+ mask_and_pattern[4] = 0x005E;
+ mask_and_pattern[5] = 0xFB00;
+ break;
+ case BRCMF_ARP_FILTER_NUM:
+ //filter_pattern = "105 0 0 12 0xFFFF 0x0806";
+ pkt_filter->id = 105;
+ pkt_filter->type = 0;
+ pkt_filter->negate_match = 0;
+ pkt_filter->u.pattern.offset = 12;
+ pkt_filter->u.pattern.size_bytes = 2;
+ mask_and_pattern[0] = 0xFFFF;
+ mask_and_pattern[1] = 0x0608;
+ break;
+ case BRCMF_BROADCAST_ARP_FILTER_NUM:
+ //filter_pattern = "106 0 0 0
+ //0xFFFFFFFFFFFF0000000000000806
+ //0xFFFFFFFFFFFF0000000000000806";
+ pkt_filter->id = 106;
+ pkt_filter->type = 0;
+ pkt_filter->negate_match = 0;
+ pkt_filter->u.pattern.offset = 0;
+ pkt_filter->u.pattern.size_bytes = 14;
+ mask_and_pattern[0] = 0xFFFF;
+ mask_and_pattern[1] = 0xFFFF;
+ mask_and_pattern[2] = 0xFFFF;
+ mask_and_pattern[3] = 0x0000;
+ mask_and_pattern[4] = 0x0000;
+ mask_and_pattern[5] = 0x0000;
+ mask_and_pattern[6] = 0x0608;
+ mask_and_pattern[7] = 0xFFFF;
+ mask_and_pattern[8] = 0xFFFF;
+ mask_and_pattern[9] = 0xFFFF;
+ mask_and_pattern[10] = 0x0000;
+ mask_and_pattern[11] = 0x0000;
+ mask_and_pattern[12] = 0x0000;
+ mask_and_pattern[13] = 0x0608;
+ break;
+ default:
+ ret = -EINVAL;
+ goto failed;
+ }
+ memcpy(pkt_filter->u.pattern.mask_and_pattern, mask_and_pattern,
+ pkt_filter->u.pattern.size_bytes * 2);
+ buflen = filter_fixed_len + pattern_fixed_len +
+ pkt_filter->u.pattern.size_bytes * 2;
+
+ if (add) {
+ /* Add filter */
+ ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add",
+ pkt_filter, buflen);
+ if (ret)
+ goto failed;
+ drvr->pkt_filter[filter_num].id = pkt_filter->id;
+ drvr->pkt_filter[filter_num].enable = 0;
+
+ } else {
+ /* Delete filter */
+ ret = brcmf_fil_iovar_int_set(ifp, "pkt_filter_delete",
+ pkt_filter->id);
+ if (ret == -ENOENT)
+ ret = 0;
+ if (ret)
+ goto failed;
+
+ drvr->pkt_filter[filter_num].id = 0;
+ drvr->pkt_filter[filter_num].enable = 0;
+ }
+failed:
+ if (ret)
+ brcmf_err("%s packet filter failed, ret=%d\n",
+ (add ? "add" : "remove"), ret);
+
+ kfree(pkt_filter);
+ return ret;
+}
+
+int brcmf_pktfilter_enable(struct net_device *ndev, bool enable)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pub *drvr = ifp->drvr;
+ int ret = 0;
+ int idx = 0;
+
+ for (idx = 0; idx < MAX_PKT_FILTER_COUNT; ++idx) {
+ if (drvr->pkt_filter[idx].id != 0) {
+ drvr->pkt_filter[idx].enable = enable;
+ ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable",
+ &drvr->pkt_filter[idx],
+ sizeof(struct brcmf_pkt_filter_enable_le));
+ if (ret) {
+ brcmf_err("%s packet filter id(%d) failed, ret=%d\n",
+ (enable ? "enable" : "disable"),
+ drvr->pkt_filter[idx].id, ret);
+ }
+ }
+ }
+ return ret;
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
index a4dd313140f3..e38f78e4d92c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
@@ -23,6 +23,7 @@
#include <net/cfg80211.h>
#include "fweh.h"
+#include "fwil_types.h"
#define TOE_TX_CSUM_OL 0x00000001
#define TOE_RX_CSUM_OL 0x00000002
@@ -36,7 +37,7 @@
#define BRCMF_DCMD_MEDLEN 1536
#define BRCMF_DCMD_MAXLEN 8192
-/* IOCTL from host to device are limited in lenght. A device can only handle
+/* IOCTL from host to device are limited in length. A device can only handle
* ethernet frame size. This limitation is to be applied by protocol layer.
*/
#define BRCMF_TX_IOCTL_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN)
@@ -141,6 +142,10 @@ struct brcmf_pub {
struct notifier_block inetaddr_notifier;
struct notifier_block inet6addr_notifier;
struct brcmf_mp_device *settings;
+
+ u8 clmver[BRCMF_DCMD_SMLEN];
+ struct brcmf_pkt_filter_enable_le pkt_filter[MAX_PKT_FILTER_COUNT];
+
};
/* forward declarations */
@@ -214,5 +219,7 @@ 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);
-
+int brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num,
+ bool add);
+int brcmf_pktfilter_enable(struct net_device *ndev, bool enable);
#endif /* BRCMFMAC_CORE_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index 53ae30259989..c7c3563f9a53 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -132,11 +132,16 @@ static void brcmf_feat_iovar_data_set(struct brcmf_if *ifp,
static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
{
- char caps[256];
+ char caps[512];
enum brcmf_feat_id id;
- int i;
+ int i, err;
+
+ err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
+ if (err) {
+ brcmf_err("could not get firmware cap (%d)\n", err);
+ return;
+ }
- brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
brcmf_dbg(INFO, "[ %s]\n", caps);
for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
index f6a2df94dba7..b839756f2891 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
@@ -110,7 +110,7 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
s32 err;
if (drvr->bus_if->state != BRCMF_BUS_UP) {
- brcmf_err("bus is down. we have nothing to do.\n");
+ brcmf_dbg(FIL, "bus is down. we have nothing to do.\n");
return -EIO;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index e0d22fedb2b4..9616c94afb76 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -128,6 +128,19 @@
#define BRCMF_WOWL_MAXPATTERNS 8
#define BRCMF_WOWL_MAXPATTERNSIZE 128
+enum {
+ BRCMF_UNICAST_FILTER_NUM = 0,
+ BRCMF_BROADCAST_FILTER_NUM,
+ BRCMF_MULTICAST4_FILTER_NUM,
+ BRCMF_MULTICAST6_FILTER_NUM,
+ BRCMF_MDNS_FILTER_NUM,
+ BRCMF_ARP_FILTER_NUM,
+ BRCMF_BROADCAST_ARP_FILTER_NUM,
+ MAX_PKT_FILTER_COUNT
+};
+
+#define MAX_PKTFILTER_PATTERN_SIZE 16
+
#define BRCMF_COUNTRY_BUF_SZ 4
#define BRCMF_ANT_MAX 4
@@ -155,6 +168,21 @@
#define BRCMF_MFP_CAPABLE 1
#define BRCMF_MFP_REQUIRED 2
+/* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each
+ * ioctl. It is relatively small because firmware has small maximum size input
+ * playload restriction for ioctls.
+ */
+#define MAX_CHUNK_LEN 1400
+
+#define DLOAD_HANDLER_VER 1 /* Downloader version */
+#define DLOAD_FLAG_VER_MASK 0xf000 /* Downloader version mask */
+#define DLOAD_FLAG_VER_SHIFT 12 /* Downloader version shift */
+
+#define DL_BEGIN 0x0002
+#define DL_END 0x0004
+
+#define DL_TYPE_CLM 2
+
/* join preference types for join_pref iovar */
enum brcmf_join_pref_types {
BRCMF_JOIN_PREF_RSSI = 1,
@@ -827,6 +855,22 @@ struct brcmf_pno_macaddr_le {
};
/**
+ * struct brcmf_dload_data_le - data passing to firmware for downloading
+ * @flag: flags related to download data.
+ * @dload_type: type of download data.
+ * @len: length in bytes of download data.
+ * @crc: crc of download data.
+ * @data: download data.
+ */
+struct brcmf_dload_data_le {
+ __le16 flag;
+ __le16 dload_type;
+ __le32 len;
+ __le32 crc;
+ u8 data[1];
+};
+
+/**
* struct brcmf_pno_bssid_le - bssid configuration for PNO scan.
*
* @bssid: BSS network identifier.
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
index 2370060ef980..22f68051ddb7 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
@@ -415,7 +415,7 @@ struct brcmf_fws_mac_descriptor {
u8 traffic_lastreported_bmp;
};
-#define BRCMF_FWS_HANGER_MAXITEMS 1024
+#define BRCMF_FWS_HANGER_MAXITEMS 3072
/**
* enum brcmf_fws_hanger_item_state - state of hanger item.
@@ -511,6 +511,7 @@ struct brcmf_fws_info {
struct work_struct fws_dequeue_work;
u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
int fifo_credit[BRCMF_FWS_FIFO_COUNT];
+ int init_fifo_credit[BRCMF_FWS_FIFO_COUNT];
int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1];
int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
u32 fifo_credit_map;
@@ -1241,6 +1242,9 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
}
fws->fifo_credit[fifo] += credits;
+ if (fws->fifo_credit[fifo] > fws->init_fifo_credit[fifo])
+ fws->fifo_credit[fifo] = fws->init_fifo_credit[fifo];
+
}
static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
@@ -1455,9 +1459,10 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
static int
brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
- u32 genbit, u16 seq)
+ u32 genbit, u16 seq, u8 compcnt)
{
u32 fifo;
+ u8 cnt = 0;
int ret;
bool remove_from_hanger = true;
struct sk_buff *skb;
@@ -1468,60 +1473,71 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
brcmf_dbg(DATA, "flags %d\n", flags);
if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
- fws->stats.txs_discard++;
+ fws->stats.txs_discard += compcnt;
else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) {
- fws->stats.txs_supp_core++;
+ fws->stats.txs_supp_core += compcnt;
remove_from_hanger = false;
} else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) {
- fws->stats.txs_supp_ps++;
+ fws->stats.txs_supp_ps += compcnt;
remove_from_hanger = false;
} else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED)
- fws->stats.txs_tossed++;
+ fws->stats.txs_tossed += compcnt;
else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)
- fws->stats.txs_host_tossed++;
+ fws->stats.txs_host_tossed += compcnt;
else
brcmf_err("unexpected txstatus\n");
- ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
- remove_from_hanger);
- if (ret != 0) {
- brcmf_err("no packet in hanger slot: hslot=%d\n", hslot);
- return ret;
- }
+ while (cnt < compcnt) {
+ ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
+ remove_from_hanger);
+ if (ret != 0) {
+ brcmf_err("no packet in hanger slot: hslot=%d\n",
+ hslot);
+ goto cont;
+ }
- skcb = brcmf_skbcb(skb);
- entry = skcb->mac;
- if (WARN_ON(!entry)) {
- brcmu_pkt_buf_free_skb(skb);
- return -EINVAL;
- }
- entry->transit_count--;
- if (entry->suppressed && entry->suppr_transit_count)
- entry->suppr_transit_count--;
+ skcb = brcmf_skbcb(skb);
+ entry = skcb->mac;
+ if (WARN_ON(!entry)) {
+ brcmu_pkt_buf_free_skb(skb);
+ goto cont;
+ }
+ entry->transit_count--;
+ if (entry->suppressed && entry->suppr_transit_count)
+ entry->suppr_transit_count--;
- brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name, flags,
- skcb->htod, seq);
+ brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name,
+ flags, skcb->htod, seq);
- /* pick up the implicit credit from this packet */
- fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
- if ((fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT) ||
- (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
- (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) {
- brcmf_fws_return_credits(fws, fifo, 1);
- brcmf_fws_schedule_deq(fws);
- }
- brcmf_fws_macdesc_return_req_credit(skb);
+ /* pick up the implicit credit from this packet */
+ fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
+ if ((fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT) ||
+ (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
+ (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) {
+ brcmf_fws_return_credits(fws, fifo, 1);
+ brcmf_fws_schedule_deq(fws);
+ }
+ brcmf_fws_macdesc_return_req_credit(skb);
- ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
- if (ret) {
- brcmu_pkt_buf_free_skb(skb);
- return -EINVAL;
+ ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
+ if (ret) {
+ brcmu_pkt_buf_free_skb(skb);
+ goto cont;
+ }
+ if (!remove_from_hanger)
+ ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
+ genbit, seq);
+ if (remove_from_hanger || ret)
+ brcmf_txfinalize(ifp, skb, true);
+
+cont:
+ hslot = (hslot + 1) & (BRCMF_FWS_TXSTAT_HSLOT_MASK >>
+ BRCMF_FWS_TXSTAT_HSLOT_SHIFT);
+ if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
+ seq = (seq + 1) & BRCMF_SKB_HTOD_SEQ_NR_MASK;
+
+ cnt++;
}
- if (!remove_from_hanger)
- ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
- genbit, seq);
- if (remove_from_hanger || ret)
- brcmf_txfinalize(ifp, skb, true);
return 0;
}
@@ -1547,7 +1563,8 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
return BRCMF_FWS_RET_OK_SCHEDULE;
}
-static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
+static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 type,
+ u8 *data)
{
__le32 status_le;
__le16 seq_le;
@@ -1556,23 +1573,31 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
u32 genbit;
u8 flags;
u16 seq;
+ u8 compcnt;
+ u8 compcnt_offset = BRCMF_FWS_TYPE_TXSTATUS_LEN;
- fws->stats.txs_indicate++;
memcpy(&status_le, data, sizeof(status_le));
status = le32_to_cpu(status_le);
flags = brcmf_txstatus_get_field(status, FLAGS);
hslot = brcmf_txstatus_get_field(status, HSLOT);
genbit = brcmf_txstatus_get_field(status, GENERATION);
if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
- memcpy(&seq_le, &data[BRCMF_FWS_TYPE_PKTTAG_LEN],
+ memcpy(&seq_le, &data[BRCMF_FWS_TYPE_TXSTATUS_LEN],
sizeof(seq_le));
seq = le16_to_cpu(seq_le);
+ compcnt_offset += BRCMF_FWS_TYPE_SEQ_LEN;
} else {
seq = 0;
}
+ if (type == BRCMF_FWS_TYPE_COMP_TXSTATUS)
+ compcnt = data[compcnt_offset];
+ else
+ compcnt = 1;
+ fws->stats.txs_indicate += compcnt;
+
brcmf_fws_lock(fws);
- brcmf_fws_txs_process(fws, flags, hslot, genbit, seq);
+ brcmf_fws_txs_process(fws, flags, hslot, genbit, seq, compcnt);
brcmf_fws_unlock(fws);
return BRCMF_FWS_RET_OK_NOSCHEDULE;
}
@@ -1599,19 +1624,21 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
brcmf_err("event payload too small (%d)\n", e->datalen);
return -EINVAL;
}
- if (fws->creditmap_received)
- return 0;
fws->creditmap_received = true;
brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
brcmf_fws_lock(fws);
for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
- if (*credits)
+ fws->fifo_credit[i] += credits[i] - fws->init_fifo_credit[i];
+ fws->init_fifo_credit[i] = credits[i];
+ if (fws->fifo_credit[i] > 0)
fws->fifo_credit_map |= 1 << i;
else
fws->fifo_credit_map &= ~(1 << i);
- fws->fifo_credit[i] = *credits++;
+ if (fws->fifo_credit[i] < 0)
+ brcmf_err("fifo_credit[%d] value is negative(%d)\n",
+ i, fws->fifo_credit[i]);
}
brcmf_fws_schedule_deq(fws);
brcmf_fws_unlock(fws);
@@ -1845,6 +1872,9 @@ void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
WARN_ON(siglen > skb->len);
+ if (siglen > skb->len)
+ siglen = skb->len;
+
if (!siglen)
return;
/* if flow control disabled, skip to packet data and leave */
@@ -1886,8 +1916,6 @@ void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
err = BRCMF_FWS_RET_OK_NOSCHEDULE;
switch (type) {
- case BRCMF_FWS_TYPE_COMP_TXSTATUS:
- break;
case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
rd = (struct brcmf_skb_reorder_data *)skb->cb;
rd->reorder = data;
@@ -1910,7 +1938,8 @@ void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
err = brcmf_fws_request_indicate(fws, type, data);
break;
case BRCMF_FWS_TYPE_TXSTATUS:
- brcmf_fws_txstatus_indicate(fws, data);
+ case BRCMF_FWS_TYPE_COMP_TXSTATUS:
+ brcmf_fws_txstatus_indicate(fws, type, data);
break;
case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
err = brcmf_fws_fifocreditback_indicate(fws, data);
@@ -1999,7 +2028,7 @@ static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
fws->stats.rollback_failed++;
hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
- hslot, 0, 0);
+ hslot, 0, 0, 1);
} else {
fws->stats.rollback_success++;
brcmf_fws_return_credits(fws, fifo, 1);
@@ -2017,7 +2046,7 @@ static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
}
for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) {
- if (fws->fifo_credit[lender_ac]) {
+ if (fws->fifo_credit[lender_ac] > 0) {
fws->credits_borrowed[lender_ac]++;
fws->fifo_credit[lender_ac]--;
if (fws->fifo_credit[lender_ac] == 0)
@@ -2096,6 +2125,7 @@ static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p,
int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
{
+ struct brcmf_pub *drvr = ifp->drvr;
struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
struct ethhdr *eh = (struct ethhdr *)(skb->data);
@@ -2109,8 +2139,10 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
skcb->if_flags = 0;
skcb->state = BRCMF_FWS_SKBSTATE_NEW;
brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
+
+ /* mapping from 802.1d priority to firmware fifo index */
if (!multicast)
- fifo = brcmf_fws_prio2fifo[skb->priority];
+ fifo = brcmf_map_prio_to_aci(drvr->config, skb->priority);
brcmf_fws_lock(fws);
if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
@@ -2216,8 +2248,9 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
}
continue;
}
- while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
- (fifo == BRCMF_FWS_FIFO_BCMC))) {
+ while ((fws->fifo_credit[fifo] > 0) ||
+ ((!fws->bcmc_credit_check) &&
+ (fifo == BRCMF_FWS_FIFO_BCMC))) {
skb = brcmf_fws_deq(fws, fifo);
if (!skb)
break;
@@ -2228,7 +2261,7 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
break;
}
if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
- (fws->fifo_credit[fifo] == 0) &&
+ (fws->fifo_credit[fifo] <= 0) &&
(!fws->bus_flow_blocked)) {
while (brcmf_fws_borrow_credit(fws) == 0) {
skb = brcmf_fws_deq(fws, fifo);
@@ -2321,7 +2354,7 @@ struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr)
struct brcmf_if *ifp;
u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
int rc;
- u32 mode;
+ u32 mode = 0;
fws = kzalloc(sizeof(*fws), GFP_KERNEL);
if (!fws) {
@@ -2458,7 +2491,8 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
}
brcmf_fws_lock(fws);
hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
- brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0);
+ brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0,
+ 1);
brcmf_fws_unlock(fws);
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
index aee6e5937c41..fb19ab792f81 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
@@ -30,14 +30,22 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
struct device_node *np = dev->of_node;
int irq;
u32 irqf;
- u32 val;
+ u32 val32;
+ u16 val16;
if (!np || bus_type != BRCMF_BUSTYPE_SDIO ||
!of_device_is_compatible(np, "brcm,bcm4329-fmac"))
return;
- if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0)
- sdio->drive_strength = val;
+ if (of_property_read_u32(np, "brcm,drive-strength", &val32) == 0)
+ sdio->drive_strength = val32;
+
+ sdio->broken_sg_support = of_property_read_bool(np,
+ "brcm,broken_sg_support");
+ if (of_property_read_u16(np, "brcm,sd_head_align", &val16) == 0)
+ sdio->sd_head_align = val16;
+ if (of_property_read_u16(np, "brcm,sd_sgentry_align", &val16) == 0)
+ sdio->sd_sgentry_align = val16;
/* make sure there are interrupts defined in the node */
if (!of_find_property(np, "interrupts", NULL))
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
index 4a883f4bbf88..52016373dce7 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -467,10 +467,21 @@ static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac)
*/
static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr)
{
+ struct brcmf_if *pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
bool random_addr = false;
+ bool local_admin = false;
- if (!dev_addr || is_zero_ether_addr(dev_addr))
- random_addr = true;
+ if (!dev_addr || is_zero_ether_addr(dev_addr)) {
+ /* If the primary interface address is already locally
+ * administered, create a new random address.
+ */
+ if (pri_ifp->mac_addr[0] & 0x02) {
+ random_addr = true;
+ } else {
+ dev_addr = pri_ifp->mac_addr;
+ local_admin = true;
+ }
+ }
/* Generate the P2P Device Address obtaining a random ethernet
* address with the locally administered bit set.
@@ -480,6 +491,9 @@ static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr)
else
memcpy(p2p->dev_addr, dev_addr, ETH_ALEN);
+ if (local_admin)
+ p2p->dev_addr[0] |= 0x02;
+
/* Generate the P2P Interface Address. If the discovery and connection
* BSSCFGs need to simultaneously co-exist, then this address must be
* different from the P2P Device Address, but also locally administered.
@@ -1498,6 +1512,7 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
struct brcmf_fil_af_params_le *af_params)
{
struct brcmf_cfg80211_vif *vif;
+ struct brcmf_p2p_action_frame *p2p_act_frame;
s32 err = 0;
s32 timeout = 0;
@@ -1507,7 +1522,13 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
- vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ /* check if it is a p2p_presence response */
+ p2p_act_frame = (struct brcmf_p2p_action_frame *) af_params->action_frame.data;
+ if (p2p_act_frame->subtype == P2P_AF_PRESENCE_RSP)
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
+ else
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+
err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params,
sizeof(*af_params));
if (err) {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index e6e9b00b79d7..ed72c928151f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -39,6 +39,7 @@
#include "chip.h"
#include "core.h"
#include "common.h"
+#include "cfg80211.h"
enum brcmf_pcie_state {
@@ -78,7 +79,7 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371),
};
-#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */
+#define BRCMF_PCIE_FW_UP_TIMEOUT 5000 /* msec */
#define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024)
@@ -184,7 +185,7 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
#define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008
#define BRCMF_H2D_HOST_D0_INFORM 0x00000010
-#define BRCMF_PCIE_MBDATA_TIMEOUT msecs_to_jiffies(2000)
+#define BRCMF_PCIE_MBDATA_TIMEOUT msecs_to_jiffies(5000)
#define BRCMF_PCIE_CFGREG_STATUS_CMD 0x4
#define BRCMF_PCIE_CFGREG_PM_CSR 0x4C
@@ -671,7 +672,6 @@ brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data)
brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data);
pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
- pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
return 0;
}
@@ -1350,6 +1350,24 @@ static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len)
return 0;
}
+static int brcmf_pcie_get_fwname(struct device *dev, u32 chip, u32 chiprev,
+ u8 *fw_name)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
+ struct brcmf_pciedev_info *devinfo = buspub->devinfo;
+ int ret = 0;
+
+ if (devinfo->fw_name[0] != '\0')
+ strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
+ else
+ ret = brcmf_fw_map_chip_to_name(chip, chiprev,
+ brcmf_pcie_fwnames,
+ ARRAY_SIZE(brcmf_pcie_fwnames),
+ fw_name, NULL);
+
+ return ret;
+}
static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
.txdata = brcmf_pcie_tx,
@@ -1359,6 +1377,7 @@ static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
.wowl_config = brcmf_pcie_wowl_config,
.get_ramsize = brcmf_pcie_get_ramsize,
.get_memdump = brcmf_pcie_get_memdump,
+ .get_fwname = brcmf_pcie_get_fwname,
};
@@ -1869,11 +1888,22 @@ static int brcmf_pcie_pm_enter_D3(struct device *dev)
{
struct brcmf_pciedev_info *devinfo;
struct brcmf_bus *bus;
+ struct brcmf_cfg80211_info *config;
+ int retry = BRCMF_PM_WAIT_MAXRETRY;
brcmf_dbg(PCIE, "Enter\n");
bus = dev_get_drvdata(dev);
devinfo = bus->bus_priv.pcie->devinfo;
+ config = bus->drvr->config;
+
+ while (retry &&
+ config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) {
+ usleep_range(10000, 20000);
+ retry--;
+ }
+ if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING)
+ brcmf_err("timed out wait for cfg80211 suspended\n");
brcmf_bus_change_state(bus, BRCMF_BUS_DOWN);
@@ -1913,6 +1943,7 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev)
if (brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM))
goto cleanup;
brcmf_dbg(PCIE, "Hot resume, continue....\n");
+ msleep(10);
devinfo->state = BRCMFMAC_PCIE_STATE_UP;
brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
brcmf_bus_change_state(bus, BRCMF_BUS_UP);
@@ -1953,6 +1984,7 @@ static const struct dev_pm_ops brcmf_pciedrvr_pm = {
static const struct pci_device_id brcmf_pcie_devid_table[] = {
BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID),
+ BRCMF_PCIE_DEVICE_SUB(BRCM_PCIE_4355_DEVICE_ID,BRCM_PCIE_VENDOR_ID_BROADCOM,BRCM_PCIE_4355_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
index ffa243e2e2d0..55974a43796a 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
@@ -496,6 +496,11 @@ int brcmf_pno_stop_sched_scan(struct brcmf_if *ifp, u64 reqid)
brcmf_dbg(TRACE, "reqid=%llu\n", reqid);
pi = ifp_to_pno(ifp);
+
+ /* No PNO reqeuset */
+ if (!pi->n_reqs)
+ return 0;
+
err = brcmf_pno_remove_request(pi, reqid);
if (err)
return err;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 4c28b04ea605..c363e4f598d2 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -45,10 +45,22 @@
#include "core.h"
#include "common.h"
#include "bcdc.h"
+#include "fwil.h"
#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500)
#define CTL_DONE_TIMEOUT msecs_to_jiffies(2500)
-
+#define ULP_HUDI_PROC_DONE_TIME msecs_to_jiffies(2500)
+
+#define DEFAULT_F2_WATERMARK 0x8
+#define CY_4373_F2_WATERMARK 0x40
+#define CY_43012_F2_WATERMARK 0x60
+#define CY_43455_F2_WATERMARK 0x60
+#define CY_43455_MES_WATERMARK 0x50
+#define CY_43455_MESBUSYCTRL (CY_43455_MES_WATERMARK | \
+ SBSDIO_MESBUSYCTRL_ENAB)
+#define CY_4339_F2_WATERMARK 48
+#define CY_4339_MES_WATERMARK 80
+#define CY_4339_MESBUSYCTRL (CY_4339_MES_WATERMARK | SBSDIO_MESBUSYCTRL_ENAB)
#ifdef DEBUG
#define BRCMF_TRAP_INFO_SIZE 80
@@ -138,6 +150,8 @@ struct rte_console {
/* 1: isolate internal sdio signals, put external pads in tri-state; requires
* sdio bus power cycle to clear (rev 9) */
#define SBSDIO_DEVCTL_PADS_ISO 0x08
+/* 1: enable F2 Watermark */
+#define SBSDIO_DEVCTL_F2WM_ENAB 0x10
/* Force SD->SB reset mapping (rev 11) */
#define SBSDIO_DEVCTL_SB_RST_CTL 0x30
/* Determined by CoreControl bit */
@@ -316,14 +330,9 @@ struct rte_console {
#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US)
#define BRCMF_SDIO_MAX_ACCESS_ERRORS 5
-/*
- * Conversion of 802.1D priority to precedence level
- */
-static uint prio2prec(u32 prio)
-{
- return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
- (prio^2) : prio;
-}
+static void brcmf_sdio_firmware_callback(struct device *dev, int err,
+ const struct firmware *code,
+ void *nvram, u32 nvram_len);
#ifdef DEBUG
/* Device console log buffer state */
@@ -619,6 +628,7 @@ BRCMF_FW_NVRAM_DEF(43455, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt");
BRCMF_FW_NVRAM_DEF(4354, "brcmfmac4354-sdio.bin", "brcmfmac4354-sdio.txt");
BRCMF_FW_NVRAM_DEF(4356, "brcmfmac4356-sdio.bin", "brcmfmac4356-sdio.txt");
BRCMF_FW_NVRAM_DEF(4373, "brcmfmac4373-sdio.bin", "brcmfmac4373-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43012, "brcmfmac43012-sdio.bin", "brcmfmac43012-sdio.txt");
static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
@@ -638,7 +648,8 @@ static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, 43455),
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354),
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
- BRCMF_FW_NVRAM_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373)
+ BRCMF_FW_NVRAM_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373),
+ BRCMF_FW_NVRAM_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012)
};
static void pkt_align(struct sk_buff *p, int len, int align)
@@ -699,6 +710,15 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
wr_val, &err);
+ /* In case of 43012 chip, the chip could go down immediately after
+ * KSO bit is cleared. So the further reads of KSO register could
+ * fail. Thereby just bailing out immediately after clearing KSO
+ * bit, to avoid polling of KSO bit.
+ */
+ if (!on && (bus->ci->chip == CY_CC_43012_CHIP_ID)) {
+ return err;
+ }
+
if (on) {
/* device WAKEUP through KSO:
* write bit 0 & read back until
@@ -2313,6 +2333,7 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
&prec_out);
if (pkt == NULL)
break;
+ skb_orphan(pkt);
__skb_queue_tail(&pktq, pkt);
}
spin_unlock_bh(&bus->txq_lock);
@@ -2442,9 +2463,20 @@ static void brcmf_sdio_bus_stop(struct device *dev)
/* Force backplane clocks to assure F2 interrupt propagates */
saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
&err);
- if (!err)
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
- (saveclk | SBSDIO_FORCE_HT), &err);
+ if (!err) {
+ if (bus->ci->chip == CY_CC_43012_CHIP_ID) {
+ brcmf_sdiod_regwb(sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk |
+ SBSDIO_HT_AVAIL_REQ),
+ &err);
+ } else {
+ brcmf_sdiod_regwb(sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_FORCE_HT),
+ &err);
+ }
+ }
if (err)
brcmf_err("Failed to force clock for F2: err %d\n",
err);
@@ -2521,6 +2553,149 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
return ret;
}
+/* This Function is used to retrieve important
+ * details from dongle related to ULP mode Mostly
+ * values/SHM details that will be vary depending
+ * on the firmware branches
+ */
+static void
+brcmf_sdio_ulp_preinit(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_if *ifp = bus_if->drvr->iflist[0];
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Query ulp_sdioctrl iovar to get the ULP related SHM offsets */
+ err = brcmf_fil_iovar_data_get(ifp, "ulp_sdioctrl", &sdiodev->shm_ulp,
+ sizeof(sdiodev->shm_ulp));
+ if (err)
+ brcmf_err("ulp_sdioctrl iovar returned err = %d\n", err);
+
+ sdiodev->ulp = false;
+
+ brcmf_dbg(TRACE, "m_ulp_ctrl_sdio[%x] m_ulp_wakeevt_ind [%x]\n",
+ M_DS1_CTRL_SDIO(sdiodev->shm_ulp),
+ M_WAKEEVENT_IND(sdiodev->shm_ulp));
+ brcmf_dbg(TRACE, "m_ulp_wakeind [%x]\n",
+ M_ULP_WAKE_IND(sdiodev->shm_ulp));
+}
+
+/* Reinitialize ARM because In DS1 mode ARM got off */
+static int
+brcmf_sdio_ulp_reinit_fw(struct brcmf_sdio *bus)
+{
+ struct brcmf_sdio_dev *sdiodev = bus->sdiodev;
+ int err = 0;
+
+ /* After firmware redownload tx/rx seq are reset accordingly
+ * these values are reset on FMAC side tx_max is initially set to 4,
+ * which later is updated by FW.
+ */
+ bus->tx_seq = 0;
+ bus->rx_seq = 0;
+ bus->tx_max = 4;
+
+ err = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
+ sdiodev->fw_name, sdiodev->nvram_name,
+ brcmf_sdio_firmware_callback);
+ if (err != 0)
+ brcmf_err("async firmware request failed: %d\n", err);
+ return err;
+}
+
+/* Check if device is in DS1 mode and handshake with ULP UCODE */
+static bool
+brcmf_sdio_ulp_pre_redownload_check(struct brcmf_sdio *bus)
+{
+ int err = 0;
+ u32 value = 0;
+ u32 val32, ulp_wake_ind, wowl_wake_ind;
+ int reg_addr;
+ unsigned long timeout;
+
+ value = brcmf_sdiod_regrb(bus->sdiodev, SDIO_CCCR_IOEx, &err);
+
+ if (value == SDIO_FUNC_ENABLE_1) {
+ brcmf_dbg(SDIO, "GOT THE INTERRUPT FROM UCODE\n");
+ bus->sdiodev->ulp = true;
+ ulp_wake_ind = D11SHM_RD(bus->sdiodev, M_ULP_WAKE_IND(
+ bus->sdiodev->shm_ulp), &err) >> 16;
+ wowl_wake_ind = D11SHM_RD(bus->sdiodev, M_WAKEEVENT_IND(
+ bus->sdiodev->shm_ulp), &err) >> 16;
+
+ brcmf_dbg(SDIO, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x\n",
+ wowl_wake_ind, ulp_wake_ind);
+
+ if (wowl_wake_ind || ulp_wake_ind) {
+ /* TX wake Don't do anything.
+ * Just bail out and re-download firmware.
+ */
+ } else {
+ /* RX wake negotiate with MAC */
+ brcmf_dbg(SDIO, "M_DS1_CTRL_SDIO: 0x%08x\n",
+ (u32)D11SHM_RD(bus->sdiodev,
+ M_DS1_CTRL_SDIO(bus->sdiodev->shm_ulp),
+ &err));
+ D11SHM_WR(bus->sdiodev, M_DS1_CTRL_SDIO(
+ bus->sdiodev->shm_ulp),
+ C_DS1_CTRL_SDIO_DS1_EXIT |
+ C_DS1_CTRL_REQ_VALID,
+ &err);
+ val32 = D11REG_RD(bus->sdiodev,
+ D11_MACCONTROL_REG, &err);
+ val32 = val32 | D11_MACCONTROL_REG_WAKE;
+ D11REG_WR(bus->sdiodev,
+ D11_MACCONTROL_REG, val32, &err);
+
+ /* Poll for PROC_DONE to be set by ucode */
+ value = D11SHM_RD(bus->sdiodev,
+ M_DS1_CTRL_SDIO(
+ bus->sdiodev->shm_ulp), &err);
+ /* Wait here (polling) for C_DS1_CTRL_PROC_DONE */
+ timeout = jiffies + ULP_HUDI_PROC_DONE_TIME;
+ while (!(value & C_DS1_CTRL_PROC_DONE)) {
+ value = D11SHM_RD(bus->sdiodev,
+ M_DS1_CTRL_SDIO(
+ bus->sdiodev->shm_ulp), &err);
+ if (time_after(jiffies, timeout))
+ break;
+ usleep_range(1000, 2000);
+ }
+ brcmf_dbg(SDIO, "M_DS1_CTRL_SDIO: 0x%08x\n",
+ (u32)D11SHM_RD(bus->sdiodev,
+ M_DS1_CTRL_SDIO(
+ bus->sdiodev->shm_ulp), &err));
+ value = D11SHM_RD(bus->sdiodev,
+ M_DS1_CTRL_SDIO(
+ bus->sdiodev->shm_ulp), &err);
+ if (!(value & C_DS1_CTRL_PROC_DONE)) {
+ brcmf_err("%s: timeout Failed to enter DS1 Exit state!\n",
+ __func__);
+ return false;
+ }
+ }
+ ulp_wake_ind = D11SHM_RD(bus->sdiodev, M_ULP_WAKE_IND(
+ bus->sdiodev->shm_ulp), &err) >> 16;
+ wowl_wake_ind = D11SHM_RD(bus->sdiodev, M_WAKEEVENT_IND(
+ bus->sdiodev->shm_ulp), &err) >> 16;
+ brcmf_dbg(SDIO, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x\n",
+ wowl_wake_ind, ulp_wake_ind);
+ reg_addr = CORE_CC_REG(
+ brcmf_chip_get_pmu(bus->ci)->base, min_res_mask);
+ brcmf_sdiod_regwl(bus->sdiodev, reg_addr,
+ DEFAULT_43012_MIN_RES_MASK, &err);
+ if (err)
+ brcmf_err("min_res_mask failed\n");
+
+ return true;
+ }
+
+ return false;
+}
+
static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
{
u32 newstatus = 0;
@@ -2593,6 +2768,8 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
if (intstatus & I_HMB_HOST_INT) {
intstatus &= ~I_HMB_HOST_INT;
intstatus |= brcmf_sdio_hostmail(bus);
+ if (brcmf_sdio_ulp_pre_redownload_check(bus))
+ brcmf_sdio_ulp_reinit_fw(bus);
}
sdio_release_host(bus->sdiodev->func[1]);
@@ -2747,7 +2924,13 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
skb_push(pkt, bus->tx_hdrlen);
/* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
- prec = prio2prec((pkt->priority & PRIOMASK));
+ /* In WLAN, priority is always set by the AP using WMM parameters
+ * and this need not always follow the standard 802.1d priority.
+ * Based on AP WMM config, map from 802.1d priority to corresponding
+ * precedence level.
+ */
+ prec = brcmf_map_prio_to_prec(bus_if->drvr->config,
+ (pkt->priority & PRIOMASK));
/* Check for existing queue, current flow-control,
pending event, or pending clock */
@@ -3338,34 +3521,50 @@ static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
{
int err = 0;
u8 val;
+ u8 wakeupctrl;
+ u8 cardcap;
+ u8 chipclkcsr;
brcmf_dbg(TRACE, "Enter\n");
+ if (bus->ci->chip == CY_CC_43012_CHIP_ID) {
+ wakeupctrl = SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT;
+ chipclkcsr = SBSDIO_HT_AVAIL_REQ;
+ } else {
+ wakeupctrl = SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
+ chipclkcsr = SBSDIO_FORCE_HT;
+ }
+
+ if (bus->ci->chip == CY_CC_43012_CHIP_ID ||
+ bus->ci->chip == BRCM_CC_4339_CHIP_ID ||
+ bus->ci->chip == BRCM_CC_4354_CHIP_ID ||
+ bus->ci->chip == BRCM_CC_4345_CHIP_ID) {
+ cardcap = SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC;
+ } else {
+ cardcap = (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
+ SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT);
+ }
+
val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err);
if (err) {
brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n");
return;
}
-
- val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
+ val |= 1 << wakeupctrl;
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err);
if (err) {
brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n");
return;
}
-
- /* Add CMD14 Support */
brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP,
- (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
- SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT),
+ cardcap,
&err);
if (err) {
brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n");
return;
}
-
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
- SBSDIO_FORCE_HT, &err);
+ chipclkcsr, &err);
if (err) {
brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n");
return;
@@ -3439,6 +3638,10 @@ static int brcmf_sdio_bus_preinit(struct device *dev)
if (err < 0)
goto done;
+ /* initialize SHM address from firmware for DS1 */
+ if (!bus->sdiodev->ulp)
+ brcmf_sdio_ulp_preinit(dev);
+
bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
if (sdiodev->sg_support) {
bus->txglom = false;
@@ -3979,6 +4182,24 @@ brcmf_sdio_watchdog(unsigned long data)
}
}
+static int brcmf_sdio_get_fwname(struct device *dev, u32 chip, u32 chiprev,
+ u8 *fw_name)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ int ret = 0;
+
+ if (sdiodev->fw_name[0] != '\0')
+ strlcpy(fw_name, sdiodev->fw_name, BRCMF_FW_NAME_LEN);
+ else
+ ret = brcmf_fw_map_chip_to_name(chip, chiprev,
+ brcmf_sdio_fwnames,
+ ARRAY_SIZE(brcmf_sdio_fwnames),
+ fw_name, NULL);
+
+ return ret;
+}
+
static const struct brcmf_bus_ops brcmf_sdio_bus_ops = {
.stop = brcmf_sdio_bus_stop,
.preinit = brcmf_sdio_bus_preinit,
@@ -3989,6 +4210,7 @@ static const struct brcmf_bus_ops brcmf_sdio_bus_ops = {
.wowl_config = brcmf_sdio_wowl_config,
.get_ramsize = brcmf_sdio_bus_get_ramsize,
.get_memdump = brcmf_sdio_bus_get_memdump,
+ .get_fwname = brcmf_sdio_get_fwname,
};
static void brcmf_sdio_firmware_callback(struct device *dev, int err,
@@ -3999,6 +4221,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
struct brcmf_sdio_dev *sdiodev;
struct brcmf_sdio *bus;
u8 saveclk;
+ u8 devctl;
brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err);
bus_if = dev_get_drvdata(dev);
@@ -4032,8 +4255,14 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
/* Force clocks on backplane to be sure F2 interrupt propagates */
saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err);
if (!err) {
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
- (saveclk | SBSDIO_FORCE_HT), &err);
+ if (bus->ci->chip == CY_CC_43012_CHIP_ID) {
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_HT_AVAIL_REQ),
+ &err);
+ } else {
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_FORCE_HT), &err);
+ }
}
if (err) {
brcmf_err("Failed to force clock for F2: err %d\n", err);
@@ -4054,8 +4283,56 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
bus->hostintmask = HOSTINTMASK;
w_sdreg32(bus, bus->hostintmask,
offsetof(struct sdpcmd_regs, hostintmask));
-
- brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err);
+ switch (sdiodev->func[0]->device) {
+ case SDIO_DEVICE_ID_CYPRESS_4373:
+ brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
+ CY_4373_F2_WATERMARK);
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, CY_4373_F2_WATERMARK, &err);
+ devctl = brcmf_sdiod_regrb(sdiodev, SBSDIO_DEVICE_CTL, &err);
+ devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_DEVICE_CTL, devctl, &err);
+ break;
+ case SDIO_DEVICE_ID_CYPRESS_43012:
+ brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes for 43012\n",
+ CY_43012_F2_WATERMARK);
+ brcmf_sdiod_regwb(sdiodev,
+ SBSDIO_WATERMARK,
+ CY_43012_F2_WATERMARK, &err);
+ devctl = brcmf_sdiod_regrb(sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+ devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
+ brcmf_sdiod_regwb(sdiodev,
+ SBSDIO_DEVICE_CTL, devctl, &err);
+ break;
+ case SDIO_DEVICE_ID_BROADCOM_43455:
+ brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes for 43455\n",
+ CY_43455_F2_WATERMARK);
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK,
+ CY_43455_F2_WATERMARK, &err);
+ devctl = brcmf_sdiod_regrb(sdiodev, SBSDIO_DEVICE_CTL,
+ &err);
+ devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_DEVICE_CTL, devctl,
+ &err);
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_MESBUSYCTRL,
+ CY_43455_MESBUSYCTRL, &err);
+ break;
+ case SDIO_DEVICE_ID_BROADCOM_4339:
+ brcmf_sdiod_regwb(sdiodev,
+ SBSDIO_WATERMARK,
+ CY_4339_F2_WATERMARK, &err);
+ devctl = brcmf_sdiod_regrb(sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+ devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
+ brcmf_sdiod_regwb(sdiodev,
+ SBSDIO_DEVICE_CTL, devctl, &err);
+ brcmf_sdiod_regwb(sdiodev,
+ SBSDIO_FUNC1_MESBUSYCTRL, CY_4339_MESBUSYCTRL, &err);
+ break;
+ default:
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, DEFAULT_F2_WATERMARK, &err);
+ break;
+ }
} else {
/* Disable F2 again */
sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
@@ -4085,11 +4362,18 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
sdio_release_host(sdiodev->func[1]);
- err = brcmf_bus_started(dev);
- if (err != 0) {
- brcmf_err("dongle is not responding\n");
- goto fail;
+ /* Waking up from deep sleep don't requirerd to reint the sdio bus
+ * as all sdiod core registers will get restored by Firmware using
+ * FCBS engine.
+ */
+ if (!bus->sdiodev->ulp) {
+ err = brcmf_bus_started(dev);
+ if (err != 0) {
+ brcmf_err("dongle is not responding\n");
+ goto fail;
+ }
}
+
return;
release:
@@ -4121,9 +4405,21 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
bus->txminmax = BRCMF_TXMINMAX;
bus->tx_seq = SDPCM_SEQ_WRAP - 1;
+ /* attempt to attach to the dongle */
+ if (!(brcmf_sdio_probe_attach(bus))) {
+ brcmf_err("brcmf_sdio_probe_attach failed\n");
+ goto fail;
+ }
+
/* single-threaded workqueue */
- wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM,
- dev_name(&sdiodev->func[1]->dev));
+ if (sdiodev->settings->sdio_wq_highpri) {
+ wq = alloc_workqueue("brcmf_wq/%s",
+ WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND,
+ 1, dev_name(&sdiodev->func[1]->dev));
+ } else {
+ wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM,
+ dev_name(&sdiodev->func[1]->dev));
+ }
if (!wq) {
brcmf_err("insufficient memory to create txworkqueue\n");
goto fail;
@@ -4132,12 +4428,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
bus->brcmf_wq = wq;
- /* attempt to attach to the dongle */
- if (!(brcmf_sdio_probe_attach(bus))) {
- brcmf_err("brcmf_sdio_probe_attach failed\n");
- goto fail;
- }
-
spin_lock_init(&bus->rxctl_lock);
spin_lock_init(&bus->txq_lock);
init_waitqueue_head(&bus->ctrl_wait);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
index f3da32fc6360..7989980dc27f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
@@ -107,6 +107,8 @@
#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C
/* MesBusyCtl (rev 11) */
#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D
+/* Enable busy capability for MES access */
+#define SBSDIO_MESBUSYCTRL_ENAB 0x80
/* Sdio Core Rev 12 */
#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E
#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1
@@ -177,6 +179,17 @@ struct brcmf_sdreg {
struct brcmf_sdio;
struct brcmf_sdiod_freezer;
+/* ULP SHM Offsets info */
+struct ulp_shm_info {
+ u32 m_ulp_ctrl_sdio;
+ u32 m_ulp_wakeevt_ind;
+ u32 m_ulp_wakeind;
+};
+
+struct fmac_ulp {
+ struct ulp_shm_info ulp_shm_offset;
+};
+
struct brcmf_sdio_dev {
struct sdio_func *func[SDIO_MAX_FUNCS];
u8 num_funcs; /* Supported funcs on client */
@@ -201,6 +214,8 @@ struct brcmf_sdio_dev {
bool wowl_enabled;
enum brcmf_sdiod_state state;
struct brcmf_sdiod_freezer *freezer;
+ struct fmac_ulp shm_ulp;
+ bool ulp;
};
/* sdio core registers */
@@ -376,4 +391,51 @@ void brcmf_sdio_wowl_config(struct device *dev, bool enabled);
int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep);
void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus);
+/* SHM offsets */
+#define M_DS1_CTRL_SDIO(ptr) ((ptr).ulp_shm_offset.m_ulp_ctrl_sdio)
+#define M_WAKEEVENT_IND(ptr) ((ptr).ulp_shm_offset.m_ulp_wakeevt_ind)
+#define M_ULP_WAKE_IND(ptr) ((ptr).ulp_shm_offset.m_ulp_wakeind)
+
+#define D11_BASE_ADDR 0x18001000
+#define D11_AXI_BASE_ADDR 0xE8000000
+#define D11_SHM_BASE_ADDR (D11_AXI_BASE_ADDR + 0x4000)
+
+#define D11REG_ADDR(offset) (D11_BASE_ADDR + (offset))
+#define D11IHR_ADDR(offset) (D11_AXI_BASE_ADDR + 0x400 + (2 * (offset)))
+#define D11SHM_ADDR(offset) (D11_SHM_BASE_ADDR + (offset))
+
+/* MacControl register */
+#define D11_MACCONTROL_REG D11REG_ADDR(0x120)
+#define D11_MACCONTROL_REG_WAKE 0x4000000
+
+/* Following are the offsets in M_DRVR_UCODE_IF_PTR block. Start address of
+ * M_DRVR_UCODE_IF_PTR block is present in M_DRVR_UCODE_IF_PTR.
+ */
+
+/* M_ULP_WAKE_IND bits */
+#define ULP_WAKE_IND_WATCHDOG_EXP 0x1
+#define ULP_WAKE_IND_FCBS_ERROR 0x2
+#define ULP_WAKE_IND_RE_TRANSMIT_ERR 0x4
+#define ULP_WAKE_IND_HOST_WKUP 0x8
+#define ULP_WAKE_IND_INVALID_FCBS_BLK 0x10
+
+#define C_DS1_CTRL_SDIO_DS1_SLEEP 0x1
+#define C_DS1_CTRL_SDIO_MAC_ON 0x2
+#define C_DS1_CTRL_SDIO_RADIO_PHY_ON 0x4
+#define C_DS1_CTRL_SDIO_DS1_EXIT 0x8
+#define C_DS1_CTRL_PROC_DONE 0x100
+#define C_DS1_CTRL_REQ_VALID 0x200
+
+#define D11SHM_WR(sdh, offset, val, ret) \
+ brcmf_sdiod_regwl(sdh, D11SHM_ADDR(offset), val, ret)
+
+#define D11SHM_RD(sdh, offset, ret) \
+ brcmf_sdiod_regrl(sdh, D11SHM_ADDR(offset), ret)
+
+#define D11REG_WR(sdh, addr, val, ret) \
+ brcmf_sdiod_regwl(sdh, addr, val, ret)
+
+#define D11REG_RD(sdh, addr, ret) \
+ brcmf_sdiod_regrl(sdh, addr, ret)
+
#endif /* BRCMFMAC_SDIO_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
index be855aa32154..a23764db4609 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
@@ -30,6 +30,7 @@
#include "core.h"
#include "common.h"
#include "bcdc.h"
+#include "cfg80211.h"
#define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000)
@@ -1134,12 +1135,30 @@ static void brcmf_usb_wowl_config(struct device *dev, bool enabled)
device_set_wakeup_enable(devinfo->dev, false);
}
+static int brcmf_usb_get_fwname(struct device *dev, u32 chip, u32 chiprev,
+ u8 *fw_name)
+{
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+ int ret = 0;
+
+ if (devinfo->fw_name[0] != '\0')
+ strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
+ else
+ ret = brcmf_fw_map_chip_to_name(chip, chiprev,
+ brcmf_usb_fwnames,
+ ARRAY_SIZE(brcmf_usb_fwnames),
+ fw_name, NULL);
+
+ return ret;
+}
+
static const struct brcmf_bus_ops brcmf_usb_bus_ops = {
.txdata = brcmf_usb_tx,
.stop = brcmf_usb_down,
.txctl = brcmf_usb_tx_ctlpkt,
.rxctl = brcmf_usb_rx_ctlpkt,
.wowl_config = brcmf_usb_wowl_config,
+ .get_fwname = brcmf_usb_get_fwname,
};
static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)
@@ -1426,8 +1445,22 @@ static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state)
{
struct usb_device *usb = interface_to_usbdev(intf);
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
+ struct brcmf_bus *bus;
+ struct brcmf_cfg80211_info *config;
+ int retry = BRCMF_PM_WAIT_MAXRETRY;
brcmf_dbg(USB, "Enter\n");
+
+ bus = devinfo->bus_pub.bus;
+ config = bus->drvr->config;
+ while (retry &&
+ config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) {
+ usleep_range(10000, 20000);
+ retry--;
+ }
+ if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING)
+ brcmf_err("timed out wait for cfg80211 suspended\n");
+
devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP;
if (devinfo->wowl_enabled)
brcmf_cancel_all_urbs(devinfo);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
index d493021f6031..fbbdb815ff73 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
@@ -81,8 +81,12 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy,
else
ret = brcmf_fil_cmd_data_get(ifp, cmdhdr->cmd, dcmd_buf,
ret_len);
- if (ret != 0)
+
+ if (ret != 0) {
+ brcmf_dbg(INFO, "error(%d), return -EPERM\n", ret);
+ ret = -EPERM;
goto exit;
+ }
wr_pointer = dcmd_buf;
while (ret_len > 0) {
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
index 57544a3a3ce4..31580360081c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
@@ -59,6 +59,7 @@
#define BRCM_CC_4366_CHIP_ID 0x4366
#define BRCM_CC_4371_CHIP_ID 0x4371
#define CY_CC_4373_CHIP_ID 0x4373
+#define CY_CC_43012_CHIP_ID 43012
/* USB Device IDs */
#define BRCM_USB_43143_DEVICE_ID 0xbd1e
@@ -73,6 +74,7 @@
/* PCIE Device IDs */
#define BRCM_PCIE_4350_DEVICE_ID 0x43a3
#define BRCM_PCIE_4354_DEVICE_ID 0x43df
+#define BRCM_PCIE_4355_DEVICE_ID 0x4355
#define BRCM_PCIE_4356_DEVICE_ID 0x43ec
#define BRCM_PCIE_43567_DEVICE_ID 0x43d3
#define BRCM_PCIE_43570_DEVICE_ID 0x43d9
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
index 75b2a0438cfa..dddebaa60352 100644
--- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
@@ -239,6 +239,7 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec)
#define WPA2_AUTH_RESERVED4 0x0400
#define WPA2_AUTH_RESERVED5 0x0800
#define WPA2_AUTH_1X_SHA256 0x1000 /* 1X with SHA256 key derivation */
+#define WPA2_AUTH_FT 0x4000 /* Fast BSS Transition */
#define WPA2_AUTH_PSK_SHA256 0x8000 /* PSK with SHA256 key derivation */
#define DOT11_DEFAULT_RTS_LEN 2347
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
index e1fd499930a0..1f23092cdad9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
@@ -298,6 +298,8 @@ struct chipcregs {
* Maximum delay for the PMU state transition in us.
* This is an upper bound intended for spinwaits etc.
*/
-#define PMU_MAX_TRANSITION_DLY 15000
+#define PMU_MAX_TRANSITION_DLY 15000
+
+#define DEFAULT_43012_MIN_RES_MASK 0x0f8bfe77
#endif /* _SBCHIPC_H */