forked from Minki/linux
ath10k: remove early irq handling
It's not really necessary to have a dedicated irq handler just for the sake of catching early fw crashes anymore. It is now safe to use one handler even during early stages of device boot up. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
This commit is contained in:
parent
ec5ba4d3b6
commit
5c771e7454
@ -266,46 +266,6 @@ static void ath10k_pci_enable_legacy_irq(struct ath10k *ar)
|
|||||||
PCIE_INTR_ENABLE_ADDRESS);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline const char *ath10k_pci_get_irq_method(struct ath10k *ar)
|
static inline const char *ath10k_pci_get_irq_method(struct ath10k *ar)
|
||||||
{
|
{
|
||||||
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
||||||
@ -948,7 +908,6 @@ static void ath10k_pci_kill_tasklet(struct ath10k *ar)
|
|||||||
|
|
||||||
tasklet_kill(&ar_pci->intr_tq);
|
tasklet_kill(&ar_pci->intr_tq);
|
||||||
tasklet_kill(&ar_pci->msi_fw_err);
|
tasklet_kill(&ar_pci->msi_fw_err);
|
||||||
tasklet_kill(&ar_pci->early_irq_tasklet);
|
|
||||||
|
|
||||||
for (i = 0; i < CE_COUNT; i++)
|
for (i = 0; i < CE_COUNT; i++)
|
||||||
tasklet_kill(&ar_pci->pipe_info[i].intr);
|
tasklet_kill(&ar_pci->pipe_info[i].intr);
|
||||||
@ -1158,20 +1117,10 @@ static void ath10k_pci_irq_enable(struct ath10k *ar)
|
|||||||
static int ath10k_pci_hif_start(struct ath10k *ar)
|
static int ath10k_pci_hif_start(struct ath10k *ar)
|
||||||
{
|
{
|
||||||
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
||||||
int ret, ret_early;
|
int ret;
|
||||||
|
|
||||||
ath10k_dbg(ATH10K_DBG_BOOT, "boot hif start\n");
|
ath10k_dbg(ATH10K_DBG_BOOT, "boot hif start\n");
|
||||||
|
|
||||||
ath10k_pci_free_early_irq(ar);
|
|
||||||
ath10k_pci_kill_tasklet(ar);
|
|
||||||
|
|
||||||
ret = ath10k_pci_request_irq(ar);
|
|
||||||
if (ret) {
|
|
||||||
ath10k_warn("failed to post RX buffers for all pipes: %d\n",
|
|
||||||
ret);
|
|
||||||
goto err_early_irq;
|
|
||||||
}
|
|
||||||
|
|
||||||
ath10k_pci_irq_enable(ar);
|
ath10k_pci_irq_enable(ar);
|
||||||
|
|
||||||
/* Post buffers once to start things off. */
|
/* Post buffers once to start things off. */
|
||||||
@ -1187,15 +1136,7 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
|
|||||||
|
|
||||||
err_stop:
|
err_stop:
|
||||||
ath10k_pci_irq_disable(ar);
|
ath10k_pci_irq_disable(ar);
|
||||||
ath10k_pci_free_irq(ar);
|
|
||||||
ath10k_pci_kill_tasklet(ar);
|
ath10k_pci_kill_tasklet(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;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1302,7 +1243,6 @@ static void ath10k_pci_ce_deinit(struct ath10k *ar)
|
|||||||
static void ath10k_pci_hif_stop(struct ath10k *ar)
|
static void ath10k_pci_hif_stop(struct ath10k *ar)
|
||||||
{
|
{
|
||||||
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
||||||
int ret;
|
|
||||||
|
|
||||||
ath10k_dbg(ATH10K_DBG_BOOT, "boot hif stop\n");
|
ath10k_dbg(ATH10K_DBG_BOOT, "boot hif stop\n");
|
||||||
|
|
||||||
@ -1310,17 +1250,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ath10k_pci_irq_disable(ar);
|
ath10k_pci_irq_disable(ar);
|
||||||
ath10k_pci_free_irq(ar);
|
|
||||||
ath10k_pci_kill_tasklet(ar);
|
ath10k_pci_kill_tasklet(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. */
|
|
||||||
|
|
||||||
ath10k_pci_buffer_cleanup(ar);
|
ath10k_pci_buffer_cleanup(ar);
|
||||||
|
|
||||||
/* Make the sure the device won't access any structures on the host by
|
/* Make the sure the device won't access any structures on the host by
|
||||||
@ -1806,28 +1736,19 @@ static int ath10k_pci_ce_init(struct ath10k *ar)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
|
static bool ath10k_pci_has_fw_crashed(struct ath10k *ar)
|
||||||
{
|
{
|
||||||
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
return ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS) &
|
||||||
u32 fw_indicator;
|
FW_IND_EVENT_PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
fw_indicator = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
|
static void ath10k_pci_fw_crashed_clear(struct ath10k *ar)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
if (fw_indicator & FW_IND_EVENT_PENDING) {
|
val = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
|
||||||
/* ACK: clear Target-side pending event */
|
val &= ~FW_IND_EVENT_PENDING;
|
||||||
ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS,
|
ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, val);
|
||||||
fw_indicator & ~FW_IND_EVENT_PENDING);
|
|
||||||
|
|
||||||
if (ar_pci->started) {
|
|
||||||
ath10k_pci_fw_crashed_dump(ar);
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Probable Target failure before we're prepared
|
|
||||||
* to handle it. Generally unexpected.
|
|
||||||
*/
|
|
||||||
ath10k_warn("early firmware event indicated\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this function effectively clears target memory controller assert line */
|
/* this function effectively clears target memory controller assert line */
|
||||||
@ -1960,34 +1881,26 @@ static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ath10k_pci_request_early_irq(ar);
|
|
||||||
if (ret) {
|
|
||||||
ath10k_err("failed to request early irq: %d\n", ret);
|
|
||||||
goto err_ce;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = ath10k_pci_wait_for_target_init(ar);
|
ret = ath10k_pci_wait_for_target_init(ar);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ath10k_err("failed to wait for target to init: %d\n", ret);
|
ath10k_err("failed to wait for target to init: %d\n", ret);
|
||||||
goto err_free_early_irq;
|
goto err_ce;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ath10k_pci_init_config(ar);
|
ret = ath10k_pci_init_config(ar);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ath10k_err("failed to setup init config: %d\n", ret);
|
ath10k_err("failed to setup init config: %d\n", ret);
|
||||||
goto err_free_early_irq;
|
goto err_ce;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ath10k_pci_wake_target_cpu(ar);
|
ret = ath10k_pci_wake_target_cpu(ar);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ath10k_err("could not wake up target CPU: %d\n", ret);
|
ath10k_err("could not wake up target CPU: %d\n", ret);
|
||||||
goto err_free_early_irq;
|
goto err_ce;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_early_irq:
|
|
||||||
ath10k_pci_free_early_irq(ar);
|
|
||||||
err_ce:
|
err_ce:
|
||||||
ath10k_pci_ce_deinit(ar);
|
ath10k_pci_ce_deinit(ar);
|
||||||
ath10k_pci_warm_reset(ar);
|
ath10k_pci_warm_reset(ar);
|
||||||
@ -2056,8 +1969,6 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar)
|
|||||||
{
|
{
|
||||||
ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power down\n");
|
ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power down\n");
|
||||||
|
|
||||||
ath10k_pci_free_early_irq(ar);
|
|
||||||
ath10k_pci_kill_tasklet(ar);
|
|
||||||
ath10k_pci_warm_reset(ar);
|
ath10k_pci_warm_reset(ar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2140,7 +2051,13 @@ static void ath10k_msi_err_tasklet(unsigned long data)
|
|||||||
{
|
{
|
||||||
struct ath10k *ar = (struct ath10k *)data;
|
struct ath10k *ar = (struct ath10k *)data;
|
||||||
|
|
||||||
ath10k_pci_fw_interrupt_handler(ar);
|
if (!ath10k_pci_has_fw_crashed(ar)) {
|
||||||
|
ath10k_warn("received unsolicited fw crash interrupt\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ath10k_pci_fw_crashed_clear(ar);
|
||||||
|
ath10k_pci_fw_crashed_dump(ar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2201,27 +2118,17 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg)
|
|||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ath10k_pci_early_irq_tasklet(unsigned long data)
|
|
||||||
{
|
|
||||||
struct ath10k *ar = (struct ath10k *)data;
|
|
||||||
u32 fw_ind;
|
|
||||||
|
|
||||||
fw_ind = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
|
|
||||||
if (fw_ind & FW_IND_EVENT_PENDING) {
|
|
||||||
ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS,
|
|
||||||
fw_ind & ~FW_IND_EVENT_PENDING);
|
|
||||||
ath10k_pci_fw_crashed_dump(ar);
|
|
||||||
}
|
|
||||||
|
|
||||||
ath10k_pci_enable_legacy_irq(ar);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ath10k_pci_tasklet(unsigned long data)
|
static void ath10k_pci_tasklet(unsigned long data)
|
||||||
{
|
{
|
||||||
struct ath10k *ar = (struct ath10k *)data;
|
struct ath10k *ar = (struct ath10k *)data;
|
||||||
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
||||||
|
|
||||||
ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */
|
if (ath10k_pci_has_fw_crashed(ar)) {
|
||||||
|
ath10k_pci_fw_crashed_clear(ar);
|
||||||
|
ath10k_pci_fw_crashed_dump(ar);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ath10k_ce_per_engine_service_any(ar);
|
ath10k_ce_per_engine_service_any(ar);
|
||||||
|
|
||||||
/* Re-enable legacy irq that was disabled in the irq handler */
|
/* Re-enable legacy irq that was disabled in the irq handler */
|
||||||
@ -2332,8 +2239,6 @@ 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->intr_tq, ath10k_pci_tasklet, (unsigned long)ar);
|
||||||
tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet,
|
tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet,
|
||||||
(unsigned long)ar);
|
(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++) {
|
for (i = 0; i < CE_COUNT; i++) {
|
||||||
ar_pci->pipe_info[i].ar_pci = ar_pci;
|
ar_pci->pipe_info[i].ar_pci = ar_pci;
|
||||||
@ -2459,8 +2364,7 @@ static int ath10k_pci_wait_for_target_init(struct ath10k *ar)
|
|||||||
|
|
||||||
if (val & FW_IND_EVENT_PENDING) {
|
if (val & FW_IND_EVENT_PENDING) {
|
||||||
ath10k_warn("device has crashed during init\n");
|
ath10k_warn("device has crashed during init\n");
|
||||||
ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS,
|
ath10k_pci_fw_crashed_clear(ar);
|
||||||
val & ~FW_IND_EVENT_PENDING);
|
|
||||||
ath10k_pci_fw_crashed_dump(ar);
|
ath10k_pci_fw_crashed_dump(ar);
|
||||||
return -ECOMM;
|
return -ECOMM;
|
||||||
}
|
}
|
||||||
@ -2643,6 +2547,13 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
|
|||||||
goto err_free_ce;
|
goto err_free_ce;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Workaround: There's no known way to mask all possible interrupts via
|
||||||
|
* device CSR. The only way to make sure device doesn't assert
|
||||||
|
* interrupts is to reset it. Interrupts are then disabled on host
|
||||||
|
* after handlers are registered.
|
||||||
|
*/
|
||||||
|
ath10k_pci_warm_reset(ar);
|
||||||
|
|
||||||
ret = ath10k_pci_init_irq(ar);
|
ret = ath10k_pci_init_irq(ar);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ath10k_err("failed to init irqs: %d\n", ret);
|
ath10k_err("failed to init irqs: %d\n", ret);
|
||||||
@ -2653,14 +2564,26 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
|
|||||||
ath10k_pci_get_irq_method(ar), ar_pci->num_msi_intrs,
|
ath10k_pci_get_irq_method(ar), ar_pci->num_msi_intrs,
|
||||||
ath10k_pci_irq_mode, ath10k_pci_reset_mode);
|
ath10k_pci_irq_mode, ath10k_pci_reset_mode);
|
||||||
|
|
||||||
ret = ath10k_core_register(ar, chip_id);
|
ret = ath10k_pci_request_irq(ar);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ath10k_err("failed to register driver core: %d\n", ret);
|
ath10k_warn("failed to request irqs: %d\n", ret);
|
||||||
goto err_deinit_irq;
|
goto err_deinit_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This shouldn't race as the device has been reset above. */
|
||||||
|
ath10k_pci_irq_disable(ar);
|
||||||
|
|
||||||
|
ret = ath10k_core_register(ar, chip_id);
|
||||||
|
if (ret) {
|
||||||
|
ath10k_err("failed to register driver core: %d\n", ret);
|
||||||
|
goto err_free_irq;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_free_irq:
|
||||||
|
ath10k_pci_free_irq(ar);
|
||||||
|
|
||||||
err_deinit_irq:
|
err_deinit_irq:
|
||||||
ath10k_pci_deinit_irq(ar);
|
ath10k_pci_deinit_irq(ar);
|
||||||
|
|
||||||
@ -2695,6 +2618,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ath10k_core_unregister(ar);
|
ath10k_core_unregister(ar);
|
||||||
|
ath10k_pci_free_irq(ar);
|
||||||
ath10k_pci_deinit_irq(ar);
|
ath10k_pci_deinit_irq(ar);
|
||||||
ath10k_pci_ce_deinit(ar);
|
ath10k_pci_ce_deinit(ar);
|
||||||
ath10k_pci_free_ce(ar);
|
ath10k_pci_free_ce(ar);
|
||||||
|
@ -166,7 +166,6 @@ struct ath10k_pci {
|
|||||||
|
|
||||||
struct tasklet_struct intr_tq;
|
struct tasklet_struct intr_tq;
|
||||||
struct tasklet_struct msi_fw_err;
|
struct tasklet_struct msi_fw_err;
|
||||||
struct tasklet_struct early_irq_tasklet;
|
|
||||||
|
|
||||||
int started;
|
int started;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user