Commit ce2e6db5 authored by Hans de Goede's avatar Hans de Goede Committed by Kalle Valo

brcmfmac: Add support for getting nvram contents from EFI variables

Various X86 laptops with a SDIO attached brcmfmac wifi chip, store the
nvram contents in a special EFI variable. This commit adds support for
getting nvram directly from this EFI variable, without the user needing
to manually copy it.

This makes Wifi / Bluetooth work out of the box on these devices instead of
requiring manual setup.

This has been tested on the following models: Acer Iconia Tab8 w1-810,
Acer One 10, Asus T100CHI, Asus T100HA, Asus T100TA, Asus T200TA and a
Lenovo Mixx 2 8.
Tested-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 55e491ed
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <linux/efi.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -445,6 +446,51 @@ struct brcmf_fw { ...@@ -445,6 +446,51 @@ struct brcmf_fw {
static void brcmf_fw_request_done(const struct firmware *fw, void *ctx); static void brcmf_fw_request_done(const struct firmware *fw, void *ctx);
#ifdef CONFIG_EFI
static u8 *brcmf_fw_nvram_from_efi(size_t *data_len_ret)
{
const u16 name[] = { 'n', 'v', 'r', 'a', 'm', 0 };
struct efivar_entry *nvram_efivar;
unsigned long data_len = 0;
u8 *data = NULL;
int err;
nvram_efivar = kzalloc(sizeof(*nvram_efivar), GFP_KERNEL);
if (!nvram_efivar)
return NULL;
memcpy(&nvram_efivar->var.VariableName, name, sizeof(name));
nvram_efivar->var.VendorGuid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61,
0xb5, 0x1f, 0x43, 0x26,
0x81, 0x23, 0xd1, 0x13);
err = efivar_entry_size(nvram_efivar, &data_len);
if (err)
goto fail;
data = kmalloc(data_len, GFP_KERNEL);
if (!data)
goto fail;
err = efivar_entry_get(nvram_efivar, NULL, &data_len, data);
if (err)
goto fail;
brcmf_info("Using nvram EFI variable\n");
kfree(nvram_efivar);
*data_len_ret = data_len;
return data;
fail:
kfree(data);
kfree(nvram_efivar);
return NULL;
}
#else
static u8 *brcmf_fw_nvram_from_efi(size_t *data_len) { return NULL; }
#endif
static void brcmf_fw_free_request(struct brcmf_fw_request *req) static void brcmf_fw_free_request(struct brcmf_fw_request *req)
{ {
struct brcmf_fw_item *item; struct brcmf_fw_item *item;
...@@ -463,11 +509,12 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) ...@@ -463,11 +509,12 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
{ {
struct brcmf_fw *fwctx = ctx; struct brcmf_fw *fwctx = ctx;
struct brcmf_fw_item *cur; struct brcmf_fw_item *cur;
bool free_bcm47xx_nvram = false;
bool kfree_nvram = false;
u32 nvram_length = 0; u32 nvram_length = 0;
void *nvram = NULL; void *nvram = NULL;
u8 *data = NULL; u8 *data = NULL;
size_t data_len; size_t data_len;
bool raw_nvram;
brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
...@@ -476,12 +523,13 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) ...@@ -476,12 +523,13 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
if (fw && fw->data) { if (fw && fw->data) {
data = (u8 *)fw->data; data = (u8 *)fw->data;
data_len = fw->size; data_len = fw->size;
raw_nvram = false;
} else { } else {
data = bcm47xx_nvram_get_contents(&data_len); if ((data = bcm47xx_nvram_get_contents(&data_len)))
if (!data && !(cur->flags & BRCMF_FW_REQF_OPTIONAL)) free_bcm47xx_nvram = true;
else if ((data = brcmf_fw_nvram_from_efi(&data_len)))
kfree_nvram = true;
else if (!(cur->flags & BRCMF_FW_REQF_OPTIONAL))
goto fail; goto fail;
raw_nvram = true;
} }
if (data) if (data)
...@@ -489,8 +537,11 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) ...@@ -489,8 +537,11 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
fwctx->req->domain_nr, fwctx->req->domain_nr,
fwctx->req->bus_nr); fwctx->req->bus_nr);
if (raw_nvram) if (free_bcm47xx_nvram)
bcm47xx_nvram_release_contents(data); bcm47xx_nvram_release_contents(data);
if (kfree_nvram)
kfree(data);
release_firmware(fw); release_firmware(fw);
if (!nvram && !(cur->flags & BRCMF_FW_REQF_OPTIONAL)) if (!nvram && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
goto fail; goto fail;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment