|
@@ -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;
|