@@ -65,6 +65,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_monitor = true,
.supports_shadow_regs = false,
.idle_ps = false,
+ .support_suspend = false,
},
{
.hw_rev = ATH11K_HW_IPQ6018_HW10,
@@ -102,6 +103,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_monitor = true,
.supports_shadow_regs = false,
.idle_ps = false,
+ .support_suspend = false,
},
{
.name = "qca6390 hw2.0",
@@ -138,6 +140,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.supports_monitor = false,
.supports_shadow_regs = true,
.idle_ps = true,
+ .support_suspend = true,
},
};
@@ -937,5 +940,40 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size,
}
EXPORT_SYMBOL(ath11k_core_alloc);
+int ath11k_core_suspend(struct ath11k_base *ab)
+{
+ struct ath11k *ar = ab->pdevs[0].ar;
+ int ret = 0;
+
+ if (ab->hw_params.support_suspend) {
+ reinit_completion(&ar->target_suspend);
+ ath11k_wmi_pdev_suspend(ar, 1, 0);
+ ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
+ if (ret == 0) {
+ ath11k_warn(ab,
+ "timed out while waiting for suspend completion\n");
+ return -ETIMEDOUT;
+ } else if (!ar->target_suspend_ack) {
+ ath11k_warn(ab, "suspend failed\n");
+ return -EAGAIN;
+ }
+
+ return ath11k_hif_suspend(ab);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ath11k_core_suspend);
+
+int ath11k_core_resume(struct ath11k_base *ab)
+{
+ if (ab->hw_params.support_suspend) {
+ ath11k_hif_resume(ab);
+ ath11k_wmi_pdev_resume(ab->pdevs[0].ar, 0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ath11k_core_resume);
+
MODULE_DESCRIPTION("Core module for Qualcomm Atheros 802.11ax wireless LAN cards.");
MODULE_LICENSE("Dual BSD/GPL");
@@ -888,6 +888,8 @@ int ath11k_core_fetch_bdf(struct ath11k_base *ath11k,
void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd);
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);
@@ -161,6 +161,7 @@ struct ath11k_hw_params {
bool supports_monitor;
bool supports_shadow_regs;
bool idle_ps;
+ bool support_suspend;
};
struct ath11k_hw_ops {
@@ -243,13 +243,17 @@ static void ath11k_pci_clear_dbg_registers(struct ath11k_base *ab)
static void ath11k_pci_force_wake(struct ath11k_base *ab)
{
+ int val;
+
+ ath11k_pci_write32(ab, PCIE_SCRATCH_0_SOC_PCIE_REG, 0);
ath11k_pci_write32(ab, PCIE_SOC_WAKE_PCIE_LOCAL_REG, 1);
- mdelay(5);
+ mdelay(10);
+ val = ath11k_pci_read32(ab, PCIE_SCRATCH_0_SOC_PCIE_REG);
+ ath11k_dbg(ab, ATH11K_DBG_PCI, "forcw_wake scratch 0: 0x%x\n", val);
}
static void ath11k_pci_sw_reset(struct ath11k_base *ab)
{
- ath11k_pci_soc_global_reset(ab);
ath11k_mhi_clear_vector(ab);
ath11k_pci_soc_global_reset(ab);
ath11k_mhi_set_mhictrl_reset(ab);
@@ -885,9 +889,9 @@ static void ath11k_pci_power_down(struct ath11k_base *ab)
{
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
+ ath11k_pci_force_wake(ab_pci->ab);
ath11k_mhi_stop(ab_pci);
clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
- ath11k_pci_force_wake(ab_pci->ab);
ath11k_pci_sw_reset(ab_pci->ab);
}
@@ -1180,12 +1184,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 hif: %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 hif: %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)