@@ -195,7 +195,7 @@ static bool ath11k_ce_need_shadow_fix(int ce_id)
return false;
}
-static void ath11k_ce_stop_shadow_timers(struct ath11k_base *ab)
+void ath11k_ce_stop_shadow_timers(struct ath11k_base *ab)
{
int i;
@@ -190,4 +190,6 @@ int ath11k_ce_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,
int ath11k_ce_attr_attach(struct ath11k_base *ab);
void ath11k_ce_get_shadow_config(struct ath11k_base *ab,
u32 **shadow_cfg, u32 *shadow_cfg_len);
+void ath11k_ce_stop_shadow_timers(struct ath11k_base *ab);
+
#endif
@@ -13,6 +13,7 @@
#include "dp_rx.h"
#include "debug.h"
#include "hif.h"
+#include "wow.h"
unsigned int ath11k_debug_mask;
EXPORT_SYMBOL(ath11k_debug_mask);
@@ -66,6 +67,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_shadow_regs = false,
.idle_ps = false,
.cold_boot_calib = true,
+ .supports_suspend = false,
},
{
.hw_rev = ATH11K_HW_IPQ6018_HW10,
@@ -103,6 +105,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_shadow_regs = false,
.idle_ps = false,
.cold_boot_calib = true,
+ .supports_suspend = false,
},
{
.name = "qca6390 hw2.0",
@@ -139,9 +142,91 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_shadow_regs = true,
.idle_ps = true,
.cold_boot_calib = false,
+ .supports_suspend = true,
},
};
+int ath11k_core_suspend(struct ath11k_base *ab)
+{
+ int ret;
+
+ if (!ab->hw_params.supports_suspend)
+ return -EOPNOTSUPP;
+
+ /* TODO: there can frames in queues so for now add delay as a hack.
+ * Need to implement to handle and remove this delay.
+ */
+ msleep(500);
+
+ ret = ath11k_dp_rx_pktlog_stop(ab, true);
+ if (ret) {
+ ath11k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ath11k_wow_enable(ab);
+ if (ret) {
+ ath11k_warn(ab, "failed to enable wow during suspend: %d\n", ret);
+ return ret;
+ }
+
+ ret = ath11k_dp_rx_pktlog_stop(ab, false);
+ if (ret) {
+ ath11k_warn(ab, "failed to stop dp rx pktlog during suspend: %d\n",
+ ret);
+ return ret;
+ }
+
+ ath11k_ce_stop_shadow_timers(ab);
+ ath11k_dp_stop_shadow_timers(ab);
+
+ ath11k_hif_irq_disable(ab);
+ ath11k_hif_ce_irq_disable(ab);
+
+ ret = ath11k_hif_suspend(ab);
+ if (!ret) {
+ ath11k_warn(ab, "failed to suspend hif: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ath11k_core_suspend);
+
+int ath11k_core_resume(struct ath11k_base *ab)
+{
+ int ret;
+
+ if (!ab->hw_params.supports_suspend)
+ return -EOPNOTSUPP;
+
+ ret = ath11k_hif_resume(ab);
+ if (ret) {
+ ath11k_warn(ab, "failed to resume hif during resume: %d\n", ret);
+ return ret;
+ }
+
+ ath11k_hif_ce_irq_enable(ab);
+ ath11k_hif_irq_enable(ab);
+
+ ret = ath11k_dp_rx_pktlog_start(ab);
+ if (ret) {
+ ath11k_warn(ab, "failed to start rx pktlog during resume: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ath11k_wow_wakeup(ab);
+ if (ret) {
+ ath11k_warn(ab, "failed to wakeup wow during resume: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ath11k_core_resume);
+
int ath11k_core_check_dt(struct ath11k_base *ab)
{
size_t max_len = sizeof(ab->qmi.target.bdf_ext);
@@ -897,6 +897,8 @@ void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd);
int ath11k_core_check_dt(struct ath11k_base *ath11k);
void ath11k_core_halt(struct ath11k *ar);
+int ath11k_core_resume(struct ath11k_base *ab);
+int ath11k_core_suspend(struct ath11k_base *ab);
const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab,
const char *filename);
@@ -304,7 +304,7 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
return 0;
}
-static void ath11k_dp_stop_shadow_timers(struct ath11k_base *ab)
+void ath11k_dp_stop_shadow_timers(struct ath11k_base *ab)
{
int i;
@@ -1641,5 +1641,6 @@ void ath11k_dp_shadow_stop_timer(struct ath11k_base *ab,
void ath11k_dp_shadow_init_timer(struct ath11k_base *ab,
struct ath11k_hp_update_timer *update_timer,
u32 interval, u32 ring_id);
+void ath11k_dp_stop_shadow_timers(struct ath11k_base *ab);
#endif
@@ -156,6 +156,7 @@ struct ath11k_hw_params {
bool supports_shadow_regs;
bool idle_ps;
bool cold_boot_calib;
+ bool supports_suspend;
};
struct ath11k_hw_ops {
@@ -1204,12 +1204,43 @@ static void ath11k_pci_shutdown(struct pci_dev *pdev)
ath11k_pci_power_down(ab);
}
+static __maybe_unused int ath11k_pci_pm_suspend(struct device *dev)
+{
+ struct ath11k_base *ab = dev_get_drvdata(dev);
+ int ret;
+
+ ret = ath11k_core_suspend(ab);
+ if (ret)
+ ath11k_warn(ab, "failed to suspend core: %d\n", ret);
+
+ return ret;
+}
+
+static __maybe_unused int ath11k_pci_pm_resume(struct device *dev)
+{
+ struct ath11k_base *ab = dev_get_drvdata(dev);
+ int ret;
+
+ ret = ath11k_core_resume(ab);
+ if (ret)
+ ath11k_warn(ab, "failed to resume core: %d\n", ret);
+
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(ath11k_pci_pm_ops,
+ ath11k_pci_pm_suspend,
+ ath11k_pci_pm_resume);
+
static struct pci_driver ath11k_pci_driver = {
.name = "ath11k_pci",
.id_table = ath11k_pci_id_table,
.probe = ath11k_pci_probe,
.remove = ath11k_pci_remove,
.shutdown = ath11k_pci_shutdown,
+#ifdef CONFIG_PM
+ .driver.pm = &ath11k_pci_pm_ops,
+#endif
};
static int ath11k_pci_init(void)