qcacmn: disable all apps CE irqs except wake_irq in bus suspend

In Moselle, currently CE interrupts are not disabled from apps
side during ipci bus suspend, so adding changes to disable all
the CE interrupts except wake_irq during bus suspend and do the
symmetric inverse operation during bus resume, also drain all
the pending FW diag logs from copy engine.

Change-Id: Ib54fc6660fd81aff18787b0b699f3a6cd2d7803d
CRs-Fixed: 2879752
This commit is contained in:
Vevek Venkatesan
2021-02-19 02:41:47 +05:30
committed by snandini
parent 4adab0e0a9
commit 17660198d7
7 changed files with 205 additions and 12 deletions

View File

@@ -1306,6 +1306,28 @@ int hif_apps_enable_irq_wake(struct hif_opaque_softc *hif_ctx);
*/ */
int hif_apps_disable_irq_wake(struct hif_opaque_softc *hif_ctx); int hif_apps_disable_irq_wake(struct hif_opaque_softc *hif_ctx);
/**
* hif_apps_enable_irqs_except_wake_irq() - Enables all irqs except wake_irq
* @hif_ctx: an opaque HIF handle to use
*
* As opposed to the standard hif_irq_enable, this function always applies to
* the APPS side kernel interrupt handling.
*
* Return: errno
*/
int hif_apps_enable_irqs_except_wake_irq(struct hif_opaque_softc *hif_ctx);
/**
* hif_apps_disable_irqs_except_wake_irq() - Disables all irqs except wake_irq
* @hif_ctx: an opaque HIF handle to use
*
* As opposed to the standard hif_irq_disable, this function always applies to
* the APPS side kernel interrupt handling.
*
* Return: errno
*/
int hif_apps_disable_irqs_except_wake_irq(struct hif_opaque_softc *hif_ctx);
#ifdef FEATURE_RUNTIME_PM #ifdef FEATURE_RUNTIME_PM
int hif_pre_runtime_suspend(struct hif_opaque_softc *hif_ctx); int hif_pre_runtime_suspend(struct hif_opaque_softc *hif_ctx);
void hif_pre_runtime_resume(struct hif_opaque_softc *hif_ctx); void hif_pre_runtime_resume(struct hif_opaque_softc *hif_ctx);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved.
* *
* Permission to use, copy, modify, and/or distribute this software for * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -552,6 +552,15 @@ static inline void ce_t2h_msg_ce_cleanup(struct CE_handle *ce_hdl)
*/ */
int hif_get_wake_ce_id(struct hif_softc *scn, uint8_t *ce_id); int hif_get_wake_ce_id(struct hif_softc *scn, uint8_t *ce_id);
/**
* hif_get_fw_diag_ce_id() - gets the copy engine id used for FW diag
* @scn: The hif context to use
* @ce_id: a pointer where the copy engine Id should be populated
*
* Return: errno
*/
int hif_get_fw_diag_ce_id(struct hif_softc *scn, uint8_t *ce_id);
#if defined(HIF_CONFIG_SLUB_DEBUG_ON) || defined(HIF_CE_DEBUG_DATA_BUF) #if defined(HIF_CONFIG_SLUB_DEBUG_ON) || defined(HIF_CE_DEBUG_DATA_BUF)
#ifndef HIF_CE_HISTORY_MAX #ifndef HIF_CE_HISTORY_MAX

View File

@@ -4480,6 +4480,27 @@ int hif_get_wake_ce_id(struct hif_softc *scn, uint8_t *ce_id)
return 0; return 0;
} }
int hif_get_fw_diag_ce_id(struct hif_softc *scn, uint8_t *ce_id)
{
int status;
uint8_t ul_pipe, dl_pipe;
int ul_is_polled, dl_is_polled;
/* DL pipe for WMI_CONTROL_DIAG_SVC should map to the FW DIAG CE_ID */
status = hif_map_service_to_pipe(GET_HIF_OPAQUE_HDL(scn),
WMI_CONTROL_DIAG_SVC,
&ul_pipe, &dl_pipe,
&ul_is_polled, &dl_is_polled);
if (status) {
hif_err("Failed to map pipe: %d", status);
return status;
}
*ce_id = dl_pipe;
return 0;
}
#ifdef HIF_CE_LOG_INFO #ifdef HIF_CE_LOG_INFO
/** /**
* ce_get_index_info(): Get CE index info * ce_get_index_info(): Get CE index info

View File

@@ -612,6 +612,64 @@ static inline bool hif_tasklet_schedule(struct hif_opaque_softc *hif_ctx,
return true; return true;
} }
/**
* ce_poll_reap_by_id() - reap the available frames from CE by polling per ce_id
* @scn: hif context
* @ce_id: CE id
*
* This function needs to be called once after all the irqs are disabled
* and tasklets are drained during bus suspend.
*
* Return: 0 on success, unlikely -EBUSY if reaping goes infinite loop
*/
static int ce_poll_reap_by_id(struct hif_softc *scn, enum ce_id_type ce_id)
{
struct HIF_CE_state *hif_ce_state = (struct HIF_CE_state *)scn;
struct CE_state *CE_state = scn->ce_id_to_state[ce_id];
if (scn->ce_latency_stats)
hif_record_tasklet_exec_entry_ts(scn, ce_id);
hif_record_ce_desc_event(scn, ce_id, HIF_CE_REAP_ENTRY,
NULL, NULL, -1, 0);
ce_per_engine_service(scn, ce_id);
/*
* In an unlikely case, if frames are still pending to reap,
* could be an infinite loop, so return -EBUSY.
*/
if (ce_check_rx_pending(CE_state))
return -EBUSY;
hif_record_ce_desc_event(scn, ce_id, HIF_CE_REAP_EXIT,
NULL, NULL, -1, 0);
if (scn->ce_latency_stats)
ce_tasklet_update_bucket(hif_ce_state, ce_id);
return 0;
}
/**
* hif_drain_fw_diag_ce() - reap all the available FW diag logs from CE
* @scn: hif context
*
* This function needs to be called once after all the irqs are disabled
* and tasklets are drained during bus suspend.
*
* Return: 0 on success, unlikely -EBUSY if reaping goes infinite loop
*/
int hif_drain_fw_diag_ce(struct hif_softc *scn)
{
uint8_t ce_id;
if (hif_get_fw_diag_ce_id(scn, &ce_id))
return 0;
return ce_poll_reap_by_id(scn, ce_id);
}
/** /**
* ce_dispatch_interrupt() - dispatch an interrupt to a processing context * ce_dispatch_interrupt() - dispatch an interrupt to a processing context
* @ce_id: ce_id * @ce_id: ce_id

View File

@@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2015-2016,2018,2020 The Linux Foundation. All rights reserved. * Copyright (c) 2015-2016,2018,2020-2021 The Linux Foundation.
* All rights reserved.
* *
* Permission to use, copy, modify, and/or distribute this software for * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -24,6 +25,7 @@ void deinit_tasklet_workers(struct hif_opaque_softc *scn);
void ce_tasklet_init(struct HIF_CE_state *hif_ce_state, uint32_t mask); void ce_tasklet_init(struct HIF_CE_state *hif_ce_state, uint32_t mask);
void ce_tasklet_kill(struct hif_softc *scn); void ce_tasklet_kill(struct hif_softc *scn);
int hif_drain_tasklets(struct hif_softc *scn); int hif_drain_tasklets(struct hif_softc *scn);
int hif_drain_fw_diag_ce(struct hif_softc *scn);
QDF_STATUS ce_register_irq(struct HIF_CE_state *hif_ce_state, uint32_t mask); QDF_STATUS ce_register_irq(struct HIF_CE_state *hif_ce_state, uint32_t mask);
QDF_STATUS ce_unregister_irq(struct HIF_CE_state *hif_ce_state, uint32_t mask); QDF_STATUS ce_unregister_irq(struct HIF_CE_state *hif_ce_state, uint32_t mask);
irqreturn_t ce_dispatch_interrupt(int irq, irqreturn_t ce_dispatch_interrupt(int irq,

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016-2018, 2020 The Linux Foundation. All rights reserved. * Copyright (c) 2016-2018, 2020-2021 The Linux Foundation. All rights reserved.
* *
* Permission to use, copy, modify, and/or distribute this software for * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -555,6 +555,46 @@ int hif_apps_enable_irq_wake(struct hif_opaque_softc *hif_ctx)
return enable_irq_wake(scn->wake_irq); return enable_irq_wake(scn->wake_irq);
} }
int hif_apps_disable_irqs_except_wake_irq(struct hif_opaque_softc *hif_ctx)
{
struct hif_softc *scn;
int i;
QDF_BUG(hif_ctx);
scn = HIF_GET_SOFTC(hif_ctx);
if (!scn)
return -EINVAL;
for (i = 0; i < scn->ce_count; ++i) {
int irq = scn->bus_ops.hif_map_ce_to_irq(scn, i);
if (irq != scn->wake_irq)
disable_irq(irq);
}
return 0;
}
int hif_apps_enable_irqs_except_wake_irq(struct hif_opaque_softc *hif_ctx)
{
struct hif_softc *scn;
int i;
QDF_BUG(hif_ctx);
scn = HIF_GET_SOFTC(hif_ctx);
if (!scn)
return -EINVAL;
for (i = 0; i < scn->ce_count; ++i) {
int irq = scn->bus_ops.hif_map_ce_to_irq(scn, i);
if (irq != scn->wake_irq)
enable_irq(irq);
}
return 0;
}
#ifdef WLAN_FEATURE_BMI #ifdef WLAN_FEATURE_BMI
bool hif_needs_bmi(struct hif_opaque_softc *scn) bool hif_needs_bmi(struct hif_opaque_softc *scn)
{ {

View File

@@ -307,29 +307,70 @@ int hif_ipci_bus_suspend(struct hif_softc *scn)
{ {
int ret; int ret;
ret = hif_apps_disable_irqs_except_wake_irq(GET_HIF_OPAQUE_HDL(scn));
if (ret) {
hif_err("Failed to disable IRQs");
goto disable_irq_fail;
}
ret = hif_apps_enable_irq_wake(GET_HIF_OPAQUE_HDL(scn)); ret = hif_apps_enable_irq_wake(GET_HIF_OPAQUE_HDL(scn));
if (ret) {
hif_err("Failed to enable Wake-IRQ");
goto enable_wake_irq_fail;
}
if (!ret) if (QDF_IS_STATUS_ERROR(hif_try_complete_tasks(scn))) {
scn->bus_suspended = true; hif_err("hif_try_complete_tasks timed-out, so abort suspend");
ret = -EBUSY;
goto drain_tasks_fail;
}
/*
* In an unlikely case, if draining becomes infinite loop,
* it returns an error, shall abort the bus suspend.
*/
ret = hif_drain_fw_diag_ce(scn);
if (ret) {
hif_err("draining fw_diag_ce goes infinite, so abort suspend");
goto drain_tasks_fail;
}
scn->bus_suspended = true;
return 0;
drain_tasks_fail:
hif_apps_disable_irq_wake(GET_HIF_OPAQUE_HDL(scn));
enable_wake_irq_fail:
hif_apps_enable_irqs_except_wake_irq(GET_HIF_OPAQUE_HDL(scn));
disable_irq_fail:
return ret; return ret;
} }
int hif_ipci_bus_resume(struct hif_softc *scn) int hif_ipci_bus_resume(struct hif_softc *scn)
{ {
int ret = 0;
ret = hif_apps_disable_irq_wake(GET_HIF_OPAQUE_HDL(scn));
if (ret) {
hif_err("Failed to disable Wake-IRQ");
goto fail;
}
ret = hif_apps_enable_irqs_except_wake_irq(GET_HIF_OPAQUE_HDL(scn));
if (ret)
hif_err("Failed to enable IRQs");
scn->bus_suspended = false; scn->bus_suspended = false;
return hif_apps_disable_irq_wake(GET_HIF_OPAQUE_HDL(scn)); fail:
return ret;
} }
int hif_ipci_bus_suspend_noirq(struct hif_softc *scn) int hif_ipci_bus_suspend_noirq(struct hif_softc *scn)
{ {
QDF_STATUS ret;
ret = hif_try_complete_tasks(scn);
if (QDF_IS_STATUS_ERROR(ret))
return -EBUSY;
return 0; return 0;
} }