Skip to content

Commit

Permalink
ath9k: add MSI support
Browse files Browse the repository at this point in the history
On new Intel platforms like ApolloLake, legacy interrupt mechanism
(INTx) is not supported, so WLAN modules are not working because
interrupts are missing, therefore this patch is to add MSI support to
ath9k.  With module paremeter "use_msi=1", ath9k driver would try to
use MSI instead of INTx.

Signed-off-by: Russell Hu <rhu@qti.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
  • Loading branch information
Russell Hu authored and Kalle Valo committed Jan 16, 2018
1 parent 79d891c commit 7368160
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 8 deletions.
33 changes: 26 additions & 7 deletions drivers/net/wireless/ath/ath9k/hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -922,36 +922,55 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
AR_IMR_RXERR |
AR_IMR_RXORN |
AR_IMR_BCNMISC;
u32 msi_cfg = 0;

if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
AR_SREV_9561(ah))
sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;

if (AR_SREV_9300_20_OR_LATER(ah)) {
imr_reg |= AR_IMR_RXOK_HP;
if (ah->config.rx_intr_mitigation)
if (ah->config.rx_intr_mitigation) {
imr_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR;
else
msi_cfg |= AR_INTCFG_MSI_RXINTM | AR_INTCFG_MSI_RXMINTR;
} else {
imr_reg |= AR_IMR_RXOK_LP;

msi_cfg |= AR_INTCFG_MSI_RXOK;
}
} else {
if (ah->config.rx_intr_mitigation)
if (ah->config.rx_intr_mitigation) {
imr_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR;
else
msi_cfg |= AR_INTCFG_MSI_RXINTM | AR_INTCFG_MSI_RXMINTR;
} else {
imr_reg |= AR_IMR_RXOK;
msi_cfg |= AR_INTCFG_MSI_RXOK;
}
}

if (ah->config.tx_intr_mitigation)
if (ah->config.tx_intr_mitigation) {
imr_reg |= AR_IMR_TXINTM | AR_IMR_TXMINTR;
else
msi_cfg |= AR_INTCFG_MSI_TXINTM | AR_INTCFG_MSI_TXMINTR;
} else {
imr_reg |= AR_IMR_TXOK;
msi_cfg |= AR_INTCFG_MSI_TXOK;
}

ENABLE_REGWRITE_BUFFER(ah);

REG_WRITE(ah, AR_IMR, imr_reg);
ah->imrs2_reg |= AR_IMR_S2_GTT;
REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg);

if (ah->msi_enabled) {
ah->msi_reg = REG_READ(ah, AR_PCIE_MSI);
ah->msi_reg |= AR_PCIE_MSI_HW_DBI_WR_EN;
ah->msi_reg &= AR_PCIE_MSI_HW_INT_PENDING_ADDR_MSI_64;
REG_WRITE(ah, AR_INTCFG, msi_cfg);
ath_dbg(ath9k_hw_common(ah), ANY,
"value of AR_INTCFG=0x%X, msi_cfg=0x%X\n",
REG_READ(ah, AR_INTCFG), msi_cfg);
}

if (!AR_SREV_9100(ah)) {
REG_WRITE(ah, AR_INTR_SYNC_CAUSE, 0xFFFFFFFF);
REG_WRITE(ah, AR_INTR_SYNC_ENABLE, sync_default);
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/wireless/ath/ath9k/hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,9 @@ struct ath_hw {
bool tpc_enabled;
u8 tx_power[Ar5416RateSize];
u8 tx_power_stbc[Ar5416RateSize];
bool msi_enabled;
u32 msi_mask;
u32 msi_reg;
};

struct ath_bus_ops {
Expand Down
4 changes: 4 additions & 0 deletions drivers/net/wireless/ath/ath9k/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency");

#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */

int ath9k_use_msi;
module_param_named(use_msi, ath9k_use_msi, int, 0444);
MODULE_PARM_DESC(use_msi, "Use MSI instead of INTx if possible");

bool is_ath9k_unloaded;

#ifdef CONFIG_MAC80211_LEDS
Expand Down
47 changes: 47 additions & 0 deletions drivers/net/wireless/ath/ath9k/mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,43 @@ static void __ath9k_hw_enable_interrupts(struct ath_hw *ah)
}
ath_dbg(common, INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));

if (ah->msi_enabled) {
u32 _msi_reg = 0;
u32 i = 0;
u32 msi_pend_addr_mask = AR_PCIE_MSI_HW_INT_PENDING_ADDR_MSI_64;

ath_dbg(ath9k_hw_common(ah), INTERRUPT,
"Enabling MSI, msi_mask=0x%X\n", ah->msi_mask);

REG_WRITE(ah, AR_INTR_PRIO_ASYNC_ENABLE, ah->msi_mask);
REG_WRITE(ah, AR_INTR_PRIO_ASYNC_MASK, ah->msi_mask);
ath_dbg(ath9k_hw_common(ah), INTERRUPT,
"AR_INTR_PRIO_ASYNC_ENABLE=0x%X, AR_INTR_PRIO_ASYNC_MASK=0x%X\n",
REG_READ(ah, AR_INTR_PRIO_ASYNC_ENABLE),
REG_READ(ah, AR_INTR_PRIO_ASYNC_MASK));

if (ah->msi_reg == 0)
ah->msi_reg = REG_READ(ah, AR_PCIE_MSI);

ath_dbg(ath9k_hw_common(ah), INTERRUPT,
"AR_PCIE_MSI=0x%X, ah->msi_reg = 0x%X\n",
AR_PCIE_MSI, ah->msi_reg);

i = 0;
do {
REG_WRITE(ah, AR_PCIE_MSI,
(ah->msi_reg | AR_PCIE_MSI_ENABLE)
& msi_pend_addr_mask);
_msi_reg = REG_READ(ah, AR_PCIE_MSI);
i++;
} while ((_msi_reg & AR_PCIE_MSI_ENABLE) == 0 && i < 200);

if (i >= 200)
ath_err(ath9k_hw_common(ah),
"%s: _msi_reg = 0x%X\n",
__func__, _msi_reg);
}
}

void ath9k_hw_resume_interrupts(struct ath_hw *ah)
Expand Down Expand Up @@ -878,12 +915,21 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah)
if (!(ints & ATH9K_INT_GLOBAL))
ath9k_hw_disable_interrupts(ah);

if (ah->msi_enabled) {
ath_dbg(common, INTERRUPT, "Clearing AR_INTR_PRIO_ASYNC_ENABLE\n");

REG_WRITE(ah, AR_INTR_PRIO_ASYNC_ENABLE, 0);
REG_READ(ah, AR_INTR_PRIO_ASYNC_ENABLE);
}

ath_dbg(common, INTERRUPT, "New interrupt mask 0x%x\n", ints);

mask = ints & ATH9K_INT_COMMON;
mask2 = 0;

ah->msi_mask = 0;
if (ints & ATH9K_INT_TX) {
ah->msi_mask |= AR_INTR_PRIO_TX;
if (ah->config.tx_intr_mitigation)
mask |= AR_IMR_TXMINTR | AR_IMR_TXINTM;
else {
Expand All @@ -898,6 +944,7 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah)
mask |= AR_IMR_TXEOL;
}
if (ints & ATH9K_INT_RX) {
ah->msi_mask |= AR_INTR_PRIO_RXLP | AR_INTR_PRIO_RXHP;
if (AR_SREV_9300_20_OR_LATER(ah)) {
mask |= AR_IMR_RXERR | AR_IMR_RXOK_HP;
if (ah->config.rx_intr_mitigation) {
Expand Down
21 changes: 20 additions & 1 deletion drivers/net/wireless/ath/ath9k/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include <linux/module.h>
#include "ath9k.h"

extern int ath9k_use_msi;

static const struct pci_device_id ath_pci_id_table[] = {
{ PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */
Expand Down Expand Up @@ -889,6 +891,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
u32 val;
int ret = 0;
char hw_name[64];
int msi_enabled = 0;

if (pcim_enable_device(pdev))
return -EIO;
Expand Down Expand Up @@ -960,7 +963,20 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
sc->mem = pcim_iomap_table(pdev)[0];
sc->driver_data = id->driver_data;

ret = request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc);
if (ath9k_use_msi) {
if (pci_enable_msi(pdev) == 0) {
msi_enabled = 1;
dev_err(&pdev->dev, "Using MSI\n");
} else {
dev_err(&pdev->dev, "Using INTx\n");
}
}

if (!msi_enabled)
ret = request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc);
else
ret = request_irq(pdev->irq, ath_isr, 0, "ath9k", sc);

if (ret) {
dev_err(&pdev->dev, "request_irq failed\n");
goto err_irq;
Expand All @@ -974,6 +990,9 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_init;
}

sc->sc_ah->msi_enabled = msi_enabled;
sc->sc_ah->msi_reg = 0;

ath9k_hw_name(sc->sc_ah, hw_name, sizeof(hw_name));
wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n",
hw_name, (unsigned long)sc->mem, pdev->irq);
Expand Down
15 changes: 15 additions & 0 deletions drivers/net/wireless/ath/ath9k/reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@
#define AR_MACMISC_MISC_OBS_BUS_MSB_S 15
#define AR_MACMISC_MISC_OBS_BUS_1 1

#define AR_INTCFG 0x005C
#define AR_INTCFG_MSI_RXOK 0x00000000
#define AR_INTCFG_MSI_RXINTM 0x00000004
#define AR_INTCFG_MSI_RXMINTR 0x00000006
#define AR_INTCFG_MSI_TXOK 0x00000000
#define AR_INTCFG_MSI_TXINTM 0x00000010
#define AR_INTCFG_MSI_TXMINTR 0x00000018

#define AR_DATABUF_SIZE 0x0060
#define AR_DATABUF_SIZE_MASK 0x00000FFF

Expand Down Expand Up @@ -1256,6 +1264,13 @@ enum {
#define AR_PCIE_MSI (AR_SREV_9340(ah) ? 0x40d8 : \
(AR_SREV_9300_20_OR_LATER(ah) ? 0x40a4 : 0x4094))
#define AR_PCIE_MSI_ENABLE 0x00000001
#define AR_PCIE_MSI_HW_DBI_WR_EN 0x02000000
#define AR_PCIE_MSI_HW_INT_PENDING_ADDR 0xFFA0C1FF /* bits 8..11: value must be 0x5060 */
#define AR_PCIE_MSI_HW_INT_PENDING_ADDR_MSI_64 0xFFA0C9FF /* bits 8..11: value must be 0x5064 */

#define AR_INTR_PRIO_TX 0x00000001
#define AR_INTR_PRIO_RXLP 0x00000002
#define AR_INTR_PRIO_RXHP 0x00000004

#define AR_INTR_PRIO_SYNC_ENABLE (AR_SREV_9340(ah) ? 0x4088 : 0x40c4)
#define AR_INTR_PRIO_ASYNC_MASK (AR_SREV_9340(ah) ? 0x408c : 0x40c8)
Expand Down

0 comments on commit 7368160

Please sign in to comment.