aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211/patches/363-brcmfmac-Add-support-for-multiple-PCIE-devices-in-nv.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/363-brcmfmac-Add-support-for-multiple-PCIE-devices-in-nv.patch')
-rw-r--r--package/kernel/mac80211/patches/363-brcmfmac-Add-support-for-multiple-PCIE-devices-in-nv.patch351
1 files changed, 351 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/363-brcmfmac-Add-support-for-multiple-PCIE-devices-in-nv.patch b/package/kernel/mac80211/patches/363-brcmfmac-Add-support-for-multiple-PCIE-devices-in-nv.patch
new file mode 100644
index 0000000000..5f9bbeaf5c
--- /dev/null
+++ b/package/kernel/mac80211/patches/363-brcmfmac-Add-support-for-multiple-PCIE-devices-in-nv.patch
@@ -0,0 +1,351 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Tue, 14 Apr 2015 20:10:33 +0200
+Subject: [PATCH] brcmfmac: Add support for multiple PCIE devices in
+ nvram.
+
+With PCIE it is possible to support multiple devices with the
+same device type. They all load the same nvram file. In order to
+support this the nvram can specify which part of the nvram is
+for which pcie device. This patch adds support for these new
+types of nvram files.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
+@@ -23,6 +23,10 @@
+ #include "debug.h"
+ #include "firmware.h"
+
++#define BRCMF_FW_MAX_NVRAM_SIZE 64000
++#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */
++#define BRCMF_FW_NVRAM_PCIEDEV_LEN 9 /* pcie/1/4/ */
++
+ char brcmf_firmware_path[BRCMF_FW_PATH_LEN];
+ module_param_string(firmware_path, brcmf_firmware_path,
+ BRCMF_FW_PATH_LEN, 0440);
+@@ -46,6 +50,8 @@ enum nvram_parser_state {
+ * @column: current column in line.
+ * @pos: byte offset in input buffer.
+ * @entry: start position of key,value entry.
++ * @multi_dev_v1: detect pcie multi device v1 (compressed).
++ * @multi_dev_v2: detect pcie multi device v2.
+ */
+ struct nvram_parser {
+ enum nvram_parser_state state;
+@@ -56,6 +62,8 @@ struct nvram_parser {
+ u32 column;
+ u32 pos;
+ u32 entry;
++ bool multi_dev_v1;
++ bool multi_dev_v2;
+ };
+
+ static bool is_nvram_char(char c)
+@@ -108,6 +116,10 @@ static enum nvram_parser_state brcmf_nvr
+ st = COMMENT;
+ else
+ st = VALUE;
++ if (strncmp(&nvp->fwnv->data[nvp->entry], "devpath", 7) == 0)
++ nvp->multi_dev_v1 = true;
++ if (strncmp(&nvp->fwnv->data[nvp->entry], "pcie/", 5) == 0)
++ nvp->multi_dev_v2 = true;
+ } else if (!is_nvram_char(c)) {
+ brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
+ nvp->line, nvp->column);
+@@ -133,6 +145,8 @@ brcmf_nvram_handle_value(struct nvram_pa
+ ekv = (u8 *)&nvp->fwnv->data[nvp->pos];
+ skv = (u8 *)&nvp->fwnv->data[nvp->entry];
+ cplen = ekv - skv;
++ if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
++ return END;
+ /* copy to output buffer */
+ memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
+ nvp->nvram_len += cplen;
+@@ -180,10 +194,18 @@ static enum nvram_parser_state
+ static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
+ const struct firmware *nv)
+ {
++ size_t size;
++
+ memset(nvp, 0, sizeof(*nvp));
+ nvp->fwnv = nv;
++ /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
++ if (nv->size > BRCMF_FW_MAX_NVRAM_SIZE)
++ size = BRCMF_FW_MAX_NVRAM_SIZE;
++ else
++ size = nv->size;
+ /* Alloc for extra 0 byte + roundup by 4 + length field */
+- nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL);
++ size += 1 + 3 + sizeof(u32);
++ nvp->nvram = kzalloc(size, GFP_KERNEL);
+ if (!nvp->nvram)
+ return -ENOMEM;
+
+@@ -192,12 +214,136 @@ static int brcmf_init_nvram_parser(struc
+ return 0;
+ }
+
++/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple
++ * devices. Strip it down for one device, use domain_nr/bus_nr to determine
++ * which data is to be returned. v1 is the version where nvram is stored
++ * compressed and "devpath" maps to index for valid entries.
++ */
++static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
++ u16 bus_nr)
++{
++ u32 i, j;
++ bool found;
++ u8 *nvram;
++ u8 id;
++
++ nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
++ if (!nvram)
++ goto fail;
++
++ /* min length: devpath0=pcie/1/4/ + 0:x=y */
++ if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6)
++ goto fail;
++
++ /* First search for the devpathX and see if it is the configuration
++ * for domain_nr/bus_nr. Search complete nvp
++ */
++ found = false;
++ i = 0;
++ while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) {
++ /* Format: devpathX=pcie/Y/Z/
++ * Y = domain_nr, Z = bus_nr, X = virtual ID
++ */
++ if ((strncmp(&nvp->nvram[i], "devpath", 7) == 0) &&
++ (strncmp(&nvp->nvram[i + 8], "=pcie/", 6) == 0)) {
++ if (((nvp->nvram[i + 14] - '0') == domain_nr) &&
++ ((nvp->nvram[i + 16] - '0') == bus_nr)) {
++ id = nvp->nvram[i + 7] - '0';
++ found = true;
++ break;
++ }
++ }
++ while (nvp->nvram[i] != 0)
++ i++;
++ i++;
++ }
++ if (!found)
++ goto fail;
++
++ /* Now copy all valid entries, release old nvram and assign new one */
++ i = 0;
++ j = 0;
++ while (i < nvp->nvram_len) {
++ if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) {
++ i += 2;
++ while (nvp->nvram[i] != 0) {
++ nvram[j] = nvp->nvram[i];
++ i++;
++ j++;
++ }
++ nvram[j] = 0;
++ j++;
++ }
++ while (nvp->nvram[i] != 0)
++ i++;
++ i++;
++ }
++ kfree(nvp->nvram);
++ nvp->nvram = nvram;
++ nvp->nvram_len = j;
++ return;
++
++fail:
++ kfree(nvram);
++ nvp->nvram_len = 0;
++}
++
++/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple
++ * devices. Strip it down for one device, use domain_nr/bus_nr to determine
++ * which data is to be returned. v2 is the version where nvram is stored
++ * uncompressed, all relevant valid entries are identified by
++ * pcie/domain_nr/bus_nr:
++ */
++static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr,
++ u16 bus_nr)
++{
++ u32 i, j;
++ u8 *nvram;
++
++ nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
++ if (!nvram)
++ goto fail;
++
++ /* Copy all valid entries, release old nvram and assign new one.
++ * Valid entries are of type pcie/X/Y/ where X = domain_nr and
++ * Y = bus_nr.
++ */
++ i = 0;
++ j = 0;
++ while (i < nvp->nvram_len - BRCMF_FW_NVRAM_PCIEDEV_LEN) {
++ if ((strncmp(&nvp->nvram[i], "pcie/", 5) == 0) &&
++ (nvp->nvram[i + 6] == '/') && (nvp->nvram[i + 8] == '/') &&
++ ((nvp->nvram[i + 5] - '0') == domain_nr) &&
++ ((nvp->nvram[i + 7] - '0') == bus_nr)) {
++ i += BRCMF_FW_NVRAM_PCIEDEV_LEN;
++ while (nvp->nvram[i] != 0) {
++ nvram[j] = nvp->nvram[i];
++ i++;
++ j++;
++ }
++ nvram[j] = 0;
++ j++;
++ }
++ while (nvp->nvram[i] != 0)
++ i++;
++ i++;
++ }
++ kfree(nvp->nvram);
++ nvp->nvram = nvram;
++ nvp->nvram_len = j;
++ return;
++fail:
++ kfree(nvram);
++ nvp->nvram_len = 0;
++}
++
+ /* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
+ * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
+ * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
+ * End of buffer is completed with token identifying length of buffer.
+ */
+-static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length)
++static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length,
++ u16 domain_nr, u16 bus_nr)
+ {
+ struct nvram_parser nvp;
+ u32 pad;
+@@ -212,6 +358,16 @@ static void *brcmf_fw_nvram_strip(const
+ if (nvp.state == END)
+ break;
+ }
++ if (nvp.multi_dev_v1)
++ brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr);
++ else if (nvp.multi_dev_v2)
++ brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr);
++
++ if (nvp.nvram_len == 0) {
++ kfree(nvp.nvram);
++ return NULL;
++ }
++
+ pad = nvp.nvram_len;
+ *new_length = roundup(nvp.nvram_len + 1, 4);
+ while (pad != *new_length) {
+@@ -239,6 +395,8 @@ struct brcmf_fw {
+ u16 flags;
+ const struct firmware *code;
+ const char *nvram_name;
++ u16 domain_nr;
++ u16 bus_nr;
+ void (*done)(struct device *dev, const struct firmware *fw,
+ void *nvram_image, u32 nvram_len);
+ };
+@@ -254,7 +412,8 @@ static void brcmf_fw_request_nvram_done(
+ goto fail;
+
+ if (fw) {
+- nvram = brcmf_fw_nvram_strip(fw, &nvram_length);
++ nvram = brcmf_fw_nvram_strip(fw, &nvram_length,
++ fwctx->domain_nr, fwctx->bus_nr);
+ release_firmware(fw);
+ if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+ goto fail;
+@@ -309,11 +468,12 @@ fail:
+ kfree(fwctx);
+ }
+
+-int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+- const char *code, const char *nvram,
+- void (*fw_cb)(struct device *dev,
+- const struct firmware *fw,
+- void *nvram_image, u32 nvram_len))
++int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
++ const char *code, const char *nvram,
++ void (*fw_cb)(struct device *dev,
++ const struct firmware *fw,
++ void *nvram_image, u32 nvram_len),
++ u16 domain_nr, u16 bus_nr)
+ {
+ struct brcmf_fw *fwctx;
+
+@@ -333,8 +493,21 @@ int brcmf_fw_get_firmwares(struct device
+ fwctx->done = fw_cb;
+ if (flags & BRCMF_FW_REQUEST_NVRAM)
+ fwctx->nvram_name = nvram;
++ fwctx->domain_nr = domain_nr;
++ fwctx->bus_nr = bus_nr;
+
+ return request_firmware_nowait(THIS_MODULE, true, code, dev,
+ GFP_KERNEL, fwctx,
+ brcmf_fw_request_code_done);
+ }
++
++int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
++ const char *code, const char *nvram,
++ void (*fw_cb)(struct device *dev,
++ const struct firmware *fw,
++ void *nvram_image, u32 nvram_len))
++{
++ return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0,
++ 0);
++}
++
+--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
+@@ -32,6 +32,12 @@ void brcmf_fw_nvram_free(void *nvram);
+ * fails it will not use the callback, but call device_release_driver()
+ * instead which will call the driver .remove() callback.
+ */
++int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
++ const char *code, const char *nvram,
++ void (*fw_cb)(struct device *dev,
++ const struct firmware *fw,
++ void *nvram_image, u32 nvram_len),
++ u16 domain_nr, u16 bus_nr);
+ int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+ const char *code, const char *nvram,
+ void (*fw_cb)(struct device *dev,
+--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+@@ -1649,8 +1649,13 @@ brcmf_pcie_probe(struct pci_dev *pdev, c
+ struct brcmf_pciedev_info *devinfo;
+ struct brcmf_pciedev *pcie_bus_dev;
+ struct brcmf_bus *bus;
++ u16 domain_nr;
++ u16 bus_nr;
+
+- brcmf_dbg(PCIE, "Enter %x:%x\n", pdev->vendor, pdev->device);
++ domain_nr = pci_domain_nr(pdev->bus) + 1;
++ bus_nr = pdev->bus->number;
++ brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device,
++ domain_nr, bus_nr);
+
+ ret = -ENOMEM;
+ devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
+@@ -1699,10 +1704,10 @@ brcmf_pcie_probe(struct pci_dev *pdev, c
+ if (ret)
+ goto fail_bus;
+
+- ret = brcmf_fw_get_firmwares(bus->dev, BRCMF_FW_REQUEST_NVRAM |
+- BRCMF_FW_REQ_NV_OPTIONAL,
+- devinfo->fw_name, devinfo->nvram_name,
+- brcmf_pcie_setup);
++ ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM |
++ BRCMF_FW_REQ_NV_OPTIONAL,
++ devinfo->fw_name, devinfo->nvram_name,
++ brcmf_pcie_setup, domain_nr, bus_nr);
+ if (ret == 0)
+ return 0;
+ fail_bus: