18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Contact Information: 78c2ecf20Sopenharmony_ci * Intel Linux Wireless <ilw@linux.intel.com> 88c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci *****************************************************************************/ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/sched.h> 188c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 198c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 208c2ecf20Sopenharmony_ci#include <linux/units.h> 218c2ecf20Sopenharmony_ci#include <net/mac80211.h> 228c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 238c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "common.h" 268c2ecf20Sopenharmony_ci#include "4965.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * il_verify_inst_sparse - verify runtime uCode image in card vs. host, 308c2ecf20Sopenharmony_ci * using sample data 100 bytes apart. If these sample points are good, 318c2ecf20Sopenharmony_ci * it's a pretty good bet that everything between them is good, too. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cistatic int 348c2ecf20Sopenharmony_ciil4965_verify_inst_sparse(struct il_priv *il, __le32 * image, u32 len) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci u32 val; 378c2ecf20Sopenharmony_ci int ret = 0; 388c2ecf20Sopenharmony_ci u32 errcnt = 0; 398c2ecf20Sopenharmony_ci u32 i; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci D_INFO("ucode inst image size is %u\n", len); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci for (i = 0; i < len; i += 100, image += 100 / sizeof(u32)) { 448c2ecf20Sopenharmony_ci /* read data comes through single port, auto-incr addr */ 458c2ecf20Sopenharmony_ci /* NOTE: Use the debugless read so we don't flood kernel log 468c2ecf20Sopenharmony_ci * if IL_DL_IO is set */ 478c2ecf20Sopenharmony_ci il_wr(il, HBUS_TARG_MEM_RADDR, i + IL4965_RTC_INST_LOWER_BOUND); 488c2ecf20Sopenharmony_ci val = _il_rd(il, HBUS_TARG_MEM_RDAT); 498c2ecf20Sopenharmony_ci if (val != le32_to_cpu(*image)) { 508c2ecf20Sopenharmony_ci ret = -EIO; 518c2ecf20Sopenharmony_ci errcnt++; 528c2ecf20Sopenharmony_ci if (errcnt >= 3) 538c2ecf20Sopenharmony_ci break; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return ret; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * il4965_verify_inst_full - verify runtime uCode image in card vs. host, 628c2ecf20Sopenharmony_ci * looking at all data. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistatic int 658c2ecf20Sopenharmony_ciil4965_verify_inst_full(struct il_priv *il, __le32 * image, u32 len) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci u32 val; 688c2ecf20Sopenharmony_ci u32 save_len = len; 698c2ecf20Sopenharmony_ci int ret = 0; 708c2ecf20Sopenharmony_ci u32 errcnt; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci D_INFO("ucode inst image size is %u\n", len); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci il_wr(il, HBUS_TARG_MEM_RADDR, IL4965_RTC_INST_LOWER_BOUND); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci errcnt = 0; 778c2ecf20Sopenharmony_ci for (; len > 0; len -= sizeof(u32), image++) { 788c2ecf20Sopenharmony_ci /* read data comes through single port, auto-incr addr */ 798c2ecf20Sopenharmony_ci /* NOTE: Use the debugless read so we don't flood kernel log 808c2ecf20Sopenharmony_ci * if IL_DL_IO is set */ 818c2ecf20Sopenharmony_ci val = _il_rd(il, HBUS_TARG_MEM_RDAT); 828c2ecf20Sopenharmony_ci if (val != le32_to_cpu(*image)) { 838c2ecf20Sopenharmony_ci IL_ERR("uCode INST section is invalid at " 848c2ecf20Sopenharmony_ci "offset 0x%x, is 0x%x, s/b 0x%x\n", 858c2ecf20Sopenharmony_ci save_len - len, val, le32_to_cpu(*image)); 868c2ecf20Sopenharmony_ci ret = -EIO; 878c2ecf20Sopenharmony_ci errcnt++; 888c2ecf20Sopenharmony_ci if (errcnt >= 20) 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (!errcnt) 948c2ecf20Sopenharmony_ci D_INFO("ucode image in INSTRUCTION memory is good\n"); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * il4965_verify_ucode - determine which instruction image is in SRAM, 1018c2ecf20Sopenharmony_ci * and verify its contents 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ciint 1048c2ecf20Sopenharmony_ciil4965_verify_ucode(struct il_priv *il) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci __le32 *image; 1078c2ecf20Sopenharmony_ci u32 len; 1088c2ecf20Sopenharmony_ci int ret; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* Try bootstrap */ 1118c2ecf20Sopenharmony_ci image = (__le32 *) il->ucode_boot.v_addr; 1128c2ecf20Sopenharmony_ci len = il->ucode_boot.len; 1138c2ecf20Sopenharmony_ci ret = il4965_verify_inst_sparse(il, image, len); 1148c2ecf20Sopenharmony_ci if (!ret) { 1158c2ecf20Sopenharmony_ci D_INFO("Bootstrap uCode is good in inst SRAM\n"); 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* Try initialize */ 1208c2ecf20Sopenharmony_ci image = (__le32 *) il->ucode_init.v_addr; 1218c2ecf20Sopenharmony_ci len = il->ucode_init.len; 1228c2ecf20Sopenharmony_ci ret = il4965_verify_inst_sparse(il, image, len); 1238c2ecf20Sopenharmony_ci if (!ret) { 1248c2ecf20Sopenharmony_ci D_INFO("Initialize uCode is good in inst SRAM\n"); 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Try runtime/protocol */ 1298c2ecf20Sopenharmony_ci image = (__le32 *) il->ucode_code.v_addr; 1308c2ecf20Sopenharmony_ci len = il->ucode_code.len; 1318c2ecf20Sopenharmony_ci ret = il4965_verify_inst_sparse(il, image, len); 1328c2ecf20Sopenharmony_ci if (!ret) { 1338c2ecf20Sopenharmony_ci D_INFO("Runtime uCode is good in inst SRAM\n"); 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci IL_ERR("NO VALID UCODE IMAGE IN INSTRUCTION SRAM!!\n"); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Since nothing seems to match, show first several data entries in 1408c2ecf20Sopenharmony_ci * instruction SRAM, so maybe visual inspection will give a clue. 1418c2ecf20Sopenharmony_ci * Selection of bootstrap image (vs. other images) is arbitrary. */ 1428c2ecf20Sopenharmony_ci image = (__le32 *) il->ucode_boot.v_addr; 1438c2ecf20Sopenharmony_ci len = il->ucode_boot.len; 1448c2ecf20Sopenharmony_ci ret = il4965_verify_inst_full(il, image, len); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return ret; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/****************************************************************************** 1508c2ecf20Sopenharmony_ci * 1518c2ecf20Sopenharmony_ci * EEPROM related functions 1528c2ecf20Sopenharmony_ci * 1538c2ecf20Sopenharmony_ci******************************************************************************/ 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* 1568c2ecf20Sopenharmony_ci * The device's EEPROM semaphore prevents conflicts between driver and uCode 1578c2ecf20Sopenharmony_ci * when accessing the EEPROM; each access is a series of pulses to/from the 1588c2ecf20Sopenharmony_ci * EEPROM chip, not a single event, so even reads could conflict if they 1598c2ecf20Sopenharmony_ci * weren't arbitrated by the semaphore. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ciint 1628c2ecf20Sopenharmony_ciil4965_eeprom_acquire_semaphore(struct il_priv *il) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci u16 count; 1658c2ecf20Sopenharmony_ci int ret; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { 1688c2ecf20Sopenharmony_ci /* Request semaphore */ 1698c2ecf20Sopenharmony_ci il_set_bit(il, CSR_HW_IF_CONFIG_REG, 1708c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* See if we got it */ 1738c2ecf20Sopenharmony_ci ret = 1748c2ecf20Sopenharmony_ci _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, 1758c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, 1768c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, 1778c2ecf20Sopenharmony_ci EEPROM_SEM_TIMEOUT); 1788c2ecf20Sopenharmony_ci if (ret >= 0) 1798c2ecf20Sopenharmony_ci return ret; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_civoid 1868c2ecf20Sopenharmony_ciil4965_eeprom_release_semaphore(struct il_priv *il) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci il_clear_bit(il, CSR_HW_IF_CONFIG_REG, 1898c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciint 1948c2ecf20Sopenharmony_ciil4965_eeprom_check_version(struct il_priv *il) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci u16 eeprom_ver; 1978c2ecf20Sopenharmony_ci u16 calib_ver; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci eeprom_ver = il_eeprom_query16(il, EEPROM_VERSION); 2008c2ecf20Sopenharmony_ci calib_ver = il_eeprom_query16(il, EEPROM_4965_CALIB_VERSION_OFFSET); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (eeprom_ver < il->cfg->eeprom_ver || 2038c2ecf20Sopenharmony_ci calib_ver < il->cfg->eeprom_calib_ver) 2048c2ecf20Sopenharmony_ci goto err; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci IL_INFO("device EEPROM VER=0x%x, CALIB=0x%x\n", eeprom_ver, calib_ver); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_cierr: 2108c2ecf20Sopenharmony_ci IL_ERR("Unsupported (too old) EEPROM VER=0x%x < 0x%x " 2118c2ecf20Sopenharmony_ci "CALIB=0x%x < 0x%x\n", eeprom_ver, il->cfg->eeprom_ver, 2128c2ecf20Sopenharmony_ci calib_ver, il->cfg->eeprom_calib_ver); 2138c2ecf20Sopenharmony_ci return -EINVAL; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_civoid 2188c2ecf20Sopenharmony_ciil4965_eeprom_get_mac(const struct il_priv *il, u8 * mac) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci const u8 *addr = il_eeprom_query_addr(il, 2218c2ecf20Sopenharmony_ci EEPROM_MAC_ADDRESS); 2228c2ecf20Sopenharmony_ci memcpy(mac, addr, ETH_ALEN); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/* Send led command */ 2268c2ecf20Sopenharmony_cistatic int 2278c2ecf20Sopenharmony_ciil4965_send_led_cmd(struct il_priv *il, struct il_led_cmd *led_cmd) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct il_host_cmd cmd = { 2308c2ecf20Sopenharmony_ci .id = C_LEDS, 2318c2ecf20Sopenharmony_ci .len = sizeof(struct il_led_cmd), 2328c2ecf20Sopenharmony_ci .data = led_cmd, 2338c2ecf20Sopenharmony_ci .flags = CMD_ASYNC, 2348c2ecf20Sopenharmony_ci .callback = NULL, 2358c2ecf20Sopenharmony_ci }; 2368c2ecf20Sopenharmony_ci u32 reg; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci reg = _il_rd(il, CSR_LED_REG); 2398c2ecf20Sopenharmony_ci if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) 2408c2ecf20Sopenharmony_ci _il_wr(il, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return il_send_cmd(il, &cmd); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* Set led register off */ 2468c2ecf20Sopenharmony_civoid 2478c2ecf20Sopenharmony_ciil4965_led_enable(struct il_priv *il) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci _il_wr(il, CSR_LED_REG, CSR_LED_REG_TRUN_ON); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int il4965_send_tx_power(struct il_priv *il); 2538c2ecf20Sopenharmony_cistatic int il4965_hw_get_temperature(struct il_priv *il); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* Highest firmware API version supported */ 2568c2ecf20Sopenharmony_ci#define IL4965_UCODE_API_MAX 2 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* Lowest firmware API version supported */ 2598c2ecf20Sopenharmony_ci#define IL4965_UCODE_API_MIN 2 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci#define IL4965_FW_PRE "iwlwifi-4965-" 2628c2ecf20Sopenharmony_ci#define _IL4965_MODULE_FIRMWARE(api) IL4965_FW_PRE #api ".ucode" 2638c2ecf20Sopenharmony_ci#define IL4965_MODULE_FIRMWARE(api) _IL4965_MODULE_FIRMWARE(api) 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* check contents of special bootstrap uCode SRAM */ 2668c2ecf20Sopenharmony_cistatic int 2678c2ecf20Sopenharmony_ciil4965_verify_bsm(struct il_priv *il) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci __le32 *image = il->ucode_boot.v_addr; 2708c2ecf20Sopenharmony_ci u32 len = il->ucode_boot.len; 2718c2ecf20Sopenharmony_ci u32 reg; 2728c2ecf20Sopenharmony_ci u32 val; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci D_INFO("Begin verify bsm\n"); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* verify BSM SRAM contents */ 2778c2ecf20Sopenharmony_ci val = il_rd_prph(il, BSM_WR_DWCOUNT_REG); 2788c2ecf20Sopenharmony_ci for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; 2798c2ecf20Sopenharmony_ci reg += sizeof(u32), image++) { 2808c2ecf20Sopenharmony_ci val = il_rd_prph(il, reg); 2818c2ecf20Sopenharmony_ci if (val != le32_to_cpu(*image)) { 2828c2ecf20Sopenharmony_ci IL_ERR("BSM uCode verification failed at " 2838c2ecf20Sopenharmony_ci "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n", 2848c2ecf20Sopenharmony_ci BSM_SRAM_LOWER_BOUND, reg - BSM_SRAM_LOWER_BOUND, 2858c2ecf20Sopenharmony_ci len, val, le32_to_cpu(*image)); 2868c2ecf20Sopenharmony_ci return -EIO; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci D_INFO("BSM bootstrap uCode image OK\n"); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* 2968c2ecf20Sopenharmony_ci * il4965_load_bsm - Load bootstrap instructions 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * BSM operation: 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program 3018c2ecf20Sopenharmony_ci * in special SRAM that does not power down during RFKILL. When powering back 3028c2ecf20Sopenharmony_ci * up after power-saving sleeps (or during initial uCode load), the BSM loads 3038c2ecf20Sopenharmony_ci * the bootstrap program into the on-board processor, and starts it. 3048c2ecf20Sopenharmony_ci * 3058c2ecf20Sopenharmony_ci * The bootstrap program loads (via DMA) instructions and data for a new 3068c2ecf20Sopenharmony_ci * program from host DRAM locations indicated by the host driver in the 3078c2ecf20Sopenharmony_ci * BSM_DRAM_* registers. Once the new program is loaded, it starts 3088c2ecf20Sopenharmony_ci * automatically. 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * When initializing the NIC, the host driver points the BSM to the 3118c2ecf20Sopenharmony_ci * "initialize" uCode image. This uCode sets up some internal data, then 3128c2ecf20Sopenharmony_ci * notifies host via "initialize alive" that it is complete. 3138c2ecf20Sopenharmony_ci * 3148c2ecf20Sopenharmony_ci * The host then replaces the BSM_DRAM_* pointer values to point to the 3158c2ecf20Sopenharmony_ci * normal runtime uCode instructions and a backup uCode data cache buffer 3168c2ecf20Sopenharmony_ci * (filled initially with starting data values for the on-board processor), 3178c2ecf20Sopenharmony_ci * then triggers the "initialize" uCode to load and launch the runtime uCode, 3188c2ecf20Sopenharmony_ci * which begins normal operation. 3198c2ecf20Sopenharmony_ci * 3208c2ecf20Sopenharmony_ci * When doing a power-save shutdown, runtime uCode saves data SRAM into 3218c2ecf20Sopenharmony_ci * the backup data cache in DRAM before SRAM is powered down. 3228c2ecf20Sopenharmony_ci * 3238c2ecf20Sopenharmony_ci * When powering back up, the BSM loads the bootstrap program. This reloads 3248c2ecf20Sopenharmony_ci * the runtime uCode instructions and the backup data cache into SRAM, 3258c2ecf20Sopenharmony_ci * and re-launches the runtime uCode from where it left off. 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_cistatic int 3288c2ecf20Sopenharmony_ciil4965_load_bsm(struct il_priv *il) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci __le32 *image = il->ucode_boot.v_addr; 3318c2ecf20Sopenharmony_ci u32 len = il->ucode_boot.len; 3328c2ecf20Sopenharmony_ci dma_addr_t pinst; 3338c2ecf20Sopenharmony_ci dma_addr_t pdata; 3348c2ecf20Sopenharmony_ci u32 inst_len; 3358c2ecf20Sopenharmony_ci u32 data_len; 3368c2ecf20Sopenharmony_ci int i; 3378c2ecf20Sopenharmony_ci u32 done; 3388c2ecf20Sopenharmony_ci u32 reg_offset; 3398c2ecf20Sopenharmony_ci int ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci D_INFO("Begin load bsm\n"); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci il->ucode_type = UCODE_RT; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* make sure bootstrap program is no larger than BSM's SRAM size */ 3468c2ecf20Sopenharmony_ci if (len > IL49_MAX_BSM_SIZE) 3478c2ecf20Sopenharmony_ci return -EINVAL; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Tell bootstrap uCode where to find the "Initialize" uCode 3508c2ecf20Sopenharmony_ci * in host DRAM ... host DRAM physical address bits 35:4 for 4965. 3518c2ecf20Sopenharmony_ci * NOTE: il_init_alive_start() will replace these values, 3528c2ecf20Sopenharmony_ci * after the "initialize" uCode has run, to point to 3538c2ecf20Sopenharmony_ci * runtime/protocol instructions and backup data cache. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_ci pinst = il->ucode_init.p_addr >> 4; 3568c2ecf20Sopenharmony_ci pdata = il->ucode_init_data.p_addr >> 4; 3578c2ecf20Sopenharmony_ci inst_len = il->ucode_init.len; 3588c2ecf20Sopenharmony_ci data_len = il->ucode_init_data.len; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); 3618c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); 3628c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); 3638c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* Fill BSM memory with bootstrap instructions */ 3668c2ecf20Sopenharmony_ci for (reg_offset = BSM_SRAM_LOWER_BOUND; 3678c2ecf20Sopenharmony_ci reg_offset < BSM_SRAM_LOWER_BOUND + len; 3688c2ecf20Sopenharmony_ci reg_offset += sizeof(u32), image++) 3698c2ecf20Sopenharmony_ci _il_wr_prph(il, reg_offset, le32_to_cpu(*image)); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ret = il4965_verify_bsm(il); 3728c2ecf20Sopenharmony_ci if (ret) 3738c2ecf20Sopenharmony_ci return ret; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ 3768c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_WR_MEM_SRC_REG, 0x0); 3778c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_WR_MEM_DST_REG, IL49_RTC_INST_LOWER_BOUND); 3788c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* Load bootstrap code into instruction SRAM now, 3818c2ecf20Sopenharmony_ci * to prepare to load "initialize" uCode */ 3828c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* Wait for load of bootstrap uCode to finish */ 3858c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 3868c2ecf20Sopenharmony_ci done = il_rd_prph(il, BSM_WR_CTRL_REG); 3878c2ecf20Sopenharmony_ci if (!(done & BSM_WR_CTRL_REG_BIT_START)) 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci udelay(10); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci if (i < 100) 3928c2ecf20Sopenharmony_ci D_INFO("BSM write complete, poll %d iterations\n", i); 3938c2ecf20Sopenharmony_ci else { 3948c2ecf20Sopenharmony_ci IL_ERR("BSM write did not complete!\n"); 3958c2ecf20Sopenharmony_ci return -EIO; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Enable future boot loads whenever power management unit triggers it 3998c2ecf20Sopenharmony_ci * (e.g. when powering back up after power-save shutdown) */ 4008c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* 4068c2ecf20Sopenharmony_ci * il4965_set_ucode_ptrs - Set uCode address location 4078c2ecf20Sopenharmony_ci * 4088c2ecf20Sopenharmony_ci * Tell initialization uCode where to find runtime uCode. 4098c2ecf20Sopenharmony_ci * 4108c2ecf20Sopenharmony_ci * BSM registers initially contain pointers to initialization uCode. 4118c2ecf20Sopenharmony_ci * We need to replace them to load runtime uCode inst and data, 4128c2ecf20Sopenharmony_ci * and to save runtime data when powering down. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_cistatic int 4158c2ecf20Sopenharmony_ciil4965_set_ucode_ptrs(struct il_priv *il) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci dma_addr_t pinst; 4188c2ecf20Sopenharmony_ci dma_addr_t pdata; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* bits 35:4 for 4965 */ 4218c2ecf20Sopenharmony_ci pinst = il->ucode_code.p_addr >> 4; 4228c2ecf20Sopenharmony_ci pdata = il->ucode_data_backup.p_addr >> 4; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* Tell bootstrap uCode where to find image to load */ 4258c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); 4268c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); 4278c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, il->ucode_data.len); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Inst byte count must be last to set up, bit 31 signals uCode 4308c2ecf20Sopenharmony_ci * that all new ptr/size info is in place */ 4318c2ecf20Sopenharmony_ci il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, 4328c2ecf20Sopenharmony_ci il->ucode_code.len | BSM_DRAM_INST_LOAD); 4338c2ecf20Sopenharmony_ci D_INFO("Runtime uCode pointers are set.\n"); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/* 4398c2ecf20Sopenharmony_ci * il4965_init_alive_start - Called after N_ALIVE notification received 4408c2ecf20Sopenharmony_ci * 4418c2ecf20Sopenharmony_ci * Called after N_ALIVE notification received from "initialize" uCode. 4428c2ecf20Sopenharmony_ci * 4438c2ecf20Sopenharmony_ci * The 4965 "initialize" ALIVE reply contains calibration data for: 4448c2ecf20Sopenharmony_ci * Voltage, temperature, and MIMO tx gain correction, now stored in il 4458c2ecf20Sopenharmony_ci * (3945 does not contain this data). 4468c2ecf20Sopenharmony_ci * 4478c2ecf20Sopenharmony_ci * Tell "initialize" uCode to go ahead and load the runtime uCode. 4488c2ecf20Sopenharmony_ci*/ 4498c2ecf20Sopenharmony_cistatic void 4508c2ecf20Sopenharmony_ciil4965_init_alive_start(struct il_priv *il) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci /* Bootstrap uCode has loaded initialize uCode ... verify inst image. 4538c2ecf20Sopenharmony_ci * This is a paranoid check, because we would not have gotten the 4548c2ecf20Sopenharmony_ci * "initialize" alive if code weren't properly loaded. */ 4558c2ecf20Sopenharmony_ci if (il4965_verify_ucode(il)) { 4568c2ecf20Sopenharmony_ci /* Runtime instruction load was bad; 4578c2ecf20Sopenharmony_ci * take it all the way back down so we can try again */ 4588c2ecf20Sopenharmony_ci D_INFO("Bad \"initialize\" uCode load.\n"); 4598c2ecf20Sopenharmony_ci goto restart; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* Calculate temperature */ 4638c2ecf20Sopenharmony_ci il->temperature = il4965_hw_get_temperature(il); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* Send pointers to protocol/runtime uCode image ... init code will 4668c2ecf20Sopenharmony_ci * load and launch runtime uCode, which will send us another "Alive" 4678c2ecf20Sopenharmony_ci * notification. */ 4688c2ecf20Sopenharmony_ci D_INFO("Initialization Alive received.\n"); 4698c2ecf20Sopenharmony_ci if (il4965_set_ucode_ptrs(il)) { 4708c2ecf20Sopenharmony_ci /* Runtime instruction load won't happen; 4718c2ecf20Sopenharmony_ci * take it all the way back down so we can try again */ 4728c2ecf20Sopenharmony_ci D_INFO("Couldn't set up uCode pointers.\n"); 4738c2ecf20Sopenharmony_ci goto restart; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci return; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cirestart: 4788c2ecf20Sopenharmony_ci queue_work(il->workqueue, &il->restart); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic bool 4828c2ecf20Sopenharmony_ciiw4965_is_ht40_channel(__le32 rxon_flags) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci int chan_mod = 4858c2ecf20Sopenharmony_ci le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK) >> 4868c2ecf20Sopenharmony_ci RXON_FLG_CHANNEL_MODE_POS; 4878c2ecf20Sopenharmony_ci return (chan_mod == CHANNEL_MODE_PURE_40 || 4888c2ecf20Sopenharmony_ci chan_mod == CHANNEL_MODE_MIXED); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_civoid 4928c2ecf20Sopenharmony_ciil4965_nic_config(struct il_priv *il) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci unsigned long flags; 4958c2ecf20Sopenharmony_ci u16 radio_cfg; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci radio_cfg = il_eeprom_query16(il, EEPROM_RADIO_CONFIG); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* write radio config values to register */ 5028c2ecf20Sopenharmony_ci if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) == EEPROM_4965_RF_CFG_TYPE_MAX) 5038c2ecf20Sopenharmony_ci il_set_bit(il, CSR_HW_IF_CONFIG_REG, 5048c2ecf20Sopenharmony_ci EEPROM_RF_CFG_TYPE_MSK(radio_cfg) | 5058c2ecf20Sopenharmony_ci EEPROM_RF_CFG_STEP_MSK(radio_cfg) | 5068c2ecf20Sopenharmony_ci EEPROM_RF_CFG_DASH_MSK(radio_cfg)); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* set CSR_HW_CONFIG_REG for uCode use */ 5098c2ecf20Sopenharmony_ci il_set_bit(il, CSR_HW_IF_CONFIG_REG, 5108c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | 5118c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci il->calib_info = 5148c2ecf20Sopenharmony_ci (struct il_eeprom_calib_info *) 5158c2ecf20Sopenharmony_ci il_eeprom_query_addr(il, EEPROM_4965_CALIB_TXPOWER_OFFSET); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci/* Reset differential Rx gains in NIC to prepare for chain noise calibration. 5218c2ecf20Sopenharmony_ci * Called after every association, but this runs only once! 5228c2ecf20Sopenharmony_ci * ... once chain noise is calibrated the first time, it's good forever. */ 5238c2ecf20Sopenharmony_cistatic void 5248c2ecf20Sopenharmony_ciil4965_chain_noise_reset(struct il_priv *il) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct il_chain_noise_data *data = &(il->chain_noise_data); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (data->state == IL_CHAIN_NOISE_ALIVE && il_is_any_associated(il)) { 5298c2ecf20Sopenharmony_ci struct il_calib_diff_gain_cmd cmd; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* clear data for chain noise calibration algorithm */ 5328c2ecf20Sopenharmony_ci data->chain_noise_a = 0; 5338c2ecf20Sopenharmony_ci data->chain_noise_b = 0; 5348c2ecf20Sopenharmony_ci data->chain_noise_c = 0; 5358c2ecf20Sopenharmony_ci data->chain_signal_a = 0; 5368c2ecf20Sopenharmony_ci data->chain_signal_b = 0; 5378c2ecf20Sopenharmony_ci data->chain_signal_c = 0; 5388c2ecf20Sopenharmony_ci data->beacon_count = 0; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 5418c2ecf20Sopenharmony_ci cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD; 5428c2ecf20Sopenharmony_ci cmd.diff_gain_a = 0; 5438c2ecf20Sopenharmony_ci cmd.diff_gain_b = 0; 5448c2ecf20Sopenharmony_ci cmd.diff_gain_c = 0; 5458c2ecf20Sopenharmony_ci if (il_send_cmd_pdu(il, C_PHY_CALIBRATION, sizeof(cmd), &cmd)) 5468c2ecf20Sopenharmony_ci IL_ERR("Could not send C_PHY_CALIBRATION\n"); 5478c2ecf20Sopenharmony_ci data->state = IL_CHAIN_NOISE_ACCUMULATE; 5488c2ecf20Sopenharmony_ci D_CALIB("Run chain_noise_calibrate\n"); 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic s32 5538c2ecf20Sopenharmony_ciil4965_math_div_round(s32 num, s32 denom, s32 * res) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci s32 sign = 1; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (num < 0) { 5588c2ecf20Sopenharmony_ci sign = -sign; 5598c2ecf20Sopenharmony_ci num = -num; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci if (denom < 0) { 5628c2ecf20Sopenharmony_ci sign = -sign; 5638c2ecf20Sopenharmony_ci denom = -denom; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci *res = ((num * 2 + denom) / (denom * 2)) * sign; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci return 1; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci/* 5718c2ecf20Sopenharmony_ci * il4965_get_voltage_compensation - Power supply voltage comp for txpower 5728c2ecf20Sopenharmony_ci * 5738c2ecf20Sopenharmony_ci * Determines power supply voltage compensation for txpower calculations. 5748c2ecf20Sopenharmony_ci * Returns number of 1/2-dB steps to subtract from gain table idx, 5758c2ecf20Sopenharmony_ci * to compensate for difference between power supply voltage during 5768c2ecf20Sopenharmony_ci * factory measurements, vs. current power supply voltage. 5778c2ecf20Sopenharmony_ci * 5788c2ecf20Sopenharmony_ci * Voltage indication is higher for lower voltage. 5798c2ecf20Sopenharmony_ci * Lower voltage requires more gain (lower gain table idx). 5808c2ecf20Sopenharmony_ci */ 5818c2ecf20Sopenharmony_cistatic s32 5828c2ecf20Sopenharmony_ciil4965_get_voltage_compensation(s32 eeprom_voltage, s32 current_voltage) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci s32 comp = 0; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (TX_POWER_IL_ILLEGAL_VOLTAGE == eeprom_voltage || 5878c2ecf20Sopenharmony_ci TX_POWER_IL_ILLEGAL_VOLTAGE == current_voltage) 5888c2ecf20Sopenharmony_ci return 0; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci il4965_math_div_round(current_voltage - eeprom_voltage, 5918c2ecf20Sopenharmony_ci TX_POWER_IL_VOLTAGE_CODES_PER_03V, &comp); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (current_voltage > eeprom_voltage) 5948c2ecf20Sopenharmony_ci comp *= 2; 5958c2ecf20Sopenharmony_ci if ((comp < -2) || (comp > 2)) 5968c2ecf20Sopenharmony_ci comp = 0; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return comp; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic s32 6028c2ecf20Sopenharmony_ciil4965_get_tx_atten_grp(u16 channel) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci if (channel >= CALIB_IL_TX_ATTEN_GR5_FCH && 6058c2ecf20Sopenharmony_ci channel <= CALIB_IL_TX_ATTEN_GR5_LCH) 6068c2ecf20Sopenharmony_ci return CALIB_CH_GROUP_5; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (channel >= CALIB_IL_TX_ATTEN_GR1_FCH && 6098c2ecf20Sopenharmony_ci channel <= CALIB_IL_TX_ATTEN_GR1_LCH) 6108c2ecf20Sopenharmony_ci return CALIB_CH_GROUP_1; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (channel >= CALIB_IL_TX_ATTEN_GR2_FCH && 6138c2ecf20Sopenharmony_ci channel <= CALIB_IL_TX_ATTEN_GR2_LCH) 6148c2ecf20Sopenharmony_ci return CALIB_CH_GROUP_2; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (channel >= CALIB_IL_TX_ATTEN_GR3_FCH && 6178c2ecf20Sopenharmony_ci channel <= CALIB_IL_TX_ATTEN_GR3_LCH) 6188c2ecf20Sopenharmony_ci return CALIB_CH_GROUP_3; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (channel >= CALIB_IL_TX_ATTEN_GR4_FCH && 6218c2ecf20Sopenharmony_ci channel <= CALIB_IL_TX_ATTEN_GR4_LCH) 6228c2ecf20Sopenharmony_ci return CALIB_CH_GROUP_4; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return -EINVAL; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic u32 6288c2ecf20Sopenharmony_ciil4965_get_sub_band(const struct il_priv *il, u32 channel) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci s32 b = -1; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci for (b = 0; b < EEPROM_TX_POWER_BANDS; b++) { 6338c2ecf20Sopenharmony_ci if (il->calib_info->band_info[b].ch_from == 0) 6348c2ecf20Sopenharmony_ci continue; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (channel >= il->calib_info->band_info[b].ch_from && 6378c2ecf20Sopenharmony_ci channel <= il->calib_info->band_info[b].ch_to) 6388c2ecf20Sopenharmony_ci break; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return b; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic s32 6458c2ecf20Sopenharmony_ciil4965_interpolate_value(s32 x, s32 x1, s32 y1, s32 x2, s32 y2) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci s32 val; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (x2 == x1) 6508c2ecf20Sopenharmony_ci return y1; 6518c2ecf20Sopenharmony_ci else { 6528c2ecf20Sopenharmony_ci il4965_math_div_round((x2 - x) * (y1 - y2), (x2 - x1), &val); 6538c2ecf20Sopenharmony_ci return val + y2; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci/* 6588c2ecf20Sopenharmony_ci * il4965_interpolate_chan - Interpolate factory measurements for one channel 6598c2ecf20Sopenharmony_ci * 6608c2ecf20Sopenharmony_ci * Interpolates factory measurements from the two sample channels within a 6618c2ecf20Sopenharmony_ci * sub-band, to apply to channel of interest. Interpolation is proportional to 6628c2ecf20Sopenharmony_ci * differences in channel frequencies, which is proportional to differences 6638c2ecf20Sopenharmony_ci * in channel number. 6648c2ecf20Sopenharmony_ci */ 6658c2ecf20Sopenharmony_cistatic int 6668c2ecf20Sopenharmony_ciil4965_interpolate_chan(struct il_priv *il, u32 channel, 6678c2ecf20Sopenharmony_ci struct il_eeprom_calib_ch_info *chan_info) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci s32 s = -1; 6708c2ecf20Sopenharmony_ci u32 c; 6718c2ecf20Sopenharmony_ci u32 m; 6728c2ecf20Sopenharmony_ci const struct il_eeprom_calib_measure *m1; 6738c2ecf20Sopenharmony_ci const struct il_eeprom_calib_measure *m2; 6748c2ecf20Sopenharmony_ci struct il_eeprom_calib_measure *omeas; 6758c2ecf20Sopenharmony_ci u32 ch_i1; 6768c2ecf20Sopenharmony_ci u32 ch_i2; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci s = il4965_get_sub_band(il, channel); 6798c2ecf20Sopenharmony_ci if (s >= EEPROM_TX_POWER_BANDS) { 6808c2ecf20Sopenharmony_ci IL_ERR("Tx Power can not find channel %d\n", channel); 6818c2ecf20Sopenharmony_ci return -1; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci ch_i1 = il->calib_info->band_info[s].ch1.ch_num; 6858c2ecf20Sopenharmony_ci ch_i2 = il->calib_info->band_info[s].ch2.ch_num; 6868c2ecf20Sopenharmony_ci chan_info->ch_num = (u8) channel; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci D_TXPOWER("channel %d subband %d factory cal ch %d & %d\n", channel, s, 6898c2ecf20Sopenharmony_ci ch_i1, ch_i2); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci for (c = 0; c < EEPROM_TX_POWER_TX_CHAINS; c++) { 6928c2ecf20Sopenharmony_ci for (m = 0; m < EEPROM_TX_POWER_MEASUREMENTS; m++) { 6938c2ecf20Sopenharmony_ci m1 = &(il->calib_info->band_info[s].ch1. 6948c2ecf20Sopenharmony_ci measurements[c][m]); 6958c2ecf20Sopenharmony_ci m2 = &(il->calib_info->band_info[s].ch2. 6968c2ecf20Sopenharmony_ci measurements[c][m]); 6978c2ecf20Sopenharmony_ci omeas = &(chan_info->measurements[c][m]); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci omeas->actual_pow = 7008c2ecf20Sopenharmony_ci (u8) il4965_interpolate_value(channel, ch_i1, 7018c2ecf20Sopenharmony_ci m1->actual_pow, ch_i2, 7028c2ecf20Sopenharmony_ci m2->actual_pow); 7038c2ecf20Sopenharmony_ci omeas->gain_idx = 7048c2ecf20Sopenharmony_ci (u8) il4965_interpolate_value(channel, ch_i1, 7058c2ecf20Sopenharmony_ci m1->gain_idx, ch_i2, 7068c2ecf20Sopenharmony_ci m2->gain_idx); 7078c2ecf20Sopenharmony_ci omeas->temperature = 7088c2ecf20Sopenharmony_ci (u8) il4965_interpolate_value(channel, ch_i1, 7098c2ecf20Sopenharmony_ci m1->temperature, 7108c2ecf20Sopenharmony_ci ch_i2, 7118c2ecf20Sopenharmony_ci m2->temperature); 7128c2ecf20Sopenharmony_ci omeas->pa_det = 7138c2ecf20Sopenharmony_ci (s8) il4965_interpolate_value(channel, ch_i1, 7148c2ecf20Sopenharmony_ci m1->pa_det, ch_i2, 7158c2ecf20Sopenharmony_ci m2->pa_det); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci D_TXPOWER("chain %d meas %d AP1=%d AP2=%d AP=%d\n", c, 7188c2ecf20Sopenharmony_ci m, m1->actual_pow, m2->actual_pow, 7198c2ecf20Sopenharmony_ci omeas->actual_pow); 7208c2ecf20Sopenharmony_ci D_TXPOWER("chain %d meas %d NI1=%d NI2=%d NI=%d\n", c, 7218c2ecf20Sopenharmony_ci m, m1->gain_idx, m2->gain_idx, 7228c2ecf20Sopenharmony_ci omeas->gain_idx); 7238c2ecf20Sopenharmony_ci D_TXPOWER("chain %d meas %d PA1=%d PA2=%d PA=%d\n", c, 7248c2ecf20Sopenharmony_ci m, m1->pa_det, m2->pa_det, omeas->pa_det); 7258c2ecf20Sopenharmony_ci D_TXPOWER("chain %d meas %d T1=%d T2=%d T=%d\n", c, 7268c2ecf20Sopenharmony_ci m, m1->temperature, m2->temperature, 7278c2ecf20Sopenharmony_ci omeas->temperature); 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci return 0; 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci/* bit-rate-dependent table to prevent Tx distortion, in half-dB units, 7358c2ecf20Sopenharmony_ci * for OFDM 6, 12, 18, 24, 36, 48, 54, 60 MBit, and CCK all rates. */ 7368c2ecf20Sopenharmony_cistatic s32 back_off_table[] = { 7378c2ecf20Sopenharmony_ci 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 20 MHz */ 7388c2ecf20Sopenharmony_ci 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 20 MHz */ 7398c2ecf20Sopenharmony_ci 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 40 MHz */ 7408c2ecf20Sopenharmony_ci 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 40 MHz */ 7418c2ecf20Sopenharmony_ci 10 /* CCK */ 7428c2ecf20Sopenharmony_ci}; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci/* Thermal compensation values for txpower for various frequency ranges ... 7458c2ecf20Sopenharmony_ci * ratios from 3:1 to 4.5:1 of degrees (Celsius) per half-dB gain adjust */ 7468c2ecf20Sopenharmony_cistatic struct il4965_txpower_comp_entry { 7478c2ecf20Sopenharmony_ci s32 degrees_per_05db_a; 7488c2ecf20Sopenharmony_ci s32 degrees_per_05db_a_denom; 7498c2ecf20Sopenharmony_ci} tx_power_cmp_tble[CALIB_CH_GROUP_MAX] = { 7508c2ecf20Sopenharmony_ci { 7518c2ecf20Sopenharmony_ci 9, 2}, /* group 0 5.2, ch 34-43 */ 7528c2ecf20Sopenharmony_ci { 7538c2ecf20Sopenharmony_ci 4, 1}, /* group 1 5.2, ch 44-70 */ 7548c2ecf20Sopenharmony_ci { 7558c2ecf20Sopenharmony_ci 4, 1}, /* group 2 5.2, ch 71-124 */ 7568c2ecf20Sopenharmony_ci { 7578c2ecf20Sopenharmony_ci 4, 1}, /* group 3 5.2, ch 125-200 */ 7588c2ecf20Sopenharmony_ci { 7598c2ecf20Sopenharmony_ci 3, 1} /* group 4 2.4, ch all */ 7608c2ecf20Sopenharmony_ci}; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic s32 7638c2ecf20Sopenharmony_ciget_min_power_idx(s32 rate_power_idx, u32 band) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci if (!band) { 7668c2ecf20Sopenharmony_ci if ((rate_power_idx & 7) <= 4) 7678c2ecf20Sopenharmony_ci return MIN_TX_GAIN_IDX_52GHZ_EXT; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci return MIN_TX_GAIN_IDX; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistruct gain_entry { 7738c2ecf20Sopenharmony_ci u8 dsp; 7748c2ecf20Sopenharmony_ci u8 radio; 7758c2ecf20Sopenharmony_ci}; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic const struct gain_entry gain_table[2][108] = { 7788c2ecf20Sopenharmony_ci /* 5.2GHz power gain idx table */ 7798c2ecf20Sopenharmony_ci { 7808c2ecf20Sopenharmony_ci {123, 0x3F}, /* highest txpower */ 7818c2ecf20Sopenharmony_ci {117, 0x3F}, 7828c2ecf20Sopenharmony_ci {110, 0x3F}, 7838c2ecf20Sopenharmony_ci {104, 0x3F}, 7848c2ecf20Sopenharmony_ci {98, 0x3F}, 7858c2ecf20Sopenharmony_ci {110, 0x3E}, 7868c2ecf20Sopenharmony_ci {104, 0x3E}, 7878c2ecf20Sopenharmony_ci {98, 0x3E}, 7888c2ecf20Sopenharmony_ci {110, 0x3D}, 7898c2ecf20Sopenharmony_ci {104, 0x3D}, 7908c2ecf20Sopenharmony_ci {98, 0x3D}, 7918c2ecf20Sopenharmony_ci {110, 0x3C}, 7928c2ecf20Sopenharmony_ci {104, 0x3C}, 7938c2ecf20Sopenharmony_ci {98, 0x3C}, 7948c2ecf20Sopenharmony_ci {110, 0x3B}, 7958c2ecf20Sopenharmony_ci {104, 0x3B}, 7968c2ecf20Sopenharmony_ci {98, 0x3B}, 7978c2ecf20Sopenharmony_ci {110, 0x3A}, 7988c2ecf20Sopenharmony_ci {104, 0x3A}, 7998c2ecf20Sopenharmony_ci {98, 0x3A}, 8008c2ecf20Sopenharmony_ci {110, 0x39}, 8018c2ecf20Sopenharmony_ci {104, 0x39}, 8028c2ecf20Sopenharmony_ci {98, 0x39}, 8038c2ecf20Sopenharmony_ci {110, 0x38}, 8048c2ecf20Sopenharmony_ci {104, 0x38}, 8058c2ecf20Sopenharmony_ci {98, 0x38}, 8068c2ecf20Sopenharmony_ci {110, 0x37}, 8078c2ecf20Sopenharmony_ci {104, 0x37}, 8088c2ecf20Sopenharmony_ci {98, 0x37}, 8098c2ecf20Sopenharmony_ci {110, 0x36}, 8108c2ecf20Sopenharmony_ci {104, 0x36}, 8118c2ecf20Sopenharmony_ci {98, 0x36}, 8128c2ecf20Sopenharmony_ci {110, 0x35}, 8138c2ecf20Sopenharmony_ci {104, 0x35}, 8148c2ecf20Sopenharmony_ci {98, 0x35}, 8158c2ecf20Sopenharmony_ci {110, 0x34}, 8168c2ecf20Sopenharmony_ci {104, 0x34}, 8178c2ecf20Sopenharmony_ci {98, 0x34}, 8188c2ecf20Sopenharmony_ci {110, 0x33}, 8198c2ecf20Sopenharmony_ci {104, 0x33}, 8208c2ecf20Sopenharmony_ci {98, 0x33}, 8218c2ecf20Sopenharmony_ci {110, 0x32}, 8228c2ecf20Sopenharmony_ci {104, 0x32}, 8238c2ecf20Sopenharmony_ci {98, 0x32}, 8248c2ecf20Sopenharmony_ci {110, 0x31}, 8258c2ecf20Sopenharmony_ci {104, 0x31}, 8268c2ecf20Sopenharmony_ci {98, 0x31}, 8278c2ecf20Sopenharmony_ci {110, 0x30}, 8288c2ecf20Sopenharmony_ci {104, 0x30}, 8298c2ecf20Sopenharmony_ci {98, 0x30}, 8308c2ecf20Sopenharmony_ci {110, 0x25}, 8318c2ecf20Sopenharmony_ci {104, 0x25}, 8328c2ecf20Sopenharmony_ci {98, 0x25}, 8338c2ecf20Sopenharmony_ci {110, 0x24}, 8348c2ecf20Sopenharmony_ci {104, 0x24}, 8358c2ecf20Sopenharmony_ci {98, 0x24}, 8368c2ecf20Sopenharmony_ci {110, 0x23}, 8378c2ecf20Sopenharmony_ci {104, 0x23}, 8388c2ecf20Sopenharmony_ci {98, 0x23}, 8398c2ecf20Sopenharmony_ci {110, 0x22}, 8408c2ecf20Sopenharmony_ci {104, 0x18}, 8418c2ecf20Sopenharmony_ci {98, 0x18}, 8428c2ecf20Sopenharmony_ci {110, 0x17}, 8438c2ecf20Sopenharmony_ci {104, 0x17}, 8448c2ecf20Sopenharmony_ci {98, 0x17}, 8458c2ecf20Sopenharmony_ci {110, 0x16}, 8468c2ecf20Sopenharmony_ci {104, 0x16}, 8478c2ecf20Sopenharmony_ci {98, 0x16}, 8488c2ecf20Sopenharmony_ci {110, 0x15}, 8498c2ecf20Sopenharmony_ci {104, 0x15}, 8508c2ecf20Sopenharmony_ci {98, 0x15}, 8518c2ecf20Sopenharmony_ci {110, 0x14}, 8528c2ecf20Sopenharmony_ci {104, 0x14}, 8538c2ecf20Sopenharmony_ci {98, 0x14}, 8548c2ecf20Sopenharmony_ci {110, 0x13}, 8558c2ecf20Sopenharmony_ci {104, 0x13}, 8568c2ecf20Sopenharmony_ci {98, 0x13}, 8578c2ecf20Sopenharmony_ci {110, 0x12}, 8588c2ecf20Sopenharmony_ci {104, 0x08}, 8598c2ecf20Sopenharmony_ci {98, 0x08}, 8608c2ecf20Sopenharmony_ci {110, 0x07}, 8618c2ecf20Sopenharmony_ci {104, 0x07}, 8628c2ecf20Sopenharmony_ci {98, 0x07}, 8638c2ecf20Sopenharmony_ci {110, 0x06}, 8648c2ecf20Sopenharmony_ci {104, 0x06}, 8658c2ecf20Sopenharmony_ci {98, 0x06}, 8668c2ecf20Sopenharmony_ci {110, 0x05}, 8678c2ecf20Sopenharmony_ci {104, 0x05}, 8688c2ecf20Sopenharmony_ci {98, 0x05}, 8698c2ecf20Sopenharmony_ci {110, 0x04}, 8708c2ecf20Sopenharmony_ci {104, 0x04}, 8718c2ecf20Sopenharmony_ci {98, 0x04}, 8728c2ecf20Sopenharmony_ci {110, 0x03}, 8738c2ecf20Sopenharmony_ci {104, 0x03}, 8748c2ecf20Sopenharmony_ci {98, 0x03}, 8758c2ecf20Sopenharmony_ci {110, 0x02}, 8768c2ecf20Sopenharmony_ci {104, 0x02}, 8778c2ecf20Sopenharmony_ci {98, 0x02}, 8788c2ecf20Sopenharmony_ci {110, 0x01}, 8798c2ecf20Sopenharmony_ci {104, 0x01}, 8808c2ecf20Sopenharmony_ci {98, 0x01}, 8818c2ecf20Sopenharmony_ci {110, 0x00}, 8828c2ecf20Sopenharmony_ci {104, 0x00}, 8838c2ecf20Sopenharmony_ci {98, 0x00}, 8848c2ecf20Sopenharmony_ci {93, 0x00}, 8858c2ecf20Sopenharmony_ci {88, 0x00}, 8868c2ecf20Sopenharmony_ci {83, 0x00}, 8878c2ecf20Sopenharmony_ci {78, 0x00}, 8888c2ecf20Sopenharmony_ci }, 8898c2ecf20Sopenharmony_ci /* 2.4GHz power gain idx table */ 8908c2ecf20Sopenharmony_ci { 8918c2ecf20Sopenharmony_ci {110, 0x3f}, /* highest txpower */ 8928c2ecf20Sopenharmony_ci {104, 0x3f}, 8938c2ecf20Sopenharmony_ci {98, 0x3f}, 8948c2ecf20Sopenharmony_ci {110, 0x3e}, 8958c2ecf20Sopenharmony_ci {104, 0x3e}, 8968c2ecf20Sopenharmony_ci {98, 0x3e}, 8978c2ecf20Sopenharmony_ci {110, 0x3d}, 8988c2ecf20Sopenharmony_ci {104, 0x3d}, 8998c2ecf20Sopenharmony_ci {98, 0x3d}, 9008c2ecf20Sopenharmony_ci {110, 0x3c}, 9018c2ecf20Sopenharmony_ci {104, 0x3c}, 9028c2ecf20Sopenharmony_ci {98, 0x3c}, 9038c2ecf20Sopenharmony_ci {110, 0x3b}, 9048c2ecf20Sopenharmony_ci {104, 0x3b}, 9058c2ecf20Sopenharmony_ci {98, 0x3b}, 9068c2ecf20Sopenharmony_ci {110, 0x3a}, 9078c2ecf20Sopenharmony_ci {104, 0x3a}, 9088c2ecf20Sopenharmony_ci {98, 0x3a}, 9098c2ecf20Sopenharmony_ci {110, 0x39}, 9108c2ecf20Sopenharmony_ci {104, 0x39}, 9118c2ecf20Sopenharmony_ci {98, 0x39}, 9128c2ecf20Sopenharmony_ci {110, 0x38}, 9138c2ecf20Sopenharmony_ci {104, 0x38}, 9148c2ecf20Sopenharmony_ci {98, 0x38}, 9158c2ecf20Sopenharmony_ci {110, 0x37}, 9168c2ecf20Sopenharmony_ci {104, 0x37}, 9178c2ecf20Sopenharmony_ci {98, 0x37}, 9188c2ecf20Sopenharmony_ci {110, 0x36}, 9198c2ecf20Sopenharmony_ci {104, 0x36}, 9208c2ecf20Sopenharmony_ci {98, 0x36}, 9218c2ecf20Sopenharmony_ci {110, 0x35}, 9228c2ecf20Sopenharmony_ci {104, 0x35}, 9238c2ecf20Sopenharmony_ci {98, 0x35}, 9248c2ecf20Sopenharmony_ci {110, 0x34}, 9258c2ecf20Sopenharmony_ci {104, 0x34}, 9268c2ecf20Sopenharmony_ci {98, 0x34}, 9278c2ecf20Sopenharmony_ci {110, 0x33}, 9288c2ecf20Sopenharmony_ci {104, 0x33}, 9298c2ecf20Sopenharmony_ci {98, 0x33}, 9308c2ecf20Sopenharmony_ci {110, 0x32}, 9318c2ecf20Sopenharmony_ci {104, 0x32}, 9328c2ecf20Sopenharmony_ci {98, 0x32}, 9338c2ecf20Sopenharmony_ci {110, 0x31}, 9348c2ecf20Sopenharmony_ci {104, 0x31}, 9358c2ecf20Sopenharmony_ci {98, 0x31}, 9368c2ecf20Sopenharmony_ci {110, 0x30}, 9378c2ecf20Sopenharmony_ci {104, 0x30}, 9388c2ecf20Sopenharmony_ci {98, 0x30}, 9398c2ecf20Sopenharmony_ci {110, 0x6}, 9408c2ecf20Sopenharmony_ci {104, 0x6}, 9418c2ecf20Sopenharmony_ci {98, 0x6}, 9428c2ecf20Sopenharmony_ci {110, 0x5}, 9438c2ecf20Sopenharmony_ci {104, 0x5}, 9448c2ecf20Sopenharmony_ci {98, 0x5}, 9458c2ecf20Sopenharmony_ci {110, 0x4}, 9468c2ecf20Sopenharmony_ci {104, 0x4}, 9478c2ecf20Sopenharmony_ci {98, 0x4}, 9488c2ecf20Sopenharmony_ci {110, 0x3}, 9498c2ecf20Sopenharmony_ci {104, 0x3}, 9508c2ecf20Sopenharmony_ci {98, 0x3}, 9518c2ecf20Sopenharmony_ci {110, 0x2}, 9528c2ecf20Sopenharmony_ci {104, 0x2}, 9538c2ecf20Sopenharmony_ci {98, 0x2}, 9548c2ecf20Sopenharmony_ci {110, 0x1}, 9558c2ecf20Sopenharmony_ci {104, 0x1}, 9568c2ecf20Sopenharmony_ci {98, 0x1}, 9578c2ecf20Sopenharmony_ci {110, 0x0}, 9588c2ecf20Sopenharmony_ci {104, 0x0}, 9598c2ecf20Sopenharmony_ci {98, 0x0}, 9608c2ecf20Sopenharmony_ci {97, 0}, 9618c2ecf20Sopenharmony_ci {96, 0}, 9628c2ecf20Sopenharmony_ci {95, 0}, 9638c2ecf20Sopenharmony_ci {94, 0}, 9648c2ecf20Sopenharmony_ci {93, 0}, 9658c2ecf20Sopenharmony_ci {92, 0}, 9668c2ecf20Sopenharmony_ci {91, 0}, 9678c2ecf20Sopenharmony_ci {90, 0}, 9688c2ecf20Sopenharmony_ci {89, 0}, 9698c2ecf20Sopenharmony_ci {88, 0}, 9708c2ecf20Sopenharmony_ci {87, 0}, 9718c2ecf20Sopenharmony_ci {86, 0}, 9728c2ecf20Sopenharmony_ci {85, 0}, 9738c2ecf20Sopenharmony_ci {84, 0}, 9748c2ecf20Sopenharmony_ci {83, 0}, 9758c2ecf20Sopenharmony_ci {82, 0}, 9768c2ecf20Sopenharmony_ci {81, 0}, 9778c2ecf20Sopenharmony_ci {80, 0}, 9788c2ecf20Sopenharmony_ci {79, 0}, 9798c2ecf20Sopenharmony_ci {78, 0}, 9808c2ecf20Sopenharmony_ci {77, 0}, 9818c2ecf20Sopenharmony_ci {76, 0}, 9828c2ecf20Sopenharmony_ci {75, 0}, 9838c2ecf20Sopenharmony_ci {74, 0}, 9848c2ecf20Sopenharmony_ci {73, 0}, 9858c2ecf20Sopenharmony_ci {72, 0}, 9868c2ecf20Sopenharmony_ci {71, 0}, 9878c2ecf20Sopenharmony_ci {70, 0}, 9888c2ecf20Sopenharmony_ci {69, 0}, 9898c2ecf20Sopenharmony_ci {68, 0}, 9908c2ecf20Sopenharmony_ci {67, 0}, 9918c2ecf20Sopenharmony_ci {66, 0}, 9928c2ecf20Sopenharmony_ci {65, 0}, 9938c2ecf20Sopenharmony_ci {64, 0}, 9948c2ecf20Sopenharmony_ci {63, 0}, 9958c2ecf20Sopenharmony_ci {62, 0}, 9968c2ecf20Sopenharmony_ci {61, 0}, 9978c2ecf20Sopenharmony_ci {60, 0}, 9988c2ecf20Sopenharmony_ci {59, 0}, 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci}; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic int 10038c2ecf20Sopenharmony_ciil4965_fill_txpower_tbl(struct il_priv *il, u8 band, u16 channel, u8 is_ht40, 10048c2ecf20Sopenharmony_ci u8 ctrl_chan_high, 10058c2ecf20Sopenharmony_ci struct il4965_tx_power_db *tx_power_tbl) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci u8 saturation_power; 10088c2ecf20Sopenharmony_ci s32 target_power; 10098c2ecf20Sopenharmony_ci s32 user_target_power; 10108c2ecf20Sopenharmony_ci s32 power_limit; 10118c2ecf20Sopenharmony_ci s32 current_temp; 10128c2ecf20Sopenharmony_ci s32 reg_limit; 10138c2ecf20Sopenharmony_ci s32 current_regulatory; 10148c2ecf20Sopenharmony_ci s32 txatten_grp = CALIB_CH_GROUP_MAX; 10158c2ecf20Sopenharmony_ci int i; 10168c2ecf20Sopenharmony_ci int c; 10178c2ecf20Sopenharmony_ci const struct il_channel_info *ch_info = NULL; 10188c2ecf20Sopenharmony_ci struct il_eeprom_calib_ch_info ch_eeprom_info; 10198c2ecf20Sopenharmony_ci const struct il_eeprom_calib_measure *measurement; 10208c2ecf20Sopenharmony_ci s16 voltage; 10218c2ecf20Sopenharmony_ci s32 init_voltage; 10228c2ecf20Sopenharmony_ci s32 voltage_compensation; 10238c2ecf20Sopenharmony_ci s32 degrees_per_05db_num; 10248c2ecf20Sopenharmony_ci s32 degrees_per_05db_denom; 10258c2ecf20Sopenharmony_ci s32 factory_temp; 10268c2ecf20Sopenharmony_ci s32 temperature_comp[2]; 10278c2ecf20Sopenharmony_ci s32 factory_gain_idx[2]; 10288c2ecf20Sopenharmony_ci s32 factory_actual_pwr[2]; 10298c2ecf20Sopenharmony_ci s32 power_idx; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* tx_power_user_lmt is in dBm, convert to half-dBm (half-dB units 10328c2ecf20Sopenharmony_ci * are used for idxing into txpower table) */ 10338c2ecf20Sopenharmony_ci user_target_power = 2 * il->tx_power_user_lmt; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci /* Get current (RXON) channel, band, width */ 10368c2ecf20Sopenharmony_ci D_TXPOWER("chan %d band %d is_ht40 %d\n", channel, band, is_ht40); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci ch_info = il_get_channel_info(il, il->band, channel); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (!il_is_channel_valid(ch_info)) 10418c2ecf20Sopenharmony_ci return -EINVAL; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* get txatten group, used to select 1) thermal txpower adjustment 10448c2ecf20Sopenharmony_ci * and 2) mimo txpower balance between Tx chains. */ 10458c2ecf20Sopenharmony_ci txatten_grp = il4965_get_tx_atten_grp(channel); 10468c2ecf20Sopenharmony_ci if (txatten_grp < 0) { 10478c2ecf20Sopenharmony_ci IL_ERR("Can't find txatten group for channel %d.\n", channel); 10488c2ecf20Sopenharmony_ci return txatten_grp; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci D_TXPOWER("channel %d belongs to txatten group %d\n", channel, 10528c2ecf20Sopenharmony_ci txatten_grp); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (is_ht40) { 10558c2ecf20Sopenharmony_ci if (ctrl_chan_high) 10568c2ecf20Sopenharmony_ci channel -= 2; 10578c2ecf20Sopenharmony_ci else 10588c2ecf20Sopenharmony_ci channel += 2; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* hardware txpower limits ... 10628c2ecf20Sopenharmony_ci * saturation (clipping distortion) txpowers are in half-dBm */ 10638c2ecf20Sopenharmony_ci if (band) 10648c2ecf20Sopenharmony_ci saturation_power = il->calib_info->saturation_power24; 10658c2ecf20Sopenharmony_ci else 10668c2ecf20Sopenharmony_ci saturation_power = il->calib_info->saturation_power52; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if (saturation_power < IL_TX_POWER_SATURATION_MIN || 10698c2ecf20Sopenharmony_ci saturation_power > IL_TX_POWER_SATURATION_MAX) { 10708c2ecf20Sopenharmony_ci if (band) 10718c2ecf20Sopenharmony_ci saturation_power = IL_TX_POWER_DEFAULT_SATURATION_24; 10728c2ecf20Sopenharmony_ci else 10738c2ecf20Sopenharmony_ci saturation_power = IL_TX_POWER_DEFAULT_SATURATION_52; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* regulatory txpower limits ... reg_limit values are in half-dBm, 10778c2ecf20Sopenharmony_ci * max_power_avg values are in dBm, convert * 2 */ 10788c2ecf20Sopenharmony_ci if (is_ht40) 10798c2ecf20Sopenharmony_ci reg_limit = ch_info->ht40_max_power_avg * 2; 10808c2ecf20Sopenharmony_ci else 10818c2ecf20Sopenharmony_ci reg_limit = ch_info->max_power_avg * 2; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci if ((reg_limit < IL_TX_POWER_REGULATORY_MIN) || 10848c2ecf20Sopenharmony_ci (reg_limit > IL_TX_POWER_REGULATORY_MAX)) { 10858c2ecf20Sopenharmony_ci if (band) 10868c2ecf20Sopenharmony_ci reg_limit = IL_TX_POWER_DEFAULT_REGULATORY_24; 10878c2ecf20Sopenharmony_ci else 10888c2ecf20Sopenharmony_ci reg_limit = IL_TX_POWER_DEFAULT_REGULATORY_52; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* Interpolate txpower calibration values for this channel, 10928c2ecf20Sopenharmony_ci * based on factory calibration tests on spaced channels. */ 10938c2ecf20Sopenharmony_ci il4965_interpolate_chan(il, channel, &ch_eeprom_info); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* calculate tx gain adjustment based on power supply voltage */ 10968c2ecf20Sopenharmony_ci voltage = le16_to_cpu(il->calib_info->voltage); 10978c2ecf20Sopenharmony_ci init_voltage = (s32) le32_to_cpu(il->card_alive_init.voltage); 10988c2ecf20Sopenharmony_ci voltage_compensation = 10998c2ecf20Sopenharmony_ci il4965_get_voltage_compensation(voltage, init_voltage); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci D_TXPOWER("curr volt %d eeprom volt %d volt comp %d\n", init_voltage, 11028c2ecf20Sopenharmony_ci voltage, voltage_compensation); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci /* get current temperature (Celsius) */ 11058c2ecf20Sopenharmony_ci current_temp = max(il->temperature, IL_TX_POWER_TEMPERATURE_MIN); 11068c2ecf20Sopenharmony_ci current_temp = min(il->temperature, IL_TX_POWER_TEMPERATURE_MAX); 11078c2ecf20Sopenharmony_ci current_temp = kelvin_to_celsius(current_temp); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci /* select thermal txpower adjustment params, based on channel group 11108c2ecf20Sopenharmony_ci * (same frequency group used for mimo txatten adjustment) */ 11118c2ecf20Sopenharmony_ci degrees_per_05db_num = 11128c2ecf20Sopenharmony_ci tx_power_cmp_tble[txatten_grp].degrees_per_05db_a; 11138c2ecf20Sopenharmony_ci degrees_per_05db_denom = 11148c2ecf20Sopenharmony_ci tx_power_cmp_tble[txatten_grp].degrees_per_05db_a_denom; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci /* get per-chain txpower values from factory measurements */ 11178c2ecf20Sopenharmony_ci for (c = 0; c < 2; c++) { 11188c2ecf20Sopenharmony_ci measurement = &ch_eeprom_info.measurements[c][1]; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci /* txgain adjustment (in half-dB steps) based on difference 11218c2ecf20Sopenharmony_ci * between factory and current temperature */ 11228c2ecf20Sopenharmony_ci factory_temp = measurement->temperature; 11238c2ecf20Sopenharmony_ci il4965_math_div_round((current_temp - 11248c2ecf20Sopenharmony_ci factory_temp) * degrees_per_05db_denom, 11258c2ecf20Sopenharmony_ci degrees_per_05db_num, 11268c2ecf20Sopenharmony_ci &temperature_comp[c]); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci factory_gain_idx[c] = measurement->gain_idx; 11298c2ecf20Sopenharmony_ci factory_actual_pwr[c] = measurement->actual_pow; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci D_TXPOWER("chain = %d\n", c); 11328c2ecf20Sopenharmony_ci D_TXPOWER("fctry tmp %d, " "curr tmp %d, comp %d steps\n", 11338c2ecf20Sopenharmony_ci factory_temp, current_temp, temperature_comp[c]); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci D_TXPOWER("fctry idx %d, fctry pwr %d\n", factory_gain_idx[c], 11368c2ecf20Sopenharmony_ci factory_actual_pwr[c]); 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci /* for each of 33 bit-rates (including 1 for CCK) */ 11408c2ecf20Sopenharmony_ci for (i = 0; i < POWER_TBL_NUM_ENTRIES; i++) { 11418c2ecf20Sopenharmony_ci u8 is_mimo_rate; 11428c2ecf20Sopenharmony_ci union il4965_tx_power_dual_stream tx_power; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* for mimo, reduce each chain's txpower by half 11458c2ecf20Sopenharmony_ci * (3dB, 6 steps), so total output power is regulatory 11468c2ecf20Sopenharmony_ci * compliant. */ 11478c2ecf20Sopenharmony_ci if (i & 0x8) { 11488c2ecf20Sopenharmony_ci current_regulatory = 11498c2ecf20Sopenharmony_ci reg_limit - 11508c2ecf20Sopenharmony_ci IL_TX_POWER_MIMO_REGULATORY_COMPENSATION; 11518c2ecf20Sopenharmony_ci is_mimo_rate = 1; 11528c2ecf20Sopenharmony_ci } else { 11538c2ecf20Sopenharmony_ci current_regulatory = reg_limit; 11548c2ecf20Sopenharmony_ci is_mimo_rate = 0; 11558c2ecf20Sopenharmony_ci } 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci /* find txpower limit, either hardware or regulatory */ 11588c2ecf20Sopenharmony_ci power_limit = saturation_power - back_off_table[i]; 11598c2ecf20Sopenharmony_ci if (power_limit > current_regulatory) 11608c2ecf20Sopenharmony_ci power_limit = current_regulatory; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* reduce user's txpower request if necessary 11638c2ecf20Sopenharmony_ci * for this rate on this channel */ 11648c2ecf20Sopenharmony_ci target_power = user_target_power; 11658c2ecf20Sopenharmony_ci if (target_power > power_limit) 11668c2ecf20Sopenharmony_ci target_power = power_limit; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci D_TXPOWER("rate %d sat %d reg %d usr %d tgt %d\n", i, 11698c2ecf20Sopenharmony_ci saturation_power - back_off_table[i], 11708c2ecf20Sopenharmony_ci current_regulatory, user_target_power, target_power); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci /* for each of 2 Tx chains (radio transmitters) */ 11738c2ecf20Sopenharmony_ci for (c = 0; c < 2; c++) { 11748c2ecf20Sopenharmony_ci s32 atten_value; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci if (is_mimo_rate) 11778c2ecf20Sopenharmony_ci atten_value = 11788c2ecf20Sopenharmony_ci (s32) le32_to_cpu(il->card_alive_init. 11798c2ecf20Sopenharmony_ci tx_atten[txatten_grp][c]); 11808c2ecf20Sopenharmony_ci else 11818c2ecf20Sopenharmony_ci atten_value = 0; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* calculate idx; higher idx means lower txpower */ 11848c2ecf20Sopenharmony_ci power_idx = 11858c2ecf20Sopenharmony_ci (u8) (factory_gain_idx[c] - 11868c2ecf20Sopenharmony_ci (target_power - factory_actual_pwr[c]) - 11878c2ecf20Sopenharmony_ci temperature_comp[c] - voltage_compensation + 11888c2ecf20Sopenharmony_ci atten_value); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci/* D_TXPOWER("calculated txpower idx %d\n", 11918c2ecf20Sopenharmony_ci power_idx); */ 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci if (power_idx < get_min_power_idx(i, band)) 11948c2ecf20Sopenharmony_ci power_idx = get_min_power_idx(i, band); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci /* adjust 5 GHz idx to support negative idxes */ 11978c2ecf20Sopenharmony_ci if (!band) 11988c2ecf20Sopenharmony_ci power_idx += 9; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* CCK, rate 32, reduce txpower for CCK */ 12018c2ecf20Sopenharmony_ci if (i == POWER_TBL_CCK_ENTRY) 12028c2ecf20Sopenharmony_ci power_idx += 12038c2ecf20Sopenharmony_ci IL_TX_POWER_CCK_COMPENSATION_C_STEP; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* stay within the table! */ 12068c2ecf20Sopenharmony_ci if (power_idx > 107) { 12078c2ecf20Sopenharmony_ci IL_WARN("txpower idx %d > 107\n", power_idx); 12088c2ecf20Sopenharmony_ci power_idx = 107; 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci if (power_idx < 0) { 12118c2ecf20Sopenharmony_ci IL_WARN("txpower idx %d < 0\n", power_idx); 12128c2ecf20Sopenharmony_ci power_idx = 0; 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci /* fill txpower command for this rate/chain */ 12168c2ecf20Sopenharmony_ci tx_power.s.radio_tx_gain[c] = 12178c2ecf20Sopenharmony_ci gain_table[band][power_idx].radio; 12188c2ecf20Sopenharmony_ci tx_power.s.dsp_predis_atten[c] = 12198c2ecf20Sopenharmony_ci gain_table[band][power_idx].dsp; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci D_TXPOWER("chain %d mimo %d idx %d " 12228c2ecf20Sopenharmony_ci "gain 0x%02x dsp %d\n", c, atten_value, 12238c2ecf20Sopenharmony_ci power_idx, tx_power.s.radio_tx_gain[c], 12248c2ecf20Sopenharmony_ci tx_power.s.dsp_predis_atten[c]); 12258c2ecf20Sopenharmony_ci } /* for each chain */ 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci tx_power_tbl->power_tbl[i].dw = cpu_to_le32(tx_power.dw); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci } /* for each rate */ 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci return 0; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci/* 12358c2ecf20Sopenharmony_ci * il4965_send_tx_power - Configure the TXPOWER level user limit 12368c2ecf20Sopenharmony_ci * 12378c2ecf20Sopenharmony_ci * Uses the active RXON for channel, band, and characteristics (ht40, high) 12388c2ecf20Sopenharmony_ci * The power limit is taken from il->tx_power_user_lmt. 12398c2ecf20Sopenharmony_ci */ 12408c2ecf20Sopenharmony_cistatic int 12418c2ecf20Sopenharmony_ciil4965_send_tx_power(struct il_priv *il) 12428c2ecf20Sopenharmony_ci{ 12438c2ecf20Sopenharmony_ci struct il4965_txpowertable_cmd cmd = { 0 }; 12448c2ecf20Sopenharmony_ci int ret; 12458c2ecf20Sopenharmony_ci u8 band = 0; 12468c2ecf20Sopenharmony_ci bool is_ht40 = false; 12478c2ecf20Sopenharmony_ci u8 ctrl_chan_high = 0; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci if (WARN_ONCE 12508c2ecf20Sopenharmony_ci (test_bit(S_SCAN_HW, &il->status), 12518c2ecf20Sopenharmony_ci "TX Power requested while scanning!\n")) 12528c2ecf20Sopenharmony_ci return -EAGAIN; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci band = il->band == NL80211_BAND_2GHZ; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci is_ht40 = iw4965_is_ht40_channel(il->active.flags); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if (is_ht40 && (il->active.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) 12598c2ecf20Sopenharmony_ci ctrl_chan_high = 1; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci cmd.band = band; 12628c2ecf20Sopenharmony_ci cmd.channel = il->active.channel; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci ret = 12658c2ecf20Sopenharmony_ci il4965_fill_txpower_tbl(il, band, le16_to_cpu(il->active.channel), 12668c2ecf20Sopenharmony_ci is_ht40, ctrl_chan_high, &cmd.tx_power); 12678c2ecf20Sopenharmony_ci if (ret) 12688c2ecf20Sopenharmony_ci goto out; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci ret = il_send_cmd_pdu(il, C_TX_PWR_TBL, sizeof(cmd), &cmd); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ciout: 12738c2ecf20Sopenharmony_ci return ret; 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_cistatic int 12778c2ecf20Sopenharmony_ciil4965_send_rxon_assoc(struct il_priv *il) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci int ret = 0; 12808c2ecf20Sopenharmony_ci struct il4965_rxon_assoc_cmd rxon_assoc; 12818c2ecf20Sopenharmony_ci const struct il_rxon_cmd *rxon1 = &il->staging; 12828c2ecf20Sopenharmony_ci const struct il_rxon_cmd *rxon2 = &il->active; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if (rxon1->flags == rxon2->flags && 12878c2ecf20Sopenharmony_ci rxon1->filter_flags == rxon2->filter_flags && 12888c2ecf20Sopenharmony_ci rxon1->cck_basic_rates == rxon2->cck_basic_rates && 12898c2ecf20Sopenharmony_ci rxon1->ofdm_ht_single_stream_basic_rates == 12908c2ecf20Sopenharmony_ci rxon2->ofdm_ht_single_stream_basic_rates && 12918c2ecf20Sopenharmony_ci rxon1->ofdm_ht_dual_stream_basic_rates == 12928c2ecf20Sopenharmony_ci rxon2->ofdm_ht_dual_stream_basic_rates && 12938c2ecf20Sopenharmony_ci rxon1->rx_chain == rxon2->rx_chain && 12948c2ecf20Sopenharmony_ci rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates) { 12958c2ecf20Sopenharmony_ci D_INFO("Using current RXON_ASSOC. Not resending.\n"); 12968c2ecf20Sopenharmony_ci return 0; 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci rxon_assoc.flags = il->staging.flags; 13008c2ecf20Sopenharmony_ci rxon_assoc.filter_flags = il->staging.filter_flags; 13018c2ecf20Sopenharmony_ci rxon_assoc.ofdm_basic_rates = il->staging.ofdm_basic_rates; 13028c2ecf20Sopenharmony_ci rxon_assoc.cck_basic_rates = il->staging.cck_basic_rates; 13038c2ecf20Sopenharmony_ci rxon_assoc.reserved = 0; 13048c2ecf20Sopenharmony_ci rxon_assoc.ofdm_ht_single_stream_basic_rates = 13058c2ecf20Sopenharmony_ci il->staging.ofdm_ht_single_stream_basic_rates; 13068c2ecf20Sopenharmony_ci rxon_assoc.ofdm_ht_dual_stream_basic_rates = 13078c2ecf20Sopenharmony_ci il->staging.ofdm_ht_dual_stream_basic_rates; 13088c2ecf20Sopenharmony_ci rxon_assoc.rx_chain_select_flags = il->staging.rx_chain; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci ret = 13118c2ecf20Sopenharmony_ci il_send_cmd_pdu_async(il, C_RXON_ASSOC, sizeof(rxon_assoc), 13128c2ecf20Sopenharmony_ci &rxon_assoc, NULL); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci return ret; 13158c2ecf20Sopenharmony_ci} 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_cistatic int 13188c2ecf20Sopenharmony_ciil4965_commit_rxon(struct il_priv *il) 13198c2ecf20Sopenharmony_ci{ 13208c2ecf20Sopenharmony_ci /* cast away the const for active_rxon in this function */ 13218c2ecf20Sopenharmony_ci struct il_rxon_cmd *active_rxon = (void *)&il->active; 13228c2ecf20Sopenharmony_ci int ret; 13238c2ecf20Sopenharmony_ci bool new_assoc = !!(il->staging.filter_flags & RXON_FILTER_ASSOC_MSK); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (!il_is_alive(il)) 13268c2ecf20Sopenharmony_ci return -EBUSY; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci /* always get timestamp with Rx frame */ 13298c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_TSF2HOST_MSK; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci ret = il_check_rxon_cmd(il); 13328c2ecf20Sopenharmony_ci if (ret) { 13338c2ecf20Sopenharmony_ci IL_ERR("Invalid RXON configuration. Not committing.\n"); 13348c2ecf20Sopenharmony_ci return -EINVAL; 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci /* 13388c2ecf20Sopenharmony_ci * receive commit_rxon request 13398c2ecf20Sopenharmony_ci * abort any previous channel switch if still in process 13408c2ecf20Sopenharmony_ci */ 13418c2ecf20Sopenharmony_ci if (test_bit(S_CHANNEL_SWITCH_PENDING, &il->status) && 13428c2ecf20Sopenharmony_ci il->switch_channel != il->staging.channel) { 13438c2ecf20Sopenharmony_ci D_11H("abort channel switch on %d\n", 13448c2ecf20Sopenharmony_ci le16_to_cpu(il->switch_channel)); 13458c2ecf20Sopenharmony_ci il_chswitch_done(il, false); 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci /* If we don't need to send a full RXON, we can use 13498c2ecf20Sopenharmony_ci * il_rxon_assoc_cmd which is used to reconfigure filter 13508c2ecf20Sopenharmony_ci * and other flags for the current radio configuration. */ 13518c2ecf20Sopenharmony_ci if (!il_full_rxon_required(il)) { 13528c2ecf20Sopenharmony_ci ret = il_send_rxon_assoc(il); 13538c2ecf20Sopenharmony_ci if (ret) { 13548c2ecf20Sopenharmony_ci IL_ERR("Error setting RXON_ASSOC (%d)\n", ret); 13558c2ecf20Sopenharmony_ci return ret; 13568c2ecf20Sopenharmony_ci } 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); 13598c2ecf20Sopenharmony_ci il_print_rx_config_cmd(il); 13608c2ecf20Sopenharmony_ci /* 13618c2ecf20Sopenharmony_ci * We do not commit tx power settings while channel changing, 13628c2ecf20Sopenharmony_ci * do it now if tx power changed. 13638c2ecf20Sopenharmony_ci */ 13648c2ecf20Sopenharmony_ci il_set_tx_power(il, il->tx_power_next, false); 13658c2ecf20Sopenharmony_ci return 0; 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci /* If we are currently associated and the new config requires 13698c2ecf20Sopenharmony_ci * an RXON_ASSOC and the new config wants the associated mask enabled, 13708c2ecf20Sopenharmony_ci * we must clear the associated from the active configuration 13718c2ecf20Sopenharmony_ci * before we apply the new config */ 13728c2ecf20Sopenharmony_ci if (il_is_associated(il) && new_assoc) { 13738c2ecf20Sopenharmony_ci D_INFO("Toggling associated bit on current RXON\n"); 13748c2ecf20Sopenharmony_ci active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci ret = 13778c2ecf20Sopenharmony_ci il_send_cmd_pdu(il, C_RXON, 13788c2ecf20Sopenharmony_ci sizeof(struct il_rxon_cmd), active_rxon); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci /* If the mask clearing failed then we set 13818c2ecf20Sopenharmony_ci * active_rxon back to what it was previously */ 13828c2ecf20Sopenharmony_ci if (ret) { 13838c2ecf20Sopenharmony_ci active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK; 13848c2ecf20Sopenharmony_ci IL_ERR("Error clearing ASSOC_MSK (%d)\n", ret); 13858c2ecf20Sopenharmony_ci return ret; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci il_clear_ucode_stations(il); 13888c2ecf20Sopenharmony_ci il_restore_stations(il); 13898c2ecf20Sopenharmony_ci ret = il4965_restore_default_wep_keys(il); 13908c2ecf20Sopenharmony_ci if (ret) { 13918c2ecf20Sopenharmony_ci IL_ERR("Failed to restore WEP keys (%d)\n", ret); 13928c2ecf20Sopenharmony_ci return ret; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci } 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci D_INFO("Sending RXON\n" "* with%s RXON_FILTER_ASSOC_MSK\n" 13978c2ecf20Sopenharmony_ci "* channel = %d\n" "* bssid = %pM\n", (new_assoc ? "" : "out"), 13988c2ecf20Sopenharmony_ci le16_to_cpu(il->staging.channel), il->staging.bssid_addr); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci il_set_rxon_hwcrypto(il, !il->cfg->mod_params->sw_crypto); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci /* Apply the new configuration 14038c2ecf20Sopenharmony_ci * RXON unassoc clears the station table in uCode so restoration of 14048c2ecf20Sopenharmony_ci * stations is needed after it (the RXON command) completes 14058c2ecf20Sopenharmony_ci */ 14068c2ecf20Sopenharmony_ci if (!new_assoc) { 14078c2ecf20Sopenharmony_ci ret = 14088c2ecf20Sopenharmony_ci il_send_cmd_pdu(il, C_RXON, 14098c2ecf20Sopenharmony_ci sizeof(struct il_rxon_cmd), &il->staging); 14108c2ecf20Sopenharmony_ci if (ret) { 14118c2ecf20Sopenharmony_ci IL_ERR("Error setting new RXON (%d)\n", ret); 14128c2ecf20Sopenharmony_ci return ret; 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci D_INFO("Return from !new_assoc RXON.\n"); 14158c2ecf20Sopenharmony_ci memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); 14168c2ecf20Sopenharmony_ci il_clear_ucode_stations(il); 14178c2ecf20Sopenharmony_ci il_restore_stations(il); 14188c2ecf20Sopenharmony_ci ret = il4965_restore_default_wep_keys(il); 14198c2ecf20Sopenharmony_ci if (ret) { 14208c2ecf20Sopenharmony_ci IL_ERR("Failed to restore WEP keys (%d)\n", ret); 14218c2ecf20Sopenharmony_ci return ret; 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci if (new_assoc) { 14258c2ecf20Sopenharmony_ci il->start_calib = 0; 14268c2ecf20Sopenharmony_ci /* Apply the new configuration 14278c2ecf20Sopenharmony_ci * RXON assoc doesn't clear the station table in uCode, 14288c2ecf20Sopenharmony_ci */ 14298c2ecf20Sopenharmony_ci ret = 14308c2ecf20Sopenharmony_ci il_send_cmd_pdu(il, C_RXON, 14318c2ecf20Sopenharmony_ci sizeof(struct il_rxon_cmd), &il->staging); 14328c2ecf20Sopenharmony_ci if (ret) { 14338c2ecf20Sopenharmony_ci IL_ERR("Error setting new RXON (%d)\n", ret); 14348c2ecf20Sopenharmony_ci return ret; 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci il_print_rx_config_cmd(il); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci il4965_init_sensitivity(il); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci /* If we issue a new RXON command which required a tune then we must 14438c2ecf20Sopenharmony_ci * send a new TXPOWER command or we won't be able to Tx any frames */ 14448c2ecf20Sopenharmony_ci ret = il_set_tx_power(il, il->tx_power_next, true); 14458c2ecf20Sopenharmony_ci if (ret) { 14468c2ecf20Sopenharmony_ci IL_ERR("Error sending TX power (%d)\n", ret); 14478c2ecf20Sopenharmony_ci return ret; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci return 0; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_cistatic int 14548c2ecf20Sopenharmony_ciil4965_hw_channel_switch(struct il_priv *il, 14558c2ecf20Sopenharmony_ci struct ieee80211_channel_switch *ch_switch) 14568c2ecf20Sopenharmony_ci{ 14578c2ecf20Sopenharmony_ci int rc; 14588c2ecf20Sopenharmony_ci u8 band = 0; 14598c2ecf20Sopenharmony_ci bool is_ht40 = false; 14608c2ecf20Sopenharmony_ci u8 ctrl_chan_high = 0; 14618c2ecf20Sopenharmony_ci struct il4965_channel_switch_cmd cmd; 14628c2ecf20Sopenharmony_ci const struct il_channel_info *ch_info; 14638c2ecf20Sopenharmony_ci u32 switch_time_in_usec, ucode_switch_time; 14648c2ecf20Sopenharmony_ci u16 ch; 14658c2ecf20Sopenharmony_ci u32 tsf_low; 14668c2ecf20Sopenharmony_ci u8 switch_count; 14678c2ecf20Sopenharmony_ci u16 beacon_interval = le16_to_cpu(il->timing.beacon_interval); 14688c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = il->vif; 14698c2ecf20Sopenharmony_ci band = (il->band == NL80211_BAND_2GHZ); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(vif == NULL)) 14728c2ecf20Sopenharmony_ci return -EIO; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci is_ht40 = iw4965_is_ht40_channel(il->staging.flags); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci if (is_ht40 && (il->staging.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) 14778c2ecf20Sopenharmony_ci ctrl_chan_high = 1; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci cmd.band = band; 14808c2ecf20Sopenharmony_ci cmd.expect_beacon = 0; 14818c2ecf20Sopenharmony_ci ch = ch_switch->chandef.chan->hw_value; 14828c2ecf20Sopenharmony_ci cmd.channel = cpu_to_le16(ch); 14838c2ecf20Sopenharmony_ci cmd.rxon_flags = il->staging.flags; 14848c2ecf20Sopenharmony_ci cmd.rxon_filter_flags = il->staging.filter_flags; 14858c2ecf20Sopenharmony_ci switch_count = ch_switch->count; 14868c2ecf20Sopenharmony_ci tsf_low = ch_switch->timestamp & 0x0ffffffff; 14878c2ecf20Sopenharmony_ci /* 14888c2ecf20Sopenharmony_ci * calculate the ucode channel switch time 14898c2ecf20Sopenharmony_ci * adding TSF as one of the factor for when to switch 14908c2ecf20Sopenharmony_ci */ 14918c2ecf20Sopenharmony_ci if (il->ucode_beacon_time > tsf_low && beacon_interval) { 14928c2ecf20Sopenharmony_ci if (switch_count > 14938c2ecf20Sopenharmony_ci ((il->ucode_beacon_time - tsf_low) / beacon_interval)) { 14948c2ecf20Sopenharmony_ci switch_count -= 14958c2ecf20Sopenharmony_ci (il->ucode_beacon_time - tsf_low) / beacon_interval; 14968c2ecf20Sopenharmony_ci } else 14978c2ecf20Sopenharmony_ci switch_count = 0; 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci if (switch_count <= 1) 15008c2ecf20Sopenharmony_ci cmd.switch_time = cpu_to_le32(il->ucode_beacon_time); 15018c2ecf20Sopenharmony_ci else { 15028c2ecf20Sopenharmony_ci switch_time_in_usec = 15038c2ecf20Sopenharmony_ci vif->bss_conf.beacon_int * switch_count * TIME_UNIT; 15048c2ecf20Sopenharmony_ci ucode_switch_time = 15058c2ecf20Sopenharmony_ci il_usecs_to_beacons(il, switch_time_in_usec, 15068c2ecf20Sopenharmony_ci beacon_interval); 15078c2ecf20Sopenharmony_ci cmd.switch_time = 15088c2ecf20Sopenharmony_ci il_add_beacon_time(il, il->ucode_beacon_time, 15098c2ecf20Sopenharmony_ci ucode_switch_time, beacon_interval); 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci D_11H("uCode time for the switch is 0x%x\n", cmd.switch_time); 15128c2ecf20Sopenharmony_ci ch_info = il_get_channel_info(il, il->band, ch); 15138c2ecf20Sopenharmony_ci if (ch_info) 15148c2ecf20Sopenharmony_ci cmd.expect_beacon = il_is_channel_radar(ch_info); 15158c2ecf20Sopenharmony_ci else { 15168c2ecf20Sopenharmony_ci IL_ERR("invalid channel switch from %u to %u\n", 15178c2ecf20Sopenharmony_ci il->active.channel, ch); 15188c2ecf20Sopenharmony_ci return -EFAULT; 15198c2ecf20Sopenharmony_ci } 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci rc = il4965_fill_txpower_tbl(il, band, ch, is_ht40, ctrl_chan_high, 15228c2ecf20Sopenharmony_ci &cmd.tx_power); 15238c2ecf20Sopenharmony_ci if (rc) { 15248c2ecf20Sopenharmony_ci D_11H("error:%d fill txpower_tbl\n", rc); 15258c2ecf20Sopenharmony_ci return rc; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci return il_send_cmd_pdu(il, C_CHANNEL_SWITCH, sizeof(cmd), &cmd); 15298c2ecf20Sopenharmony_ci} 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci/* 15328c2ecf20Sopenharmony_ci * il4965_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array 15338c2ecf20Sopenharmony_ci */ 15348c2ecf20Sopenharmony_cistatic void 15358c2ecf20Sopenharmony_ciil4965_txq_update_byte_cnt_tbl(struct il_priv *il, struct il_tx_queue *txq, 15368c2ecf20Sopenharmony_ci u16 byte_cnt) 15378c2ecf20Sopenharmony_ci{ 15388c2ecf20Sopenharmony_ci struct il4965_scd_bc_tbl *scd_bc_tbl = il->scd_bc_tbls.addr; 15398c2ecf20Sopenharmony_ci int txq_id = txq->q.id; 15408c2ecf20Sopenharmony_ci int write_ptr = txq->q.write_ptr; 15418c2ecf20Sopenharmony_ci int len = byte_cnt + IL_TX_CRC_SIZE + IL_TX_DELIMITER_SIZE; 15428c2ecf20Sopenharmony_ci __le16 bc_ent; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci bc_ent = cpu_to_le16(len & 0xFFF); 15478c2ecf20Sopenharmony_ci /* Set up byte count within first 256 entries */ 15488c2ecf20Sopenharmony_ci scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci /* If within first 64 entries, duplicate at end */ 15518c2ecf20Sopenharmony_ci if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) 15528c2ecf20Sopenharmony_ci scd_bc_tbl[txq_id].tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = 15538c2ecf20Sopenharmony_ci bc_ent; 15548c2ecf20Sopenharmony_ci} 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci/* 15578c2ecf20Sopenharmony_ci * il4965_hw_get_temperature - return the calibrated temperature (in Kelvin) 15588c2ecf20Sopenharmony_ci * 15598c2ecf20Sopenharmony_ci * A return of <0 indicates bogus data in the stats 15608c2ecf20Sopenharmony_ci */ 15618c2ecf20Sopenharmony_cistatic int 15628c2ecf20Sopenharmony_ciil4965_hw_get_temperature(struct il_priv *il) 15638c2ecf20Sopenharmony_ci{ 15648c2ecf20Sopenharmony_ci s32 temperature; 15658c2ecf20Sopenharmony_ci s32 vt; 15668c2ecf20Sopenharmony_ci s32 R1, R2, R3; 15678c2ecf20Sopenharmony_ci u32 R4; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci if (test_bit(S_TEMPERATURE, &il->status) && 15708c2ecf20Sopenharmony_ci (il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK)) { 15718c2ecf20Sopenharmony_ci D_TEMP("Running HT40 temperature calibration\n"); 15728c2ecf20Sopenharmony_ci R1 = (s32) le32_to_cpu(il->card_alive_init.therm_r1[1]); 15738c2ecf20Sopenharmony_ci R2 = (s32) le32_to_cpu(il->card_alive_init.therm_r2[1]); 15748c2ecf20Sopenharmony_ci R3 = (s32) le32_to_cpu(il->card_alive_init.therm_r3[1]); 15758c2ecf20Sopenharmony_ci R4 = le32_to_cpu(il->card_alive_init.therm_r4[1]); 15768c2ecf20Sopenharmony_ci } else { 15778c2ecf20Sopenharmony_ci D_TEMP("Running temperature calibration\n"); 15788c2ecf20Sopenharmony_ci R1 = (s32) le32_to_cpu(il->card_alive_init.therm_r1[0]); 15798c2ecf20Sopenharmony_ci R2 = (s32) le32_to_cpu(il->card_alive_init.therm_r2[0]); 15808c2ecf20Sopenharmony_ci R3 = (s32) le32_to_cpu(il->card_alive_init.therm_r3[0]); 15818c2ecf20Sopenharmony_ci R4 = le32_to_cpu(il->card_alive_init.therm_r4[0]); 15828c2ecf20Sopenharmony_ci } 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci /* 15858c2ecf20Sopenharmony_ci * Temperature is only 23 bits, so sign extend out to 32. 15868c2ecf20Sopenharmony_ci * 15878c2ecf20Sopenharmony_ci * NOTE If we haven't received a stats notification yet 15888c2ecf20Sopenharmony_ci * with an updated temperature, use R4 provided to us in the 15898c2ecf20Sopenharmony_ci * "initialize" ALIVE response. 15908c2ecf20Sopenharmony_ci */ 15918c2ecf20Sopenharmony_ci if (!test_bit(S_TEMPERATURE, &il->status)) 15928c2ecf20Sopenharmony_ci vt = sign_extend32(R4, 23); 15938c2ecf20Sopenharmony_ci else 15948c2ecf20Sopenharmony_ci vt = sign_extend32(le32_to_cpu 15958c2ecf20Sopenharmony_ci (il->_4965.stats.general.common.temperature), 15968c2ecf20Sopenharmony_ci 23); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci D_TEMP("Calib values R[1-3]: %d %d %d R4: %d\n", R1, R2, R3, vt); 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci if (R3 == R1) { 16018c2ecf20Sopenharmony_ci IL_ERR("Calibration conflict R1 == R3\n"); 16028c2ecf20Sopenharmony_ci return -1; 16038c2ecf20Sopenharmony_ci } 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci /* Calculate temperature in degrees Kelvin, adjust by 97%. 16068c2ecf20Sopenharmony_ci * Add offset to center the adjustment around 0 degrees Centigrade. */ 16078c2ecf20Sopenharmony_ci temperature = TEMPERATURE_CALIB_A_VAL * (vt - R2); 16088c2ecf20Sopenharmony_ci temperature /= (R3 - R1); 16098c2ecf20Sopenharmony_ci temperature = 16108c2ecf20Sopenharmony_ci (temperature * 97) / 100 + TEMPERATURE_CALIB_KELVIN_OFFSET; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci D_TEMP("Calibrated temperature: %dK, %ldC\n", temperature, 16138c2ecf20Sopenharmony_ci kelvin_to_celsius(temperature)); 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci return temperature; 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci/* Adjust Txpower only if temperature variance is greater than threshold. */ 16198c2ecf20Sopenharmony_ci#define IL_TEMPERATURE_THRESHOLD 3 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci/* 16228c2ecf20Sopenharmony_ci * il4965_is_temp_calib_needed - determines if new calibration is needed 16238c2ecf20Sopenharmony_ci * 16248c2ecf20Sopenharmony_ci * If the temperature changed has changed sufficiently, then a recalibration 16258c2ecf20Sopenharmony_ci * is needed. 16268c2ecf20Sopenharmony_ci * 16278c2ecf20Sopenharmony_ci * Assumes caller will replace il->last_temperature once calibration 16288c2ecf20Sopenharmony_ci * executed. 16298c2ecf20Sopenharmony_ci */ 16308c2ecf20Sopenharmony_cistatic int 16318c2ecf20Sopenharmony_ciil4965_is_temp_calib_needed(struct il_priv *il) 16328c2ecf20Sopenharmony_ci{ 16338c2ecf20Sopenharmony_ci int temp_diff; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci if (!test_bit(S_STATS, &il->status)) { 16368c2ecf20Sopenharmony_ci D_TEMP("Temperature not updated -- no stats.\n"); 16378c2ecf20Sopenharmony_ci return 0; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci temp_diff = il->temperature - il->last_temperature; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci /* get absolute value */ 16438c2ecf20Sopenharmony_ci if (temp_diff < 0) { 16448c2ecf20Sopenharmony_ci D_POWER("Getting cooler, delta %d\n", temp_diff); 16458c2ecf20Sopenharmony_ci temp_diff = -temp_diff; 16468c2ecf20Sopenharmony_ci } else if (temp_diff == 0) 16478c2ecf20Sopenharmony_ci D_POWER("Temperature unchanged\n"); 16488c2ecf20Sopenharmony_ci else 16498c2ecf20Sopenharmony_ci D_POWER("Getting warmer, delta %d\n", temp_diff); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci if (temp_diff < IL_TEMPERATURE_THRESHOLD) { 16528c2ecf20Sopenharmony_ci D_POWER(" => thermal txpower calib not needed\n"); 16538c2ecf20Sopenharmony_ci return 0; 16548c2ecf20Sopenharmony_ci } 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci D_POWER(" => thermal txpower calib needed\n"); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci return 1; 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_civoid 16628c2ecf20Sopenharmony_ciil4965_temperature_calib(struct il_priv *il) 16638c2ecf20Sopenharmony_ci{ 16648c2ecf20Sopenharmony_ci s32 temp; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci temp = il4965_hw_get_temperature(il); 16678c2ecf20Sopenharmony_ci if (IL_TX_POWER_TEMPERATURE_OUT_OF_RANGE(temp)) 16688c2ecf20Sopenharmony_ci return; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci if (il->temperature != temp) { 16718c2ecf20Sopenharmony_ci if (il->temperature) 16728c2ecf20Sopenharmony_ci D_TEMP("Temperature changed " "from %ldC to %ldC\n", 16738c2ecf20Sopenharmony_ci kelvin_to_celsius(il->temperature), 16748c2ecf20Sopenharmony_ci kelvin_to_celsius(temp)); 16758c2ecf20Sopenharmony_ci else 16768c2ecf20Sopenharmony_ci D_TEMP("Temperature " "initialized to %ldC\n", 16778c2ecf20Sopenharmony_ci kelvin_to_celsius(temp)); 16788c2ecf20Sopenharmony_ci } 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci il->temperature = temp; 16818c2ecf20Sopenharmony_ci set_bit(S_TEMPERATURE, &il->status); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci if (!il->disable_tx_power_cal && 16848c2ecf20Sopenharmony_ci unlikely(!test_bit(S_SCANNING, &il->status)) && 16858c2ecf20Sopenharmony_ci il4965_is_temp_calib_needed(il)) 16868c2ecf20Sopenharmony_ci queue_work(il->workqueue, &il->txpower_work); 16878c2ecf20Sopenharmony_ci} 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_cistatic u16 16908c2ecf20Sopenharmony_ciil4965_get_hcmd_size(u8 cmd_id, u16 len) 16918c2ecf20Sopenharmony_ci{ 16928c2ecf20Sopenharmony_ci switch (cmd_id) { 16938c2ecf20Sopenharmony_ci case C_RXON: 16948c2ecf20Sopenharmony_ci return (u16) sizeof(struct il4965_rxon_cmd); 16958c2ecf20Sopenharmony_ci default: 16968c2ecf20Sopenharmony_ci return len; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci} 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_cistatic u16 17018c2ecf20Sopenharmony_ciil4965_build_addsta_hcmd(const struct il_addsta_cmd *cmd, u8 * data) 17028c2ecf20Sopenharmony_ci{ 17038c2ecf20Sopenharmony_ci struct il4965_addsta_cmd *addsta = (struct il4965_addsta_cmd *)data; 17048c2ecf20Sopenharmony_ci addsta->mode = cmd->mode; 17058c2ecf20Sopenharmony_ci memcpy(&addsta->sta, &cmd->sta, sizeof(struct sta_id_modify)); 17068c2ecf20Sopenharmony_ci memcpy(&addsta->key, &cmd->key, sizeof(struct il4965_keyinfo)); 17078c2ecf20Sopenharmony_ci addsta->station_flags = cmd->station_flags; 17088c2ecf20Sopenharmony_ci addsta->station_flags_msk = cmd->station_flags_msk; 17098c2ecf20Sopenharmony_ci addsta->tid_disable_tx = cmd->tid_disable_tx; 17108c2ecf20Sopenharmony_ci addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid; 17118c2ecf20Sopenharmony_ci addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid; 17128c2ecf20Sopenharmony_ci addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn; 17138c2ecf20Sopenharmony_ci addsta->sleep_tx_count = cmd->sleep_tx_count; 17148c2ecf20Sopenharmony_ci addsta->reserved1 = cpu_to_le16(0); 17158c2ecf20Sopenharmony_ci addsta->reserved2 = cpu_to_le16(0); 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci return (u16) sizeof(struct il4965_addsta_cmd); 17188c2ecf20Sopenharmony_ci} 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_cistatic void 17218c2ecf20Sopenharmony_ciil4965_post_scan(struct il_priv *il) 17228c2ecf20Sopenharmony_ci{ 17238c2ecf20Sopenharmony_ci /* 17248c2ecf20Sopenharmony_ci * Since setting the RXON may have been deferred while 17258c2ecf20Sopenharmony_ci * performing the scan, fire one off if needed 17268c2ecf20Sopenharmony_ci */ 17278c2ecf20Sopenharmony_ci if (memcmp(&il->staging, &il->active, sizeof(il->staging))) 17288c2ecf20Sopenharmony_ci il_commit_rxon(il); 17298c2ecf20Sopenharmony_ci} 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_cistatic void 17328c2ecf20Sopenharmony_ciil4965_post_associate(struct il_priv *il) 17338c2ecf20Sopenharmony_ci{ 17348c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = il->vif; 17358c2ecf20Sopenharmony_ci int ret = 0; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci if (!vif || !il->is_open) 17388c2ecf20Sopenharmony_ci return; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 17418c2ecf20Sopenharmony_ci return; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci il_scan_cancel_timeout(il, 200); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; 17468c2ecf20Sopenharmony_ci il_commit_rxon(il); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci ret = il_send_rxon_timing(il); 17498c2ecf20Sopenharmony_ci if (ret) 17508c2ecf20Sopenharmony_ci IL_WARN("RXON timing - " "Attempting to continue.\n"); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci il_set_rxon_ht(il, &il->current_ht_config); 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci if (il->ops->set_rxon_chain) 17578c2ecf20Sopenharmony_ci il->ops->set_rxon_chain(il); 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci il->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid); 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci D_ASSOC("assoc id %d beacon interval %d\n", vif->bss_conf.aid, 17628c2ecf20Sopenharmony_ci vif->bss_conf.beacon_int); 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci if (vif->bss_conf.use_short_preamble) 17658c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; 17668c2ecf20Sopenharmony_ci else 17678c2ecf20Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { 17708c2ecf20Sopenharmony_ci if (vif->bss_conf.use_short_slot) 17718c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; 17728c2ecf20Sopenharmony_ci else 17738c2ecf20Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; 17748c2ecf20Sopenharmony_ci } 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci il_commit_rxon(il); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci D_ASSOC("Associated as %d to: %pM\n", vif->bss_conf.aid, 17798c2ecf20Sopenharmony_ci il->active.bssid_addr); 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci switch (vif->type) { 17828c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 17838c2ecf20Sopenharmony_ci break; 17848c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 17858c2ecf20Sopenharmony_ci il4965_send_beacon_cmd(il); 17868c2ecf20Sopenharmony_ci break; 17878c2ecf20Sopenharmony_ci default: 17888c2ecf20Sopenharmony_ci IL_ERR("%s Should not be called in %d mode\n", __func__, 17898c2ecf20Sopenharmony_ci vif->type); 17908c2ecf20Sopenharmony_ci break; 17918c2ecf20Sopenharmony_ci } 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci /* the chain noise calibration will enabled PM upon completion 17948c2ecf20Sopenharmony_ci * If chain noise has already been run, then we need to enable 17958c2ecf20Sopenharmony_ci * power management here */ 17968c2ecf20Sopenharmony_ci if (il->chain_noise_data.state == IL_CHAIN_NOISE_DONE) 17978c2ecf20Sopenharmony_ci il_power_update_mode(il, false); 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci /* Enable Rx differential gain and sensitivity calibrations */ 18008c2ecf20Sopenharmony_ci il4965_chain_noise_reset(il); 18018c2ecf20Sopenharmony_ci il->start_calib = 1; 18028c2ecf20Sopenharmony_ci} 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_cistatic void 18058c2ecf20Sopenharmony_ciil4965_config_ap(struct il_priv *il) 18068c2ecf20Sopenharmony_ci{ 18078c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = il->vif; 18088c2ecf20Sopenharmony_ci int ret = 0; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 18138c2ecf20Sopenharmony_ci return; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci /* The following should be done only at AP bring up */ 18168c2ecf20Sopenharmony_ci if (!il_is_associated(il)) { 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci /* RXON - unassoc (to set timing command) */ 18198c2ecf20Sopenharmony_ci il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; 18208c2ecf20Sopenharmony_ci il_commit_rxon(il); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci /* RXON Timing */ 18238c2ecf20Sopenharmony_ci ret = il_send_rxon_timing(il); 18248c2ecf20Sopenharmony_ci if (ret) 18258c2ecf20Sopenharmony_ci IL_WARN("RXON timing failed - " 18268c2ecf20Sopenharmony_ci "Attempting to continue.\n"); 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci /* AP has all antennas */ 18298c2ecf20Sopenharmony_ci il->chain_noise_data.active_chains = il->hw_params.valid_rx_ant; 18308c2ecf20Sopenharmony_ci il_set_rxon_ht(il, &il->current_ht_config); 18318c2ecf20Sopenharmony_ci if (il->ops->set_rxon_chain) 18328c2ecf20Sopenharmony_ci il->ops->set_rxon_chain(il); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci il->staging.assoc_id = 0; 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci if (vif->bss_conf.use_short_preamble) 18378c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; 18388c2ecf20Sopenharmony_ci else 18398c2ecf20Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { 18428c2ecf20Sopenharmony_ci if (vif->bss_conf.use_short_slot) 18438c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; 18448c2ecf20Sopenharmony_ci else 18458c2ecf20Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; 18468c2ecf20Sopenharmony_ci } 18478c2ecf20Sopenharmony_ci /* need to send beacon cmd before committing assoc RXON! */ 18488c2ecf20Sopenharmony_ci il4965_send_beacon_cmd(il); 18498c2ecf20Sopenharmony_ci /* restore RXON assoc */ 18508c2ecf20Sopenharmony_ci il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; 18518c2ecf20Sopenharmony_ci il_commit_rxon(il); 18528c2ecf20Sopenharmony_ci } 18538c2ecf20Sopenharmony_ci il4965_send_beacon_cmd(il); 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ciconst struct il_ops il4965_ops = { 18578c2ecf20Sopenharmony_ci .txq_update_byte_cnt_tbl = il4965_txq_update_byte_cnt_tbl, 18588c2ecf20Sopenharmony_ci .txq_attach_buf_to_tfd = il4965_hw_txq_attach_buf_to_tfd, 18598c2ecf20Sopenharmony_ci .txq_free_tfd = il4965_hw_txq_free_tfd, 18608c2ecf20Sopenharmony_ci .txq_init = il4965_hw_tx_queue_init, 18618c2ecf20Sopenharmony_ci .is_valid_rtc_data_addr = il4965_hw_valid_rtc_data_addr, 18628c2ecf20Sopenharmony_ci .init_alive_start = il4965_init_alive_start, 18638c2ecf20Sopenharmony_ci .load_ucode = il4965_load_bsm, 18648c2ecf20Sopenharmony_ci .dump_nic_error_log = il4965_dump_nic_error_log, 18658c2ecf20Sopenharmony_ci .dump_fh = il4965_dump_fh, 18668c2ecf20Sopenharmony_ci .set_channel_switch = il4965_hw_channel_switch, 18678c2ecf20Sopenharmony_ci .apm_init = il_apm_init, 18688c2ecf20Sopenharmony_ci .send_tx_power = il4965_send_tx_power, 18698c2ecf20Sopenharmony_ci .update_chain_flags = il4965_update_chain_flags, 18708c2ecf20Sopenharmony_ci .eeprom_acquire_semaphore = il4965_eeprom_acquire_semaphore, 18718c2ecf20Sopenharmony_ci .eeprom_release_semaphore = il4965_eeprom_release_semaphore, 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci .rxon_assoc = il4965_send_rxon_assoc, 18748c2ecf20Sopenharmony_ci .commit_rxon = il4965_commit_rxon, 18758c2ecf20Sopenharmony_ci .set_rxon_chain = il4965_set_rxon_chain, 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci .get_hcmd_size = il4965_get_hcmd_size, 18788c2ecf20Sopenharmony_ci .build_addsta_hcmd = il4965_build_addsta_hcmd, 18798c2ecf20Sopenharmony_ci .request_scan = il4965_request_scan, 18808c2ecf20Sopenharmony_ci .post_scan = il4965_post_scan, 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci .post_associate = il4965_post_associate, 18838c2ecf20Sopenharmony_ci .config_ap = il4965_config_ap, 18848c2ecf20Sopenharmony_ci .manage_ibss_station = il4965_manage_ibss_station, 18858c2ecf20Sopenharmony_ci .update_bcast_stations = il4965_update_bcast_stations, 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci .send_led_cmd = il4965_send_led_cmd, 18888c2ecf20Sopenharmony_ci}; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_cistruct il_cfg il4965_cfg = { 18918c2ecf20Sopenharmony_ci .name = "Intel(R) Wireless WiFi Link 4965AGN", 18928c2ecf20Sopenharmony_ci .fw_name_pre = IL4965_FW_PRE, 18938c2ecf20Sopenharmony_ci .ucode_api_max = IL4965_UCODE_API_MAX, 18948c2ecf20Sopenharmony_ci .ucode_api_min = IL4965_UCODE_API_MIN, 18958c2ecf20Sopenharmony_ci .sku = IL_SKU_A | IL_SKU_G | IL_SKU_N, 18968c2ecf20Sopenharmony_ci .valid_tx_ant = ANT_AB, 18978c2ecf20Sopenharmony_ci .valid_rx_ant = ANT_ABC, 18988c2ecf20Sopenharmony_ci .eeprom_ver = EEPROM_4965_EEPROM_VERSION, 18998c2ecf20Sopenharmony_ci .eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION, 19008c2ecf20Sopenharmony_ci .mod_params = &il4965_mod_params, 19018c2ecf20Sopenharmony_ci .led_mode = IL_LED_BLINK, 19028c2ecf20Sopenharmony_ci /* 19038c2ecf20Sopenharmony_ci * Force use of chains B and C for scan RX on 5 GHz band 19048c2ecf20Sopenharmony_ci * because the device has off-channel reception on chain A. 19058c2ecf20Sopenharmony_ci */ 19068c2ecf20Sopenharmony_ci .scan_rx_antennas[NL80211_BAND_5GHZ] = ANT_BC, 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci .eeprom_size = IL4965_EEPROM_IMG_SIZE, 19098c2ecf20Sopenharmony_ci .num_of_queues = IL49_NUM_QUEUES, 19108c2ecf20Sopenharmony_ci .num_of_ampdu_queues = IL49_NUM_AMPDU_QUEUES, 19118c2ecf20Sopenharmony_ci .pll_cfg_val = 0, 19128c2ecf20Sopenharmony_ci .set_l0s = true, 19138c2ecf20Sopenharmony_ci .use_bsm = true, 19148c2ecf20Sopenharmony_ci .led_compensation = 61, 19158c2ecf20Sopenharmony_ci .chain_noise_num_beacons = IL4965_CAL_NUM_BEACONS, 19168c2ecf20Sopenharmony_ci .wd_timeout = IL_DEF_WD_TIMEOUT, 19178c2ecf20Sopenharmony_ci .temperature_kelvin = true, 19188c2ecf20Sopenharmony_ci .ucode_tracing = true, 19198c2ecf20Sopenharmony_ci .sensitivity_calib_by_driver = true, 19208c2ecf20Sopenharmony_ci .chain_noise_calib_by_driver = true, 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci .regulatory_bands = { 19238c2ecf20Sopenharmony_ci EEPROM_REGULATORY_BAND_1_CHANNELS, 19248c2ecf20Sopenharmony_ci EEPROM_REGULATORY_BAND_2_CHANNELS, 19258c2ecf20Sopenharmony_ci EEPROM_REGULATORY_BAND_3_CHANNELS, 19268c2ecf20Sopenharmony_ci EEPROM_REGULATORY_BAND_4_CHANNELS, 19278c2ecf20Sopenharmony_ci EEPROM_REGULATORY_BAND_5_CHANNELS, 19288c2ecf20Sopenharmony_ci EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS, 19298c2ecf20Sopenharmony_ci EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS 19308c2ecf20Sopenharmony_ci }, 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci}; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci/* Module firmware */ 19358c2ecf20Sopenharmony_ciMODULE_FIRMWARE(IL4965_MODULE_FIRMWARE(IL4965_UCODE_API_MAX)); 1936