162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Contact Information: 762306a36Sopenharmony_ci * Intel Linux Wireless <ilw@linux.intel.com> 862306a36Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci *****************************************************************************/ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/sched.h> 1862306a36Sopenharmony_ci#include <linux/skbuff.h> 1962306a36Sopenharmony_ci#include <linux/netdevice.h> 2062306a36Sopenharmony_ci#include <linux/units.h> 2162306a36Sopenharmony_ci#include <net/mac80211.h> 2262306a36Sopenharmony_ci#include <linux/etherdevice.h> 2362306a36Sopenharmony_ci#include <asm/unaligned.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "common.h" 2662306a36Sopenharmony_ci#include "4965.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * il_verify_inst_sparse - verify runtime uCode image in card vs. host, 3062306a36Sopenharmony_ci * using sample data 100 bytes apart. If these sample points are good, 3162306a36Sopenharmony_ci * it's a pretty good bet that everything between them is good, too. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_cistatic int 3462306a36Sopenharmony_ciil4965_verify_inst_sparse(struct il_priv *il, __le32 * image, u32 len) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci u32 val; 3762306a36Sopenharmony_ci int ret = 0; 3862306a36Sopenharmony_ci u32 errcnt = 0; 3962306a36Sopenharmony_ci u32 i; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci D_INFO("ucode inst image size is %u\n", len); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci for (i = 0; i < len; i += 100, image += 100 / sizeof(u32)) { 4462306a36Sopenharmony_ci /* read data comes through single port, auto-incr addr */ 4562306a36Sopenharmony_ci /* NOTE: Use the debugless read so we don't flood kernel log 4662306a36Sopenharmony_ci * if IL_DL_IO is set */ 4762306a36Sopenharmony_ci il_wr(il, HBUS_TARG_MEM_RADDR, i + IL4965_RTC_INST_LOWER_BOUND); 4862306a36Sopenharmony_ci val = _il_rd(il, HBUS_TARG_MEM_RDAT); 4962306a36Sopenharmony_ci if (val != le32_to_cpu(*image)) { 5062306a36Sopenharmony_ci ret = -EIO; 5162306a36Sopenharmony_ci errcnt++; 5262306a36Sopenharmony_ci if (errcnt >= 3) 5362306a36Sopenharmony_ci break; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return ret; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * il4965_verify_inst_full - verify runtime uCode image in card vs. host, 6262306a36Sopenharmony_ci * looking at all data. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic int 6562306a36Sopenharmony_ciil4965_verify_inst_full(struct il_priv *il, __le32 * image, u32 len) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci u32 val; 6862306a36Sopenharmony_ci u32 save_len = len; 6962306a36Sopenharmony_ci int ret = 0; 7062306a36Sopenharmony_ci u32 errcnt; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci D_INFO("ucode inst image size is %u\n", len); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci il_wr(il, HBUS_TARG_MEM_RADDR, IL4965_RTC_INST_LOWER_BOUND); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci errcnt = 0; 7762306a36Sopenharmony_ci for (; len > 0; len -= sizeof(u32), image++) { 7862306a36Sopenharmony_ci /* read data comes through single port, auto-incr addr */ 7962306a36Sopenharmony_ci /* NOTE: Use the debugless read so we don't flood kernel log 8062306a36Sopenharmony_ci * if IL_DL_IO is set */ 8162306a36Sopenharmony_ci val = _il_rd(il, HBUS_TARG_MEM_RDAT); 8262306a36Sopenharmony_ci if (val != le32_to_cpu(*image)) { 8362306a36Sopenharmony_ci IL_ERR("uCode INST section is invalid at " 8462306a36Sopenharmony_ci "offset 0x%x, is 0x%x, s/b 0x%x\n", 8562306a36Sopenharmony_ci save_len - len, val, le32_to_cpu(*image)); 8662306a36Sopenharmony_ci ret = -EIO; 8762306a36Sopenharmony_ci errcnt++; 8862306a36Sopenharmony_ci if (errcnt >= 20) 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (!errcnt) 9462306a36Sopenharmony_ci D_INFO("ucode image in INSTRUCTION memory is good\n"); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return ret; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * il4965_verify_ucode - determine which instruction image is in SRAM, 10162306a36Sopenharmony_ci * and verify its contents 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ciint 10462306a36Sopenharmony_ciil4965_verify_ucode(struct il_priv *il) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci __le32 *image; 10762306a36Sopenharmony_ci u32 len; 10862306a36Sopenharmony_ci int ret; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Try bootstrap */ 11162306a36Sopenharmony_ci image = (__le32 *) il->ucode_boot.v_addr; 11262306a36Sopenharmony_ci len = il->ucode_boot.len; 11362306a36Sopenharmony_ci ret = il4965_verify_inst_sparse(il, image, len); 11462306a36Sopenharmony_ci if (!ret) { 11562306a36Sopenharmony_ci D_INFO("Bootstrap uCode is good in inst SRAM\n"); 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Try initialize */ 12062306a36Sopenharmony_ci image = (__le32 *) il->ucode_init.v_addr; 12162306a36Sopenharmony_ci len = il->ucode_init.len; 12262306a36Sopenharmony_ci ret = il4965_verify_inst_sparse(il, image, len); 12362306a36Sopenharmony_ci if (!ret) { 12462306a36Sopenharmony_ci D_INFO("Initialize uCode is good in inst SRAM\n"); 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Try runtime/protocol */ 12962306a36Sopenharmony_ci image = (__le32 *) il->ucode_code.v_addr; 13062306a36Sopenharmony_ci len = il->ucode_code.len; 13162306a36Sopenharmony_ci ret = il4965_verify_inst_sparse(il, image, len); 13262306a36Sopenharmony_ci if (!ret) { 13362306a36Sopenharmony_ci D_INFO("Runtime uCode is good in inst SRAM\n"); 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci IL_ERR("NO VALID UCODE IMAGE IN INSTRUCTION SRAM!!\n"); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Since nothing seems to match, show first several data entries in 14062306a36Sopenharmony_ci * instruction SRAM, so maybe visual inspection will give a clue. 14162306a36Sopenharmony_ci * Selection of bootstrap image (vs. other images) is arbitrary. */ 14262306a36Sopenharmony_ci image = (__le32 *) il->ucode_boot.v_addr; 14362306a36Sopenharmony_ci len = il->ucode_boot.len; 14462306a36Sopenharmony_ci ret = il4965_verify_inst_full(il, image, len); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/****************************************************************************** 15062306a36Sopenharmony_ci * 15162306a36Sopenharmony_ci * EEPROM related functions 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci******************************************************************************/ 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* 15662306a36Sopenharmony_ci * The device's EEPROM semaphore prevents conflicts between driver and uCode 15762306a36Sopenharmony_ci * when accessing the EEPROM; each access is a series of pulses to/from the 15862306a36Sopenharmony_ci * EEPROM chip, not a single event, so even reads could conflict if they 15962306a36Sopenharmony_ci * weren't arbitrated by the semaphore. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ciint 16262306a36Sopenharmony_ciil4965_eeprom_acquire_semaphore(struct il_priv *il) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci u16 count; 16562306a36Sopenharmony_ci int ret; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { 16862306a36Sopenharmony_ci /* Request semaphore */ 16962306a36Sopenharmony_ci il_set_bit(il, CSR_HW_IF_CONFIG_REG, 17062306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* See if we got it */ 17362306a36Sopenharmony_ci ret = 17462306a36Sopenharmony_ci _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, 17562306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, 17662306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, 17762306a36Sopenharmony_ci EEPROM_SEM_TIMEOUT); 17862306a36Sopenharmony_ci if (ret >= 0) 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return ret; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_civoid 18662306a36Sopenharmony_ciil4965_eeprom_release_semaphore(struct il_priv *il) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci il_clear_bit(il, CSR_HW_IF_CONFIG_REG, 18962306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciint 19462306a36Sopenharmony_ciil4965_eeprom_check_version(struct il_priv *il) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci u16 eeprom_ver; 19762306a36Sopenharmony_ci u16 calib_ver; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci eeprom_ver = il_eeprom_query16(il, EEPROM_VERSION); 20062306a36Sopenharmony_ci calib_ver = il_eeprom_query16(il, EEPROM_4965_CALIB_VERSION_OFFSET); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (eeprom_ver < il->cfg->eeprom_ver || 20362306a36Sopenharmony_ci calib_ver < il->cfg->eeprom_calib_ver) 20462306a36Sopenharmony_ci goto err; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci IL_INFO("device EEPROM VER=0x%x, CALIB=0x%x\n", eeprom_ver, calib_ver); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_cierr: 21062306a36Sopenharmony_ci IL_ERR("Unsupported (too old) EEPROM VER=0x%x < 0x%x " 21162306a36Sopenharmony_ci "CALIB=0x%x < 0x%x\n", eeprom_ver, il->cfg->eeprom_ver, 21262306a36Sopenharmony_ci calib_ver, il->cfg->eeprom_calib_ver); 21362306a36Sopenharmony_ci return -EINVAL; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_civoid 21862306a36Sopenharmony_ciil4965_eeprom_get_mac(const struct il_priv *il, u8 * mac) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci const u8 *addr = il_eeprom_query_addr(il, 22162306a36Sopenharmony_ci EEPROM_MAC_ADDRESS); 22262306a36Sopenharmony_ci memcpy(mac, addr, ETH_ALEN); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* Send led command */ 22662306a36Sopenharmony_cistatic int 22762306a36Sopenharmony_ciil4965_send_led_cmd(struct il_priv *il, struct il_led_cmd *led_cmd) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct il_host_cmd cmd = { 23062306a36Sopenharmony_ci .id = C_LEDS, 23162306a36Sopenharmony_ci .len = sizeof(struct il_led_cmd), 23262306a36Sopenharmony_ci .data = led_cmd, 23362306a36Sopenharmony_ci .flags = CMD_ASYNC, 23462306a36Sopenharmony_ci .callback = NULL, 23562306a36Sopenharmony_ci }; 23662306a36Sopenharmony_ci u32 reg; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci reg = _il_rd(il, CSR_LED_REG); 23962306a36Sopenharmony_ci if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) 24062306a36Sopenharmony_ci _il_wr(il, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return il_send_cmd(il, &cmd); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/* Set led register off */ 24662306a36Sopenharmony_civoid 24762306a36Sopenharmony_ciil4965_led_enable(struct il_priv *il) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci _il_wr(il, CSR_LED_REG, CSR_LED_REG_TRUN_ON); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int il4965_send_tx_power(struct il_priv *il); 25362306a36Sopenharmony_cistatic int il4965_hw_get_temperature(struct il_priv *il); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* Highest firmware API version supported */ 25662306a36Sopenharmony_ci#define IL4965_UCODE_API_MAX 2 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* Lowest firmware API version supported */ 25962306a36Sopenharmony_ci#define IL4965_UCODE_API_MIN 2 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci#define IL4965_FW_PRE "iwlwifi-4965-" 26262306a36Sopenharmony_ci#define _IL4965_MODULE_FIRMWARE(api) IL4965_FW_PRE #api ".ucode" 26362306a36Sopenharmony_ci#define IL4965_MODULE_FIRMWARE(api) _IL4965_MODULE_FIRMWARE(api) 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* check contents of special bootstrap uCode SRAM */ 26662306a36Sopenharmony_cistatic int 26762306a36Sopenharmony_ciil4965_verify_bsm(struct il_priv *il) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci __le32 *image = il->ucode_boot.v_addr; 27062306a36Sopenharmony_ci u32 len = il->ucode_boot.len; 27162306a36Sopenharmony_ci u32 reg; 27262306a36Sopenharmony_ci u32 val; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci D_INFO("Begin verify bsm\n"); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* verify BSM SRAM contents */ 27762306a36Sopenharmony_ci val = il_rd_prph(il, BSM_WR_DWCOUNT_REG); 27862306a36Sopenharmony_ci for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; 27962306a36Sopenharmony_ci reg += sizeof(u32), image++) { 28062306a36Sopenharmony_ci val = il_rd_prph(il, reg); 28162306a36Sopenharmony_ci if (val != le32_to_cpu(*image)) { 28262306a36Sopenharmony_ci IL_ERR("BSM uCode verification failed at " 28362306a36Sopenharmony_ci "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n", 28462306a36Sopenharmony_ci BSM_SRAM_LOWER_BOUND, reg - BSM_SRAM_LOWER_BOUND, 28562306a36Sopenharmony_ci len, val, le32_to_cpu(*image)); 28662306a36Sopenharmony_ci return -EIO; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci D_INFO("BSM bootstrap uCode image OK\n"); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* 29662306a36Sopenharmony_ci * il4965_load_bsm - Load bootstrap instructions 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * BSM operation: 29962306a36Sopenharmony_ci * 30062306a36Sopenharmony_ci * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program 30162306a36Sopenharmony_ci * in special SRAM that does not power down during RFKILL. When powering back 30262306a36Sopenharmony_ci * up after power-saving sleeps (or during initial uCode load), the BSM loads 30362306a36Sopenharmony_ci * the bootstrap program into the on-board processor, and starts it. 30462306a36Sopenharmony_ci * 30562306a36Sopenharmony_ci * The bootstrap program loads (via DMA) instructions and data for a new 30662306a36Sopenharmony_ci * program from host DRAM locations indicated by the host driver in the 30762306a36Sopenharmony_ci * BSM_DRAM_* registers. Once the new program is loaded, it starts 30862306a36Sopenharmony_ci * automatically. 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * When initializing the NIC, the host driver points the BSM to the 31162306a36Sopenharmony_ci * "initialize" uCode image. This uCode sets up some internal data, then 31262306a36Sopenharmony_ci * notifies host via "initialize alive" that it is complete. 31362306a36Sopenharmony_ci * 31462306a36Sopenharmony_ci * The host then replaces the BSM_DRAM_* pointer values to point to the 31562306a36Sopenharmony_ci * normal runtime uCode instructions and a backup uCode data cache buffer 31662306a36Sopenharmony_ci * (filled initially with starting data values for the on-board processor), 31762306a36Sopenharmony_ci * then triggers the "initialize" uCode to load and launch the runtime uCode, 31862306a36Sopenharmony_ci * which begins normal operation. 31962306a36Sopenharmony_ci * 32062306a36Sopenharmony_ci * When doing a power-save shutdown, runtime uCode saves data SRAM into 32162306a36Sopenharmony_ci * the backup data cache in DRAM before SRAM is powered down. 32262306a36Sopenharmony_ci * 32362306a36Sopenharmony_ci * When powering back up, the BSM loads the bootstrap program. This reloads 32462306a36Sopenharmony_ci * the runtime uCode instructions and the backup data cache into SRAM, 32562306a36Sopenharmony_ci * and re-launches the runtime uCode from where it left off. 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_cistatic int 32862306a36Sopenharmony_ciil4965_load_bsm(struct il_priv *il) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci __le32 *image = il->ucode_boot.v_addr; 33162306a36Sopenharmony_ci u32 len = il->ucode_boot.len; 33262306a36Sopenharmony_ci dma_addr_t pinst; 33362306a36Sopenharmony_ci dma_addr_t pdata; 33462306a36Sopenharmony_ci u32 inst_len; 33562306a36Sopenharmony_ci u32 data_len; 33662306a36Sopenharmony_ci int i; 33762306a36Sopenharmony_ci u32 done; 33862306a36Sopenharmony_ci u32 reg_offset; 33962306a36Sopenharmony_ci int ret; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci D_INFO("Begin load bsm\n"); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci il->ucode_type = UCODE_RT; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* make sure bootstrap program is no larger than BSM's SRAM size */ 34662306a36Sopenharmony_ci if (len > IL49_MAX_BSM_SIZE) 34762306a36Sopenharmony_ci return -EINVAL; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Tell bootstrap uCode where to find the "Initialize" uCode 35062306a36Sopenharmony_ci * in host DRAM ... host DRAM physical address bits 35:4 for 4965. 35162306a36Sopenharmony_ci * NOTE: il_init_alive_start() will replace these values, 35262306a36Sopenharmony_ci * after the "initialize" uCode has run, to point to 35362306a36Sopenharmony_ci * runtime/protocol instructions and backup data cache. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci pinst = il->ucode_init.p_addr >> 4; 35662306a36Sopenharmony_ci pdata = il->ucode_init_data.p_addr >> 4; 35762306a36Sopenharmony_ci inst_len = il->ucode_init.len; 35862306a36Sopenharmony_ci data_len = il->ucode_init_data.len; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); 36162306a36Sopenharmony_ci il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); 36262306a36Sopenharmony_ci il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); 36362306a36Sopenharmony_ci il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* Fill BSM memory with bootstrap instructions */ 36662306a36Sopenharmony_ci for (reg_offset = BSM_SRAM_LOWER_BOUND; 36762306a36Sopenharmony_ci reg_offset < BSM_SRAM_LOWER_BOUND + len; 36862306a36Sopenharmony_ci reg_offset += sizeof(u32), image++) 36962306a36Sopenharmony_ci _il_wr_prph(il, reg_offset, le32_to_cpu(*image)); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci ret = il4965_verify_bsm(il); 37262306a36Sopenharmony_ci if (ret) 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ 37662306a36Sopenharmony_ci il_wr_prph(il, BSM_WR_MEM_SRC_REG, 0x0); 37762306a36Sopenharmony_ci il_wr_prph(il, BSM_WR_MEM_DST_REG, IL49_RTC_INST_LOWER_BOUND); 37862306a36Sopenharmony_ci il_wr_prph(il, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* Load bootstrap code into instruction SRAM now, 38162306a36Sopenharmony_ci * to prepare to load "initialize" uCode */ 38262306a36Sopenharmony_ci il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Wait for load of bootstrap uCode to finish */ 38562306a36Sopenharmony_ci for (i = 0; i < 100; i++) { 38662306a36Sopenharmony_ci done = il_rd_prph(il, BSM_WR_CTRL_REG); 38762306a36Sopenharmony_ci if (!(done & BSM_WR_CTRL_REG_BIT_START)) 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci udelay(10); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci if (i < 100) 39262306a36Sopenharmony_ci D_INFO("BSM write complete, poll %d iterations\n", i); 39362306a36Sopenharmony_ci else { 39462306a36Sopenharmony_ci IL_ERR("BSM write did not complete!\n"); 39562306a36Sopenharmony_ci return -EIO; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Enable future boot loads whenever power management unit triggers it 39962306a36Sopenharmony_ci * (e.g. when powering back up after power-save shutdown) */ 40062306a36Sopenharmony_ci il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci/* 40662306a36Sopenharmony_ci * il4965_set_ucode_ptrs - Set uCode address location 40762306a36Sopenharmony_ci * 40862306a36Sopenharmony_ci * Tell initialization uCode where to find runtime uCode. 40962306a36Sopenharmony_ci * 41062306a36Sopenharmony_ci * BSM registers initially contain pointers to initialization uCode. 41162306a36Sopenharmony_ci * We need to replace them to load runtime uCode inst and data, 41262306a36Sopenharmony_ci * and to save runtime data when powering down. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_cistatic int 41562306a36Sopenharmony_ciil4965_set_ucode_ptrs(struct il_priv *il) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci dma_addr_t pinst; 41862306a36Sopenharmony_ci dma_addr_t pdata; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* bits 35:4 for 4965 */ 42162306a36Sopenharmony_ci pinst = il->ucode_code.p_addr >> 4; 42262306a36Sopenharmony_ci pdata = il->ucode_data_backup.p_addr >> 4; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Tell bootstrap uCode where to find image to load */ 42562306a36Sopenharmony_ci il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); 42662306a36Sopenharmony_ci il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); 42762306a36Sopenharmony_ci il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, il->ucode_data.len); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Inst byte count must be last to set up, bit 31 signals uCode 43062306a36Sopenharmony_ci * that all new ptr/size info is in place */ 43162306a36Sopenharmony_ci il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, 43262306a36Sopenharmony_ci il->ucode_code.len | BSM_DRAM_INST_LOAD); 43362306a36Sopenharmony_ci D_INFO("Runtime uCode pointers are set.\n"); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci/* 43962306a36Sopenharmony_ci * il4965_init_alive_start - Called after N_ALIVE notification received 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * Called after N_ALIVE notification received from "initialize" uCode. 44262306a36Sopenharmony_ci * 44362306a36Sopenharmony_ci * The 4965 "initialize" ALIVE reply contains calibration data for: 44462306a36Sopenharmony_ci * Voltage, temperature, and MIMO tx gain correction, now stored in il 44562306a36Sopenharmony_ci * (3945 does not contain this data). 44662306a36Sopenharmony_ci * 44762306a36Sopenharmony_ci * Tell "initialize" uCode to go ahead and load the runtime uCode. 44862306a36Sopenharmony_ci*/ 44962306a36Sopenharmony_cistatic void 45062306a36Sopenharmony_ciil4965_init_alive_start(struct il_priv *il) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci /* Bootstrap uCode has loaded initialize uCode ... verify inst image. 45362306a36Sopenharmony_ci * This is a paranoid check, because we would not have gotten the 45462306a36Sopenharmony_ci * "initialize" alive if code weren't properly loaded. */ 45562306a36Sopenharmony_ci if (il4965_verify_ucode(il)) { 45662306a36Sopenharmony_ci /* Runtime instruction load was bad; 45762306a36Sopenharmony_ci * take it all the way back down so we can try again */ 45862306a36Sopenharmony_ci D_INFO("Bad \"initialize\" uCode load.\n"); 45962306a36Sopenharmony_ci goto restart; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Calculate temperature */ 46362306a36Sopenharmony_ci il->temperature = il4965_hw_get_temperature(il); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* Send pointers to protocol/runtime uCode image ... init code will 46662306a36Sopenharmony_ci * load and launch runtime uCode, which will send us another "Alive" 46762306a36Sopenharmony_ci * notification. */ 46862306a36Sopenharmony_ci D_INFO("Initialization Alive received.\n"); 46962306a36Sopenharmony_ci if (il4965_set_ucode_ptrs(il)) { 47062306a36Sopenharmony_ci /* Runtime instruction load won't happen; 47162306a36Sopenharmony_ci * take it all the way back down so we can try again */ 47262306a36Sopenharmony_ci D_INFO("Couldn't set up uCode pointers.\n"); 47362306a36Sopenharmony_ci goto restart; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci return; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cirestart: 47862306a36Sopenharmony_ci queue_work(il->workqueue, &il->restart); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic bool 48262306a36Sopenharmony_ciiw4965_is_ht40_channel(__le32 rxon_flags) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci int chan_mod = 48562306a36Sopenharmony_ci le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK) >> 48662306a36Sopenharmony_ci RXON_FLG_CHANNEL_MODE_POS; 48762306a36Sopenharmony_ci return (chan_mod == CHANNEL_MODE_PURE_40 || 48862306a36Sopenharmony_ci chan_mod == CHANNEL_MODE_MIXED); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_civoid 49262306a36Sopenharmony_ciil4965_nic_config(struct il_priv *il) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci unsigned long flags; 49562306a36Sopenharmony_ci u16 radio_cfg; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci radio_cfg = il_eeprom_query16(il, EEPROM_RADIO_CONFIG); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* write radio config values to register */ 50262306a36Sopenharmony_ci if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) == EEPROM_4965_RF_CFG_TYPE_MAX) 50362306a36Sopenharmony_ci il_set_bit(il, CSR_HW_IF_CONFIG_REG, 50462306a36Sopenharmony_ci EEPROM_RF_CFG_TYPE_MSK(radio_cfg) | 50562306a36Sopenharmony_ci EEPROM_RF_CFG_STEP_MSK(radio_cfg) | 50662306a36Sopenharmony_ci EEPROM_RF_CFG_DASH_MSK(radio_cfg)); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* set CSR_HW_CONFIG_REG for uCode use */ 50962306a36Sopenharmony_ci il_set_bit(il, CSR_HW_IF_CONFIG_REG, 51062306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | 51162306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci il->calib_info = 51462306a36Sopenharmony_ci (struct il_eeprom_calib_info *) 51562306a36Sopenharmony_ci il_eeprom_query_addr(il, EEPROM_4965_CALIB_TXPOWER_OFFSET); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci/* Reset differential Rx gains in NIC to prepare for chain noise calibration. 52162306a36Sopenharmony_ci * Called after every association, but this runs only once! 52262306a36Sopenharmony_ci * ... once chain noise is calibrated the first time, it's good forever. */ 52362306a36Sopenharmony_cistatic void 52462306a36Sopenharmony_ciil4965_chain_noise_reset(struct il_priv *il) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct il_chain_noise_data *data = &(il->chain_noise_data); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (data->state == IL_CHAIN_NOISE_ALIVE && il_is_any_associated(il)) { 52962306a36Sopenharmony_ci struct il_calib_diff_gain_cmd cmd; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* clear data for chain noise calibration algorithm */ 53262306a36Sopenharmony_ci data->chain_noise_a = 0; 53362306a36Sopenharmony_ci data->chain_noise_b = 0; 53462306a36Sopenharmony_ci data->chain_noise_c = 0; 53562306a36Sopenharmony_ci data->chain_signal_a = 0; 53662306a36Sopenharmony_ci data->chain_signal_b = 0; 53762306a36Sopenharmony_ci data->chain_signal_c = 0; 53862306a36Sopenharmony_ci data->beacon_count = 0; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 54162306a36Sopenharmony_ci cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD; 54262306a36Sopenharmony_ci cmd.diff_gain_a = 0; 54362306a36Sopenharmony_ci cmd.diff_gain_b = 0; 54462306a36Sopenharmony_ci cmd.diff_gain_c = 0; 54562306a36Sopenharmony_ci if (il_send_cmd_pdu(il, C_PHY_CALIBRATION, sizeof(cmd), &cmd)) 54662306a36Sopenharmony_ci IL_ERR("Could not send C_PHY_CALIBRATION\n"); 54762306a36Sopenharmony_ci data->state = IL_CHAIN_NOISE_ACCUMULATE; 54862306a36Sopenharmony_ci D_CALIB("Run chain_noise_calibrate\n"); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic s32 55362306a36Sopenharmony_ciil4965_math_div_round(s32 num, s32 denom, s32 * res) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci s32 sign = 1; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (num < 0) { 55862306a36Sopenharmony_ci sign = -sign; 55962306a36Sopenharmony_ci num = -num; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci if (denom < 0) { 56262306a36Sopenharmony_ci sign = -sign; 56362306a36Sopenharmony_ci denom = -denom; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci *res = ((num * 2 + denom) / (denom * 2)) * sign; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return 1; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/* 57162306a36Sopenharmony_ci * il4965_get_voltage_compensation - Power supply voltage comp for txpower 57262306a36Sopenharmony_ci * 57362306a36Sopenharmony_ci * Determines power supply voltage compensation for txpower calculations. 57462306a36Sopenharmony_ci * Returns number of 1/2-dB steps to subtract from gain table idx, 57562306a36Sopenharmony_ci * to compensate for difference between power supply voltage during 57662306a36Sopenharmony_ci * factory measurements, vs. current power supply voltage. 57762306a36Sopenharmony_ci * 57862306a36Sopenharmony_ci * Voltage indication is higher for lower voltage. 57962306a36Sopenharmony_ci * Lower voltage requires more gain (lower gain table idx). 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_cistatic s32 58262306a36Sopenharmony_ciil4965_get_voltage_compensation(s32 eeprom_voltage, s32 current_voltage) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci s32 comp = 0; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (TX_POWER_IL_ILLEGAL_VOLTAGE == eeprom_voltage || 58762306a36Sopenharmony_ci TX_POWER_IL_ILLEGAL_VOLTAGE == current_voltage) 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci il4965_math_div_round(current_voltage - eeprom_voltage, 59162306a36Sopenharmony_ci TX_POWER_IL_VOLTAGE_CODES_PER_03V, &comp); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (current_voltage > eeprom_voltage) 59462306a36Sopenharmony_ci comp *= 2; 59562306a36Sopenharmony_ci if ((comp < -2) || (comp > 2)) 59662306a36Sopenharmony_ci comp = 0; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return comp; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic s32 60262306a36Sopenharmony_ciil4965_get_tx_atten_grp(u16 channel) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci if (channel >= CALIB_IL_TX_ATTEN_GR5_FCH && 60562306a36Sopenharmony_ci channel <= CALIB_IL_TX_ATTEN_GR5_LCH) 60662306a36Sopenharmony_ci return CALIB_CH_GROUP_5; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (channel >= CALIB_IL_TX_ATTEN_GR1_FCH && 60962306a36Sopenharmony_ci channel <= CALIB_IL_TX_ATTEN_GR1_LCH) 61062306a36Sopenharmony_ci return CALIB_CH_GROUP_1; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (channel >= CALIB_IL_TX_ATTEN_GR2_FCH && 61362306a36Sopenharmony_ci channel <= CALIB_IL_TX_ATTEN_GR2_LCH) 61462306a36Sopenharmony_ci return CALIB_CH_GROUP_2; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (channel >= CALIB_IL_TX_ATTEN_GR3_FCH && 61762306a36Sopenharmony_ci channel <= CALIB_IL_TX_ATTEN_GR3_LCH) 61862306a36Sopenharmony_ci return CALIB_CH_GROUP_3; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (channel >= CALIB_IL_TX_ATTEN_GR4_FCH && 62162306a36Sopenharmony_ci channel <= CALIB_IL_TX_ATTEN_GR4_LCH) 62262306a36Sopenharmony_ci return CALIB_CH_GROUP_4; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci return -EINVAL; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic u32 62862306a36Sopenharmony_ciil4965_get_sub_band(const struct il_priv *il, u32 channel) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci s32 b = -1; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci for (b = 0; b < EEPROM_TX_POWER_BANDS; b++) { 63362306a36Sopenharmony_ci if (il->calib_info->band_info[b].ch_from == 0) 63462306a36Sopenharmony_ci continue; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (channel >= il->calib_info->band_info[b].ch_from && 63762306a36Sopenharmony_ci channel <= il->calib_info->band_info[b].ch_to) 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return b; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic s32 64562306a36Sopenharmony_ciil4965_interpolate_value(s32 x, s32 x1, s32 y1, s32 x2, s32 y2) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci s32 val; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (x2 == x1) 65062306a36Sopenharmony_ci return y1; 65162306a36Sopenharmony_ci else { 65262306a36Sopenharmony_ci il4965_math_div_round((x2 - x) * (y1 - y2), (x2 - x1), &val); 65362306a36Sopenharmony_ci return val + y2; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci/* 65862306a36Sopenharmony_ci * il4965_interpolate_chan - Interpolate factory measurements for one channel 65962306a36Sopenharmony_ci * 66062306a36Sopenharmony_ci * Interpolates factory measurements from the two sample channels within a 66162306a36Sopenharmony_ci * sub-band, to apply to channel of interest. Interpolation is proportional to 66262306a36Sopenharmony_ci * differences in channel frequencies, which is proportional to differences 66362306a36Sopenharmony_ci * in channel number. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_cistatic int 66662306a36Sopenharmony_ciil4965_interpolate_chan(struct il_priv *il, u32 channel, 66762306a36Sopenharmony_ci struct il_eeprom_calib_ch_info *chan_info) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci s32 s = -1; 67062306a36Sopenharmony_ci u32 c; 67162306a36Sopenharmony_ci u32 m; 67262306a36Sopenharmony_ci const struct il_eeprom_calib_measure *m1; 67362306a36Sopenharmony_ci const struct il_eeprom_calib_measure *m2; 67462306a36Sopenharmony_ci struct il_eeprom_calib_measure *omeas; 67562306a36Sopenharmony_ci u32 ch_i1; 67662306a36Sopenharmony_ci u32 ch_i2; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci s = il4965_get_sub_band(il, channel); 67962306a36Sopenharmony_ci if (s >= EEPROM_TX_POWER_BANDS) { 68062306a36Sopenharmony_ci IL_ERR("Tx Power can not find channel %d\n", channel); 68162306a36Sopenharmony_ci return -1; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci ch_i1 = il->calib_info->band_info[s].ch1.ch_num; 68562306a36Sopenharmony_ci ch_i2 = il->calib_info->band_info[s].ch2.ch_num; 68662306a36Sopenharmony_ci chan_info->ch_num = (u8) channel; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci D_TXPOWER("channel %d subband %d factory cal ch %d & %d\n", channel, s, 68962306a36Sopenharmony_ci ch_i1, ch_i2); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci for (c = 0; c < EEPROM_TX_POWER_TX_CHAINS; c++) { 69262306a36Sopenharmony_ci for (m = 0; m < EEPROM_TX_POWER_MEASUREMENTS; m++) { 69362306a36Sopenharmony_ci m1 = &(il->calib_info->band_info[s].ch1. 69462306a36Sopenharmony_ci measurements[c][m]); 69562306a36Sopenharmony_ci m2 = &(il->calib_info->band_info[s].ch2. 69662306a36Sopenharmony_ci measurements[c][m]); 69762306a36Sopenharmony_ci omeas = &(chan_info->measurements[c][m]); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci omeas->actual_pow = 70062306a36Sopenharmony_ci (u8) il4965_interpolate_value(channel, ch_i1, 70162306a36Sopenharmony_ci m1->actual_pow, ch_i2, 70262306a36Sopenharmony_ci m2->actual_pow); 70362306a36Sopenharmony_ci omeas->gain_idx = 70462306a36Sopenharmony_ci (u8) il4965_interpolate_value(channel, ch_i1, 70562306a36Sopenharmony_ci m1->gain_idx, ch_i2, 70662306a36Sopenharmony_ci m2->gain_idx); 70762306a36Sopenharmony_ci omeas->temperature = 70862306a36Sopenharmony_ci (u8) il4965_interpolate_value(channel, ch_i1, 70962306a36Sopenharmony_ci m1->temperature, 71062306a36Sopenharmony_ci ch_i2, 71162306a36Sopenharmony_ci m2->temperature); 71262306a36Sopenharmony_ci omeas->pa_det = 71362306a36Sopenharmony_ci (s8) il4965_interpolate_value(channel, ch_i1, 71462306a36Sopenharmony_ci m1->pa_det, ch_i2, 71562306a36Sopenharmony_ci m2->pa_det); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci D_TXPOWER("chain %d meas %d AP1=%d AP2=%d AP=%d\n", c, 71862306a36Sopenharmony_ci m, m1->actual_pow, m2->actual_pow, 71962306a36Sopenharmony_ci omeas->actual_pow); 72062306a36Sopenharmony_ci D_TXPOWER("chain %d meas %d NI1=%d NI2=%d NI=%d\n", c, 72162306a36Sopenharmony_ci m, m1->gain_idx, m2->gain_idx, 72262306a36Sopenharmony_ci omeas->gain_idx); 72362306a36Sopenharmony_ci D_TXPOWER("chain %d meas %d PA1=%d PA2=%d PA=%d\n", c, 72462306a36Sopenharmony_ci m, m1->pa_det, m2->pa_det, omeas->pa_det); 72562306a36Sopenharmony_ci D_TXPOWER("chain %d meas %d T1=%d T2=%d T=%d\n", c, 72662306a36Sopenharmony_ci m, m1->temperature, m2->temperature, 72762306a36Sopenharmony_ci omeas->temperature); 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci/* bit-rate-dependent table to prevent Tx distortion, in half-dB units, 73562306a36Sopenharmony_ci * for OFDM 6, 12, 18, 24, 36, 48, 54, 60 MBit, and CCK all rates. */ 73662306a36Sopenharmony_cistatic s32 back_off_table[] = { 73762306a36Sopenharmony_ci 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 20 MHz */ 73862306a36Sopenharmony_ci 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 20 MHz */ 73962306a36Sopenharmony_ci 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 40 MHz */ 74062306a36Sopenharmony_ci 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 40 MHz */ 74162306a36Sopenharmony_ci 10 /* CCK */ 74262306a36Sopenharmony_ci}; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci/* Thermal compensation values for txpower for various frequency ranges ... 74562306a36Sopenharmony_ci * ratios from 3:1 to 4.5:1 of degrees (Celsius) per half-dB gain adjust */ 74662306a36Sopenharmony_cistatic struct il4965_txpower_comp_entry { 74762306a36Sopenharmony_ci s32 degrees_per_05db_a; 74862306a36Sopenharmony_ci s32 degrees_per_05db_a_denom; 74962306a36Sopenharmony_ci} tx_power_cmp_tble[CALIB_CH_GROUP_MAX] = { 75062306a36Sopenharmony_ci { 75162306a36Sopenharmony_ci 9, 2}, /* group 0 5.2, ch 34-43 */ 75262306a36Sopenharmony_ci { 75362306a36Sopenharmony_ci 4, 1}, /* group 1 5.2, ch 44-70 */ 75462306a36Sopenharmony_ci { 75562306a36Sopenharmony_ci 4, 1}, /* group 2 5.2, ch 71-124 */ 75662306a36Sopenharmony_ci { 75762306a36Sopenharmony_ci 4, 1}, /* group 3 5.2, ch 125-200 */ 75862306a36Sopenharmony_ci { 75962306a36Sopenharmony_ci 3, 1} /* group 4 2.4, ch all */ 76062306a36Sopenharmony_ci}; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic s32 76362306a36Sopenharmony_ciget_min_power_idx(s32 rate_power_idx, u32 band) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci if (!band) { 76662306a36Sopenharmony_ci if ((rate_power_idx & 7) <= 4) 76762306a36Sopenharmony_ci return MIN_TX_GAIN_IDX_52GHZ_EXT; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci return MIN_TX_GAIN_IDX; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistruct gain_entry { 77362306a36Sopenharmony_ci u8 dsp; 77462306a36Sopenharmony_ci u8 radio; 77562306a36Sopenharmony_ci}; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic const struct gain_entry gain_table[2][108] = { 77862306a36Sopenharmony_ci /* 5.2GHz power gain idx table */ 77962306a36Sopenharmony_ci { 78062306a36Sopenharmony_ci {123, 0x3F}, /* highest txpower */ 78162306a36Sopenharmony_ci {117, 0x3F}, 78262306a36Sopenharmony_ci {110, 0x3F}, 78362306a36Sopenharmony_ci {104, 0x3F}, 78462306a36Sopenharmony_ci {98, 0x3F}, 78562306a36Sopenharmony_ci {110, 0x3E}, 78662306a36Sopenharmony_ci {104, 0x3E}, 78762306a36Sopenharmony_ci {98, 0x3E}, 78862306a36Sopenharmony_ci {110, 0x3D}, 78962306a36Sopenharmony_ci {104, 0x3D}, 79062306a36Sopenharmony_ci {98, 0x3D}, 79162306a36Sopenharmony_ci {110, 0x3C}, 79262306a36Sopenharmony_ci {104, 0x3C}, 79362306a36Sopenharmony_ci {98, 0x3C}, 79462306a36Sopenharmony_ci {110, 0x3B}, 79562306a36Sopenharmony_ci {104, 0x3B}, 79662306a36Sopenharmony_ci {98, 0x3B}, 79762306a36Sopenharmony_ci {110, 0x3A}, 79862306a36Sopenharmony_ci {104, 0x3A}, 79962306a36Sopenharmony_ci {98, 0x3A}, 80062306a36Sopenharmony_ci {110, 0x39}, 80162306a36Sopenharmony_ci {104, 0x39}, 80262306a36Sopenharmony_ci {98, 0x39}, 80362306a36Sopenharmony_ci {110, 0x38}, 80462306a36Sopenharmony_ci {104, 0x38}, 80562306a36Sopenharmony_ci {98, 0x38}, 80662306a36Sopenharmony_ci {110, 0x37}, 80762306a36Sopenharmony_ci {104, 0x37}, 80862306a36Sopenharmony_ci {98, 0x37}, 80962306a36Sopenharmony_ci {110, 0x36}, 81062306a36Sopenharmony_ci {104, 0x36}, 81162306a36Sopenharmony_ci {98, 0x36}, 81262306a36Sopenharmony_ci {110, 0x35}, 81362306a36Sopenharmony_ci {104, 0x35}, 81462306a36Sopenharmony_ci {98, 0x35}, 81562306a36Sopenharmony_ci {110, 0x34}, 81662306a36Sopenharmony_ci {104, 0x34}, 81762306a36Sopenharmony_ci {98, 0x34}, 81862306a36Sopenharmony_ci {110, 0x33}, 81962306a36Sopenharmony_ci {104, 0x33}, 82062306a36Sopenharmony_ci {98, 0x33}, 82162306a36Sopenharmony_ci {110, 0x32}, 82262306a36Sopenharmony_ci {104, 0x32}, 82362306a36Sopenharmony_ci {98, 0x32}, 82462306a36Sopenharmony_ci {110, 0x31}, 82562306a36Sopenharmony_ci {104, 0x31}, 82662306a36Sopenharmony_ci {98, 0x31}, 82762306a36Sopenharmony_ci {110, 0x30}, 82862306a36Sopenharmony_ci {104, 0x30}, 82962306a36Sopenharmony_ci {98, 0x30}, 83062306a36Sopenharmony_ci {110, 0x25}, 83162306a36Sopenharmony_ci {104, 0x25}, 83262306a36Sopenharmony_ci {98, 0x25}, 83362306a36Sopenharmony_ci {110, 0x24}, 83462306a36Sopenharmony_ci {104, 0x24}, 83562306a36Sopenharmony_ci {98, 0x24}, 83662306a36Sopenharmony_ci {110, 0x23}, 83762306a36Sopenharmony_ci {104, 0x23}, 83862306a36Sopenharmony_ci {98, 0x23}, 83962306a36Sopenharmony_ci {110, 0x22}, 84062306a36Sopenharmony_ci {104, 0x18}, 84162306a36Sopenharmony_ci {98, 0x18}, 84262306a36Sopenharmony_ci {110, 0x17}, 84362306a36Sopenharmony_ci {104, 0x17}, 84462306a36Sopenharmony_ci {98, 0x17}, 84562306a36Sopenharmony_ci {110, 0x16}, 84662306a36Sopenharmony_ci {104, 0x16}, 84762306a36Sopenharmony_ci {98, 0x16}, 84862306a36Sopenharmony_ci {110, 0x15}, 84962306a36Sopenharmony_ci {104, 0x15}, 85062306a36Sopenharmony_ci {98, 0x15}, 85162306a36Sopenharmony_ci {110, 0x14}, 85262306a36Sopenharmony_ci {104, 0x14}, 85362306a36Sopenharmony_ci {98, 0x14}, 85462306a36Sopenharmony_ci {110, 0x13}, 85562306a36Sopenharmony_ci {104, 0x13}, 85662306a36Sopenharmony_ci {98, 0x13}, 85762306a36Sopenharmony_ci {110, 0x12}, 85862306a36Sopenharmony_ci {104, 0x08}, 85962306a36Sopenharmony_ci {98, 0x08}, 86062306a36Sopenharmony_ci {110, 0x07}, 86162306a36Sopenharmony_ci {104, 0x07}, 86262306a36Sopenharmony_ci {98, 0x07}, 86362306a36Sopenharmony_ci {110, 0x06}, 86462306a36Sopenharmony_ci {104, 0x06}, 86562306a36Sopenharmony_ci {98, 0x06}, 86662306a36Sopenharmony_ci {110, 0x05}, 86762306a36Sopenharmony_ci {104, 0x05}, 86862306a36Sopenharmony_ci {98, 0x05}, 86962306a36Sopenharmony_ci {110, 0x04}, 87062306a36Sopenharmony_ci {104, 0x04}, 87162306a36Sopenharmony_ci {98, 0x04}, 87262306a36Sopenharmony_ci {110, 0x03}, 87362306a36Sopenharmony_ci {104, 0x03}, 87462306a36Sopenharmony_ci {98, 0x03}, 87562306a36Sopenharmony_ci {110, 0x02}, 87662306a36Sopenharmony_ci {104, 0x02}, 87762306a36Sopenharmony_ci {98, 0x02}, 87862306a36Sopenharmony_ci {110, 0x01}, 87962306a36Sopenharmony_ci {104, 0x01}, 88062306a36Sopenharmony_ci {98, 0x01}, 88162306a36Sopenharmony_ci {110, 0x00}, 88262306a36Sopenharmony_ci {104, 0x00}, 88362306a36Sopenharmony_ci {98, 0x00}, 88462306a36Sopenharmony_ci {93, 0x00}, 88562306a36Sopenharmony_ci {88, 0x00}, 88662306a36Sopenharmony_ci {83, 0x00}, 88762306a36Sopenharmony_ci {78, 0x00}, 88862306a36Sopenharmony_ci }, 88962306a36Sopenharmony_ci /* 2.4GHz power gain idx table */ 89062306a36Sopenharmony_ci { 89162306a36Sopenharmony_ci {110, 0x3f}, /* highest txpower */ 89262306a36Sopenharmony_ci {104, 0x3f}, 89362306a36Sopenharmony_ci {98, 0x3f}, 89462306a36Sopenharmony_ci {110, 0x3e}, 89562306a36Sopenharmony_ci {104, 0x3e}, 89662306a36Sopenharmony_ci {98, 0x3e}, 89762306a36Sopenharmony_ci {110, 0x3d}, 89862306a36Sopenharmony_ci {104, 0x3d}, 89962306a36Sopenharmony_ci {98, 0x3d}, 90062306a36Sopenharmony_ci {110, 0x3c}, 90162306a36Sopenharmony_ci {104, 0x3c}, 90262306a36Sopenharmony_ci {98, 0x3c}, 90362306a36Sopenharmony_ci {110, 0x3b}, 90462306a36Sopenharmony_ci {104, 0x3b}, 90562306a36Sopenharmony_ci {98, 0x3b}, 90662306a36Sopenharmony_ci {110, 0x3a}, 90762306a36Sopenharmony_ci {104, 0x3a}, 90862306a36Sopenharmony_ci {98, 0x3a}, 90962306a36Sopenharmony_ci {110, 0x39}, 91062306a36Sopenharmony_ci {104, 0x39}, 91162306a36Sopenharmony_ci {98, 0x39}, 91262306a36Sopenharmony_ci {110, 0x38}, 91362306a36Sopenharmony_ci {104, 0x38}, 91462306a36Sopenharmony_ci {98, 0x38}, 91562306a36Sopenharmony_ci {110, 0x37}, 91662306a36Sopenharmony_ci {104, 0x37}, 91762306a36Sopenharmony_ci {98, 0x37}, 91862306a36Sopenharmony_ci {110, 0x36}, 91962306a36Sopenharmony_ci {104, 0x36}, 92062306a36Sopenharmony_ci {98, 0x36}, 92162306a36Sopenharmony_ci {110, 0x35}, 92262306a36Sopenharmony_ci {104, 0x35}, 92362306a36Sopenharmony_ci {98, 0x35}, 92462306a36Sopenharmony_ci {110, 0x34}, 92562306a36Sopenharmony_ci {104, 0x34}, 92662306a36Sopenharmony_ci {98, 0x34}, 92762306a36Sopenharmony_ci {110, 0x33}, 92862306a36Sopenharmony_ci {104, 0x33}, 92962306a36Sopenharmony_ci {98, 0x33}, 93062306a36Sopenharmony_ci {110, 0x32}, 93162306a36Sopenharmony_ci {104, 0x32}, 93262306a36Sopenharmony_ci {98, 0x32}, 93362306a36Sopenharmony_ci {110, 0x31}, 93462306a36Sopenharmony_ci {104, 0x31}, 93562306a36Sopenharmony_ci {98, 0x31}, 93662306a36Sopenharmony_ci {110, 0x30}, 93762306a36Sopenharmony_ci {104, 0x30}, 93862306a36Sopenharmony_ci {98, 0x30}, 93962306a36Sopenharmony_ci {110, 0x6}, 94062306a36Sopenharmony_ci {104, 0x6}, 94162306a36Sopenharmony_ci {98, 0x6}, 94262306a36Sopenharmony_ci {110, 0x5}, 94362306a36Sopenharmony_ci {104, 0x5}, 94462306a36Sopenharmony_ci {98, 0x5}, 94562306a36Sopenharmony_ci {110, 0x4}, 94662306a36Sopenharmony_ci {104, 0x4}, 94762306a36Sopenharmony_ci {98, 0x4}, 94862306a36Sopenharmony_ci {110, 0x3}, 94962306a36Sopenharmony_ci {104, 0x3}, 95062306a36Sopenharmony_ci {98, 0x3}, 95162306a36Sopenharmony_ci {110, 0x2}, 95262306a36Sopenharmony_ci {104, 0x2}, 95362306a36Sopenharmony_ci {98, 0x2}, 95462306a36Sopenharmony_ci {110, 0x1}, 95562306a36Sopenharmony_ci {104, 0x1}, 95662306a36Sopenharmony_ci {98, 0x1}, 95762306a36Sopenharmony_ci {110, 0x0}, 95862306a36Sopenharmony_ci {104, 0x0}, 95962306a36Sopenharmony_ci {98, 0x0}, 96062306a36Sopenharmony_ci {97, 0}, 96162306a36Sopenharmony_ci {96, 0}, 96262306a36Sopenharmony_ci {95, 0}, 96362306a36Sopenharmony_ci {94, 0}, 96462306a36Sopenharmony_ci {93, 0}, 96562306a36Sopenharmony_ci {92, 0}, 96662306a36Sopenharmony_ci {91, 0}, 96762306a36Sopenharmony_ci {90, 0}, 96862306a36Sopenharmony_ci {89, 0}, 96962306a36Sopenharmony_ci {88, 0}, 97062306a36Sopenharmony_ci {87, 0}, 97162306a36Sopenharmony_ci {86, 0}, 97262306a36Sopenharmony_ci {85, 0}, 97362306a36Sopenharmony_ci {84, 0}, 97462306a36Sopenharmony_ci {83, 0}, 97562306a36Sopenharmony_ci {82, 0}, 97662306a36Sopenharmony_ci {81, 0}, 97762306a36Sopenharmony_ci {80, 0}, 97862306a36Sopenharmony_ci {79, 0}, 97962306a36Sopenharmony_ci {78, 0}, 98062306a36Sopenharmony_ci {77, 0}, 98162306a36Sopenharmony_ci {76, 0}, 98262306a36Sopenharmony_ci {75, 0}, 98362306a36Sopenharmony_ci {74, 0}, 98462306a36Sopenharmony_ci {73, 0}, 98562306a36Sopenharmony_ci {72, 0}, 98662306a36Sopenharmony_ci {71, 0}, 98762306a36Sopenharmony_ci {70, 0}, 98862306a36Sopenharmony_ci {69, 0}, 98962306a36Sopenharmony_ci {68, 0}, 99062306a36Sopenharmony_ci {67, 0}, 99162306a36Sopenharmony_ci {66, 0}, 99262306a36Sopenharmony_ci {65, 0}, 99362306a36Sopenharmony_ci {64, 0}, 99462306a36Sopenharmony_ci {63, 0}, 99562306a36Sopenharmony_ci {62, 0}, 99662306a36Sopenharmony_ci {61, 0}, 99762306a36Sopenharmony_ci {60, 0}, 99862306a36Sopenharmony_ci {59, 0}, 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci}; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic int 100362306a36Sopenharmony_ciil4965_fill_txpower_tbl(struct il_priv *il, u8 band, u16 channel, u8 is_ht40, 100462306a36Sopenharmony_ci u8 ctrl_chan_high, 100562306a36Sopenharmony_ci struct il4965_tx_power_db *tx_power_tbl) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci u8 saturation_power; 100862306a36Sopenharmony_ci s32 target_power; 100962306a36Sopenharmony_ci s32 user_target_power; 101062306a36Sopenharmony_ci s32 power_limit; 101162306a36Sopenharmony_ci s32 current_temp; 101262306a36Sopenharmony_ci s32 reg_limit; 101362306a36Sopenharmony_ci s32 current_regulatory; 101462306a36Sopenharmony_ci s32 txatten_grp = CALIB_CH_GROUP_MAX; 101562306a36Sopenharmony_ci int i; 101662306a36Sopenharmony_ci int c; 101762306a36Sopenharmony_ci const struct il_channel_info *ch_info = NULL; 101862306a36Sopenharmony_ci struct il_eeprom_calib_ch_info ch_eeprom_info; 101962306a36Sopenharmony_ci const struct il_eeprom_calib_measure *measurement; 102062306a36Sopenharmony_ci s16 voltage; 102162306a36Sopenharmony_ci s32 init_voltage; 102262306a36Sopenharmony_ci s32 voltage_compensation; 102362306a36Sopenharmony_ci s32 degrees_per_05db_num; 102462306a36Sopenharmony_ci s32 degrees_per_05db_denom; 102562306a36Sopenharmony_ci s32 factory_temp; 102662306a36Sopenharmony_ci s32 temperature_comp[2]; 102762306a36Sopenharmony_ci s32 factory_gain_idx[2]; 102862306a36Sopenharmony_ci s32 factory_actual_pwr[2]; 102962306a36Sopenharmony_ci s32 power_idx; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci /* tx_power_user_lmt is in dBm, convert to half-dBm (half-dB units 103262306a36Sopenharmony_ci * are used for idxing into txpower table) */ 103362306a36Sopenharmony_ci user_target_power = 2 * il->tx_power_user_lmt; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* Get current (RXON) channel, band, width */ 103662306a36Sopenharmony_ci D_TXPOWER("chan %d band %d is_ht40 %d\n", channel, band, is_ht40); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci ch_info = il_get_channel_info(il, il->band, channel); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (!il_is_channel_valid(ch_info)) 104162306a36Sopenharmony_ci return -EINVAL; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci /* get txatten group, used to select 1) thermal txpower adjustment 104462306a36Sopenharmony_ci * and 2) mimo txpower balance between Tx chains. */ 104562306a36Sopenharmony_ci txatten_grp = il4965_get_tx_atten_grp(channel); 104662306a36Sopenharmony_ci if (txatten_grp < 0) { 104762306a36Sopenharmony_ci IL_ERR("Can't find txatten group for channel %d.\n", channel); 104862306a36Sopenharmony_ci return txatten_grp; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci D_TXPOWER("channel %d belongs to txatten group %d\n", channel, 105262306a36Sopenharmony_ci txatten_grp); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (is_ht40) { 105562306a36Sopenharmony_ci if (ctrl_chan_high) 105662306a36Sopenharmony_ci channel -= 2; 105762306a36Sopenharmony_ci else 105862306a36Sopenharmony_ci channel += 2; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci /* hardware txpower limits ... 106262306a36Sopenharmony_ci * saturation (clipping distortion) txpowers are in half-dBm */ 106362306a36Sopenharmony_ci if (band) 106462306a36Sopenharmony_ci saturation_power = il->calib_info->saturation_power24; 106562306a36Sopenharmony_ci else 106662306a36Sopenharmony_ci saturation_power = il->calib_info->saturation_power52; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (saturation_power < IL_TX_POWER_SATURATION_MIN || 106962306a36Sopenharmony_ci saturation_power > IL_TX_POWER_SATURATION_MAX) { 107062306a36Sopenharmony_ci if (band) 107162306a36Sopenharmony_ci saturation_power = IL_TX_POWER_DEFAULT_SATURATION_24; 107262306a36Sopenharmony_ci else 107362306a36Sopenharmony_ci saturation_power = IL_TX_POWER_DEFAULT_SATURATION_52; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* regulatory txpower limits ... reg_limit values are in half-dBm, 107762306a36Sopenharmony_ci * max_power_avg values are in dBm, convert * 2 */ 107862306a36Sopenharmony_ci if (is_ht40) 107962306a36Sopenharmony_ci reg_limit = ch_info->ht40_max_power_avg * 2; 108062306a36Sopenharmony_ci else 108162306a36Sopenharmony_ci reg_limit = ch_info->max_power_avg * 2; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if ((reg_limit < IL_TX_POWER_REGULATORY_MIN) || 108462306a36Sopenharmony_ci (reg_limit > IL_TX_POWER_REGULATORY_MAX)) { 108562306a36Sopenharmony_ci if (band) 108662306a36Sopenharmony_ci reg_limit = IL_TX_POWER_DEFAULT_REGULATORY_24; 108762306a36Sopenharmony_ci else 108862306a36Sopenharmony_ci reg_limit = IL_TX_POWER_DEFAULT_REGULATORY_52; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci /* Interpolate txpower calibration values for this channel, 109262306a36Sopenharmony_ci * based on factory calibration tests on spaced channels. */ 109362306a36Sopenharmony_ci il4965_interpolate_chan(il, channel, &ch_eeprom_info); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci /* calculate tx gain adjustment based on power supply voltage */ 109662306a36Sopenharmony_ci voltage = le16_to_cpu(il->calib_info->voltage); 109762306a36Sopenharmony_ci init_voltage = (s32) le32_to_cpu(il->card_alive_init.voltage); 109862306a36Sopenharmony_ci voltage_compensation = 109962306a36Sopenharmony_ci il4965_get_voltage_compensation(voltage, init_voltage); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci D_TXPOWER("curr volt %d eeprom volt %d volt comp %d\n", init_voltage, 110262306a36Sopenharmony_ci voltage, voltage_compensation); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* get current temperature (Celsius) */ 110562306a36Sopenharmony_ci current_temp = max(il->temperature, IL_TX_POWER_TEMPERATURE_MIN); 110662306a36Sopenharmony_ci current_temp = min(il->temperature, IL_TX_POWER_TEMPERATURE_MAX); 110762306a36Sopenharmony_ci current_temp = kelvin_to_celsius(current_temp); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci /* select thermal txpower adjustment params, based on channel group 111062306a36Sopenharmony_ci * (same frequency group used for mimo txatten adjustment) */ 111162306a36Sopenharmony_ci degrees_per_05db_num = 111262306a36Sopenharmony_ci tx_power_cmp_tble[txatten_grp].degrees_per_05db_a; 111362306a36Sopenharmony_ci degrees_per_05db_denom = 111462306a36Sopenharmony_ci tx_power_cmp_tble[txatten_grp].degrees_per_05db_a_denom; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci /* get per-chain txpower values from factory measurements */ 111762306a36Sopenharmony_ci for (c = 0; c < 2; c++) { 111862306a36Sopenharmony_ci measurement = &ch_eeprom_info.measurements[c][1]; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci /* txgain adjustment (in half-dB steps) based on difference 112162306a36Sopenharmony_ci * between factory and current temperature */ 112262306a36Sopenharmony_ci factory_temp = measurement->temperature; 112362306a36Sopenharmony_ci il4965_math_div_round((current_temp - 112462306a36Sopenharmony_ci factory_temp) * degrees_per_05db_denom, 112562306a36Sopenharmony_ci degrees_per_05db_num, 112662306a36Sopenharmony_ci &temperature_comp[c]); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci factory_gain_idx[c] = measurement->gain_idx; 112962306a36Sopenharmony_ci factory_actual_pwr[c] = measurement->actual_pow; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci D_TXPOWER("chain = %d\n", c); 113262306a36Sopenharmony_ci D_TXPOWER("fctry tmp %d, " "curr tmp %d, comp %d steps\n", 113362306a36Sopenharmony_ci factory_temp, current_temp, temperature_comp[c]); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci D_TXPOWER("fctry idx %d, fctry pwr %d\n", factory_gain_idx[c], 113662306a36Sopenharmony_ci factory_actual_pwr[c]); 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci /* for each of 33 bit-rates (including 1 for CCK) */ 114062306a36Sopenharmony_ci for (i = 0; i < POWER_TBL_NUM_ENTRIES; i++) { 114162306a36Sopenharmony_ci u8 is_mimo_rate; 114262306a36Sopenharmony_ci union il4965_tx_power_dual_stream tx_power; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci /* for mimo, reduce each chain's txpower by half 114562306a36Sopenharmony_ci * (3dB, 6 steps), so total output power is regulatory 114662306a36Sopenharmony_ci * compliant. */ 114762306a36Sopenharmony_ci if (i & 0x8) { 114862306a36Sopenharmony_ci current_regulatory = 114962306a36Sopenharmony_ci reg_limit - 115062306a36Sopenharmony_ci IL_TX_POWER_MIMO_REGULATORY_COMPENSATION; 115162306a36Sopenharmony_ci is_mimo_rate = 1; 115262306a36Sopenharmony_ci } else { 115362306a36Sopenharmony_ci current_regulatory = reg_limit; 115462306a36Sopenharmony_ci is_mimo_rate = 0; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci /* find txpower limit, either hardware or regulatory */ 115862306a36Sopenharmony_ci power_limit = saturation_power - back_off_table[i]; 115962306a36Sopenharmony_ci if (power_limit > current_regulatory) 116062306a36Sopenharmony_ci power_limit = current_regulatory; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci /* reduce user's txpower request if necessary 116362306a36Sopenharmony_ci * for this rate on this channel */ 116462306a36Sopenharmony_ci target_power = user_target_power; 116562306a36Sopenharmony_ci if (target_power > power_limit) 116662306a36Sopenharmony_ci target_power = power_limit; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci D_TXPOWER("rate %d sat %d reg %d usr %d tgt %d\n", i, 116962306a36Sopenharmony_ci saturation_power - back_off_table[i], 117062306a36Sopenharmony_ci current_regulatory, user_target_power, target_power); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci /* for each of 2 Tx chains (radio transmitters) */ 117362306a36Sopenharmony_ci for (c = 0; c < 2; c++) { 117462306a36Sopenharmony_ci s32 atten_value; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci if (is_mimo_rate) 117762306a36Sopenharmony_ci atten_value = 117862306a36Sopenharmony_ci (s32) le32_to_cpu(il->card_alive_init. 117962306a36Sopenharmony_ci tx_atten[txatten_grp][c]); 118062306a36Sopenharmony_ci else 118162306a36Sopenharmony_ci atten_value = 0; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* calculate idx; higher idx means lower txpower */ 118462306a36Sopenharmony_ci power_idx = 118562306a36Sopenharmony_ci (u8) (factory_gain_idx[c] - 118662306a36Sopenharmony_ci (target_power - factory_actual_pwr[c]) - 118762306a36Sopenharmony_ci temperature_comp[c] - voltage_compensation + 118862306a36Sopenharmony_ci atten_value); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci/* D_TXPOWER("calculated txpower idx %d\n", 119162306a36Sopenharmony_ci power_idx); */ 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (power_idx < get_min_power_idx(i, band)) 119462306a36Sopenharmony_ci power_idx = get_min_power_idx(i, band); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci /* adjust 5 GHz idx to support negative idxes */ 119762306a36Sopenharmony_ci if (!band) 119862306a36Sopenharmony_ci power_idx += 9; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci /* CCK, rate 32, reduce txpower for CCK */ 120162306a36Sopenharmony_ci if (i == POWER_TBL_CCK_ENTRY) 120262306a36Sopenharmony_ci power_idx += 120362306a36Sopenharmony_ci IL_TX_POWER_CCK_COMPENSATION_C_STEP; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* stay within the table! */ 120662306a36Sopenharmony_ci if (power_idx > 107) { 120762306a36Sopenharmony_ci IL_WARN("txpower idx %d > 107\n", power_idx); 120862306a36Sopenharmony_ci power_idx = 107; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci if (power_idx < 0) { 121162306a36Sopenharmony_ci IL_WARN("txpower idx %d < 0\n", power_idx); 121262306a36Sopenharmony_ci power_idx = 0; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci /* fill txpower command for this rate/chain */ 121662306a36Sopenharmony_ci tx_power.s.radio_tx_gain[c] = 121762306a36Sopenharmony_ci gain_table[band][power_idx].radio; 121862306a36Sopenharmony_ci tx_power.s.dsp_predis_atten[c] = 121962306a36Sopenharmony_ci gain_table[band][power_idx].dsp; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci D_TXPOWER("chain %d mimo %d idx %d " 122262306a36Sopenharmony_ci "gain 0x%02x dsp %d\n", c, atten_value, 122362306a36Sopenharmony_ci power_idx, tx_power.s.radio_tx_gain[c], 122462306a36Sopenharmony_ci tx_power.s.dsp_predis_atten[c]); 122562306a36Sopenharmony_ci } /* for each chain */ 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci tx_power_tbl->power_tbl[i].dw = cpu_to_le32(tx_power.dw); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci } /* for each rate */ 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci return 0; 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci/* 123562306a36Sopenharmony_ci * il4965_send_tx_power - Configure the TXPOWER level user limit 123662306a36Sopenharmony_ci * 123762306a36Sopenharmony_ci * Uses the active RXON for channel, band, and characteristics (ht40, high) 123862306a36Sopenharmony_ci * The power limit is taken from il->tx_power_user_lmt. 123962306a36Sopenharmony_ci */ 124062306a36Sopenharmony_cistatic int 124162306a36Sopenharmony_ciil4965_send_tx_power(struct il_priv *il) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci struct il4965_txpowertable_cmd cmd = { 0 }; 124462306a36Sopenharmony_ci int ret; 124562306a36Sopenharmony_ci u8 band = 0; 124662306a36Sopenharmony_ci bool is_ht40 = false; 124762306a36Sopenharmony_ci u8 ctrl_chan_high = 0; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci if (WARN_ONCE 125062306a36Sopenharmony_ci (test_bit(S_SCAN_HW, &il->status), 125162306a36Sopenharmony_ci "TX Power requested while scanning!\n")) 125262306a36Sopenharmony_ci return -EAGAIN; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci band = il->band == NL80211_BAND_2GHZ; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci is_ht40 = iw4965_is_ht40_channel(il->active.flags); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci if (is_ht40 && (il->active.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) 125962306a36Sopenharmony_ci ctrl_chan_high = 1; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci cmd.band = band; 126262306a36Sopenharmony_ci cmd.channel = il->active.channel; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci ret = 126562306a36Sopenharmony_ci il4965_fill_txpower_tbl(il, band, le16_to_cpu(il->active.channel), 126662306a36Sopenharmony_ci is_ht40, ctrl_chan_high, &cmd.tx_power); 126762306a36Sopenharmony_ci if (ret) 126862306a36Sopenharmony_ci goto out; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci ret = il_send_cmd_pdu(il, C_TX_PWR_TBL, sizeof(cmd), &cmd); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ciout: 127362306a36Sopenharmony_ci return ret; 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic int 127762306a36Sopenharmony_ciil4965_send_rxon_assoc(struct il_priv *il) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci int ret = 0; 128062306a36Sopenharmony_ci struct il4965_rxon_assoc_cmd rxon_assoc; 128162306a36Sopenharmony_ci const struct il_rxon_cmd *rxon1 = &il->staging; 128262306a36Sopenharmony_ci const struct il_rxon_cmd *rxon2 = &il->active; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (rxon1->flags == rxon2->flags && 128762306a36Sopenharmony_ci rxon1->filter_flags == rxon2->filter_flags && 128862306a36Sopenharmony_ci rxon1->cck_basic_rates == rxon2->cck_basic_rates && 128962306a36Sopenharmony_ci rxon1->ofdm_ht_single_stream_basic_rates == 129062306a36Sopenharmony_ci rxon2->ofdm_ht_single_stream_basic_rates && 129162306a36Sopenharmony_ci rxon1->ofdm_ht_dual_stream_basic_rates == 129262306a36Sopenharmony_ci rxon2->ofdm_ht_dual_stream_basic_rates && 129362306a36Sopenharmony_ci rxon1->rx_chain == rxon2->rx_chain && 129462306a36Sopenharmony_ci rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates) { 129562306a36Sopenharmony_ci D_INFO("Using current RXON_ASSOC. Not resending.\n"); 129662306a36Sopenharmony_ci return 0; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci rxon_assoc.flags = il->staging.flags; 130062306a36Sopenharmony_ci rxon_assoc.filter_flags = il->staging.filter_flags; 130162306a36Sopenharmony_ci rxon_assoc.ofdm_basic_rates = il->staging.ofdm_basic_rates; 130262306a36Sopenharmony_ci rxon_assoc.cck_basic_rates = il->staging.cck_basic_rates; 130362306a36Sopenharmony_ci rxon_assoc.reserved = 0; 130462306a36Sopenharmony_ci rxon_assoc.ofdm_ht_single_stream_basic_rates = 130562306a36Sopenharmony_ci il->staging.ofdm_ht_single_stream_basic_rates; 130662306a36Sopenharmony_ci rxon_assoc.ofdm_ht_dual_stream_basic_rates = 130762306a36Sopenharmony_ci il->staging.ofdm_ht_dual_stream_basic_rates; 130862306a36Sopenharmony_ci rxon_assoc.rx_chain_select_flags = il->staging.rx_chain; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci ret = 131162306a36Sopenharmony_ci il_send_cmd_pdu_async(il, C_RXON_ASSOC, sizeof(rxon_assoc), 131262306a36Sopenharmony_ci &rxon_assoc, NULL); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci return ret; 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistatic int 131862306a36Sopenharmony_ciil4965_commit_rxon(struct il_priv *il) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci /* cast away the const for active_rxon in this function */ 132162306a36Sopenharmony_ci struct il_rxon_cmd *active_rxon = (void *)&il->active; 132262306a36Sopenharmony_ci int ret; 132362306a36Sopenharmony_ci bool new_assoc = !!(il->staging.filter_flags & RXON_FILTER_ASSOC_MSK); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci if (!il_is_alive(il)) 132662306a36Sopenharmony_ci return -EBUSY; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci /* always get timestamp with Rx frame */ 132962306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_TSF2HOST_MSK; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci ret = il_check_rxon_cmd(il); 133262306a36Sopenharmony_ci if (ret) { 133362306a36Sopenharmony_ci IL_ERR("Invalid RXON configuration. Not committing.\n"); 133462306a36Sopenharmony_ci return -EINVAL; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci /* 133862306a36Sopenharmony_ci * receive commit_rxon request 133962306a36Sopenharmony_ci * abort any previous channel switch if still in process 134062306a36Sopenharmony_ci */ 134162306a36Sopenharmony_ci if (test_bit(S_CHANNEL_SWITCH_PENDING, &il->status) && 134262306a36Sopenharmony_ci il->switch_channel != il->staging.channel) { 134362306a36Sopenharmony_ci D_11H("abort channel switch on %d\n", 134462306a36Sopenharmony_ci le16_to_cpu(il->switch_channel)); 134562306a36Sopenharmony_ci il_chswitch_done(il, false); 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* If we don't need to send a full RXON, we can use 134962306a36Sopenharmony_ci * il_rxon_assoc_cmd which is used to reconfigure filter 135062306a36Sopenharmony_ci * and other flags for the current radio configuration. */ 135162306a36Sopenharmony_ci if (!il_full_rxon_required(il)) { 135262306a36Sopenharmony_ci ret = il_send_rxon_assoc(il); 135362306a36Sopenharmony_ci if (ret) { 135462306a36Sopenharmony_ci IL_ERR("Error setting RXON_ASSOC (%d)\n", ret); 135562306a36Sopenharmony_ci return ret; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); 135962306a36Sopenharmony_ci il_print_rx_config_cmd(il); 136062306a36Sopenharmony_ci /* 136162306a36Sopenharmony_ci * We do not commit tx power settings while channel changing, 136262306a36Sopenharmony_ci * do it now if tx power changed. 136362306a36Sopenharmony_ci */ 136462306a36Sopenharmony_ci il_set_tx_power(il, il->tx_power_next, false); 136562306a36Sopenharmony_ci return 0; 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* If we are currently associated and the new config requires 136962306a36Sopenharmony_ci * an RXON_ASSOC and the new config wants the associated mask enabled, 137062306a36Sopenharmony_ci * we must clear the associated from the active configuration 137162306a36Sopenharmony_ci * before we apply the new config */ 137262306a36Sopenharmony_ci if (il_is_associated(il) && new_assoc) { 137362306a36Sopenharmony_ci D_INFO("Toggling associated bit on current RXON\n"); 137462306a36Sopenharmony_ci active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci ret = 137762306a36Sopenharmony_ci il_send_cmd_pdu(il, C_RXON, 137862306a36Sopenharmony_ci sizeof(struct il_rxon_cmd), active_rxon); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci /* If the mask clearing failed then we set 138162306a36Sopenharmony_ci * active_rxon back to what it was previously */ 138262306a36Sopenharmony_ci if (ret) { 138362306a36Sopenharmony_ci active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK; 138462306a36Sopenharmony_ci IL_ERR("Error clearing ASSOC_MSK (%d)\n", ret); 138562306a36Sopenharmony_ci return ret; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci il_clear_ucode_stations(il); 138862306a36Sopenharmony_ci il_restore_stations(il); 138962306a36Sopenharmony_ci ret = il4965_restore_default_wep_keys(il); 139062306a36Sopenharmony_ci if (ret) { 139162306a36Sopenharmony_ci IL_ERR("Failed to restore WEP keys (%d)\n", ret); 139262306a36Sopenharmony_ci return ret; 139362306a36Sopenharmony_ci } 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci D_INFO("Sending RXON\n" "* with%s RXON_FILTER_ASSOC_MSK\n" 139762306a36Sopenharmony_ci "* channel = %d\n" "* bssid = %pM\n", (new_assoc ? "" : "out"), 139862306a36Sopenharmony_ci le16_to_cpu(il->staging.channel), il->staging.bssid_addr); 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci il_set_rxon_hwcrypto(il, !il->cfg->mod_params->sw_crypto); 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci /* Apply the new configuration 140362306a36Sopenharmony_ci * RXON unassoc clears the station table in uCode so restoration of 140462306a36Sopenharmony_ci * stations is needed after it (the RXON command) completes 140562306a36Sopenharmony_ci */ 140662306a36Sopenharmony_ci if (!new_assoc) { 140762306a36Sopenharmony_ci ret = 140862306a36Sopenharmony_ci il_send_cmd_pdu(il, C_RXON, 140962306a36Sopenharmony_ci sizeof(struct il_rxon_cmd), &il->staging); 141062306a36Sopenharmony_ci if (ret) { 141162306a36Sopenharmony_ci IL_ERR("Error setting new RXON (%d)\n", ret); 141262306a36Sopenharmony_ci return ret; 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci D_INFO("Return from !new_assoc RXON.\n"); 141562306a36Sopenharmony_ci memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); 141662306a36Sopenharmony_ci il_clear_ucode_stations(il); 141762306a36Sopenharmony_ci il_restore_stations(il); 141862306a36Sopenharmony_ci ret = il4965_restore_default_wep_keys(il); 141962306a36Sopenharmony_ci if (ret) { 142062306a36Sopenharmony_ci IL_ERR("Failed to restore WEP keys (%d)\n", ret); 142162306a36Sopenharmony_ci return ret; 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci if (new_assoc) { 142562306a36Sopenharmony_ci il->start_calib = 0; 142662306a36Sopenharmony_ci /* Apply the new configuration 142762306a36Sopenharmony_ci * RXON assoc doesn't clear the station table in uCode, 142862306a36Sopenharmony_ci */ 142962306a36Sopenharmony_ci ret = 143062306a36Sopenharmony_ci il_send_cmd_pdu(il, C_RXON, 143162306a36Sopenharmony_ci sizeof(struct il_rxon_cmd), &il->staging); 143262306a36Sopenharmony_ci if (ret) { 143362306a36Sopenharmony_ci IL_ERR("Error setting new RXON (%d)\n", ret); 143462306a36Sopenharmony_ci return ret; 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci il_print_rx_config_cmd(il); 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci il4965_init_sensitivity(il); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci /* If we issue a new RXON command which required a tune then we must 144362306a36Sopenharmony_ci * send a new TXPOWER command or we won't be able to Tx any frames */ 144462306a36Sopenharmony_ci ret = il_set_tx_power(il, il->tx_power_next, true); 144562306a36Sopenharmony_ci if (ret) { 144662306a36Sopenharmony_ci IL_ERR("Error sending TX power (%d)\n", ret); 144762306a36Sopenharmony_ci return ret; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci return 0; 145162306a36Sopenharmony_ci} 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_cistatic int 145462306a36Sopenharmony_ciil4965_hw_channel_switch(struct il_priv *il, 145562306a36Sopenharmony_ci struct ieee80211_channel_switch *ch_switch) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci int rc; 145862306a36Sopenharmony_ci u8 band = 0; 145962306a36Sopenharmony_ci bool is_ht40 = false; 146062306a36Sopenharmony_ci u8 ctrl_chan_high = 0; 146162306a36Sopenharmony_ci struct il4965_channel_switch_cmd cmd; 146262306a36Sopenharmony_ci const struct il_channel_info *ch_info; 146362306a36Sopenharmony_ci u32 switch_time_in_usec, ucode_switch_time; 146462306a36Sopenharmony_ci u16 ch; 146562306a36Sopenharmony_ci u32 tsf_low; 146662306a36Sopenharmony_ci u8 switch_count; 146762306a36Sopenharmony_ci u16 beacon_interval = le16_to_cpu(il->timing.beacon_interval); 146862306a36Sopenharmony_ci struct ieee80211_vif *vif = il->vif; 146962306a36Sopenharmony_ci band = (il->band == NL80211_BAND_2GHZ); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci if (WARN_ON_ONCE(vif == NULL)) 147262306a36Sopenharmony_ci return -EIO; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci is_ht40 = iw4965_is_ht40_channel(il->staging.flags); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci if (is_ht40 && (il->staging.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) 147762306a36Sopenharmony_ci ctrl_chan_high = 1; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci cmd.band = band; 148062306a36Sopenharmony_ci cmd.expect_beacon = 0; 148162306a36Sopenharmony_ci ch = ch_switch->chandef.chan->hw_value; 148262306a36Sopenharmony_ci cmd.channel = cpu_to_le16(ch); 148362306a36Sopenharmony_ci cmd.rxon_flags = il->staging.flags; 148462306a36Sopenharmony_ci cmd.rxon_filter_flags = il->staging.filter_flags; 148562306a36Sopenharmony_ci switch_count = ch_switch->count; 148662306a36Sopenharmony_ci tsf_low = ch_switch->timestamp & 0x0ffffffff; 148762306a36Sopenharmony_ci /* 148862306a36Sopenharmony_ci * calculate the ucode channel switch time 148962306a36Sopenharmony_ci * adding TSF as one of the factor for when to switch 149062306a36Sopenharmony_ci */ 149162306a36Sopenharmony_ci if (il->ucode_beacon_time > tsf_low && beacon_interval) { 149262306a36Sopenharmony_ci if (switch_count > 149362306a36Sopenharmony_ci ((il->ucode_beacon_time - tsf_low) / beacon_interval)) { 149462306a36Sopenharmony_ci switch_count -= 149562306a36Sopenharmony_ci (il->ucode_beacon_time - tsf_low) / beacon_interval; 149662306a36Sopenharmony_ci } else 149762306a36Sopenharmony_ci switch_count = 0; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci if (switch_count <= 1) 150062306a36Sopenharmony_ci cmd.switch_time = cpu_to_le32(il->ucode_beacon_time); 150162306a36Sopenharmony_ci else { 150262306a36Sopenharmony_ci switch_time_in_usec = 150362306a36Sopenharmony_ci vif->bss_conf.beacon_int * switch_count * TIME_UNIT; 150462306a36Sopenharmony_ci ucode_switch_time = 150562306a36Sopenharmony_ci il_usecs_to_beacons(il, switch_time_in_usec, 150662306a36Sopenharmony_ci beacon_interval); 150762306a36Sopenharmony_ci cmd.switch_time = 150862306a36Sopenharmony_ci il_add_beacon_time(il, il->ucode_beacon_time, 150962306a36Sopenharmony_ci ucode_switch_time, beacon_interval); 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci D_11H("uCode time for the switch is 0x%x\n", cmd.switch_time); 151262306a36Sopenharmony_ci ch_info = il_get_channel_info(il, il->band, ch); 151362306a36Sopenharmony_ci if (ch_info) 151462306a36Sopenharmony_ci cmd.expect_beacon = il_is_channel_radar(ch_info); 151562306a36Sopenharmony_ci else { 151662306a36Sopenharmony_ci IL_ERR("invalid channel switch from %u to %u\n", 151762306a36Sopenharmony_ci il->active.channel, ch); 151862306a36Sopenharmony_ci return -EFAULT; 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci rc = il4965_fill_txpower_tbl(il, band, ch, is_ht40, ctrl_chan_high, 152262306a36Sopenharmony_ci &cmd.tx_power); 152362306a36Sopenharmony_ci if (rc) { 152462306a36Sopenharmony_ci D_11H("error:%d fill txpower_tbl\n", rc); 152562306a36Sopenharmony_ci return rc; 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci return il_send_cmd_pdu(il, C_CHANNEL_SWITCH, sizeof(cmd), &cmd); 152962306a36Sopenharmony_ci} 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci/* 153262306a36Sopenharmony_ci * il4965_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array 153362306a36Sopenharmony_ci */ 153462306a36Sopenharmony_cistatic void 153562306a36Sopenharmony_ciil4965_txq_update_byte_cnt_tbl(struct il_priv *il, struct il_tx_queue *txq, 153662306a36Sopenharmony_ci u16 byte_cnt) 153762306a36Sopenharmony_ci{ 153862306a36Sopenharmony_ci struct il4965_scd_bc_tbl *scd_bc_tbl = il->scd_bc_tbls.addr; 153962306a36Sopenharmony_ci int txq_id = txq->q.id; 154062306a36Sopenharmony_ci int write_ptr = txq->q.write_ptr; 154162306a36Sopenharmony_ci int len = byte_cnt + IL_TX_CRC_SIZE + IL_TX_DELIMITER_SIZE; 154262306a36Sopenharmony_ci __le16 bc_ent; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci bc_ent = cpu_to_le16(len & 0xFFF); 154762306a36Sopenharmony_ci /* Set up byte count within first 256 entries */ 154862306a36Sopenharmony_ci scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci /* If within first 64 entries, duplicate at end */ 155162306a36Sopenharmony_ci if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) 155262306a36Sopenharmony_ci scd_bc_tbl[txq_id].tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = 155362306a36Sopenharmony_ci bc_ent; 155462306a36Sopenharmony_ci} 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci/* 155762306a36Sopenharmony_ci * il4965_hw_get_temperature - return the calibrated temperature (in Kelvin) 155862306a36Sopenharmony_ci * 155962306a36Sopenharmony_ci * A return of <0 indicates bogus data in the stats 156062306a36Sopenharmony_ci */ 156162306a36Sopenharmony_cistatic int 156262306a36Sopenharmony_ciil4965_hw_get_temperature(struct il_priv *il) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci s32 temperature; 156562306a36Sopenharmony_ci s32 vt; 156662306a36Sopenharmony_ci s32 R1, R2, R3; 156762306a36Sopenharmony_ci u32 R4; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci if (test_bit(S_TEMPERATURE, &il->status) && 157062306a36Sopenharmony_ci (il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK)) { 157162306a36Sopenharmony_ci D_TEMP("Running HT40 temperature calibration\n"); 157262306a36Sopenharmony_ci R1 = (s32) le32_to_cpu(il->card_alive_init.therm_r1[1]); 157362306a36Sopenharmony_ci R2 = (s32) le32_to_cpu(il->card_alive_init.therm_r2[1]); 157462306a36Sopenharmony_ci R3 = (s32) le32_to_cpu(il->card_alive_init.therm_r3[1]); 157562306a36Sopenharmony_ci R4 = le32_to_cpu(il->card_alive_init.therm_r4[1]); 157662306a36Sopenharmony_ci } else { 157762306a36Sopenharmony_ci D_TEMP("Running temperature calibration\n"); 157862306a36Sopenharmony_ci R1 = (s32) le32_to_cpu(il->card_alive_init.therm_r1[0]); 157962306a36Sopenharmony_ci R2 = (s32) le32_to_cpu(il->card_alive_init.therm_r2[0]); 158062306a36Sopenharmony_ci R3 = (s32) le32_to_cpu(il->card_alive_init.therm_r3[0]); 158162306a36Sopenharmony_ci R4 = le32_to_cpu(il->card_alive_init.therm_r4[0]); 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci /* 158562306a36Sopenharmony_ci * Temperature is only 23 bits, so sign extend out to 32. 158662306a36Sopenharmony_ci * 158762306a36Sopenharmony_ci * NOTE If we haven't received a stats notification yet 158862306a36Sopenharmony_ci * with an updated temperature, use R4 provided to us in the 158962306a36Sopenharmony_ci * "initialize" ALIVE response. 159062306a36Sopenharmony_ci */ 159162306a36Sopenharmony_ci if (!test_bit(S_TEMPERATURE, &il->status)) 159262306a36Sopenharmony_ci vt = sign_extend32(R4, 23); 159362306a36Sopenharmony_ci else 159462306a36Sopenharmony_ci vt = sign_extend32(le32_to_cpu 159562306a36Sopenharmony_ci (il->_4965.stats.general.common.temperature), 159662306a36Sopenharmony_ci 23); 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci D_TEMP("Calib values R[1-3]: %d %d %d R4: %d\n", R1, R2, R3, vt); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci if (R3 == R1) { 160162306a36Sopenharmony_ci IL_ERR("Calibration conflict R1 == R3\n"); 160262306a36Sopenharmony_ci return -1; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci /* Calculate temperature in degrees Kelvin, adjust by 97%. 160662306a36Sopenharmony_ci * Add offset to center the adjustment around 0 degrees Centigrade. */ 160762306a36Sopenharmony_ci temperature = TEMPERATURE_CALIB_A_VAL * (vt - R2); 160862306a36Sopenharmony_ci temperature /= (R3 - R1); 160962306a36Sopenharmony_ci temperature = 161062306a36Sopenharmony_ci (temperature * 97) / 100 + TEMPERATURE_CALIB_KELVIN_OFFSET; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci D_TEMP("Calibrated temperature: %dK, %ldC\n", temperature, 161362306a36Sopenharmony_ci kelvin_to_celsius(temperature)); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci return temperature; 161662306a36Sopenharmony_ci} 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci/* Adjust Txpower only if temperature variance is greater than threshold. */ 161962306a36Sopenharmony_ci#define IL_TEMPERATURE_THRESHOLD 3 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci/* 162262306a36Sopenharmony_ci * il4965_is_temp_calib_needed - determines if new calibration is needed 162362306a36Sopenharmony_ci * 162462306a36Sopenharmony_ci * If the temperature changed has changed sufficiently, then a recalibration 162562306a36Sopenharmony_ci * is needed. 162662306a36Sopenharmony_ci * 162762306a36Sopenharmony_ci * Assumes caller will replace il->last_temperature once calibration 162862306a36Sopenharmony_ci * executed. 162962306a36Sopenharmony_ci */ 163062306a36Sopenharmony_cistatic int 163162306a36Sopenharmony_ciil4965_is_temp_calib_needed(struct il_priv *il) 163262306a36Sopenharmony_ci{ 163362306a36Sopenharmony_ci int temp_diff; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci if (!test_bit(S_STATS, &il->status)) { 163662306a36Sopenharmony_ci D_TEMP("Temperature not updated -- no stats.\n"); 163762306a36Sopenharmony_ci return 0; 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci temp_diff = il->temperature - il->last_temperature; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci /* get absolute value */ 164362306a36Sopenharmony_ci if (temp_diff < 0) { 164462306a36Sopenharmony_ci D_POWER("Getting cooler, delta %d\n", temp_diff); 164562306a36Sopenharmony_ci temp_diff = -temp_diff; 164662306a36Sopenharmony_ci } else if (temp_diff == 0) 164762306a36Sopenharmony_ci D_POWER("Temperature unchanged\n"); 164862306a36Sopenharmony_ci else 164962306a36Sopenharmony_ci D_POWER("Getting warmer, delta %d\n", temp_diff); 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci if (temp_diff < IL_TEMPERATURE_THRESHOLD) { 165262306a36Sopenharmony_ci D_POWER(" => thermal txpower calib not needed\n"); 165362306a36Sopenharmony_ci return 0; 165462306a36Sopenharmony_ci } 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci D_POWER(" => thermal txpower calib needed\n"); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci return 1; 165962306a36Sopenharmony_ci} 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_civoid 166262306a36Sopenharmony_ciil4965_temperature_calib(struct il_priv *il) 166362306a36Sopenharmony_ci{ 166462306a36Sopenharmony_ci s32 temp; 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci temp = il4965_hw_get_temperature(il); 166762306a36Sopenharmony_ci if (IL_TX_POWER_TEMPERATURE_OUT_OF_RANGE(temp)) 166862306a36Sopenharmony_ci return; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (il->temperature != temp) { 167162306a36Sopenharmony_ci if (il->temperature) 167262306a36Sopenharmony_ci D_TEMP("Temperature changed " "from %ldC to %ldC\n", 167362306a36Sopenharmony_ci kelvin_to_celsius(il->temperature), 167462306a36Sopenharmony_ci kelvin_to_celsius(temp)); 167562306a36Sopenharmony_ci else 167662306a36Sopenharmony_ci D_TEMP("Temperature " "initialized to %ldC\n", 167762306a36Sopenharmony_ci kelvin_to_celsius(temp)); 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci il->temperature = temp; 168162306a36Sopenharmony_ci set_bit(S_TEMPERATURE, &il->status); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci if (!il->disable_tx_power_cal && 168462306a36Sopenharmony_ci unlikely(!test_bit(S_SCANNING, &il->status)) && 168562306a36Sopenharmony_ci il4965_is_temp_calib_needed(il)) 168662306a36Sopenharmony_ci queue_work(il->workqueue, &il->txpower_work); 168762306a36Sopenharmony_ci} 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_cistatic u16 169062306a36Sopenharmony_ciil4965_get_hcmd_size(u8 cmd_id, u16 len) 169162306a36Sopenharmony_ci{ 169262306a36Sopenharmony_ci switch (cmd_id) { 169362306a36Sopenharmony_ci case C_RXON: 169462306a36Sopenharmony_ci return (u16) sizeof(struct il4965_rxon_cmd); 169562306a36Sopenharmony_ci default: 169662306a36Sopenharmony_ci return len; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci} 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_cistatic u16 170162306a36Sopenharmony_ciil4965_build_addsta_hcmd(const struct il_addsta_cmd *cmd, u8 * data) 170262306a36Sopenharmony_ci{ 170362306a36Sopenharmony_ci struct il4965_addsta_cmd *addsta = (struct il4965_addsta_cmd *)data; 170462306a36Sopenharmony_ci addsta->mode = cmd->mode; 170562306a36Sopenharmony_ci memcpy(&addsta->sta, &cmd->sta, sizeof(struct sta_id_modify)); 170662306a36Sopenharmony_ci memcpy(&addsta->key, &cmd->key, sizeof(struct il4965_keyinfo)); 170762306a36Sopenharmony_ci addsta->station_flags = cmd->station_flags; 170862306a36Sopenharmony_ci addsta->station_flags_msk = cmd->station_flags_msk; 170962306a36Sopenharmony_ci addsta->tid_disable_tx = cmd->tid_disable_tx; 171062306a36Sopenharmony_ci addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid; 171162306a36Sopenharmony_ci addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid; 171262306a36Sopenharmony_ci addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn; 171362306a36Sopenharmony_ci addsta->sleep_tx_count = cmd->sleep_tx_count; 171462306a36Sopenharmony_ci addsta->reserved1 = cpu_to_le16(0); 171562306a36Sopenharmony_ci addsta->reserved2 = cpu_to_le16(0); 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci return (u16) sizeof(struct il4965_addsta_cmd); 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_cistatic void 172162306a36Sopenharmony_ciil4965_post_scan(struct il_priv *il) 172262306a36Sopenharmony_ci{ 172362306a36Sopenharmony_ci /* 172462306a36Sopenharmony_ci * Since setting the RXON may have been deferred while 172562306a36Sopenharmony_ci * performing the scan, fire one off if needed 172662306a36Sopenharmony_ci */ 172762306a36Sopenharmony_ci if (memcmp(&il->staging, &il->active, sizeof(il->staging))) 172862306a36Sopenharmony_ci il_commit_rxon(il); 172962306a36Sopenharmony_ci} 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_cistatic void 173262306a36Sopenharmony_ciil4965_post_associate(struct il_priv *il) 173362306a36Sopenharmony_ci{ 173462306a36Sopenharmony_ci struct ieee80211_vif *vif = il->vif; 173562306a36Sopenharmony_ci int ret = 0; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci if (!vif || !il->is_open) 173862306a36Sopenharmony_ci return; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 174162306a36Sopenharmony_ci return; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci il_scan_cancel_timeout(il, 200); 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; 174662306a36Sopenharmony_ci il_commit_rxon(il); 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci ret = il_send_rxon_timing(il); 174962306a36Sopenharmony_ci if (ret) 175062306a36Sopenharmony_ci IL_WARN("RXON timing - " "Attempting to continue.\n"); 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci il_set_rxon_ht(il, &il->current_ht_config); 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci if (il->ops->set_rxon_chain) 175762306a36Sopenharmony_ci il->ops->set_rxon_chain(il); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci il->staging.assoc_id = cpu_to_le16(vif->cfg.aid); 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci D_ASSOC("assoc id %d beacon interval %d\n", vif->cfg.aid, 176262306a36Sopenharmony_ci vif->bss_conf.beacon_int); 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci if (vif->bss_conf.use_short_preamble) 176562306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; 176662306a36Sopenharmony_ci else 176762306a36Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { 177062306a36Sopenharmony_ci if (vif->bss_conf.use_short_slot) 177162306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; 177262306a36Sopenharmony_ci else 177362306a36Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; 177462306a36Sopenharmony_ci } 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci il_commit_rxon(il); 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci D_ASSOC("Associated as %d to: %pM\n", vif->cfg.aid, 177962306a36Sopenharmony_ci il->active.bssid_addr); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci switch (vif->type) { 178262306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 178362306a36Sopenharmony_ci break; 178462306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 178562306a36Sopenharmony_ci il4965_send_beacon_cmd(il); 178662306a36Sopenharmony_ci break; 178762306a36Sopenharmony_ci default: 178862306a36Sopenharmony_ci IL_ERR("%s Should not be called in %d mode\n", __func__, 178962306a36Sopenharmony_ci vif->type); 179062306a36Sopenharmony_ci break; 179162306a36Sopenharmony_ci } 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci /* the chain noise calibration will enabled PM upon completion 179462306a36Sopenharmony_ci * If chain noise has already been run, then we need to enable 179562306a36Sopenharmony_ci * power management here */ 179662306a36Sopenharmony_ci if (il->chain_noise_data.state == IL_CHAIN_NOISE_DONE) 179762306a36Sopenharmony_ci il_power_update_mode(il, false); 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci /* Enable Rx differential gain and sensitivity calibrations */ 180062306a36Sopenharmony_ci il4965_chain_noise_reset(il); 180162306a36Sopenharmony_ci il->start_calib = 1; 180262306a36Sopenharmony_ci} 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_cistatic void 180562306a36Sopenharmony_ciil4965_config_ap(struct il_priv *il) 180662306a36Sopenharmony_ci{ 180762306a36Sopenharmony_ci struct ieee80211_vif *vif = il->vif; 180862306a36Sopenharmony_ci int ret = 0; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 181362306a36Sopenharmony_ci return; 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci /* The following should be done only at AP bring up */ 181662306a36Sopenharmony_ci if (!il_is_associated(il)) { 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci /* RXON - unassoc (to set timing command) */ 181962306a36Sopenharmony_ci il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; 182062306a36Sopenharmony_ci il_commit_rxon(il); 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci /* RXON Timing */ 182362306a36Sopenharmony_ci ret = il_send_rxon_timing(il); 182462306a36Sopenharmony_ci if (ret) 182562306a36Sopenharmony_ci IL_WARN("RXON timing failed - " 182662306a36Sopenharmony_ci "Attempting to continue.\n"); 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci /* AP has all antennas */ 182962306a36Sopenharmony_ci il->chain_noise_data.active_chains = il->hw_params.valid_rx_ant; 183062306a36Sopenharmony_ci il_set_rxon_ht(il, &il->current_ht_config); 183162306a36Sopenharmony_ci if (il->ops->set_rxon_chain) 183262306a36Sopenharmony_ci il->ops->set_rxon_chain(il); 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci il->staging.assoc_id = 0; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci if (vif->bss_conf.use_short_preamble) 183762306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; 183862306a36Sopenharmony_ci else 183962306a36Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { 184262306a36Sopenharmony_ci if (vif->bss_conf.use_short_slot) 184362306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; 184462306a36Sopenharmony_ci else 184562306a36Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; 184662306a36Sopenharmony_ci } 184762306a36Sopenharmony_ci /* need to send beacon cmd before committing assoc RXON! */ 184862306a36Sopenharmony_ci il4965_send_beacon_cmd(il); 184962306a36Sopenharmony_ci /* restore RXON assoc */ 185062306a36Sopenharmony_ci il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; 185162306a36Sopenharmony_ci il_commit_rxon(il); 185262306a36Sopenharmony_ci } 185362306a36Sopenharmony_ci il4965_send_beacon_cmd(il); 185462306a36Sopenharmony_ci} 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ciconst struct il_ops il4965_ops = { 185762306a36Sopenharmony_ci .txq_update_byte_cnt_tbl = il4965_txq_update_byte_cnt_tbl, 185862306a36Sopenharmony_ci .txq_attach_buf_to_tfd = il4965_hw_txq_attach_buf_to_tfd, 185962306a36Sopenharmony_ci .txq_free_tfd = il4965_hw_txq_free_tfd, 186062306a36Sopenharmony_ci .txq_init = il4965_hw_tx_queue_init, 186162306a36Sopenharmony_ci .is_valid_rtc_data_addr = il4965_hw_valid_rtc_data_addr, 186262306a36Sopenharmony_ci .init_alive_start = il4965_init_alive_start, 186362306a36Sopenharmony_ci .load_ucode = il4965_load_bsm, 186462306a36Sopenharmony_ci .dump_nic_error_log = il4965_dump_nic_error_log, 186562306a36Sopenharmony_ci .dump_fh = il4965_dump_fh, 186662306a36Sopenharmony_ci .set_channel_switch = il4965_hw_channel_switch, 186762306a36Sopenharmony_ci .apm_init = il_apm_init, 186862306a36Sopenharmony_ci .send_tx_power = il4965_send_tx_power, 186962306a36Sopenharmony_ci .update_chain_flags = il4965_update_chain_flags, 187062306a36Sopenharmony_ci .eeprom_acquire_semaphore = il4965_eeprom_acquire_semaphore, 187162306a36Sopenharmony_ci .eeprom_release_semaphore = il4965_eeprom_release_semaphore, 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci .rxon_assoc = il4965_send_rxon_assoc, 187462306a36Sopenharmony_ci .commit_rxon = il4965_commit_rxon, 187562306a36Sopenharmony_ci .set_rxon_chain = il4965_set_rxon_chain, 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci .get_hcmd_size = il4965_get_hcmd_size, 187862306a36Sopenharmony_ci .build_addsta_hcmd = il4965_build_addsta_hcmd, 187962306a36Sopenharmony_ci .request_scan = il4965_request_scan, 188062306a36Sopenharmony_ci .post_scan = il4965_post_scan, 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci .post_associate = il4965_post_associate, 188362306a36Sopenharmony_ci .config_ap = il4965_config_ap, 188462306a36Sopenharmony_ci .manage_ibss_station = il4965_manage_ibss_station, 188562306a36Sopenharmony_ci .update_bcast_stations = il4965_update_bcast_stations, 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci .send_led_cmd = il4965_send_led_cmd, 188862306a36Sopenharmony_ci}; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_cistruct il_cfg il4965_cfg = { 189162306a36Sopenharmony_ci .name = "Intel(R) Wireless WiFi Link 4965AGN", 189262306a36Sopenharmony_ci .fw_name_pre = IL4965_FW_PRE, 189362306a36Sopenharmony_ci .ucode_api_max = IL4965_UCODE_API_MAX, 189462306a36Sopenharmony_ci .ucode_api_min = IL4965_UCODE_API_MIN, 189562306a36Sopenharmony_ci .sku = IL_SKU_A | IL_SKU_G | IL_SKU_N, 189662306a36Sopenharmony_ci .valid_tx_ant = ANT_AB, 189762306a36Sopenharmony_ci .valid_rx_ant = ANT_ABC, 189862306a36Sopenharmony_ci .eeprom_ver = EEPROM_4965_EEPROM_VERSION, 189962306a36Sopenharmony_ci .eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION, 190062306a36Sopenharmony_ci .mod_params = &il4965_mod_params, 190162306a36Sopenharmony_ci .led_mode = IL_LED_BLINK, 190262306a36Sopenharmony_ci /* 190362306a36Sopenharmony_ci * Force use of chains B and C for scan RX on 5 GHz band 190462306a36Sopenharmony_ci * because the device has off-channel reception on chain A. 190562306a36Sopenharmony_ci */ 190662306a36Sopenharmony_ci .scan_rx_antennas[NL80211_BAND_5GHZ] = ANT_BC, 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci .eeprom_size = IL4965_EEPROM_IMG_SIZE, 190962306a36Sopenharmony_ci .num_of_queues = IL49_NUM_QUEUES, 191062306a36Sopenharmony_ci .num_of_ampdu_queues = IL49_NUM_AMPDU_QUEUES, 191162306a36Sopenharmony_ci .pll_cfg_val = 0, 191262306a36Sopenharmony_ci .set_l0s = true, 191362306a36Sopenharmony_ci .use_bsm = true, 191462306a36Sopenharmony_ci .led_compensation = 61, 191562306a36Sopenharmony_ci .chain_noise_num_beacons = IL4965_CAL_NUM_BEACONS, 191662306a36Sopenharmony_ci .wd_timeout = IL_DEF_WD_TIMEOUT, 191762306a36Sopenharmony_ci .temperature_kelvin = true, 191862306a36Sopenharmony_ci .ucode_tracing = true, 191962306a36Sopenharmony_ci .sensitivity_calib_by_driver = true, 192062306a36Sopenharmony_ci .chain_noise_calib_by_driver = true, 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci .regulatory_bands = { 192362306a36Sopenharmony_ci EEPROM_REGULATORY_BAND_1_CHANNELS, 192462306a36Sopenharmony_ci EEPROM_REGULATORY_BAND_2_CHANNELS, 192562306a36Sopenharmony_ci EEPROM_REGULATORY_BAND_3_CHANNELS, 192662306a36Sopenharmony_ci EEPROM_REGULATORY_BAND_4_CHANNELS, 192762306a36Sopenharmony_ci EEPROM_REGULATORY_BAND_5_CHANNELS, 192862306a36Sopenharmony_ci EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS, 192962306a36Sopenharmony_ci EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS 193062306a36Sopenharmony_ci }, 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci}; 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci/* Module firmware */ 193562306a36Sopenharmony_ciMODULE_FIRMWARE(IL4965_MODULE_FIRMWARE(IL4965_UCODE_API_MAX)); 1936