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