summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ti/wl12xx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ti/wl12xx')
-rw-r--r--drivers/net/wireless/ti/wl12xx/Kconfig11
-rw-r--r--drivers/net/wireless/ti/wl12xx/Makefile3
-rw-r--r--drivers/net/wireless/ti/wl12xx/acx.c53
-rw-r--r--drivers/net/wireless/ti/wl12xx/acx.h273
-rw-r--r--drivers/net/wireless/ti/wl12xx/cmd.c323
-rw-r--r--drivers/net/wireless/ti/wl12xx/cmd.h132
-rw-r--r--drivers/net/wireless/ti/wl12xx/conf.h283
-rw-r--r--drivers/net/wireless/ti/wl12xx/debugfs.c243
-rw-r--r--drivers/net/wireless/ti/wl12xx/debugfs.h28
-rw-r--r--drivers/net/wireless/ti/wl12xx/event.c116
-rw-r--r--drivers/net/wireless/ti/wl12xx/event.h111
-rw-r--r--drivers/net/wireless/ti/wl12xx/main.c1977
-rw-r--r--drivers/net/wireless/ti/wl12xx/reg.h556
-rw-r--r--drivers/net/wireless/ti/wl12xx/scan.c512
-rw-r--r--drivers/net/wireless/ti/wl12xx/scan.h140
-rw-r--r--drivers/net/wireless/ti/wl12xx/wl12xx.h163
16 files changed, 4924 insertions, 0 deletions
diff --git a/drivers/net/wireless/ti/wl12xx/Kconfig b/drivers/net/wireless/ti/wl12xx/Kconfig
new file mode 100644
index 0000000..e3a18b2
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/Kconfig
@@ -0,0 +1,11 @@
+config WL12XX
+ depends on !KERNEL_3_2
+ tristate "TI wl12xx support"
+ depends on m
+ depends on MAC80211
+ select WLCORE
+ ---help---
+ This module adds support for wireless adapters based on TI wl1271,
+ wl1273, wl1281 and wl1283 chipsets. This module does *not* include
+ support for wl1251. For wl1251 support, use the separate homonymous
+ driver instead.
diff --git a/drivers/net/wireless/ti/wl12xx/Makefile b/drivers/net/wireless/ti/wl12xx/Makefile
new file mode 100644
index 0000000..8b231e1
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/Makefile
@@ -0,0 +1,3 @@
+wl12xx-objs = main.o cmd.o acx.o debugfs.o scan.o event.o
+
+obj-$(CPTCFG_WL12XX) += wl12xx.o
diff --git a/drivers/net/wireless/ti/wl12xx/acx.c b/drivers/net/wireless/ti/wl12xx/acx.c
new file mode 100644
index 0000000..bea06b2
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/acx.c
@@ -0,0 +1,53 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/acx.h"
+
+#include "acx.h"
+
+int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap)
+{
+ struct wl1271_acx_host_config_bitmap *bitmap_conf;
+ int ret;
+
+ bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL);
+ if (!bitmap_conf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap);
+
+ ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP,
+ bitmap_conf, sizeof(*bitmap_conf));
+ if (ret < 0) {
+ wl1271_warning("wl1271 bitmap config opt failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(bitmap_conf);
+
+ return ret;
+}
diff --git a/drivers/net/wireless/ti/wl12xx/acx.h b/drivers/net/wireless/ti/wl12xx/acx.h
new file mode 100644
index 0000000..2a26868
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/acx.h
@@ -0,0 +1,273 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 1998-2009, 2011 Texas Instruments. All rights reserved.
+ * Copyright (C) 2008-2010 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_ACX_H__
+#define __WL12XX_ACX_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/acx.h"
+
+#define WL12XX_ACX_ALL_EVENTS_VECTOR (WL1271_ACX_INTR_WATCHDOG | \
+ WL1271_ACX_INTR_INIT_COMPLETE | \
+ WL1271_ACX_INTR_EVENT_A | \
+ WL1271_ACX_INTR_EVENT_B | \
+ WL1271_ACX_INTR_CMD_COMPLETE | \
+ WL1271_ACX_INTR_HW_AVAILABLE | \
+ WL1271_ACX_INTR_DATA)
+
+#define WL12XX_INTR_MASK (WL1271_ACX_INTR_WATCHDOG | \
+ WL1271_ACX_INTR_EVENT_A | \
+ WL1271_ACX_INTR_EVENT_B | \
+ WL1271_ACX_INTR_HW_AVAILABLE | \
+ WL1271_ACX_INTR_DATA)
+
+struct wl1271_acx_host_config_bitmap {
+ struct acx_header header;
+
+ __le32 host_cfg_bitmap;
+} __packed;
+
+struct wl12xx_acx_tx_statistics {
+ __le32 internal_desc_overflow;
+} __packed;
+
+struct wl12xx_acx_rx_statistics {
+ __le32 out_of_mem;
+ __le32 hdr_overflow;
+ __le32 hw_stuck;
+ __le32 dropped;
+ __le32 fcs_err;
+ __le32 xfr_hint_trig;
+ __le32 path_reset;
+ __le32 reset_counter;
+} __packed;
+
+struct wl12xx_acx_dma_statistics {
+ __le32 rx_requested;
+ __le32 rx_errors;
+ __le32 tx_requested;
+ __le32 tx_errors;
+} __packed;
+
+struct wl12xx_acx_isr_statistics {
+ /* host command complete */
+ __le32 cmd_cmplt;
+
+ /* fiqisr() */
+ __le32 fiqs;
+
+ /* (INT_STS_ND & INT_TRIG_RX_HEADER) */
+ __le32 rx_headers;
+
+ /* (INT_STS_ND & INT_TRIG_RX_CMPLT) */
+ __le32 rx_completes;
+
+ /* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */
+ __le32 rx_mem_overflow;
+
+ /* (INT_STS_ND & INT_TRIG_S_RX_RDY) */
+ __le32 rx_rdys;
+
+ /* irqisr() */
+ __le32 irqs;
+
+ /* (INT_STS_ND & INT_TRIG_TX_PROC) */
+ __le32 tx_procs;
+
+ /* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */
+ __le32 decrypt_done;
+
+ /* (INT_STS_ND & INT_TRIG_DMA0) */
+ __le32 dma0_done;
+
+ /* (INT_STS_ND & INT_TRIG_DMA1) */
+ __le32 dma1_done;
+
+ /* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */
+ __le32 tx_exch_complete;
+
+ /* (INT_STS_ND & INT_TRIG_COMMAND) */
+ __le32 commands;
+
+ /* (INT_STS_ND & INT_TRIG_RX_PROC) */
+ __le32 rx_procs;
+
+ /* (INT_STS_ND & INT_TRIG_PM_802) */
+ __le32 hw_pm_mode_changes;
+
+ /* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */
+ __le32 host_acknowledges;
+
+ /* (INT_STS_ND & INT_TRIG_PM_PCI) */
+ __le32 pci_pm;
+
+ /* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */
+ __le32 wakeups;
+
+ /* (INT_STS_ND & INT_TRIG_LOW_RSSI) */
+ __le32 low_rssi;
+} __packed;
+
+struct wl12xx_acx_wep_statistics {
+ /* WEP address keys configured */
+ __le32 addr_key_count;
+
+ /* default keys configured */
+ __le32 default_key_count;
+
+ __le32 reserved;
+
+ /* number of times that WEP key not found on lookup */
+ __le32 key_not_found;
+
+ /* number of times that WEP key decryption failed */
+ __le32 decrypt_fail;
+
+ /* WEP packets decrypted */
+ __le32 packets;
+
+ /* WEP decrypt interrupts */
+ __le32 interrupt;
+} __packed;
+
+#define ACX_MISSED_BEACONS_SPREAD 10
+
+struct wl12xx_acx_pwr_statistics {
+ /* the amount of enters into power save mode (both PD & ELP) */
+ __le32 ps_enter;
+
+ /* the amount of enters into ELP mode */
+ __le32 elp_enter;
+
+ /* the amount of missing beacon interrupts to the host */
+ __le32 missing_bcns;
+
+ /* the amount of wake on host-access times */
+ __le32 wake_on_host;
+
+ /* the amount of wake on timer-expire */
+ __le32 wake_on_timer_exp;
+
+ /* the number of packets that were transmitted with PS bit set */
+ __le32 tx_with_ps;
+
+ /* the number of packets that were transmitted with PS bit clear */
+ __le32 tx_without_ps;
+
+ /* the number of received beacons */
+ __le32 rcvd_beacons;
+
+ /* the number of entering into PowerOn (power save off) */
+ __le32 power_save_off;
+
+ /* the number of entries into power save mode */
+ __le16 enable_ps;
+
+ /*
+ * the number of exits from power save, not including failed PS
+ * transitions
+ */
+ __le16 disable_ps;
+
+ /*
+ * the number of times the TSF counter was adjusted because
+ * of drift
+ */
+ __le32 fix_tsf_ps;
+
+ /* Gives statistics about the spread continuous missed beacons.
+ * The 16 LSB are dedicated for the PS mode.
+ * The 16 MSB are dedicated for the PS mode.
+ * cont_miss_bcns_spread[0] - single missed beacon.
+ * cont_miss_bcns_spread[1] - two continuous missed beacons.
+ * cont_miss_bcns_spread[2] - three continuous missed beacons.
+ * ...
+ * cont_miss_bcns_spread[9] - ten and more continuous missed beacons.
+ */
+ __le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD];
+
+ /* the number of beacons in awake mode */
+ __le32 rcvd_awake_beacons;
+} __packed;
+
+struct wl12xx_acx_mic_statistics {
+ __le32 rx_pkts;
+ __le32 calc_failure;
+} __packed;
+
+struct wl12xx_acx_aes_statistics {
+ __le32 encrypt_fail;
+ __le32 decrypt_fail;
+ __le32 encrypt_packets;
+ __le32 decrypt_packets;
+ __le32 encrypt_interrupt;
+ __le32 decrypt_interrupt;
+} __packed;
+
+struct wl12xx_acx_event_statistics {
+ __le32 heart_beat;
+ __le32 calibration;
+ __le32 rx_mismatch;
+ __le32 rx_mem_empty;
+ __le32 rx_pool;
+ __le32 oom_late;
+ __le32 phy_transmit_error;
+ __le32 tx_stuck;
+} __packed;
+
+struct wl12xx_acx_ps_statistics {
+ __le32 pspoll_timeouts;
+ __le32 upsd_timeouts;
+ __le32 upsd_max_sptime;
+ __le32 upsd_max_apturn;
+ __le32 pspoll_max_apturn;
+ __le32 pspoll_utilization;
+ __le32 upsd_utilization;
+} __packed;
+
+struct wl12xx_acx_rxpipe_statistics {
+ __le32 rx_prep_beacon_drop;
+ __le32 descr_host_int_trig_rx_data;
+ __le32 beacon_buffer_thres_host_int_trig_rx_data;
+ __le32 missed_beacon_host_int_trig_rx_data;
+ __le32 tx_xfr_host_int_trig_rx_data;
+} __packed;
+
+struct wl12xx_acx_statistics {
+ struct acx_header header;
+
+ struct wl12xx_acx_tx_statistics tx;
+ struct wl12xx_acx_rx_statistics rx;
+ struct wl12xx_acx_dma_statistics dma;
+ struct wl12xx_acx_isr_statistics isr;
+ struct wl12xx_acx_wep_statistics wep;
+ struct wl12xx_acx_pwr_statistics pwr;
+ struct wl12xx_acx_aes_statistics aes;
+ struct wl12xx_acx_mic_statistics mic;
+ struct wl12xx_acx_event_statistics event;
+ struct wl12xx_acx_ps_statistics ps;
+ struct wl12xx_acx_rxpipe_statistics rxpipe;
+} __packed;
+
+int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap);
+
+#endif /* __WL12XX_ACX_H__ */
diff --git a/drivers/net/wireless/ti/wl12xx/cmd.c b/drivers/net/wireless/ti/wl12xx/cmd.c
new file mode 100644
index 0000000..7485dba
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/cmd.c
@@ -0,0 +1,323 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+
+#include "wl12xx.h"
+#include "cmd.h"
+
+int wl1271_cmd_ext_radio_parms(struct wl1271 *wl)
+{
+ struct wl1271_ext_radio_parms_cmd *ext_radio_parms;
+ struct wl12xx_priv *priv = wl->priv;
+ struct wl12xx_conf_rf *rf = &priv->conf.rf;
+ int ret;
+
+ if (!wl->nvs)
+ return -ENODEV;
+
+ ext_radio_parms = kzalloc(sizeof(*ext_radio_parms), GFP_KERNEL);
+ if (!ext_radio_parms)
+ return -ENOMEM;
+
+ ext_radio_parms->test.id = TEST_CMD_INI_FILE_RF_EXTENDED_PARAM;
+
+ memcpy(ext_radio_parms->tx_per_channel_power_compensation_2,
+ rf->tx_per_channel_power_compensation_2,
+ CONF_TX_PWR_COMPENSATION_LEN_2);
+ memcpy(ext_radio_parms->tx_per_channel_power_compensation_5,
+ rf->tx_per_channel_power_compensation_5,
+ CONF_TX_PWR_COMPENSATION_LEN_5);
+
+ wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_EXT_RADIO_PARAM: ",
+ ext_radio_parms, sizeof(*ext_radio_parms));
+
+ ret = wl1271_cmd_test(wl, ext_radio_parms, sizeof(*ext_radio_parms), 0);
+ if (ret < 0)
+ wl1271_warning("TEST_CMD_INI_FILE_RF_EXTENDED_PARAM failed");
+
+ kfree(ext_radio_parms);
+ return ret;
+}
+
+int wl1271_cmd_general_parms(struct wl1271 *wl)
+{
+ struct wl1271_general_parms_cmd *gen_parms;
+ struct wl1271_ini_general_params *gp =
+ &((struct wl1271_nvs_file *)wl->nvs)->general_params;
+ struct wl12xx_priv *priv = wl->priv;
+ bool answer = false;
+ int ret;
+
+ if (!wl->nvs)
+ return -ENODEV;
+
+ if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
+ wl1271_warning("FEM index from INI out of bounds");
+ return -EINVAL;
+ }
+
+ gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL);
+ if (!gen_parms)
+ return -ENOMEM;
+
+ gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM;
+
+ memcpy(&gen_parms->general_params, gp, sizeof(*gp));
+
+ /* If we started in PLT FEM_DETECT mode, force auto detect */
+ if (wl->plt_mode == PLT_FEM_DETECT)
+ gen_parms->general_params.tx_bip_fem_auto_detect = true;
+
+ if (gen_parms->general_params.tx_bip_fem_auto_detect)
+ answer = true;
+
+ /* Override the REF CLK from the NVS with the one from platform data */
+ gen_parms->general_params.ref_clock = priv->ref_clock;
+
+ ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
+ if (ret < 0) {
+ wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
+ goto out;
+ }
+
+ gp->tx_bip_fem_manufacturer =
+ gen_parms->general_params.tx_bip_fem_manufacturer;
+
+ if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
+ wl1271_warning("FEM index from FW out of bounds");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* If we are in calibrator based fem auto detect - save fem nr */
+ if (wl->plt_mode == PLT_FEM_DETECT)
+ wl->fem_manuf = gp->tx_bip_fem_manufacturer;
+
+ wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n",
+ answer == false ?
+ "manual" :
+ wl->plt_mode == PLT_FEM_DETECT ?
+ "calibrator_fem_detect" :
+ "auto",
+ gp->tx_bip_fem_manufacturer);
+
+out:
+ kfree(gen_parms);
+ return ret;
+}
+
+int wl128x_cmd_general_parms(struct wl1271 *wl)
+{
+ struct wl128x_general_parms_cmd *gen_parms;
+ struct wl128x_ini_general_params *gp =
+ &((struct wl128x_nvs_file *)wl->nvs)->general_params;
+ struct wl12xx_priv *priv = wl->priv;
+ bool answer = false;
+ int ret;
+
+ if (!wl->nvs)
+ return -ENODEV;
+
+ if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
+ wl1271_warning("FEM index from ini out of bounds");
+ return -EINVAL;
+ }
+
+ gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL);
+ if (!gen_parms)
+ return -ENOMEM;
+
+ gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM;
+
+ memcpy(&gen_parms->general_params, gp, sizeof(*gp));
+
+ /* If we started in PLT FEM_DETECT mode, force auto detect */
+ if (wl->plt_mode == PLT_FEM_DETECT)
+ gen_parms->general_params.tx_bip_fem_auto_detect = true;
+
+ if (gen_parms->general_params.tx_bip_fem_auto_detect)
+ answer = true;
+
+ /* Replace REF and TCXO CLKs with the ones from platform data */
+ gen_parms->general_params.ref_clock = priv->ref_clock;
+ gen_parms->general_params.tcxo_ref_clock = priv->tcxo_clock;
+
+ ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
+ if (ret < 0) {
+ wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
+ goto out;
+ }
+
+ gp->tx_bip_fem_manufacturer =
+ gen_parms->general_params.tx_bip_fem_manufacturer;
+
+ if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
+ wl1271_warning("FEM index from FW out of bounds");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* If we are in calibrator based fem auto detect - save fem nr */
+ if (wl->plt_mode == PLT_FEM_DETECT)
+ wl->fem_manuf = gp->tx_bip_fem_manufacturer;
+
+ wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n",
+ answer == false ?
+ "manual" :
+ wl->plt_mode == PLT_FEM_DETECT ?
+ "calibrator_fem_detect" :
+ "auto",
+ gp->tx_bip_fem_manufacturer);
+
+out:
+ kfree(gen_parms);
+ return ret;
+}
+
+int wl1271_cmd_radio_parms(struct wl1271 *wl)
+{
+ struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *)wl->nvs;
+ struct wl1271_radio_parms_cmd *radio_parms;
+ struct wl1271_ini_general_params *gp = &nvs->general_params;
+ int ret, fem_idx;
+
+ if (!wl->nvs)
+ return -ENODEV;
+
+ radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL);
+ if (!radio_parms)
+ return -ENOMEM;
+
+ radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM;
+
+ fem_idx = WL12XX_FEM_TO_NVS_ENTRY(gp->tx_bip_fem_manufacturer);
+
+ /* 2.4GHz parameters */
+ memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2,
+ sizeof(struct wl1271_ini_band_params_2));
+ memcpy(&radio_parms->dyn_params_2,
+ &nvs->dyn_radio_params_2[fem_idx].params,
+ sizeof(struct wl1271_ini_fem_params_2));
+
+ /* 5GHz parameters */
+ memcpy(&radio_parms->static_params_5,
+ &nvs->stat_radio_params_5,
+ sizeof(struct wl1271_ini_band_params_5));
+ memcpy(&radio_parms->dyn_params_5,
+ &nvs->dyn_radio_params_5[fem_idx].params,
+ sizeof(struct wl1271_ini_fem_params_5));
+
+ wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
+ radio_parms, sizeof(*radio_parms));
+
+ ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0);
+ if (ret < 0)
+ wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed");
+
+ kfree(radio_parms);
+ return ret;
+}
+
+int wl128x_cmd_radio_parms(struct wl1271 *wl)
+{
+ struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs;
+ struct wl128x_radio_parms_cmd *radio_parms;
+ struct wl128x_ini_general_params *gp = &nvs->general_params;
+ int ret, fem_idx;
+
+ if (!wl->nvs)
+ return -ENODEV;
+
+ radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL);
+ if (!radio_parms)
+ return -ENOMEM;
+
+ radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM;
+
+ fem_idx = WL12XX_FEM_TO_NVS_ENTRY(gp->tx_bip_fem_manufacturer);
+
+ /* 2.4GHz parameters */
+ memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2,
+ sizeof(struct wl128x_ini_band_params_2));
+ memcpy(&radio_parms->dyn_params_2,
+ &nvs->dyn_radio_params_2[fem_idx].params,
+ sizeof(struct wl128x_ini_fem_params_2));
+
+ /* 5GHz parameters */
+ memcpy(&radio_parms->static_params_5,
+ &nvs->stat_radio_params_5,
+ sizeof(struct wl128x_ini_band_params_5));
+ memcpy(&radio_parms->dyn_params_5,
+ &nvs->dyn_radio_params_5[fem_idx].params,
+ sizeof(struct wl128x_ini_fem_params_5));
+
+ radio_parms->fem_vendor_and_options = nvs->fem_vendor_and_options;
+
+ wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
+ radio_parms, sizeof(*radio_parms));
+
+ ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0);
+ if (ret < 0)
+ wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed");
+
+ kfree(radio_parms);
+ return ret;
+}
+
+int wl12xx_cmd_channel_switch(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif,
+ struct ieee80211_channel_switch *ch_switch)
+{
+ struct wl12xx_cmd_channel_switch *cmd;
+ int ret;
+
+ wl1271_debug(DEBUG_ACX, "cmd channel switch");
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->role_id = wlvif->role_id;
+ cmd->channel = ch_switch->chandef.chan->hw_value;
+ cmd->switch_time = ch_switch->count;
+ cmd->stop_tx = ch_switch->block_tx;
+
+ /* FIXME: control from mac80211 in the future */
+ /* Enable TX on the target channel */
+ cmd->post_switch_tx_disable = 0;
+
+ ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ wl1271_error("failed to send channel switch command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
diff --git a/drivers/net/wireless/ti/wl12xx/cmd.h b/drivers/net/wireless/ti/wl12xx/cmd.h
new file mode 100644
index 0000000..32cbad5
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/cmd.h
@@ -0,0 +1,132 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 1998-2009, 2011 Texas Instruments. All rights reserved.
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_CMD_H__
+#define __WL12XX_CMD_H__
+
+#include "conf.h"
+
+#define TEST_CMD_INI_FILE_RADIO_PARAM 0x19
+#define TEST_CMD_INI_FILE_GENERAL_PARAM 0x1E
+
+struct wl1271_general_parms_cmd {
+ struct wl1271_cmd_header header;
+
+ struct wl1271_cmd_test_header test;
+
+ struct wl1271_ini_general_params general_params;
+
+ u8 sr_debug_table[WL1271_INI_MAX_SMART_REFLEX_PARAM];
+ u8 sr_sen_n_p;
+ u8 sr_sen_n_p_gain;
+ u8 sr_sen_nrn;
+ u8 sr_sen_prn;
+ u8 padding[3];
+} __packed;
+
+struct wl128x_general_parms_cmd {
+ struct wl1271_cmd_header header;
+
+ struct wl1271_cmd_test_header test;
+
+ struct wl128x_ini_general_params general_params;
+
+ u8 sr_debug_table[WL1271_INI_MAX_SMART_REFLEX_PARAM];
+ u8 sr_sen_n_p;
+ u8 sr_sen_n_p_gain;
+ u8 sr_sen_nrn;
+ u8 sr_sen_prn;
+ u8 padding[3];
+} __packed;
+
+struct wl1271_radio_parms_cmd {
+ struct wl1271_cmd_header header;
+
+ struct wl1271_cmd_test_header test;
+
+ /* Static radio parameters */
+ struct wl1271_ini_band_params_2 static_params_2;
+ struct wl1271_ini_band_params_5 static_params_5;
+
+ /* Dynamic radio parameters */
+ struct wl1271_ini_fem_params_2 dyn_params_2;
+ u8 padding2;
+ struct wl1271_ini_fem_params_5 dyn_params_5;
+ u8 padding3[2];
+} __packed;
+
+struct wl128x_radio_parms_cmd {
+ struct wl1271_cmd_header header;
+
+ struct wl1271_cmd_test_header test;
+
+ /* Static radio parameters */
+ struct wl128x_ini_band_params_2 static_params_2;
+ struct wl128x_ini_band_params_5 static_params_5;
+
+ u8 fem_vendor_and_options;
+
+ /* Dynamic radio parameters */
+ struct wl128x_ini_fem_params_2 dyn_params_2;
+ u8 padding2;
+ struct wl128x_ini_fem_params_5 dyn_params_5;
+} __packed;
+
+#define TEST_CMD_INI_FILE_RF_EXTENDED_PARAM 0x26
+
+struct wl1271_ext_radio_parms_cmd {
+ struct wl1271_cmd_header header;
+
+ struct wl1271_cmd_test_header test;
+
+ u8 tx_per_channel_power_compensation_2[CONF_TX_PWR_COMPENSATION_LEN_2];
+ u8 tx_per_channel_power_compensation_5[CONF_TX_PWR_COMPENSATION_LEN_5];
+ u8 padding[3];
+} __packed;
+
+struct wl12xx_cmd_channel_switch {
+ struct wl1271_cmd_header header;
+
+ u8 role_id;
+
+ /* The new serving channel */
+ u8 channel;
+ /* Relative time of the serving channel switch in TBTT units */
+ u8 switch_time;
+ /* Stop the role TX, should expect it after radar detection */
+ u8 stop_tx;
+ /* The target channel tx status 1-stopped 0-open*/
+ u8 post_switch_tx_disable;
+
+ u8 padding[3];
+} __packed;
+
+int wl1271_cmd_general_parms(struct wl1271 *wl);
+int wl128x_cmd_general_parms(struct wl1271 *wl);
+int wl1271_cmd_radio_parms(struct wl1271 *wl);
+int wl128x_cmd_radio_parms(struct wl1271 *wl);
+int wl1271_cmd_ext_radio_parms(struct wl1271 *wl);
+int wl12xx_cmd_channel_switch(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif,
+ struct ieee80211_channel_switch *ch_switch);
+
+#endif /* __WL12XX_CMD_H__ */
diff --git a/drivers/net/wireless/ti/wl12xx/conf.h b/drivers/net/wireless/ti/wl12xx/conf.h
new file mode 100644
index 0000000..a606ba9
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/conf.h
@@ -0,0 +1,283 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_CONF_H__
+#define __WL12XX_CONF_H__
+
+/* these are number of channels on the band divided by two, rounded up */
+#define CONF_TX_PWR_COMPENSATION_LEN_2 7
+#define CONF_TX_PWR_COMPENSATION_LEN_5 18
+
+struct wl12xx_conf_rf {
+ /*
+ * Per channel power compensation for 2.4GHz
+ *
+ * Range: s8
+ */
+ u8 tx_per_channel_power_compensation_2[CONF_TX_PWR_COMPENSATION_LEN_2];
+
+ /*
+ * Per channel power compensation for 5GHz
+ *
+ * Range: s8
+ */
+ u8 tx_per_channel_power_compensation_5[CONF_TX_PWR_COMPENSATION_LEN_5];
+};
+
+struct wl12xx_priv_conf {
+ struct wl12xx_conf_rf rf;
+ struct conf_memory_settings mem_wl127x;
+};
+
+enum wl12xx_sg_params {
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT master basic rate
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_MASTER_MIN_BR = 0,
+ WL12XX_CONF_SG_ACL_BT_MASTER_MAX_BR,
+
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT slave basic rate
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_BR,
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_BR,
+
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT master EDR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_MASTER_MIN_EDR,
+ WL12XX_CONF_SG_ACL_BT_MASTER_MAX_EDR,
+
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT slave EDR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_EDR,
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_EDR,
+
+ /*
+ * The maximum time WLAN can gain the antenna
+ * in WLAN PSM / BT master/slave BR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_BR,
+ WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_BR,
+
+ /*
+ * The maximum time WLAN can gain the antenna
+ * in WLAN PSM / BT master/slave EDR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_EDR,
+
+ /* TODO: explain these values */
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR,
+
+ WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR,
+ WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_BR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_EDR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR,
+
+ /*
+ * Compensation percentage of probe requests when scan initiated
+ * during BT voice/ACL link.
+ *
+ * Range: 0 - 255 (%)
+ */
+ WL12XX_CONF_SG_AUTO_SCAN_PROBE_REQ,
+
+ /*
+ * Compensation percentage of probe requests when active scan initiated
+ * during BT voice
+ *
+ * Range: 0 - 255 (%)
+ */
+ WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3,
+
+ /*
+ * Compensation percentage of WLAN active scan window if initiated
+ * during BT A2DP
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP,
+
+ /*
+ * Compensation percentage of WLAN passive scan window if initiated
+ * during BT A2DP BR
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_BR,
+
+ /*
+ * Compensation percentage of WLAN passive scan window if initiated
+ * during BT A2DP EDR
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_EDR,
+
+ /*
+ * Compensation percentage of WLAN passive scan window if initiated
+ * during BT voice
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_HV3,
+
+ /* TODO: explain these values */
+ WL12XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN,
+ WL12XX_CONF_SG_BCN_HV3_COLL_THR_IN_PASSIVE_SCAN,
+ WL12XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN,
+
+ /*
+ * Defines whether the SG will force WLAN host to enter/exit PSM
+ *
+ * Range: 1 - SG can force, 0 - host handles PSM
+ */
+ WL12XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO,
+
+ /*
+ * Defines antenna configuration (single/dual antenna)
+ *
+ * Range: 0 - single antenna, 1 - dual antenna
+ */
+ WL12XX_CONF_SG_ANTENNA_CONFIGURATION,
+
+ /*
+ * The threshold (percent) of max consecutive beacon misses before
+ * increasing priority of beacon reception.
+ *
+ * Range: 0 - 100 (%)
+ */
+ WL12XX_CONF_SG_BEACON_MISS_PERCENT,
+
+ /*
+ * Protection time of the DHCP procedure.
+ *
+ * Range: 0 - 100000 (ms)
+ */
+ WL12XX_CONF_SG_DHCP_TIME,
+
+ /*
+ * RX guard time before the beginning of a new BT voice frame during
+ * which no new WLAN trigger frame is transmitted.
+ *
+ * Range: 0 - 100000 (us)
+ */
+ WL12XX_CONF_SG_RXT,
+
+ /*
+ * TX guard time before the beginning of a new BT voice frame during
+ * which no new WLAN frame is transmitted.
+ *
+ * Range: 0 - 100000 (us)
+ */
+ WL12XX_CONF_SG_TXT,
+
+ /*
+ * Enable adaptive RXT/TXT algorithm. If disabled, the host values
+ * will be utilized.
+ *
+ * Range: 0 - disable, 1 - enable
+ */
+ WL12XX_CONF_SG_ADAPTIVE_RXT_TXT,
+
+ /* TODO: explain this value */
+ WL12XX_CONF_SG_GENERAL_USAGE_BIT_MAP,
+
+ /*
+ * Number of consecutive BT voice frames not interrupted by WLAN
+ *
+ * Range: 0 - 100
+ */
+ WL12XX_CONF_SG_HV3_MAX_SERVED,
+
+ /*
+ * The used WLAN legacy service period during active BT ACL link
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_PS_POLL_TIMEOUT,
+
+ /*
+ * The used WLAN UPSD service period during active BT ACL link
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_UPSD_TIMEOUT,
+
+ WL12XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD,
+ WL12XX_CONF_SG_STA_RX_WINDOW_AFTER_DTIM,
+ WL12XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME,
+
+ /* AP params */
+ WL12XX_CONF_AP_BEACON_MISS_TX,
+ WL12XX_CONF_AP_RX_WINDOW_AFTER_BEACON,
+ WL12XX_CONF_AP_BEACON_WINDOW_INTERVAL,
+ WL12XX_CONF_AP_CONNECTION_PROTECTION_TIME,
+ WL12XX_CONF_AP_BT_ACL_VAL_BT_SERVE_TIME,
+ WL12XX_CONF_AP_BT_ACL_VAL_WL_SERVE_TIME,
+
+ /* CTS Diluting params */
+ WL12XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH,
+ WL12XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER,
+
+ WL12XX_CONF_SG_TEMP_PARAM_1,
+ WL12XX_CONF_SG_TEMP_PARAM_2,
+ WL12XX_CONF_SG_TEMP_PARAM_3,
+ WL12XX_CONF_SG_TEMP_PARAM_4,
+ WL12XX_CONF_SG_TEMP_PARAM_5,
+ WL12XX_CONF_SG_TEMP_PARAM_6,
+ WL12XX_CONF_SG_TEMP_PARAM_7,
+ WL12XX_CONF_SG_TEMP_PARAM_8,
+ WL12XX_CONF_SG_TEMP_PARAM_9,
+ WL12XX_CONF_SG_TEMP_PARAM_10,
+
+ WL12XX_CONF_SG_PARAMS_MAX,
+ WL12XX_CONF_SG_PARAMS_ALL = 0xff
+};
+
+#endif /* __WL12XX_CONF_H__ */
diff --git a/drivers/net/wireless/ti/wl12xx/debugfs.c b/drivers/net/wireless/ti/wl12xx/debugfs.c
new file mode 100644
index 0000000..0521cbf
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/debugfs.c
@@ -0,0 +1,243 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/debugfs.h"
+#include "../wlcore/wlcore.h"
+
+#include "wl12xx.h"
+#include "acx.h"
+#include "debugfs.h"
+
+#define WL12XX_DEBUGFS_FWSTATS_FILE(a, b, c) \
+ DEBUGFS_FWSTATS_FILE(a, b, c, wl12xx_acx_statistics)
+
+WL12XX_DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, out_of_mem, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, hw_stuck, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, dropped, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, fcs_err, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, path_reset, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, reset_counter, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_requested, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_errors, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_requested, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_errors, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, fiqs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_headers, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_rdys, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_procs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, decrypt_done, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma0_done, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma1_done, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, commands, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_procs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, pci_pm, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, wakeups, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, low_rssi, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, addr_key_count, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, default_key_count, "%u");
+/* skipping wep.reserved */
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, key_not_found, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, packets, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, interrupt, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, ps_enter, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, elp_enter, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, power_save_off, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, enable_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, disable_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, "%u");
+/* skipping cont_miss_bcns_spread for now */
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(mic, rx_pkts, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(mic, calc_failure, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(event, heart_beat, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, calibration, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_pool, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, oom_late, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, tx_stuck, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data,
+ "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, "%u");
+
+int wl12xx_debugfs_add_files(struct wl1271 *wl,
+ struct dentry *rootdir)
+{
+ int ret = 0;
+ struct dentry *entry, *stats, *moddir;
+
+ moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir);
+ if (!moddir || IS_ERR(moddir)) {
+ entry = moddir;
+ goto err;
+ }
+
+ stats = debugfs_create_dir("fw_stats", moddir);
+ if (!stats || IS_ERR(stats)) {
+ entry = stats;
+ goto err;
+ }
+
+ DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow);
+
+ DEBUGFS_FWSTATS_ADD(rx, out_of_mem);
+ DEBUGFS_FWSTATS_ADD(rx, hdr_overflow);
+ DEBUGFS_FWSTATS_ADD(rx, hw_stuck);
+ DEBUGFS_FWSTATS_ADD(rx, dropped);
+ DEBUGFS_FWSTATS_ADD(rx, fcs_err);
+ DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig);
+ DEBUGFS_FWSTATS_ADD(rx, path_reset);
+ DEBUGFS_FWSTATS_ADD(rx, reset_counter);
+
+ DEBUGFS_FWSTATS_ADD(dma, rx_requested);
+ DEBUGFS_FWSTATS_ADD(dma, rx_errors);
+ DEBUGFS_FWSTATS_ADD(dma, tx_requested);
+ DEBUGFS_FWSTATS_ADD(dma, tx_errors);
+
+ DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt);
+ DEBUGFS_FWSTATS_ADD(isr, fiqs);
+ DEBUGFS_FWSTATS_ADD(isr, rx_headers);
+ DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow);
+ DEBUGFS_FWSTATS_ADD(isr, rx_rdys);
+ DEBUGFS_FWSTATS_ADD(isr, irqs);
+ DEBUGFS_FWSTATS_ADD(isr, tx_procs);
+ DEBUGFS_FWSTATS_ADD(isr, decrypt_done);
+ DEBUGFS_FWSTATS_ADD(isr, dma0_done);
+ DEBUGFS_FWSTATS_ADD(isr, dma1_done);
+ DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete);
+ DEBUGFS_FWSTATS_ADD(isr, commands);
+ DEBUGFS_FWSTATS_ADD(isr, rx_procs);
+ DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes);
+ DEBUGFS_FWSTATS_ADD(isr, host_acknowledges);
+ DEBUGFS_FWSTATS_ADD(isr, pci_pm);
+ DEBUGFS_FWSTATS_ADD(isr, wakeups);
+ DEBUGFS_FWSTATS_ADD(isr, low_rssi);
+
+ DEBUGFS_FWSTATS_ADD(wep, addr_key_count);
+ DEBUGFS_FWSTATS_ADD(wep, default_key_count);
+ /* skipping wep.reserved */
+ DEBUGFS_FWSTATS_ADD(wep, key_not_found);
+ DEBUGFS_FWSTATS_ADD(wep, decrypt_fail);
+ DEBUGFS_FWSTATS_ADD(wep, packets);
+ DEBUGFS_FWSTATS_ADD(wep, interrupt);
+
+ DEBUGFS_FWSTATS_ADD(pwr, ps_enter);
+ DEBUGFS_FWSTATS_ADD(pwr, elp_enter);
+ DEBUGFS_FWSTATS_ADD(pwr, missing_bcns);
+ DEBUGFS_FWSTATS_ADD(pwr, wake_on_host);
+ DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp);
+ DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps);
+ DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps);
+ DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons);
+ DEBUGFS_FWSTATS_ADD(pwr, power_save_off);
+ DEBUGFS_FWSTATS_ADD(pwr, enable_ps);
+ DEBUGFS_FWSTATS_ADD(pwr, disable_ps);
+ DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps);
+ /* skipping cont_miss_bcns_spread for now */
+ DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons);
+
+ DEBUGFS_FWSTATS_ADD(mic, rx_pkts);
+ DEBUGFS_FWSTATS_ADD(mic, calc_failure);
+
+ DEBUGFS_FWSTATS_ADD(aes, encrypt_fail);
+ DEBUGFS_FWSTATS_ADD(aes, decrypt_fail);
+ DEBUGFS_FWSTATS_ADD(aes, encrypt_packets);
+ DEBUGFS_FWSTATS_ADD(aes, decrypt_packets);
+ DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt);
+ DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt);
+
+ DEBUGFS_FWSTATS_ADD(event, heart_beat);
+ DEBUGFS_FWSTATS_ADD(event, calibration);
+ DEBUGFS_FWSTATS_ADD(event, rx_mismatch);
+ DEBUGFS_FWSTATS_ADD(event, rx_mem_empty);
+ DEBUGFS_FWSTATS_ADD(event, rx_pool);
+ DEBUGFS_FWSTATS_ADD(event, oom_late);
+ DEBUGFS_FWSTATS_ADD(event, phy_transmit_error);
+ DEBUGFS_FWSTATS_ADD(event, tx_stuck);
+
+ DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts);
+ DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts);
+ DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime);
+ DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn);
+ DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn);
+ DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization);
+ DEBUGFS_FWSTATS_ADD(ps, upsd_utilization);
+
+ DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop);
+ DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data);
+ DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data);
+ DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data);
+ DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data);
+
+ return 0;
+
+err:
+ if (IS_ERR(entry))
+ ret = PTR_ERR(entry);
+ else
+ ret = -ENOMEM;
+
+ return ret;
+}
diff --git a/drivers/net/wireless/ti/wl12xx/debugfs.h b/drivers/net/wireless/ti/wl12xx/debugfs.h
new file mode 100644
index 0000000..96898e2
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/debugfs.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_DEBUGFS_H__
+#define __WL12XX_DEBUGFS_H__
+
+int wl12xx_debugfs_add_files(struct wl1271 *wl,
+ struct dentry *rootdir);
+
+#endif /* __WL12XX_DEBUGFS_H__ */
diff --git a/drivers/net/wireless/ti/wl12xx/event.c b/drivers/net/wireless/ti/wl12xx/event.c
new file mode 100644
index 0000000..6ac0ed7
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/event.c
@@ -0,0 +1,116 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "event.h"
+#include "scan.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+
+int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+ bool *timeout)
+{
+ u32 local_event;
+
+ switch (event) {
+ case WLCORE_EVENT_ROLE_STOP_COMPLETE:
+ local_event = ROLE_STOP_COMPLETE_EVENT_ID;
+ break;
+
+ case WLCORE_EVENT_PEER_REMOVE_COMPLETE:
+ local_event = PEER_REMOVE_COMPLETE_EVENT_ID;
+ break;
+
+ default:
+ /* event not implemented */
+ return 0;
+ }
+ return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
+}
+
+int wl12xx_process_mailbox_events(struct wl1271 *wl)
+{
+ struct wl12xx_event_mailbox *mbox = wl->mbox;
+ u32 vector;
+
+
+ vector = le32_to_cpu(mbox->events_vector);
+ vector &= ~(le32_to_cpu(mbox->events_mask));
+
+ wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector);
+
+ if (vector & SCAN_COMPLETE_EVENT_ID) {
+ wl1271_debug(DEBUG_EVENT, "status: 0x%x",
+ mbox->scheduled_scan_status);
+
+ if (wl->scan_wlvif)
+ wl12xx_scan_completed(wl, wl->scan_wlvif);
+ }
+
+ if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
+ wl1271_debug(DEBUG_EVENT,
+ "PERIODIC_SCAN_REPORT_EVENT (status 0x%0x)",
+ mbox->scheduled_scan_status);
+
+ wlcore_scan_sched_scan_results(wl);
+ }
+
+ if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID)
+ wlcore_event_sched_scan_completed(wl,
+ mbox->scheduled_scan_status);
+ if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
+ wlcore_event_soft_gemini_sense(wl,
+ mbox->soft_gemini_sense_info);
+
+ if (vector & BSS_LOSE_EVENT_ID)
+ wlcore_event_beacon_loss(wl, 0xff);
+
+ if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID)
+ wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric);
+
+ if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)
+ wlcore_event_ba_rx_constraint(wl,
+ BIT(mbox->role_id),
+ mbox->rx_ba_allowed);
+
+ if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID)
+ wlcore_event_channel_switch(wl, 0xff,
+ mbox->channel_switch_status);
+
+ if (vector & DUMMY_PACKET_EVENT_ID)
+ wlcore_event_dummy_packet(wl);
+
+ /*
+ * "TX retries exceeded" has a different meaning according to mode.
+ * In AP mode the offending station is disconnected.
+ */
+ if (vector & MAX_TX_RETRY_EVENT_ID)
+ wlcore_event_max_tx_failure(wl,
+ le16_to_cpu(mbox->sta_tx_retry_exceeded));
+
+ if (vector & INACTIVE_STA_EVENT_ID)
+ wlcore_event_inactive_sta(wl,
+ le16_to_cpu(mbox->sta_aging_status));
+
+ if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
+ wlcore_event_roc_complete(wl);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ti/wl12xx/event.h b/drivers/net/wireless/ti/wl12xx/event.h
new file mode 100644
index 0000000..a5cc3fc
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/event.h
@@ -0,0 +1,111 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_EVENT_H__
+#define __WL12XX_EVENT_H__
+
+#include "../wlcore/wlcore.h"
+
+enum {
+ MEASUREMENT_START_EVENT_ID = BIT(8),
+ MEASUREMENT_COMPLETE_EVENT_ID = BIT(9),
+ SCAN_COMPLETE_EVENT_ID = BIT(10),
+ WFD_DISCOVERY_COMPLETE_EVENT_ID = BIT(11),
+ AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12),
+ RESERVED1 = BIT(13),
+ PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14),
+ ROLE_STOP_COMPLETE_EVENT_ID = BIT(15),
+ RADAR_DETECTED_EVENT_ID = BIT(16),
+ CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17),
+ BSS_LOSE_EVENT_ID = BIT(18),
+ REGAINED_BSS_EVENT_ID = BIT(19),
+ MAX_TX_RETRY_EVENT_ID = BIT(20),
+ DUMMY_PACKET_EVENT_ID = BIT(21),
+ SOFT_GEMINI_SENSE_EVENT_ID = BIT(22),
+ CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID = BIT(23),
+ SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24),
+ PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25),
+ INACTIVE_STA_EVENT_ID = BIT(26),
+ PEER_REMOVE_COMPLETE_EVENT_ID = BIT(27),
+ PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(28),
+ PERIODIC_SCAN_REPORT_EVENT_ID = BIT(29),
+ BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(30),
+ REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(31),
+};
+
+struct wl12xx_event_mailbox {
+ __le32 events_vector;
+ __le32 events_mask;
+ __le32 reserved_1;
+ __le32 reserved_2;
+
+ u8 number_of_scan_results;
+ u8 scan_tag;
+ u8 completed_scan_status;
+ u8 reserved_3;
+
+ u8 soft_gemini_sense_info;
+ u8 soft_gemini_protective_info;
+ s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
+ u8 change_auto_mode_timeout;
+ u8 scheduled_scan_status;
+ u8 reserved4;
+ /* tuned channel (roc) */
+ u8 roc_channel;
+
+ __le16 hlid_removed_bitmap;
+
+ /* bitmap of aged stations (by HLID) */
+ __le16 sta_aging_status;
+
+ /* bitmap of stations (by HLID) which exceeded max tx retries */
+ __le16 sta_tx_retry_exceeded;
+
+ /* discovery completed results */
+ u8 discovery_tag;
+ u8 number_of_preq_results;
+ u8 number_of_prsp_results;
+ u8 reserved_5;
+
+ /* rx ba constraint */
+ u8 role_id; /* 0xFF means any role. */
+ u8 rx_ba_allowed;
+ u8 reserved_6[2];
+
+ /* Channel switch results */
+
+ u8 channel_switch_role_id;
+ u8 channel_switch_status;
+ u8 reserved_7[2];
+
+ u8 ps_poll_delivery_failure_role_ids;
+ u8 stopped_role_ids;
+ u8 started_role_ids;
+
+ u8 reserved_8[9];
+} __packed;
+
+int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+ bool *timeout);
+int wl12xx_process_mailbox_events(struct wl1271 *wl);
+
+#endif
+
diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
new file mode 100644
index 0000000..22009e1
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -0,0 +1,1977 @@
+/*
+ * This file is part of wl1271
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <linux/err.h>
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/io.h"
+#include "../wlcore/acx.h"
+#include "../wlcore/tx.h"
+#include "../wlcore/rx.h"
+#include "../wlcore/boot.h"
+
+#include "wl12xx.h"
+#include "reg.h"
+#include "cmd.h"
+#include "acx.h"
+#include "scan.h"
+#include "event.h"
+#include "debugfs.h"
+#include "conf.h"
+
+static char *fref_param;
+static char *tcxo_param;
+
+static struct wlcore_conf wl12xx_conf = {
+ .sg = {
+ .params = {
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
+ [WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
+ [WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
+ /* active scan params */
+ [WL12XX_CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
+ [WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
+ [WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
+ /* passive scan params */
+ [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_BR] = 800,
+ [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_EDR] = 200,
+ [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_HV3] = 200,
+ /* passive scan in dual antenna params */
+ [WL12XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
+ [WL12XX_CONF_SG_BCN_HV3_COLL_THR_IN_PASSIVE_SCAN] = 0,
+ [WL12XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN] = 0,
+ /* general params */
+ [WL12XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
+ [WL12XX_CONF_SG_ANTENNA_CONFIGURATION] = 0,
+ [WL12XX_CONF_SG_BEACON_MISS_PERCENT] = 60,
+ [WL12XX_CONF_SG_DHCP_TIME] = 5000,
+ [WL12XX_CONF_SG_RXT] = 1200,
+ [WL12XX_CONF_SG_TXT] = 1000,
+ [WL12XX_CONF_SG_ADAPTIVE_RXT_TXT] = 1,
+ [WL12XX_CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
+ [WL12XX_CONF_SG_HV3_MAX_SERVED] = 6,
+ [WL12XX_CONF_SG_PS_POLL_TIMEOUT] = 10,
+ [WL12XX_CONF_SG_UPSD_TIMEOUT] = 10,
+ [WL12XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
+ [WL12XX_CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
+ [WL12XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
+ /* AP params */
+ [WL12XX_CONF_AP_BEACON_MISS_TX] = 3,
+ [WL12XX_CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
+ [WL12XX_CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
+ [WL12XX_CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
+ [WL12XX_CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
+ [WL12XX_CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
+ /* CTS Diluting params */
+ [WL12XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
+ [WL12XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
+ },
+ .state = CONF_SG_PROTECTIVE,
+ },
+ .rx = {
+ .rx_msdu_life_time = 512000,
+ .packet_detection_threshold = 0,
+ .ps_poll_timeout = 15,
+ .upsd_timeout = 15,
+ .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD,
+ .rx_cca_threshold = 0,
+ .irq_blk_threshold = 0xFFFF,
+ .irq_pkt_threshold = 0,
+ .irq_timeout = 600,
+ .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
+ },
+ .tx = {
+ .tx_energy_detection = 0,
+ .sta_rc_conf = {
+ .enabled_rates = 0,
+ .short_retry_limit = 10,
+ .long_retry_limit = 10,
+ .aflags = 0,
+ },
+ .ac_conf_count = 4,
+ .ac_conf = {
+ [CONF_TX_AC_BE] = {
+ .ac = CONF_TX_AC_BE,
+ .cw_min = 15,
+ .cw_max = 63,
+ .aifsn = 3,
+ .tx_op_limit = 0,
+ },
+ [CONF_TX_AC_BK] = {
+ .ac = CONF_TX_AC_BK,
+ .cw_min = 15,
+ .cw_max = 63,
+ .aifsn = 7,
+ .tx_op_limit = 0,
+ },
+ [CONF_TX_AC_VI] = {
+ .ac = CONF_TX_AC_VI,
+ .cw_min = 15,
+ .cw_max = 63,
+ .aifsn = CONF_TX_AIFS_PIFS,
+ .tx_op_limit = 3008,
+ },
+ [CONF_TX_AC_VO] = {
+ .ac = CONF_TX_AC_VO,
+ .cw_min = 15,
+ .cw_max = 63,
+ .aifsn = CONF_TX_AIFS_PIFS,
+ .tx_op_limit = 1504,
+ },
+ },
+ .max_tx_retries = 100,
+ .ap_aging_period = 300,
+ .tid_conf_count = 4,
+ .tid_conf = {
+ [CONF_TX_AC_BE] = {
+ .queue_id = CONF_TX_AC_BE,
+ .channel_type = CONF_CHANNEL_TYPE_EDCF,
+ .tsid = CONF_TX_AC_BE,
+ .ps_scheme = CONF_PS_SCHEME_LEGACY,
+ .ack_policy = CONF_ACK_POLICY_LEGACY,
+ .apsd_conf = {0, 0},
+ },
+ [CONF_TX_AC_BK] = {
+ .queue_id = CONF_TX_AC_BK,
+ .channel_type = CONF_CHANNEL_TYPE_EDCF,
+ .tsid = CONF_TX_AC_BK,
+ .ps_scheme = CONF_PS_SCHEME_LEGACY,
+ .ack_policy = CONF_ACK_POLICY_LEGACY,
+ .apsd_conf = {0, 0},
+ },
+ [CONF_TX_AC_VI] = {
+ .queue_id = CONF_TX_AC_VI,
+ .channel_type = CONF_CHANNEL_TYPE_EDCF,
+ .tsid = CONF_TX_AC_VI,
+ .ps_scheme = CONF_PS_SCHEME_LEGACY,
+ .ack_policy = CONF_ACK_POLICY_LEGACY,
+ .apsd_conf = {0, 0},
+ },
+ [CONF_TX_AC_VO] = {
+ .queue_id = CONF_TX_AC_VO,
+ .channel_type = CONF_CHANNEL_TYPE_EDCF,
+ .tsid = CONF_TX_AC_VO,
+ .ps_scheme = CONF_PS_SCHEME_LEGACY,
+ .ack_policy = CONF_ACK_POLICY_LEGACY,
+ .apsd_conf = {0, 0},
+ },
+ },
+ .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
+ .tx_compl_timeout = 700,
+ .tx_compl_threshold = 4,
+ .basic_rate = CONF_HW_BIT_RATE_1MBPS,
+ .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
+ .tmpl_short_retry_limit = 10,
+ .tmpl_long_retry_limit = 10,
+ .tx_watchdog_timeout = 5000,
+ .slow_link_thold = 3,
+ .fast_link_thold = 10,
+ },
+ .conn = {
+ .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
+ .listen_interval = 1,
+ .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 = 3,
+ .bcn_filt_ie = {
+ [0] = {
+ .ie = WLAN_EID_CHANNEL_SWITCH,
+ .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE,
+ },
+ [1] = {
+ .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 = 12,
+ .bss_lose_timeout = 400,
+ .beacon_rx_timeout = 10000,
+ .broadcast_timeout = 20000,
+ .rx_broadcast_in_ps = 1,
+ .ps_poll_threshold = 10,
+ .bet_enable = CONF_BET_MODE_ENABLE,
+ .bet_max_consecutive = 50,
+ .psm_entry_retries = 8,
+ .psm_exit_retries = 16,
+ .psm_entry_nullfunc_retries = 3,
+ .dynamic_ps_timeout = 1500,
+ .forced_ps = false,
+ .keep_alive_interval = 55000,
+ .max_listen_interval = 20,
+ .sta_sleep_auth = WL1271_PSM_ILLEGAL,
+ .suspend_rx_ba_activity = 0,
+ },
+ .itrim = {
+ .enable = false,
+ .timeout = 50000,
+ },
+ .pm_config = {
+ .host_clk_settling_time = 5000,
+ .host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE,
+ },
+ .roam_trigger = {
+ .trigger_pacing = 1,
+ .avg_weight_rssi_beacon = 20,
+ .avg_weight_rssi_data = 10,
+ .avg_weight_snr_beacon = 20,
+ .avg_weight_snr_data = 10,
+ },
+ .scan = {
+ .min_dwell_time_active = 7500,
+ .max_dwell_time_active = 30000,
+ .min_dwell_time_active_long = 25000,
+ .max_dwell_time_active_long = 50000,
+ .dwell_time_passive = 100000,
+ .dwell_time_dfs = 150000,
+ .num_probe_reqs = 2,
+ .split_scan_timeout = 50000,
+ },
+ .sched_scan = {
+ /*
+ * Values are in TU/1000 but since sched scan FW command
+ * params are in TUs rounding up may occur.
+ */
+ .base_dwell_time = 7500,
+ .max_dwell_time_delta = 22500,
+ /* based on 250bits per probe @1Mbps */
+ .dwell_time_delta_per_probe = 2000,
+ /* based on 250bits per probe @6Mbps (plus a bit more) */
+ .dwell_time_delta_per_probe_5 = 350,
+ .dwell_time_passive = 100000,
+ .dwell_time_dfs = 150000,
+ .num_probe_reqs = 2,
+ .rssi_threshold = -90,
+ .snr_threshold = 0,
+ },
+ .ht = {
+ .rx_ba_win_size = 8,
+ .tx_ba_win_size = 64,
+ .inactivity_timeout = 10000,
+ .tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP,
+ },
+ /*
+ * Memory config for wl127x chips is given in the
+ * wl12xx_default_priv_conf struct. The below configuration is
+ * for wl128x chips.
+ */
+ .mem = {
+ .num_stations = 1,
+ .ssid_profiles = 1,
+ .rx_block_num = 40,
+ .tx_min_block_num = 40,
+ .dynamic_memory = 1,
+ .min_req_tx_blocks = 45,
+ .min_req_rx_blocks = 22,
+ .tx_min = 27,
+ },
+ .fm_coex = {
+ .enable = true,
+ .swallow_period = 5,
+ .n_divider_fref_set_1 = 0xff, /* default */
+ .n_divider_fref_set_2 = 12,
+ .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 */
+ .swallow_clk_diff = 0xff, /* default */
+ },
+ .rx_streaming = {
+ .duration = 150,
+ .queues = 0x1,
+ .interval = 20,
+ .always = 0,
+ },
+ .fwlog = {
+ .mode = WL12XX_FWLOG_CONTINUOUS,
+ .mem_blocks = 2,
+ .severity = 0,
+ .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED,
+ .output = WL12XX_FWLOG_OUTPUT_DBG_PINS,
+ .threshold = 0,
+ },
+ .rate = {
+ .rate_retry_score = 32000,
+ .per_add = 8192,
+ .per_th1 = 2048,
+ .per_th2 = 4096,
+ .max_per = 8100,
+ .inverse_curiosity_factor = 5,
+ .tx_fail_low_th = 4,
+ .tx_fail_high_th = 10,
+ .per_alpha_shift = 4,
+ .per_add_shift = 13,
+ .per_beta1_shift = 10,
+ .per_beta2_shift = 8,
+ .rate_check_up = 2,
+ .rate_check_down = 12,
+ .rate_retry_policy = {
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ },
+ },
+ .hangover = {
+ .recover_time = 0,
+ .hangover_period = 20,
+ .dynamic_mode = 1,
+ .early_termination_mode = 1,
+ .max_period = 20,
+ .min_period = 1,
+ .increase_delta = 1,
+ .decrease_delta = 2,
+ .quiet_time = 4,
+ .increase_time = 1,
+ .window_size = 16,
+ },
+ .recovery = {
+ .bug_on_recovery = 0,
+ .no_recovery = 0,
+ },
+};
+
+static struct wl12xx_priv_conf wl12xx_default_priv_conf = {
+ .rf = {
+ .tx_per_channel_power_compensation_2 = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ .tx_per_channel_power_compensation_5 = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ },
+ .mem_wl127x = {
+ .num_stations = 1,
+ .ssid_profiles = 1,
+ .rx_block_num = 70,
+ .tx_min_block_num = 40,
+ .dynamic_memory = 1,
+ .min_req_tx_blocks = 100,
+ .min_req_rx_blocks = 22,
+ .tx_min = 27,
+ },
+
+};
+
+#define WL12XX_TX_HW_BLOCK_SPARE_DEFAULT 1
+#define WL12XX_TX_HW_BLOCK_GEM_SPARE 2
+#define WL12XX_TX_HW_BLOCK_SIZE 252
+
+static const u8 wl12xx_rate_to_idx_2ghz[] = {
+ /* MCS rates are used only with 11n */
+ 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */
+ 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */
+ 6, /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */
+ 5, /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */
+ 4, /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */
+ 3, /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */
+ 2, /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */
+ 1, /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */
+ 0, /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */
+
+ 11, /* WL12XX_CONF_HW_RXTX_RATE_54 */
+ 10, /* WL12XX_CONF_HW_RXTX_RATE_48 */
+ 9, /* WL12XX_CONF_HW_RXTX_RATE_36 */
+ 8, /* WL12XX_CONF_HW_RXTX_RATE_24 */
+
+ /* TI-specific rate */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22 */
+
+ 7, /* WL12XX_CONF_HW_RXTX_RATE_18 */
+ 6, /* WL12XX_CONF_HW_RXTX_RATE_12 */
+ 3, /* WL12XX_CONF_HW_RXTX_RATE_11 */
+ 5, /* WL12XX_CONF_HW_RXTX_RATE_9 */
+ 4, /* WL12XX_CONF_HW_RXTX_RATE_6 */
+ 2, /* WL12XX_CONF_HW_RXTX_RATE_5_5 */
+ 1, /* WL12XX_CONF_HW_RXTX_RATE_2 */
+ 0 /* WL12XX_CONF_HW_RXTX_RATE_1 */
+};
+
+static const u8 wl12xx_rate_to_idx_5ghz[] = {
+ /* MCS rates are used only with 11n */
+ 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */
+ 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */
+ 6, /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */
+ 5, /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */
+ 4, /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */
+ 3, /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */
+ 2, /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */
+ 1, /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */
+ 0, /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */
+
+ 7, /* WL12XX_CONF_HW_RXTX_RATE_54 */
+ 6, /* WL12XX_CONF_HW_RXTX_RATE_48 */
+ 5, /* WL12XX_CONF_HW_RXTX_RATE_36 */
+ 4, /* WL12XX_CONF_HW_RXTX_RATE_24 */
+
+ /* TI-specific rate */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22 */
+
+ 3, /* WL12XX_CONF_HW_RXTX_RATE_18 */
+ 2, /* WL12XX_CONF_HW_RXTX_RATE_12 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_11 */
+ 1, /* WL12XX_CONF_HW_RXTX_RATE_9 */
+ 0, /* WL12XX_CONF_HW_RXTX_RATE_6 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_5_5 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_2 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED /* WL12XX_CONF_HW_RXTX_RATE_1 */
+};
+
+static const u8 *wl12xx_band_rate_to_idx[] = {
+ [NL80211_BAND_2GHZ] = wl12xx_rate_to_idx_2ghz,
+ [NL80211_BAND_5GHZ] = wl12xx_rate_to_idx_5ghz
+};
+
+enum wl12xx_hw_rates {
+ WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI = 0,
+ WL12XX_CONF_HW_RXTX_RATE_MCS7,
+ WL12XX_CONF_HW_RXTX_RATE_MCS6,
+ WL12XX_CONF_HW_RXTX_RATE_MCS5,
+ WL12XX_CONF_HW_RXTX_RATE_MCS4,
+ WL12XX_CONF_HW_RXTX_RATE_MCS3,
+ WL12XX_CONF_HW_RXTX_RATE_MCS2,
+ WL12XX_CONF_HW_RXTX_RATE_MCS1,
+ WL12XX_CONF_HW_RXTX_RATE_MCS0,
+ WL12XX_CONF_HW_RXTX_RATE_54,
+ WL12XX_CONF_HW_RXTX_RATE_48,
+ WL12XX_CONF_HW_RXTX_RATE_36,
+ WL12XX_CONF_HW_RXTX_RATE_24,
+ WL12XX_CONF_HW_RXTX_RATE_22,
+ WL12XX_CONF_HW_RXTX_RATE_18,
+ WL12XX_CONF_HW_RXTX_RATE_12,
+ WL12XX_CONF_HW_RXTX_RATE_11,
+ WL12XX_CONF_HW_RXTX_RATE_9,
+ WL12XX_CONF_HW_RXTX_RATE_6,
+ WL12XX_CONF_HW_RXTX_RATE_5_5,
+ WL12XX_CONF_HW_RXTX_RATE_2,
+ WL12XX_CONF_HW_RXTX_RATE_1,
+ WL12XX_CONF_HW_RXTX_RATE_MAX,
+};
+
+static struct wlcore_partition_set wl12xx_ptable[PART_TABLE_LEN] = {
+ [PART_DOWN] = {
+ .mem = {
+ .start = 0x00000000,
+ .size = 0x000177c0
+ },
+ .reg = {
+ .start = REGISTERS_BASE,
+ .size = 0x00008800
+ },
+ .mem2 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ },
+ .mem3 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ },
+ },
+
+ [PART_BOOT] = { /* in wl12xx we can use a mix of work and down
+ * partition here */
+ .mem = {
+ .start = 0x00040000,
+ .size = 0x00014fc0
+ },
+ .reg = {
+ .start = REGISTERS_BASE,
+ .size = 0x00008800
+ },
+ .mem2 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ },
+ .mem3 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ },
+ },
+
+ [PART_WORK] = {
+ .mem = {
+ .start = 0x00040000,
+ .size = 0x00014fc0
+ },
+ .reg = {
+ .start = REGISTERS_BASE,
+ .size = 0x0000a000
+ },
+ .mem2 = {
+ .start = 0x003004f8,
+ .size = 0x00000004
+ },
+ .mem3 = {
+ .start = 0x00000000,
+ .size = 0x00040404
+ },
+ },
+
+ [PART_DRPW] = {
+ .mem = {
+ .start = 0x00040000,
+ .size = 0x00014fc0
+ },
+ .reg = {
+ .start = DRPW_BASE,
+ .size = 0x00006000
+ },
+ .mem2 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ },
+ .mem3 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ }
+ }
+};
+
+static const int wl12xx_rtable[REG_TABLE_LEN] = {
+ [REG_ECPU_CONTROL] = WL12XX_REG_ECPU_CONTROL,
+ [REG_INTERRUPT_NO_CLEAR] = WL12XX_REG_INTERRUPT_NO_CLEAR,
+ [REG_INTERRUPT_ACK] = WL12XX_REG_INTERRUPT_ACK,
+ [REG_COMMAND_MAILBOX_PTR] = WL12XX_REG_COMMAND_MAILBOX_PTR,
+ [REG_EVENT_MAILBOX_PTR] = WL12XX_REG_EVENT_MAILBOX_PTR,
+ [REG_INTERRUPT_TRIG] = WL12XX_REG_INTERRUPT_TRIG,
+ [REG_INTERRUPT_MASK] = WL12XX_REG_INTERRUPT_MASK,
+ [REG_PC_ON_RECOVERY] = WL12XX_SCR_PAD4,
+ [REG_CHIP_ID_B] = WL12XX_CHIP_ID_B,
+ [REG_CMD_MBOX_ADDRESS] = WL12XX_CMD_MBOX_ADDRESS,
+
+ /* data access memory addresses, used with partition translation */
+ [REG_SLV_MEM_DATA] = WL1271_SLV_MEM_DATA,
+ [REG_SLV_REG_DATA] = WL1271_SLV_REG_DATA,
+
+ /* raw data access memory addresses */
+ [REG_RAW_FW_STATUS_ADDR] = FW_STATUS_ADDR,
+};
+
+/* TODO: maybe move to a new header file? */
+#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-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 int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
+{
+ int ret;
+
+ if (wl->chip.id != CHIP_ID_128X_PG20) {
+ struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
+ struct wl12xx_priv *priv = wl->priv;
+
+ /*
+ * Choose the block we want to read
+ * For aggregated packets, only the first memory block
+ * should be retrieved. The FW takes care of the rest.
+ */
+ u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK;
+
+ priv->rx_mem_addr->addr = (mem_block << 8) +
+ le32_to_cpu(wl_mem_map->packet_memory_pool_start);
+
+ priv->rx_mem_addr->addr_extra = priv->rx_mem_addr->addr + 4;
+
+ ret = wlcore_write(wl, WL1271_SLV_REG_DATA, priv->rx_mem_addr,
+ sizeof(*priv->rx_mem_addr), false);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wl12xx_identify_chip(struct wl1271 *wl)
+{
+ int ret = 0;
+
+ switch (wl->chip.id) {
+ case CHIP_ID_127X_PG10:
+ wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
+ wl->chip.id);
+
+ wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
+ WLCORE_QUIRK_DUAL_PROBE_TMPL |
+ WLCORE_QUIRK_TKIP_HEADER_SPACE |
+ WLCORE_QUIRK_START_STA_FAILS |
+ WLCORE_QUIRK_AP_ZERO_SESSION_ID;
+ 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,
+ sizeof(wl->conf.mem));
+
+ /* 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_SR_VER, WL127X_MAJOR_SR_VER,
+ WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
+ WL127X_IFTYPE_MR_VER, WL127X_MAJOR_MR_VER,
+ WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
+ break;
+
+ case CHIP_ID_127X_PG20:
+ wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
+ wl->chip.id);
+
+ wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
+ WLCORE_QUIRK_DUAL_PROBE_TMPL |
+ WLCORE_QUIRK_TKIP_HEADER_SPACE |
+ WLCORE_QUIRK_START_STA_FAILS |
+ WLCORE_QUIRK_AP_ZERO_SESSION_ID;
+ wl->plt_fw_name = WL127X_PLT_FW_NAME;
+ 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,
+ sizeof(wl->conf.mem));
+
+ /* 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_SR_VER, WL127X_MAJOR_SR_VER,
+ WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
+ WL127X_IFTYPE_MR_VER, WL127X_MAJOR_MR_VER,
+ WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
+ break;
+
+ case CHIP_ID_128X_PG20:
+ wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
+ wl->chip.id);
+ 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_QUIRK_START_STA_FAILS |
+ WLCORE_QUIRK_AP_ZERO_SESSION_ID;
+
+ wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER,
+ WL128X_IFTYPE_SR_VER, WL128X_MAJOR_SR_VER,
+ WL128X_SUBTYPE_SR_VER, WL128X_MINOR_SR_VER,
+ WL128X_IFTYPE_MR_VER, WL128X_MAJOR_MR_VER,
+ WL128X_SUBTYPE_MR_VER, WL128X_MINOR_MR_VER);
+ break;
+ case CHIP_ID_128X_PG10:
+ default:
+ wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
+ ret = -ENODEV;
+ goto out;
+ }
+
+ wl->fw_mem_block_size = 256;
+ wl->fwlog_end = 0x2000000;
+
+ /* common settings */
+ wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY;
+ wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY;
+ wl->sched_scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
+ wl->sched_scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+ wl->max_channels_5 = WL12XX_MAX_CHANNELS_5GHZ;
+ wl->ba_rx_session_count_max = WL12XX_RX_BA_MAX_SESSIONS;
+out:
+ return ret;
+}
+
+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;
+ ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr);
+ if (ret < 0)
+ goto out;
+
+ /* write value to OCP_POR_WDATA */
+ ret = wlcore_write32(wl, WL12XX_OCP_DATA_WRITE, val);
+ if (ret < 0)
+ goto out;
+
+ /* write 1 to OCP_CMD */
+ ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE);
+ if (ret < 0)
+ goto out;
+
+out:
+ return ret;
+}
+
+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;
+ ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr);
+ if (ret < 0)
+ return ret;
+
+ /* write 2 to OCP_CMD */
+ ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_READ);
+ if (ret < 0)
+ return ret;
+
+ /* poll for data ready */
+ do {
+ 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 -ETIMEDOUT;
+ }
+
+ /* check data status and return if OK */
+ if ((val & OCP_STATUS_MASK) != OCP_STATUS_OK) {
+ wl1271_warning("Top register access returned error.");
+ 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 */
+ 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));
+ 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 */
+ 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);
+
+ return 0;
+}
+
+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;
+
+ if (tcxo_detection & TCXO_DET_FAILED)
+ return false;
+
+ return true;
+}
+
+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;
+
+ if (fref_detection & FREF_CLK_DETECT_FAIL)
+ return false;
+
+ return true;
+}
+
+static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl)
+{
+ int ret;
+
+ 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)
+{
+ 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 */
+ 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);
+ ret = wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
+ if (ret < 0)
+ return ret;
+
+ /* Handle special cases of the TCXO clock */
+ 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;
+
+ 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;
+ ret = wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config);
+
+ return ret;
+}
+
+/*
+ * 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)
+{
+ 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 (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 */
+ 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 (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;
+ }
+
+ /* TCXO clock is selected */
+ if (!wl128x_is_tcxo_valid(wl))
+ return -EINVAL;
+ *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 = priv->ref_clock;
+
+config_mcs_pll:
+ return wl128x_configure_mcs_pll(wl, *selected_clock);
+}
+
+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 (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 (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 (priv->ref_clock != CONF_REF_CLK_19_2_E) {
+ u16 val;
+ /* Set clock type (open drain) */
+ ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE, &val);
+ if (ret < 0)
+ goto out;
+
+ val &= FREF_CLK_TYPE_BITS;
+ ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val);
+ if (ret < 0)
+ goto out;
+
+ /* Set clock pull mode (no pull) */
+ ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL, &val);
+ if (ret < 0)
+ goto out;
+
+ val |= NO_PULL;
+ ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val);
+ if (ret < 0)
+ goto out;
+ } else {
+ u16 val;
+ /* Set clock 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;
+ ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = wlcore_write32(wl, WL12XX_PLL_PARAMETERS, clk);
+ if (ret < 0)
+ goto out;
+
+ 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;
+ ret = wlcore_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause);
+
+out:
+ return ret;
+}
+
+static int wl1271_boot_soft_reset(struct wl1271 *wl)
+{
+ unsigned long timeout;
+ u32 boot_data;
+ int ret = 0;
+
+ /* perform soft reset */
+ 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) {
+ 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;
+
+ if (time_after(jiffies, timeout)) {
+ /* 1.2 check pWhalBus->uSelfClearTime if the
+ * timeout was reached */
+ wl1271_error("soft reset timeout");
+ return -1;
+ }
+
+ udelay(SOFT_RESET_STALL_TIME);
+ }
+
+ /* disable Rx/Tx */
+ ret = wlcore_write32(wl, WL12XX_ENABLE, 0x0);
+ if (ret < 0)
+ goto out;
+
+ /* disable auto calibration on start*/
+ ret = wlcore_write32(wl, WL12XX_SPARE_A2, 0xffff);
+
+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;
+
+ if (wl->chip.id == CHIP_ID_128X_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 */
+ ret = wlcore_write32(wl, WL12XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
+ if (ret < 0)
+ goto out;
+
+ udelay(500);
+
+ 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 */
+
+ 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_128X_PG20)
+ clk |= ((selected_clock & 0x3) << 1) << 4;
+ else
+ clk |= (priv->ref_clock << 1) << 4;
+
+ ret = wlcore_write32(wl, WL12XX_DRPW_SCRATCH_START, clk);
+ if (ret < 0)
+ goto out;
+
+ ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
+ if (ret < 0)
+ goto out;
+
+ /* Disable interrupts */
+ 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)
+ goto out;
+
+out:
+ return ret;
+}
+
+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");
+
+ ret = wlcore_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND);
+ if (ret < 0)
+ goto out;
+
+ 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 */
+ 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_128X_PG20) {
+ ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
+ if (ret < 0)
+ goto out;
+ }
+
+ /* 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;
+ ret = wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity);
+
+out:
+ return ret;
+}
+
+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);
+ 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;
+
+ return ret;
+
+disable_interrupts:
+ wlcore_disable_interrupts(wl);
+
+out:
+ return ret;
+}
+
+static int wl12xx_boot(struct wl1271 *wl)
+{
+ int ret;
+
+ ret = wl12xx_pre_boot(wl);
+ if (ret < 0)
+ goto out;
+
+ ret = wlcore_boot_upload_nvs(wl);
+ if (ret < 0)
+ goto out;
+
+ ret = wl12xx_pre_upload(wl);
+ if (ret < 0)
+ goto out;
+
+ ret = wlcore_boot_upload_firmware(wl);
+ if (ret < 0)
+ goto out;
+
+ wl->event_mask = BSS_LOSE_EVENT_ID |
+ REGAINED_BSS_EVENT_ID |
+ SCAN_COMPLETE_EVENT_ID |
+ ROLE_STOP_COMPLETE_EVENT_ID |
+ RSSI_SNR_TRIGGER_0_EVENT_ID |
+ PSPOLL_DELIVERY_FAILURE_EVENT_ID |
+ SOFT_GEMINI_SENSE_EVENT_ID |
+ PERIODIC_SCAN_REPORT_EVENT_ID |
+ PERIODIC_SCAN_COMPLETE_EVENT_ID |
+ DUMMY_PACKET_EVENT_ID |
+ PEER_REMOVE_COMPLETE_EVENT_ID |
+ BA_SESSION_RX_CONSTRAINT_EVENT_ID |
+ REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
+ INACTIVE_STA_EVENT_ID |
+ CHANNEL_SWITCH_COMPLETE_EVENT_ID;
+
+ wl->ap_event_mask = MAX_TX_RETRY_EVENT_ID;
+
+ ret = wlcore_boot_run_firmware(wl);
+ if (ret < 0)
+ goto out;
+
+ ret = wl12xx_enable_interrupts(wl);
+
+out:
+ return ret;
+}
+
+static int wl12xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr,
+ void *buf, size_t len)
+{
+ 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 int wl12xx_ack_event(struct wl1271 *wl)
+{
+ 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)
+{
+ u32 blk_size = WL12XX_TX_HW_BLOCK_SIZE;
+ u32 align_len = wlcore_calc_packet_alignment(wl, len);
+
+ return (align_len + blk_size - 1) / blk_size + spare_blks;
+}
+
+static void
+wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
+ u32 blks, u32 spare_blks)
+{
+ if (wl->chip.id == CHIP_ID_128X_PG20) {
+ desc->wl128x_mem.total_mem_blocks = blks;
+ } else {
+ desc->wl127x_mem.extra_blocks = spare_blks;
+ desc->wl127x_mem.total_mem_blocks = blks;
+ }
+}
+
+static void
+wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
+ struct sk_buff *skb)
+{
+ u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len);
+
+ if (wl->chip.id == CHIP_ID_128X_PG20) {
+ desc->wl128x_mem.extra_bytes = aligned_len - skb->len;
+ desc->length = cpu_to_le16(aligned_len >> 2);
+
+ wl1271_debug(DEBUG_TX,
+ "tx_fill_hdr: hlid: %d len: %d life: %d mem: %d extra: %d",
+ desc->hlid,
+ le16_to_cpu(desc->length),
+ le16_to_cpu(desc->life_time),
+ desc->wl128x_mem.total_mem_blocks,
+ desc->wl128x_mem.extra_bytes);
+ } else {
+ /* calculate number of padding bytes */
+ int pad = aligned_len - skb->len;
+ desc->tx_attr |=
+ cpu_to_le16(pad << TX_HW_ATTR_OFST_LAST_WORD_PAD);
+
+ /* Store the aligned length in terms of words */
+ desc->length = cpu_to_le16(aligned_len >> 2);
+
+ wl1271_debug(DEBUG_TX,
+ "tx_fill_hdr: pad: %d hlid: %d len: %d life: %d mem: %d",
+ pad, desc->hlid,
+ le16_to_cpu(desc->length),
+ le16_to_cpu(desc->life_time),
+ desc->wl127x_mem.total_mem_blocks);
+ }
+}
+
+static enum wl_rx_buf_align
+wl12xx_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc)
+{
+ if (rx_desc & RX_BUF_UNALIGNED_PAYLOAD)
+ return WLCORE_RX_BUF_UNALIGNED;
+
+ return WLCORE_RX_BUF_ALIGNED;
+}
+
+static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
+ u32 data_len)
+{
+ struct wl1271_rx_descriptor *desc = rx_data;
+
+ /* invalid packet */
+ if (data_len < sizeof(*desc) ||
+ data_len < sizeof(*desc) + desc->pad_len)
+ return 0;
+
+ return data_len - sizeof(*desc) - desc->pad_len;
+}
+
+static int wl12xx_tx_delayed_compl(struct wl1271 *wl)
+{
+ if (wl->fw_status->tx_results_counter ==
+ (wl->tx_results_count & 0xff))
+ return 0;
+
+ return wlcore_tx_complete(wl);
+}
+
+static int wl12xx_hw_init(struct wl1271 *wl)
+{
+ int ret;
+
+ if (wl->chip.id == CHIP_ID_128X_PG20) {
+ u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
+
+ 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;
+
+ if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN)
+ /* Enable SDIO padding */
+ host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
+
+ /* Must be before wl1271_acx_init_mem_config() */
+ ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap);
+ if (ret < 0)
+ goto out;
+ } else {
+ 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;
+ ret = wl1271_cmd_ext_radio_parms(wl);
+ if (ret < 0)
+ goto out;
+ }
+out:
+ return ret;
+}
+
+static void wl12xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
+ struct wl_fw_status *fw_status)
+{
+ struct wl12xx_fw_status *int_fw_status = raw_fw_status;
+
+ fw_status->intr = le32_to_cpu(int_fw_status->intr);
+ fw_status->fw_rx_counter = int_fw_status->fw_rx_counter;
+ fw_status->drv_rx_counter = int_fw_status->drv_rx_counter;
+ fw_status->tx_results_counter = int_fw_status->tx_results_counter;
+ fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs;
+
+ fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime);
+ fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap);
+ fw_status->link_fast_bitmap =
+ le32_to_cpu(int_fw_status->link_fast_bitmap);
+ fw_status->total_released_blks =
+ le32_to_cpu(int_fw_status->total_released_blks);
+ fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total);
+
+ fw_status->counters.tx_released_pkts =
+ int_fw_status->counters.tx_released_pkts;
+ fw_status->counters.tx_lnk_free_pkts =
+ int_fw_status->counters.tx_lnk_free_pkts;
+ fw_status->counters.tx_voice_released_blks =
+ int_fw_status->counters.tx_voice_released_blks;
+ fw_status->counters.tx_last_rate =
+ int_fw_status->counters.tx_last_rate;
+
+ fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr);
+}
+
+static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif)
+{
+ return wlvif->rate_set;
+}
+
+static void wl12xx_conf_init(struct wl1271 *wl)
+{
+ struct wl12xx_priv *priv = wl->priv;
+
+ /* apply driver default configuration */
+ memcpy(&wl->conf, &wl12xx_conf, sizeof(wl12xx_conf));
+
+ /* apply default private configuration */
+ memcpy(&priv->conf, &wl12xx_default_priv_conf, sizeof(priv->conf));
+}
+
+static bool wl12xx_mac_in_fuse(struct wl1271 *wl)
+{
+ bool supported = false;
+ u8 major, minor;
+
+ if (wl->chip.id == CHIP_ID_128X_PG20) {
+ major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver);
+ minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver);
+
+ /* in wl128x we have the MAC address if the PG is >= (2, 1) */
+ if (major > 2 || (major == 2 && minor >= 1))
+ supported = true;
+ } else {
+ major = WL127X_PG_GET_MAJOR(wl->hw_pg_ver);
+ minor = WL127X_PG_GET_MINOR(wl->hw_pg_ver);
+
+ /* in wl127x we have the MAC address if the PG is >= (3, 1) */
+ if (major == 3 && minor >= 1)
+ supported = true;
+ }
+
+ wl1271_debug(DEBUG_PROBE,
+ "PG Ver major = %d minor = %d, MAC %s present",
+ major, minor, supported ? "is" : "is not");
+
+ return supported;
+}
+
+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;
+
+ ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1, &mac1);
+ if (ret < 0)
+ goto out;
+
+ 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;
+
+ ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
+
+out:
+ return ret;
+}
+
+static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
+{
+ u16 die_info;
+ int ret;
+
+ if (wl->chip.id == CHIP_ID_128X_PG20)
+ ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1,
+ &die_info);
+ else
+ ret = wl12xx_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1,
+ &die_info);
+
+ if (ret >= 0 && ver)
+ *ver = (s8)((die_info & PG_VER_MASK) >> PG_VER_OFFSET);
+
+ return ret;
+}
+
+static int wl12xx_get_mac(struct wl1271 *wl)
+{
+ if (wl12xx_mac_in_fuse(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_set_peer_cap(struct wl1271 *wl,
+ struct ieee80211_sta_ht_cap *ht_cap,
+ bool allow_ht_operation,
+ u32 rate_set, u8 hlid)
+{
+ return wl1271_acx_set_ht_capabilities(wl, ht_cap, allow_ht_operation,
+ hlid);
+}
+
+static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+ struct wl1271_link *lnk)
+{
+ u8 thold;
+
+ if (test_bit(hlid, &wl->fw_fast_lnk_map))
+ thold = wl->conf.tx.fast_link_thold;
+ else
+ thold = wl->conf.tx.slow_link_thold;
+
+ return lnk->allocated_pkts < thold;
+}
+
+static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
+ struct wl1271_link *lnk)
+{
+ /* any link is good for low priority */
+ return true;
+}
+
+static u32 wl12xx_convert_hwaddr(struct wl1271 *wl, u32 hwaddr)
+{
+ return hwaddr << 5;
+}
+
+static int wl12xx_setup(struct wl1271 *wl);
+
+static struct wlcore_ops wl12xx_ops = {
+ .setup = wl12xx_setup,
+ .identify_chip = wl12xx_identify_chip,
+ .boot = wl12xx_boot,
+ .plt_init = wl12xx_plt_init,
+ .trigger_cmd = wl12xx_trigger_cmd,
+ .ack_event = wl12xx_ack_event,
+ .wait_for_event = wl12xx_wait_for_event,
+ .process_mailbox_events = wl12xx_process_mailbox_events,
+ .calc_tx_blocks = wl12xx_calc_tx_blocks,
+ .set_tx_desc_blocks = wl12xx_set_tx_desc_blocks,
+ .set_tx_desc_data_len = wl12xx_set_tx_desc_data_len,
+ .get_rx_buf_align = wl12xx_get_rx_buf_align,
+ .get_rx_packet_len = wl12xx_get_rx_packet_len,
+ .tx_immediate_compl = NULL,
+ .tx_delayed_compl = wl12xx_tx_delayed_compl,
+ .hw_init = wl12xx_hw_init,
+ .init_vif = NULL,
+ .convert_fw_status = wl12xx_convert_fw_status,
+ .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,
+ .scan_start = wl12xx_scan_start,
+ .scan_stop = wl12xx_scan_stop,
+ .sched_scan_start = wl12xx_sched_scan_start,
+ .sched_scan_stop = wl12xx_scan_sched_scan_stop,
+ .get_spare_blocks = wl12xx_get_spare_blocks,
+ .set_key = wl12xx_set_key,
+ .channel_switch = wl12xx_cmd_channel_switch,
+ .pre_pkt_send = NULL,
+ .set_peer_cap = wl12xx_set_peer_cap,
+ .convert_hwaddr = wl12xx_convert_hwaddr,
+ .lnk_high_prio = wl12xx_lnk_high_prio,
+ .lnk_low_prio = wl12xx_lnk_low_prio,
+ .interrupt_notify = NULL,
+ .rx_ba_filter = NULL,
+ .ap_sleep = NULL,
+};
+
+static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
+ .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 |
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT),
+ .ht_supported = true,
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8,
+ .mcs = {
+ .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ .rx_highest = cpu_to_le16(72),
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+ },
+};
+
+static const struct ieee80211_iface_limit wl12xx_iface_limits[] = {
+ {
+ .max = 3,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT),
+ },
+};
+
+static const struct ieee80211_iface_combination
+wl12xx_iface_combinations[] = {
+ {
+ .max_interfaces = 3,
+ .limits = wl12xx_iface_limits,
+ .n_limits = ARRAY_SIZE(wl12xx_iface_limits),
+ .num_different_channels = 1,
+ },
+};
+
+static const struct wl12xx_clock wl12xx_refclock_table[] = {
+ { 19200000, false, WL12XX_REFCLOCK_19 },
+ { 26000000, false, WL12XX_REFCLOCK_26 },
+ { 26000000, true, WL12XX_REFCLOCK_26_XTAL },
+ { 38400000, false, WL12XX_REFCLOCK_38 },
+ { 38400000, true, WL12XX_REFCLOCK_38_XTAL },
+ { 52000000, false, WL12XX_REFCLOCK_52 },
+ { 0, false, 0 }
+};
+
+static const struct wl12xx_clock wl12xx_tcxoclock_table[] = {
+ { 16368000, true, WL12XX_TCXOCLOCK_16_368 },
+ { 16800000, true, WL12XX_TCXOCLOCK_16_8 },
+ { 19200000, true, WL12XX_TCXOCLOCK_19_2 },
+ { 26000000, true, WL12XX_TCXOCLOCK_26 },
+ { 32736000, true, WL12XX_TCXOCLOCK_32_736 },
+ { 33600000, true, WL12XX_TCXOCLOCK_33_6 },
+ { 38400000, true, WL12XX_TCXOCLOCK_38_4 },
+ { 52000000, true, WL12XX_TCXOCLOCK_52 },
+ { 0, false, 0 }
+};
+
+static int wl12xx_get_clock_idx(const struct wl12xx_clock *table,
+ u32 freq, bool xtal)
+{
+ int i;
+
+ for (i = 0; table[i].freq != 0; i++)
+ if ((table[i].freq == freq) && (table[i].xtal == xtal))
+ return table[i].hw_idx;
+
+ return -EINVAL;
+}
+
+static int wl12xx_setup(struct wl1271 *wl)
+{
+ struct wl12xx_priv *priv = wl->priv;
+ struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev);
+
+ BUILD_BUG_ON(WL12XX_MAX_LINKS > WLCORE_MAX_LINKS);
+ BUILD_BUG_ON(WL12XX_MAX_AP_STATIONS > WL12XX_MAX_LINKS);
+ BUILD_BUG_ON(WL12XX_CONF_SG_PARAMS_MAX > WLCORE_CONF_SG_PARAMS_MAX);
+
+ wl->rtable = wl12xx_rtable;
+ wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
+ wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
+ wl->num_links = WL12XX_MAX_LINKS;
+ wl->max_ap_stations = WL12XX_MAX_AP_STATIONS;
+ wl->iface_combinations = wl12xx_iface_combinations;
+ wl->n_iface_combinations = ARRAY_SIZE(wl12xx_iface_combinations);
+ 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_len = sizeof(struct wl12xx_fw_status);
+ wl->fw_status_priv_len = 0;
+ wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics);
+ wl->ofdm_only_ap = true;
+ wlcore_set_ht_cap(wl, NL80211_BAND_2GHZ, &wl12xx_ht_cap);
+ wlcore_set_ht_cap(wl, NL80211_BAND_5GHZ, &wl12xx_ht_cap);
+ wl12xx_conf_init(wl);
+
+ if (!fref_param) {
+ priv->ref_clock = wl12xx_get_clock_idx(wl12xx_refclock_table,
+ pdev_data->ref_clock_freq,
+ pdev_data->ref_clock_xtal);
+ if (priv->ref_clock < 0) {
+ wl1271_error("Invalid ref_clock frequency (%d Hz, %s)",
+ pdev_data->ref_clock_freq,
+ pdev_data->ref_clock_xtal ?
+ "XTAL" : "not XTAL");
+
+ return priv->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 && pdev_data->tcxo_clock_freq) {
+ priv->tcxo_clock = wl12xx_get_clock_idx(wl12xx_tcxoclock_table,
+ pdev_data->tcxo_clock_freq,
+ true);
+ if (priv->tcxo_clock < 0) {
+ wl1271_error("Invalid tcxo_clock frequency (%d Hz)",
+ pdev_data->tcxo_clock_freq);
+
+ return priv->tcxo_clock;
+ }
+ } else if (tcxo_param) {
+ 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);
+ }
+
+ priv->rx_mem_addr = kmalloc(sizeof(*priv->rx_mem_addr), GFP_KERNEL);
+ if (!priv->rx_mem_addr)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int wl12xx_probe(struct platform_device *pdev)
+{
+ struct wl1271 *wl;
+ struct ieee80211_hw *hw;
+ int ret;
+
+ hw = wlcore_alloc_hw(sizeof(struct wl12xx_priv),
+ WL12XX_AGGR_BUFFER_SIZE,
+ sizeof(struct wl12xx_event_mailbox));
+ if (IS_ERR(hw)) {
+ wl1271_error("can't allocate hw");
+ ret = PTR_ERR(hw);
+ goto out;
+ }
+
+ wl = hw->priv;
+ wl->ops = &wl12xx_ops;
+ wl->ptable = wl12xx_ptable;
+ ret = wlcore_probe(wl, pdev);
+ if (ret)
+ goto out_free;
+
+ return ret;
+
+out_free:
+ wlcore_free_hw(wl);
+out:
+ return ret;
+}
+
+static int wl12xx_remove(struct platform_device *pdev)
+{
+ struct wl1271 *wl = platform_get_drvdata(pdev);
+ struct wl12xx_priv *priv;
+
+ if (!wl)
+ goto out;
+ priv = wl->priv;
+
+ kfree(priv->rx_mem_addr);
+
+out:
+ return wlcore_remove(pdev);
+}
+
+static const struct platform_device_id wl12xx_id_table[] = {
+ { "wl12xx", 0 },
+ { } /* Terminating Entry */
+};
+MODULE_DEVICE_TABLE(platform, wl12xx_id_table);
+
+static struct platform_driver wl12xx_driver = {
+ .probe = wl12xx_probe,
+ .remove = wl12xx_remove,
+ .id_table = wl12xx_id_table,
+ .driver = {
+ .name = "wl12xx_driver",
+ }
+};
+
+module_platform_driver(wl12xx_driver);
+
+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>");
+MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
+MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
+MODULE_FIRMWARE(WL127X_PLT_FW_NAME);
+MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE);
+MODULE_FIRMWARE(WL128X_FW_NAME_MULTI);
+MODULE_FIRMWARE(WL128X_PLT_FW_NAME);
diff --git a/drivers/net/wireless/ti/wl12xx/reg.h b/drivers/net/wireless/ti/wl12xx/reg.h
new file mode 100644
index 0000000..79ede02
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/reg.h
@@ -0,0 +1,556 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __REG_H__
+#define __REG_H__
+
+#include <linux/bitops.h>
+
+#define REGISTERS_BASE 0x00300000
+#define DRPW_BASE 0x00310000
+
+#define REGISTERS_DOWN_SIZE 0x00008800
+#define REGISTERS_WORK_SIZE 0x0000b000
+
+#define FW_STATUS_ADDR (0x14FC0 + 0xA000)
+
+/*===============================================
+ Host Software Reset - 32bit RW
+ ------------------------------------------
+ [31:1] Reserved
+ 0 SOFT_RESET Soft Reset - When this bit is set,
+ it holds the Wlan hardware in a soft reset state.
+ This reset disables all MAC and baseband processor
+ clocks except the CardBus/PCI interface clock.
+ It also initializes all MAC state machines except
+ the host interface. It does not reload the
+ contents of the EEPROM. When this bit is cleared
+ (not self-clearing), the Wlan hardware
+ exits the software reset state.
+===============================================*/
+#define WL12XX_SLV_SOFT_RESET (REGISTERS_BASE + 0x0000)
+
+#define WL1271_SLV_REG_DATA (REGISTERS_BASE + 0x0008)
+#define WL1271_SLV_REG_ADATA (REGISTERS_BASE + 0x000c)
+#define WL1271_SLV_MEM_DATA (REGISTERS_BASE + 0x0018)
+
+#define WL12XX_REG_INTERRUPT_TRIG (REGISTERS_BASE + 0x0474)
+#define WL12XX_REG_INTERRUPT_TRIG_H (REGISTERS_BASE + 0x0478)
+
+/*=============================================
+ Host Interrupt Mask Register - 32bit (RW)
+ ------------------------------------------
+ Setting a bit in this register masks the
+ corresponding interrupt to the host.
+ 0 - RX0 - Rx first dubble buffer Data Interrupt
+ 1 - TXD - Tx Data Interrupt
+ 2 - TXXFR - Tx Transfer Interrupt
+ 3 - RX1 - Rx second dubble buffer Data Interrupt
+ 4 - RXXFR - Rx Transfer Interrupt
+ 5 - EVENT_A - Event Mailbox interrupt
+ 6 - EVENT_B - Event Mailbox interrupt
+ 7 - WNONHST - Wake On Host Interrupt
+ 8 - TRACE_A - Debug Trace interrupt
+ 9 - TRACE_B - Debug Trace interrupt
+ 10 - CDCMP - Command Complete Interrupt
+ 11 -
+ 12 -
+ 13 -
+ 14 - ICOMP - Initialization Complete Interrupt
+ 16 - SG SE - Soft Gemini - Sense enable interrupt
+ 17 - SG SD - Soft Gemini - Sense disable interrupt
+ 18 - -
+ 19 - -
+ 20 - -
+ 21- -
+ Default: 0x0001
+*==============================================*/
+#define WL12XX_REG_INTERRUPT_MASK (REGISTERS_BASE + 0x04DC)
+
+/*=============================================
+ Host Interrupt Mask Set 16bit, (Write only)
+ ------------------------------------------
+ Setting a bit in this register sets
+ the corresponding bin in ACX_HINT_MASK register
+ without effecting the mask
+ state of other bits (0 = no effect).
+==============================================*/
+#define ACX_REG_HINT_MASK_SET (REGISTERS_BASE + 0x04E0)
+
+/*=============================================
+ Host Interrupt Mask Clear 16bit,(Write only)
+ ------------------------------------------
+ Setting a bit in this register clears
+ the corresponding bin in ACX_HINT_MASK register
+ without effecting the mask
+ state of other bits (0 = no effect).
+=============================================*/
+#define ACX_REG_HINT_MASK_CLR (REGISTERS_BASE + 0x04E4)
+
+/*=============================================
+ Host Interrupt Status Nondestructive Read
+ 16bit,(Read only)
+ ------------------------------------------
+ The host can read this register to determine
+ which interrupts are active.
+ Reading this register doesn't
+ effect its content.
+=============================================*/
+#define WL12XX_REG_INTERRUPT_NO_CLEAR (REGISTERS_BASE + 0x04E8)
+
+/*=============================================
+ Host Interrupt Status Clear on Read Register
+ 16bit,(Read only)
+ ------------------------------------------
+ The host can read this register to determine
+ which interrupts are active.
+ Reading this register clears it,
+ thus making all interrupts inactive.
+==============================================*/
+#define ACX_REG_INTERRUPT_CLEAR (REGISTERS_BASE + 0x04F8)
+
+/*=============================================
+ Host Interrupt Acknowledge Register
+ 16bit,(Write only)
+ ------------------------------------------
+ The host can set individual bits in this
+ register to clear (acknowledge) the corresp.
+ interrupt status bits in the HINT_STS_CLR and
+ HINT_STS_ND registers, thus making the
+ assotiated interrupt inactive. (0-no effect)
+==============================================*/
+#define WL12XX_REG_INTERRUPT_ACK (REGISTERS_BASE + 0x04F0)
+
+#define WL12XX_REG_RX_DRIVER_COUNTER (REGISTERS_BASE + 0x0538)
+
+/* Device Configuration registers*/
+#define SOR_CFG (REGISTERS_BASE + 0x0800)
+
+/* Embedded ARM CPU Control */
+
+/*===============================================
+ Halt eCPU - 32bit RW
+ ------------------------------------------
+ 0 HALT_ECPU Halt Embedded CPU - This bit is the
+ compliment of bit 1 (MDATA2) in the SOR_CFG register.
+ During a hardware reset, this bit holds
+ the inverse of MDATA2.
+ When downloading firmware from the host,
+ set this bit (pull down MDATA2).
+ The host clears this bit after downloading the firmware into
+ zero-wait-state SSRAM.
+ When loading firmware from Flash, clear this bit (pull up MDATA2)
+ so that the eCPU can run the bootloader code in Flash
+ HALT_ECPU eCPU State
+ --------------------
+ 1 halt eCPU
+ 0 enable eCPU
+ ===============================================*/
+#define WL12XX_REG_ECPU_CONTROL (REGISTERS_BASE + 0x0804)
+
+#define WL12XX_HI_CFG (REGISTERS_BASE + 0x0808)
+
+/*===============================================
+ EEPROM Burst Read Start - 32bit RW
+ ------------------------------------------
+ [31:1] Reserved
+ 0 ACX_EE_START - EEPROM Burst Read Start 0
+ Setting this bit starts a burst read from
+ the external EEPROM.
+ If this bit is set (after reset) before an EEPROM read/write,
+ the burst read starts at EEPROM address 0.
+ Otherwise, it starts at the address
+ following the address of the previous access.
+ TheWlan hardware hardware clears this bit automatically.
+
+ Default: 0x00000000
+*================================================*/
+#define ACX_REG_EE_START (REGISTERS_BASE + 0x080C)
+
+#define WL12XX_OCP_POR_CTR (REGISTERS_BASE + 0x09B4)
+#define WL12XX_OCP_DATA_WRITE (REGISTERS_BASE + 0x09B8)
+#define WL12XX_OCP_DATA_READ (REGISTERS_BASE + 0x09BC)
+#define WL12XX_OCP_CMD (REGISTERS_BASE + 0x09C0)
+
+#define WL12XX_HOST_WR_ACCESS (REGISTERS_BASE + 0x09F8)
+
+#define WL12XX_CHIP_ID_B (REGISTERS_BASE + 0x5674)
+
+#define WL12XX_ENABLE (REGISTERS_BASE + 0x5450)
+
+/* Power Management registers */
+#define WL12XX_ELP_CFG_MODE (REGISTERS_BASE + 0x5804)
+#define WL12XX_ELP_CMD (REGISTERS_BASE + 0x5808)
+#define WL12XX_PLL_CAL_TIME (REGISTERS_BASE + 0x5810)
+#define WL12XX_CLK_REQ_TIME (REGISTERS_BASE + 0x5814)
+#define WL12XX_CLK_BUF_TIME (REGISTERS_BASE + 0x5818)
+
+#define WL12XX_CFG_PLL_SYNC_CNT (REGISTERS_BASE + 0x5820)
+
+/* Scratch Pad registers*/
+#define WL12XX_SCR_PAD0 (REGISTERS_BASE + 0x5608)
+#define WL12XX_SCR_PAD1 (REGISTERS_BASE + 0x560C)
+#define WL12XX_SCR_PAD2 (REGISTERS_BASE + 0x5610)
+#define WL12XX_SCR_PAD3 (REGISTERS_BASE + 0x5614)
+#define WL12XX_SCR_PAD4 (REGISTERS_BASE + 0x5618)
+#define WL12XX_SCR_PAD4_SET (REGISTERS_BASE + 0x561C)
+#define WL12XX_SCR_PAD4_CLR (REGISTERS_BASE + 0x5620)
+#define WL12XX_SCR_PAD5 (REGISTERS_BASE + 0x5624)
+#define WL12XX_SCR_PAD5_SET (REGISTERS_BASE + 0x5628)
+#define WL12XX_SCR_PAD5_CLR (REGISTERS_BASE + 0x562C)
+#define WL12XX_SCR_PAD6 (REGISTERS_BASE + 0x5630)
+#define WL12XX_SCR_PAD7 (REGISTERS_BASE + 0x5634)
+#define WL12XX_SCR_PAD8 (REGISTERS_BASE + 0x5638)
+#define WL12XX_SCR_PAD9 (REGISTERS_BASE + 0x563C)
+
+/* Spare registers*/
+#define WL12XX_SPARE_A1 (REGISTERS_BASE + 0x0994)
+#define WL12XX_SPARE_A2 (REGISTERS_BASE + 0x0998)
+#define WL12XX_SPARE_A3 (REGISTERS_BASE + 0x099C)
+#define WL12XX_SPARE_A4 (REGISTERS_BASE + 0x09A0)
+#define WL12XX_SPARE_A5 (REGISTERS_BASE + 0x09A4)
+#define WL12XX_SPARE_A6 (REGISTERS_BASE + 0x09A8)
+#define WL12XX_SPARE_A7 (REGISTERS_BASE + 0x09AC)
+#define WL12XX_SPARE_A8 (REGISTERS_BASE + 0x09B0)
+#define WL12XX_SPARE_B1 (REGISTERS_BASE + 0x5420)
+#define WL12XX_SPARE_B2 (REGISTERS_BASE + 0x5424)
+#define WL12XX_SPARE_B3 (REGISTERS_BASE + 0x5428)
+#define WL12XX_SPARE_B4 (REGISTERS_BASE + 0x542C)
+#define WL12XX_SPARE_B5 (REGISTERS_BASE + 0x5430)
+#define WL12XX_SPARE_B6 (REGISTERS_BASE + 0x5434)
+#define WL12XX_SPARE_B7 (REGISTERS_BASE + 0x5438)
+#define WL12XX_SPARE_B8 (REGISTERS_BASE + 0x543C)
+
+#define WL12XX_PLL_PARAMETERS (REGISTERS_BASE + 0x6040)
+#define WL12XX_WU_COUNTER_PAUSE (REGISTERS_BASE + 0x6008)
+#define WL12XX_WELP_ARM_COMMAND (REGISTERS_BASE + 0x6100)
+#define WL12XX_DRPW_SCRATCH_START (DRPW_BASE + 0x002C)
+
+#define WL12XX_CMD_MBOX_ADDRESS 0x407B4
+
+#define ACX_REG_EEPROM_START_BIT BIT(1)
+
+/* Command/Information Mailbox Pointers */
+
+/*===============================================
+ Command Mailbox Pointer - 32bit RW
+ ------------------------------------------
+ This register holds the start address of
+ the command mailbox located in the Wlan hardware memory.
+ The host must read this pointer after a reset to
+ find the location of the command mailbox.
+ The Wlan hardware initializes the command mailbox
+ pointer with the default address of the command mailbox.
+ The command mailbox pointer is not valid until after
+ the host receives the Init Complete interrupt from
+ the Wlan hardware.
+ ===============================================*/
+#define WL12XX_REG_COMMAND_MAILBOX_PTR (WL12XX_SCR_PAD0)
+
+/*===============================================
+ Information Mailbox Pointer - 32bit RW
+ ------------------------------------------
+ This register holds the start address of
+ the information mailbox located in the Wlan hardware memory.
+ The host must read this pointer after a reset to find
+ the location of the information mailbox.
+ The Wlan hardware initializes the information mailbox pointer
+ with the default address of the information mailbox.
+ The information mailbox pointer is not valid
+ until after the host receives the Init Complete interrupt from
+ the Wlan hardware.
+ ===============================================*/
+#define WL12XX_REG_EVENT_MAILBOX_PTR (WL12XX_SCR_PAD1)
+
+/*===============================================
+ EEPROM Read/Write Request 32bit RW
+ ------------------------------------------
+ 1 EE_READ - EEPROM Read Request 1 - Setting this bit
+ loads a single byte of data into the EE_DATA
+ register from the EEPROM location specified in
+ the EE_ADDR register.
+ The Wlan hardware hardware clears this bit automatically.
+ EE_DATA is valid when this bit is cleared.
+
+ 0 EE_WRITE - EEPROM Write Request - Setting this bit
+ writes a single byte of data from the EE_DATA register into the
+ EEPROM location specified in the EE_ADDR register.
+ The Wlan hardware hardware clears this bit automatically.
+*===============================================*/
+#define ACX_EE_CTL_REG EE_CTL
+#define EE_WRITE 0x00000001ul
+#define EE_READ 0x00000002ul
+
+/*===============================================
+ EEPROM Address - 32bit RW
+ ------------------------------------------
+ This register specifies the address
+ within the EEPROM from/to which to read/write data.
+ ===============================================*/
+#define ACX_EE_ADDR_REG EE_ADDR
+
+/*===============================================
+ EEPROM Data - 32bit RW
+ ------------------------------------------
+ This register either holds the read 8 bits of
+ data from the EEPROM or the write data
+ to be written to the EEPROM.
+ ===============================================*/
+#define ACX_EE_DATA_REG EE_DATA
+
+/*===============================================
+ EEPROM Base Address - 32bit RW
+ ------------------------------------------
+ This register holds the upper nine bits
+ [23:15] of the 24-bit Wlan hardware memory
+ address for burst reads from EEPROM accesses.
+ The EEPROM provides the lower 15 bits of this address.
+ The MSB of the address from the EEPROM is ignored.
+ ===============================================*/
+#define ACX_EE_CFG EE_CFG
+
+/*===============================================
+ GPIO Output Values -32bit, RW
+ ------------------------------------------
+ [31:16] Reserved
+ [15: 0] Specify the output values (at the output driver inputs) for
+ GPIO[15:0], respectively.
+ ===============================================*/
+#define ACX_GPIO_OUT_REG GPIO_OUT
+#define ACX_MAX_GPIO_LINES 15
+
+/*===============================================
+ Contention window -32bit, RW
+ ------------------------------------------
+ [31:26] Reserved
+ [25:16] Max (0x3ff)
+ [15:07] Reserved
+ [06:00] Current contention window value - default is 0x1F
+ ===============================================*/
+#define ACX_CONT_WIND_CFG_REG CONT_WIND_CFG
+#define ACX_CONT_WIND_MIN_MASK 0x0000007f
+#define ACX_CONT_WIND_MAX 0x03ff0000
+
+#define REF_FREQ_19_2 0
+#define REF_FREQ_26_0 1
+#define REF_FREQ_38_4 2
+#define REF_FREQ_40_0 3
+#define REF_FREQ_33_6 4
+#define REF_FREQ_NUM 5
+
+#define LUT_PARAM_INTEGER_DIVIDER 0
+#define LUT_PARAM_FRACTIONAL_DIVIDER 1
+#define LUT_PARAM_ATTN_BB 2
+#define LUT_PARAM_ALPHA_BB 3
+#define LUT_PARAM_STOP_TIME_BB 4
+#define LUT_PARAM_BB_PLL_LOOP_FILTER 5
+#define LUT_PARAM_NUM 6
+
+#define WL12XX_EEPROMLESS_IND (WL12XX_SCR_PAD4)
+#define USE_EEPROM 0
+#define NVS_DATA_BUNDARY_ALIGNMENT 4
+
+/* Firmware image header size */
+#define FW_HDR_SIZE 8
+
+/******************************************************************************
+
+ CHANNELS, BAND & REG DOMAINS definitions
+
+******************************************************************************/
+
+#define SHORT_PREAMBLE_BIT BIT(0) /* CCK or Barker depending on the rate */
+#define OFDM_RATE_BIT BIT(6)
+#define PBCC_RATE_BIT BIT(7)
+
+enum {
+ CCK_LONG = 0,
+ CCK_SHORT = SHORT_PREAMBLE_BIT,
+ PBCC_LONG = PBCC_RATE_BIT,
+ PBCC_SHORT = PBCC_RATE_BIT | SHORT_PREAMBLE_BIT,
+ OFDM = OFDM_RATE_BIT
+};
+
+/******************************************************************************
+
+Transmit-Descriptor RATE-SET field definitions...
+
+Define a new "Rate-Set" for TX path that incorporates the
+Rate & Modulation info into a single 16-bit field.
+
+TxdRateSet_t:
+b15 - Indicates Preamble type (1=SHORT, 0=LONG).
+ Notes:
+ Must be LONG (0) for 1Mbps rate.
+ Does not apply (set to 0) for RevG-OFDM rates.
+b14 - Indicates PBCC encoding (1=PBCC, 0=not).
+ Notes:
+ Does not apply (set to 0) for rates 1 and 2 Mbps.
+ Does not apply (set to 0) for RevG-OFDM rates.
+b13 - Unused (set to 0).
+b12-b0 - Supported Rate indicator bits as defined below.
+
+******************************************************************************/
+
+#define OCP_CMD_LOOP 32
+#define OCP_CMD_WRITE 0x1
+#define OCP_CMD_READ 0x2
+#define OCP_READY_MASK BIT(18)
+#define OCP_STATUS_MASK (BIT(16) | BIT(17))
+#define OCP_STATUS_NO_RESP 0x00000
+#define OCP_STATUS_OK 0x10000
+#define OCP_STATUS_REQ_FAILED 0x20000
+#define OCP_STATUS_RESP_ERROR 0x30000
+
+#define OCP_REG_POLARITY 0x0064
+#define OCP_REG_CLK_TYPE 0x0448
+#define OCP_REG_CLK_POLARITY 0x0cb2
+#define OCP_REG_CLK_PULL 0x0cb4
+
+#define POLARITY_LOW BIT(1)
+#define NO_PULL (BIT(14) | BIT(15))
+
+#define FREF_CLK_TYPE_BITS 0xfffffe7f
+#define CLK_REQ_PRCM 0x100
+#define FREF_CLK_POLARITY_BITS 0xfffff8ff
+#define CLK_REQ_OUTN_SEL 0x700
+
+#define WU_COUNTER_PAUSE_VAL 0x3FF
+
+/* PLL configuration algorithm for wl128x */
+#define SYS_CLK_CFG_REG 0x2200
+/* Bit[0] - 0-TCXO, 1-FREF */
+#define MCS_PLL_CLK_SEL_FREF BIT(0)
+/* Bit[3:2] - 01-TCXO, 10-FREF */
+#define WL_CLK_REQ_TYPE_FREF BIT(3)
+#define WL_CLK_REQ_TYPE_PG2 (BIT(3) | BIT(2))
+/* Bit[4] - 0-TCXO, 1-FREF */
+#define PRCM_CM_EN_MUX_WLAN_FREF BIT(4)
+
+#define TCXO_ILOAD_INT_REG 0x2264
+#define TCXO_CLK_DETECT_REG 0x2266
+
+#define TCXO_DET_FAILED BIT(4)
+
+#define FREF_ILOAD_INT_REG 0x2084
+#define FREF_CLK_DETECT_REG 0x2086
+#define FREF_CLK_DETECT_FAIL BIT(4)
+
+/* Use this reg for masking during driver access */
+#define WL_SPARE_REG 0x2320
+#define WL_SPARE_VAL BIT(2)
+/* Bit[6:5:3] - mask wl write SYS_CLK_CFG[8:5:2:4] */
+#define WL_SPARE_MASK_8526 (BIT(6) | BIT(5) | BIT(3))
+
+#define PLL_LOCK_COUNTERS_REG 0xD8C
+#define PLL_LOCK_COUNTERS_COEX 0x0F
+#define PLL_LOCK_COUNTERS_MCS 0xF0
+#define MCS_PLL_OVERRIDE_REG 0xD90
+#define MCS_PLL_CONFIG_REG 0xD92
+#define MCS_SEL_IN_FREQ_MASK 0x0070
+#define MCS_SEL_IN_FREQ_SHIFT 4
+#define MCS_PLL_CONFIG_REG_VAL 0x73
+#define MCS_PLL_ENABLE_HP (BIT(0) | BIT(1))
+
+#define MCS_PLL_M_REG 0xD94
+#define MCS_PLL_N_REG 0xD96
+#define MCS_PLL_M_REG_VAL 0xC8
+#define MCS_PLL_N_REG_VAL 0x07
+
+#define SDIO_IO_DS 0xd14
+
+/* SDIO/wSPI DS configuration values */
+enum {
+ HCI_IO_DS_8MA = 0,
+ HCI_IO_DS_4MA = 1, /* default */
+ HCI_IO_DS_6MA = 2,
+ HCI_IO_DS_2MA = 3,
+};
+
+/* end PLL configuration algorithm for wl128x */
+
+/*
+ * Host Command Interrupt. Setting this bit masks
+ * the interrupt that the host issues to inform
+ * the FW that it has sent a command
+ * to the Wlan hardware Command Mailbox.
+ */
+#define WL12XX_INTR_TRIG_CMD BIT(0)
+
+/*
+ * Host Event Acknowlegde Interrupt. The host
+ * sets this bit to acknowledge that it received
+ * the unsolicited information from the event
+ * mailbox.
+ */
+#define WL12XX_INTR_TRIG_EVENT_ACK BIT(1)
+
+/*===============================================
+ HI_CFG Interface Configuration Register Values
+ ------------------------------------------
+ ===============================================*/
+#define HI_CFG_UART_ENABLE 0x00000004
+#define HI_CFG_RST232_ENABLE 0x00000008
+#define HI_CFG_CLOCK_REQ_SELECT 0x00000010
+#define HI_CFG_HOST_INT_ENABLE 0x00000020
+#define HI_CFG_VLYNQ_OUTPUT_ENABLE 0x00000040
+#define HI_CFG_HOST_INT_ACTIVE_LOW 0x00000080
+#define HI_CFG_UART_TX_OUT_GPIO_15 0x00000100
+#define HI_CFG_UART_TX_OUT_GPIO_14 0x00000200
+#define HI_CFG_UART_TX_OUT_GPIO_7 0x00000400
+
+#define HI_CFG_DEF_VAL \
+ (HI_CFG_UART_ENABLE | \
+ HI_CFG_RST232_ENABLE | \
+ HI_CFG_CLOCK_REQ_SELECT | \
+ HI_CFG_HOST_INT_ENABLE)
+
+#define WL127X_REG_FUSE_DATA_2_1 0x050a
+#define WL128X_REG_FUSE_DATA_2_1 0x2152
+#define PG_VER_MASK 0x3c
+#define PG_VER_OFFSET 2
+
+#define WL127X_PG_MAJOR_VER_MASK 0x3
+#define WL127X_PG_MAJOR_VER_OFFSET 0x0
+#define WL127X_PG_MINOR_VER_MASK 0xc
+#define WL127X_PG_MINOR_VER_OFFSET 0x2
+
+#define WL128X_PG_MAJOR_VER_MASK 0xc
+#define WL128X_PG_MAJOR_VER_OFFSET 0x2
+#define WL128X_PG_MINOR_VER_MASK 0x3
+#define WL128X_PG_MINOR_VER_OFFSET 0x0
+
+#define WL127X_PG_GET_MAJOR(pg_ver) ((pg_ver & WL127X_PG_MAJOR_VER_MASK) >> \
+ WL127X_PG_MAJOR_VER_OFFSET)
+#define WL127X_PG_GET_MINOR(pg_ver) ((pg_ver & WL127X_PG_MINOR_VER_MASK) >> \
+ WL127X_PG_MINOR_VER_OFFSET)
+#define WL128X_PG_GET_MAJOR(pg_ver) ((pg_ver & WL128X_PG_MAJOR_VER_MASK) >> \
+ WL128X_PG_MAJOR_VER_OFFSET)
+#define WL128X_PG_GET_MINOR(pg_ver) ((pg_ver & WL128X_PG_MINOR_VER_MASK) >> \
+ WL128X_PG_MINOR_VER_OFFSET)
+
+#define WL12XX_REG_FUSE_BD_ADDR_1 0x00310eb4
+#define WL12XX_REG_FUSE_BD_ADDR_2 0x00310eb8
+
+#endif
diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c
new file mode 100644
index 0000000..8d47539
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/scan.c
@@ -0,0 +1,512 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/ieee80211.h>
+#include "scan.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/tx.h"
+
+static int wl1271_get_scan_channels(struct wl1271 *wl,
+ struct cfg80211_scan_request *req,
+ struct basic_scan_channel_params *channels,
+ enum nl80211_band band, bool passive)
+{
+ struct conf_scan_settings *c = &wl->conf.scan;
+ int i, j;
+ u32 flags;
+
+ for (i = 0, j = 0;
+ i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
+ i++) {
+ flags = req->channels[i]->flags;
+
+ if (!test_bit(i, wl->scan.scanned_ch) &&
+ !(flags & IEEE80211_CHAN_DISABLED) &&
+ (req->channels[i]->band == band) &&
+ /*
+ * In passive scans, we scan all remaining
+ * channels, even if not marked as such.
+ * In active scans, we only scan channels not
+ * marked as passive.
+ */
+ (passive || !(flags & IEEE80211_CHAN_NO_IR))) {
+ wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
+ req->channels[i]->band,
+ req->channels[i]->center_freq);
+ wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
+ req->channels[i]->hw_value,
+ req->channels[i]->flags);
+ wl1271_debug(DEBUG_SCAN,
+ "max_antenna_gain %d, max_power %d",
+ req->channels[i]->max_antenna_gain,
+ req->channels[i]->max_power);
+ wl1271_debug(DEBUG_SCAN, "beacon_found %d",
+ req->channels[i]->beacon_found);
+
+ if (!passive) {
+ channels[j].min_duration =
+ cpu_to_le32(c->min_dwell_time_active);
+ channels[j].max_duration =
+ cpu_to_le32(c->max_dwell_time_active);
+ } else {
+ channels[j].min_duration =
+ cpu_to_le32(c->dwell_time_passive);
+ channels[j].max_duration =
+ cpu_to_le32(c->dwell_time_passive);
+ }
+ channels[j].early_termination = 0;
+ channels[j].tx_power_att = req->channels[i]->max_power;
+ channels[j].channel = req->channels[i]->hw_value;
+
+ memset(&channels[j].bssid_lsb, 0xff, 4);
+ memset(&channels[j].bssid_msb, 0xff, 2);
+
+ /* Mark the channels we already used */
+ set_bit(i, wl->scan.scanned_ch);
+
+ j++;
+ }
+ }
+
+ return j;
+}
+
+#define WL1271_NOTHING_TO_SCAN 1
+
+static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ enum nl80211_band band,
+ bool passive, u32 basic_rate)
+{
+ struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+ struct wl1271_cmd_scan *cmd;
+ struct wl1271_cmd_trigger_scan_to *trigger;
+ int ret;
+ u16 scan_options = 0;
+
+ /* skip active scans if we don't have SSIDs */
+ if (!passive && wl->scan.req->n_ssids == 0)
+ return WL1271_NOTHING_TO_SCAN;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
+ if (!cmd || !trigger) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (wl->conf.scan.split_scan_timeout)
+ scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN;
+
+ if (passive)
+ scan_options |= WL1271_SCAN_OPT_PASSIVE;
+
+ /* scan on the dev role if the regular one is not started */
+ if (wlcore_is_p2p_mgmt(wlvif))
+ cmd->params.role_id = wlvif->dev_role_id;
+ else
+ cmd->params.role_id = wlvif->role_id;
+
+ if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cmd->params.scan_options = cpu_to_le16(scan_options);
+
+ cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
+ cmd->channels,
+ band, passive);
+ if (cmd->params.n_ch == 0) {
+ ret = WL1271_NOTHING_TO_SCAN;
+ goto out;
+ }
+
+ cmd->params.tx_rate = cpu_to_le32(basic_rate);
+ cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
+ cmd->params.tid_trigger = CONF_TX_AC_ANY_TID;
+ cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
+
+ if (band == NL80211_BAND_2GHZ)
+ cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
+ else
+ cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
+
+ if (wl->scan.ssid_len) {
+ cmd->params.ssid_len = wl->scan.ssid_len;
+ memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
+ }
+
+ memcpy(cmd->addr, vif->addr, ETH_ALEN);
+
+ ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+ cmd->params.role_id, band,
+ wl->scan.ssid, wl->scan.ssid_len,
+ wl->scan.req->ie,
+ wl->scan.req->ie_len, NULL, 0, false);
+ if (ret < 0) {
+ wl1271_error("PROBE request template failed");
+ goto out;
+ }
+
+ trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout);
+ ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
+ sizeof(*trigger), 0);
+ if (ret < 0) {
+ wl1271_error("trigger scan to failed for hw scan");
+ goto out;
+ }
+
+ wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+
+ ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ wl1271_error("SCAN failed");
+ goto out;
+ }
+
+out:
+ kfree(cmd);
+ kfree(trigger);
+ return ret;
+}
+
+int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+ struct wl1271_cmd_header *cmd = NULL;
+ int ret = 0;
+
+ if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
+ return -EINVAL;
+
+ wl1271_debug(DEBUG_CMD, "cmd scan stop");
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd,
+ sizeof(*cmd), 0);
+ if (ret < 0) {
+ wl1271_error("cmd stop_scan failed");
+ goto out;
+ }
+out:
+ kfree(cmd);
+ return ret;
+}
+
+void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+ int ret = 0;
+ enum nl80211_band band;
+ u32 rate, mask;
+
+ switch (wl->scan.state) {
+ case WL1271_SCAN_STATE_IDLE:
+ break;
+
+ case WL1271_SCAN_STATE_2GHZ_ACTIVE:
+ band = NL80211_BAND_2GHZ;
+ mask = wlvif->bitrate_masks[band];
+ if (wl->scan.req->no_cck) {
+ mask &= ~CONF_TX_CCK_RATES;
+ if (!mask)
+ mask = CONF_TX_RATE_MASK_BASIC_P2P;
+ }
+ rate = wl1271_tx_min_rate_get(wl, mask);
+ ret = wl1271_scan_send(wl, wlvif, band, false, rate);
+ if (ret == WL1271_NOTHING_TO_SCAN) {
+ wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
+ wl1271_scan_stm(wl, wlvif);
+ }
+
+ break;
+
+ case WL1271_SCAN_STATE_2GHZ_PASSIVE:
+ band = NL80211_BAND_2GHZ;
+ mask = wlvif->bitrate_masks[band];
+ if (wl->scan.req->no_cck) {
+ mask &= ~CONF_TX_CCK_RATES;
+ if (!mask)
+ mask = CONF_TX_RATE_MASK_BASIC_P2P;
+ }
+ rate = wl1271_tx_min_rate_get(wl, mask);
+ ret = wl1271_scan_send(wl, wlvif, band, true, rate);
+ if (ret == WL1271_NOTHING_TO_SCAN) {
+ if (wl->enable_11a)
+ wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
+ else
+ wl->scan.state = WL1271_SCAN_STATE_DONE;
+ wl1271_scan_stm(wl, wlvif);
+ }
+
+ break;
+
+ case WL1271_SCAN_STATE_5GHZ_ACTIVE:
+ band = NL80211_BAND_5GHZ;
+ rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
+ ret = wl1271_scan_send(wl, wlvif, band, false, rate);
+ if (ret == WL1271_NOTHING_TO_SCAN) {
+ wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
+ wl1271_scan_stm(wl, wlvif);
+ }
+
+ break;
+
+ case WL1271_SCAN_STATE_5GHZ_PASSIVE:
+ band = NL80211_BAND_5GHZ;
+ rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
+ ret = wl1271_scan_send(wl, wlvif, band, true, rate);
+ if (ret == WL1271_NOTHING_TO_SCAN) {
+ wl->scan.state = WL1271_SCAN_STATE_DONE;
+ wl1271_scan_stm(wl, wlvif);
+ }
+
+ break;
+
+ case WL1271_SCAN_STATE_DONE:
+ wl->scan.failed = false;
+ cancel_delayed_work(&wl->scan_complete_work);
+ ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+ msecs_to_jiffies(0));
+ break;
+
+ default:
+ wl1271_error("invalid scan state");
+ break;
+ }
+
+ if (ret < 0) {
+ cancel_delayed_work(&wl->scan_complete_work);
+ ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+ msecs_to_jiffies(0));
+ }
+}
+
+static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd,
+ struct wlcore_scan_channels *cmd_channels)
+{
+ memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive));
+ memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active));
+ cmd->dfs = cmd_channels->dfs;
+ cmd->n_pactive_ch = cmd_channels->passive_active;
+
+ memcpy(cmd->channels_2, cmd_channels->channels_2,
+ sizeof(cmd->channels_2));
+ memcpy(cmd->channels_5, cmd_channels->channels_5,
+ sizeof(cmd->channels_5));
+ /* channels_4 are not supported, so no need to copy them */
+}
+
+int wl1271_scan_sched_scan_config(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies)
+{
+ struct wl1271_cmd_sched_scan_config *cfg = NULL;
+ struct wlcore_scan_channels *cfg_channels = NULL;
+ struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
+ int i, ret;
+ bool force_passive = !req->n_ssids;
+
+ wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
+
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ cfg->role_id = wlvif->role_id;
+ cfg->rssi_threshold = c->rssi_threshold;
+ cfg->snr_threshold = c->snr_threshold;
+ cfg->n_probe_reqs = c->num_probe_reqs;
+ /* cycles set to 0 it means infinite (until manually stopped) */
+ cfg->cycles = 0;
+ /* report APs when at least 1 is found */
+ cfg->report_after = 1;
+ /* don't stop scanning automatically when something is found */
+ cfg->terminate = 0;
+ cfg->tag = WL1271_SCAN_DEFAULT_TAG;
+ /* don't filter on BSS type */
+ cfg->bss_type = SCAN_BSS_TYPE_ANY;
+ /* currently NL80211 supports only a single interval */
+ for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
+ cfg->intervals[i] = cpu_to_le32(req->scan_plans[0].interval *
+ MSEC_PER_SEC);
+
+ cfg->ssid_len = 0;
+ ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
+ if (ret < 0)
+ goto out;
+
+ cfg->filter_type = ret;
+
+ wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
+
+ cfg_channels = kzalloc(sizeof(*cfg_channels), GFP_KERNEL);
+ if (!cfg_channels) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (!wlcore_set_scan_chan_params(wl, cfg_channels, req->channels,
+ req->n_channels, req->n_ssids,
+ SCAN_TYPE_PERIODIC)) {
+ wl1271_error("scan channel list is empty");
+ ret = -EINVAL;
+ goto out;
+ }
+ wl12xx_adjust_channels(cfg, cfg_channels);
+
+ if (!force_passive && cfg->active[0]) {
+ u8 band = NL80211_BAND_2GHZ;
+ ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+ wlvif->role_id, band,
+ req->ssids[0].ssid,
+ req->ssids[0].ssid_len,
+ ies->ies[band],
+ ies->len[band],
+ ies->common_ies,
+ ies->common_ie_len,
+ true);
+ if (ret < 0) {
+ wl1271_error("2.4GHz PROBE request template failed");
+ goto out;
+ }
+ }
+
+ if (!force_passive && cfg->active[1]) {
+ u8 band = NL80211_BAND_5GHZ;
+ ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+ wlvif->role_id, band,
+ req->ssids[0].ssid,
+ req->ssids[0].ssid_len,
+ ies->ies[band],
+ ies->len[band],
+ ies->common_ies,
+ ies->common_ie_len,
+ true);
+ if (ret < 0) {
+ wl1271_error("5GHz PROBE request template failed");
+ goto out;
+ }
+ }
+
+ wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg));
+
+ ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg,
+ sizeof(*cfg), 0);
+ if (ret < 0) {
+ wl1271_error("SCAN configuration failed");
+ goto out;
+ }
+out:
+ kfree(cfg_channels);
+ kfree(cfg);
+ return ret;
+}
+
+int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+ struct wl1271_cmd_sched_scan_start *start;
+ int ret = 0;
+
+ wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
+
+ if (wlvif->bss_type != BSS_TYPE_STA_BSS)
+ return -EOPNOTSUPP;
+
+ if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) &&
+ test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
+ return -EBUSY;
+
+ start = kzalloc(sizeof(*start), GFP_KERNEL);
+ if (!start)
+ return -ENOMEM;
+
+ start->role_id = wlvif->role_id;
+ start->tag = WL1271_SCAN_DEFAULT_TAG;
+
+ ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
+ sizeof(*start), 0);
+ if (ret < 0) {
+ wl1271_error("failed to send scan start command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(start);
+ return ret;
+}
+
+int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies)
+{
+ int ret;
+
+ ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
+ if (ret < 0)
+ return ret;
+
+ return wl1271_scan_sched_scan_start(wl, wlvif);
+}
+
+void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+ struct wl1271_cmd_sched_scan_stop *stop;
+ int ret = 0;
+
+ wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
+
+ /* FIXME: what to do if alloc'ing to stop fails? */
+ stop = kzalloc(sizeof(*stop), GFP_KERNEL);
+ if (!stop) {
+ wl1271_error("failed to alloc memory to send sched scan stop");
+ return;
+ }
+
+ stop->role_id = wlvif->role_id;
+ stop->tag = WL1271_SCAN_DEFAULT_TAG;
+
+ ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
+ sizeof(*stop), 0);
+ if (ret < 0) {
+ wl1271_error("failed to send sched scan stop command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(stop);
+}
+
+int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ struct cfg80211_scan_request *req)
+{
+ wl1271_scan_stm(wl, wlvif);
+ return 0;
+}
+
+void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+ wl1271_scan_stm(wl, wlvif);
+}
diff --git a/drivers/net/wireless/ti/wl12xx/scan.h b/drivers/net/wireless/ti/wl12xx/scan.h
new file mode 100644
index 0000000..427f9af
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/scan.h
@@ -0,0 +1,140 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_SCAN_H__
+#define __WL12XX_SCAN_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/scan.h"
+
+#define WL12XX_MAX_CHANNELS_5GHZ 23
+
+struct basic_scan_params {
+ /* Scan option flags (WL1271_SCAN_OPT_*) */
+ __le16 scan_options;
+ u8 role_id;
+ /* Number of scan channels in the list (maximum 30) */
+ u8 n_ch;
+ /* This field indicates the number of probe requests to send
+ per channel for an active scan */
+ u8 n_probe_reqs;
+ u8 tid_trigger;
+ u8 ssid_len;
+ u8 use_ssid_list;
+
+ /* Rate bit field for sending the probes */
+ __le32 tx_rate;
+
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ /* Band to scan */
+ u8 band;
+
+ u8 scan_tag;
+ u8 padding2[2];
+} __packed;
+
+struct basic_scan_channel_params {
+ /* Duration in TU to wait for frames on a channel for active scan */
+ __le32 min_duration;
+ __le32 max_duration;
+ __le32 bssid_lsb;
+ __le16 bssid_msb;
+ u8 early_termination;
+ u8 tx_power_att;
+ u8 channel;
+ /* FW internal use only! */
+ u8 dfs_candidate;
+ u8 activity_detected;
+ u8 pad;
+} __packed;
+
+struct wl1271_cmd_scan {
+ struct wl1271_cmd_header header;
+
+ struct basic_scan_params params;
+ struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
+
+ /* src mac address */
+ u8 addr[ETH_ALEN];
+ u8 padding[2];
+} __packed;
+
+struct wl1271_cmd_sched_scan_config {
+ struct wl1271_cmd_header header;
+
+ __le32 intervals[SCAN_MAX_CYCLE_INTERVALS];
+
+ s8 rssi_threshold; /* for filtering (in dBm) */
+ s8 snr_threshold; /* for filtering (in dB) */
+
+ u8 cycles; /* maximum number of scan cycles */
+ u8 report_after; /* report when this number of results are received */
+ u8 terminate; /* stop scanning after reporting */
+
+ u8 tag;
+ u8 bss_type; /* for filtering */
+ u8 filter_type;
+
+ u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+
+ u8 n_probe_reqs; /* Number of probes requests per channel */
+
+ u8 passive[SCAN_MAX_BANDS];
+ u8 active[SCAN_MAX_BANDS];
+
+ u8 dfs;
+
+ u8 n_pactive_ch; /* number of pactive (passive until fw detects energy)
+ channels in BG band */
+ u8 role_id;
+ u8 padding[1];
+ struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+ struct conn_scan_ch_params channels_5[WL12XX_MAX_CHANNELS_5GHZ];
+ struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+} __packed;
+
+struct wl1271_cmd_sched_scan_start {
+ struct wl1271_cmd_header header;
+
+ u8 tag;
+ u8 role_id;
+ u8 padding[2];
+} __packed;
+
+struct wl1271_cmd_sched_scan_stop {
+ struct wl1271_cmd_header header;
+
+ u8 tag;
+ u8 role_id;
+ u8 padding[2];
+} __packed;
+
+int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ struct cfg80211_scan_request *req);
+int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies);
+void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+#endif
diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h
new file mode 100644
index 0000000..5952e99
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h
@@ -0,0 +1,163 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_PRIV_H__
+#define __WL12XX_PRIV_H__
+
+#include "conf.h"
+
+/* WiLink 6/7 chip IDs */
+#define CHIP_ID_127X_PG10 (0x04030101)
+#define CHIP_ID_127X_PG20 (0x04030111)
+#define CHIP_ID_128X_PG10 (0x05030101)
+#define CHIP_ID_128X_PG20 (0x05030111)
+
+/* FW chip version for wl127x */
+#define WL127X_CHIP_VER 6
+/* minimum single-role FW version for wl127x */
+#define WL127X_IFTYPE_SR_VER 3
+#define WL127X_MAJOR_SR_VER 10
+#define WL127X_SUBTYPE_SR_VER WLCORE_FW_VER_IGNORE
+#define WL127X_MINOR_SR_VER 133
+/* minimum multi-role FW version for wl127x */
+#define WL127X_IFTYPE_MR_VER 5
+#define WL127X_MAJOR_MR_VER 7
+#define WL127X_SUBTYPE_MR_VER WLCORE_FW_VER_IGNORE
+#define WL127X_MINOR_MR_VER 42
+
+/* FW chip version for wl128x */
+#define WL128X_CHIP_VER 7
+/* minimum single-role FW version for wl128x */
+#define WL128X_IFTYPE_SR_VER 3
+#define WL128X_MAJOR_SR_VER 10
+#define WL128X_SUBTYPE_SR_VER WLCORE_FW_VER_IGNORE
+#define WL128X_MINOR_SR_VER 133
+/* minimum multi-role FW version for wl128x */
+#define WL128X_IFTYPE_MR_VER 5
+#define WL128X_MAJOR_MR_VER 7
+#define WL128X_SUBTYPE_MR_VER WLCORE_FW_VER_IGNORE
+#define WL128X_MINOR_MR_VER 42
+
+#define WL12XX_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
+
+#define WL12XX_NUM_TX_DESCRIPTORS 16
+#define WL12XX_NUM_RX_DESCRIPTORS 8
+
+#define WL12XX_NUM_MAC_ADDRESSES 2
+
+#define WL12XX_RX_BA_MAX_SESSIONS 3
+
+#define WL12XX_MAX_AP_STATIONS 8
+#define WL12XX_MAX_LINKS 12
+
+struct wl127x_rx_mem_pool_addr {
+ u32 addr;
+ u32 addr_extra;
+};
+
+struct wl12xx_priv {
+ struct wl12xx_priv_conf conf;
+
+ int ref_clock;
+ int tcxo_clock;
+
+ struct wl127x_rx_mem_pool_addr *rx_mem_addr;
+};
+
+/* Reference clock values */
+enum {
+ WL12XX_REFCLOCK_19 = 0, /* 19.2 MHz */
+ WL12XX_REFCLOCK_26 = 1, /* 26 MHz */
+ WL12XX_REFCLOCK_38 = 2, /* 38.4 MHz */
+ WL12XX_REFCLOCK_52 = 3, /* 52 MHz */
+ WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */
+ WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */
+};
+
+/* TCXO clock values */
+enum {
+ WL12XX_TCXOCLOCK_19_2 = 0, /* 19.2MHz */
+ WL12XX_TCXOCLOCK_26 = 1, /* 26 MHz */
+ WL12XX_TCXOCLOCK_38_4 = 2, /* 38.4MHz */
+ WL12XX_TCXOCLOCK_52 = 3, /* 52 MHz */
+ WL12XX_TCXOCLOCK_16_368 = 4, /* 16.368 MHz */
+ WL12XX_TCXOCLOCK_32_736 = 5, /* 32.736 MHz */
+ WL12XX_TCXOCLOCK_16_8 = 6, /* 16.8 MHz */
+ WL12XX_TCXOCLOCK_33_6 = 7, /* 33.6 MHz */
+};
+
+struct wl12xx_clock {
+ u32 freq;
+ bool xtal;
+ u8 hw_idx;
+};
+
+struct wl12xx_fw_packet_counters {
+ /* Cumulative counter of released packets per AC */
+ u8 tx_released_pkts[NUM_TX_QUEUES];
+
+ /* Cumulative counter of freed packets per HLID */
+ u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS];
+
+ /* Cumulative counter of released Voice memory blocks */
+ u8 tx_voice_released_blks;
+
+ /* Tx rate of the last transmitted packet */
+ u8 tx_last_rate;
+
+ u8 padding[2];
+} __packed;
+
+/* FW status registers */
+struct wl12xx_fw_status {
+ __le32 intr;
+ u8 fw_rx_counter;
+ u8 drv_rx_counter;
+ u8 reserved;
+ u8 tx_results_counter;
+ __le32 rx_pkt_descs[WL12XX_NUM_RX_DESCRIPTORS];
+
+ __le32 fw_localtime;
+
+ /*
+ * A bitmap (where each bit represents a single HLID)
+ * to indicate if the station is in PS mode.
+ */
+ __le32 link_ps_bitmap;
+
+ /*
+ * A bitmap (where each bit represents a single HLID) to indicate
+ * if the station is in Fast mode
+ */
+ __le32 link_fast_bitmap;
+
+ /* Cumulative counter of total released mem blocks since FW-reset */
+ __le32 total_released_blks;
+
+ /* Size (in Memory Blocks) of TX pool */
+ __le32 tx_total;
+
+ struct wl12xx_fw_packet_counters counters;
+
+ __le32 log_start_addr;
+} __packed;
+
+#endif /* __WL12XX_PRIV_H__ */