qcacmn: Add Wake MSI support

Using the same MSI for wake and copy engines has a number of negative
side effects. Instead, set aside a whole MSI just for the wake
notification from firmware.

Change-Id: If871b88ca255ad970dabb4a7773d2b4d3b71c3fe
CRs-Fixed: 2055359
This commit is contained in:
Dustin Brown 2017-05-30 16:14:01 -07:00 • committed by snandini
commit 2af3d67336
5 changed files with 104 additions and 85 deletions

View file

@ -439,7 +439,6 @@ void hif_set_bundle_mode(struct hif_opaque_softc *scn, bool enabled,
* Return: int 0 for success, non zero for failure
*/
int hif_bus_reset_resume(struct hif_opaque_softc *scn)
{
struct hif_softc *hif_sc = HIF_GET_SOFTC(scn);
@ -451,14 +450,19 @@ int hif_apps_irqs_disable(struct hif_opaque_softc *hif_ctx)
struct hif_softc *scn;
int i;
QDF_BUG(hif_ctx);
scn = HIF_GET_SOFTC(hif_ctx);
if (!scn) {
QDF_BUG(0);
if (!scn)
return -EINVAL;
}
for (i = 0; i < scn->ce_count; ++i)
disable_irq(scn->bus_ops.hif_map_ce_to_irq(scn, i));
/* if the wake_irq is shared, don't disable it twice */
disable_irq(scn->wake_irq);
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;
}
@ -468,60 +472,47 @@ int hif_apps_irqs_enable(struct hif_opaque_softc *hif_ctx)
struct hif_softc *scn;
int i;
QDF_BUG(hif_ctx);
scn = HIF_GET_SOFTC(hif_ctx);
if (!scn) {
QDF_BUG(0);
if (!scn)
return -EINVAL;
}
for (i = 0; i < scn->ce_count; ++i)
enable_irq(scn->bus_ops.hif_map_ce_to_irq(scn, i));
/* if the wake_irq is shared, don't enable it twice */
enable_irq(scn->wake_irq);
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;
}
int hif_apps_wake_irq_disable(struct hif_opaque_softc *hif_ctx)
{
int errno;
struct hif_softc *scn;
uint8_t wake_ce_id;
QDF_BUG(hif_ctx);
scn = HIF_GET_SOFTC(hif_ctx);
if (!scn) {
QDF_BUG(0);
if (!scn)
return -EINVAL;
}
errno = hif_get_wake_ce_id(scn, &wake_ce_id);
if (errno) {
HIF_ERROR("%s: failed to get wake CE Id: %d", __func__, errno);
return errno;
}
disable_irq(scn->bus_ops.hif_map_ce_to_irq(scn, wake_ce_id));
disable_irq(scn->wake_irq);
return 0;
}
int hif_apps_wake_irq_enable(struct hif_opaque_softc *hif_ctx)
{
int errno;
struct hif_softc *scn;
uint8_t wake_ce_id;
QDF_BUG(hif_ctx);
scn = HIF_GET_SOFTC(hif_ctx);
if (!scn) {
QDF_BUG(0);
if (!scn)
return -EINVAL;
}
errno = hif_get_wake_ce_id(scn, &wake_ce_id);
if (errno) {
HIF_ERROR("%s: failed to get wake CE Id: %d", __func__, errno);
return errno;
}
enable_irq(scn->bus_ops.hif_map_ce_to_irq(scn, wake_ce_id));
enable_irq(scn->wake_irq);
return 0;
}

View file

@ -1186,3 +1186,23 @@ void hif_ramdump_handler(struct hif_opaque_softc *scn)
}
#endif
#ifdef WLAN_SUSPEND_RESUME_TEST
irqreturn_t hif_wake_interrupt_handler(int irq, void *context)
{
struct hif_softc *scn = context;
HIF_INFO("wake interrupt received on irq %d", irq);
if (hif_is_ut_suspended(scn))
hif_ut_fw_resume(scn);
return IRQ_HANDLED;
}
#else /* WLAN_SUSPEND_RESUME_TEST */
irqreturn_t hif_wake_interrupt_handler(int irq, void *context)
{
HIF_INFO("wake interrupt received on irq %d", irq);
return IRQ_HANDLED;
}
#endif /* WLAN_SUSPEND_RESUME_TEST */

View file

@ -167,6 +167,7 @@ struct hif_softc {
void *hal_soc;
struct hif_ut_suspend_context ut_suspend_ctx;
uint32_t hif_attribute;
int wake_irq;
};
static inline void *hif_get_hal_handle(void *hif_hdl)
@ -247,4 +248,14 @@ void hif_ramdump_handler(struct hif_opaque_softc *scn);
static inline void hif_usb_get_hw_info(struct hif_softc *scn) {}
static inline void hif_ramdump_handler(struct hif_opaque_softc *scn) {}
#endif
/**
* hif_wake_interrupt_handler() - interrupt handler for standalone wake irq
* @irq: the irq number that fired
* @context: the opaque pointer passed to request_irq()
*
* Return: an irq return type
*/
irqreturn_t hif_wake_interrupt_handler(int irq, void *context);
#endif /* __HIF_MAIN_H__ */

View file

@ -2498,6 +2498,7 @@ static int hif_pci_configure_legacy_irq(struct hif_pci_softc *sc)
HIF_ERROR("%s: request_irq failed, ret = %d", __func__, ret);
goto end;
}
scn->wake_irq = sc->pdev->irq;
/* Use sc->irq instead of sc->pdev-irq
* platform_device pdev doesn't have an irq field
*/
@ -2732,15 +2733,8 @@ void hif_pci_prevent_linkdown(struct hif_softc *scn, bool flag)
static int hif_mark_wake_irq_wakeable(struct hif_softc *scn)
{
int errno;
uint8_t ce_id;
errno = hif_get_wake_ce_id(scn, &ce_id);
if (errno) {
HIF_ERROR("%s: failed to get wake CE Id: %d", __func__, errno);
return errno;
}
errno = enable_irq_wake(scn->bus_ops.hif_map_ce_to_irq(scn, ce_id));
errno = enable_irq_wake(scn->wake_irq);
if (errno) {
HIF_ERROR("%s: Failed to mark wake IRQ: %d", __func__, errno);
return errno;
@ -2758,14 +2752,6 @@ static int hif_mark_wake_irq_wakeable(struct hif_softc *scn)
*/
int hif_pci_bus_suspend(struct hif_softc *scn)
{
/*
* This is a workaround to a spurious wake interrupt issue. The
* proper fix is being worked on, and this should be removed as
* soon as possible.
*/
HIF_ERROR("%s: Pausing bus suspend for 200ms", __func__);
msleep(200);
if (hif_can_suspend_link(GET_HIF_OPAQUE_HDL(scn)))
return 0;
@ -2815,15 +2801,8 @@ static int __hif_check_link_status(struct hif_softc *scn)
static int hif_unmark_wake_irq_wakeable(struct hif_softc *scn)
{
int errno;
uint8_t ce_id;
errno = hif_get_wake_ce_id(scn, &ce_id);
if (errno) {
HIF_ERROR("%s: failed to get wake CE Id: %d", __func__, errno);
return errno;
}
errno = disable_irq_wake(scn->bus_ops.hif_map_ce_to_irq(scn, ce_id));
errno = disable_irq_wake(scn->wake_irq);
if (errno) {
HIF_ERROR("%s: Failed to unmark wake IRQ: %d", __func__, errno);
return errno;
@ -3586,11 +3565,25 @@ static int hif_ce_msi_configure_irq(struct hif_softc *scn)
struct HIF_CE_state *ce_sc = HIF_GET_CE_STATE(scn);
struct hif_pci_softc *pci_sc = HIF_GET_PCI_SOFTC(scn);
/* do wake irq assignment */
ret = pld_get_user_msi_assignment(scn->qdf_dev->dev, "WAKE",
&msi_data_count, &msi_data_start,
&msi_irq_start);
if (ret)
return ret;
scn->wake_irq = pld_get_msi_irq(scn->qdf_dev->dev, msi_irq_start);
ret = request_irq(scn->wake_irq, hif_wake_interrupt_handler, 0,
"wlan_wake_irq", scn);
if (ret)
return ret;
/* do ce irq assignments */
ret = pld_get_user_msi_assignment(scn->qdf_dev->dev, "CE",
&msi_data_count, &msi_data_start,
&msi_irq_start);
if (ret)
return ret;
goto free_wake_irq;
if (ce_srng_based(scn)) {
scn->bus_ops.hif_irq_disable = &hif_ce_srng_msi_irq_disable;
@ -3638,6 +3631,11 @@ free_irq:
irq = pld_get_msi_irq(scn->qdf_dev->dev, msi_data);
free_irq(irq, &ce_sc->tasklets[ce_id]);
}
free_wake_irq:
free_irq(scn->wake_irq, scn->qdf_dev->dev);
scn->wake_irq = 0;
return ret;
}

View file

@ -37,6 +37,8 @@
#include "hif_io32.h"
#include "ce_main.h"
#include "ce_tasklet.h"
#include "ce_api.h"
#include "ce_internal.h"
#include "snoc_api.h"
#include <soc/qcom/icnss.h>
#include "pld_common.h"
@ -166,6 +168,7 @@ static QDF_STATUS hif_snoc_get_soc_info(struct hif_softc *scn)
int hif_snoc_bus_configure(struct hif_softc *scn)
{
int ret;
uint8_t wake_ce_id;
ret = hif_snoc_get_soc_info(scn);
if (ret)
@ -182,7 +185,25 @@ int hif_snoc_bus_configure(struct hif_softc *scn)
ret = hif_config_ce(scn);
if (ret)
hif_wlan_disable(scn);
goto wlan_disable;
ret = hif_get_wake_ce_id(scn, &wake_ce_id);
if (ret)
goto unconfig_ce;
scn->wake_irq = icnss_get_irq(wake_ce_id);
HIF_INFO(FL("expecting wake from ce %d, irq %d"),
wake_ce_id, scn->wake_irq);
return 0;
unconfig_ce:
hif_unconfig_ce(scn);
wlan_disable:
hif_wlan_disable(scn);
return ret;
}
@ -369,40 +390,18 @@ void hif_snoc_irq_disable(struct hif_softc *scn, int ce_id)
static
QDF_STATUS hif_snoc_setup_wakeup_sources(struct hif_softc *scn, bool enable)
{
struct hif_opaque_softc *hif_hdl = GET_HIF_OPAQUE_HDL(scn);
uint8_t ul_pipe, dl_pipe;
int ul_is_polled, dl_is_polled;
int irq_to_wake_on;
QDF_STATUS status;
int ret;
status = hif_map_service_to_pipe(hif_hdl, HTC_CTRL_RSVD_SVC,
&ul_pipe, &dl_pipe,
&ul_is_polled, &dl_is_polled);
if (status) {
HIF_ERROR("%s: pipe_mapping failure", __func__);
return status;
}
irq_to_wake_on = icnss_get_irq(dl_pipe);
if (irq_to_wake_on < 0) {
HIF_ERROR("%s: failed to map ce to irq", __func__);
return QDF_STATUS_E_RESOURCES;
}
if (enable)
ret = enable_irq_wake(irq_to_wake_on);
ret = enable_irq_wake(scn->wake_irq);
else
ret = disable_irq_wake(irq_to_wake_on);
ret = disable_irq_wake(scn->wake_irq);
if (ret) {
HIF_ERROR("%s: Fail to setup wake IRQ!", __func__);
return QDF_STATUS_E_RESOURCES;
}
HIF_INFO("%s: expecting wake from ce %d, irq %d enable %d",
__func__, dl_pipe, irq_to_wake_on, enable);
return QDF_STATUS_SUCCESS;
}