ath10k: Add xo calibration support for wifi rf clock
authorGovind Singh <govinds@codeaurora.org>
Wed, 18 Sep 2019 13:27:35 +0000 (16:27 +0300)
committerKalle Valo <kvalo@codeaurora.org>
Sat, 21 Sep 2019 06:46:39 +0000 (09:46 +0300)
PMIC XO is the clock source for wifi rf clock in integrated wifi
chipset ex: WCN3990. Due to board layout errors XO frequency drifts
can cause wifi rf clock inaccuracy.
XO calibration test tree in Factory Test Mode is used to find the
best frequency offset(for example +/-2KHz )by programming XO trim
register. This ensure system clock stays within required 20 ppm
WLAN rf clock.

Retrieve the xo trim offset via system firmware (e.g., device tree),
especially in the case where the device doesn't have a useful EEPROM
on which to store the calibrated XO offset (e.g., for integrated Wifi).
Calibrated XO offset is sent to fw, which compensate the clock drift
by programing the XO trim register.

Signed-off-by: Govind Singh <govinds@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/ath/ath10k/qmi.c
drivers/net/wireless/ath/ath10k/snoc.c
drivers/net/wireless/ath/ath10k/snoc.h

index 44ad009f8e2e123075f2a57611e20d001f44dfd9..59e15c946db89cc33f74a514c5423bd81e7986c7 100644 (file)
@@ -306,10 +306,16 @@ static int ath10k_qmi_send_cal_report_req(struct ath10k_qmi *qmi)
        struct wlfw_cal_report_resp_msg_v01 resp = {};
        struct wlfw_cal_report_req_msg_v01 req = {};
        struct ath10k *ar = qmi->ar;
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
        struct qmi_txn txn;
        int i, j = 0;
        int ret;
 
+       if (ar_snoc->xo_cal_supported) {
+               req.xo_cal_data_valid = 1;
+               req.xo_cal_data = ar_snoc->xo_cal_data;
+       }
+
        ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_cal_report_resp_msg_v01_ei,
                           &resp);
        if (ret < 0)
@@ -693,6 +699,7 @@ ath10k_qmi_ind_register_send_sync_msg(struct ath10k_qmi *qmi)
        struct wlfw_ind_register_resp_msg_v01 resp = {};
        struct wlfw_ind_register_req_msg_v01 req = {};
        struct ath10k *ar = qmi->ar;
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
        struct qmi_txn txn;
        int ret;
 
@@ -703,6 +710,11 @@ ath10k_qmi_ind_register_send_sync_msg(struct ath10k_qmi *qmi)
        req.msa_ready_enable_valid = 1;
        req.msa_ready_enable = 1;
 
+       if (ar_snoc->xo_cal_supported) {
+               req.xo_cal_enable_valid = 1;
+               req.xo_cal_enable = 1;
+       }
+
        ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
                           wlfw_ind_register_resp_msg_v01_ei, &resp);
        if (ret < 0)
index 13ee97edf6df265afa038388001809a48a392779..c8c92906a178044ccd47654cf9f9bb6e8e97cb4f 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/regulator/consumer.h>
 
 #include "ce.h"
@@ -1256,6 +1257,16 @@ static int ath10k_snoc_resource_init(struct ath10k *ar)
                ar_snoc->ce_irqs[i].irq_line = res->start;
        }
 
+       ret = device_property_read_u32(&pdev->dev, "qcom,xo-cal-data",
+                                      &ar_snoc->xo_cal_data);
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc xo-cal-data return %d\n", ret);
+       if (ret == 0) {
+               ar_snoc->xo_cal_supported = true;
+               ath10k_dbg(ar, ATH10K_DBG_SNOC, "xo cal data %x\n",
+                          ar_snoc->xo_cal_data);
+       }
+       ret = 0;
+
 out:
        return ret;
 }
index d2449a3b4a8f7cc6724fd96d190adc8169bb9061..bc2b925b1d5a6ae76d00c86158ad48d4aeca23ef 100644 (file)
@@ -69,6 +69,8 @@ struct ath10k_snoc {
        size_t num_clks;
        struct ath10k_qmi *qmi;
        unsigned long flags;
+       bool xo_cal_supported;
+       u32 xo_cal_data;
 };
 
 static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar)