xref: /openbmc/linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c (revision fb1862ce266479b7e0229ff2ce46f6433dcd3807)
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