diff options
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.patch | 351 |
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 0000000..5f9bbea --- /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: |