*
* 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
*
* 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
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/init.h>
#include <net/mac80211.h>
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;
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;
}
*/
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 */
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 */
* 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))
*/
int iwl_eeprom_init(struct iwl_priv *priv)
{
- u16 *e;
+ __le16 *e;
u32 gp = iwl_read32(priv, CSR_EEPROM_GP);
int sz;
int ret;
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) {
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");
}
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)
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;
}
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;
}
* 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) */
(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);
}
/**
*/
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;
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++;
}
*/
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;
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++) {
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;
}
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
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;
}
}
}