forked from mirrors/linux
		
	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: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
		
							parent
							
								
									55e491edbf
								
							
						
					
					
						commit
						ce2e6db554
					
				
					 1 changed files with 57 additions and 6 deletions
				
			
		| 
						 | 
					@ -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 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	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)
 | 
				
			||||||
	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)
 | 
				
			||||||
					     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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue