]> err.no Git - linux-2.6/blobdiff - drivers/net/wireless/iwlwifi/iwl-5000.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6] / drivers / net / wireless / iwlwifi / iwl-5000.c
index 8e2a6a5749a1f96eb1abee1330fd2150b7752cf4..b5e28b811796c5456a549e8166c1edf0b33c2cf0 100644 (file)
@@ -38,7 +38,7 @@
 #include <asm/unaligned.h>
 
 #include "iwl-eeprom.h"
-#include "iwl-4965.h"
+#include "iwl-dev.h"
 #include "iwl-core.h"
 #include "iwl-io.h"
 #include "iwl-helpers.h"
@@ -86,6 +86,38 @@ static int iwl5000_apm_init(struct iwl_priv *priv)
        return ret;
 }
 
+static void iwl5000_nic_config(struct iwl_priv *priv)
+{
+       unsigned long flags;
+       u16 radio_cfg;
+       u8 val_link;
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       pci_read_config_byte(priv->pci_dev, PCI_LINK_CTRL, &val_link);
+
+       /* disable L1 entry -- workaround for pre-B1 */
+       pci_write_config_byte(priv->pci_dev, PCI_LINK_CTRL, val_link & ~0x02);
+
+       radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
+
+       /* write radio config values to register */
+       if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) < EEPROM_5000_RF_CFG_TYPE_MAX)
+               iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+                           EEPROM_RF_CFG_TYPE_MSK(radio_cfg) |
+                           EEPROM_RF_CFG_STEP_MSK(radio_cfg) |
+                           EEPROM_RF_CFG_DASH_MSK(radio_cfg));
+
+       /* set CSR_HW_CONFIG_REG for uCode use */
+       iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+                   CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
+                   CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+
+
 /*
  * EEPROM
  */
@@ -125,6 +157,33 @@ static u32 eeprom_indirect_address(const struct iwl_priv *priv, u32 address)
        return (address & ADDRESS_MSK) + (offset << 1);
 }
 
+static int iwl5000_eeprom_check_version(struct iwl_priv *priv)
+{
+       u16 eeprom_ver;
+       struct iwl_eeprom_calib_hdr {
+               u8 version;
+               u8 pa_type;
+               u16 voltage;
+       } *hdr;
+
+       eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
+
+       hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv,
+                                                       EEPROM_5000_CALIB_ALL);
+
+       if (eeprom_ver < EEPROM_5000_EEPROM_VERSION ||
+           hdr->version < EEPROM_5000_TX_POWER_VERSION)
+               goto err;
+
+       return 0;
+err:
+       IWL_ERROR("Unsuported EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n",
+                 eeprom_ver, EEPROM_5000_EEPROM_VERSION,
+                 hdr->version, EEPROM_5000_TX_POWER_VERSION);
+       return -EINVAL;
+
+}
+
 #ifdef CONFIG_IWL5000_RUN_TIME_CALIB
 
 static void iwl5000_gain_computation(struct iwl_priv *priv,
@@ -179,6 +238,7 @@ static void iwl5000_gain_computation(struct iwl_priv *priv,
        data->beacon_count = 0;
 }
 
+
 static void iwl5000_chain_noise_reset(struct iwl_priv *priv)
 {
        struct iwl_chain_noise_data *data = &priv->chain_noise_data;
@@ -302,6 +362,8 @@ static int iwl5000_alloc_shared_mem(struct iwl_priv *priv)
 
        memset(priv->shared_virt, 0, sizeof(struct iwl5000_shared));
 
+       priv->rb_closed_offset = offsetof(struct iwl5000_shared, rb_closed);
+
        return 0;
 }
 
@@ -314,10 +376,94 @@ static void iwl5000_free_shared_mem(struct iwl_priv *priv)
                                    priv->shared_phys);
 }
 
+static int iwl5000_shared_mem_rx_idx(struct iwl_priv *priv)
+{
+       struct iwl5000_shared *s = priv->shared_virt;
+       return le32_to_cpu(s->rb_closed) & 0xFFF;
+}
+
+/**
+ * iwl5000_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
+ */
+static void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
+                                           struct iwl_tx_queue *txq,
+                                           u16 byte_cnt)
+{
+       struct iwl5000_shared *shared_data = priv->shared_virt;
+       int txq_id = txq->q.id;
+       u8 sec_ctl = 0;
+       u8 sta = 0;
+       int len;
+
+       len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
+
+       if (txq_id != IWL_CMD_QUEUE_NUM) {
+               sta = txq->cmd[txq->q.write_ptr].cmd.tx.sta_id;
+               sec_ctl = txq->cmd[txq->q.write_ptr].cmd.tx.sec_ctl;
+
+               switch (sec_ctl & TX_CMD_SEC_MSK) {
+               case TX_CMD_SEC_CCM:
+                       len += CCMP_MIC_LEN;
+                       break;
+               case TX_CMD_SEC_TKIP:
+                       len += TKIP_ICV_LEN;
+                       break;
+               case TX_CMD_SEC_WEP:
+                       len += WEP_IV_LEN + WEP_ICV_LEN;
+                       break;
+               }
+       }
+
+       IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
+                      tfd_offset[txq->q.write_ptr], byte_cnt, len);
+
+       IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
+                      tfd_offset[txq->q.write_ptr], sta_id, sta);
+
+       if (txq->q.write_ptr < IWL50_MAX_WIN_SIZE) {
+               IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
+                       tfd_offset[IWL50_QUEUE_SIZE + txq->q.write_ptr],
+                       byte_cnt, len);
+               IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
+                       tfd_offset[IWL50_QUEUE_SIZE + txq->q.write_ptr],
+                       sta_id, sta);
+       }
+}
+
+static u16 iwl5000_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data)
+{
+       u16 size = (u16)sizeof(struct iwl_addsta_cmd);
+       memcpy(data, cmd, size);
+       return size;
+}
+
+
+static int iwl5000_disable_tx_fifo(struct iwl_priv *priv)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       ret = iwl_grab_nic_access(priv);
+       if (unlikely(ret)) {
+               IWL_ERROR("Tx fifo reset failed");
+               spin_unlock_irqrestore(&priv->lock, flags);
+               return ret;
+       }
+
+       iwl_write_prph(priv, IWL50_SCD_TXFACT, 0);
+       iwl_release_nic_access(priv);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 0;
+}
+
 static struct iwl_hcmd_ops iwl5000_hcmd = {
 };
 
 static struct iwl_hcmd_utils_ops iwl5000_hcmd_utils = {
+       .build_addsta_hcmd = iwl5000_build_addsta_hcmd,
 #ifdef CONFIG_IWL5000_RUN_TIME_CALIB
        .gain_computation = iwl5000_gain_computation,
        .chain_noise_reset = iwl5000_chain_noise_reset,
@@ -328,8 +474,12 @@ static struct iwl_lib_ops iwl5000_lib = {
        .set_hw_params = iwl5000_hw_set_hw_params,
        .alloc_shared_mem = iwl5000_alloc_shared_mem,
        .free_shared_mem = iwl5000_free_shared_mem,
+       .shared_mem_rx_idx = iwl5000_shared_mem_rx_idx,
+       .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl,
+       .disable_tx_fifo = iwl5000_disable_tx_fifo,
        .apm_ops = {
                .init = iwl5000_apm_init,
+               .config = iwl5000_nic_config,
                .set_pwr_src = iwl4965_set_pwr_src,
        },
        .eeprom_ops = {
@@ -345,6 +495,7 @@ static struct iwl_lib_ops iwl5000_lib = {
                .verify_signature  = iwlcore_eeprom_verify_signature,
                .acquire_semaphore = iwlcore_eeprom_acquire_semaphore,
                .release_semaphore = iwlcore_eeprom_release_semaphore,
+               .check_version  = iwl5000_eeprom_check_version,
                .query_addr = iwl5000_eeprom_query_addr,
        },
 };
@@ -359,6 +510,7 @@ static struct iwl_mod_params iwl50_mod_params = {
        .num_of_queues = IWL50_NUM_QUEUES,
        .enable_qos = 1,
        .amsdu_size_8K = 1,
+       .restart_fw = 1,
        /* the rest are 0 by default */
 };
 
@@ -404,5 +556,5 @@ module_param_named(qos_enable50, iwl50_mod_params.enable_qos, int, 0444);
 MODULE_PARM_DESC(qos_enable50, "enable all 50XX QoS functionality");
 module_param_named(amsdu_size_8K50, iwl50_mod_params.amsdu_size_8K, int, 0444);
 MODULE_PARM_DESC(amsdu_size_8K50, "enable 8K amsdu size in 50XX series");
-
-
+module_param_named(fw_restart50, iwl50_mod_params.restart_fw, int, 0444);
+MODULE_PARM_DESC(fw_restart50, "restart firmware in case of error");