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 * Portions of this file are derived from the ipw3945 project, as well
762306a36Sopenharmony_ci * as portions of the ieee80211 subsystem header files.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Contact Information:
1062306a36Sopenharmony_ci *  Intel Linux Wireless <ilw@linux.intel.com>
1162306a36Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci *****************************************************************************/
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/kernel.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/init.h>
2062306a36Sopenharmony_ci#include <linux/pci.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2362306a36Sopenharmony_ci#include <linux/delay.h>
2462306a36Sopenharmony_ci#include <linux/sched.h>
2562306a36Sopenharmony_ci#include <linux/skbuff.h>
2662306a36Sopenharmony_ci#include <linux/netdevice.h>
2762306a36Sopenharmony_ci#include <linux/firmware.h>
2862306a36Sopenharmony_ci#include <linux/etherdevice.h>
2962306a36Sopenharmony_ci#include <linux/if_arp.h>
3062306a36Sopenharmony_ci#include <linux/units.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <net/mac80211.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <asm/div64.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define DRV_NAME        "iwl4965"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include "common.h"
3962306a36Sopenharmony_ci#include "4965.h"
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/******************************************************************************
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * module boiler plate
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci ******************************************************************************/
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * module name, copyright, version, etc.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci#define DRV_DESCRIPTION	"Intel(R) Wireless WiFi 4965 driver for Linux"
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
5362306a36Sopenharmony_ci#define VD "d"
5462306a36Sopenharmony_ci#else
5562306a36Sopenharmony_ci#define VD
5662306a36Sopenharmony_ci#endif
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define DRV_VERSION     IWLWIFI_VERSION VD
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION);
6162306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
6262306a36Sopenharmony_ciMODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
6362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
6462306a36Sopenharmony_ciMODULE_ALIAS("iwl4965");
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_civoid
6762306a36Sopenharmony_ciil4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
7062306a36Sopenharmony_ci		IL_ERR("Tx flush command to flush out all frames\n");
7162306a36Sopenharmony_ci		if (!test_bit(S_EXIT_PENDING, &il->status))
7262306a36Sopenharmony_ci			queue_work(il->workqueue, &il->tx_flush);
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * EEPROM
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_cistruct il_mod_params il4965_mod_params = {
8062306a36Sopenharmony_ci	.restart_fw = 1,
8162306a36Sopenharmony_ci	/* the rest are 0 by default */
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_civoid
8562306a36Sopenharmony_ciil4965_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	unsigned long flags;
8862306a36Sopenharmony_ci	int i;
8962306a36Sopenharmony_ci	spin_lock_irqsave(&rxq->lock, flags);
9062306a36Sopenharmony_ci	INIT_LIST_HEAD(&rxq->rx_free);
9162306a36Sopenharmony_ci	INIT_LIST_HEAD(&rxq->rx_used);
9262306a36Sopenharmony_ci	/* Fill the rx_used queue with _all_ of the Rx buffers */
9362306a36Sopenharmony_ci	for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
9462306a36Sopenharmony_ci		/* In the reset function, these buffers may have been allocated
9562306a36Sopenharmony_ci		 * to an SKB, so we need to unmap and free potential storage */
9662306a36Sopenharmony_ci		if (rxq->pool[i].page != NULL) {
9762306a36Sopenharmony_ci			dma_unmap_page(&il->pci_dev->dev,
9862306a36Sopenharmony_ci				       rxq->pool[i].page_dma,
9962306a36Sopenharmony_ci				       PAGE_SIZE << il->hw_params.rx_page_order,
10062306a36Sopenharmony_ci				       DMA_FROM_DEVICE);
10162306a36Sopenharmony_ci			__il_free_pages(il, rxq->pool[i].page);
10262306a36Sopenharmony_ci			rxq->pool[i].page = NULL;
10362306a36Sopenharmony_ci		}
10462306a36Sopenharmony_ci		list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	for (i = 0; i < RX_QUEUE_SIZE; i++)
10862306a36Sopenharmony_ci		rxq->queue[i] = NULL;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* Set us so that we have processed and used all buffers, but have
11162306a36Sopenharmony_ci	 * not restocked the Rx queue with fresh buffers */
11262306a36Sopenharmony_ci	rxq->read = rxq->write = 0;
11362306a36Sopenharmony_ci	rxq->write_actual = 0;
11462306a36Sopenharmony_ci	rxq->free_count = 0;
11562306a36Sopenharmony_ci	spin_unlock_irqrestore(&rxq->lock, flags);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ciint
11962306a36Sopenharmony_ciil4965_rx_init(struct il_priv *il, struct il_rx_queue *rxq)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	u32 rb_size;
12262306a36Sopenharmony_ci	const u32 rfdnlog = RX_QUEUE_SIZE_LOG;	/* 256 RBDs */
12362306a36Sopenharmony_ci	u32 rb_timeout = 0;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (il->cfg->mod_params->amsdu_size_8K)
12662306a36Sopenharmony_ci		rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
12762306a36Sopenharmony_ci	else
12862306a36Sopenharmony_ci		rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* Stop Rx DMA */
13162306a36Sopenharmony_ci	il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* Reset driver's Rx queue write idx */
13462306a36Sopenharmony_ci	il_wr(il, FH49_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* Tell device where to find RBD circular buffer in DRAM */
13762306a36Sopenharmony_ci	il_wr(il, FH49_RSCSR_CHNL0_RBDCB_BASE_REG, (u32) (rxq->bd_dma >> 8));
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* Tell device where in DRAM to update its Rx status */
14062306a36Sopenharmony_ci	il_wr(il, FH49_RSCSR_CHNL0_STTS_WPTR_REG, rxq->rb_stts_dma >> 4);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* Enable Rx DMA
14362306a36Sopenharmony_ci	 * Direct rx interrupts to hosts
14462306a36Sopenharmony_ci	 * Rx buffer size 4 or 8k
14562306a36Sopenharmony_ci	 * RB timeout 0x10
14662306a36Sopenharmony_ci	 * 256 RBDs
14762306a36Sopenharmony_ci	 */
14862306a36Sopenharmony_ci	il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG,
14962306a36Sopenharmony_ci	      FH49_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
15062306a36Sopenharmony_ci	      FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
15162306a36Sopenharmony_ci	      FH49_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
15262306a36Sopenharmony_ci	      rb_size |
15362306a36Sopenharmony_ci	      (rb_timeout << FH49_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
15462306a36Sopenharmony_ci	      (rfdnlog << FH49_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* Set interrupt coalescing timer to default (2048 usecs) */
15762306a36Sopenharmony_ci	il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_TIMEOUT_DEF);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return 0;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic void
16362306a36Sopenharmony_ciil4965_set_pwr_vmain(struct il_priv *il)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci/*
16662306a36Sopenharmony_ci * (for documentation purposes)
16762306a36Sopenharmony_ci * to set power to V_AUX, do:
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci		if (pci_pme_capable(il->pci_dev, PCI_D3cold))
17062306a36Sopenharmony_ci			il_set_bits_mask_prph(il, APMG_PS_CTRL_REG,
17162306a36Sopenharmony_ci					       APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
17262306a36Sopenharmony_ci					       ~APMG_PS_CTRL_MSK_PWR_SRC);
17362306a36Sopenharmony_ci */
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	il_set_bits_mask_prph(il, APMG_PS_CTRL_REG,
17662306a36Sopenharmony_ci			      APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
17762306a36Sopenharmony_ci			      ~APMG_PS_CTRL_MSK_PWR_SRC);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ciint
18162306a36Sopenharmony_ciil4965_hw_nic_init(struct il_priv *il)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	unsigned long flags;
18462306a36Sopenharmony_ci	struct il_rx_queue *rxq = &il->rxq;
18562306a36Sopenharmony_ci	int ret;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
18862306a36Sopenharmony_ci	il_apm_init(il);
18962306a36Sopenharmony_ci	/* Set interrupt coalescing calibration timer to default (512 usecs) */
19062306a36Sopenharmony_ci	il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_CALIB_TIMEOUT_DEF);
19162306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	il4965_set_pwr_vmain(il);
19462306a36Sopenharmony_ci	il4965_nic_config(il);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Allocate the RX queue, or reset if it is already allocated */
19762306a36Sopenharmony_ci	if (!rxq->bd) {
19862306a36Sopenharmony_ci		ret = il_rx_queue_alloc(il);
19962306a36Sopenharmony_ci		if (ret) {
20062306a36Sopenharmony_ci			IL_ERR("Unable to initialize Rx queue\n");
20162306a36Sopenharmony_ci			return -ENOMEM;
20262306a36Sopenharmony_ci		}
20362306a36Sopenharmony_ci	} else
20462306a36Sopenharmony_ci		il4965_rx_queue_reset(il, rxq);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	il4965_rx_replenish(il);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	il4965_rx_init(il, rxq);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	rxq->need_update = 1;
21362306a36Sopenharmony_ci	il_rx_queue_update_write_ptr(il, rxq);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Allocate or reset and init all Tx and Command queues */
21862306a36Sopenharmony_ci	if (!il->txq) {
21962306a36Sopenharmony_ci		ret = il4965_txq_ctx_alloc(il);
22062306a36Sopenharmony_ci		if (ret)
22162306a36Sopenharmony_ci			return ret;
22262306a36Sopenharmony_ci	} else
22362306a36Sopenharmony_ci		il4965_txq_ctx_reset(il);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	set_bit(S_INIT, &il->status);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return 0;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/*
23162306a36Sopenharmony_ci * il4965_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_cistatic inline __le32
23462306a36Sopenharmony_ciil4965_dma_addr2rbd_ptr(struct il_priv *il, dma_addr_t dma_addr)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	return cpu_to_le32((u32) (dma_addr >> 8));
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/*
24062306a36Sopenharmony_ci * il4965_rx_queue_restock - refill RX queue from pre-allocated pool
24162306a36Sopenharmony_ci *
24262306a36Sopenharmony_ci * If there are slots in the RX queue that need to be restocked,
24362306a36Sopenharmony_ci * and we have free pre-allocated buffers, fill the ranks as much
24462306a36Sopenharmony_ci * as we can, pulling from rx_free.
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * This moves the 'write' idx forward to catch up with 'processed', and
24762306a36Sopenharmony_ci * also updates the memory address in the firmware to reference the new
24862306a36Sopenharmony_ci * target buffer.
24962306a36Sopenharmony_ci */
25062306a36Sopenharmony_civoid
25162306a36Sopenharmony_ciil4965_rx_queue_restock(struct il_priv *il)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct il_rx_queue *rxq = &il->rxq;
25462306a36Sopenharmony_ci	struct list_head *element;
25562306a36Sopenharmony_ci	struct il_rx_buf *rxb;
25662306a36Sopenharmony_ci	unsigned long flags;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	spin_lock_irqsave(&rxq->lock, flags);
25962306a36Sopenharmony_ci	while (il_rx_queue_space(rxq) > 0 && rxq->free_count) {
26062306a36Sopenharmony_ci		/* The overwritten rxb must be a used one */
26162306a36Sopenharmony_ci		rxb = rxq->queue[rxq->write];
26262306a36Sopenharmony_ci		BUG_ON(rxb && rxb->page);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		/* Get next free Rx buffer, remove from free list */
26562306a36Sopenharmony_ci		element = rxq->rx_free.next;
26662306a36Sopenharmony_ci		rxb = list_entry(element, struct il_rx_buf, list);
26762306a36Sopenharmony_ci		list_del(element);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		/* Point to Rx buffer via next RBD in circular buffer */
27062306a36Sopenharmony_ci		rxq->bd[rxq->write] =
27162306a36Sopenharmony_ci		    il4965_dma_addr2rbd_ptr(il, rxb->page_dma);
27262306a36Sopenharmony_ci		rxq->queue[rxq->write] = rxb;
27362306a36Sopenharmony_ci		rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
27462306a36Sopenharmony_ci		rxq->free_count--;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci	spin_unlock_irqrestore(&rxq->lock, flags);
27762306a36Sopenharmony_ci	/* If the pre-allocated buffer pool is dropping low, schedule to
27862306a36Sopenharmony_ci	 * refill it */
27962306a36Sopenharmony_ci	if (rxq->free_count <= RX_LOW_WATERMARK)
28062306a36Sopenharmony_ci		queue_work(il->workqueue, &il->rx_replenish);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* If we've added more space for the firmware to place data, tell it.
28362306a36Sopenharmony_ci	 * Increment device's write pointer in multiples of 8. */
28462306a36Sopenharmony_ci	if (rxq->write_actual != (rxq->write & ~0x7)) {
28562306a36Sopenharmony_ci		spin_lock_irqsave(&rxq->lock, flags);
28662306a36Sopenharmony_ci		rxq->need_update = 1;
28762306a36Sopenharmony_ci		spin_unlock_irqrestore(&rxq->lock, flags);
28862306a36Sopenharmony_ci		il_rx_queue_update_write_ptr(il, rxq);
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci/*
29362306a36Sopenharmony_ci * il4965_rx_replenish - Move all used packet from rx_used to rx_free
29462306a36Sopenharmony_ci *
29562306a36Sopenharmony_ci * When moving to rx_free an SKB is allocated for the slot.
29662306a36Sopenharmony_ci *
29762306a36Sopenharmony_ci * Also restock the Rx queue via il_rx_queue_restock.
29862306a36Sopenharmony_ci * This is called as a scheduled work item (except for during initialization)
29962306a36Sopenharmony_ci */
30062306a36Sopenharmony_cistatic void
30162306a36Sopenharmony_ciil4965_rx_allocate(struct il_priv *il, gfp_t priority)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct il_rx_queue *rxq = &il->rxq;
30462306a36Sopenharmony_ci	struct list_head *element;
30562306a36Sopenharmony_ci	struct il_rx_buf *rxb;
30662306a36Sopenharmony_ci	struct page *page;
30762306a36Sopenharmony_ci	dma_addr_t page_dma;
30862306a36Sopenharmony_ci	unsigned long flags;
30962306a36Sopenharmony_ci	gfp_t gfp_mask = priority;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	while (1) {
31262306a36Sopenharmony_ci		spin_lock_irqsave(&rxq->lock, flags);
31362306a36Sopenharmony_ci		if (list_empty(&rxq->rx_used)) {
31462306a36Sopenharmony_ci			spin_unlock_irqrestore(&rxq->lock, flags);
31562306a36Sopenharmony_ci			return;
31662306a36Sopenharmony_ci		}
31762306a36Sopenharmony_ci		spin_unlock_irqrestore(&rxq->lock, flags);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		if (rxq->free_count > RX_LOW_WATERMARK)
32062306a36Sopenharmony_ci			gfp_mask |= __GFP_NOWARN;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		if (il->hw_params.rx_page_order > 0)
32362306a36Sopenharmony_ci			gfp_mask |= __GFP_COMP;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		/* Alloc a new receive buffer */
32662306a36Sopenharmony_ci		page = alloc_pages(gfp_mask, il->hw_params.rx_page_order);
32762306a36Sopenharmony_ci		if (!page) {
32862306a36Sopenharmony_ci			if (net_ratelimit())
32962306a36Sopenharmony_ci				D_INFO("alloc_pages failed, " "order: %d\n",
33062306a36Sopenharmony_ci				       il->hw_params.rx_page_order);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci			if (rxq->free_count <= RX_LOW_WATERMARK &&
33362306a36Sopenharmony_ci			    net_ratelimit())
33462306a36Sopenharmony_ci				IL_ERR("Failed to alloc_pages with %s. "
33562306a36Sopenharmony_ci				       "Only %u free buffers remaining.\n",
33662306a36Sopenharmony_ci				       priority ==
33762306a36Sopenharmony_ci				       GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL",
33862306a36Sopenharmony_ci				       rxq->free_count);
33962306a36Sopenharmony_ci			/* We don't reschedule replenish work here -- we will
34062306a36Sopenharmony_ci			 * call the restock method and if it still needs
34162306a36Sopenharmony_ci			 * more buffers it will schedule replenish */
34262306a36Sopenharmony_ci			return;
34362306a36Sopenharmony_ci		}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		/* Get physical address of the RB */
34662306a36Sopenharmony_ci		page_dma = dma_map_page(&il->pci_dev->dev, page, 0,
34762306a36Sopenharmony_ci					PAGE_SIZE << il->hw_params.rx_page_order,
34862306a36Sopenharmony_ci					DMA_FROM_DEVICE);
34962306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(&il->pci_dev->dev, page_dma))) {
35062306a36Sopenharmony_ci			__free_pages(page, il->hw_params.rx_page_order);
35162306a36Sopenharmony_ci			break;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		spin_lock_irqsave(&rxq->lock, flags);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		if (list_empty(&rxq->rx_used)) {
35762306a36Sopenharmony_ci			spin_unlock_irqrestore(&rxq->lock, flags);
35862306a36Sopenharmony_ci			dma_unmap_page(&il->pci_dev->dev, page_dma,
35962306a36Sopenharmony_ci				       PAGE_SIZE << il->hw_params.rx_page_order,
36062306a36Sopenharmony_ci				       DMA_FROM_DEVICE);
36162306a36Sopenharmony_ci			__free_pages(page, il->hw_params.rx_page_order);
36262306a36Sopenharmony_ci			return;
36362306a36Sopenharmony_ci		}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		element = rxq->rx_used.next;
36662306a36Sopenharmony_ci		rxb = list_entry(element, struct il_rx_buf, list);
36762306a36Sopenharmony_ci		list_del(element);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		BUG_ON(rxb->page);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		rxb->page = page;
37262306a36Sopenharmony_ci		rxb->page_dma = page_dma;
37362306a36Sopenharmony_ci		list_add_tail(&rxb->list, &rxq->rx_free);
37462306a36Sopenharmony_ci		rxq->free_count++;
37562306a36Sopenharmony_ci		il->alloc_rxb_page++;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		spin_unlock_irqrestore(&rxq->lock, flags);
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_civoid
38262306a36Sopenharmony_ciil4965_rx_replenish(struct il_priv *il)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	unsigned long flags;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	il4965_rx_allocate(il, GFP_KERNEL);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
38962306a36Sopenharmony_ci	il4965_rx_queue_restock(il);
39062306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_civoid
39462306a36Sopenharmony_ciil4965_rx_replenish_now(struct il_priv *il)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	il4965_rx_allocate(il, GFP_ATOMIC);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	il4965_rx_queue_restock(il);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
40262306a36Sopenharmony_ci * If an SKB has been detached, the POOL needs to have its SKB set to NULL
40362306a36Sopenharmony_ci * This free routine walks the list of POOL entries and if SKB is set to
40462306a36Sopenharmony_ci * non NULL it is unmapped and freed
40562306a36Sopenharmony_ci */
40662306a36Sopenharmony_civoid
40762306a36Sopenharmony_ciil4965_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	int i;
41062306a36Sopenharmony_ci	for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
41162306a36Sopenharmony_ci		if (rxq->pool[i].page != NULL) {
41262306a36Sopenharmony_ci			dma_unmap_page(&il->pci_dev->dev,
41362306a36Sopenharmony_ci				       rxq->pool[i].page_dma,
41462306a36Sopenharmony_ci				       PAGE_SIZE << il->hw_params.rx_page_order,
41562306a36Sopenharmony_ci				       DMA_FROM_DEVICE);
41662306a36Sopenharmony_ci			__il_free_pages(il, rxq->pool[i].page);
41762306a36Sopenharmony_ci			rxq->pool[i].page = NULL;
41862306a36Sopenharmony_ci		}
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
42262306a36Sopenharmony_ci			  rxq->bd_dma);
42362306a36Sopenharmony_ci	dma_free_coherent(&il->pci_dev->dev, sizeof(struct il_rb_status),
42462306a36Sopenharmony_ci			  rxq->rb_stts, rxq->rb_stts_dma);
42562306a36Sopenharmony_ci	rxq->bd = NULL;
42662306a36Sopenharmony_ci	rxq->rb_stts = NULL;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ciint
43062306a36Sopenharmony_ciil4965_rxq_stop(struct il_priv *il)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	int ret;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	_il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0);
43562306a36Sopenharmony_ci	ret = _il_poll_bit(il, FH49_MEM_RSSR_RX_STATUS_REG,
43662306a36Sopenharmony_ci			   FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE,
43762306a36Sopenharmony_ci			   FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE,
43862306a36Sopenharmony_ci			   1000);
43962306a36Sopenharmony_ci	if (ret < 0)
44062306a36Sopenharmony_ci		IL_ERR("Can't stop Rx DMA.\n");
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	return 0;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ciint
44662306a36Sopenharmony_ciil4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum nl80211_band band)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	int idx = 0;
44962306a36Sopenharmony_ci	int band_offset = 0;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* HT rate format: mac80211 wants an MCS number, which is just LSB */
45262306a36Sopenharmony_ci	if (rate_n_flags & RATE_MCS_HT_MSK) {
45362306a36Sopenharmony_ci		idx = (rate_n_flags & 0xff);
45462306a36Sopenharmony_ci		return idx;
45562306a36Sopenharmony_ci		/* Legacy rate format, search for match in table */
45662306a36Sopenharmony_ci	} else {
45762306a36Sopenharmony_ci		if (band == NL80211_BAND_5GHZ)
45862306a36Sopenharmony_ci			band_offset = IL_FIRST_OFDM_RATE;
45962306a36Sopenharmony_ci		for (idx = band_offset; idx < RATE_COUNT_LEGACY; idx++)
46062306a36Sopenharmony_ci			if (il_rates[idx].plcp == (rate_n_flags & 0xFF))
46162306a36Sopenharmony_ci				return idx - band_offset;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return -1;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic int
46862306a36Sopenharmony_ciil4965_calc_rssi(struct il_priv *il, struct il_rx_phy_res *rx_resp)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	/* data from PHY/DSP regarding signal strength, etc.,
47162306a36Sopenharmony_ci	 *   contents are always there, not configurable by host.  */
47262306a36Sopenharmony_ci	struct il4965_rx_non_cfg_phy *ncphy =
47362306a36Sopenharmony_ci	    (struct il4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy_buf;
47462306a36Sopenharmony_ci	u32 agc =
47562306a36Sopenharmony_ci	    (le16_to_cpu(ncphy->agc_info) & IL49_AGC_DB_MASK) >>
47662306a36Sopenharmony_ci	    IL49_AGC_DB_POS;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	u32 valid_antennae =
47962306a36Sopenharmony_ci	    (le16_to_cpu(rx_resp->phy_flags) & IL49_RX_PHY_FLAGS_ANTENNAE_MASK)
48062306a36Sopenharmony_ci	    >> IL49_RX_PHY_FLAGS_ANTENNAE_OFFSET;
48162306a36Sopenharmony_ci	u8 max_rssi = 0;
48262306a36Sopenharmony_ci	u32 i;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/* Find max rssi among 3 possible receivers.
48562306a36Sopenharmony_ci	 * These values are measured by the digital signal processor (DSP).
48662306a36Sopenharmony_ci	 * They should stay fairly constant even as the signal strength varies,
48762306a36Sopenharmony_ci	 *   if the radio's automatic gain control (AGC) is working right.
48862306a36Sopenharmony_ci	 * AGC value (see below) will provide the "interesting" info. */
48962306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
49062306a36Sopenharmony_ci		if (valid_antennae & (1 << i))
49162306a36Sopenharmony_ci			max_rssi = max(ncphy->rssi_info[i << 1], max_rssi);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	D_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n",
49462306a36Sopenharmony_ci		ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4],
49562306a36Sopenharmony_ci		max_rssi, agc);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/* dBm = max_rssi dB - agc dB - constant.
49862306a36Sopenharmony_ci	 * Higher AGC (higher radio gain) means lower signal. */
49962306a36Sopenharmony_ci	return max_rssi - agc - IL4965_RSSI_OFFSET;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic u32
50362306a36Sopenharmony_ciil4965_translate_rx_status(struct il_priv *il, u32 decrypt_in)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	u32 decrypt_out = 0;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) ==
50862306a36Sopenharmony_ci	    RX_RES_STATUS_STATION_FOUND)
50962306a36Sopenharmony_ci		decrypt_out |=
51062306a36Sopenharmony_ci		    (RX_RES_STATUS_STATION_FOUND |
51162306a36Sopenharmony_ci		     RX_RES_STATUS_NO_STATION_INFO_MISMATCH);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* packet was not encrypted */
51662306a36Sopenharmony_ci	if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
51762306a36Sopenharmony_ci	    RX_RES_STATUS_SEC_TYPE_NONE)
51862306a36Sopenharmony_ci		return decrypt_out;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* packet was encrypted with unknown alg */
52162306a36Sopenharmony_ci	if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
52262306a36Sopenharmony_ci	    RX_RES_STATUS_SEC_TYPE_ERR)
52362306a36Sopenharmony_ci		return decrypt_out;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	/* decryption was not done in HW */
52662306a36Sopenharmony_ci	if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) !=
52762306a36Sopenharmony_ci	    RX_MPDU_RES_STATUS_DEC_DONE_MSK)
52862306a36Sopenharmony_ci		return decrypt_out;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) {
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	case RX_RES_STATUS_SEC_TYPE_CCMP:
53362306a36Sopenharmony_ci		/* alg is CCM: check MIC only */
53462306a36Sopenharmony_ci		if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK))
53562306a36Sopenharmony_ci			/* Bad MIC */
53662306a36Sopenharmony_ci			decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
53762306a36Sopenharmony_ci		else
53862306a36Sopenharmony_ci			decrypt_out |= RX_RES_STATUS_DECRYPT_OK;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		break;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	case RX_RES_STATUS_SEC_TYPE_TKIP:
54362306a36Sopenharmony_ci		if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) {
54462306a36Sopenharmony_ci			/* Bad TTAK */
54562306a36Sopenharmony_ci			decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK;
54662306a36Sopenharmony_ci			break;
54762306a36Sopenharmony_ci		}
54862306a36Sopenharmony_ci		fallthrough;	/* if TTAK OK */
54962306a36Sopenharmony_ci	default:
55062306a36Sopenharmony_ci		if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK))
55162306a36Sopenharmony_ci			decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
55262306a36Sopenharmony_ci		else
55362306a36Sopenharmony_ci			decrypt_out |= RX_RES_STATUS_DECRYPT_OK;
55462306a36Sopenharmony_ci		break;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	D_RX("decrypt_in:0x%x  decrypt_out = 0x%x\n", decrypt_in, decrypt_out);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return decrypt_out;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci#define SMALL_PACKET_SIZE 256
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic void
56562306a36Sopenharmony_ciil4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
56662306a36Sopenharmony_ci			       u32 len, u32 ampdu_status, struct il_rx_buf *rxb,
56762306a36Sopenharmony_ci			       struct ieee80211_rx_status *stats)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct sk_buff *skb;
57062306a36Sopenharmony_ci	__le16 fc = hdr->frame_control;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* We only process data packets if the interface is open */
57362306a36Sopenharmony_ci	if (unlikely(!il->is_open)) {
57462306a36Sopenharmony_ci		D_DROP("Dropping packet while interface is not open.\n");
57562306a36Sopenharmony_ci		return;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) {
57962306a36Sopenharmony_ci		il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
58062306a36Sopenharmony_ci		D_INFO("Woke queues - frame received on passive channel\n");
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* In case of HW accelerated crypto and bad decryption, drop */
58462306a36Sopenharmony_ci	if (!il->cfg->mod_params->sw_crypto &&
58562306a36Sopenharmony_ci	    il_set_decrypted_flag(il, hdr, ampdu_status, stats))
58662306a36Sopenharmony_ci		return;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	skb = dev_alloc_skb(SMALL_PACKET_SIZE);
58962306a36Sopenharmony_ci	if (!skb) {
59062306a36Sopenharmony_ci		IL_ERR("dev_alloc_skb failed\n");
59162306a36Sopenharmony_ci		return;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (len <= SMALL_PACKET_SIZE) {
59562306a36Sopenharmony_ci		skb_put_data(skb, hdr, len);
59662306a36Sopenharmony_ci	} else {
59762306a36Sopenharmony_ci		skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb),
59862306a36Sopenharmony_ci				len, PAGE_SIZE << il->hw_params.rx_page_order);
59962306a36Sopenharmony_ci		il->alloc_rxb_page--;
60062306a36Sopenharmony_ci		rxb->page = NULL;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	il_update_stats(il, false, fc, len);
60462306a36Sopenharmony_ci	memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	ieee80211_rx(il->hw, skb);
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci/* Called for N_RX (legacy ABG frames), or
61062306a36Sopenharmony_ci * N_RX_MPDU (HT high-throughput N frames). */
61162306a36Sopenharmony_cistatic void
61262306a36Sopenharmony_ciil4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	struct ieee80211_hdr *header;
61562306a36Sopenharmony_ci	struct ieee80211_rx_status rx_status = {};
61662306a36Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
61762306a36Sopenharmony_ci	struct il_rx_phy_res *phy_res;
61862306a36Sopenharmony_ci	__le32 rx_pkt_status;
61962306a36Sopenharmony_ci	struct il_rx_mpdu_res_start *amsdu;
62062306a36Sopenharmony_ci	u32 len;
62162306a36Sopenharmony_ci	u32 ampdu_status;
62262306a36Sopenharmony_ci	u32 rate_n_flags;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/**
62562306a36Sopenharmony_ci	 * N_RX and N_RX_MPDU are handled differently.
62662306a36Sopenharmony_ci	 *	N_RX: physical layer info is in this buffer
62762306a36Sopenharmony_ci	 *	N_RX_MPDU: physical layer info was sent in separate
62862306a36Sopenharmony_ci	 *		command and cached in il->last_phy_res
62962306a36Sopenharmony_ci	 *
63062306a36Sopenharmony_ci	 * Here we set up local variables depending on which command is
63162306a36Sopenharmony_ci	 * received.
63262306a36Sopenharmony_ci	 */
63362306a36Sopenharmony_ci	if (pkt->hdr.cmd == N_RX) {
63462306a36Sopenharmony_ci		phy_res = (struct il_rx_phy_res *)pkt->u.raw;
63562306a36Sopenharmony_ci		header =
63662306a36Sopenharmony_ci		    (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*phy_res) +
63762306a36Sopenharmony_ci					     phy_res->cfg_phy_cnt);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci		len = le16_to_cpu(phy_res->byte_count);
64062306a36Sopenharmony_ci		rx_pkt_status =
64162306a36Sopenharmony_ci		    *(__le32 *) (pkt->u.raw + sizeof(*phy_res) +
64262306a36Sopenharmony_ci				 phy_res->cfg_phy_cnt + len);
64362306a36Sopenharmony_ci		ampdu_status = le32_to_cpu(rx_pkt_status);
64462306a36Sopenharmony_ci	} else {
64562306a36Sopenharmony_ci		if (!il->_4965.last_phy_res_valid) {
64662306a36Sopenharmony_ci			IL_ERR("MPDU frame without cached PHY data\n");
64762306a36Sopenharmony_ci			return;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci		phy_res = &il->_4965.last_phy_res;
65062306a36Sopenharmony_ci		amsdu = (struct il_rx_mpdu_res_start *)pkt->u.raw;
65162306a36Sopenharmony_ci		header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu));
65262306a36Sopenharmony_ci		len = le16_to_cpu(amsdu->byte_count);
65362306a36Sopenharmony_ci		rx_pkt_status = *(__le32 *) (pkt->u.raw + sizeof(*amsdu) + len);
65462306a36Sopenharmony_ci		ampdu_status =
65562306a36Sopenharmony_ci		    il4965_translate_rx_status(il, le32_to_cpu(rx_pkt_status));
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if ((unlikely(phy_res->cfg_phy_cnt > 20))) {
65962306a36Sopenharmony_ci		D_DROP("dsp size out of range [0,20]: %d\n",
66062306a36Sopenharmony_ci		       phy_res->cfg_phy_cnt);
66162306a36Sopenharmony_ci		return;
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) ||
66562306a36Sopenharmony_ci	    !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) {
66662306a36Sopenharmony_ci		D_RX("Bad CRC or FIFO: 0x%08X.\n", le32_to_cpu(rx_pkt_status));
66762306a36Sopenharmony_ci		return;
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/* This will be used in several places later */
67162306a36Sopenharmony_ci	rate_n_flags = le32_to_cpu(phy_res->rate_n_flags);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	/* rx_status carries information about the packet to mac80211 */
67462306a36Sopenharmony_ci	rx_status.mactime = le64_to_cpu(phy_res->timestamp);
67562306a36Sopenharmony_ci	rx_status.band =
67662306a36Sopenharmony_ci	    (phy_res->
67762306a36Sopenharmony_ci	     phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? NL80211_BAND_2GHZ :
67862306a36Sopenharmony_ci	    NL80211_BAND_5GHZ;
67962306a36Sopenharmony_ci	rx_status.freq =
68062306a36Sopenharmony_ci	    ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel),
68162306a36Sopenharmony_ci					   rx_status.band);
68262306a36Sopenharmony_ci	rx_status.rate_idx =
68362306a36Sopenharmony_ci	    il4965_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band);
68462306a36Sopenharmony_ci	rx_status.flag = 0;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* TSF isn't reliable. In order to allow smooth user experience,
68762306a36Sopenharmony_ci	 * this W/A doesn't propagate it to the mac80211 */
68862306a36Sopenharmony_ci	/*rx_status.flag |= RX_FLAG_MACTIME_START; */
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	il->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* Find max signal strength (dBm) among 3 antenna/receiver chains */
69362306a36Sopenharmony_ci	rx_status.signal = il4965_calc_rssi(il, phy_res);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	D_STATS("Rssi %d, TSF %llu\n", rx_status.signal,
69662306a36Sopenharmony_ci		(unsigned long long)rx_status.mactime);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/*
69962306a36Sopenharmony_ci	 * "antenna number"
70062306a36Sopenharmony_ci	 *
70162306a36Sopenharmony_ci	 * It seems that the antenna field in the phy flags value
70262306a36Sopenharmony_ci	 * is actually a bit field. This is undefined by radiotap,
70362306a36Sopenharmony_ci	 * it wants an actual antenna number but I always get "7"
70462306a36Sopenharmony_ci	 * for most legacy frames I receive indicating that the
70562306a36Sopenharmony_ci	 * same frame was received on all three RX chains.
70662306a36Sopenharmony_ci	 *
70762306a36Sopenharmony_ci	 * I think this field should be removed in favor of a
70862306a36Sopenharmony_ci	 * new 802.11n radiotap field "RX chains" that is defined
70962306a36Sopenharmony_ci	 * as a bitmask.
71062306a36Sopenharmony_ci	 */
71162306a36Sopenharmony_ci	rx_status.antenna =
71262306a36Sopenharmony_ci	    (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >>
71362306a36Sopenharmony_ci	    RX_RES_PHY_FLAGS_ANTENNA_POS;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	/* set the preamble flag if appropriate */
71662306a36Sopenharmony_ci	if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK)
71762306a36Sopenharmony_ci		rx_status.enc_flags |= RX_ENC_FLAG_SHORTPRE;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* Set up the HT phy flags */
72062306a36Sopenharmony_ci	if (rate_n_flags & RATE_MCS_HT_MSK)
72162306a36Sopenharmony_ci		rx_status.encoding = RX_ENC_HT;
72262306a36Sopenharmony_ci	if (rate_n_flags & RATE_MCS_HT40_MSK)
72362306a36Sopenharmony_ci		rx_status.bw = RATE_INFO_BW_40;
72462306a36Sopenharmony_ci	else
72562306a36Sopenharmony_ci		rx_status.bw = RATE_INFO_BW_20;
72662306a36Sopenharmony_ci	if (rate_n_flags & RATE_MCS_SGI_MSK)
72762306a36Sopenharmony_ci		rx_status.enc_flags |= RX_ENC_FLAG_SHORT_GI;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) {
73062306a36Sopenharmony_ci		/* We know which subframes of an A-MPDU belong
73162306a36Sopenharmony_ci		 * together since we get a single PHY response
73262306a36Sopenharmony_ci		 * from the firmware for all of them.
73362306a36Sopenharmony_ci		 */
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
73662306a36Sopenharmony_ci		rx_status.ampdu_reference = il->_4965.ampdu_ref;
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	il4965_pass_packet_to_mac80211(il, header, len, ampdu_status, rxb,
74062306a36Sopenharmony_ci				       &rx_status);
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci/* Cache phy data (Rx signal strength, etc) for HT frame (N_RX_PHY).
74462306a36Sopenharmony_ci * This will be used later in il_hdl_rx() for N_RX_MPDU. */
74562306a36Sopenharmony_cistatic void
74662306a36Sopenharmony_ciil4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
74962306a36Sopenharmony_ci	il->_4965.last_phy_res_valid = true;
75062306a36Sopenharmony_ci	il->_4965.ampdu_ref++;
75162306a36Sopenharmony_ci	memcpy(&il->_4965.last_phy_res, pkt->u.raw,
75262306a36Sopenharmony_ci	       sizeof(struct il_rx_phy_res));
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cistatic int
75662306a36Sopenharmony_ciil4965_get_channels_for_scan(struct il_priv *il, struct ieee80211_vif *vif,
75762306a36Sopenharmony_ci			     enum nl80211_band band, u8 is_active,
75862306a36Sopenharmony_ci			     u8 n_probes, struct il_scan_channel *scan_ch)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct ieee80211_channel *chan;
76162306a36Sopenharmony_ci	const struct ieee80211_supported_band *sband;
76262306a36Sopenharmony_ci	const struct il_channel_info *ch_info;
76362306a36Sopenharmony_ci	u16 passive_dwell = 0;
76462306a36Sopenharmony_ci	u16 active_dwell = 0;
76562306a36Sopenharmony_ci	int added, i;
76662306a36Sopenharmony_ci	u16 channel;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	sband = il_get_hw_mode(il, band);
76962306a36Sopenharmony_ci	if (!sband)
77062306a36Sopenharmony_ci		return 0;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	active_dwell = il_get_active_dwell_time(il, band, n_probes);
77362306a36Sopenharmony_ci	passive_dwell = il_get_passive_dwell_time(il, band, vif);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	if (passive_dwell <= active_dwell)
77662306a36Sopenharmony_ci		passive_dwell = active_dwell + 1;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	for (i = 0, added = 0; i < il->scan_request->n_channels; i++) {
77962306a36Sopenharmony_ci		chan = il->scan_request->channels[i];
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		if (chan->band != band)
78262306a36Sopenharmony_ci			continue;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		channel = chan->hw_value;
78562306a36Sopenharmony_ci		scan_ch->channel = cpu_to_le16(channel);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		ch_info = il_get_channel_info(il, band, channel);
78862306a36Sopenharmony_ci		if (!il_is_channel_valid(ch_info)) {
78962306a36Sopenharmony_ci			D_SCAN("Channel %d is INVALID for this band.\n",
79062306a36Sopenharmony_ci			       channel);
79162306a36Sopenharmony_ci			continue;
79262306a36Sopenharmony_ci		}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci		if (!is_active || il_is_channel_passive(ch_info) ||
79562306a36Sopenharmony_ci		    (chan->flags & IEEE80211_CHAN_NO_IR))
79662306a36Sopenharmony_ci			scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
79762306a36Sopenharmony_ci		else
79862306a36Sopenharmony_ci			scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci		if (n_probes)
80162306a36Sopenharmony_ci			scan_ch->type |= IL_SCAN_PROBE_MASK(n_probes);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci		scan_ch->active_dwell = cpu_to_le16(active_dwell);
80462306a36Sopenharmony_ci		scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci		/* Set txpower levels to defaults */
80762306a36Sopenharmony_ci		scan_ch->dsp_atten = 110;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci		/* NOTE: if we were doing 6Mb OFDM for scans we'd use
81062306a36Sopenharmony_ci		 * power level:
81162306a36Sopenharmony_ci		 * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
81262306a36Sopenharmony_ci		 */
81362306a36Sopenharmony_ci		if (band == NL80211_BAND_5GHZ)
81462306a36Sopenharmony_ci			scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
81562306a36Sopenharmony_ci		else
81662306a36Sopenharmony_ci			scan_ch->tx_gain = ((1 << 5) | (5 << 3));
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		D_SCAN("Scanning ch=%d prob=0x%X [%s %d]\n", channel,
81962306a36Sopenharmony_ci		       le32_to_cpu(scan_ch->type),
82062306a36Sopenharmony_ci		       (scan_ch->
82162306a36Sopenharmony_ci			type & SCAN_CHANNEL_TYPE_ACTIVE) ? "ACTIVE" : "PASSIVE",
82262306a36Sopenharmony_ci		       (scan_ch->
82362306a36Sopenharmony_ci			type & SCAN_CHANNEL_TYPE_ACTIVE) ? active_dwell :
82462306a36Sopenharmony_ci		       passive_dwell);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci		scan_ch++;
82762306a36Sopenharmony_ci		added++;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	D_SCAN("total channels to scan %d\n", added);
83162306a36Sopenharmony_ci	return added;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic void
83562306a36Sopenharmony_ciil4965_toggle_tx_ant(struct il_priv *il, u8 *ant, u8 valid)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	int i;
83862306a36Sopenharmony_ci	u8 ind = *ant;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	for (i = 0; i < RATE_ANT_NUM - 1; i++) {
84162306a36Sopenharmony_ci		ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0;
84262306a36Sopenharmony_ci		if (valid & BIT(ind)) {
84362306a36Sopenharmony_ci			*ant = ind;
84462306a36Sopenharmony_ci			return;
84562306a36Sopenharmony_ci		}
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ciint
85062306a36Sopenharmony_ciil4965_request_scan(struct il_priv *il, struct ieee80211_vif *vif)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	struct il_host_cmd cmd = {
85362306a36Sopenharmony_ci		.id = C_SCAN,
85462306a36Sopenharmony_ci		.len = sizeof(struct il_scan_cmd),
85562306a36Sopenharmony_ci		.flags = CMD_SIZE_HUGE,
85662306a36Sopenharmony_ci	};
85762306a36Sopenharmony_ci	struct il_scan_cmd *scan;
85862306a36Sopenharmony_ci	u32 rate_flags = 0;
85962306a36Sopenharmony_ci	u16 cmd_len;
86062306a36Sopenharmony_ci	u16 rx_chain = 0;
86162306a36Sopenharmony_ci	enum nl80211_band band;
86262306a36Sopenharmony_ci	u8 n_probes = 0;
86362306a36Sopenharmony_ci	u8 rx_ant = il->hw_params.valid_rx_ant;
86462306a36Sopenharmony_ci	u8 rate;
86562306a36Sopenharmony_ci	bool is_active = false;
86662306a36Sopenharmony_ci	int chan_mod;
86762306a36Sopenharmony_ci	u8 active_chains;
86862306a36Sopenharmony_ci	u8 scan_tx_antennas = il->hw_params.valid_tx_ant;
86962306a36Sopenharmony_ci	int ret;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	if (!il->scan_cmd) {
87462306a36Sopenharmony_ci		il->scan_cmd =
87562306a36Sopenharmony_ci		    kmalloc(sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE,
87662306a36Sopenharmony_ci			    GFP_KERNEL);
87762306a36Sopenharmony_ci		if (!il->scan_cmd) {
87862306a36Sopenharmony_ci			D_SCAN("fail to allocate memory for scan\n");
87962306a36Sopenharmony_ci			return -ENOMEM;
88062306a36Sopenharmony_ci		}
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci	scan = il->scan_cmd;
88362306a36Sopenharmony_ci	memset(scan, 0, sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	scan->quiet_plcp_th = IL_PLCP_QUIET_THRESH;
88662306a36Sopenharmony_ci	scan->quiet_time = IL_ACTIVE_QUIET_TIME;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	if (il_is_any_associated(il)) {
88962306a36Sopenharmony_ci		u16 interval;
89062306a36Sopenharmony_ci		u32 extra;
89162306a36Sopenharmony_ci		u32 suspend_time = 100;
89262306a36Sopenharmony_ci		u32 scan_suspend_time = 100;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci		D_INFO("Scanning while associated...\n");
89562306a36Sopenharmony_ci		interval = vif->bss_conf.beacon_int;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci		scan->suspend_time = 0;
89862306a36Sopenharmony_ci		scan->max_out_time = cpu_to_le32(200 * 1024);
89962306a36Sopenharmony_ci		if (!interval)
90062306a36Sopenharmony_ci			interval = suspend_time;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci		extra = (suspend_time / interval) << 22;
90362306a36Sopenharmony_ci		scan_suspend_time =
90462306a36Sopenharmony_ci		    (extra | ((suspend_time % interval) * 1024));
90562306a36Sopenharmony_ci		scan->suspend_time = cpu_to_le32(scan_suspend_time);
90662306a36Sopenharmony_ci		D_SCAN("suspend_time 0x%X beacon interval %d\n",
90762306a36Sopenharmony_ci		       scan_suspend_time, interval);
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (il->scan_request->n_ssids) {
91162306a36Sopenharmony_ci		int i, p = 0;
91262306a36Sopenharmony_ci		D_SCAN("Kicking off active scan\n");
91362306a36Sopenharmony_ci		for (i = 0; i < il->scan_request->n_ssids; i++) {
91462306a36Sopenharmony_ci			/* always does wildcard anyway */
91562306a36Sopenharmony_ci			if (!il->scan_request->ssids[i].ssid_len)
91662306a36Sopenharmony_ci				continue;
91762306a36Sopenharmony_ci			scan->direct_scan[p].id = WLAN_EID_SSID;
91862306a36Sopenharmony_ci			scan->direct_scan[p].len =
91962306a36Sopenharmony_ci			    il->scan_request->ssids[i].ssid_len;
92062306a36Sopenharmony_ci			memcpy(scan->direct_scan[p].ssid,
92162306a36Sopenharmony_ci			       il->scan_request->ssids[i].ssid,
92262306a36Sopenharmony_ci			       il->scan_request->ssids[i].ssid_len);
92362306a36Sopenharmony_ci			n_probes++;
92462306a36Sopenharmony_ci			p++;
92562306a36Sopenharmony_ci		}
92662306a36Sopenharmony_ci		is_active = true;
92762306a36Sopenharmony_ci	} else
92862306a36Sopenharmony_ci		D_SCAN("Start passive scan.\n");
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
93162306a36Sopenharmony_ci	scan->tx_cmd.sta_id = il->hw_params.bcast_id;
93262306a36Sopenharmony_ci	scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	switch (il->scan_band) {
93562306a36Sopenharmony_ci	case NL80211_BAND_2GHZ:
93662306a36Sopenharmony_ci		scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
93762306a36Sopenharmony_ci		chan_mod =
93862306a36Sopenharmony_ci		    le32_to_cpu(il->active.flags & RXON_FLG_CHANNEL_MODE_MSK) >>
93962306a36Sopenharmony_ci		    RXON_FLG_CHANNEL_MODE_POS;
94062306a36Sopenharmony_ci		if (chan_mod == CHANNEL_MODE_PURE_40) {
94162306a36Sopenharmony_ci			rate = RATE_6M_PLCP;
94262306a36Sopenharmony_ci		} else {
94362306a36Sopenharmony_ci			rate = RATE_1M_PLCP;
94462306a36Sopenharmony_ci			rate_flags = RATE_MCS_CCK_MSK;
94562306a36Sopenharmony_ci		}
94662306a36Sopenharmony_ci		break;
94762306a36Sopenharmony_ci	case NL80211_BAND_5GHZ:
94862306a36Sopenharmony_ci		rate = RATE_6M_PLCP;
94962306a36Sopenharmony_ci		break;
95062306a36Sopenharmony_ci	default:
95162306a36Sopenharmony_ci		IL_WARN("Invalid scan band\n");
95262306a36Sopenharmony_ci		return -EIO;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/*
95662306a36Sopenharmony_ci	 * If active scanning is requested but a certain channel is
95762306a36Sopenharmony_ci	 * marked passive, we can do active scanning if we detect
95862306a36Sopenharmony_ci	 * transmissions.
95962306a36Sopenharmony_ci	 *
96062306a36Sopenharmony_ci	 * There is an issue with some firmware versions that triggers
96162306a36Sopenharmony_ci	 * a sysassert on a "good CRC threshold" of zero (== disabled),
96262306a36Sopenharmony_ci	 * on a radar channel even though this means that we should NOT
96362306a36Sopenharmony_ci	 * send probes.
96462306a36Sopenharmony_ci	 *
96562306a36Sopenharmony_ci	 * The "good CRC threshold" is the number of frames that we
96662306a36Sopenharmony_ci	 * need to receive during our dwell time on a channel before
96762306a36Sopenharmony_ci	 * sending out probes -- setting this to a huge value will
96862306a36Sopenharmony_ci	 * mean we never reach it, but at the same time work around
96962306a36Sopenharmony_ci	 * the aforementioned issue. Thus use IL_GOOD_CRC_TH_NEVER
97062306a36Sopenharmony_ci	 * here instead of IL_GOOD_CRC_TH_DISABLED.
97162306a36Sopenharmony_ci	 */
97262306a36Sopenharmony_ci	scan->good_CRC_th =
97362306a36Sopenharmony_ci	    is_active ? IL_GOOD_CRC_TH_DEFAULT : IL_GOOD_CRC_TH_NEVER;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	band = il->scan_band;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (il->cfg->scan_rx_antennas[band])
97862306a36Sopenharmony_ci		rx_ant = il->cfg->scan_rx_antennas[band];
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	il4965_toggle_tx_ant(il, &il->scan_tx_ant[band], scan_tx_antennas);
98162306a36Sopenharmony_ci	rate_flags |= BIT(il->scan_tx_ant[band]) << RATE_MCS_ANT_POS;
98262306a36Sopenharmony_ci	scan->tx_cmd.rate_n_flags = cpu_to_le32(rate | rate_flags);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	/* In power save mode use one chain, otherwise use all chains */
98562306a36Sopenharmony_ci	if (test_bit(S_POWER_PMI, &il->status)) {
98662306a36Sopenharmony_ci		/* rx_ant has been set to all valid chains previously */
98762306a36Sopenharmony_ci		active_chains =
98862306a36Sopenharmony_ci		    rx_ant & ((u8) (il->chain_noise_data.active_chains));
98962306a36Sopenharmony_ci		if (!active_chains)
99062306a36Sopenharmony_ci			active_chains = rx_ant;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci		D_SCAN("chain_noise_data.active_chains: %u\n",
99362306a36Sopenharmony_ci		       il->chain_noise_data.active_chains);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci		rx_ant = il4965_first_antenna(active_chains);
99662306a36Sopenharmony_ci	}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	/* MIMO is not used here, but value is required */
99962306a36Sopenharmony_ci	rx_chain |= il->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
100062306a36Sopenharmony_ci	rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
100162306a36Sopenharmony_ci	rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
100262306a36Sopenharmony_ci	rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
100362306a36Sopenharmony_ci	scan->rx_chain = cpu_to_le16(rx_chain);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	cmd_len =
100662306a36Sopenharmony_ci	    il_fill_probe_req(il, (struct ieee80211_mgmt *)scan->data,
100762306a36Sopenharmony_ci			      vif->addr, il->scan_request->ie,
100862306a36Sopenharmony_ci			      il->scan_request->ie_len,
100962306a36Sopenharmony_ci			      IL_MAX_SCAN_SIZE - sizeof(*scan));
101062306a36Sopenharmony_ci	scan->tx_cmd.len = cpu_to_le16(cmd_len);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	scan->filter_flags |=
101362306a36Sopenharmony_ci	    (RXON_FILTER_ACCEPT_GRP_MSK | RXON_FILTER_BCON_AWARE_MSK);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	scan->channel_count =
101662306a36Sopenharmony_ci	    il4965_get_channels_for_scan(il, vif, band, is_active, n_probes,
101762306a36Sopenharmony_ci					 (void *)&scan->data[cmd_len]);
101862306a36Sopenharmony_ci	if (scan->channel_count == 0) {
101962306a36Sopenharmony_ci		D_SCAN("channel count %d\n", scan->channel_count);
102062306a36Sopenharmony_ci		return -EIO;
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	cmd.len +=
102462306a36Sopenharmony_ci	    le16_to_cpu(scan->tx_cmd.len) +
102562306a36Sopenharmony_ci	    scan->channel_count * sizeof(struct il_scan_channel);
102662306a36Sopenharmony_ci	cmd.data = scan;
102762306a36Sopenharmony_ci	scan->len = cpu_to_le16(cmd.len);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	set_bit(S_SCAN_HW, &il->status);
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	ret = il_send_cmd_sync(il, &cmd);
103262306a36Sopenharmony_ci	if (ret)
103362306a36Sopenharmony_ci		clear_bit(S_SCAN_HW, &il->status);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	return ret;
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ciint
103962306a36Sopenharmony_ciil4965_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif,
104062306a36Sopenharmony_ci			   bool add)
104162306a36Sopenharmony_ci{
104262306a36Sopenharmony_ci	struct il_vif_priv *vif_priv = (void *)vif->drv_priv;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	if (add)
104562306a36Sopenharmony_ci		return il4965_add_bssid_station(il, vif->bss_conf.bssid,
104662306a36Sopenharmony_ci						&vif_priv->ibss_bssid_sta_id);
104762306a36Sopenharmony_ci	return il_remove_station(il, vif_priv->ibss_bssid_sta_id,
104862306a36Sopenharmony_ci				 vif->bss_conf.bssid);
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_civoid
105262306a36Sopenharmony_ciil4965_free_tfds_in_queue(struct il_priv *il, int sta_id, int tid, int freed)
105362306a36Sopenharmony_ci{
105462306a36Sopenharmony_ci	lockdep_assert_held(&il->sta_lock);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	if (il->stations[sta_id].tid[tid].tfds_in_queue >= freed)
105762306a36Sopenharmony_ci		il->stations[sta_id].tid[tid].tfds_in_queue -= freed;
105862306a36Sopenharmony_ci	else {
105962306a36Sopenharmony_ci		D_TX("free more than tfds_in_queue (%u:%d)\n",
106062306a36Sopenharmony_ci		     il->stations[sta_id].tid[tid].tfds_in_queue, freed);
106162306a36Sopenharmony_ci		il->stations[sta_id].tid[tid].tfds_in_queue = 0;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci#define IL_TX_QUEUE_MSK	0xfffff
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_cistatic bool
106862306a36Sopenharmony_ciil4965_is_single_rx_stream(struct il_priv *il)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	return il->current_ht_config.smps == IEEE80211_SMPS_STATIC ||
107162306a36Sopenharmony_ci	    il->current_ht_config.single_chain_sufficient;
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci#define IL_NUM_RX_CHAINS_MULTIPLE	3
107562306a36Sopenharmony_ci#define IL_NUM_RX_CHAINS_SINGLE	2
107662306a36Sopenharmony_ci#define IL_NUM_IDLE_CHAINS_DUAL	2
107762306a36Sopenharmony_ci#define IL_NUM_IDLE_CHAINS_SINGLE	1
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci/*
108062306a36Sopenharmony_ci * Determine how many receiver/antenna chains to use.
108162306a36Sopenharmony_ci *
108262306a36Sopenharmony_ci * More provides better reception via diversity.  Fewer saves power
108362306a36Sopenharmony_ci * at the expense of throughput, but only when not in powersave to
108462306a36Sopenharmony_ci * start with.
108562306a36Sopenharmony_ci *
108662306a36Sopenharmony_ci * MIMO (dual stream) requires at least 2, but works better with 3.
108762306a36Sopenharmony_ci * This does not determine *which* chains to use, just how many.
108862306a36Sopenharmony_ci */
108962306a36Sopenharmony_cistatic int
109062306a36Sopenharmony_ciil4965_get_active_rx_chain_count(struct il_priv *il)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	/* # of Rx chains to use when expecting MIMO. */
109362306a36Sopenharmony_ci	if (il4965_is_single_rx_stream(il))
109462306a36Sopenharmony_ci		return IL_NUM_RX_CHAINS_SINGLE;
109562306a36Sopenharmony_ci	else
109662306a36Sopenharmony_ci		return IL_NUM_RX_CHAINS_MULTIPLE;
109762306a36Sopenharmony_ci}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci/*
110062306a36Sopenharmony_ci * When we are in power saving mode, unless device support spatial
110162306a36Sopenharmony_ci * multiplexing power save, use the active count for rx chain count.
110262306a36Sopenharmony_ci */
110362306a36Sopenharmony_cistatic int
110462306a36Sopenharmony_ciil4965_get_idle_rx_chain_count(struct il_priv *il, int active_cnt)
110562306a36Sopenharmony_ci{
110662306a36Sopenharmony_ci	/* # Rx chains when idling, depending on SMPS mode */
110762306a36Sopenharmony_ci	switch (il->current_ht_config.smps) {
110862306a36Sopenharmony_ci	case IEEE80211_SMPS_STATIC:
110962306a36Sopenharmony_ci	case IEEE80211_SMPS_DYNAMIC:
111062306a36Sopenharmony_ci		return IL_NUM_IDLE_CHAINS_SINGLE;
111162306a36Sopenharmony_ci	case IEEE80211_SMPS_OFF:
111262306a36Sopenharmony_ci		return active_cnt;
111362306a36Sopenharmony_ci	default:
111462306a36Sopenharmony_ci		WARN(1, "invalid SMPS mode %d", il->current_ht_config.smps);
111562306a36Sopenharmony_ci		return active_cnt;
111662306a36Sopenharmony_ci	}
111762306a36Sopenharmony_ci}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci/* up to 4 chains */
112062306a36Sopenharmony_cistatic u8
112162306a36Sopenharmony_ciil4965_count_chain_bitmap(u32 chain_bitmap)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	u8 res;
112462306a36Sopenharmony_ci	res = (chain_bitmap & BIT(0)) >> 0;
112562306a36Sopenharmony_ci	res += (chain_bitmap & BIT(1)) >> 1;
112662306a36Sopenharmony_ci	res += (chain_bitmap & BIT(2)) >> 2;
112762306a36Sopenharmony_ci	res += (chain_bitmap & BIT(3)) >> 3;
112862306a36Sopenharmony_ci	return res;
112962306a36Sopenharmony_ci}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci/*
113262306a36Sopenharmony_ci * il4965_set_rxon_chain - Set up Rx chain usage in "staging" RXON image
113362306a36Sopenharmony_ci *
113462306a36Sopenharmony_ci * Selects how many and which Rx receivers/antennas/chains to use.
113562306a36Sopenharmony_ci * This should not be used for scan command ... it puts data in wrong place.
113662306a36Sopenharmony_ci */
113762306a36Sopenharmony_civoid
113862306a36Sopenharmony_ciil4965_set_rxon_chain(struct il_priv *il)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	bool is_single = il4965_is_single_rx_stream(il);
114162306a36Sopenharmony_ci	bool is_cam = !test_bit(S_POWER_PMI, &il->status);
114262306a36Sopenharmony_ci	u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt;
114362306a36Sopenharmony_ci	u32 active_chains;
114462306a36Sopenharmony_ci	u16 rx_chain;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	/* Tell uCode which antennas are actually connected.
114762306a36Sopenharmony_ci	 * Before first association, we assume all antennas are connected.
114862306a36Sopenharmony_ci	 * Just after first association, il4965_chain_noise_calibration()
114962306a36Sopenharmony_ci	 *    checks which antennas actually *are* connected. */
115062306a36Sopenharmony_ci	if (il->chain_noise_data.active_chains)
115162306a36Sopenharmony_ci		active_chains = il->chain_noise_data.active_chains;
115262306a36Sopenharmony_ci	else
115362306a36Sopenharmony_ci		active_chains = il->hw_params.valid_rx_ant;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	/* How many receivers should we use? */
115862306a36Sopenharmony_ci	active_rx_cnt = il4965_get_active_rx_chain_count(il);
115962306a36Sopenharmony_ci	idle_rx_cnt = il4965_get_idle_rx_chain_count(il, active_rx_cnt);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	/* correct rx chain count according hw settings
116262306a36Sopenharmony_ci	 * and chain noise calibration
116362306a36Sopenharmony_ci	 */
116462306a36Sopenharmony_ci	valid_rx_cnt = il4965_count_chain_bitmap(active_chains);
116562306a36Sopenharmony_ci	if (valid_rx_cnt < active_rx_cnt)
116662306a36Sopenharmony_ci		active_rx_cnt = valid_rx_cnt;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	if (valid_rx_cnt < idle_rx_cnt)
116962306a36Sopenharmony_ci		idle_rx_cnt = valid_rx_cnt;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS;
117262306a36Sopenharmony_ci	rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	il->staging.rx_chain = cpu_to_le16(rx_chain);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	if (!is_single && active_rx_cnt >= IL_NUM_RX_CHAINS_SINGLE && is_cam)
117762306a36Sopenharmony_ci		il->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK;
117862306a36Sopenharmony_ci	else
117962306a36Sopenharmony_ci		il->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	D_ASSOC("rx_chain=0x%X active=%d idle=%d\n", il->staging.rx_chain,
118262306a36Sopenharmony_ci		active_rx_cnt, idle_rx_cnt);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 ||
118562306a36Sopenharmony_ci		active_rx_cnt < idle_rx_cnt);
118662306a36Sopenharmony_ci}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_cistatic const char *
118962306a36Sopenharmony_ciil4965_get_fh_string(int cmd)
119062306a36Sopenharmony_ci{
119162306a36Sopenharmony_ci	switch (cmd) {
119262306a36Sopenharmony_ci		IL_CMD(FH49_RSCSR_CHNL0_STTS_WPTR_REG);
119362306a36Sopenharmony_ci		IL_CMD(FH49_RSCSR_CHNL0_RBDCB_BASE_REG);
119462306a36Sopenharmony_ci		IL_CMD(FH49_RSCSR_CHNL0_WPTR);
119562306a36Sopenharmony_ci		IL_CMD(FH49_MEM_RCSR_CHNL0_CONFIG_REG);
119662306a36Sopenharmony_ci		IL_CMD(FH49_MEM_RSSR_SHARED_CTRL_REG);
119762306a36Sopenharmony_ci		IL_CMD(FH49_MEM_RSSR_RX_STATUS_REG);
119862306a36Sopenharmony_ci		IL_CMD(FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
119962306a36Sopenharmony_ci		IL_CMD(FH49_TSSR_TX_STATUS_REG);
120062306a36Sopenharmony_ci		IL_CMD(FH49_TSSR_TX_ERROR_REG);
120162306a36Sopenharmony_ci	default:
120262306a36Sopenharmony_ci		return "UNKNOWN";
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ciint
120762306a36Sopenharmony_ciil4965_dump_fh(struct il_priv *il, char **buf, bool display)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	int i;
121062306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
121162306a36Sopenharmony_ci	int pos = 0;
121262306a36Sopenharmony_ci	size_t bufsz = 0;
121362306a36Sopenharmony_ci#endif
121462306a36Sopenharmony_ci	static const u32 fh_tbl[] = {
121562306a36Sopenharmony_ci		FH49_RSCSR_CHNL0_STTS_WPTR_REG,
121662306a36Sopenharmony_ci		FH49_RSCSR_CHNL0_RBDCB_BASE_REG,
121762306a36Sopenharmony_ci		FH49_RSCSR_CHNL0_WPTR,
121862306a36Sopenharmony_ci		FH49_MEM_RCSR_CHNL0_CONFIG_REG,
121962306a36Sopenharmony_ci		FH49_MEM_RSSR_SHARED_CTRL_REG,
122062306a36Sopenharmony_ci		FH49_MEM_RSSR_RX_STATUS_REG,
122162306a36Sopenharmony_ci		FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
122262306a36Sopenharmony_ci		FH49_TSSR_TX_STATUS_REG,
122362306a36Sopenharmony_ci		FH49_TSSR_TX_ERROR_REG
122462306a36Sopenharmony_ci	};
122562306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
122662306a36Sopenharmony_ci	if (display) {
122762306a36Sopenharmony_ci		bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
122862306a36Sopenharmony_ci		*buf = kmalloc(bufsz, GFP_KERNEL);
122962306a36Sopenharmony_ci		if (!*buf)
123062306a36Sopenharmony_ci			return -ENOMEM;
123162306a36Sopenharmony_ci		pos +=
123262306a36Sopenharmony_ci		    scnprintf(*buf + pos, bufsz - pos, "FH register values:\n");
123362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
123462306a36Sopenharmony_ci			pos +=
123562306a36Sopenharmony_ci			    scnprintf(*buf + pos, bufsz - pos,
123662306a36Sopenharmony_ci				      "  %34s: 0X%08x\n",
123762306a36Sopenharmony_ci				      il4965_get_fh_string(fh_tbl[i]),
123862306a36Sopenharmony_ci				      il_rd(il, fh_tbl[i]));
123962306a36Sopenharmony_ci		}
124062306a36Sopenharmony_ci		return pos;
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci#endif
124362306a36Sopenharmony_ci	IL_ERR("FH register values:\n");
124462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
124562306a36Sopenharmony_ci		IL_ERR("  %34s: 0X%08x\n", il4965_get_fh_string(fh_tbl[i]),
124662306a36Sopenharmony_ci		       il_rd(il, fh_tbl[i]));
124762306a36Sopenharmony_ci	}
124862306a36Sopenharmony_ci	return 0;
124962306a36Sopenharmony_ci}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_cistatic void
125262306a36Sopenharmony_ciil4965_hdl_missed_beacon(struct il_priv *il, struct il_rx_buf *rxb)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
125562306a36Sopenharmony_ci	struct il_missed_beacon_notif *missed_beacon;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	missed_beacon = &pkt->u.missed_beacon;
125862306a36Sopenharmony_ci	if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) >
125962306a36Sopenharmony_ci	    il->missed_beacon_threshold) {
126062306a36Sopenharmony_ci		D_CALIB("missed bcn cnsq %d totl %d rcd %d expctd %d\n",
126162306a36Sopenharmony_ci			le32_to_cpu(missed_beacon->consecutive_missed_beacons),
126262306a36Sopenharmony_ci			le32_to_cpu(missed_beacon->total_missed_becons),
126362306a36Sopenharmony_ci			le32_to_cpu(missed_beacon->num_recvd_beacons),
126462306a36Sopenharmony_ci			le32_to_cpu(missed_beacon->num_expected_beacons));
126562306a36Sopenharmony_ci		if (!test_bit(S_SCANNING, &il->status))
126662306a36Sopenharmony_ci			il4965_init_sensitivity(il);
126762306a36Sopenharmony_ci	}
126862306a36Sopenharmony_ci}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci/* Calculate noise level, based on measurements during network silence just
127162306a36Sopenharmony_ci *   before arriving beacon.  This measurement can be done only if we know
127262306a36Sopenharmony_ci *   exactly when to expect beacons, therefore only when we're associated. */
127362306a36Sopenharmony_cistatic void
127462306a36Sopenharmony_ciil4965_rx_calc_noise(struct il_priv *il)
127562306a36Sopenharmony_ci{
127662306a36Sopenharmony_ci	struct stats_rx_non_phy *rx_info;
127762306a36Sopenharmony_ci	int num_active_rx = 0;
127862306a36Sopenharmony_ci	int total_silence = 0;
127962306a36Sopenharmony_ci	int bcn_silence_a, bcn_silence_b, bcn_silence_c;
128062306a36Sopenharmony_ci	int last_rx_noise;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	rx_info = &(il->_4965.stats.rx.general);
128362306a36Sopenharmony_ci	bcn_silence_a =
128462306a36Sopenharmony_ci	    le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER;
128562306a36Sopenharmony_ci	bcn_silence_b =
128662306a36Sopenharmony_ci	    le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER;
128762306a36Sopenharmony_ci	bcn_silence_c =
128862306a36Sopenharmony_ci	    le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	if (bcn_silence_a) {
129162306a36Sopenharmony_ci		total_silence += bcn_silence_a;
129262306a36Sopenharmony_ci		num_active_rx++;
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci	if (bcn_silence_b) {
129562306a36Sopenharmony_ci		total_silence += bcn_silence_b;
129662306a36Sopenharmony_ci		num_active_rx++;
129762306a36Sopenharmony_ci	}
129862306a36Sopenharmony_ci	if (bcn_silence_c) {
129962306a36Sopenharmony_ci		total_silence += bcn_silence_c;
130062306a36Sopenharmony_ci		num_active_rx++;
130162306a36Sopenharmony_ci	}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	/* Average among active antennas */
130462306a36Sopenharmony_ci	if (num_active_rx)
130562306a36Sopenharmony_ci		last_rx_noise = (total_silence / num_active_rx) - 107;
130662306a36Sopenharmony_ci	else
130762306a36Sopenharmony_ci		last_rx_noise = IL_NOISE_MEAS_NOT_AVAILABLE;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	D_CALIB("inband silence a %u, b %u, c %u, dBm %d\n", bcn_silence_a,
131062306a36Sopenharmony_ci		bcn_silence_b, bcn_silence_c, last_rx_noise);
131162306a36Sopenharmony_ci}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUGFS
131462306a36Sopenharmony_ci/*
131562306a36Sopenharmony_ci *  based on the assumption of all stats counter are in DWORD
131662306a36Sopenharmony_ci *  FIXME: This function is for debugging, do not deal with
131762306a36Sopenharmony_ci *  the case of counters roll-over.
131862306a36Sopenharmony_ci */
131962306a36Sopenharmony_cistatic void
132062306a36Sopenharmony_ciil4965_accumulative_stats(struct il_priv *il, __le32 * stats)
132162306a36Sopenharmony_ci{
132262306a36Sopenharmony_ci	int i, size;
132362306a36Sopenharmony_ci	__le32 *prev_stats;
132462306a36Sopenharmony_ci	u32 *accum_stats;
132562306a36Sopenharmony_ci	u32 *delta, *max_delta;
132662306a36Sopenharmony_ci	struct stats_general_common *general, *accum_general;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	prev_stats = (__le32 *) &il->_4965.stats;
132962306a36Sopenharmony_ci	accum_stats = (u32 *) &il->_4965.accum_stats;
133062306a36Sopenharmony_ci	size = sizeof(struct il_notif_stats);
133162306a36Sopenharmony_ci	general = &il->_4965.stats.general.common;
133262306a36Sopenharmony_ci	accum_general = &il->_4965.accum_stats.general.common;
133362306a36Sopenharmony_ci	delta = (u32 *) &il->_4965.delta_stats;
133462306a36Sopenharmony_ci	max_delta = (u32 *) &il->_4965.max_delta;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	for (i = sizeof(__le32); i < size;
133762306a36Sopenharmony_ci	     i +=
133862306a36Sopenharmony_ci	     sizeof(__le32), stats++, prev_stats++, delta++, max_delta++,
133962306a36Sopenharmony_ci	     accum_stats++) {
134062306a36Sopenharmony_ci		if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) {
134162306a36Sopenharmony_ci			*delta =
134262306a36Sopenharmony_ci			    (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats));
134362306a36Sopenharmony_ci			*accum_stats += *delta;
134462306a36Sopenharmony_ci			if (*delta > *max_delta)
134562306a36Sopenharmony_ci				*max_delta = *delta;
134662306a36Sopenharmony_ci		}
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	/* reset accumulative stats for "no-counter" type stats */
135062306a36Sopenharmony_ci	accum_general->temperature = general->temperature;
135162306a36Sopenharmony_ci	accum_general->ttl_timestamp = general->ttl_timestamp;
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci#endif
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_cistatic void
135662306a36Sopenharmony_ciil4965_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb)
135762306a36Sopenharmony_ci{
135862306a36Sopenharmony_ci	const int recalib_seconds = 60;
135962306a36Sopenharmony_ci	bool change;
136062306a36Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	D_RX("Statistics notification received (%d vs %d).\n",
136362306a36Sopenharmony_ci	     (int)sizeof(struct il_notif_stats),
136462306a36Sopenharmony_ci	     le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	change =
136762306a36Sopenharmony_ci	    ((il->_4965.stats.general.common.temperature !=
136862306a36Sopenharmony_ci	      pkt->u.stats.general.common.temperature) ||
136962306a36Sopenharmony_ci	     ((il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK) !=
137062306a36Sopenharmony_ci	      (pkt->u.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK)));
137162306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUGFS
137262306a36Sopenharmony_ci	il4965_accumulative_stats(il, (__le32 *) &pkt->u.stats);
137362306a36Sopenharmony_ci#endif
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	/* TODO: reading some of stats is unneeded */
137662306a36Sopenharmony_ci	memcpy(&il->_4965.stats, &pkt->u.stats, sizeof(il->_4965.stats));
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	set_bit(S_STATS, &il->status);
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	/*
138162306a36Sopenharmony_ci	 * Reschedule the stats timer to occur in recalib_seconds to ensure
138262306a36Sopenharmony_ci	 * we get a thermal update even if the uCode doesn't give us one
138362306a36Sopenharmony_ci	 */
138462306a36Sopenharmony_ci	mod_timer(&il->stats_periodic,
138562306a36Sopenharmony_ci		  jiffies + msecs_to_jiffies(recalib_seconds * 1000));
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	if (unlikely(!test_bit(S_SCANNING, &il->status)) &&
138862306a36Sopenharmony_ci	    (pkt->hdr.cmd == N_STATS)) {
138962306a36Sopenharmony_ci		il4965_rx_calc_noise(il);
139062306a36Sopenharmony_ci		queue_work(il->workqueue, &il->run_time_calib_work);
139162306a36Sopenharmony_ci	}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	if (change)
139462306a36Sopenharmony_ci		il4965_temperature_calib(il);
139562306a36Sopenharmony_ci}
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_cistatic void
139862306a36Sopenharmony_ciil4965_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb)
139962306a36Sopenharmony_ci{
140062306a36Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATS_CLEAR_MSK) {
140362306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUGFS
140462306a36Sopenharmony_ci		memset(&il->_4965.accum_stats, 0,
140562306a36Sopenharmony_ci		       sizeof(struct il_notif_stats));
140662306a36Sopenharmony_ci		memset(&il->_4965.delta_stats, 0,
140762306a36Sopenharmony_ci		       sizeof(struct il_notif_stats));
140862306a36Sopenharmony_ci		memset(&il->_4965.max_delta, 0, sizeof(struct il_notif_stats));
140962306a36Sopenharmony_ci#endif
141062306a36Sopenharmony_ci		D_RX("Statistics have been cleared\n");
141162306a36Sopenharmony_ci	}
141262306a36Sopenharmony_ci	il4965_hdl_stats(il, rxb);
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci/*
141762306a36Sopenharmony_ci * mac80211 queues, ACs, hardware queues, FIFOs.
141862306a36Sopenharmony_ci *
141962306a36Sopenharmony_ci * Cf. https://wireless.wiki.kernel.org/en/developers/Documentation/mac80211/queues
142062306a36Sopenharmony_ci *
142162306a36Sopenharmony_ci * Mac80211 uses the following numbers, which we get as from it
142262306a36Sopenharmony_ci * by way of skb_get_queue_mapping(skb):
142362306a36Sopenharmony_ci *
142462306a36Sopenharmony_ci *     VO      0
142562306a36Sopenharmony_ci *     VI      1
142662306a36Sopenharmony_ci *     BE      2
142762306a36Sopenharmony_ci *     BK      3
142862306a36Sopenharmony_ci *
142962306a36Sopenharmony_ci *
143062306a36Sopenharmony_ci * Regular (not A-MPDU) frames are put into hardware queues corresponding
143162306a36Sopenharmony_ci * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their
143262306a36Sopenharmony_ci * own queue per aggregation session (RA/TID combination), such queues are
143362306a36Sopenharmony_ci * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In
143462306a36Sopenharmony_ci * order to map frames to the right queue, we also need an AC->hw queue
143562306a36Sopenharmony_ci * mapping. This is implemented here.
143662306a36Sopenharmony_ci *
143762306a36Sopenharmony_ci * Due to the way hw queues are set up (by the hw specific modules like
143862306a36Sopenharmony_ci * 4965.c), the AC->hw queue mapping is the identity
143962306a36Sopenharmony_ci * mapping.
144062306a36Sopenharmony_ci */
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_cistatic const u8 tid_to_ac[] = {
144362306a36Sopenharmony_ci	IEEE80211_AC_BE,
144462306a36Sopenharmony_ci	IEEE80211_AC_BK,
144562306a36Sopenharmony_ci	IEEE80211_AC_BK,
144662306a36Sopenharmony_ci	IEEE80211_AC_BE,
144762306a36Sopenharmony_ci	IEEE80211_AC_VI,
144862306a36Sopenharmony_ci	IEEE80211_AC_VI,
144962306a36Sopenharmony_ci	IEEE80211_AC_VO,
145062306a36Sopenharmony_ci	IEEE80211_AC_VO
145162306a36Sopenharmony_ci};
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic inline int
145462306a36Sopenharmony_ciil4965_get_ac_from_tid(u16 tid)
145562306a36Sopenharmony_ci{
145662306a36Sopenharmony_ci	if (likely(tid < ARRAY_SIZE(tid_to_ac)))
145762306a36Sopenharmony_ci		return tid_to_ac[tid];
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	/* no support for TIDs 8-15 yet */
146062306a36Sopenharmony_ci	return -EINVAL;
146162306a36Sopenharmony_ci}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_cistatic inline int
146462306a36Sopenharmony_ciil4965_get_fifo_from_tid(u16 tid)
146562306a36Sopenharmony_ci{
146662306a36Sopenharmony_ci	static const u8 ac_to_fifo[] = {
146762306a36Sopenharmony_ci		IL_TX_FIFO_VO,
146862306a36Sopenharmony_ci		IL_TX_FIFO_VI,
146962306a36Sopenharmony_ci		IL_TX_FIFO_BE,
147062306a36Sopenharmony_ci		IL_TX_FIFO_BK,
147162306a36Sopenharmony_ci	};
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	if (likely(tid < ARRAY_SIZE(tid_to_ac)))
147462306a36Sopenharmony_ci		return ac_to_fifo[tid_to_ac[tid]];
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	/* no support for TIDs 8-15 yet */
147762306a36Sopenharmony_ci	return -EINVAL;
147862306a36Sopenharmony_ci}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci/*
148162306a36Sopenharmony_ci * handle build C_TX command notification.
148262306a36Sopenharmony_ci */
148362306a36Sopenharmony_cistatic void
148462306a36Sopenharmony_ciil4965_tx_cmd_build_basic(struct il_priv *il, struct sk_buff *skb,
148562306a36Sopenharmony_ci			  struct il_tx_cmd *tx_cmd,
148662306a36Sopenharmony_ci			  struct ieee80211_tx_info *info,
148762306a36Sopenharmony_ci			  struct ieee80211_hdr *hdr, u8 std_id)
148862306a36Sopenharmony_ci{
148962306a36Sopenharmony_ci	__le16 fc = hdr->frame_control;
149062306a36Sopenharmony_ci	__le32 tx_flags = tx_cmd->tx_flags;
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
149362306a36Sopenharmony_ci	if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
149462306a36Sopenharmony_ci		tx_flags |= TX_CMD_FLG_ACK_MSK;
149562306a36Sopenharmony_ci		if (ieee80211_is_mgmt(fc))
149662306a36Sopenharmony_ci			tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
149762306a36Sopenharmony_ci		if (ieee80211_is_probe_resp(fc) &&
149862306a36Sopenharmony_ci		    !(le16_to_cpu(hdr->seq_ctrl) & 0xf))
149962306a36Sopenharmony_ci			tx_flags |= TX_CMD_FLG_TSF_MSK;
150062306a36Sopenharmony_ci	} else {
150162306a36Sopenharmony_ci		tx_flags &= (~TX_CMD_FLG_ACK_MSK);
150262306a36Sopenharmony_ci		tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
150362306a36Sopenharmony_ci	}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	if (ieee80211_is_back_req(fc))
150662306a36Sopenharmony_ci		tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	tx_cmd->sta_id = std_id;
150962306a36Sopenharmony_ci	if (ieee80211_has_morefrags(fc))
151062306a36Sopenharmony_ci		tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	if (ieee80211_is_data_qos(fc)) {
151362306a36Sopenharmony_ci		u8 *qc = ieee80211_get_qos_ctl(hdr);
151462306a36Sopenharmony_ci		tx_cmd->tid_tspec = qc[0] & 0xf;
151562306a36Sopenharmony_ci		tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
151662306a36Sopenharmony_ci	} else {
151762306a36Sopenharmony_ci		tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
151862306a36Sopenharmony_ci	}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	il_tx_cmd_protection(il, info, fc, &tx_flags);
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
152362306a36Sopenharmony_ci	if (ieee80211_is_mgmt(fc)) {
152462306a36Sopenharmony_ci		if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
152562306a36Sopenharmony_ci			tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3);
152662306a36Sopenharmony_ci		else
152762306a36Sopenharmony_ci			tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2);
152862306a36Sopenharmony_ci	} else {
152962306a36Sopenharmony_ci		tx_cmd->timeout.pm_frame_timeout = 0;
153062306a36Sopenharmony_ci	}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	tx_cmd->driver_txop = 0;
153362306a36Sopenharmony_ci	tx_cmd->tx_flags = tx_flags;
153462306a36Sopenharmony_ci	tx_cmd->next_frame_len = 0;
153562306a36Sopenharmony_ci}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_cistatic void
153862306a36Sopenharmony_ciil4965_tx_cmd_build_rate(struct il_priv *il,
153962306a36Sopenharmony_ci			 struct il_tx_cmd *tx_cmd,
154062306a36Sopenharmony_ci			 struct ieee80211_tx_info *info,
154162306a36Sopenharmony_ci			 struct ieee80211_sta *sta,
154262306a36Sopenharmony_ci			 __le16 fc)
154362306a36Sopenharmony_ci{
154462306a36Sopenharmony_ci	const u8 rts_retry_limit = 60;
154562306a36Sopenharmony_ci	u32 rate_flags;
154662306a36Sopenharmony_ci	int rate_idx;
154762306a36Sopenharmony_ci	u8 data_retry_limit;
154862306a36Sopenharmony_ci	u8 rate_plcp;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	/* Set retry limit on DATA packets and Probe Responses */
155162306a36Sopenharmony_ci	if (ieee80211_is_probe_resp(fc))
155262306a36Sopenharmony_ci		data_retry_limit = 3;
155362306a36Sopenharmony_ci	else
155462306a36Sopenharmony_ci		data_retry_limit = IL4965_DEFAULT_TX_RETRY;
155562306a36Sopenharmony_ci	tx_cmd->data_retry_limit = data_retry_limit;
155662306a36Sopenharmony_ci	/* Set retry limit on RTS packets */
155762306a36Sopenharmony_ci	tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit);
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	/* DATA packets will use the uCode station table for rate/antenna
156062306a36Sopenharmony_ci	 * selection */
156162306a36Sopenharmony_ci	if (ieee80211_is_data(fc)) {
156262306a36Sopenharmony_ci		tx_cmd->initial_rate_idx = 0;
156362306a36Sopenharmony_ci		tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
156462306a36Sopenharmony_ci		return;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	/**
156862306a36Sopenharmony_ci	 * If the current TX rate stored in mac80211 has the MCS bit set, it's
156962306a36Sopenharmony_ci	 * not really a TX rate.  Thus, we use the lowest supported rate for
157062306a36Sopenharmony_ci	 * this band.  Also use the lowest supported rate if the stored rate
157162306a36Sopenharmony_ci	 * idx is invalid.
157262306a36Sopenharmony_ci	 */
157362306a36Sopenharmony_ci	rate_idx = info->control.rates[0].idx;
157462306a36Sopenharmony_ci	if ((info->control.rates[0].flags & IEEE80211_TX_RC_MCS) || rate_idx < 0
157562306a36Sopenharmony_ci	    || rate_idx > RATE_COUNT_LEGACY)
157662306a36Sopenharmony_ci		rate_idx = rate_lowest_index(&il->bands[info->band], sta);
157762306a36Sopenharmony_ci	/* For 5 GHZ band, remap mac80211 rate indices into driver indices */
157862306a36Sopenharmony_ci	if (info->band == NL80211_BAND_5GHZ)
157962306a36Sopenharmony_ci		rate_idx += IL_FIRST_OFDM_RATE;
158062306a36Sopenharmony_ci	/* Get PLCP rate for tx_cmd->rate_n_flags */
158162306a36Sopenharmony_ci	rate_plcp = il_rates[rate_idx].plcp;
158262306a36Sopenharmony_ci	/* Zero out flags for this packet */
158362306a36Sopenharmony_ci	rate_flags = 0;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	/* Set CCK flag as needed */
158662306a36Sopenharmony_ci	if (rate_idx >= IL_FIRST_CCK_RATE && rate_idx <= IL_LAST_CCK_RATE)
158762306a36Sopenharmony_ci		rate_flags |= RATE_MCS_CCK_MSK;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	/* Set up antennas */
159062306a36Sopenharmony_ci	il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant);
159162306a36Sopenharmony_ci	rate_flags |= BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	/* Set the rate in the TX cmd */
159462306a36Sopenharmony_ci	tx_cmd->rate_n_flags = cpu_to_le32(rate_plcp | rate_flags);
159562306a36Sopenharmony_ci}
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_cistatic void
159862306a36Sopenharmony_ciil4965_tx_cmd_build_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info,
159962306a36Sopenharmony_ci			     struct il_tx_cmd *tx_cmd, struct sk_buff *skb_frag,
160062306a36Sopenharmony_ci			     int sta_id)
160162306a36Sopenharmony_ci{
160262306a36Sopenharmony_ci	struct ieee80211_key_conf *keyconf = info->control.hw_key;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	switch (keyconf->cipher) {
160562306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
160662306a36Sopenharmony_ci		tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
160762306a36Sopenharmony_ci		memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
160862306a36Sopenharmony_ci		if (info->flags & IEEE80211_TX_CTL_AMPDU)
160962306a36Sopenharmony_ci			tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK;
161062306a36Sopenharmony_ci		D_TX("tx_cmd with AES hwcrypto\n");
161162306a36Sopenharmony_ci		break;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
161462306a36Sopenharmony_ci		tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
161562306a36Sopenharmony_ci		ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
161662306a36Sopenharmony_ci		D_TX("tx_cmd with tkip hwcrypto\n");
161762306a36Sopenharmony_ci		break;
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
162062306a36Sopenharmony_ci		tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
162162306a36Sopenharmony_ci		fallthrough;
162262306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
162362306a36Sopenharmony_ci		tx_cmd->sec_ctl |=
162462306a36Sopenharmony_ci		    (TX_CMD_SEC_WEP | (keyconf->keyidx & TX_CMD_SEC_MSK) <<
162562306a36Sopenharmony_ci		     TX_CMD_SEC_SHIFT);
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci		memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci		D_TX("Configuring packet for WEP encryption " "with key %d\n",
163062306a36Sopenharmony_ci		     keyconf->keyidx);
163162306a36Sopenharmony_ci		break;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	default:
163462306a36Sopenharmony_ci		IL_ERR("Unknown encode cipher %x\n", keyconf->cipher);
163562306a36Sopenharmony_ci		break;
163662306a36Sopenharmony_ci	}
163762306a36Sopenharmony_ci}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci/*
164062306a36Sopenharmony_ci * start C_TX command process
164162306a36Sopenharmony_ci */
164262306a36Sopenharmony_ciint
164362306a36Sopenharmony_ciil4965_tx_skb(struct il_priv *il,
164462306a36Sopenharmony_ci	      struct ieee80211_sta *sta,
164562306a36Sopenharmony_ci	      struct sk_buff *skb)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
164862306a36Sopenharmony_ci	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
164962306a36Sopenharmony_ci	struct il_station_priv *sta_priv = NULL;
165062306a36Sopenharmony_ci	struct il_tx_queue *txq;
165162306a36Sopenharmony_ci	struct il_queue *q;
165262306a36Sopenharmony_ci	struct il_device_cmd *out_cmd;
165362306a36Sopenharmony_ci	struct il_cmd_meta *out_meta;
165462306a36Sopenharmony_ci	struct il_tx_cmd *tx_cmd;
165562306a36Sopenharmony_ci	int txq_id;
165662306a36Sopenharmony_ci	dma_addr_t phys_addr;
165762306a36Sopenharmony_ci	dma_addr_t txcmd_phys;
165862306a36Sopenharmony_ci	dma_addr_t scratch_phys;
165962306a36Sopenharmony_ci	u16 len, firstlen, secondlen;
166062306a36Sopenharmony_ci	u16 seq_number = 0;
166162306a36Sopenharmony_ci	__le16 fc;
166262306a36Sopenharmony_ci	u8 hdr_len;
166362306a36Sopenharmony_ci	u8 sta_id;
166462306a36Sopenharmony_ci	u8 wait_write_ptr = 0;
166562306a36Sopenharmony_ci	u8 tid = 0;
166662306a36Sopenharmony_ci	u8 *qc = NULL;
166762306a36Sopenharmony_ci	unsigned long flags;
166862306a36Sopenharmony_ci	bool is_agg = false;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
167162306a36Sopenharmony_ci	if (il_is_rfkill(il)) {
167262306a36Sopenharmony_ci		D_DROP("Dropping - RF KILL\n");
167362306a36Sopenharmony_ci		goto drop_unlock;
167462306a36Sopenharmony_ci	}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	fc = hdr->frame_control;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
167962306a36Sopenharmony_ci	if (ieee80211_is_auth(fc))
168062306a36Sopenharmony_ci		D_TX("Sending AUTH frame\n");
168162306a36Sopenharmony_ci	else if (ieee80211_is_assoc_req(fc))
168262306a36Sopenharmony_ci		D_TX("Sending ASSOC frame\n");
168362306a36Sopenharmony_ci	else if (ieee80211_is_reassoc_req(fc))
168462306a36Sopenharmony_ci		D_TX("Sending REASSOC frame\n");
168562306a36Sopenharmony_ci#endif
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	hdr_len = ieee80211_hdrlen(fc);
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	/* For management frames use broadcast id to do not break aggregation */
169062306a36Sopenharmony_ci	if (!ieee80211_is_data(fc))
169162306a36Sopenharmony_ci		sta_id = il->hw_params.bcast_id;
169262306a36Sopenharmony_ci	else {
169362306a36Sopenharmony_ci		/* Find idx into station table for destination station */
169462306a36Sopenharmony_ci		sta_id = il_sta_id_or_broadcast(il, sta);
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci		if (sta_id == IL_INVALID_STATION) {
169762306a36Sopenharmony_ci			D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1);
169862306a36Sopenharmony_ci			goto drop_unlock;
169962306a36Sopenharmony_ci		}
170062306a36Sopenharmony_ci	}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	D_TX("station Id %d\n", sta_id);
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	if (sta)
170562306a36Sopenharmony_ci		sta_priv = (void *)sta->drv_priv;
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	if (sta_priv && sta_priv->asleep &&
170862306a36Sopenharmony_ci	    (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) {
170962306a36Sopenharmony_ci		/*
171062306a36Sopenharmony_ci		 * This sends an asynchronous command to the device,
171162306a36Sopenharmony_ci		 * but we can rely on it being processed before the
171262306a36Sopenharmony_ci		 * next frame is processed -- and the next frame to
171362306a36Sopenharmony_ci		 * this station is the one that will consume this
171462306a36Sopenharmony_ci		 * counter.
171562306a36Sopenharmony_ci		 * For now set the counter to just 1 since we do not
171662306a36Sopenharmony_ci		 * support uAPSD yet.
171762306a36Sopenharmony_ci		 */
171862306a36Sopenharmony_ci		il4965_sta_modify_sleep_tx_count(il, sta_id, 1);
171962306a36Sopenharmony_ci	}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	/* FIXME: remove me ? */
172262306a36Sopenharmony_ci	WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	/* Access category (AC) is also the queue number */
172562306a36Sopenharmony_ci	txq_id = skb_get_queue_mapping(skb);
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	/* irqs already disabled/saved above when locking il->lock */
172862306a36Sopenharmony_ci	spin_lock(&il->sta_lock);
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	if (ieee80211_is_data_qos(fc)) {
173162306a36Sopenharmony_ci		qc = ieee80211_get_qos_ctl(hdr);
173262306a36Sopenharmony_ci		tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
173362306a36Sopenharmony_ci		if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) {
173462306a36Sopenharmony_ci			spin_unlock(&il->sta_lock);
173562306a36Sopenharmony_ci			goto drop_unlock;
173662306a36Sopenharmony_ci		}
173762306a36Sopenharmony_ci		seq_number = il->stations[sta_id].tid[tid].seq_number;
173862306a36Sopenharmony_ci		seq_number &= IEEE80211_SCTL_SEQ;
173962306a36Sopenharmony_ci		hdr->seq_ctrl =
174062306a36Sopenharmony_ci		    hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG);
174162306a36Sopenharmony_ci		hdr->seq_ctrl |= cpu_to_le16(seq_number);
174262306a36Sopenharmony_ci		seq_number += 0x10;
174362306a36Sopenharmony_ci		/* aggregation is on for this <sta,tid> */
174462306a36Sopenharmony_ci		if (info->flags & IEEE80211_TX_CTL_AMPDU &&
174562306a36Sopenharmony_ci		    il->stations[sta_id].tid[tid].agg.state == IL_AGG_ON) {
174662306a36Sopenharmony_ci			txq_id = il->stations[sta_id].tid[tid].agg.txq_id;
174762306a36Sopenharmony_ci			is_agg = true;
174862306a36Sopenharmony_ci		}
174962306a36Sopenharmony_ci	}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	txq = &il->txq[txq_id];
175262306a36Sopenharmony_ci	q = &txq->q;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	if (unlikely(il_queue_space(q) < q->high_mark)) {
175562306a36Sopenharmony_ci		spin_unlock(&il->sta_lock);
175662306a36Sopenharmony_ci		goto drop_unlock;
175762306a36Sopenharmony_ci	}
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	if (ieee80211_is_data_qos(fc)) {
176062306a36Sopenharmony_ci		il->stations[sta_id].tid[tid].tfds_in_queue++;
176162306a36Sopenharmony_ci		if (!ieee80211_has_morefrags(fc))
176262306a36Sopenharmony_ci			il->stations[sta_id].tid[tid].seq_number = seq_number;
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	spin_unlock(&il->sta_lock);
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	txq->skbs[q->write_ptr] = skb;
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	/* Set up first empty entry in queue's array of Tx/cmd buffers */
177062306a36Sopenharmony_ci	out_cmd = txq->cmd[q->write_ptr];
177162306a36Sopenharmony_ci	out_meta = &txq->meta[q->write_ptr];
177262306a36Sopenharmony_ci	tx_cmd = &out_cmd->cmd.tx;
177362306a36Sopenharmony_ci	memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr));
177462306a36Sopenharmony_ci	memset(tx_cmd, 0, sizeof(struct il_tx_cmd));
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	/*
177762306a36Sopenharmony_ci	 * Set up the Tx-command (not MAC!) header.
177862306a36Sopenharmony_ci	 * Store the chosen Tx queue and TFD idx within the sequence field;
177962306a36Sopenharmony_ci	 * after Tx, uCode's Tx response will return this value so driver can
178062306a36Sopenharmony_ci	 * locate the frame within the tx queue and do post-tx processing.
178162306a36Sopenharmony_ci	 */
178262306a36Sopenharmony_ci	out_cmd->hdr.cmd = C_TX;
178362306a36Sopenharmony_ci	out_cmd->hdr.sequence =
178462306a36Sopenharmony_ci	    cpu_to_le16((u16)
178562306a36Sopenharmony_ci			(QUEUE_TO_SEQ(txq_id) | IDX_TO_SEQ(q->write_ptr)));
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	/* Copy MAC header from skb into command buffer */
178862306a36Sopenharmony_ci	memcpy(tx_cmd->hdr, hdr, hdr_len);
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	/* Total # bytes to be transmitted */
179162306a36Sopenharmony_ci	tx_cmd->len = cpu_to_le16((u16) skb->len);
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	if (info->control.hw_key)
179462306a36Sopenharmony_ci		il4965_tx_cmd_build_hwcrypto(il, info, tx_cmd, skb, sta_id);
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	/* TODO need this for burst mode later on */
179762306a36Sopenharmony_ci	il4965_tx_cmd_build_basic(il, skb, tx_cmd, info, hdr, sta_id);
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	il4965_tx_cmd_build_rate(il, tx_cmd, info, sta, fc);
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	/*
180262306a36Sopenharmony_ci	 * Use the first empty entry in this queue's command buffer array
180362306a36Sopenharmony_ci	 * to contain the Tx command and MAC header concatenated together
180462306a36Sopenharmony_ci	 * (payload data will be in another buffer).
180562306a36Sopenharmony_ci	 * Size of this varies, due to varying MAC header length.
180662306a36Sopenharmony_ci	 * If end is not dword aligned, we'll have 2 extra bytes at the end
180762306a36Sopenharmony_ci	 * of the MAC header (device reads on dword boundaries).
180862306a36Sopenharmony_ci	 * We'll tell device about this padding later.
180962306a36Sopenharmony_ci	 */
181062306a36Sopenharmony_ci	len = sizeof(struct il_tx_cmd) + sizeof(struct il_cmd_header) + hdr_len;
181162306a36Sopenharmony_ci	firstlen = (len + 3) & ~3;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	/* Tell NIC about any 2-byte padding after MAC header */
181462306a36Sopenharmony_ci	if (firstlen != len)
181562306a36Sopenharmony_ci		tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	/* Physical address of this Tx command's header (not MAC header!),
181862306a36Sopenharmony_ci	 * within command buffer array. */
181962306a36Sopenharmony_ci	txcmd_phys = dma_map_single(&il->pci_dev->dev, &out_cmd->hdr, firstlen,
182062306a36Sopenharmony_ci				    DMA_BIDIRECTIONAL);
182162306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(&il->pci_dev->dev, txcmd_phys)))
182262306a36Sopenharmony_ci		goto drop_unlock;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	/* Set up TFD's 2nd entry to point directly to remainder of skb,
182562306a36Sopenharmony_ci	 * if any (802.11 null frames have no payload). */
182662306a36Sopenharmony_ci	secondlen = skb->len - hdr_len;
182762306a36Sopenharmony_ci	if (secondlen > 0) {
182862306a36Sopenharmony_ci		phys_addr = dma_map_single(&il->pci_dev->dev, skb->data + hdr_len,
182962306a36Sopenharmony_ci					   secondlen, DMA_TO_DEVICE);
183062306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(&il->pci_dev->dev, phys_addr)))
183162306a36Sopenharmony_ci			goto drop_unlock;
183262306a36Sopenharmony_ci	}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	/* Add buffer containing Tx command and MAC(!) header to TFD's
183562306a36Sopenharmony_ci	 * first entry */
183662306a36Sopenharmony_ci	il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, firstlen, 1, 0);
183762306a36Sopenharmony_ci	dma_unmap_addr_set(out_meta, mapping, txcmd_phys);
183862306a36Sopenharmony_ci	dma_unmap_len_set(out_meta, len, firstlen);
183962306a36Sopenharmony_ci	if (secondlen)
184062306a36Sopenharmony_ci		il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, secondlen,
184162306a36Sopenharmony_ci					       0, 0);
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	if (!ieee80211_has_morefrags(hdr->frame_control)) {
184462306a36Sopenharmony_ci		txq->need_update = 1;
184562306a36Sopenharmony_ci	} else {
184662306a36Sopenharmony_ci		wait_write_ptr = 1;
184762306a36Sopenharmony_ci		txq->need_update = 0;
184862306a36Sopenharmony_ci	}
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	scratch_phys =
185162306a36Sopenharmony_ci	    txcmd_phys + sizeof(struct il_cmd_header) +
185262306a36Sopenharmony_ci	    offsetof(struct il_tx_cmd, scratch);
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	/* take back ownership of DMA buffer to enable update */
185562306a36Sopenharmony_ci	dma_sync_single_for_cpu(&il->pci_dev->dev, txcmd_phys, firstlen,
185662306a36Sopenharmony_ci				DMA_BIDIRECTIONAL);
185762306a36Sopenharmony_ci	tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
185862306a36Sopenharmony_ci	tx_cmd->dram_msb_ptr = il_get_dma_hi_addr(scratch_phys);
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	il_update_stats(il, true, fc, skb->len);
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	D_TX("sequence nr = 0X%x\n", le16_to_cpu(out_cmd->hdr.sequence));
186362306a36Sopenharmony_ci	D_TX("tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags));
186462306a36Sopenharmony_ci	il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd, sizeof(*tx_cmd));
186562306a36Sopenharmony_ci	il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd->hdr, hdr_len);
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	/* Set up entry for this TFD in Tx byte-count array */
186862306a36Sopenharmony_ci	if (info->flags & IEEE80211_TX_CTL_AMPDU)
186962306a36Sopenharmony_ci		il->ops->txq_update_byte_cnt_tbl(il, txq, le16_to_cpu(tx_cmd->len));
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	dma_sync_single_for_device(&il->pci_dev->dev, txcmd_phys, firstlen,
187262306a36Sopenharmony_ci				   DMA_BIDIRECTIONAL);
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	/* Tell device the write idx *just past* this latest filled TFD */
187562306a36Sopenharmony_ci	q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd);
187662306a36Sopenharmony_ci	il_txq_update_write_ptr(il, txq);
187762306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	/*
188062306a36Sopenharmony_ci	 * At this point the frame is "transmitted" successfully
188162306a36Sopenharmony_ci	 * and we will get a TX status notification eventually,
188262306a36Sopenharmony_ci	 * regardless of the value of ret. "ret" only indicates
188362306a36Sopenharmony_ci	 * whether or not we should update the write pointer.
188462306a36Sopenharmony_ci	 */
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	/*
188762306a36Sopenharmony_ci	 * Avoid atomic ops if it isn't an associated client.
188862306a36Sopenharmony_ci	 * Also, if this is a packet for aggregation, don't
188962306a36Sopenharmony_ci	 * increase the counter because the ucode will stop
189062306a36Sopenharmony_ci	 * aggregation queues when their respective station
189162306a36Sopenharmony_ci	 * goes to sleep.
189262306a36Sopenharmony_ci	 */
189362306a36Sopenharmony_ci	if (sta_priv && sta_priv->client && !is_agg)
189462306a36Sopenharmony_ci		atomic_inc(&sta_priv->pending_frames);
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci	if (il_queue_space(q) < q->high_mark && il->mac80211_registered) {
189762306a36Sopenharmony_ci		if (wait_write_ptr) {
189862306a36Sopenharmony_ci			spin_lock_irqsave(&il->lock, flags);
189962306a36Sopenharmony_ci			txq->need_update = 1;
190062306a36Sopenharmony_ci			il_txq_update_write_ptr(il, txq);
190162306a36Sopenharmony_ci			spin_unlock_irqrestore(&il->lock, flags);
190262306a36Sopenharmony_ci		} else {
190362306a36Sopenharmony_ci			il_stop_queue(il, txq);
190462306a36Sopenharmony_ci		}
190562306a36Sopenharmony_ci	}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	return 0;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_cidrop_unlock:
191062306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
191162306a36Sopenharmony_ci	return -1;
191262306a36Sopenharmony_ci}
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_cistatic inline int
191562306a36Sopenharmony_ciil4965_alloc_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr, size_t size)
191662306a36Sopenharmony_ci{
191762306a36Sopenharmony_ci	ptr->addr = dma_alloc_coherent(&il->pci_dev->dev, size, &ptr->dma,
191862306a36Sopenharmony_ci				       GFP_KERNEL);
191962306a36Sopenharmony_ci	if (!ptr->addr)
192062306a36Sopenharmony_ci		return -ENOMEM;
192162306a36Sopenharmony_ci	ptr->size = size;
192262306a36Sopenharmony_ci	return 0;
192362306a36Sopenharmony_ci}
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_cistatic inline void
192662306a36Sopenharmony_ciil4965_free_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr)
192762306a36Sopenharmony_ci{
192862306a36Sopenharmony_ci	if (unlikely(!ptr->addr))
192962306a36Sopenharmony_ci		return;
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	dma_free_coherent(&il->pci_dev->dev, ptr->size, ptr->addr, ptr->dma);
193262306a36Sopenharmony_ci	memset(ptr, 0, sizeof(*ptr));
193362306a36Sopenharmony_ci}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci/*
193662306a36Sopenharmony_ci * il4965_hw_txq_ctx_free - Free TXQ Context
193762306a36Sopenharmony_ci *
193862306a36Sopenharmony_ci * Destroy all TX DMA queues and structures
193962306a36Sopenharmony_ci */
194062306a36Sopenharmony_civoid
194162306a36Sopenharmony_ciil4965_hw_txq_ctx_free(struct il_priv *il)
194262306a36Sopenharmony_ci{
194362306a36Sopenharmony_ci	int txq_id;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	/* Tx queues */
194662306a36Sopenharmony_ci	if (il->txq) {
194762306a36Sopenharmony_ci		for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
194862306a36Sopenharmony_ci			if (txq_id == il->cmd_queue)
194962306a36Sopenharmony_ci				il_cmd_queue_free(il);
195062306a36Sopenharmony_ci			else
195162306a36Sopenharmony_ci				il_tx_queue_free(il, txq_id);
195262306a36Sopenharmony_ci	}
195362306a36Sopenharmony_ci	il4965_free_dma_ptr(il, &il->kw);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	il4965_free_dma_ptr(il, &il->scd_bc_tbls);
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	/* free tx queue structure */
195862306a36Sopenharmony_ci	il_free_txq_mem(il);
195962306a36Sopenharmony_ci}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci/*
196262306a36Sopenharmony_ci * il4965_txq_ctx_alloc - allocate TX queue context
196362306a36Sopenharmony_ci * Allocate all Tx DMA structures and initialize them
196462306a36Sopenharmony_ci */
196562306a36Sopenharmony_ciint
196662306a36Sopenharmony_ciil4965_txq_ctx_alloc(struct il_priv *il)
196762306a36Sopenharmony_ci{
196862306a36Sopenharmony_ci	int ret, txq_id;
196962306a36Sopenharmony_ci	unsigned long flags;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	/* Free all tx/cmd queues and keep-warm buffer */
197262306a36Sopenharmony_ci	il4965_hw_txq_ctx_free(il);
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	ret =
197562306a36Sopenharmony_ci	    il4965_alloc_dma_ptr(il, &il->scd_bc_tbls,
197662306a36Sopenharmony_ci				 il->hw_params.scd_bc_tbls_size);
197762306a36Sopenharmony_ci	if (ret) {
197862306a36Sopenharmony_ci		IL_ERR("Scheduler BC Table allocation failed\n");
197962306a36Sopenharmony_ci		goto error_bc_tbls;
198062306a36Sopenharmony_ci	}
198162306a36Sopenharmony_ci	/* Alloc keep-warm buffer */
198262306a36Sopenharmony_ci	ret = il4965_alloc_dma_ptr(il, &il->kw, IL_KW_SIZE);
198362306a36Sopenharmony_ci	if (ret) {
198462306a36Sopenharmony_ci		IL_ERR("Keep Warm allocation failed\n");
198562306a36Sopenharmony_ci		goto error_kw;
198662306a36Sopenharmony_ci	}
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	/* allocate tx queue structure */
198962306a36Sopenharmony_ci	ret = il_alloc_txq_mem(il);
199062306a36Sopenharmony_ci	if (ret)
199162306a36Sopenharmony_ci		goto error;
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	/* Turn off all Tx DMA fifos */
199662306a36Sopenharmony_ci	il4965_txq_set_sched(il, 0);
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	/* Tell NIC where to find the "keep warm" buffer */
199962306a36Sopenharmony_ci	il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4);
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	/* Alloc and init all Tx queues, including the command queue (#4/#9) */
200462306a36Sopenharmony_ci	for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) {
200562306a36Sopenharmony_ci		ret = il_tx_queue_init(il, txq_id);
200662306a36Sopenharmony_ci		if (ret) {
200762306a36Sopenharmony_ci			IL_ERR("Tx %d queue init failed\n", txq_id);
200862306a36Sopenharmony_ci			goto error;
200962306a36Sopenharmony_ci		}
201062306a36Sopenharmony_ci	}
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	return ret;
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_cierror:
201562306a36Sopenharmony_ci	il4965_hw_txq_ctx_free(il);
201662306a36Sopenharmony_ci	il4965_free_dma_ptr(il, &il->kw);
201762306a36Sopenharmony_cierror_kw:
201862306a36Sopenharmony_ci	il4965_free_dma_ptr(il, &il->scd_bc_tbls);
201962306a36Sopenharmony_cierror_bc_tbls:
202062306a36Sopenharmony_ci	return ret;
202162306a36Sopenharmony_ci}
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_civoid
202462306a36Sopenharmony_ciil4965_txq_ctx_reset(struct il_priv *il)
202562306a36Sopenharmony_ci{
202662306a36Sopenharmony_ci	int txq_id;
202762306a36Sopenharmony_ci	unsigned long flags;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	/* Turn off all Tx DMA fifos */
203262306a36Sopenharmony_ci	il4965_txq_set_sched(il, 0);
203362306a36Sopenharmony_ci	/* Tell NIC where to find the "keep warm" buffer */
203462306a36Sopenharmony_ci	il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4);
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	/* Alloc and init all Tx queues, including the command queue (#4) */
203962306a36Sopenharmony_ci	for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
204062306a36Sopenharmony_ci		il_tx_queue_reset(il, txq_id);
204162306a36Sopenharmony_ci}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_cistatic void
204462306a36Sopenharmony_ciil4965_txq_ctx_unmap(struct il_priv *il)
204562306a36Sopenharmony_ci{
204662306a36Sopenharmony_ci	int txq_id;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	if (!il->txq)
204962306a36Sopenharmony_ci		return;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	/* Unmap DMA from host system and free skb's */
205262306a36Sopenharmony_ci	for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
205362306a36Sopenharmony_ci		if (txq_id == il->cmd_queue)
205462306a36Sopenharmony_ci			il_cmd_queue_unmap(il);
205562306a36Sopenharmony_ci		else
205662306a36Sopenharmony_ci			il_tx_queue_unmap(il, txq_id);
205762306a36Sopenharmony_ci}
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci/*
206062306a36Sopenharmony_ci * il4965_txq_ctx_stop - Stop all Tx DMA channels
206162306a36Sopenharmony_ci */
206262306a36Sopenharmony_civoid
206362306a36Sopenharmony_ciil4965_txq_ctx_stop(struct il_priv *il)
206462306a36Sopenharmony_ci{
206562306a36Sopenharmony_ci	int ch, ret;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	_il_wr_prph(il, IL49_SCD_TXFACT, 0);
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	/* Stop each Tx DMA channel, and wait for it to be idle */
207062306a36Sopenharmony_ci	for (ch = 0; ch < il->hw_params.dma_chnl_num; ch++) {
207162306a36Sopenharmony_ci		_il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
207262306a36Sopenharmony_ci		ret =
207362306a36Sopenharmony_ci		    _il_poll_bit(il, FH49_TSSR_TX_STATUS_REG,
207462306a36Sopenharmony_ci				 FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
207562306a36Sopenharmony_ci				 FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
207662306a36Sopenharmony_ci				 1000);
207762306a36Sopenharmony_ci		if (ret < 0)
207862306a36Sopenharmony_ci			IL_ERR("Timeout stopping DMA channel %d [0x%08x]",
207962306a36Sopenharmony_ci			       ch, _il_rd(il, FH49_TSSR_TX_STATUS_REG));
208062306a36Sopenharmony_ci	}
208162306a36Sopenharmony_ci}
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci/*
208462306a36Sopenharmony_ci * Find first available (lowest unused) Tx Queue, mark it "active".
208562306a36Sopenharmony_ci * Called only when finding queue for aggregation.
208662306a36Sopenharmony_ci * Should never return anything < 7, because they should already
208762306a36Sopenharmony_ci * be in use as EDCA AC (0-3), Command (4), reserved (5, 6)
208862306a36Sopenharmony_ci */
208962306a36Sopenharmony_cistatic int
209062306a36Sopenharmony_ciil4965_txq_ctx_activate_free(struct il_priv *il)
209162306a36Sopenharmony_ci{
209262306a36Sopenharmony_ci	int txq_id;
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
209562306a36Sopenharmony_ci		if (!test_and_set_bit(txq_id, &il->txq_ctx_active_msk))
209662306a36Sopenharmony_ci			return txq_id;
209762306a36Sopenharmony_ci	return -1;
209862306a36Sopenharmony_ci}
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci/*
210162306a36Sopenharmony_ci * il4965_tx_queue_stop_scheduler - Stop queue, but keep configuration
210262306a36Sopenharmony_ci */
210362306a36Sopenharmony_cistatic void
210462306a36Sopenharmony_ciil4965_tx_queue_stop_scheduler(struct il_priv *il, u16 txq_id)
210562306a36Sopenharmony_ci{
210662306a36Sopenharmony_ci	/* Simply stop the queue, but don't change any configuration;
210762306a36Sopenharmony_ci	 * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
210862306a36Sopenharmony_ci	il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id),
210962306a36Sopenharmony_ci		   (0 << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
211062306a36Sopenharmony_ci		   (1 << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci/*
211462306a36Sopenharmony_ci * il4965_tx_queue_set_q2ratid - Map unique receiver/tid combination to a queue
211562306a36Sopenharmony_ci */
211662306a36Sopenharmony_cistatic int
211762306a36Sopenharmony_ciil4965_tx_queue_set_q2ratid(struct il_priv *il, u16 ra_tid, u16 txq_id)
211862306a36Sopenharmony_ci{
211962306a36Sopenharmony_ci	u32 tbl_dw_addr;
212062306a36Sopenharmony_ci	u32 tbl_dw;
212162306a36Sopenharmony_ci	u16 scd_q2ratid;
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	scd_q2ratid = ra_tid & IL_SCD_QUEUE_RA_TID_MAP_RATID_MSK;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	tbl_dw_addr =
212662306a36Sopenharmony_ci	    il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id);
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	tbl_dw = il_read_targ_mem(il, tbl_dw_addr);
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	if (txq_id & 0x1)
213162306a36Sopenharmony_ci		tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
213262306a36Sopenharmony_ci	else
213362306a36Sopenharmony_ci		tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	il_write_targ_mem(il, tbl_dw_addr, tbl_dw);
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	return 0;
213862306a36Sopenharmony_ci}
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci/*
214162306a36Sopenharmony_ci * il4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue
214262306a36Sopenharmony_ci *
214362306a36Sopenharmony_ci * NOTE:  txq_id must be greater than IL49_FIRST_AMPDU_QUEUE,
214462306a36Sopenharmony_ci *        i.e. it must be one of the higher queues used for aggregation
214562306a36Sopenharmony_ci */
214662306a36Sopenharmony_cistatic int
214762306a36Sopenharmony_ciil4965_txq_agg_enable(struct il_priv *il, int txq_id, int tx_fifo, int sta_id,
214862306a36Sopenharmony_ci		      int tid, u16 ssn_idx)
214962306a36Sopenharmony_ci{
215062306a36Sopenharmony_ci	unsigned long flags;
215162306a36Sopenharmony_ci	u16 ra_tid;
215262306a36Sopenharmony_ci	int ret;
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	if ((IL49_FIRST_AMPDU_QUEUE > txq_id) ||
215562306a36Sopenharmony_ci	    (IL49_FIRST_AMPDU_QUEUE +
215662306a36Sopenharmony_ci	     il->cfg->num_of_ampdu_queues <= txq_id)) {
215762306a36Sopenharmony_ci		IL_WARN("queue number out of range: %d, must be %d to %d\n",
215862306a36Sopenharmony_ci			txq_id, IL49_FIRST_AMPDU_QUEUE,
215962306a36Sopenharmony_ci			IL49_FIRST_AMPDU_QUEUE +
216062306a36Sopenharmony_ci			il->cfg->num_of_ampdu_queues - 1);
216162306a36Sopenharmony_ci		return -EINVAL;
216262306a36Sopenharmony_ci	}
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	ra_tid = BUILD_RAxTID(sta_id, tid);
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	/* Modify device's station table to Tx this TID */
216762306a36Sopenharmony_ci	ret = il4965_sta_tx_modify_enable_tid(il, sta_id, tid);
216862306a36Sopenharmony_ci	if (ret)
216962306a36Sopenharmony_ci		return ret;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	/* Stop this Tx queue before configuring it */
217462306a36Sopenharmony_ci	il4965_tx_queue_stop_scheduler(il, txq_id);
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	/* Map receiver-address / traffic-ID to this queue */
217762306a36Sopenharmony_ci	il4965_tx_queue_set_q2ratid(il, ra_tid, txq_id);
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	/* Set this queue as a chain-building queue */
218062306a36Sopenharmony_ci	il_set_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id));
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	/* Place first TFD at idx corresponding to start sequence number.
218362306a36Sopenharmony_ci	 * Assumes that ssn_idx is valid (!= 0xFFF) */
218462306a36Sopenharmony_ci	il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
218562306a36Sopenharmony_ci	il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
218662306a36Sopenharmony_ci	il4965_set_wr_ptrs(il, txq_id, ssn_idx);
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_ci	/* Set up Tx win size and frame limit for this queue */
218962306a36Sopenharmony_ci	il_write_targ_mem(il,
219062306a36Sopenharmony_ci			  il->scd_base_addr +
219162306a36Sopenharmony_ci			  IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id),
219262306a36Sopenharmony_ci			  (SCD_WIN_SIZE << IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS)
219362306a36Sopenharmony_ci			  & IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK);
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	il_write_targ_mem(il,
219662306a36Sopenharmony_ci			  il->scd_base_addr +
219762306a36Sopenharmony_ci			  IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32),
219862306a36Sopenharmony_ci			  (SCD_FRAME_LIMIT <<
219962306a36Sopenharmony_ci			   IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
220062306a36Sopenharmony_ci			  IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK);
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	il_set_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id));
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	/* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
220562306a36Sopenharmony_ci	il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 1);
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	return 0;
221062306a36Sopenharmony_ci}
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ciint
221362306a36Sopenharmony_ciil4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif,
221462306a36Sopenharmony_ci		    struct ieee80211_sta *sta, u16 tid, u16 * ssn)
221562306a36Sopenharmony_ci{
221662306a36Sopenharmony_ci	int sta_id;
221762306a36Sopenharmony_ci	int tx_fifo;
221862306a36Sopenharmony_ci	int txq_id;
221962306a36Sopenharmony_ci	int ret;
222062306a36Sopenharmony_ci	unsigned long flags;
222162306a36Sopenharmony_ci	struct il_tid_data *tid_data;
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	/* FIXME: warning if tx fifo not found ? */
222462306a36Sopenharmony_ci	tx_fifo = il4965_get_fifo_from_tid(tid);
222562306a36Sopenharmony_ci	if (unlikely(tx_fifo < 0))
222662306a36Sopenharmony_ci		return tx_fifo;
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	D_HT("%s on ra = %pM tid = %d\n", __func__, sta->addr, tid);
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci	sta_id = il_sta_id(sta);
223162306a36Sopenharmony_ci	if (sta_id == IL_INVALID_STATION) {
223262306a36Sopenharmony_ci		IL_ERR("Start AGG on invalid station\n");
223362306a36Sopenharmony_ci		return -ENXIO;
223462306a36Sopenharmony_ci	}
223562306a36Sopenharmony_ci	if (unlikely(tid >= MAX_TID_COUNT))
223662306a36Sopenharmony_ci		return -EINVAL;
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	if (il->stations[sta_id].tid[tid].agg.state != IL_AGG_OFF) {
223962306a36Sopenharmony_ci		IL_ERR("Start AGG when state is not IL_AGG_OFF !\n");
224062306a36Sopenharmony_ci		return -ENXIO;
224162306a36Sopenharmony_ci	}
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	txq_id = il4965_txq_ctx_activate_free(il);
224462306a36Sopenharmony_ci	if (txq_id == -1) {
224562306a36Sopenharmony_ci		IL_ERR("No free aggregation queue available\n");
224662306a36Sopenharmony_ci		return -ENXIO;
224762306a36Sopenharmony_ci	}
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
225062306a36Sopenharmony_ci	tid_data = &il->stations[sta_id].tid[tid];
225162306a36Sopenharmony_ci	*ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
225262306a36Sopenharmony_ci	tid_data->agg.txq_id = txq_id;
225362306a36Sopenharmony_ci	il_set_swq_id(&il->txq[txq_id], il4965_get_ac_from_tid(tid), txq_id);
225462306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	ret = il4965_txq_agg_enable(il, txq_id, tx_fifo, sta_id, tid, *ssn);
225762306a36Sopenharmony_ci	if (ret)
225862306a36Sopenharmony_ci		return ret;
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
226162306a36Sopenharmony_ci	tid_data = &il->stations[sta_id].tid[tid];
226262306a36Sopenharmony_ci	if (tid_data->tfds_in_queue == 0) {
226362306a36Sopenharmony_ci		D_HT("HW queue is empty\n");
226462306a36Sopenharmony_ci		tid_data->agg.state = IL_AGG_ON;
226562306a36Sopenharmony_ci		ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
226662306a36Sopenharmony_ci	} else {
226762306a36Sopenharmony_ci		D_HT("HW queue is NOT empty: %d packets in HW queue\n",
226862306a36Sopenharmony_ci		     tid_data->tfds_in_queue);
226962306a36Sopenharmony_ci		tid_data->agg.state = IL_EMPTYING_HW_QUEUE_ADDBA;
227062306a36Sopenharmony_ci	}
227162306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
227262306a36Sopenharmony_ci	return ret;
227362306a36Sopenharmony_ci}
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci/*
227662306a36Sopenharmony_ci * txq_id must be greater than IL49_FIRST_AMPDU_QUEUE
227762306a36Sopenharmony_ci * il->lock must be held by the caller
227862306a36Sopenharmony_ci */
227962306a36Sopenharmony_cistatic int
228062306a36Sopenharmony_ciil4965_txq_agg_disable(struct il_priv *il, u16 txq_id, u16 ssn_idx, u8 tx_fifo)
228162306a36Sopenharmony_ci{
228262306a36Sopenharmony_ci	if ((IL49_FIRST_AMPDU_QUEUE > txq_id) ||
228362306a36Sopenharmony_ci	    (IL49_FIRST_AMPDU_QUEUE +
228462306a36Sopenharmony_ci	     il->cfg->num_of_ampdu_queues <= txq_id)) {
228562306a36Sopenharmony_ci		IL_WARN("queue number out of range: %d, must be %d to %d\n",
228662306a36Sopenharmony_ci			txq_id, IL49_FIRST_AMPDU_QUEUE,
228762306a36Sopenharmony_ci			IL49_FIRST_AMPDU_QUEUE +
228862306a36Sopenharmony_ci			il->cfg->num_of_ampdu_queues - 1);
228962306a36Sopenharmony_ci		return -EINVAL;
229062306a36Sopenharmony_ci	}
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	il4965_tx_queue_stop_scheduler(il, txq_id);
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	il_clear_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id));
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
229762306a36Sopenharmony_ci	il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
229862306a36Sopenharmony_ci	/* supposes that ssn_idx is valid (!= 0xFFF) */
229962306a36Sopenharmony_ci	il4965_set_wr_ptrs(il, txq_id, ssn_idx);
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	il_clear_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id));
230262306a36Sopenharmony_ci	il_txq_ctx_deactivate(il, txq_id);
230362306a36Sopenharmony_ci	il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 0);
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	return 0;
230662306a36Sopenharmony_ci}
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ciint
230962306a36Sopenharmony_ciil4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif,
231062306a36Sopenharmony_ci		   struct ieee80211_sta *sta, u16 tid)
231162306a36Sopenharmony_ci{
231262306a36Sopenharmony_ci	int tx_fifo_id, txq_id, sta_id, ssn;
231362306a36Sopenharmony_ci	struct il_tid_data *tid_data;
231462306a36Sopenharmony_ci	int write_ptr, read_ptr;
231562306a36Sopenharmony_ci	unsigned long flags;
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	/* FIXME: warning if tx_fifo_id not found ? */
231862306a36Sopenharmony_ci	tx_fifo_id = il4965_get_fifo_from_tid(tid);
231962306a36Sopenharmony_ci	if (unlikely(tx_fifo_id < 0))
232062306a36Sopenharmony_ci		return tx_fifo_id;
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci	sta_id = il_sta_id(sta);
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	if (sta_id == IL_INVALID_STATION) {
232562306a36Sopenharmony_ci		IL_ERR("Invalid station for AGG tid %d\n", tid);
232662306a36Sopenharmony_ci		return -ENXIO;
232762306a36Sopenharmony_ci	}
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_ci	tid_data = &il->stations[sta_id].tid[tid];
233262306a36Sopenharmony_ci	ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4;
233362306a36Sopenharmony_ci	txq_id = tid_data->agg.txq_id;
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	switch (il->stations[sta_id].tid[tid].agg.state) {
233662306a36Sopenharmony_ci	case IL_EMPTYING_HW_QUEUE_ADDBA:
233762306a36Sopenharmony_ci		/*
233862306a36Sopenharmony_ci		 * This can happen if the peer stops aggregation
233962306a36Sopenharmony_ci		 * again before we've had a chance to drain the
234062306a36Sopenharmony_ci		 * queue we selected previously, i.e. before the
234162306a36Sopenharmony_ci		 * session was really started completely.
234262306a36Sopenharmony_ci		 */
234362306a36Sopenharmony_ci		D_HT("AGG stop before setup done\n");
234462306a36Sopenharmony_ci		goto turn_off;
234562306a36Sopenharmony_ci	case IL_AGG_ON:
234662306a36Sopenharmony_ci		break;
234762306a36Sopenharmony_ci	default:
234862306a36Sopenharmony_ci		IL_WARN("Stopping AGG while state not ON or starting\n");
234962306a36Sopenharmony_ci	}
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci	write_ptr = il->txq[txq_id].q.write_ptr;
235262306a36Sopenharmony_ci	read_ptr = il->txq[txq_id].q.read_ptr;
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	/* The queue is not empty */
235562306a36Sopenharmony_ci	if (write_ptr != read_ptr) {
235662306a36Sopenharmony_ci		D_HT("Stopping a non empty AGG HW QUEUE\n");
235762306a36Sopenharmony_ci		il->stations[sta_id].tid[tid].agg.state =
235862306a36Sopenharmony_ci		    IL_EMPTYING_HW_QUEUE_DELBA;
235962306a36Sopenharmony_ci		spin_unlock_irqrestore(&il->sta_lock, flags);
236062306a36Sopenharmony_ci		return 0;
236162306a36Sopenharmony_ci	}
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	D_HT("HW queue is empty\n");
236462306a36Sopenharmony_citurn_off:
236562306a36Sopenharmony_ci	il->stations[sta_id].tid[tid].agg.state = IL_AGG_OFF;
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci	/* do not restore/save irqs */
236862306a36Sopenharmony_ci	spin_unlock(&il->sta_lock);
236962306a36Sopenharmony_ci	spin_lock(&il->lock);
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	/*
237262306a36Sopenharmony_ci	 * the only reason this call can fail is queue number out of range,
237362306a36Sopenharmony_ci	 * which can happen if uCode is reloaded and all the station
237462306a36Sopenharmony_ci	 * information are lost. if it is outside the range, there is no need
237562306a36Sopenharmony_ci	 * to deactivate the uCode queue, just return "success" to allow
237662306a36Sopenharmony_ci	 *  mac80211 to clean up it own data.
237762306a36Sopenharmony_ci	 */
237862306a36Sopenharmony_ci	il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo_id);
237962306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	return 0;
238462306a36Sopenharmony_ci}
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ciint
238762306a36Sopenharmony_ciil4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id)
238862306a36Sopenharmony_ci{
238962306a36Sopenharmony_ci	struct il_queue *q = &il->txq[txq_id].q;
239062306a36Sopenharmony_ci	u8 *addr = il->stations[sta_id].sta.sta.addr;
239162306a36Sopenharmony_ci	struct il_tid_data *tid_data = &il->stations[sta_id].tid[tid];
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci	lockdep_assert_held(&il->sta_lock);
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	switch (il->stations[sta_id].tid[tid].agg.state) {
239662306a36Sopenharmony_ci	case IL_EMPTYING_HW_QUEUE_DELBA:
239762306a36Sopenharmony_ci		/* We are reclaiming the last packet of the */
239862306a36Sopenharmony_ci		/* aggregated HW queue */
239962306a36Sopenharmony_ci		if (txq_id == tid_data->agg.txq_id &&
240062306a36Sopenharmony_ci		    q->read_ptr == q->write_ptr) {
240162306a36Sopenharmony_ci			u16 ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
240262306a36Sopenharmony_ci			int tx_fifo = il4965_get_fifo_from_tid(tid);
240362306a36Sopenharmony_ci			D_HT("HW queue empty: continue DELBA flow\n");
240462306a36Sopenharmony_ci			il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo);
240562306a36Sopenharmony_ci			tid_data->agg.state = IL_AGG_OFF;
240662306a36Sopenharmony_ci			ieee80211_stop_tx_ba_cb_irqsafe(il->vif, addr, tid);
240762306a36Sopenharmony_ci		}
240862306a36Sopenharmony_ci		break;
240962306a36Sopenharmony_ci	case IL_EMPTYING_HW_QUEUE_ADDBA:
241062306a36Sopenharmony_ci		/* We are reclaiming the last packet of the queue */
241162306a36Sopenharmony_ci		if (tid_data->tfds_in_queue == 0) {
241262306a36Sopenharmony_ci			D_HT("HW queue empty: continue ADDBA flow\n");
241362306a36Sopenharmony_ci			tid_data->agg.state = IL_AGG_ON;
241462306a36Sopenharmony_ci			ieee80211_start_tx_ba_cb_irqsafe(il->vif, addr, tid);
241562306a36Sopenharmony_ci		}
241662306a36Sopenharmony_ci		break;
241762306a36Sopenharmony_ci	}
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci	return 0;
242062306a36Sopenharmony_ci}
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_cistatic void
242362306a36Sopenharmony_ciil4965_non_agg_tx_status(struct il_priv *il, const u8 *addr1)
242462306a36Sopenharmony_ci{
242562306a36Sopenharmony_ci	struct ieee80211_sta *sta;
242662306a36Sopenharmony_ci	struct il_station_priv *sta_priv;
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	rcu_read_lock();
242962306a36Sopenharmony_ci	sta = ieee80211_find_sta(il->vif, addr1);
243062306a36Sopenharmony_ci	if (sta) {
243162306a36Sopenharmony_ci		sta_priv = (void *)sta->drv_priv;
243262306a36Sopenharmony_ci		/* avoid atomic ops if this isn't a client */
243362306a36Sopenharmony_ci		if (sta_priv->client &&
243462306a36Sopenharmony_ci		    atomic_dec_return(&sta_priv->pending_frames) == 0)
243562306a36Sopenharmony_ci			ieee80211_sta_block_awake(il->hw, sta, false);
243662306a36Sopenharmony_ci	}
243762306a36Sopenharmony_ci	rcu_read_unlock();
243862306a36Sopenharmony_ci}
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_cistatic void
244162306a36Sopenharmony_ciil4965_tx_status(struct il_priv *il, struct sk_buff *skb, bool is_agg)
244262306a36Sopenharmony_ci{
244362306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	if (!is_agg)
244662306a36Sopenharmony_ci		il4965_non_agg_tx_status(il, hdr->addr1);
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	ieee80211_tx_status_irqsafe(il->hw, skb);
244962306a36Sopenharmony_ci}
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ciint
245262306a36Sopenharmony_ciil4965_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx)
245362306a36Sopenharmony_ci{
245462306a36Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[txq_id];
245562306a36Sopenharmony_ci	struct il_queue *q = &txq->q;
245662306a36Sopenharmony_ci	int nfreed = 0;
245762306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
245862306a36Sopenharmony_ci	struct sk_buff *skb;
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	if (idx >= q->n_bd || il_queue_used(q, idx) == 0) {
246162306a36Sopenharmony_ci		IL_ERR("Read idx for DMA queue txq id (%d), idx %d, "
246262306a36Sopenharmony_ci		       "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd,
246362306a36Sopenharmony_ci		       q->write_ptr, q->read_ptr);
246462306a36Sopenharmony_ci		return 0;
246562306a36Sopenharmony_ci	}
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci	for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx;
246862306a36Sopenharmony_ci	     q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) {
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci		skb = txq->skbs[txq->q.read_ptr];
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci		if (WARN_ON_ONCE(skb == NULL))
247362306a36Sopenharmony_ci			continue;
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci		hdr = (struct ieee80211_hdr *) skb->data;
247662306a36Sopenharmony_ci		if (ieee80211_is_data_qos(hdr->frame_control))
247762306a36Sopenharmony_ci			nfreed++;
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci		il4965_tx_status(il, skb, txq_id >= IL4965_FIRST_AMPDU_QUEUE);
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci		txq->skbs[txq->q.read_ptr] = NULL;
248262306a36Sopenharmony_ci		il->ops->txq_free_tfd(il, txq);
248362306a36Sopenharmony_ci	}
248462306a36Sopenharmony_ci	return nfreed;
248562306a36Sopenharmony_ci}
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci/*
248862306a36Sopenharmony_ci * il4965_tx_status_reply_compressed_ba - Update tx status from block-ack
248962306a36Sopenharmony_ci *
249062306a36Sopenharmony_ci * Go through block-ack's bitmap of ACK'd frames, update driver's record of
249162306a36Sopenharmony_ci * ACK vs. not.  This gets sent to mac80211, then to rate scaling algo.
249262306a36Sopenharmony_ci */
249362306a36Sopenharmony_cistatic int
249462306a36Sopenharmony_ciil4965_tx_status_reply_compressed_ba(struct il_priv *il, struct il_ht_agg *agg,
249562306a36Sopenharmony_ci				     struct il_compressed_ba_resp *ba_resp)
249662306a36Sopenharmony_ci{
249762306a36Sopenharmony_ci	int i, sh, ack;
249862306a36Sopenharmony_ci	u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl);
249962306a36Sopenharmony_ci	u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
250062306a36Sopenharmony_ci	int successes = 0;
250162306a36Sopenharmony_ci	struct ieee80211_tx_info *info;
250262306a36Sopenharmony_ci	u64 bitmap, sent_bitmap;
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci	if (unlikely(!agg->wait_for_ba)) {
250562306a36Sopenharmony_ci		if (unlikely(ba_resp->bitmap))
250662306a36Sopenharmony_ci			IL_ERR("Received BA when not expected\n");
250762306a36Sopenharmony_ci		return -EINVAL;
250862306a36Sopenharmony_ci	}
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	/* Mark that the expected block-ack response arrived */
251162306a36Sopenharmony_ci	agg->wait_for_ba = 0;
251262306a36Sopenharmony_ci	D_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->seq_ctl);
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci	/* Calculate shift to align block-ack bits with our Tx win bits */
251562306a36Sopenharmony_ci	sh = agg->start_idx - SEQ_TO_IDX(seq_ctl >> 4);
251662306a36Sopenharmony_ci	if (sh < 0)		/* tbw something is wrong with indices */
251762306a36Sopenharmony_ci		sh += 0x100;
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	if (agg->frame_count > (64 - sh)) {
252062306a36Sopenharmony_ci		D_TX_REPLY("more frames than bitmap size");
252162306a36Sopenharmony_ci		return -1;
252262306a36Sopenharmony_ci	}
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ci	/* don't use 64-bit values for now */
252562306a36Sopenharmony_ci	bitmap = le64_to_cpu(ba_resp->bitmap) >> sh;
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	/* check for success or failure according to the
252862306a36Sopenharmony_ci	 * transmitted bitmap and block-ack bitmap */
252962306a36Sopenharmony_ci	sent_bitmap = bitmap & agg->bitmap;
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci	/* For each frame attempted in aggregation,
253262306a36Sopenharmony_ci	 * update driver's record of tx frame's status. */
253362306a36Sopenharmony_ci	i = 0;
253462306a36Sopenharmony_ci	while (sent_bitmap) {
253562306a36Sopenharmony_ci		ack = sent_bitmap & 1ULL;
253662306a36Sopenharmony_ci		successes += ack;
253762306a36Sopenharmony_ci		D_TX_REPLY("%s ON i=%d idx=%d raw=%d\n", ack ? "ACK" : "NACK",
253862306a36Sopenharmony_ci			   i, (agg->start_idx + i) & 0xff, agg->start_idx + i);
253962306a36Sopenharmony_ci		sent_bitmap >>= 1;
254062306a36Sopenharmony_ci		++i;
254162306a36Sopenharmony_ci	}
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci	D_TX_REPLY("Bitmap %llx\n", (unsigned long long)bitmap);
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci	info = IEEE80211_SKB_CB(il->txq[scd_flow].skbs[agg->start_idx]);
254662306a36Sopenharmony_ci	memset(&info->status, 0, sizeof(info->status));
254762306a36Sopenharmony_ci	info->flags |= IEEE80211_TX_STAT_ACK;
254862306a36Sopenharmony_ci	info->flags |= IEEE80211_TX_STAT_AMPDU;
254962306a36Sopenharmony_ci	info->status.ampdu_ack_len = successes;
255062306a36Sopenharmony_ci	info->status.ampdu_len = agg->frame_count;
255162306a36Sopenharmony_ci	il4965_hwrate_to_tx_control(il, agg->rate_n_flags, info);
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	return 0;
255462306a36Sopenharmony_ci}
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_cistatic inline bool
255762306a36Sopenharmony_ciil4965_is_tx_success(u32 status)
255862306a36Sopenharmony_ci{
255962306a36Sopenharmony_ci	status &= TX_STATUS_MSK;
256062306a36Sopenharmony_ci	return (status == TX_STATUS_SUCCESS || status == TX_STATUS_DIRECT_DONE);
256162306a36Sopenharmony_ci}
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_cistatic u8
256462306a36Sopenharmony_ciil4965_find_station(struct il_priv *il, const u8 *addr)
256562306a36Sopenharmony_ci{
256662306a36Sopenharmony_ci	int i;
256762306a36Sopenharmony_ci	int start = 0;
256862306a36Sopenharmony_ci	int ret = IL_INVALID_STATION;
256962306a36Sopenharmony_ci	unsigned long flags;
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	if (il->iw_mode == NL80211_IFTYPE_ADHOC)
257262306a36Sopenharmony_ci		start = IL_STA_ID;
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	if (is_broadcast_ether_addr(addr))
257562306a36Sopenharmony_ci		return il->hw_params.bcast_id;
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
257862306a36Sopenharmony_ci	for (i = start; i < il->hw_params.max_stations; i++)
257962306a36Sopenharmony_ci		if (il->stations[i].used &&
258062306a36Sopenharmony_ci		    ether_addr_equal(il->stations[i].sta.sta.addr, addr)) {
258162306a36Sopenharmony_ci			ret = i;
258262306a36Sopenharmony_ci			goto out;
258362306a36Sopenharmony_ci		}
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	D_ASSOC("can not find STA %pM total %d\n", addr, il->num_stations);
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ciout:
258862306a36Sopenharmony_ci	/*
258962306a36Sopenharmony_ci	 * It may be possible that more commands interacting with stations
259062306a36Sopenharmony_ci	 * arrive before we completed processing the adding of
259162306a36Sopenharmony_ci	 * station
259262306a36Sopenharmony_ci	 */
259362306a36Sopenharmony_ci	if (ret != IL_INVALID_STATION &&
259462306a36Sopenharmony_ci	    (!(il->stations[ret].used & IL_STA_UCODE_ACTIVE) ||
259562306a36Sopenharmony_ci	      (il->stations[ret].used & IL_STA_UCODE_INPROGRESS))) {
259662306a36Sopenharmony_ci		IL_ERR("Requested station info for sta %d before ready.\n",
259762306a36Sopenharmony_ci		       ret);
259862306a36Sopenharmony_ci		ret = IL_INVALID_STATION;
259962306a36Sopenharmony_ci	}
260062306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
260162306a36Sopenharmony_ci	return ret;
260262306a36Sopenharmony_ci}
260362306a36Sopenharmony_ci
260462306a36Sopenharmony_cistatic int
260562306a36Sopenharmony_ciil4965_get_ra_sta_id(struct il_priv *il, struct ieee80211_hdr *hdr)
260662306a36Sopenharmony_ci{
260762306a36Sopenharmony_ci	if (il->iw_mode == NL80211_IFTYPE_STATION)
260862306a36Sopenharmony_ci		return IL_AP_ID;
260962306a36Sopenharmony_ci	else {
261062306a36Sopenharmony_ci		u8 *da = ieee80211_get_DA(hdr);
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci		return il4965_find_station(il, da);
261362306a36Sopenharmony_ci	}
261462306a36Sopenharmony_ci}
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_cistatic inline u32
261762306a36Sopenharmony_ciil4965_get_scd_ssn(struct il4965_tx_resp *tx_resp)
261862306a36Sopenharmony_ci{
261962306a36Sopenharmony_ci	return le32_to_cpup(&tx_resp->u.status +
262062306a36Sopenharmony_ci			    tx_resp->frame_count) & IEEE80211_MAX_SN;
262162306a36Sopenharmony_ci}
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_cistatic inline u32
262462306a36Sopenharmony_ciil4965_tx_status_to_mac80211(u32 status)
262562306a36Sopenharmony_ci{
262662306a36Sopenharmony_ci	status &= TX_STATUS_MSK;
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	switch (status) {
262962306a36Sopenharmony_ci	case TX_STATUS_SUCCESS:
263062306a36Sopenharmony_ci	case TX_STATUS_DIRECT_DONE:
263162306a36Sopenharmony_ci		return IEEE80211_TX_STAT_ACK;
263262306a36Sopenharmony_ci	case TX_STATUS_FAIL_DEST_PS:
263362306a36Sopenharmony_ci		return IEEE80211_TX_STAT_TX_FILTERED;
263462306a36Sopenharmony_ci	default:
263562306a36Sopenharmony_ci		return 0;
263662306a36Sopenharmony_ci	}
263762306a36Sopenharmony_ci}
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci/*
264062306a36Sopenharmony_ci * il4965_tx_status_reply_tx - Handle Tx response for frames in aggregation queue
264162306a36Sopenharmony_ci */
264262306a36Sopenharmony_cistatic int
264362306a36Sopenharmony_ciil4965_tx_status_reply_tx(struct il_priv *il, struct il_ht_agg *agg,
264462306a36Sopenharmony_ci			  struct il4965_tx_resp *tx_resp, int txq_id,
264562306a36Sopenharmony_ci			  u16 start_idx)
264662306a36Sopenharmony_ci{
264762306a36Sopenharmony_ci	u16 status;
264862306a36Sopenharmony_ci	struct agg_tx_status *frame_status = tx_resp->u.agg_status;
264962306a36Sopenharmony_ci	struct ieee80211_tx_info *info = NULL;
265062306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = NULL;
265162306a36Sopenharmony_ci	u32 rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
265262306a36Sopenharmony_ci	int i, sh, idx;
265362306a36Sopenharmony_ci	u16 seq;
265462306a36Sopenharmony_ci	if (agg->wait_for_ba)
265562306a36Sopenharmony_ci		D_TX_REPLY("got tx response w/o block-ack\n");
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	agg->frame_count = tx_resp->frame_count;
265862306a36Sopenharmony_ci	agg->start_idx = start_idx;
265962306a36Sopenharmony_ci	agg->rate_n_flags = rate_n_flags;
266062306a36Sopenharmony_ci	agg->bitmap = 0;
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci	/* num frames attempted by Tx command */
266362306a36Sopenharmony_ci	if (agg->frame_count == 1) {
266462306a36Sopenharmony_ci		/* Only one frame was attempted; no block-ack will arrive */
266562306a36Sopenharmony_ci		status = le16_to_cpu(frame_status[0].status);
266662306a36Sopenharmony_ci		idx = start_idx;
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci		D_TX_REPLY("FrameCnt = %d, StartIdx=%d idx=%d\n",
266962306a36Sopenharmony_ci			   agg->frame_count, agg->start_idx, idx);
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci		info = IEEE80211_SKB_CB(il->txq[txq_id].skbs[idx]);
267262306a36Sopenharmony_ci		info->status.rates[0].count = tx_resp->failure_frame + 1;
267362306a36Sopenharmony_ci		info->flags &= ~IEEE80211_TX_CTL_AMPDU;
267462306a36Sopenharmony_ci		info->flags |= il4965_tx_status_to_mac80211(status);
267562306a36Sopenharmony_ci		il4965_hwrate_to_tx_control(il, rate_n_flags, info);
267662306a36Sopenharmony_ci
267762306a36Sopenharmony_ci		D_TX_REPLY("1 Frame 0x%x failure :%d\n", status & 0xff,
267862306a36Sopenharmony_ci			   tx_resp->failure_frame);
267962306a36Sopenharmony_ci		D_TX_REPLY("Rate Info rate_n_flags=%x\n", rate_n_flags);
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci		agg->wait_for_ba = 0;
268262306a36Sopenharmony_ci	} else {
268362306a36Sopenharmony_ci		/* Two or more frames were attempted; expect block-ack */
268462306a36Sopenharmony_ci		u64 bitmap = 0;
268562306a36Sopenharmony_ci		int start = agg->start_idx;
268662306a36Sopenharmony_ci		struct sk_buff *skb;
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_ci		/* Construct bit-map of pending frames within Tx win */
268962306a36Sopenharmony_ci		for (i = 0; i < agg->frame_count; i++) {
269062306a36Sopenharmony_ci			u16 sc;
269162306a36Sopenharmony_ci			status = le16_to_cpu(frame_status[i].status);
269262306a36Sopenharmony_ci			seq = le16_to_cpu(frame_status[i].sequence);
269362306a36Sopenharmony_ci			idx = SEQ_TO_IDX(seq);
269462306a36Sopenharmony_ci			txq_id = SEQ_TO_QUEUE(seq);
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci			if (status &
269762306a36Sopenharmony_ci			    (AGG_TX_STATE_FEW_BYTES_MSK |
269862306a36Sopenharmony_ci			     AGG_TX_STATE_ABORT_MSK))
269962306a36Sopenharmony_ci				continue;
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci			D_TX_REPLY("FrameCnt = %d, txq_id=%d idx=%d\n",
270262306a36Sopenharmony_ci				   agg->frame_count, txq_id, idx);
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci			skb = il->txq[txq_id].skbs[idx];
270562306a36Sopenharmony_ci			if (WARN_ON_ONCE(skb == NULL))
270662306a36Sopenharmony_ci				return -1;
270762306a36Sopenharmony_ci			hdr = (struct ieee80211_hdr *) skb->data;
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci			sc = le16_to_cpu(hdr->seq_ctrl);
271062306a36Sopenharmony_ci			if (idx != (IEEE80211_SEQ_TO_SN(sc) & 0xff)) {
271162306a36Sopenharmony_ci				IL_ERR("BUG_ON idx doesn't match seq control"
271262306a36Sopenharmony_ci				       " idx=%d, seq_idx=%d, seq=%d\n", idx,
271362306a36Sopenharmony_ci				       IEEE80211_SEQ_TO_SN(sc), hdr->seq_ctrl);
271462306a36Sopenharmony_ci				return -1;
271562306a36Sopenharmony_ci			}
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_ci			D_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n", i, idx,
271862306a36Sopenharmony_ci				   IEEE80211_SEQ_TO_SN(sc));
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci			sh = idx - start;
272162306a36Sopenharmony_ci			if (sh > 64) {
272262306a36Sopenharmony_ci				sh = (start - idx) + 0xff;
272362306a36Sopenharmony_ci				bitmap = bitmap << sh;
272462306a36Sopenharmony_ci				sh = 0;
272562306a36Sopenharmony_ci				start = idx;
272662306a36Sopenharmony_ci			} else if (sh < -64)
272762306a36Sopenharmony_ci				sh = 0xff - (start - idx);
272862306a36Sopenharmony_ci			else if (sh < 0) {
272962306a36Sopenharmony_ci				sh = start - idx;
273062306a36Sopenharmony_ci				start = idx;
273162306a36Sopenharmony_ci				bitmap = bitmap << sh;
273262306a36Sopenharmony_ci				sh = 0;
273362306a36Sopenharmony_ci			}
273462306a36Sopenharmony_ci			bitmap |= 1ULL << sh;
273562306a36Sopenharmony_ci			D_TX_REPLY("start=%d bitmap=0x%llx\n", start,
273662306a36Sopenharmony_ci				   (unsigned long long)bitmap);
273762306a36Sopenharmony_ci		}
273862306a36Sopenharmony_ci
273962306a36Sopenharmony_ci		agg->bitmap = bitmap;
274062306a36Sopenharmony_ci		agg->start_idx = start;
274162306a36Sopenharmony_ci		D_TX_REPLY("Frames %d start_idx=%d bitmap=0x%llx\n",
274262306a36Sopenharmony_ci			   agg->frame_count, agg->start_idx,
274362306a36Sopenharmony_ci			   (unsigned long long)agg->bitmap);
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci		if (bitmap)
274662306a36Sopenharmony_ci			agg->wait_for_ba = 1;
274762306a36Sopenharmony_ci	}
274862306a36Sopenharmony_ci	return 0;
274962306a36Sopenharmony_ci}
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci/*
275262306a36Sopenharmony_ci * il4965_hdl_tx - Handle standard (non-aggregation) Tx response
275362306a36Sopenharmony_ci */
275462306a36Sopenharmony_cistatic void
275562306a36Sopenharmony_ciil4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb)
275662306a36Sopenharmony_ci{
275762306a36Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
275862306a36Sopenharmony_ci	u16 sequence = le16_to_cpu(pkt->hdr.sequence);
275962306a36Sopenharmony_ci	int txq_id = SEQ_TO_QUEUE(sequence);
276062306a36Sopenharmony_ci	int idx = SEQ_TO_IDX(sequence);
276162306a36Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[txq_id];
276262306a36Sopenharmony_ci	struct sk_buff *skb;
276362306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
276462306a36Sopenharmony_ci	struct ieee80211_tx_info *info;
276562306a36Sopenharmony_ci	struct il4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
276662306a36Sopenharmony_ci	u32 status = le32_to_cpu(tx_resp->u.status);
276762306a36Sopenharmony_ci	int tid;
276862306a36Sopenharmony_ci	int sta_id;
276962306a36Sopenharmony_ci	int freed;
277062306a36Sopenharmony_ci	u8 *qc = NULL;
277162306a36Sopenharmony_ci	unsigned long flags;
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci	if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) {
277462306a36Sopenharmony_ci		IL_ERR("Read idx for DMA queue txq_id (%d) idx %d "
277562306a36Sopenharmony_ci		       "is out of range [0-%d] %d %d\n", txq_id, idx,
277662306a36Sopenharmony_ci		       txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr);
277762306a36Sopenharmony_ci		return;
277862306a36Sopenharmony_ci	}
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_ci	txq->time_stamp = jiffies;
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci	skb = txq->skbs[txq->q.read_ptr];
278362306a36Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
278462306a36Sopenharmony_ci	memset(&info->status, 0, sizeof(info->status));
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *) skb->data;
278762306a36Sopenharmony_ci	if (ieee80211_is_data_qos(hdr->frame_control)) {
278862306a36Sopenharmony_ci		qc = ieee80211_get_qos_ctl(hdr);
278962306a36Sopenharmony_ci		tid = qc[0] & 0xf;
279062306a36Sopenharmony_ci	}
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_ci	sta_id = il4965_get_ra_sta_id(il, hdr);
279362306a36Sopenharmony_ci	if (txq->sched_retry && unlikely(sta_id == IL_INVALID_STATION)) {
279462306a36Sopenharmony_ci		IL_ERR("Station not known\n");
279562306a36Sopenharmony_ci		return;
279662306a36Sopenharmony_ci	}
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	/*
279962306a36Sopenharmony_ci	 * Firmware will not transmit frame on passive channel, if it not yet
280062306a36Sopenharmony_ci	 * received some valid frame on that channel. When this error happen
280162306a36Sopenharmony_ci	 * we have to wait until firmware will unblock itself i.e. when we
280262306a36Sopenharmony_ci	 * note received beacon or other frame. We unblock queues in
280362306a36Sopenharmony_ci	 * il4965_pass_packet_to_mac80211 or in il_mac_bss_info_changed.
280462306a36Sopenharmony_ci	 */
280562306a36Sopenharmony_ci	if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) &&
280662306a36Sopenharmony_ci	    il->iw_mode == NL80211_IFTYPE_STATION) {
280762306a36Sopenharmony_ci		il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
280862306a36Sopenharmony_ci		D_INFO("Stopped queues - RX waiting on passive channel\n");
280962306a36Sopenharmony_ci	}
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
281262306a36Sopenharmony_ci	if (txq->sched_retry) {
281362306a36Sopenharmony_ci		const u32 scd_ssn = il4965_get_scd_ssn(tx_resp);
281462306a36Sopenharmony_ci		struct il_ht_agg *agg;
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci		if (WARN_ON(!qc))
281762306a36Sopenharmony_ci			goto out;
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_ci		agg = &il->stations[sta_id].tid[tid].agg;
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci		il4965_tx_status_reply_tx(il, agg, tx_resp, txq_id, idx);
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci		/* check if BAR is needed */
282462306a36Sopenharmony_ci		if (tx_resp->frame_count == 1 &&
282562306a36Sopenharmony_ci		    !il4965_is_tx_success(status))
282662306a36Sopenharmony_ci			info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci		if (txq->q.read_ptr != (scd_ssn & 0xff)) {
282962306a36Sopenharmony_ci			idx = il_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
283062306a36Sopenharmony_ci			D_TX_REPLY("Retry scheduler reclaim scd_ssn "
283162306a36Sopenharmony_ci				   "%d idx %d\n", scd_ssn, idx);
283262306a36Sopenharmony_ci			freed = il4965_tx_queue_reclaim(il, txq_id, idx);
283362306a36Sopenharmony_ci			il4965_free_tfds_in_queue(il, sta_id, tid, freed);
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci			if (il->mac80211_registered &&
283662306a36Sopenharmony_ci			    il_queue_space(&txq->q) > txq->q.low_mark &&
283762306a36Sopenharmony_ci			    agg->state != IL_EMPTYING_HW_QUEUE_DELBA)
283862306a36Sopenharmony_ci				il_wake_queue(il, txq);
283962306a36Sopenharmony_ci		}
284062306a36Sopenharmony_ci	} else {
284162306a36Sopenharmony_ci		info->status.rates[0].count = tx_resp->failure_frame + 1;
284262306a36Sopenharmony_ci		info->flags |= il4965_tx_status_to_mac80211(status);
284362306a36Sopenharmony_ci		il4965_hwrate_to_tx_control(il,
284462306a36Sopenharmony_ci					    le32_to_cpu(tx_resp->rate_n_flags),
284562306a36Sopenharmony_ci					    info);
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci		D_TX_REPLY("TXQ %d status %s (0x%08x) "
284862306a36Sopenharmony_ci			   "rate_n_flags 0x%x retries %d\n", txq_id,
284962306a36Sopenharmony_ci			   il4965_get_tx_fail_reason(status), status,
285062306a36Sopenharmony_ci			   le32_to_cpu(tx_resp->rate_n_flags),
285162306a36Sopenharmony_ci			   tx_resp->failure_frame);
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci		freed = il4965_tx_queue_reclaim(il, txq_id, idx);
285462306a36Sopenharmony_ci		if (qc && likely(sta_id != IL_INVALID_STATION))
285562306a36Sopenharmony_ci			il4965_free_tfds_in_queue(il, sta_id, tid, freed);
285662306a36Sopenharmony_ci		else if (sta_id == IL_INVALID_STATION)
285762306a36Sopenharmony_ci			D_TX_REPLY("Station not known\n");
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_ci		if (il->mac80211_registered &&
286062306a36Sopenharmony_ci		    il_queue_space(&txq->q) > txq->q.low_mark)
286162306a36Sopenharmony_ci			il_wake_queue(il, txq);
286262306a36Sopenharmony_ci	}
286362306a36Sopenharmony_ciout:
286462306a36Sopenharmony_ci	if (qc && likely(sta_id != IL_INVALID_STATION))
286562306a36Sopenharmony_ci		il4965_txq_check_empty(il, sta_id, tid, txq_id);
286662306a36Sopenharmony_ci
286762306a36Sopenharmony_ci	il4965_check_abort_status(il, tx_resp->frame_count, status);
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
287062306a36Sopenharmony_ci}
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci/*
287362306a36Sopenharmony_ci * translate ucode response to mac80211 tx status control values
287462306a36Sopenharmony_ci */
287562306a36Sopenharmony_civoid
287662306a36Sopenharmony_ciil4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags,
287762306a36Sopenharmony_ci			    struct ieee80211_tx_info *info)
287862306a36Sopenharmony_ci{
287962306a36Sopenharmony_ci	struct ieee80211_tx_rate *r = &info->status.rates[0];
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci	info->status.antenna =
288262306a36Sopenharmony_ci	    ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
288362306a36Sopenharmony_ci	if (rate_n_flags & RATE_MCS_HT_MSK)
288462306a36Sopenharmony_ci		r->flags |= IEEE80211_TX_RC_MCS;
288562306a36Sopenharmony_ci	if (rate_n_flags & RATE_MCS_GF_MSK)
288662306a36Sopenharmony_ci		r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
288762306a36Sopenharmony_ci	if (rate_n_flags & RATE_MCS_HT40_MSK)
288862306a36Sopenharmony_ci		r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
288962306a36Sopenharmony_ci	if (rate_n_flags & RATE_MCS_DUP_MSK)
289062306a36Sopenharmony_ci		r->flags |= IEEE80211_TX_RC_DUP_DATA;
289162306a36Sopenharmony_ci	if (rate_n_flags & RATE_MCS_SGI_MSK)
289262306a36Sopenharmony_ci		r->flags |= IEEE80211_TX_RC_SHORT_GI;
289362306a36Sopenharmony_ci	r->idx = il4965_hwrate_to_mac80211_idx(rate_n_flags, info->band);
289462306a36Sopenharmony_ci}
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci/*
289762306a36Sopenharmony_ci * il4965_hdl_compressed_ba - Handler for N_COMPRESSED_BA
289862306a36Sopenharmony_ci *
289962306a36Sopenharmony_ci * Handles block-acknowledge notification from device, which reports success
290062306a36Sopenharmony_ci * of frames sent via aggregation.
290162306a36Sopenharmony_ci */
290262306a36Sopenharmony_cistatic void
290362306a36Sopenharmony_ciil4965_hdl_compressed_ba(struct il_priv *il, struct il_rx_buf *rxb)
290462306a36Sopenharmony_ci{
290562306a36Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
290662306a36Sopenharmony_ci	struct il_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba;
290762306a36Sopenharmony_ci	struct il_tx_queue *txq = NULL;
290862306a36Sopenharmony_ci	struct il_ht_agg *agg;
290962306a36Sopenharmony_ci	int idx;
291062306a36Sopenharmony_ci	int sta_id;
291162306a36Sopenharmony_ci	int tid;
291262306a36Sopenharmony_ci	unsigned long flags;
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci	/* "flow" corresponds to Tx queue */
291562306a36Sopenharmony_ci	u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
291662306a36Sopenharmony_ci
291762306a36Sopenharmony_ci	/* "ssn" is start of block-ack Tx win, corresponds to idx
291862306a36Sopenharmony_ci	 * (in Tx queue's circular buffer) of first TFD/frame in win */
291962306a36Sopenharmony_ci	u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn);
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci	if (scd_flow >= il->hw_params.max_txq_num) {
292262306a36Sopenharmony_ci		IL_ERR("BUG_ON scd_flow is bigger than number of queues\n");
292362306a36Sopenharmony_ci		return;
292462306a36Sopenharmony_ci	}
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci	txq = &il->txq[scd_flow];
292762306a36Sopenharmony_ci	sta_id = ba_resp->sta_id;
292862306a36Sopenharmony_ci	tid = ba_resp->tid;
292962306a36Sopenharmony_ci	agg = &il->stations[sta_id].tid[tid].agg;
293062306a36Sopenharmony_ci	if (unlikely(agg->txq_id != scd_flow)) {
293162306a36Sopenharmony_ci		/*
293262306a36Sopenharmony_ci		 * FIXME: this is a uCode bug which need to be addressed,
293362306a36Sopenharmony_ci		 * log the information and return for now!
293462306a36Sopenharmony_ci		 * since it is possible happen very often and in order
293562306a36Sopenharmony_ci		 * not to fill the syslog, don't enable the logging by default
293662306a36Sopenharmony_ci		 */
293762306a36Sopenharmony_ci		D_TX_REPLY("BA scd_flow %d does not match txq_id %d\n",
293862306a36Sopenharmony_ci			   scd_flow, agg->txq_id);
293962306a36Sopenharmony_ci		return;
294062306a36Sopenharmony_ci	}
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci	/* Find idx just before block-ack win */
294362306a36Sopenharmony_ci	idx = il_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd);
294462306a36Sopenharmony_ci
294562306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci	D_TX_REPLY("N_COMPRESSED_BA [%d] Received from %pM, " "sta_id = %d\n",
294862306a36Sopenharmony_ci		   agg->wait_for_ba, (u8 *) &ba_resp->sta_addr_lo32,
294962306a36Sopenharmony_ci		   ba_resp->sta_id);
295062306a36Sopenharmony_ci	D_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%llx," "scd_flow = "
295162306a36Sopenharmony_ci		   "%d, scd_ssn = %d\n", ba_resp->tid, ba_resp->seq_ctl,
295262306a36Sopenharmony_ci		   (unsigned long long)le64_to_cpu(ba_resp->bitmap),
295362306a36Sopenharmony_ci		   ba_resp->scd_flow, ba_resp->scd_ssn);
295462306a36Sopenharmony_ci	D_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx\n", agg->start_idx,
295562306a36Sopenharmony_ci		   (unsigned long long)agg->bitmap);
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci	/* Update driver's record of ACK vs. not for each frame in win */
295862306a36Sopenharmony_ci	il4965_tx_status_reply_compressed_ba(il, agg, ba_resp);
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	/* Release all TFDs before the SSN, i.e. all TFDs in front of
296162306a36Sopenharmony_ci	 * block-ack win (we assume that they've been successfully
296262306a36Sopenharmony_ci	 * transmitted ... if not, it's too late anyway). */
296362306a36Sopenharmony_ci	if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) {
296462306a36Sopenharmony_ci		/* calculate mac80211 ampdu sw queue to wake */
296562306a36Sopenharmony_ci		int freed = il4965_tx_queue_reclaim(il, scd_flow, idx);
296662306a36Sopenharmony_ci		il4965_free_tfds_in_queue(il, sta_id, tid, freed);
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci		if (il_queue_space(&txq->q) > txq->q.low_mark &&
296962306a36Sopenharmony_ci		    il->mac80211_registered &&
297062306a36Sopenharmony_ci		    agg->state != IL_EMPTYING_HW_QUEUE_DELBA)
297162306a36Sopenharmony_ci			il_wake_queue(il, txq);
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ci		il4965_txq_check_empty(il, sta_id, tid, scd_flow);
297462306a36Sopenharmony_ci	}
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
297762306a36Sopenharmony_ci}
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
298062306a36Sopenharmony_ciconst char *
298162306a36Sopenharmony_ciil4965_get_tx_fail_reason(u32 status)
298262306a36Sopenharmony_ci{
298362306a36Sopenharmony_ci#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
298462306a36Sopenharmony_ci#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci	switch (status & TX_STATUS_MSK) {
298762306a36Sopenharmony_ci	case TX_STATUS_SUCCESS:
298862306a36Sopenharmony_ci		return "SUCCESS";
298962306a36Sopenharmony_ci		TX_STATUS_POSTPONE(DELAY);
299062306a36Sopenharmony_ci		TX_STATUS_POSTPONE(FEW_BYTES);
299162306a36Sopenharmony_ci		TX_STATUS_POSTPONE(QUIET_PERIOD);
299262306a36Sopenharmony_ci		TX_STATUS_POSTPONE(CALC_TTAK);
299362306a36Sopenharmony_ci		TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
299462306a36Sopenharmony_ci		TX_STATUS_FAIL(SHORT_LIMIT);
299562306a36Sopenharmony_ci		TX_STATUS_FAIL(LONG_LIMIT);
299662306a36Sopenharmony_ci		TX_STATUS_FAIL(FIFO_UNDERRUN);
299762306a36Sopenharmony_ci		TX_STATUS_FAIL(DRAIN_FLOW);
299862306a36Sopenharmony_ci		TX_STATUS_FAIL(RFKILL_FLUSH);
299962306a36Sopenharmony_ci		TX_STATUS_FAIL(LIFE_EXPIRE);
300062306a36Sopenharmony_ci		TX_STATUS_FAIL(DEST_PS);
300162306a36Sopenharmony_ci		TX_STATUS_FAIL(HOST_ABORTED);
300262306a36Sopenharmony_ci		TX_STATUS_FAIL(BT_RETRY);
300362306a36Sopenharmony_ci		TX_STATUS_FAIL(STA_INVALID);
300462306a36Sopenharmony_ci		TX_STATUS_FAIL(FRAG_DROPPED);
300562306a36Sopenharmony_ci		TX_STATUS_FAIL(TID_DISABLE);
300662306a36Sopenharmony_ci		TX_STATUS_FAIL(FIFO_FLUSHED);
300762306a36Sopenharmony_ci		TX_STATUS_FAIL(INSUFFICIENT_CF_POLL);
300862306a36Sopenharmony_ci		TX_STATUS_FAIL(PASSIVE_NO_RX);
300962306a36Sopenharmony_ci		TX_STATUS_FAIL(NO_BEACON_ON_RADAR);
301062306a36Sopenharmony_ci	}
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	return "UNKNOWN";
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ci#undef TX_STATUS_FAIL
301562306a36Sopenharmony_ci#undef TX_STATUS_POSTPONE
301662306a36Sopenharmony_ci}
301762306a36Sopenharmony_ci#endif /* CONFIG_IWLEGACY_DEBUG */
301862306a36Sopenharmony_ci
301962306a36Sopenharmony_cistatic struct il_link_quality_cmd *
302062306a36Sopenharmony_ciil4965_sta_alloc_lq(struct il_priv *il, u8 sta_id)
302162306a36Sopenharmony_ci{
302262306a36Sopenharmony_ci	int i, r;
302362306a36Sopenharmony_ci	struct il_link_quality_cmd *link_cmd;
302462306a36Sopenharmony_ci	u32 rate_flags = 0;
302562306a36Sopenharmony_ci	__le32 rate_n_flags;
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_ci	link_cmd = kzalloc(sizeof(struct il_link_quality_cmd), GFP_KERNEL);
302862306a36Sopenharmony_ci	if (!link_cmd) {
302962306a36Sopenharmony_ci		IL_ERR("Unable to allocate memory for LQ cmd.\n");
303062306a36Sopenharmony_ci		return NULL;
303162306a36Sopenharmony_ci	}
303262306a36Sopenharmony_ci	/* Set up the rate scaling to start at selected rate, fall back
303362306a36Sopenharmony_ci	 * all the way down to 1M in IEEE order, and then spin on 1M */
303462306a36Sopenharmony_ci	if (il->band == NL80211_BAND_5GHZ)
303562306a36Sopenharmony_ci		r = RATE_6M_IDX;
303662306a36Sopenharmony_ci	else
303762306a36Sopenharmony_ci		r = RATE_1M_IDX;
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ci	if (r >= IL_FIRST_CCK_RATE && r <= IL_LAST_CCK_RATE)
304062306a36Sopenharmony_ci		rate_flags |= RATE_MCS_CCK_MSK;
304162306a36Sopenharmony_ci
304262306a36Sopenharmony_ci	rate_flags |=
304362306a36Sopenharmony_ci	    il4965_first_antenna(il->hw_params.
304462306a36Sopenharmony_ci				 valid_tx_ant) << RATE_MCS_ANT_POS;
304562306a36Sopenharmony_ci	rate_n_flags = cpu_to_le32(il_rates[r].plcp | rate_flags);
304662306a36Sopenharmony_ci	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
304762306a36Sopenharmony_ci		link_cmd->rs_table[i].rate_n_flags = rate_n_flags;
304862306a36Sopenharmony_ci
304962306a36Sopenharmony_ci	link_cmd->general_params.single_stream_ant_msk =
305062306a36Sopenharmony_ci	    il4965_first_antenna(il->hw_params.valid_tx_ant);
305162306a36Sopenharmony_ci
305262306a36Sopenharmony_ci	link_cmd->general_params.dual_stream_ant_msk =
305362306a36Sopenharmony_ci	    il->hw_params.valid_tx_ant & ~il4965_first_antenna(il->hw_params.
305462306a36Sopenharmony_ci							       valid_tx_ant);
305562306a36Sopenharmony_ci	if (!link_cmd->general_params.dual_stream_ant_msk) {
305662306a36Sopenharmony_ci		link_cmd->general_params.dual_stream_ant_msk = ANT_AB;
305762306a36Sopenharmony_ci	} else if (il4965_num_of_ant(il->hw_params.valid_tx_ant) == 2) {
305862306a36Sopenharmony_ci		link_cmd->general_params.dual_stream_ant_msk =
305962306a36Sopenharmony_ci		    il->hw_params.valid_tx_ant;
306062306a36Sopenharmony_ci	}
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci	link_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
306362306a36Sopenharmony_ci	link_cmd->agg_params.agg_time_limit =
306462306a36Sopenharmony_ci	    cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
306562306a36Sopenharmony_ci
306662306a36Sopenharmony_ci	link_cmd->sta_id = sta_id;
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci	return link_cmd;
306962306a36Sopenharmony_ci}
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci/*
307262306a36Sopenharmony_ci * il4965_add_bssid_station - Add the special IBSS BSSID station
307362306a36Sopenharmony_ci *
307462306a36Sopenharmony_ci * Function sleeps.
307562306a36Sopenharmony_ci */
307662306a36Sopenharmony_ciint
307762306a36Sopenharmony_ciil4965_add_bssid_station(struct il_priv *il, const u8 *addr, u8 *sta_id_r)
307862306a36Sopenharmony_ci{
307962306a36Sopenharmony_ci	int ret;
308062306a36Sopenharmony_ci	u8 sta_id;
308162306a36Sopenharmony_ci	struct il_link_quality_cmd *link_cmd;
308262306a36Sopenharmony_ci	unsigned long flags;
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_ci	if (sta_id_r)
308562306a36Sopenharmony_ci		*sta_id_r = IL_INVALID_STATION;
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ci	ret = il_add_station_common(il, addr, 0, NULL, &sta_id);
308862306a36Sopenharmony_ci	if (ret) {
308962306a36Sopenharmony_ci		IL_ERR("Unable to add station %pM\n", addr);
309062306a36Sopenharmony_ci		return ret;
309162306a36Sopenharmony_ci	}
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	if (sta_id_r)
309462306a36Sopenharmony_ci		*sta_id_r = sta_id;
309562306a36Sopenharmony_ci
309662306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
309762306a36Sopenharmony_ci	il->stations[sta_id].used |= IL_STA_LOCAL;
309862306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
309962306a36Sopenharmony_ci
310062306a36Sopenharmony_ci	/* Set up default rate scaling table in device's station table */
310162306a36Sopenharmony_ci	link_cmd = il4965_sta_alloc_lq(il, sta_id);
310262306a36Sopenharmony_ci	if (!link_cmd) {
310362306a36Sopenharmony_ci		IL_ERR("Unable to initialize rate scaling for station %pM.\n",
310462306a36Sopenharmony_ci		       addr);
310562306a36Sopenharmony_ci		return -ENOMEM;
310662306a36Sopenharmony_ci	}
310762306a36Sopenharmony_ci
310862306a36Sopenharmony_ci	ret = il_send_lq_cmd(il, link_cmd, CMD_SYNC, true);
310962306a36Sopenharmony_ci	if (ret)
311062306a36Sopenharmony_ci		IL_ERR("Link quality command failed (%d)\n", ret);
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
311362306a36Sopenharmony_ci	il->stations[sta_id].lq = link_cmd;
311462306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
311562306a36Sopenharmony_ci
311662306a36Sopenharmony_ci	return 0;
311762306a36Sopenharmony_ci}
311862306a36Sopenharmony_ci
311962306a36Sopenharmony_cistatic int
312062306a36Sopenharmony_ciil4965_static_wepkey_cmd(struct il_priv *il, bool send_if_empty)
312162306a36Sopenharmony_ci{
312262306a36Sopenharmony_ci	int i;
312362306a36Sopenharmony_ci	u8 buff[sizeof(struct il_wep_cmd) +
312462306a36Sopenharmony_ci		sizeof(struct il_wep_key) * WEP_KEYS_MAX];
312562306a36Sopenharmony_ci	struct il_wep_cmd *wep_cmd = (struct il_wep_cmd *)buff;
312662306a36Sopenharmony_ci	size_t cmd_size = sizeof(struct il_wep_cmd);
312762306a36Sopenharmony_ci	struct il_host_cmd cmd = {
312862306a36Sopenharmony_ci		.id = C_WEPKEY,
312962306a36Sopenharmony_ci		.data = wep_cmd,
313062306a36Sopenharmony_ci		.flags = CMD_SYNC,
313162306a36Sopenharmony_ci	};
313262306a36Sopenharmony_ci	bool not_empty = false;
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_ci	might_sleep();
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	memset(wep_cmd, 0,
313762306a36Sopenharmony_ci	       cmd_size + (sizeof(struct il_wep_key) * WEP_KEYS_MAX));
313862306a36Sopenharmony_ci
313962306a36Sopenharmony_ci	for (i = 0; i < WEP_KEYS_MAX; i++) {
314062306a36Sopenharmony_ci		u8 key_size = il->_4965.wep_keys[i].key_size;
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ci		wep_cmd->key[i].key_idx = i;
314362306a36Sopenharmony_ci		if (key_size) {
314462306a36Sopenharmony_ci			wep_cmd->key[i].key_offset = i;
314562306a36Sopenharmony_ci			not_empty = true;
314662306a36Sopenharmony_ci		} else
314762306a36Sopenharmony_ci			wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET;
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci		wep_cmd->key[i].key_size = key_size;
315062306a36Sopenharmony_ci		memcpy(&wep_cmd->key[i].key[3], il->_4965.wep_keys[i].key, key_size);
315162306a36Sopenharmony_ci	}
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_ci	wep_cmd->global_key_type = WEP_KEY_WEP_TYPE;
315462306a36Sopenharmony_ci	wep_cmd->num_keys = WEP_KEYS_MAX;
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	cmd_size += sizeof(struct il_wep_key) * WEP_KEYS_MAX;
315762306a36Sopenharmony_ci	cmd.len = cmd_size;
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_ci	if (not_empty || send_if_empty)
316062306a36Sopenharmony_ci		return il_send_cmd(il, &cmd);
316162306a36Sopenharmony_ci	else
316262306a36Sopenharmony_ci		return 0;
316362306a36Sopenharmony_ci}
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ciint
316662306a36Sopenharmony_ciil4965_restore_default_wep_keys(struct il_priv *il)
316762306a36Sopenharmony_ci{
316862306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci	return il4965_static_wepkey_cmd(il, false);
317162306a36Sopenharmony_ci}
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ciint
317462306a36Sopenharmony_ciil4965_remove_default_wep_key(struct il_priv *il,
317562306a36Sopenharmony_ci			      struct ieee80211_key_conf *keyconf)
317662306a36Sopenharmony_ci{
317762306a36Sopenharmony_ci	int ret;
317862306a36Sopenharmony_ci	int idx = keyconf->keyidx;
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ci	D_WEP("Removing default WEP key: idx=%d\n", idx);
318362306a36Sopenharmony_ci
318462306a36Sopenharmony_ci	memset(&il->_4965.wep_keys[idx], 0, sizeof(struct il_wep_key));
318562306a36Sopenharmony_ci	if (il_is_rfkill(il)) {
318662306a36Sopenharmony_ci		D_WEP("Not sending C_WEPKEY command due to RFKILL.\n");
318762306a36Sopenharmony_ci		/* but keys in device are clear anyway so return success */
318862306a36Sopenharmony_ci		return 0;
318962306a36Sopenharmony_ci	}
319062306a36Sopenharmony_ci	ret = il4965_static_wepkey_cmd(il, 1);
319162306a36Sopenharmony_ci	D_WEP("Remove default WEP key: idx=%d ret=%d\n", idx, ret);
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci	return ret;
319462306a36Sopenharmony_ci}
319562306a36Sopenharmony_ci
319662306a36Sopenharmony_ciint
319762306a36Sopenharmony_ciil4965_set_default_wep_key(struct il_priv *il,
319862306a36Sopenharmony_ci			   struct ieee80211_key_conf *keyconf)
319962306a36Sopenharmony_ci{
320062306a36Sopenharmony_ci	int ret;
320162306a36Sopenharmony_ci	int len = keyconf->keylen;
320262306a36Sopenharmony_ci	int idx = keyconf->keyidx;
320362306a36Sopenharmony_ci
320462306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci	if (len != WEP_KEY_LEN_128 && len != WEP_KEY_LEN_64) {
320762306a36Sopenharmony_ci		D_WEP("Bad WEP key length %d\n", keyconf->keylen);
320862306a36Sopenharmony_ci		return -EINVAL;
320962306a36Sopenharmony_ci	}
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci	keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
321262306a36Sopenharmony_ci	keyconf->hw_key_idx = HW_KEY_DEFAULT;
321362306a36Sopenharmony_ci	il->stations[IL_AP_ID].keyinfo.cipher = keyconf->cipher;
321462306a36Sopenharmony_ci
321562306a36Sopenharmony_ci	il->_4965.wep_keys[idx].key_size = len;
321662306a36Sopenharmony_ci	memcpy(&il->_4965.wep_keys[idx].key, &keyconf->key, len);
321762306a36Sopenharmony_ci
321862306a36Sopenharmony_ci	ret = il4965_static_wepkey_cmd(il, false);
321962306a36Sopenharmony_ci
322062306a36Sopenharmony_ci	D_WEP("Set default WEP key: len=%d idx=%d ret=%d\n", len, idx, ret);
322162306a36Sopenharmony_ci	return ret;
322262306a36Sopenharmony_ci}
322362306a36Sopenharmony_ci
322462306a36Sopenharmony_cistatic int
322562306a36Sopenharmony_ciil4965_set_wep_dynamic_key_info(struct il_priv *il,
322662306a36Sopenharmony_ci				struct ieee80211_key_conf *keyconf, u8 sta_id)
322762306a36Sopenharmony_ci{
322862306a36Sopenharmony_ci	unsigned long flags;
322962306a36Sopenharmony_ci	__le16 key_flags = 0;
323062306a36Sopenharmony_ci	struct il_addsta_cmd sta_cmd;
323162306a36Sopenharmony_ci
323262306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ci	keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
323562306a36Sopenharmony_ci
323662306a36Sopenharmony_ci	key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK);
323762306a36Sopenharmony_ci	key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
323862306a36Sopenharmony_ci	key_flags &= ~STA_KEY_FLG_INVALID;
323962306a36Sopenharmony_ci
324062306a36Sopenharmony_ci	if (keyconf->keylen == WEP_KEY_LEN_128)
324162306a36Sopenharmony_ci		key_flags |= STA_KEY_FLG_KEY_SIZE_MSK;
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci	if (sta_id == il->hw_params.bcast_id)
324462306a36Sopenharmony_ci		key_flags |= STA_KEY_MULTICAST_MSK;
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci	il->stations[sta_id].keyinfo.cipher = keyconf->cipher;
324962306a36Sopenharmony_ci	il->stations[sta_id].keyinfo.keylen = keyconf->keylen;
325062306a36Sopenharmony_ci	il->stations[sta_id].keyinfo.keyidx = keyconf->keyidx;
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_ci	memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen);
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_ci	memcpy(&il->stations[sta_id].sta.key.key[3], keyconf->key,
325562306a36Sopenharmony_ci	       keyconf->keylen);
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci	if ((il->stations[sta_id].sta.key.
325862306a36Sopenharmony_ci	     key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC)
325962306a36Sopenharmony_ci		il->stations[sta_id].sta.key.key_offset =
326062306a36Sopenharmony_ci		    il_get_free_ucode_key_idx(il);
326162306a36Sopenharmony_ci	/* else, we are overriding an existing key => no need to allocated room
326262306a36Sopenharmony_ci	 * in uCode. */
326362306a36Sopenharmony_ci
326462306a36Sopenharmony_ci	WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
326562306a36Sopenharmony_ci	     "no space for a new key");
326662306a36Sopenharmony_ci
326762306a36Sopenharmony_ci	il->stations[sta_id].sta.key.key_flags = key_flags;
326862306a36Sopenharmony_ci	il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
326962306a36Sopenharmony_ci	il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci	memcpy(&sta_cmd, &il->stations[sta_id].sta,
327262306a36Sopenharmony_ci	       sizeof(struct il_addsta_cmd));
327362306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_ci	return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
327662306a36Sopenharmony_ci}
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_cistatic int
327962306a36Sopenharmony_ciil4965_set_ccmp_dynamic_key_info(struct il_priv *il,
328062306a36Sopenharmony_ci				 struct ieee80211_key_conf *keyconf, u8 sta_id)
328162306a36Sopenharmony_ci{
328262306a36Sopenharmony_ci	unsigned long flags;
328362306a36Sopenharmony_ci	__le16 key_flags = 0;
328462306a36Sopenharmony_ci	struct il_addsta_cmd sta_cmd;
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
328762306a36Sopenharmony_ci
328862306a36Sopenharmony_ci	key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK);
328962306a36Sopenharmony_ci	key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
329062306a36Sopenharmony_ci	key_flags &= ~STA_KEY_FLG_INVALID;
329162306a36Sopenharmony_ci
329262306a36Sopenharmony_ci	if (sta_id == il->hw_params.bcast_id)
329362306a36Sopenharmony_ci		key_flags |= STA_KEY_MULTICAST_MSK;
329462306a36Sopenharmony_ci
329562306a36Sopenharmony_ci	keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
329662306a36Sopenharmony_ci
329762306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
329862306a36Sopenharmony_ci	il->stations[sta_id].keyinfo.cipher = keyconf->cipher;
329962306a36Sopenharmony_ci	il->stations[sta_id].keyinfo.keylen = keyconf->keylen;
330062306a36Sopenharmony_ci
330162306a36Sopenharmony_ci	memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen);
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci	memcpy(il->stations[sta_id].sta.key.key, keyconf->key, keyconf->keylen);
330462306a36Sopenharmony_ci
330562306a36Sopenharmony_ci	if ((il->stations[sta_id].sta.key.
330662306a36Sopenharmony_ci	     key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC)
330762306a36Sopenharmony_ci		il->stations[sta_id].sta.key.key_offset =
330862306a36Sopenharmony_ci		    il_get_free_ucode_key_idx(il);
330962306a36Sopenharmony_ci	/* else, we are overriding an existing key => no need to allocated room
331062306a36Sopenharmony_ci	 * in uCode. */
331162306a36Sopenharmony_ci
331262306a36Sopenharmony_ci	WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
331362306a36Sopenharmony_ci	     "no space for a new key");
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci	il->stations[sta_id].sta.key.key_flags = key_flags;
331662306a36Sopenharmony_ci	il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
331762306a36Sopenharmony_ci	il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
331862306a36Sopenharmony_ci
331962306a36Sopenharmony_ci	memcpy(&sta_cmd, &il->stations[sta_id].sta,
332062306a36Sopenharmony_ci	       sizeof(struct il_addsta_cmd));
332162306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
332462306a36Sopenharmony_ci}
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_cistatic int
332762306a36Sopenharmony_ciil4965_set_tkip_dynamic_key_info(struct il_priv *il,
332862306a36Sopenharmony_ci				 struct ieee80211_key_conf *keyconf, u8 sta_id)
332962306a36Sopenharmony_ci{
333062306a36Sopenharmony_ci	unsigned long flags;
333162306a36Sopenharmony_ci	__le16 key_flags = 0;
333262306a36Sopenharmony_ci
333362306a36Sopenharmony_ci	key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK);
333462306a36Sopenharmony_ci	key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
333562306a36Sopenharmony_ci	key_flags &= ~STA_KEY_FLG_INVALID;
333662306a36Sopenharmony_ci
333762306a36Sopenharmony_ci	if (sta_id == il->hw_params.bcast_id)
333862306a36Sopenharmony_ci		key_flags |= STA_KEY_MULTICAST_MSK;
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_ci	keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
334162306a36Sopenharmony_ci	keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
334262306a36Sopenharmony_ci
334362306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
334462306a36Sopenharmony_ci
334562306a36Sopenharmony_ci	il->stations[sta_id].keyinfo.cipher = keyconf->cipher;
334662306a36Sopenharmony_ci	il->stations[sta_id].keyinfo.keylen = 16;
334762306a36Sopenharmony_ci
334862306a36Sopenharmony_ci	if ((il->stations[sta_id].sta.key.
334962306a36Sopenharmony_ci	     key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC)
335062306a36Sopenharmony_ci		il->stations[sta_id].sta.key.key_offset =
335162306a36Sopenharmony_ci		    il_get_free_ucode_key_idx(il);
335262306a36Sopenharmony_ci	/* else, we are overriding an existing key => no need to allocated room
335362306a36Sopenharmony_ci	 * in uCode. */
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_ci	WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
335662306a36Sopenharmony_ci	     "no space for a new key");
335762306a36Sopenharmony_ci
335862306a36Sopenharmony_ci	il->stations[sta_id].sta.key.key_flags = key_flags;
335962306a36Sopenharmony_ci
336062306a36Sopenharmony_ci	/* This copy is acutally not needed: we get the key with each TX */
336162306a36Sopenharmony_ci	memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, 16);
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_ci	memcpy(il->stations[sta_id].sta.key.key, keyconf->key, 16);
336462306a36Sopenharmony_ci
336562306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_ci	return 0;
336862306a36Sopenharmony_ci}
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_civoid
337162306a36Sopenharmony_ciil4965_update_tkip_key(struct il_priv *il, struct ieee80211_key_conf *keyconf,
337262306a36Sopenharmony_ci		       struct ieee80211_sta *sta, u32 iv32, u16 *phase1key)
337362306a36Sopenharmony_ci{
337462306a36Sopenharmony_ci	u8 sta_id;
337562306a36Sopenharmony_ci	unsigned long flags;
337662306a36Sopenharmony_ci	int i;
337762306a36Sopenharmony_ci
337862306a36Sopenharmony_ci	if (il_scan_cancel(il)) {
337962306a36Sopenharmony_ci		/* cancel scan failed, just live w/ bad key and rely
338062306a36Sopenharmony_ci		   briefly on SW decryption */
338162306a36Sopenharmony_ci		return;
338262306a36Sopenharmony_ci	}
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci	sta_id = il_sta_id_or_broadcast(il, sta);
338562306a36Sopenharmony_ci	if (sta_id == IL_INVALID_STATION)
338662306a36Sopenharmony_ci		return;
338762306a36Sopenharmony_ci
338862306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_ci	il->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32;
339162306a36Sopenharmony_ci
339262306a36Sopenharmony_ci	for (i = 0; i < 5; i++)
339362306a36Sopenharmony_ci		il->stations[sta_id].sta.key.tkip_rx_ttak[i] =
339462306a36Sopenharmony_ci		    cpu_to_le16(phase1key[i]);
339562306a36Sopenharmony_ci
339662306a36Sopenharmony_ci	il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
339762306a36Sopenharmony_ci	il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
339862306a36Sopenharmony_ci
339962306a36Sopenharmony_ci	il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC);
340062306a36Sopenharmony_ci
340162306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
340262306a36Sopenharmony_ci}
340362306a36Sopenharmony_ci
340462306a36Sopenharmony_ciint
340562306a36Sopenharmony_ciil4965_remove_dynamic_key(struct il_priv *il,
340662306a36Sopenharmony_ci			  struct ieee80211_key_conf *keyconf, u8 sta_id)
340762306a36Sopenharmony_ci{
340862306a36Sopenharmony_ci	unsigned long flags;
340962306a36Sopenharmony_ci	u16 key_flags;
341062306a36Sopenharmony_ci	u8 keyidx;
341162306a36Sopenharmony_ci	struct il_addsta_cmd sta_cmd;
341262306a36Sopenharmony_ci
341362306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
341462306a36Sopenharmony_ci
341562306a36Sopenharmony_ci	il->_4965.key_mapping_keys--;
341662306a36Sopenharmony_ci
341762306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
341862306a36Sopenharmony_ci	key_flags = le16_to_cpu(il->stations[sta_id].sta.key.key_flags);
341962306a36Sopenharmony_ci	keyidx = (key_flags >> STA_KEY_FLG_KEYID_POS) & 0x3;
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci	D_WEP("Remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id);
342262306a36Sopenharmony_ci
342362306a36Sopenharmony_ci	if (keyconf->keyidx != keyidx) {
342462306a36Sopenharmony_ci		/* We need to remove a key with idx different that the one
342562306a36Sopenharmony_ci		 * in the uCode. This means that the key we need to remove has
342662306a36Sopenharmony_ci		 * been replaced by another one with different idx.
342762306a36Sopenharmony_ci		 * Don't do anything and return ok
342862306a36Sopenharmony_ci		 */
342962306a36Sopenharmony_ci		spin_unlock_irqrestore(&il->sta_lock, flags);
343062306a36Sopenharmony_ci		return 0;
343162306a36Sopenharmony_ci	}
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_ci	if (il->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_INVALID) {
343462306a36Sopenharmony_ci		IL_WARN("Removing wrong key %d 0x%x\n", keyconf->keyidx,
343562306a36Sopenharmony_ci			key_flags);
343662306a36Sopenharmony_ci		spin_unlock_irqrestore(&il->sta_lock, flags);
343762306a36Sopenharmony_ci		return 0;
343862306a36Sopenharmony_ci	}
343962306a36Sopenharmony_ci
344062306a36Sopenharmony_ci	if (!test_and_clear_bit
344162306a36Sopenharmony_ci	    (il->stations[sta_id].sta.key.key_offset, &il->ucode_key_table))
344262306a36Sopenharmony_ci		IL_ERR("idx %d not used in uCode key table.\n",
344362306a36Sopenharmony_ci		       il->stations[sta_id].sta.key.key_offset);
344462306a36Sopenharmony_ci	memset(&il->stations[sta_id].keyinfo, 0, sizeof(struct il_hw_key));
344562306a36Sopenharmony_ci	memset(&il->stations[sta_id].sta.key, 0, sizeof(struct il4965_keyinfo));
344662306a36Sopenharmony_ci	il->stations[sta_id].sta.key.key_flags =
344762306a36Sopenharmony_ci	    STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID;
344862306a36Sopenharmony_ci	il->stations[sta_id].sta.key.key_offset = keyconf->hw_key_idx;
344962306a36Sopenharmony_ci	il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
345062306a36Sopenharmony_ci	il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
345162306a36Sopenharmony_ci
345262306a36Sopenharmony_ci	if (il_is_rfkill(il)) {
345362306a36Sopenharmony_ci		D_WEP
345462306a36Sopenharmony_ci		    ("Not sending C_ADD_STA command because RFKILL enabled.\n");
345562306a36Sopenharmony_ci		spin_unlock_irqrestore(&il->sta_lock, flags);
345662306a36Sopenharmony_ci		return 0;
345762306a36Sopenharmony_ci	}
345862306a36Sopenharmony_ci	memcpy(&sta_cmd, &il->stations[sta_id].sta,
345962306a36Sopenharmony_ci	       sizeof(struct il_addsta_cmd));
346062306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ci	return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
346362306a36Sopenharmony_ci}
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ciint
346662306a36Sopenharmony_ciil4965_set_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *keyconf,
346762306a36Sopenharmony_ci		       u8 sta_id)
346862306a36Sopenharmony_ci{
346962306a36Sopenharmony_ci	int ret;
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci	il->_4965.key_mapping_keys++;
347462306a36Sopenharmony_ci	keyconf->hw_key_idx = HW_KEY_DYNAMIC;
347562306a36Sopenharmony_ci
347662306a36Sopenharmony_ci	switch (keyconf->cipher) {
347762306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
347862306a36Sopenharmony_ci		ret =
347962306a36Sopenharmony_ci		    il4965_set_ccmp_dynamic_key_info(il, keyconf, sta_id);
348062306a36Sopenharmony_ci		break;
348162306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
348262306a36Sopenharmony_ci		ret =
348362306a36Sopenharmony_ci		    il4965_set_tkip_dynamic_key_info(il, keyconf, sta_id);
348462306a36Sopenharmony_ci		break;
348562306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
348662306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
348762306a36Sopenharmony_ci		ret = il4965_set_wep_dynamic_key_info(il, keyconf, sta_id);
348862306a36Sopenharmony_ci		break;
348962306a36Sopenharmony_ci	default:
349062306a36Sopenharmony_ci		IL_ERR("Unknown alg: %s cipher = %x\n", __func__,
349162306a36Sopenharmony_ci		       keyconf->cipher);
349262306a36Sopenharmony_ci		ret = -EINVAL;
349362306a36Sopenharmony_ci	}
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ci	D_WEP("Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n",
349662306a36Sopenharmony_ci	      keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret);
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_ci	return ret;
349962306a36Sopenharmony_ci}
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci/*
350262306a36Sopenharmony_ci * il4965_alloc_bcast_station - add broadcast station into driver's station table.
350362306a36Sopenharmony_ci *
350462306a36Sopenharmony_ci * This adds the broadcast station into the driver's station table
350562306a36Sopenharmony_ci * and marks it driver active, so that it will be restored to the
350662306a36Sopenharmony_ci * device at the next best time.
350762306a36Sopenharmony_ci */
350862306a36Sopenharmony_ciint
350962306a36Sopenharmony_ciil4965_alloc_bcast_station(struct il_priv *il)
351062306a36Sopenharmony_ci{
351162306a36Sopenharmony_ci	struct il_link_quality_cmd *link_cmd;
351262306a36Sopenharmony_ci	unsigned long flags;
351362306a36Sopenharmony_ci	u8 sta_id;
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
351662306a36Sopenharmony_ci	sta_id = il_prep_station(il, il_bcast_addr, false, NULL);
351762306a36Sopenharmony_ci	if (sta_id == IL_INVALID_STATION) {
351862306a36Sopenharmony_ci		IL_ERR("Unable to prepare broadcast station\n");
351962306a36Sopenharmony_ci		spin_unlock_irqrestore(&il->sta_lock, flags);
352062306a36Sopenharmony_ci
352162306a36Sopenharmony_ci		return -EINVAL;
352262306a36Sopenharmony_ci	}
352362306a36Sopenharmony_ci
352462306a36Sopenharmony_ci	il->stations[sta_id].used |= IL_STA_DRIVER_ACTIVE;
352562306a36Sopenharmony_ci	il->stations[sta_id].used |= IL_STA_BCAST;
352662306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci	link_cmd = il4965_sta_alloc_lq(il, sta_id);
352962306a36Sopenharmony_ci	if (!link_cmd) {
353062306a36Sopenharmony_ci		IL_ERR
353162306a36Sopenharmony_ci		    ("Unable to initialize rate scaling for bcast station.\n");
353262306a36Sopenharmony_ci		return -ENOMEM;
353362306a36Sopenharmony_ci	}
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
353662306a36Sopenharmony_ci	il->stations[sta_id].lq = link_cmd;
353762306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
353862306a36Sopenharmony_ci
353962306a36Sopenharmony_ci	return 0;
354062306a36Sopenharmony_ci}
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci/*
354362306a36Sopenharmony_ci * il4965_update_bcast_station - update broadcast station's LQ command
354462306a36Sopenharmony_ci *
354562306a36Sopenharmony_ci * Only used by iwl4965. Placed here to have all bcast station management
354662306a36Sopenharmony_ci * code together.
354762306a36Sopenharmony_ci */
354862306a36Sopenharmony_cistatic int
354962306a36Sopenharmony_ciil4965_update_bcast_station(struct il_priv *il)
355062306a36Sopenharmony_ci{
355162306a36Sopenharmony_ci	unsigned long flags;
355262306a36Sopenharmony_ci	struct il_link_quality_cmd *link_cmd;
355362306a36Sopenharmony_ci	u8 sta_id = il->hw_params.bcast_id;
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ci	link_cmd = il4965_sta_alloc_lq(il, sta_id);
355662306a36Sopenharmony_ci	if (!link_cmd) {
355762306a36Sopenharmony_ci		IL_ERR("Unable to initialize rate scaling for bcast sta.\n");
355862306a36Sopenharmony_ci		return -ENOMEM;
355962306a36Sopenharmony_ci	}
356062306a36Sopenharmony_ci
356162306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
356262306a36Sopenharmony_ci	if (il->stations[sta_id].lq)
356362306a36Sopenharmony_ci		kfree(il->stations[sta_id].lq);
356462306a36Sopenharmony_ci	else
356562306a36Sopenharmony_ci		D_INFO("Bcast sta rate scaling has not been initialized.\n");
356662306a36Sopenharmony_ci	il->stations[sta_id].lq = link_cmd;
356762306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
356862306a36Sopenharmony_ci
356962306a36Sopenharmony_ci	return 0;
357062306a36Sopenharmony_ci}
357162306a36Sopenharmony_ci
357262306a36Sopenharmony_ciint
357362306a36Sopenharmony_ciil4965_update_bcast_stations(struct il_priv *il)
357462306a36Sopenharmony_ci{
357562306a36Sopenharmony_ci	return il4965_update_bcast_station(il);
357662306a36Sopenharmony_ci}
357762306a36Sopenharmony_ci
357862306a36Sopenharmony_ci/*
357962306a36Sopenharmony_ci * il4965_sta_tx_modify_enable_tid - Enable Tx for this TID in station table
358062306a36Sopenharmony_ci */
358162306a36Sopenharmony_ciint
358262306a36Sopenharmony_ciil4965_sta_tx_modify_enable_tid(struct il_priv *il, int sta_id, int tid)
358362306a36Sopenharmony_ci{
358462306a36Sopenharmony_ci	unsigned long flags;
358562306a36Sopenharmony_ci	struct il_addsta_cmd sta_cmd;
358662306a36Sopenharmony_ci
358762306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
358862306a36Sopenharmony_ci
358962306a36Sopenharmony_ci	/* Remove "disable" flag, to enable Tx for this TID */
359062306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
359162306a36Sopenharmony_ci	il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX;
359262306a36Sopenharmony_ci	il->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid));
359362306a36Sopenharmony_ci	il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
359462306a36Sopenharmony_ci	memcpy(&sta_cmd, &il->stations[sta_id].sta,
359562306a36Sopenharmony_ci	       sizeof(struct il_addsta_cmd));
359662306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
359762306a36Sopenharmony_ci
359862306a36Sopenharmony_ci	return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
359962306a36Sopenharmony_ci}
360062306a36Sopenharmony_ci
360162306a36Sopenharmony_ciint
360262306a36Sopenharmony_ciil4965_sta_rx_agg_start(struct il_priv *il, struct ieee80211_sta *sta, int tid,
360362306a36Sopenharmony_ci			u16 ssn)
360462306a36Sopenharmony_ci{
360562306a36Sopenharmony_ci	unsigned long flags;
360662306a36Sopenharmony_ci	int sta_id;
360762306a36Sopenharmony_ci	struct il_addsta_cmd sta_cmd;
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
361062306a36Sopenharmony_ci
361162306a36Sopenharmony_ci	sta_id = il_sta_id(sta);
361262306a36Sopenharmony_ci	if (sta_id == IL_INVALID_STATION)
361362306a36Sopenharmony_ci		return -ENXIO;
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
361662306a36Sopenharmony_ci	il->stations[sta_id].sta.station_flags_msk = 0;
361762306a36Sopenharmony_ci	il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
361862306a36Sopenharmony_ci	il->stations[sta_id].sta.add_immediate_ba_tid = (u8) tid;
361962306a36Sopenharmony_ci	il->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
362062306a36Sopenharmony_ci	il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
362162306a36Sopenharmony_ci	memcpy(&sta_cmd, &il->stations[sta_id].sta,
362262306a36Sopenharmony_ci	       sizeof(struct il_addsta_cmd));
362362306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
362462306a36Sopenharmony_ci
362562306a36Sopenharmony_ci	return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
362662306a36Sopenharmony_ci}
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_ciint
362962306a36Sopenharmony_ciil4965_sta_rx_agg_stop(struct il_priv *il, struct ieee80211_sta *sta, int tid)
363062306a36Sopenharmony_ci{
363162306a36Sopenharmony_ci	unsigned long flags;
363262306a36Sopenharmony_ci	int sta_id;
363362306a36Sopenharmony_ci	struct il_addsta_cmd sta_cmd;
363462306a36Sopenharmony_ci
363562306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_ci	sta_id = il_sta_id(sta);
363862306a36Sopenharmony_ci	if (sta_id == IL_INVALID_STATION) {
363962306a36Sopenharmony_ci		IL_ERR("Invalid station for AGG tid %d\n", tid);
364062306a36Sopenharmony_ci		return -ENXIO;
364162306a36Sopenharmony_ci	}
364262306a36Sopenharmony_ci
364362306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
364462306a36Sopenharmony_ci	il->stations[sta_id].sta.station_flags_msk = 0;
364562306a36Sopenharmony_ci	il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
364662306a36Sopenharmony_ci	il->stations[sta_id].sta.remove_immediate_ba_tid = (u8) tid;
364762306a36Sopenharmony_ci	il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
364862306a36Sopenharmony_ci	memcpy(&sta_cmd, &il->stations[sta_id].sta,
364962306a36Sopenharmony_ci	       sizeof(struct il_addsta_cmd));
365062306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
365162306a36Sopenharmony_ci
365262306a36Sopenharmony_ci	return il_send_add_sta(il, &sta_cmd, CMD_SYNC);
365362306a36Sopenharmony_ci}
365462306a36Sopenharmony_ci
365562306a36Sopenharmony_civoid
365662306a36Sopenharmony_ciil4965_sta_modify_sleep_tx_count(struct il_priv *il, int sta_id, int cnt)
365762306a36Sopenharmony_ci{
365862306a36Sopenharmony_ci	unsigned long flags;
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
366162306a36Sopenharmony_ci	il->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK;
366262306a36Sopenharmony_ci	il->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
366362306a36Sopenharmony_ci	il->stations[sta_id].sta.sta.modify_mask =
366462306a36Sopenharmony_ci	    STA_MODIFY_SLEEP_TX_COUNT_MSK;
366562306a36Sopenharmony_ci	il->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt);
366662306a36Sopenharmony_ci	il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
366762306a36Sopenharmony_ci	il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC);
366862306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
366962306a36Sopenharmony_ci
367062306a36Sopenharmony_ci}
367162306a36Sopenharmony_ci
367262306a36Sopenharmony_civoid
367362306a36Sopenharmony_ciil4965_update_chain_flags(struct il_priv *il)
367462306a36Sopenharmony_ci{
367562306a36Sopenharmony_ci	if (il->ops->set_rxon_chain) {
367662306a36Sopenharmony_ci		il->ops->set_rxon_chain(il);
367762306a36Sopenharmony_ci		if (il->active.rx_chain != il->staging.rx_chain)
367862306a36Sopenharmony_ci			il_commit_rxon(il);
367962306a36Sopenharmony_ci	}
368062306a36Sopenharmony_ci}
368162306a36Sopenharmony_ci
368262306a36Sopenharmony_cistatic void
368362306a36Sopenharmony_ciil4965_clear_free_frames(struct il_priv *il)
368462306a36Sopenharmony_ci{
368562306a36Sopenharmony_ci	struct list_head *element;
368662306a36Sopenharmony_ci
368762306a36Sopenharmony_ci	D_INFO("%d frames on pre-allocated heap on clear.\n", il->frames_count);
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_ci	while (!list_empty(&il->free_frames)) {
369062306a36Sopenharmony_ci		element = il->free_frames.next;
369162306a36Sopenharmony_ci		list_del(element);
369262306a36Sopenharmony_ci		kfree(list_entry(element, struct il_frame, list));
369362306a36Sopenharmony_ci		il->frames_count--;
369462306a36Sopenharmony_ci	}
369562306a36Sopenharmony_ci
369662306a36Sopenharmony_ci	if (il->frames_count) {
369762306a36Sopenharmony_ci		IL_WARN("%d frames still in use.  Did we lose one?\n",
369862306a36Sopenharmony_ci			il->frames_count);
369962306a36Sopenharmony_ci		il->frames_count = 0;
370062306a36Sopenharmony_ci	}
370162306a36Sopenharmony_ci}
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_cistatic struct il_frame *
370462306a36Sopenharmony_ciil4965_get_free_frame(struct il_priv *il)
370562306a36Sopenharmony_ci{
370662306a36Sopenharmony_ci	struct il_frame *frame;
370762306a36Sopenharmony_ci	struct list_head *element;
370862306a36Sopenharmony_ci	if (list_empty(&il->free_frames)) {
370962306a36Sopenharmony_ci		frame = kzalloc(sizeof(*frame), GFP_KERNEL);
371062306a36Sopenharmony_ci		if (!frame) {
371162306a36Sopenharmony_ci			IL_ERR("Could not allocate frame!\n");
371262306a36Sopenharmony_ci			return NULL;
371362306a36Sopenharmony_ci		}
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_ci		il->frames_count++;
371662306a36Sopenharmony_ci		return frame;
371762306a36Sopenharmony_ci	}
371862306a36Sopenharmony_ci
371962306a36Sopenharmony_ci	element = il->free_frames.next;
372062306a36Sopenharmony_ci	list_del(element);
372162306a36Sopenharmony_ci	return list_entry(element, struct il_frame, list);
372262306a36Sopenharmony_ci}
372362306a36Sopenharmony_ci
372462306a36Sopenharmony_cistatic void
372562306a36Sopenharmony_ciil4965_free_frame(struct il_priv *il, struct il_frame *frame)
372662306a36Sopenharmony_ci{
372762306a36Sopenharmony_ci	memset(frame, 0, sizeof(*frame));
372862306a36Sopenharmony_ci	list_add(&frame->list, &il->free_frames);
372962306a36Sopenharmony_ci}
373062306a36Sopenharmony_ci
373162306a36Sopenharmony_cistatic u32
373262306a36Sopenharmony_ciil4965_fill_beacon_frame(struct il_priv *il, struct ieee80211_hdr *hdr,
373362306a36Sopenharmony_ci			 int left)
373462306a36Sopenharmony_ci{
373562306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
373662306a36Sopenharmony_ci
373762306a36Sopenharmony_ci	if (!il->beacon_skb)
373862306a36Sopenharmony_ci		return 0;
373962306a36Sopenharmony_ci
374062306a36Sopenharmony_ci	if (il->beacon_skb->len > left)
374162306a36Sopenharmony_ci		return 0;
374262306a36Sopenharmony_ci
374362306a36Sopenharmony_ci	memcpy(hdr, il->beacon_skb->data, il->beacon_skb->len);
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_ci	return il->beacon_skb->len;
374662306a36Sopenharmony_ci}
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_ci/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */
374962306a36Sopenharmony_cistatic void
375062306a36Sopenharmony_ciil4965_set_beacon_tim(struct il_priv *il,
375162306a36Sopenharmony_ci		      struct il_tx_beacon_cmd *tx_beacon_cmd, u8 * beacon,
375262306a36Sopenharmony_ci		      u32 frame_size)
375362306a36Sopenharmony_ci{
375462306a36Sopenharmony_ci	u16 tim_idx;
375562306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
375662306a36Sopenharmony_ci
375762306a36Sopenharmony_ci	/*
375862306a36Sopenharmony_ci	 * The idx is relative to frame start but we start looking at the
375962306a36Sopenharmony_ci	 * variable-length part of the beacon.
376062306a36Sopenharmony_ci	 */
376162306a36Sopenharmony_ci	tim_idx = mgmt->u.beacon.variable - beacon;
376262306a36Sopenharmony_ci
376362306a36Sopenharmony_ci	/* Parse variable-length elements of beacon to find WLAN_EID_TIM */
376462306a36Sopenharmony_ci	while ((tim_idx < (frame_size - 2)) &&
376562306a36Sopenharmony_ci	       (beacon[tim_idx] != WLAN_EID_TIM))
376662306a36Sopenharmony_ci		tim_idx += beacon[tim_idx + 1] + 2;
376762306a36Sopenharmony_ci
376862306a36Sopenharmony_ci	/* If TIM field was found, set variables */
376962306a36Sopenharmony_ci	if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
377062306a36Sopenharmony_ci		tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx);
377162306a36Sopenharmony_ci		tx_beacon_cmd->tim_size = beacon[tim_idx + 1];
377262306a36Sopenharmony_ci	} else
377362306a36Sopenharmony_ci		IL_WARN("Unable to find TIM Element in beacon\n");
377462306a36Sopenharmony_ci}
377562306a36Sopenharmony_ci
377662306a36Sopenharmony_cistatic unsigned int
377762306a36Sopenharmony_ciil4965_hw_get_beacon_cmd(struct il_priv *il, struct il_frame *frame)
377862306a36Sopenharmony_ci{
377962306a36Sopenharmony_ci	struct il_tx_beacon_cmd *tx_beacon_cmd;
378062306a36Sopenharmony_ci	u32 frame_size;
378162306a36Sopenharmony_ci	u32 rate_flags;
378262306a36Sopenharmony_ci	u32 rate;
378362306a36Sopenharmony_ci	/*
378462306a36Sopenharmony_ci	 * We have to set up the TX command, the TX Beacon command, and the
378562306a36Sopenharmony_ci	 * beacon contents.
378662306a36Sopenharmony_ci	 */
378762306a36Sopenharmony_ci
378862306a36Sopenharmony_ci	lockdep_assert_held(&il->mutex);
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_ci	if (!il->beacon_enabled) {
379162306a36Sopenharmony_ci		IL_ERR("Trying to build beacon without beaconing enabled\n");
379262306a36Sopenharmony_ci		return 0;
379362306a36Sopenharmony_ci	}
379462306a36Sopenharmony_ci
379562306a36Sopenharmony_ci	/* Initialize memory */
379662306a36Sopenharmony_ci	tx_beacon_cmd = &frame->u.beacon;
379762306a36Sopenharmony_ci	memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd));
379862306a36Sopenharmony_ci
379962306a36Sopenharmony_ci	/* Set up TX beacon contents */
380062306a36Sopenharmony_ci	frame_size =
380162306a36Sopenharmony_ci	    il4965_fill_beacon_frame(il, tx_beacon_cmd->frame,
380262306a36Sopenharmony_ci				     sizeof(frame->u) - sizeof(*tx_beacon_cmd));
380362306a36Sopenharmony_ci	if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE))
380462306a36Sopenharmony_ci		return 0;
380562306a36Sopenharmony_ci	if (!frame_size)
380662306a36Sopenharmony_ci		return 0;
380762306a36Sopenharmony_ci
380862306a36Sopenharmony_ci	/* Set up TX command fields */
380962306a36Sopenharmony_ci	tx_beacon_cmd->tx.len = cpu_to_le16((u16) frame_size);
381062306a36Sopenharmony_ci	tx_beacon_cmd->tx.sta_id = il->hw_params.bcast_id;
381162306a36Sopenharmony_ci	tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
381262306a36Sopenharmony_ci	tx_beacon_cmd->tx.tx_flags =
381362306a36Sopenharmony_ci	    TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK |
381462306a36Sopenharmony_ci	    TX_CMD_FLG_STA_RATE_MSK;
381562306a36Sopenharmony_ci
381662306a36Sopenharmony_ci	/* Set up TX beacon command fields */
381762306a36Sopenharmony_ci	il4965_set_beacon_tim(il, tx_beacon_cmd, (u8 *) tx_beacon_cmd->frame,
381862306a36Sopenharmony_ci			      frame_size);
381962306a36Sopenharmony_ci
382062306a36Sopenharmony_ci	/* Set up packet rate and flags */
382162306a36Sopenharmony_ci	rate = il_get_lowest_plcp(il);
382262306a36Sopenharmony_ci	il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant);
382362306a36Sopenharmony_ci	rate_flags = BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS;
382462306a36Sopenharmony_ci	if ((rate >= IL_FIRST_CCK_RATE) && (rate <= IL_LAST_CCK_RATE))
382562306a36Sopenharmony_ci		rate_flags |= RATE_MCS_CCK_MSK;
382662306a36Sopenharmony_ci	tx_beacon_cmd->tx.rate_n_flags = cpu_to_le32(rate | rate_flags);
382762306a36Sopenharmony_ci
382862306a36Sopenharmony_ci	return sizeof(*tx_beacon_cmd) + frame_size;
382962306a36Sopenharmony_ci}
383062306a36Sopenharmony_ci
383162306a36Sopenharmony_ciint
383262306a36Sopenharmony_ciil4965_send_beacon_cmd(struct il_priv *il)
383362306a36Sopenharmony_ci{
383462306a36Sopenharmony_ci	struct il_frame *frame;
383562306a36Sopenharmony_ci	unsigned int frame_size;
383662306a36Sopenharmony_ci	int rc;
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci	frame = il4965_get_free_frame(il);
383962306a36Sopenharmony_ci	if (!frame) {
384062306a36Sopenharmony_ci		IL_ERR("Could not obtain free frame buffer for beacon "
384162306a36Sopenharmony_ci		       "command.\n");
384262306a36Sopenharmony_ci		return -ENOMEM;
384362306a36Sopenharmony_ci	}
384462306a36Sopenharmony_ci
384562306a36Sopenharmony_ci	frame_size = il4965_hw_get_beacon_cmd(il, frame);
384662306a36Sopenharmony_ci	if (!frame_size) {
384762306a36Sopenharmony_ci		IL_ERR("Error configuring the beacon command\n");
384862306a36Sopenharmony_ci		il4965_free_frame(il, frame);
384962306a36Sopenharmony_ci		return -EINVAL;
385062306a36Sopenharmony_ci	}
385162306a36Sopenharmony_ci
385262306a36Sopenharmony_ci	rc = il_send_cmd_pdu(il, C_TX_BEACON, frame_size, &frame->u.cmd[0]);
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci	il4965_free_frame(il, frame);
385562306a36Sopenharmony_ci
385662306a36Sopenharmony_ci	return rc;
385762306a36Sopenharmony_ci}
385862306a36Sopenharmony_ci
385962306a36Sopenharmony_cistatic inline dma_addr_t
386062306a36Sopenharmony_ciil4965_tfd_tb_get_addr(struct il_tfd *tfd, u8 idx)
386162306a36Sopenharmony_ci{
386262306a36Sopenharmony_ci	struct il_tfd_tb *tb = &tfd->tbs[idx];
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci	dma_addr_t addr = get_unaligned_le32(&tb->lo);
386562306a36Sopenharmony_ci	if (sizeof(dma_addr_t) > sizeof(u32))
386662306a36Sopenharmony_ci		addr |=
386762306a36Sopenharmony_ci		    ((dma_addr_t) (le16_to_cpu(tb->hi_n_len) & 0xF) << 16) <<
386862306a36Sopenharmony_ci		    16;
386962306a36Sopenharmony_ci
387062306a36Sopenharmony_ci	return addr;
387162306a36Sopenharmony_ci}
387262306a36Sopenharmony_ci
387362306a36Sopenharmony_cistatic inline u16
387462306a36Sopenharmony_ciil4965_tfd_tb_get_len(struct il_tfd *tfd, u8 idx)
387562306a36Sopenharmony_ci{
387662306a36Sopenharmony_ci	struct il_tfd_tb *tb = &tfd->tbs[idx];
387762306a36Sopenharmony_ci
387862306a36Sopenharmony_ci	return le16_to_cpu(tb->hi_n_len) >> 4;
387962306a36Sopenharmony_ci}
388062306a36Sopenharmony_ci
388162306a36Sopenharmony_cistatic inline void
388262306a36Sopenharmony_ciil4965_tfd_set_tb(struct il_tfd *tfd, u8 idx, dma_addr_t addr, u16 len)
388362306a36Sopenharmony_ci{
388462306a36Sopenharmony_ci	struct il_tfd_tb *tb = &tfd->tbs[idx];
388562306a36Sopenharmony_ci	u16 hi_n_len = len << 4;
388662306a36Sopenharmony_ci
388762306a36Sopenharmony_ci	put_unaligned_le32(addr, &tb->lo);
388862306a36Sopenharmony_ci	if (sizeof(dma_addr_t) > sizeof(u32))
388962306a36Sopenharmony_ci		hi_n_len |= ((addr >> 16) >> 16) & 0xF;
389062306a36Sopenharmony_ci
389162306a36Sopenharmony_ci	tb->hi_n_len = cpu_to_le16(hi_n_len);
389262306a36Sopenharmony_ci
389362306a36Sopenharmony_ci	tfd->num_tbs = idx + 1;
389462306a36Sopenharmony_ci}
389562306a36Sopenharmony_ci
389662306a36Sopenharmony_cistatic inline u8
389762306a36Sopenharmony_ciil4965_tfd_get_num_tbs(struct il_tfd *tfd)
389862306a36Sopenharmony_ci{
389962306a36Sopenharmony_ci	return tfd->num_tbs & 0x1f;
390062306a36Sopenharmony_ci}
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci/*
390362306a36Sopenharmony_ci * il4965_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr]
390462306a36Sopenharmony_ci *
390562306a36Sopenharmony_ci * Does NOT advance any TFD circular buffer read/write idxes
390662306a36Sopenharmony_ci * Does NOT free the TFD itself (which is within circular buffer)
390762306a36Sopenharmony_ci */
390862306a36Sopenharmony_civoid
390962306a36Sopenharmony_ciil4965_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq)
391062306a36Sopenharmony_ci{
391162306a36Sopenharmony_ci	struct il_tfd *tfd_tmp = (struct il_tfd *)txq->tfds;
391262306a36Sopenharmony_ci	struct il_tfd *tfd;
391362306a36Sopenharmony_ci	struct pci_dev *dev = il->pci_dev;
391462306a36Sopenharmony_ci	int idx = txq->q.read_ptr;
391562306a36Sopenharmony_ci	int i;
391662306a36Sopenharmony_ci	int num_tbs;
391762306a36Sopenharmony_ci
391862306a36Sopenharmony_ci	tfd = &tfd_tmp[idx];
391962306a36Sopenharmony_ci
392062306a36Sopenharmony_ci	/* Sanity check on number of chunks */
392162306a36Sopenharmony_ci	num_tbs = il4965_tfd_get_num_tbs(tfd);
392262306a36Sopenharmony_ci
392362306a36Sopenharmony_ci	if (num_tbs >= IL_NUM_OF_TBS) {
392462306a36Sopenharmony_ci		IL_ERR("Too many chunks: %i\n", num_tbs);
392562306a36Sopenharmony_ci		/* @todo issue fatal error, it is quite serious situation */
392662306a36Sopenharmony_ci		return;
392762306a36Sopenharmony_ci	}
392862306a36Sopenharmony_ci
392962306a36Sopenharmony_ci	/* Unmap tx_cmd */
393062306a36Sopenharmony_ci	if (num_tbs)
393162306a36Sopenharmony_ci		dma_unmap_single(&dev->dev,
393262306a36Sopenharmony_ci				 dma_unmap_addr(&txq->meta[idx], mapping),
393362306a36Sopenharmony_ci				 dma_unmap_len(&txq->meta[idx], len),
393462306a36Sopenharmony_ci				 DMA_BIDIRECTIONAL);
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci	/* Unmap chunks, if any. */
393762306a36Sopenharmony_ci	for (i = 1; i < num_tbs; i++)
393862306a36Sopenharmony_ci		dma_unmap_single(&dev->dev, il4965_tfd_tb_get_addr(tfd, i),
393962306a36Sopenharmony_ci				 il4965_tfd_tb_get_len(tfd, i), DMA_TO_DEVICE);
394062306a36Sopenharmony_ci
394162306a36Sopenharmony_ci	/* free SKB */
394262306a36Sopenharmony_ci	if (txq->skbs) {
394362306a36Sopenharmony_ci		struct sk_buff *skb = txq->skbs[txq->q.read_ptr];
394462306a36Sopenharmony_ci
394562306a36Sopenharmony_ci		/* can be called from irqs-disabled context */
394662306a36Sopenharmony_ci		if (skb) {
394762306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
394862306a36Sopenharmony_ci			txq->skbs[txq->q.read_ptr] = NULL;
394962306a36Sopenharmony_ci		}
395062306a36Sopenharmony_ci	}
395162306a36Sopenharmony_ci}
395262306a36Sopenharmony_ci
395362306a36Sopenharmony_ciint
395462306a36Sopenharmony_ciil4965_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq,
395562306a36Sopenharmony_ci				dma_addr_t addr, u16 len, u8 reset, u8 pad)
395662306a36Sopenharmony_ci{
395762306a36Sopenharmony_ci	struct il_queue *q;
395862306a36Sopenharmony_ci	struct il_tfd *tfd, *tfd_tmp;
395962306a36Sopenharmony_ci	u32 num_tbs;
396062306a36Sopenharmony_ci
396162306a36Sopenharmony_ci	q = &txq->q;
396262306a36Sopenharmony_ci	tfd_tmp = (struct il_tfd *)txq->tfds;
396362306a36Sopenharmony_ci	tfd = &tfd_tmp[q->write_ptr];
396462306a36Sopenharmony_ci
396562306a36Sopenharmony_ci	if (reset)
396662306a36Sopenharmony_ci		memset(tfd, 0, sizeof(*tfd));
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_ci	num_tbs = il4965_tfd_get_num_tbs(tfd);
396962306a36Sopenharmony_ci
397062306a36Sopenharmony_ci	/* Each TFD can point to a maximum 20 Tx buffers */
397162306a36Sopenharmony_ci	if (num_tbs >= IL_NUM_OF_TBS) {
397262306a36Sopenharmony_ci		IL_ERR("Error can not send more than %d chunks\n",
397362306a36Sopenharmony_ci		       IL_NUM_OF_TBS);
397462306a36Sopenharmony_ci		return -EINVAL;
397562306a36Sopenharmony_ci	}
397662306a36Sopenharmony_ci
397762306a36Sopenharmony_ci	BUG_ON(addr & ~DMA_BIT_MASK(36));
397862306a36Sopenharmony_ci	if (unlikely(addr & ~IL_TX_DMA_MASK))
397962306a36Sopenharmony_ci		IL_ERR("Unaligned address = %llx\n", (unsigned long long)addr);
398062306a36Sopenharmony_ci
398162306a36Sopenharmony_ci	il4965_tfd_set_tb(tfd, num_tbs, addr, len);
398262306a36Sopenharmony_ci
398362306a36Sopenharmony_ci	return 0;
398462306a36Sopenharmony_ci}
398562306a36Sopenharmony_ci
398662306a36Sopenharmony_ci/*
398762306a36Sopenharmony_ci * Tell nic where to find circular buffer of Tx Frame Descriptors for
398862306a36Sopenharmony_ci * given Tx queue, and enable the DMA channel used for that queue.
398962306a36Sopenharmony_ci *
399062306a36Sopenharmony_ci * 4965 supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA
399162306a36Sopenharmony_ci * channels supported in hardware.
399262306a36Sopenharmony_ci */
399362306a36Sopenharmony_ciint
399462306a36Sopenharmony_ciil4965_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq)
399562306a36Sopenharmony_ci{
399662306a36Sopenharmony_ci	int txq_id = txq->q.id;
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci	/* Circular buffer (TFD queue in DRAM) physical base address */
399962306a36Sopenharmony_ci	il_wr(il, FH49_MEM_CBBC_QUEUE(txq_id), txq->q.dma_addr >> 8);
400062306a36Sopenharmony_ci
400162306a36Sopenharmony_ci	return 0;
400262306a36Sopenharmony_ci}
400362306a36Sopenharmony_ci
400462306a36Sopenharmony_ci/******************************************************************************
400562306a36Sopenharmony_ci *
400662306a36Sopenharmony_ci * Generic RX handler implementations
400762306a36Sopenharmony_ci *
400862306a36Sopenharmony_ci ******************************************************************************/
400962306a36Sopenharmony_cistatic void
401062306a36Sopenharmony_ciil4965_hdl_alive(struct il_priv *il, struct il_rx_buf *rxb)
401162306a36Sopenharmony_ci{
401262306a36Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
401362306a36Sopenharmony_ci	struct il_alive_resp *palive;
401462306a36Sopenharmony_ci	struct delayed_work *pwork;
401562306a36Sopenharmony_ci
401662306a36Sopenharmony_ci	palive = &pkt->u.alive_frame;
401762306a36Sopenharmony_ci
401862306a36Sopenharmony_ci	D_INFO("Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n",
401962306a36Sopenharmony_ci	       palive->is_valid, palive->ver_type, palive->ver_subtype);
402062306a36Sopenharmony_ci
402162306a36Sopenharmony_ci	if (palive->ver_subtype == INITIALIZE_SUBTYPE) {
402262306a36Sopenharmony_ci		D_INFO("Initialization Alive received.\n");
402362306a36Sopenharmony_ci		memcpy(&il->card_alive_init, &pkt->u.raw,
402462306a36Sopenharmony_ci		       sizeof(struct il_init_alive_resp));
402562306a36Sopenharmony_ci		pwork = &il->init_alive_start;
402662306a36Sopenharmony_ci	} else {
402762306a36Sopenharmony_ci		D_INFO("Runtime Alive received.\n");
402862306a36Sopenharmony_ci		memcpy(&il->card_alive, &pkt->u.alive_frame,
402962306a36Sopenharmony_ci		       sizeof(struct il_alive_resp));
403062306a36Sopenharmony_ci		pwork = &il->alive_start;
403162306a36Sopenharmony_ci	}
403262306a36Sopenharmony_ci
403362306a36Sopenharmony_ci	/* We delay the ALIVE response by 5ms to
403462306a36Sopenharmony_ci	 * give the HW RF Kill time to activate... */
403562306a36Sopenharmony_ci	if (palive->is_valid == UCODE_VALID_OK)
403662306a36Sopenharmony_ci		queue_delayed_work(il->workqueue, pwork, msecs_to_jiffies(5));
403762306a36Sopenharmony_ci	else
403862306a36Sopenharmony_ci		IL_WARN("uCode did not respond OK.\n");
403962306a36Sopenharmony_ci}
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci/*
404262306a36Sopenharmony_ci * il4965_bg_stats_periodic - Timer callback to queue stats
404362306a36Sopenharmony_ci *
404462306a36Sopenharmony_ci * This callback is provided in order to send a stats request.
404562306a36Sopenharmony_ci *
404662306a36Sopenharmony_ci * This timer function is continually reset to execute within
404762306a36Sopenharmony_ci * 60 seconds since the last N_STATS was received.  We need to
404862306a36Sopenharmony_ci * ensure we receive the stats in order to update the temperature
404962306a36Sopenharmony_ci * used for calibrating the TXPOWER.
405062306a36Sopenharmony_ci */
405162306a36Sopenharmony_cistatic void
405262306a36Sopenharmony_ciil4965_bg_stats_periodic(struct timer_list *t)
405362306a36Sopenharmony_ci{
405462306a36Sopenharmony_ci	struct il_priv *il = from_timer(il, t, stats_periodic);
405562306a36Sopenharmony_ci
405662306a36Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status))
405762306a36Sopenharmony_ci		return;
405862306a36Sopenharmony_ci
405962306a36Sopenharmony_ci	/* dont send host command if rf-kill is on */
406062306a36Sopenharmony_ci	if (!il_is_ready_rf(il))
406162306a36Sopenharmony_ci		return;
406262306a36Sopenharmony_ci
406362306a36Sopenharmony_ci	il_send_stats_request(il, CMD_ASYNC, false);
406462306a36Sopenharmony_ci}
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_cistatic void
406762306a36Sopenharmony_ciil4965_hdl_beacon(struct il_priv *il, struct il_rx_buf *rxb)
406862306a36Sopenharmony_ci{
406962306a36Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
407062306a36Sopenharmony_ci	struct il4965_beacon_notif *beacon =
407162306a36Sopenharmony_ci	    (struct il4965_beacon_notif *)pkt->u.raw;
407262306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
407362306a36Sopenharmony_ci	u8 rate = il4965_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
407462306a36Sopenharmony_ci
407562306a36Sopenharmony_ci	D_RX("beacon status %x retries %d iss %d tsf:0x%.8x%.8x rate %d\n",
407662306a36Sopenharmony_ci	     le32_to_cpu(beacon->beacon_notify_hdr.u.status) & TX_STATUS_MSK,
407762306a36Sopenharmony_ci	     beacon->beacon_notify_hdr.failure_frame,
407862306a36Sopenharmony_ci	     le32_to_cpu(beacon->ibss_mgr_status),
407962306a36Sopenharmony_ci	     le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate);
408062306a36Sopenharmony_ci#endif
408162306a36Sopenharmony_ci	il->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status);
408262306a36Sopenharmony_ci}
408362306a36Sopenharmony_ci
408462306a36Sopenharmony_cistatic void
408562306a36Sopenharmony_ciil4965_perform_ct_kill_task(struct il_priv *il)
408662306a36Sopenharmony_ci{
408762306a36Sopenharmony_ci	unsigned long flags;
408862306a36Sopenharmony_ci
408962306a36Sopenharmony_ci	D_POWER("Stop all queues\n");
409062306a36Sopenharmony_ci
409162306a36Sopenharmony_ci	if (il->mac80211_registered)
409262306a36Sopenharmony_ci		ieee80211_stop_queues(il->hw);
409362306a36Sopenharmony_ci
409462306a36Sopenharmony_ci	_il_wr(il, CSR_UCODE_DRV_GP1_SET,
409562306a36Sopenharmony_ci	       CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
409662306a36Sopenharmony_ci	_il_rd(il, CSR_UCODE_DRV_GP1);
409762306a36Sopenharmony_ci
409862306a36Sopenharmony_ci	spin_lock_irqsave(&il->reg_lock, flags);
409962306a36Sopenharmony_ci	if (likely(_il_grab_nic_access(il)))
410062306a36Sopenharmony_ci		_il_release_nic_access(il);
410162306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->reg_lock, flags);
410262306a36Sopenharmony_ci}
410362306a36Sopenharmony_ci
410462306a36Sopenharmony_ci/* Handle notification from uCode that card's power state is changing
410562306a36Sopenharmony_ci * due to software, hardware, or critical temperature RFKILL */
410662306a36Sopenharmony_cistatic void
410762306a36Sopenharmony_ciil4965_hdl_card_state(struct il_priv *il, struct il_rx_buf *rxb)
410862306a36Sopenharmony_ci{
410962306a36Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
411062306a36Sopenharmony_ci	u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
411162306a36Sopenharmony_ci	unsigned long status = il->status;
411262306a36Sopenharmony_ci
411362306a36Sopenharmony_ci	D_RF_KILL("Card state received: HW:%s SW:%s CT:%s\n",
411462306a36Sopenharmony_ci		  (flags & HW_CARD_DISABLED) ? "Kill" : "On",
411562306a36Sopenharmony_ci		  (flags & SW_CARD_DISABLED) ? "Kill" : "On",
411662306a36Sopenharmony_ci		  (flags & CT_CARD_DISABLED) ? "Reached" : "Not reached");
411762306a36Sopenharmony_ci
411862306a36Sopenharmony_ci	if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | CT_CARD_DISABLED)) {
411962306a36Sopenharmony_ci
412062306a36Sopenharmony_ci		_il_wr(il, CSR_UCODE_DRV_GP1_SET,
412162306a36Sopenharmony_ci		       CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
412262306a36Sopenharmony_ci
412362306a36Sopenharmony_ci		il_wr(il, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
412462306a36Sopenharmony_ci
412562306a36Sopenharmony_ci		if (!(flags & RXON_CARD_DISABLED)) {
412662306a36Sopenharmony_ci			_il_wr(il, CSR_UCODE_DRV_GP1_CLR,
412762306a36Sopenharmony_ci			       CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
412862306a36Sopenharmony_ci			il_wr(il, HBUS_TARG_MBX_C,
412962306a36Sopenharmony_ci			      HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
413062306a36Sopenharmony_ci		}
413162306a36Sopenharmony_ci	}
413262306a36Sopenharmony_ci
413362306a36Sopenharmony_ci	if (flags & CT_CARD_DISABLED)
413462306a36Sopenharmony_ci		il4965_perform_ct_kill_task(il);
413562306a36Sopenharmony_ci
413662306a36Sopenharmony_ci	if (flags & HW_CARD_DISABLED)
413762306a36Sopenharmony_ci		set_bit(S_RFKILL, &il->status);
413862306a36Sopenharmony_ci	else
413962306a36Sopenharmony_ci		clear_bit(S_RFKILL, &il->status);
414062306a36Sopenharmony_ci
414162306a36Sopenharmony_ci	if (!(flags & RXON_CARD_DISABLED))
414262306a36Sopenharmony_ci		il_scan_cancel(il);
414362306a36Sopenharmony_ci
414462306a36Sopenharmony_ci	if ((test_bit(S_RFKILL, &status) !=
414562306a36Sopenharmony_ci	     test_bit(S_RFKILL, &il->status)))
414662306a36Sopenharmony_ci		wiphy_rfkill_set_hw_state(il->hw->wiphy,
414762306a36Sopenharmony_ci					  test_bit(S_RFKILL, &il->status));
414862306a36Sopenharmony_ci	else
414962306a36Sopenharmony_ci		wake_up(&il->wait_command_queue);
415062306a36Sopenharmony_ci}
415162306a36Sopenharmony_ci
415262306a36Sopenharmony_ci/*
415362306a36Sopenharmony_ci * il4965_setup_handlers - Initialize Rx handler callbacks
415462306a36Sopenharmony_ci *
415562306a36Sopenharmony_ci * Setup the RX handlers for each of the reply types sent from the uCode
415662306a36Sopenharmony_ci * to the host.
415762306a36Sopenharmony_ci *
415862306a36Sopenharmony_ci * This function chains into the hardware specific files for them to setup
415962306a36Sopenharmony_ci * any hardware specific handlers as well.
416062306a36Sopenharmony_ci */
416162306a36Sopenharmony_cistatic void
416262306a36Sopenharmony_ciil4965_setup_handlers(struct il_priv *il)
416362306a36Sopenharmony_ci{
416462306a36Sopenharmony_ci	il->handlers[N_ALIVE] = il4965_hdl_alive;
416562306a36Sopenharmony_ci	il->handlers[N_ERROR] = il_hdl_error;
416662306a36Sopenharmony_ci	il->handlers[N_CHANNEL_SWITCH] = il_hdl_csa;
416762306a36Sopenharmony_ci	il->handlers[N_SPECTRUM_MEASUREMENT] = il_hdl_spectrum_measurement;
416862306a36Sopenharmony_ci	il->handlers[N_PM_SLEEP] = il_hdl_pm_sleep;
416962306a36Sopenharmony_ci	il->handlers[N_PM_DEBUG_STATS] = il_hdl_pm_debug_stats;
417062306a36Sopenharmony_ci	il->handlers[N_BEACON] = il4965_hdl_beacon;
417162306a36Sopenharmony_ci
417262306a36Sopenharmony_ci	/*
417362306a36Sopenharmony_ci	 * The same handler is used for both the REPLY to a discrete
417462306a36Sopenharmony_ci	 * stats request from the host as well as for the periodic
417562306a36Sopenharmony_ci	 * stats notifications (after received beacons) from the uCode.
417662306a36Sopenharmony_ci	 */
417762306a36Sopenharmony_ci	il->handlers[C_STATS] = il4965_hdl_c_stats;
417862306a36Sopenharmony_ci	il->handlers[N_STATS] = il4965_hdl_stats;
417962306a36Sopenharmony_ci
418062306a36Sopenharmony_ci	il_setup_rx_scan_handlers(il);
418162306a36Sopenharmony_ci
418262306a36Sopenharmony_ci	/* status change handler */
418362306a36Sopenharmony_ci	il->handlers[N_CARD_STATE] = il4965_hdl_card_state;
418462306a36Sopenharmony_ci
418562306a36Sopenharmony_ci	il->handlers[N_MISSED_BEACONS] = il4965_hdl_missed_beacon;
418662306a36Sopenharmony_ci	/* Rx handlers */
418762306a36Sopenharmony_ci	il->handlers[N_RX_PHY] = il4965_hdl_rx_phy;
418862306a36Sopenharmony_ci	il->handlers[N_RX_MPDU] = il4965_hdl_rx;
418962306a36Sopenharmony_ci	il->handlers[N_RX] = il4965_hdl_rx;
419062306a36Sopenharmony_ci	/* block ack */
419162306a36Sopenharmony_ci	il->handlers[N_COMPRESSED_BA] = il4965_hdl_compressed_ba;
419262306a36Sopenharmony_ci	/* Tx response */
419362306a36Sopenharmony_ci	il->handlers[C_TX] = il4965_hdl_tx;
419462306a36Sopenharmony_ci}
419562306a36Sopenharmony_ci
419662306a36Sopenharmony_ci/*
419762306a36Sopenharmony_ci * il4965_rx_handle - Main entry function for receiving responses from uCode
419862306a36Sopenharmony_ci *
419962306a36Sopenharmony_ci * Uses the il->handlers callback function array to invoke
420062306a36Sopenharmony_ci * the appropriate handlers, including command responses,
420162306a36Sopenharmony_ci * frame-received notifications, and other notifications.
420262306a36Sopenharmony_ci */
420362306a36Sopenharmony_civoid
420462306a36Sopenharmony_ciil4965_rx_handle(struct il_priv *il)
420562306a36Sopenharmony_ci{
420662306a36Sopenharmony_ci	struct il_rx_buf *rxb;
420762306a36Sopenharmony_ci	struct il_rx_pkt *pkt;
420862306a36Sopenharmony_ci	struct il_rx_queue *rxq = &il->rxq;
420962306a36Sopenharmony_ci	u32 r, i;
421062306a36Sopenharmony_ci	int reclaim;
421162306a36Sopenharmony_ci	unsigned long flags;
421262306a36Sopenharmony_ci	u8 fill_rx = 0;
421362306a36Sopenharmony_ci	u32 count = 8;
421462306a36Sopenharmony_ci	int total_empty;
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_ci	/* uCode's read idx (stored in shared DRAM) indicates the last Rx
421762306a36Sopenharmony_ci	 * buffer that the driver may process (last buffer filled by ucode). */
421862306a36Sopenharmony_ci	r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF;
421962306a36Sopenharmony_ci	i = rxq->read;
422062306a36Sopenharmony_ci
422162306a36Sopenharmony_ci	/* Rx interrupt, but nothing sent from uCode */
422262306a36Sopenharmony_ci	if (i == r)
422362306a36Sopenharmony_ci		D_RX("r = %d, i = %d\n", r, i);
422462306a36Sopenharmony_ci
422562306a36Sopenharmony_ci	/* calculate total frames need to be restock after handling RX */
422662306a36Sopenharmony_ci	total_empty = r - rxq->write_actual;
422762306a36Sopenharmony_ci	if (total_empty < 0)
422862306a36Sopenharmony_ci		total_empty += RX_QUEUE_SIZE;
422962306a36Sopenharmony_ci
423062306a36Sopenharmony_ci	if (total_empty > (RX_QUEUE_SIZE / 2))
423162306a36Sopenharmony_ci		fill_rx = 1;
423262306a36Sopenharmony_ci
423362306a36Sopenharmony_ci	while (i != r) {
423462306a36Sopenharmony_ci		int len;
423562306a36Sopenharmony_ci
423662306a36Sopenharmony_ci		rxb = rxq->queue[i];
423762306a36Sopenharmony_ci
423862306a36Sopenharmony_ci		/* If an RXB doesn't have a Rx queue slot associated with it,
423962306a36Sopenharmony_ci		 * then a bug has been introduced in the queue refilling
424062306a36Sopenharmony_ci		 * routines -- catch it here */
424162306a36Sopenharmony_ci		BUG_ON(rxb == NULL);
424262306a36Sopenharmony_ci
424362306a36Sopenharmony_ci		rxq->queue[i] = NULL;
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci		dma_unmap_page(&il->pci_dev->dev, rxb->page_dma,
424662306a36Sopenharmony_ci			       PAGE_SIZE << il->hw_params.rx_page_order,
424762306a36Sopenharmony_ci			       DMA_FROM_DEVICE);
424862306a36Sopenharmony_ci		pkt = rxb_addr(rxb);
424962306a36Sopenharmony_ci
425062306a36Sopenharmony_ci		len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK;
425162306a36Sopenharmony_ci		len += sizeof(u32);	/* account for status word */
425262306a36Sopenharmony_ci
425362306a36Sopenharmony_ci		reclaim = il_need_reclaim(il, pkt);
425462306a36Sopenharmony_ci
425562306a36Sopenharmony_ci		/* Based on type of command response or notification,
425662306a36Sopenharmony_ci		 *   handle those that need handling via function in
425762306a36Sopenharmony_ci		 *   handlers table.  See il4965_setup_handlers() */
425862306a36Sopenharmony_ci		if (il->handlers[pkt->hdr.cmd]) {
425962306a36Sopenharmony_ci			D_RX("r = %d, i = %d, %s, 0x%02x\n", r, i,
426062306a36Sopenharmony_ci			     il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
426162306a36Sopenharmony_ci			il->isr_stats.handlers[pkt->hdr.cmd]++;
426262306a36Sopenharmony_ci			il->handlers[pkt->hdr.cmd] (il, rxb);
426362306a36Sopenharmony_ci		} else {
426462306a36Sopenharmony_ci			/* No handling needed */
426562306a36Sopenharmony_ci			D_RX("r %d i %d No handler needed for %s, 0x%02x\n", r,
426662306a36Sopenharmony_ci			     i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
426762306a36Sopenharmony_ci		}
426862306a36Sopenharmony_ci
426962306a36Sopenharmony_ci		/*
427062306a36Sopenharmony_ci		 * XXX: After here, we should always check rxb->page
427162306a36Sopenharmony_ci		 * against NULL before touching it or its virtual
427262306a36Sopenharmony_ci		 * memory (pkt). Because some handler might have
427362306a36Sopenharmony_ci		 * already taken or freed the pages.
427462306a36Sopenharmony_ci		 */
427562306a36Sopenharmony_ci
427662306a36Sopenharmony_ci		if (reclaim) {
427762306a36Sopenharmony_ci			/* Invoke any callbacks, transfer the buffer to caller,
427862306a36Sopenharmony_ci			 * and fire off the (possibly) blocking il_send_cmd()
427962306a36Sopenharmony_ci			 * as we reclaim the driver command queue */
428062306a36Sopenharmony_ci			if (rxb->page)
428162306a36Sopenharmony_ci				il_tx_cmd_complete(il, rxb);
428262306a36Sopenharmony_ci			else
428362306a36Sopenharmony_ci				IL_WARN("Claim null rxb?\n");
428462306a36Sopenharmony_ci		}
428562306a36Sopenharmony_ci
428662306a36Sopenharmony_ci		/* Reuse the page if possible. For notification packets and
428762306a36Sopenharmony_ci		 * SKBs that fail to Rx correctly, add them back into the
428862306a36Sopenharmony_ci		 * rx_free list for reuse later. */
428962306a36Sopenharmony_ci		spin_lock_irqsave(&rxq->lock, flags);
429062306a36Sopenharmony_ci		if (rxb->page != NULL) {
429162306a36Sopenharmony_ci			rxb->page_dma =
429262306a36Sopenharmony_ci			    dma_map_page(&il->pci_dev->dev, rxb->page, 0,
429362306a36Sopenharmony_ci					 PAGE_SIZE << il->hw_params.rx_page_order,
429462306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
429562306a36Sopenharmony_ci
429662306a36Sopenharmony_ci			if (unlikely(dma_mapping_error(&il->pci_dev->dev,
429762306a36Sopenharmony_ci						       rxb->page_dma))) {
429862306a36Sopenharmony_ci				__il_free_pages(il, rxb->page);
429962306a36Sopenharmony_ci				rxb->page = NULL;
430062306a36Sopenharmony_ci				list_add_tail(&rxb->list, &rxq->rx_used);
430162306a36Sopenharmony_ci			} else {
430262306a36Sopenharmony_ci				list_add_tail(&rxb->list, &rxq->rx_free);
430362306a36Sopenharmony_ci				rxq->free_count++;
430462306a36Sopenharmony_ci			}
430562306a36Sopenharmony_ci		} else
430662306a36Sopenharmony_ci			list_add_tail(&rxb->list, &rxq->rx_used);
430762306a36Sopenharmony_ci
430862306a36Sopenharmony_ci		spin_unlock_irqrestore(&rxq->lock, flags);
430962306a36Sopenharmony_ci
431062306a36Sopenharmony_ci		i = (i + 1) & RX_QUEUE_MASK;
431162306a36Sopenharmony_ci		/* If there are a lot of unused frames,
431262306a36Sopenharmony_ci		 * restock the Rx queue so ucode wont assert. */
431362306a36Sopenharmony_ci		if (fill_rx) {
431462306a36Sopenharmony_ci			count++;
431562306a36Sopenharmony_ci			if (count >= 8) {
431662306a36Sopenharmony_ci				rxq->read = i;
431762306a36Sopenharmony_ci				il4965_rx_replenish_now(il);
431862306a36Sopenharmony_ci				count = 0;
431962306a36Sopenharmony_ci			}
432062306a36Sopenharmony_ci		}
432162306a36Sopenharmony_ci	}
432262306a36Sopenharmony_ci
432362306a36Sopenharmony_ci	/* Backtrack one entry */
432462306a36Sopenharmony_ci	rxq->read = i;
432562306a36Sopenharmony_ci	if (fill_rx)
432662306a36Sopenharmony_ci		il4965_rx_replenish_now(il);
432762306a36Sopenharmony_ci	else
432862306a36Sopenharmony_ci		il4965_rx_queue_restock(il);
432962306a36Sopenharmony_ci}
433062306a36Sopenharmony_ci
433162306a36Sopenharmony_ci/* call this function to flush any scheduled tasklet */
433262306a36Sopenharmony_cistatic inline void
433362306a36Sopenharmony_ciil4965_synchronize_irq(struct il_priv *il)
433462306a36Sopenharmony_ci{
433562306a36Sopenharmony_ci	/* wait to make sure we flush pending tasklet */
433662306a36Sopenharmony_ci	synchronize_irq(il->pci_dev->irq);
433762306a36Sopenharmony_ci	tasklet_kill(&il->irq_tasklet);
433862306a36Sopenharmony_ci}
433962306a36Sopenharmony_ci
434062306a36Sopenharmony_cistatic void
434162306a36Sopenharmony_ciil4965_irq_tasklet(struct tasklet_struct *t)
434262306a36Sopenharmony_ci{
434362306a36Sopenharmony_ci	struct il_priv *il = from_tasklet(il, t, irq_tasklet);
434462306a36Sopenharmony_ci	u32 inta, handled = 0;
434562306a36Sopenharmony_ci	u32 inta_fh;
434662306a36Sopenharmony_ci	unsigned long flags;
434762306a36Sopenharmony_ci	u32 i;
434862306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
434962306a36Sopenharmony_ci	u32 inta_mask;
435062306a36Sopenharmony_ci#endif
435162306a36Sopenharmony_ci
435262306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
435362306a36Sopenharmony_ci
435462306a36Sopenharmony_ci	/* Ack/clear/reset pending uCode interrupts.
435562306a36Sopenharmony_ci	 * Note:  Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
435662306a36Sopenharmony_ci	 *  and will clear only when CSR_FH_INT_STATUS gets cleared. */
435762306a36Sopenharmony_ci	inta = _il_rd(il, CSR_INT);
435862306a36Sopenharmony_ci	_il_wr(il, CSR_INT, inta);
435962306a36Sopenharmony_ci
436062306a36Sopenharmony_ci	/* Ack/clear/reset pending flow-handler (DMA) interrupts.
436162306a36Sopenharmony_ci	 * Any new interrupts that happen after this, either while we're
436262306a36Sopenharmony_ci	 * in this tasklet, or later, will show up in next ISR/tasklet. */
436362306a36Sopenharmony_ci	inta_fh = _il_rd(il, CSR_FH_INT_STATUS);
436462306a36Sopenharmony_ci	_il_wr(il, CSR_FH_INT_STATUS, inta_fh);
436562306a36Sopenharmony_ci
436662306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
436762306a36Sopenharmony_ci	if (il_get_debug_level(il) & IL_DL_ISR) {
436862306a36Sopenharmony_ci		/* just for debug */
436962306a36Sopenharmony_ci		inta_mask = _il_rd(il, CSR_INT_MASK);
437062306a36Sopenharmony_ci		D_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta,
437162306a36Sopenharmony_ci		      inta_mask, inta_fh);
437262306a36Sopenharmony_ci	}
437362306a36Sopenharmony_ci#endif
437462306a36Sopenharmony_ci
437562306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
437662306a36Sopenharmony_ci
437762306a36Sopenharmony_ci	/* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not
437862306a36Sopenharmony_ci	 * atomic, make sure that inta covers all the interrupts that
437962306a36Sopenharmony_ci	 * we've discovered, even if FH interrupt came in just after
438062306a36Sopenharmony_ci	 * reading CSR_INT. */
438162306a36Sopenharmony_ci	if (inta_fh & CSR49_FH_INT_RX_MASK)
438262306a36Sopenharmony_ci		inta |= CSR_INT_BIT_FH_RX;
438362306a36Sopenharmony_ci	if (inta_fh & CSR49_FH_INT_TX_MASK)
438462306a36Sopenharmony_ci		inta |= CSR_INT_BIT_FH_TX;
438562306a36Sopenharmony_ci
438662306a36Sopenharmony_ci	/* Now service all interrupt bits discovered above. */
438762306a36Sopenharmony_ci	if (inta & CSR_INT_BIT_HW_ERR) {
438862306a36Sopenharmony_ci		IL_ERR("Hardware error detected.  Restarting.\n");
438962306a36Sopenharmony_ci
439062306a36Sopenharmony_ci		/* Tell the device to stop sending interrupts */
439162306a36Sopenharmony_ci		il_disable_interrupts(il);
439262306a36Sopenharmony_ci
439362306a36Sopenharmony_ci		il->isr_stats.hw++;
439462306a36Sopenharmony_ci		il_irq_handle_error(il);
439562306a36Sopenharmony_ci
439662306a36Sopenharmony_ci		handled |= CSR_INT_BIT_HW_ERR;
439762306a36Sopenharmony_ci
439862306a36Sopenharmony_ci		return;
439962306a36Sopenharmony_ci	}
440062306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
440162306a36Sopenharmony_ci	if (il_get_debug_level(il) & (IL_DL_ISR)) {
440262306a36Sopenharmony_ci		/* NIC fires this, but we don't use it, redundant with WAKEUP */
440362306a36Sopenharmony_ci		if (inta & CSR_INT_BIT_SCD) {
440462306a36Sopenharmony_ci			D_ISR("Scheduler finished to transmit "
440562306a36Sopenharmony_ci			      "the frame/frames.\n");
440662306a36Sopenharmony_ci			il->isr_stats.sch++;
440762306a36Sopenharmony_ci		}
440862306a36Sopenharmony_ci
440962306a36Sopenharmony_ci		/* Alive notification via Rx interrupt will do the real work */
441062306a36Sopenharmony_ci		if (inta & CSR_INT_BIT_ALIVE) {
441162306a36Sopenharmony_ci			D_ISR("Alive interrupt\n");
441262306a36Sopenharmony_ci			il->isr_stats.alive++;
441362306a36Sopenharmony_ci		}
441462306a36Sopenharmony_ci	}
441562306a36Sopenharmony_ci#endif
441662306a36Sopenharmony_ci	/* Safely ignore these bits for debug checks below */
441762306a36Sopenharmony_ci	inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
441862306a36Sopenharmony_ci
441962306a36Sopenharmony_ci	/* HW RF KILL switch toggled */
442062306a36Sopenharmony_ci	if (inta & CSR_INT_BIT_RF_KILL) {
442162306a36Sopenharmony_ci		int hw_rf_kill = 0;
442262306a36Sopenharmony_ci
442362306a36Sopenharmony_ci		if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
442462306a36Sopenharmony_ci			hw_rf_kill = 1;
442562306a36Sopenharmony_ci
442662306a36Sopenharmony_ci		IL_WARN("RF_KILL bit toggled to %s.\n",
442762306a36Sopenharmony_ci			hw_rf_kill ? "disable radio" : "enable radio");
442862306a36Sopenharmony_ci
442962306a36Sopenharmony_ci		il->isr_stats.rfkill++;
443062306a36Sopenharmony_ci
443162306a36Sopenharmony_ci		/* driver only loads ucode once setting the interface up.
443262306a36Sopenharmony_ci		 * the driver allows loading the ucode even if the radio
443362306a36Sopenharmony_ci		 * is killed. Hence update the killswitch state here. The
443462306a36Sopenharmony_ci		 * rfkill handler will care about restarting if needed.
443562306a36Sopenharmony_ci		 */
443662306a36Sopenharmony_ci		if (hw_rf_kill) {
443762306a36Sopenharmony_ci			set_bit(S_RFKILL, &il->status);
443862306a36Sopenharmony_ci		} else {
443962306a36Sopenharmony_ci			clear_bit(S_RFKILL, &il->status);
444062306a36Sopenharmony_ci			il_force_reset(il, true);
444162306a36Sopenharmony_ci		}
444262306a36Sopenharmony_ci		wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill);
444362306a36Sopenharmony_ci
444462306a36Sopenharmony_ci		handled |= CSR_INT_BIT_RF_KILL;
444562306a36Sopenharmony_ci	}
444662306a36Sopenharmony_ci
444762306a36Sopenharmony_ci	/* Chip got too hot and stopped itself */
444862306a36Sopenharmony_ci	if (inta & CSR_INT_BIT_CT_KILL) {
444962306a36Sopenharmony_ci		IL_ERR("Microcode CT kill error detected.\n");
445062306a36Sopenharmony_ci		il->isr_stats.ctkill++;
445162306a36Sopenharmony_ci		handled |= CSR_INT_BIT_CT_KILL;
445262306a36Sopenharmony_ci	}
445362306a36Sopenharmony_ci
445462306a36Sopenharmony_ci	/* Error detected by uCode */
445562306a36Sopenharmony_ci	if (inta & CSR_INT_BIT_SW_ERR) {
445662306a36Sopenharmony_ci		IL_ERR("Microcode SW error detected. " " Restarting 0x%X.\n",
445762306a36Sopenharmony_ci		       inta);
445862306a36Sopenharmony_ci		il->isr_stats.sw++;
445962306a36Sopenharmony_ci		il_irq_handle_error(il);
446062306a36Sopenharmony_ci		handled |= CSR_INT_BIT_SW_ERR;
446162306a36Sopenharmony_ci	}
446262306a36Sopenharmony_ci
446362306a36Sopenharmony_ci	/*
446462306a36Sopenharmony_ci	 * uCode wakes up after power-down sleep.
446562306a36Sopenharmony_ci	 * Tell device about any new tx or host commands enqueued,
446662306a36Sopenharmony_ci	 * and about any Rx buffers made available while asleep.
446762306a36Sopenharmony_ci	 */
446862306a36Sopenharmony_ci	if (inta & CSR_INT_BIT_WAKEUP) {
446962306a36Sopenharmony_ci		D_ISR("Wakeup interrupt\n");
447062306a36Sopenharmony_ci		il_rx_queue_update_write_ptr(il, &il->rxq);
447162306a36Sopenharmony_ci		for (i = 0; i < il->hw_params.max_txq_num; i++)
447262306a36Sopenharmony_ci			il_txq_update_write_ptr(il, &il->txq[i]);
447362306a36Sopenharmony_ci		il->isr_stats.wakeup++;
447462306a36Sopenharmony_ci		handled |= CSR_INT_BIT_WAKEUP;
447562306a36Sopenharmony_ci	}
447662306a36Sopenharmony_ci
447762306a36Sopenharmony_ci	/* All uCode command responses, including Tx command responses,
447862306a36Sopenharmony_ci	 * Rx "responses" (frame-received notification), and other
447962306a36Sopenharmony_ci	 * notifications from uCode come through here*/
448062306a36Sopenharmony_ci	if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) {
448162306a36Sopenharmony_ci		il4965_rx_handle(il);
448262306a36Sopenharmony_ci		il->isr_stats.rx++;
448362306a36Sopenharmony_ci		handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
448462306a36Sopenharmony_ci	}
448562306a36Sopenharmony_ci
448662306a36Sopenharmony_ci	/* This "Tx" DMA channel is used only for loading uCode */
448762306a36Sopenharmony_ci	if (inta & CSR_INT_BIT_FH_TX) {
448862306a36Sopenharmony_ci		D_ISR("uCode load interrupt\n");
448962306a36Sopenharmony_ci		il->isr_stats.tx++;
449062306a36Sopenharmony_ci		handled |= CSR_INT_BIT_FH_TX;
449162306a36Sopenharmony_ci		/* Wake up uCode load routine, now that load is complete */
449262306a36Sopenharmony_ci		il->ucode_write_complete = 1;
449362306a36Sopenharmony_ci		wake_up(&il->wait_command_queue);
449462306a36Sopenharmony_ci	}
449562306a36Sopenharmony_ci
449662306a36Sopenharmony_ci	if (inta & ~handled) {
449762306a36Sopenharmony_ci		IL_ERR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
449862306a36Sopenharmony_ci		il->isr_stats.unhandled++;
449962306a36Sopenharmony_ci	}
450062306a36Sopenharmony_ci
450162306a36Sopenharmony_ci	if (inta & ~(il->inta_mask)) {
450262306a36Sopenharmony_ci		IL_WARN("Disabled INTA bits 0x%08x were pending\n",
450362306a36Sopenharmony_ci			inta & ~il->inta_mask);
450462306a36Sopenharmony_ci		IL_WARN("   with FH49_INT = 0x%08x\n", inta_fh);
450562306a36Sopenharmony_ci	}
450662306a36Sopenharmony_ci
450762306a36Sopenharmony_ci	/* Re-enable all interrupts */
450862306a36Sopenharmony_ci	/* only Re-enable if disabled by irq */
450962306a36Sopenharmony_ci	if (test_bit(S_INT_ENABLED, &il->status))
451062306a36Sopenharmony_ci		il_enable_interrupts(il);
451162306a36Sopenharmony_ci	/* Re-enable RF_KILL if it occurred */
451262306a36Sopenharmony_ci	else if (handled & CSR_INT_BIT_RF_KILL)
451362306a36Sopenharmony_ci		il_enable_rfkill_int(il);
451462306a36Sopenharmony_ci
451562306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
451662306a36Sopenharmony_ci	if (il_get_debug_level(il) & (IL_DL_ISR)) {
451762306a36Sopenharmony_ci		inta = _il_rd(il, CSR_INT);
451862306a36Sopenharmony_ci		inta_mask = _il_rd(il, CSR_INT_MASK);
451962306a36Sopenharmony_ci		inta_fh = _il_rd(il, CSR_FH_INT_STATUS);
452062306a36Sopenharmony_ci		D_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, "
452162306a36Sopenharmony_ci		      "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags);
452262306a36Sopenharmony_ci	}
452362306a36Sopenharmony_ci#endif
452462306a36Sopenharmony_ci}
452562306a36Sopenharmony_ci
452662306a36Sopenharmony_ci/*****************************************************************************
452762306a36Sopenharmony_ci *
452862306a36Sopenharmony_ci * sysfs attributes
452962306a36Sopenharmony_ci *
453062306a36Sopenharmony_ci *****************************************************************************/
453162306a36Sopenharmony_ci
453262306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
453362306a36Sopenharmony_ci
453462306a36Sopenharmony_ci/*
453562306a36Sopenharmony_ci * The following adds a new attribute to the sysfs representation
453662306a36Sopenharmony_ci * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/)
453762306a36Sopenharmony_ci * used for controlling the debug level.
453862306a36Sopenharmony_ci *
453962306a36Sopenharmony_ci * See the level definitions in iwl for details.
454062306a36Sopenharmony_ci *
454162306a36Sopenharmony_ci * The debug_level being managed using sysfs below is a per device debug
454262306a36Sopenharmony_ci * level that is used instead of the global debug level if it (the per
454362306a36Sopenharmony_ci * device debug level) is set.
454462306a36Sopenharmony_ci */
454562306a36Sopenharmony_cistatic ssize_t
454662306a36Sopenharmony_ciil4965_show_debug_level(struct device *d, struct device_attribute *attr,
454762306a36Sopenharmony_ci			char *buf)
454862306a36Sopenharmony_ci{
454962306a36Sopenharmony_ci	struct il_priv *il = dev_get_drvdata(d);
455062306a36Sopenharmony_ci	return sprintf(buf, "0x%08X\n", il_get_debug_level(il));
455162306a36Sopenharmony_ci}
455262306a36Sopenharmony_ci
455362306a36Sopenharmony_cistatic ssize_t
455462306a36Sopenharmony_ciil4965_store_debug_level(struct device *d, struct device_attribute *attr,
455562306a36Sopenharmony_ci			 const char *buf, size_t count)
455662306a36Sopenharmony_ci{
455762306a36Sopenharmony_ci	struct il_priv *il = dev_get_drvdata(d);
455862306a36Sopenharmony_ci	unsigned long val;
455962306a36Sopenharmony_ci	int ret;
456062306a36Sopenharmony_ci
456162306a36Sopenharmony_ci	ret = kstrtoul(buf, 0, &val);
456262306a36Sopenharmony_ci	if (ret)
456362306a36Sopenharmony_ci		IL_ERR("%s is not in hex or decimal form.\n", buf);
456462306a36Sopenharmony_ci	else
456562306a36Sopenharmony_ci		il->debug_level = val;
456662306a36Sopenharmony_ci
456762306a36Sopenharmony_ci	return strnlen(buf, count);
456862306a36Sopenharmony_ci}
456962306a36Sopenharmony_ci
457062306a36Sopenharmony_cistatic DEVICE_ATTR(debug_level, 0644, il4965_show_debug_level,
457162306a36Sopenharmony_ci		   il4965_store_debug_level);
457262306a36Sopenharmony_ci
457362306a36Sopenharmony_ci#endif /* CONFIG_IWLEGACY_DEBUG */
457462306a36Sopenharmony_ci
457562306a36Sopenharmony_cistatic ssize_t
457662306a36Sopenharmony_ciil4965_show_temperature(struct device *d, struct device_attribute *attr,
457762306a36Sopenharmony_ci			char *buf)
457862306a36Sopenharmony_ci{
457962306a36Sopenharmony_ci	struct il_priv *il = dev_get_drvdata(d);
458062306a36Sopenharmony_ci
458162306a36Sopenharmony_ci	if (!il_is_alive(il))
458262306a36Sopenharmony_ci		return -EAGAIN;
458362306a36Sopenharmony_ci
458462306a36Sopenharmony_ci	return sprintf(buf, "%d\n", il->temperature);
458562306a36Sopenharmony_ci}
458662306a36Sopenharmony_ci
458762306a36Sopenharmony_cistatic DEVICE_ATTR(temperature, 0444, il4965_show_temperature, NULL);
458862306a36Sopenharmony_ci
458962306a36Sopenharmony_cistatic ssize_t
459062306a36Sopenharmony_ciil4965_show_tx_power(struct device *d, struct device_attribute *attr, char *buf)
459162306a36Sopenharmony_ci{
459262306a36Sopenharmony_ci	struct il_priv *il = dev_get_drvdata(d);
459362306a36Sopenharmony_ci
459462306a36Sopenharmony_ci	if (!il_is_ready_rf(il))
459562306a36Sopenharmony_ci		return sprintf(buf, "off\n");
459662306a36Sopenharmony_ci	else
459762306a36Sopenharmony_ci		return sprintf(buf, "%d\n", il->tx_power_user_lmt);
459862306a36Sopenharmony_ci}
459962306a36Sopenharmony_ci
460062306a36Sopenharmony_cistatic ssize_t
460162306a36Sopenharmony_ciil4965_store_tx_power(struct device *d, struct device_attribute *attr,
460262306a36Sopenharmony_ci		      const char *buf, size_t count)
460362306a36Sopenharmony_ci{
460462306a36Sopenharmony_ci	struct il_priv *il = dev_get_drvdata(d);
460562306a36Sopenharmony_ci	unsigned long val;
460662306a36Sopenharmony_ci	int ret;
460762306a36Sopenharmony_ci
460862306a36Sopenharmony_ci	ret = kstrtoul(buf, 10, &val);
460962306a36Sopenharmony_ci	if (ret)
461062306a36Sopenharmony_ci		IL_INFO("%s is not in decimal form.\n", buf);
461162306a36Sopenharmony_ci	else {
461262306a36Sopenharmony_ci		ret = il_set_tx_power(il, val, false);
461362306a36Sopenharmony_ci		if (ret)
461462306a36Sopenharmony_ci			IL_ERR("failed setting tx power (0x%08x).\n", ret);
461562306a36Sopenharmony_ci		else
461662306a36Sopenharmony_ci			ret = count;
461762306a36Sopenharmony_ci	}
461862306a36Sopenharmony_ci	return ret;
461962306a36Sopenharmony_ci}
462062306a36Sopenharmony_ci
462162306a36Sopenharmony_cistatic DEVICE_ATTR(tx_power, 0644, il4965_show_tx_power,
462262306a36Sopenharmony_ci		   il4965_store_tx_power);
462362306a36Sopenharmony_ci
462462306a36Sopenharmony_cistatic struct attribute *il_sysfs_entries[] = {
462562306a36Sopenharmony_ci	&dev_attr_temperature.attr,
462662306a36Sopenharmony_ci	&dev_attr_tx_power.attr,
462762306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
462862306a36Sopenharmony_ci	&dev_attr_debug_level.attr,
462962306a36Sopenharmony_ci#endif
463062306a36Sopenharmony_ci	NULL
463162306a36Sopenharmony_ci};
463262306a36Sopenharmony_ci
463362306a36Sopenharmony_cistatic const struct attribute_group il_attribute_group = {
463462306a36Sopenharmony_ci	.name = NULL,		/* put in device directory */
463562306a36Sopenharmony_ci	.attrs = il_sysfs_entries,
463662306a36Sopenharmony_ci};
463762306a36Sopenharmony_ci
463862306a36Sopenharmony_ci/******************************************************************************
463962306a36Sopenharmony_ci *
464062306a36Sopenharmony_ci * uCode download functions
464162306a36Sopenharmony_ci *
464262306a36Sopenharmony_ci ******************************************************************************/
464362306a36Sopenharmony_ci
464462306a36Sopenharmony_cistatic void
464562306a36Sopenharmony_ciil4965_dealloc_ucode_pci(struct il_priv *il)
464662306a36Sopenharmony_ci{
464762306a36Sopenharmony_ci	il_free_fw_desc(il->pci_dev, &il->ucode_code);
464862306a36Sopenharmony_ci	il_free_fw_desc(il->pci_dev, &il->ucode_data);
464962306a36Sopenharmony_ci	il_free_fw_desc(il->pci_dev, &il->ucode_data_backup);
465062306a36Sopenharmony_ci	il_free_fw_desc(il->pci_dev, &il->ucode_init);
465162306a36Sopenharmony_ci	il_free_fw_desc(il->pci_dev, &il->ucode_init_data);
465262306a36Sopenharmony_ci	il_free_fw_desc(il->pci_dev, &il->ucode_boot);
465362306a36Sopenharmony_ci}
465462306a36Sopenharmony_ci
465562306a36Sopenharmony_cistatic void
465662306a36Sopenharmony_ciil4965_nic_start(struct il_priv *il)
465762306a36Sopenharmony_ci{
465862306a36Sopenharmony_ci	/* Remove all resets to allow NIC to operate */
465962306a36Sopenharmony_ci	_il_wr(il, CSR_RESET, 0);
466062306a36Sopenharmony_ci}
466162306a36Sopenharmony_ci
466262306a36Sopenharmony_cistatic void il4965_ucode_callback(const struct firmware *ucode_raw,
466362306a36Sopenharmony_ci				  void *context);
466462306a36Sopenharmony_cistatic int il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length);
466562306a36Sopenharmony_ci
466662306a36Sopenharmony_cistatic int __must_check
466762306a36Sopenharmony_ciil4965_request_firmware(struct il_priv *il, bool first)
466862306a36Sopenharmony_ci{
466962306a36Sopenharmony_ci	const char *name_pre = il->cfg->fw_name_pre;
467062306a36Sopenharmony_ci	char tag[8];
467162306a36Sopenharmony_ci
467262306a36Sopenharmony_ci	if (first) {
467362306a36Sopenharmony_ci		il->fw_idx = il->cfg->ucode_api_max;
467462306a36Sopenharmony_ci		sprintf(tag, "%d", il->fw_idx);
467562306a36Sopenharmony_ci	} else {
467662306a36Sopenharmony_ci		il->fw_idx--;
467762306a36Sopenharmony_ci		sprintf(tag, "%d", il->fw_idx);
467862306a36Sopenharmony_ci	}
467962306a36Sopenharmony_ci
468062306a36Sopenharmony_ci	if (il->fw_idx < il->cfg->ucode_api_min) {
468162306a36Sopenharmony_ci		IL_ERR("no suitable firmware found!\n");
468262306a36Sopenharmony_ci		return -ENOENT;
468362306a36Sopenharmony_ci	}
468462306a36Sopenharmony_ci
468562306a36Sopenharmony_ci	sprintf(il->firmware_name, "%s%s%s", name_pre, tag, ".ucode");
468662306a36Sopenharmony_ci
468762306a36Sopenharmony_ci	D_INFO("attempting to load firmware '%s'\n", il->firmware_name);
468862306a36Sopenharmony_ci
468962306a36Sopenharmony_ci	return request_firmware_nowait(THIS_MODULE, 1, il->firmware_name,
469062306a36Sopenharmony_ci				       &il->pci_dev->dev, GFP_KERNEL, il,
469162306a36Sopenharmony_ci				       il4965_ucode_callback);
469262306a36Sopenharmony_ci}
469362306a36Sopenharmony_ci
469462306a36Sopenharmony_cistruct il4965_firmware_pieces {
469562306a36Sopenharmony_ci	const void *inst, *data, *init, *init_data, *boot;
469662306a36Sopenharmony_ci	size_t inst_size, data_size, init_size, init_data_size, boot_size;
469762306a36Sopenharmony_ci};
469862306a36Sopenharmony_ci
469962306a36Sopenharmony_cistatic int
470062306a36Sopenharmony_ciil4965_load_firmware(struct il_priv *il, const struct firmware *ucode_raw,
470162306a36Sopenharmony_ci		     struct il4965_firmware_pieces *pieces)
470262306a36Sopenharmony_ci{
470362306a36Sopenharmony_ci	struct il_ucode_header *ucode = (void *)ucode_raw->data;
470462306a36Sopenharmony_ci	u32 api_ver, hdr_size;
470562306a36Sopenharmony_ci	const u8 *src;
470662306a36Sopenharmony_ci
470762306a36Sopenharmony_ci	il->ucode_ver = le32_to_cpu(ucode->ver);
470862306a36Sopenharmony_ci	api_ver = IL_UCODE_API(il->ucode_ver);
470962306a36Sopenharmony_ci
471062306a36Sopenharmony_ci	switch (api_ver) {
471162306a36Sopenharmony_ci	default:
471262306a36Sopenharmony_ci	case 0:
471362306a36Sopenharmony_ci	case 1:
471462306a36Sopenharmony_ci	case 2:
471562306a36Sopenharmony_ci		hdr_size = 24;
471662306a36Sopenharmony_ci		if (ucode_raw->size < hdr_size) {
471762306a36Sopenharmony_ci			IL_ERR("File size too small!\n");
471862306a36Sopenharmony_ci			return -EINVAL;
471962306a36Sopenharmony_ci		}
472062306a36Sopenharmony_ci		pieces->inst_size = le32_to_cpu(ucode->v1.inst_size);
472162306a36Sopenharmony_ci		pieces->data_size = le32_to_cpu(ucode->v1.data_size);
472262306a36Sopenharmony_ci		pieces->init_size = le32_to_cpu(ucode->v1.init_size);
472362306a36Sopenharmony_ci		pieces->init_data_size = le32_to_cpu(ucode->v1.init_data_size);
472462306a36Sopenharmony_ci		pieces->boot_size = le32_to_cpu(ucode->v1.boot_size);
472562306a36Sopenharmony_ci		src = ucode->v1.data;
472662306a36Sopenharmony_ci		break;
472762306a36Sopenharmony_ci	}
472862306a36Sopenharmony_ci
472962306a36Sopenharmony_ci	/* Verify size of file vs. image size info in file's header */
473062306a36Sopenharmony_ci	if (ucode_raw->size !=
473162306a36Sopenharmony_ci	    hdr_size + pieces->inst_size + pieces->data_size +
473262306a36Sopenharmony_ci	    pieces->init_size + pieces->init_data_size + pieces->boot_size) {
473362306a36Sopenharmony_ci
473462306a36Sopenharmony_ci		IL_ERR("uCode file size %d does not match expected size\n",
473562306a36Sopenharmony_ci		       (int)ucode_raw->size);
473662306a36Sopenharmony_ci		return -EINVAL;
473762306a36Sopenharmony_ci	}
473862306a36Sopenharmony_ci
473962306a36Sopenharmony_ci	pieces->inst = src;
474062306a36Sopenharmony_ci	src += pieces->inst_size;
474162306a36Sopenharmony_ci	pieces->data = src;
474262306a36Sopenharmony_ci	src += pieces->data_size;
474362306a36Sopenharmony_ci	pieces->init = src;
474462306a36Sopenharmony_ci	src += pieces->init_size;
474562306a36Sopenharmony_ci	pieces->init_data = src;
474662306a36Sopenharmony_ci	src += pieces->init_data_size;
474762306a36Sopenharmony_ci	pieces->boot = src;
474862306a36Sopenharmony_ci	src += pieces->boot_size;
474962306a36Sopenharmony_ci
475062306a36Sopenharmony_ci	return 0;
475162306a36Sopenharmony_ci}
475262306a36Sopenharmony_ci
475362306a36Sopenharmony_ci/*
475462306a36Sopenharmony_ci * il4965_ucode_callback - callback when firmware was loaded
475562306a36Sopenharmony_ci *
475662306a36Sopenharmony_ci * If loaded successfully, copies the firmware into buffers
475762306a36Sopenharmony_ci * for the card to fetch (via DMA).
475862306a36Sopenharmony_ci */
475962306a36Sopenharmony_cistatic void
476062306a36Sopenharmony_ciil4965_ucode_callback(const struct firmware *ucode_raw, void *context)
476162306a36Sopenharmony_ci{
476262306a36Sopenharmony_ci	struct il_priv *il = context;
476362306a36Sopenharmony_ci	int err;
476462306a36Sopenharmony_ci	struct il4965_firmware_pieces pieces;
476562306a36Sopenharmony_ci	const unsigned int api_max = il->cfg->ucode_api_max;
476662306a36Sopenharmony_ci	const unsigned int api_min = il->cfg->ucode_api_min;
476762306a36Sopenharmony_ci	u32 api_ver;
476862306a36Sopenharmony_ci
476962306a36Sopenharmony_ci	u32 max_probe_length = 200;
477062306a36Sopenharmony_ci	u32 standard_phy_calibration_size =
477162306a36Sopenharmony_ci	    IL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;
477262306a36Sopenharmony_ci
477362306a36Sopenharmony_ci	memset(&pieces, 0, sizeof(pieces));
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_ci	if (!ucode_raw) {
477662306a36Sopenharmony_ci		if (il->fw_idx <= il->cfg->ucode_api_max)
477762306a36Sopenharmony_ci			IL_ERR("request for firmware file '%s' failed.\n",
477862306a36Sopenharmony_ci			       il->firmware_name);
477962306a36Sopenharmony_ci		goto try_again;
478062306a36Sopenharmony_ci	}
478162306a36Sopenharmony_ci
478262306a36Sopenharmony_ci	D_INFO("Loaded firmware file '%s' (%zd bytes).\n", il->firmware_name,
478362306a36Sopenharmony_ci	       ucode_raw->size);
478462306a36Sopenharmony_ci
478562306a36Sopenharmony_ci	/* Make sure that we got at least the API version number */
478662306a36Sopenharmony_ci	if (ucode_raw->size < 4) {
478762306a36Sopenharmony_ci		IL_ERR("File size way too small!\n");
478862306a36Sopenharmony_ci		goto try_again;
478962306a36Sopenharmony_ci	}
479062306a36Sopenharmony_ci
479162306a36Sopenharmony_ci	/* Data from ucode file:  header followed by uCode images */
479262306a36Sopenharmony_ci	err = il4965_load_firmware(il, ucode_raw, &pieces);
479362306a36Sopenharmony_ci
479462306a36Sopenharmony_ci	if (err)
479562306a36Sopenharmony_ci		goto try_again;
479662306a36Sopenharmony_ci
479762306a36Sopenharmony_ci	api_ver = IL_UCODE_API(il->ucode_ver);
479862306a36Sopenharmony_ci
479962306a36Sopenharmony_ci	/*
480062306a36Sopenharmony_ci	 * api_ver should match the api version forming part of the
480162306a36Sopenharmony_ci	 * firmware filename ... but we don't check for that and only rely
480262306a36Sopenharmony_ci	 * on the API version read from firmware header from here on forward
480362306a36Sopenharmony_ci	 */
480462306a36Sopenharmony_ci	if (api_ver < api_min || api_ver > api_max) {
480562306a36Sopenharmony_ci		IL_ERR("Driver unable to support your firmware API. "
480662306a36Sopenharmony_ci		       "Driver supports v%u, firmware is v%u.\n", api_max,
480762306a36Sopenharmony_ci		       api_ver);
480862306a36Sopenharmony_ci		goto try_again;
480962306a36Sopenharmony_ci	}
481062306a36Sopenharmony_ci
481162306a36Sopenharmony_ci	if (api_ver != api_max)
481262306a36Sopenharmony_ci		IL_ERR("Firmware has old API version. Expected v%u, "
481362306a36Sopenharmony_ci		       "got v%u. New firmware can be obtained "
481462306a36Sopenharmony_ci		       "from http://www.intellinuxwireless.org.\n", api_max,
481562306a36Sopenharmony_ci		       api_ver);
481662306a36Sopenharmony_ci
481762306a36Sopenharmony_ci	IL_INFO("loaded firmware version %u.%u.%u.%u\n",
481862306a36Sopenharmony_ci		IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver),
481962306a36Sopenharmony_ci		IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver));
482062306a36Sopenharmony_ci
482162306a36Sopenharmony_ci	snprintf(il->hw->wiphy->fw_version, sizeof(il->hw->wiphy->fw_version),
482262306a36Sopenharmony_ci		 "%u.%u.%u.%u", IL_UCODE_MAJOR(il->ucode_ver),
482362306a36Sopenharmony_ci		 IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver),
482462306a36Sopenharmony_ci		 IL_UCODE_SERIAL(il->ucode_ver));
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci	/*
482762306a36Sopenharmony_ci	 * For any of the failures below (before allocating pci memory)
482862306a36Sopenharmony_ci	 * we will try to load a version with a smaller API -- maybe the
482962306a36Sopenharmony_ci	 * user just got a corrupted version of the latest API.
483062306a36Sopenharmony_ci	 */
483162306a36Sopenharmony_ci
483262306a36Sopenharmony_ci	D_INFO("f/w package hdr ucode version raw = 0x%x\n", il->ucode_ver);
483362306a36Sopenharmony_ci	D_INFO("f/w package hdr runtime inst size = %zd\n", pieces.inst_size);
483462306a36Sopenharmony_ci	D_INFO("f/w package hdr runtime data size = %zd\n", pieces.data_size);
483562306a36Sopenharmony_ci	D_INFO("f/w package hdr init inst size = %zd\n", pieces.init_size);
483662306a36Sopenharmony_ci	D_INFO("f/w package hdr init data size = %zd\n", pieces.init_data_size);
483762306a36Sopenharmony_ci	D_INFO("f/w package hdr boot inst size = %zd\n", pieces.boot_size);
483862306a36Sopenharmony_ci
483962306a36Sopenharmony_ci	/* Verify that uCode images will fit in card's SRAM */
484062306a36Sopenharmony_ci	if (pieces.inst_size > il->hw_params.max_inst_size) {
484162306a36Sopenharmony_ci		IL_ERR("uCode instr len %zd too large to fit in\n",
484262306a36Sopenharmony_ci		       pieces.inst_size);
484362306a36Sopenharmony_ci		goto try_again;
484462306a36Sopenharmony_ci	}
484562306a36Sopenharmony_ci
484662306a36Sopenharmony_ci	if (pieces.data_size > il->hw_params.max_data_size) {
484762306a36Sopenharmony_ci		IL_ERR("uCode data len %zd too large to fit in\n",
484862306a36Sopenharmony_ci		       pieces.data_size);
484962306a36Sopenharmony_ci		goto try_again;
485062306a36Sopenharmony_ci	}
485162306a36Sopenharmony_ci
485262306a36Sopenharmony_ci	if (pieces.init_size > il->hw_params.max_inst_size) {
485362306a36Sopenharmony_ci		IL_ERR("uCode init instr len %zd too large to fit in\n",
485462306a36Sopenharmony_ci		       pieces.init_size);
485562306a36Sopenharmony_ci		goto try_again;
485662306a36Sopenharmony_ci	}
485762306a36Sopenharmony_ci
485862306a36Sopenharmony_ci	if (pieces.init_data_size > il->hw_params.max_data_size) {
485962306a36Sopenharmony_ci		IL_ERR("uCode init data len %zd too large to fit in\n",
486062306a36Sopenharmony_ci		       pieces.init_data_size);
486162306a36Sopenharmony_ci		goto try_again;
486262306a36Sopenharmony_ci	}
486362306a36Sopenharmony_ci
486462306a36Sopenharmony_ci	if (pieces.boot_size > il->hw_params.max_bsm_size) {
486562306a36Sopenharmony_ci		IL_ERR("uCode boot instr len %zd too large to fit in\n",
486662306a36Sopenharmony_ci		       pieces.boot_size);
486762306a36Sopenharmony_ci		goto try_again;
486862306a36Sopenharmony_ci	}
486962306a36Sopenharmony_ci
487062306a36Sopenharmony_ci	/* Allocate ucode buffers for card's bus-master loading ... */
487162306a36Sopenharmony_ci
487262306a36Sopenharmony_ci	/* Runtime instructions and 2 copies of data:
487362306a36Sopenharmony_ci	 * 1) unmodified from disk
487462306a36Sopenharmony_ci	 * 2) backup cache for save/restore during power-downs */
487562306a36Sopenharmony_ci	il->ucode_code.len = pieces.inst_size;
487662306a36Sopenharmony_ci	il_alloc_fw_desc(il->pci_dev, &il->ucode_code);
487762306a36Sopenharmony_ci
487862306a36Sopenharmony_ci	il->ucode_data.len = pieces.data_size;
487962306a36Sopenharmony_ci	il_alloc_fw_desc(il->pci_dev, &il->ucode_data);
488062306a36Sopenharmony_ci
488162306a36Sopenharmony_ci	il->ucode_data_backup.len = pieces.data_size;
488262306a36Sopenharmony_ci	il_alloc_fw_desc(il->pci_dev, &il->ucode_data_backup);
488362306a36Sopenharmony_ci
488462306a36Sopenharmony_ci	if (!il->ucode_code.v_addr || !il->ucode_data.v_addr ||
488562306a36Sopenharmony_ci	    !il->ucode_data_backup.v_addr)
488662306a36Sopenharmony_ci		goto err_pci_alloc;
488762306a36Sopenharmony_ci
488862306a36Sopenharmony_ci	/* Initialization instructions and data */
488962306a36Sopenharmony_ci	if (pieces.init_size && pieces.init_data_size) {
489062306a36Sopenharmony_ci		il->ucode_init.len = pieces.init_size;
489162306a36Sopenharmony_ci		il_alloc_fw_desc(il->pci_dev, &il->ucode_init);
489262306a36Sopenharmony_ci
489362306a36Sopenharmony_ci		il->ucode_init_data.len = pieces.init_data_size;
489462306a36Sopenharmony_ci		il_alloc_fw_desc(il->pci_dev, &il->ucode_init_data);
489562306a36Sopenharmony_ci
489662306a36Sopenharmony_ci		if (!il->ucode_init.v_addr || !il->ucode_init_data.v_addr)
489762306a36Sopenharmony_ci			goto err_pci_alloc;
489862306a36Sopenharmony_ci	}
489962306a36Sopenharmony_ci
490062306a36Sopenharmony_ci	/* Bootstrap (instructions only, no data) */
490162306a36Sopenharmony_ci	if (pieces.boot_size) {
490262306a36Sopenharmony_ci		il->ucode_boot.len = pieces.boot_size;
490362306a36Sopenharmony_ci		il_alloc_fw_desc(il->pci_dev, &il->ucode_boot);
490462306a36Sopenharmony_ci
490562306a36Sopenharmony_ci		if (!il->ucode_boot.v_addr)
490662306a36Sopenharmony_ci			goto err_pci_alloc;
490762306a36Sopenharmony_ci	}
490862306a36Sopenharmony_ci
490962306a36Sopenharmony_ci	/* Now that we can no longer fail, copy information */
491062306a36Sopenharmony_ci
491162306a36Sopenharmony_ci	il->sta_key_max_num = STA_KEY_MAX_NUM;
491262306a36Sopenharmony_ci
491362306a36Sopenharmony_ci	/* Copy images into buffers for card's bus-master reads ... */
491462306a36Sopenharmony_ci
491562306a36Sopenharmony_ci	/* Runtime instructions (first block of data in file) */
491662306a36Sopenharmony_ci	D_INFO("Copying (but not loading) uCode instr len %zd\n",
491762306a36Sopenharmony_ci	       pieces.inst_size);
491862306a36Sopenharmony_ci	memcpy(il->ucode_code.v_addr, pieces.inst, pieces.inst_size);
491962306a36Sopenharmony_ci
492062306a36Sopenharmony_ci	D_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n",
492162306a36Sopenharmony_ci	       il->ucode_code.v_addr, (u32) il->ucode_code.p_addr);
492262306a36Sopenharmony_ci
492362306a36Sopenharmony_ci	/*
492462306a36Sopenharmony_ci	 * Runtime data
492562306a36Sopenharmony_ci	 * NOTE:  Copy into backup buffer will be done in il_up()
492662306a36Sopenharmony_ci	 */
492762306a36Sopenharmony_ci	D_INFO("Copying (but not loading) uCode data len %zd\n",
492862306a36Sopenharmony_ci	       pieces.data_size);
492962306a36Sopenharmony_ci	memcpy(il->ucode_data.v_addr, pieces.data, pieces.data_size);
493062306a36Sopenharmony_ci	memcpy(il->ucode_data_backup.v_addr, pieces.data, pieces.data_size);
493162306a36Sopenharmony_ci
493262306a36Sopenharmony_ci	/* Initialization instructions */
493362306a36Sopenharmony_ci	if (pieces.init_size) {
493462306a36Sopenharmony_ci		D_INFO("Copying (but not loading) init instr len %zd\n",
493562306a36Sopenharmony_ci		       pieces.init_size);
493662306a36Sopenharmony_ci		memcpy(il->ucode_init.v_addr, pieces.init, pieces.init_size);
493762306a36Sopenharmony_ci	}
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_ci	/* Initialization data */
494062306a36Sopenharmony_ci	if (pieces.init_data_size) {
494162306a36Sopenharmony_ci		D_INFO("Copying (but not loading) init data len %zd\n",
494262306a36Sopenharmony_ci		       pieces.init_data_size);
494362306a36Sopenharmony_ci		memcpy(il->ucode_init_data.v_addr, pieces.init_data,
494462306a36Sopenharmony_ci		       pieces.init_data_size);
494562306a36Sopenharmony_ci	}
494662306a36Sopenharmony_ci
494762306a36Sopenharmony_ci	/* Bootstrap instructions */
494862306a36Sopenharmony_ci	D_INFO("Copying (but not loading) boot instr len %zd\n",
494962306a36Sopenharmony_ci	       pieces.boot_size);
495062306a36Sopenharmony_ci	memcpy(il->ucode_boot.v_addr, pieces.boot, pieces.boot_size);
495162306a36Sopenharmony_ci
495262306a36Sopenharmony_ci	/*
495362306a36Sopenharmony_ci	 * figure out the offset of chain noise reset and gain commands
495462306a36Sopenharmony_ci	 * base on the size of standard phy calibration commands table size
495562306a36Sopenharmony_ci	 */
495662306a36Sopenharmony_ci	il->_4965.phy_calib_chain_noise_reset_cmd =
495762306a36Sopenharmony_ci	    standard_phy_calibration_size;
495862306a36Sopenharmony_ci	il->_4965.phy_calib_chain_noise_gain_cmd =
495962306a36Sopenharmony_ci	    standard_phy_calibration_size + 1;
496062306a36Sopenharmony_ci
496162306a36Sopenharmony_ci	/**************************************************
496262306a36Sopenharmony_ci	 * This is still part of probe() in a sense...
496362306a36Sopenharmony_ci	 *
496462306a36Sopenharmony_ci	 * 9. Setup and register with mac80211 and debugfs
496562306a36Sopenharmony_ci	 **************************************************/
496662306a36Sopenharmony_ci	err = il4965_mac_setup_register(il, max_probe_length);
496762306a36Sopenharmony_ci	if (err)
496862306a36Sopenharmony_ci		goto out_unbind;
496962306a36Sopenharmony_ci
497062306a36Sopenharmony_ci	il_dbgfs_register(il, DRV_NAME);
497162306a36Sopenharmony_ci
497262306a36Sopenharmony_ci	err = sysfs_create_group(&il->pci_dev->dev.kobj, &il_attribute_group);
497362306a36Sopenharmony_ci	if (err) {
497462306a36Sopenharmony_ci		IL_ERR("failed to create sysfs device attributes\n");
497562306a36Sopenharmony_ci		goto out_unbind;
497662306a36Sopenharmony_ci	}
497762306a36Sopenharmony_ci
497862306a36Sopenharmony_ci	/* We have our copies now, allow OS release its copies */
497962306a36Sopenharmony_ci	release_firmware(ucode_raw);
498062306a36Sopenharmony_ci	complete(&il->_4965.firmware_loading_complete);
498162306a36Sopenharmony_ci	return;
498262306a36Sopenharmony_ci
498362306a36Sopenharmony_citry_again:
498462306a36Sopenharmony_ci	/* try next, if any */
498562306a36Sopenharmony_ci	if (il4965_request_firmware(il, false))
498662306a36Sopenharmony_ci		goto out_unbind;
498762306a36Sopenharmony_ci	release_firmware(ucode_raw);
498862306a36Sopenharmony_ci	return;
498962306a36Sopenharmony_ci
499062306a36Sopenharmony_cierr_pci_alloc:
499162306a36Sopenharmony_ci	IL_ERR("failed to allocate pci memory\n");
499262306a36Sopenharmony_ci	il4965_dealloc_ucode_pci(il);
499362306a36Sopenharmony_ciout_unbind:
499462306a36Sopenharmony_ci	complete(&il->_4965.firmware_loading_complete);
499562306a36Sopenharmony_ci	device_release_driver(&il->pci_dev->dev);
499662306a36Sopenharmony_ci	release_firmware(ucode_raw);
499762306a36Sopenharmony_ci}
499862306a36Sopenharmony_ci
499962306a36Sopenharmony_cistatic const char *const desc_lookup_text[] = {
500062306a36Sopenharmony_ci	"OK",
500162306a36Sopenharmony_ci	"FAIL",
500262306a36Sopenharmony_ci	"BAD_PARAM",
500362306a36Sopenharmony_ci	"BAD_CHECKSUM",
500462306a36Sopenharmony_ci	"NMI_INTERRUPT_WDG",
500562306a36Sopenharmony_ci	"SYSASSERT",
500662306a36Sopenharmony_ci	"FATAL_ERROR",
500762306a36Sopenharmony_ci	"BAD_COMMAND",
500862306a36Sopenharmony_ci	"HW_ERROR_TUNE_LOCK",
500962306a36Sopenharmony_ci	"HW_ERROR_TEMPERATURE",
501062306a36Sopenharmony_ci	"ILLEGAL_CHAN_FREQ",
501162306a36Sopenharmony_ci	"VCC_NOT_STBL",
501262306a36Sopenharmony_ci	"FH49_ERROR",
501362306a36Sopenharmony_ci	"NMI_INTERRUPT_HOST",
501462306a36Sopenharmony_ci	"NMI_INTERRUPT_ACTION_PT",
501562306a36Sopenharmony_ci	"NMI_INTERRUPT_UNKNOWN",
501662306a36Sopenharmony_ci	"UCODE_VERSION_MISMATCH",
501762306a36Sopenharmony_ci	"HW_ERROR_ABS_LOCK",
501862306a36Sopenharmony_ci	"HW_ERROR_CAL_LOCK_FAIL",
501962306a36Sopenharmony_ci	"NMI_INTERRUPT_INST_ACTION_PT",
502062306a36Sopenharmony_ci	"NMI_INTERRUPT_DATA_ACTION_PT",
502162306a36Sopenharmony_ci	"NMI_TRM_HW_ER",
502262306a36Sopenharmony_ci	"NMI_INTERRUPT_TRM",
502362306a36Sopenharmony_ci	"NMI_INTERRUPT_BREAK_POINT",
502462306a36Sopenharmony_ci	"DEBUG_0",
502562306a36Sopenharmony_ci	"DEBUG_1",
502662306a36Sopenharmony_ci	"DEBUG_2",
502762306a36Sopenharmony_ci	"DEBUG_3",
502862306a36Sopenharmony_ci};
502962306a36Sopenharmony_ci
503062306a36Sopenharmony_cistatic struct {
503162306a36Sopenharmony_ci	char *name;
503262306a36Sopenharmony_ci	u8 num;
503362306a36Sopenharmony_ci} advanced_lookup[] = {
503462306a36Sopenharmony_ci	{
503562306a36Sopenharmony_ci	"NMI_INTERRUPT_WDG", 0x34}, {
503662306a36Sopenharmony_ci	"SYSASSERT", 0x35}, {
503762306a36Sopenharmony_ci	"UCODE_VERSION_MISMATCH", 0x37}, {
503862306a36Sopenharmony_ci	"BAD_COMMAND", 0x38}, {
503962306a36Sopenharmony_ci	"NMI_INTERRUPT_DATA_ACTION_PT", 0x3C}, {
504062306a36Sopenharmony_ci	"FATAL_ERROR", 0x3D}, {
504162306a36Sopenharmony_ci	"NMI_TRM_HW_ERR", 0x46}, {
504262306a36Sopenharmony_ci	"NMI_INTERRUPT_TRM", 0x4C}, {
504362306a36Sopenharmony_ci	"NMI_INTERRUPT_BREAK_POINT", 0x54}, {
504462306a36Sopenharmony_ci	"NMI_INTERRUPT_WDG_RXF_FULL", 0x5C}, {
504562306a36Sopenharmony_ci	"NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64}, {
504662306a36Sopenharmony_ci	"NMI_INTERRUPT_HOST", 0x66}, {
504762306a36Sopenharmony_ci	"NMI_INTERRUPT_ACTION_PT", 0x7C}, {
504862306a36Sopenharmony_ci	"NMI_INTERRUPT_UNKNOWN", 0x84}, {
504962306a36Sopenharmony_ci	"NMI_INTERRUPT_INST_ACTION_PT", 0x86}, {
505062306a36Sopenharmony_ci"ADVANCED_SYSASSERT", 0},};
505162306a36Sopenharmony_ci
505262306a36Sopenharmony_cistatic const char *
505362306a36Sopenharmony_ciil4965_desc_lookup(u32 num)
505462306a36Sopenharmony_ci{
505562306a36Sopenharmony_ci	int i;
505662306a36Sopenharmony_ci	int max = ARRAY_SIZE(desc_lookup_text);
505762306a36Sopenharmony_ci
505862306a36Sopenharmony_ci	if (num < max)
505962306a36Sopenharmony_ci		return desc_lookup_text[num];
506062306a36Sopenharmony_ci
506162306a36Sopenharmony_ci	max = ARRAY_SIZE(advanced_lookup) - 1;
506262306a36Sopenharmony_ci	for (i = 0; i < max; i++) {
506362306a36Sopenharmony_ci		if (advanced_lookup[i].num == num)
506462306a36Sopenharmony_ci			break;
506562306a36Sopenharmony_ci	}
506662306a36Sopenharmony_ci	return advanced_lookup[i].name;
506762306a36Sopenharmony_ci}
506862306a36Sopenharmony_ci
506962306a36Sopenharmony_ci#define ERROR_START_OFFSET  (1 * sizeof(u32))
507062306a36Sopenharmony_ci#define ERROR_ELEM_SIZE     (7 * sizeof(u32))
507162306a36Sopenharmony_ci
507262306a36Sopenharmony_civoid
507362306a36Sopenharmony_ciil4965_dump_nic_error_log(struct il_priv *il)
507462306a36Sopenharmony_ci{
507562306a36Sopenharmony_ci	u32 data2, line;
507662306a36Sopenharmony_ci	u32 desc, time, count, base, data1;
507762306a36Sopenharmony_ci	u32 blink1, blink2, ilink1, ilink2;
507862306a36Sopenharmony_ci	u32 pc, hcmd;
507962306a36Sopenharmony_ci
508062306a36Sopenharmony_ci	if (il->ucode_type == UCODE_INIT)
508162306a36Sopenharmony_ci		base = le32_to_cpu(il->card_alive_init.error_event_table_ptr);
508262306a36Sopenharmony_ci	else
508362306a36Sopenharmony_ci		base = le32_to_cpu(il->card_alive.error_event_table_ptr);
508462306a36Sopenharmony_ci
508562306a36Sopenharmony_ci	if (!il->ops->is_valid_rtc_data_addr(base)) {
508662306a36Sopenharmony_ci		IL_ERR("Not valid error log pointer 0x%08X for %s uCode\n",
508762306a36Sopenharmony_ci		       base, (il->ucode_type == UCODE_INIT) ? "Init" : "RT");
508862306a36Sopenharmony_ci		return;
508962306a36Sopenharmony_ci	}
509062306a36Sopenharmony_ci
509162306a36Sopenharmony_ci	count = il_read_targ_mem(il, base);
509262306a36Sopenharmony_ci
509362306a36Sopenharmony_ci	if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
509462306a36Sopenharmony_ci		IL_ERR("Start IWL Error Log Dump:\n");
509562306a36Sopenharmony_ci		IL_ERR("Status: 0x%08lX, count: %d\n", il->status, count);
509662306a36Sopenharmony_ci	}
509762306a36Sopenharmony_ci
509862306a36Sopenharmony_ci	desc = il_read_targ_mem(il, base + 1 * sizeof(u32));
509962306a36Sopenharmony_ci	il->isr_stats.err_code = desc;
510062306a36Sopenharmony_ci	pc = il_read_targ_mem(il, base + 2 * sizeof(u32));
510162306a36Sopenharmony_ci	blink1 = il_read_targ_mem(il, base + 3 * sizeof(u32));
510262306a36Sopenharmony_ci	blink2 = il_read_targ_mem(il, base + 4 * sizeof(u32));
510362306a36Sopenharmony_ci	ilink1 = il_read_targ_mem(il, base + 5 * sizeof(u32));
510462306a36Sopenharmony_ci	ilink2 = il_read_targ_mem(il, base + 6 * sizeof(u32));
510562306a36Sopenharmony_ci	data1 = il_read_targ_mem(il, base + 7 * sizeof(u32));
510662306a36Sopenharmony_ci	data2 = il_read_targ_mem(il, base + 8 * sizeof(u32));
510762306a36Sopenharmony_ci	line = il_read_targ_mem(il, base + 9 * sizeof(u32));
510862306a36Sopenharmony_ci	time = il_read_targ_mem(il, base + 11 * sizeof(u32));
510962306a36Sopenharmony_ci	hcmd = il_read_targ_mem(il, base + 22 * sizeof(u32));
511062306a36Sopenharmony_ci
511162306a36Sopenharmony_ci	IL_ERR("Desc                                  Time       "
511262306a36Sopenharmony_ci	       "data1      data2      line\n");
511362306a36Sopenharmony_ci	IL_ERR("%-28s (0x%04X) %010u 0x%08X 0x%08X %u\n",
511462306a36Sopenharmony_ci	       il4965_desc_lookup(desc), desc, time, data1, data2, line);
511562306a36Sopenharmony_ci	IL_ERR("pc      blink1  blink2  ilink1  ilink2  hcmd\n");
511662306a36Sopenharmony_ci	IL_ERR("0x%05X 0x%05X 0x%05X 0x%05X 0x%05X 0x%05X\n", pc, blink1,
511762306a36Sopenharmony_ci	       blink2, ilink1, ilink2, hcmd);
511862306a36Sopenharmony_ci}
511962306a36Sopenharmony_ci
512062306a36Sopenharmony_cistatic void
512162306a36Sopenharmony_ciil4965_rf_kill_ct_config(struct il_priv *il)
512262306a36Sopenharmony_ci{
512362306a36Sopenharmony_ci	struct il_ct_kill_config cmd;
512462306a36Sopenharmony_ci	unsigned long flags;
512562306a36Sopenharmony_ci	int ret = 0;
512662306a36Sopenharmony_ci
512762306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
512862306a36Sopenharmony_ci	_il_wr(il, CSR_UCODE_DRV_GP1_CLR,
512962306a36Sopenharmony_ci	       CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
513062306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
513162306a36Sopenharmony_ci
513262306a36Sopenharmony_ci	cmd.critical_temperature_R =
513362306a36Sopenharmony_ci	    cpu_to_le32(il->hw_params.ct_kill_threshold);
513462306a36Sopenharmony_ci
513562306a36Sopenharmony_ci	ret = il_send_cmd_pdu(il, C_CT_KILL_CONFIG, sizeof(cmd), &cmd);
513662306a36Sopenharmony_ci	if (ret)
513762306a36Sopenharmony_ci		IL_ERR("C_CT_KILL_CONFIG failed\n");
513862306a36Sopenharmony_ci	else
513962306a36Sopenharmony_ci		D_INFO("C_CT_KILL_CONFIG " "succeeded, "
514062306a36Sopenharmony_ci		       "critical temperature is %d\n",
514162306a36Sopenharmony_ci		       il->hw_params.ct_kill_threshold);
514262306a36Sopenharmony_ci}
514362306a36Sopenharmony_ci
514462306a36Sopenharmony_cistatic const s8 default_queue_to_tx_fifo[] = {
514562306a36Sopenharmony_ci	IL_TX_FIFO_VO,
514662306a36Sopenharmony_ci	IL_TX_FIFO_VI,
514762306a36Sopenharmony_ci	IL_TX_FIFO_BE,
514862306a36Sopenharmony_ci	IL_TX_FIFO_BK,
514962306a36Sopenharmony_ci	IL49_CMD_FIFO_NUM,
515062306a36Sopenharmony_ci	IL_TX_FIFO_UNUSED,
515162306a36Sopenharmony_ci	IL_TX_FIFO_UNUSED,
515262306a36Sopenharmony_ci};
515362306a36Sopenharmony_ci
515462306a36Sopenharmony_ci#define IL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo))))
515562306a36Sopenharmony_ci
515662306a36Sopenharmony_cistatic int
515762306a36Sopenharmony_ciil4965_alive_notify(struct il_priv *il)
515862306a36Sopenharmony_ci{
515962306a36Sopenharmony_ci	u32 a;
516062306a36Sopenharmony_ci	unsigned long flags;
516162306a36Sopenharmony_ci	int i, chan;
516262306a36Sopenharmony_ci	u32 reg_val;
516362306a36Sopenharmony_ci
516462306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
516562306a36Sopenharmony_ci
516662306a36Sopenharmony_ci	/* Clear 4965's internal Tx Scheduler data base */
516762306a36Sopenharmony_ci	il->scd_base_addr = il_rd_prph(il, IL49_SCD_SRAM_BASE_ADDR);
516862306a36Sopenharmony_ci	a = il->scd_base_addr + IL49_SCD_CONTEXT_DATA_OFFSET;
516962306a36Sopenharmony_ci	for (; a < il->scd_base_addr + IL49_SCD_TX_STTS_BITMAP_OFFSET; a += 4)
517062306a36Sopenharmony_ci		il_write_targ_mem(il, a, 0);
517162306a36Sopenharmony_ci	for (; a < il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET; a += 4)
517262306a36Sopenharmony_ci		il_write_targ_mem(il, a, 0);
517362306a36Sopenharmony_ci	for (;
517462306a36Sopenharmony_ci	     a <
517562306a36Sopenharmony_ci	     il->scd_base_addr +
517662306a36Sopenharmony_ci	     IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(il->hw_params.max_txq_num);
517762306a36Sopenharmony_ci	     a += 4)
517862306a36Sopenharmony_ci		il_write_targ_mem(il, a, 0);
517962306a36Sopenharmony_ci
518062306a36Sopenharmony_ci	/* Tel 4965 where to find Tx byte count tables */
518162306a36Sopenharmony_ci	il_wr_prph(il, IL49_SCD_DRAM_BASE_ADDR, il->scd_bc_tbls.dma >> 10);
518262306a36Sopenharmony_ci
518362306a36Sopenharmony_ci	/* Enable DMA channel */
518462306a36Sopenharmony_ci	for (chan = 0; chan < FH49_TCSR_CHNL_NUM; chan++)
518562306a36Sopenharmony_ci		il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(chan),
518662306a36Sopenharmony_ci		      FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
518762306a36Sopenharmony_ci		      FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
518862306a36Sopenharmony_ci
518962306a36Sopenharmony_ci	/* Update FH chicken bits */
519062306a36Sopenharmony_ci	reg_val = il_rd(il, FH49_TX_CHICKEN_BITS_REG);
519162306a36Sopenharmony_ci	il_wr(il, FH49_TX_CHICKEN_BITS_REG,
519262306a36Sopenharmony_ci	      reg_val | FH49_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
519362306a36Sopenharmony_ci
519462306a36Sopenharmony_ci	/* Disable chain mode for all queues */
519562306a36Sopenharmony_ci	il_wr_prph(il, IL49_SCD_QUEUECHAIN_SEL, 0);
519662306a36Sopenharmony_ci
519762306a36Sopenharmony_ci	/* Initialize each Tx queue (including the command queue) */
519862306a36Sopenharmony_ci	for (i = 0; i < il->hw_params.max_txq_num; i++) {
519962306a36Sopenharmony_ci
520062306a36Sopenharmony_ci		/* TFD circular buffer read/write idxes */
520162306a36Sopenharmony_ci		il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(i), 0);
520262306a36Sopenharmony_ci		il_wr(il, HBUS_TARG_WRPTR, 0 | (i << 8));
520362306a36Sopenharmony_ci
520462306a36Sopenharmony_ci		/* Max Tx Window size for Scheduler-ACK mode */
520562306a36Sopenharmony_ci		il_write_targ_mem(il,
520662306a36Sopenharmony_ci				  il->scd_base_addr +
520762306a36Sopenharmony_ci				  IL49_SCD_CONTEXT_QUEUE_OFFSET(i),
520862306a36Sopenharmony_ci				  (SCD_WIN_SIZE <<
520962306a36Sopenharmony_ci				   IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) &
521062306a36Sopenharmony_ci				  IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK);
521162306a36Sopenharmony_ci
521262306a36Sopenharmony_ci		/* Frame limit */
521362306a36Sopenharmony_ci		il_write_targ_mem(il,
521462306a36Sopenharmony_ci				  il->scd_base_addr +
521562306a36Sopenharmony_ci				  IL49_SCD_CONTEXT_QUEUE_OFFSET(i) +
521662306a36Sopenharmony_ci				  sizeof(u32),
521762306a36Sopenharmony_ci				  (SCD_FRAME_LIMIT <<
521862306a36Sopenharmony_ci				   IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
521962306a36Sopenharmony_ci				  IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK);
522062306a36Sopenharmony_ci
522162306a36Sopenharmony_ci	}
522262306a36Sopenharmony_ci	il_wr_prph(il, IL49_SCD_INTERRUPT_MASK,
522362306a36Sopenharmony_ci		   (1 << il->hw_params.max_txq_num) - 1);
522462306a36Sopenharmony_ci
522562306a36Sopenharmony_ci	/* Activate all Tx DMA/FIFO channels */
522662306a36Sopenharmony_ci	il4965_txq_set_sched(il, IL_MASK(0, 6));
522762306a36Sopenharmony_ci
522862306a36Sopenharmony_ci	il4965_set_wr_ptrs(il, IL_DEFAULT_CMD_QUEUE_NUM, 0);
522962306a36Sopenharmony_ci
523062306a36Sopenharmony_ci	/* make sure all queue are not stopped */
523162306a36Sopenharmony_ci	memset(&il->queue_stopped[0], 0, sizeof(il->queue_stopped));
523262306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
523362306a36Sopenharmony_ci		atomic_set(&il->queue_stop_count[i], 0);
523462306a36Sopenharmony_ci
523562306a36Sopenharmony_ci	/* reset to 0 to enable all the queue first */
523662306a36Sopenharmony_ci	il->txq_ctx_active_msk = 0;
523762306a36Sopenharmony_ci	/* Map each Tx/cmd queue to its corresponding fifo */
523862306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(default_queue_to_tx_fifo) != 7);
523962306a36Sopenharmony_ci
524062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(default_queue_to_tx_fifo); i++) {
524162306a36Sopenharmony_ci		int ac = default_queue_to_tx_fifo[i];
524262306a36Sopenharmony_ci
524362306a36Sopenharmony_ci		il_txq_ctx_activate(il, i);
524462306a36Sopenharmony_ci
524562306a36Sopenharmony_ci		if (ac == IL_TX_FIFO_UNUSED)
524662306a36Sopenharmony_ci			continue;
524762306a36Sopenharmony_ci
524862306a36Sopenharmony_ci		il4965_tx_queue_set_status(il, &il->txq[i], ac, 0);
524962306a36Sopenharmony_ci	}
525062306a36Sopenharmony_ci
525162306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
525262306a36Sopenharmony_ci
525362306a36Sopenharmony_ci	return 0;
525462306a36Sopenharmony_ci}
525562306a36Sopenharmony_ci
525662306a36Sopenharmony_ci/*
525762306a36Sopenharmony_ci * il4965_alive_start - called after N_ALIVE notification received
525862306a36Sopenharmony_ci *                   from protocol/runtime uCode (initialization uCode's
525962306a36Sopenharmony_ci *                   Alive gets handled by il_init_alive_start()).
526062306a36Sopenharmony_ci */
526162306a36Sopenharmony_cistatic void
526262306a36Sopenharmony_ciil4965_alive_start(struct il_priv *il)
526362306a36Sopenharmony_ci{
526462306a36Sopenharmony_ci	int ret = 0;
526562306a36Sopenharmony_ci
526662306a36Sopenharmony_ci	D_INFO("Runtime Alive received.\n");
526762306a36Sopenharmony_ci
526862306a36Sopenharmony_ci	if (il->card_alive.is_valid != UCODE_VALID_OK) {
526962306a36Sopenharmony_ci		/* We had an error bringing up the hardware, so take it
527062306a36Sopenharmony_ci		 * all the way back down so we can try again */
527162306a36Sopenharmony_ci		D_INFO("Alive failed.\n");
527262306a36Sopenharmony_ci		goto restart;
527362306a36Sopenharmony_ci	}
527462306a36Sopenharmony_ci
527562306a36Sopenharmony_ci	/* Initialize uCode has loaded Runtime uCode ... verify inst image.
527662306a36Sopenharmony_ci	 * This is a paranoid check, because we would not have gotten the
527762306a36Sopenharmony_ci	 * "runtime" alive if code weren't properly loaded.  */
527862306a36Sopenharmony_ci	if (il4965_verify_ucode(il)) {
527962306a36Sopenharmony_ci		/* Runtime instruction load was bad;
528062306a36Sopenharmony_ci		 * take it all the way back down so we can try again */
528162306a36Sopenharmony_ci		D_INFO("Bad runtime uCode load.\n");
528262306a36Sopenharmony_ci		goto restart;
528362306a36Sopenharmony_ci	}
528462306a36Sopenharmony_ci
528562306a36Sopenharmony_ci	ret = il4965_alive_notify(il);
528662306a36Sopenharmony_ci	if (ret) {
528762306a36Sopenharmony_ci		IL_WARN("Could not complete ALIVE transition [ntf]: %d\n", ret);
528862306a36Sopenharmony_ci		goto restart;
528962306a36Sopenharmony_ci	}
529062306a36Sopenharmony_ci
529162306a36Sopenharmony_ci	/* After the ALIVE response, we can send host commands to the uCode */
529262306a36Sopenharmony_ci	set_bit(S_ALIVE, &il->status);
529362306a36Sopenharmony_ci
529462306a36Sopenharmony_ci	/* Enable watchdog to monitor the driver tx queues */
529562306a36Sopenharmony_ci	il_setup_watchdog(il);
529662306a36Sopenharmony_ci
529762306a36Sopenharmony_ci	if (il_is_rfkill(il))
529862306a36Sopenharmony_ci		return;
529962306a36Sopenharmony_ci
530062306a36Sopenharmony_ci	ieee80211_wake_queues(il->hw);
530162306a36Sopenharmony_ci
530262306a36Sopenharmony_ci	il->active_rate = RATES_MASK;
530362306a36Sopenharmony_ci
530462306a36Sopenharmony_ci	il_power_update_mode(il, true);
530562306a36Sopenharmony_ci	D_INFO("Updated power mode\n");
530662306a36Sopenharmony_ci
530762306a36Sopenharmony_ci	if (il_is_associated(il)) {
530862306a36Sopenharmony_ci		struct il_rxon_cmd *active_rxon =
530962306a36Sopenharmony_ci		    (struct il_rxon_cmd *)&il->active;
531062306a36Sopenharmony_ci		/* apply any changes in staging */
531162306a36Sopenharmony_ci		il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
531262306a36Sopenharmony_ci		active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
531362306a36Sopenharmony_ci	} else {
531462306a36Sopenharmony_ci		/* Initialize our rx_config data */
531562306a36Sopenharmony_ci		il_connection_init_rx_config(il);
531662306a36Sopenharmony_ci
531762306a36Sopenharmony_ci		if (il->ops->set_rxon_chain)
531862306a36Sopenharmony_ci			il->ops->set_rxon_chain(il);
531962306a36Sopenharmony_ci	}
532062306a36Sopenharmony_ci
532162306a36Sopenharmony_ci	/* Configure bluetooth coexistence if enabled */
532262306a36Sopenharmony_ci	il_send_bt_config(il);
532362306a36Sopenharmony_ci
532462306a36Sopenharmony_ci	il4965_reset_run_time_calib(il);
532562306a36Sopenharmony_ci
532662306a36Sopenharmony_ci	set_bit(S_READY, &il->status);
532762306a36Sopenharmony_ci
532862306a36Sopenharmony_ci	/* Configure the adapter for unassociated operation */
532962306a36Sopenharmony_ci	il_commit_rxon(il);
533062306a36Sopenharmony_ci
533162306a36Sopenharmony_ci	/* At this point, the NIC is initialized and operational */
533262306a36Sopenharmony_ci	il4965_rf_kill_ct_config(il);
533362306a36Sopenharmony_ci
533462306a36Sopenharmony_ci	D_INFO("ALIVE processing complete.\n");
533562306a36Sopenharmony_ci	wake_up(&il->wait_command_queue);
533662306a36Sopenharmony_ci
533762306a36Sopenharmony_ci	return;
533862306a36Sopenharmony_ci
533962306a36Sopenharmony_cirestart:
534062306a36Sopenharmony_ci	queue_work(il->workqueue, &il->restart);
534162306a36Sopenharmony_ci}
534262306a36Sopenharmony_ci
534362306a36Sopenharmony_cistatic void il4965_cancel_deferred_work(struct il_priv *il);
534462306a36Sopenharmony_ci
534562306a36Sopenharmony_cistatic void
534662306a36Sopenharmony_ci__il4965_down(struct il_priv *il)
534762306a36Sopenharmony_ci{
534862306a36Sopenharmony_ci	unsigned long flags;
534962306a36Sopenharmony_ci	int exit_pending;
535062306a36Sopenharmony_ci
535162306a36Sopenharmony_ci	D_INFO(DRV_NAME " is going down\n");
535262306a36Sopenharmony_ci
535362306a36Sopenharmony_ci	il_scan_cancel_timeout(il, 200);
535462306a36Sopenharmony_ci
535562306a36Sopenharmony_ci	exit_pending = test_and_set_bit(S_EXIT_PENDING, &il->status);
535662306a36Sopenharmony_ci
535762306a36Sopenharmony_ci	/* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set
535862306a36Sopenharmony_ci	 * to prevent rearm timer */
535962306a36Sopenharmony_ci	del_timer_sync(&il->watchdog);
536062306a36Sopenharmony_ci
536162306a36Sopenharmony_ci	il_clear_ucode_stations(il);
536262306a36Sopenharmony_ci
536362306a36Sopenharmony_ci	/* FIXME: race conditions ? */
536462306a36Sopenharmony_ci	spin_lock_irq(&il->sta_lock);
536562306a36Sopenharmony_ci	/*
536662306a36Sopenharmony_ci	 * Remove all key information that is not stored as part
536762306a36Sopenharmony_ci	 * of station information since mac80211 may not have had
536862306a36Sopenharmony_ci	 * a chance to remove all the keys. When device is
536962306a36Sopenharmony_ci	 * reconfigured by mac80211 after an error all keys will
537062306a36Sopenharmony_ci	 * be reconfigured.
537162306a36Sopenharmony_ci	 */
537262306a36Sopenharmony_ci	memset(il->_4965.wep_keys, 0, sizeof(il->_4965.wep_keys));
537362306a36Sopenharmony_ci	il->_4965.key_mapping_keys = 0;
537462306a36Sopenharmony_ci	spin_unlock_irq(&il->sta_lock);
537562306a36Sopenharmony_ci
537662306a36Sopenharmony_ci	il_dealloc_bcast_stations(il);
537762306a36Sopenharmony_ci	il_clear_driver_stations(il);
537862306a36Sopenharmony_ci
537962306a36Sopenharmony_ci	/* Unblock any waiting calls */
538062306a36Sopenharmony_ci	wake_up_all(&il->wait_command_queue);
538162306a36Sopenharmony_ci
538262306a36Sopenharmony_ci	/* Wipe out the EXIT_PENDING status bit if we are not actually
538362306a36Sopenharmony_ci	 * exiting the module */
538462306a36Sopenharmony_ci	if (!exit_pending)
538562306a36Sopenharmony_ci		clear_bit(S_EXIT_PENDING, &il->status);
538662306a36Sopenharmony_ci
538762306a36Sopenharmony_ci	/* stop and reset the on-board processor */
538862306a36Sopenharmony_ci	_il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
538962306a36Sopenharmony_ci
539062306a36Sopenharmony_ci	/* tell the device to stop sending interrupts */
539162306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
539262306a36Sopenharmony_ci	il_disable_interrupts(il);
539362306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
539462306a36Sopenharmony_ci	il4965_synchronize_irq(il);
539562306a36Sopenharmony_ci
539662306a36Sopenharmony_ci	if (il->mac80211_registered)
539762306a36Sopenharmony_ci		ieee80211_stop_queues(il->hw);
539862306a36Sopenharmony_ci
539962306a36Sopenharmony_ci	/* If we have not previously called il_init() then
540062306a36Sopenharmony_ci	 * clear all bits but the RF Kill bit and return */
540162306a36Sopenharmony_ci	if (!il_is_init(il)) {
540262306a36Sopenharmony_ci		il->status =
540362306a36Sopenharmony_ci		    test_bit(S_RFKILL, &il->status) << S_RFKILL |
540462306a36Sopenharmony_ci		    test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED |
540562306a36Sopenharmony_ci		    test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING;
540662306a36Sopenharmony_ci		goto exit;
540762306a36Sopenharmony_ci	}
540862306a36Sopenharmony_ci
540962306a36Sopenharmony_ci	/* ...otherwise clear out all the status bits but the RF Kill
541062306a36Sopenharmony_ci	 * bit and continue taking the NIC down. */
541162306a36Sopenharmony_ci	il->status &=
541262306a36Sopenharmony_ci	    test_bit(S_RFKILL, &il->status) << S_RFKILL |
541362306a36Sopenharmony_ci	    test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED |
541462306a36Sopenharmony_ci	    test_bit(S_FW_ERROR, &il->status) << S_FW_ERROR |
541562306a36Sopenharmony_ci	    test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING;
541662306a36Sopenharmony_ci
541762306a36Sopenharmony_ci	/*
541862306a36Sopenharmony_ci	 * We disabled and synchronized interrupt, and priv->mutex is taken, so
541962306a36Sopenharmony_ci	 * here is the only thread which will program device registers, but
542062306a36Sopenharmony_ci	 * still have lockdep assertions, so we are taking reg_lock.
542162306a36Sopenharmony_ci	 */
542262306a36Sopenharmony_ci	spin_lock_irq(&il->reg_lock);
542362306a36Sopenharmony_ci	/* FIXME: il_grab_nic_access if rfkill is off ? */
542462306a36Sopenharmony_ci
542562306a36Sopenharmony_ci	il4965_txq_ctx_stop(il);
542662306a36Sopenharmony_ci	il4965_rxq_stop(il);
542762306a36Sopenharmony_ci	/* Power-down device's busmaster DMA clocks */
542862306a36Sopenharmony_ci	_il_wr_prph(il, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT);
542962306a36Sopenharmony_ci	udelay(5);
543062306a36Sopenharmony_ci	/* Make sure (redundant) we've released our request to stay awake */
543162306a36Sopenharmony_ci	_il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
543262306a36Sopenharmony_ci	/* Stop the device, and put it in low power state */
543362306a36Sopenharmony_ci	_il_apm_stop(il);
543462306a36Sopenharmony_ci
543562306a36Sopenharmony_ci	spin_unlock_irq(&il->reg_lock);
543662306a36Sopenharmony_ci
543762306a36Sopenharmony_ci	il4965_txq_ctx_unmap(il);
543862306a36Sopenharmony_ciexit:
543962306a36Sopenharmony_ci	memset(&il->card_alive, 0, sizeof(struct il_alive_resp));
544062306a36Sopenharmony_ci
544162306a36Sopenharmony_ci	dev_kfree_skb(il->beacon_skb);
544262306a36Sopenharmony_ci	il->beacon_skb = NULL;
544362306a36Sopenharmony_ci
544462306a36Sopenharmony_ci	/* clear out any free frames */
544562306a36Sopenharmony_ci	il4965_clear_free_frames(il);
544662306a36Sopenharmony_ci}
544762306a36Sopenharmony_ci
544862306a36Sopenharmony_cistatic void
544962306a36Sopenharmony_ciil4965_down(struct il_priv *il)
545062306a36Sopenharmony_ci{
545162306a36Sopenharmony_ci	mutex_lock(&il->mutex);
545262306a36Sopenharmony_ci	__il4965_down(il);
545362306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
545462306a36Sopenharmony_ci
545562306a36Sopenharmony_ci	il4965_cancel_deferred_work(il);
545662306a36Sopenharmony_ci}
545762306a36Sopenharmony_ci
545862306a36Sopenharmony_ci
545962306a36Sopenharmony_cistatic void
546062306a36Sopenharmony_ciil4965_set_hw_ready(struct il_priv *il)
546162306a36Sopenharmony_ci{
546262306a36Sopenharmony_ci	int ret;
546362306a36Sopenharmony_ci
546462306a36Sopenharmony_ci	il_set_bit(il, CSR_HW_IF_CONFIG_REG,
546562306a36Sopenharmony_ci		   CSR_HW_IF_CONFIG_REG_BIT_NIC_READY);
546662306a36Sopenharmony_ci
546762306a36Sopenharmony_ci	/* See if we got it */
546862306a36Sopenharmony_ci	ret = _il_poll_bit(il, CSR_HW_IF_CONFIG_REG,
546962306a36Sopenharmony_ci			   CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
547062306a36Sopenharmony_ci			   CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
547162306a36Sopenharmony_ci			   100);
547262306a36Sopenharmony_ci	if (ret >= 0)
547362306a36Sopenharmony_ci		il->hw_ready = true;
547462306a36Sopenharmony_ci
547562306a36Sopenharmony_ci	D_INFO("hardware %s ready\n", (il->hw_ready) ? "" : "not");
547662306a36Sopenharmony_ci}
547762306a36Sopenharmony_ci
547862306a36Sopenharmony_cistatic void
547962306a36Sopenharmony_ciil4965_prepare_card_hw(struct il_priv *il)
548062306a36Sopenharmony_ci{
548162306a36Sopenharmony_ci	int ret;
548262306a36Sopenharmony_ci
548362306a36Sopenharmony_ci	il->hw_ready = false;
548462306a36Sopenharmony_ci
548562306a36Sopenharmony_ci	il4965_set_hw_ready(il);
548662306a36Sopenharmony_ci	if (il->hw_ready)
548762306a36Sopenharmony_ci		return;
548862306a36Sopenharmony_ci
548962306a36Sopenharmony_ci	/* If HW is not ready, prepare the conditions to check again */
549062306a36Sopenharmony_ci	il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE);
549162306a36Sopenharmony_ci
549262306a36Sopenharmony_ci	ret =
549362306a36Sopenharmony_ci	    _il_poll_bit(il, CSR_HW_IF_CONFIG_REG,
549462306a36Sopenharmony_ci			 ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE,
549562306a36Sopenharmony_ci			 CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000);
549662306a36Sopenharmony_ci
549762306a36Sopenharmony_ci	/* HW should be ready by now, check again. */
549862306a36Sopenharmony_ci	if (ret != -ETIMEDOUT)
549962306a36Sopenharmony_ci		il4965_set_hw_ready(il);
550062306a36Sopenharmony_ci}
550162306a36Sopenharmony_ci
550262306a36Sopenharmony_ci#define MAX_HW_RESTARTS 5
550362306a36Sopenharmony_ci
550462306a36Sopenharmony_cistatic int
550562306a36Sopenharmony_ci__il4965_up(struct il_priv *il)
550662306a36Sopenharmony_ci{
550762306a36Sopenharmony_ci	int i;
550862306a36Sopenharmony_ci	int ret;
550962306a36Sopenharmony_ci
551062306a36Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status)) {
551162306a36Sopenharmony_ci		IL_WARN("Exit pending; will not bring the NIC up\n");
551262306a36Sopenharmony_ci		return -EIO;
551362306a36Sopenharmony_ci	}
551462306a36Sopenharmony_ci
551562306a36Sopenharmony_ci	if (!il->ucode_data_backup.v_addr || !il->ucode_data.v_addr) {
551662306a36Sopenharmony_ci		IL_ERR("ucode not available for device bringup\n");
551762306a36Sopenharmony_ci		return -EIO;
551862306a36Sopenharmony_ci	}
551962306a36Sopenharmony_ci
552062306a36Sopenharmony_ci	ret = il4965_alloc_bcast_station(il);
552162306a36Sopenharmony_ci	if (ret) {
552262306a36Sopenharmony_ci		il_dealloc_bcast_stations(il);
552362306a36Sopenharmony_ci		return ret;
552462306a36Sopenharmony_ci	}
552562306a36Sopenharmony_ci
552662306a36Sopenharmony_ci	il4965_prepare_card_hw(il);
552762306a36Sopenharmony_ci	if (!il->hw_ready) {
552862306a36Sopenharmony_ci		il_dealloc_bcast_stations(il);
552962306a36Sopenharmony_ci		IL_ERR("HW not ready\n");
553062306a36Sopenharmony_ci		return -EIO;
553162306a36Sopenharmony_ci	}
553262306a36Sopenharmony_ci
553362306a36Sopenharmony_ci	/* If platform's RF_KILL switch is NOT set to KILL */
553462306a36Sopenharmony_ci	if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
553562306a36Sopenharmony_ci		clear_bit(S_RFKILL, &il->status);
553662306a36Sopenharmony_ci	else {
553762306a36Sopenharmony_ci		set_bit(S_RFKILL, &il->status);
553862306a36Sopenharmony_ci		wiphy_rfkill_set_hw_state(il->hw->wiphy, true);
553962306a36Sopenharmony_ci
554062306a36Sopenharmony_ci		il_dealloc_bcast_stations(il);
554162306a36Sopenharmony_ci		il_enable_rfkill_int(il);
554262306a36Sopenharmony_ci		IL_WARN("Radio disabled by HW RF Kill switch\n");
554362306a36Sopenharmony_ci		return 0;
554462306a36Sopenharmony_ci	}
554562306a36Sopenharmony_ci
554662306a36Sopenharmony_ci	_il_wr(il, CSR_INT, 0xFFFFFFFF);
554762306a36Sopenharmony_ci
554862306a36Sopenharmony_ci	/* must be initialised before il_hw_nic_init */
554962306a36Sopenharmony_ci	il->cmd_queue = IL_DEFAULT_CMD_QUEUE_NUM;
555062306a36Sopenharmony_ci
555162306a36Sopenharmony_ci	ret = il4965_hw_nic_init(il);
555262306a36Sopenharmony_ci	if (ret) {
555362306a36Sopenharmony_ci		IL_ERR("Unable to init nic\n");
555462306a36Sopenharmony_ci		il_dealloc_bcast_stations(il);
555562306a36Sopenharmony_ci		return ret;
555662306a36Sopenharmony_ci	}
555762306a36Sopenharmony_ci
555862306a36Sopenharmony_ci	/* make sure rfkill handshake bits are cleared */
555962306a36Sopenharmony_ci	_il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
556062306a36Sopenharmony_ci	_il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
556162306a36Sopenharmony_ci
556262306a36Sopenharmony_ci	/* clear (again), then enable host interrupts */
556362306a36Sopenharmony_ci	_il_wr(il, CSR_INT, 0xFFFFFFFF);
556462306a36Sopenharmony_ci	il_enable_interrupts(il);
556562306a36Sopenharmony_ci
556662306a36Sopenharmony_ci	/* really make sure rfkill handshake bits are cleared */
556762306a36Sopenharmony_ci	_il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
556862306a36Sopenharmony_ci	_il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
556962306a36Sopenharmony_ci
557062306a36Sopenharmony_ci	/* Copy original ucode data image from disk into backup cache.
557162306a36Sopenharmony_ci	 * This will be used to initialize the on-board processor's
557262306a36Sopenharmony_ci	 * data SRAM for a clean start when the runtime program first loads. */
557362306a36Sopenharmony_ci	memcpy(il->ucode_data_backup.v_addr, il->ucode_data.v_addr,
557462306a36Sopenharmony_ci	       il->ucode_data.len);
557562306a36Sopenharmony_ci
557662306a36Sopenharmony_ci	for (i = 0; i < MAX_HW_RESTARTS; i++) {
557762306a36Sopenharmony_ci
557862306a36Sopenharmony_ci		/* load bootstrap state machine,
557962306a36Sopenharmony_ci		 * load bootstrap program into processor's memory,
558062306a36Sopenharmony_ci		 * prepare to load the "initialize" uCode */
558162306a36Sopenharmony_ci		ret = il->ops->load_ucode(il);
558262306a36Sopenharmony_ci
558362306a36Sopenharmony_ci		if (ret) {
558462306a36Sopenharmony_ci			IL_ERR("Unable to set up bootstrap uCode: %d\n", ret);
558562306a36Sopenharmony_ci			continue;
558662306a36Sopenharmony_ci		}
558762306a36Sopenharmony_ci
558862306a36Sopenharmony_ci		/* start card; "initialize" will load runtime ucode */
558962306a36Sopenharmony_ci		il4965_nic_start(il);
559062306a36Sopenharmony_ci
559162306a36Sopenharmony_ci		D_INFO(DRV_NAME " is coming up\n");
559262306a36Sopenharmony_ci
559362306a36Sopenharmony_ci		return 0;
559462306a36Sopenharmony_ci	}
559562306a36Sopenharmony_ci
559662306a36Sopenharmony_ci	set_bit(S_EXIT_PENDING, &il->status);
559762306a36Sopenharmony_ci	__il4965_down(il);
559862306a36Sopenharmony_ci	clear_bit(S_EXIT_PENDING, &il->status);
559962306a36Sopenharmony_ci
560062306a36Sopenharmony_ci	/* tried to restart and config the device for as long as our
560162306a36Sopenharmony_ci	 * patience could withstand */
560262306a36Sopenharmony_ci	IL_ERR("Unable to initialize device after %d attempts.\n", i);
560362306a36Sopenharmony_ci	return -EIO;
560462306a36Sopenharmony_ci}
560562306a36Sopenharmony_ci
560662306a36Sopenharmony_ci/*****************************************************************************
560762306a36Sopenharmony_ci *
560862306a36Sopenharmony_ci * Workqueue callbacks
560962306a36Sopenharmony_ci *
561062306a36Sopenharmony_ci *****************************************************************************/
561162306a36Sopenharmony_ci
561262306a36Sopenharmony_cistatic void
561362306a36Sopenharmony_ciil4965_bg_init_alive_start(struct work_struct *data)
561462306a36Sopenharmony_ci{
561562306a36Sopenharmony_ci	struct il_priv *il =
561662306a36Sopenharmony_ci	    container_of(data, struct il_priv, init_alive_start.work);
561762306a36Sopenharmony_ci
561862306a36Sopenharmony_ci	mutex_lock(&il->mutex);
561962306a36Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status))
562062306a36Sopenharmony_ci		goto out;
562162306a36Sopenharmony_ci
562262306a36Sopenharmony_ci	il->ops->init_alive_start(il);
562362306a36Sopenharmony_ciout:
562462306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
562562306a36Sopenharmony_ci}
562662306a36Sopenharmony_ci
562762306a36Sopenharmony_cistatic void
562862306a36Sopenharmony_ciil4965_bg_alive_start(struct work_struct *data)
562962306a36Sopenharmony_ci{
563062306a36Sopenharmony_ci	struct il_priv *il =
563162306a36Sopenharmony_ci	    container_of(data, struct il_priv, alive_start.work);
563262306a36Sopenharmony_ci
563362306a36Sopenharmony_ci	mutex_lock(&il->mutex);
563462306a36Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status))
563562306a36Sopenharmony_ci		goto out;
563662306a36Sopenharmony_ci
563762306a36Sopenharmony_ci	il4965_alive_start(il);
563862306a36Sopenharmony_ciout:
563962306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
564062306a36Sopenharmony_ci}
564162306a36Sopenharmony_ci
564262306a36Sopenharmony_cistatic void
564362306a36Sopenharmony_ciil4965_bg_run_time_calib_work(struct work_struct *work)
564462306a36Sopenharmony_ci{
564562306a36Sopenharmony_ci	struct il_priv *il = container_of(work, struct il_priv,
564662306a36Sopenharmony_ci					  run_time_calib_work);
564762306a36Sopenharmony_ci
564862306a36Sopenharmony_ci	mutex_lock(&il->mutex);
564962306a36Sopenharmony_ci
565062306a36Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status) ||
565162306a36Sopenharmony_ci	    test_bit(S_SCANNING, &il->status)) {
565262306a36Sopenharmony_ci		mutex_unlock(&il->mutex);
565362306a36Sopenharmony_ci		return;
565462306a36Sopenharmony_ci	}
565562306a36Sopenharmony_ci
565662306a36Sopenharmony_ci	if (il->start_calib) {
565762306a36Sopenharmony_ci		il4965_chain_noise_calibration(il, (void *)&il->_4965.stats);
565862306a36Sopenharmony_ci		il4965_sensitivity_calibration(il, (void *)&il->_4965.stats);
565962306a36Sopenharmony_ci	}
566062306a36Sopenharmony_ci
566162306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
566262306a36Sopenharmony_ci}
566362306a36Sopenharmony_ci
566462306a36Sopenharmony_cistatic void
566562306a36Sopenharmony_ciil4965_bg_restart(struct work_struct *data)
566662306a36Sopenharmony_ci{
566762306a36Sopenharmony_ci	struct il_priv *il = container_of(data, struct il_priv, restart);
566862306a36Sopenharmony_ci
566962306a36Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status))
567062306a36Sopenharmony_ci		return;
567162306a36Sopenharmony_ci
567262306a36Sopenharmony_ci	if (test_and_clear_bit(S_FW_ERROR, &il->status)) {
567362306a36Sopenharmony_ci		mutex_lock(&il->mutex);
567462306a36Sopenharmony_ci		il->is_open = 0;
567562306a36Sopenharmony_ci
567662306a36Sopenharmony_ci		__il4965_down(il);
567762306a36Sopenharmony_ci
567862306a36Sopenharmony_ci		mutex_unlock(&il->mutex);
567962306a36Sopenharmony_ci		il4965_cancel_deferred_work(il);
568062306a36Sopenharmony_ci		ieee80211_restart_hw(il->hw);
568162306a36Sopenharmony_ci	} else {
568262306a36Sopenharmony_ci		il4965_down(il);
568362306a36Sopenharmony_ci
568462306a36Sopenharmony_ci		mutex_lock(&il->mutex);
568562306a36Sopenharmony_ci		if (test_bit(S_EXIT_PENDING, &il->status)) {
568662306a36Sopenharmony_ci			mutex_unlock(&il->mutex);
568762306a36Sopenharmony_ci			return;
568862306a36Sopenharmony_ci		}
568962306a36Sopenharmony_ci
569062306a36Sopenharmony_ci		__il4965_up(il);
569162306a36Sopenharmony_ci		mutex_unlock(&il->mutex);
569262306a36Sopenharmony_ci	}
569362306a36Sopenharmony_ci}
569462306a36Sopenharmony_ci
569562306a36Sopenharmony_cistatic void
569662306a36Sopenharmony_ciil4965_bg_rx_replenish(struct work_struct *data)
569762306a36Sopenharmony_ci{
569862306a36Sopenharmony_ci	struct il_priv *il = container_of(data, struct il_priv, rx_replenish);
569962306a36Sopenharmony_ci
570062306a36Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status))
570162306a36Sopenharmony_ci		return;
570262306a36Sopenharmony_ci
570362306a36Sopenharmony_ci	mutex_lock(&il->mutex);
570462306a36Sopenharmony_ci	il4965_rx_replenish(il);
570562306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
570662306a36Sopenharmony_ci}
570762306a36Sopenharmony_ci
570862306a36Sopenharmony_ci/*****************************************************************************
570962306a36Sopenharmony_ci *
571062306a36Sopenharmony_ci * mac80211 entry point functions
571162306a36Sopenharmony_ci *
571262306a36Sopenharmony_ci *****************************************************************************/
571362306a36Sopenharmony_ci
571462306a36Sopenharmony_ci#define UCODE_READY_TIMEOUT	(4 * HZ)
571562306a36Sopenharmony_ci
571662306a36Sopenharmony_ci/*
571762306a36Sopenharmony_ci * Not a mac80211 entry point function, but it fits in with all the
571862306a36Sopenharmony_ci * other mac80211 functions grouped here.
571962306a36Sopenharmony_ci */
572062306a36Sopenharmony_cistatic int
572162306a36Sopenharmony_ciil4965_mac_setup_register(struct il_priv *il, u32 max_probe_length)
572262306a36Sopenharmony_ci{
572362306a36Sopenharmony_ci	int ret;
572462306a36Sopenharmony_ci	struct ieee80211_hw *hw = il->hw;
572562306a36Sopenharmony_ci
572662306a36Sopenharmony_ci	hw->rate_control_algorithm = "iwl-4965-rs";
572762306a36Sopenharmony_ci
572862306a36Sopenharmony_ci	/* Tell mac80211 our characteristics */
572962306a36Sopenharmony_ci	ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
573062306a36Sopenharmony_ci	ieee80211_hw_set(hw, SUPPORTS_PS);
573162306a36Sopenharmony_ci	ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
573262306a36Sopenharmony_ci	ieee80211_hw_set(hw, SPECTRUM_MGMT);
573362306a36Sopenharmony_ci	ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
573462306a36Sopenharmony_ci	ieee80211_hw_set(hw, SIGNAL_DBM);
573562306a36Sopenharmony_ci	ieee80211_hw_set(hw, AMPDU_AGGREGATION);
573662306a36Sopenharmony_ci	if (il->cfg->sku & IL_SKU_N)
573762306a36Sopenharmony_ci		hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS |
573862306a36Sopenharmony_ci				       NL80211_FEATURE_STATIC_SMPS;
573962306a36Sopenharmony_ci
574062306a36Sopenharmony_ci	hw->sta_data_size = sizeof(struct il_station_priv);
574162306a36Sopenharmony_ci	hw->vif_data_size = sizeof(struct il_vif_priv);
574262306a36Sopenharmony_ci
574362306a36Sopenharmony_ci	hw->wiphy->interface_modes =
574462306a36Sopenharmony_ci	    BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC);
574562306a36Sopenharmony_ci
574662306a36Sopenharmony_ci	hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
574762306a36Sopenharmony_ci	hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
574862306a36Sopenharmony_ci				       REGULATORY_DISABLE_BEACON_HINTS;
574962306a36Sopenharmony_ci
575062306a36Sopenharmony_ci	/*
575162306a36Sopenharmony_ci	 * For now, disable PS by default because it affects
575262306a36Sopenharmony_ci	 * RX performance significantly.
575362306a36Sopenharmony_ci	 */
575462306a36Sopenharmony_ci	hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
575562306a36Sopenharmony_ci
575662306a36Sopenharmony_ci	hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
575762306a36Sopenharmony_ci	/* we create the 802.11 header and a zero-length SSID element */
575862306a36Sopenharmony_ci	hw->wiphy->max_scan_ie_len = max_probe_length - 24 - 2;
575962306a36Sopenharmony_ci
576062306a36Sopenharmony_ci	/* Default value; 4 EDCA QOS priorities */
576162306a36Sopenharmony_ci	hw->queues = 4;
576262306a36Sopenharmony_ci
576362306a36Sopenharmony_ci	hw->max_listen_interval = IL_CONN_MAX_LISTEN_INTERVAL;
576462306a36Sopenharmony_ci
576562306a36Sopenharmony_ci	if (il->bands[NL80211_BAND_2GHZ].n_channels)
576662306a36Sopenharmony_ci		il->hw->wiphy->bands[NL80211_BAND_2GHZ] =
576762306a36Sopenharmony_ci		    &il->bands[NL80211_BAND_2GHZ];
576862306a36Sopenharmony_ci	if (il->bands[NL80211_BAND_5GHZ].n_channels)
576962306a36Sopenharmony_ci		il->hw->wiphy->bands[NL80211_BAND_5GHZ] =
577062306a36Sopenharmony_ci		    &il->bands[NL80211_BAND_5GHZ];
577162306a36Sopenharmony_ci
577262306a36Sopenharmony_ci	il_leds_init(il);
577362306a36Sopenharmony_ci
577462306a36Sopenharmony_ci	wiphy_ext_feature_set(il->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
577562306a36Sopenharmony_ci
577662306a36Sopenharmony_ci	ret = ieee80211_register_hw(il->hw);
577762306a36Sopenharmony_ci	if (ret) {
577862306a36Sopenharmony_ci		IL_ERR("Failed to register hw (error %d)\n", ret);
577962306a36Sopenharmony_ci		return ret;
578062306a36Sopenharmony_ci	}
578162306a36Sopenharmony_ci	il->mac80211_registered = 1;
578262306a36Sopenharmony_ci
578362306a36Sopenharmony_ci	return 0;
578462306a36Sopenharmony_ci}
578562306a36Sopenharmony_ci
578662306a36Sopenharmony_ciint
578762306a36Sopenharmony_ciil4965_mac_start(struct ieee80211_hw *hw)
578862306a36Sopenharmony_ci{
578962306a36Sopenharmony_ci	struct il_priv *il = hw->priv;
579062306a36Sopenharmony_ci	int ret;
579162306a36Sopenharmony_ci
579262306a36Sopenharmony_ci	D_MAC80211("enter\n");
579362306a36Sopenharmony_ci
579462306a36Sopenharmony_ci	/* we should be verifying the device is ready to be opened */
579562306a36Sopenharmony_ci	mutex_lock(&il->mutex);
579662306a36Sopenharmony_ci	ret = __il4965_up(il);
579762306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
579862306a36Sopenharmony_ci
579962306a36Sopenharmony_ci	if (ret)
580062306a36Sopenharmony_ci		return ret;
580162306a36Sopenharmony_ci
580262306a36Sopenharmony_ci	if (il_is_rfkill(il))
580362306a36Sopenharmony_ci		goto out;
580462306a36Sopenharmony_ci
580562306a36Sopenharmony_ci	D_INFO("Start UP work done.\n");
580662306a36Sopenharmony_ci
580762306a36Sopenharmony_ci	/* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from
580862306a36Sopenharmony_ci	 * mac80211 will not be run successfully. */
580962306a36Sopenharmony_ci	ret = wait_event_timeout(il->wait_command_queue,
581062306a36Sopenharmony_ci				 test_bit(S_READY, &il->status),
581162306a36Sopenharmony_ci				 UCODE_READY_TIMEOUT);
581262306a36Sopenharmony_ci	if (!ret) {
581362306a36Sopenharmony_ci		if (!test_bit(S_READY, &il->status)) {
581462306a36Sopenharmony_ci			IL_ERR("START_ALIVE timeout after %dms.\n",
581562306a36Sopenharmony_ci				jiffies_to_msecs(UCODE_READY_TIMEOUT));
581662306a36Sopenharmony_ci			return -ETIMEDOUT;
581762306a36Sopenharmony_ci		}
581862306a36Sopenharmony_ci	}
581962306a36Sopenharmony_ci
582062306a36Sopenharmony_ci	il4965_led_enable(il);
582162306a36Sopenharmony_ci
582262306a36Sopenharmony_ciout:
582362306a36Sopenharmony_ci	il->is_open = 1;
582462306a36Sopenharmony_ci	D_MAC80211("leave\n");
582562306a36Sopenharmony_ci	return 0;
582662306a36Sopenharmony_ci}
582762306a36Sopenharmony_ci
582862306a36Sopenharmony_civoid
582962306a36Sopenharmony_ciil4965_mac_stop(struct ieee80211_hw *hw)
583062306a36Sopenharmony_ci{
583162306a36Sopenharmony_ci	struct il_priv *il = hw->priv;
583262306a36Sopenharmony_ci
583362306a36Sopenharmony_ci	D_MAC80211("enter\n");
583462306a36Sopenharmony_ci
583562306a36Sopenharmony_ci	if (!il->is_open)
583662306a36Sopenharmony_ci		return;
583762306a36Sopenharmony_ci
583862306a36Sopenharmony_ci	il->is_open = 0;
583962306a36Sopenharmony_ci
584062306a36Sopenharmony_ci	il4965_down(il);
584162306a36Sopenharmony_ci
584262306a36Sopenharmony_ci	flush_workqueue(il->workqueue);
584362306a36Sopenharmony_ci
584462306a36Sopenharmony_ci	/* User space software may expect getting rfkill changes
584562306a36Sopenharmony_ci	 * even if interface is down */
584662306a36Sopenharmony_ci	_il_wr(il, CSR_INT, 0xFFFFFFFF);
584762306a36Sopenharmony_ci	il_enable_rfkill_int(il);
584862306a36Sopenharmony_ci
584962306a36Sopenharmony_ci	D_MAC80211("leave\n");
585062306a36Sopenharmony_ci}
585162306a36Sopenharmony_ci
585262306a36Sopenharmony_civoid
585362306a36Sopenharmony_ciil4965_mac_tx(struct ieee80211_hw *hw,
585462306a36Sopenharmony_ci	      struct ieee80211_tx_control *control,
585562306a36Sopenharmony_ci	      struct sk_buff *skb)
585662306a36Sopenharmony_ci{
585762306a36Sopenharmony_ci	struct il_priv *il = hw->priv;
585862306a36Sopenharmony_ci
585962306a36Sopenharmony_ci	D_MACDUMP("enter\n");
586062306a36Sopenharmony_ci
586162306a36Sopenharmony_ci	D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len,
586262306a36Sopenharmony_ci	     ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate);
586362306a36Sopenharmony_ci
586462306a36Sopenharmony_ci	if (il4965_tx_skb(il, control->sta, skb))
586562306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
586662306a36Sopenharmony_ci
586762306a36Sopenharmony_ci	D_MACDUMP("leave\n");
586862306a36Sopenharmony_ci}
586962306a36Sopenharmony_ci
587062306a36Sopenharmony_civoid
587162306a36Sopenharmony_ciil4965_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
587262306a36Sopenharmony_ci			   struct ieee80211_key_conf *keyconf,
587362306a36Sopenharmony_ci			   struct ieee80211_sta *sta, u32 iv32, u16 * phase1key)
587462306a36Sopenharmony_ci{
587562306a36Sopenharmony_ci	struct il_priv *il = hw->priv;
587662306a36Sopenharmony_ci
587762306a36Sopenharmony_ci	D_MAC80211("enter\n");
587862306a36Sopenharmony_ci
587962306a36Sopenharmony_ci	il4965_update_tkip_key(il, keyconf, sta, iv32, phase1key);
588062306a36Sopenharmony_ci
588162306a36Sopenharmony_ci	D_MAC80211("leave\n");
588262306a36Sopenharmony_ci}
588362306a36Sopenharmony_ci
588462306a36Sopenharmony_ciint
588562306a36Sopenharmony_ciil4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
588662306a36Sopenharmony_ci		   struct ieee80211_vif *vif, struct ieee80211_sta *sta,
588762306a36Sopenharmony_ci		   struct ieee80211_key_conf *key)
588862306a36Sopenharmony_ci{
588962306a36Sopenharmony_ci	struct il_priv *il = hw->priv;
589062306a36Sopenharmony_ci	int ret;
589162306a36Sopenharmony_ci	u8 sta_id;
589262306a36Sopenharmony_ci	bool is_default_wep_key = false;
589362306a36Sopenharmony_ci
589462306a36Sopenharmony_ci	D_MAC80211("enter\n");
589562306a36Sopenharmony_ci
589662306a36Sopenharmony_ci	if (il->cfg->mod_params->sw_crypto) {
589762306a36Sopenharmony_ci		D_MAC80211("leave - hwcrypto disabled\n");
589862306a36Sopenharmony_ci		return -EOPNOTSUPP;
589962306a36Sopenharmony_ci	}
590062306a36Sopenharmony_ci
590162306a36Sopenharmony_ci	/*
590262306a36Sopenharmony_ci	 * To support IBSS RSN, don't program group keys in IBSS, the
590362306a36Sopenharmony_ci	 * hardware will then not attempt to decrypt the frames.
590462306a36Sopenharmony_ci	 */
590562306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_ADHOC &&
590662306a36Sopenharmony_ci	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
590762306a36Sopenharmony_ci		D_MAC80211("leave - ad-hoc group key\n");
590862306a36Sopenharmony_ci		return -EOPNOTSUPP;
590962306a36Sopenharmony_ci	}
591062306a36Sopenharmony_ci
591162306a36Sopenharmony_ci	sta_id = il_sta_id_or_broadcast(il, sta);
591262306a36Sopenharmony_ci	if (sta_id == IL_INVALID_STATION)
591362306a36Sopenharmony_ci		return -EINVAL;
591462306a36Sopenharmony_ci
591562306a36Sopenharmony_ci	mutex_lock(&il->mutex);
591662306a36Sopenharmony_ci	il_scan_cancel_timeout(il, 100);
591762306a36Sopenharmony_ci
591862306a36Sopenharmony_ci	/*
591962306a36Sopenharmony_ci	 * If we are getting WEP group key and we didn't receive any key mapping
592062306a36Sopenharmony_ci	 * so far, we are in legacy wep mode (group key only), otherwise we are
592162306a36Sopenharmony_ci	 * in 1X mode.
592262306a36Sopenharmony_ci	 * In legacy wep mode, we use another host command to the uCode.
592362306a36Sopenharmony_ci	 */
592462306a36Sopenharmony_ci	if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
592562306a36Sopenharmony_ci	     key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) {
592662306a36Sopenharmony_ci		if (cmd == SET_KEY)
592762306a36Sopenharmony_ci			is_default_wep_key = !il->_4965.key_mapping_keys;
592862306a36Sopenharmony_ci		else
592962306a36Sopenharmony_ci			is_default_wep_key =
593062306a36Sopenharmony_ci			    (key->hw_key_idx == HW_KEY_DEFAULT);
593162306a36Sopenharmony_ci	}
593262306a36Sopenharmony_ci
593362306a36Sopenharmony_ci	switch (cmd) {
593462306a36Sopenharmony_ci	case SET_KEY:
593562306a36Sopenharmony_ci		if (is_default_wep_key)
593662306a36Sopenharmony_ci			ret = il4965_set_default_wep_key(il, key);
593762306a36Sopenharmony_ci		else
593862306a36Sopenharmony_ci			ret = il4965_set_dynamic_key(il, key, sta_id);
593962306a36Sopenharmony_ci
594062306a36Sopenharmony_ci		D_MAC80211("enable hwcrypto key\n");
594162306a36Sopenharmony_ci		break;
594262306a36Sopenharmony_ci	case DISABLE_KEY:
594362306a36Sopenharmony_ci		if (is_default_wep_key)
594462306a36Sopenharmony_ci			ret = il4965_remove_default_wep_key(il, key);
594562306a36Sopenharmony_ci		else
594662306a36Sopenharmony_ci			ret = il4965_remove_dynamic_key(il, key, sta_id);
594762306a36Sopenharmony_ci
594862306a36Sopenharmony_ci		D_MAC80211("disable hwcrypto key\n");
594962306a36Sopenharmony_ci		break;
595062306a36Sopenharmony_ci	default:
595162306a36Sopenharmony_ci		ret = -EINVAL;
595262306a36Sopenharmony_ci	}
595362306a36Sopenharmony_ci
595462306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
595562306a36Sopenharmony_ci	D_MAC80211("leave\n");
595662306a36Sopenharmony_ci
595762306a36Sopenharmony_ci	return ret;
595862306a36Sopenharmony_ci}
595962306a36Sopenharmony_ci
596062306a36Sopenharmony_ciint
596162306a36Sopenharmony_ciil4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
596262306a36Sopenharmony_ci			struct ieee80211_ampdu_params *params)
596362306a36Sopenharmony_ci{
596462306a36Sopenharmony_ci	struct il_priv *il = hw->priv;
596562306a36Sopenharmony_ci	int ret = -EINVAL;
596662306a36Sopenharmony_ci	struct ieee80211_sta *sta = params->sta;
596762306a36Sopenharmony_ci	enum ieee80211_ampdu_mlme_action action = params->action;
596862306a36Sopenharmony_ci	u16 tid = params->tid;
596962306a36Sopenharmony_ci	u16 *ssn = &params->ssn;
597062306a36Sopenharmony_ci
597162306a36Sopenharmony_ci	D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid);
597262306a36Sopenharmony_ci
597362306a36Sopenharmony_ci	if (!(il->cfg->sku & IL_SKU_N))
597462306a36Sopenharmony_ci		return -EACCES;
597562306a36Sopenharmony_ci
597662306a36Sopenharmony_ci	mutex_lock(&il->mutex);
597762306a36Sopenharmony_ci
597862306a36Sopenharmony_ci	switch (action) {
597962306a36Sopenharmony_ci	case IEEE80211_AMPDU_RX_START:
598062306a36Sopenharmony_ci		D_HT("start Rx\n");
598162306a36Sopenharmony_ci		ret = il4965_sta_rx_agg_start(il, sta, tid, *ssn);
598262306a36Sopenharmony_ci		break;
598362306a36Sopenharmony_ci	case IEEE80211_AMPDU_RX_STOP:
598462306a36Sopenharmony_ci		D_HT("stop Rx\n");
598562306a36Sopenharmony_ci		ret = il4965_sta_rx_agg_stop(il, sta, tid);
598662306a36Sopenharmony_ci		if (test_bit(S_EXIT_PENDING, &il->status))
598762306a36Sopenharmony_ci			ret = 0;
598862306a36Sopenharmony_ci		break;
598962306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_START:
599062306a36Sopenharmony_ci		D_HT("start Tx\n");
599162306a36Sopenharmony_ci		ret = il4965_tx_agg_start(il, vif, sta, tid, ssn);
599262306a36Sopenharmony_ci		break;
599362306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_CONT:
599462306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_FLUSH:
599562306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
599662306a36Sopenharmony_ci		D_HT("stop Tx\n");
599762306a36Sopenharmony_ci		ret = il4965_tx_agg_stop(il, vif, sta, tid);
599862306a36Sopenharmony_ci		if (test_bit(S_EXIT_PENDING, &il->status))
599962306a36Sopenharmony_ci			ret = 0;
600062306a36Sopenharmony_ci		break;
600162306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_OPERATIONAL:
600262306a36Sopenharmony_ci		ret = 0;
600362306a36Sopenharmony_ci		break;
600462306a36Sopenharmony_ci	}
600562306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
600662306a36Sopenharmony_ci
600762306a36Sopenharmony_ci	return ret;
600862306a36Sopenharmony_ci}
600962306a36Sopenharmony_ci
601062306a36Sopenharmony_ciint
601162306a36Sopenharmony_ciil4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
601262306a36Sopenharmony_ci		   struct ieee80211_sta *sta)
601362306a36Sopenharmony_ci{
601462306a36Sopenharmony_ci	struct il_priv *il = hw->priv;
601562306a36Sopenharmony_ci	struct il_station_priv *sta_priv = (void *)sta->drv_priv;
601662306a36Sopenharmony_ci	bool is_ap = vif->type == NL80211_IFTYPE_STATION;
601762306a36Sopenharmony_ci	int ret;
601862306a36Sopenharmony_ci	u8 sta_id;
601962306a36Sopenharmony_ci
602062306a36Sopenharmony_ci	D_INFO("received request to add station %pM\n", sta->addr);
602162306a36Sopenharmony_ci	mutex_lock(&il->mutex);
602262306a36Sopenharmony_ci	D_INFO("proceeding to add station %pM\n", sta->addr);
602362306a36Sopenharmony_ci	sta_priv->common.sta_id = IL_INVALID_STATION;
602462306a36Sopenharmony_ci
602562306a36Sopenharmony_ci	atomic_set(&sta_priv->pending_frames, 0);
602662306a36Sopenharmony_ci
602762306a36Sopenharmony_ci	ret =
602862306a36Sopenharmony_ci	    il_add_station_common(il, sta->addr, is_ap, sta, &sta_id);
602962306a36Sopenharmony_ci	if (ret) {
603062306a36Sopenharmony_ci		IL_ERR("Unable to add station %pM (%d)\n", sta->addr, ret);
603162306a36Sopenharmony_ci		/* Should we return success if return code is EEXIST ? */
603262306a36Sopenharmony_ci		mutex_unlock(&il->mutex);
603362306a36Sopenharmony_ci		return ret;
603462306a36Sopenharmony_ci	}
603562306a36Sopenharmony_ci
603662306a36Sopenharmony_ci	sta_priv->common.sta_id = sta_id;
603762306a36Sopenharmony_ci
603862306a36Sopenharmony_ci	/* Initialize rate scaling */
603962306a36Sopenharmony_ci	D_INFO("Initializing rate scaling for station %pM\n", sta->addr);
604062306a36Sopenharmony_ci	il4965_rs_rate_init(il, sta, sta_id);
604162306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
604262306a36Sopenharmony_ci
604362306a36Sopenharmony_ci	return 0;
604462306a36Sopenharmony_ci}
604562306a36Sopenharmony_ci
604662306a36Sopenharmony_civoid
604762306a36Sopenharmony_ciil4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
604862306a36Sopenharmony_ci			  struct ieee80211_channel_switch *ch_switch)
604962306a36Sopenharmony_ci{
605062306a36Sopenharmony_ci	struct il_priv *il = hw->priv;
605162306a36Sopenharmony_ci	const struct il_channel_info *ch_info;
605262306a36Sopenharmony_ci	struct ieee80211_conf *conf = &hw->conf;
605362306a36Sopenharmony_ci	struct ieee80211_channel *channel = ch_switch->chandef.chan;
605462306a36Sopenharmony_ci	struct il_ht_config *ht_conf = &il->current_ht_config;
605562306a36Sopenharmony_ci	u16 ch;
605662306a36Sopenharmony_ci
605762306a36Sopenharmony_ci	D_MAC80211("enter\n");
605862306a36Sopenharmony_ci
605962306a36Sopenharmony_ci	mutex_lock(&il->mutex);
606062306a36Sopenharmony_ci
606162306a36Sopenharmony_ci	if (il_is_rfkill(il))
606262306a36Sopenharmony_ci		goto out;
606362306a36Sopenharmony_ci
606462306a36Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status) ||
606562306a36Sopenharmony_ci	    test_bit(S_SCANNING, &il->status) ||
606662306a36Sopenharmony_ci	    test_bit(S_CHANNEL_SWITCH_PENDING, &il->status))
606762306a36Sopenharmony_ci		goto out;
606862306a36Sopenharmony_ci
606962306a36Sopenharmony_ci	if (!il_is_associated(il))
607062306a36Sopenharmony_ci		goto out;
607162306a36Sopenharmony_ci
607262306a36Sopenharmony_ci	if (!il->ops->set_channel_switch)
607362306a36Sopenharmony_ci		goto out;
607462306a36Sopenharmony_ci
607562306a36Sopenharmony_ci	ch = channel->hw_value;
607662306a36Sopenharmony_ci	if (le16_to_cpu(il->active.channel) == ch)
607762306a36Sopenharmony_ci		goto out;
607862306a36Sopenharmony_ci
607962306a36Sopenharmony_ci	ch_info = il_get_channel_info(il, channel->band, ch);
608062306a36Sopenharmony_ci	if (!il_is_channel_valid(ch_info)) {
608162306a36Sopenharmony_ci		D_MAC80211("invalid channel\n");
608262306a36Sopenharmony_ci		goto out;
608362306a36Sopenharmony_ci	}
608462306a36Sopenharmony_ci
608562306a36Sopenharmony_ci	spin_lock_irq(&il->lock);
608662306a36Sopenharmony_ci
608762306a36Sopenharmony_ci	il->current_ht_config.smps = conf->smps_mode;
608862306a36Sopenharmony_ci
608962306a36Sopenharmony_ci	/* Configure HT40 channels */
609062306a36Sopenharmony_ci	switch (cfg80211_get_chandef_type(&ch_switch->chandef)) {
609162306a36Sopenharmony_ci	case NL80211_CHAN_NO_HT:
609262306a36Sopenharmony_ci	case NL80211_CHAN_HT20:
609362306a36Sopenharmony_ci		il->ht.is_40mhz = false;
609462306a36Sopenharmony_ci		il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
609562306a36Sopenharmony_ci		break;
609662306a36Sopenharmony_ci	case NL80211_CHAN_HT40MINUS:
609762306a36Sopenharmony_ci		il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
609862306a36Sopenharmony_ci		il->ht.is_40mhz = true;
609962306a36Sopenharmony_ci		break;
610062306a36Sopenharmony_ci	case NL80211_CHAN_HT40PLUS:
610162306a36Sopenharmony_ci		il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
610262306a36Sopenharmony_ci		il->ht.is_40mhz = true;
610362306a36Sopenharmony_ci		break;
610462306a36Sopenharmony_ci	}
610562306a36Sopenharmony_ci
610662306a36Sopenharmony_ci	if ((le16_to_cpu(il->staging.channel) != ch))
610762306a36Sopenharmony_ci		il->staging.flags = 0;
610862306a36Sopenharmony_ci
610962306a36Sopenharmony_ci	il_set_rxon_channel(il, channel);
611062306a36Sopenharmony_ci	il_set_rxon_ht(il, ht_conf);
611162306a36Sopenharmony_ci	il_set_flags_for_band(il, channel->band, il->vif);
611262306a36Sopenharmony_ci
611362306a36Sopenharmony_ci	spin_unlock_irq(&il->lock);
611462306a36Sopenharmony_ci
611562306a36Sopenharmony_ci	il_set_rate(il);
611662306a36Sopenharmony_ci	/*
611762306a36Sopenharmony_ci	 * at this point, staging_rxon has the
611862306a36Sopenharmony_ci	 * configuration for channel switch
611962306a36Sopenharmony_ci	 */
612062306a36Sopenharmony_ci	set_bit(S_CHANNEL_SWITCH_PENDING, &il->status);
612162306a36Sopenharmony_ci	il->switch_channel = cpu_to_le16(ch);
612262306a36Sopenharmony_ci	if (il->ops->set_channel_switch(il, ch_switch)) {
612362306a36Sopenharmony_ci		clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status);
612462306a36Sopenharmony_ci		il->switch_channel = 0;
612562306a36Sopenharmony_ci		ieee80211_chswitch_done(il->vif, false);
612662306a36Sopenharmony_ci	}
612762306a36Sopenharmony_ci
612862306a36Sopenharmony_ciout:
612962306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
613062306a36Sopenharmony_ci	D_MAC80211("leave\n");
613162306a36Sopenharmony_ci}
613262306a36Sopenharmony_ci
613362306a36Sopenharmony_civoid
613462306a36Sopenharmony_ciil4965_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
613562306a36Sopenharmony_ci			unsigned int *total_flags, u64 multicast)
613662306a36Sopenharmony_ci{
613762306a36Sopenharmony_ci	struct il_priv *il = hw->priv;
613862306a36Sopenharmony_ci	__le32 filter_or = 0, filter_nand = 0;
613962306a36Sopenharmony_ci
614062306a36Sopenharmony_ci#define CHK(test, flag)	do { \
614162306a36Sopenharmony_ci	if (*total_flags & (test))		\
614262306a36Sopenharmony_ci		filter_or |= (flag);		\
614362306a36Sopenharmony_ci	else					\
614462306a36Sopenharmony_ci		filter_nand |= (flag);		\
614562306a36Sopenharmony_ci	} while (0)
614662306a36Sopenharmony_ci
614762306a36Sopenharmony_ci	D_MAC80211("Enter: changed: 0x%x, total: 0x%x\n", changed_flags,
614862306a36Sopenharmony_ci		   *total_flags);
614962306a36Sopenharmony_ci
615062306a36Sopenharmony_ci	CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK);
615162306a36Sopenharmony_ci	/* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */
615262306a36Sopenharmony_ci	CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK);
615362306a36Sopenharmony_ci	CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK);
615462306a36Sopenharmony_ci
615562306a36Sopenharmony_ci#undef CHK
615662306a36Sopenharmony_ci
615762306a36Sopenharmony_ci	mutex_lock(&il->mutex);
615862306a36Sopenharmony_ci
615962306a36Sopenharmony_ci	il->staging.filter_flags &= ~filter_nand;
616062306a36Sopenharmony_ci	il->staging.filter_flags |= filter_or;
616162306a36Sopenharmony_ci
616262306a36Sopenharmony_ci	/*
616362306a36Sopenharmony_ci	 * Not committing directly because hardware can perform a scan,
616462306a36Sopenharmony_ci	 * but we'll eventually commit the filter flags change anyway.
616562306a36Sopenharmony_ci	 */
616662306a36Sopenharmony_ci
616762306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
616862306a36Sopenharmony_ci
616962306a36Sopenharmony_ci	/*
617062306a36Sopenharmony_ci	 * Receiving all multicast frames is always enabled by the
617162306a36Sopenharmony_ci	 * default flags setup in il_connection_init_rx_config()
617262306a36Sopenharmony_ci	 * since we currently do not support programming multicast
617362306a36Sopenharmony_ci	 * filters into the device.
617462306a36Sopenharmony_ci	 */
617562306a36Sopenharmony_ci	*total_flags &=
617662306a36Sopenharmony_ci	    FIF_OTHER_BSS | FIF_ALLMULTI |
617762306a36Sopenharmony_ci	    FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL;
617862306a36Sopenharmony_ci}
617962306a36Sopenharmony_ci
618062306a36Sopenharmony_ci/*****************************************************************************
618162306a36Sopenharmony_ci *
618262306a36Sopenharmony_ci * driver setup and teardown
618362306a36Sopenharmony_ci *
618462306a36Sopenharmony_ci *****************************************************************************/
618562306a36Sopenharmony_ci
618662306a36Sopenharmony_cistatic void
618762306a36Sopenharmony_ciil4965_bg_txpower_work(struct work_struct *work)
618862306a36Sopenharmony_ci{
618962306a36Sopenharmony_ci	struct il_priv *il = container_of(work, struct il_priv,
619062306a36Sopenharmony_ci					  txpower_work);
619162306a36Sopenharmony_ci
619262306a36Sopenharmony_ci	mutex_lock(&il->mutex);
619362306a36Sopenharmony_ci
619462306a36Sopenharmony_ci	/* If a scan happened to start before we got here
619562306a36Sopenharmony_ci	 * then just return; the stats notification will
619662306a36Sopenharmony_ci	 * kick off another scheduled work to compensate for
619762306a36Sopenharmony_ci	 * any temperature delta we missed here. */
619862306a36Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status) ||
619962306a36Sopenharmony_ci	    test_bit(S_SCANNING, &il->status))
620062306a36Sopenharmony_ci		goto out;
620162306a36Sopenharmony_ci
620262306a36Sopenharmony_ci	/* Regardless of if we are associated, we must reconfigure the
620362306a36Sopenharmony_ci	 * TX power since frames can be sent on non-radar channels while
620462306a36Sopenharmony_ci	 * not associated */
620562306a36Sopenharmony_ci	il->ops->send_tx_power(il);
620662306a36Sopenharmony_ci
620762306a36Sopenharmony_ci	/* Update last_temperature to keep is_calib_needed from running
620862306a36Sopenharmony_ci	 * when it isn't needed... */
620962306a36Sopenharmony_ci	il->last_temperature = il->temperature;
621062306a36Sopenharmony_ciout:
621162306a36Sopenharmony_ci	mutex_unlock(&il->mutex);
621262306a36Sopenharmony_ci}
621362306a36Sopenharmony_ci
621462306a36Sopenharmony_cistatic int
621562306a36Sopenharmony_ciil4965_setup_deferred_work(struct il_priv *il)
621662306a36Sopenharmony_ci{
621762306a36Sopenharmony_ci	il->workqueue = create_singlethread_workqueue(DRV_NAME);
621862306a36Sopenharmony_ci	if (!il->workqueue)
621962306a36Sopenharmony_ci		return -ENOMEM;
622062306a36Sopenharmony_ci
622162306a36Sopenharmony_ci	init_waitqueue_head(&il->wait_command_queue);
622262306a36Sopenharmony_ci
622362306a36Sopenharmony_ci	INIT_WORK(&il->restart, il4965_bg_restart);
622462306a36Sopenharmony_ci	INIT_WORK(&il->rx_replenish, il4965_bg_rx_replenish);
622562306a36Sopenharmony_ci	INIT_WORK(&il->run_time_calib_work, il4965_bg_run_time_calib_work);
622662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&il->init_alive_start, il4965_bg_init_alive_start);
622762306a36Sopenharmony_ci	INIT_DELAYED_WORK(&il->alive_start, il4965_bg_alive_start);
622862306a36Sopenharmony_ci
622962306a36Sopenharmony_ci	il_setup_scan_deferred_work(il);
623062306a36Sopenharmony_ci
623162306a36Sopenharmony_ci	INIT_WORK(&il->txpower_work, il4965_bg_txpower_work);
623262306a36Sopenharmony_ci
623362306a36Sopenharmony_ci	timer_setup(&il->stats_periodic, il4965_bg_stats_periodic, 0);
623462306a36Sopenharmony_ci
623562306a36Sopenharmony_ci	timer_setup(&il->watchdog, il_bg_watchdog, 0);
623662306a36Sopenharmony_ci
623762306a36Sopenharmony_ci	tasklet_setup(&il->irq_tasklet, il4965_irq_tasklet);
623862306a36Sopenharmony_ci
623962306a36Sopenharmony_ci	return 0;
624062306a36Sopenharmony_ci}
624162306a36Sopenharmony_ci
624262306a36Sopenharmony_cistatic void
624362306a36Sopenharmony_ciil4965_cancel_deferred_work(struct il_priv *il)
624462306a36Sopenharmony_ci{
624562306a36Sopenharmony_ci	cancel_work_sync(&il->txpower_work);
624662306a36Sopenharmony_ci	cancel_delayed_work_sync(&il->init_alive_start);
624762306a36Sopenharmony_ci	cancel_delayed_work(&il->alive_start);
624862306a36Sopenharmony_ci	cancel_work_sync(&il->run_time_calib_work);
624962306a36Sopenharmony_ci
625062306a36Sopenharmony_ci	il_cancel_scan_deferred_work(il);
625162306a36Sopenharmony_ci
625262306a36Sopenharmony_ci	del_timer_sync(&il->stats_periodic);
625362306a36Sopenharmony_ci}
625462306a36Sopenharmony_ci
625562306a36Sopenharmony_cistatic void
625662306a36Sopenharmony_ciil4965_init_hw_rates(struct il_priv *il, struct ieee80211_rate *rates)
625762306a36Sopenharmony_ci{
625862306a36Sopenharmony_ci	int i;
625962306a36Sopenharmony_ci
626062306a36Sopenharmony_ci	for (i = 0; i < RATE_COUNT_LEGACY; i++) {
626162306a36Sopenharmony_ci		rates[i].bitrate = il_rates[i].ieee * 5;
626262306a36Sopenharmony_ci		rates[i].hw_value = i;	/* Rate scaling will work on idxes */
626362306a36Sopenharmony_ci		rates[i].hw_value_short = i;
626462306a36Sopenharmony_ci		rates[i].flags = 0;
626562306a36Sopenharmony_ci		if ((i >= IL_FIRST_CCK_RATE) && (i <= IL_LAST_CCK_RATE)) {
626662306a36Sopenharmony_ci			/*
626762306a36Sopenharmony_ci			 * If CCK != 1M then set short preamble rate flag.
626862306a36Sopenharmony_ci			 */
626962306a36Sopenharmony_ci			rates[i].flags |=
627062306a36Sopenharmony_ci			    (il_rates[i].plcp ==
627162306a36Sopenharmony_ci			     RATE_1M_PLCP) ? 0 : IEEE80211_RATE_SHORT_PREAMBLE;
627262306a36Sopenharmony_ci		}
627362306a36Sopenharmony_ci	}
627462306a36Sopenharmony_ci}
627562306a36Sopenharmony_ci
627662306a36Sopenharmony_ci/*
627762306a36Sopenharmony_ci * Acquire il->lock before calling this function !
627862306a36Sopenharmony_ci */
627962306a36Sopenharmony_civoid
628062306a36Sopenharmony_ciil4965_set_wr_ptrs(struct il_priv *il, int txq_id, u32 idx)
628162306a36Sopenharmony_ci{
628262306a36Sopenharmony_ci	il_wr(il, HBUS_TARG_WRPTR, (idx & 0xff) | (txq_id << 8));
628362306a36Sopenharmony_ci	il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(txq_id), idx);
628462306a36Sopenharmony_ci}
628562306a36Sopenharmony_ci
628662306a36Sopenharmony_civoid
628762306a36Sopenharmony_ciil4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq,
628862306a36Sopenharmony_ci			   int tx_fifo_id, int scd_retry)
628962306a36Sopenharmony_ci{
629062306a36Sopenharmony_ci	int txq_id = txq->q.id;
629162306a36Sopenharmony_ci
629262306a36Sopenharmony_ci	/* Find out whether to activate Tx queue */
629362306a36Sopenharmony_ci	int active = test_bit(txq_id, &il->txq_ctx_active_msk) ? 1 : 0;
629462306a36Sopenharmony_ci
629562306a36Sopenharmony_ci	/* Set up and activate */
629662306a36Sopenharmony_ci	il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id),
629762306a36Sopenharmony_ci		   (active << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
629862306a36Sopenharmony_ci		   (tx_fifo_id << IL49_SCD_QUEUE_STTS_REG_POS_TXF) |
629962306a36Sopenharmony_ci		   (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_WSL) |
630062306a36Sopenharmony_ci		   (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK) |
630162306a36Sopenharmony_ci		   IL49_SCD_QUEUE_STTS_REG_MSK);
630262306a36Sopenharmony_ci
630362306a36Sopenharmony_ci	txq->sched_retry = scd_retry;
630462306a36Sopenharmony_ci
630562306a36Sopenharmony_ci	D_INFO("%s %s Queue %d on AC %d\n", active ? "Activate" : "Deactivate",
630662306a36Sopenharmony_ci	       scd_retry ? "BA" : "AC", txq_id, tx_fifo_id);
630762306a36Sopenharmony_ci}
630862306a36Sopenharmony_ci
630962306a36Sopenharmony_cistatic const struct ieee80211_ops il4965_mac_ops = {
631062306a36Sopenharmony_ci	.tx = il4965_mac_tx,
631162306a36Sopenharmony_ci	.wake_tx_queue = ieee80211_handle_wake_tx_queue,
631262306a36Sopenharmony_ci	.start = il4965_mac_start,
631362306a36Sopenharmony_ci	.stop = il4965_mac_stop,
631462306a36Sopenharmony_ci	.add_interface = il_mac_add_interface,
631562306a36Sopenharmony_ci	.remove_interface = il_mac_remove_interface,
631662306a36Sopenharmony_ci	.change_interface = il_mac_change_interface,
631762306a36Sopenharmony_ci	.config = il_mac_config,
631862306a36Sopenharmony_ci	.configure_filter = il4965_configure_filter,
631962306a36Sopenharmony_ci	.set_key = il4965_mac_set_key,
632062306a36Sopenharmony_ci	.update_tkip_key = il4965_mac_update_tkip_key,
632162306a36Sopenharmony_ci	.conf_tx = il_mac_conf_tx,
632262306a36Sopenharmony_ci	.reset_tsf = il_mac_reset_tsf,
632362306a36Sopenharmony_ci	.bss_info_changed = il_mac_bss_info_changed,
632462306a36Sopenharmony_ci	.ampdu_action = il4965_mac_ampdu_action,
632562306a36Sopenharmony_ci	.hw_scan = il_mac_hw_scan,
632662306a36Sopenharmony_ci	.sta_add = il4965_mac_sta_add,
632762306a36Sopenharmony_ci	.sta_remove = il_mac_sta_remove,
632862306a36Sopenharmony_ci	.channel_switch = il4965_mac_channel_switch,
632962306a36Sopenharmony_ci	.tx_last_beacon = il_mac_tx_last_beacon,
633062306a36Sopenharmony_ci	.flush = il_mac_flush,
633162306a36Sopenharmony_ci};
633262306a36Sopenharmony_ci
633362306a36Sopenharmony_cistatic int
633462306a36Sopenharmony_ciil4965_init_drv(struct il_priv *il)
633562306a36Sopenharmony_ci{
633662306a36Sopenharmony_ci	int ret;
633762306a36Sopenharmony_ci
633862306a36Sopenharmony_ci	spin_lock_init(&il->sta_lock);
633962306a36Sopenharmony_ci	spin_lock_init(&il->hcmd_lock);
634062306a36Sopenharmony_ci
634162306a36Sopenharmony_ci	INIT_LIST_HEAD(&il->free_frames);
634262306a36Sopenharmony_ci
634362306a36Sopenharmony_ci	mutex_init(&il->mutex);
634462306a36Sopenharmony_ci
634562306a36Sopenharmony_ci	il->ieee_channels = NULL;
634662306a36Sopenharmony_ci	il->ieee_rates = NULL;
634762306a36Sopenharmony_ci	il->band = NL80211_BAND_2GHZ;
634862306a36Sopenharmony_ci
634962306a36Sopenharmony_ci	il->iw_mode = NL80211_IFTYPE_STATION;
635062306a36Sopenharmony_ci	il->current_ht_config.smps = IEEE80211_SMPS_STATIC;
635162306a36Sopenharmony_ci	il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF;
635262306a36Sopenharmony_ci
635362306a36Sopenharmony_ci	/* initialize force reset */
635462306a36Sopenharmony_ci	il->force_reset.reset_duration = IL_DELAY_NEXT_FORCE_FW_RELOAD;
635562306a36Sopenharmony_ci
635662306a36Sopenharmony_ci	/* Choose which receivers/antennas to use */
635762306a36Sopenharmony_ci	if (il->ops->set_rxon_chain)
635862306a36Sopenharmony_ci		il->ops->set_rxon_chain(il);
635962306a36Sopenharmony_ci
636062306a36Sopenharmony_ci	il_init_scan_params(il);
636162306a36Sopenharmony_ci
636262306a36Sopenharmony_ci	ret = il_init_channel_map(il);
636362306a36Sopenharmony_ci	if (ret) {
636462306a36Sopenharmony_ci		IL_ERR("initializing regulatory failed: %d\n", ret);
636562306a36Sopenharmony_ci		goto err;
636662306a36Sopenharmony_ci	}
636762306a36Sopenharmony_ci
636862306a36Sopenharmony_ci	ret = il_init_geos(il);
636962306a36Sopenharmony_ci	if (ret) {
637062306a36Sopenharmony_ci		IL_ERR("initializing geos failed: %d\n", ret);
637162306a36Sopenharmony_ci		goto err_free_channel_map;
637262306a36Sopenharmony_ci	}
637362306a36Sopenharmony_ci	il4965_init_hw_rates(il, il->ieee_rates);
637462306a36Sopenharmony_ci
637562306a36Sopenharmony_ci	return 0;
637662306a36Sopenharmony_ci
637762306a36Sopenharmony_cierr_free_channel_map:
637862306a36Sopenharmony_ci	il_free_channel_map(il);
637962306a36Sopenharmony_cierr:
638062306a36Sopenharmony_ci	return ret;
638162306a36Sopenharmony_ci}
638262306a36Sopenharmony_ci
638362306a36Sopenharmony_cistatic void
638462306a36Sopenharmony_ciil4965_uninit_drv(struct il_priv *il)
638562306a36Sopenharmony_ci{
638662306a36Sopenharmony_ci	il_free_geos(il);
638762306a36Sopenharmony_ci	il_free_channel_map(il);
638862306a36Sopenharmony_ci	kfree(il->scan_cmd);
638962306a36Sopenharmony_ci}
639062306a36Sopenharmony_ci
639162306a36Sopenharmony_cistatic void
639262306a36Sopenharmony_ciil4965_hw_detect(struct il_priv *il)
639362306a36Sopenharmony_ci{
639462306a36Sopenharmony_ci	il->hw_rev = _il_rd(il, CSR_HW_REV);
639562306a36Sopenharmony_ci	il->hw_wa_rev = _il_rd(il, CSR_HW_REV_WA_REG);
639662306a36Sopenharmony_ci	il->rev_id = il->pci_dev->revision;
639762306a36Sopenharmony_ci	D_INFO("HW Revision ID = 0x%X\n", il->rev_id);
639862306a36Sopenharmony_ci}
639962306a36Sopenharmony_ci
640062306a36Sopenharmony_cistatic const struct il_sensitivity_ranges il4965_sensitivity = {
640162306a36Sopenharmony_ci	.min_nrg_cck = 97,
640262306a36Sopenharmony_ci	.max_nrg_cck = 0,	/* not used, set to 0 */
640362306a36Sopenharmony_ci
640462306a36Sopenharmony_ci	.auto_corr_min_ofdm = 85,
640562306a36Sopenharmony_ci	.auto_corr_min_ofdm_mrc = 170,
640662306a36Sopenharmony_ci	.auto_corr_min_ofdm_x1 = 105,
640762306a36Sopenharmony_ci	.auto_corr_min_ofdm_mrc_x1 = 220,
640862306a36Sopenharmony_ci
640962306a36Sopenharmony_ci	.auto_corr_max_ofdm = 120,
641062306a36Sopenharmony_ci	.auto_corr_max_ofdm_mrc = 210,
641162306a36Sopenharmony_ci	.auto_corr_max_ofdm_x1 = 140,
641262306a36Sopenharmony_ci	.auto_corr_max_ofdm_mrc_x1 = 270,
641362306a36Sopenharmony_ci
641462306a36Sopenharmony_ci	.auto_corr_min_cck = 125,
641562306a36Sopenharmony_ci	.auto_corr_max_cck = 200,
641662306a36Sopenharmony_ci	.auto_corr_min_cck_mrc = 200,
641762306a36Sopenharmony_ci	.auto_corr_max_cck_mrc = 400,
641862306a36Sopenharmony_ci
641962306a36Sopenharmony_ci	.nrg_th_cck = 100,
642062306a36Sopenharmony_ci	.nrg_th_ofdm = 100,
642162306a36Sopenharmony_ci
642262306a36Sopenharmony_ci	.barker_corr_th_min = 190,
642362306a36Sopenharmony_ci	.barker_corr_th_min_mrc = 390,
642462306a36Sopenharmony_ci	.nrg_th_cca = 62,
642562306a36Sopenharmony_ci};
642662306a36Sopenharmony_ci
642762306a36Sopenharmony_cistatic void
642862306a36Sopenharmony_ciil4965_set_hw_params(struct il_priv *il)
642962306a36Sopenharmony_ci{
643062306a36Sopenharmony_ci	il->hw_params.bcast_id = IL4965_BROADCAST_ID;
643162306a36Sopenharmony_ci	il->hw_params.max_rxq_size = RX_QUEUE_SIZE;
643262306a36Sopenharmony_ci	il->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG;
643362306a36Sopenharmony_ci	if (il->cfg->mod_params->amsdu_size_8K)
643462306a36Sopenharmony_ci		il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_8K);
643562306a36Sopenharmony_ci	else
643662306a36Sopenharmony_ci		il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_4K);
643762306a36Sopenharmony_ci
643862306a36Sopenharmony_ci	il->hw_params.max_beacon_itrvl = IL_MAX_UCODE_BEACON_INTERVAL;
643962306a36Sopenharmony_ci
644062306a36Sopenharmony_ci	if (il->cfg->mod_params->disable_11n)
644162306a36Sopenharmony_ci		il->cfg->sku &= ~IL_SKU_N;
644262306a36Sopenharmony_ci
644362306a36Sopenharmony_ci	if (il->cfg->mod_params->num_of_queues >= IL_MIN_NUM_QUEUES &&
644462306a36Sopenharmony_ci	    il->cfg->mod_params->num_of_queues <= IL49_NUM_QUEUES)
644562306a36Sopenharmony_ci		il->cfg->num_of_queues =
644662306a36Sopenharmony_ci		    il->cfg->mod_params->num_of_queues;
644762306a36Sopenharmony_ci
644862306a36Sopenharmony_ci	il->hw_params.max_txq_num = il->cfg->num_of_queues;
644962306a36Sopenharmony_ci	il->hw_params.dma_chnl_num = FH49_TCSR_CHNL_NUM;
645062306a36Sopenharmony_ci	il->hw_params.scd_bc_tbls_size =
645162306a36Sopenharmony_ci	    il->cfg->num_of_queues *
645262306a36Sopenharmony_ci	    sizeof(struct il4965_scd_bc_tbl);
645362306a36Sopenharmony_ci
645462306a36Sopenharmony_ci	il->hw_params.tfd_size = sizeof(struct il_tfd);
645562306a36Sopenharmony_ci	il->hw_params.max_stations = IL4965_STATION_COUNT;
645662306a36Sopenharmony_ci	il->hw_params.max_data_size = IL49_RTC_DATA_SIZE;
645762306a36Sopenharmony_ci	il->hw_params.max_inst_size = IL49_RTC_INST_SIZE;
645862306a36Sopenharmony_ci	il->hw_params.max_bsm_size = BSM_SRAM_SIZE;
645962306a36Sopenharmony_ci	il->hw_params.ht40_channel = BIT(NL80211_BAND_5GHZ);
646062306a36Sopenharmony_ci
646162306a36Sopenharmony_ci	il->hw_params.rx_wrt_ptr_reg = FH49_RSCSR_CHNL0_WPTR;
646262306a36Sopenharmony_ci
646362306a36Sopenharmony_ci	il->hw_params.tx_chains_num = il4965_num_of_ant(il->cfg->valid_tx_ant);
646462306a36Sopenharmony_ci	il->hw_params.rx_chains_num = il4965_num_of_ant(il->cfg->valid_rx_ant);
646562306a36Sopenharmony_ci	il->hw_params.valid_tx_ant = il->cfg->valid_tx_ant;
646662306a36Sopenharmony_ci	il->hw_params.valid_rx_ant = il->cfg->valid_rx_ant;
646762306a36Sopenharmony_ci
646862306a36Sopenharmony_ci	il->hw_params.ct_kill_threshold =
646962306a36Sopenharmony_ci	   celsius_to_kelvin(CT_KILL_THRESHOLD_LEGACY);
647062306a36Sopenharmony_ci
647162306a36Sopenharmony_ci	il->hw_params.sens = &il4965_sensitivity;
647262306a36Sopenharmony_ci	il->hw_params.beacon_time_tsf_bits = IL4965_EXT_BEACON_TIME_POS;
647362306a36Sopenharmony_ci}
647462306a36Sopenharmony_ci
647562306a36Sopenharmony_cistatic int
647662306a36Sopenharmony_ciil4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
647762306a36Sopenharmony_ci{
647862306a36Sopenharmony_ci	int err = 0;
647962306a36Sopenharmony_ci	struct il_priv *il;
648062306a36Sopenharmony_ci	struct ieee80211_hw *hw;
648162306a36Sopenharmony_ci	struct il_cfg *cfg = (struct il_cfg *)(ent->driver_data);
648262306a36Sopenharmony_ci	unsigned long flags;
648362306a36Sopenharmony_ci	u16 pci_cmd;
648462306a36Sopenharmony_ci
648562306a36Sopenharmony_ci	/************************
648662306a36Sopenharmony_ci	 * 1. Allocating HW data
648762306a36Sopenharmony_ci	 ************************/
648862306a36Sopenharmony_ci
648962306a36Sopenharmony_ci	hw = ieee80211_alloc_hw(sizeof(struct il_priv), &il4965_mac_ops);
649062306a36Sopenharmony_ci	if (!hw) {
649162306a36Sopenharmony_ci		err = -ENOMEM;
649262306a36Sopenharmony_ci		goto out;
649362306a36Sopenharmony_ci	}
649462306a36Sopenharmony_ci	il = hw->priv;
649562306a36Sopenharmony_ci	il->hw = hw;
649662306a36Sopenharmony_ci	SET_IEEE80211_DEV(hw, &pdev->dev);
649762306a36Sopenharmony_ci
649862306a36Sopenharmony_ci	D_INFO("*** LOAD DRIVER ***\n");
649962306a36Sopenharmony_ci	il->cfg = cfg;
650062306a36Sopenharmony_ci	il->ops = &il4965_ops;
650162306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUGFS
650262306a36Sopenharmony_ci	il->debugfs_ops = &il4965_debugfs_ops;
650362306a36Sopenharmony_ci#endif
650462306a36Sopenharmony_ci	il->pci_dev = pdev;
650562306a36Sopenharmony_ci	il->inta_mask = CSR_INI_SET_MASK;
650662306a36Sopenharmony_ci
650762306a36Sopenharmony_ci	/**************************
650862306a36Sopenharmony_ci	 * 2. Initializing PCI bus
650962306a36Sopenharmony_ci	 **************************/
651062306a36Sopenharmony_ci	pci_disable_link_state(pdev,
651162306a36Sopenharmony_ci			       PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
651262306a36Sopenharmony_ci			       PCIE_LINK_STATE_CLKPM);
651362306a36Sopenharmony_ci
651462306a36Sopenharmony_ci	if (pci_enable_device(pdev)) {
651562306a36Sopenharmony_ci		err = -ENODEV;
651662306a36Sopenharmony_ci		goto out_ieee80211_free_hw;
651762306a36Sopenharmony_ci	}
651862306a36Sopenharmony_ci
651962306a36Sopenharmony_ci	pci_set_master(pdev);
652062306a36Sopenharmony_ci
652162306a36Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36));
652262306a36Sopenharmony_ci	if (err) {
652362306a36Sopenharmony_ci		err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
652462306a36Sopenharmony_ci		/* both attempts failed: */
652562306a36Sopenharmony_ci		if (err) {
652662306a36Sopenharmony_ci			IL_WARN("No suitable DMA available.\n");
652762306a36Sopenharmony_ci			goto out_pci_disable_device;
652862306a36Sopenharmony_ci		}
652962306a36Sopenharmony_ci	}
653062306a36Sopenharmony_ci
653162306a36Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
653262306a36Sopenharmony_ci	if (err)
653362306a36Sopenharmony_ci		goto out_pci_disable_device;
653462306a36Sopenharmony_ci
653562306a36Sopenharmony_ci	pci_set_drvdata(pdev, il);
653662306a36Sopenharmony_ci
653762306a36Sopenharmony_ci	/***********************
653862306a36Sopenharmony_ci	 * 3. Read REV register
653962306a36Sopenharmony_ci	 ***********************/
654062306a36Sopenharmony_ci	il->hw_base = pci_ioremap_bar(pdev, 0);
654162306a36Sopenharmony_ci	if (!il->hw_base) {
654262306a36Sopenharmony_ci		err = -ENODEV;
654362306a36Sopenharmony_ci		goto out_pci_release_regions;
654462306a36Sopenharmony_ci	}
654562306a36Sopenharmony_ci
654662306a36Sopenharmony_ci	D_INFO("pci_resource_len = 0x%08llx\n",
654762306a36Sopenharmony_ci	       (unsigned long long)pci_resource_len(pdev, 0));
654862306a36Sopenharmony_ci	D_INFO("pci_resource_base = %p\n", il->hw_base);
654962306a36Sopenharmony_ci
655062306a36Sopenharmony_ci	/* these spin locks will be used in apm_ops.init and EEPROM access
655162306a36Sopenharmony_ci	 * we should init now
655262306a36Sopenharmony_ci	 */
655362306a36Sopenharmony_ci	spin_lock_init(&il->reg_lock);
655462306a36Sopenharmony_ci	spin_lock_init(&il->lock);
655562306a36Sopenharmony_ci
655662306a36Sopenharmony_ci	/*
655762306a36Sopenharmony_ci	 * stop and reset the on-board processor just in case it is in a
655862306a36Sopenharmony_ci	 * strange state ... like being left stranded by a primary kernel
655962306a36Sopenharmony_ci	 * and this is now the kdump kernel trying to start up
656062306a36Sopenharmony_ci	 */
656162306a36Sopenharmony_ci	_il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
656262306a36Sopenharmony_ci
656362306a36Sopenharmony_ci	il4965_hw_detect(il);
656462306a36Sopenharmony_ci	IL_INFO("Detected %s, REV=0x%X\n", il->cfg->name, il->hw_rev);
656562306a36Sopenharmony_ci
656662306a36Sopenharmony_ci	/* We disable the RETRY_TIMEOUT register (0x41) to keep
656762306a36Sopenharmony_ci	 * PCI Tx retries from interfering with C3 CPU state */
656862306a36Sopenharmony_ci	pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
656962306a36Sopenharmony_ci
657062306a36Sopenharmony_ci	il4965_prepare_card_hw(il);
657162306a36Sopenharmony_ci	if (!il->hw_ready) {
657262306a36Sopenharmony_ci		IL_WARN("Failed, HW not ready\n");
657362306a36Sopenharmony_ci		err = -EIO;
657462306a36Sopenharmony_ci		goto out_iounmap;
657562306a36Sopenharmony_ci	}
657662306a36Sopenharmony_ci
657762306a36Sopenharmony_ci	/*****************
657862306a36Sopenharmony_ci	 * 4. Read EEPROM
657962306a36Sopenharmony_ci	 *****************/
658062306a36Sopenharmony_ci	/* Read the EEPROM */
658162306a36Sopenharmony_ci	err = il_eeprom_init(il);
658262306a36Sopenharmony_ci	if (err) {
658362306a36Sopenharmony_ci		IL_ERR("Unable to init EEPROM\n");
658462306a36Sopenharmony_ci		goto out_iounmap;
658562306a36Sopenharmony_ci	}
658662306a36Sopenharmony_ci	err = il4965_eeprom_check_version(il);
658762306a36Sopenharmony_ci	if (err)
658862306a36Sopenharmony_ci		goto out_free_eeprom;
658962306a36Sopenharmony_ci
659062306a36Sopenharmony_ci	/* extract MAC Address */
659162306a36Sopenharmony_ci	il4965_eeprom_get_mac(il, il->addresses[0].addr);
659262306a36Sopenharmony_ci	D_INFO("MAC address: %pM\n", il->addresses[0].addr);
659362306a36Sopenharmony_ci	il->hw->wiphy->addresses = il->addresses;
659462306a36Sopenharmony_ci	il->hw->wiphy->n_addresses = 1;
659562306a36Sopenharmony_ci
659662306a36Sopenharmony_ci	/************************
659762306a36Sopenharmony_ci	 * 5. Setup HW constants
659862306a36Sopenharmony_ci	 ************************/
659962306a36Sopenharmony_ci	il4965_set_hw_params(il);
660062306a36Sopenharmony_ci
660162306a36Sopenharmony_ci	/*******************
660262306a36Sopenharmony_ci	 * 6. Setup il
660362306a36Sopenharmony_ci	 *******************/
660462306a36Sopenharmony_ci
660562306a36Sopenharmony_ci	err = il4965_init_drv(il);
660662306a36Sopenharmony_ci	if (err)
660762306a36Sopenharmony_ci		goto out_free_eeprom;
660862306a36Sopenharmony_ci	/* At this point both hw and il are initialized. */
660962306a36Sopenharmony_ci
661062306a36Sopenharmony_ci	/********************
661162306a36Sopenharmony_ci	 * 7. Setup services
661262306a36Sopenharmony_ci	 ********************/
661362306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
661462306a36Sopenharmony_ci	il_disable_interrupts(il);
661562306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
661662306a36Sopenharmony_ci
661762306a36Sopenharmony_ci	pci_enable_msi(il->pci_dev);
661862306a36Sopenharmony_ci
661962306a36Sopenharmony_ci	err = request_irq(il->pci_dev->irq, il_isr, IRQF_SHARED, DRV_NAME, il);
662062306a36Sopenharmony_ci	if (err) {
662162306a36Sopenharmony_ci		IL_ERR("Error allocating IRQ %d\n", il->pci_dev->irq);
662262306a36Sopenharmony_ci		goto out_disable_msi;
662362306a36Sopenharmony_ci	}
662462306a36Sopenharmony_ci
662562306a36Sopenharmony_ci	err = il4965_setup_deferred_work(il);
662662306a36Sopenharmony_ci	if (err)
662762306a36Sopenharmony_ci		goto out_free_irq;
662862306a36Sopenharmony_ci
662962306a36Sopenharmony_ci	il4965_setup_handlers(il);
663062306a36Sopenharmony_ci
663162306a36Sopenharmony_ci	/*********************************************
663262306a36Sopenharmony_ci	 * 8. Enable interrupts and read RFKILL state
663362306a36Sopenharmony_ci	 *********************************************/
663462306a36Sopenharmony_ci
663562306a36Sopenharmony_ci	/* enable rfkill interrupt: hw bug w/a */
663662306a36Sopenharmony_ci	pci_read_config_word(il->pci_dev, PCI_COMMAND, &pci_cmd);
663762306a36Sopenharmony_ci	if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
663862306a36Sopenharmony_ci		pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
663962306a36Sopenharmony_ci		pci_write_config_word(il->pci_dev, PCI_COMMAND, pci_cmd);
664062306a36Sopenharmony_ci	}
664162306a36Sopenharmony_ci
664262306a36Sopenharmony_ci	il_enable_rfkill_int(il);
664362306a36Sopenharmony_ci
664462306a36Sopenharmony_ci	/* If platform's RF_KILL switch is NOT set to KILL */
664562306a36Sopenharmony_ci	if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
664662306a36Sopenharmony_ci		clear_bit(S_RFKILL, &il->status);
664762306a36Sopenharmony_ci	else
664862306a36Sopenharmony_ci		set_bit(S_RFKILL, &il->status);
664962306a36Sopenharmony_ci
665062306a36Sopenharmony_ci	wiphy_rfkill_set_hw_state(il->hw->wiphy,
665162306a36Sopenharmony_ci				  test_bit(S_RFKILL, &il->status));
665262306a36Sopenharmony_ci
665362306a36Sopenharmony_ci	il_power_initialize(il);
665462306a36Sopenharmony_ci
665562306a36Sopenharmony_ci	init_completion(&il->_4965.firmware_loading_complete);
665662306a36Sopenharmony_ci
665762306a36Sopenharmony_ci	err = il4965_request_firmware(il, true);
665862306a36Sopenharmony_ci	if (err)
665962306a36Sopenharmony_ci		goto out_destroy_workqueue;
666062306a36Sopenharmony_ci
666162306a36Sopenharmony_ci	return 0;
666262306a36Sopenharmony_ci
666362306a36Sopenharmony_ciout_destroy_workqueue:
666462306a36Sopenharmony_ci	destroy_workqueue(il->workqueue);
666562306a36Sopenharmony_ci	il->workqueue = NULL;
666662306a36Sopenharmony_ciout_free_irq:
666762306a36Sopenharmony_ci	free_irq(il->pci_dev->irq, il);
666862306a36Sopenharmony_ciout_disable_msi:
666962306a36Sopenharmony_ci	pci_disable_msi(il->pci_dev);
667062306a36Sopenharmony_ci	il4965_uninit_drv(il);
667162306a36Sopenharmony_ciout_free_eeprom:
667262306a36Sopenharmony_ci	il_eeprom_free(il);
667362306a36Sopenharmony_ciout_iounmap:
667462306a36Sopenharmony_ci	iounmap(il->hw_base);
667562306a36Sopenharmony_ciout_pci_release_regions:
667662306a36Sopenharmony_ci	pci_release_regions(pdev);
667762306a36Sopenharmony_ciout_pci_disable_device:
667862306a36Sopenharmony_ci	pci_disable_device(pdev);
667962306a36Sopenharmony_ciout_ieee80211_free_hw:
668062306a36Sopenharmony_ci	ieee80211_free_hw(il->hw);
668162306a36Sopenharmony_ciout:
668262306a36Sopenharmony_ci	return err;
668362306a36Sopenharmony_ci}
668462306a36Sopenharmony_ci
668562306a36Sopenharmony_cistatic void
668662306a36Sopenharmony_ciil4965_pci_remove(struct pci_dev *pdev)
668762306a36Sopenharmony_ci{
668862306a36Sopenharmony_ci	struct il_priv *il = pci_get_drvdata(pdev);
668962306a36Sopenharmony_ci	unsigned long flags;
669062306a36Sopenharmony_ci
669162306a36Sopenharmony_ci	if (!il)
669262306a36Sopenharmony_ci		return;
669362306a36Sopenharmony_ci
669462306a36Sopenharmony_ci	wait_for_completion(&il->_4965.firmware_loading_complete);
669562306a36Sopenharmony_ci
669662306a36Sopenharmony_ci	D_INFO("*** UNLOAD DRIVER ***\n");
669762306a36Sopenharmony_ci
669862306a36Sopenharmony_ci	il_dbgfs_unregister(il);
669962306a36Sopenharmony_ci	sysfs_remove_group(&pdev->dev.kobj, &il_attribute_group);
670062306a36Sopenharmony_ci
670162306a36Sopenharmony_ci	/* ieee80211_unregister_hw call wil cause il_mac_stop to
670262306a36Sopenharmony_ci	 * be called and il4965_down since we are removing the device
670362306a36Sopenharmony_ci	 * we need to set S_EXIT_PENDING bit.
670462306a36Sopenharmony_ci	 */
670562306a36Sopenharmony_ci	set_bit(S_EXIT_PENDING, &il->status);
670662306a36Sopenharmony_ci
670762306a36Sopenharmony_ci	il_leds_exit(il);
670862306a36Sopenharmony_ci
670962306a36Sopenharmony_ci	if (il->mac80211_registered) {
671062306a36Sopenharmony_ci		ieee80211_unregister_hw(il->hw);
671162306a36Sopenharmony_ci		il->mac80211_registered = 0;
671262306a36Sopenharmony_ci	} else {
671362306a36Sopenharmony_ci		il4965_down(il);
671462306a36Sopenharmony_ci	}
671562306a36Sopenharmony_ci
671662306a36Sopenharmony_ci	/*
671762306a36Sopenharmony_ci	 * Make sure device is reset to low power before unloading driver.
671862306a36Sopenharmony_ci	 * This may be redundant with il4965_down(), but there are paths to
671962306a36Sopenharmony_ci	 * run il4965_down() without calling apm_ops.stop(), and there are
672062306a36Sopenharmony_ci	 * paths to avoid running il4965_down() at all before leaving driver.
672162306a36Sopenharmony_ci	 * This (inexpensive) call *makes sure* device is reset.
672262306a36Sopenharmony_ci	 */
672362306a36Sopenharmony_ci	il_apm_stop(il);
672462306a36Sopenharmony_ci
672562306a36Sopenharmony_ci	/* make sure we flush any pending irq or
672662306a36Sopenharmony_ci	 * tasklet for the driver
672762306a36Sopenharmony_ci	 */
672862306a36Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
672962306a36Sopenharmony_ci	il_disable_interrupts(il);
673062306a36Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
673162306a36Sopenharmony_ci
673262306a36Sopenharmony_ci	il4965_synchronize_irq(il);
673362306a36Sopenharmony_ci
673462306a36Sopenharmony_ci	il4965_dealloc_ucode_pci(il);
673562306a36Sopenharmony_ci
673662306a36Sopenharmony_ci	if (il->rxq.bd)
673762306a36Sopenharmony_ci		il4965_rx_queue_free(il, &il->rxq);
673862306a36Sopenharmony_ci	il4965_hw_txq_ctx_free(il);
673962306a36Sopenharmony_ci
674062306a36Sopenharmony_ci	il_eeprom_free(il);
674162306a36Sopenharmony_ci
674262306a36Sopenharmony_ci	/*netif_stop_queue(dev); */
674362306a36Sopenharmony_ci
674462306a36Sopenharmony_ci	/* ieee80211_unregister_hw calls il_mac_stop, which flushes
674562306a36Sopenharmony_ci	 * il->workqueue... so we can't take down the workqueue
674662306a36Sopenharmony_ci	 * until now... */
674762306a36Sopenharmony_ci	destroy_workqueue(il->workqueue);
674862306a36Sopenharmony_ci	il->workqueue = NULL;
674962306a36Sopenharmony_ci
675062306a36Sopenharmony_ci	free_irq(il->pci_dev->irq, il);
675162306a36Sopenharmony_ci	pci_disable_msi(il->pci_dev);
675262306a36Sopenharmony_ci	iounmap(il->hw_base);
675362306a36Sopenharmony_ci	pci_release_regions(pdev);
675462306a36Sopenharmony_ci	pci_disable_device(pdev);
675562306a36Sopenharmony_ci
675662306a36Sopenharmony_ci	il4965_uninit_drv(il);
675762306a36Sopenharmony_ci
675862306a36Sopenharmony_ci	dev_kfree_skb(il->beacon_skb);
675962306a36Sopenharmony_ci
676062306a36Sopenharmony_ci	ieee80211_free_hw(il->hw);
676162306a36Sopenharmony_ci}
676262306a36Sopenharmony_ci
676362306a36Sopenharmony_ci/*
676462306a36Sopenharmony_ci * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask
676562306a36Sopenharmony_ci * must be called under il->lock and mac access
676662306a36Sopenharmony_ci */
676762306a36Sopenharmony_civoid
676862306a36Sopenharmony_ciil4965_txq_set_sched(struct il_priv *il, u32 mask)
676962306a36Sopenharmony_ci{
677062306a36Sopenharmony_ci	il_wr_prph(il, IL49_SCD_TXFACT, mask);
677162306a36Sopenharmony_ci}
677262306a36Sopenharmony_ci
677362306a36Sopenharmony_ci/*****************************************************************************
677462306a36Sopenharmony_ci *
677562306a36Sopenharmony_ci * driver and module entry point
677662306a36Sopenharmony_ci *
677762306a36Sopenharmony_ci *****************************************************************************/
677862306a36Sopenharmony_ci
677962306a36Sopenharmony_ci/* Hardware specific file defines the PCI IDs table for that hardware module */
678062306a36Sopenharmony_cistatic const struct pci_device_id il4965_hw_card_ids[] = {
678162306a36Sopenharmony_ci	{IL_PCI_DEVICE(0x4229, PCI_ANY_ID, il4965_cfg)},
678262306a36Sopenharmony_ci	{IL_PCI_DEVICE(0x4230, PCI_ANY_ID, il4965_cfg)},
678362306a36Sopenharmony_ci	{0}
678462306a36Sopenharmony_ci};
678562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, il4965_hw_card_ids);
678662306a36Sopenharmony_ci
678762306a36Sopenharmony_cistatic struct pci_driver il4965_driver = {
678862306a36Sopenharmony_ci	.name = DRV_NAME,
678962306a36Sopenharmony_ci	.id_table = il4965_hw_card_ids,
679062306a36Sopenharmony_ci	.probe = il4965_pci_probe,
679162306a36Sopenharmony_ci	.remove = il4965_pci_remove,
679262306a36Sopenharmony_ci	.driver.pm = IL_LEGACY_PM_OPS,
679362306a36Sopenharmony_ci};
679462306a36Sopenharmony_ci
679562306a36Sopenharmony_cistatic int __init
679662306a36Sopenharmony_ciil4965_init(void)
679762306a36Sopenharmony_ci{
679862306a36Sopenharmony_ci
679962306a36Sopenharmony_ci	int ret;
680062306a36Sopenharmony_ci	pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n");
680162306a36Sopenharmony_ci	pr_info(DRV_COPYRIGHT "\n");
680262306a36Sopenharmony_ci
680362306a36Sopenharmony_ci	ret = il4965_rate_control_register();
680462306a36Sopenharmony_ci	if (ret) {
680562306a36Sopenharmony_ci		pr_err("Unable to register rate control algorithm: %d\n", ret);
680662306a36Sopenharmony_ci		return ret;
680762306a36Sopenharmony_ci	}
680862306a36Sopenharmony_ci
680962306a36Sopenharmony_ci	ret = pci_register_driver(&il4965_driver);
681062306a36Sopenharmony_ci	if (ret) {
681162306a36Sopenharmony_ci		pr_err("Unable to initialize PCI module\n");
681262306a36Sopenharmony_ci		goto error_register;
681362306a36Sopenharmony_ci	}
681462306a36Sopenharmony_ci
681562306a36Sopenharmony_ci	return ret;
681662306a36Sopenharmony_ci
681762306a36Sopenharmony_cierror_register:
681862306a36Sopenharmony_ci	il4965_rate_control_unregister();
681962306a36Sopenharmony_ci	return ret;
682062306a36Sopenharmony_ci}
682162306a36Sopenharmony_ci
682262306a36Sopenharmony_cistatic void __exit
682362306a36Sopenharmony_ciil4965_exit(void)
682462306a36Sopenharmony_ci{
682562306a36Sopenharmony_ci	pci_unregister_driver(&il4965_driver);
682662306a36Sopenharmony_ci	il4965_rate_control_unregister();
682762306a36Sopenharmony_ci}
682862306a36Sopenharmony_ci
682962306a36Sopenharmony_cimodule_exit(il4965_exit);
683062306a36Sopenharmony_cimodule_init(il4965_init);
683162306a36Sopenharmony_ci
683262306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
683362306a36Sopenharmony_cimodule_param_named(debug, il_debug_level, uint, 0644);
683462306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "debug output mask");
683562306a36Sopenharmony_ci#endif
683662306a36Sopenharmony_ci
683762306a36Sopenharmony_cimodule_param_named(swcrypto, il4965_mod_params.sw_crypto, int, 0444);
683862306a36Sopenharmony_ciMODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])");
683962306a36Sopenharmony_cimodule_param_named(queues_num, il4965_mod_params.num_of_queues, int, 0444);
684062306a36Sopenharmony_ciMODULE_PARM_DESC(queues_num, "number of hw queues.");
684162306a36Sopenharmony_cimodule_param_named(11n_disable, il4965_mod_params.disable_11n, int, 0444);
684262306a36Sopenharmony_ciMODULE_PARM_DESC(11n_disable, "disable 11n functionality");
684362306a36Sopenharmony_cimodule_param_named(amsdu_size_8K, il4965_mod_params.amsdu_size_8K, int, 0444);
684462306a36Sopenharmony_ciMODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0 [disabled])");
684562306a36Sopenharmony_cimodule_param_named(fw_restart, il4965_mod_params.restart_fw, int, 0444);
684662306a36Sopenharmony_ciMODULE_PARM_DESC(fw_restart, "restart firmware in case of error");
6847