]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/wireless/ti/wl18xx/main.c
Merge branch 'wl12xx-next' into for-linville
[karo-tx-linux.git] / drivers / net / wireless / ti / wl18xx / main.c
index 8d8c1f8c63b79b8775a70c85fb4b4669380afbb7..0be1cfc17a86f3878d30da87159237eacdaba33b 100644 (file)
 
 #include "reg.h"
 #include "conf.h"
+#include "cmd.h"
 #include "acx.h"
 #include "tx.h"
 #include "wl18xx.h"
 #include "io.h"
+#include "scan.h"
+#include "event.h"
 #include "debugfs.h"
 
 #define WL18XX_RX_CHECKSUM_MASK      0x40
@@ -334,6 +337,8 @@ static struct wlcore_conf wl18xx_conf = {
                .tmpl_short_retry_limit      = 10,
                .tmpl_long_retry_limit       = 10,
                .tx_watchdog_timeout         = 5000,
+               .slow_link_thold             = 3,
+               .fast_link_thold             = 30,
        },
        .conn = {
                .wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
@@ -391,8 +396,10 @@ static struct wlcore_conf wl18xx_conf = {
        .scan = {
                .min_dwell_time_active        = 7500,
                .max_dwell_time_active        = 30000,
-               .min_dwell_time_passive       = 100000,
-               .max_dwell_time_passive       = 100000,
+               .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,
        },
@@ -489,6 +496,10 @@ static struct wlcore_conf wl18xx_conf = {
                .increase_time              = 1,
                .window_size                = 16,
        },
+       .recovery = {
+               .bug_on_recovery            = 0,
+               .no_recovery                = 0,
+       },
 };
 
 static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
@@ -501,7 +512,6 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .clock_valid_on_wake_up         = 0x00,
                .secondary_clock_setting_time   = 0x05,
                .board_type                     = BOARD_TYPE_HDK_18XX,
-               .rdl                            = 0x01,
                .auto_detect                    = 0x00,
                .dedicated_fem                  = FEM_NONE,
                .low_band_component             = COMPONENT_3_WAY_SWITCH,
@@ -517,14 +527,39 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .enable_clpc                    = 0x00,
                .enable_tx_low_pwr_on_siso_rdl  = 0x00,
                .rx_profile                     = 0x00,
-               .pwr_limit_reference_11_abg     = 0xc8,
+               .pwr_limit_reference_11_abg     = 0x64,
+               .per_chan_pwr_limit_arr_11abg   = {
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+               .pwr_limit_reference_11p        = 0x64,
+               .per_chan_pwr_limit_arr_11p     = { 0xff, 0xff, 0xff, 0xff,
+                                                   0xff, 0xff, 0xff },
                .psat                           = 0,
-               .low_power_val                  = 0x00,
-               .med_power_val                  = 0x0a,
-               .high_power_val                 = 0x1e,
+               .low_power_val                  = 0x08,
+               .med_power_val                  = 0x12,
+               .high_power_val                 = 0x18,
+               .low_power_val_2nd              = 0x05,
+               .med_power_val_2nd              = 0x0a,
+               .high_power_val_2nd             = 0x14,
                .external_pa_dc2dc              = 0,
-               .number_of_assembled_ant2_4     = 1,
+               .number_of_assembled_ant2_4     = 2,
                .number_of_assembled_ant5       = 1,
+               .tx_rf_margin                   = 1,
        },
 };
 
@@ -595,7 +630,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
 };
 
 /* TODO: maybe move to a new header file? */
-#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw.bin"
+#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-2.bin"
 
 static int wl18xx_identify_chip(struct wl1271 *wl)
 {
@@ -608,15 +643,18 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
                wl->sr_fw_name = WL18XX_FW_NAME;
                /* wl18xx uses the same firmware for PLT */
                wl->plt_fw_name = WL18XX_FW_NAME;
-               wl->quirks |= WLCORE_QUIRK_NO_ELP |
-                             WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
+               wl->quirks |= WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
                              WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
                              WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN |
-                             WLCORE_QUIRK_TX_PAD_LAST_FRAME;
-
-               wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, WL18XX_IFTYPE_VER,
-                                     WL18XX_MAJOR_VER, WL18XX_SUBTYPE_VER,
-                                     WL18XX_MINOR_VER);
+                             WLCORE_QUIRK_TX_PAD_LAST_FRAME |
+                             WLCORE_QUIRK_REGDOMAIN_CONF |
+                             WLCORE_QUIRK_DUAL_PROBE_TMPL;
+
+               wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER,
+                                     WL18XX_IFTYPE_VER,  WL18XX_MAJOR_VER,
+                                     WL18XX_SUBTYPE_VER, WL18XX_MINOR_VER,
+                                     /* there's no separate multi-role FW */
+                                     0, 0, 0, 0);
                break;
        case CHIP_ID_185x_PG10:
                wl1271_warning("chip id 0x%x (185x PG10) is deprecated",
@@ -630,6 +668,11 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
                goto out;
        }
 
+       wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
+       wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+       wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC;
+       wl->sched_scan_templ_id_5 = CMD_TEMPL_PROBE_REQ_5_PERIODIC;
+       wl->max_channels_5 = WL18XX_MAX_CHANNELS_5GHZ;
 out:
        return ret;
 }
@@ -843,6 +886,20 @@ static int wl18xx_boot(struct wl1271 *wl)
        if (ret < 0)
                goto out;
 
+       wl->event_mask = BSS_LOSS_EVENT_ID |
+               SCAN_COMPLETE_EVENT_ID |
+               RSSI_SNR_TRIGGER_0_EVENT_ID |
+               PERIODIC_SCAN_COMPLETE_EVENT_ID |
+               PERIODIC_SCAN_REPORT_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 |
+               MAX_TX_FAILURE_EVENT_ID |
+               CHANNEL_SWITCH_COMPLETE_EVENT_ID |
+               DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -964,7 +1021,7 @@ static int wl18xx_hw_init(struct wl1271 *wl)
 
        /* (re)init private structures. Relevant on recovery as well. */
        priv->last_fw_rls_idx = 0;
-       priv->extra_spare_vif_count = 0;
+       priv->extra_spare_key_count = 0;
 
        /* set the default amount of spare blocks in the bitmap */
        ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE);
@@ -1022,7 +1079,12 @@ static bool wl18xx_is_mimo_supported(struct wl1271 *wl)
 {
        struct wl18xx_priv *priv = wl->priv;
 
-       return priv->conf.phy.number_of_assembled_ant2_4 >= 2;
+       /* only support MIMO with multiple antennas, and when SISO
+        * is not forced through config
+        */
+       return (priv->conf.phy.number_of_assembled_ant2_4 >= 2) &&
+              (priv->conf.ht.mode != HT_MODE_WIDE) &&
+              (priv->conf.ht.mode != HT_MODE_SISO20);
 }
 
 /*
@@ -1223,8 +1285,8 @@ static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
 {
        struct wl18xx_priv *priv = wl->priv;
 
-       /* If we have VIFs requiring extra spare, indulge them */
-       if (priv->extra_spare_vif_count)
+       /* If we have keys requiring extra spare, indulge them */
+       if (priv->extra_spare_key_count)
                return WL18XX_TX_HW_EXTRA_BLOCK_SPARE;
 
        return WL18XX_TX_HW_BLOCK_SPARE;
@@ -1236,42 +1298,48 @@ static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                          struct ieee80211_key_conf *key_conf)
 {
        struct wl18xx_priv *priv = wl->priv;
-       bool change_spare = false;
+       bool change_spare = false, special_enc;
        int ret;
 
+       wl1271_debug(DEBUG_CRYPT, "extra spare keys before: %d",
+                    priv->extra_spare_key_count);
+
+       special_enc = key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
+                     key_conf->cipher == WLAN_CIPHER_SUITE_TKIP;
+
+       ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
+       if (ret < 0)
+               goto out;
+
        /*
-        * when adding the first or removing the last GEM/TKIP interface,
+        * when adding the first or removing the last GEM/TKIP key,
         * we have to adjust the number of spare blocks.
         */
-       change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
-               key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) &&
-               ((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) ||
-                (priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY));
+       if (special_enc) {
+               if (cmd == SET_KEY) {
+                       /* first key */
+                       change_spare = (priv->extra_spare_key_count == 0);
+                       priv->extra_spare_key_count++;
+               } else if (cmd == DISABLE_KEY) {
+                       /* last key */
+                       change_spare = (priv->extra_spare_key_count == 1);
+                       priv->extra_spare_key_count--;
+               }
+       }
 
-       /* no need to change spare - just regular set_key */
-       if (!change_spare)
-               return wlcore_set_key(wl, cmd, vif, sta, key_conf);
+       wl1271_debug(DEBUG_CRYPT, "extra spare keys after: %d",
+                    priv->extra_spare_key_count);
 
-       ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
-       if (ret < 0)
+       if (!change_spare)
                goto out;
 
        /* key is now set, change the spare blocks */
-       if (cmd == SET_KEY) {
+       if (priv->extra_spare_key_count)
                ret = wl18xx_set_host_cfg_bitmap(wl,
                                        WL18XX_TX_HW_EXTRA_BLOCK_SPARE);
-               if (ret < 0)
-                       goto out;
-
-               priv->extra_spare_vif_count++;
-       } else {
+       else
                ret = wl18xx_set_host_cfg_bitmap(wl,
                                        WL18XX_TX_HW_BLOCK_SPARE);
-               if (ret < 0)
-                       goto out;
-
-               priv->extra_spare_vif_count--;
-       }
 
 out:
        return ret;
@@ -1296,6 +1364,92 @@ static u32 wl18xx_pre_pkt_send(struct wl1271 *wl,
        return buf_offset;
 }
 
+static void wl18xx_sta_rc_update(struct wl1271 *wl,
+                                struct wl12xx_vif *wlvif,
+                                struct ieee80211_sta *sta,
+                                u32 changed)
+{
+       bool wide = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide);
+
+       if (!(changed & IEEE80211_RC_BW_CHANGED))
+               return;
+
+       mutex_lock(&wl->mutex);
+
+       /* sanity */
+       if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
+               goto out;
+
+       /* ignore the change before association */
+       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+               goto out;
+
+       /*
+        * If we started out as wide, we can change the operation mode. If we
+        * thought this was a 20mhz AP, we have to reconnect
+        */
+       if (wlvif->sta.role_chan_type == NL80211_CHAN_HT40MINUS ||
+           wlvif->sta.role_chan_type == NL80211_CHAN_HT40PLUS)
+               wl18xx_acx_peer_ht_operation_mode(wl, wlvif->sta.hlid, wide);
+       else
+               ieee80211_connection_loss(wl12xx_wlvif_to_vif(wlvif));
+
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+static int wl18xx_set_peer_cap(struct wl1271 *wl,
+                              struct ieee80211_sta_ht_cap *ht_cap,
+                              bool allow_ht_operation,
+                              u32 rate_set, u8 hlid)
+{
+       return wl18xx_acx_set_peer_cap(wl, ht_cap, allow_ht_operation,
+                                      rate_set, hlid);
+}
+
+static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+                                struct wl1271_link *lnk)
+{
+       u8 thold;
+       struct wl18xx_fw_status_priv *status_priv =
+               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+
+       /* suspended links are never high priority */
+       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+               return false;
+
+       /* the priority thresholds are taken from FW */
+       if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
+           !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
+               thold = status_priv->tx_fast_link_prio_threshold;
+       else
+               thold = status_priv->tx_slow_link_prio_threshold;
+
+       return lnk->allocated_pkts < thold;
+}
+
+static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
+                               struct wl1271_link *lnk)
+{
+       u8 thold;
+       struct wl18xx_fw_status_priv *status_priv =
+               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+
+       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+               thold = status_priv->tx_suspend_threshold;
+       else if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
+                !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
+               thold = status_priv->tx_fast_stop_threshold;
+       else
+               thold = status_priv->tx_slow_stop_threshold;
+
+       return lnk->allocated_pkts < thold;
+}
+
 static int wl18xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl18xx_ops = {
@@ -1305,6 +1459,8 @@ static struct wlcore_ops wl18xx_ops = {
        .plt_init       = wl18xx_plt_init,
        .trigger_cmd    = wl18xx_trigger_cmd,
        .ack_event      = wl18xx_ack_event,
+       .wait_for_event = wl18xx_wait_for_event,
+       .process_mailbox_events = wl18xx_process_mailbox_events,
        .calc_tx_blocks = wl18xx_calc_tx_blocks,
        .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks,
        .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len,
@@ -1320,16 +1476,26 @@ static struct wlcore_ops wl18xx_ops = {
        .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask,
        .get_mac        = wl18xx_get_mac,
        .debugfs_init   = wl18xx_debugfs_add_files,
+       .scan_start     = wl18xx_scan_start,
+       .scan_stop      = wl18xx_scan_stop,
+       .sched_scan_start       = wl18xx_sched_scan_start,
+       .sched_scan_stop        = wl18xx_scan_sched_scan_stop,
        .handle_static_data     = wl18xx_handle_static_data,
        .get_spare_blocks = wl18xx_get_spare_blocks,
        .set_key        = wl18xx_set_key,
+       .channel_switch = wl18xx_cmd_channel_switch,
        .pre_pkt_send   = wl18xx_pre_pkt_send,
+       .sta_rc_update  = wl18xx_sta_rc_update,
+       .set_peer_cap   = wl18xx_set_peer_cap,
+       .lnk_high_prio  = wl18xx_lnk_high_prio,
+       .lnk_low_prio   = wl18xx_lnk_low_prio,
 };
 
 /* HT cap appropriate for wide channels in 2Ghz */
 static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
        .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
-              IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40,
+              IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1343,7 +1509,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
 /* HT cap appropriate for wide channels in 5Ghz */
 static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
        .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
-              IEEE80211_HT_CAP_SUP_WIDTH_20_40,
+              IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1356,7 +1523,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
 
 /* HT cap appropriate for SISO 20 */
 static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
-       .cap = IEEE80211_HT_CAP_SGI_20,
+       .cap = IEEE80211_HT_CAP_SGI_20 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1369,7 +1537,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
 
 /* HT cap appropriate for MIMO rates in 20mhz channel */
 static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
-       .cap = IEEE80211_HT_CAP_SGI_20,
+       .cap = IEEE80211_HT_CAP_SGI_20 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1387,7 +1556,8 @@ static int wl18xx_setup(struct wl1271 *wl)
 
        wl->rtable = wl18xx_rtable;
        wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS;
-       wl->num_rx_desc = WL18XX_NUM_TX_DESCRIPTORS;
+       wl->num_rx_desc = WL18XX_NUM_RX_DESCRIPTORS;
+       wl->num_channels = 2;
        wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES;
        wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
@@ -1506,7 +1676,8 @@ static int wl18xx_probe(struct platform_device *pdev)
        int ret;
 
        hw = wlcore_alloc_hw(sizeof(struct wl18xx_priv),
-                            WL18XX_AGGR_BUFFER_SIZE);
+                            WL18XX_AGGR_BUFFER_SIZE,
+                            sizeof(struct wl18xx_event_mailbox));
        if (IS_ERR(hw)) {
                wl1271_error("can't allocate hw");
                ret = PTR_ERR(hw);