📄 iwl-4965.c
字号:
/****************************************************************************** * * Copyright(c) 2003 - 2007 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 * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * James P. Ketrenos <ipw2100-admin@linux.intel.com> * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/#include <linux/kernel.h>#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/dma-mapping.h>#include <linux/delay.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/wireless.h>#include <net/mac80211.h>#include <linux/etherdevice.h>#define IWL 4965#include "iwlwifi.h"#include "iwl-4965.h"#include "iwl-helpers.h"#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ IWL_RATE_SISO_##s##M_PLCP, \ IWL_RATE_MIMO_##s##M_PLCP, \ IWL_RATE_##r##M_IEEE, \ IWL_RATE_##ip##M_INDEX, \ IWL_RATE_##in##M_INDEX, \ IWL_RATE_##rp##M_INDEX, \ IWL_RATE_##rn##M_INDEX, \ IWL_RATE_##pp##M_INDEX, \ IWL_RATE_##np##M_INDEX }/* * Parameter order: * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate * * If there isn't a valid next or previous rate then INV is used which * maps to IWL_RATE_INVALID * */const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */};static int is_fat_channel(__le32 rxon_flags){ return (rxon_flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) || (rxon_flags & RXON_FLG_CHANNEL_MODE_MIXED_MSK);}static u8 is_single_stream(struct iwl_priv *priv){#ifdef CONFIG_IWLWIFI_HT if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht || (priv->active_rate_ht[1] == 0) || (priv->ps_mode == IWL_MIMO_PS_STATIC)) return 1;#else return 1;#endif /*CONFIG_IWLWIFI_HT */ return 0;}/* * Determine how many receiver/antenna chains to use. * More provides better reception via diversity. Fewer saves power. * MIMO (dual stream) requires at least 2, but works better with 3. * This does not determine *which* chains to use, just how many. */static int iwl4965_get_rx_chain_counter(struct iwl_priv *priv, u8 *idle_state, u8 *rx_state){ u8 is_single = is_single_stream(priv); u8 is_cam = test_bit(STATUS_POWER_PMI, &priv->status) ? 0 : 1; /* # of Rx chains to use when expecting MIMO. */ if (is_single || (!is_cam && (priv->ps_mode == IWL_MIMO_PS_STATIC))) *rx_state = 2; else *rx_state = 3; /* # Rx chains when idling and maybe trying to save power */ switch (priv->ps_mode) { case IWL_MIMO_PS_STATIC: case IWL_MIMO_PS_DYNAMIC: *idle_state = (is_cam) ? 2 : 1; break; case IWL_MIMO_PS_NONE: *idle_state = (is_cam) ? *rx_state : 1; break; default: *idle_state = 1; break; } return 0;}int iwl_hw_rxq_stop(struct iwl_priv *priv){ int rc; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); rc = iwl_grab_restricted_access(priv); if (rc) { spin_unlock_irqrestore(&priv->lock, flags); return rc; } /* stop HW */ iwl_write_restricted(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); rc = iwl_poll_restricted_bit(priv, FH_MEM_RSSR_RX_STATUS_REG, (1 << 24), 1000); if (rc < 0) IWL_ERROR("Can't stop Rx DMA.\n"); iwl_release_restricted_access(priv); spin_unlock_irqrestore(&priv->lock, flags); return 0;}u8 iwl_hw_find_station(struct iwl_priv *priv, const u8 *addr){ int i; int start = 0; int ret = IWL_INVALID_STATION; unsigned long flags; DECLARE_MAC_BUF(mac); if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) || (priv->iw_mode == IEEE80211_IF_TYPE_AP)) start = IWL_STA_ID; if (is_broadcast_ether_addr(addr)) return IWL4965_BROADCAST_ID; spin_lock_irqsave(&priv->sta_lock, flags); for (i = start; i < priv->hw_setting.max_stations; i++) if ((priv->stations[i].used) && (!compare_ether_addr (priv->stations[i].sta.sta.addr, addr))) { ret = i; goto out; } IWL_DEBUG_ASSOC_LIMIT("can not find STA %s total %d\n", print_mac(mac, addr), priv->num_stations); out: spin_unlock_irqrestore(&priv->sta_lock, flags); return ret;}static int iwl4965_nic_set_pwr_src(struct iwl_priv *priv, int pwr_max){ int rc = 0; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); rc = iwl_grab_restricted_access(priv); if (rc) { spin_unlock_irqrestore(&priv->lock, flags); return rc; } if (!pwr_max) { u32 val; rc = pci_read_config_dword(priv->pci_dev, PCI_POWER_SOURCE, &val); if (val & PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT) iwl_set_bits_mask_restricted_reg( priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_PWR_SRC_VAUX, ~APMG_PS_CTRL_MSK_PWR_SRC); } else iwl_set_bits_mask_restricted_reg( priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, ~APMG_PS_CTRL_MSK_PWR_SRC); iwl_release_restricted_access(priv); spin_unlock_irqrestore(&priv->lock, flags); return rc;}static int iwl4965_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq){ int rc; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); rc = iwl_grab_restricted_access(priv); if (rc) { spin_unlock_irqrestore(&priv->lock, flags); return rc; } /* stop HW */ iwl_write_restricted(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); iwl_write_restricted(priv, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); iwl_write_restricted(priv, FH_RSCSR_CHNL0_RBDCB_BASE_REG, rxq->dma_addr >> 8); iwl_write_restricted(priv, FH_RSCSR_CHNL0_STTS_WPTR_REG, (priv->hw_setting.shared_phys + offsetof(struct iwl_shared, val0)) >> 4); iwl_write_restricted(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | IWL_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K | /*0x10 << 4 | */ (RX_QUEUE_SIZE_LOG << FH_RCSR_RX_CONFIG_RBDCB_SIZE_BITSHIFT)); /* * iwl_write32(priv,CSR_INT_COAL_REG,0); */ iwl_release_restricted_access(priv); spin_unlock_irqrestore(&priv->lock, flags); return 0;}static int iwl4965_kw_init(struct iwl_priv *priv){ unsigned long flags; int rc; spin_lock_irqsave(&priv->lock, flags); rc = iwl_grab_restricted_access(priv); if (rc) goto out; iwl_write_restricted(priv, IWL_FH_KW_MEM_ADDR_REG, priv->kw.dma_addr >> 4); iwl_release_restricted_access(priv);out: spin_unlock_irqrestore(&priv->lock, flags); return rc;}static int iwl4965_kw_alloc(struct iwl_priv *priv){ struct pci_dev *dev = priv->pci_dev; struct iwl_kw *kw = &priv->kw; kw->size = IWL4965_KW_SIZE; /* TBW need set somewhere else */ kw->v_addr = pci_alloc_consistent(dev, kw->size, &kw->dma_addr); if (!kw->v_addr) return -ENOMEM; return 0;}#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \ ? # x " " : "")int iwl4965_set_fat_chan_info(struct iwl_priv *priv, int phymode, u16 channel, const struct iwl_eeprom_channel *eeprom_ch, u8 fat_extension_channel){ struct iwl_channel_info *ch_info; ch_info = (struct iwl_channel_info *) iwl_get_channel_info(priv, phymode, channel); if (!is_channel_valid(ch_info)) return -1; IWL_DEBUG_INFO("FAT Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x" " %ddBm): Ad-Hoc %ssupported\n", ch_info->channel, is_channel_a_band(ch_info) ? "5.2" : "2.4", CHECK_AND_PRINT(IBSS), CHECK_AND_PRINT(ACTIVE), CHECK_AND_PRINT(RADAR), CHECK_AND_PRINT(WIDE), CHECK_AND_PRINT(NARROW), CHECK_AND_PRINT(DFS), eeprom_ch->flags, eeprom_ch->max_power_avg, ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) && !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); ch_info->fat_eeprom = *eeprom_ch; ch_info->fat_max_power_avg = eeprom_ch->max_power_avg; ch_info->fat_curr_txpow = eeprom_ch->max_power_avg; ch_info->fat_min_power = 0; ch_info->fat_scan_power = eeprom_ch->max_power_avg; ch_info->fat_flags = eeprom_ch->flags; ch_info->fat_extension_channel = fat_extension_channel; return 0;}static void iwl4965_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)); }}/** * iwl4965_txq_ctx_reset - Reset TX queue context * Destroys all DMA structures and initialise them again * * @param priv * @return error code */static int iwl4965_txq_ctx_reset(struct iwl_priv *priv){ int rc = 0; int txq_id, slots_num; unsigned long flags; iwl4965_kw_free(priv); iwl_hw_txq_ctx_free(priv); /* Tx CMD queue */ rc = iwl4965_kw_alloc(priv); if (rc) { IWL_ERROR("Keep Warm allocation failed"); goto error_kw; } spin_lock_irqsave(&priv->lock, flags); rc = iwl_grab_restricted_access(priv); if (unlikely(rc)) { IWL_ERROR("TX reset failed"); spin_unlock_irqrestore(&priv->lock, flags); goto error_reset; } iwl_write_restricted_reg(priv, SCD_TXFACT, 0); iwl_release_restricted_access(priv); spin_unlock_irqrestore(&priv->lock, flags); rc = iwl4965_kw_init(priv); if (rc) { IWL_ERROR("kw_init failed\n"); goto error_reset; } /* Tx queue(s) */ for (txq_id = 0; txq_id < priv->hw_setting.max_txq_num; txq_id++) { slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; rc = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num, txq_id); if (rc) { IWL_ERROR("Tx %d queue init failed\n", txq_id); goto error; } } return rc; error: iwl_hw_txq_ctx_free(priv); error_reset: iwl4965_kw_free(priv); error_kw: return rc;}int iwl_hw_nic_init(struct iwl_priv *priv){ int rc; unsigned long flags; struct iwl_rx_queue *rxq = &priv->rxq; u8 rev_id; u32 val; u8 val_link; iwl_power_init_handle(priv); /* nic_init */ spin_lock_irqsave(&priv->lock, flags); iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS, CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); rc = iwl_poll_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); if (rc < 0) { spin_unlock_irqrestore(&priv->lock, flags); IWL_DEBUG_INFO("Failed to init the card\n"); return rc; } rc = iwl_grab_restricted_access(priv); if (rc) { spin_unlock_irqrestore(&priv->lock, flags); return rc; } iwl_read_restricted_reg(priv, APMG_CLK_CTRL_REG); iwl_write_restricted_reg(priv, APMG_CLK_CTRL_REG, APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT); iwl_read_restricted_reg(priv, APMG_CLK_CTRL_REG); udelay(20); iwl_set_bits_restricted_reg(priv, APMG_PCIDEV_STT_REG, APMG_PCIDEV_STT_VAL_L1_ACT_DIS); iwl_release_restricted_access(priv); iwl_write32(priv, CSR_INT_COALESCING, 512 / 32); spin_unlock_irqrestore(&priv->lock, flags); /* Determine HW type */ rc = pci_read_config_byte(priv->pci_dev, PCI_REVISION_ID, &rev_id); if (rc) return rc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -