* [PATCH 0/8] ath10k: pci fixes 2013-11-22
@ 2013-11-22 13:05 Michal Kazior
2013-11-22 13:05 ` [PATCH 1/8] ath10k: don't consume other's shared interrupts Michal Kazior
` (8 more replies)
0 siblings, 9 replies; 24+ messages in thread
From: Michal Kazior @ 2013-11-22 13:05 UTC (permalink / raw)
To: ath10k; +Cc: linux-wireless, Michal Kazior
Hi,
ath10k didn't play well with other devices on a
shared irq line. This patchset fixes support for
shared legacy interrupts.
Some rework was necessary because ath10k was using
disable_irq and CE irq masking (which is not
sufficient for shared interrupts).
Since main irq handlers are now registered after
boot, BMI is now polling for CE updates. I haven't
observed any differences in boot speed.
Also this plugs a leak I spotted during rework and
adds an option for testing different irq modes.
Michal Kazior (8):
ath10k: don't consume other's shared interrupts
ath10k: split up pci irq code
ath10k: fix memory leak on hif_start failpath
ath10k: don't use interrupts for BMI
ath10k: defer irq registration until hif start()
ath10k: extract functions for legacy irq handling
ath10k: re-add support for early fw indication
ath10k: allow explicit MSI/MSI-X disabling
drivers/net/wireless/ath/ath10k/hw.h | 1 +
drivers/net/wireless/ath/ath10k/pci.c | 503 ++++++++++++++++++++++------------
drivers/net/wireless/ath/ath10k/pci.h | 1 +
3 files changed, 333 insertions(+), 172 deletions(-)
--
1.8.4.rc3
^ permalink raw reply [flat|nested] 24+ messages in thread* [PATCH 1/8] ath10k: don't consume other's shared interrupts 2013-11-22 13:05 [PATCH 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior @ 2013-11-22 13:05 ` Michal Kazior 2013-11-24 13:59 ` Kalle Valo 2013-11-22 13:05 ` [PATCH 2/8] ath10k: split up pci irq code Michal Kazior ` (7 subsequent siblings) 8 siblings, 1 reply; 24+ messages in thread From: Michal Kazior @ 2013-11-22 13:05 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior ath10k assumed all interrupts were directed to it. This isn't the case for legacy shared interrupts. ath10k consumed interrupts for other devices. Check device irq status and return IRQ_NONE when appropriate. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- drivers/net/wireless/ath/ath10k/hw.h | 1 + drivers/net/wireless/ath/ath10k/pci.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 8aeb46d..9535eaa 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -269,6 +269,7 @@ enum ath10k_mcast2ucast_mode { #define CORE_CTRL_CPU_INTR_MASK 0x00002000 #define CORE_CTRL_ADDRESS 0x0000 #define PCIE_INTR_ENABLE_ADDRESS 0x0008 +#define PCIE_INTR_CAUSE_ADDRESS 0x000c #define PCIE_INTR_CLR_ADDRESS 0x0014 #define SCRATCH_3_ADDRESS 0x0030 diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 2457c8b..ed752d6 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2075,6 +2075,24 @@ static irqreturn_t ath10k_pci_msi_fw_handler(int irq, void *arg) return IRQ_HANDLED; } +static bool ath10k_pci_irq_pending(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + u32 cause; + + /* MSIs are always exclusive */ + if (ar_pci->num_msi_intrs > 0) + return true; + + /* Check if the shared legacy irq is for us */ + cause = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_CAUSE_ADDRESS); + if (cause & (PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL)) + return true; + + return false; +} + /* * Top-level interrupt handler for all PCI interrupts from a Target. * When a block of MSI interrupts is allocated, this top-level handler @@ -2085,6 +2103,9 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) struct ath10k *ar = arg; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + if (!ath10k_pci_irq_pending(ar)) + return IRQ_NONE; + if (ar_pci->num_msi_intrs == 0) { /* * IMPORTANT: INTR_CLR regiser has to be set after -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH 1/8] ath10k: don't consume other's shared interrupts 2013-11-22 13:05 ` [PATCH 1/8] ath10k: don't consume other's shared interrupts Michal Kazior @ 2013-11-24 13:59 ` Kalle Valo 0 siblings, 0 replies; 24+ messages in thread From: Kalle Valo @ 2013-11-24 13:59 UTC (permalink / raw) To: Michal Kazior; +Cc: ath10k, linux-wireless Michal Kazior <michal.kazior@tieto.com> writes: > ath10k assumed all interrupts were directed to it. > This isn't the case for legacy shared interrupts. > ath10k consumed interrupts for other devices. > > Check device irq status and return IRQ_NONE when > appropriate. > > Signed-off-by: Michal Kazior <michal.kazior@tieto.com> [...] > @@ -2085,6 +2103,9 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) > struct ath10k *ar = arg; > struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); > > + if (!ath10k_pci_irq_pending(ar)) > + return IRQ_NONE; > + > if (ar_pci->num_msi_intrs == 0) { > /* > * IMPORTANT: INTR_CLR regiser has to be set after What if you move ath10k_pci_irq_pending() call after ar_pci->num_msi_intrs == 0 check? That way you could remove the "ar_pci->num_msi_intrs == 0" check from irq_pending() function. -- Kalle Valo ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 2/8] ath10k: split up pci irq code 2013-11-22 13:05 [PATCH 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior 2013-11-22 13:05 ` [PATCH 1/8] ath10k: don't consume other's shared interrupts Michal Kazior @ 2013-11-22 13:05 ` Michal Kazior 2013-11-22 13:05 ` [PATCH 3/8] ath10k: fix memory leak on hif_start failpath Michal Kazior ` (6 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-22 13:05 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior Hardware waits until host signals whether it has chosen MSI(-X) or shared legacy interrupts. It is not required for the driver to register interrupt handlers immediately. This patch prepares the pci irq code for more changes. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- drivers/net/wireless/ath/ath10k/pci.c | 225 ++++++++++++++++++++-------------- 1 file changed, 131 insertions(+), 94 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index ed752d6..d4536cb 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -55,8 +55,10 @@ static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info); static void ath10k_pci_stop_ce(struct ath10k *ar); static int ath10k_pci_device_reset(struct ath10k *ar); static int ath10k_pci_wait_for_target_init(struct ath10k *ar); -static int ath10k_pci_start_intr(struct ath10k *ar); -static void ath10k_pci_stop_intr(struct ath10k *ar); +static int ath10k_pci_init_irq(struct ath10k *ar); +static int ath10k_pci_deinit_irq(struct ath10k *ar); +static int ath10k_pci_request_irq(struct ath10k *ar); +static void ath10k_pci_free_irq(struct ath10k *ar); static const struct ce_attr host_ce_config_wlan[] = { /* CE0: host->target HTC control and raw streams */ @@ -1341,7 +1343,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); /* Irqs are never explicitly re-enabled. They are implicitly re-enabled - * by ath10k_pci_start_intr(). */ + * by upon power_up. */ ath10k_pci_disable_irqs(ar); ath10k_pci_stop_ce(ar); @@ -1887,34 +1889,40 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) goto err_ce; } - ret = ath10k_pci_start_intr(ar); + ret = ath10k_pci_init_irq(ar); if (ret) { - ath10k_err("failed to start interrupt handling: %d\n", ret); + ath10k_err("failed to init irqs: %d\n", ret); goto err_ce; } + ret = ath10k_pci_request_irq(ar); + if (ret) { + ath10k_err("failed to request irqs: %d\n", ret); + goto err_deinit_irq; + } + ret = ath10k_pci_wait_for_target_init(ar); if (ret) { ath10k_err("failed to wait for target to init: %d\n", ret); - goto err_irq; + goto err_free_irq; } ret = ath10k_ce_enable_err_irq(ar); if (ret) { ath10k_err("failed to enable CE error irq: %d\n", ret); - goto err_irq; + goto err_free_irq; } ret = ath10k_pci_init_config(ar); if (ret) { ath10k_err("failed to setup init config: %d\n", ret); - goto err_irq; + goto err_free_irq; } ret = ath10k_pci_wake_target_cpu(ar); if (ret) { ath10k_err("could not wake up target CPU: %d\n", ret); - goto err_irq; + goto err_free_irq; } ath10k_pci_start_bmi(ar); @@ -1931,11 +1939,12 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) return 0; -err_irq: - ath10k_ce_disable_interrupts(ar); - ath10k_pci_stop_intr(ar); +err_free_irq: + ath10k_pci_free_irq(ar); ath10k_pci_kill_tasklet(ar); ath10k_pci_device_reset(ar); +err_deinit_irq: + ath10k_pci_deinit_irq(ar); err_ce: ath10k_pci_ce_deinit(ar); err_ps: @@ -1949,7 +1958,8 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - ath10k_pci_stop_intr(ar); + ath10k_pci_free_irq(ar); + ath10k_pci_deinit_irq(ar); ath10k_pci_device_reset(ar); ath10k_pci_ce_deinit(ar); @@ -2157,24 +2167,17 @@ static void ath10k_pci_tasklet(unsigned long data) } } -static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num) +static int ath10k_pci_request_irq_msix(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ret; - int i; - - ret = pci_enable_msi_block(ar_pci->pdev, num); - if (ret) - return ret; + int ret, i; ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, ath10k_pci_msi_fw_handler, IRQF_SHARED, "ath10k_pci", ar); if (ret) { - ath10k_warn("request_irq(%d) failed %d\n", + ath10k_warn("failed to request MSI-X fw irq %d: %d\n", ar_pci->pdev->irq + MSI_ASSIGN_FW, ret); - - pci_disable_msi(ar_pci->pdev); return ret; } @@ -2183,45 +2186,38 @@ static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num) ath10k_pci_per_engine_handler, IRQF_SHARED, "ath10k_pci", ar); if (ret) { - ath10k_warn("request_irq(%d) failed %d\n", + ath10k_warn("failed to request MSI-X ce irq %d: %d\n", ar_pci->pdev->irq + i, ret); for (i--; i >= MSI_ASSIGN_CE_INITIAL; i--) free_irq(ar_pci->pdev->irq + i, ar); free_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, ar); - pci_disable_msi(ar_pci->pdev); return ret; } } - ath10k_dbg(ATH10K_DBG_BOOT, - "MSI-X interrupt handling (%d intrs)\n", num); return 0; } -static int ath10k_pci_start_intr_msi(struct ath10k *ar) +static int ath10k_pci_request_irq_msi(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; - ret = pci_enable_msi(ar_pci->pdev); - if (ret < 0) - return ret; - ret = request_irq(ar_pci->pdev->irq, ath10k_pci_interrupt_handler, IRQF_SHARED, "ath10k_pci", ar); - if (ret < 0) { - pci_disable_msi(ar_pci->pdev); + if (ret) { + ath10k_warn("failed to request MSI irq %d: %d\n", + ar_pci->pdev->irq, ret); return ret; } - ath10k_dbg(ATH10K_DBG_BOOT, "MSI interrupt handling\n"); return 0; } -static int ath10k_pci_start_intr_legacy(struct ath10k *ar) +static int ath10k_pci_request_irq_legacy(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; @@ -2229,99 +2225,140 @@ static int ath10k_pci_start_intr_legacy(struct ath10k *ar) ret = request_irq(ar_pci->pdev->irq, ath10k_pci_interrupt_handler, IRQF_SHARED, "ath10k_pci", ar); - if (ret < 0) - return ret; - - ret = ath10k_pci_wake(ar); if (ret) { - free_irq(ar_pci->pdev->irq, ar); - ath10k_err("failed to wake up target: %d\n", ret); + ath10k_warn("failed to request legacy irq %d: %d\n", + ar_pci->pdev->irq, ret); return ret; } - /* - * A potential race occurs here: The CORE_BASE write - * depends on target correctly decoding AXI address but - * host won't know when target writes BAR to CORE_CTRL. - * This write might get lost if target has NOT written BAR. - * For now, fix the race by repeating the write in below - * synchronization checking. - */ - iowrite32(PCIE_INTR_FIRMWARE_MASK | - PCIE_INTR_CE_MASK_ALL, - ar_pci->mem + (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - - ath10k_pci_sleep(ar); - ath10k_dbg(ATH10K_DBG_BOOT, "legacy interrupt handling\n"); return 0; } -static int ath10k_pci_start_intr(struct ath10k *ar) +static int ath10k_pci_request_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + switch (ar_pci->num_msi_intrs) { + case 0: + return ath10k_pci_request_irq_legacy(ar); + case 1: + return ath10k_pci_request_irq_msi(ar); + case MSI_NUM_REQUEST: + return ath10k_pci_request_irq_msix(ar); + } + + ath10k_warn("unknown irq configuration upon request\n"); + return -EINVAL; +} + +static void ath10k_pci_free_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int i; + + /* There's at least one interrupt irregardless whether its legacy INTR + * or MSI or MSI-X */ + for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) + free_irq(ar_pci->pdev->irq + i, ar); +} + +static void ath10k_pci_init_irq_tasklets(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int num = MSI_NUM_REQUEST; - int ret; int i; - tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long) ar); + tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long)ar); tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet, - (unsigned long) ar); + (unsigned long)ar); for (i = 0; i < CE_COUNT; i++) { ar_pci->pipe_info[i].ar_pci = ar_pci; - tasklet_init(&ar_pci->pipe_info[i].intr, - ath10k_pci_ce_tasklet, + tasklet_init(&ar_pci->pipe_info[i].intr, ath10k_pci_ce_tasklet, (unsigned long)&ar_pci->pipe_info[i]); } +} + +static int ath10k_pci_init_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + ath10k_pci_init_irq_tasklets(ar); if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features)) - num = 1; + goto msi; - if (num > 1) { - ret = ath10k_pci_start_intr_msix(ar, num); - if (ret == 0) - goto exit; + /* Try MSI-X */ + ar_pci->num_msi_intrs = MSI_NUM_REQUEST; + ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs); + if (ret == 0) + return 0; + if (ret > 0) + pci_disable_msi(ar_pci->pdev); - ath10k_dbg(ATH10K_DBG_BOOT, - "MSI-X didn't succeed (%d), trying MSI\n", ret); - num = 1; - } +msi: + /* Try MSI */ + ar_pci->num_msi_intrs = 1; + ret = pci_enable_msi(ar_pci->pdev); + if (ret == 0) + return 0; - if (num == 1) { - ret = ath10k_pci_start_intr_msi(ar); - if (ret == 0) - goto exit; + /* Try legacy irq + * + * A potential race occurs here: The CORE_BASE write + * depends on target correctly decoding AXI address but + * host won't know when target writes BAR to CORE_CTRL. + * This write might get lost if target has NOT written BAR. + * For now, fix the race by repeating the write in below + * synchronization checking. */ + ar_pci->num_msi_intrs = 0; - ath10k_dbg(ATH10K_DBG_BOOT, - "MSI didn't succeed (%d), trying legacy INTR\n", - ret); - num = 0; + ret = ath10k_pci_wake(ar); + if (ret) { + ath10k_warn("failed to wake target: %d\n", ret); + return ret; } - ret = ath10k_pci_start_intr_legacy(ar); + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, + PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); + ath10k_pci_sleep(ar); + + return 0; +} + +static int ath10k_pci_deinit_irq_legacy(struct ath10k *ar) +{ + int ret; + + ret = ath10k_pci_wake(ar); if (ret) { - ath10k_warn("Failed to start legacy interrupts: %d\n", ret); + ath10k_warn("failed to wake target: %d\n", ret); return ret; } -exit: - ar_pci->num_msi_intrs = num; - return ret; + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, + 0); + ath10k_pci_sleep(ar); + + return 0; } -static void ath10k_pci_stop_intr(struct ath10k *ar) +static int ath10k_pci_deinit_irq(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int i; - /* There's at least one interrupt irregardless whether its legacy INTR - * or MSI or MSI-X */ - for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) - free_irq(ar_pci->pdev->irq + i, ar); - - if (ar_pci->num_msi_intrs > 0) + switch (ar_pci->num_msi_intrs) { + case 0: + return ath10k_pci_deinit_irq_legacy(ar); + case 1: + /* fall-through */ + case MSI_NUM_REQUEST: pci_disable_msi(ar_pci->pdev); + return 0; + } + + ath10k_warn("unknown irq configuration upon deinit\n"); + return -EINVAL; } static int ath10k_pci_wait_for_target_init(struct ath10k *ar) -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 3/8] ath10k: fix memory leak on hif_start failpath 2013-11-22 13:05 [PATCH 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior 2013-11-22 13:05 ` [PATCH 1/8] ath10k: don't consume other's shared interrupts Michal Kazior 2013-11-22 13:05 ` [PATCH 2/8] ath10k: split up pci irq code Michal Kazior @ 2013-11-22 13:05 ` Michal Kazior 2013-11-22 13:05 ` [PATCH 4/8] ath10k: don't use interrupts for BMI Michal Kazior ` (5 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-22 13:05 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior If post_rx failed then completions were not freed. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- drivers/net/wireless/ath/ath10k/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index d4536cb..4067890 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1205,6 +1205,7 @@ static int ath10k_pci_hif_start(struct ath10k *ar) if (ret) { ath10k_warn("failed to post RX buffers for all pipes: %d\n", ret); + ath10k_pci_stop_ce(ar); return ret; } -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 4/8] ath10k: don't use interrupts for BMI 2013-11-22 13:05 [PATCH 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (2 preceding siblings ...) 2013-11-22 13:05 ` [PATCH 3/8] ath10k: fix memory leak on hif_start failpath Michal Kazior @ 2013-11-22 13:05 ` Michal Kazior 2013-11-22 13:05 ` [PATCH 5/8] ath10k: defer irq registration until hif start() Michal Kazior ` (4 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-22 13:05 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior It's not really necessary for interrupts to be used for BMI. BMI already assumes there's only one caller at a time and it works directly with CE. Make BMI poll for CE completions instead of waiting for interrupts. This makes disabling interrupts during early boot possible. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- drivers/net/wireless/ath/ath10k/pci.c | 50 ++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 4067890..1b12db8 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -59,6 +59,9 @@ static int ath10k_pci_init_irq(struct ath10k *ar); static int ath10k_pci_deinit_irq(struct ath10k *ar); static int ath10k_pci_request_irq(struct ath10k *ar); static void ath10k_pci_free_irq(struct ath10k *ar); +static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, + struct ath10k_ce_pipe *rx_pipe, + struct bmi_xfer *xfer); static const struct ce_attr host_ce_config_wlan[] = { /* CE0: host->target HTC control and raw streams */ @@ -1382,6 +1385,8 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, void *treq, *tresp = NULL; int ret = 0; + might_sleep(); + if (resp && !resp_len) return -EINVAL; @@ -1422,14 +1427,12 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, if (ret) goto err_resp; - ret = wait_for_completion_timeout(&xfer.done, - BMI_COMMUNICATION_TIMEOUT_HZ); - if (ret <= 0) { + ret = ath10k_pci_bmi_wait(ce_tx, ce_rx, &xfer); + if (ret) { u32 unused_buffer; unsigned int unused_nbytes; unsigned int unused_id; - ret = -ETIMEDOUT; ath10k_ce_cancel_send_next(ce_tx, NULL, &unused_buffer, &unused_nbytes, &unused_id); } else { @@ -1497,6 +1500,25 @@ static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state) complete(&xfer->done); } +static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, + struct ath10k_ce_pipe *rx_pipe, + struct bmi_xfer *xfer) +{ + unsigned long timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ; + + while (time_before_eq(jiffies, timeout)) { + ath10k_pci_bmi_send_done(tx_pipe); + ath10k_pci_bmi_recv_data(rx_pipe); + + if (completion_done(&xfer->done)) + return 0; + + schedule(); + } + + return -ETIMEDOUT; +} + /* * Map from service/endpoint to Copy Engine. * This table is derived from the CE_PCI TABLE, above. @@ -1834,24 +1856,6 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) ath10k_pci_sleep(ar); } -static void ath10k_pci_start_bmi(struct ath10k *ar) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_pci_pipe *pipe; - - /* - * Initially, establish CE completion handlers for use with BMI. - * These are overwritten with generic handlers after we exit BMI phase. - */ - pipe = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG]; - ath10k_ce_send_cb_register(pipe->ce_hdl, ath10k_pci_bmi_send_done, 0); - - pipe = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST]; - ath10k_ce_recv_cb_register(pipe->ce_hdl, ath10k_pci_bmi_recv_data); - - ath10k_dbg(ATH10K_DBG_BOOT, "boot start bmi\n"); -} - static int ath10k_pci_hif_power_up(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -1926,8 +1930,6 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) goto err_free_irq; } - ath10k_pci_start_bmi(ar); - if (ar_pci->num_msi_intrs > 1) irq_mode = "MSI-X"; else if (ar_pci->num_msi_intrs == 1) -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 5/8] ath10k: defer irq registration until hif start() 2013-11-22 13:05 [PATCH 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (3 preceding siblings ...) 2013-11-22 13:05 ` [PATCH 4/8] ath10k: don't use interrupts for BMI Michal Kazior @ 2013-11-22 13:05 ` Michal Kazior 2013-11-22 13:05 ` [PATCH 6/8] ath10k: extract functions for legacy irq handling Michal Kazior ` (3 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-22 13:05 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior It's impossible to rely on disable_irq() and/or CE interrupt masking with legacy shared interrupts. Other devices sharing the same irq line may assert it while ath10k is doing something that requires no interrupts. Irq handlers are now registered after all preparations are complete so spurious/foreign interrupts won't do any harm. The handlers are unregistered when no interrupts are required (i.e. during driver teardown). This also removes the ability to receive FW early indication (since interrupts are not registered until early boot is complete). This is not mission critical (it's more of a hint that early boot failed due to unexpected FW crash) and will be re-added in a follow up patch. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- drivers/net/wireless/ath/ath10k/pci.c | 62 +++++++++++++++-------------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 1b12db8..fa1162e 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -886,13 +886,6 @@ static void ath10k_pci_stop_ce(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci_compl *compl; struct sk_buff *skb; - int ret; - - ret = ath10k_ce_disable_interrupts(ar); - if (ret) - ath10k_warn("failed to disable CE interrupts: %d\n", ret); - - ath10k_pci_kill_tasklet(ar); /* Mark pending completions as aborted, so that upper layers free up * their associated resources */ @@ -1203,17 +1196,30 @@ static int ath10k_pci_hif_start(struct ath10k *ar) return ret; } + ret = ath10k_pci_request_irq(ar); + if (ret) { + ath10k_warn("failed to post RX buffers for all pipes: %d\n", + ret); + goto err_stop_ce; + } + /* Post buffers once to start things off. */ ret = ath10k_pci_post_rx(ar); if (ret) { ath10k_warn("failed to post RX buffers for all pipes: %d\n", ret); - ath10k_pci_stop_ce(ar); - return ret; + goto err_free_irq; } ar_pci->started = 1; return 0; + +err_free_irq: + ath10k_pci_free_irq(ar); + ath10k_pci_kill_tasklet(ar); +err_stop_ce: + ath10k_pci_stop_ce(ar); + return ret; } static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info) @@ -1331,25 +1337,19 @@ static void ath10k_pci_ce_deinit(struct ath10k *ar) } } -static void ath10k_pci_disable_irqs(struct ath10k *ar) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int i; - - for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) - disable_irq(ar_pci->pdev->irq + i); -} - static void ath10k_pci_hif_stop(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); - /* Irqs are never explicitly re-enabled. They are implicitly re-enabled - * by upon power_up. */ - ath10k_pci_disable_irqs(ar); + ret = ath10k_ce_disable_interrupts(ar); + if (ret) + ath10k_warn("failed to disable CE interrupts: %d\n", ret); + ath10k_pci_free_irq(ar); + ath10k_pci_kill_tasklet(ar); ath10k_pci_stop_ce(ar); /* At this point, asynchronous threads are stopped, the target should @@ -1900,34 +1900,28 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) goto err_ce; } - ret = ath10k_pci_request_irq(ar); - if (ret) { - ath10k_err("failed to request irqs: %d\n", ret); - goto err_deinit_irq; - } - ret = ath10k_pci_wait_for_target_init(ar); if (ret) { ath10k_err("failed to wait for target to init: %d\n", ret); - goto err_free_irq; + goto err_deinit_irq; } ret = ath10k_ce_enable_err_irq(ar); if (ret) { ath10k_err("failed to enable CE error irq: %d\n", ret); - goto err_free_irq; + goto err_deinit_irq; } ret = ath10k_pci_init_config(ar); if (ret) { ath10k_err("failed to setup init config: %d\n", ret); - goto err_free_irq; + goto err_deinit_irq; } ret = ath10k_pci_wake_target_cpu(ar); if (ret) { ath10k_err("could not wake up target CPU: %d\n", ret); - goto err_free_irq; + goto err_deinit_irq; } if (ar_pci->num_msi_intrs > 1) @@ -1942,14 +1936,11 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) return 0; -err_free_irq: - ath10k_pci_free_irq(ar); - ath10k_pci_kill_tasklet(ar); - ath10k_pci_device_reset(ar); err_deinit_irq: ath10k_pci_deinit_irq(ar); err_ce: ath10k_pci_ce_deinit(ar); + ath10k_pci_device_reset(ar); err_ps: if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) ath10k_do_pci_sleep(ar); @@ -1961,7 +1952,6 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - ath10k_pci_free_irq(ar); ath10k_pci_deinit_irq(ar); ath10k_pci_device_reset(ar); -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 6/8] ath10k: extract functions for legacy irq handling 2013-11-22 13:05 [PATCH 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (4 preceding siblings ...) 2013-11-22 13:05 ` [PATCH 5/8] ath10k: defer irq registration until hif start() Michal Kazior @ 2013-11-22 13:05 ` Michal Kazior 2013-11-22 13:05 ` [PATCH 7/8] ath10k: re-add support for early fw indication Michal Kazior ` (2 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-22 13:05 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior Preparation for code re-use. Also use ioread/write wrappers. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- drivers/net/wireless/ath/ath10k/pci.c | 68 +++++++++++++++++------------------ 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index fa1162e..a9bc290 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2096,6 +2096,34 @@ static bool ath10k_pci_irq_pending(struct ath10k *ar) return false; } +static void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar) +{ + /* IMPORTANT: INTR_CLR register has to be set after + * INTR_ENABLE is set to 0, otherwise interrupt can not be + * really cleared. */ + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, + 0); + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_CLR_ADDRESS, + PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); + + /* IMPORTANT: this extra read transaction is required to + * flush the posted write buffer. */ + (void) ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS); +} + +static void ath10k_pci_enable_legacy_irq(struct ath10k *ar) +{ + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS, + PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); + + /* IMPORTANT: this extra read transaction is required to + * flush the posted write buffer. */ + (void) ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS); +} + /* * Top-level interrupt handler for all PCI interrupts from a Target. * When a block of MSI interrupts is allocated, this top-level handler @@ -2109,27 +2137,8 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) if (!ath10k_pci_irq_pending(ar)) return IRQ_NONE; - if (ar_pci->num_msi_intrs == 0) { - /* - * IMPORTANT: INTR_CLR regiser has to be set after - * INTR_ENABLE is set to 0, otherwise interrupt can not be - * really cleared. - */ - iowrite32(0, ar_pci->mem + - (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - iowrite32(PCIE_INTR_FIRMWARE_MASK | - PCIE_INTR_CE_MASK_ALL, - ar_pci->mem + (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_CLR_ADDRESS)); - /* - * IMPORTANT: this extra read transaction is required to - * flush the posted write buffer. - */ - (void) ioread32(ar_pci->mem + - (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - } + if (ar_pci->num_msi_intrs == 0) + ath10k_pci_disable_and_clear_legacy_irq(ar); tasklet_schedule(&ar_pci->intr_tq); @@ -2144,20 +2153,9 @@ static void ath10k_pci_tasklet(unsigned long data) ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */ ath10k_ce_per_engine_service_any(ar); - if (ar_pci->num_msi_intrs == 0) { - /* Enable Legacy PCI line interrupts */ - iowrite32(PCIE_INTR_FIRMWARE_MASK | - PCIE_INTR_CE_MASK_ALL, - ar_pci->mem + (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - /* - * IMPORTANT: this extra read transaction is required to - * flush the posted write buffer - */ - (void) ioread32(ar_pci->mem + - (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - } + /* Re-enable legacy irq that was disabled in the irq handler */ + if (ar_pci->num_msi_intrs == 0) + ath10k_pci_enable_legacy_irq(ar); } static int ath10k_pci_request_irq_msix(struct ath10k *ar) -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH 7/8] ath10k: re-add support for early fw indication 2013-11-22 13:05 [PATCH 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (5 preceding siblings ...) 2013-11-22 13:05 ` [PATCH 6/8] ath10k: extract functions for legacy irq handling Michal Kazior @ 2013-11-22 13:05 ` Michal Kazior 2013-11-25 12:20 ` Kalle Valo 2013-11-22 13:05 ` [PATCH 8/8] ath10k: allow explicit MSI/MSI-X disabling Michal Kazior 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior 8 siblings, 1 reply; 24+ messages in thread From: Michal Kazior @ 2013-11-22 13:05 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior It's possible for FW to panic during early boot or at driver teardown in some rare cases. The patch re-introduces support to detect and print those crashes. This introduces an additional irq handler that is set for the duration of early boot and shutdown. The handler is then overriden with regular handlers upon hif start(). Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- drivers/net/wireless/ath/ath10k/pci.c | 107 ++++++++++++++++++++++++++++++++-- drivers/net/wireless/ath/ath10k/pci.h | 1 + 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index a9bc290..47d9b6e 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -59,6 +59,8 @@ static int ath10k_pci_init_irq(struct ath10k *ar); static int ath10k_pci_deinit_irq(struct ath10k *ar); static int ath10k_pci_request_irq(struct ath10k *ar); static void ath10k_pci_free_irq(struct ath10k *ar); +static int ath10k_pci_request_early_irq(struct ath10k *ar); +static void ath10k_pci_free_early_irq(struct ath10k *ar); static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, struct ath10k_ce_pipe *rx_pipe, struct bmi_xfer *xfer); @@ -876,6 +878,7 @@ static void ath10k_pci_kill_tasklet(struct ath10k *ar) tasklet_kill(&ar_pci->intr_tq); tasklet_kill(&ar_pci->msi_fw_err); + tasklet_kill(&ar_pci->early_irq_tasklet); for (i = 0; i < CE_COUNT; i++) tasklet_kill(&ar_pci->pipe_info[i].intr); @@ -1188,12 +1191,15 @@ static int ath10k_pci_post_rx(struct ath10k *ar) static int ath10k_pci_hif_start(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ret; + int ret, ret2; + + ath10k_pci_free_early_irq(ar); + ath10k_pci_kill_tasklet(ar); ret = ath10k_pci_start_ce(ar); if (ret) { ath10k_warn("failed to start CE: %d\n", ret); - return ret; + goto err_early_irq; } ret = ath10k_pci_request_irq(ar); @@ -1219,6 +1225,11 @@ err_free_irq: ath10k_pci_kill_tasklet(ar); err_stop_ce: ath10k_pci_stop_ce(ar); +err_early_irq: + ret2 = ath10k_pci_request_early_irq(ar); + if (ret2) + ath10k_warn("failed to re-enable early irq: %d\n", ret2); + return ret; } @@ -1352,6 +1363,10 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_pci_kill_tasklet(ar); ath10k_pci_stop_ce(ar); + ret = ath10k_pci_request_early_irq(ar); + if (ret) + ath10k_warn("failed to re-enable early irq: %d\n", ret); + /* At this point, asynchronous threads are stopped, the target should * not DMA nor interrupt. We process the leftovers and then free * everything else up. */ @@ -1900,28 +1915,34 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) goto err_ce; } + ret = ath10k_pci_request_early_irq(ar); + if (ret) { + ath10k_err("failed to request early irq: %d\n", ret); + goto err_deinit_irq; + } + ret = ath10k_pci_wait_for_target_init(ar); if (ret) { ath10k_err("failed to wait for target to init: %d\n", ret); - goto err_deinit_irq; + goto err_free_early_irq; } ret = ath10k_ce_enable_err_irq(ar); if (ret) { ath10k_err("failed to enable CE error irq: %d\n", ret); - goto err_deinit_irq; + goto err_free_early_irq; } ret = ath10k_pci_init_config(ar); if (ret) { ath10k_err("failed to setup init config: %d\n", ret); - goto err_deinit_irq; + goto err_free_early_irq; } ret = ath10k_pci_wake_target_cpu(ar); if (ret) { ath10k_err("could not wake up target CPU: %d\n", ret); - goto err_deinit_irq; + goto err_free_early_irq; } if (ar_pci->num_msi_intrs > 1) @@ -1936,6 +1957,8 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) return 0; +err_free_early_irq: + ath10k_pci_free_early_irq(ar); err_deinit_irq: ath10k_pci_deinit_irq(ar); err_ce: @@ -1952,6 +1975,9 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + ath10k_ce_disable_interrupts(ar); + ath10k_pci_free_early_irq(ar); + ath10k_pci_kill_tasklet(ar); ath10k_pci_deinit_irq(ar); ath10k_pci_device_reset(ar); @@ -2145,6 +2171,50 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) return IRQ_HANDLED; } +static irqreturn_t ath10k_pci_early_irq_handler(int irq, void *arg) +{ + struct ath10k *ar = arg; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + if (!ath10k_pci_irq_pending(ar)) + return IRQ_NONE; + + if (ar_pci->num_msi_intrs == 0) + ath10k_pci_disable_and_clear_legacy_irq(ar); + + tasklet_schedule(&ar_pci->early_irq_tasklet); + + return IRQ_HANDLED; +} + +static void ath10k_pci_early_irq_tasklet(unsigned long data) +{ + struct ath10k *ar = (struct ath10k *)data; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + u32 fw_ind; + int ret; + + ret = ath10k_pci_wake(ar); + if (ret) { + ath10k_warn("failed to wake target in early irq tasklet: %d\n", + ret); + return; + } + + fw_ind = ath10k_pci_read32(ar, ar_pci->fw_indicator_address); + if (fw_ind & FW_IND_EVENT_PENDING) { + ath10k_pci_write32(ar, ar_pci->fw_indicator_address, + fw_ind & ~FW_IND_EVENT_PENDING); + + /* Some structures are unavailable during early boot or at + * driver teardown so just print that the device has crashed. */ + ath10k_warn("device crashed - no diagnostics available\n"); + } + + ath10k_pci_sleep(ar); + ath10k_pci_enable_legacy_irq(ar); +} + static void ath10k_pci_tasklet(unsigned long data) { struct ath10k *ar = (struct ath10k *)data; @@ -2253,6 +2323,29 @@ static void ath10k_pci_free_irq(struct ath10k *ar) free_irq(ar_pci->pdev->irq + i, ar); } +static int ath10k_pci_request_early_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + /* Regardless whether MSI-X/MSI/legacy irqs have been set up the first + * interrupt from irq vector is triggered in all cases for FW + * indication/errors */ + ret = request_irq(ar_pci->pdev->irq, ath10k_pci_early_irq_handler, + IRQF_SHARED, "ath10k_pci (early)", ar); + if (ret) { + ath10k_warn("failed to request early irq: %d\n", ret); + return ret; + } + + return 0; +} + +static void ath10k_pci_free_early_irq(struct ath10k *ar) +{ + free_irq(ath10k_pci_priv(ar)->pdev->irq, ar); +} + static void ath10k_pci_init_irq_tasklets(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -2261,6 +2354,8 @@ static void ath10k_pci_init_irq_tasklets(struct ath10k *ar) tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long)ar); tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet, (unsigned long)ar); + tasklet_init(&ar_pci->early_irq_tasklet, ath10k_pci_early_irq_tasklet, + (unsigned long)ar); for (i = 0; i < CE_COUNT; i++) { ar_pci->pipe_info[i].ar_pci = ar_pci; diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 73a3d4e..a4f3203 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -198,6 +198,7 @@ struct ath10k_pci { struct tasklet_struct intr_tq; struct tasklet_struct msi_fw_err; + struct tasklet_struct early_irq_tasklet; int started; -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH 7/8] ath10k: re-add support for early fw indication 2013-11-22 13:05 ` [PATCH 7/8] ath10k: re-add support for early fw indication Michal Kazior @ 2013-11-25 12:20 ` Kalle Valo 2013-11-25 12:46 ` Michal Kazior 0 siblings, 1 reply; 24+ messages in thread From: Kalle Valo @ 2013-11-25 12:20 UTC (permalink / raw) To: Michal Kazior; +Cc: ath10k, linux-wireless Michal Kazior <michal.kazior@tieto.com> writes: > It's possible for FW to panic during early boot or > at driver teardown in some rare cases. > > The patch re-introduces support to detect and > print those crashes. > > This introduces an additional irq handler that is > set for the duration of early boot and shutdown. > The handler is then overriden with regular > handlers upon hif start(). > > Signed-off-by: Michal Kazior <michal.kazior@tieto.com> [...] > --- a/drivers/net/wireless/ath/ath10k/pci.c > +++ b/drivers/net/wireless/ath/ath10k/pci.c > @@ -59,6 +59,8 @@ static int ath10k_pci_init_irq(struct ath10k *ar); > static int ath10k_pci_deinit_irq(struct ath10k *ar); > static int ath10k_pci_request_irq(struct ath10k *ar); > static void ath10k_pci_free_irq(struct ath10k *ar); > +static int ath10k_pci_request_early_irq(struct ath10k *ar); > +static void ath10k_pci_free_early_irq(struct ath10k *ar); We should always try to avoid using forward declarations. If I understood correctly these are not needed. > static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, > struct ath10k_ce_pipe *rx_pipe, > struct bmi_xfer *xfer); > @@ -876,6 +878,7 @@ static void ath10k_pci_kill_tasklet(struct ath10k *ar) > > tasklet_kill(&ar_pci->intr_tq); > tasklet_kill(&ar_pci->msi_fw_err); > + tasklet_kill(&ar_pci->early_irq_tasklet); > > for (i = 0; i < CE_COUNT; i++) > tasklet_kill(&ar_pci->pipe_info[i].intr); > @@ -1188,12 +1191,15 @@ static int ath10k_pci_post_rx(struct ath10k *ar) > static int ath10k_pci_hif_start(struct ath10k *ar) > { > struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); > - int ret; > + int ret, ret2; > + > + ath10k_pci_free_early_irq(ar); > + ath10k_pci_kill_tasklet(ar); Calling kill_tasklet() looks a bit odd here when you only want to kill early_irq_tasklet. But I guess that's ok. > ret = ath10k_pci_start_ce(ar); > if (ret) { > ath10k_warn("failed to start CE: %d\n", ret); > - return ret; > + goto err_early_irq; > } > > ret = ath10k_pci_request_irq(ar); > @@ -1219,6 +1225,11 @@ err_free_irq: > ath10k_pci_kill_tasklet(ar); > err_stop_ce: > ath10k_pci_stop_ce(ar); > +err_early_irq: > + ret2 = ath10k_pci_request_early_irq(ar); > + if (ret2) > + ath10k_warn("failed to re-enable early irq: %d\n", ret2); ret_early or something like that would be a nicer name. > @@ -1952,6 +1975,9 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar) > { > struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); > > + ath10k_ce_disable_interrupts(ar); > + ath10k_pci_free_early_irq(ar); > + ath10k_pci_kill_tasklet(ar); Should disable_interrupts() and kill_tasklet() be in an earlier patch? -- Kalle Valo ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 7/8] ath10k: re-add support for early fw indication 2013-11-25 12:20 ` Kalle Valo @ 2013-11-25 12:46 ` Michal Kazior 0 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-25 12:46 UTC (permalink / raw) To: Kalle Valo; +Cc: ath10k, linux-wireless On 25 November 2013 13:20, Kalle Valo <kvalo@qca.qualcomm.com> wrote: > Michal Kazior <michal.kazior@tieto.com> writes: > >> It's possible for FW to panic during early boot or >> at driver teardown in some rare cases. >> >> The patch re-introduces support to detect and >> print those crashes. >> >> This introduces an additional irq handler that is >> set for the duration of early boot and shutdown. >> The handler is then overriden with regular >> handlers upon hif start(). >> >> Signed-off-by: Michal Kazior <michal.kazior@tieto.com> [...] >> @@ -1952,6 +1975,9 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar) >> { >> struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); >> >> + ath10k_ce_disable_interrupts(ar); >> + ath10k_pci_free_early_irq(ar); >> + ath10k_pci_kill_tasklet(ar); > > Should disable_interrupts() and kill_tasklet() be in an earlier patch? No. Before this patch there are no interrupt handlers registered by power_up, so there are no interrupts to be cleaned up in power_down. Since this patch introduces early irq handling in power_up, then power_down must shut everything down. Now that I think about the ath10k_ce_disable_interrupts() isn't necessary here. The other two functions are though. Michał ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 8/8] ath10k: allow explicit MSI/MSI-X disabling 2013-11-22 13:05 [PATCH 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (6 preceding siblings ...) 2013-11-22 13:05 ` [PATCH 7/8] ath10k: re-add support for early fw indication Michal Kazior @ 2013-11-22 13:05 ` Michal Kazior 2013-11-25 12:01 ` Kalle Valo 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior 8 siblings, 1 reply; 24+ messages in thread From: Michal Kazior @ 2013-11-22 13:05 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior This can be useful for testing and debugging. Two new ath10k_pci module options are now available: disable_msix - disable just MSI-X disable_msi - disable MSI (along with MSI-X) Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- drivers/net/wireless/ath/ath10k/pci.c | 43 +++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 47d9b6e..b89fd7b 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -34,9 +34,18 @@ #include "pci.h" static unsigned int ath10k_target_ps; +static bool ath10k_disable_msix; +static bool ath10k_disable_msi; + module_param(ath10k_target_ps, uint, 0644); MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option"); +module_param_named(disable_msix, ath10k_disable_msix, bool, 0644); +MODULE_PARM_DESC(disable_msix, "Disable MSI-X support"); + +module_param_named(disable_msi, ath10k_disable_msi, bool, 0644); +MODULE_PARM_DESC(disable_msi, "Disable MSI support"); + #define QCA988X_2_0_DEVICE_ID (0x003c) static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = { @@ -2367,27 +2376,33 @@ static void ath10k_pci_init_irq_tasklets(struct ath10k *ar) static int ath10k_pci_init_irq(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + bool msix_supported = test_bit(ATH10K_PCI_FEATURE_MSI_X, + ar_pci->features); int ret; ath10k_pci_init_irq_tasklets(ar); - if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features)) - goto msi; - /* Try MSI-X */ - ar_pci->num_msi_intrs = MSI_NUM_REQUEST; - ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs); - if (ret == 0) - return 0; - if (ret > 0) - pci_disable_msi(ar_pci->pdev); + if (msix_supported && !ath10k_disable_msix && !ath10k_disable_msi) { + ar_pci->num_msi_intrs = MSI_NUM_REQUEST; + ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs); + if (ret == 0) + return 0; + if (ret > 0) + pci_disable_msi(ar_pci->pdev); + + /* fall-through */ + } -msi: /* Try MSI */ - ar_pci->num_msi_intrs = 1; - ret = pci_enable_msi(ar_pci->pdev); - if (ret == 0) - return 0; + if (!ath10k_disable_msi) { + ar_pci->num_msi_intrs = 1; + ret = pci_enable_msi(ar_pci->pdev); + if (ret == 0) + return 0; + + /* fall-through */ + } /* Try legacy irq * -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH 8/8] ath10k: allow explicit MSI/MSI-X disabling 2013-11-22 13:05 ` [PATCH 8/8] ath10k: allow explicit MSI/MSI-X disabling Michal Kazior @ 2013-11-25 12:01 ` Kalle Valo 0 siblings, 0 replies; 24+ messages in thread From: Kalle Valo @ 2013-11-25 12:01 UTC (permalink / raw) To: Michal Kazior; +Cc: ath10k, linux-wireless Michal Kazior <michal.kazior@tieto.com> writes: > This can be useful for testing and debugging. > > Two new ath10k_pci module options are now available: > > disable_msix - disable just MSI-X > disable_msi - disable MSI (along with MSI-X) > > Signed-off-by: Michal Kazior <michal.kazior@tieto.com> This is useful but I think it's a bit too much to have two more options, it would be simpler to have just one. Few quick ideas how to do this a bit differently: irq_mode: 0 = automatic, 2 = MSI, 1 = legacy Here the problem is of course that we can't force use of MSI if the host only supports legacy interrupts, but IMHO that's a minor nuisance and in that case we should just enable legacy interrupts. disable_msi: 0x1 = disable MSI-X, 0x2 = disable MSI Basically this is just your booleans converted to a bitfield. I vote for irq_mode because I think that's easier for the user to understand. But I'm sure there is a better way to do this. > + if (msix_supported && !ath10k_disable_msix && !ath10k_disable_msi) { > + ar_pci->num_msi_intrs = MSI_NUM_REQUEST; > + ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs); > + if (ret == 0) > + return 0; > + if (ret > 0) > + pci_disable_msi(ar_pci->pdev); > + > + /* fall-through */ > + } If we force some other irq mode than the default it would be good to have an info print for that. -- Kalle Valo ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 2013-11-22 13:05 [PATCH 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (7 preceding siblings ...) 2013-11-22 13:05 ` [PATCH 8/8] ath10k: allow explicit MSI/MSI-X disabling Michal Kazior @ 2013-11-25 13:06 ` Michal Kazior 2013-11-25 13:06 ` [PATCH v2 1/8] ath10k: don't consume other's shared interrupts Michal Kazior ` (8 more replies) 8 siblings, 9 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-25 13:06 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior Hi, ath10k didn't play well with other devices on a shared irq line. This patchset fixes support for shared legacy interrupts. Some rework was necessary because ath10k was using disable_irq and CE irq masking (which is not sufficient for shared interrupts). Since main irq handlers are now registered after boot, BMI is now polling for CE updates. I haven't observed any differences in boot speed. Also this plugs a leak I spotted during rework and adds an option for testing different irq modes. v2: * simplify ath10k_pci_irq_pending() * combine memory leak fix patch with a functionality decoupling (it's very closely related) * fix 'irq: nobody cared' * change MSI/MSI-X disabling parameters * some minor fixes & code shuffling to avoid forward declarations Michal Kazior (8): ath10k: don't consume other's shared interrupts ath10k: split up pci irq code ath10k: don't use interrupts for BMI ath10k: decouple ath10k_pci_start_ce() ath10k: defer irq registration until hif start() ath10k: extract functions for legacy irq handling ath10k: re-add support for early fw indication ath10k: allow explicit MSI/MSI-X disabling drivers/net/wireless/ath/ath10k/ce.c | 15 - drivers/net/wireless/ath/ath10k/ce.h | 1 - drivers/net/wireless/ath/ath10k/hw.h | 1 + drivers/net/wireless/ath/ath10k/pci.c | 577 ++++++++++++++++++++++------------ drivers/net/wireless/ath/ath10k/pci.h | 1 + 5 files changed, 383 insertions(+), 212 deletions(-) -- 1.8.4.rc3 ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v2 1/8] ath10k: don't consume other's shared interrupts 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior @ 2013-11-25 13:06 ` Michal Kazior 2013-11-25 13:06 ` [PATCH v2 2/8] ath10k: split up pci irq code Michal Kazior ` (7 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-25 13:06 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior ath10k assumed all interrupts were directed to it. This isn't the case for legacy shared interrupts. ath10k consumed interrupts for other devices. Check device irq status and return IRQ_NONE when appropriate. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- v2: * move function definition up to avoid forward declaration in subsequent patches * remove check for legacy interrupts inside the irq_pending() by guaranteeing it is called only for legacy interrupts drivers/net/wireless/ath/ath10k/hw.h | 1 + drivers/net/wireless/ath/ath10k/pci.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 8aeb46d..9535eaa 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -269,6 +269,7 @@ enum ath10k_mcast2ucast_mode { #define CORE_CTRL_CPU_INTR_MASK 0x00002000 #define CORE_CTRL_ADDRESS 0x0000 #define PCIE_INTR_ENABLE_ADDRESS 0x0008 +#define PCIE_INTR_CAUSE_ADDRESS 0x000c #define PCIE_INTR_CLR_ADDRESS 0x0014 #define SCRATCH_3_ADDRESS 0x0030 diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 2457c8b..12fb12e 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -201,6 +201,19 @@ static const struct ce_pipe_config target_ce_config_wlan[] = { /* CE7 used only by Host */ }; +static bool ath10k_pci_irq_pending(struct ath10k *ar) +{ + u32 cause; + + /* Check if the shared legacy irq is for us */ + cause = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_CAUSE_ADDRESS); + if (cause & (PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL)) + return true; + + return false; +} + /* * Diagnostic read/write access is provided for startup/config/debug usage. * Caller must guarantee proper alignment, when applicable, and single user @@ -2086,6 +2099,9 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); if (ar_pci->num_msi_intrs == 0) { + if (!ath10k_pci_irq_pending(ar)) + return IRQ_NONE; + /* * IMPORTANT: INTR_CLR regiser has to be set after * INTR_ENABLE is set to 0, otherwise interrupt can not be -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 2/8] ath10k: split up pci irq code 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior 2013-11-25 13:06 ` [PATCH v2 1/8] ath10k: don't consume other's shared interrupts Michal Kazior @ 2013-11-25 13:06 ` Michal Kazior 2013-11-25 13:06 ` [PATCH v2 3/8] ath10k: don't use interrupts for BMI Michal Kazior ` (6 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-25 13:06 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior Hardware waits until host signals whether it has chosen MSI(-X) or shared legacy interrupts. It is not required for the driver to register interrupt handlers immediately. This patch prepares the pci irq code for more changes. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- drivers/net/wireless/ath/ath10k/pci.c | 225 ++++++++++++++++++++-------------- 1 file changed, 131 insertions(+), 94 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 12fb12e..957fc59 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -55,8 +55,10 @@ static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info); static void ath10k_pci_stop_ce(struct ath10k *ar); static int ath10k_pci_device_reset(struct ath10k *ar); static int ath10k_pci_wait_for_target_init(struct ath10k *ar); -static int ath10k_pci_start_intr(struct ath10k *ar); -static void ath10k_pci_stop_intr(struct ath10k *ar); +static int ath10k_pci_init_irq(struct ath10k *ar); +static int ath10k_pci_deinit_irq(struct ath10k *ar); +static int ath10k_pci_request_irq(struct ath10k *ar); +static void ath10k_pci_free_irq(struct ath10k *ar); static const struct ce_attr host_ce_config_wlan[] = { /* CE0: host->target HTC control and raw streams */ @@ -1354,7 +1356,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); /* Irqs are never explicitly re-enabled. They are implicitly re-enabled - * by ath10k_pci_start_intr(). */ + * by upon power_up. */ ath10k_pci_disable_irqs(ar); ath10k_pci_stop_ce(ar); @@ -1900,34 +1902,40 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) goto err_ce; } - ret = ath10k_pci_start_intr(ar); + ret = ath10k_pci_init_irq(ar); if (ret) { - ath10k_err("failed to start interrupt handling: %d\n", ret); + ath10k_err("failed to init irqs: %d\n", ret); goto err_ce; } + ret = ath10k_pci_request_irq(ar); + if (ret) { + ath10k_err("failed to request irqs: %d\n", ret); + goto err_deinit_irq; + } + ret = ath10k_pci_wait_for_target_init(ar); if (ret) { ath10k_err("failed to wait for target to init: %d\n", ret); - goto err_irq; + goto err_free_irq; } ret = ath10k_ce_enable_err_irq(ar); if (ret) { ath10k_err("failed to enable CE error irq: %d\n", ret); - goto err_irq; + goto err_free_irq; } ret = ath10k_pci_init_config(ar); if (ret) { ath10k_err("failed to setup init config: %d\n", ret); - goto err_irq; + goto err_free_irq; } ret = ath10k_pci_wake_target_cpu(ar); if (ret) { ath10k_err("could not wake up target CPU: %d\n", ret); - goto err_irq; + goto err_free_irq; } ath10k_pci_start_bmi(ar); @@ -1944,11 +1952,12 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) return 0; -err_irq: - ath10k_ce_disable_interrupts(ar); - ath10k_pci_stop_intr(ar); +err_free_irq: + ath10k_pci_free_irq(ar); ath10k_pci_kill_tasklet(ar); ath10k_pci_device_reset(ar); +err_deinit_irq: + ath10k_pci_deinit_irq(ar); err_ce: ath10k_pci_ce_deinit(ar); err_ps: @@ -1962,7 +1971,8 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - ath10k_pci_stop_intr(ar); + ath10k_pci_free_irq(ar); + ath10k_pci_deinit_irq(ar); ath10k_pci_device_reset(ar); ath10k_pci_ce_deinit(ar); @@ -2152,24 +2162,17 @@ static void ath10k_pci_tasklet(unsigned long data) } } -static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num) +static int ath10k_pci_request_irq_msix(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ret; - int i; - - ret = pci_enable_msi_block(ar_pci->pdev, num); - if (ret) - return ret; + int ret, i; ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, ath10k_pci_msi_fw_handler, IRQF_SHARED, "ath10k_pci", ar); if (ret) { - ath10k_warn("request_irq(%d) failed %d\n", + ath10k_warn("failed to request MSI-X fw irq %d: %d\n", ar_pci->pdev->irq + MSI_ASSIGN_FW, ret); - - pci_disable_msi(ar_pci->pdev); return ret; } @@ -2178,45 +2181,38 @@ static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num) ath10k_pci_per_engine_handler, IRQF_SHARED, "ath10k_pci", ar); if (ret) { - ath10k_warn("request_irq(%d) failed %d\n", + ath10k_warn("failed to request MSI-X ce irq %d: %d\n", ar_pci->pdev->irq + i, ret); for (i--; i >= MSI_ASSIGN_CE_INITIAL; i--) free_irq(ar_pci->pdev->irq + i, ar); free_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, ar); - pci_disable_msi(ar_pci->pdev); return ret; } } - ath10k_dbg(ATH10K_DBG_BOOT, - "MSI-X interrupt handling (%d intrs)\n", num); return 0; } -static int ath10k_pci_start_intr_msi(struct ath10k *ar) +static int ath10k_pci_request_irq_msi(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; - ret = pci_enable_msi(ar_pci->pdev); - if (ret < 0) - return ret; - ret = request_irq(ar_pci->pdev->irq, ath10k_pci_interrupt_handler, IRQF_SHARED, "ath10k_pci", ar); - if (ret < 0) { - pci_disable_msi(ar_pci->pdev); + if (ret) { + ath10k_warn("failed to request MSI irq %d: %d\n", + ar_pci->pdev->irq, ret); return ret; } - ath10k_dbg(ATH10K_DBG_BOOT, "MSI interrupt handling\n"); return 0; } -static int ath10k_pci_start_intr_legacy(struct ath10k *ar) +static int ath10k_pci_request_irq_legacy(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; @@ -2224,99 +2220,140 @@ static int ath10k_pci_start_intr_legacy(struct ath10k *ar) ret = request_irq(ar_pci->pdev->irq, ath10k_pci_interrupt_handler, IRQF_SHARED, "ath10k_pci", ar); - if (ret < 0) - return ret; - - ret = ath10k_pci_wake(ar); if (ret) { - free_irq(ar_pci->pdev->irq, ar); - ath10k_err("failed to wake up target: %d\n", ret); + ath10k_warn("failed to request legacy irq %d: %d\n", + ar_pci->pdev->irq, ret); return ret; } - /* - * A potential race occurs here: The CORE_BASE write - * depends on target correctly decoding AXI address but - * host won't know when target writes BAR to CORE_CTRL. - * This write might get lost if target has NOT written BAR. - * For now, fix the race by repeating the write in below - * synchronization checking. - */ - iowrite32(PCIE_INTR_FIRMWARE_MASK | - PCIE_INTR_CE_MASK_ALL, - ar_pci->mem + (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - - ath10k_pci_sleep(ar); - ath10k_dbg(ATH10K_DBG_BOOT, "legacy interrupt handling\n"); return 0; } -static int ath10k_pci_start_intr(struct ath10k *ar) +static int ath10k_pci_request_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + switch (ar_pci->num_msi_intrs) { + case 0: + return ath10k_pci_request_irq_legacy(ar); + case 1: + return ath10k_pci_request_irq_msi(ar); + case MSI_NUM_REQUEST: + return ath10k_pci_request_irq_msix(ar); + } + + ath10k_warn("unknown irq configuration upon request\n"); + return -EINVAL; +} + +static void ath10k_pci_free_irq(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int num = MSI_NUM_REQUEST; - int ret; int i; - tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long) ar); + /* There's at least one interrupt irregardless whether its legacy INTR + * or MSI or MSI-X */ + for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) + free_irq(ar_pci->pdev->irq + i, ar); +} + +static void ath10k_pci_init_irq_tasklets(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int i; + + tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long)ar); tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet, - (unsigned long) ar); + (unsigned long)ar); for (i = 0; i < CE_COUNT; i++) { ar_pci->pipe_info[i].ar_pci = ar_pci; - tasklet_init(&ar_pci->pipe_info[i].intr, - ath10k_pci_ce_tasklet, + tasklet_init(&ar_pci->pipe_info[i].intr, ath10k_pci_ce_tasklet, (unsigned long)&ar_pci->pipe_info[i]); } +} + +static int ath10k_pci_init_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + ath10k_pci_init_irq_tasklets(ar); if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features)) - num = 1; + goto msi; - if (num > 1) { - ret = ath10k_pci_start_intr_msix(ar, num); - if (ret == 0) - goto exit; + /* Try MSI-X */ + ar_pci->num_msi_intrs = MSI_NUM_REQUEST; + ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs); + if (ret == 0) + return 0; + if (ret > 0) + pci_disable_msi(ar_pci->pdev); - ath10k_dbg(ATH10K_DBG_BOOT, - "MSI-X didn't succeed (%d), trying MSI\n", ret); - num = 1; - } +msi: + /* Try MSI */ + ar_pci->num_msi_intrs = 1; + ret = pci_enable_msi(ar_pci->pdev); + if (ret == 0) + return 0; - if (num == 1) { - ret = ath10k_pci_start_intr_msi(ar); - if (ret == 0) - goto exit; + /* Try legacy irq + * + * A potential race occurs here: The CORE_BASE write + * depends on target correctly decoding AXI address but + * host won't know when target writes BAR to CORE_CTRL. + * This write might get lost if target has NOT written BAR. + * For now, fix the race by repeating the write in below + * synchronization checking. */ + ar_pci->num_msi_intrs = 0; - ath10k_dbg(ATH10K_DBG_BOOT, - "MSI didn't succeed (%d), trying legacy INTR\n", - ret); - num = 0; + ret = ath10k_pci_wake(ar); + if (ret) { + ath10k_warn("failed to wake target: %d\n", ret); + return ret; } - ret = ath10k_pci_start_intr_legacy(ar); + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, + PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); + ath10k_pci_sleep(ar); + + return 0; +} + +static int ath10k_pci_deinit_irq_legacy(struct ath10k *ar) +{ + int ret; + + ret = ath10k_pci_wake(ar); if (ret) { - ath10k_warn("Failed to start legacy interrupts: %d\n", ret); + ath10k_warn("failed to wake target: %d\n", ret); return ret; } -exit: - ar_pci->num_msi_intrs = num; - return ret; + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, + 0); + ath10k_pci_sleep(ar); + + return 0; } -static void ath10k_pci_stop_intr(struct ath10k *ar) +static int ath10k_pci_deinit_irq(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int i; - /* There's at least one interrupt irregardless whether its legacy INTR - * or MSI or MSI-X */ - for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) - free_irq(ar_pci->pdev->irq + i, ar); - - if (ar_pci->num_msi_intrs > 0) + switch (ar_pci->num_msi_intrs) { + case 0: + return ath10k_pci_deinit_irq_legacy(ar); + case 1: + /* fall-through */ + case MSI_NUM_REQUEST: pci_disable_msi(ar_pci->pdev); + return 0; + } + + ath10k_warn("unknown irq configuration upon deinit\n"); + return -EINVAL; } static int ath10k_pci_wait_for_target_init(struct ath10k *ar) -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 3/8] ath10k: don't use interrupts for BMI 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior 2013-11-25 13:06 ` [PATCH v2 1/8] ath10k: don't consume other's shared interrupts Michal Kazior 2013-11-25 13:06 ` [PATCH v2 2/8] ath10k: split up pci irq code Michal Kazior @ 2013-11-25 13:06 ` Michal Kazior 2013-11-25 13:06 ` [PATCH v2 4/8] ath10k: decouple ath10k_pci_start_ce() Michal Kazior ` (5 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-25 13:06 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior It's not really necessary for interrupts to be used for BMI. BMI already assumes there's only one caller at a time and it works directly with CE. Make BMI poll for CE completions instead of waiting for interrupts. This makes disabling interrupts during early boot possible. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- drivers/net/wireless/ath/ath10k/pci.c | 50 ++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 957fc59..327afbc 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -59,6 +59,9 @@ static int ath10k_pci_init_irq(struct ath10k *ar); static int ath10k_pci_deinit_irq(struct ath10k *ar); static int ath10k_pci_request_irq(struct ath10k *ar); static void ath10k_pci_free_irq(struct ath10k *ar); +static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, + struct ath10k_ce_pipe *rx_pipe, + struct bmi_xfer *xfer); static const struct ce_attr host_ce_config_wlan[] = { /* CE0: host->target HTC control and raw streams */ @@ -1394,6 +1397,8 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, void *treq, *tresp = NULL; int ret = 0; + might_sleep(); + if (resp && !resp_len) return -EINVAL; @@ -1434,14 +1439,12 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, if (ret) goto err_resp; - ret = wait_for_completion_timeout(&xfer.done, - BMI_COMMUNICATION_TIMEOUT_HZ); - if (ret <= 0) { + ret = ath10k_pci_bmi_wait(ce_tx, ce_rx, &xfer); + if (ret) { u32 unused_buffer; unsigned int unused_nbytes; unsigned int unused_id; - ret = -ETIMEDOUT; ath10k_ce_cancel_send_next(ce_tx, NULL, &unused_buffer, &unused_nbytes, &unused_id); } else { @@ -1509,6 +1512,25 @@ static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state) complete(&xfer->done); } +static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, + struct ath10k_ce_pipe *rx_pipe, + struct bmi_xfer *xfer) +{ + unsigned long timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ; + + while (time_before_eq(jiffies, timeout)) { + ath10k_pci_bmi_send_done(tx_pipe); + ath10k_pci_bmi_recv_data(rx_pipe); + + if (completion_done(&xfer->done)) + return 0; + + schedule(); + } + + return -ETIMEDOUT; +} + /* * Map from service/endpoint to Copy Engine. * This table is derived from the CE_PCI TABLE, above. @@ -1846,24 +1868,6 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) ath10k_pci_sleep(ar); } -static void ath10k_pci_start_bmi(struct ath10k *ar) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_pci_pipe *pipe; - - /* - * Initially, establish CE completion handlers for use with BMI. - * These are overwritten with generic handlers after we exit BMI phase. - */ - pipe = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG]; - ath10k_ce_send_cb_register(pipe->ce_hdl, ath10k_pci_bmi_send_done, 0); - - pipe = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST]; - ath10k_ce_recv_cb_register(pipe->ce_hdl, ath10k_pci_bmi_recv_data); - - ath10k_dbg(ATH10K_DBG_BOOT, "boot start bmi\n"); -} - static int ath10k_pci_hif_power_up(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -1938,8 +1942,6 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) goto err_free_irq; } - ath10k_pci_start_bmi(ar); - if (ar_pci->num_msi_intrs > 1) irq_mode = "MSI-X"; else if (ar_pci->num_msi_intrs == 1) -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 4/8] ath10k: decouple ath10k_pci_start_ce() 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (2 preceding siblings ...) 2013-11-25 13:06 ` [PATCH v2 3/8] ath10k: don't use interrupts for BMI Michal Kazior @ 2013-11-25 13:06 ` Michal Kazior 2013-11-25 13:06 ` [PATCH v2 5/8] ath10k: defer irq registration until hif start() Michal Kazior ` (4 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-25 13:06 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior The function did a couple of things: it allocated CE completions, registered CE callbacks and enabled CE interrupts through HW registers. This cannot be so. Split the function into one that allocates CE completions and the other one that starts off CE operation. This is required for future legacy shared interrupt handling. This also fixes possible memory leak if post rx failed. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- drivers/net/wireless/ath/ath10k/pci.c | 75 +++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 327afbc..05e6f8b 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -62,6 +62,7 @@ static void ath10k_pci_free_irq(struct ath10k *ar); static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, struct ath10k_ce_pipe *rx_pipe, struct bmi_xfer *xfer); +static void ath10k_pci_cleanup_ce(struct ath10k *ar); static const struct ce_attr host_ce_config_wlan[] = { /* CE0: host->target HTC control and raw streams */ @@ -824,14 +825,13 @@ static void ath10k_pci_hif_set_callbacks(struct ath10k *ar, sizeof(ar_pci->msg_callbacks_current)); } -static int ath10k_pci_start_ce(struct ath10k *ar) +static int ath10k_pci_alloc_compl(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_ce_pipe *ce_diag = ar_pci->ce_diag; const struct ce_attr *attr; struct ath10k_pci_pipe *pipe_info; struct ath10k_pci_compl *compl; - int i, pipe_num, completions, disable_interrupts; + int i, pipe_num, completions; spin_lock_init(&ar_pci->compl_lock); INIT_LIST_HEAD(&ar_pci->compl_process); @@ -843,34 +843,23 @@ static int ath10k_pci_start_ce(struct ath10k *ar) INIT_LIST_HEAD(&pipe_info->compl_free); /* Handle Diagnostic CE specially */ - if (pipe_info->ce_hdl == ce_diag) + if (pipe_info->ce_hdl == ar_pci->ce_diag) continue; attr = &host_ce_config_wlan[pipe_num]; completions = 0; - if (attr->src_nentries) { - disable_interrupts = attr->flags & CE_ATTR_DIS_INTR; - ath10k_ce_send_cb_register(pipe_info->ce_hdl, - ath10k_pci_ce_send_done, - disable_interrupts); + if (attr->src_nentries) completions += attr->src_nentries; - } - if (attr->dest_nentries) { - ath10k_ce_recv_cb_register(pipe_info->ce_hdl, - ath10k_pci_ce_recv_data); + if (attr->dest_nentries) completions += attr->dest_nentries; - } - - if (completions == 0) - continue; for (i = 0; i < completions; i++) { compl = kmalloc(sizeof(*compl), GFP_KERNEL); if (!compl) { ath10k_warn("No memory for completion state\n"); - ath10k_pci_stop_ce(ar); + ath10k_pci_cleanup_ce(ar); return -ENOMEM; } @@ -882,6 +871,37 @@ static int ath10k_pci_start_ce(struct ath10k *ar) return 0; } +static int ath10k_pci_setup_ce_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + const struct ce_attr *attr; + struct ath10k_pci_pipe *pipe_info; + int pipe_num, disable_interrupts; + + for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + + /* Handle Diagnostic CE specially */ + if (pipe_info->ce_hdl == ar_pci->ce_diag) + continue; + + attr = &host_ce_config_wlan[pipe_num]; + + if (attr->src_nentries) { + disable_interrupts = attr->flags & CE_ATTR_DIS_INTR; + ath10k_ce_send_cb_register(pipe_info->ce_hdl, + ath10k_pci_ce_send_done, + disable_interrupts); + } + + if (attr->dest_nentries) + ath10k_ce_recv_cb_register(pipe_info->ce_hdl, + ath10k_pci_ce_recv_data); + } + + return 0; +} + static void ath10k_pci_kill_tasklet(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -1210,22 +1230,35 @@ static int ath10k_pci_hif_start(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; - ret = ath10k_pci_start_ce(ar); + ret = ath10k_pci_alloc_compl(ar); if (ret) { - ath10k_warn("failed to start CE: %d\n", ret); + ath10k_warn("failed to allocate CE completions: %d\n", ret); return ret; } + ret = ath10k_pci_setup_ce_irq(ar); + if (ret) { + ath10k_warn("failed to setup CE interrupts: %d\n", ret); + goto err_free_compl; + } + /* Post buffers once to start things off. */ ret = ath10k_pci_post_rx(ar); if (ret) { ath10k_warn("failed to post RX buffers for all pipes: %d\n", ret); - return ret; + goto err_stop_ce; } ar_pci->started = 1; return 0; + +err_stop_ce: + ath10k_pci_stop_ce(ar); + ath10k_pci_process_ce(ar); +err_free_compl: + ath10k_pci_cleanup_ce(ar); + return ret; } static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info) -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 5/8] ath10k: defer irq registration until hif start() 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (3 preceding siblings ...) 2013-11-25 13:06 ` [PATCH v2 4/8] ath10k: decouple ath10k_pci_start_ce() Michal Kazior @ 2013-11-25 13:06 ` Michal Kazior 2013-11-25 13:06 ` [PATCH v2 6/8] ath10k: extract functions for legacy irq handling Michal Kazior ` (3 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-25 13:06 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior It's impossible to rely on disable_irq() and/or CE interrupt masking with legacy shared interrupts. Other devices sharing the same irq line may assert it while ath10k is doing something that requires no interrupts. Irq handlers are now registered after all preparations are complete so spurious/foreign interrupts won't do any harm. The handlers are unregistered when no interrupts are required (i.e. during driver teardown). This also removes the ability to receive FW early indication (since interrupts are not registered until early boot is complete). This is not mission critical (it's more of a hint that early boot failed due to unexpected FW crash) and will be re-added in a follow up patch. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- v2: * remove ce_enable_err_irq() - dont enable device interrupts before irq handlers are registered * with the earlier pci_start_ce() decoupling patch, irq handlers are now registered after CE completions are allocated but before CE interrupts are enabled via HW registers (fixes 'irq: nobody cared') drivers/net/wireless/ath/ath10k/ce.c | 15 -------- drivers/net/wireless/ath/ath10k/ce.h | 1 - drivers/net/wireless/ath/ath10k/pci.c | 65 +++++++++++++---------------------- 3 files changed, 23 insertions(+), 58 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 476928f..d44d618 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -792,21 +792,6 @@ static void ath10k_ce_per_engine_handler_adjust(struct ath10k_ce_pipe *ce_state, ath10k_pci_sleep(ar); } -int ath10k_ce_enable_err_irq(struct ath10k *ar) -{ - int i, ret; - - ret = ath10k_pci_wake(ar); - if (ret) - return ret; - - for (i = 0; i < CE_COUNT; i++) - ath10k_ce_error_intr_enable(ar, ath10k_ce_base_address(i)); - - ath10k_pci_sleep(ar); - return 0; -} - int ath10k_ce_disable_interrupts(struct ath10k *ar) { int ce_id, ret; diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index 8a58bac..67dbde6 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -235,7 +235,6 @@ void ath10k_ce_deinit(struct ath10k_ce_pipe *ce_state); void ath10k_ce_per_engine_service_any(struct ath10k *ar); void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id); int ath10k_ce_disable_interrupts(struct ath10k *ar); -int ath10k_ce_enable_err_irq(struct ath10k *ar); /* ce_attr.flags values */ /* Use NonSnooping PCIe accesses? */ diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 05e6f8b..bd67311 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -919,13 +919,6 @@ static void ath10k_pci_stop_ce(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci_compl *compl; struct sk_buff *skb; - int ret; - - ret = ath10k_ce_disable_interrupts(ar); - if (ret) - ath10k_warn("failed to disable CE interrupts: %d\n", ret); - - ath10k_pci_kill_tasklet(ar); /* Mark pending completions as aborted, so that upper layers free up * their associated resources */ @@ -1236,10 +1229,17 @@ static int ath10k_pci_hif_start(struct ath10k *ar) return ret; } + ret = ath10k_pci_request_irq(ar); + if (ret) { + ath10k_warn("failed to post RX buffers for all pipes: %d\n", + ret); + goto err_free_compl; + } + ret = ath10k_pci_setup_ce_irq(ar); if (ret) { ath10k_warn("failed to setup CE interrupts: %d\n", ret); - goto err_free_compl; + goto err_stop; } /* Post buffers once to start things off. */ @@ -1247,13 +1247,16 @@ static int ath10k_pci_hif_start(struct ath10k *ar) if (ret) { ath10k_warn("failed to post RX buffers for all pipes: %d\n", ret); - goto err_stop_ce; + goto err_stop; } ar_pci->started = 1; return 0; -err_stop_ce: +err_stop: + ath10k_ce_disable_interrupts(ar); + ath10k_pci_free_irq(ar); + ath10k_pci_kill_tasklet(ar); ath10k_pci_stop_ce(ar); ath10k_pci_process_ce(ar); err_free_compl: @@ -1376,25 +1379,19 @@ static void ath10k_pci_ce_deinit(struct ath10k *ar) } } -static void ath10k_pci_disable_irqs(struct ath10k *ar) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int i; - - for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) - disable_irq(ar_pci->pdev->irq + i); -} - static void ath10k_pci_hif_stop(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); - /* Irqs are never explicitly re-enabled. They are implicitly re-enabled - * by upon power_up. */ - ath10k_pci_disable_irqs(ar); + ret = ath10k_ce_disable_interrupts(ar); + if (ret) + ath10k_warn("failed to disable CE interrupts: %d\n", ret); + ath10k_pci_free_irq(ar); + ath10k_pci_kill_tasklet(ar); ath10k_pci_stop_ce(ar); /* At this point, asynchronous threads are stopped, the target should @@ -1945,34 +1942,22 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) goto err_ce; } - ret = ath10k_pci_request_irq(ar); - if (ret) { - ath10k_err("failed to request irqs: %d\n", ret); - goto err_deinit_irq; - } - ret = ath10k_pci_wait_for_target_init(ar); if (ret) { ath10k_err("failed to wait for target to init: %d\n", ret); - goto err_free_irq; - } - - ret = ath10k_ce_enable_err_irq(ar); - if (ret) { - ath10k_err("failed to enable CE error irq: %d\n", ret); - goto err_free_irq; + goto err_deinit_irq; } ret = ath10k_pci_init_config(ar); if (ret) { ath10k_err("failed to setup init config: %d\n", ret); - goto err_free_irq; + goto err_deinit_irq; } ret = ath10k_pci_wake_target_cpu(ar); if (ret) { ath10k_err("could not wake up target CPU: %d\n", ret); - goto err_free_irq; + goto err_deinit_irq; } if (ar_pci->num_msi_intrs > 1) @@ -1987,14 +1972,11 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) return 0; -err_free_irq: - ath10k_pci_free_irq(ar); - ath10k_pci_kill_tasklet(ar); - ath10k_pci_device_reset(ar); err_deinit_irq: ath10k_pci_deinit_irq(ar); err_ce: ath10k_pci_ce_deinit(ar); + ath10k_pci_device_reset(ar); err_ps: if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) ath10k_do_pci_sleep(ar); @@ -2006,7 +1988,6 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - ath10k_pci_free_irq(ar); ath10k_pci_deinit_irq(ar); ath10k_pci_device_reset(ar); -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 6/8] ath10k: extract functions for legacy irq handling 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (4 preceding siblings ...) 2013-11-25 13:06 ` [PATCH v2 5/8] ath10k: defer irq registration until hif start() Michal Kazior @ 2013-11-25 13:06 ` Michal Kazior 2013-11-25 13:06 ` [PATCH v2 7/8] ath10k: re-add support for early fw indication Michal Kazior ` (2 subsequent siblings) 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-25 13:06 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior Preparation for code re-use. Also use ioread/write wrappers. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- v2: * move function definitions up to avoid forward declarations in subsequent patches drivers/net/wireless/ath/ath10k/pci.c | 65 +++++++++++++++++------------------ 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index bd67311..1d2939c 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -220,6 +220,34 @@ static bool ath10k_pci_irq_pending(struct ath10k *ar) return false; } +static void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar) +{ + /* IMPORTANT: INTR_CLR register has to be set after + * INTR_ENABLE is set to 0, otherwise interrupt can not be + * really cleared. */ + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, + 0); + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_CLR_ADDRESS, + PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); + + /* IMPORTANT: this extra read transaction is required to + * flush the posted write buffer. */ + (void) ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS); +} + +static void ath10k_pci_enable_legacy_irq(struct ath10k *ar) +{ + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS, + PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); + + /* IMPORTANT: this extra read transaction is required to + * flush the posted write buffer. */ + (void) ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS); +} + /* * Diagnostic read/write access is provided for startup/config/debug usage. * Caller must guarantee proper alignment, when applicable, and single user @@ -2128,25 +2156,7 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) if (!ath10k_pci_irq_pending(ar)) return IRQ_NONE; - /* - * IMPORTANT: INTR_CLR regiser has to be set after - * INTR_ENABLE is set to 0, otherwise interrupt can not be - * really cleared. - */ - iowrite32(0, ar_pci->mem + - (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - iowrite32(PCIE_INTR_FIRMWARE_MASK | - PCIE_INTR_CE_MASK_ALL, - ar_pci->mem + (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_CLR_ADDRESS)); - /* - * IMPORTANT: this extra read transaction is required to - * flush the posted write buffer. - */ - (void) ioread32(ar_pci->mem + - (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); + ath10k_pci_disable_and_clear_legacy_irq(ar); } tasklet_schedule(&ar_pci->intr_tq); @@ -2162,20 +2172,9 @@ static void ath10k_pci_tasklet(unsigned long data) ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */ ath10k_ce_per_engine_service_any(ar); - if (ar_pci->num_msi_intrs == 0) { - /* Enable Legacy PCI line interrupts */ - iowrite32(PCIE_INTR_FIRMWARE_MASK | - PCIE_INTR_CE_MASK_ALL, - ar_pci->mem + (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - /* - * IMPORTANT: this extra read transaction is required to - * flush the posted write buffer - */ - (void) ioread32(ar_pci->mem + - (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - } + /* Re-enable legacy irq that was disabled in the irq handler */ + if (ar_pci->num_msi_intrs == 0) + ath10k_pci_enable_legacy_irq(ar); } static int ath10k_pci_request_irq_msix(struct ath10k *ar) -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 7/8] ath10k: re-add support for early fw indication 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (5 preceding siblings ...) 2013-11-25 13:06 ` [PATCH v2 6/8] ath10k: extract functions for legacy irq handling Michal Kazior @ 2013-11-25 13:06 ` Michal Kazior 2013-11-25 13:06 ` [PATCH v2 8/8] ath10k: allow explicit MSI/MSI-X disabling Michal Kazior 2013-11-27 14:48 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Kalle Valo 8 siblings, 0 replies; 24+ messages in thread From: Michal Kazior @ 2013-11-25 13:06 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior It's possible for FW to panic during early boot. The patch re-introduces support to detect and print those crashes. This introduces an additional irq handler that is set for the duration of early boot and shutdown. The handler is then overriden with regular handlers upon hif start(). Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- v2: * move function definitions up to avoid forward declarations * forward declarations are gone now * rename ret2 to ret_early drivers/net/wireless/ath/ath10k/pci.c | 106 ++++++++++++++++++++++++++++++++-- drivers/net/wireless/ath/ath10k/pci.h | 1 + 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 1d2939c..0a2d1c2 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -248,6 +248,46 @@ static void ath10k_pci_enable_legacy_irq(struct ath10k *ar) PCIE_INTR_ENABLE_ADDRESS); } +static irqreturn_t ath10k_pci_early_irq_handler(int irq, void *arg) +{ + struct ath10k *ar = arg; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + if (ar_pci->num_msi_intrs == 0) { + if (!ath10k_pci_irq_pending(ar)) + return IRQ_NONE; + + ath10k_pci_disable_and_clear_legacy_irq(ar); + } + + tasklet_schedule(&ar_pci->early_irq_tasklet); + + return IRQ_HANDLED; +} + +static int ath10k_pci_request_early_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + /* Regardless whether MSI-X/MSI/legacy irqs have been set up the first + * interrupt from irq vector is triggered in all cases for FW + * indication/errors */ + ret = request_irq(ar_pci->pdev->irq, ath10k_pci_early_irq_handler, + IRQF_SHARED, "ath10k_pci (early)", ar); + if (ret) { + ath10k_warn("failed to request early irq: %d\n", ret); + return ret; + } + + return 0; +} + +static void ath10k_pci_free_early_irq(struct ath10k *ar) +{ + free_irq(ath10k_pci_priv(ar)->pdev->irq, ar); +} + /* * Diagnostic read/write access is provided for startup/config/debug usage. * Caller must guarantee proper alignment, when applicable, and single user @@ -937,6 +977,7 @@ static void ath10k_pci_kill_tasklet(struct ath10k *ar) tasklet_kill(&ar_pci->intr_tq); tasklet_kill(&ar_pci->msi_fw_err); + tasklet_kill(&ar_pci->early_irq_tasklet); for (i = 0; i < CE_COUNT; i++) tasklet_kill(&ar_pci->pipe_info[i].intr); @@ -1249,12 +1290,15 @@ static int ath10k_pci_post_rx(struct ath10k *ar) static int ath10k_pci_hif_start(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ret; + int ret, ret_early; + + ath10k_pci_free_early_irq(ar); + ath10k_pci_kill_tasklet(ar); ret = ath10k_pci_alloc_compl(ar); if (ret) { ath10k_warn("failed to allocate CE completions: %d\n", ret); - return ret; + goto err_early_irq; } ret = ath10k_pci_request_irq(ar); @@ -1289,6 +1333,14 @@ err_stop: ath10k_pci_process_ce(ar); err_free_compl: ath10k_pci_cleanup_ce(ar); +err_early_irq: + /* Though there should be no interrupts (device was reset) + * power_down() expects the early IRQ to be installed as per the + * driver lifecycle. */ + ret_early = ath10k_pci_request_early_irq(ar); + if (ret_early) + ath10k_warn("failed to re-enable early irq: %d\n", ret_early); + return ret; } @@ -1422,6 +1474,10 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_pci_kill_tasklet(ar); ath10k_pci_stop_ce(ar); + ret = ath10k_pci_request_early_irq(ar); + if (ret) + ath10k_warn("failed to re-enable early irq: %d\n", ret); + /* At this point, asynchronous threads are stopped, the target should * not DMA nor interrupt. We process the leftovers and then free * everything else up. */ @@ -1970,22 +2026,28 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) goto err_ce; } + ret = ath10k_pci_request_early_irq(ar); + if (ret) { + ath10k_err("failed to request early irq: %d\n", ret); + goto err_deinit_irq; + } + ret = ath10k_pci_wait_for_target_init(ar); if (ret) { ath10k_err("failed to wait for target to init: %d\n", ret); - goto err_deinit_irq; + goto err_free_early_irq; } ret = ath10k_pci_init_config(ar); if (ret) { ath10k_err("failed to setup init config: %d\n", ret); - goto err_deinit_irq; + goto err_free_early_irq; } ret = ath10k_pci_wake_target_cpu(ar); if (ret) { ath10k_err("could not wake up target CPU: %d\n", ret); - goto err_deinit_irq; + goto err_free_early_irq; } if (ar_pci->num_msi_intrs > 1) @@ -2000,6 +2062,8 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) return 0; +err_free_early_irq: + ath10k_pci_free_early_irq(ar); err_deinit_irq: ath10k_pci_deinit_irq(ar); err_ce: @@ -2016,6 +2080,8 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + ath10k_pci_free_early_irq(ar); + ath10k_pci_kill_tasklet(ar); ath10k_pci_deinit_irq(ar); ath10k_pci_device_reset(ar); @@ -2164,6 +2230,34 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) return IRQ_HANDLED; } +static void ath10k_pci_early_irq_tasklet(unsigned long data) +{ + struct ath10k *ar = (struct ath10k *)data; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + u32 fw_ind; + int ret; + + ret = ath10k_pci_wake(ar); + if (ret) { + ath10k_warn("failed to wake target in early irq tasklet: %d\n", + ret); + return; + } + + fw_ind = ath10k_pci_read32(ar, ar_pci->fw_indicator_address); + if (fw_ind & FW_IND_EVENT_PENDING) { + ath10k_pci_write32(ar, ar_pci->fw_indicator_address, + fw_ind & ~FW_IND_EVENT_PENDING); + + /* Some structures are unavailable during early boot or at + * driver teardown so just print that the device has crashed. */ + ath10k_warn("device crashed - no diagnostics available\n"); + } + + ath10k_pci_sleep(ar); + ath10k_pci_enable_legacy_irq(ar); +} + static void ath10k_pci_tasklet(unsigned long data) { struct ath10k *ar = (struct ath10k *)data; @@ -2280,6 +2374,8 @@ static void ath10k_pci_init_irq_tasklets(struct ath10k *ar) tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long)ar); tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet, (unsigned long)ar); + tasklet_init(&ar_pci->early_irq_tasklet, ath10k_pci_early_irq_tasklet, + (unsigned long)ar); for (i = 0; i < CE_COUNT; i++) { ar_pci->pipe_info[i].ar_pci = ar_pci; diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 73a3d4e..a4f3203 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -198,6 +198,7 @@ struct ath10k_pci { struct tasklet_struct intr_tq; struct tasklet_struct msi_fw_err; + struct tasklet_struct early_irq_tasklet; int started; -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 8/8] ath10k: allow explicit MSI/MSI-X disabling 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (6 preceding siblings ...) 2013-11-25 13:06 ` [PATCH v2 7/8] ath10k: re-add support for early fw indication Michal Kazior @ 2013-11-25 13:06 ` Michal Kazior 2013-11-27 14:47 ` Kalle Valo 2013-11-27 14:48 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Kalle Valo 8 siblings, 1 reply; 24+ messages in thread From: Michal Kazior @ 2013-11-25 13:06 UTC (permalink / raw) To: ath10k; +Cc: linux-wireless, Michal Kazior This can be useful for testing and debugging. This introduces new ath10k_pci module parameter `irq_mode`. By default it is 0, meaning automatic irq mode (MSI-X as long as both target HW and host platform supports it). The parameter works on a best effort basis. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> --- v2: * merge 2 parameters into 1 * add info print if the mode is != auto drivers/net/wireless/ath/ath10k/pci.c | 47 +++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 0a2d1c2..9606b9b 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -33,10 +33,21 @@ #include "ce.h" #include "pci.h" +enum ath10k_pci_irq_mode { + ATH10K_PCI_IRQ_AUTO = 0, + ATH10K_PCI_IRQ_LEGACY = 1, + ATH10K_PCI_IRQ_MSI = 2, +}; + static unsigned int ath10k_target_ps; +static unsigned int ath10k_pci_irq_mode = ATH10K_PCI_IRQ_AUTO; + module_param(ath10k_target_ps, uint, 0644); MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option"); +module_param_named(irq_mode, ath10k_pci_irq_mode, uint, 0644); +MODULE_PARM_DESC(irq_mode, "0: ayto, 1: legacy, 2: msi (default: 0)"); + #define QCA988X_2_0_DEVICE_ID (0x003c) static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = { @@ -2387,27 +2398,37 @@ static void ath10k_pci_init_irq_tasklets(struct ath10k *ar) static int ath10k_pci_init_irq(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + bool msix_supported = test_bit(ATH10K_PCI_FEATURE_MSI_X, + ar_pci->features); int ret; ath10k_pci_init_irq_tasklets(ar); - if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features)) - goto msi; + if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_AUTO && + !test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) + ath10k_info("limiting irq mode to: %d\n", ath10k_pci_irq_mode); /* Try MSI-X */ - ar_pci->num_msi_intrs = MSI_NUM_REQUEST; - ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs); - if (ret == 0) - return 0; - if (ret > 0) - pci_disable_msi(ar_pci->pdev); + if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO && msix_supported) { + ar_pci->num_msi_intrs = MSI_NUM_REQUEST; + ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs); + if (ret == 0) + return 0; + if (ret > 0) + pci_disable_msi(ar_pci->pdev); + + /* fall-through */ + } -msi: /* Try MSI */ - ar_pci->num_msi_intrs = 1; - ret = pci_enable_msi(ar_pci->pdev); - if (ret == 0) - return 0; + if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_LEGACY) { + ar_pci->num_msi_intrs = 1; + ret = pci_enable_msi(ar_pci->pdev); + if (ret == 0) + return 0; + + /* fall-through */ + } /* Try legacy irq * -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH v2 8/8] ath10k: allow explicit MSI/MSI-X disabling 2013-11-25 13:06 ` [PATCH v2 8/8] ath10k: allow explicit MSI/MSI-X disabling Michal Kazior @ 2013-11-27 14:47 ` Kalle Valo 0 siblings, 0 replies; 24+ messages in thread From: Kalle Valo @ 2013-11-27 14:47 UTC (permalink / raw) To: Michal Kazior; +Cc: ath10k, linux-wireless Michal Kazior <michal.kazior@tieto.com> writes: > This can be useful for testing and debugging. > > This introduces new ath10k_pci module parameter > `irq_mode`. By default it is 0, meaning automatic > irq mode (MSI-X as long as both target HW and host > platform supports it). The parameter works on a > best effort basis. > > Signed-off-by: Michal Kazior <michal.kazior@tieto.com> [...] > +MODULE_PARM_DESC(irq_mode, "0: ayto, 1: legacy, 2: msi (default: 0)"); I fixed this typo ("ayto"). -- Kalle Valo ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior ` (7 preceding siblings ...) 2013-11-25 13:06 ` [PATCH v2 8/8] ath10k: allow explicit MSI/MSI-X disabling Michal Kazior @ 2013-11-27 14:48 ` Kalle Valo 8 siblings, 0 replies; 24+ messages in thread From: Kalle Valo @ 2013-11-27 14:48 UTC (permalink / raw) To: Michal Kazior; +Cc: ath10k, linux-wireless Michal Kazior <michal.kazior@tieto.com> writes: > Hi, > > ath10k didn't play well with other devices on a > shared irq line. This patchset fixes support for > shared legacy interrupts. > > Some rework was necessary because ath10k was using > disable_irq and CE irq masking (which is not > sufficient for shared interrupts). > > Since main irq handlers are now registered after > boot, BMI is now polling for CE updates. I haven't > observed any differences in boot speed. > > Also this plugs a leak I spotted during rework and > adds an option for testing different irq modes. > > v2: > * simplify ath10k_pci_irq_pending() > * combine memory leak fix patch with a > functionality decoupling (it's very closely > related) > * fix 'irq: nobody cared' > * change MSI/MSI-X disabling parameters > * some minor fixes & code shuffling to avoid > forward declarations > > > Michal Kazior (8): > ath10k: don't consume other's shared interrupts > ath10k: split up pci irq code > ath10k: don't use interrupts for BMI > ath10k: decouple ath10k_pci_start_ce() > ath10k: defer irq registration until hif start() > ath10k: extract functions for legacy irq handling > ath10k: re-add support for early fw indication > ath10k: allow explicit MSI/MSI-X disabling Thanks, all eight applied. -- Kalle Valo ^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2013-11-27 14:48 UTC | newest] Thread overview: 24+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-11-22 13:05 [PATCH 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior 2013-11-22 13:05 ` [PATCH 1/8] ath10k: don't consume other's shared interrupts Michal Kazior 2013-11-24 13:59 ` Kalle Valo 2013-11-22 13:05 ` [PATCH 2/8] ath10k: split up pci irq code Michal Kazior 2013-11-22 13:05 ` [PATCH 3/8] ath10k: fix memory leak on hif_start failpath Michal Kazior 2013-11-22 13:05 ` [PATCH 4/8] ath10k: don't use interrupts for BMI Michal Kazior 2013-11-22 13:05 ` [PATCH 5/8] ath10k: defer irq registration until hif start() Michal Kazior 2013-11-22 13:05 ` [PATCH 6/8] ath10k: extract functions for legacy irq handling Michal Kazior 2013-11-22 13:05 ` [PATCH 7/8] ath10k: re-add support for early fw indication Michal Kazior 2013-11-25 12:20 ` Kalle Valo 2013-11-25 12:46 ` Michal Kazior 2013-11-22 13:05 ` [PATCH 8/8] ath10k: allow explicit MSI/MSI-X disabling Michal Kazior 2013-11-25 12:01 ` Kalle Valo 2013-11-25 13:06 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Michal Kazior 2013-11-25 13:06 ` [PATCH v2 1/8] ath10k: don't consume other's shared interrupts Michal Kazior 2013-11-25 13:06 ` [PATCH v2 2/8] ath10k: split up pci irq code Michal Kazior 2013-11-25 13:06 ` [PATCH v2 3/8] ath10k: don't use interrupts for BMI Michal Kazior 2013-11-25 13:06 ` [PATCH v2 4/8] ath10k: decouple ath10k_pci_start_ce() Michal Kazior 2013-11-25 13:06 ` [PATCH v2 5/8] ath10k: defer irq registration until hif start() Michal Kazior 2013-11-25 13:06 ` [PATCH v2 6/8] ath10k: extract functions for legacy irq handling Michal Kazior 2013-11-25 13:06 ` [PATCH v2 7/8] ath10k: re-add support for early fw indication Michal Kazior 2013-11-25 13:06 ` [PATCH v2 8/8] ath10k: allow explicit MSI/MSI-X disabling Michal Kazior 2013-11-27 14:47 ` Kalle Valo 2013-11-27 14:48 ` [PATCH v2 0/8] ath10k: pci fixes 2013-11-22 Kalle Valo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox