ath10k: defer hw setup to start/stop mac80211 hooks
authorMichal Kazior <michal.kazior@tieto.com>
Tue, 16 Jul 2013 07:38:57 +0000 (09:38 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Tue, 30 Jul 2013 15:01:19 +0000 (18:01 +0300)
This fixes suspend-to-disk. The hardware is now
re-initialized upon freeze/thaw properly.

This also makes suspend/resume re-initialize the
hardware as WoWLAN support is not done yet.

With some little work it should be possible to
support hw reconfiguration for hw/fw recovery.

HW must be initialized once before registering to
mac80211 because FW determinates what hw
capabilities can be advertised.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/pci.c

index 0e4a704516f75fe1dcde665eb5c2e7cca0681407..1d4c24583f1d635211f7977570d46a2063836c61 100644 (file)
@@ -601,6 +601,7 @@ err_wmi_detach:
 err:
        return status;
 }
+EXPORT_SYMBOL(ath10k_core_start);
 
 void ath10k_core_stop(struct ath10k *ar)
 {
@@ -608,18 +609,49 @@ void ath10k_core_stop(struct ath10k *ar)
        ath10k_htt_detach(&ar->htt);
        ath10k_wmi_detach(ar);
 }
+EXPORT_SYMBOL(ath10k_core_stop);
+
+/* mac80211 manages fw/hw initialization through start/stop hooks. However in
+ * order to know what hw capabilities should be advertised to mac80211 it is
+ * necessary to load the firmware (and tear it down immediately since start
+ * hook will try to init it again) before registering */
+static int ath10k_core_probe_fw(struct ath10k *ar)
+{
+       int ret;
+
+       ret = ath10k_hif_power_up(ar);
+       if (ret) {
+               ath10k_err("could not start pci hif (%d)\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_core_start(ar);
+       if (ret) {
+               ath10k_err("could not init core (%d)\n", ret);
+               ath10k_hif_power_down(ar);
+               return ret;
+       }
+
+       ath10k_core_stop(ar);
+       ath10k_hif_power_down(ar);
+       return 0;
+}
 
 int ath10k_core_register(struct ath10k *ar)
 {
        int status;
 
-       status = ath10k_core_start(ar);
-       if (status)
-               goto err;
+       status = ath10k_core_probe_fw(ar);
+       if (status) {
+               ath10k_err("could not probe fw (%d)\n", status);
+               return status;
+       }
 
        status = ath10k_mac_register(ar);
-       if (status)
-               goto err_core_stop;
+       if (status) {
+               ath10k_err("could not register to mac80211 (%d)\n", status);
+               return status;
+       }
 
        status = ath10k_debug_create(ar);
        if (status) {
@@ -631,9 +663,6 @@ int ath10k_core_register(struct ath10k *ar)
 
 err_unregister_mac:
        ath10k_mac_unregister(ar);
-err_core_stop:
-       ath10k_core_stop(ar);
-err:
        return status;
 }
 EXPORT_SYMBOL(ath10k_core_register);
@@ -644,7 +673,6 @@ void ath10k_core_unregister(struct ath10k *ar)
         * Otherwise we will fail to submit commands to FW and mac80211 will be
         * unhappy about callback failures. */
        ath10k_mac_unregister(ar);
-       ath10k_core_stop(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
index b9663e94dba54e7a97eed26940764f487cbab5cd..2dfd446251b1fbf6f9e44847711d425ff3e2555c 100644 (file)
@@ -1738,13 +1738,52 @@ static void ath10k_tx(struct ieee80211_hw *hw,
 /*
  * Initialize various parameters with default vaules.
  */
+static void ath10k_halt(struct ath10k *ar)
+{
+       lockdep_assert_held(&ar->conf_mutex);
+
+       del_timer_sync(&ar->scan.timeout);
+       ath10k_offchan_tx_purge(ar);
+       ath10k_peer_cleanup_all(ar);
+       ath10k_core_stop(ar);
+       ath10k_hif_power_down(ar);
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               del_timer(&ar->scan.timeout);
+               ar->scan.in_progress = false;
+               ieee80211_scan_completed(ar->hw, true);
+       }
+       spin_unlock_bh(&ar->data_lock);
+}
+
 static int ath10k_start(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
-       int ret;
+       int ret = 0;
 
        mutex_lock(&ar->conf_mutex);
 
+       if (ar->state != ATH10K_STATE_OFF) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       ret = ath10k_hif_power_up(ar);
+       if (ret) {
+               ath10k_err("could not init hif (%d)\n", ret);
+               ar->state = ATH10K_STATE_OFF;
+               goto exit;
+       }
+
+       ret = ath10k_core_start(ar);
+       if (ret) {
+               ath10k_err("could not init core (%d)\n", ret);
+               ath10k_hif_power_down(ar);
+               ar->state = ATH10K_STATE_OFF;
+               goto exit;
+       }
+
        ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
        if (ret)
                ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
@@ -1755,9 +1794,9 @@ static int ath10k_start(struct ieee80211_hw *hw)
                ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
                            ret);
 
-       ar->state = ATH10K_STATE_ON;
        ath10k_regd_update(ar);
 
+exit:
        mutex_unlock(&ar->conf_mutex);
        return 0;
 }
@@ -1767,17 +1806,8 @@ static void ath10k_stop(struct ieee80211_hw *hw)
        struct ath10k *ar = hw->priv;
 
        mutex_lock(&ar->conf_mutex);
-       del_timer_sync(&ar->scan.timeout);
-       ath10k_offchan_tx_purge(ar);
-       ath10k_peer_cleanup_all(ar);
-
-       spin_lock_bh(&ar->data_lock);
-       if (ar->scan.in_progress) {
-               del_timer(&ar->scan.timeout);
-               ar->scan.in_progress = false;
-               ieee80211_scan_completed(ar->hw, true);
-       }
-       spin_unlock_bh(&ar->data_lock);
+       if (ar->state == ATH10K_STATE_ON)
+               ath10k_halt(ar);
 
        ar->state = ATH10K_STATE_OFF;
        mutex_unlock(&ar->conf_mutex);
index 75f5427bb416f00d298f723c4395f89d218aa0c4..bfe856166bb0dc516486cddefcb62199794fd725 100644 (file)
@@ -2362,22 +2362,14 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
                goto err_iomap;
        }
 
-       ret = ath10k_pci_hif_power_up(ar);
-       if (ret) {
-               ath10k_err("could not start pci hif (%d)\n", ret);
-               goto err_intr;
-       }
-
        ret = ath10k_core_register(ar);
        if (ret) {
                ath10k_err("could not register driver core (%d)\n", ret);
-               goto err_hif;
+               goto err_intr;
        }
 
        return 0;
 
-err_hif:
-       ath10k_pci_hif_power_down(ar);
 err_intr:
        ath10k_pci_stop_intr(ar);
 err_iomap:
@@ -2416,7 +2408,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
        tasklet_kill(&ar_pci->msi_fw_err);
 
        ath10k_core_unregister(ar);
-       ath10k_pci_hif_power_down(ar);
        ath10k_pci_stop_intr(ar);
 
        pci_set_drvdata(pdev, NULL);