ath10k: add board data download from target
authorSven Eckelmann <sven.eckelmann@open-mesh.com>
Thu, 2 Jun 2016 14:59:50 +0000 (17:59 +0300)
committerKalle Valo <kvalo@qca.qualcomm.com>
Tue, 7 Jun 2016 11:28:35 +0000 (14:28 +0300)
The QCA9887 stores its calibration data (board.bin) inside the EEPROM of
the target. This has to be downloaded manually to allow the device to
initialize correctly.

Signed-off-by: Sven Eckelmann <sven.eckelmann@open-mesh.com>
[kvalo@qca.qualcomm.com: handle -EOPNOTSUPP and s/fetch_board_data/fetch_cal_eeprom]
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/hif.h
drivers/net/wireless/ath/ath10k/hw.h
drivers/net/wireless/ath/ath10k/pci.c

index ed4e52e..1e88251 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/firmware.h>
 #include <linux/of.h>
+#include <asm/byteorder.h>
 
 #include "core.h"
 #include "mac.h"
@@ -573,6 +574,35 @@ out:
        return ret;
 }
 
+static int ath10k_download_cal_eeprom(struct ath10k *ar)
+{
+       size_t data_len;
+       void *data = NULL;
+       int ret;
+
+       ret = ath10k_hif_fetch_cal_eeprom(ar, &data, &data_len);
+       if (ret) {
+               if (ret != -EOPNOTSUPP)
+                       ath10k_warn(ar, "failed to read calibration data from EEPROM: %d\n",
+                                   ret);
+               goto out_free;
+       }
+
+       ret = ath10k_download_board_data(ar, data, data_len);
+       if (ret) {
+               ath10k_warn(ar, "failed to download calibration data from EEPROM: %d\n",
+                           ret);
+               goto out_free;
+       }
+
+       ret = 0;
+
+out_free:
+       kfree(data);
+
+       return ret;
+}
+
 static int ath10k_core_get_board_id_from_otp(struct ath10k *ar)
 {
        u32 result, address;
@@ -1335,7 +1365,17 @@ static int ath10k_download_cal_data(struct ath10k *ar)
        }
 
        ath10k_dbg(ar, ATH10K_DBG_BOOT,
-                  "boot did not find DT entry, try OTP next: %d\n",
+                  "boot did not find DT entry, try target EEPROM next: %d\n",
+                  ret);
+
+       ret = ath10k_download_cal_eeprom(ar);
+       if (ret == 0) {
+               ar->cal_mode = ATH10K_CAL_MODE_EEPROM;
+               goto done;
+       }
+
+       ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                  "boot did not find target EEPROM entry, try OTP next: %d\n",
                   ret);
 
        ret = ath10k_download_and_run_otp(ar);
index 4462c3f..3387733 100644 (file)
@@ -578,6 +578,7 @@ enum ath10k_cal_mode {
        ATH10K_CAL_MODE_DT,
        ATH10K_PRE_CAL_MODE_FILE,
        ATH10K_PRE_CAL_MODE_DT,
+       ATH10K_CAL_MODE_EEPROM,
 };
 
 enum ath10k_crypt_mode {
@@ -600,6 +601,8 @@ static inline const char *ath10k_cal_mode_str(enum ath10k_cal_mode mode)
                return "pre-cal-file";
        case ATH10K_PRE_CAL_MODE_DT:
                return "pre-cal-dt";
+       case ATH10K_CAL_MODE_EEPROM:
+               return "eeprom";
        }
 
        return "unknown";
index 89e7076..b2566b0 100644 (file)
@@ -87,6 +87,10 @@ struct ath10k_hif_ops {
 
        int (*suspend)(struct ath10k *ar);
        int (*resume)(struct ath10k *ar);
+
+       /* fetch calibration data from target eeprom */
+       int (*fetch_cal_eeprom)(struct ath10k *ar, void **data,
+                               size_t *data_len);
 };
 
 static inline int ath10k_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
@@ -202,4 +206,14 @@ static inline void ath10k_hif_write32(struct ath10k *ar,
        ar->hif.ops->write32(ar, address, data);
 }
 
+static inline int ath10k_hif_fetch_cal_eeprom(struct ath10k *ar,
+                                             void **data,
+                                             size_t *data_len)
+{
+       if (!ar->hif.ops->fetch_cal_eeprom)
+               return -EOPNOTSUPP;
+
+       return ar->hif.ops->fetch_cal_eeprom(ar, data, data_len);
+}
+
 #endif /* _HIF_H_ */
index 5ef1fa0..49097af 100644 (file)
@@ -568,7 +568,10 @@ enum ath10k_hw_4addr_pad {
 #define WLAN_SYSTEM_SLEEP_DISABLE_MASK         0x00000001
 
 #define WLAN_GPIO_PIN0_ADDRESS                 0x00000028
+#define WLAN_GPIO_PIN0_CONFIG_LSB              11
 #define WLAN_GPIO_PIN0_CONFIG_MASK             0x00007800
+#define WLAN_GPIO_PIN0_PAD_PULL_LSB            5
+#define WLAN_GPIO_PIN0_PAD_PULL_MASK           0x00000060
 #define WLAN_GPIO_PIN1_ADDRESS                 0x0000002c
 #define WLAN_GPIO_PIN1_CONFIG_MASK             0x00007800
 #define WLAN_GPIO_PIN10_ADDRESS                        0x00000050
@@ -581,6 +584,8 @@ enum ath10k_hw_4addr_pad {
 #define CLOCK_GPIO_BT_CLK_OUT_EN_MASK          0
 
 #define SI_CONFIG_OFFSET                       0x00000000
+#define SI_CONFIG_ERR_INT_LSB                  19
+#define SI_CONFIG_ERR_INT_MASK                 0x00080000
 #define SI_CONFIG_BIDIR_OD_DATA_LSB            18
 #define SI_CONFIG_BIDIR_OD_DATA_MASK           0x00040000
 #define SI_CONFIG_I2C_LSB                      16
@@ -594,7 +599,9 @@ enum ath10k_hw_4addr_pad {
 #define SI_CONFIG_DIVIDER_LSB                  0
 #define SI_CONFIG_DIVIDER_MASK                 0x0000000f
 #define SI_CS_OFFSET                           0x00000004
+#define SI_CS_DONE_ERR_LSB                     10
 #define SI_CS_DONE_ERR_MASK                    0x00000400
+#define SI_CS_DONE_INT_LSB                     9
 #define SI_CS_DONE_INT_MASK                    0x00000200
 #define SI_CS_START_LSB                                8
 #define SI_CS_START_MASK                       0x00000100
@@ -645,7 +652,10 @@ enum ath10k_hw_4addr_pad {
 #define GPIO_BASE_ADDRESS                      WLAN_GPIO_BASE_ADDRESS
 #define GPIO_PIN0_OFFSET                       WLAN_GPIO_PIN0_ADDRESS
 #define GPIO_PIN1_OFFSET                       WLAN_GPIO_PIN1_ADDRESS
+#define GPIO_PIN0_CONFIG_LSB                   WLAN_GPIO_PIN0_CONFIG_LSB
 #define GPIO_PIN0_CONFIG_MASK                  WLAN_GPIO_PIN0_CONFIG_MASK
+#define GPIO_PIN0_PAD_PULL_LSB                 WLAN_GPIO_PIN0_PAD_PULL_LSB
+#define GPIO_PIN0_PAD_PULL_MASK                        WLAN_GPIO_PIN0_PAD_PULL_MASK
 #define GPIO_PIN1_CONFIG_MASK                  WLAN_GPIO_PIN1_CONFIG_MASK
 #define SI_BASE_ADDRESS                                WLAN_SI_BASE_ADDRESS
 #define SCRATCH_BASE_ADDRESS                   SOC_CORE_BASE_ADDRESS
@@ -700,6 +710,18 @@ enum ath10k_hw_4addr_pad {
 #define WINDOW_READ_ADDR_ADDRESS               MISSING
 #define WINDOW_WRITE_ADDR_ADDRESS              MISSING
 
+#define QCA9887_1_0_I2C_SDA_GPIO_PIN           5
+#define QCA9887_1_0_I2C_SDA_PIN_CONFIG         3
+#define QCA9887_1_0_SI_CLK_GPIO_PIN            17
+#define QCA9887_1_0_SI_CLK_PIN_CONFIG          3
+#define QCA9887_1_0_GPIO_ENABLE_W1TS_LOW_ADDRESS 0x00000010
+
+#define QCA9887_EEPROM_SELECT_READ             0xa10000a0
+#define QCA9887_EEPROM_ADDR_HI_MASK            0x0000ff00
+#define QCA9887_EEPROM_ADDR_HI_LSB             8
+#define QCA9887_EEPROM_ADDR_LO_MASK            0x00ff0000
+#define QCA9887_EEPROM_ADDR_LO_LSB             16
+
 #define RTC_STATE_V_GET(x) (((x) & RTC_STATE_V_MASK) >> RTC_STATE_V_LSB)
 
 #endif /* _HW_H_ */
index 3e8e7ed..f06dd39 100644 (file)
@@ -2577,6 +2577,144 @@ static int ath10k_pci_hif_resume(struct ath10k *ar)
 }
 #endif
 
+static bool ath10k_pci_validate_cal(void *data, size_t size)
+{
+       __le16 *cal_words = data;
+       u16 checksum = 0;
+       size_t i;
+
+       if (size % 2 != 0)
+               return false;
+
+       for (i = 0; i < size / 2; i++)
+               checksum ^= le16_to_cpu(cal_words[i]);
+
+       return checksum == 0xffff;
+}
+
+static void ath10k_pci_enable_eeprom(struct ath10k *ar)
+{
+       /* Enable SI clock */
+       ath10k_pci_soc_write32(ar, CLOCK_CONTROL_OFFSET, 0x0);
+
+       /* Configure GPIOs for I2C operation */
+       ath10k_pci_write32(ar,
+                          GPIO_BASE_ADDRESS + GPIO_PIN0_OFFSET +
+                          4 * QCA9887_1_0_I2C_SDA_GPIO_PIN,
+                          SM(QCA9887_1_0_I2C_SDA_PIN_CONFIG,
+                             GPIO_PIN0_CONFIG) |
+                          SM(1, GPIO_PIN0_PAD_PULL));
+
+       ath10k_pci_write32(ar,
+                          GPIO_BASE_ADDRESS + GPIO_PIN0_OFFSET +
+                          4 * QCA9887_1_0_SI_CLK_GPIO_PIN,
+                          SM(QCA9887_1_0_SI_CLK_PIN_CONFIG, GPIO_PIN0_CONFIG) |
+                          SM(1, GPIO_PIN0_PAD_PULL));
+
+       ath10k_pci_write32(ar,
+                          GPIO_BASE_ADDRESS +
+                          QCA9887_1_0_GPIO_ENABLE_W1TS_LOW_ADDRESS,
+                          1u << QCA9887_1_0_SI_CLK_GPIO_PIN);
+
+       /* In Swift ASIC - EEPROM clock will be (110MHz/512) = 214KHz */
+       ath10k_pci_write32(ar,
+                          SI_BASE_ADDRESS + SI_CONFIG_OFFSET,
+                          SM(1, SI_CONFIG_ERR_INT) |
+                          SM(1, SI_CONFIG_BIDIR_OD_DATA) |
+                          SM(1, SI_CONFIG_I2C) |
+                          SM(1, SI_CONFIG_POS_SAMPLE) |
+                          SM(1, SI_CONFIG_INACTIVE_DATA) |
+                          SM(1, SI_CONFIG_INACTIVE_CLK) |
+                          SM(8, SI_CONFIG_DIVIDER));
+}
+
+static int ath10k_pci_read_eeprom(struct ath10k *ar, u16 addr, u8 *out)
+{
+       u32 reg;
+       int wait_limit;
+
+       /* set device select byte and for the read operation */
+       reg = QCA9887_EEPROM_SELECT_READ |
+             SM(addr, QCA9887_EEPROM_ADDR_LO) |
+             SM(addr >> 8, QCA9887_EEPROM_ADDR_HI);
+       ath10k_pci_write32(ar, SI_BASE_ADDRESS + SI_TX_DATA0_OFFSET, reg);
+
+       /* write transmit data, transfer length, and START bit */
+       ath10k_pci_write32(ar, SI_BASE_ADDRESS + SI_CS_OFFSET,
+                          SM(1, SI_CS_START) | SM(1, SI_CS_RX_CNT) |
+                          SM(4, SI_CS_TX_CNT));
+
+       /* wait max 1 sec */
+       wait_limit = 100000;
+
+       /* wait for SI_CS_DONE_INT */
+       do {
+               reg = ath10k_pci_read32(ar, SI_BASE_ADDRESS + SI_CS_OFFSET);
+               if (MS(reg, SI_CS_DONE_INT))
+                       break;
+
+               wait_limit--;
+               udelay(10);
+       } while (wait_limit > 0);
+
+       if (!MS(reg, SI_CS_DONE_INT)) {
+               ath10k_err(ar, "timeout while reading device EEPROM at %04x\n",
+                          addr);
+               return -ETIMEDOUT;
+       }
+
+       /* clear SI_CS_DONE_INT */
+       ath10k_pci_write32(ar, SI_BASE_ADDRESS + SI_CS_OFFSET, reg);
+
+       if (MS(reg, SI_CS_DONE_ERR)) {
+               ath10k_err(ar, "failed to read device EEPROM at %04x\n", addr);
+               return -EIO;
+       }
+
+       /* extract receive data */
+       reg = ath10k_pci_read32(ar, SI_BASE_ADDRESS + SI_RX_DATA0_OFFSET);
+       *out = reg;
+
+       return 0;
+}
+
+static int ath10k_pci_hif_fetch_cal_eeprom(struct ath10k *ar, void **data,
+                                          size_t *data_len)
+{
+       u8 *caldata = NULL;
+       size_t calsize, i;
+       int ret;
+
+       if (!QCA_REV_9887(ar))
+               return -EOPNOTSUPP;
+
+       calsize = ar->hw_params.cal_data_len;
+       caldata = kmalloc(calsize, GFP_KERNEL);
+       if (!caldata)
+               return -ENOMEM;
+
+       ath10k_pci_enable_eeprom(ar);
+
+       for (i = 0; i < calsize; i++) {
+               ret = ath10k_pci_read_eeprom(ar, i, &caldata[i]);
+               if (ret)
+                       goto err_free;
+       }
+
+       if (!ath10k_pci_validate_cal(caldata, calsize))
+               goto err_free;
+
+       *data = caldata;
+       *data_len = calsize;
+
+       return 0;
+
+err_free:
+       kfree(data);
+
+       return -EINVAL;
+}
+
 static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
        .tx_sg                  = ath10k_pci_hif_tx_sg,
        .diag_read              = ath10k_pci_hif_diag_read,
@@ -2596,6 +2734,7 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
        .suspend                = ath10k_pci_hif_suspend,
        .resume                 = ath10k_pci_hif_resume,
 #endif
+       .fetch_cal_eeprom       = ath10k_pci_hif_fetch_cal_eeprom,
 };
 
 /*