Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[cascardo/linux.git] / drivers / net / wireless / intel / iwlwifi / iwl-nvm-parse.c
index 43f8f7d..3bd6fc1 100644 (file)
@@ -67,6 +67,7 @@
 #include <linux/export.h>
 #include <linux/etherdevice.h>
 #include <linux/pci.h>
+#include <linux/acpi.h>
 #include "iwl-drv.h"
 #include "iwl-modparams.h"
 #include "iwl-nvm-parse.h"
@@ -564,11 +565,16 @@ static void iwl_set_hw_address_from_csr(struct iwl_trans *trans,
        __le32 mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_STRAP));
        __le32 mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_STRAP));
 
-       /* If OEM did not fuse address - get it from OTP */
-       if (!mac_addr0 && !mac_addr1) {
-               mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_OTP));
-               mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_OTP));
-       }
+       iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr);
+       /*
+        * If the OEM fused a valid address, use it instead of the one in the
+        * OTP
+        */
+       if (is_valid_ether_addr(data->hw_addr))
+               return;
+
+       mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_OTP));
+       mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_OTP));
 
        iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr);
 }
@@ -899,3 +905,91 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
        return regd;
 }
 IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info);
+
+#ifdef CONFIG_ACPI
+#define WRDD_METHOD            "WRDD"
+#define WRDD_WIFI              (0x07)
+#define WRDD_WIGIG             (0x10)
+
+static u32 iwl_wrdd_get_mcc(struct device *dev, union acpi_object *wrdd)
+{
+       union acpi_object *mcc_pkg, *domain_type, *mcc_value;
+       u32 i;
+
+       if (wrdd->type != ACPI_TYPE_PACKAGE ||
+           wrdd->package.count < 2 ||
+           wrdd->package.elements[0].type != ACPI_TYPE_INTEGER ||
+           wrdd->package.elements[0].integer.value != 0) {
+               IWL_DEBUG_EEPROM(dev, "Unsupported wrdd structure\n");
+               return 0;
+       }
+
+       for (i = 1 ; i < wrdd->package.count ; ++i) {
+               mcc_pkg = &wrdd->package.elements[i];
+
+               if (mcc_pkg->type != ACPI_TYPE_PACKAGE ||
+                   mcc_pkg->package.count < 2 ||
+                   mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
+                   mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
+                       mcc_pkg = NULL;
+                       continue;
+               }
+
+               domain_type = &mcc_pkg->package.elements[0];
+               if (domain_type->integer.value == WRDD_WIFI)
+                       break;
+
+               mcc_pkg = NULL;
+       }
+
+       if (mcc_pkg) {
+               mcc_value = &mcc_pkg->package.elements[1];
+               return mcc_value->integer.value;
+       }
+
+       return 0;
+}
+
+int iwl_get_bios_mcc(struct device *dev, char *mcc)
+{
+       acpi_handle root_handle;
+       acpi_handle handle;
+       struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL};
+       acpi_status status;
+       u32 mcc_val;
+
+       root_handle = ACPI_HANDLE(dev);
+       if (!root_handle) {
+               IWL_DEBUG_EEPROM(dev,
+                                "Could not retrieve root port ACPI handle\n");
+               return -ENOENT;
+       }
+
+       /* Get the method's handle */
+       status = acpi_get_handle(root_handle, (acpi_string)WRDD_METHOD,
+                                &handle);
+       if (ACPI_FAILURE(status)) {
+               IWL_DEBUG_EEPROM(dev, "WRD method not found\n");
+               return -ENOENT;
+       }
+
+       /* Call WRDD with no arguments */
+       status = acpi_evaluate_object(handle, NULL, NULL, &wrdd);
+       if (ACPI_FAILURE(status)) {
+               IWL_DEBUG_EEPROM(dev, "WRDC invocation failed (0x%x)\n",
+                                status);
+               return -ENOENT;
+       }
+
+       mcc_val = iwl_wrdd_get_mcc(dev, wrdd.pointer);
+       kfree(wrdd.pointer);
+       if (!mcc_val)
+               return -ENOENT;
+
+       mcc[0] = (mcc_val >> 8) & 0xff;
+       mcc[1] = mcc_val & 0xff;
+       mcc[2] = '\0';
+       return 0;
+}
+IWL_EXPORT_SYMBOL(iwl_get_bios_mcc);
+#endif