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:

committed by
snandini

parent
4adab0e0a9
commit
17660198d7
@@ -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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
int hif_pre_runtime_suspend(struct hif_opaque_softc *hif_ctx);
|
||||
void hif_pre_runtime_resume(struct hif_opaque_softc *hif_ctx);
|
||||
|
@@ -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
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
||||
#ifndef HIF_CE_HISTORY_MAX
|
||||
|
@@ -4480,6 +4480,27 @@ int hif_get_wake_ce_id(struct hif_softc *scn, uint8_t *ce_id)
|
||||
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
|
||||
/**
|
||||
* ce_get_index_info(): Get CE index info
|
||||
|
@@ -612,6 +612,64 @@ static inline bool hif_tasklet_schedule(struct hif_opaque_softc *hif_ctx,
|
||||
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_id: ce_id
|
||||
|
@@ -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
|
||||
* 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_kill(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_unregister_irq(struct HIF_CE_state *hif_ce_state, uint32_t mask);
|
||||
irqreturn_t ce_dispatch_interrupt(int irq,
|
||||
|
@@ -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
|
||||
* 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);
|
||||
}
|
||||
|
||||
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
|
||||
bool hif_needs_bmi(struct hif_opaque_softc *scn)
|
||||
{
|
||||
|
@@ -307,29 +307,70 @@ int hif_ipci_bus_suspend(struct hif_softc *scn)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hif_apps_enable_irq_wake(GET_HIF_OPAQUE_HDL(scn));
|
||||
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));
|
||||
if (ret) {
|
||||
hif_err("Failed to enable Wake-IRQ");
|
||||
goto enable_wake_irq_fail;
|
||||
}
|
||||
|
||||
if (QDF_IS_STATUS_ERROR(hif_try_complete_tasks(scn))) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return hif_apps_disable_irq_wake(GET_HIF_OPAQUE_HDL(scn));
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user