1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
205491d2cSKalle Valo /*
305491d2cSKalle Valo * Copyright (c) 2014 Broadcom Corporation
405491d2cSKalle Valo */
505491d2cSKalle Valo
605491d2cSKalle Valo #include <linux/netdevice.h>
705491d2cSKalle Valo #include <linux/module.h>
805491d2cSKalle Valo
905491d2cSKalle Valo #include <brcm_hw_ids.h>
1048ed16e8SHante Meuleman #include <brcmu_wifi.h>
1105491d2cSKalle Valo #include "core.h"
1205491d2cSKalle Valo #include "bus.h"
1305491d2cSKalle Valo #include "debug.h"
1405491d2cSKalle Valo #include "fwil.h"
1548ed16e8SHante Meuleman #include "fwil_types.h"
1647b56329SArend van Spriel #include "fwvid.h"
1705491d2cSKalle Valo #include "feature.h"
187d34b056SHante Meuleman #include "common.h"
1905491d2cSKalle Valo
209fe929aaSArend van Spriel #define BRCMF_FW_UNSUPPORTED 23
2105491d2cSKalle Valo
2205491d2cSKalle Valo /*
2305491d2cSKalle Valo * expand feature list to array of feature strings.
2405491d2cSKalle Valo */
2505491d2cSKalle Valo #define BRCMF_FEAT_DEF(_f) \
2605491d2cSKalle Valo #_f,
2705491d2cSKalle Valo static const char *brcmf_feat_names[] = {
2805491d2cSKalle Valo BRCMF_FEAT_LIST
2905491d2cSKalle Valo };
3005491d2cSKalle Valo #undef BRCMF_FEAT_DEF
3105491d2cSKalle Valo
32ec64241cSArend van Spriel struct brcmf_feat_fwcap {
33ec64241cSArend van Spriel enum brcmf_feat_id feature;
34ec64241cSArend van Spriel const char * const fwcap_id;
35ec64241cSArend van Spriel };
36ec64241cSArend van Spriel
37ec64241cSArend van Spriel static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
38ec64241cSArend van Spriel { BRCMF_FEAT_MBSS, "mbss" },
39ec64241cSArend van Spriel { BRCMF_FEAT_MCHAN, "mchan" },
40ec64241cSArend van Spriel { BRCMF_FEAT_P2P, "p2p" },
4101f69dfaSRafał Miłecki { BRCMF_FEAT_MONITOR, "monitor" },
4220f2c5faSRafał Miłecki { BRCMF_FEAT_MONITOR_FLAG, "rtap" },
43e63410acSRafał Miłecki { BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" },
443b1e0a7bSChung-Hsien Hsu { BRCMF_FEAT_DOT11H, "802.11h" },
453b1e0a7bSChung-Hsien Hsu { BRCMF_FEAT_SAE, "sae" },
46787fb926SChung-Hsien Hsu { BRCMF_FEAT_FWAUTH, "idauth" },
47ec64241cSArend van Spriel };
48ec64241cSArend van Spriel
4905491d2cSKalle Valo #ifdef DEBUG
5005491d2cSKalle Valo /*
5105491d2cSKalle Valo * expand quirk list to array of quirk strings.
5205491d2cSKalle Valo */
5305491d2cSKalle Valo #define BRCMF_QUIRK_DEF(_q) \
5405491d2cSKalle Valo #_q,
5505491d2cSKalle Valo static const char * const brcmf_quirk_names[] = {
5605491d2cSKalle Valo BRCMF_QUIRK_LIST
5705491d2cSKalle Valo };
5805491d2cSKalle Valo #undef BRCMF_QUIRK_DEF
5905491d2cSKalle Valo
6005491d2cSKalle Valo /**
6105491d2cSKalle Valo * brcmf_feat_debugfs_read() - expose feature info to debugfs.
6205491d2cSKalle Valo *
6305491d2cSKalle Valo * @seq: sequence for debugfs entry.
6405491d2cSKalle Valo * @data: raw data pointer.
6505491d2cSKalle Valo */
brcmf_feat_debugfs_read(struct seq_file * seq,void * data)6605491d2cSKalle Valo static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
6705491d2cSKalle Valo {
6805491d2cSKalle Valo struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
6905491d2cSKalle Valo u32 feats = bus_if->drvr->feat_flags;
7005491d2cSKalle Valo u32 quirks = bus_if->drvr->chip_quirks;
7105491d2cSKalle Valo int id;
7205491d2cSKalle Valo
7305491d2cSKalle Valo seq_printf(seq, "Features: %08x\n", feats);
7405491d2cSKalle Valo for (id = 0; id < BRCMF_FEAT_LAST; id++)
7505491d2cSKalle Valo if (feats & BIT(id))
7605491d2cSKalle Valo seq_printf(seq, "\t%s\n", brcmf_feat_names[id]);
7705491d2cSKalle Valo seq_printf(seq, "\nQuirks: %08x\n", quirks);
7805491d2cSKalle Valo for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++)
7905491d2cSKalle Valo if (quirks & BIT(id))
8005491d2cSKalle Valo seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]);
8105491d2cSKalle Valo return 0;
8205491d2cSKalle Valo }
8305491d2cSKalle Valo #else
brcmf_feat_debugfs_read(struct seq_file * seq,void * data)8405491d2cSKalle Valo static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
8505491d2cSKalle Valo {
8605491d2cSKalle Valo return 0;
8705491d2cSKalle Valo }
8805491d2cSKalle Valo #endif /* DEBUG */
8905491d2cSKalle Valo
901e591c56SRafał Miłecki struct brcmf_feat_fwfeat {
911e591c56SRafał Miłecki const char * const fwid;
921e591c56SRafał Miłecki u32 feat_flags;
931e591c56SRafał Miłecki };
941e591c56SRafał Miłecki
951e591c56SRafał Miłecki static const struct brcmf_feat_fwfeat brcmf_feat_fwfeat_map[] = {
961e591c56SRafał Miłecki /* brcmfmac43602-pcie.ap.bin from linux-firmware.git commit ea1178515b88 */
971e591c56SRafał Miłecki { "01-6cb8e269", BIT(BRCMF_FEAT_MONITOR) },
981e591c56SRafał Miłecki /* brcmfmac4366b-pcie.bin from linux-firmware.git commit 52442afee990 */
991e591c56SRafał Miłecki { "01-c47a91a4", BIT(BRCMF_FEAT_MONITOR) },
100e665988bSRafał Miłecki /* brcmfmac4366b-pcie.bin from linux-firmware.git commit 211de1679a68 */
101e665988bSRafał Miłecki { "01-801fb449", BIT(BRCMF_FEAT_MONITOR_FMT_HW_RX_HDR) },
102e665988bSRafał Miłecki /* brcmfmac4366c-pcie.bin from linux-firmware.git commit 211de1679a68 */
103e665988bSRafał Miłecki { "01-d2cbb8fd", BIT(BRCMF_FEAT_MONITOR_FMT_HW_RX_HDR) },
1041e591c56SRafał Miłecki };
1051e591c56SRafał Miłecki
brcmf_feat_firmware_overrides(struct brcmf_pub * drv)1061e591c56SRafał Miłecki static void brcmf_feat_firmware_overrides(struct brcmf_pub *drv)
1071e591c56SRafał Miłecki {
1081e591c56SRafał Miłecki const struct brcmf_feat_fwfeat *e;
1091e591c56SRafał Miłecki u32 feat_flags = 0;
1101e591c56SRafał Miłecki int i;
1111e591c56SRafał Miłecki
1121e591c56SRafał Miłecki for (i = 0; i < ARRAY_SIZE(brcmf_feat_fwfeat_map); i++) {
1131e591c56SRafał Miłecki e = &brcmf_feat_fwfeat_map[i];
1141e591c56SRafał Miłecki if (!strcmp(e->fwid, drv->fwver)) {
1151e591c56SRafał Miłecki feat_flags = e->feat_flags;
1161e591c56SRafał Miłecki break;
1171e591c56SRafał Miłecki }
1181e591c56SRafał Miłecki }
1191e591c56SRafał Miłecki
1201e591c56SRafał Miłecki if (!feat_flags)
1211e591c56SRafał Miłecki return;
1221e591c56SRafał Miłecki
1231e591c56SRafał Miłecki for (i = 0; i < BRCMF_FEAT_LAST; i++)
1241e591c56SRafał Miłecki if (feat_flags & BIT(i))
1251e591c56SRafał Miłecki brcmf_dbg(INFO, "enabling firmware feature: %s\n",
1261e591c56SRafał Miłecki brcmf_feat_names[i]);
1271e591c56SRafał Miłecki drv->feat_flags |= feat_flags;
1281e591c56SRafał Miłecki }
1291e591c56SRafał Miłecki
130d75ef1f8SHector Martin struct brcmf_feat_wlcfeat {
131d75ef1f8SHector Martin u16 min_ver_major;
132d75ef1f8SHector Martin u16 min_ver_minor;
133d75ef1f8SHector Martin u32 feat_flags;
134d75ef1f8SHector Martin };
135d75ef1f8SHector Martin
136d75ef1f8SHector Martin static const struct brcmf_feat_wlcfeat brcmf_feat_wlcfeat_map[] = {
137d75ef1f8SHector Martin { 12, 0, BIT(BRCMF_FEAT_PMKID_V2) },
138d75ef1f8SHector Martin { 13, 0, BIT(BRCMF_FEAT_PMKID_V3) },
139d75ef1f8SHector Martin };
140d75ef1f8SHector Martin
brcmf_feat_wlc_version_overrides(struct brcmf_pub * drv)141d75ef1f8SHector Martin static void brcmf_feat_wlc_version_overrides(struct brcmf_pub *drv)
142d75ef1f8SHector Martin {
143d75ef1f8SHector Martin struct brcmf_if *ifp = brcmf_get_ifp(drv, 0);
144d75ef1f8SHector Martin const struct brcmf_feat_wlcfeat *e;
145d75ef1f8SHector Martin struct brcmf_wlc_version_le ver;
146d75ef1f8SHector Martin u32 feat_flags = 0;
147d75ef1f8SHector Martin int i, err, major, minor;
148d75ef1f8SHector Martin
149d75ef1f8SHector Martin err = brcmf_fil_iovar_data_get(ifp, "wlc_ver", &ver, sizeof(ver));
150d75ef1f8SHector Martin if (err)
151d75ef1f8SHector Martin return;
152d75ef1f8SHector Martin
153d75ef1f8SHector Martin major = le16_to_cpu(ver.wlc_ver_major);
154d75ef1f8SHector Martin minor = le16_to_cpu(ver.wlc_ver_minor);
155d75ef1f8SHector Martin
156d75ef1f8SHector Martin brcmf_dbg(INFO, "WLC version: %d.%d\n", major, minor);
157d75ef1f8SHector Martin
158d75ef1f8SHector Martin for (i = 0; i < ARRAY_SIZE(brcmf_feat_wlcfeat_map); i++) {
159d75ef1f8SHector Martin e = &brcmf_feat_wlcfeat_map[i];
160d75ef1f8SHector Martin if (major > e->min_ver_major ||
161d75ef1f8SHector Martin (major == e->min_ver_major &&
162d75ef1f8SHector Martin minor >= e->min_ver_minor)) {
163d75ef1f8SHector Martin feat_flags |= e->feat_flags;
164d75ef1f8SHector Martin }
165d75ef1f8SHector Martin }
166d75ef1f8SHector Martin
167d75ef1f8SHector Martin if (!feat_flags)
168d75ef1f8SHector Martin return;
169d75ef1f8SHector Martin
170d75ef1f8SHector Martin for (i = 0; i < BRCMF_FEAT_LAST; i++)
171d75ef1f8SHector Martin if (feat_flags & BIT(i))
172d75ef1f8SHector Martin brcmf_dbg(INFO, "enabling firmware feature: %s\n",
173d75ef1f8SHector Martin brcmf_feat_names[i]);
174d75ef1f8SHector Martin drv->feat_flags |= feat_flags;
175d75ef1f8SHector Martin }
176d75ef1f8SHector Martin
17705491d2cSKalle Valo /**
17805491d2cSKalle Valo * brcmf_feat_iovar_int_get() - determine feature through iovar query.
17905491d2cSKalle Valo *
18005491d2cSKalle Valo * @ifp: interface to query.
18105491d2cSKalle Valo * @id: feature id.
18205491d2cSKalle Valo * @name: iovar name.
18305491d2cSKalle Valo */
brcmf_feat_iovar_int_get(struct brcmf_if * ifp,enum brcmf_feat_id id,char * name)18405491d2cSKalle Valo static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
18505491d2cSKalle Valo enum brcmf_feat_id id, char *name)
18605491d2cSKalle Valo {
187*fb1862ceSArend van Spriel u32 data;
18805491d2cSKalle Valo int err;
18905491d2cSKalle Valo
19093389734SArend Van Spriel /* we need to know firmware error */
19193389734SArend Van Spriel ifp->fwil_fwerr = true;
19293389734SArend Van Spriel
19305491d2cSKalle Valo err = brcmf_fil_iovar_int_get(ifp, name, &data);
1946c04deaeSWright Feng if (err != -BRCMF_FW_UNSUPPORTED) {
19505491d2cSKalle Valo brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
19605491d2cSKalle Valo ifp->drvr->feat_flags |= BIT(id);
19705491d2cSKalle Valo } else {
19805491d2cSKalle Valo brcmf_dbg(TRACE, "%s feature check failed: %d\n",
19905491d2cSKalle Valo brcmf_feat_names[id], err);
20005491d2cSKalle Valo }
20193389734SArend Van Spriel
20293389734SArend Van Spriel ifp->fwil_fwerr = false;
20305491d2cSKalle Valo }
20405491d2cSKalle Valo
brcmf_feat_iovar_data_set(struct brcmf_if * ifp,enum brcmf_feat_id id,char * name,const void * data,size_t len)2059fe929aaSArend van Spriel static void brcmf_feat_iovar_data_set(struct brcmf_if *ifp,
2069fe929aaSArend van Spriel enum brcmf_feat_id id, char *name,
2079fe929aaSArend van Spriel const void *data, size_t len)
2089fe929aaSArend van Spriel {
2099fe929aaSArend van Spriel int err;
2109fe929aaSArend van Spriel
21193389734SArend Van Spriel /* we need to know firmware error */
21293389734SArend Van Spriel ifp->fwil_fwerr = true;
21393389734SArend Van Spriel
2149fe929aaSArend van Spriel err = brcmf_fil_iovar_data_set(ifp, name, data, len);
2159fe929aaSArend van Spriel if (err != -BRCMF_FW_UNSUPPORTED) {
2169fe929aaSArend van Spriel brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
2179fe929aaSArend van Spriel ifp->drvr->feat_flags |= BIT(id);
2189fe929aaSArend van Spriel } else {
2199fe929aaSArend van Spriel brcmf_dbg(TRACE, "%s feature check failed: %d\n",
2209fe929aaSArend van Spriel brcmf_feat_names[id], err);
2219fe929aaSArend van Spriel }
22293389734SArend Van Spriel
22393389734SArend Van Spriel ifp->fwil_fwerr = false;
2249fe929aaSArend van Spriel }
2259fe929aaSArend van Spriel
22659c2a30dSArend van Spriel #define MAX_CAPS_BUFFER_SIZE 768
brcmf_feat_firmware_capabilities(struct brcmf_if * ifp)227ec64241cSArend van Spriel static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
22805491d2cSKalle Valo {
229dcb1471bSRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
2307762bb13SWright Feng char caps[MAX_CAPS_BUFFER_SIZE];
231ec64241cSArend van Spriel enum brcmf_feat_id id;
2327762bb13SWright Feng int i, err;
23305491d2cSKalle Valo
2347762bb13SWright Feng err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
2357762bb13SWright Feng if (err) {
236dcb1471bSRafał Miłecki bphy_err(drvr, "could not get firmware cap (%d)\n", err);
2377762bb13SWright Feng return;
2387762bb13SWright Feng }
2397762bb13SWright Feng
240ec64241cSArend van Spriel brcmf_dbg(INFO, "[ %s]\n", caps);
241ec64241cSArend van Spriel
242ec64241cSArend van Spriel for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) {
243ec64241cSArend van Spriel if (strnstr(caps, brcmf_fwcap_map[i].fwcap_id, sizeof(caps))) {
244ec64241cSArend van Spriel id = brcmf_fwcap_map[i].feature;
245ec64241cSArend van Spriel brcmf_dbg(INFO, "enabling feature: %s\n",
246ec64241cSArend van Spriel brcmf_feat_names[id]);
24705491d2cSKalle Valo ifp->drvr->feat_flags |= BIT(id);
248ec64241cSArend van Spriel }
24905491d2cSKalle Valo }
25005491d2cSKalle Valo }
25105491d2cSKalle Valo
25288001968SRafał Miłecki /**
25388001968SRafał Miłecki * brcmf_feat_fwcap_debugfs_read() - expose firmware capabilities to debugfs.
25488001968SRafał Miłecki *
25588001968SRafał Miłecki * @seq: sequence for debugfs entry.
25688001968SRafał Miłecki * @data: raw data pointer.
25788001968SRafał Miłecki */
brcmf_feat_fwcap_debugfs_read(struct seq_file * seq,void * data)25888001968SRafał Miłecki static int brcmf_feat_fwcap_debugfs_read(struct seq_file *seq, void *data)
25988001968SRafał Miłecki {
26088001968SRafał Miłecki struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
261dcb1471bSRafał Miłecki struct brcmf_pub *drvr = bus_if->drvr;
262dcb1471bSRafał Miłecki struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
26388001968SRafał Miłecki char caps[MAX_CAPS_BUFFER_SIZE + 1] = { };
26488001968SRafał Miłecki char *tmp;
26588001968SRafał Miłecki int err;
26688001968SRafał Miłecki
26788001968SRafał Miłecki err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
26888001968SRafał Miłecki if (err) {
269dcb1471bSRafał Miłecki bphy_err(drvr, "could not get firmware cap (%d)\n", err);
27088001968SRafał Miłecki return err;
27188001968SRafał Miłecki }
27288001968SRafał Miłecki
27388001968SRafał Miłecki /* Put every capability in a new line */
27488001968SRafał Miłecki for (tmp = caps; *tmp; tmp++) {
27588001968SRafał Miłecki if (*tmp == ' ')
27688001968SRafał Miłecki *tmp = '\n';
27788001968SRafał Miłecki }
27888001968SRafał Miłecki
27988001968SRafał Miłecki /* Usually there is a space at the end of capabilities string */
28088001968SRafał Miłecki seq_printf(seq, "%s", caps);
28188001968SRafał Miłecki /* So make sure we don't print two line breaks */
28288001968SRafał Miłecki if (tmp > caps && *(tmp - 1) != '\n')
28388001968SRafał Miłecki seq_printf(seq, "\n");
28488001968SRafał Miłecki
28588001968SRafał Miłecki return 0;
28688001968SRafał Miłecki }
28788001968SRafał Miłecki
brcmf_feat_attach(struct brcmf_pub * drvr)28805491d2cSKalle Valo void brcmf_feat_attach(struct brcmf_pub *drvr)
28905491d2cSKalle Valo {
29005491d2cSKalle Valo struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
29148ed16e8SHante Meuleman struct brcmf_pno_macaddr_le pfn_mac;
2929fe929aaSArend van Spriel struct brcmf_gscan_config gscan_cfg;
2935c22fb85SHante Meuleman u32 wowl_cap;
29448ed16e8SHante Meuleman s32 err;
29505491d2cSKalle Valo
296ec64241cSArend van Spriel brcmf_feat_firmware_capabilities(ifp);
2979fe929aaSArend van Spriel memset(&gscan_cfg, 0, sizeof(gscan_cfg));
298f957dd3cSIan W MORRISON if (drvr->bus_if->chip != BRCM_CC_43430_CHIP_ID &&
299ed26edf7SZhao, Jiaqing drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID &&
300be376df7SMarek Vasut drvr->bus_if->chip != BRCM_CC_43454_CHIP_ID &&
301be376df7SMarek Vasut drvr->bus_if->chip != CY_CC_43439_CHIP_ID)
302e9bf53abSArend Van Spriel brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN,
303e9bf53abSArend Van Spriel "pfn_gscan_cfg",
3049fe929aaSArend van Spriel &gscan_cfg, sizeof(gscan_cfg));
30505491d2cSKalle Valo brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
30605491d2cSKalle Valo if (drvr->bus_if->wowl_supported)
30705491d2cSKalle Valo brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
3085c22fb85SHante Meuleman if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) {
3095c22fb85SHante Meuleman err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
3105c22fb85SHante Meuleman if (!err) {
31173ef9e64SHante Meuleman ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_WOWL_ARP_ND);
3125c22fb85SHante Meuleman if (wowl_cap & BRCMF_WOWL_PFN_FOUND)
3135c22fb85SHante Meuleman ifp->drvr->feat_flags |=
3145c22fb85SHante Meuleman BIT(BRCMF_FEAT_WOWL_ND);
3155c22fb85SHante Meuleman if (wowl_cap & BRCMF_WOWL_GTK_FAILURE)
3165c22fb85SHante Meuleman ifp->drvr->feat_flags |=
3175c22fb85SHante Meuleman BIT(BRCMF_FEAT_WOWL_GTK);
3185c22fb85SHante Meuleman }
3195c22fb85SHante Meuleman }
32092d3b88bSArend van Spriel /* MBSS does not work for all chips */
32192d3b88bSArend van Spriel switch (drvr->bus_if->chip) {
32292d3b88bSArend van Spriel case BRCM_CC_4330_CHIP_ID:
32392d3b88bSArend van Spriel case BRCM_CC_43362_CHIP_ID:
324ec64241cSArend van Spriel ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS);
32592d3b88bSArend van Spriel break;
32692d3b88bSArend van Spriel default:
32792d3b88bSArend van Spriel break;
32892d3b88bSArend van Spriel }
3298abffd81SHante Meuleman brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode");
330a7b82d47SHante Meuleman brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable");
331240d61a9SHante Meuleman brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp");
3326c04deaeSWright Feng brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_DUMP_OBSS, "dump_obss");
33305491d2cSKalle Valo
33448ed16e8SHante Meuleman pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
33548ed16e8SHante Meuleman err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac,
33648ed16e8SHante Meuleman sizeof(pfn_mac));
33748ed16e8SHante Meuleman if (!err)
33848ed16e8SHante Meuleman ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC);
33948ed16e8SHante Meuleman
340c5767385SJaehoon Chung brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_FWSUP, "sup_wpa");
341398ce273SHector Martin brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_SCAN_V2, "scan_ver");
342c5767385SJaehoon Chung
34347b56329SArend van Spriel brcmf_fwvid_feat_attach(ifp);
34447b56329SArend van Spriel
3457d34b056SHante Meuleman if (drvr->settings->feature_disable) {
34605491d2cSKalle Valo brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n",
3477d34b056SHante Meuleman ifp->drvr->feat_flags,
3487d34b056SHante Meuleman drvr->settings->feature_disable);
3497d34b056SHante Meuleman ifp->drvr->feat_flags &= ~drvr->settings->feature_disable;
35005491d2cSKalle Valo }
35105491d2cSKalle Valo
352d75ef1f8SHector Martin brcmf_feat_wlc_version_overrides(drvr);
3531e591c56SRafał Miłecki brcmf_feat_firmware_overrides(drvr);
3541e591c56SRafał Miłecki
35505491d2cSKalle Valo /* set chip related quirks */
35605491d2cSKalle Valo switch (drvr->bus_if->chip) {
35705491d2cSKalle Valo case BRCM_CC_43236_CHIP_ID:
35805491d2cSKalle Valo drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH);
35905491d2cSKalle Valo break;
36005491d2cSKalle Valo case BRCM_CC_4329_CHIP_ID:
36105491d2cSKalle Valo drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC);
36205491d2cSKalle Valo break;
36305491d2cSKalle Valo default:
36405491d2cSKalle Valo /* no quirks */
36505491d2cSKalle Valo break;
36605491d2cSKalle Valo }
36734789d0cSArend Van Spriel }
36805491d2cSKalle Valo
brcmf_feat_debugfs_create(struct brcmf_pub * drvr)36934789d0cSArend Van Spriel void brcmf_feat_debugfs_create(struct brcmf_pub *drvr)
37034789d0cSArend Van Spriel {
37105491d2cSKalle Valo brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read);
37288001968SRafał Miłecki brcmf_debugfs_add_entry(drvr, "fwcap", brcmf_feat_fwcap_debugfs_read);
37305491d2cSKalle Valo }
37405491d2cSKalle Valo
brcmf_feat_is_enabled(struct brcmf_if * ifp,enum brcmf_feat_id id)37505491d2cSKalle Valo bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id)
37605491d2cSKalle Valo {
37705491d2cSKalle Valo return (ifp->drvr->feat_flags & BIT(id));
37805491d2cSKalle Valo }
37905491d2cSKalle Valo
brcmf_feat_is_quirk_enabled(struct brcmf_if * ifp,enum brcmf_feat_quirk quirk)38005491d2cSKalle Valo bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
38105491d2cSKalle Valo enum brcmf_feat_quirk quirk)
38205491d2cSKalle Valo {
38305491d2cSKalle Valo return (ifp->drvr->chip_quirks & BIT(quirk));
38405491d2cSKalle Valo }
385