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);
|
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);
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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,
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user