Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[kernel.git] / drivers / net / wireless / iwlwifi / iwl-agn.c
index 1c9866d..771b03c 100644 (file)
@@ -657,6 +657,131 @@ static void iwl_bg_statistics_periodic(unsigned long data)
        iwl_send_statistics_request(priv, CMD_ASYNC, false);
 }
 
+
+static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
+                                       u32 start_idx, u32 num_events,
+                                       u32 mode)
+{
+       u32 i;
+       u32 ptr;        /* SRAM byte address of log data */
+       u32 ev, time, data; /* event log data */
+       unsigned long reg_flags;
+
+       if (mode == 0)
+               ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32));
+       else
+               ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32));
+
+       /* Make sure device is powered up for SRAM reads */
+       spin_lock_irqsave(&priv->reg_lock, reg_flags);
+       if (iwl_grab_nic_access(priv)) {
+               spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+               return;
+       }
+
+       /* Set starting address; reads will auto-increment */
+       _iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr);
+       rmb();
+
+       /*
+        * "time" is actually "data" for mode 0 (no timestamp).
+        * place event id # at far right for easier visual parsing.
+        */
+       for (i = 0; i < num_events; i++) {
+               ev = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+               time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+               if (mode == 0) {
+                       trace_iwlwifi_dev_ucode_cont_event(priv,
+                                                       0, time, ev);
+               } else {
+                       data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+                       trace_iwlwifi_dev_ucode_cont_event(priv,
+                                               time, data, ev);
+               }
+       }
+       /* Allow device to power down */
+       iwl_release_nic_access(priv);
+       spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+}
+
+void iwl_continuous_event_trace(struct iwl_priv *priv)
+{
+       u32 capacity;   /* event log capacity in # entries */
+       u32 base;       /* SRAM byte address of event log header */
+       u32 mode;       /* 0 - no timestamp, 1 - timestamp recorded */
+       u32 num_wraps;  /* # times uCode wrapped to top of log */
+       u32 next_entry; /* index of next entry to be written by uCode */
+
+       if (priv->ucode_type == UCODE_INIT)
+               base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr);
+       else
+               base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
+       if (priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
+               capacity = iwl_read_targ_mem(priv, base);
+               num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32)));
+               mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32)));
+               next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32)));
+       } else
+               return;
+
+       if (num_wraps == priv->event_log.num_wraps) {
+               iwl_print_cont_event_trace(priv,
+                                      base, priv->event_log.next_entry,
+                                      next_entry - priv->event_log.next_entry,
+                                      mode);
+               priv->event_log.non_wraps_count++;
+       } else {
+               if ((num_wraps - priv->event_log.num_wraps) > 1)
+                       priv->event_log.wraps_more_count++;
+               else
+                       priv->event_log.wraps_once_count++;
+               trace_iwlwifi_dev_ucode_wrap_event(priv,
+                               num_wraps - priv->event_log.num_wraps,
+                               next_entry, priv->event_log.next_entry);
+               if (next_entry < priv->event_log.next_entry) {
+                       iwl_print_cont_event_trace(priv, base,
+                              priv->event_log.next_entry,
+                              capacity - priv->event_log.next_entry,
+                              mode);
+
+                       iwl_print_cont_event_trace(priv, base, 0,
+                               next_entry, mode);
+               } else {
+                       iwl_print_cont_event_trace(priv, base,
+                              next_entry, capacity - next_entry,
+                              mode);
+
+                       iwl_print_cont_event_trace(priv, base, 0,
+                               next_entry, mode);
+               }
+       }
+       priv->event_log.num_wraps = num_wraps;
+       priv->event_log.next_entry = next_entry;
+}
+
+/**
+ * iwl_bg_ucode_trace - Timer callback to log ucode event
+ *
+ * The timer is continually set to execute every
+ * UCODE_TRACE_PERIOD milliseconds after the last timer expired
+ * this function is to perform continuous uCode event logging operation
+ * if enabled
+ */
+static void iwl_bg_ucode_trace(unsigned long data)
+{
+       struct iwl_priv *priv = (struct iwl_priv *)data;
+
+       if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+               return;
+
+       if (priv->event_log.ucode_trace) {
+               iwl_continuous_event_trace(priv);
+               /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */
+               mod_timer(&priv->ucode_trace,
+                        jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD));
+       }
+}
+
 static void iwl_rx_beacon_notif(struct iwl_priv *priv,
                                struct iwl_rx_mem_buffer *rxb)
 {
@@ -689,12 +814,14 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
        u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
        unsigned long status = priv->status;
 
-       IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s\n",
+       IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n",
                          (flags & HW_CARD_DISABLED) ? "Kill" : "On",
-                         (flags & SW_CARD_DISABLED) ? "Kill" : "On");
+                         (flags & SW_CARD_DISABLED) ? "Kill" : "On",
+                         (flags & CT_CARD_DISABLED) ?
+                         "Reached" : "Not reached");
 
        if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED |
-                    RF_CARD_DISABLED)) {
+                    CT_CARD_DISABLED)) {
 
                iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
                            CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
@@ -708,10 +835,10 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
                        iwl_write_direct32(priv, HBUS_TARG_MBX_C,
                                        HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
                }
-               if (flags & RF_CARD_DISABLED)
+               if (flags & CT_CARD_DISABLED)
                        iwl_tt_enter_ct_kill(priv);
        }
-       if (!(flags & RF_CARD_DISABLED))
+       if (!(flags & CT_CARD_DISABLED))
                iwl_tt_exit_ct_kill(priv);
 
        if (flags & HW_CARD_DISABLED)
@@ -1705,8 +1832,9 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv)
  * iwl_print_event_log - Dump error event log to syslog
  *
  */
-static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
-                               u32 num_events, u32 mode)
+static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
+                              u32 num_events, u32 mode,
+                              int pos, char **buf, size_t bufsz)
 {
        u32 i;
        u32 base;       /* SRAM byte address of event log header */
@@ -1716,7 +1844,7 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
        unsigned long reg_flags;
 
        if (num_events == 0)
-               return;
+               return pos;
        if (priv->ucode_type == UCODE_INIT)
                base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr);
        else
@@ -1744,27 +1872,44 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
                time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
                if (mode == 0) {
                        /* data, ev */
-                       trace_iwlwifi_dev_ucode_event(priv, 0, time, ev);
-                       IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev);
+                       if (bufsz) {
+                               pos += scnprintf(*buf + pos, bufsz - pos,
+                                               "EVT_LOG:0x%08x:%04u\n",
+                                               time, ev);
+                       } else {
+                               trace_iwlwifi_dev_ucode_event(priv, 0,
+                                       time, ev);
+                               IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n",
+                                       time, ev);
+                       }
                } else {
                        data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
-                       IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
+                       if (bufsz) {
+                               pos += scnprintf(*buf + pos, bufsz - pos,
+                                               "EVT_LOGT:%010u:0x%08x:%04u\n",
+                                                time, data, ev);
+                       } else {
+                               IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
                                        time, data, ev);
-                       trace_iwlwifi_dev_ucode_event(priv, time, data, ev);
+                               trace_iwlwifi_dev_ucode_event(priv, time,
+                                       data, ev);
+                       }
                }
        }
 
        /* Allow device to power down */
        iwl_release_nic_access(priv);
        spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+       return pos;
 }
 
 /**
  * iwl_print_last_event_logs - Dump the newest # of event log to syslog
  */
-static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
-                                     u32 num_wraps, u32 next_entry,
-                                     u32 size, u32 mode)
+static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
+                                   u32 num_wraps, u32 next_entry,
+                                   u32 size, u32 mode,
+                                   int pos, char **buf, size_t bufsz)
 {
        /*
         * display the newest DEFAULT_LOG_ENTRIES entries
@@ -1772,21 +1917,26 @@ static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
         */
        if (num_wraps) {
                if (next_entry < size) {
-                       iwl_print_event_log(priv,
-                                       capacity - (size - next_entry),
-                                       size - next_entry, mode);
-                       iwl_print_event_log(priv, 0,
-                                   next_entry, mode);
+                       pos = iwl_print_event_log(priv,
+                                               capacity - (size - next_entry),
+                                               size - next_entry, mode,
+                                               pos, buf, bufsz);
+                       pos = iwl_print_event_log(priv, 0,
+                                                 next_entry, mode,
+                                                 pos, buf, bufsz);
                } else
-                       iwl_print_event_log(priv, next_entry - size,
-                                   size, mode);
+                       pos = iwl_print_event_log(priv, next_entry - size,
+                                                 size, mode, pos, buf, bufsz);
        } else {
-               if (next_entry < size)
-                       iwl_print_event_log(priv, 0, next_entry, mode);
-               else
-                       iwl_print_event_log(priv, next_entry - size,
-                                           size, mode);
+               if (next_entry < size) {
+                       pos = iwl_print_event_log(priv, 0, next_entry,
+                                                 mode, pos, buf, bufsz);
+               } else {
+                       pos = iwl_print_event_log(priv, next_entry - size,
+                                                 size, mode, pos, buf, bufsz);
+               }
        }
+       return pos;
 }
 
 /* For sanity check only.  Actual size is determined by uCode, typ. 512 */
@@ -1794,7 +1944,8 @@ static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
 
 #define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
 
-void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
+int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
+                           char **buf, bool display)
 {
        u32 base;       /* SRAM byte address of event log header */
        u32 capacity;   /* event log capacity in # entries */
@@ -1802,6 +1953,8 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
        u32 num_wraps;  /* # times uCode wrapped to top of log */
        u32 next_entry; /* index of next entry to be written by uCode */
        u32 size;       /* # entries that we'll print */
+       int pos = 0;
+       size_t bufsz = 0;
 
        if (priv->ucode_type == UCODE_INIT)
                base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr);
@@ -1812,7 +1965,7 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
                IWL_ERR(priv,
                        "Invalid event log pointer 0x%08X for %s uCode\n",
                        base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT");
-               return;
+               return pos;
        }
 
        /* event log header */
@@ -1838,7 +1991,7 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
        /* bail out if nothing in log */
        if (size == 0) {
                IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
-               return;
+               return pos;
        }
 
 #ifdef CONFIG_IWLWIFI_DEBUG
@@ -1853,6 +2006,15 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
                size);
 
 #ifdef CONFIG_IWLWIFI_DEBUG
+       if (display) {
+               if (full_log)
+                       bufsz = capacity * 48;
+               else
+                       bufsz = size * 48;
+               *buf = kmalloc(bufsz, GFP_KERNEL);
+               if (!*buf)
+                       return pos;
+       }
        if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) {
                /*
                 * if uCode has wrapped back to top of log,
@@ -1860,17 +2022,22 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
                 * i.e the next one that uCode would fill.
                 */
                if (num_wraps)
-                       iwl_print_event_log(priv, next_entry,
-                                           capacity - next_entry, mode);
+                       pos = iwl_print_event_log(priv, next_entry,
+                                               capacity - next_entry, mode,
+                                               pos, buf, bufsz);
                /* (then/else) start at top of log */
-               iwl_print_event_log(priv, 0, next_entry, mode);
+               pos = iwl_print_event_log(priv, 0,
+                                         next_entry, mode, pos, buf, bufsz);
        } else
-               iwl_print_last_event_logs(priv, capacity, num_wraps,
-                                       next_entry, size, mode);
+               pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
+                                               next_entry, size, mode,
+                                               pos, buf, bufsz);
 #else
-       iwl_print_last_event_logs(priv, capacity, num_wraps,
-                               next_entry, size, mode);
+       pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
+                                       next_entry, size, mode,
+                                       pos, buf, bufsz);
 #endif
+       return pos;
 }
 
 /**
@@ -2456,6 +2623,10 @@ static int iwl_setup_mac(struct iwl_priv *priv)
                hw->flags |= IEEE80211_HW_SUPPORTS_PS |
                             IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
 
+       if (priv->cfg->sku & IWL_SKU_N)
+               hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
+                            IEEE80211_HW_SUPPORTS_STATIC_SMPS;
+
        hw->sta_data_size = sizeof(struct iwl_station_priv);
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
@@ -3126,6 +3297,10 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
        priv->statistics_periodic.data = (unsigned long)priv;
        priv->statistics_periodic.function = iwl_bg_statistics_periodic;
 
+       init_timer(&priv->ucode_trace);
+       priv->ucode_trace.data = (unsigned long)priv;
+       priv->ucode_trace.function = iwl_bg_ucode_trace;
+
        if (!priv->cfg->use_isr_legacy)
                tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
                        iwl_irq_tasklet, (unsigned long)priv);
@@ -3144,6 +3319,7 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
        cancel_delayed_work(&priv->alive_start);
        cancel_work_sync(&priv->beacon_update);
        del_timer_sync(&priv->statistics_periodic);
+       del_timer_sync(&priv->ucode_trace);
 }
 
 static void iwl_init_hw_rates(struct iwl_priv *priv,
@@ -3188,6 +3364,7 @@ static int iwl_init_drv(struct iwl_priv *priv)
        priv->band = IEEE80211_BAND_2GHZ;
 
        priv->iw_mode = NL80211_IFTYPE_STATION;
+       priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
 
        /* Choose which receivers/antennas to use */
        if (priv->cfg->ops->hcmd->set_rxon_chain)