mt76: update to the latest version
[oweals/openwrt.git] / package / kernel / mac80211 / patches / 304-v4.15-brcmfmac-add-CLM-download-support.patch
1 From fdd0bd88ceaecf729db103ac8836af5805dd2dc1 Mon Sep 17 00:00:00 2001
2 From: Chung-Hsien Hsu <stanley.hsu@cypress.com>
3 Date: Fri, 10 Nov 2017 17:27:15 +0800
4 Subject: [PATCH] brcmfmac: add CLM download support
5
6 The firmware for brcmfmac devices includes information regarding
7 regulatory constraints. For certain devices this information is kept
8 separately in a binary form that needs to be downloaded to the device.
9 This patch adds support to download this so-called CLM blob file. It
10 uses the same naming scheme as the other firmware files with extension
11 of .clm_blob.
12
13 The CLM blob file is optional. If the file does not exist, the download
14 process will be bypassed. It will not affect the driver loading.
15
16 Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com>
17 Signed-off-by: Chung-Hsien Hsu <stanley.hsu@cypress.com>
18 Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
19 ---
20  .../net/wireless/broadcom/brcm80211/brcmfmac/bus.h |  10 ++
21  .../wireless/broadcom/brcm80211/brcmfmac/common.c  | 157 +++++++++++++++++++++
22  .../wireless/broadcom/brcm80211/brcmfmac/core.c    |   2 +
23  .../wireless/broadcom/brcm80211/brcmfmac/core.h    |   2 +
24  .../broadcom/brcm80211/brcmfmac/fwil_types.h       |  31 ++++
25  .../wireless/broadcom/brcm80211/brcmfmac/pcie.c    |  19 +++
26  .../wireless/broadcom/brcm80211/brcmfmac/sdio.c    |  19 +++
27  .../net/wireless/broadcom/brcm80211/brcmfmac/usb.c |  18 +++
28  8 files changed, 258 insertions(+)
29
30 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
31 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
32 @@ -71,6 +71,7 @@ struct brcmf_bus_dcmd {
33   * @wowl_config: specify if dongle is configured for wowl when going to suspend
34   * @get_ramsize: obtain size of device memory.
35   * @get_memdump: obtain device memory dump in provided buffer.
36 + * @get_fwname: obtain firmware name.
37   *
38   * This structure provides an abstract interface towards the
39   * bus specific driver. For control messages to common driver
40 @@ -87,6 +88,8 @@ struct brcmf_bus_ops {
41         void (*wowl_config)(struct device *dev, bool enabled);
42         size_t (*get_ramsize)(struct device *dev);
43         int (*get_memdump)(struct device *dev, void *data, size_t len);
44 +       int (*get_fwname)(struct device *dev, uint chip, uint chiprev,
45 +                         unsigned char *fw_name);
46  };
47  
48  
49 @@ -224,6 +227,13 @@ int brcmf_bus_get_memdump(struct brcmf_b
50         return bus->ops->get_memdump(bus->dev, data, len);
51  }
52  
53 +static inline
54 +int brcmf_bus_get_fwname(struct brcmf_bus *bus, uint chip, uint chiprev,
55 +                        unsigned char *fw_name)
56 +{
57 +       return bus->ops->get_fwname(bus->dev, chip, chiprev, fw_name);
58 +}
59 +
60  /*
61   * interface functions from common layer
62   */
63 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
64 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
65 @@ -18,6 +18,7 @@
66  #include <linux/string.h>
67  #include <linux/netdevice.h>
68  #include <linux/module.h>
69 +#include <linux/firmware.h>
70  #include <brcmu_wifi.h>
71  #include <brcmu_utils.h>
72  #include "core.h"
73 @@ -28,6 +29,7 @@
74  #include "tracepoint.h"
75  #include "common.h"
76  #include "of.h"
77 +#include "firmware.h"
78  
79  MODULE_AUTHOR("Broadcom Corporation");
80  MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
81 @@ -104,12 +106,140 @@ void brcmf_c_set_joinpref_default(struct
82                 brcmf_err("Set join_pref error (%d)\n", err);
83  }
84  
85 +static int brcmf_c_download(struct brcmf_if *ifp, u16 flag,
86 +                           struct brcmf_dload_data_le *dload_buf,
87 +                           u32 len)
88 +{
89 +       s32 err;
90 +
91 +       flag |= (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT);
92 +       dload_buf->flag = cpu_to_le16(flag);
93 +       dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM);
94 +       dload_buf->len = cpu_to_le32(len);
95 +       dload_buf->crc = cpu_to_le32(0);
96 +       len = sizeof(*dload_buf) + len - 1;
97 +
98 +       err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, len);
99 +
100 +       return err;
101 +}
102 +
103 +static int brcmf_c_get_clm_name(struct brcmf_if *ifp, u8 *clm_name)
104 +{
105 +       struct brcmf_bus *bus = ifp->drvr->bus_if;
106 +       struct brcmf_rev_info *ri = &ifp->drvr->revinfo;
107 +       u8 fw_name[BRCMF_FW_NAME_LEN];
108 +       u8 *ptr;
109 +       size_t len;
110 +       s32 err;
111 +
112 +       memset(fw_name, 0, BRCMF_FW_NAME_LEN);
113 +       err = brcmf_bus_get_fwname(bus, ri->chipnum, ri->chiprev, fw_name);
114 +       if (err) {
115 +               brcmf_err("get firmware name failed (%d)\n", err);
116 +               goto done;
117 +       }
118 +
119 +       /* generate CLM blob file name */
120 +       ptr = strrchr(fw_name, '.');
121 +       if (!ptr) {
122 +               err = -ENOENT;
123 +               goto done;
124 +       }
125 +
126 +       len = ptr - fw_name + 1;
127 +       if (len + strlen(".clm_blob") > BRCMF_FW_NAME_LEN) {
128 +               err = -E2BIG;
129 +       } else {
130 +               strlcpy(clm_name, fw_name, len);
131 +               strlcat(clm_name, ".clm_blob", BRCMF_FW_NAME_LEN);
132 +       }
133 +done:
134 +       return err;
135 +}
136 +
137 +static int brcmf_c_process_clm_blob(struct brcmf_if *ifp)
138 +{
139 +       struct device *dev = ifp->drvr->bus_if->dev;
140 +       struct brcmf_dload_data_le *chunk_buf;
141 +       const struct firmware *clm = NULL;
142 +       u8 clm_name[BRCMF_FW_NAME_LEN];
143 +       u32 chunk_len;
144 +       u32 datalen;
145 +       u32 cumulative_len;
146 +       u16 dl_flag = DL_BEGIN;
147 +       u32 status;
148 +       s32 err;
149 +
150 +       brcmf_dbg(TRACE, "Enter\n");
151 +
152 +       memset(clm_name, 0, BRCMF_FW_NAME_LEN);
153 +       err = brcmf_c_get_clm_name(ifp, clm_name);
154 +       if (err) {
155 +               brcmf_err("get CLM blob file name failed (%d)\n", err);
156 +               return err;
157 +       }
158 +
159 +       err = request_firmware(&clm, clm_name, dev);
160 +       if (err) {
161 +               if (err == -ENOENT) {
162 +                       brcmf_dbg(INFO, "continue with CLM data currently present in firmware\n");
163 +                       return 0;
164 +               }
165 +               brcmf_err("request CLM blob file failed (%d)\n", err);
166 +               return err;
167 +       }
168 +
169 +       chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN - 1, GFP_KERNEL);
170 +       if (!chunk_buf) {
171 +               err = -ENOMEM;
172 +               goto done;
173 +       }
174 +
175 +       datalen = clm->size;
176 +       cumulative_len = 0;
177 +       do {
178 +               if (datalen > MAX_CHUNK_LEN) {
179 +                       chunk_len = MAX_CHUNK_LEN;
180 +               } else {
181 +                       chunk_len = datalen;
182 +                       dl_flag |= DL_END;
183 +               }
184 +               memcpy(chunk_buf->data, clm->data + cumulative_len, chunk_len);
185 +
186 +               err = brcmf_c_download(ifp, dl_flag, chunk_buf, chunk_len);
187 +
188 +               dl_flag &= ~DL_BEGIN;
189 +
190 +               cumulative_len += chunk_len;
191 +               datalen -= chunk_len;
192 +       } while ((datalen > 0) && (err == 0));
193 +
194 +       if (err) {
195 +               brcmf_err("clmload (%zu byte file) failed (%d); ",
196 +                         clm->size, err);
197 +               /* Retrieve clmload_status and print */
198 +               err = brcmf_fil_iovar_int_get(ifp, "clmload_status", &status);
199 +               if (err)
200 +                       brcmf_err("get clmload_status failed (%d)\n", err);
201 +               else
202 +                       brcmf_dbg(INFO, "clmload_status=%d\n", status);
203 +               err = -EIO;
204 +       }
205 +
206 +       kfree(chunk_buf);
207 +done:
208 +       release_firmware(clm);
209 +       return err;
210 +}
211 +
212  int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
213  {
214         s8 eventmask[BRCMF_EVENTING_MASK_LEN];
215         u8 buf[BRCMF_DCMD_SMLEN];
216         struct brcmf_rev_info_le revinfo;
217         struct brcmf_rev_info *ri;
218 +       char *clmver;
219         char *ptr;
220         s32 err;
221  
222 @@ -148,6 +278,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_i
223         }
224         ri->result = err;
225  
226 +       /* Do any CLM downloading */
227 +       err = brcmf_c_process_clm_blob(ifp);
228 +       if (err < 0) {
229 +               brcmf_err("download CLM blob file failed, %d\n", err);
230 +               goto done;
231 +       }
232 +
233         /* query for 'ver' to get version info from firmware */
234         memset(buf, 0, sizeof(buf));
235         strcpy(buf, "ver");
236 @@ -167,6 +304,26 @@ int brcmf_c_preinit_dcmds(struct brcmf_i
237         ptr = strrchr(buf, ' ') + 1;
238         strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver));
239  
240 +       /* Query for 'clmver' to get CLM version info from firmware */
241 +       memset(buf, 0, sizeof(buf));
242 +       err = brcmf_fil_iovar_data_get(ifp, "clmver", buf, sizeof(buf));
243 +       if (err) {
244 +               brcmf_dbg(TRACE, "retrieving clmver failed, %d\n", err);
245 +       } else {
246 +               clmver = (char *)buf;
247 +               /* store CLM version for adding it to revinfo debugfs file */
248 +               memcpy(ifp->drvr->clmver, clmver, sizeof(ifp->drvr->clmver));
249 +
250 +               /* Replace all newline/linefeed characters with space
251 +                * character
252 +                */
253 +               ptr = clmver;
254 +               while ((ptr = strnchr(ptr, '\n', sizeof(buf))) != NULL)
255 +                       *ptr = ' ';
256 +
257 +               brcmf_dbg(INFO, "CLM version = %s\n", clmver);
258 +       }
259 +
260         /* set mpc */
261         err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
262         if (err) {
263 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
264 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
265 @@ -1009,6 +1009,8 @@ static int brcmf_revinfo_read(struct seq
266         seq_printf(s, "anarev: %u\n", ri->anarev);
267         seq_printf(s, "nvramrev: %08x\n", ri->nvramrev);
268  
269 +       seq_printf(s, "clmver: %s\n", bus_if->drvr->clmver);
270 +
271         return 0;
272  }
273  
274 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
275 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
276 @@ -141,6 +141,8 @@ struct brcmf_pub {
277         struct notifier_block inetaddr_notifier;
278         struct notifier_block inet6addr_notifier;
279         struct brcmf_mp_device *settings;
280 +
281 +       u8 clmver[BRCMF_DCMD_SMLEN];
282  };
283  
284  /* forward declarations */
285 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
286 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
287 @@ -155,6 +155,21 @@
288  #define BRCMF_MFP_CAPABLE              1
289  #define BRCMF_MFP_REQUIRED             2
290  
291 +/* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each
292 + * ioctl. It is relatively small because firmware has small maximum size input
293 + * playload restriction for ioctls.
294 + */
295 +#define MAX_CHUNK_LEN                  1400
296 +
297 +#define DLOAD_HANDLER_VER              1       /* Downloader version */
298 +#define DLOAD_FLAG_VER_MASK            0xf000  /* Downloader version mask */
299 +#define DLOAD_FLAG_VER_SHIFT           12      /* Downloader version shift */
300 +
301 +#define DL_BEGIN                       0x0002
302 +#define DL_END                         0x0004
303 +
304 +#define DL_TYPE_CLM                    2
305 +
306  /* join preference types for join_pref iovar */
307  enum brcmf_join_pref_types {
308         BRCMF_JOIN_PREF_RSSI = 1,
309 @@ -827,6 +842,22 @@ struct brcmf_pno_macaddr_le {
310  };
311  
312  /**
313 + * struct brcmf_dload_data_le - data passing to firmware for downloading
314 + * @flag: flags related to download data.
315 + * @dload_type: type of download data.
316 + * @len: length in bytes of download data.
317 + * @crc: crc of download data.
318 + * @data: download data.
319 + */
320 +struct brcmf_dload_data_le {
321 +       __le16 flag;
322 +       __le16 dload_type;
323 +       __le32 len;
324 +       __le32 crc;
325 +       u8 data[1];
326 +};
327 +
328 +/**
329   * struct brcmf_pno_bssid_le - bssid configuration for PNO scan.
330   *
331   * @bssid: BSS network identifier.
332 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
333 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
334 @@ -1350,6 +1350,24 @@ static int brcmf_pcie_get_memdump(struct
335         return 0;
336  }
337  
338 +static int brcmf_pcie_get_fwname(struct device *dev, u32 chip, u32 chiprev,
339 +                                u8 *fw_name)
340 +{
341 +       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
342 +       struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
343 +       struct brcmf_pciedev_info *devinfo = buspub->devinfo;
344 +       int ret = 0;
345 +
346 +       if (devinfo->fw_name[0] != '\0')
347 +               strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
348 +       else
349 +               ret = brcmf_fw_map_chip_to_name(chip, chiprev,
350 +                                               brcmf_pcie_fwnames,
351 +                                               ARRAY_SIZE(brcmf_pcie_fwnames),
352 +                                               fw_name, NULL);
353 +
354 +       return ret;
355 +}
356  
357  static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
358         .txdata = brcmf_pcie_tx,
359 @@ -1359,6 +1377,7 @@ static const struct brcmf_bus_ops brcmf_
360         .wowl_config = brcmf_pcie_wowl_config,
361         .get_ramsize = brcmf_pcie_get_ramsize,
362         .get_memdump = brcmf_pcie_get_memdump,
363 +       .get_fwname = brcmf_pcie_get_fwname,
364  };
365  
366  
367 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
368 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
369 @@ -3985,6 +3985,24 @@ brcmf_sdio_watchdog(unsigned long data)
370         }
371  }
372  
373 +static int brcmf_sdio_get_fwname(struct device *dev, u32 chip, u32 chiprev,
374 +                                u8 *fw_name)
375 +{
376 +       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
377 +       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
378 +       int ret = 0;
379 +
380 +       if (sdiodev->fw_name[0] != '\0')
381 +               strlcpy(fw_name, sdiodev->fw_name, BRCMF_FW_NAME_LEN);
382 +       else
383 +               ret = brcmf_fw_map_chip_to_name(chip, chiprev,
384 +                                               brcmf_sdio_fwnames,
385 +                                               ARRAY_SIZE(brcmf_sdio_fwnames),
386 +                                               fw_name, NULL);
387 +
388 +       return ret;
389 +}
390 +
391  static const struct brcmf_bus_ops brcmf_sdio_bus_ops = {
392         .stop = brcmf_sdio_bus_stop,
393         .preinit = brcmf_sdio_bus_preinit,
394 @@ -3995,6 +4013,7 @@ static const struct brcmf_bus_ops brcmf_
395         .wowl_config = brcmf_sdio_wowl_config,
396         .get_ramsize = brcmf_sdio_bus_get_ramsize,
397         .get_memdump = brcmf_sdio_bus_get_memdump,
398 +       .get_fwname = brcmf_sdio_get_fwname,
399  };
400  
401  static void brcmf_sdio_firmware_callback(struct device *dev, int err,
402 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
403 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
404 @@ -1128,12 +1128,30 @@ static void brcmf_usb_wowl_config(struct
405                 device_set_wakeup_enable(devinfo->dev, false);
406  }
407  
408 +static int brcmf_usb_get_fwname(struct device *dev, u32 chip, u32 chiprev,
409 +                               u8 *fw_name)
410 +{
411 +       struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
412 +       int ret = 0;
413 +
414 +       if (devinfo->fw_name[0] != '\0')
415 +               strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
416 +       else
417 +               ret = brcmf_fw_map_chip_to_name(chip, chiprev,
418 +                                               brcmf_usb_fwnames,
419 +                                               ARRAY_SIZE(brcmf_usb_fwnames),
420 +                                               fw_name, NULL);
421 +
422 +       return ret;
423 +}
424 +
425  static const struct brcmf_bus_ops brcmf_usb_bus_ops = {
426         .txdata = brcmf_usb_tx,
427         .stop = brcmf_usb_down,
428         .txctl = brcmf_usb_tx_ctlpkt,
429         .rxctl = brcmf_usb_rx_ctlpkt,
430         .wowl_config = brcmf_usb_wowl_config,
431 +       .get_fwname = brcmf_usb_get_fwname,
432  };
433  
434  static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)