diff options
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 36 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 202 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/chip.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/commonring.c | 18 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/feature.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/feature.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/firmware.c | 217 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/firmware.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/flowring.c | 5 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/flowring.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 32 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/of.c | 11 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 184 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 11 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/usb.c | 9 |
16 files changed, 535 insertions, 208 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 9b508bd3b839..71779b9e4bbe 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -33,6 +33,7 @@ #include <linux/suspend.h> #include <linux/errno.h> #include <linux/module.h> +#include <linux/acpi.h> #include <net/cfg80211.h> #include <defs.h> @@ -1011,6 +1012,14 @@ static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) return 0; } +static void brcmf_sdiod_host_fixup(struct mmc_host *host) +{ + /* runtime-pm powers off the device */ + pm_runtime_forbid(host->parent); + /* avoid removal detection upon resume */ + host->caps |= MMC_CAP_NONREMOVABLE; +} + static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) { struct sdio_func *func; @@ -1076,7 +1085,7 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) ret = -ENODEV; goto out; } - pm_runtime_forbid(host->parent); + brcmf_sdiod_host_fixup(host); out: if (ret) brcmf_sdiod_remove(sdiodev); @@ -1108,12 +1117,25 @@ MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; +static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev, + int val) +{ +#if IS_ENABLED(CONFIG_ACPI) + struct acpi_device *adev; + + adev = ACPI_COMPANION(dev); + if (adev) + adev->flags.power_manageable = 0; +#endif +} + static int brcmf_ops_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { int err; struct brcmf_sdio_dev *sdiodev; struct brcmf_bus *bus_if; + struct device *dev; brcmf_dbg(SDIO, "Enter\n"); brcmf_dbg(SDIO, "Class=%x\n", func->class); @@ -1121,6 +1143,10 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); brcmf_dbg(SDIO, "Function#: %d\n", func->num); + dev = &func->dev; + /* prohibit ACPI power management for this device */ + brcmf_sdiod_acpi_set_power_manageable(dev, 0); + /* Consume func num 1 but dont do anything with it. */ if (func->num == 1) return 0; @@ -1246,15 +1272,15 @@ static int brcmf_ops_sdio_suspend(struct device *dev) brcmf_sdiod_freezer_on(sdiodev); brcmf_sdio_wd_timer(sdiodev->bus, 0); + sdio_flags = MMC_PM_KEEP_POWER; if (sdiodev->wowl_enabled) { - sdio_flags = MMC_PM_KEEP_POWER; if (sdiodev->pdata->oob_irq_supported) enable_irq_wake(sdiodev->pdata->oob_irq_nr); else - sdio_flags = MMC_PM_WAKE_SDIO_IRQ; - if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) - brcmf_err("Failed to set pm_flags %x\n", sdio_flags); + sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; } + if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) + brcmf_err("Failed to set pm_flags %x\n", sdio_flags); return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 2e4e42245b8f..e10fa67010c0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -129,13 +129,47 @@ static struct ieee80211_rate __wl_rates[] = { RATETAB_ENT(BRCM_RATE_54M, 0), }; -#define wl_a_rates (__wl_rates + 4) -#define wl_a_rates_size 8 #define wl_g_rates (__wl_rates + 0) -#define wl_g_rates_size 12 +#define wl_g_rates_size ARRAY_SIZE(__wl_rates) +#define wl_a_rates (__wl_rates + 4) +#define wl_a_rates_size (wl_g_rates_size - 4) + +#define CHAN2G(_channel, _freq) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = IEEE80211_CHAN_DISABLED, \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = IEEE80211_CHAN_DISABLED, \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel __wl_2ghz_channels[] = { + CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427), + CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447), + CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467), + CHAN2G(13, 2472), CHAN2G(14, 2484) +}; + +static struct ieee80211_channel __wl_5ghz_channels[] = { + CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42), + CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56), + CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108), + CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128), + CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149), + CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165) +}; /* Band templates duplicated per wiphy. The channel info - * is filled in after querying the device. + * above is added to the band during setup. */ static const struct ieee80211_supported_band __wl_band_2ghz = { .band = IEEE80211_BAND_2GHZ, @@ -143,7 +177,7 @@ static const struct ieee80211_supported_band __wl_band_2ghz = { .n_bitrates = wl_g_rates_size, }; -static const struct ieee80211_supported_band __wl_band_5ghz_a = { +static const struct ieee80211_supported_band __wl_band_5ghz = { .band = IEEE80211_BAND_5GHZ, .bitrates = wl_a_rates, .n_bitrates = wl_a_rates_size, @@ -5253,40 +5287,6 @@ dongle_scantime_out: return err; } -/* Filter the list of channels received from firmware counting only - * the 20MHz channels. The wiphy band data only needs those which get - * flagged to indicate if they can take part in higher bandwidth. - */ -static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg, - struct brcmf_chanspec_list *chlist, - u32 chcnt[]) -{ - u32 total = le32_to_cpu(chlist->count); - struct brcmu_chan ch; - int i; - - for (i = 0; i < total; i++) { - ch.chspec = (u16)le32_to_cpu(chlist->element[i]); - cfg->d11inf.decchspec(&ch); - - /* Firmware gives a ordered list. We skip non-20MHz - * channels is 2G. For 5G we can abort upon reaching - * a non-20MHz channel in the list. - */ - if (ch.bw != BRCMU_CHAN_BW_20) { - if (ch.band == BRCMU_CHAN_BAND_5G) - break; - else - continue; - } - - if (ch.band == BRCMU_CHAN_BAND_2G) - chcnt[0] += 1; - else if (ch.band == BRCMU_CHAN_BAND_5G) - chcnt[1] += 1; - } -} - static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel, struct brcmu_chan *ch) { @@ -5322,7 +5322,6 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, u32 i, j; u32 total; u32 chaninfo; - u32 chcnt[2] = { 0, 0 }; u32 index; pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); @@ -5339,42 +5338,15 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, goto fail_pbuf; } - brcmf_count_20mhz_channels(cfg, list, chcnt); wiphy = cfg_to_wiphy(cfg); - if (chcnt[0]) { - band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), - GFP_KERNEL); - if (band == NULL) { - err = -ENOMEM; - goto fail_pbuf; - } - band->channels = kcalloc(chcnt[0], sizeof(*channel), - GFP_KERNEL); - if (band->channels == NULL) { - kfree(band); - err = -ENOMEM; - goto fail_pbuf; - } - band->n_channels = 0; - wiphy->bands[IEEE80211_BAND_2GHZ] = band; - } - if (chcnt[1]) { - band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a), - GFP_KERNEL); - if (band == NULL) { - err = -ENOMEM; - goto fail_band2g; - } - band->channels = kcalloc(chcnt[1], sizeof(*channel), - GFP_KERNEL); - if (band->channels == NULL) { - kfree(band); - err = -ENOMEM; - goto fail_band2g; - } - band->n_channels = 0; - wiphy->bands[IEEE80211_BAND_5GHZ] = band; - } + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + if (band) + for (i = 0; i < band->n_channels; i++) + band->channels[i].flags = IEEE80211_CHAN_DISABLED; + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (band) + for (i = 0; i < band->n_channels; i++) + band->channels[i].flags = IEEE80211_CHAN_DISABLED; total = le32_to_cpu(list->count); for (i = 0; i < total; i++) { @@ -5389,6 +5361,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec); continue; } + if (!band) + continue; if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) && ch.bw == BRCMU_CHAN_BW_40) continue; @@ -5416,9 +5390,9 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, } else if (ch.bw == BRCMU_CHAN_BW_40) { brcmf_update_bw40_channel_flag(&channel[index], &ch); } else { - /* disable other bandwidths for now as mentioned - * order assure they are enabled for subsequent - * chanspecs. + /* enable the channel and disable other bandwidths + * for now as mentioned order assure they are enabled + * for subsequent chanspecs. */ channel[index].flags = IEEE80211_CHAN_NO_HT40 | IEEE80211_CHAN_NO_80MHZ; @@ -5437,16 +5411,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, IEEE80211_CHAN_NO_IR; } } - if (index == band->n_channels) - band->n_channels++; } - kfree(pbuf); - return 0; -fail_band2g: - kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); - kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); - wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; fail_pbuf: kfree(pbuf); return err; @@ -5779,7 +5745,12 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy) static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) { + struct ieee80211_supported_band *band; struct ieee80211_iface_combination ifc_combo; + __le32 bandlist[3]; + u32 n_bands; + int err, i; + wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; @@ -5812,7 +5783,8 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; - brcmf_wiphy_pno_params(wiphy); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) + brcmf_wiphy_pno_params(wiphy); /* vendor commands/events support */ wiphy->vendor_commands = brcmf_vendor_cmds; @@ -5821,7 +5793,52 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) brcmf_wiphy_wowl_params(wiphy); - return brcmf_setup_wiphybands(wiphy); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, + sizeof(bandlist)); + if (err) { + brcmf_err("could not obtain band info: err=%d\n", err); + return err; + } + /* first entry in bandlist is number of bands */ + n_bands = le32_to_cpu(bandlist[0]); + for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) { + if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) { + band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), + GFP_KERNEL); + if (!band) + return -ENOMEM; + + band->channels = kmemdup(&__wl_2ghz_channels, + sizeof(__wl_2ghz_channels), + GFP_KERNEL); + if (!band->channels) { + kfree(band); + return -ENOMEM; + } + + band->n_channels = ARRAY_SIZE(__wl_2ghz_channels); + wiphy->bands[IEEE80211_BAND_2GHZ] = band; + } + if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) { + band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz), + GFP_KERNEL); + if (!band) + return -ENOMEM; + + band->channels = kmemdup(&__wl_5ghz_channels, + sizeof(__wl_5ghz_channels), + GFP_KERNEL); + if (!band->channels) { + kfree(band); + return -ENOMEM; + } + + band->n_channels = ARRAY_SIZE(__wl_5ghz_channels); + wiphy->bands[IEEE80211_BAND_5GHZ] = band; + } + } + err = brcmf_setup_wiphybands(wiphy); + return err; } static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) @@ -6007,11 +6024,18 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, memset(&ccreq, 0, sizeof(ccreq)); ccreq.rev = cpu_to_le32(-1); memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2)); - brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq)); + if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) { + brcmf_err("firmware rejected country setting\n"); + return; + } + brcmf_setup_wiphybands(wiphy); } static void brcmf_free_wiphy(struct wiphy *wiphy) { + if (!wiphy) + return; + kfree(wiphy->iface_combinations); if (wiphy->bands[IEEE80211_BAND_2GHZ]) { kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index ab2fac8b2760..288f8314f208 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -649,6 +649,7 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) case BRCM_CC_43567_CHIP_ID: case BRCM_CC_43569_CHIP_ID: case BRCM_CC_43570_CHIP_ID: + case BRCM_CC_4358_CHIP_ID: case BRCM_CC_43602_CHIP_ID: return 0x180000; default: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c index 77656c711bed..26c65872dae3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c @@ -22,17 +22,6 @@ #include "core.h" #include "commonring.h" - -/* dma flushing needs implementation for mips and arm platforms. Should - * be put in util. Note, this is not real flushing. It is virtual non - * cached memory. Only write buffers should have to be drained. Though - * this may be different depending on platform...... - * SEE ALSO msgbuf.c - */ -#define brcmf_dma_flush(addr, len) -#define brcmf_dma_invalidate_cache(addr, len) - - void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, int (*cr_ring_bell)(void *ctx), int (*cr_update_rptr)(void *ctx), @@ -206,14 +195,9 @@ int brcmf_commonring_write_complete(struct brcmf_commonring *commonring) address = commonring->buf_addr; address += (commonring->f_ptr * commonring->item_len); if (commonring->f_ptr > commonring->w_ptr) { - brcmf_dma_flush(address, - (commonring->depth - commonring->f_ptr) * - commonring->item_len); address = commonring->buf_addr; commonring->f_ptr = 0; } - brcmf_dma_flush(address, (commonring->w_ptr - commonring->f_ptr) * - commonring->item_len); commonring->f_ptr = commonring->w_ptr; @@ -258,8 +242,6 @@ void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, if (commonring->r_ptr == commonring->depth) commonring->r_ptr = 0; - brcmf_dma_invalidate_cache(ret_addr, *n_ items * commonring->item_len); - return ret_addr; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 7748a1ccf14f..2c5fad3a3aa2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -124,6 +124,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) struct brcmf_if *ifp = drvr->iflist[0]; brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h index f5832e077bb7..546962525cd2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h @@ -19,11 +19,15 @@ /* * Features: * + * MBSS: multiple BSSID support (eg. guest network in AP mode). * MCHAN: multi-channel for concurrent P2P. + * PNO: preferred network offload. + * WOWL: Wake-On-WLAN. */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ BRCMF_FEAT_DEF(MCHAN) \ + BRCMF_FEAT_DEF(PNO) \ BRCMF_FEAT_DEF(WOWL) /* * Quirks: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 9cb99152ad17..7ae6461df932 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -23,6 +23,10 @@ #include "debug.h" #include "firmware.h" +#define BRCMF_FW_MAX_NVRAM_SIZE 64000 +#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */ +#define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */ + char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; module_param_string(firmware_path, brcmf_firmware_path, BRCMF_FW_PATH_LEN, 0440); @@ -46,6 +50,8 @@ enum nvram_parser_state { * @column: current column in line. * @pos: byte offset in input buffer. * @entry: start position of key,value entry. + * @multi_dev_v1: detect pcie multi device v1 (compressed). + * @multi_dev_v2: detect pcie multi device v2. */ struct nvram_parser { enum nvram_parser_state state; @@ -56,8 +62,16 @@ struct nvram_parser { u32 column; u32 pos; u32 entry; + bool multi_dev_v1; + bool multi_dev_v2; }; +/** + * is_nvram_char() - check if char is a valid one for NVRAM entry + * + * It accepts all printable ASCII chars except for '#' which opens a comment. + * Please note that ' ' (space) while accepted is not a valid key name char. + */ static bool is_nvram_char(char c) { /* comment marker excluded */ @@ -65,7 +79,7 @@ static bool is_nvram_char(char c) return false; /* key and value may have any other readable character */ - return (c > 0x20 && c < 0x7f); + return (c >= 0x20 && c < 0x7f); } static bool is_whitespace(char c) @@ -108,7 +122,11 @@ static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp) st = COMMENT; else st = VALUE; - } else if (!is_nvram_char(c)) { + if (strncmp(&nvp->fwnv->data[nvp->entry], "devpath", 7) == 0) + nvp->multi_dev_v1 = true; + if (strncmp(&nvp->fwnv->data[nvp->entry], "pcie/", 5) == 0) + nvp->multi_dev_v2 = true; + } else if (!is_nvram_char(c) || c == ' ') { brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", nvp->line, nvp->column); return COMMENT; @@ -133,6 +151,8 @@ brcmf_nvram_handle_value(struct nvram_parser *nvp) ekv = (u8 *)&nvp->fwnv->data[nvp->pos]; skv = (u8 *)&nvp->fwnv->data[nvp->entry]; cplen = ekv - skv; + if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE) + return END; /* copy to output buffer */ memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen); nvp->nvram_len += cplen; @@ -148,17 +168,20 @@ brcmf_nvram_handle_value(struct nvram_parser *nvp) static enum nvram_parser_state brcmf_nvram_handle_comment(struct nvram_parser *nvp) { - char *eol, *sol; + char *eoc, *sol; sol = (char *)&nvp->fwnv->data[nvp->pos]; - eol = strchr(sol, '\n'); - if (eol == NULL) - return END; + eoc = strchr(sol, '\n'); + if (!eoc) { + eoc = strchr(sol, '\0'); + if (!eoc) + return END; + } /* eat all moving to next line */ nvp->line++; nvp->column = 1; - nvp->pos += (eol - sol) + 1; + nvp->pos += (eoc - sol) + 1; return IDLE; } @@ -180,10 +203,18 @@ static enum nvram_parser_state static int brcmf_init_nvram_parser(struct nvram_parser *nvp, const struct firmware *nv) { + size_t size; + memset(nvp, 0, sizeof(*nvp)); nvp->fwnv = nv; + /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */ + if (nv->size > BRCMF_FW_MAX_NVRAM_SIZE) + size = BRCMF_FW_MAX_NVRAM_SIZE; + else + size = nv->size; /* Alloc for extra 0 byte + roundup by 4 + length field */ - nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL); + size += 1 + 3 + sizeof(u32); + nvp->nvram = kzalloc(size, GFP_KERNEL); if (!nvp->nvram) return -ENOMEM; @@ -192,12 +223,141 @@ static int brcmf_init_nvram_parser(struct nvram_parser *nvp, return 0; } +/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple + * devices. Strip it down for one device, use domain_nr/bus_nr to determine + * which data is to be returned. v1 is the version where nvram is stored + * compressed and "devpath" maps to index for valid entries. + */ +static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr, + u16 bus_nr) +{ + /* Device path with a leading '=' key-value separator */ + char pcie_path[] = "=pcie/?/?"; + size_t pcie_len; + + u32 i, j; + bool found; + u8 *nvram; + u8 id; + + nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); + if (!nvram) + goto fail; + + /* min length: devpath0=pcie/1/4/ + 0:x=y */ + if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6) + goto fail; + + /* First search for the devpathX and see if it is the configuration + * for domain_nr/bus_nr. Search complete nvp + */ + snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr, + bus_nr); + pcie_len = strlen(pcie_path); + found = false; + i = 0; + while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) { + /* Format: devpathX=pcie/Y/Z/ + * Y = domain_nr, Z = bus_nr, X = virtual ID + */ + if ((strncmp(&nvp->nvram[i], "devpath", 7) == 0) && + (strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len) == 0)) { + id = nvp->nvram[i + 7] - '0'; + found = true; + break; + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + if (!found) + goto fail; + + /* Now copy all valid entries, release old nvram and assign new one */ + i = 0; + j = 0; + while (i < nvp->nvram_len) { + if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) { + i += 2; + while (nvp->nvram[i] != 0) { + nvram[j] = nvp->nvram[i]; + i++; + j++; + } + nvram[j] = 0; + j++; + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + kfree(nvp->nvram); + nvp->nvram = nvram; + nvp->nvram_len = j; + return; + +fail: + kfree(nvram); + nvp->nvram_len = 0; +} + +/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple + * devices. Strip it down for one device, use domain_nr/bus_nr to determine + * which data is to be returned. v2 is the version where nvram is stored + * uncompressed, all relevant valid entries are identified by + * pcie/domain_nr/bus_nr: + */ +static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr, + u16 bus_nr) +{ + char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN]; + size_t len; + u32 i, j; + u8 *nvram; + + nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); + if (!nvram) + goto fail; + + /* Copy all valid entries, release old nvram and assign new one. + * Valid entries are of type pcie/X/Y/ where X = domain_nr and + * Y = bus_nr. + */ + snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr); + len = strlen(prefix); + i = 0; + j = 0; + while (i < nvp->nvram_len - len) { + if (strncmp(&nvp->nvram[i], prefix, len) == 0) { + i += len; + while (nvp->nvram[i] != 0) { + nvram[j] = nvp->nvram[i]; + i++; + j++; + } + nvram[j] = 0; + j++; + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + kfree(nvp->nvram); + nvp->nvram = nvram; + nvp->nvram_len = j; + return; +fail: + kfree(nvram); + nvp->nvram_len = 0; +} + /* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil * and ending in a NUL. Removes carriage returns, empty lines, comment lines, * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. * End of buffer is completed with token identifying length of buffer. */ -static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) +static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length, + u16 domain_nr, u16 bus_nr) { struct nvram_parser nvp; u32 pad; @@ -212,6 +372,16 @@ static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) if (nvp.state == END) break; } + if (nvp.multi_dev_v1) + brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr); + else if (nvp.multi_dev_v2) + brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr); + + if (nvp.nvram_len == 0) { + kfree(nvp.nvram); + return NULL; + } + pad = nvp.nvram_len; *new_length = roundup(nvp.nvram_len + 1, 4); while (pad != *new_length) { @@ -239,6 +409,8 @@ struct brcmf_fw { u16 flags; const struct firmware *code; const char *nvram_name; + u16 domain_nr; + u16 bus_nr; void (*done)(struct device *dev, const struct firmware *fw, void *nvram_image, u32 nvram_len); }; @@ -254,7 +426,8 @@ static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) goto fail; if (fw) { - nvram = brcmf_fw_nvram_strip(fw, &nvram_length); + nvram = brcmf_fw_nvram_strip(fw, &nvram_length, + fwctx->domain_nr, fwctx->bus_nr); release_firmware(fw); if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) goto fail; @@ -309,11 +482,12 @@ fail: kfree(fwctx); } -int brcmf_fw_get_firmwares(struct device *dev, u16 flags, - const char *code, const char *nvram, - void (*fw_cb)(struct device *dev, - const struct firmware *fw, - void *nvram_image, u32 nvram_len)) +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len), + u16 domain_nr, u16 bus_nr) { struct brcmf_fw *fwctx; @@ -333,8 +507,21 @@ int brcmf_fw_get_firmwares(struct device *dev, u16 flags, fwctx->done = fw_cb; if (flags & BRCMF_FW_REQUEST_NVRAM) fwctx->nvram_name = nvram; + fwctx->domain_nr = domain_nr; + fwctx->bus_nr = bus_nr; return request_firmware_nowait(THIS_MODULE, true, code, dev, GFP_KERNEL, fwctx, brcmf_fw_request_code_done); } + +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len)) +{ + return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0, + 0); +} + diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h index 4d3482356b77..604dd48ab4e0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h @@ -32,6 +32,12 @@ void brcmf_fw_nvram_free(void *nvram); * fails it will not use the callback, but call device_release_driver() * instead which will call the driver .remove() callback. */ +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len), + u16 domain_nr, u16 bus_nr); int brcmf_fw_get_firmwares(struct device *dev, u16 flags, const char *code, const char *nvram, void (*fw_cb)(struct device *dev, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index eb1325371d3a..59440631fec5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -249,8 +249,8 @@ void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) } -void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, - struct sk_buff *skb) +u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb) { struct brcmf_flowring_ring *ring; @@ -271,6 +271,7 @@ void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, if (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW) brcmf_flowring_block(flow, flowid, false); } + return skb_queue_len(&ring->skblist); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h index a34cd394c616..5551861a44bc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h @@ -64,8 +64,8 @@ u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid); void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid); u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid); -void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, - struct sk_buff *skb); +u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb); struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid); void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, struct sk_buff *skb); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index f0dda0ecd23b..5017eaa4af45 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -635,7 +635,7 @@ static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, return 0; } -static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, +static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, u32 slot_id, struct sk_buff **pktout, bool remove_item) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 4ec9811f49c8..1b47de067d25 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -73,7 +73,7 @@ #define BRCMF_MSGBUF_TX_FLUSH_CNT1 32 #define BRCMF_MSGBUF_TX_FLUSH_CNT2 96 -#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 64 +#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 96 #define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32 struct msgbuf_common_hdr { @@ -278,16 +278,6 @@ struct brcmf_msgbuf_pktids { struct brcmf_msgbuf_pktid *array; }; - -/* dma flushing needs implementation for mips and arm platforms. Should - * be put in util. Note, this is not real flushing. It is virtual non - * cached memory. Only write buffers should have to be drained. Though - * this may be different depending on platform...... - */ -#define brcmf_dma_flush(addr, len) -#define brcmf_dma_invalidate_cache(addr, len) - - static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf); @@ -462,7 +452,6 @@ static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx, memcpy(msgbuf->ioctbuf, buf, buf_len); else memset(msgbuf->ioctbuf, 0, buf_len); - brcmf_dma_flush(ioctl_buf, buf_len); err = brcmf_commonring_write_complete(commonring); brcmf_commonring_unlock(commonring); @@ -511,11 +500,9 @@ static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx, msgbuf->rx_pktids, msgbuf->ioctl_resp_pktid); if (msgbuf->ioctl_resp_ret_len != 0) { - if (!skb) { - brcmf_err("Invalid packet id idx recv'd %d\n", - msgbuf->ioctl_resp_pktid); + if (!skb) return -EBADF; - } + memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ? len : msgbuf->ioctl_resp_ret_len); } @@ -797,6 +784,8 @@ static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, struct brcmf_flowring *flow = msgbuf->flow; struct ethhdr *eh = (struct ethhdr *)(skb->data); u32 flowid; + u32 queue_count; + bool force; flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx); if (flowid == BRCMF_FLOWRING_INVALID_ID) { @@ -804,8 +793,9 @@ static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, if (flowid == BRCMF_FLOWRING_INVALID_ID) return -ENOMEM; } - brcmf_flowring_enqueue(flow, flowid, skb); - brcmf_msgbuf_schedule_txdata(msgbuf, flowid, false); + queue_count = brcmf_flowring_enqueue(flow, flowid, skb); + force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0); + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, force); return 0; } @@ -874,10 +864,8 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, msgbuf->tx_pktids, idx); - if (!skb) { - brcmf_err("Invalid packet id idx recv'd %d\n", idx); + if (!skb) return; - } set_bit(flowid, msgbuf->txstatus_done_map); commonring = msgbuf->flowrings[flowid]; @@ -1156,6 +1144,8 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, msgbuf->rx_pktids, idx); + if (!skb) + return; if (data_offset) skb_pull(skb, data_offset); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c index c824570ddea3..03f35e0c52ca 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c @@ -39,10 +39,16 @@ void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) if (!sdiodev->pdata) return; + if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) + sdiodev->pdata->drive_strength = val; + + /* make sure there are interrupts defined in the node */ + if (!of_find_property(np, "interrupts", NULL)) + return; + irq = irq_of_parse_and_map(np, 0); if (!irq) { brcmf_err("interrupt could not be mapped\n"); - devm_kfree(dev, sdiodev->pdata); return; } irqf = irqd_get_trigger_type(irq_get_irq_data(irq)); @@ -50,7 +56,4 @@ void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) sdiodev->pdata->oob_irq_supported = true; sdiodev->pdata->oob_irq_nr = irq; sdiodev->pdata->oob_irq_flags = irqf; - - if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) - sdiodev->pdata->drive_strength = val; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 1831ecd0813e..37a2624d7bba 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -51,6 +51,8 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt" #define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin" #define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt" +#define BRCMF_PCIE_4358_FW_NAME "brcm/brcmfmac4358-pcie.bin" +#define BRCMF_PCIE_4358_NVRAM_NAME "brcm/brcmfmac4358-pcie.txt" #define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ @@ -110,10 +112,11 @@ enum brcmf_pcie_state { BRCMF_PCIE_MB_INT_D2H3_DB0 | \ BRCMF_PCIE_MB_INT_D2H3_DB1) -#define BRCMF_PCIE_MIN_SHARED_VERSION 4 +#define BRCMF_PCIE_MIN_SHARED_VERSION 5 #define BRCMF_PCIE_MAX_SHARED_VERSION 5 #define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF -#define BRCMF_PCIE_SHARED_TXPUSH_SUPPORT 0x4000 +#define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000 +#define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000 #define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000 #define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000 @@ -145,6 +148,10 @@ enum brcmf_pcie_state { #define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET 8 #define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET 12 #define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET 16 +#define BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET 20 +#define BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET 28 +#define BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET 36 +#define BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET 44 #define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET 0 #define BRCMF_SHARED_RING_MAX_SUB_QUEUES 52 @@ -189,6 +196,8 @@ MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME); struct brcmf_pcie_console { @@ -244,6 +253,13 @@ struct brcmf_pciedev_info { bool mbdata_completed; bool irq_allocated; bool wowl_enabled; + u8 dma_idx_sz; + void *idxbuf; + u32 idxbuf_sz; + dma_addr_t idxbuf_dmahandle; + u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset); + void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u16 value); }; struct brcmf_pcie_ringbuf { @@ -273,15 +289,6 @@ static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = { }; -/* dma flushing needs implementation for mips and arm platforms. Should - * be put in util. Note, this is not real flushing. It is virtual non - * cached memory. Only write buffers should have to be drained. Though - * this may be different depending on platform...... - */ -#define brcmf_dma_flush(addr, len) -#define brcmf_dma_invalidate_cache(addr, len) - - static u32 brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) { @@ -329,6 +336,25 @@ brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset, } +static u16 +brcmf_pcie_read_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + u16 *address = devinfo->idxbuf + mem_offset; + + return (*(address)); +} + + +static void +brcmf_pcie_write_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u16 value) +{ + u16 *address = devinfo->idxbuf + mem_offset; + + *(address) = value; +} + + static u32 brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { @@ -874,7 +900,7 @@ static int brcmf_pcie_ring_mb_write_rptr(void *ctx) brcmf_dbg(PCIE, "W r_ptr %d (%d), ring %d\n", commonring->r_ptr, commonring->w_ptr, ring->id); - brcmf_pcie_write_tcm16(devinfo, ring->r_idx_addr, commonring->r_ptr); + devinfo->write_ptr(devinfo, ring->r_idx_addr, commonring->r_ptr); return 0; } @@ -892,7 +918,7 @@ static int brcmf_pcie_ring_mb_write_wptr(void *ctx) brcmf_dbg(PCIE, "W w_ptr %d (%d), ring %d\n", commonring->w_ptr, commonring->r_ptr, ring->id); - brcmf_pcie_write_tcm16(devinfo, ring->w_idx_addr, commonring->w_ptr); + devinfo->write_ptr(devinfo, ring->w_idx_addr, commonring->w_ptr); return 0; } @@ -921,7 +947,7 @@ static int brcmf_pcie_ring_mb_update_rptr(void *ctx) if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) return -EIO; - commonring->r_ptr = brcmf_pcie_read_tcm16(devinfo, ring->r_idx_addr); + commonring->r_ptr = devinfo->read_ptr(devinfo, ring->r_idx_addr); brcmf_dbg(PCIE, "R r_ptr %d (%d), ring %d\n", commonring->r_ptr, commonring->w_ptr, ring->id); @@ -939,7 +965,7 @@ static int brcmf_pcie_ring_mb_update_wptr(void *ctx) if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) return -EIO; - commonring->w_ptr = brcmf_pcie_read_tcm16(devinfo, ring->w_idx_addr); + commonring->w_ptr = devinfo->read_ptr(devinfo, ring->w_idx_addr); brcmf_dbg(PCIE, "R w_ptr %d (%d), ring %d\n", commonring->w_ptr, commonring->r_ptr, ring->id); @@ -1044,6 +1070,13 @@ static void brcmf_pcie_release_ringbuffers(struct brcmf_pciedev_info *devinfo) } kfree(devinfo->shared.flowrings); devinfo->shared.flowrings = NULL; + if (devinfo->idxbuf) { + dma_free_coherent(&devinfo->pdev->dev, + devinfo->idxbuf_sz, + devinfo->idxbuf, + devinfo->idxbuf_dmahandle); + devinfo->idxbuf = NULL; + } } @@ -1059,19 +1092,72 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) u32 addr; u32 ring_mem_ptr; u32 i; + u64 address; + u32 bufsz; u16 max_sub_queues; + u8 idx_offset; ring_addr = devinfo->shared.ring_info_addr; brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr); + addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; + max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); + + if (devinfo->dma_idx_sz != 0) { + bufsz = (BRCMF_NROF_D2H_COMMON_MSGRINGS + max_sub_queues) * + devinfo->dma_idx_sz * 2; + devinfo->idxbuf = dma_alloc_coherent(&devinfo->pdev->dev, bufsz, + &devinfo->idxbuf_dmahandle, + GFP_KERNEL); + if (!devinfo->idxbuf) + devinfo->dma_idx_sz = 0; + } - addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; - d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; - d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; - h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; - h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + if (devinfo->dma_idx_sz == 0) { + addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; + d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; + d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; + h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; + h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + idx_offset = sizeof(u32); + devinfo->write_ptr = brcmf_pcie_write_tcm16; + devinfo->read_ptr = brcmf_pcie_read_tcm16; + brcmf_dbg(PCIE, "Using TCM indices\n"); + } else { + memset(devinfo->idxbuf, 0, bufsz); + devinfo->idxbuf_sz = bufsz; + idx_offset = devinfo->dma_idx_sz; + devinfo->write_ptr = brcmf_pcie_write_idx; + devinfo->read_ptr = brcmf_pcie_read_idx; + + h2d_w_idx_ptr = 0; + addr = ring_addr + BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET; + address = (u64)devinfo->idxbuf_dmahandle; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + h2d_r_idx_ptr = h2d_w_idx_ptr + max_sub_queues * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET; + address += max_sub_queues * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + d2h_w_idx_ptr = h2d_r_idx_ptr + max_sub_queues * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET; + address += max_sub_queues * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + d2h_r_idx_ptr = d2h_w_idx_ptr + + BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET; + address += BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + brcmf_dbg(PCIE, "Using host memory indices\n"); + } addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET; ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr); @@ -1085,8 +1171,8 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ring->id = i; devinfo->shared.commonrings[i] = ring; - h2d_w_idx_ptr += sizeof(u32); - h2d_r_idx_ptr += sizeof(u32); + h2d_w_idx_ptr += idx_offset; + h2d_r_idx_ptr += idx_offset; ring_mem_ptr += BRCMF_RING_MEM_SZ; } @@ -1100,13 +1186,11 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ring->id = i; devinfo->shared.commonrings[i] = ring; - d2h_w_idx_ptr += sizeof(u32); - d2h_r_idx_ptr += sizeof(u32); + d2h_w_idx_ptr += idx_offset; + d2h_r_idx_ptr += idx_offset; ring_mem_ptr += BRCMF_RING_MEM_SZ; } - addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; - max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); devinfo->shared.nrof_flowrings = max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS; rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring), @@ -1130,15 +1214,15 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ring); ring->w_idx_addr = h2d_w_idx_ptr; ring->r_idx_addr = h2d_r_idx_ptr; - h2d_w_idx_ptr += sizeof(u32); - h2d_r_idx_ptr += sizeof(u32); + h2d_w_idx_ptr += idx_offset; + h2d_r_idx_ptr += idx_offset; } devinfo->shared.flowrings = rings; return 0; fail: - brcmf_err("Allocating commonring buffers failed\n"); + brcmf_err("Allocating ring buffers failed\n"); brcmf_pcie_release_ringbuffers(devinfo); return -ENOMEM; } @@ -1171,7 +1255,6 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) goto fail; memset(devinfo->shared.scratch, 0, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); - brcmf_dma_flush(devinfo->shared.scratch, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET; @@ -1189,7 +1272,6 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) goto fail; memset(devinfo->shared.ringupd, 0, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); - brcmf_dma_flush(devinfo->shared.ringupd, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET; @@ -1276,10 +1358,13 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, brcmf_err("Unsupported PCIE version %d\n", version); return -EINVAL; } - if (shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT) { - brcmf_err("Unsupported legacy TX mode 0x%x\n", - shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT); - return -EINVAL; + + /* check firmware support dma indicies */ + if (shared->flags & BRCMF_PCIE_SHARED_DMA_INDEX) { + if (shared->flags & BRCMF_PCIE_SHARED_DMA_2B_IDX) + devinfo->dma_idx_sz = sizeof(u16); + else + devinfo->dma_idx_sz = sizeof(u32); } addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET; @@ -1333,6 +1418,10 @@ static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) fw_name = BRCMF_PCIE_43570_FW_NAME; nvram_name = BRCMF_PCIE_43570_NVRAM_NAME; break; + case BRCM_CC_4358_CHIP_ID: + fw_name = BRCMF_PCIE_4358_FW_NAME; + nvram_name = BRCMF_PCIE_4358_NVRAM_NAME; + break; default: brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip); return -ENODEV; @@ -1609,7 +1698,7 @@ static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw, bus->msgbuf->commonrings[i] = &devinfo->shared.commonrings[i]->commonring; - flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(flowrings), + flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*flowrings), GFP_KERNEL); if (!flowrings) goto fail; @@ -1641,8 +1730,13 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct brcmf_pciedev_info *devinfo; struct brcmf_pciedev *pcie_bus_dev; struct brcmf_bus *bus; + u16 domain_nr; + u16 bus_nr; - brcmf_dbg(PCIE, "Enter %x:%x\n", pdev->vendor, pdev->device); + domain_nr = pci_domain_nr(pdev->bus) + 1; + bus_nr = pdev->bus->number; + brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device, + domain_nr, bus_nr); ret = -ENOMEM; devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); @@ -1691,10 +1785,10 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) goto fail_bus; - ret = brcmf_fw_get_firmwares(bus->dev, BRCMF_FW_REQUEST_NVRAM | - BRCMF_FW_REQ_NV_OPTIONAL, - devinfo->fw_name, devinfo->nvram_name, - brcmf_pcie_setup); + ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM | + BRCMF_FW_REQ_NV_OPTIONAL, + devinfo->fw_name, devinfo->nvram_name, + brcmf_pcie_setup, domain_nr, bus_nr); if (ret == 0) return 0; fail_bus: @@ -1850,9 +1944,11 @@ static struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID), { /* end: all zeroes */ } }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index ab0c89833013..bf7a8b1ad914 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -601,6 +601,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM43241B0_NVRAM_NAME "brcm/brcmfmac43241b0-sdio.txt" #define BCM43241B4_FIRMWARE_NAME "brcm/brcmfmac43241b4-sdio.bin" #define BCM43241B4_NVRAM_NAME "brcm/brcmfmac43241b4-sdio.txt" +#define BCM43241B5_FIRMWARE_NAME "brcm/brcmfmac43241b5-sdio.bin" +#define BCM43241B5_NVRAM_NAME "brcm/brcmfmac43241b5-sdio.txt" #define BCM4329_FIRMWARE_NAME "brcm/brcmfmac4329-sdio.bin" #define BCM4329_NVRAM_NAME "brcm/brcmfmac4329-sdio.txt" #define BCM4330_FIRMWARE_NAME "brcm/brcmfmac4330-sdio.bin" @@ -628,6 +630,8 @@ MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B5_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B5_NVRAM_NAME); MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4329_NVRAM_NAME); MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); @@ -667,7 +671,8 @@ enum brcmf_firmware_type { static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, - { BRCM_CC_43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, + { BRCM_CC_43241_CHIP_ID, 0x00000020, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, + { BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43241B5) }, { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, @@ -3550,10 +3555,6 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) return; } - if (bus->sdiodev->state != BRCMF_SDIOD_DATA) { - brcmf_err("bus is down. we have nothing to do\n"); - return; - } /* Count the interrupt call */ bus->sdcnt.intrcount++; if (in_interrupt()) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 5df6aa72cc2d..daba86d881bc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1270,8 +1270,13 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->chiprev = bus_pub->chiprev; /* request firmware here */ - brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL, - brcmf_usb_probe_phase2); + ret = brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), + NULL, brcmf_usb_probe_phase2); + if (ret) { + brcmf_err("firmware request failed: %d\n", ret); + goto fail; + } + return 0; fail: |