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