wl12xx: 1281/1283 support - New boot sequence
authorShahar Levi <shahar_levi@ti.com>
Sun, 6 Mar 2011 14:32:11 +0000 (16:32 +0200)
committerLuciano Coelho <coelho@ti.com>
Tue, 19 Apr 2011 13:48:58 +0000 (16:48 +0300)
Boot sequence support FREF clock and TCXO clock.
WL128x has two clocks input - TCXO and FREF.
TCXO is the main clock of the device, while FREF is used to sync
between the GPS and the cellular modem.
Auto-detection checks where TCXO is 32.736MHz or 16.368MHz, in that
case the FREF will be used as the WLAN/BT main clock.

[Use clock enumeration as defined in linux/wl12xx.h; remove
unnecessary else block in wl128x_switch_fref; remove unnecessary
change in main.c; remove some unnecessary debug prints and comments;
fix potential use of uninitialized value (pll_config) -- Luca]

Signed-off-by: Shahar Levi <shahar_levi@ti.com>
Reviewed-by: Luciano Coelho <coelho@ti.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
drivers/net/wireless/wl12xx/boot.c
drivers/net/wireless/wl12xx/sdio.c
drivers/net/wireless/wl12xx/sdio_test.c
drivers/net/wireless/wl12xx/spi.c
drivers/net/wireless/wl12xx/wl12xx.h

index 38f3e8ba2628c8d54f32c7dcfdc18ed8f0e38eca..9d742c1e75a920ee4d6269d0b82be882d32e7366 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 #include <linux/slab.h>
+#include <linux/wl12xx.h>
 
 #include "acx.h"
 #include "reg.h"
@@ -520,24 +521,159 @@ static void wl1271_boot_hw_version(struct wl1271 *wl)
                wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION;
 }
 
-/* uploads NVS and firmware */
-int wl1271_load_firmware(struct wl1271 *wl)
+/*
+ * WL128x has two clocks input - TCXO and FREF.
+ * TCXO is the main clock of the device, while FREF is used to sync
+ * between the GPS and the cellular modem.
+ * In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used
+ * as the WLAN/BT main clock.
+ */
+static int wl128x_switch_fref(struct wl1271 *wl, bool *is_ref_clk)
 {
-       int ret = 0;
-       u32 tmp, clk, pause;
+       u16 sys_clk_cfg_val;
+
+       /* if working on XTAL-only mode go directly to TCXO TO FREF SWITCH */
+       if ((wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL) ||
+           (wl->ref_clock == CONF_REF_CLK_26_M_XTAL))
+               return true;
+
+       /* Read clock source FREF or TCXO */
+       sys_clk_cfg_val = wl1271_top_reg_read(wl, SYS_CLK_CFG_REG);
+
+       if (sys_clk_cfg_val & PRCM_CM_EN_MUX_WLAN_FREF) {
+               /* if bit 3 is set - working with FREF clock */
+               wl1271_debug(DEBUG_BOOT, "working with FREF clock, skip"
+                            " to FREF");
+
+               *is_ref_clk = true;
+       } else {
+               /* if bit 3 is clear - working with TCXO clock */
+               wl1271_debug(DEBUG_BOOT, "working with TCXO clock");
+
+               /* TCXO to FREF switch, check TXCO clock config */
+               if ((wl->tcxo_clock != WL12XX_TCXOCLOCK_16_368) &&
+                   (wl->tcxo_clock != WL12XX_TCXOCLOCK_32_736)) {
+                       /*
+                        * not 16.368Mhz and not 32.736Mhz - skip to
+                        * configure ELP stage
+                        */
+                       wl1271_debug(DEBUG_BOOT, "NEW PLL ALGO:"
+                                    " TcxoRefClk=%d - not 16.368Mhz and not"
+                                    " 32.736Mhz - skip to configure ELP"
+                                    " stage", wl->tcxo_clock);
+
+                       *is_ref_clk = false;
+               } else {
+                       wl1271_debug(DEBUG_BOOT, "NEW PLL ALGO:"
+                                    "TcxoRefClk=%d - 16.368Mhz or 32.736Mhz"
+                                    " - TCXO to FREF switch",
+                                    wl->tcxo_clock);
+
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static int wl128x_boot_clk(struct wl1271 *wl, bool *is_ref_clk)
+{
+       if (wl128x_switch_fref(wl, is_ref_clk)) {
+               wl1271_debug(DEBUG_BOOT, "XTAL-only mode go directly to"
+                                        " TCXO TO FREF SWITCH");
+               /* TCXO to FREF switch - for PG2.0 */
+               wl1271_top_reg_write(wl, WL_SPARE_REG,
+                                    WL_SPARE_MASK_8526);
+
+               wl1271_top_reg_write(wl, SYS_CLK_CFG_REG,
+                       WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF);
+
+               *is_ref_clk = true;
+               mdelay(15);
+       }
+
+       /* Set bit 2 in spare register to avoid illegal access */
+       wl1271_top_reg_write(wl, WL_SPARE_REG, WL_SPARE_VAL);
+
+       /* working with TCXO clock */
+       if ((*is_ref_clk == false) &&
+           ((wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8) ||
+            (wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6))) {
+               wl1271_debug(DEBUG_BOOT, "16_8_M or 33_6_M TCXO detected");
+
+               /* Manually Configure MCS PLL settings PG2.0 Only */
+               wl1271_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL);
+               wl1271_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL);
+               wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG,
+                                    MCS_PLL_CONFIG_REG_VAL);
+       } else {
+               int pll_config;
+               u16 mcs_pll_config_val;
+
+               /*
+                * Configure MCS PLL settings to FREF Freq
+                * Set the values that determine the time elapse since the PLL's
+                * get their enable signal until the lock indication is set
+                */
+               wl1271_top_reg_write(wl, PLL_LOCK_COUNTERS_REG,
+                       PLL_LOCK_COUNTERS_COEX | PLL_LOCK_COUNTERS_MCS);
+
+               mcs_pll_config_val = wl1271_top_reg_read(wl,
+                                                MCS_PLL_CONFIG_REG);
+               /*
+                * Set the MCS PLL input frequency value according to the
+                * reference clock value detected/read
+                */
+               if (*is_ref_clk == false) {
+                       if ((wl->tcxo_clock == WL12XX_TCXOCLOCK_19_2) ||
+                           (wl->tcxo_clock == WL12XX_TCXOCLOCK_38_4))
+                               pll_config = 1;
+                       else if ((wl->tcxo_clock == WL12XX_TCXOCLOCK_26)
+                                ||
+                                (wl->tcxo_clock == WL12XX_TCXOCLOCK_52))
+                               pll_config = 2;
+                       else
+                               return -EINVAL;
+               } else {
+                       if ((wl->ref_clock == CONF_REF_CLK_19_2_E) ||
+                           (wl->ref_clock == CONF_REF_CLK_38_4_E))
+                               pll_config = 1;
+                       else if ((wl->ref_clock == CONF_REF_CLK_26_E) ||
+                                (wl->ref_clock == CONF_REF_CLK_52_E))
+                               pll_config = 2;
+                       else
+                               return -EINVAL;
+               }
+
+               mcs_pll_config_val |= (pll_config << (MCS_SEL_IN_FREQ_SHIFT)) &
+                                     (MCS_SEL_IN_FREQ_MASK);
+               wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG,
+                                    mcs_pll_config_val);
+       }
+
+       return 0;
+}
+
+static int wl127x_boot_clk(struct wl1271 *wl)
+{
+       u32 pause;
+       u32 clk;
 
        wl1271_boot_hw_version(wl);
 
-       if (wl->ref_clock == 0 || wl->ref_clock == 2 || wl->ref_clock == 4)
+       if (wl->ref_clock == CONF_REF_CLK_19_2_E ||
+           wl->ref_clock == CONF_REF_CLK_38_4_E ||
+           wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
                /* ref clk: 19.2/38.4/38.4-XTAL */
                clk = 0x3;
-       else if (wl->ref_clock == 1 || wl->ref_clock == 3)
+       else if (wl->ref_clock == CONF_REF_CLK_26_E ||
+                wl->ref_clock == CONF_REF_CLK_52_E)
                /* ref clk: 26/52 */
                clk = 0x5;
        else
                return -EINVAL;
 
-       if (wl->ref_clock != 0) {
+       if (wl->ref_clock != CONF_REF_CLK_19_2_E) {
                u16 val;
                /* Set clock type (open drain) */
                val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE);
@@ -567,6 +703,26 @@ int wl1271_load_firmware(struct wl1271 *wl)
        pause |= WU_COUNTER_PAUSE_VAL;
        wl1271_write32(wl, WU_COUNTER_PAUSE, pause);
 
+       return 0;
+}
+
+/* uploads NVS and firmware */
+int wl1271_load_firmware(struct wl1271 *wl)
+{
+       int ret = 0;
+       u32 tmp, clk;
+       bool is_ref_clk = false;
+
+       if (wl->chip.id == CHIP_ID_1283_PG20) {
+               ret = wl128x_boot_clk(wl, &is_ref_clk);
+               if (ret < 0)
+                       goto out;
+       } else {
+               ret = wl127x_boot_clk(wl);
+               if (ret < 0)
+                       goto out;
+       }
+
        /* Continue the ELP wake up sequence */
        wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
        udelay(500);
@@ -582,7 +738,15 @@ int wl1271_load_firmware(struct wl1271 *wl)
 
        wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
 
-       clk |= (wl->ref_clock << 1) << 4;
+       if (wl->chip.id == CHIP_ID_1283_PG20) {
+               if (is_ref_clk == false)
+                       clk |= ((wl->tcxo_clock & 0x3) << 1) << 4;
+               else
+                       clk |= ((wl->ref_clock & 0x3) << 1) << 4;
+       } else {
+               clk |= (wl->ref_clock << 1) << 4;
+       }
+
        wl1271_write32(wl, DRPW_SCRATCH_START, clk);
 
        wl1271_set_partition(wl, &part_table[PART_WORK]);
@@ -615,6 +779,9 @@ int wl1271_load_firmware(struct wl1271 *wl)
        /* WL1271: The reference driver skips steps 7 to 10 (jumps directly
         * to upload_fw) */
 
+       if (wl->chip.id == CHIP_ID_1283_PG20)
+               wl1271_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
+
        ret = wl1271_boot_upload_firmware(wl);
        if (ret < 0)
                goto out;
index 7491b3d8487a6b0dab5d386ea8bca67e838ef64a..f6dd3dea4f30f0dc908322a19c5f070cf75d08fd 100644 (file)
@@ -255,6 +255,7 @@ static int __devinit wl1271_probe(struct sdio_func *func,
 
        wl->irq = wlan_data->irq;
        wl->ref_clock = wlan_data->board_ref_clock;
+       wl->tcxo_clock = wlan_data->board_tcxo_clock;
 
        ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq,
                                   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
index e6e2ad63a1c1f02f8b053f46f1a705e81c9683e1..c02c870466154fa4d51dda1b747912044ee40b26 100644 (file)
@@ -421,6 +421,7 @@ static int __devinit wl1271_probe(struct sdio_func *func,
 
        wl->irq = wlan_data->irq;
        wl->ref_clock = wlan_data->board_ref_clock;
+       wl->tcxo_clock = wlan_data->board_tcxo_clock;
 
        sdio_set_drvdata(func, wl_test);
 
index bfb1171176ca500796d2edbc2cb2f29e321778e5..f5525361f2f58217f9c32fdfedc21e625330fc01 100644 (file)
@@ -401,6 +401,7 @@ static int __devinit wl1271_probe(struct spi_device *spi)
        }
 
        wl->ref_clock = pdata->board_ref_clock;
+       wl->tcxo_clock = pdata->board_tcxo_clock;
 
        wl->irq = spi->irq;
        if (wl->irq < 0) {
index e59f5392e909def6312b75ff3dee2797c0bda823..4b556776fd57d745fa4c7a1e89ca97d27ef13a21 100644 (file)
@@ -535,6 +535,7 @@ struct wl1271 {
        u8 ba_rx_bitmap;
 
        u32 block_size;
+       int tcxo_clock;
 
        /*
         * AP-mode - links indexed by HLID. The global and broadcast links