123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- From: Hante Meuleman <[email protected]>
- 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 <[email protected]>
- Reviewed-by: Franky (Zhenhui) Lin <[email protected]>
- Reviewed-by: Pieter-Paul Giesberts <[email protected]>
- Reviewed-by: Daniel (Deognyoun) Kim <[email protected]>
- Signed-off-by: Hante Meuleman <[email protected]>
- Signed-off-by: Arend van Spriel <[email protected]>
- ---
- --- 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:
|