add chaos_calmer branch
[15.05/openwrt.git] / package / kernel / mac80211 / patches / 363-brcmfmac-Add-support-for-multiple-PCIE-devices-in-nv.patch
1 From: Hante Meuleman <meuleman@broadcom.com>
2 Date: Tue, 14 Apr 2015 20:10:33 +0200
3 Subject: [PATCH] brcmfmac: Add support for multiple PCIE devices in
4  nvram.
5
6 With PCIE it is possible to support multiple devices with the
7 same device type. They all load the same nvram file. In order to
8 support this the nvram can specify which part of the nvram is
9 for which pcie device. This patch adds support for these new
10 types of nvram files.
11
12 Reviewed-by: Arend Van Spriel <arend@broadcom.com>
13 Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
14 Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
15 Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
16 Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
17 Signed-off-by: Arend van Spriel <arend@broadcom.com>
18 ---
19
20 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
21 +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
22 @@ -23,6 +23,10 @@
23  #include "debug.h"
24  #include "firmware.h"
25  
26 +#define BRCMF_FW_MAX_NVRAM_SIZE                        64000
27 +#define BRCMF_FW_NVRAM_DEVPATH_LEN             19      /* devpath0=pcie/1/4/ */
28 +#define BRCMF_FW_NVRAM_PCIEDEV_LEN             9       /* pcie/1/4/ */
29 +
30  char brcmf_firmware_path[BRCMF_FW_PATH_LEN];
31  module_param_string(firmware_path, brcmf_firmware_path,
32                     BRCMF_FW_PATH_LEN, 0440);
33 @@ -46,6 +50,8 @@ enum nvram_parser_state {
34   * @column: current column in line.
35   * @pos: byte offset in input buffer.
36   * @entry: start position of key,value entry.
37 + * @multi_dev_v1: detect pcie multi device v1 (compressed).
38 + * @multi_dev_v2: detect pcie multi device v2.
39   */
40  struct nvram_parser {
41         enum nvram_parser_state state;
42 @@ -56,6 +62,8 @@ struct nvram_parser {
43         u32 column;
44         u32 pos;
45         u32 entry;
46 +       bool multi_dev_v1;
47 +       bool multi_dev_v2;
48  };
49  
50  static bool is_nvram_char(char c)
51 @@ -108,6 +116,10 @@ static enum nvram_parser_state brcmf_nvr
52                         st = COMMENT;
53                 else
54                         st = VALUE;
55 +               if (strncmp(&nvp->fwnv->data[nvp->entry], "devpath", 7) == 0)
56 +                       nvp->multi_dev_v1 = true;
57 +               if (strncmp(&nvp->fwnv->data[nvp->entry], "pcie/", 5) == 0)
58 +                       nvp->multi_dev_v2 = true;
59         } else if (!is_nvram_char(c)) {
60                 brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
61                           nvp->line, nvp->column);
62 @@ -133,6 +145,8 @@ brcmf_nvram_handle_value(struct nvram_pa
63                 ekv = (u8 *)&nvp->fwnv->data[nvp->pos];
64                 skv = (u8 *)&nvp->fwnv->data[nvp->entry];
65                 cplen = ekv - skv;
66 +               if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
67 +                       return END;
68                 /* copy to output buffer */
69                 memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
70                 nvp->nvram_len += cplen;
71 @@ -180,10 +194,18 @@ static enum nvram_parser_state
72  static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
73                                    const struct firmware *nv)
74  {
75 +       size_t size;
76 +
77         memset(nvp, 0, sizeof(*nvp));
78         nvp->fwnv = nv;
79 +       /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
80 +       if (nv->size > BRCMF_FW_MAX_NVRAM_SIZE)
81 +               size = BRCMF_FW_MAX_NVRAM_SIZE;
82 +       else
83 +               size = nv->size;
84         /* Alloc for extra 0 byte + roundup by 4 + length field */
85 -       nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL);
86 +       size += 1 + 3 + sizeof(u32);
87 +       nvp->nvram = kzalloc(size, GFP_KERNEL);
88         if (!nvp->nvram)
89                 return -ENOMEM;
90  
91 @@ -192,12 +214,136 @@ static int brcmf_init_nvram_parser(struc
92         return 0;
93  }
94  
95 +/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple
96 + * devices. Strip it down for one device, use domain_nr/bus_nr to determine
97 + * which data is to be returned. v1 is the version where nvram is stored
98 + * compressed and "devpath" maps to index for valid entries.
99 + */
100 +static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
101 +                                   u16 bus_nr)
102 +{
103 +       u32 i, j;
104 +       bool found;
105 +       u8 *nvram;
106 +       u8 id;
107 +
108 +       nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
109 +       if (!nvram)
110 +               goto fail;
111 +
112 +       /* min length: devpath0=pcie/1/4/ + 0:x=y */
113 +       if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6)
114 +               goto fail;
115 +
116 +       /* First search for the devpathX and see if it is the configuration
117 +        * for domain_nr/bus_nr. Search complete nvp
118 +        */
119 +       found = false;
120 +       i = 0;
121 +       while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) {
122 +               /* Format: devpathX=pcie/Y/Z/
123 +                * Y = domain_nr, Z = bus_nr, X = virtual ID
124 +                */
125 +               if ((strncmp(&nvp->nvram[i], "devpath", 7) == 0) &&
126 +                   (strncmp(&nvp->nvram[i + 8], "=pcie/", 6) == 0)) {
127 +                       if (((nvp->nvram[i + 14] - '0') == domain_nr) &&
128 +                           ((nvp->nvram[i + 16] - '0') == bus_nr)) {
129 +                               id = nvp->nvram[i + 7] - '0';
130 +                               found = true;
131 +                               break;
132 +                       }
133 +               }
134 +               while (nvp->nvram[i] != 0)
135 +                       i++;
136 +               i++;
137 +       }
138 +       if (!found)
139 +               goto fail;
140 +
141 +       /* Now copy all valid entries, release old nvram and assign new one */
142 +       i = 0;
143 +       j = 0;
144 +       while (i < nvp->nvram_len) {
145 +               if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) {
146 +                       i += 2;
147 +                       while (nvp->nvram[i] != 0) {
148 +                               nvram[j] = nvp->nvram[i];
149 +                               i++;
150 +                               j++;
151 +                       }
152 +                       nvram[j] = 0;
153 +                       j++;
154 +               }
155 +               while (nvp->nvram[i] != 0)
156 +                       i++;
157 +               i++;
158 +       }
159 +       kfree(nvp->nvram);
160 +       nvp->nvram = nvram;
161 +       nvp->nvram_len = j;
162 +       return;
163 +
164 +fail:
165 +       kfree(nvram);
166 +       nvp->nvram_len = 0;
167 +}
168 +
169 +/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple
170 + * devices. Strip it down for one device, use domain_nr/bus_nr to determine
171 + * which data is to be returned. v2 is the version where nvram is stored
172 + * uncompressed, all relevant valid entries are identified by
173 + * pcie/domain_nr/bus_nr:
174 + */
175 +static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr,
176 +                                   u16 bus_nr)
177 +{
178 +       u32 i, j;
179 +       u8 *nvram;
180 +
181 +       nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
182 +       if (!nvram)
183 +               goto fail;
184 +
185 +       /* Copy all valid entries, release old nvram and assign new one.
186 +        * Valid entries are of type pcie/X/Y/ where X = domain_nr and
187 +        * Y = bus_nr.
188 +        */
189 +       i = 0;
190 +       j = 0;
191 +       while (i < nvp->nvram_len - BRCMF_FW_NVRAM_PCIEDEV_LEN) {
192 +               if ((strncmp(&nvp->nvram[i], "pcie/", 5) == 0) &&
193 +                   (nvp->nvram[i + 6] == '/') && (nvp->nvram[i + 8] == '/') &&
194 +                   ((nvp->nvram[i + 5] - '0') == domain_nr) &&
195 +                   ((nvp->nvram[i + 7] - '0') == bus_nr)) {
196 +                       i += BRCMF_FW_NVRAM_PCIEDEV_LEN;
197 +                       while (nvp->nvram[i] != 0) {
198 +                               nvram[j] = nvp->nvram[i];
199 +                               i++;
200 +                               j++;
201 +                       }
202 +                       nvram[j] = 0;
203 +                       j++;
204 +               }
205 +               while (nvp->nvram[i] != 0)
206 +                       i++;
207 +               i++;
208 +       }
209 +       kfree(nvp->nvram);
210 +       nvp->nvram = nvram;
211 +       nvp->nvram_len = j;
212 +       return;
213 +fail:
214 +       kfree(nvram);
215 +       nvp->nvram_len = 0;
216 +}
217 +
218  /* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
219   * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
220   * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
221   * End of buffer is completed with token identifying length of buffer.
222   */
223 -static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length)
224 +static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length,
225 +                                 u16 domain_nr, u16 bus_nr)
226  {
227         struct nvram_parser nvp;
228         u32 pad;
229 @@ -212,6 +358,16 @@ static void *brcmf_fw_nvram_strip(const
230                 if (nvp.state == END)
231                         break;
232         }
233 +       if (nvp.multi_dev_v1)
234 +               brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr);
235 +       else if (nvp.multi_dev_v2)
236 +               brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr);
237 +
238 +       if (nvp.nvram_len == 0) {
239 +               kfree(nvp.nvram);
240 +               return NULL;
241 +       }
242 +
243         pad = nvp.nvram_len;
244         *new_length = roundup(nvp.nvram_len + 1, 4);
245         while (pad != *new_length) {
246 @@ -239,6 +395,8 @@ struct brcmf_fw {
247         u16 flags;
248         const struct firmware *code;
249         const char *nvram_name;
250 +       u16 domain_nr;
251 +       u16 bus_nr;
252         void (*done)(struct device *dev, const struct firmware *fw,
253                      void *nvram_image, u32 nvram_len);
254  };
255 @@ -254,7 +412,8 @@ static void brcmf_fw_request_nvram_done(
256                 goto fail;
257  
258         if (fw) {
259 -               nvram = brcmf_fw_nvram_strip(fw, &nvram_length);
260 +               nvram = brcmf_fw_nvram_strip(fw, &nvram_length,
261 +                                            fwctx->domain_nr, fwctx->bus_nr);
262                 release_firmware(fw);
263                 if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
264                         goto fail;
265 @@ -309,11 +468,12 @@ fail:
266         kfree(fwctx);
267  }
268  
269 -int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
270 -                          const char *code, const char *nvram,
271 -                          void (*fw_cb)(struct device *dev,
272 -                                        const struct firmware *fw,
273 -                                        void *nvram_image, u32 nvram_len))
274 +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
275 +                               const char *code, const char *nvram,
276 +                               void (*fw_cb)(struct device *dev,
277 +                                             const struct firmware *fw,
278 +                                             void *nvram_image, u32 nvram_len),
279 +                               u16 domain_nr, u16 bus_nr)
280  {
281         struct brcmf_fw *fwctx;
282  
283 @@ -333,8 +493,21 @@ int brcmf_fw_get_firmwares(struct device
284         fwctx->done = fw_cb;
285         if (flags & BRCMF_FW_REQUEST_NVRAM)
286                 fwctx->nvram_name = nvram;
287 +       fwctx->domain_nr = domain_nr;
288 +       fwctx->bus_nr = bus_nr;
289  
290         return request_firmware_nowait(THIS_MODULE, true, code, dev,
291                                        GFP_KERNEL, fwctx,
292                                        brcmf_fw_request_code_done);
293  }
294 +
295 +int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
296 +                          const char *code, const char *nvram,
297 +                          void (*fw_cb)(struct device *dev,
298 +                                        const struct firmware *fw,
299 +                                        void *nvram_image, u32 nvram_len))
300 +{
301 +       return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0,
302 +                                          0);
303 +}
304 +
305 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
306 +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
307 @@ -32,6 +32,12 @@ void brcmf_fw_nvram_free(void *nvram);
308   * fails it will not use the callback, but call device_release_driver()
309   * instead which will call the driver .remove() callback.
310   */
311 +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
312 +                               const char *code, const char *nvram,
313 +                               void (*fw_cb)(struct device *dev,
314 +                                             const struct firmware *fw,
315 +                                             void *nvram_image, u32 nvram_len),
316 +                               u16 domain_nr, u16 bus_nr);
317  int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
318                            const char *code, const char *nvram,
319                            void (*fw_cb)(struct device *dev,
320 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
321 +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
322 @@ -1649,8 +1649,13 @@ brcmf_pcie_probe(struct pci_dev *pdev, c
323         struct brcmf_pciedev_info *devinfo;
324         struct brcmf_pciedev *pcie_bus_dev;
325         struct brcmf_bus *bus;
326 +       u16 domain_nr;
327 +       u16 bus_nr;
328  
329 -       brcmf_dbg(PCIE, "Enter %x:%x\n", pdev->vendor, pdev->device);
330 +       domain_nr = pci_domain_nr(pdev->bus) + 1;
331 +       bus_nr = pdev->bus->number;
332 +       brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device,
333 +                 domain_nr, bus_nr);
334  
335         ret = -ENOMEM;
336         devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
337 @@ -1699,10 +1704,10 @@ brcmf_pcie_probe(struct pci_dev *pdev, c
338         if (ret)
339                 goto fail_bus;
340  
341 -       ret = brcmf_fw_get_firmwares(bus->dev, BRCMF_FW_REQUEST_NVRAM |
342 -                                              BRCMF_FW_REQ_NV_OPTIONAL,
343 -                                    devinfo->fw_name, devinfo->nvram_name,
344 -                                    brcmf_pcie_setup);
345 +       ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM |
346 +                                                   BRCMF_FW_REQ_NV_OPTIONAL,
347 +                                         devinfo->fw_name, devinfo->nvram_name,
348 +                                         brcmf_pcie_setup, domain_nr, bus_nr);
349         if (ret == 0)
350                 return 0;
351  fail_bus: