iwlwifi: add missing rcu_read_lock
[safe/jmp/linux-2.6] / drivers / net / wireless / iwlwifi / iwl-eeprom.c
index 9429cb1..ee11452 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -63,6 +63,7 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/init.h>
 
 #include <net/mac80211.h>
@@ -370,7 +371,7 @@ static int iwl_init_otp_access(struct iwl_priv *priv)
        return ret;
 }
 
-static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, u16 *eeprom_data)
+static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, __le16 *eeprom_data)
 {
        int ret = 0;
        u32 r;
@@ -404,7 +405,7 @@ static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, u16 *eeprom_data)
                                CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
                IWL_ERR(priv, "Correctable OTP ECC error, continue read\n");
        }
-       *eeprom_data = le16_to_cpu((__force __le16)(r >> 16));
+       *eeprom_data = cpu_to_le16(r >> 16);
        return 0;
 }
 
@@ -413,7 +414,8 @@ static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, u16 *eeprom_data)
  */
 static bool iwl_is_otp_empty(struct iwl_priv *priv)
 {
-       u16 next_link_addr = 0, link_value;
+       u16 next_link_addr = 0;
+       __le16 link_value;
        bool is_empty = false;
 
        /* locate the beginning of OTP link list */
@@ -443,7 +445,8 @@ static bool iwl_is_otp_empty(struct iwl_priv *priv)
 static int iwl_find_otp_image(struct iwl_priv *priv,
                                        u16 *validblockaddr)
 {
-       u16 next_link_addr = 0, link_value = 0, valid_addr;
+       u16 next_link_addr = 0, valid_addr;
+       __le16 link_value = 0;
        int usedblocks = 0;
 
        /* set addressing mode to absolute to traverse the link list */
@@ -463,7 +466,7 @@ static int iwl_find_otp_image(struct iwl_priv *priv,
                 * check for more block on the link list
                 */
                valid_addr = next_link_addr;
-               next_link_addr = link_value * sizeof(u16);
+               next_link_addr = le16_to_cpu(link_value) * sizeof(u16);
                IWL_DEBUG_INFO(priv, "OTP blocks %d addr 0x%x\n",
                               usedblocks, next_link_addr);
                if (iwl_read_otp_word(priv, next_link_addr, &link_value))
@@ -497,7 +500,7 @@ static int iwl_find_otp_image(struct iwl_priv *priv,
  */
 int iwl_eeprom_init(struct iwl_priv *priv)
 {
-       u16 *e;
+       __le16 *e;
        u32 gp = iwl_read32(priv, CSR_EEPROM_GP);
        int sz;
        int ret;
@@ -516,7 +519,9 @@ int iwl_eeprom_init(struct iwl_priv *priv)
                ret = -ENOMEM;
                goto alloc_err;
        }
-       e = (u16 *)priv->eeprom;
+       e = (__le16 *)priv->eeprom;
+
+       priv->cfg->ops->lib->apm_ops.init(priv);
 
        ret = priv->cfg->ops->lib->eeprom_ops.verify_signature(priv);
        if (ret < 0) {
@@ -532,7 +537,9 @@ int iwl_eeprom_init(struct iwl_priv *priv)
                ret = -ENOENT;
                goto err;
        }
+
        if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) {
+
                ret = iwl_init_otp_access(priv);
                if (ret) {
                        IWL_ERR(priv, "Failed to initialize OTP access.\n");
@@ -555,7 +562,7 @@ int iwl_eeprom_init(struct iwl_priv *priv)
                }
                for (addr = validblockaddr; addr < validblockaddr + sz;
                     addr += sizeof(u16)) {
-                       u16 eeprom_data;
+                       __le16 eeprom_data;
 
                        ret = iwl_read_otp_word(priv, addr, &eeprom_data);
                        if (ret)
@@ -580,15 +587,24 @@ int iwl_eeprom_init(struct iwl_priv *priv)
                                goto done;
                        }
                        r = _iwl_read_direct32(priv, CSR_EEPROM_REG);
-                       e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
+                       e[addr / 2] = cpu_to_le16(r >> 16);
                }
        }
+
+       IWL_DEBUG_INFO(priv, "NVM Type: %s, version: 0x%x\n",
+                      (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
+                      ? "OTP" : "EEPROM",
+                      iwl_eeprom_query16(priv, EEPROM_VERSION));
+
        ret = 0;
 done:
        priv->cfg->ops->lib->eeprom_ops.release_semaphore(priv);
+
 err:
        if (ret)
                iwl_eeprom_free(priv);
+       /* Reset chip to save power until we load uCode during "up". */
+       priv->cfg->ops->lib->apm_ops.stop(priv);
 alloc_err:
        return ret;
 }
@@ -740,11 +756,9 @@ static int iwl_mod_ht40_chan_info(struct iwl_priv *priv,
 
        ch_info->ht40_eeprom = *eeprom_ch;
        ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg;
-       ch_info->ht40_curr_txpow = eeprom_ch->max_power_avg;
-       ch_info->ht40_min_power = 0;
-       ch_info->ht40_scan_power = eeprom_ch->max_power_avg;
        ch_info->ht40_flags = eeprom_ch->flags;
-       ch_info->ht40_extension_channel &= ~clear_ht40_extension_channel;
+       if (eeprom_ch->flags & EEPROM_CHANNEL_VALID)
+               ch_info->ht40_extension_channel &= ~clear_ht40_extension_channel;
 
        return 0;
 }
@@ -754,7 +768,8 @@ static int iwl_mod_ht40_chan_info(struct iwl_priv *priv,
  *     find the highest tx power from all chains for the channel
  */
 static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
-               struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, int element)
+               struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
+               int element, s8 *max_txpower_in_half_dbm)
 {
        s8 max_txpower_avg = 0; /* (dBm) */
 
@@ -786,10 +801,14 @@ static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
            (enhanced_txpower[element].mimo3_max > max_txpower_avg))
                max_txpower_avg = enhanced_txpower[element].mimo3_max;
 
-       /* max. tx power in EEPROM is in 1/2 dBm format
-        * convert from 1/2 dBm to dBm
+       /*
+        * max. tx power in EEPROM is in 1/2 dBm format
+        * convert from 1/2 dBm to dBm (round-up convert)
+        * but we also do not want to loss 1/2 dBm resolution which
+        * will impact performance
         */
-       return max_txpower_avg >> 1;
+       *max_txpower_in_half_dbm = max_txpower_avg;
+       return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1);
 }
 
 /**
@@ -798,7 +817,7 @@ static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
  */
 static s8 iwl_update_common_txpower(struct iwl_priv *priv,
                struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
-               int section, int element)
+               int section, int element, s8 *max_txpower_in_half_dbm)
 {
        struct iwl_channel_info *ch_info;
        int ch;
@@ -812,25 +831,25 @@ static s8 iwl_update_common_txpower(struct iwl_priv *priv,
        if (element == EEPROM_TXPOWER_COMMON_HT40_INDEX)
                is_ht40 = true;
        max_txpower_avg =
-               iwl_get_max_txpower_avg(priv, enhanced_txpower, element);
+               iwl_get_max_txpower_avg(priv, enhanced_txpower,
+                                       element, max_txpower_in_half_dbm);
+
        ch_info = priv->channel_info;
 
        for (ch = 0; ch < priv->channel_count; ch++) {
                /* find matching band and update tx power if needed */
                if ((ch_info->band == enhinfo[section].band) &&
-                   (ch_info->max_power_avg < max_txpower_avg) && (!is_ht40)) {
+                   (ch_info->max_power_avg < max_txpower_avg) &&
+                   (!is_ht40)) {
                        /* Update regulatory-based run-time data */
                        ch_info->max_power_avg = ch_info->curr_txpow =
-                           max_txpower_avg;
+                               max_txpower_avg;
                        ch_info->scan_power = max_txpower_avg;
                }
                if ((ch_info->band == enhinfo[section].band) && is_ht40 &&
-                   ch_info->ht40_max_power_avg &&
                    (ch_info->ht40_max_power_avg < max_txpower_avg)) {
                        /* Update regulatory-based run-time data */
                        ch_info->ht40_max_power_avg = max_txpower_avg;
-                       ch_info->ht40_curr_txpow = max_txpower_avg;
-                       ch_info->ht40_scan_power = max_txpower_avg;
                }
                ch_info++;
        }
@@ -843,7 +862,7 @@ static s8 iwl_update_common_txpower(struct iwl_priv *priv,
  */
 static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
                struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
-               int section, int element)
+               int section, int element, s8 *max_txpower_in_half_dbm)
 {
        struct iwl_channel_info *ch_info;
        int ch;
@@ -852,7 +871,8 @@ static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
 
        channel = enhinfo[section].iwl_eeprom_section_channel[element];
        max_txpower_avg =
-               iwl_get_max_txpower_avg(priv, enhanced_txpower, element);
+               iwl_get_max_txpower_avg(priv, enhanced_txpower,
+                                       element, max_txpower_in_half_dbm);
 
        ch_info = priv->channel_info;
        for (ch = 0; ch < priv->channel_count; ch++) {
@@ -866,12 +886,9 @@ static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
                                ch_info->scan_power = max_txpower_avg;
                        }
                        if ((enhinfo[section].is_ht40) &&
-                           (ch_info->ht40_max_power_avg) &&
                            (ch_info->ht40_max_power_avg < max_txpower_avg)) {
                                /* Update regulatory-based run-time data */
                                ch_info->ht40_max_power_avg = max_txpower_avg;
-                               ch_info->ht40_curr_txpow = max_txpower_avg;
-                               ch_info->ht40_scan_power = max_txpower_avg;
                        }
                        break;
                }
@@ -890,6 +907,7 @@ void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv)
        struct iwl_eeprom_enhanced_txpwr *enhanced_txpower;
        u32 offset;
        s8 max_txpower_avg; /* (dBm) */
+       s8 max_txpower_in_half_dbm; /* (half-dBm) */
 
        /* Loop through all the sections
         * adjust bands and channel's max tx power
@@ -902,20 +920,43 @@ void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv)
                enhanced_txpower = (struct iwl_eeprom_enhanced_txpwr *)
                                iwl_eeprom_query_addr(priv, offset);
 
+               /*
+                * check for valid entry -
+                * different version of EEPROM might contain different set
+                * of enhanced tx power table
+                * always check for valid entry before process
+                * the information
+                */
+               if (!enhanced_txpower->common || enhanced_txpower->reserved)
+                       continue;
+
                for (element = 0; element < eeprom_section_count; element++) {
                        if (enhinfo[section].is_common)
                                max_txpower_avg =
                                        iwl_update_common_txpower(priv,
-                                       enhanced_txpower, section, element);
+                                               enhanced_txpower, section,
+                                               element,
+                                               &max_txpower_in_half_dbm);
                        else
                                max_txpower_avg =
                                        iwl_update_channel_txpower(priv,
-                                       enhanced_txpower, section, element);
+                                               enhanced_txpower, section,
+                                               element,
+                                               &max_txpower_in_half_dbm);
 
                        /* Update the tx_power_user_lmt to the highest power
                         * supported by any channel */
                        if (max_txpower_avg > priv->tx_power_user_lmt)
                                priv->tx_power_user_lmt = max_txpower_avg;
+
+                       /*
+                        * Update the tx_power_lmt_in_half_dbm to
+                        * the highest power supported by any channel
+                        */
+                       if (max_txpower_in_half_dbm >
+                           priv->tx_power_lmt_in_half_dbm)
+                               priv->tx_power_lmt_in_half_dbm =
+                                       max_txpower_in_half_dbm;
                }
        }
 }