ath10k: add platform regulatory domain support
authorBartosz Markowski <bartosz.markowski@tieto.com>
Wed, 28 Sep 2016 12:11:58 +0000 (15:11 +0300)
committerKalle Valo <kvalo@qca.qualcomm.com>
Tue, 4 Oct 2016 15:01:53 +0000 (18:01 +0300)
This overrides whatever regulatory the device
EEPROM contains and uses what the platform says
instead - in this implementation the ACPI driver.

In case the hint is not programmed or corrupted (0xffff)
the device falls back to the eeprom programmed settings.

Signed-off-by: Bartosz Markowski <bartosz.markowski@tieto.com>
[kvalo@qca.qualcomm.com: remove ifdef, change error handling, change info messages to dbg]
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/wmi.c

index dda49af..c091306 100644 (file)
@@ -713,6 +713,7 @@ struct ath10k {
        u32 phy_capability;
        u32 hw_min_tx_power;
        u32 hw_max_tx_power;
+       u32 hw_eeprom_rd;
        u32 ht_cap_info;
        u32 vht_cap_info;
        u32 num_rf_chains;
index 76297d6..ed71b95 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <net/mac80211.h>
 #include <linux/etherdevice.h>
+#include <linux/acpi.h>
 
 #include "hif.h"
 #include "core.h"
@@ -3179,7 +3180,8 @@ static void ath10k_mac_vif_handle_tx_pause(struct ath10k_vif *arvif,
                ath10k_mac_vif_tx_unlock(arvif, pause_id);
                break;
        default:
-               ath10k_warn(ar, "received unknown tx pause action %d on vdev %i, ignoring\n",
+               ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                          "received unknown tx pause action %d on vdev %i, ignoring\n",
                            action, arvif->vdev_id);
                break;
        }
@@ -7789,6 +7791,109 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id)
        return arvif_iter.arvif;
 }
 
+#define WRD_METHOD "WRDD"
+#define WRDD_WIFI  (0x07)
+
+static u32 ath10k_mac_wrdd_get_mcc(struct ath10k *ar, union acpi_object *wrdd)
+{
+       union acpi_object *mcc_pkg;
+       union acpi_object *domain_type;
+       union acpi_object *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) {
+               ath10k_warn(ar, "ignoring malformed/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)
+                       continue;
+               if (mcc_pkg->package.count < 2)
+                       continue;
+               if (mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
+                   mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
+                       continue;
+
+               domain_type = &mcc_pkg->package.elements[0];
+               if (domain_type->integer.value != WRDD_WIFI)
+                       continue;
+
+               mcc_value = &mcc_pkg->package.elements[1];
+               return mcc_value->integer.value;
+       }
+       return 0;
+}
+
+static int ath10k_mac_get_wrdd_regulatory(struct ath10k *ar, u16 *rd)
+{
+       struct pci_dev __maybe_unused *pdev = to_pci_dev(ar->dev);
+       acpi_handle root_handle;
+       acpi_handle handle;
+       struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL};
+       acpi_status status;
+       u32 alpha2_code;
+       char alpha2[3];
+
+       root_handle = ACPI_HANDLE(&pdev->dev);
+       if (!root_handle)
+               return -EOPNOTSUPP;
+
+       status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle);
+       if (ACPI_FAILURE(status)) {
+               ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                          "failed to get wrd method %d\n", status);
+               return -EIO;
+       }
+
+       status = acpi_evaluate_object(handle, NULL, NULL, &wrdd);
+       if (ACPI_FAILURE(status)) {
+               ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                          "failed to call wrdc %d\n", status);
+               return -EIO;
+       }
+
+       alpha2_code = ath10k_mac_wrdd_get_mcc(ar, wrdd.pointer);
+       kfree(wrdd.pointer);
+       if (!alpha2_code)
+               return -EIO;
+
+       alpha2[0] = (alpha2_code >> 8) & 0xff;
+       alpha2[1] = (alpha2_code >> 0) & 0xff;
+       alpha2[2] = '\0';
+
+       ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                  "regulatory hint from WRDD (alpha2-code): %s\n", alpha2);
+
+       *rd = ath_regd_find_country_by_name(alpha2);
+       if (*rd == 0xffff)
+               return -EIO;
+
+       *rd |= COUNTRY_ERD_FLAG;
+       return 0;
+}
+
+static int ath10k_mac_init_rd(struct ath10k *ar)
+{
+       int ret;
+       u16 rd;
+
+       ret = ath10k_mac_get_wrdd_regulatory(ar, &rd);
+       if (ret) {
+               ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                          "fallback to eeprom programmed regulatory settings\n");
+               rd = ar->hw_eeprom_rd;
+       }
+
+       ar->ath_common.regulatory.current_rd = rd;
+       return 0;
+}
+
 int ath10k_mac_register(struct ath10k *ar)
 {
        static const u32 cipher_suites[] = {
@@ -8012,6 +8117,12 @@ int ath10k_mac_register(struct ath10k *ar)
                      ar->running_fw->fw_file.fw_features))
                ar->ops->wake_tx_queue = NULL;
 
+       ret = ath10k_mac_init_rd(ar);
+       if (ret) {
+               ath10k_err(ar, "failed to derive regdom: %d\n", ret);
+               goto err_dfs_detector_exit;
+       }
+
        ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
                            ath10k_reg_notifier);
        if (ret) {
index 4829513..b4cfaf9 100644 (file)
@@ -4676,7 +4676,7 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work)
        ar->fw_version_build = (__le32_to_cpu(arg.sw_ver1) & 0x0000ffff);
        ar->phy_capability = __le32_to_cpu(arg.phy_capab);
        ar->num_rf_chains = __le32_to_cpu(arg.num_rf_chains);
-       ar->ath_common.regulatory.current_rd = __le32_to_cpu(arg.eeprom_rd);
+       ar->hw_eeprom_rd = __le32_to_cpu(arg.eeprom_rd);
 
        ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ",
                        arg.service_map, arg.service_map_len);