162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
362306a36Sopenharmony_ci * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Permission to use, copy, modify, and distribute this software for any
662306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
762306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1062306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1162306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1262306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1362306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1462306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1562306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*************************************\
2062306a36Sopenharmony_ci* DMA and interrupt masking functions *
2162306a36Sopenharmony_ci\*************************************/
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/**
2462306a36Sopenharmony_ci * DOC: DMA and interrupt masking functions
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * Here we setup descriptor pointers (rxdp/txdp) start/stop dma engine and
2762306a36Sopenharmony_ci * handle queue setup for 5210 chipset (rest are handled on qcu.c).
2862306a36Sopenharmony_ci * Also we setup interrupt mask register (IMR) and read the various interrupt
2962306a36Sopenharmony_ci * status registers (ISR).
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include "ath5k.h"
3562306a36Sopenharmony_ci#include "reg.h"
3662306a36Sopenharmony_ci#include "debug.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*********\
4062306a36Sopenharmony_ci* Receive *
4162306a36Sopenharmony_ci\*********/
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/**
4462306a36Sopenharmony_ci * ath5k_hw_start_rx_dma() - Start DMA receive
4562306a36Sopenharmony_ci * @ah:	The &struct ath5k_hw
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_civoid
4862306a36Sopenharmony_ciath5k_hw_start_rx_dma(struct ath5k_hw *ah)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR);
5162306a36Sopenharmony_ci	ath5k_hw_reg_read(ah, AR5K_CR);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/**
5562306a36Sopenharmony_ci * ath5k_hw_stop_rx_dma() - Stop DMA receive
5662306a36Sopenharmony_ci * @ah:	The &struct ath5k_hw
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_cistatic int
5962306a36Sopenharmony_ciath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	unsigned int i;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/*
6662306a36Sopenharmony_ci	 * It may take some time to disable the DMA receive unit
6762306a36Sopenharmony_ci	 */
6862306a36Sopenharmony_ci	for (i = 1000; i > 0 &&
6962306a36Sopenharmony_ci			(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0;
7062306a36Sopenharmony_ci			i--)
7162306a36Sopenharmony_ci		udelay(100);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (!i)
7462306a36Sopenharmony_ci		ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
7562306a36Sopenharmony_ci				"failed to stop RX DMA !\n");
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return i ? 0 : -EBUSY;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/**
8162306a36Sopenharmony_ci * ath5k_hw_get_rxdp() - Get RX Descriptor's address
8262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_ciu32
8562306a36Sopenharmony_ciath5k_hw_get_rxdp(struct ath5k_hw *ah)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	return ath5k_hw_reg_read(ah, AR5K_RXDP);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/**
9162306a36Sopenharmony_ci * ath5k_hw_set_rxdp() - Set RX Descriptor's address
9262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
9362306a36Sopenharmony_ci * @phys_addr: RX descriptor address
9462306a36Sopenharmony_ci *
9562306a36Sopenharmony_ci * Returns -EIO if rx is active
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_ciint
9862306a36Sopenharmony_ciath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	if (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) {
10162306a36Sopenharmony_ci		ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
10262306a36Sopenharmony_ci				"tried to set RXDP while rx was active !\n");
10362306a36Sopenharmony_ci		return -EIO;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP);
10762306a36Sopenharmony_ci	return 0;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/**********\
11262306a36Sopenharmony_ci* Transmit *
11362306a36Sopenharmony_ci\**********/
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/**
11662306a36Sopenharmony_ci * ath5k_hw_start_tx_dma() - Start DMA transmit for a specific queue
11762306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
11862306a36Sopenharmony_ci * @queue: The hw queue number
11962306a36Sopenharmony_ci *
12062306a36Sopenharmony_ci * Start DMA transmit for a specific queue and since 5210 doesn't have
12162306a36Sopenharmony_ci * QCU/DCU, set up queue parameters for 5210 here based on queue type (one
12262306a36Sopenharmony_ci * queue for normal data and one queue for beacons). For queue setup
12362306a36Sopenharmony_ci * on newer chips check out qcu.c. Returns -EINVAL if queue number is out
12462306a36Sopenharmony_ci * of range or if queue is already disabled.
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci * NOTE: Must be called after setting up tx control descriptor for that
12762306a36Sopenharmony_ci * queue (see below).
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_ciint
13062306a36Sopenharmony_ciath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	u32 tx_queue;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* Return if queue is declared inactive */
13762306a36Sopenharmony_ci	if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
13862306a36Sopenharmony_ci		return -EINVAL;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (ah->ah_version == AR5K_AR5210) {
14162306a36Sopenharmony_ci		tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		/*
14462306a36Sopenharmony_ci		 * Set the queue by type on 5210
14562306a36Sopenharmony_ci		 */
14662306a36Sopenharmony_ci		switch (ah->ah_txq[queue].tqi_type) {
14762306a36Sopenharmony_ci		case AR5K_TX_QUEUE_DATA:
14862306a36Sopenharmony_ci			tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0;
14962306a36Sopenharmony_ci			break;
15062306a36Sopenharmony_ci		case AR5K_TX_QUEUE_BEACON:
15162306a36Sopenharmony_ci			tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
15262306a36Sopenharmony_ci			ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE,
15362306a36Sopenharmony_ci					AR5K_BSR);
15462306a36Sopenharmony_ci			break;
15562306a36Sopenharmony_ci		case AR5K_TX_QUEUE_CAB:
15662306a36Sopenharmony_ci			tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
15762306a36Sopenharmony_ci			ath5k_hw_reg_write(ah, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V |
15862306a36Sopenharmony_ci				AR5K_BCR_BDMAE, AR5K_BSR);
15962306a36Sopenharmony_ci			break;
16062306a36Sopenharmony_ci		default:
16162306a36Sopenharmony_ci			return -EINVAL;
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci		/* Start queue */
16462306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
16562306a36Sopenharmony_ci		ath5k_hw_reg_read(ah, AR5K_CR);
16662306a36Sopenharmony_ci	} else {
16762306a36Sopenharmony_ci		/* Return if queue is disabled */
16862306a36Sopenharmony_ci		if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue))
16962306a36Sopenharmony_ci			return -EIO;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		/* Start queue */
17262306a36Sopenharmony_ci		AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXE, queue);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return 0;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/**
17962306a36Sopenharmony_ci * ath5k_hw_stop_tx_dma() - Stop DMA transmit on a specific queue
18062306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
18162306a36Sopenharmony_ci * @queue: The hw queue number
18262306a36Sopenharmony_ci *
18362306a36Sopenharmony_ci * Stop DMA transmit on a specific hw queue and drain queue so we don't
18462306a36Sopenharmony_ci * have any pending frames. Returns -EBUSY if we still have pending frames,
18562306a36Sopenharmony_ci * -EINVAL if queue number is out of range or inactive.
18662306a36Sopenharmony_ci */
18762306a36Sopenharmony_cistatic int
18862306a36Sopenharmony_ciath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	unsigned int i = 40;
19162306a36Sopenharmony_ci	u32 tx_queue, pending;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* Return if queue is declared inactive */
19662306a36Sopenharmony_ci	if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
19762306a36Sopenharmony_ci		return -EINVAL;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (ah->ah_version == AR5K_AR5210) {
20062306a36Sopenharmony_ci		tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		/*
20362306a36Sopenharmony_ci		 * Set by queue type
20462306a36Sopenharmony_ci		 */
20562306a36Sopenharmony_ci		switch (ah->ah_txq[queue].tqi_type) {
20662306a36Sopenharmony_ci		case AR5K_TX_QUEUE_DATA:
20762306a36Sopenharmony_ci			tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0;
20862306a36Sopenharmony_ci			break;
20962306a36Sopenharmony_ci		case AR5K_TX_QUEUE_BEACON:
21062306a36Sopenharmony_ci		case AR5K_TX_QUEUE_CAB:
21162306a36Sopenharmony_ci			/* XXX Fix me... */
21262306a36Sopenharmony_ci			tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1;
21362306a36Sopenharmony_ci			ath5k_hw_reg_write(ah, 0, AR5K_BSR);
21462306a36Sopenharmony_ci			break;
21562306a36Sopenharmony_ci		default:
21662306a36Sopenharmony_ci			return -EINVAL;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		/* Stop queue */
22062306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
22162306a36Sopenharmony_ci		ath5k_hw_reg_read(ah, AR5K_CR);
22262306a36Sopenharmony_ci	} else {
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		/*
22562306a36Sopenharmony_ci		 * Enable DCU early termination to quickly
22662306a36Sopenharmony_ci		 * flush any pending frames from QCU
22762306a36Sopenharmony_ci		 */
22862306a36Sopenharmony_ci		AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
22962306a36Sopenharmony_ci					AR5K_QCU_MISC_DCU_EARLY);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		/*
23262306a36Sopenharmony_ci		 * Schedule TX disable and wait until queue is empty
23362306a36Sopenharmony_ci		 */
23462306a36Sopenharmony_ci		AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		/* Wait for queue to stop */
23762306a36Sopenharmony_ci		for (i = 1000; i > 0 &&
23862306a36Sopenharmony_ci		(AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue) != 0);
23962306a36Sopenharmony_ci		i--)
24062306a36Sopenharmony_ci			udelay(100);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
24362306a36Sopenharmony_ci			ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
24462306a36Sopenharmony_ci				"queue %i didn't stop !\n", queue);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci		/* Check for pending frames */
24762306a36Sopenharmony_ci		i = 1000;
24862306a36Sopenharmony_ci		do {
24962306a36Sopenharmony_ci			pending = ath5k_hw_reg_read(ah,
25062306a36Sopenharmony_ci				AR5K_QUEUE_STATUS(queue)) &
25162306a36Sopenharmony_ci				AR5K_QCU_STS_FRMPENDCNT;
25262306a36Sopenharmony_ci			udelay(100);
25362306a36Sopenharmony_ci		} while (--i && pending);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		/* For 2413+ order PCU to drop packets using
25662306a36Sopenharmony_ci		 * QUIET mechanism */
25762306a36Sopenharmony_ci		if (ah->ah_mac_version >= (AR5K_SREV_AR2414 >> 4) &&
25862306a36Sopenharmony_ci		    pending) {
25962306a36Sopenharmony_ci			/* Set periodicity and duration */
26062306a36Sopenharmony_ci			ath5k_hw_reg_write(ah,
26162306a36Sopenharmony_ci				AR5K_REG_SM(100, AR5K_QUIET_CTL2_QT_PER)|
26262306a36Sopenharmony_ci				AR5K_REG_SM(10, AR5K_QUIET_CTL2_QT_DUR),
26362306a36Sopenharmony_ci				AR5K_QUIET_CTL2);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci			/* Enable quiet period for current TSF */
26662306a36Sopenharmony_ci			ath5k_hw_reg_write(ah,
26762306a36Sopenharmony_ci				AR5K_QUIET_CTL1_QT_EN |
26862306a36Sopenharmony_ci				AR5K_REG_SM(ath5k_hw_reg_read(ah,
26962306a36Sopenharmony_ci						AR5K_TSF_L32_5211) >> 10,
27062306a36Sopenharmony_ci						AR5K_QUIET_CTL1_NEXT_QT_TSF),
27162306a36Sopenharmony_ci				AR5K_QUIET_CTL1);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci			/* Force channel idle high */
27462306a36Sopenharmony_ci			AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211,
27562306a36Sopenharmony_ci					AR5K_DIAG_SW_CHANNEL_IDLE_HIGH);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci			/* Wait a while and disable mechanism */
27862306a36Sopenharmony_ci			udelay(400);
27962306a36Sopenharmony_ci			AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1,
28062306a36Sopenharmony_ci						AR5K_QUIET_CTL1_QT_EN);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci			/* Re-check for pending frames */
28362306a36Sopenharmony_ci			i = 100;
28462306a36Sopenharmony_ci			do {
28562306a36Sopenharmony_ci				pending = ath5k_hw_reg_read(ah,
28662306a36Sopenharmony_ci					AR5K_QUEUE_STATUS(queue)) &
28762306a36Sopenharmony_ci					AR5K_QCU_STS_FRMPENDCNT;
28862306a36Sopenharmony_ci				udelay(100);
28962306a36Sopenharmony_ci			} while (--i && pending);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci			AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211,
29262306a36Sopenharmony_ci					AR5K_DIAG_SW_CHANNEL_IDLE_HIGH);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci			if (pending)
29562306a36Sopenharmony_ci				ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
29662306a36Sopenharmony_ci					"quiet mechanism didn't work q:%i !\n",
29762306a36Sopenharmony_ci					queue);
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		/*
30162306a36Sopenharmony_ci		 * Disable DCU early termination
30262306a36Sopenharmony_ci		 */
30362306a36Sopenharmony_ci		AR5K_REG_DISABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
30462306a36Sopenharmony_ci					AR5K_QCU_MISC_DCU_EARLY);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		/* Clear register */
30762306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD);
30862306a36Sopenharmony_ci		if (pending) {
30962306a36Sopenharmony_ci			ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
31062306a36Sopenharmony_ci					"tx dma didn't stop (q:%i, frm:%i) !\n",
31162306a36Sopenharmony_ci					queue, pending);
31262306a36Sopenharmony_ci			return -EBUSY;
31362306a36Sopenharmony_ci		}
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* TODO: Check for success on 5210 else return error */
31762306a36Sopenharmony_ci	return 0;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci/**
32162306a36Sopenharmony_ci * ath5k_hw_stop_beacon_queue() - Stop beacon queue
32262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
32362306a36Sopenharmony_ci * @queue: The queue number
32462306a36Sopenharmony_ci *
32562306a36Sopenharmony_ci * Returns -EIO if queue didn't stop
32662306a36Sopenharmony_ci */
32762306a36Sopenharmony_ciint
32862306a36Sopenharmony_ciath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	int ret;
33162306a36Sopenharmony_ci	ret = ath5k_hw_stop_tx_dma(ah, queue);
33262306a36Sopenharmony_ci	if (ret) {
33362306a36Sopenharmony_ci		ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
33462306a36Sopenharmony_ci				"beacon queue didn't stop !\n");
33562306a36Sopenharmony_ci		return -EIO;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	return 0;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/**
34162306a36Sopenharmony_ci * ath5k_hw_get_txdp() - Get TX Descriptor's address for a specific queue
34262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
34362306a36Sopenharmony_ci * @queue: The hw queue number
34462306a36Sopenharmony_ci *
34562306a36Sopenharmony_ci * Get TX descriptor's address for a specific queue. For 5210 we ignore
34662306a36Sopenharmony_ci * the queue number and use tx queue type since we only have 2 queues.
34762306a36Sopenharmony_ci * We use TXDP0 for normal data queue and TXDP1 for beacon queue.
34862306a36Sopenharmony_ci * For newer chips with QCU/DCU we just read the corresponding TXDP register.
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci * XXX: Is TXDP read and clear ?
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_ciu32
35362306a36Sopenharmony_ciath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	u16 tx_reg;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	/*
36062306a36Sopenharmony_ci	 * Get the transmit queue descriptor pointer from the selected queue
36162306a36Sopenharmony_ci	 */
36262306a36Sopenharmony_ci	/*5210 doesn't have QCU*/
36362306a36Sopenharmony_ci	if (ah->ah_version == AR5K_AR5210) {
36462306a36Sopenharmony_ci		switch (ah->ah_txq[queue].tqi_type) {
36562306a36Sopenharmony_ci		case AR5K_TX_QUEUE_DATA:
36662306a36Sopenharmony_ci			tx_reg = AR5K_NOQCU_TXDP0;
36762306a36Sopenharmony_ci			break;
36862306a36Sopenharmony_ci		case AR5K_TX_QUEUE_BEACON:
36962306a36Sopenharmony_ci		case AR5K_TX_QUEUE_CAB:
37062306a36Sopenharmony_ci			tx_reg = AR5K_NOQCU_TXDP1;
37162306a36Sopenharmony_ci			break;
37262306a36Sopenharmony_ci		default:
37362306a36Sopenharmony_ci			return 0xffffffff;
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci	} else {
37662306a36Sopenharmony_ci		tx_reg = AR5K_QUEUE_TXDP(queue);
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return ath5k_hw_reg_read(ah, tx_reg);
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/**
38362306a36Sopenharmony_ci * ath5k_hw_set_txdp() - Set TX Descriptor's address for a specific queue
38462306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
38562306a36Sopenharmony_ci * @queue: The hw queue number
38662306a36Sopenharmony_ci * @phys_addr: The physical address
38762306a36Sopenharmony_ci *
38862306a36Sopenharmony_ci * Set TX descriptor's address for a specific queue. For 5210 we ignore
38962306a36Sopenharmony_ci * the queue number and we use tx queue type since we only have 2 queues
39062306a36Sopenharmony_ci * so as above we use TXDP0 for normal data queue and TXDP1 for beacon queue.
39162306a36Sopenharmony_ci * For newer chips with QCU/DCU we just set the corresponding TXDP register.
39262306a36Sopenharmony_ci * Returns -EINVAL if queue type is invalid for 5210 and -EIO if queue is still
39362306a36Sopenharmony_ci * active.
39462306a36Sopenharmony_ci */
39562306a36Sopenharmony_ciint
39662306a36Sopenharmony_ciath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	u16 tx_reg;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/*
40362306a36Sopenharmony_ci	 * Set the transmit queue descriptor pointer register by type
40462306a36Sopenharmony_ci	 * on 5210
40562306a36Sopenharmony_ci	 */
40662306a36Sopenharmony_ci	if (ah->ah_version == AR5K_AR5210) {
40762306a36Sopenharmony_ci		switch (ah->ah_txq[queue].tqi_type) {
40862306a36Sopenharmony_ci		case AR5K_TX_QUEUE_DATA:
40962306a36Sopenharmony_ci			tx_reg = AR5K_NOQCU_TXDP0;
41062306a36Sopenharmony_ci			break;
41162306a36Sopenharmony_ci		case AR5K_TX_QUEUE_BEACON:
41262306a36Sopenharmony_ci		case AR5K_TX_QUEUE_CAB:
41362306a36Sopenharmony_ci			tx_reg = AR5K_NOQCU_TXDP1;
41462306a36Sopenharmony_ci			break;
41562306a36Sopenharmony_ci		default:
41662306a36Sopenharmony_ci			return -EINVAL;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci	} else {
41962306a36Sopenharmony_ci		/*
42062306a36Sopenharmony_ci		 * Set the transmit queue descriptor pointer for
42162306a36Sopenharmony_ci		 * the selected queue on QCU for 5211+
42262306a36Sopenharmony_ci		 * (this won't work if the queue is still active)
42362306a36Sopenharmony_ci		 */
42462306a36Sopenharmony_ci		if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
42562306a36Sopenharmony_ci			return -EIO;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		tx_reg = AR5K_QUEUE_TXDP(queue);
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/* Set descriptor pointer */
43162306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, phys_addr, tx_reg);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	return 0;
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci/**
43762306a36Sopenharmony_ci * ath5k_hw_update_tx_triglevel() - Update tx trigger level
43862306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
43962306a36Sopenharmony_ci * @increase: Flag to force increase of trigger level
44062306a36Sopenharmony_ci *
44162306a36Sopenharmony_ci * This function increases/decreases the tx trigger level for the tx fifo
44262306a36Sopenharmony_ci * buffer (aka FIFO threshold) that is used to indicate when PCU flushes
44362306a36Sopenharmony_ci * the buffer and transmits its data. Lowering this results sending small
44462306a36Sopenharmony_ci * frames more quickly but can lead to tx underruns, raising it a lot can
44562306a36Sopenharmony_ci * result other problems. Right now we start with the lowest possible
44662306a36Sopenharmony_ci * (64Bytes) and if we get tx underrun we increase it using the increase
44762306a36Sopenharmony_ci * flag. Returns -EIO if we have reached maximum/minimum.
44862306a36Sopenharmony_ci *
44962306a36Sopenharmony_ci * XXX: Link this with tx DMA size ?
45062306a36Sopenharmony_ci * XXX2: Use it to save interrupts ?
45162306a36Sopenharmony_ci */
45262306a36Sopenharmony_ciint
45362306a36Sopenharmony_ciath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	u32 trigger_level, imr;
45662306a36Sopenharmony_ci	int ret = -EIO;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/*
45962306a36Sopenharmony_ci	 * Disable interrupts by setting the mask
46062306a36Sopenharmony_ci	 */
46162306a36Sopenharmony_ci	imr = ath5k_hw_set_imr(ah, ah->ah_imr & ~AR5K_INT_GLOBAL);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(ah, AR5K_TXCFG),
46462306a36Sopenharmony_ci			AR5K_TXCFG_TXFULL);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (!increase) {
46762306a36Sopenharmony_ci		if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES)
46862306a36Sopenharmony_ci			goto done;
46962306a36Sopenharmony_ci	} else
47062306a36Sopenharmony_ci		trigger_level +=
47162306a36Sopenharmony_ci			((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/*
47462306a36Sopenharmony_ci	 * Update trigger level on success
47562306a36Sopenharmony_ci	 */
47662306a36Sopenharmony_ci	if (ah->ah_version == AR5K_AR5210)
47762306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, trigger_level, AR5K_TRIG_LVL);
47862306a36Sopenharmony_ci	else
47962306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
48062306a36Sopenharmony_ci				AR5K_TXCFG_TXFULL, trigger_level);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	ret = 0;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cidone:
48562306a36Sopenharmony_ci	/*
48662306a36Sopenharmony_ci	 * Restore interrupt mask
48762306a36Sopenharmony_ci	 */
48862306a36Sopenharmony_ci	ath5k_hw_set_imr(ah, imr);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	return ret;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci/*******************\
49562306a36Sopenharmony_ci* Interrupt masking *
49662306a36Sopenharmony_ci\*******************/
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci/**
49962306a36Sopenharmony_ci * ath5k_hw_is_intr_pending() - Check if we have pending interrupts
50062306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
50162306a36Sopenharmony_ci *
50262306a36Sopenharmony_ci * Check if we have pending interrupts to process. Returns 1 if we
50362306a36Sopenharmony_ci * have pending interrupts and 0 if we haven't.
50462306a36Sopenharmony_ci */
50562306a36Sopenharmony_cibool
50662306a36Sopenharmony_ciath5k_hw_is_intr_pending(struct ath5k_hw *ah)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	return ath5k_hw_reg_read(ah, AR5K_INTPEND) == 1 ? 1 : 0;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci/**
51262306a36Sopenharmony_ci * ath5k_hw_get_isr() - Get interrupt status
51362306a36Sopenharmony_ci * @ah: The @struct ath5k_hw
51462306a36Sopenharmony_ci * @interrupt_mask: Driver's interrupt mask used to filter out
51562306a36Sopenharmony_ci * interrupts in sw.
51662306a36Sopenharmony_ci *
51762306a36Sopenharmony_ci * This function is used inside our interrupt handler to determine the reason
51862306a36Sopenharmony_ci * for the interrupt by reading Primary Interrupt Status Register. Returns an
51962306a36Sopenharmony_ci * abstract interrupt status mask which is mostly ISR with some uncommon bits
52062306a36Sopenharmony_ci * being mapped on some standard non hw-specific positions
52162306a36Sopenharmony_ci * (check out &ath5k_int).
52262306a36Sopenharmony_ci *
52362306a36Sopenharmony_ci * NOTE: We do write-to-clear, so the active PISR/SISR bits at the time this
52462306a36Sopenharmony_ci * function gets called are cleared on return.
52562306a36Sopenharmony_ci */
52662306a36Sopenharmony_ciint
52762306a36Sopenharmony_ciath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	u32 data = 0;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/*
53262306a36Sopenharmony_ci	 * Read interrupt status from Primary Interrupt
53362306a36Sopenharmony_ci	 * Register.
53462306a36Sopenharmony_ci	 *
53562306a36Sopenharmony_ci	 * Note: PISR/SISR Not available on 5210
53662306a36Sopenharmony_ci	 */
53762306a36Sopenharmony_ci	if (ah->ah_version == AR5K_AR5210) {
53862306a36Sopenharmony_ci		u32 isr = 0;
53962306a36Sopenharmony_ci		isr = ath5k_hw_reg_read(ah, AR5K_ISR);
54062306a36Sopenharmony_ci		if (unlikely(isr == AR5K_INT_NOCARD)) {
54162306a36Sopenharmony_ci			*interrupt_mask = isr;
54262306a36Sopenharmony_ci			return -ENODEV;
54362306a36Sopenharmony_ci		}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		/*
54662306a36Sopenharmony_ci		 * Filter out the non-common bits from the interrupt
54762306a36Sopenharmony_ci		 * status.
54862306a36Sopenharmony_ci		 */
54962306a36Sopenharmony_ci		*interrupt_mask = (isr & AR5K_INT_COMMON) & ah->ah_imr;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		/* Hanlde INT_FATAL */
55262306a36Sopenharmony_ci		if (unlikely(isr & (AR5K_ISR_SSERR | AR5K_ISR_MCABT
55362306a36Sopenharmony_ci						| AR5K_ISR_DPERR)))
55462306a36Sopenharmony_ci			*interrupt_mask |= AR5K_INT_FATAL;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		/*
55762306a36Sopenharmony_ci		 * XXX: BMISS interrupts may occur after association.
55862306a36Sopenharmony_ci		 * I found this on 5210 code but it needs testing. If this is
55962306a36Sopenharmony_ci		 * true we should disable them before assoc and re-enable them
56062306a36Sopenharmony_ci		 * after a successful assoc + some jiffies.
56162306a36Sopenharmony_ci			interrupt_mask &= ~AR5K_INT_BMISS;
56262306a36Sopenharmony_ci		 */
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		data = isr;
56562306a36Sopenharmony_ci	} else {
56662306a36Sopenharmony_ci		u32 pisr = 0;
56762306a36Sopenharmony_ci		u32 pisr_clear = 0;
56862306a36Sopenharmony_ci		u32 sisr0 = 0;
56962306a36Sopenharmony_ci		u32 sisr1 = 0;
57062306a36Sopenharmony_ci		u32 sisr2 = 0;
57162306a36Sopenharmony_ci		u32 sisr3 = 0;
57262306a36Sopenharmony_ci		u32 sisr4 = 0;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		/* Read PISR and SISRs... */
57562306a36Sopenharmony_ci		pisr = ath5k_hw_reg_read(ah, AR5K_PISR);
57662306a36Sopenharmony_ci		if (unlikely(pisr == AR5K_INT_NOCARD)) {
57762306a36Sopenharmony_ci			*interrupt_mask = pisr;
57862306a36Sopenharmony_ci			return -ENODEV;
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		sisr0 = ath5k_hw_reg_read(ah, AR5K_SISR0);
58262306a36Sopenharmony_ci		sisr1 = ath5k_hw_reg_read(ah, AR5K_SISR1);
58362306a36Sopenharmony_ci		sisr2 = ath5k_hw_reg_read(ah, AR5K_SISR2);
58462306a36Sopenharmony_ci		sisr3 = ath5k_hw_reg_read(ah, AR5K_SISR3);
58562306a36Sopenharmony_ci		sisr4 = ath5k_hw_reg_read(ah, AR5K_SISR4);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci		/*
58862306a36Sopenharmony_ci		 * PISR holds the logical OR of interrupt bits
58962306a36Sopenharmony_ci		 * from SISR registers:
59062306a36Sopenharmony_ci		 *
59162306a36Sopenharmony_ci		 * TXOK and TXDESC  -> Logical OR of TXOK and TXDESC
59262306a36Sopenharmony_ci		 *			per-queue bits on SISR0
59362306a36Sopenharmony_ci		 *
59462306a36Sopenharmony_ci		 * TXERR and TXEOL -> Logical OR of TXERR and TXEOL
59562306a36Sopenharmony_ci		 *			per-queue bits on SISR1
59662306a36Sopenharmony_ci		 *
59762306a36Sopenharmony_ci		 * TXURN -> Logical OR of TXURN per-queue bits on SISR2
59862306a36Sopenharmony_ci		 *
59962306a36Sopenharmony_ci		 * HIUERR -> Logical OR of MCABT, SSERR and DPER bits on SISR2
60062306a36Sopenharmony_ci		 *
60162306a36Sopenharmony_ci		 * BCNMISC -> Logical OR of TIM, CAB_END, DTIM_SYNC
60262306a36Sopenharmony_ci		 *		BCN_TIMEOUT, CAB_TIMEOUT and DTIM
60362306a36Sopenharmony_ci		 *		(and TSFOOR ?) bits on SISR2
60462306a36Sopenharmony_ci		 *
60562306a36Sopenharmony_ci		 * QCBRORN and QCBRURN -> Logical OR of QCBRORN and
60662306a36Sopenharmony_ci		 *			QCBRURN per-queue bits on SISR3
60762306a36Sopenharmony_ci		 * QTRIG -> Logical OR of QTRIG per-queue bits on SISR4
60862306a36Sopenharmony_ci		 *
60962306a36Sopenharmony_ci		 * If we clean these bits on PISR we 'll also clear all
61062306a36Sopenharmony_ci		 * related bits from SISRs, e.g. if we write the TXOK bit on
61162306a36Sopenharmony_ci		 * PISR we 'll clean all TXOK bits from SISR0 so if a new TXOK
61262306a36Sopenharmony_ci		 * interrupt got fired for another queue while we were reading
61362306a36Sopenharmony_ci		 * the interrupt registers and we write back the TXOK bit on
61462306a36Sopenharmony_ci		 * PISR we 'll lose it. So make sure that we don't write back
61562306a36Sopenharmony_ci		 * on PISR any bits that come from SISRs. Clearing them from
61662306a36Sopenharmony_ci		 * SISRs will also clear PISR so no need to worry here.
61762306a36Sopenharmony_ci		 */
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		/* XXX: There seems to be  an issue on some cards
62062306a36Sopenharmony_ci		 *	with tx interrupt flags not being updated
62162306a36Sopenharmony_ci		 *	on PISR despite that all Tx interrupt bits
62262306a36Sopenharmony_ci		 * 	are cleared on SISRs. Since we handle all
62362306a36Sopenharmony_ci		 *	Tx queues all together it shouldn't be an
62462306a36Sopenharmony_ci		 *	issue if we clear Tx interrupt flags also
62562306a36Sopenharmony_ci		 * 	on PISR to avoid that.
62662306a36Sopenharmony_ci		 */
62762306a36Sopenharmony_ci		pisr_clear = (pisr & ~AR5K_ISR_BITS_FROM_SISRS) |
62862306a36Sopenharmony_ci					(pisr & AR5K_INT_TX_ALL);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		/*
63162306a36Sopenharmony_ci		 * Write to clear them...
63262306a36Sopenharmony_ci		 * Note: This means that each bit we write back
63362306a36Sopenharmony_ci		 * to the registers will get cleared, leaving the
63462306a36Sopenharmony_ci		 * rest unaffected. So this won't affect new interrupts
63562306a36Sopenharmony_ci		 * we didn't catch while reading/processing, we 'll get
63662306a36Sopenharmony_ci		 * them next time get_isr gets called.
63762306a36Sopenharmony_ci		 */
63862306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, sisr0, AR5K_SISR0);
63962306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, sisr1, AR5K_SISR1);
64062306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, sisr2, AR5K_SISR2);
64162306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, sisr3, AR5K_SISR3);
64262306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, sisr4, AR5K_SISR4);
64362306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, pisr_clear, AR5K_PISR);
64462306a36Sopenharmony_ci		/* Flush previous write */
64562306a36Sopenharmony_ci		ath5k_hw_reg_read(ah, AR5K_PISR);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		/*
64862306a36Sopenharmony_ci		 * Filter out the non-common bits from the interrupt
64962306a36Sopenharmony_ci		 * status.
65062306a36Sopenharmony_ci		 */
65162306a36Sopenharmony_ci		*interrupt_mask = (pisr & AR5K_INT_COMMON) & ah->ah_imr;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		ah->ah_txq_isr_txok_all = 0;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci		/* We treat TXOK,TXDESC, TXERR and TXEOL
65662306a36Sopenharmony_ci		 * the same way (schedule the tx tasklet)
65762306a36Sopenharmony_ci		 * so we track them all together per queue */
65862306a36Sopenharmony_ci		if (pisr & AR5K_ISR_TXOK)
65962306a36Sopenharmony_ci			ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0,
66062306a36Sopenharmony_ci						AR5K_SISR0_QCU_TXOK);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		if (pisr & AR5K_ISR_TXDESC)
66362306a36Sopenharmony_ci			ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0,
66462306a36Sopenharmony_ci						AR5K_SISR0_QCU_TXDESC);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		if (pisr & AR5K_ISR_TXERR)
66762306a36Sopenharmony_ci			ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1,
66862306a36Sopenharmony_ci						AR5K_SISR1_QCU_TXERR);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci		if (pisr & AR5K_ISR_TXEOL)
67162306a36Sopenharmony_ci			ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1,
67262306a36Sopenharmony_ci						AR5K_SISR1_QCU_TXEOL);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci		/* Misc Beacon related interrupts */
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci		/* For AR5211 */
67762306a36Sopenharmony_ci		if (pisr & AR5K_ISR_TIM)
67862306a36Sopenharmony_ci			*interrupt_mask |= AR5K_INT_TIM;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci		/* For AR5212+ */
68162306a36Sopenharmony_ci		if (pisr & AR5K_ISR_BCNMISC) {
68262306a36Sopenharmony_ci			if (sisr2 & AR5K_SISR2_TIM)
68362306a36Sopenharmony_ci				*interrupt_mask |= AR5K_INT_TIM;
68462306a36Sopenharmony_ci			if (sisr2 & AR5K_SISR2_DTIM)
68562306a36Sopenharmony_ci				*interrupt_mask |= AR5K_INT_DTIM;
68662306a36Sopenharmony_ci			if (sisr2 & AR5K_SISR2_DTIM_SYNC)
68762306a36Sopenharmony_ci				*interrupt_mask |= AR5K_INT_DTIM_SYNC;
68862306a36Sopenharmony_ci			if (sisr2 & AR5K_SISR2_BCN_TIMEOUT)
68962306a36Sopenharmony_ci				*interrupt_mask |= AR5K_INT_BCN_TIMEOUT;
69062306a36Sopenharmony_ci			if (sisr2 & AR5K_SISR2_CAB_TIMEOUT)
69162306a36Sopenharmony_ci				*interrupt_mask |= AR5K_INT_CAB_TIMEOUT;
69262306a36Sopenharmony_ci		}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		/* Below interrupts are unlikely to happen */
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci		/* HIU = Host Interface Unit (PCI etc)
69762306a36Sopenharmony_ci		 * Can be one of MCABT, SSERR, DPERR from SISR2 */
69862306a36Sopenharmony_ci		if (unlikely(pisr & (AR5K_ISR_HIUERR)))
69962306a36Sopenharmony_ci			*interrupt_mask |= AR5K_INT_FATAL;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		/*Beacon Not Ready*/
70262306a36Sopenharmony_ci		if (unlikely(pisr & (AR5K_ISR_BNR)))
70362306a36Sopenharmony_ci			*interrupt_mask |= AR5K_INT_BNR;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		/* A queue got CBR overrun */
70662306a36Sopenharmony_ci		if (unlikely(pisr & (AR5K_ISR_QCBRORN)))
70762306a36Sopenharmony_ci			*interrupt_mask |= AR5K_INT_QCBRORN;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		/* A queue got CBR underrun */
71062306a36Sopenharmony_ci		if (unlikely(pisr & (AR5K_ISR_QCBRURN)))
71162306a36Sopenharmony_ci			*interrupt_mask |= AR5K_INT_QCBRURN;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci		/* A queue got triggered */
71462306a36Sopenharmony_ci		if (unlikely(pisr & (AR5K_ISR_QTRIG)))
71562306a36Sopenharmony_ci			*interrupt_mask |= AR5K_INT_QTRIG;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		data = pisr;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/*
72162306a36Sopenharmony_ci	 * In case we didn't handle anything,
72262306a36Sopenharmony_ci	 * print the register value.
72362306a36Sopenharmony_ci	 */
72462306a36Sopenharmony_ci	if (unlikely(*interrupt_mask == 0 && net_ratelimit()))
72562306a36Sopenharmony_ci		ATH5K_PRINTF("ISR: 0x%08x IMR: 0x%08x\n", data, ah->ah_imr);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	return 0;
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci/**
73162306a36Sopenharmony_ci * ath5k_hw_set_imr() - Set interrupt mask
73262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
73362306a36Sopenharmony_ci * @new_mask: The new interrupt mask to be set
73462306a36Sopenharmony_ci *
73562306a36Sopenharmony_ci * Set the interrupt mask in hw to save interrupts. We do that by mapping
73662306a36Sopenharmony_ci * ath5k_int bits to hw-specific bits to remove abstraction and writing
73762306a36Sopenharmony_ci * Interrupt Mask Register.
73862306a36Sopenharmony_ci */
73962306a36Sopenharmony_cienum ath5k_int
74062306a36Sopenharmony_ciath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	enum ath5k_int old_mask, int_mask;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	old_mask = ah->ah_imr;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/*
74762306a36Sopenharmony_ci	 * Disable card interrupts to prevent any race conditions
74862306a36Sopenharmony_ci	 * (they will be re-enabled afterwards if AR5K_INT GLOBAL
74962306a36Sopenharmony_ci	 * is set again on the new mask).
75062306a36Sopenharmony_ci	 */
75162306a36Sopenharmony_ci	if (old_mask & AR5K_INT_GLOBAL) {
75262306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER);
75362306a36Sopenharmony_ci		ath5k_hw_reg_read(ah, AR5K_IER);
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	/*
75762306a36Sopenharmony_ci	 * Add additional, chipset-dependent interrupt mask flags
75862306a36Sopenharmony_ci	 * and write them to the IMR (interrupt mask register).
75962306a36Sopenharmony_ci	 */
76062306a36Sopenharmony_ci	int_mask = new_mask & AR5K_INT_COMMON;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	if (ah->ah_version != AR5K_AR5210) {
76362306a36Sopenharmony_ci		/* Preserve per queue TXURN interrupt mask */
76462306a36Sopenharmony_ci		u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2)
76562306a36Sopenharmony_ci				& AR5K_SIMR2_QCU_TXURN;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		/* Fatal interrupt abstraction for 5211+ */
76862306a36Sopenharmony_ci		if (new_mask & AR5K_INT_FATAL) {
76962306a36Sopenharmony_ci			int_mask |= AR5K_IMR_HIUERR;
77062306a36Sopenharmony_ci			simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR
77162306a36Sopenharmony_ci				| AR5K_SIMR2_DPERR);
77262306a36Sopenharmony_ci		}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci		/* Misc beacon related interrupts */
77562306a36Sopenharmony_ci		if (new_mask & AR5K_INT_TIM)
77662306a36Sopenharmony_ci			int_mask |= AR5K_IMR_TIM;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci		if (new_mask & AR5K_INT_TIM)
77962306a36Sopenharmony_ci			simr2 |= AR5K_SISR2_TIM;
78062306a36Sopenharmony_ci		if (new_mask & AR5K_INT_DTIM)
78162306a36Sopenharmony_ci			simr2 |= AR5K_SISR2_DTIM;
78262306a36Sopenharmony_ci		if (new_mask & AR5K_INT_DTIM_SYNC)
78362306a36Sopenharmony_ci			simr2 |= AR5K_SISR2_DTIM_SYNC;
78462306a36Sopenharmony_ci		if (new_mask & AR5K_INT_BCN_TIMEOUT)
78562306a36Sopenharmony_ci			simr2 |= AR5K_SISR2_BCN_TIMEOUT;
78662306a36Sopenharmony_ci		if (new_mask & AR5K_INT_CAB_TIMEOUT)
78762306a36Sopenharmony_ci			simr2 |= AR5K_SISR2_CAB_TIMEOUT;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		/*Beacon Not Ready*/
79062306a36Sopenharmony_ci		if (new_mask & AR5K_INT_BNR)
79162306a36Sopenharmony_ci			int_mask |= AR5K_INT_BNR;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci		/* Note: Per queue interrupt masks
79462306a36Sopenharmony_ci		 * are set via ath5k_hw_reset_tx_queue() (qcu.c) */
79562306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR);
79662306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	} else {
79962306a36Sopenharmony_ci		/* Fatal interrupt abstraction for 5210 */
80062306a36Sopenharmony_ci		if (new_mask & AR5K_INT_FATAL)
80162306a36Sopenharmony_ci			int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT
80262306a36Sopenharmony_ci				| AR5K_IMR_HIUERR | AR5K_IMR_DPERR);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		/* Only common interrupts left for 5210 (no SIMRs) */
80562306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, int_mask, AR5K_IMR);
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	/* If RXNOFRM interrupt is masked disable it
80962306a36Sopenharmony_ci	 * by setting AR5K_RXNOFRM to zero */
81062306a36Sopenharmony_ci	if (!(new_mask & AR5K_INT_RXNOFRM))
81162306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	/* Store new interrupt mask */
81462306a36Sopenharmony_ci	ah->ah_imr = new_mask;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	/* ..re-enable interrupts if AR5K_INT_GLOBAL is set */
81762306a36Sopenharmony_ci	if (new_mask & AR5K_INT_GLOBAL) {
81862306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER);
81962306a36Sopenharmony_ci		ath5k_hw_reg_read(ah, AR5K_IER);
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return old_mask;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci/********************\
82762306a36Sopenharmony_ci Init/Stop functions
82862306a36Sopenharmony_ci\********************/
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci/**
83162306a36Sopenharmony_ci * ath5k_hw_dma_init() - Initialize DMA unit
83262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
83362306a36Sopenharmony_ci *
83462306a36Sopenharmony_ci * Set DMA size and pre-enable interrupts
83562306a36Sopenharmony_ci * (driver handles tx/rx buffer setup and
83662306a36Sopenharmony_ci * dma start/stop)
83762306a36Sopenharmony_ci *
83862306a36Sopenharmony_ci * XXX: Save/restore RXDP/TXDP registers ?
83962306a36Sopenharmony_ci */
84062306a36Sopenharmony_civoid
84162306a36Sopenharmony_ciath5k_hw_dma_init(struct ath5k_hw *ah)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	/*
84462306a36Sopenharmony_ci	 * Set Rx/Tx DMA Configuration
84562306a36Sopenharmony_ci	 *
84662306a36Sopenharmony_ci	 * Set standard DMA size (128). Note that
84762306a36Sopenharmony_ci	 * a DMA size of 512 causes rx overruns and tx errors
84862306a36Sopenharmony_ci	 * on pci-e cards (tested on 5424 but since rx overruns
84962306a36Sopenharmony_ci	 * also occur on 5416/5418 with madwifi we set 128
85062306a36Sopenharmony_ci	 * for all PCI-E cards to be safe).
85162306a36Sopenharmony_ci	 *
85262306a36Sopenharmony_ci	 * XXX: need to check 5210 for this
85362306a36Sopenharmony_ci	 * TODO: Check out tx trigger level, it's always 64 on dumps but I
85462306a36Sopenharmony_ci	 * guess we can tweak it and see how it goes ;-)
85562306a36Sopenharmony_ci	 */
85662306a36Sopenharmony_ci	if (ah->ah_version != AR5K_AR5210) {
85762306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
85862306a36Sopenharmony_ci			AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
85962306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
86062306a36Sopenharmony_ci			AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B);
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	/* Pre-enable interrupts on 5211/5212*/
86462306a36Sopenharmony_ci	if (ah->ah_version != AR5K_AR5210)
86562306a36Sopenharmony_ci		ath5k_hw_set_imr(ah, ah->ah_imr);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci/**
87062306a36Sopenharmony_ci * ath5k_hw_dma_stop() - stop DMA unit
87162306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
87262306a36Sopenharmony_ci *
87362306a36Sopenharmony_ci * Stop tx/rx DMA and interrupts. Returns
87462306a36Sopenharmony_ci * -EBUSY if tx or rx dma failed to stop.
87562306a36Sopenharmony_ci *
87662306a36Sopenharmony_ci * XXX: Sometimes DMA unit hangs and we have
87762306a36Sopenharmony_ci * stuck frames on tx queues, only a reset
87862306a36Sopenharmony_ci * can fix that.
87962306a36Sopenharmony_ci */
88062306a36Sopenharmony_ciint
88162306a36Sopenharmony_ciath5k_hw_dma_stop(struct ath5k_hw *ah)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	int i, qmax, err;
88462306a36Sopenharmony_ci	err = 0;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	/* Disable interrupts */
88762306a36Sopenharmony_ci	ath5k_hw_set_imr(ah, 0);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	/* Stop rx dma */
89062306a36Sopenharmony_ci	err = ath5k_hw_stop_rx_dma(ah);
89162306a36Sopenharmony_ci	if (err)
89262306a36Sopenharmony_ci		return err;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* Clear any pending interrupts
89562306a36Sopenharmony_ci	 * and disable tx dma */
89662306a36Sopenharmony_ci	if (ah->ah_version != AR5K_AR5210) {
89762306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR);
89862306a36Sopenharmony_ci		qmax = AR5K_NUM_TX_QUEUES;
89962306a36Sopenharmony_ci	} else {
90062306a36Sopenharmony_ci		/* PISR/SISR Not available on 5210 */
90162306a36Sopenharmony_ci		ath5k_hw_reg_read(ah, AR5K_ISR);
90262306a36Sopenharmony_ci		qmax = AR5K_NUM_TX_QUEUES_NOQCU;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	for (i = 0; i < qmax; i++) {
90662306a36Sopenharmony_ci		err = ath5k_hw_stop_tx_dma(ah, i);
90762306a36Sopenharmony_ci		/* -EINVAL -> queue inactive */
90862306a36Sopenharmony_ci		if (err && err != -EINVAL)
90962306a36Sopenharmony_ci			return err;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	return 0;
91362306a36Sopenharmony_ci}
914