Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7368160f authored by Russell Hu's avatar Russell Hu Committed by Kalle Valo
Browse files

ath9k: add MSI support



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: default avatarRussell Hu <rhu@qti.qualcomm.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 79d891c1
Loading
Loading
Loading
Loading
+26 −7
Original line number Diff line number Diff line
@@ -922,6 +922,7 @@ 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))
@@ -929,22 +930,30 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,

	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);

@@ -952,6 +961,16 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
	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);
+3 −0
Original line number Diff line number Diff line
@@ -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 {
+4 −0
Original line number Diff line number Diff line
@@ -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
+47 −0
Original line number Diff line number Diff line
@@ -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)
@@ -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 {
@@ -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) {
+20 −1
Original line number Diff line number Diff line
@@ -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 */
@@ -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;
@@ -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;

	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;
@@ -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);
Loading