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

Commit fc15ca13 authored by Michal Kazior's avatar Michal Kazior Committed by Kalle Valo
Browse files

ath10k: split up pci irq code



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: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent e539887b
Loading
Loading
Loading
Loading
+131 −94
Original line number Diff line number Diff line
@@ -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,39 +2220,46 @@ 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);
@@ -2265,58 +2268,92 @@ static int ath10k_pci_start_intr(struct ath10k *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);
	/* 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)
			goto exit;
		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;

	/* 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;

	ret = ath10k_pci_wake(ar);
	if (ret) {
		ath10k_warn("failed to wake target: %d\n", ret);
		return ret;
	}

	if (num == 1) {
		ret = ath10k_pci_start_intr_msi(ar);
		if (ret == 0)
			goto exit;
	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);

		ath10k_dbg(ATH10K_DBG_BOOT,
			   "MSI didn't succeed (%d), trying legacy INTR\n",
			   ret);
		num = 0;
	return 0;
}

	ret = ath10k_pci_start_intr_legacy(ar);
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)