*  Copyright (C) 2015  Intel Corporation
  */
 
+#include <linux/efi.h>
 #include <linux/module.h>
 #include <linux/firmware.h>
 #include <linux/dmi.h>
 /* For kmalloc-ing the fw-name array instead of putting it on the stack */
 typedef char bcm_fw_name[BCM_FW_NAME_LEN];
 
+#ifdef CONFIG_EFI
+static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev)
+{
+       efi_guid_t guid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61, 0xb5, 0x1f,
+                                  0x43, 0x26, 0x81, 0x23, 0xd1, 0x13);
+       bdaddr_t efi_bdaddr, bdaddr;
+       efi_status_t status;
+       unsigned long len;
+       int ret;
+
+       if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
+               return -EOPNOTSUPP;
+
+       len = sizeof(efi_bdaddr);
+       status = efi.get_variable(L"BDADDR", &guid, NULL, &len, &efi_bdaddr);
+       if (status != EFI_SUCCESS)
+               return -ENXIO;
+
+       if (len != sizeof(efi_bdaddr))
+               return -EIO;
+
+       baswap(&bdaddr, &efi_bdaddr);
+
+       ret = btbcm_set_bdaddr(hdev, &bdaddr);
+       if (ret)
+               return ret;
+
+       bt_dev_info(hdev, "BCM: Using EFI device address (%pMR)", &bdaddr);
+       return 0;
+}
+#else
+static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev)
+{
+       return -EOPNOTSUPP;
+}
+#endif
+
 int btbcm_check_bdaddr(struct hci_dev *hdev)
 {
        struct hci_rp_read_bd_addr *bda;
            !bacmp(&bda->bdaddr, BDADDR_BCM4345C5) ||
            !bacmp(&bda->bdaddr, BDADDR_BCM43430A0) ||
            !bacmp(&bda->bdaddr, BDADDR_BCM43341B)) {
-               bt_dev_info(hdev, "BCM: Using default device address (%pMR)",
-                           &bda->bdaddr);
-               set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+               /* Try falling back to BDADDR EFI variable */
+               if (btbcm_set_bdaddr_from_efi(hdev) != 0) {
+                       bt_dev_info(hdev, "BCM: Using default device address (%pMR)",
+                                   &bda->bdaddr);
+                       set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+               }
        }
 
        kfree_skb(skb);