wlcore: Refactor probe
[cascardo/linux.git] / drivers / net / wireless / ti / wl12xx / main.c
index d7dd3de..dadf1db 100644 (file)
 #include "../wlcore/acx.h"
 #include "../wlcore/tx.h"
 #include "../wlcore/rx.h"
-#include "../wlcore/io.h"
 #include "../wlcore/boot.h"
 
 #include "wl12xx.h"
 #include "reg.h"
 #include "cmd.h"
 #include "acx.h"
+#include "debugfs.h"
+
+static char *fref_param;
+static char *tcxo_param;
 
 static struct wlcore_conf wl12xx_conf = {
        .sg = {
@@ -212,7 +215,7 @@ static struct wlcore_conf wl12xx_conf = {
                .suspend_wake_up_event       = CONF_WAKE_UP_EVENT_N_DTIM,
                .suspend_listen_interval     = 3,
                .bcn_filt_mode               = CONF_BCN_FILT_MODE_ENABLED,
-               .bcn_filt_ie_count           = 2,
+               .bcn_filt_ie_count           = 3,
                .bcn_filt_ie = {
                        [0] = {
                                .ie          = WLAN_EID_CHANNEL_SWITCH,
@@ -222,9 +225,13 @@ static struct wlcore_conf wl12xx_conf = {
                                .ie          = WLAN_EID_HT_OPERATION,
                                .rule        = CONF_BCN_RULE_PASS_ON_CHANGE,
                        },
+                       [2] = {
+                               .ie          = WLAN_EID_ERP_INFO,
+                               .rule        = CONF_BCN_RULE_PASS_ON_CHANGE,
+                       },
                },
-               .synch_fail_thold            = 10,
-               .bss_lose_timeout            = 100,
+               .synch_fail_thold            = 12,
+               .bss_lose_timeout            = 400,
                .beacon_rx_timeout           = 10000,
                .broadcast_timeout           = 20000,
                .rx_broadcast_in_ps          = 1,
@@ -234,10 +241,11 @@ static struct wlcore_conf wl12xx_conf = {
                .psm_entry_retries           = 8,
                .psm_exit_retries            = 16,
                .psm_entry_nullfunc_retries  = 3,
-               .dynamic_ps_timeout          = 40,
+               .dynamic_ps_timeout          = 1500,
                .forced_ps                   = false,
                .keep_alive_interval         = 55000,
                .max_listen_interval         = 20,
+               .sta_sleep_auth              = WL1271_PSM_ILLEGAL,
        },
        .itrim = {
                .enable = false,
@@ -245,7 +253,7 @@ static struct wlcore_conf wl12xx_conf = {
        },
        .pm_config = {
                .host_clk_settling_time = 5000,
-               .host_fast_wakeup_support = false
+               .host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE,
        },
        .roam_trigger = {
                .trigger_pacing               = 1,
@@ -305,8 +313,8 @@ static struct wlcore_conf wl12xx_conf = {
                .swallow_period               = 5,
                .n_divider_fref_set_1         = 0xff,       /* default */
                .n_divider_fref_set_2         = 12,
-               .m_divider_fref_set_1         = 148,
-               .m_divider_fref_set_2         = 0xffff,     /* default */
+               .m_divider_fref_set_1         = 0xffff,
+               .m_divider_fref_set_2         = 148,        /* default */
                .coex_pll_stabilization_time  = 0xffffffff, /* default */
                .ldo_stabilization_time       = 0xffff,     /* default */
                .fm_disturbed_band_margin     = 0xff,       /* default */
@@ -581,19 +589,21 @@ static const int wl12xx_rtable[REG_TABLE_LEN] = {
 };
 
 /* TODO: maybe move to a new header file? */
-#define WL127X_FW_NAME_MULTI   "ti-connectivity/wl127x-fw-4-mr.bin"
-#define WL127X_FW_NAME_SINGLE  "ti-connectivity/wl127x-fw-4-sr.bin"
-#define WL127X_PLT_FW_NAME     "ti-connectivity/wl127x-fw-4-plt.bin"
+#define WL127X_FW_NAME_MULTI   "ti-connectivity/wl127x-fw-5-mr.bin"
+#define WL127X_FW_NAME_SINGLE  "ti-connectivity/wl127x-fw-5-sr.bin"
+#define WL127X_PLT_FW_NAME     "ti-connectivity/wl127x-fw-5-plt.bin"
 
-#define WL128X_FW_NAME_MULTI   "ti-connectivity/wl128x-fw-4-mr.bin"
-#define WL128X_FW_NAME_SINGLE  "ti-connectivity/wl128x-fw-4-sr.bin"
-#define WL128X_PLT_FW_NAME     "ti-connectivity/wl128x-fw-4-plt.bin"
+#define WL128X_FW_NAME_MULTI   "ti-connectivity/wl128x-fw-5-mr.bin"
+#define WL128X_FW_NAME_SINGLE  "ti-connectivity/wl128x-fw-5-sr.bin"
+#define WL128X_PLT_FW_NAME     "ti-connectivity/wl128x-fw-5-plt.bin"
 
-static void wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
+static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
 {
+       int ret;
+
        if (wl->chip.id != CHIP_ID_1283_PG20) {
                struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
-               struct wl1271_rx_mem_pool_addr rx_mem_addr;
+               struct wl127x_rx_mem_pool_addr rx_mem_addr;
 
                /*
                 * Choose the block we want to read
@@ -607,9 +617,13 @@ static void wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
 
                rx_mem_addr.addr_extra = rx_mem_addr.addr + 4;
 
-               wl1271_write(wl, WL1271_SLV_REG_DATA,
-                            &rx_mem_addr, sizeof(rx_mem_addr), false);
+               ret = wlcore_write(wl, WL1271_SLV_REG_DATA, &rx_mem_addr,
+                                  sizeof(rx_mem_addr), false);
+               if (ret < 0)
+                       return ret;
        }
+
+       return 0;
 }
 
 static int wl12xx_identify_chip(struct wl1271 *wl)
@@ -621,10 +635,9 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
                               wl->chip.id);
 
-               /* clear the alignment quirk, since we don't support it */
-               wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
-
-               wl->quirks |= WLCORE_QUIRK_LEGACY_NVS;
+               wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
+                             WLCORE_QUIRK_DUAL_PROBE_TMPL |
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
                wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL127X_FW_NAME_MULTI;
                memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
@@ -633,16 +646,18 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                /* read data preparation is only needed by wl127x */
                wl->ops->prepare_read = wl127x_prepare_read;
 
+               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER,
+                                     WL127X_MAJOR_VER, WL127X_SUBTYPE_VER,
+                                     WL127X_MINOR_VER);
                break;
 
        case CHIP_ID_1271_PG20:
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
                             wl->chip.id);
 
-               /* clear the alignment quirk, since we don't support it */
-               wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
-
-               wl->quirks |= WLCORE_QUIRK_LEGACY_NVS;
+               wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
+                             WLCORE_QUIRK_DUAL_PROBE_TMPL |
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
                wl->plt_fw_name = WL127X_PLT_FW_NAME;
                wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL127X_FW_NAME_MULTI;
@@ -652,6 +667,9 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                /* read data preparation is only needed by wl127x */
                wl->ops->prepare_read = wl127x_prepare_read;
 
+               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER,
+                                     WL127X_MAJOR_VER, WL127X_SUBTYPE_VER,
+                                     WL127X_MINOR_VER);
                break;
 
        case CHIP_ID_1283_PG20:
@@ -660,6 +678,15 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                wl->plt_fw_name = WL128X_PLT_FW_NAME;
                wl->sr_fw_name = WL128X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL128X_FW_NAME_MULTI;
+
+               /* wl128x requires TX blocksize alignment */
+               wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
+                             WLCORE_QUIRK_DUAL_PROBE_TMPL |
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
+
+               wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER,
+                                     WL128X_MAJOR_VER, WL128X_SUBTYPE_VER,
+                                     WL128X_MINOR_VER);
                break;
        case CHIP_ID_1283_PG10:
        default:
@@ -672,64 +699,95 @@ out:
        return ret;
 }
 
-static void wl12xx_top_reg_write(struct wl1271 *wl, int addr, u16 val)
+static int __must_check wl12xx_top_reg_write(struct wl1271 *wl, int addr,
+                                            u16 val)
 {
+       int ret;
+
        /* write address >> 1 + 0x30000 to OCP_POR_CTR */
        addr = (addr >> 1) + 0x30000;
-       wl1271_write32(wl, WL12XX_OCP_POR_CTR, addr);
+       ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr);
+       if (ret < 0)
+               goto out;
 
        /* write value to OCP_POR_WDATA */
-       wl1271_write32(wl, WL12XX_OCP_DATA_WRITE, val);
+       ret = wlcore_write32(wl, WL12XX_OCP_DATA_WRITE, val);
+       if (ret < 0)
+               goto out;
 
        /* write 1 to OCP_CMD */
-       wl1271_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE);
+       ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE);
+       if (ret < 0)
+               goto out;
+
+out:
+       return ret;
 }
 
-static u16 wl12xx_top_reg_read(struct wl1271 *wl, int addr)
+static int __must_check wl12xx_top_reg_read(struct wl1271 *wl, int addr,
+                                           u16 *out)
 {
        u32 val;
        int timeout = OCP_CMD_LOOP;
+       int ret;
 
        /* write address >> 1 + 0x30000 to OCP_POR_CTR */
        addr = (addr >> 1) + 0x30000;
-       wl1271_write32(wl, WL12XX_OCP_POR_CTR, addr);
+       ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr);
+       if (ret < 0)
+               return ret;
 
        /* write 2 to OCP_CMD */
-       wl1271_write32(wl, WL12XX_OCP_CMD, OCP_CMD_READ);
+       ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_READ);
+       if (ret < 0)
+               return ret;
 
        /* poll for data ready */
        do {
-               val = wl1271_read32(wl, WL12XX_OCP_DATA_READ);
+               ret = wlcore_read32(wl, WL12XX_OCP_DATA_READ, &val);
+               if (ret < 0)
+                       return ret;
        } while (!(val & OCP_READY_MASK) && --timeout);
 
        if (!timeout) {
                wl1271_warning("Top register access timed out.");
-               return 0xffff;
+               return -ETIMEDOUT;
        }
 
        /* check data status and return if OK */
-       if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK)
-               return val & 0xffff;
-       else {
+       if ((val & OCP_STATUS_MASK) != OCP_STATUS_OK) {
                wl1271_warning("Top register access returned error.");
-               return 0xffff;
+               return -EIO;
        }
+
+       if (out)
+               *out = val & 0xffff;
+
+       return 0;
 }
 
 static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl)
 {
        u16 spare_reg;
+       int ret;
 
        /* Mask bits [2] & [8:4] in the sys_clk_cfg register */
-       spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG);
+       ret = wl12xx_top_reg_read(wl, WL_SPARE_REG, &spare_reg);
+       if (ret < 0)
+               return ret;
+
        if (spare_reg == 0xFFFF)
                return -EFAULT;
        spare_reg |= (BIT(3) | BIT(5) | BIT(6));
-       wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
+       ret = wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
+       if (ret < 0)
+               return ret;
 
        /* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */
-       wl12xx_top_reg_write(wl, SYS_CLK_CFG_REG,
-                            WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF);
+       ret = wl12xx_top_reg_write(wl, SYS_CLK_CFG_REG,
+                                  WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF);
+       if (ret < 0)
+               return ret;
 
        /* Delay execution for 15msec, to let the HW settle */
        mdelay(15);
@@ -740,8 +798,12 @@ static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl)
 static bool wl128x_is_tcxo_valid(struct wl1271 *wl)
 {
        u16 tcxo_detection;
+       int ret;
+
+       ret = wl12xx_top_reg_read(wl, TCXO_CLK_DETECT_REG, &tcxo_detection);
+       if (ret < 0)
+               return false;
 
-       tcxo_detection = wl12xx_top_reg_read(wl, TCXO_CLK_DETECT_REG);
        if (tcxo_detection & TCXO_DET_FAILED)
                return false;
 
@@ -751,8 +813,12 @@ static bool wl128x_is_tcxo_valid(struct wl1271 *wl)
 static bool wl128x_is_fref_valid(struct wl1271 *wl)
 {
        u16 fref_detection;
+       int ret;
+
+       ret = wl12xx_top_reg_read(wl, FREF_CLK_DETECT_REG, &fref_detection);
+       if (ret < 0)
+               return false;
 
-       fref_detection = wl12xx_top_reg_read(wl, FREF_CLK_DETECT_REG);
        if (fref_detection & FREF_CLK_DETECT_FAIL)
                return false;
 
@@ -761,11 +827,21 @@ static bool wl128x_is_fref_valid(struct wl1271 *wl)
 
 static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl)
 {
-       wl12xx_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL);
-       wl12xx_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL);
-       wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, MCS_PLL_CONFIG_REG_VAL);
+       int ret;
 
-       return 0;
+       ret = wl12xx_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL);
+       if (ret < 0)
+               goto out;
+
+       ret = wl12xx_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL);
+       if (ret < 0)
+               goto out;
+
+       ret = wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG,
+                                  MCS_PLL_CONFIG_REG_VAL);
+
+out:
+       return ret;
 }
 
 static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
@@ -773,30 +849,40 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
        u16 spare_reg;
        u16 pll_config;
        u8 input_freq;
+       struct wl12xx_priv *priv = wl->priv;
+       int ret;
 
        /* Mask bits [3:1] in the sys_clk_cfg register */
-       spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG);
+       ret = wl12xx_top_reg_read(wl, WL_SPARE_REG, &spare_reg);
+       if (ret < 0)
+               return ret;
+
        if (spare_reg == 0xFFFF)
                return -EFAULT;
        spare_reg |= BIT(2);
-       wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
+       ret = wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
+       if (ret < 0)
+               return ret;
 
        /* Handle special cases of the TCXO clock */
-       if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
-           wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
+       if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
+           priv->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 = wl12xx_top_reg_read(wl, MCS_PLL_CONFIG_REG);
+       ret = wl12xx_top_reg_read(wl, MCS_PLL_CONFIG_REG, &pll_config);
+       if (ret < 0)
+               return ret;
+
        if (pll_config == 0xFFFF)
                return -EFAULT;
        pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT);
        pll_config |= MCS_PLL_ENABLE_HP;
-       wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config);
+       ret = wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config);
 
-       return 0;
+       return ret;
 }
 
 /*
@@ -808,26 +894,31 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
  */
 static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
 {
+       struct wl12xx_priv *priv = wl->priv;
        u16 sys_clk_cfg;
+       int ret;
 
        /* 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 (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
+           priv->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 = wl12xx_top_reg_read(wl, SYS_CLK_CFG_REG);
+       ret = wl12xx_top_reg_read(wl, SYS_CLK_CFG_REG, &sys_clk_cfg);
+       if (ret < 0)
+               return ret;
+
        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 (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
+           priv->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
                if (!wl128x_switch_tcxo_to_fref(wl))
                        return -EINVAL;
                goto fref_clk;
@@ -836,14 +927,14 @@ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
        /* TCXO clock is selected */
        if (!wl128x_is_tcxo_valid(wl))
                return -EINVAL;
-       *selected_clock = wl->tcxo_clock;
+       *selected_clock = priv->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;
+       *selected_clock = priv->ref_clock;
 
 config_mcs_pll:
        return wl128x_configure_mcs_pll(wl, *selected_clock);
@@ -851,69 +942,98 @@ config_mcs_pll:
 
 static int wl127x_boot_clk(struct wl1271 *wl)
 {
+       struct wl12xx_priv *priv = wl->priv;
        u32 pause;
        u32 clk;
+       int ret;
 
        if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3)
                wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION;
 
-       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)
+       if (priv->ref_clock == CONF_REF_CLK_19_2_E ||
+           priv->ref_clock == CONF_REF_CLK_38_4_E ||
+           priv->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 == CONF_REF_CLK_26_E ||
-                wl->ref_clock == CONF_REF_CLK_52_E)
+       else if (priv->ref_clock == CONF_REF_CLK_26_E ||
+                priv->ref_clock == CONF_REF_CLK_26_M_XTAL ||
+                priv->ref_clock == CONF_REF_CLK_52_E)
                /* ref clk: 26/52 */
                clk = 0x5;
        else
                return -EINVAL;
 
-       if (wl->ref_clock != CONF_REF_CLK_19_2_E) {
+       if (priv->ref_clock != CONF_REF_CLK_19_2_E) {
                u16 val;
                /* Set clock type (open drain) */
-               val = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE);
+               ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE, &val);
+               if (ret < 0)
+                       goto out;
+
                val &= FREF_CLK_TYPE_BITS;
-               wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val);
+               ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val);
+               if (ret < 0)
+                       goto out;
 
                /* Set clock pull mode (no pull) */
-               val = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL);
+               ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL, &val);
+               if (ret < 0)
+                       goto out;
+
                val |= NO_PULL;
-               wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val);
+               ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val);
+               if (ret < 0)
+                       goto out;
        } else {
                u16 val;
                /* Set clock polarity */
-               val = wl12xx_top_reg_read(wl, OCP_REG_CLK_POLARITY);
+               ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_POLARITY, &val);
+               if (ret < 0)
+                       goto out;
+
                val &= FREF_CLK_POLARITY_BITS;
                val |= CLK_REQ_OUTN_SEL;
-               wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val);
+               ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val);
+               if (ret < 0)
+                       goto out;
        }
 
-       wl1271_write32(wl, WL12XX_PLL_PARAMETERS, clk);
+       ret = wlcore_write32(wl, WL12XX_PLL_PARAMETERS, clk);
+       if (ret < 0)
+               goto out;
 
-       pause = wl1271_read32(wl, WL12XX_PLL_PARAMETERS);
+       ret = wlcore_read32(wl, WL12XX_PLL_PARAMETERS, &pause);
+       if (ret < 0)
+               goto out;
 
        wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause);
 
        pause &= ~(WU_COUNTER_PAUSE_VAL);
        pause |= WU_COUNTER_PAUSE_VAL;
-       wl1271_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause);
+       ret = wlcore_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause);
 
-       return 0;
+out:
+       return ret;
 }
 
 static int wl1271_boot_soft_reset(struct wl1271 *wl)
 {
        unsigned long timeout;
        u32 boot_data;
+       int ret = 0;
 
        /* perform soft reset */
-       wl1271_write32(wl, WL12XX_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT);
+       ret = wlcore_write32(wl, WL12XX_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT);
+       if (ret < 0)
+               goto out;
 
        /* SOFT_RESET is self clearing */
        timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME);
        while (1) {
-               boot_data = wl1271_read32(wl, WL12XX_SLV_SOFT_RESET);
+               ret = wlcore_read32(wl, WL12XX_SLV_SOFT_RESET, &boot_data);
+               if (ret < 0)
+                       goto out;
+
                wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data);
                if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0)
                        break;
@@ -929,16 +1049,20 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl)
        }
 
        /* disable Rx/Tx */
-       wl1271_write32(wl, WL12XX_ENABLE, 0x0);
+       ret = wlcore_write32(wl, WL12XX_ENABLE, 0x0);
+       if (ret < 0)
+               goto out;
 
        /* disable auto calibration on start*/
-       wl1271_write32(wl, WL12XX_SPARE_A2, 0xffff);
+       ret = wlcore_write32(wl, WL12XX_SPARE_A2, 0xffff);
 
-       return 0;
+out:
+       return ret;
 }
 
 static int wl12xx_pre_boot(struct wl1271 *wl)
 {
+       struct wl12xx_priv *priv = wl->priv;
        int ret = 0;
        u32 clk;
        int selected_clock = -1;
@@ -954,30 +1078,43 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
        }
 
        /* Continue the ELP wake up sequence */
-       wl1271_write32(wl, WL12XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
+       ret = wlcore_write32(wl, WL12XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
+       if (ret < 0)
+               goto out;
+
        udelay(500);
 
-       wlcore_set_partition(wl, &wl->ptable[PART_DRPW]);
+       ret = wlcore_set_partition(wl, &wl->ptable[PART_DRPW]);
+       if (ret < 0)
+               goto out;
 
        /* Read-modify-write DRPW_SCRATCH_START register (see next state)
           to be used by DRPw FW. The RTRIM value will be added by the FW
           before taking DRPw out of reset */
 
-       clk = wl1271_read32(wl, WL12XX_DRPW_SCRATCH_START);
+       ret = wlcore_read32(wl, WL12XX_DRPW_SCRATCH_START, &clk);
+       if (ret < 0)
+               goto out;
 
        wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
 
        if (wl->chip.id == CHIP_ID_1283_PG20)
                clk |= ((selected_clock & 0x3) << 1) << 4;
        else
-               clk |= (wl->ref_clock << 1) << 4;
+               clk |= (priv->ref_clock << 1) << 4;
 
-       wl1271_write32(wl, WL12XX_DRPW_SCRATCH_START, clk);
+       ret = wlcore_write32(wl, WL12XX_DRPW_SCRATCH_START, clk);
+       if (ret < 0)
+               goto out;
 
-       wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
+       ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
+       if (ret < 0)
+               goto out;
 
        /* Disable interrupts */
-       wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
+       ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
+       if (ret < 0)
+               goto out;
 
        ret = wl1271_boot_soft_reset(wl);
        if (ret < 0)
@@ -987,47 +1124,79 @@ out:
        return ret;
 }
 
-static void wl12xx_pre_upload(struct wl1271 *wl)
+static int wl12xx_pre_upload(struct wl1271 *wl)
 {
        u32 tmp;
+       u16 polarity;
+       int ret;
 
        /* write firmware's last address (ie. it's length) to
         * ACX_EEPROMLESS_IND_REG */
        wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG");
 
-       wl1271_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND);
+       ret = wlcore_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND);
+       if (ret < 0)
+               goto out;
 
-       tmp = wlcore_read_reg(wl, REG_CHIP_ID_B);
+       ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &tmp);
+       if (ret < 0)
+               goto out;
 
        wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
 
        /* 6. read the EEPROM parameters */
-       tmp = wl1271_read32(wl, WL12XX_SCR_PAD2);
+       ret = wlcore_read32(wl, WL12XX_SCR_PAD2, &tmp);
+       if (ret < 0)
+               goto out;
 
        /* WL1271: The reference driver skips steps 7 to 10 (jumps directly
         * to upload_fw) */
 
-       if (wl->chip.id == CHIP_ID_1283_PG20)
-               wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
-}
-
-static void wl12xx_enable_interrupts(struct wl1271 *wl)
-{
-       u32 polarity;
+       if (wl->chip.id == CHIP_ID_1283_PG20) {
+               ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
+               if (ret < 0)
+                       goto out;
+       }
 
-       polarity = wl12xx_top_reg_read(wl, OCP_REG_POLARITY);
+       /* polarity must be set before the firmware is loaded */
+       ret = wl12xx_top_reg_read(wl, OCP_REG_POLARITY, &polarity);
+       if (ret < 0)
+               goto out;
 
        /* We use HIGH polarity, so unset the LOW bit */
        polarity &= ~POLARITY_LOW;
-       wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity);
+       ret = wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity);
+
+out:
+       return ret;
+}
 
-       wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR);
+static int wl12xx_enable_interrupts(struct wl1271 *wl)
+{
+       int ret;
+
+       ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK,
+                              WL12XX_ACX_ALL_EVENTS_VECTOR);
+       if (ret < 0)
+               goto out;
 
        wlcore_enable_interrupts(wl);
-       wlcore_write_reg(wl, REG_INTERRUPT_MASK,
-                        WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
+       ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK,
+                              WL1271_ACX_INTR_ALL & ~(WL12XX_INTR_MASK));
+       if (ret < 0)
+               goto disable_interrupts;
+
+       ret = wlcore_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL);
+       if (ret < 0)
+               goto disable_interrupts;
 
-       wl1271_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL);
+       return ret;
+
+disable_interrupts:
+       wlcore_disable_interrupts(wl);
+
+out:
+       return ret;
 }
 
 static int wl12xx_boot(struct wl1271 *wl)
@@ -1042,7 +1211,9 @@ static int wl12xx_boot(struct wl1271 *wl)
        if (ret < 0)
                goto out;
 
-       wl12xx_pre_upload(wl);
+       ret = wl12xx_pre_upload(wl);
+       if (ret < 0)
+               goto out;
 
        ret = wlcore_boot_upload_firmware(wl);
        if (ret < 0)
@@ -1052,22 +1223,30 @@ static int wl12xx_boot(struct wl1271 *wl)
        if (ret < 0)
                goto out;
 
-       wl12xx_enable_interrupts(wl);
+       ret = wl12xx_enable_interrupts(wl);
 
 out:
        return ret;
 }
 
-static void wl12xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr,
+static int wl12xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr,
                               void *buf, size_t len)
 {
-       wl1271_write(wl, cmd_box_addr, buf, len, false);
-       wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_CMD);
+       int ret;
+
+       ret = wlcore_write(wl, cmd_box_addr, buf, len, false);
+       if (ret < 0)
+               return ret;
+
+       ret = wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_CMD);
+
+       return ret;
 }
 
-static void wl12xx_ack_event(struct wl1271 *wl)
+static int wl12xx_ack_event(struct wl1271 *wl)
 {
-       wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_EVENT_ACK);
+       return wlcore_write_reg(wl, REG_INTERRUPT_TRIG,
+                               WL12XX_INTR_TRIG_EVENT_ACK);
 }
 
 static u32 wl12xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks)
@@ -1147,12 +1326,13 @@ static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
        return data_len - sizeof(*desc) - desc->pad_len;
 }
 
-static void wl12xx_tx_delayed_compl(struct wl1271 *wl)
+static int wl12xx_tx_delayed_compl(struct wl1271 *wl)
 {
-       if (wl->fw_status->tx_results_counter == (wl->tx_results_count & 0xff))
-               return;
+       if (wl->fw_status_1->tx_results_counter ==
+           (wl->tx_results_count & 0xff))
+               return 0;
 
-       wl1271_tx_complete(wl);
+       return wlcore_tx_complete(wl);
 }
 
 static int wl12xx_hw_init(struct wl1271 *wl)
@@ -1165,6 +1345,14 @@ static int wl12xx_hw_init(struct wl1271 *wl)
                ret = wl128x_cmd_general_parms(wl);
                if (ret < 0)
                        goto out;
+
+               /*
+                * If we are in calibrator based auto detect then we got the FEM nr
+                * in wl->fem_manuf. No need to continue further
+                */
+               if (wl->plt_mode == PLT_FEM_DETECT)
+                       goto out;
+
                ret = wl128x_cmd_radio_parms(wl);
                if (ret < 0)
                        goto out;
@@ -1181,6 +1369,14 @@ static int wl12xx_hw_init(struct wl1271 *wl)
                ret = wl1271_cmd_general_parms(wl);
                if (ret < 0)
                        goto out;
+
+               /*
+                * If we are in calibrator based auto detect then we got the FEM nr
+                * in wl->fem_manuf. No need to continue further
+                */
+               if (wl->plt_mode == PLT_FEM_DETECT)
+                       goto out;
+
                ret = wl1271_cmd_radio_parms(wl);
                if (ret < 0)
                        goto out;
@@ -1253,45 +1449,154 @@ static bool wl12xx_mac_in_fuse(struct wl1271 *wl)
        return supported;
 }
 
-static void wl12xx_get_fuse_mac(struct wl1271 *wl)
+static int wl12xx_get_fuse_mac(struct wl1271 *wl)
 {
        u32 mac1, mac2;
+       int ret;
+
+       ret = wlcore_set_partition(wl, &wl->ptable[PART_DRPW]);
+       if (ret < 0)
+               goto out;
 
-       wlcore_set_partition(wl, &wl->ptable[PART_DRPW]);
+       ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1, &mac1);
+       if (ret < 0)
+               goto out;
 
-       mac1 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1);
-       mac2 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2);
+       ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2, &mac2);
+       if (ret < 0)
+               goto out;
 
        /* these are the two parts of the BD_ADDR */
        wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) +
                ((mac1 & 0xff000000) >> 24);
        wl->fuse_nic_addr = mac1 & 0xffffff;
 
-       wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
+       ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
+
+out:
+       return ret;
 }
 
-static s8 wl12xx_get_pg_ver(struct wl1271 *wl)
+static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
 {
-       u32 die_info;
+       u16 die_info;
+       int ret;
 
        if (wl->chip.id == CHIP_ID_1283_PG20)
-               die_info = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1);
+               ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1,
+                                         &die_info);
        else
-               die_info = wl12xx_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1);
+               ret = wl12xx_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1,
+                                         &die_info);
 
-       return (s8) (die_info & PG_VER_MASK) >> PG_VER_OFFSET;
+       if (ret >= 0 && ver)
+               *ver = (s8)((die_info & PG_VER_MASK) >> PG_VER_OFFSET);
+
+       return ret;
 }
 
-static void wl12xx_get_mac(struct wl1271 *wl)
+static int wl12xx_get_mac(struct wl1271 *wl)
 {
        if (wl12xx_mac_in_fuse(wl))
-               wl12xx_get_fuse_mac(wl);
+               return wl12xx_get_fuse_mac(wl);
+
+       return 0;
+}
+
+static void wl12xx_set_tx_desc_csum(struct wl1271 *wl,
+                                   struct wl1271_tx_hw_descr *desc,
+                                   struct sk_buff *skb)
+{
+       desc->wl12xx_reserved = 0;
+}
+
+static int wl12xx_plt_init(struct wl1271 *wl)
+{
+       int ret;
+
+       ret = wl->ops->boot(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wl->ops->hw_init(wl);
+       if (ret < 0)
+               goto out_irq_disable;
+
+       /*
+        * If we are in calibrator based auto detect then we got the FEM nr
+        * in wl->fem_manuf. No need to continue further
+        */
+       if (wl->plt_mode == PLT_FEM_DETECT)
+               goto out;
+
+       ret = wl1271_acx_init_mem_config(wl);
+       if (ret < 0)
+               goto out_irq_disable;
+
+       ret = wl12xx_acx_mem_cfg(wl);
+       if (ret < 0)
+               goto out_free_memmap;
+
+       /* Enable data path */
+       ret = wl1271_cmd_data_path(wl, 1);
+       if (ret < 0)
+               goto out_free_memmap;
+
+       /* Configure for CAM power saving (ie. always active) */
+       ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
+       if (ret < 0)
+               goto out_free_memmap;
+
+       /* configure PM */
+       ret = wl1271_acx_pm_config(wl);
+       if (ret < 0)
+               goto out_free_memmap;
+
+       goto out;
+
+out_free_memmap:
+       kfree(wl->target_mem_map);
+       wl->target_mem_map = NULL;
+
+out_irq_disable:
+       mutex_unlock(&wl->mutex);
+       /* Unlocking the mutex in the middle of handling is
+          inherently unsafe. In this case we deem it safe to do,
+          because we need to let any possibly pending IRQ out of
+          the system (and while we are WL1271_STATE_OFF the IRQ
+          work function will not do anything.) Also, any other
+          possible concurrent operations will fail due to the
+          current state, hence the wl1271 struct should be safe. */
+       wlcore_disable_interrupts(wl);
+       mutex_lock(&wl->mutex);
+out:
+       return ret;
+}
+
+static int wl12xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
+{
+       if (is_gem)
+               return WL12XX_TX_HW_BLOCK_GEM_SPARE;
+
+       return WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
 }
 
+static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+                         struct ieee80211_vif *vif,
+                         struct ieee80211_sta *sta,
+                         struct ieee80211_key_conf *key_conf)
+{
+       return wlcore_set_key(wl, cmd, vif, sta, key_conf);
+}
+
+static int wl12xx_setup(struct wl1271 *wl);
+
 static struct wlcore_ops wl12xx_ops = {
+       .setup                  = wl12xx_setup,
        .identify_chip          = wl12xx_identify_chip,
        .identify_fw            = wl12xx_identify_fw,
        .boot                   = wl12xx_boot,
+       .plt_init               = wl12xx_plt_init,
        .trigger_cmd            = wl12xx_trigger_cmd,
        .ack_event              = wl12xx_ack_event,
        .calc_tx_blocks         = wl12xx_calc_tx_blocks,
@@ -1306,6 +1611,13 @@ static struct wlcore_ops wl12xx_ops = {
        .sta_get_ap_rate_mask   = wl12xx_sta_get_ap_rate_mask,
        .get_pg_ver             = wl12xx_get_pg_ver,
        .get_mac                = wl12xx_get_mac,
+       .set_tx_desc_csum       = wl12xx_set_tx_desc_csum,
+       .set_rx_csum            = NULL,
+       .ap_get_mimo_wide_rate_mask = NULL,
+       .debugfs_init           = wl12xx_debugfs_add_files,
+       .get_spare_blocks       = wl12xx_get_spare_blocks,
+       .set_key                = wl12xx_set_key,
+       .pre_pkt_send           = NULL,
 };
 
 static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
@@ -1321,33 +1633,96 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
                },
 };
 
+static int wl12xx_setup(struct wl1271 *wl)
+{
+       struct wl12xx_priv *priv = wl->priv;
+       struct wl12xx_platform_data *pdata = wl->pdev->dev.platform_data;
+
+       wl->rtable = wl12xx_rtable;
+       wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
+       wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
+       wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;
+       wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
+       wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
+       wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
+       wl->fw_status_priv_len = 0;
+       wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics);
+       wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl12xx_ht_cap);
+       wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap);
+       wl12xx_conf_init(wl);
+
+       if (!fref_param) {
+               priv->ref_clock = pdata->board_ref_clock;
+       } else {
+               if (!strcmp(fref_param, "19.2"))
+                       priv->ref_clock = WL12XX_REFCLOCK_19;
+               else if (!strcmp(fref_param, "26"))
+                       priv->ref_clock = WL12XX_REFCLOCK_26;
+               else if (!strcmp(fref_param, "26x"))
+                       priv->ref_clock = WL12XX_REFCLOCK_26_XTAL;
+               else if (!strcmp(fref_param, "38.4"))
+                       priv->ref_clock = WL12XX_REFCLOCK_38;
+               else if (!strcmp(fref_param, "38.4x"))
+                       priv->ref_clock = WL12XX_REFCLOCK_38_XTAL;
+               else if (!strcmp(fref_param, "52"))
+                       priv->ref_clock = WL12XX_REFCLOCK_52;
+               else
+                       wl1271_error("Invalid fref parameter %s", fref_param);
+       }
+
+       if (!tcxo_param) {
+               priv->tcxo_clock = pdata->board_tcxo_clock;
+       } else {
+               if (!strcmp(tcxo_param, "19.2"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2;
+               else if (!strcmp(tcxo_param, "26"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_26;
+               else if (!strcmp(tcxo_param, "38.4"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_38_4;
+               else if (!strcmp(tcxo_param, "52"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_52;
+               else if (!strcmp(tcxo_param, "16.368"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_16_368;
+               else if (!strcmp(tcxo_param, "32.736"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_32_736;
+               else if (!strcmp(tcxo_param, "16.8"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_16_8;
+               else if (!strcmp(tcxo_param, "33.6"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_33_6;
+               else
+                       wl1271_error("Invalid tcxo parameter %s", tcxo_param);
+       }
+
+       return 0;
+}
+
 static int __devinit wl12xx_probe(struct platform_device *pdev)
 {
        struct wl1271 *wl;
        struct ieee80211_hw *hw;
-       struct wl12xx_priv *priv;
+       int ret;
 
-       hw = wlcore_alloc_hw(sizeof(*priv));
+       hw = wlcore_alloc_hw(sizeof(struct wl12xx_priv),
+                            WL12XX_AGGR_BUFFER_SIZE);
        if (IS_ERR(hw)) {
                wl1271_error("can't allocate hw");
-               return PTR_ERR(hw);
+               ret = PTR_ERR(hw);
+               goto out;
        }
 
        wl = hw->priv;
        wl->ops = &wl12xx_ops;
        wl->ptable = wl12xx_ptable;
-       wl->rtable = wl12xx_rtable;
-       wl->num_tx_desc = 16;
-       wl->normal_tx_spare = WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
-       wl->gem_tx_spare = WL12XX_TX_HW_BLOCK_GEM_SPARE;
-       wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
-       wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
-       wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
-       wl->fw_status_priv_len = 0;
-       memcpy(&wl->ht_cap, &wl12xx_ht_cap, sizeof(wl12xx_ht_cap));
-       wl12xx_conf_init(wl);
+       ret = wlcore_probe(wl, pdev);
+       if (ret)
+               goto out_free;
+
+       return ret;
 
-       return wlcore_probe(wl, pdev);
+out_free:
+       wlcore_free_hw(wl);
+out:
+       return ret;
 }
 
 static const struct platform_device_id wl12xx_id_table[] __devinitconst = {
@@ -1366,17 +1741,14 @@ static struct platform_driver wl12xx_driver = {
        }
 };
 
-static int __init wl12xx_init(void)
-{
-       return platform_driver_register(&wl12xx_driver);
-}
-module_init(wl12xx_init);
+module_platform_driver(wl12xx_driver);
 
-static void __exit wl12xx_exit(void)
-{
-       platform_driver_unregister(&wl12xx_driver);
-}
-module_exit(wl12xx_exit);
+module_param_named(fref, fref_param, charp, 0);
+MODULE_PARM_DESC(fref, "FREF clock: 19.2, 26, 26x, 38.4, 38.4x, 52");
+
+module_param_named(tcxo, tcxo_param, charp, 0);
+MODULE_PARM_DESC(tcxo,
+                "TCXO clock: 19.2, 26, 38.4, 52, 16.368, 32.736, 16.8, 33.6");
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");