Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / drivers / net / wireless / wl12xx / boot.c
index 38f3e8b..b07f8b7 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 #include <linux/slab.h>
+#include <linux/wl12xx.h>
 
 #include "acx.h"
 #include "reg.h"
@@ -477,10 +478,14 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
                DISCONNECT_EVENT_COMPLETE_ID |
                RSSI_SNR_TRIGGER_0_EVENT_ID |
                PSPOLL_DELIVERY_FAILURE_EVENT_ID |
-               SOFT_GEMINI_SENSE_EVENT_ID;
+               SOFT_GEMINI_SENSE_EVENT_ID |
+               PERIODIC_SCAN_REPORT_EVENT_ID |
+               PERIODIC_SCAN_COMPLETE_EVENT_ID;
 
        if (wl->bss_type == BSS_TYPE_AP_BSS)
                wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID;
+       else
+               wl->event_mask |= DUMMY_PACKET_EVENT_ID;
 
        ret = wl1271_event_unmask(wl);
        if (ret < 0) {
@@ -520,24 +525,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)
+static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl)
 {
-       int ret = 0;
-       u32 tmp, clk, pause;
+       u16 spare_reg;
+
+       /* Mask bits [2] & [8:4] in the sys_clk_cfg register */
+       spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG);
+       if (spare_reg == 0xFFFF)
+               return -EFAULT;
+       spare_reg |= (BIT(3) | BIT(5) | BIT(6));
+       wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg);
+
+       /* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */
+       wl1271_top_reg_write(wl, SYS_CLK_CFG_REG,
+                            WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF);
+
+       /* Delay execution for 15msec, to let the HW settle */
+       mdelay(15);
+
+       return 0;
+}
+
+static bool wl128x_is_tcxo_valid(struct wl1271 *wl)
+{
+       u16 tcxo_detection;
+
+       tcxo_detection = wl1271_top_reg_read(wl, TCXO_CLK_DETECT_REG);
+       if (tcxo_detection & TCXO_DET_FAILED)
+               return false;
+
+       return true;
+}
+
+static bool wl128x_is_fref_valid(struct wl1271 *wl)
+{
+       u16 fref_detection;
+
+       fref_detection = wl1271_top_reg_read(wl, FREF_CLK_DETECT_REG);
+       if (fref_detection & FREF_CLK_DETECT_FAIL)
+               return false;
+
+       return true;
+}
+
+static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl)
+{
+       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);
+
+       return 0;
+}
+
+static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
+{
+       u16 spare_reg;
+       u16 pll_config;
+       u8 input_freq;
+
+       /* Mask bits [3:1] in the sys_clk_cfg register */
+       spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG);
+       if (spare_reg == 0xFFFF)
+               return -EFAULT;
+       spare_reg |= BIT(2);
+       wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg);
+
+       /* Handle special cases of the TCXO clock */
+       if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
+           wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
+               return wl128x_manually_configure_mcs_pll(wl);
+
+       /* Set the input frequency according to the selected clock source */
+       input_freq = (clk & 1) + 1;
+
+       pll_config = wl1271_top_reg_read(wl, MCS_PLL_CONFIG_REG);
+       if (pll_config == 0xFFFF)
+               return -EFAULT;
+       pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT);
+       pll_config |= MCS_PLL_ENABLE_HP;
+       wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config);
+
+       return 0;
+}
+
+/*
+ * 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_boot_clk(struct wl1271 *wl, int *selected_clock)
+{
+       u16 sys_clk_cfg;
+
+       /* For XTAL-only modes, FREF will be used after switching from TCXO */
+       if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
+           wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
+               if (!wl128x_switch_tcxo_to_fref(wl))
+                       return -EINVAL;
+               goto fref_clk;
+       }
+
+       /* Query the HW, to determine which clock source we should use */
+       sys_clk_cfg = wl1271_top_reg_read(wl, SYS_CLK_CFG_REG);
+       if (sys_clk_cfg == 0xFFFF)
+               return -EINVAL;
+       if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF)
+               goto fref_clk;
+
+       /* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */
+       if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
+           wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
+               if (!wl128x_switch_tcxo_to_fref(wl))
+                       return -EINVAL;
+               goto fref_clk;
+       }
+
+       /* TCXO clock is selected */
+       if (!wl128x_is_tcxo_valid(wl))
+               return -EINVAL;
+       *selected_clock = wl->tcxo_clock;
+       goto config_mcs_pll;
+
+fref_clk:
+       /* FREF clock is selected */
+       if (!wl128x_is_fref_valid(wl))
+               return -EINVAL;
+       *selected_clock = wl->ref_clock;
+
+config_mcs_pll:
+       return wl128x_configure_mcs_pll(wl, *selected_clock);
+}
+
+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 +707,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;
+       int selected_clock = -1;
+
+       if (wl->chip.id == CHIP_ID_1283_PG20) {
+               ret = wl128x_boot_clk(wl, &selected_clock);
+               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 +742,12 @@ 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) {
+               clk |= ((selected_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 +780,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, wl->conf.hci_io_ds);
+
        ret = wl1271_boot_upload_firmware(wl);
        if (ret < 0)
                goto out;