]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/wireless/iwlwifi/iwl-core.c
iwlwifi: remove 4965 prefix from iwl4965_kw and iwl4965_tx_queue
[mv-sheeva.git] / drivers / net / wireless / iwlwifi / iwl-core.c
index ca32fb3b9e1bed281f064e85026de9e770827268..aab0ee6d8af49b036d6126c21ac50d21fb4a58e8 100644 (file)
@@ -34,7 +34,7 @@
 struct iwl_priv; /* FIXME: remove */
 #include "iwl-debug.h"
 #include "iwl-eeprom.h"
-#include "iwl-4965.h" /* FIXME: remove */
+#include "iwl-dev.h" /* FIXME: remove */
 #include "iwl-core.h"
 #include "iwl-io.h"
 #include "iwl-rfkill.h"
@@ -121,6 +121,101 @@ void iwl_hw_detect(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_hw_detect);
 
+/* Tell nic where to find the "keep warm" buffer */
+int iwl_kw_init(struct iwl_priv *priv)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       ret = iwl_grab_nic_access(priv);
+       if (ret)
+               goto out;
+
+       iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG,
+                            priv->kw.dma_addr >> 4);
+       iwl_release_nic_access(priv);
+out:
+       spin_unlock_irqrestore(&priv->lock, flags);
+       return ret;
+}
+
+int iwl_kw_alloc(struct iwl_priv *priv)
+{
+       struct pci_dev *dev = priv->pci_dev;
+       struct iwl_kw *kw = &priv->kw;
+
+       kw->size = IWL_KW_SIZE;
+       kw->v_addr = pci_alloc_consistent(dev, kw->size, &kw->dma_addr);
+       if (!kw->v_addr)
+               return -ENOMEM;
+
+       return 0;
+}
+
+/**
+ * iwl_kw_free - Free the "keep warm" buffer
+ */
+void iwl_kw_free(struct iwl_priv *priv)
+{
+       struct pci_dev *dev = priv->pci_dev;
+       struct iwl_kw *kw = &priv->kw;
+
+       if (kw->v_addr) {
+               pci_free_consistent(dev, kw->size, kw->v_addr, kw->dma_addr);
+               memset(kw, 0, sizeof(*kw));
+       }
+}
+
+int iwl_hw_nic_init(struct iwl_priv *priv)
+{
+       unsigned long flags;
+       struct iwl_rx_queue *rxq = &priv->rxq;
+       int ret;
+
+       /* nic_init */
+       priv->cfg->ops->lib->apm_ops.init(priv);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       iwl_write32(priv, CSR_INT_COALESCING, 512 / 32);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       ret = priv->cfg->ops->lib->apm_ops.set_pwr_src(priv, IWL_PWR_SRC_VMAIN);
+
+       priv->cfg->ops->lib->apm_ops.config(priv);
+
+       /* Allocate the RX queue, or reset if it is already allocated */
+       if (!rxq->bd) {
+               ret = iwl_rx_queue_alloc(priv);
+               if (ret) {
+                       IWL_ERROR("Unable to initialize Rx queue\n");
+                       return -ENOMEM;
+               }
+       } else
+               iwl_rx_queue_reset(priv, rxq);
+
+       iwl_rx_replenish(priv);
+
+       iwl_rx_init(priv, rxq);
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       rxq->need_update = 1;
+       iwl_rx_queue_update_write_ptr(priv, rxq);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       /* Allocate and init all Tx and Command queues */
+       ret = iwl_txq_ctx_reset(priv);
+       if (ret)
+               return ret;
+
+       set_bit(STATUS_INIT, &priv->status);
+
+       return 0;
+}
+EXPORT_SYMBOL(iwl_hw_nic_init);
+
 /**
  * iwlcore_clear_stations_table - Clear the driver's station table
  *
@@ -259,6 +354,12 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
        if (priv->hw_params.tx_chains_num >= 3)
                ht_info->supp_mcs_set[2] = 0xFF;
 }
+#else
+static inline void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
+                             struct ieee80211_ht_info *ht_info,
+                             enum ieee80211_band band)
+{
+}
 #endif /* CONFIG_IWL4965_HT */
 
 static void iwlcore_init_hw_rates(struct iwl_priv *priv,
@@ -428,6 +529,105 @@ static u8 is_single_rx_stream(struct iwl_priv *priv)
                (priv->current_ht_config.supp_mcs_set[2] == 0)) ||
               priv->ps_mode == IWL_MIMO_PS_STATIC;
 }
+static u8 iwl_is_channel_extension(struct iwl_priv *priv,
+                                  enum ieee80211_band band,
+                                  u16 channel, u8 extension_chan_offset)
+{
+       const struct iwl_channel_info *ch_info;
+
+       ch_info = iwl_get_channel_info(priv, band, channel);
+       if (!is_channel_valid(ch_info))
+               return 0;
+
+       if (extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_NONE)
+               return 0;
+
+       if ((ch_info->fat_extension_channel == extension_chan_offset) ||
+           (ch_info->fat_extension_channel == HT_IE_EXT_CHANNEL_MAX))
+               return 1;
+
+       return 0;
+}
+
+u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv,
+                            struct ieee80211_ht_info *sta_ht_inf)
+{
+       struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config;
+
+       if ((!iwl_ht_conf->is_ht) ||
+          (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) ||
+          (iwl_ht_conf->extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_NONE))
+               return 0;
+
+       if (sta_ht_inf) {
+               if ((!sta_ht_inf->ht_supported) ||
+                  (!(sta_ht_inf->cap & IEEE80211_HT_CAP_SUP_WIDTH)))
+                       return 0;
+       }
+
+       return iwl_is_channel_extension(priv, priv->band,
+                                        iwl_ht_conf->control_channel,
+                                        iwl_ht_conf->extension_chan_offset);
+}
+EXPORT_SYMBOL(iwl_is_fat_tx_allowed);
+
+void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
+{
+       struct iwl4965_rxon_cmd *rxon = &priv->staging_rxon;
+       u32 val;
+
+       if (!ht_info->is_ht)
+               return;
+
+       /* Set up channel bandwidth:  20 MHz only, or 20/40 mixed if fat ok */
+       if (iwl_is_fat_tx_allowed(priv, NULL))
+               rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED_MSK;
+       else
+               rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
+                                RXON_FLG_CHANNEL_MODE_PURE_40_MSK);
+
+       if (le16_to_cpu(rxon->channel) != ht_info->control_channel) {
+               IWL_DEBUG_ASSOC("control diff than current %d %d\n",
+                               le16_to_cpu(rxon->channel),
+                               ht_info->control_channel);
+               rxon->channel = cpu_to_le16(ht_info->control_channel);
+               return;
+       }
+
+       /* Note: control channel is opposite of extension channel */
+       switch (ht_info->extension_chan_offset) {
+       case IWL_EXT_CHANNEL_OFFSET_ABOVE:
+               rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
+               break;
+       case IWL_EXT_CHANNEL_OFFSET_BELOW:
+               rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
+               break;
+       case IWL_EXT_CHANNEL_OFFSET_NONE:
+       default:
+               rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
+               break;
+       }
+
+       val = ht_info->ht_protection;
+
+       rxon->flags |= cpu_to_le32(val << RXON_FLG_HT_OPERATING_MODE_POS);
+
+       iwl_set_rxon_chain(priv);
+
+       IWL_DEBUG_ASSOC("supported HT rate 0x%X 0x%X 0x%X "
+                       "rxon flags 0x%X operation mode :0x%X "
+                       "extension channel offset 0x%x "
+                       "control chan %d\n",
+                       ht_info->supp_mcs_set[0],
+                       ht_info->supp_mcs_set[1],
+                       ht_info->supp_mcs_set[2],
+                       le32_to_cpu(rxon->flags), ht_info->ht_protection,
+                       ht_info->extension_chan_offset,
+                       ht_info->control_channel);
+       return;
+}
+EXPORT_SYMBOL(iwl_set_rxon_ht);
+
 #else
 static inline u8 is_single_rx_stream(struct iwl_priv *priv)
 {
@@ -568,7 +768,7 @@ static void iwlcore_init_hw(struct iwl_priv *priv)
        hw->queues = 4;
 #ifdef CONFIG_IWL4965_HT
        /* Enhanced value; more queues, to support 11n aggregation */
-       hw->queues = 16;
+       hw->ampdu_queues = 12;
 #endif /* CONFIG_IWL4965_HT */
 }
 
@@ -704,3 +904,137 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags)
 }
 EXPORT_SYMBOL(iwl_send_statistics_request);
 
+/**
+ * iwl_verify_inst_sparse - verify runtime uCode image in card vs. host,
+ *   using sample data 100 bytes apart.  If these sample points are good,
+ *   it's a pretty good bet that everything between them is good, too.
+ */
+static int iwlcore_verify_inst_sparse(struct iwl_priv *priv, __le32 *image, u32 len)
+{
+       u32 val;
+       int ret = 0;
+       u32 errcnt = 0;
+       u32 i;
+
+       IWL_DEBUG_INFO("ucode inst image size is %u\n", len);
+
+       ret = iwl_grab_nic_access(priv);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) {
+               /* read data comes through single port, auto-incr addr */
+               /* NOTE: Use the debugless read so we don't flood kernel log
+                * if IWL_DL_IO is set */
+               iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR,
+                       i + RTC_INST_LOWER_BOUND);
+               val = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+               if (val != le32_to_cpu(*image)) {
+                       ret = -EIO;
+                       errcnt++;
+                       if (errcnt >= 3)
+                               break;
+               }
+       }
+
+       iwl_release_nic_access(priv);
+
+       return ret;
+}
+
+/**
+ * iwlcore_verify_inst_full - verify runtime uCode image in card vs. host,
+ *     looking at all data.
+ */
+static int iwl_verify_inst_full(struct iwl_priv *priv, __le32 *image,
+                                u32 len)
+{
+       u32 val;
+       u32 save_len = len;
+       int ret = 0;
+       u32 errcnt;
+
+       IWL_DEBUG_INFO("ucode inst image size is %u\n", len);
+
+       ret = iwl_grab_nic_access(priv);
+       if (ret)
+               return ret;
+
+       iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, RTC_INST_LOWER_BOUND);
+
+       errcnt = 0;
+       for (; len > 0; len -= sizeof(u32), image++) {
+               /* read data comes through single port, auto-incr addr */
+               /* NOTE: Use the debugless read so we don't flood kernel log
+                * if IWL_DL_IO is set */
+               val = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+               if (val != le32_to_cpu(*image)) {
+                       IWL_ERROR("uCode INST section is invalid at "
+                                 "offset 0x%x, is 0x%x, s/b 0x%x\n",
+                                 save_len - len, val, le32_to_cpu(*image));
+                       ret = -EIO;
+                       errcnt++;
+                       if (errcnt >= 20)
+                               break;
+               }
+       }
+
+       iwl_release_nic_access(priv);
+
+       if (!errcnt)
+               IWL_DEBUG_INFO
+                   ("ucode image in INSTRUCTION memory is good\n");
+
+       return ret;
+}
+
+/**
+ * iwl_verify_ucode - determine which instruction image is in SRAM,
+ *    and verify its contents
+ */
+int iwl_verify_ucode(struct iwl_priv *priv)
+{
+       __le32 *image;
+       u32 len;
+       int ret;
+
+       /* Try bootstrap */
+       image = (__le32 *)priv->ucode_boot.v_addr;
+       len = priv->ucode_boot.len;
+       ret = iwlcore_verify_inst_sparse(priv, image, len);
+       if (!ret) {
+               IWL_DEBUG_INFO("Bootstrap uCode is good in inst SRAM\n");
+               return 0;
+       }
+
+       /* Try initialize */
+       image = (__le32 *)priv->ucode_init.v_addr;
+       len = priv->ucode_init.len;
+       ret = iwlcore_verify_inst_sparse(priv, image, len);
+       if (!ret) {
+               IWL_DEBUG_INFO("Initialize uCode is good in inst SRAM\n");
+               return 0;
+       }
+
+       /* Try runtime/protocol */
+       image = (__le32 *)priv->ucode_code.v_addr;
+       len = priv->ucode_code.len;
+       ret = iwlcore_verify_inst_sparse(priv, image, len);
+       if (!ret) {
+               IWL_DEBUG_INFO("Runtime uCode is good in inst SRAM\n");
+               return 0;
+       }
+
+       IWL_ERROR("NO VALID UCODE IMAGE IN INSTRUCTION SRAM!!\n");
+
+       /* Since nothing seems to match, show first several data entries in
+        * instruction SRAM, so maybe visual inspection will give a clue.
+        * Selection of bootstrap image (vs. other images) is arbitrary. */
+       image = (__le32 *)priv->ucode_boot.v_addr;
+       len = priv->ucode_boot.len;
+       ret = iwl_verify_inst_full(priv, image, len);
+
+       return ret;
+}
+EXPORT_SYMBOL(iwl_verify_ucode);
+