Commit 8cd13cad authored by Michal Kazior's avatar Michal Kazior Committed by Kalle Valo

ath10k: decouple suspend code

Split up fw-related and hw-related suspension code.

Although we don't advertise WoW support to
mac80211 yet it's useful to keep the code in
suspend/resume hooks.

At this point there's no need to keep pci pm ops.
In case of WoW mac80211 calls ath10k_suspend()
which should take care of entering low-power mode.
In case WoW is not available mac80211 will go
through regular interface teradown and use start/stop.
Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 64d151d4
......@@ -648,34 +648,6 @@ void ath10k_core_unregister(struct ath10k *ar)
}
EXPORT_SYMBOL(ath10k_core_unregister);
int ath10k_core_target_suspend(struct ath10k *ar)
{
int ret;
ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
ret = ath10k_wmi_pdev_suspend_target(ar);
if (ret)
ath10k_warn("could not suspend target (%d)\n", ret);
return ret;
}
EXPORT_SYMBOL(ath10k_core_target_suspend);
int ath10k_core_target_resume(struct ath10k *ar)
{
int ret;
ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
ret = ath10k_wmi_pdev_resume_target(ar);
if (ret)
ath10k_warn("could not resume target (%d)\n", ret);
return ret;
}
EXPORT_SYMBOL(ath10k_core_target_resume);
MODULE_AUTHOR("Qualcomm Atheros");
MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
MODULE_LICENSE("Dual BSD/GPL");
......@@ -365,7 +365,4 @@ void ath10k_core_stop(struct ath10k *ar);
int ath10k_core_register(struct ath10k *ar);
void ath10k_core_unregister(struct ath10k *ar);
int ath10k_core_target_suspend(struct ath10k *ar);
int ath10k_core_target_resume(struct ath10k *ar);
#endif /* _CORE_H_ */
......@@ -80,6 +80,9 @@ struct ath10k_hif_ops {
/* Power down the device and free up resources. stop() must be called
* before this if start() was called earlier */
void (*power_down)(struct ath10k *ar);
int (*suspend)(struct ath10k *ar);
int (*resume)(struct ath10k *ar);
};
......@@ -154,4 +157,20 @@ static inline void ath10k_hif_power_down(struct ath10k *ar)
ar->hif.ops->power_down(ar);
}
static inline int ath10k_hif_suspend(struct ath10k *ar)
{
if (!ar->hif.ops->suspend)
return -EOPNOTSUPP;
return ar->hif.ops->suspend(ar);
}
static inline int ath10k_hif_resume(struct ath10k *ar)
{
if (!ar->hif.ops->resume)
return -EOPNOTSUPP;
return ar->hif.ops->resume(ar);
}
#endif /* _HIF_H_ */
......@@ -20,6 +20,7 @@
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include "hif.h"
#include "core.h"
#include "debug.h"
#include "wmi.h"
......@@ -2749,6 +2750,67 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
return 1;
}
#ifdef CONFIG_PM
static int ath10k_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan)
{
struct ath10k *ar = hw->priv;
int ret;
ar->is_target_paused = false;
ret = ath10k_wmi_pdev_suspend_target(ar);
if (ret) {
ath10k_warn("could not suspend target (%d)\n", ret);
return 1;
}
ret = wait_event_interruptible_timeout(ar->event_queue,
ar->is_target_paused == true,
1 * HZ);
if (ret < 0) {
ath10k_warn("suspend interrupted (%d)\n", ret);
goto resume;
} else if (ret == 0) {
ath10k_warn("suspend timed out - target pause event never came\n");
goto resume;
}
ret = ath10k_hif_suspend(ar);
if (ret) {
ath10k_warn("could not suspend hif (%d)\n", ret);
goto resume;
}
return 0;
resume:
ret = ath10k_wmi_pdev_resume_target(ar);
if (ret)
ath10k_warn("could not resume target (%d)\n", ret);
return 1;
}
static int ath10k_resume(struct ieee80211_hw *hw)
{
struct ath10k *ar = hw->priv;
int ret;
ret = ath10k_hif_resume(ar);
if (ret) {
ath10k_warn("could not resume hif (%d)\n", ret);
return 1;
}
ret = ath10k_wmi_pdev_resume_target(ar);
if (ret) {
ath10k_warn("could not resume target (%d)\n", ret);
return 1;
}
return 0;
}
#endif
static const struct ieee80211_ops ath10k_ops = {
.tx = ath10k_tx,
.start = ath10k_start,
......@@ -2769,6 +2831,10 @@ static const struct ieee80211_ops ath10k_ops = {
.set_frag_threshold = ath10k_set_frag_threshold,
.flush = ath10k_flush,
.tx_last_beacon = ath10k_tx_last_beacon,
#ifdef CONFIG_PM
.suspend = ath10k_suspend,
.resume = ath10k_resume,
#endif
};
#define RATETAB_ENT(_rate, _rateid, _flags) { \
......
......@@ -1796,6 +1796,55 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar)
ath10k_do_pci_sleep(ar);
}
#ifdef CONFIG_PM
#define ATH10K_PCI_PM_CONTROL 0x44
static int ath10k_pci_hif_suspend(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct pci_dev *pdev = ar_pci->pdev;
u32 val;
pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
if ((val & 0x000000ff) != 0x3) {
pci_save_state(pdev);
pci_disable_device(pdev);
pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
(val & 0xffffff00) | 0x03);
}
return 0;
}
static int ath10k_pci_hif_resume(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct pci_dev *pdev = ar_pci->pdev;
u32 val;
pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
if ((val & 0x000000ff) != 0) {
pci_restore_state(pdev);
pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
val & 0xffffff00);
/*
* Suspend/Resume resets the PCI configuration space,
* so we have to re-disable the RETRY_TIMEOUT register (0x41)
* to keep PCI Tx retries from interfering with C3 CPU state
*/
pci_read_config_dword(pdev, 0x40, &val);
if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
}
return 0;
}
#endif
static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
.send_head = ath10k_pci_hif_send_head,
.exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg,
......@@ -1808,6 +1857,10 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
.get_free_queue_number = ath10k_pci_hif_get_free_queue_number,
.power_up = ath10k_pci_hif_power_up,
.power_down = ath10k_pci_hif_power_down,
#ifdef CONFIG_PM
.suspend = ath10k_pci_hif_suspend,
.resume = ath10k_pci_hif_resume,
#endif
};
static void ath10k_pci_ce_tasklet(unsigned long ptr)
......@@ -2376,128 +2429,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
kfree(ar_pci);
}
#if defined(CONFIG_PM_SLEEP)
#define ATH10K_PCI_PM_CONTROL 0x44
static int ath10k_pci_suspend(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct ath10k *ar = pci_get_drvdata(pdev);
struct ath10k_pci *ar_pci;
u32 val;
int ret, retval;
ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
if (!ar)
return -ENODEV;
ar_pci = ath10k_pci_priv(ar);
if (!ar_pci)
return -ENODEV;
if (ath10k_core_target_suspend(ar))
return -EBUSY;
ret = wait_event_interruptible_timeout(ar->event_queue,
ar->is_target_paused == true,
1 * HZ);
if (ret < 0) {
ath10k_warn("suspend interrupted (%d)\n", ret);
retval = ret;
goto resume;
} else if (ret == 0) {
ath10k_warn("suspend timed out - target pause event never came\n");
retval = EIO;
goto resume;
}
/*
* reset is_target_paused and host can check that in next time,
* or it will always be TRUE and host just skip the waiting
* condition, it causes target assert due to host already
* suspend
*/
ar->is_target_paused = false;
pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
if ((val & 0x000000ff) != 0x3) {
pci_save_state(pdev);
pci_disable_device(pdev);
pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
(val & 0xffffff00) | 0x03);
}
return 0;
resume:
ret = ath10k_core_target_resume(ar);
if (ret)
ath10k_warn("could not resume (%d)\n", ret);
return retval;
}
static int ath10k_pci_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct ath10k *ar = pci_get_drvdata(pdev);
struct ath10k_pci *ar_pci;
int ret;
u32 val;
ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
if (!ar)
return -ENODEV;
ar_pci = ath10k_pci_priv(ar);
if (!ar_pci)
return -ENODEV;
ret = pci_enable_device(pdev);
if (ret) {
ath10k_warn("cannot enable PCI device: %d\n", ret);
return ret;
}
pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
if ((val & 0x000000ff) != 0) {
pci_restore_state(pdev);
pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
val & 0xffffff00);
/*
* Suspend/Resume resets the PCI configuration space,
* so we have to re-disable the RETRY_TIMEOUT register (0x41)
* to keep PCI Tx retries from interfering with C3 CPU state
*/
pci_read_config_dword(pdev, 0x40, &val);
if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
}
ret = ath10k_core_target_resume(ar);
if (ret)
ath10k_warn("target resume failed: %d\n", ret);
return ret;
}
static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops,
ath10k_pci_suspend,
ath10k_pci_resume);
#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops)
#else
#define ATH10K_PCI_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table);
static struct pci_driver ath10k_pci_driver = {
......@@ -2505,7 +2436,6 @@ static struct pci_driver ath10k_pci_driver = {
.id_table = ath10k_pci_id_table,
.probe = ath10k_pci_probe,
.remove = ath10k_pci_remove,
.driver.pm = ATH10K_PCI_PM_OPS,
};
static int __init ath10k_pci_init(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment