diff --git a/hif/inc/hif.h b/hif/inc/hif.h index 813fa7677abc..943b2847d774 100644 --- a/hif/inc/hif.h +++ b/hif/inc/hif.h @@ -722,6 +722,50 @@ int hif_bus_suspend(struct hif_opaque_softc *); int hif_bus_resume_noirq(struct hif_opaque_softc *); int hif_bus_suspend_noirq(struct hif_opaque_softc *); +/** + * hif_apps_irqs_enable() - Enables all irqs from the APPS side + * @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_irqs_enable(struct hif_opaque_softc *hif_ctx); + +/** + * hif_apps_irqs_disable() - Disables all irqs from the APPS side + * @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_irqs_disable(struct hif_opaque_softc *hif_ctx); + +/** + * hif_apps_wake_irq_enable() - Enables the wake irq from the APPS side + * @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_wake_irq_enable(struct hif_opaque_softc *hif_ctx); + +/** + * hif_apps_wake_irq_disable() - Disables the wake irq from the APPS side + * @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_wake_irq_disable(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); diff --git a/hif/src/ce/ce_internal.h b/hif/src/ce/ce_internal.h index 9de73843193e..40f54c6e1df1 100644 --- a/hif/src/ce/ce_internal.h +++ b/hif/src/ce/ce_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -495,4 +495,13 @@ static inline void ce_t2h_msg_ce_cleanup(struct CE_handle *ce_hdl) /* Additional internal-only ce_send flags */ #define CE_SEND_FLAG_GATHER 0x00010000 /* Use Gather */ + +/** + * hif_get_wake_ce_id() - gets the copy engine id used for waking up + * @scn: The hif context to use + * @ce_id: a pointer where the copy engine Id should be populated + * + * Return: errno + */ +int hif_get_wake_ce_id(struct hif_softc *scn, uint8_t *ce_id); #endif /* __COPY_ENGINE_INTERNAL_H__ */ diff --git a/hif/src/ce/ce_main.c b/hif/src/ce/ce_main.c index 7c53e6aefa7c..3b223fed1b52 100644 --- a/hif/src/ce/ce_main.c +++ b/hif/src/ce/ce_main.c @@ -3177,3 +3177,23 @@ void hif_wlan_disable(struct hif_softc *scn) pld_wlan_disable(scn->qdf_dev->dev, mode); } +int hif_get_wake_ce_id(struct hif_softc *scn, uint8_t *ce_id) +{ + QDF_STATUS status; + uint8_t ul_pipe, dl_pipe; + int ul_is_polled, dl_is_polled; + + /* DL pipe for HTC_CTRL_RSVD_SVC should map to the wake CE */ + status = hif_map_service_to_pipe(GET_HIF_OPAQUE_HDL(scn), + HTC_CTRL_RSVD_SVC, + &ul_pipe, &dl_pipe, + &ul_is_polled, &dl_is_polled); + if (status) { + HIF_ERROR("%s: failed to map pipe: %d", __func__, status); + return qdf_status_to_os_return(status); + } + + *ce_id = dl_pipe; + + return 0; +} diff --git a/hif/src/ce/ce_tasklet.c b/hif/src/ce/ce_tasklet.c index ef13e4e2d0e1..95cc6cc2805b 100644 --- a/hif/src/ce/ce_tasklet.c +++ b/hif/src/ce/ce_tasklet.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -335,25 +335,20 @@ static bool hif_interrupt_is_fake_apps_resume(struct hif_opaque_softc *hif_ctx, int ce_id) { struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); - uint8_t ul_pipe, dl_pipe; - int ul_is_polled, dl_is_polled; - QDF_STATUS status; + int errno; + uint8_t wake_ce_id; if (!test_bit(HIF_FA_SUSPENDED_BIT, &scn->fake_apps_ctx.state)) return false; - /* ensure passed ce_id matches wake irq */ - /* dl_pipe will be populated with the wake irq number */ - status = hif_map_service_to_pipe(hif_ctx, HTC_CTRL_RSVD_SVC, - &ul_pipe, &dl_pipe, - &ul_is_polled, &dl_is_polled); - - if (status) { - HIF_ERROR("%s: pipe_mapping failure", __func__); + /* ensure passed ce_id matches wake ce_id */ + 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 false; } - return ce_id == dl_pipe; + return ce_id == wake_ce_id; } /** diff --git a/hif/src/dispatcher/multibus.c b/hif/src/dispatcher/multibus.c index 8f361582dcc9..05f938507a47 100644 --- a/hif/src/dispatcher/multibus.c +++ b/hif/src/dispatcher/multibus.c @@ -35,6 +35,8 @@ #include "dummy.h" #if defined(HIF_PCI) || defined(HIF_SNOC) || defined(HIF_AHB) #include "ce_main.h" +#include "ce_api.h" +#include "ce_internal.h" #endif #include "htc_services.h" #include "a_types.h" @@ -412,3 +414,83 @@ int hif_bus_reset_resume(struct hif_opaque_softc *scn) struct hif_softc *hif_sc = HIF_GET_SOFTC(scn); return hif_sc->bus_ops.hif_bus_reset_resume(hif_sc); } + +int hif_apps_irqs_disable(struct hif_opaque_softc *hif_ctx) +{ + struct hif_softc *scn; + int i; + + scn = HIF_GET_SOFTC(hif_ctx); + if (!scn) { + QDF_BUG(0); + return -EINVAL; + } + + for (i = 0; i < scn->ce_count; ++i) + disable_irq(scn->bus_ops.hif_map_ce_to_irq(scn, i)); + + return 0; +} + +int hif_apps_irqs_enable(struct hif_opaque_softc *hif_ctx) +{ + struct hif_softc *scn; + int i; + + scn = HIF_GET_SOFTC(hif_ctx); + if (!scn) { + QDF_BUG(0); + return -EINVAL; + } + + for (i = 0; i < scn->ce_count; ++i) + enable_irq(scn->bus_ops.hif_map_ce_to_irq(scn, i)); + + 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; + + scn = HIF_GET_SOFTC(hif_ctx); + if (!scn) { + QDF_BUG(0); + 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)); + + 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; + + scn = HIF_GET_SOFTC(hif_ctx); + if (!scn) { + QDF_BUG(0); + 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)); + + return 0; +} diff --git a/hif/src/dispatcher/multibus.h b/hif/src/dispatcher/multibus.h index e90a94599fdf..1608f0506380 100644 --- a/hif/src/dispatcher/multibus.h +++ b/hif/src/dispatcher/multibus.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -81,6 +81,7 @@ struct hif_bus_ops { void (*hif_set_bundle_mode) (struct hif_softc *hif_ctx, bool enabled, int rx_bundle_cnt); int (*hif_bus_reset_resume)(struct hif_softc *hif_ctx); + int (*hif_map_ce_to_irq)(struct hif_softc *hif_sc, int ce_id); }; #ifdef HIF_SNOC diff --git a/hif/src/dispatcher/multibus_pci.c b/hif/src/dispatcher/multibus_pci.c index ad1ffdf7c8d1..da4caedac9d0 100644 --- a/hif/src/dispatcher/multibus_pci.c +++ b/hif/src/dispatcher/multibus_pci.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -90,6 +90,9 @@ QDF_STATUS hif_initialize_pci_ops(struct hif_softc *hif_sc) bus_ops->hif_grp_irq_disable = &hif_dummy_grp_irq_disable; bus_ops->hif_grp_irq_enable = &hif_dummy_grp_irq_enable; + /* default to legacy mapping handler; override as needed */ + bus_ops->hif_map_ce_to_irq = &hif_pci_legacy_map_ce_to_irq; + return QDF_STATUS_SUCCESS; } diff --git a/hif/src/dispatcher/multibus_snoc.c b/hif/src/dispatcher/multibus_snoc.c index 3d310777534c..5f6fcd393ca0 100644 --- a/hif/src/dispatcher/multibus_snoc.c +++ b/hif/src/dispatcher/multibus_snoc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -81,6 +81,7 @@ QDF_STATUS hif_initialize_snoc_ops(struct hif_bus_ops *bus_ops) &hif_snoc_clear_stats; bus_ops->hif_grp_irq_disable = &hif_dummy_grp_irq_disable; bus_ops->hif_grp_irq_enable = &hif_dummy_grp_irq_enable; + bus_ops->hif_map_ce_to_irq = &hif_snoc_map_ce_to_irq; return QDF_STATUS_SUCCESS; } diff --git a/hif/src/dispatcher/pci_api.h b/hif/src/dispatcher/pci_api.h index 88c52f9054f6..c252e2e1ef08 100644 --- a/hif/src/dispatcher/pci_api.h +++ b/hif/src/dispatcher/pci_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -54,4 +54,5 @@ void hif_pci_enable_power_management(struct hif_softc *hif_ctx, void hif_pci_disable_power_management(struct hif_softc *hif_ctx); void hif_pci_display_stats(struct hif_softc *hif_ctx); void hif_pci_clear_stats(struct hif_softc *hif_ctx); +int hif_pci_legacy_map_ce_to_irq(struct hif_softc *scn, int ce_id); #endif /* _PCI_API_H_ */ diff --git a/hif/src/dispatcher/snoc_api.h b/hif/src/dispatcher/snoc_api.h index 06fc43d5aefd..9e3822cbeab1 100644 --- a/hif/src/dispatcher/snoc_api.h +++ b/hif/src/dispatcher/snoc_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -46,4 +46,5 @@ void hif_snoc_irq_enable(struct hif_softc *scn, int ce_id); int hif_snoc_dump_registers(struct hif_softc *scn); void hif_snoc_display_stats(struct hif_softc *hif_ctx); void hif_snoc_clear_stats(struct hif_softc *hif_ctx); +int hif_snoc_map_ce_to_irq(struct hif_softc *scn, int ce_id); #endif /* _SNOC_API_H_ */ diff --git a/hif/src/pcie/if_pci.c b/hif/src/pcie/if_pci.c index 1df16203b8bb..72e1e3e84628 100644 --- a/hif/src/pcie/if_pci.c +++ b/hif/src/pcie/if_pci.c @@ -2730,30 +2730,21 @@ void hif_pci_prevent_linkdown(struct hif_softc *scn, bool flag) } #endif -/** - * hif_pci_bus_enable_wake_irq() - enable pci bus wake irq - * - * Configures the pci irq line as a wakeup source. - * - * Return: 0 for success and non-zero for failure - */ -static int hif_pci_bus_enable_wake_irq(struct hif_softc *scn) +static int hif_mark_wake_irq_wakeable(struct hif_softc *scn) { - struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn); + int errno; + uint8_t ce_id; - if (!sc) { - HIF_ERROR("%s: sc is null", __func__); - return -EFAULT; + 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; } - if (!sc->pdev) { - HIF_ERROR("%s: pdev is null", __func__); - return -EFAULT; - } - - if (unlikely(enable_irq_wake(sc->pdev->irq))) { - HIF_ERROR("%s: Failed to enable wake IRQ", __func__); - return -EINVAL; + errno = enable_irq_wake(scn->bus_ops.hif_map_ce_to_irq(scn, ce_id)); + if (errno) { + HIF_ERROR("%s: Failed to mark wake IRQ: %d", __func__, errno); + return errno; } return 0; @@ -2768,11 +2759,19 @@ static int hif_pci_bus_enable_wake_irq(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; /* pci link is staying up; enable wake irq */ - return hif_pci_bus_enable_wake_irq(scn); + return hif_mark_wake_irq_wakeable(scn); } /** @@ -2814,30 +2813,21 @@ static int __hif_check_link_status(struct hif_softc *scn) return -EACCES; } -/** - * hif_pci_bus_disable_wake_irq() - disable pci bus wake irq - * - * Deconfigures the pci irq line as a wakeup source. - * - * Return: 0 for success and non-zero for failure - */ -static int hif_pci_bus_disable_wake_irq(struct hif_softc *scn) +static int hif_unmark_wake_irq_wakeable(struct hif_softc *scn) { - struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn); + int errno; + uint8_t ce_id; - if (!sc) { - HIF_ERROR("%s: sc is null", __func__); - return -EFAULT; + 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; } - if (!sc->pdev) { - HIF_ERROR("%s: pdev is null", __func__); - return -EFAULT; - } - - if (unlikely(disable_irq_wake(sc->pdev->irq))) { - HIF_ERROR("%s: Failed to disable wake IRQ", __func__); - return -EFAULT; + errno = disable_irq_wake(scn->bus_ops.hif_map_ce_to_irq(scn, ce_id)); + if (errno) { + HIF_ERROR("%s: Failed to unmark wake IRQ: %d", __func__, errno); + return errno; } return 0; @@ -2862,7 +2852,7 @@ int hif_pci_bus_resume(struct hif_softc *scn) return 0; /* pci link is up; disable wake irq */ - return hif_pci_bus_disable_wake_irq(scn); + return hif_unmark_wake_irq_wakeable(scn); } /** @@ -3093,30 +3083,39 @@ int hif_runtime_suspend(struct hif_opaque_softc *hif_ctx) { struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn); - int err; + int errno; - err = hif_pci_bus_suspend(scn); - if (err) - goto exit_with_error; + errno = hif_bus_suspend(hif_ctx); + if (errno) { + HIF_ERROR("%s: failed bus suspend: %d", __func__, errno); + return errno; + } - /* - * normal 3-stage suspend from CNSS disables irqs before calling - * noirq stage - */ - disable_irq(sc->pdev->irq); - - err = hif_pci_bus_suspend_noirq(scn); - if (err) + errno = hif_apps_irqs_disable(hif_ctx); + if (errno) { + HIF_ERROR("%s: failed disable irqs: %d", __func__, errno); goto bus_resume; + } + + errno = hif_bus_suspend_noirq(hif_ctx); + if (errno) { + HIF_ERROR("%s: failed bus suspend noirq: %d", __func__, errno); + goto irqs_enable; + } + + /* link should always be down; skip enable wake irq */ return 0; -bus_resume: - enable_irq(sc->pdev->irq); - err = hif_pci_bus_resume(scn); - QDF_BUG(err == 0); +bus_resume_noirq: + QDF_BUG(!hif_bus_resume_noirq(hif_ctx)); + +irq_enable: + QDF_BUG(!hif_apps_irqs_enable(hif_ctx)); + +bus_resume: + QDF_BUG(!hif_bus_resume(hif_ctx)); -exit_with_error: return err; } @@ -3154,7 +3153,6 @@ static void hif_fastpath_resume(struct hif_opaque_softc *hif_ctx) static void hif_fastpath_resume(struct hif_opaque_softc *hif_ctx) {} #endif - /** * hif_runtime_resume() - do the bus resume part of a runtime resume * @@ -3162,35 +3160,14 @@ static void hif_fastpath_resume(struct hif_opaque_softc *hif_ctx) {} */ int hif_runtime_resume(struct hif_opaque_softc *hif_ctx) { - struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); - struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn); - int err; - - err = hif_pci_bus_resume_noirq(scn); - if (err) - goto exit_with_error; - - /* - * normal 3-stage resume from CNSS enables irqs after calling - * noirq stage - */ - enable_irq(sc->pdev->irq); - - err = hif_pci_bus_resume(scn); - if (err) - goto bus_suspend_noirq; + /* link should always be down; skip disable wake irq */ + QDF_BUG(!hif_bus_resume_noirq(hif_ctx)); + QDF_BUG(!hif_apps_irqs_enable(hif_ctx)); + QDF_BUG(!hif_bus_resume(hif_ctx)); hif_fastpath_resume(hif_ctx); return 0; - -bus_suspend_noirq: - disable_irq(sc->pdev->irq); - err = hif_pci_bus_suspend_noirq(scn); - QDF_BUG(err == 0); - -exit_with_error: - return err; } #endif /* #ifdef FEATURE_RUNTIME_PM */ @@ -3580,6 +3557,13 @@ static irqreturn_t hif_ce_interrupt_handler(int irq, void *context) } extern const char *ce_name[]; +static int hif_ce_msi_map_ce_to_irq(struct hif_softc *scn, int ce_id) +{ + struct hif_pci_softc *pci_scn = HIF_GET_PCI_SOFTC(scn); + + return pci_scn->ce_msi_irq_num[ce_id]; +} + /* hif_srng_msi_irq_disable() - disable the irq for msi * @hif_sc: hif context * @ce_id: which ce to disable copy complete interrupts for @@ -3590,16 +3574,12 @@ extern const char *ce_name[]; */ static void hif_ce_srng_msi_irq_disable(struct hif_softc *hif_sc, int ce_id) { - struct hif_pci_softc *pci_sc = HIF_GET_PCI_SOFTC(hif_sc); - - disable_irq_nosync(pci_sc->ce_msi_irq_num[ce_id]); + disable_irq_nosync(hif_ce_msi_map_ce_to_irq(hif_sc, ce_id)); } static void hif_ce_srng_msi_irq_enable(struct hif_softc *hif_sc, int ce_id) { - struct hif_pci_softc *pci_sc = HIF_GET_PCI_SOFTC(hif_sc); - - enable_irq(pci_sc->ce_msi_irq_num[ce_id]); + enable_irq(hif_ce_msi_map_ce_to_irq(hif_sc, ce_id)); } static void hif_ce_legacy_msi_irq_disable(struct hif_softc *hif_sc, int ce_id) @@ -3625,17 +3605,15 @@ static int hif_ce_msi_configure_irq(struct hif_softc *scn) return ret; if (ce_srng_based(scn)) { - scn->bus_ops.hif_irq_disable = - &hif_ce_srng_msi_irq_disable; - scn->bus_ops.hif_irq_enable = - &hif_ce_srng_msi_irq_enable; + scn->bus_ops.hif_irq_disable = &hif_ce_srng_msi_irq_disable; + scn->bus_ops.hif_irq_enable = &hif_ce_srng_msi_irq_enable; } else { - scn->bus_ops.hif_irq_disable = - &hif_ce_legacy_msi_irq_disable; - scn->bus_ops.hif_irq_enable = - &hif_ce_legacy_msi_irq_enable; + scn->bus_ops.hif_irq_disable = &hif_ce_legacy_msi_irq_disable; + scn->bus_ops.hif_irq_enable = &hif_ce_legacy_msi_irq_enable; } + scn->bus_ops.hif_map_ce_to_irq = &hif_ce_msi_map_ce_to_irq; + /* needs to match the ce_id -> irq data mapping * used in the srng parameter configuration */ @@ -3955,6 +3933,7 @@ void hif_pci_irq_enable(struct hif_softc *scn, int ce_id) /* check for missed firmware crash */ hif_fw_interrupt_handler(0, scn); } + /** * hif_pci_irq_disable() - ce_irq_disable * @scn: hif_softc @@ -4436,3 +4415,11 @@ void hif_runtime_lock_deinit(struct hif_opaque_softc *hif_ctx, qdf_mem_free(context); } #endif /* FEATURE_RUNTIME_PM */ + +int hif_pci_legacy_map_ce_to_irq(struct hif_softc *scn, int ce_id) +{ + struct hif_pci_softc *pci_scn = HIF_GET_PCI_SOFTC(scn); + + /* legacy case only has one irq */ + return pci_scn->irq; +} diff --git a/hif/src/snoc/if_snoc.c b/hif/src/snoc/if_snoc.c index afdcd22724db..296e16a2cb61 100644 --- a/hif/src/snoc/if_snoc.c +++ b/hif/src/snoc/if_snoc.c @@ -457,3 +457,7 @@ int hif_snoc_bus_suspend_noirq(struct hif_softc *scn) return 0; } +int hif_snoc_map_ce_to_irq(struct hif_softc *scn, int ce_id) +{ + return icnss_get_irq(ce_id); +}