1*b4c3e9b5SBjoern A. Zeeb // SPDX-License-Identifier: ISC
2*b4c3e9b5SBjoern A. Zeeb /*
3*b4c3e9b5SBjoern A. Zeeb * Copyright (c) 2014 Broadcom Corporation
4*b4c3e9b5SBjoern A. Zeeb */
5*b4c3e9b5SBjoern A. Zeeb
6*b4c3e9b5SBjoern A. Zeeb #include <linux/netdevice.h>
7*b4c3e9b5SBjoern A. Zeeb #include <linux/module.h>
8*b4c3e9b5SBjoern A. Zeeb
9*b4c3e9b5SBjoern A. Zeeb #include <brcm_hw_ids.h>
10*b4c3e9b5SBjoern A. Zeeb #include <brcmu_wifi.h>
11*b4c3e9b5SBjoern A. Zeeb #include "core.h"
12*b4c3e9b5SBjoern A. Zeeb #include "bus.h"
13*b4c3e9b5SBjoern A. Zeeb #include "debug.h"
14*b4c3e9b5SBjoern A. Zeeb #include "fwil.h"
15*b4c3e9b5SBjoern A. Zeeb #include "fwil_types.h"
16*b4c3e9b5SBjoern A. Zeeb #include "fwvid.h"
17*b4c3e9b5SBjoern A. Zeeb #include "feature.h"
18*b4c3e9b5SBjoern A. Zeeb #include "common.h"
19*b4c3e9b5SBjoern A. Zeeb
20*b4c3e9b5SBjoern A. Zeeb #define BRCMF_FW_UNSUPPORTED 23
21*b4c3e9b5SBjoern A. Zeeb
22*b4c3e9b5SBjoern A. Zeeb /*
23*b4c3e9b5SBjoern A. Zeeb * expand feature list to array of feature strings.
24*b4c3e9b5SBjoern A. Zeeb */
25*b4c3e9b5SBjoern A. Zeeb #define BRCMF_FEAT_DEF(_f) \
26*b4c3e9b5SBjoern A. Zeeb #_f,
27*b4c3e9b5SBjoern A. Zeeb static const char *brcmf_feat_names[] = {
28*b4c3e9b5SBjoern A. Zeeb BRCMF_FEAT_LIST
29*b4c3e9b5SBjoern A. Zeeb };
30*b4c3e9b5SBjoern A. Zeeb #undef BRCMF_FEAT_DEF
31*b4c3e9b5SBjoern A. Zeeb
32*b4c3e9b5SBjoern A. Zeeb struct brcmf_feat_fwcap {
33*b4c3e9b5SBjoern A. Zeeb enum brcmf_feat_id feature;
34*b4c3e9b5SBjoern A. Zeeb const char * const fwcap_id;
35*b4c3e9b5SBjoern A. Zeeb };
36*b4c3e9b5SBjoern A. Zeeb
37*b4c3e9b5SBjoern A. Zeeb static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
38*b4c3e9b5SBjoern A. Zeeb { BRCMF_FEAT_MBSS, "mbss" },
39*b4c3e9b5SBjoern A. Zeeb { BRCMF_FEAT_MCHAN, "mchan" },
40*b4c3e9b5SBjoern A. Zeeb { BRCMF_FEAT_P2P, "p2p" },
41*b4c3e9b5SBjoern A. Zeeb { BRCMF_FEAT_MONITOR, "monitor" },
42*b4c3e9b5SBjoern A. Zeeb { BRCMF_FEAT_MONITOR_FLAG, "rtap" },
43*b4c3e9b5SBjoern A. Zeeb { BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" },
44*b4c3e9b5SBjoern A. Zeeb { BRCMF_FEAT_DOT11H, "802.11h" },
45*b4c3e9b5SBjoern A. Zeeb { BRCMF_FEAT_SAE, "sae " },
46*b4c3e9b5SBjoern A. Zeeb { BRCMF_FEAT_FWAUTH, "idauth" },
47*b4c3e9b5SBjoern A. Zeeb { BRCMF_FEAT_SAE_EXT, "sae_ext" },
48*b4c3e9b5SBjoern A. Zeeb };
49*b4c3e9b5SBjoern A. Zeeb
50*b4c3e9b5SBjoern A. Zeeb #ifdef DEBUG
51*b4c3e9b5SBjoern A. Zeeb /*
52*b4c3e9b5SBjoern A. Zeeb * expand quirk list to array of quirk strings.
53*b4c3e9b5SBjoern A. Zeeb */
54*b4c3e9b5SBjoern A. Zeeb #define BRCMF_QUIRK_DEF(_q) \
55*b4c3e9b5SBjoern A. Zeeb #_q,
56*b4c3e9b5SBjoern A. Zeeb static const char * const brcmf_quirk_names[] = {
57*b4c3e9b5SBjoern A. Zeeb BRCMF_QUIRK_LIST
58*b4c3e9b5SBjoern A. Zeeb };
59*b4c3e9b5SBjoern A. Zeeb #undef BRCMF_QUIRK_DEF
60*b4c3e9b5SBjoern A. Zeeb
61*b4c3e9b5SBjoern A. Zeeb /**
62*b4c3e9b5SBjoern A. Zeeb * brcmf_feat_debugfs_read() - expose feature info to debugfs.
63*b4c3e9b5SBjoern A. Zeeb *
64*b4c3e9b5SBjoern A. Zeeb * @seq: sequence for debugfs entry.
65*b4c3e9b5SBjoern A. Zeeb * @data: raw data pointer.
66*b4c3e9b5SBjoern A. Zeeb */
brcmf_feat_debugfs_read(struct seq_file * seq,void * data)67*b4c3e9b5SBjoern A. Zeeb static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
68*b4c3e9b5SBjoern A. Zeeb {
69*b4c3e9b5SBjoern A. Zeeb struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
70*b4c3e9b5SBjoern A. Zeeb u32 feats = bus_if->drvr->feat_flags;
71*b4c3e9b5SBjoern A. Zeeb u32 quirks = bus_if->drvr->chip_quirks;
72*b4c3e9b5SBjoern A. Zeeb int id;
73*b4c3e9b5SBjoern A. Zeeb
74*b4c3e9b5SBjoern A. Zeeb seq_printf(seq, "Features: %08x\n", feats);
75*b4c3e9b5SBjoern A. Zeeb for (id = 0; id < BRCMF_FEAT_LAST; id++)
76*b4c3e9b5SBjoern A. Zeeb if (feats & BIT(id))
77*b4c3e9b5SBjoern A. Zeeb seq_printf(seq, "\t%s\n", brcmf_feat_names[id]);
78*b4c3e9b5SBjoern A. Zeeb seq_printf(seq, "\nQuirks: %08x\n", quirks);
79*b4c3e9b5SBjoern A. Zeeb for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++)
80*b4c3e9b5SBjoern A. Zeeb if (quirks & BIT(id))
81*b4c3e9b5SBjoern A. Zeeb seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]);
82*b4c3e9b5SBjoern A. Zeeb return 0;
83*b4c3e9b5SBjoern A. Zeeb }
84*b4c3e9b5SBjoern A. Zeeb #else
brcmf_feat_debugfs_read(struct seq_file * seq,void * data)85*b4c3e9b5SBjoern A. Zeeb static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
86*b4c3e9b5SBjoern A. Zeeb {
87*b4c3e9b5SBjoern A. Zeeb return 0;
88*b4c3e9b5SBjoern A. Zeeb }
89*b4c3e9b5SBjoern A. Zeeb #endif /* DEBUG */
90*b4c3e9b5SBjoern A. Zeeb
91*b4c3e9b5SBjoern A. Zeeb struct brcmf_feat_fwfeat {
92*b4c3e9b5SBjoern A. Zeeb const char * const fwid;
93*b4c3e9b5SBjoern A. Zeeb u32 feat_flags;
94*b4c3e9b5SBjoern A. Zeeb };
95*b4c3e9b5SBjoern A. Zeeb
96*b4c3e9b5SBjoern A. Zeeb static const struct brcmf_feat_fwfeat brcmf_feat_fwfeat_map[] = {
97*b4c3e9b5SBjoern A. Zeeb /* brcmfmac43602-pcie.ap.bin from linux-firmware.git commit ea1178515b88 */
98*b4c3e9b5SBjoern A. Zeeb { "01-6cb8e269", BIT(BRCMF_FEAT_MONITOR) },
99*b4c3e9b5SBjoern A. Zeeb /* brcmfmac4366b-pcie.bin from linux-firmware.git commit 52442afee990 */
100*b4c3e9b5SBjoern A. Zeeb { "01-c47a91a4", BIT(BRCMF_FEAT_MONITOR) },
101*b4c3e9b5SBjoern A. Zeeb /* brcmfmac4366b-pcie.bin from linux-firmware.git commit 211de1679a68 */
102*b4c3e9b5SBjoern A. Zeeb { "01-801fb449", BIT(BRCMF_FEAT_MONITOR_FMT_HW_RX_HDR) },
103*b4c3e9b5SBjoern A. Zeeb /* brcmfmac4366c-pcie.bin from linux-firmware.git commit 211de1679a68 */
104*b4c3e9b5SBjoern A. Zeeb { "01-d2cbb8fd", BIT(BRCMF_FEAT_MONITOR_FMT_HW_RX_HDR) },
105*b4c3e9b5SBjoern A. Zeeb };
106*b4c3e9b5SBjoern A. Zeeb
brcmf_feat_firmware_overrides(struct brcmf_pub * drv)107*b4c3e9b5SBjoern A. Zeeb static void brcmf_feat_firmware_overrides(struct brcmf_pub *drv)
108*b4c3e9b5SBjoern A. Zeeb {
109*b4c3e9b5SBjoern A. Zeeb const struct brcmf_feat_fwfeat *e;
110*b4c3e9b5SBjoern A. Zeeb u32 feat_flags = 0;
111*b4c3e9b5SBjoern A. Zeeb int i;
112*b4c3e9b5SBjoern A. Zeeb
113*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(brcmf_feat_fwfeat_map); i++) {
114*b4c3e9b5SBjoern A. Zeeb e = &brcmf_feat_fwfeat_map[i];
115*b4c3e9b5SBjoern A. Zeeb if (!strcmp(e->fwid, drv->fwver)) {
116*b4c3e9b5SBjoern A. Zeeb feat_flags = e->feat_flags;
117*b4c3e9b5SBjoern A. Zeeb break;
118*b4c3e9b5SBjoern A. Zeeb }
119*b4c3e9b5SBjoern A. Zeeb }
120*b4c3e9b5SBjoern A. Zeeb
121*b4c3e9b5SBjoern A. Zeeb if (!feat_flags)
122*b4c3e9b5SBjoern A. Zeeb return;
123*b4c3e9b5SBjoern A. Zeeb
124*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < BRCMF_FEAT_LAST; i++)
125*b4c3e9b5SBjoern A. Zeeb if (feat_flags & BIT(i))
126*b4c3e9b5SBjoern A. Zeeb brcmf_dbg(INFO, "enabling firmware feature: %s\n",
127*b4c3e9b5SBjoern A. Zeeb brcmf_feat_names[i]);
128*b4c3e9b5SBjoern A. Zeeb drv->feat_flags |= feat_flags;
129*b4c3e9b5SBjoern A. Zeeb }
130*b4c3e9b5SBjoern A. Zeeb
131*b4c3e9b5SBjoern A. Zeeb struct brcmf_feat_wlcfeat {
132*b4c3e9b5SBjoern A. Zeeb u16 min_ver_major;
133*b4c3e9b5SBjoern A. Zeeb u16 min_ver_minor;
134*b4c3e9b5SBjoern A. Zeeb u32 feat_flags;
135*b4c3e9b5SBjoern A. Zeeb };
136*b4c3e9b5SBjoern A. Zeeb
137*b4c3e9b5SBjoern A. Zeeb static const struct brcmf_feat_wlcfeat brcmf_feat_wlcfeat_map[] = {
138*b4c3e9b5SBjoern A. Zeeb { 12, 0, BIT(BRCMF_FEAT_PMKID_V2) },
139*b4c3e9b5SBjoern A. Zeeb { 13, 0, BIT(BRCMF_FEAT_PMKID_V3) },
140*b4c3e9b5SBjoern A. Zeeb };
141*b4c3e9b5SBjoern A. Zeeb
brcmf_feat_wlc_version_overrides(struct brcmf_pub * drv)142*b4c3e9b5SBjoern A. Zeeb static void brcmf_feat_wlc_version_overrides(struct brcmf_pub *drv)
143*b4c3e9b5SBjoern A. Zeeb {
144*b4c3e9b5SBjoern A. Zeeb struct brcmf_if *ifp = brcmf_get_ifp(drv, 0);
145*b4c3e9b5SBjoern A. Zeeb const struct brcmf_feat_wlcfeat *e;
146*b4c3e9b5SBjoern A. Zeeb struct brcmf_wlc_version_le ver;
147*b4c3e9b5SBjoern A. Zeeb u32 feat_flags = 0;
148*b4c3e9b5SBjoern A. Zeeb int i, err, major, minor;
149*b4c3e9b5SBjoern A. Zeeb
150*b4c3e9b5SBjoern A. Zeeb err = brcmf_fil_iovar_data_get(ifp, "wlc_ver", &ver, sizeof(ver));
151*b4c3e9b5SBjoern A. Zeeb if (err)
152*b4c3e9b5SBjoern A. Zeeb return;
153*b4c3e9b5SBjoern A. Zeeb
154*b4c3e9b5SBjoern A. Zeeb major = le16_to_cpu(ver.wlc_ver_major);
155*b4c3e9b5SBjoern A. Zeeb minor = le16_to_cpu(ver.wlc_ver_minor);
156*b4c3e9b5SBjoern A. Zeeb
157*b4c3e9b5SBjoern A. Zeeb brcmf_dbg(INFO, "WLC version: %d.%d\n", major, minor);
158*b4c3e9b5SBjoern A. Zeeb
159*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(brcmf_feat_wlcfeat_map); i++) {
160*b4c3e9b5SBjoern A. Zeeb e = &brcmf_feat_wlcfeat_map[i];
161*b4c3e9b5SBjoern A. Zeeb if (major > e->min_ver_major ||
162*b4c3e9b5SBjoern A. Zeeb (major == e->min_ver_major &&
163*b4c3e9b5SBjoern A. Zeeb minor >= e->min_ver_minor)) {
164*b4c3e9b5SBjoern A. Zeeb feat_flags |= e->feat_flags;
165*b4c3e9b5SBjoern A. Zeeb }
166*b4c3e9b5SBjoern A. Zeeb }
167*b4c3e9b5SBjoern A. Zeeb
168*b4c3e9b5SBjoern A. Zeeb if (!feat_flags)
169*b4c3e9b5SBjoern A. Zeeb return;
170*b4c3e9b5SBjoern A. Zeeb
171*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < BRCMF_FEAT_LAST; i++)
172*b4c3e9b5SBjoern A. Zeeb if (feat_flags & BIT(i))
173*b4c3e9b5SBjoern A. Zeeb brcmf_dbg(INFO, "enabling firmware feature: %s\n",
174*b4c3e9b5SBjoern A. Zeeb brcmf_feat_names[i]);
175*b4c3e9b5SBjoern A. Zeeb drv->feat_flags |= feat_flags;
176*b4c3e9b5SBjoern A. Zeeb }
177*b4c3e9b5SBjoern A. Zeeb
178*b4c3e9b5SBjoern A. Zeeb /**
179*b4c3e9b5SBjoern A. Zeeb * brcmf_feat_iovar_int_get() - determine feature through iovar query.
180*b4c3e9b5SBjoern A. Zeeb *
181*b4c3e9b5SBjoern A. Zeeb * @ifp: interface to query.
182*b4c3e9b5SBjoern A. Zeeb * @id: feature id.
183*b4c3e9b5SBjoern A. Zeeb * @name: iovar name.
184*b4c3e9b5SBjoern A. Zeeb */
brcmf_feat_iovar_int_get(struct brcmf_if * ifp,enum brcmf_feat_id id,char * name)185*b4c3e9b5SBjoern A. Zeeb static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
186*b4c3e9b5SBjoern A. Zeeb enum brcmf_feat_id id, char *name)
187*b4c3e9b5SBjoern A. Zeeb {
188*b4c3e9b5SBjoern A. Zeeb u32 data;
189*b4c3e9b5SBjoern A. Zeeb int err;
190*b4c3e9b5SBjoern A. Zeeb
191*b4c3e9b5SBjoern A. Zeeb /* we need to know firmware error */
192*b4c3e9b5SBjoern A. Zeeb ifp->fwil_fwerr = true;
193*b4c3e9b5SBjoern A. Zeeb
194*b4c3e9b5SBjoern A. Zeeb err = brcmf_fil_iovar_int_get(ifp, name, &data);
195*b4c3e9b5SBjoern A. Zeeb if (err != -BRCMF_FW_UNSUPPORTED) {
196*b4c3e9b5SBjoern A. Zeeb brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
197*b4c3e9b5SBjoern A. Zeeb ifp->drvr->feat_flags |= BIT(id);
198*b4c3e9b5SBjoern A. Zeeb } else {
199*b4c3e9b5SBjoern A. Zeeb brcmf_dbg(TRACE, "%s feature check failed: %d\n",
200*b4c3e9b5SBjoern A. Zeeb brcmf_feat_names[id], err);
201*b4c3e9b5SBjoern A. Zeeb }
202*b4c3e9b5SBjoern A. Zeeb
203*b4c3e9b5SBjoern A. Zeeb ifp->fwil_fwerr = false;
204*b4c3e9b5SBjoern A. Zeeb }
205*b4c3e9b5SBjoern A. Zeeb
brcmf_feat_iovar_data_set(struct brcmf_if * ifp,enum brcmf_feat_id id,char * name,const void * data,size_t len)206*b4c3e9b5SBjoern A. Zeeb static void brcmf_feat_iovar_data_set(struct brcmf_if *ifp,
207*b4c3e9b5SBjoern A. Zeeb enum brcmf_feat_id id, char *name,
208*b4c3e9b5SBjoern A. Zeeb const void *data, size_t len)
209*b4c3e9b5SBjoern A. Zeeb {
210*b4c3e9b5SBjoern A. Zeeb int err;
211*b4c3e9b5SBjoern A. Zeeb
212*b4c3e9b5SBjoern A. Zeeb /* we need to know firmware error */
213*b4c3e9b5SBjoern A. Zeeb ifp->fwil_fwerr = true;
214*b4c3e9b5SBjoern A. Zeeb
215*b4c3e9b5SBjoern A. Zeeb err = brcmf_fil_iovar_data_set(ifp, name, data, len);
216*b4c3e9b5SBjoern A. Zeeb if (err != -BRCMF_FW_UNSUPPORTED) {
217*b4c3e9b5SBjoern A. Zeeb brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
218*b4c3e9b5SBjoern A. Zeeb ifp->drvr->feat_flags |= BIT(id);
219*b4c3e9b5SBjoern A. Zeeb } else {
220*b4c3e9b5SBjoern A. Zeeb brcmf_dbg(TRACE, "%s feature check failed: %d\n",
221*b4c3e9b5SBjoern A. Zeeb brcmf_feat_names[id], err);
222*b4c3e9b5SBjoern A. Zeeb }
223*b4c3e9b5SBjoern A. Zeeb
224*b4c3e9b5SBjoern A. Zeeb ifp->fwil_fwerr = false;
225*b4c3e9b5SBjoern A. Zeeb }
226*b4c3e9b5SBjoern A. Zeeb
227*b4c3e9b5SBjoern A. Zeeb #define MAX_CAPS_BUFFER_SIZE 768
brcmf_feat_firmware_capabilities(struct brcmf_if * ifp)228*b4c3e9b5SBjoern A. Zeeb static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
229*b4c3e9b5SBjoern A. Zeeb {
230*b4c3e9b5SBjoern A. Zeeb struct brcmf_pub *drvr = ifp->drvr;
231*b4c3e9b5SBjoern A. Zeeb char caps[MAX_CAPS_BUFFER_SIZE];
232*b4c3e9b5SBjoern A. Zeeb enum brcmf_feat_id id;
233*b4c3e9b5SBjoern A. Zeeb int i, err;
234*b4c3e9b5SBjoern A. Zeeb
235*b4c3e9b5SBjoern A. Zeeb err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
236*b4c3e9b5SBjoern A. Zeeb if (err) {
237*b4c3e9b5SBjoern A. Zeeb bphy_err(drvr, "could not get firmware cap (%d)\n", err);
238*b4c3e9b5SBjoern A. Zeeb return;
239*b4c3e9b5SBjoern A. Zeeb }
240*b4c3e9b5SBjoern A. Zeeb
241*b4c3e9b5SBjoern A. Zeeb brcmf_dbg(INFO, "[ %s]\n", caps);
242*b4c3e9b5SBjoern A. Zeeb
243*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) {
244*b4c3e9b5SBjoern A. Zeeb if (strnstr(caps, brcmf_fwcap_map[i].fwcap_id, sizeof(caps))) {
245*b4c3e9b5SBjoern A. Zeeb id = brcmf_fwcap_map[i].feature;
246*b4c3e9b5SBjoern A. Zeeb brcmf_dbg(INFO, "enabling feature: %s\n",
247*b4c3e9b5SBjoern A. Zeeb brcmf_feat_names[id]);
248*b4c3e9b5SBjoern A. Zeeb ifp->drvr->feat_flags |= BIT(id);
249*b4c3e9b5SBjoern A. Zeeb }
250*b4c3e9b5SBjoern A. Zeeb }
251*b4c3e9b5SBjoern A. Zeeb }
252*b4c3e9b5SBjoern A. Zeeb
253*b4c3e9b5SBjoern A. Zeeb /**
254*b4c3e9b5SBjoern A. Zeeb * brcmf_feat_fwcap_debugfs_read() - expose firmware capabilities to debugfs.
255*b4c3e9b5SBjoern A. Zeeb *
256*b4c3e9b5SBjoern A. Zeeb * @seq: sequence for debugfs entry.
257*b4c3e9b5SBjoern A. Zeeb * @data: raw data pointer.
258*b4c3e9b5SBjoern A. Zeeb */
brcmf_feat_fwcap_debugfs_read(struct seq_file * seq,void * data)259*b4c3e9b5SBjoern A. Zeeb static int brcmf_feat_fwcap_debugfs_read(struct seq_file *seq, void *data)
260*b4c3e9b5SBjoern A. Zeeb {
261*b4c3e9b5SBjoern A. Zeeb struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
262*b4c3e9b5SBjoern A. Zeeb struct brcmf_pub *drvr = bus_if->drvr;
263*b4c3e9b5SBjoern A. Zeeb struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
264*b4c3e9b5SBjoern A. Zeeb char caps[MAX_CAPS_BUFFER_SIZE + 1] = { };
265*b4c3e9b5SBjoern A. Zeeb char *tmp;
266*b4c3e9b5SBjoern A. Zeeb int err;
267*b4c3e9b5SBjoern A. Zeeb
268*b4c3e9b5SBjoern A. Zeeb err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
269*b4c3e9b5SBjoern A. Zeeb if (err) {
270*b4c3e9b5SBjoern A. Zeeb bphy_err(drvr, "could not get firmware cap (%d)\n", err);
271*b4c3e9b5SBjoern A. Zeeb return err;
272*b4c3e9b5SBjoern A. Zeeb }
273*b4c3e9b5SBjoern A. Zeeb
274*b4c3e9b5SBjoern A. Zeeb /* Put every capability in a new line */
275*b4c3e9b5SBjoern A. Zeeb for (tmp = caps; *tmp; tmp++) {
276*b4c3e9b5SBjoern A. Zeeb if (*tmp == ' ')
277*b4c3e9b5SBjoern A. Zeeb *tmp = '\n';
278*b4c3e9b5SBjoern A. Zeeb }
279*b4c3e9b5SBjoern A. Zeeb
280*b4c3e9b5SBjoern A. Zeeb /* Usually there is a space at the end of capabilities string */
281*b4c3e9b5SBjoern A. Zeeb seq_printf(seq, "%s", caps);
282*b4c3e9b5SBjoern A. Zeeb /* So make sure we don't print two line breaks */
283*b4c3e9b5SBjoern A. Zeeb if (tmp > caps && *(tmp - 1) != '\n')
284*b4c3e9b5SBjoern A. Zeeb seq_printf(seq, "\n");
285*b4c3e9b5SBjoern A. Zeeb
286*b4c3e9b5SBjoern A. Zeeb return 0;
287*b4c3e9b5SBjoern A. Zeeb }
288*b4c3e9b5SBjoern A. Zeeb
brcmf_feat_attach(struct brcmf_pub * drvr)289*b4c3e9b5SBjoern A. Zeeb void brcmf_feat_attach(struct brcmf_pub *drvr)
290*b4c3e9b5SBjoern A. Zeeb {
291*b4c3e9b5SBjoern A. Zeeb struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
292*b4c3e9b5SBjoern A. Zeeb struct brcmf_pno_macaddr_le pfn_mac;
293*b4c3e9b5SBjoern A. Zeeb struct brcmf_gscan_config gscan_cfg;
294*b4c3e9b5SBjoern A. Zeeb u32 wowl_cap;
295*b4c3e9b5SBjoern A. Zeeb s32 err;
296*b4c3e9b5SBjoern A. Zeeb
297*b4c3e9b5SBjoern A. Zeeb brcmf_feat_firmware_capabilities(ifp);
298*b4c3e9b5SBjoern A. Zeeb memset(&gscan_cfg, 0, sizeof(gscan_cfg));
299*b4c3e9b5SBjoern A. Zeeb if (drvr->bus_if->chip != BRCM_CC_43430_CHIP_ID &&
300*b4c3e9b5SBjoern A. Zeeb drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID &&
301*b4c3e9b5SBjoern A. Zeeb drvr->bus_if->chip != BRCM_CC_43454_CHIP_ID &&
302*b4c3e9b5SBjoern A. Zeeb drvr->bus_if->chip != CY_CC_43439_CHIP_ID)
303*b4c3e9b5SBjoern A. Zeeb brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN,
304*b4c3e9b5SBjoern A. Zeeb "pfn_gscan_cfg",
305*b4c3e9b5SBjoern A. Zeeb &gscan_cfg, sizeof(gscan_cfg));
306*b4c3e9b5SBjoern A. Zeeb brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
307*b4c3e9b5SBjoern A. Zeeb if (drvr->bus_if->wowl_supported)
308*b4c3e9b5SBjoern A. Zeeb brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
309*b4c3e9b5SBjoern A. Zeeb if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) {
310*b4c3e9b5SBjoern A. Zeeb err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
311*b4c3e9b5SBjoern A. Zeeb if (!err) {
312*b4c3e9b5SBjoern A. Zeeb ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_WOWL_ARP_ND);
313*b4c3e9b5SBjoern A. Zeeb if (wowl_cap & BRCMF_WOWL_PFN_FOUND)
314*b4c3e9b5SBjoern A. Zeeb ifp->drvr->feat_flags |=
315*b4c3e9b5SBjoern A. Zeeb BIT(BRCMF_FEAT_WOWL_ND);
316*b4c3e9b5SBjoern A. Zeeb if (wowl_cap & BRCMF_WOWL_GTK_FAILURE)
317*b4c3e9b5SBjoern A. Zeeb ifp->drvr->feat_flags |=
318*b4c3e9b5SBjoern A. Zeeb BIT(BRCMF_FEAT_WOWL_GTK);
319*b4c3e9b5SBjoern A. Zeeb }
320*b4c3e9b5SBjoern A. Zeeb }
321*b4c3e9b5SBjoern A. Zeeb /* MBSS does not work for all chips */
322*b4c3e9b5SBjoern A. Zeeb switch (drvr->bus_if->chip) {
323*b4c3e9b5SBjoern A. Zeeb case BRCM_CC_4330_CHIP_ID:
324*b4c3e9b5SBjoern A. Zeeb case BRCM_CC_43362_CHIP_ID:
325*b4c3e9b5SBjoern A. Zeeb ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS);
326*b4c3e9b5SBjoern A. Zeeb break;
327*b4c3e9b5SBjoern A. Zeeb default:
328*b4c3e9b5SBjoern A. Zeeb break;
329*b4c3e9b5SBjoern A. Zeeb }
330*b4c3e9b5SBjoern A. Zeeb brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode");
331*b4c3e9b5SBjoern A. Zeeb brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable");
332*b4c3e9b5SBjoern A. Zeeb brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp");
333*b4c3e9b5SBjoern A. Zeeb brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_DUMP_OBSS, "dump_obss");
334*b4c3e9b5SBjoern A. Zeeb
335*b4c3e9b5SBjoern A. Zeeb pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
336*b4c3e9b5SBjoern A. Zeeb err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac,
337*b4c3e9b5SBjoern A. Zeeb sizeof(pfn_mac));
338*b4c3e9b5SBjoern A. Zeeb if (!err)
339*b4c3e9b5SBjoern A. Zeeb ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC);
340*b4c3e9b5SBjoern A. Zeeb
341*b4c3e9b5SBjoern A. Zeeb brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_FWSUP, "sup_wpa");
342*b4c3e9b5SBjoern A. Zeeb brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_SCAN_V2, "scan_ver");
343*b4c3e9b5SBjoern A. Zeeb
344*b4c3e9b5SBjoern A. Zeeb brcmf_feat_wlc_version_overrides(drvr);
345*b4c3e9b5SBjoern A. Zeeb brcmf_feat_firmware_overrides(drvr);
346*b4c3e9b5SBjoern A. Zeeb
347*b4c3e9b5SBjoern A. Zeeb brcmf_fwvid_feat_attach(ifp);
348*b4c3e9b5SBjoern A. Zeeb
349*b4c3e9b5SBjoern A. Zeeb if (drvr->settings->feature_disable) {
350*b4c3e9b5SBjoern A. Zeeb brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n",
351*b4c3e9b5SBjoern A. Zeeb ifp->drvr->feat_flags,
352*b4c3e9b5SBjoern A. Zeeb drvr->settings->feature_disable);
353*b4c3e9b5SBjoern A. Zeeb ifp->drvr->feat_flags &= ~drvr->settings->feature_disable;
354*b4c3e9b5SBjoern A. Zeeb }
355*b4c3e9b5SBjoern A. Zeeb
356*b4c3e9b5SBjoern A. Zeeb /* set chip related quirks */
357*b4c3e9b5SBjoern A. Zeeb switch (drvr->bus_if->chip) {
358*b4c3e9b5SBjoern A. Zeeb case BRCM_CC_43236_CHIP_ID:
359*b4c3e9b5SBjoern A. Zeeb drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH);
360*b4c3e9b5SBjoern A. Zeeb break;
361*b4c3e9b5SBjoern A. Zeeb case BRCM_CC_4329_CHIP_ID:
362*b4c3e9b5SBjoern A. Zeeb drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC);
363*b4c3e9b5SBjoern A. Zeeb break;
364*b4c3e9b5SBjoern A. Zeeb default:
365*b4c3e9b5SBjoern A. Zeeb /* no quirks */
366*b4c3e9b5SBjoern A. Zeeb break;
367*b4c3e9b5SBjoern A. Zeeb }
368*b4c3e9b5SBjoern A. Zeeb }
369*b4c3e9b5SBjoern A. Zeeb
brcmf_feat_debugfs_create(struct brcmf_pub * drvr)370*b4c3e9b5SBjoern A. Zeeb void brcmf_feat_debugfs_create(struct brcmf_pub *drvr)
371*b4c3e9b5SBjoern A. Zeeb {
372*b4c3e9b5SBjoern A. Zeeb brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read);
373*b4c3e9b5SBjoern A. Zeeb brcmf_debugfs_add_entry(drvr, "fwcap", brcmf_feat_fwcap_debugfs_read);
374*b4c3e9b5SBjoern A. Zeeb }
375*b4c3e9b5SBjoern A. Zeeb
brcmf_feat_is_enabled(struct brcmf_if * ifp,enum brcmf_feat_id id)376*b4c3e9b5SBjoern A. Zeeb bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id)
377*b4c3e9b5SBjoern A. Zeeb {
378*b4c3e9b5SBjoern A. Zeeb return (ifp->drvr->feat_flags & BIT(id));
379*b4c3e9b5SBjoern A. Zeeb }
380*b4c3e9b5SBjoern A. Zeeb
brcmf_feat_is_quirk_enabled(struct brcmf_if * ifp,enum brcmf_feat_quirk quirk)381*b4c3e9b5SBjoern A. Zeeb bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
382*b4c3e9b5SBjoern A. Zeeb enum brcmf_feat_quirk quirk)
383*b4c3e9b5SBjoern A. Zeeb {
384*b4c3e9b5SBjoern A. Zeeb return (ifp->drvr->chip_quirks & BIT(quirk));
385*b4c3e9b5SBjoern A. Zeeb }
386