*/
 
 #include <linux/slab.h>
+#include <linux/wl12xx.h>
 
 #include "acx.h"
 #include "reg.h"
                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);
        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);
 
        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]);
        /* 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;