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: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
This commit is contained in:
parent
64d151d470
commit
8cd13cad1c
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue