All of lore.kernel.org
 help / color / mirror / Atom feed
* [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor
@ 2013-06-12 12:53 Michal Kazior
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 01/10] ath10k: decouple pci init/deinit logic Michal Kazior
                   ` (12 more replies)
  0 siblings, 13 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-12 12:53 UTC (permalink / raw)
  To: ath9k-devel

This is a respin of my patchset that addresses
hibernation issues.

This is also groundwork for proper hw recovery
that I'm going to post soon.

I addressed review comments and some bugs I've
found along.

Note: this is not based on master branch but on my
latest 'ath10k: fixes' patches. Split for easier
review.

Michal Kazior (10):
  ath10k: decouple pci init/deinit logic
  ath10k: decouple core start/stop logic
  ath10k: allow deferred regd update
  ath10k: reset BMI state upon init
  ath10k: decouple suspend code
  ath10k: move free_vdev_map initialization
  ath10k: make sure all resources are freed upon ath10k_stop()
  ath10k: defer hw setup to start/stop mac80211 hooks
  ath10k: skip updating some params during resume
  ath10k: store firmware files in memory

 drivers/net/wireless/ath/ath10k/bmi.c  |    6 +
 drivers/net/wireless/ath/ath10k/bmi.h  |    1 +
 drivers/net/wireless/ath/ath10k/core.c |  249 ++++++++++++++++++----------
 drivers/net/wireless/ath/ath10k/core.h |   17 +-
 drivers/net/wireless/ath/ath10k/hif.h  |   39 +++++
 drivers/net/wireless/ath/ath10k/mac.c  |  202 ++++++++++++++++++++---
 drivers/net/wireless/ath/ath10k/pci.c  |  284 +++++++++++++-------------------
 7 files changed, 520 insertions(+), 278 deletions(-)

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 01/10] ath10k: decouple pci init/deinit logic
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
@ 2013-06-12 12:53 ` Michal Kazior
  2013-06-13 18:09   ` Kalle Valo
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 02/10] ath10k: decouple core start/stop logic Michal Kazior
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 72+ messages in thread
From: Michal Kazior @ 2013-06-12 12:53 UTC (permalink / raw)
  To: ath9k-devel

Split logic that prepares the device for BMI
phase/cleans up related resources.

This is necessary for ath10k to be able to restart
hw on the fly without reloading the module.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/hif.h |   20 ++++++
 drivers/net/wireless/ath/ath10k/pci.c |  110 +++++++++++++++++++++------------
 2 files changed, 91 insertions(+), 39 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 010e265..3a257d1 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -46,8 +46,11 @@ struct ath10k_hif_ops {
 				void *request, u32 request_len,
 				void *response, u32 *response_len);
 
+	/* Post BMI phase, after FW is loaded. Starts regular operation */
 	int (*start)(struct ath10k *ar);
 
+	/* Clean up what start() did. This does not revert to BMI phase. If
+	 * desired so, call deinit() and init() */
 	void (*stop)(struct ath10k *ar);
 
 	int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
@@ -70,6 +73,13 @@ struct ath10k_hif_ops {
 			      struct ath10k_hif_cb *callbacks);
 
 	u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+
+	/* Power up the device and enter BMI transfer mode for FW download */
+	int (*init)(struct ath10k *ar);
+
+	/* Power down the device and free up resources. stop() must be called
+	 * before this if start() was called earlier */
+	void (*deinit)(struct ath10k *ar);
 };
 
 
@@ -134,4 +144,14 @@ static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
 	return ar->hif.ops->get_free_queue_number(ar, pipe_id);
 }
 
+static inline int ath10k_hif_init(struct ath10k *ar)
+{
+	return ar->hif.ops->init(ar);
+}
+
+static inline void ath10k_hif_deinit(struct ath10k *ar)
+{
+	ar->hif.ops->deinit(ar);
+}
+
 #endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 06124a4..97f80e5 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -54,6 +54,8 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
 					     int num);
 static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
 static void ath10k_pci_stop_ce(struct ath10k *ar);
+static void ath10k_pci_device_reset(struct ath10k *ar);
+static int ath10k_pci_reset_target(struct ath10k *ar);
 
 static const struct ce_attr host_ce_config_wlan[] = {
 	/* host->target HTC control and raw streams */
@@ -1755,6 +1757,66 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
 	ath10k_pci_sleep(ar);
 }
 
+static int ath10k_pci_hif_init(struct ath10k *ar)
+{
+	int ret;
+
+	/*
+	 * Bring the target up cleanly.
+	 *
+	 * The target may be in an undefined state with an AUX-powered Target
+	 * and a Host in WoW mode. If the Host crashes, loses power, or is
+	 * restarted (without unloading the driver) then the Target is left
+	 * (aux) powered and running. On a subsequent driver load, the Target
+	 * is in an unexpected state. We try to catch that here in order to
+	 * reset the Target and retry the probe.
+	 */
+	ath10k_pci_device_reset(ar);
+
+	ret = ath10k_pci_reset_target(ar);
+	if (ret)
+		goto err;
+
+	if (ath10k_target_ps) {
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
+	} else {
+		/* Force AWAKE forever */
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
+		ath10k_do_pci_wake(ar);
+	}
+
+	ret = ath10k_pci_ce_init(ar);
+	if (ret)
+		goto err_ps;
+
+	ret = ath10k_pci_init_config(ar);
+	if (ret)
+		goto err_ce;
+
+	ret = ath10k_pci_wake_target_cpu(ar);
+	if (ret) {
+		ath10k_err("could not wake up target CPU (%d)\n", ret);
+		goto err_ce;
+	}
+
+	return 0;
+
+err_ce:
+	ath10k_pci_ce_deinit(ar);
+err_ps:
+	if (!ath10k_target_ps)
+		ath10k_do_pci_sleep(ar);
+err:
+	return ret;
+}
+
+static void ath10k_pci_hif_deinit(struct ath10k *ar)
+{
+	ath10k_pci_ce_deinit(ar);
+	if (!ath10k_target_ps)
+		ath10k_do_pci_sleep(ar);
+}
+
 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,
@@ -1765,6 +1827,8 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
 	.send_complete_check	= ath10k_pci_hif_send_complete_check,
 	.set_callbacks		= ath10k_pci_hif_set_callbacks,
 	.get_free_queue_number	= ath10k_pci_hif_get_free_queue_number,
+	.init			= ath10k_pci_hif_init,
+	.deinit			= ath10k_pci_hif_deinit,
 };
 
 static void ath10k_pci_ce_tasklet(unsigned long ptr)
@@ -2265,54 +2329,22 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 		goto err_iomap;
 	}
 
-	/*
-	 * Bring the target up cleanly.
-	 *
-	 * The target may be in an undefined state with an AUX-powered Target
-	 * and a Host in WoW mode. If the Host crashes, loses power, or is
-	 * restarted (without unloading the driver) then the Target is left
-	 * (aux) powered and running. On a subsequent driver load, the Target
-	 * is in an unexpected state. We try to catch that here in order to
-	 * reset the Target and retry the probe.
-	 */
-	ath10k_pci_device_reset(ar);
-
-	ret = ath10k_pci_reset_target(ar);
-	if (ret)
-		goto err_intr;
-
-	if (ath10k_target_ps) {
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
-	} else {
-		/* Force AWAKE forever */
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
-		ath10k_do_pci_wake(ar);
-	}
-
-	ret = ath10k_pci_ce_init(ar);
-	if (ret)
-		goto err_intr;
-
-	ret = ath10k_pci_init_config(ar);
-	if (ret)
-		goto err_ce;
-
-	ret = ath10k_pci_wake_target_cpu(ar);
+	ret = ath10k_pci_hif_init(ar);
 	if (ret) {
-		ath10k_err("could not wake up target CPU (%d)\n", ret);
-		goto err_ce;
+		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_ce;
+		goto err_hif;
 	}
 
 	return 0;
 
-err_ce:
-	ath10k_pci_ce_deinit(ar);
+err_hif:
+	ath10k_pci_hif_deinit(ar);
 err_intr:
 	ath10k_pci_stop_intr(ar);
 err_iomap:
@@ -2351,7 +2383,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
 	tasklet_kill(&ar_pci->msi_fw_err);
 
 	ath10k_core_unregister(ar);
-	ath10k_pci_ce_deinit(ar);
+	ath10k_pci_hif_deinit(ar);
 	ath10k_pci_stop_intr(ar);
 
 	pci_set_drvdata(pdev, NULL);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 02/10] ath10k: decouple core start/stop logic
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 01/10] ath10k: decouple pci init/deinit logic Michal Kazior
@ 2013-06-12 12:53 ` Michal Kazior
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 03/10] ath10k: allow deferred regd update Michal Kazior
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-12 12:53 UTC (permalink / raw)
  To: ath9k-devel

Enables code reuse for proper hw reconfiguration
that is in turn required for proper
suspend/hibernation/wowlan support.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |   44 +++++++++++++++++++++++---------
 drivers/net/wireless/ath/ath10k/core.h |    2 ++
 2 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index f1312fa..dcddae4 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -502,8 +502,7 @@ void ath10k_core_destroy(struct ath10k *ar)
 }
 EXPORT_SYMBOL(ath10k_core_destroy);
 
-
-int ath10k_core_register(struct ath10k *ar)
+int ath10k_core_start(struct ath10k *ar)
 {
 	struct bmi_target_info target_info;
 	int status;
@@ -589,9 +588,36 @@ int ath10k_core_register(struct ath10k *ar)
 	if (status)
 		goto err_disconnect_htc;
 
+	return 0;
+
+err_disconnect_htc:
+	ath10k_htc_stop(&ar->htc);
+err_htt_detach:
+	ath10k_htt_detach(&ar->htt);
+err_wmi_detach:
+	ath10k_wmi_detach(ar);
+err:
+	return status;
+}
+
+void ath10k_core_stop(struct ath10k *ar)
+{
+	ath10k_htc_stop(&ar->htc);
+	ath10k_htt_detach(&ar->htt);
+	ath10k_wmi_detach(ar);
+}
+
+int ath10k_core_register(struct ath10k *ar)
+{
+	int status;
+
+	status = ath10k_core_start(ar);
+	if (status)
+		goto err;
+
 	status = ath10k_mac_register(ar);
 	if (status)
-		goto err_disconnect_htc;
+		goto err_core_stop;
 
 	status = ath10k_debug_create(ar);
 	if (status) {
@@ -603,12 +629,8 @@ int ath10k_core_register(struct ath10k *ar)
 
 err_unregister_mac:
 	ath10k_mac_unregister(ar);
-err_disconnect_htc:
-	ath10k_htc_stop(&ar->htc);
-err_htt_detach:
-	ath10k_htt_detach(&ar->htt);
-err_wmi_detach:
-	ath10k_wmi_detach(ar);
+err_core_stop:
+	ath10k_core_stop(ar);
 err:
 	return status;
 }
@@ -620,9 +642,7 @@ 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_htc_stop(&ar->htc);
-	ath10k_htt_detach(&ar->htt);
-	ath10k_wmi_detach(ar);
+	ath10k_core_stop(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 2f7065c..5a0b2ce 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -353,6 +353,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 				  const struct ath10k_hif_ops *hif_ops);
 void ath10k_core_destroy(struct ath10k *ar);
 
+int ath10k_core_start(struct ath10k *ar);
+void ath10k_core_stop(struct ath10k *ar);
 int ath10k_core_register(struct ath10k *ar);
 void ath10k_core_unregister(struct ath10k *ar);
 
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 03/10] ath10k: allow deferred regd update
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 01/10] ath10k: decouple pci init/deinit logic Michal Kazior
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 02/10] ath10k: decouple core start/stop logic Michal Kazior
@ 2013-06-12 12:53 ` Michal Kazior
  2013-06-13 18:08   ` Kalle Valo
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 04/10] ath10k: reset BMI state upon init Michal Kazior
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 72+ messages in thread
From: Michal Kazior @ 2013-06-12 12:53 UTC (permalink / raw)
  To: ath9k-devel

Regulatory domain notification hook can be called
regardless of the hw state (i.e. before start
mac80211 callback).

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.h |    7 +++++++
 drivers/net/wireless/ath/ath10k/mac.c  |   26 +++++++++++++++++++-------
 2 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5a0b2ce..844b160 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -247,6 +247,11 @@ struct ath10k_debug {
 	struct completion event_stats_compl;
 };
 
+enum ath10k_state {
+	ATH10K_STATE_OFF = 0,
+	ATH10K_STATE_ON
+};
+
 struct ath10k {
 	struct ath_common ath_common;
 	struct ieee80211_hw *hw;
@@ -344,6 +349,8 @@ struct ath10k {
 	struct completion offchan_tx_completed;
 	struct sk_buff *offchan_tx_skb;
 
+	enum ath10k_state state;
+
 #ifdef CONFIG_ATH10K_DEBUGFS
 	struct ath10k_debug debug;
 #endif
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 4867f30..f34ddb2 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1307,23 +1307,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
 	return ret;
 }
 
-static void ath10k_reg_notifier(struct wiphy *wiphy,
-				struct regulatory_request *request)
+static void ath10k_regd_update(struct ath10k *ar)
 {
-	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
 	struct reg_dmn_pair_mapping *regpair;
-	struct ath10k *ar = hw->priv;
 	int ret;
 
-	mutex_lock(&ar->conf_mutex);
-
-	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+	lockdep_assert_held(&ar->conf_mutex);
 
 	ret = ath10k_update_channel_list(ar);
 	if (ret)
 		ath10k_warn("could not update channel list (%d)\n", ret);
 
 	regpair = ar->ath_common.regulatory.regpair;
+
 	/* Target allows setting up per-band regdomain but ath_common provides
 	 * a combined one only */
 	ret = ath10k_wmi_pdev_set_regdomain(ar,
@@ -1334,7 +1330,19 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
 					    regpair->reg_5ghz_ctl);
 	if (ret)
 		ath10k_warn("could not set pdev regdomain (%d)\n", ret);
+}
 
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+				struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct ath10k *ar = hw->priv;
+
+	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+	mutex_lock(&ar->conf_mutex);
+	if (ar->state == ATH10K_STATE_ON)
+		ath10k_regd_update(ar);
 	mutex_unlock(&ar->conf_mutex);
 }
 
@@ -1729,6 +1737,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);
+
 	mutex_unlock(&ar->conf_mutex);
 	return 0;
 }
@@ -1739,6 +1750,7 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 
 	mutex_lock(&ar->conf_mutex);
 	ath10k_offchan_tx_purge(ar);
+	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
 	cancel_work_sync(&ar->offchan_tx_work);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 04/10] ath10k: reset BMI state upon init
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
                   ` (2 preceding siblings ...)
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 03/10] ath10k: allow deferred regd update Michal Kazior
@ 2013-06-12 12:53 ` Michal Kazior
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 05/10] ath10k: decouple suspend code Michal Kazior
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-12 12:53 UTC (permalink / raw)
  To: ath9k-devel

This is necessary if we want to be able to restart
hw on-the-fly.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/bmi.c  |    6 ++++++
 drivers/net/wireless/ath/ath10k/bmi.h  |    1 +
 drivers/net/wireless/ath/ath10k/core.c |    2 ++
 3 files changed, 9 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c
index aeae029..744da6d 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.c
+++ b/drivers/net/wireless/ath/ath10k/bmi.c
@@ -20,6 +20,12 @@
 #include "debug.h"
 #include "htc.h"
 
+void ath10k_bmi_start(struct ath10k *ar)
+{
+	ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
+	ar->bmi.done_sent = false;
+}
+
 int ath10k_bmi_done(struct ath10k *ar)
 {
 	struct bmi_cmd cmd;
diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h
index 32c56aa..8d81ce1 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.h
+++ b/drivers/net/wireless/ath/ath10k/bmi.h
@@ -184,6 +184,7 @@ struct bmi_target_info {
 #define BMI_CE_NUM_TO_TARG 0
 #define BMI_CE_NUM_TO_HOST 1
 
+void ath10k_bmi_start(struct ath10k *ar);
 int ath10k_bmi_done(struct ath10k *ar);
 int ath10k_bmi_get_target_info(struct ath10k *ar,
 			       struct bmi_target_info *target_info);
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index dcddae4..3d75c6a 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -507,6 +507,8 @@ int ath10k_core_start(struct ath10k *ar)
 	struct bmi_target_info target_info;
 	int status;
 
+	ath10k_bmi_start(ar);
+
 	memset(&target_info, 0, sizeof(target_info));
 	status = ath10k_bmi_get_target_info(ar, &target_info);
 	if (status)
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 05/10] ath10k: decouple suspend code
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
                   ` (3 preceding siblings ...)
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 04/10] ath10k: reset BMI state upon init Michal Kazior
@ 2013-06-12 12:53 ` Michal Kazior
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 06/10] ath10k: move free_vdev_map initialization Michal Kazior
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-12 12:53 UTC (permalink / raw)
  To: ath9k-devel

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>
---
 drivers/net/wireless/ath/ath10k/core.c |   28 -----
 drivers/net/wireless/ath/ath10k/core.h |    3 -
 drivers/net/wireless/ath/ath10k/hif.h  |   19 ++++
 drivers/net/wireless/ath/ath10k/mac.c  |   66 ++++++++++++
 drivers/net/wireless/ath/ath10k/pci.c  |  176 ++++++++++----------------------
 5 files changed, 138 insertions(+), 154 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 3d75c6a..01c1d82 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -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");
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 844b160..eca723b 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -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_ */
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 3a257d1..00bfb03 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.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 (*deinit)(struct ath10k *ar);
+
+	int (*suspend)(struct ath10k *ar);
+	int (*resume)(struct ath10k *ar);
 };
 
 
@@ -154,4 +157,20 @@ static inline void ath10k_hif_deinit(struct ath10k *ar)
 	ar->hif.ops->deinit(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_ */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index f34ddb2..23e48ca5 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -20,6 +20,7 @@
 #include <net/mac80211.h>
 #include <linux/etherdevice.h>
 
+#include "hif.h"
 #include "core.h"
 #include "debug.h"
 #include "wmi.h"
@@ -2746,6 +2747,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,
@@ -2766,6 +2828,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) { \
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 97f80e5..e0e15b1 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -1817,6 +1817,55 @@ static void ath10k_pci_hif_deinit(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,
@@ -1829,6 +1878,10 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
 	.get_free_queue_number	= ath10k_pci_hif_get_free_queue_number,
 	.init			= ath10k_pci_hif_init,
 	.deinit			= ath10k_pci_hif_deinit,
+#ifdef CONFIG_PM
+	.suspend		= ath10k_pci_hif_suspend,
+	.resume			= ath10k_pci_hif_resume,
+#endif
 };
 
 static void ath10k_pci_ce_tasklet(unsigned long ptr)
@@ -2396,128 +2449,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 = {
@@ -2525,7 +2456,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)
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 06/10] ath10k: move free_vdev_map initialization
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
                   ` (4 preceding siblings ...)
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 05/10] ath10k: decouple suspend code Michal Kazior
@ 2013-06-12 12:53 ` Michal Kazior
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 07/10] ath10k: make sure all resources are freed upon ath10k_stop() Michal Kazior
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-12 12:53 UTC (permalink / raw)
  To: ath9k-devel

This is necessary for hw reconfiguration to work.
Since mac80211 is not calling remove_interface()
is such case we must reset free_vdev_map.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 01c1d82..0e4a704 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -458,8 +458,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 	ar->hif.priv = hif_priv;
 	ar->hif.ops = hif_ops;
 
-	ar->free_vdev_map = 0xFF; /* 8 vdevs */
-
 	init_completion(&ar->scan.started);
 	init_completion(&ar->scan.completed);
 	init_completion(&ar->scan.on_channel);
@@ -590,6 +588,8 @@ int ath10k_core_start(struct ath10k *ar)
 	if (status)
 		goto err_disconnect_htc;
 
+	ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+
 	return 0;
 
 err_disconnect_htc:
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 07/10] ath10k: make sure all resources are freed upon ath10k_stop()
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
                   ` (5 preceding siblings ...)
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 06/10] ath10k: move free_vdev_map initialization Michal Kazior
@ 2013-06-12 12:53 ` Michal Kazior
  2013-06-12 12:54 ` [ath9k-devel] [PATCH v2 08/10] ath10k: defer hw setup to start/stop mac80211 hooks Michal Kazior
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-12 12:53 UTC (permalink / raw)
  To: ath9k-devel

This is necessary for proper hw reconfiguration
and to avoid memory leaks.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/mac.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 23e48ca5..a49a8bf 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -366,6 +366,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
 	spin_unlock_bh(&ar->data_lock);
 }
 
+static void ath10k_peer_cleanup_all(struct ath10k *ar)
+{
+	struct ath10k_peer *peer, *tmp;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	spin_lock_bh(&ar->data_lock);
+	list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+		list_del(&peer->list);
+		kfree(peer);
+	}
+	spin_unlock_bh(&ar->data_lock);
+}
+
 /************************/
 /* Interface management */
 /************************/
@@ -1750,7 +1764,9 @@ 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);
 	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 08/10] ath10k: defer hw setup to start/stop mac80211 hooks
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
                   ` (6 preceding siblings ...)
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 07/10] ath10k: make sure all resources are freed upon ath10k_stop() Michal Kazior
@ 2013-06-12 12:54 ` Michal Kazior
  2013-06-14 12:23   ` Kalle Valo
  2013-06-12 12:54 ` [ath9k-devel] [PATCH v2 09/10] ath10k: skip updating some params during resume Michal Kazior
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 72+ messages in thread
From: Michal Kazior @ 2013-06-12 12:54 UTC (permalink / raw)
  To: ath9k-devel

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>
---
 drivers/net/wireless/ath/ath10k/core.c |   46 +++++++++++++++++++++++++-------
 drivers/net/wireless/ath/ath10k/core.h |    1 +
 drivers/net/wireless/ath/ath10k/mac.c  |   45 +++++++++++++++++++++++++++----
 drivers/net/wireless/ath/ath10k/pci.c  |   10 +------
 4 files changed, 79 insertions(+), 23 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 0e4a704..97bf27a 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -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_init(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_deinit(ar);
+		return ret;
+	}
+
+	ath10k_core_stop(ar);
+	ath10k_hif_deinit(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);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index eca723b..5224a84 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -249,6 +249,7 @@ struct ath10k_debug {
 
 enum ath10k_state {
 	ATH10K_STATE_OFF = 0,
+	ATH10K_STATE_RESTARTING,
 	ATH10K_STATE_ON
 };
 
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index a49a8bf..ebdaf1a 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1735,13 +1735,50 @@ static void ath10k_tx(struct ieee80211_hw *hw,
 /*
  * Initialize various parameters with default vaules.
  */
+static void ath10k_halt(struct ath10k *ar)
+{
+	if (ar->state == ATH10K_STATE_OFF)
+		return;
+
+	del_timer_sync(&ar->scan.timeout);
+	ath10k_offchan_tx_purge(ar);
+	ath10k_peer_cleanup_all(ar);
+	ath10k_core_stop(ar);
+	ath10k_hif_deinit(ar);
+}
+
 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) {
+		/* This can be called in case if resume fails or
+		 * ieee80211_hw_restart() was issued */
+		ath10k_info("hw reconfiguration started\n");
+		ath10k_halt(ar);
+		ar->state = ATH10K_STATE_RESTARTING;
+	} else {
+		ar->state = ATH10K_STATE_ON;
+	}
+
+	ret = ath10k_hif_init(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_deinit(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",
@@ -1752,9 +1789,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;
 }
@@ -1764,9 +1801,7 @@ 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);
+	ath10k_halt(ar);
 	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index e0e15b1..e9a6572 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -2382,22 +2382,14 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 		goto err_iomap;
 	}
 
-	ret = ath10k_pci_hif_init(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_deinit(ar);
 err_intr:
 	ath10k_pci_stop_intr(ar);
 err_iomap:
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 09/10] ath10k: skip updating some params during resume
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
                   ` (7 preceding siblings ...)
  2013-06-12 12:54 ` [ath9k-devel] [PATCH v2 08/10] ath10k: defer hw setup to start/stop mac80211 hooks Michal Kazior
@ 2013-06-12 12:54 ` Michal Kazior
  2013-06-12 12:54 ` [ath9k-devel] [PATCH v2 10/10] ath10k: store firmware files in memory Michal Kazior
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-12 12:54 UTC (permalink / raw)
  To: ath9k-devel

ath10k handles some hw configuration per-vdev
(i.e. per-vif). When resuming/restarting hw
configuration vdevs may not be re-added yet.

Setting vdev params for non existent vdevs crashes
the firmware.

The parameters are updated when interface is
re-added.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/mac.c |   55 ++++++++++++++++++++++++---------
 1 file changed, 41 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index ebdaf1a..c64e7be 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1808,13 +1808,35 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 	cancel_work_sync(&ar->offchan_tx_work);
 }
 
-static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+static void ath10k_config_ps(struct ath10k *ar)
 {
 	struct ath10k_generic_iter ar_iter;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar->state == ATH10K_STATE_RESTARTING)
+		return;
+
+	memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+	ar_iter.ar = ar;
+
+	ieee80211_iterate_active_interfaces_atomic(
+		ar->hw, IEEE80211_IFACE_ITER_NORMAL,
+		ath10k_ps_iter, &ar_iter);
+
+	if (ar_iter.ret)
+		ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
 	struct ath10k *ar = hw->priv;
 	struct ieee80211_conf *conf = &hw->conf;
 	int ret = 0;
-	u32 flags;
 
 	mutex_lock(&ar->conf_mutex);
 
@@ -1826,18 +1848,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 		spin_unlock_bh(&ar->data_lock);
 	}
 
-	if (changed & IEEE80211_CONF_CHANGE_PS) {
-		memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-		ar_iter.ar = ar;
-		flags = IEEE80211_IFACE_ITER_RESUME_ALL;
-
-		ieee80211_iterate_active_interfaces_atomic(hw,
-							   flags,
-							   ath10k_ps_iter,
-							   &ar_iter);
-
-		ret = ar_iter.ret;
-	}
+	if (changed & IEEE80211_CONF_CHANGE_PS)
+		ath10k_config_ps(ar);
 
 	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
 		if (conf->flags & IEEE80211_CONF_MONITOR)
@@ -1846,6 +1858,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 			ret = ath10k_monitor_destroy(ar);
 	}
 
+	ath10k_wmi_flush_tx(ar);
 	mutex_unlock(&ar->conf_mutex);
 	return ret;
 }
@@ -2688,6 +2701,13 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 
 	lockdep_assert_held(&arvif->ar->conf_mutex);
 
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar_iter->ar->state == ATH10K_STATE_RESTARTING)
+		return;
+
 	rts = min_t(u32, rts, ATH10K_RTS_MAX);
 
 	ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
@@ -2728,6 +2748,13 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 
 	lockdep_assert_held(&arvif->ar->conf_mutex);
 
+	/* During HW reconfiguration mac80211 reports all interfaces that were
+	 * running until reconfiguration was started. Since FW doesn't have any
+	 * vdevs at this point we must not iterate over this interface list.
+	 * This setting will be updated upon add_interface(). */
+	if (ar_iter->ar->state == ATH10K_STATE_RESTARTING)
+		return;
+
 	frag = clamp_t(u32, frag,
 		       ATH10K_FRAGMT_THRESHOLD_MIN,
 		       ATH10K_FRAGMT_THRESHOLD_MAX);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 10/10] ath10k: store firmware files in memory
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
                   ` (8 preceding siblings ...)
  2013-06-12 12:54 ` [ath9k-devel] [PATCH v2 09/10] ath10k: skip updating some params during resume Michal Kazior
@ 2013-06-12 12:54 ` Michal Kazior
  2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-12 12:54 UTC (permalink / raw)
  To: ath9k-devel

Different FW versions may provide different
functions thus mean different hw capabilities
advertised to mac80211.

It is safe to swap firmware files on disk during
driver/device runtime without worries.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |  157 +++++++++++++++++++++-----------
 drivers/net/wireless/ath/ath10k/core.h |    4 +
 2 files changed, 109 insertions(+), 52 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 97bf27a..94230fb 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
 
 static int ath10k_download_board_data(struct ath10k *ar)
 {
+	const struct firmware *fw = ar->board_data;
 	u32 board_data_size = QCA988X_BOARD_DATA_SZ;
 	u32 address;
-	const struct firmware *fw;
 	int ret;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.board);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch board data fw file (%ld)\n",
-			   PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
 	ret = ath10k_push_board_ext_data(ar, fw);
 	if (ret) {
 		ath10k_err("could not push board ext data (%d)\n", ret);
@@ -286,32 +278,20 @@ static int ath10k_download_board_data(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-	const struct firmware *fw;
-	u32 address;
+	const struct firmware *fw = ar->otp;
+	u32 address = ar->hw_params.patch_load_addr;
 	u32 exec_param;
 	int ret;
 
 	/* OTP is optional */
 
-	if (ar->hw_params.fw.otp == NULL) {
-		ath10k_info("otp file not defined\n");
-		return 0;
-	}
-
-	address = ar->hw_params.patch_load_addr;
-
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.otp);
-	if (IS_ERR(fw)) {
-		ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+	if (!ar->otp)
 		return 0;
-	}
 
 	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
 	if (ret) {
@@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_fw(struct ath10k *ar)
 {
-	const struct firmware *fw;
+	const struct firmware *fw = ar->firmware;
 	u32 address;
 	int ret;
 
-	if (ar->hw_params.fw.fw == NULL)
-		return -EINVAL;
-
 	address = ar->hw_params.patch_load_addr;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.fw);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
 	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
 	if (ret) {
 		ath10k_err("could not write fw (%d)\n", ret);
@@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
+	return ret;
+}
+
+static void ath10k_core_free_firmware_files(struct ath10k *ar)
+{
+	if (ar->board_data && !IS_ERR(ar->board_data))
+		release_firmware(ar->board_data);
+
+	if (ar->otp && !IS_ERR(ar->otp))
+		release_firmware(ar->otp);
+
+	if (ar->firmware && !IS_ERR(ar->firmware))
+		release_firmware(ar->firmware);
+
+	ar->board_data = NULL;
+	ar->otp = NULL;
+	ar->firmware = NULL;
+}
+
+static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+{
+	int ret = 0;
+
+	if (ar->hw_params.fw.fw == NULL) {
+		ath10k_err("firmware file not defined\n");
+		return -EINVAL;
+	}
+
+	if (ar->hw_params.fw.board == NULL) {
+		ath10k_err("board data file not defined");
+		return -EINVAL;
+	}
+
+	ar->board_data = ath10k_fetch_fw_file(ar,
+					      ar->hw_params.fw.dir,
+					      ar->hw_params.fw.board);
+	if (IS_ERR(ar->board_data)) {
+		ret = PTR_ERR(ar->board_data);
+		ath10k_err("could not fetch board data (%d)\n", ret);
+		goto err;
+	}
+
+	ar->firmware = ath10k_fetch_fw_file(ar,
+					    ar->hw_params.fw.dir,
+					    ar->hw_params.fw.fw);
+	if (IS_ERR(ar->firmware)) {
+		ret = PTR_ERR(ar->firmware);
+		ath10k_err("could not fetch firmware (%d)\n", ret);
+		goto err;
+	}
+
+	/* OTP may be undefined. If so, don't fetch it at all */
+	if (ar->hw_params.fw.otp == NULL)
+		return 0;
+
+	ar->otp = ath10k_fetch_fw_file(ar,
+				       ar->hw_params.fw.dir,
+				       ar->hw_params.fw.otp);
+	if (IS_ERR(ar->otp)) {
+		ret = PTR_ERR(ar->otp);
+		ath10k_err("could not fetch otp (%d)\n", ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	ath10k_core_free_firmware_files(ar);
 	return ret;
 }
 
@@ -502,23 +538,10 @@ EXPORT_SYMBOL(ath10k_core_destroy);
 
 int ath10k_core_start(struct ath10k *ar)
 {
-	struct bmi_target_info target_info;
 	int status;
 
 	ath10k_bmi_start(ar);
 
-	memset(&target_info, 0, sizeof(target_info));
-	status = ath10k_bmi_get_target_info(ar, &target_info);
-	if (status)
-		goto err;
-
-	ar->target_version = target_info.version;
-	ar->hw->wiphy->hw_version = target_info.version;
-
-	status = ath10k_init_hw_params(ar);
-	if (status)
-		goto err;
-
 	if (ath10k_init_configure_target(ar)) {
 		status = -EINVAL;
 		goto err;
@@ -617,7 +640,8 @@ EXPORT_SYMBOL(ath10k_core_stop);
  * hook will try to init it again) before registering */
 static int ath10k_core_probe_fw(struct ath10k *ar)
 {
-	int ret;
+	struct bmi_target_info target_info;
+	int ret = 0;
 
 	ret = ath10k_hif_init(ar);
 	if (ret) {
@@ -625,9 +649,35 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
 		return ret;
 	}
 
+	memset(&target_info, 0, sizeof(target_info));
+	ret = ath10k_bmi_get_target_info(ar, &target_info);
+	if (ret) {
+		ath10k_err("could not get target info (%d)\n", ret);
+		ath10k_hif_deinit(ar);
+		return ret;
+	}
+
+	ar->target_version = target_info.version;
+	ar->hw->wiphy->hw_version = target_info.version;
+
+	ret = ath10k_init_hw_params(ar);
+	if (ret) {
+		ath10k_err("could not get hw params (%d)\n", ret);
+		ath10k_hif_deinit(ar);
+		return ret;
+	}
+
+	ret = ath10k_core_fetch_firmware_files(ar);
+	if (ret) {
+		ath10k_err("could not fetch firmware files (%d)\n", ret);
+		ath10k_hif_deinit(ar);
+		return ret;
+	}
+
 	ret = ath10k_core_start(ar);
 	if (ret) {
 		ath10k_err("could not init core (%d)\n", ret);
+		ath10k_core_free_firmware_files(ar);
 		ath10k_hif_deinit(ar);
 		return ret;
 	}
@@ -650,7 +700,7 @@ int ath10k_core_register(struct ath10k *ar)
 	status = ath10k_mac_register(ar);
 	if (status) {
 		ath10k_err("could not register to mac80211 (%d)\n", status);
-		return status;
+		goto err_release_fw;
 	}
 
 	status = ath10k_debug_create(ar);
@@ -663,6 +713,8 @@ int ath10k_core_register(struct ath10k *ar)
 
 err_unregister_mac:
 	ath10k_mac_unregister(ar);
+err_release_fw:
+	ath10k_core_free_firmware_files(ar);
 	return status;
 }
 EXPORT_SYMBOL(ath10k_core_register);
@@ -673,6 +725,7 @@ 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_free_firmware_files(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5224a84..ed672f3 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -301,6 +301,10 @@ struct ath10k {
 		} fw;
 	} hw_params;
 
+	const struct firmware *board_data;
+	const struct firmware *otp;
+	const struct firmware *firmware;
+
 	struct {
 		struct completion started;
 		struct completion completed;
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 03/10] ath10k: allow deferred regd update
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 03/10] ath10k: allow deferred regd update Michal Kazior
@ 2013-06-13 18:08   ` Kalle Valo
  2013-06-14 12:03     ` Michal Kazior
  0 siblings, 1 reply; 72+ messages in thread
From: Kalle Valo @ 2013-06-13 18:08 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior <michal.kazior@tieto.com> writes:

> Regulatory domain notification hook can be called
> regardless of the hw state (i.e. before start
> mac80211 callback).

We should have the same check also in debugfs files, right?

(No need to change this patch, can be done in a followup patch.)

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 01/10] ath10k: decouple pci init/deinit logic
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 01/10] ath10k: decouple pci init/deinit logic Michal Kazior
@ 2013-06-13 18:09   ` Kalle Valo
  2013-06-14 12:02     ` Michal Kazior
  0 siblings, 1 reply; 72+ messages in thread
From: Kalle Valo @ 2013-06-13 18:09 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior <michal.kazior@tieto.com> writes:

> Split logic that prepares the device for BMI
> phase/cleans up related resources.
>
> This is necessary for ath10k to be able to restart
> hw on the fly without reloading the module.
>
> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>

Few comments:

> --- a/drivers/net/wireless/ath/ath10k/hif.h
> +++ b/drivers/net/wireless/ath/ath10k/hif.h
> @@ -46,8 +46,11 @@ struct ath10k_hif_ops {
>  				void *request, u32 request_len,
>  				void *response, u32 *response_len);
>  
> +	/* Post BMI phase, after FW is loaded. Starts regular operation */
>  	int (*start)(struct ath10k *ar);
>  
> +	/* Clean up what start() did. This does not revert to BMI phase. If
> +	 * desired so, call deinit() and init() */
>  	void (*stop)(struct ath10k *ar);
>  
>  	int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
> @@ -70,6 +73,13 @@ struct ath10k_hif_ops {
>  			      struct ath10k_hif_cb *callbacks);
>  
>  	u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
> +
> +	/* Power up the device and enter BMI transfer mode for FW download */
> +	int (*init)(struct ath10k *ar);
> +
> +	/* Power down the device and free up resources. stop() must be called
> +	 * before this if start() was called earlier */
> +	void (*deinit)(struct ath10k *ar);

I think terminology is mixed here as well. To me init() does here a lot
more than other init() functions in ath10k. Should we rename these to
power_up() and power_down(), as how your documentation already uses
those terms?

So when booting the firwmware we call:

hif_power_up()
hif_start()

and when we want to kill the firmware we do:

hif_stop()
hif_power_down()

[...]

> +static int ath10k_pci_hif_init(struct ath10k *ar)
> +{
> +	int ret;
> +
> +	/*
> +	 * Bring the target up cleanly.
> +	 *
> +	 * The target may be in an undefined state with an AUX-powered Target
> +	 * and a Host in WoW mode. If the Host crashes, loses power, or is
> +	 * restarted (without unloading the driver) then the Target is left
> +	 * (aux) powered and running. On a subsequent driver load, the Target
> +	 * is in an unexpected state. We try to catch that here in order to
> +	 * reset the Target and retry the probe.
> +	 */
> +	ath10k_pci_device_reset(ar);
> +
> +	ret = ath10k_pci_reset_target(ar);
> +	if (ret)
> +		goto err;
> +
> +	if (ath10k_target_ps) {
> +		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
> +	} else {
> +		/* Force AWAKE forever */
> +		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
> +		ath10k_do_pci_wake(ar);
> +	}
> +
> +	ret = ath10k_pci_ce_init(ar);
> +	if (ret)
> +		goto err_ps;

I noticed ath10k_pci_ce_init() also allocs host memory etc. If possible,
in the future we might want to refactor the function into two:
ath10k_pci_ce_init() and ath10k_pci_ce_start(). And the former would be
called only from ath10k_pci_probe(). That way we would not need to do
any memory allocation during start time.

But no need to refactor it right now, this is good enough for the first
implementation.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 01/10] ath10k: decouple pci init/deinit logic
  2013-06-13 18:09   ` Kalle Valo
@ 2013-06-14 12:02     ` Michal Kazior
  2013-06-14 12:14       ` Kalle Valo
  0 siblings, 1 reply; 72+ messages in thread
From: Michal Kazior @ 2013-06-14 12:02 UTC (permalink / raw)
  To: ath9k-devel

On 13/06/13 20:09, Kalle Valo wrote:
> So when booting the firwmware we call:
>
> hif_power_up()
> hif_start()
>
> and when we want to kill the firmware we do:
>
> hif_stop()
> hif_power_down()
>
> [...]

You're right. I'll fix that.


> I noticed ath10k_pci_ce_init() also allocs host memory etc. If possible,
> in the future we might want to refactor the function into two:
> ath10k_pci_ce_init() and ath10k_pci_ce_start(). And the former would be
> called only from ath10k_pci_probe(). That way we would not need to do
> any memory allocation during start time.
>
> But no need to refactor it right now, this is good enough for the first
> implementation.

There's also some an asymmetry here.

We should be enabling/disabling irqs when we start/stop, shouldn't we? 
But then again we need interrupts _before_ start() for BMI phase, while 
it's a also a good idea to disable them upon stop(). We do stop CE 
interrupts by poking up device registers, but we don't unregister irq 
handlers. There might be a race in there that we're not aware 
of/observed yet.

I'm thinking of having only 3 functions: start_bmi_phase, 
start_htc_phase, stop (that would handle either state transition). But 
that would require the aforementioned proper alloc/init/setup separation 
to be done first.


-- Pozdrawiam / Best regards, Michal Kazior.

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 03/10] ath10k: allow deferred regd update
  2013-06-13 18:08   ` Kalle Valo
@ 2013-06-14 12:03     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-14 12:03 UTC (permalink / raw)
  To: ath9k-devel

On 13/06/13 20:08, Kalle Valo wrote:
> Michal Kazior <michal.kazior@tieto.com> writes:
>
>> Regulatory domain notification hook can be called
>> regardless of the hw state (i.e. before start
>> mac80211 callback).
>
> We should have the same check also in debugfs files, right?

Good catch.

As always, thanks for the review!


-- Pozdrawiam / Best regards, Michal Kazior.

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 01/10] ath10k: decouple pci init/deinit logic
  2013-06-14 12:02     ` Michal Kazior
@ 2013-06-14 12:14       ` Kalle Valo
  0 siblings, 0 replies; 72+ messages in thread
From: Kalle Valo @ 2013-06-14 12:14 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior <michal.kazior@tieto.com> writes:

>> I noticed ath10k_pci_ce_init() also allocs host memory etc. If possible,
>> in the future we might want to refactor the function into two:
>> ath10k_pci_ce_init() and ath10k_pci_ce_start(). And the former would be
>> called only from ath10k_pci_probe(). That way we would not need to do
>> any memory allocation during start time.
>>
>> But no need to refactor it right now, this is good enough for the first
>> implementation.
>
> There's also some an asymmetry here.
>
> We should be enabling/disabling irqs when we start/stop, shouldn't we?
> But then again we need interrupts _before_ start() for BMI phase,
> while it's a also a good idea to disable them upon stop(). We do stop
> CE interrupts by poking up device registers, but we don't unregister
> irq handlers. There might be a race in there that we're not aware
> of/observed yet.

Hehe, our emails crossed. I was thinking exactly the same issue in my
previous email. So I agree, disabling interrupts from host would be good
to have. But that's again for the future.

> I'm thinking of having only 3 functions: start_bmi_phase,
> start_htc_phase, stop (that would handle either state transition). But
> that would require the aforementioned proper alloc/init/setup
> separation to be done first.

Yeah, we can discuss about this later.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 08/10] ath10k: defer hw setup to start/stop mac80211 hooks
  2013-06-12 12:54 ` [ath9k-devel] [PATCH v2 08/10] ath10k: defer hw setup to start/stop mac80211 hooks Michal Kazior
@ 2013-06-14 12:23   ` Kalle Valo
  2013-06-14 12:43     ` Michal Kazior
  0 siblings, 1 reply; 72+ messages in thread
From: Kalle Valo @ 2013-06-14 12:23 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior <michal.kazior@tieto.com> writes:

> 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>

[...]

>  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) {
> +		/* This can be called in case if resume fails or
> +		 * ieee80211_hw_restart() was issued */
> +		ath10k_info("hw reconfiguration started\n");
> +		ath10k_halt(ar);
> +		ar->state = ATH10K_STATE_RESTARTING;
> +	} else {
> +		ar->state = ATH10K_STATE_ON;
> +	}

This part still worries me, especially that mac80211 calls
ath10k_start() when state != OFF. To me it looks like that ath10k is not
cleaning up it's somewhere else (in resume path?), or mac80211 is
buggy/strange.

Also I didn't fully get when state changes from RESTARTING to ON. Can
you describe that, please?

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v2 08/10] ath10k: defer hw setup to start/stop mac80211 hooks
  2013-06-14 12:23   ` Kalle Valo
@ 2013-06-14 12:43     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-14 12:43 UTC (permalink / raw)
  To: ath9k-devel

On 14/06/13 14:23, Kalle Valo wrote:
> Michal Kazior <michal.kazior@tieto.com> writes:
> [...]
>
>>   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) {
>> +		/* This can be called in case if resume fails or
>> +		 * ieee80211_hw_restart() was issued */
>> +		ath10k_info("hw reconfiguration started\n");
>> +		ath10k_halt(ar);
>> +		ar->state = ATH10K_STATE_RESTARTING;
>> +	} else {
>> +		ar->state = ATH10K_STATE_ON;
>> +	}
>
> This part still worries me, especially that mac80211 calls
> ath10k_start() when state != OFF. To me it looks like that ath10k is not
> cleaning up it's somewhere else (in resume path?), or mac80211 is
> buggy/strange.

Hmm, you're right. I'll move ath10k_halt() call to ath10k_core_restart() 
and ath10k_resume() separately.


> Also I didn't fully get when state changes from RESTARTING to ON. Can
> you describe that, please?

When mac80211 calls restat_complete() we switch it to ON if it isn't 
WEDGED. WEDGED can be set in ath10k_core_restart() if ar->state is 
already RESTARTING to avoid recursive recovery.


-- Pozdrawiam / Best regards, Michal Kazior.

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
                   ` (9 preceding siblings ...)
  2013-06-12 12:54 ` [ath9k-devel] [PATCH v2 10/10] ath10k: store firmware files in memory Michal Kazior
@ 2013-06-25  7:59 ` Michal Kazior
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 1/9] ath10k: decouple pci start/stop logic Michal Kazior
                     ` (8 more replies)
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
  2013-07-16  7:38   ` Michal Kazior
  12 siblings, 9 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-25  7:59 UTC (permalink / raw)
  To: ath9k-devel

This is a respin of my patches. I've addressed
issues raised in the previous review round.

This is groundwork for proper hw recovery that I'm
going to post too.

I squashed the `ath10k: skip updating some params
during resume` from v2 into firmware recovery
patchset since it depends on some bits from that
patchset.

Michal Kazior (9):
  ath10k: decouple pci start/stop logic
  ath10k: decouple core start/stop logic
  ath10k: allow deferred regd update
  ath10k: reset BMI state upon init
  ath10k: decouple suspend code
  ath10k: move free_vdev_map initialization
  ath10k: make sure all resources are freed upon ath10k_stop()
  ath10k: defer hw setup to start/stop mac80211 hooks
  ath10k: store firmware files in memory

 drivers/net/wireless/ath/ath10k/bmi.c  |    6 +
 drivers/net/wireless/ath/ath10k/bmi.h  |    1 +
 drivers/net/wireless/ath/ath10k/core.c |  249 ++++++++++++++++++----------
 drivers/net/wireless/ath/ath10k/core.h |   16 +-
 drivers/net/wireless/ath/ath10k/hif.h  |   39 +++++
 drivers/net/wireless/ath/ath10k/mac.c  |  151 ++++++++++++++++-
 drivers/net/wireless/ath/ath10k/pci.c  |  283 +++++++++++++-------------------
 7 files changed, 481 insertions(+), 264 deletions(-)

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 1/9] ath10k: decouple pci start/stop logic
  2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
@ 2013-06-25  7:59   ` Michal Kazior
  2013-07-05  6:29     ` Kalle Valo
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 2/9] ath10k: decouple core " Michal Kazior
                     ` (7 subsequent siblings)
  8 siblings, 1 reply; 72+ messages in thread
From: Michal Kazior @ 2013-06-25  7:59 UTC (permalink / raw)
  To: ath9k-devel

Split logic that prepares the device for BMI
phase/cleans up related resources.

This is necessary for ath10k to be able to restart
hw on the fly without reloading the module.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/hif.h |   20 ++++++
 drivers/net/wireless/ath/ath10k/pci.c |  110 +++++++++++++++++++++------------
 2 files changed, 91 insertions(+), 39 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 010e265..b9d0927 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -46,8 +46,11 @@ struct ath10k_hif_ops {
 				void *request, u32 request_len,
 				void *response, u32 *response_len);
 
+	/* Post BMI phase, after FW is loaded. Starts regular operation */
 	int (*start)(struct ath10k *ar);
 
+	/* Clean up what start() did. This does not revert to BMI phase. If
+	 * desired so, call deinit() and init() */
 	void (*stop)(struct ath10k *ar);
 
 	int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
@@ -70,6 +73,13 @@ struct ath10k_hif_ops {
 			      struct ath10k_hif_cb *callbacks);
 
 	u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+
+	/* Power up the device and enter BMI transfer mode for FW download */
+	int (*power_up)(struct ath10k *ar);
+
+	/* 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);
 };
 
 
@@ -134,4 +144,14 @@ static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
 	return ar->hif.ops->get_free_queue_number(ar, pipe_id);
 }
 
+static inline int ath10k_hif_power_up(struct ath10k *ar)
+{
+	return ar->hif.ops->power_up(ar);
+}
+
+static inline void ath10k_hif_power_down(struct ath10k *ar)
+{
+	ar->hif.ops->power_down(ar);
+}
+
 #endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 1a59638..cd065e0 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -54,6 +54,8 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
 					     int num);
 static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
 static void ath10k_pci_stop_ce(struct ath10k *ar);
+static void ath10k_pci_device_reset(struct ath10k *ar);
+static int ath10k_pci_reset_target(struct ath10k *ar);
 
 static const struct ce_attr host_ce_config_wlan[] = {
 	/* host->target HTC control and raw streams */
@@ -1734,6 +1736,66 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
 	ath10k_pci_sleep(ar);
 }
 
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
+{
+	int ret;
+
+	/*
+	 * Bring the target up cleanly.
+	 *
+	 * The target may be in an undefined state with an AUX-powered Target
+	 * and a Host in WoW mode. If the Host crashes, loses power, or is
+	 * restarted (without unloading the driver) then the Target is left
+	 * (aux) powered and running. On a subsequent driver load, the Target
+	 * is in an unexpected state. We try to catch that here in order to
+	 * reset the Target and retry the probe.
+	 */
+	ath10k_pci_device_reset(ar);
+
+	ret = ath10k_pci_reset_target(ar);
+	if (ret)
+		goto err;
+
+	if (ath10k_target_ps) {
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
+	} else {
+		/* Force AWAKE forever */
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
+		ath10k_do_pci_wake(ar);
+	}
+
+	ret = ath10k_pci_ce_init(ar);
+	if (ret)
+		goto err_ps;
+
+	ret = ath10k_pci_init_config(ar);
+	if (ret)
+		goto err_ce;
+
+	ret = ath10k_pci_wake_target_cpu(ar);
+	if (ret) {
+		ath10k_err("could not wake up target CPU (%d)\n", ret);
+		goto err_ce;
+	}
+
+	return 0;
+
+err_ce:
+	ath10k_pci_ce_deinit(ar);
+err_ps:
+	if (!ath10k_target_ps)
+		ath10k_do_pci_sleep(ar);
+err:
+	return ret;
+}
+
+static void ath10k_pci_hif_power_down(struct ath10k *ar)
+{
+	ath10k_pci_ce_deinit(ar);
+	if (!ath10k_target_ps)
+		ath10k_do_pci_sleep(ar);
+}
+
 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,
@@ -1744,6 +1806,8 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
 	.send_complete_check	= ath10k_pci_hif_send_complete_check,
 	.set_callbacks		= ath10k_pci_hif_set_callbacks,
 	.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,
 };
 
 static void ath10k_pci_ce_tasklet(unsigned long ptr)
@@ -2244,54 +2308,22 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 		goto err_iomap;
 	}
 
-	/*
-	 * Bring the target up cleanly.
-	 *
-	 * The target may be in an undefined state with an AUX-powered Target
-	 * and a Host in WoW mode. If the Host crashes, loses power, or is
-	 * restarted (without unloading the driver) then the Target is left
-	 * (aux) powered and running. On a subsequent driver load, the Target
-	 * is in an unexpected state. We try to catch that here in order to
-	 * reset the Target and retry the probe.
-	 */
-	ath10k_pci_device_reset(ar);
-
-	ret = ath10k_pci_reset_target(ar);
-	if (ret)
-		goto err_intr;
-
-	if (ath10k_target_ps) {
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
-	} else {
-		/* Force AWAKE forever */
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
-		ath10k_do_pci_wake(ar);
-	}
-
-	ret = ath10k_pci_ce_init(ar);
-	if (ret)
-		goto err_intr;
-
-	ret = ath10k_pci_init_config(ar);
-	if (ret)
-		goto err_ce;
-
-	ret = ath10k_pci_wake_target_cpu(ar);
+	ret = ath10k_pci_hif_power_up(ar);
 	if (ret) {
-		ath10k_err("could not wake up target CPU (%d)\n", ret);
-		goto err_ce;
+		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_ce;
+		goto err_hif;
 	}
 
 	return 0;
 
-err_ce:
-	ath10k_pci_ce_deinit(ar);
+err_hif:
+	ath10k_pci_hif_power_down(ar);
 err_intr:
 	ath10k_pci_stop_intr(ar);
 err_iomap:
@@ -2330,7 +2362,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
 	tasklet_kill(&ar_pci->msi_fw_err);
 
 	ath10k_core_unregister(ar);
-	ath10k_pci_ce_deinit(ar);
+	ath10k_pci_hif_power_down(ar);
 	ath10k_pci_stop_intr(ar);
 
 	pci_set_drvdata(pdev, NULL);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 2/9] ath10k: decouple core start/stop logic
  2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 1/9] ath10k: decouple pci start/stop logic Michal Kazior
@ 2013-06-25  7:59   ` Michal Kazior
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 3/9] ath10k: allow deferred regd update Michal Kazior
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-25  7:59 UTC (permalink / raw)
  To: ath9k-devel

Enables code reuse for proper hw reconfiguration
that is in turn required for proper
suspend/hibernation/wowlan support.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |   44 +++++++++++++++++++++++---------
 drivers/net/wireless/ath/ath10k/core.h |    2 ++
 2 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index f1312fa..dcddae4 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -502,8 +502,7 @@ void ath10k_core_destroy(struct ath10k *ar)
 }
 EXPORT_SYMBOL(ath10k_core_destroy);
 
-
-int ath10k_core_register(struct ath10k *ar)
+int ath10k_core_start(struct ath10k *ar)
 {
 	struct bmi_target_info target_info;
 	int status;
@@ -589,9 +588,36 @@ int ath10k_core_register(struct ath10k *ar)
 	if (status)
 		goto err_disconnect_htc;
 
+	return 0;
+
+err_disconnect_htc:
+	ath10k_htc_stop(&ar->htc);
+err_htt_detach:
+	ath10k_htt_detach(&ar->htt);
+err_wmi_detach:
+	ath10k_wmi_detach(ar);
+err:
+	return status;
+}
+
+void ath10k_core_stop(struct ath10k *ar)
+{
+	ath10k_htc_stop(&ar->htc);
+	ath10k_htt_detach(&ar->htt);
+	ath10k_wmi_detach(ar);
+}
+
+int ath10k_core_register(struct ath10k *ar)
+{
+	int status;
+
+	status = ath10k_core_start(ar);
+	if (status)
+		goto err;
+
 	status = ath10k_mac_register(ar);
 	if (status)
-		goto err_disconnect_htc;
+		goto err_core_stop;
 
 	status = ath10k_debug_create(ar);
 	if (status) {
@@ -603,12 +629,8 @@ int ath10k_core_register(struct ath10k *ar)
 
 err_unregister_mac:
 	ath10k_mac_unregister(ar);
-err_disconnect_htc:
-	ath10k_htc_stop(&ar->htc);
-err_htt_detach:
-	ath10k_htt_detach(&ar->htt);
-err_wmi_detach:
-	ath10k_wmi_detach(ar);
+err_core_stop:
+	ath10k_core_stop(ar);
 err:
 	return status;
 }
@@ -620,9 +642,7 @@ 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_htc_stop(&ar->htc);
-	ath10k_htt_detach(&ar->htt);
-	ath10k_wmi_detach(ar);
+	ath10k_core_stop(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 2f7065c..5a0b2ce 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -353,6 +353,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 				  const struct ath10k_hif_ops *hif_ops);
 void ath10k_core_destroy(struct ath10k *ar);
 
+int ath10k_core_start(struct ath10k *ar);
+void ath10k_core_stop(struct ath10k *ar);
 int ath10k_core_register(struct ath10k *ar);
 void ath10k_core_unregister(struct ath10k *ar);
 
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 3/9] ath10k: allow deferred regd update
  2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 1/9] ath10k: decouple pci start/stop logic Michal Kazior
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 2/9] ath10k: decouple core " Michal Kazior
@ 2013-06-25  7:59   ` Michal Kazior
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 4/9] ath10k: reset BMI state upon init Michal Kazior
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-25  7:59 UTC (permalink / raw)
  To: ath9k-devel

Regulatory domain notification hook can be called
regardless of the hw state (i.e. before start
mac80211 callback).

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.h |    7 +++++++
 drivers/net/wireless/ath/ath10k/mac.c  |   26 +++++++++++++++++++-------
 2 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5a0b2ce..d5b2533 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -247,6 +247,11 @@ struct ath10k_debug {
 	struct completion event_stats_compl;
 };
 
+enum ath10k_state {
+	ATH10K_STATE_OFF = 0,
+	ATH10K_STATE_ON,
+};
+
 struct ath10k {
 	struct ath_common ath_common;
 	struct ieee80211_hw *hw;
@@ -344,6 +349,8 @@ struct ath10k {
 	struct completion offchan_tx_completed;
 	struct sk_buff *offchan_tx_skb;
 
+	enum ath10k_state state;
+
 #ifdef CONFIG_ATH10K_DEBUGFS
 	struct ath10k_debug debug;
 #endif
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 4867f30..f34ddb2 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1307,23 +1307,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
 	return ret;
 }
 
-static void ath10k_reg_notifier(struct wiphy *wiphy,
-				struct regulatory_request *request)
+static void ath10k_regd_update(struct ath10k *ar)
 {
-	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
 	struct reg_dmn_pair_mapping *regpair;
-	struct ath10k *ar = hw->priv;
 	int ret;
 
-	mutex_lock(&ar->conf_mutex);
-
-	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+	lockdep_assert_held(&ar->conf_mutex);
 
 	ret = ath10k_update_channel_list(ar);
 	if (ret)
 		ath10k_warn("could not update channel list (%d)\n", ret);
 
 	regpair = ar->ath_common.regulatory.regpair;
+
 	/* Target allows setting up per-band regdomain but ath_common provides
 	 * a combined one only */
 	ret = ath10k_wmi_pdev_set_regdomain(ar,
@@ -1334,7 +1330,19 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
 					    regpair->reg_5ghz_ctl);
 	if (ret)
 		ath10k_warn("could not set pdev regdomain (%d)\n", ret);
+}
 
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+				struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct ath10k *ar = hw->priv;
+
+	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+	mutex_lock(&ar->conf_mutex);
+	if (ar->state == ATH10K_STATE_ON)
+		ath10k_regd_update(ar);
 	mutex_unlock(&ar->conf_mutex);
 }
 
@@ -1729,6 +1737,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);
+
 	mutex_unlock(&ar->conf_mutex);
 	return 0;
 }
@@ -1739,6 +1750,7 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 
 	mutex_lock(&ar->conf_mutex);
 	ath10k_offchan_tx_purge(ar);
+	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
 	cancel_work_sync(&ar->offchan_tx_work);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 4/9] ath10k: reset BMI state upon init
  2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
                     ` (2 preceding siblings ...)
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 3/9] ath10k: allow deferred regd update Michal Kazior
@ 2013-06-25  7:59   ` Michal Kazior
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 5/9] ath10k: decouple suspend code Michal Kazior
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-25  7:59 UTC (permalink / raw)
  To: ath9k-devel

This is necessary if we want to be able to restart
hw on-the-fly.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/bmi.c  |    6 ++++++
 drivers/net/wireless/ath/ath10k/bmi.h  |    1 +
 drivers/net/wireless/ath/ath10k/core.c |    2 ++
 3 files changed, 9 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c
index aeae029..744da6d 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.c
+++ b/drivers/net/wireless/ath/ath10k/bmi.c
@@ -20,6 +20,12 @@
 #include "debug.h"
 #include "htc.h"
 
+void ath10k_bmi_start(struct ath10k *ar)
+{
+	ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
+	ar->bmi.done_sent = false;
+}
+
 int ath10k_bmi_done(struct ath10k *ar)
 {
 	struct bmi_cmd cmd;
diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h
index 32c56aa..8d81ce1 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.h
+++ b/drivers/net/wireless/ath/ath10k/bmi.h
@@ -184,6 +184,7 @@ struct bmi_target_info {
 #define BMI_CE_NUM_TO_TARG 0
 #define BMI_CE_NUM_TO_HOST 1
 
+void ath10k_bmi_start(struct ath10k *ar);
 int ath10k_bmi_done(struct ath10k *ar);
 int ath10k_bmi_get_target_info(struct ath10k *ar,
 			       struct bmi_target_info *target_info);
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index dcddae4..3d75c6a 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -507,6 +507,8 @@ int ath10k_core_start(struct ath10k *ar)
 	struct bmi_target_info target_info;
 	int status;
 
+	ath10k_bmi_start(ar);
+
 	memset(&target_info, 0, sizeof(target_info));
 	status = ath10k_bmi_get_target_info(ar, &target_info);
 	if (status)
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 5/9] ath10k: decouple suspend code
  2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
                     ` (3 preceding siblings ...)
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 4/9] ath10k: reset BMI state upon init Michal Kazior
@ 2013-06-25  7:59   ` Michal Kazior
  2013-07-05  6:51     ` Kalle Valo
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 6/9] ath10k: move free_vdev_map initialization Michal Kazior
                     ` (3 subsequent siblings)
  8 siblings, 1 reply; 72+ messages in thread
From: Michal Kazior @ 2013-06-25  7:59 UTC (permalink / raw)
  To: ath9k-devel

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>
---
 drivers/net/wireless/ath/ath10k/core.c |   28 -----
 drivers/net/wireless/ath/ath10k/core.h |    3 -
 drivers/net/wireless/ath/ath10k/hif.h  |   19 ++++
 drivers/net/wireless/ath/ath10k/mac.c  |   66 ++++++++++++
 drivers/net/wireless/ath/ath10k/pci.c  |  176 ++++++++++----------------------
 5 files changed, 138 insertions(+), 154 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 3d75c6a..01c1d82 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -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");
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index d5b2533..5c94ea0 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -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_ */
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index b9d0927..6ebd445 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.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_ */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index f34ddb2..23e48ca5 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -20,6 +20,7 @@
 #include <net/mac80211.h>
 #include <linux/etherdevice.h>
 
+#include "hif.h"
 #include "core.h"
 #include "debug.h"
 #include "wmi.h"
@@ -2746,6 +2747,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,
@@ -2766,6 +2828,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) { \
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index cd065e0..c046df8 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -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)
@@ -2375,128 +2428,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 = {
@@ -2504,7 +2435,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)
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 6/9] ath10k: move free_vdev_map initialization
  2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
                     ` (4 preceding siblings ...)
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 5/9] ath10k: decouple suspend code Michal Kazior
@ 2013-06-25  7:59   ` Michal Kazior
  2013-07-05  7:03     ` Kalle Valo
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 7/9] ath10k: make sure all resources are freed upon ath10k_stop() Michal Kazior
                     ` (2 subsequent siblings)
  8 siblings, 1 reply; 72+ messages in thread
From: Michal Kazior @ 2013-06-25  7:59 UTC (permalink / raw)
  To: ath9k-devel

This is necessary for hw reconfiguration to work.
Since mac80211 is not calling remove_interface()
is such case we must reset free_vdev_map.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 01c1d82..0e4a704 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -458,8 +458,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 	ar->hif.priv = hif_priv;
 	ar->hif.ops = hif_ops;
 
-	ar->free_vdev_map = 0xFF; /* 8 vdevs */
-
 	init_completion(&ar->scan.started);
 	init_completion(&ar->scan.completed);
 	init_completion(&ar->scan.on_channel);
@@ -590,6 +588,8 @@ int ath10k_core_start(struct ath10k *ar)
 	if (status)
 		goto err_disconnect_htc;
 
+	ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+
 	return 0;
 
 err_disconnect_htc:
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 7/9] ath10k: make sure all resources are freed upon ath10k_stop()
  2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
                     ` (5 preceding siblings ...)
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 6/9] ath10k: move free_vdev_map initialization Michal Kazior
@ 2013-06-25  7:59   ` Michal Kazior
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 8/9] ath10k: defer hw setup to start/stop mac80211 hooks Michal Kazior
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 9/9] ath10k: store firmware files in memory Michal Kazior
  8 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-25  7:59 UTC (permalink / raw)
  To: ath9k-devel

This is necessary for proper hw reconfiguration
and to avoid memory leaks.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/mac.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 23e48ca5..afa8088 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -366,6 +366,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
 	spin_unlock_bh(&ar->data_lock);
 }
 
+static void ath10k_peer_cleanup_all(struct ath10k *ar)
+{
+	struct ath10k_peer *peer, *tmp;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	spin_lock_bh(&ar->data_lock);
+	list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+		list_del(&peer->list);
+		kfree(peer);
+	}
+	spin_unlock_bh(&ar->data_lock);
+}
+
 /************************/
 /* Interface management */
 /************************/
@@ -1750,7 +1764,18 @@ 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);
+
 	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 8/9] ath10k: defer hw setup to start/stop mac80211 hooks
  2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
                     ` (6 preceding siblings ...)
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 7/9] ath10k: make sure all resources are freed upon ath10k_stop() Michal Kazior
@ 2013-06-25  7:59   ` Michal Kazior
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 9/9] ath10k: store firmware files in memory Michal Kazior
  8 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-06-25  7:59 UTC (permalink / raw)
  To: ath9k-devel

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>
---
 drivers/net/wireless/ath/ath10k/core.c |   46 +++++++++++++++++++++-----
 drivers/net/wireless/ath/ath10k/mac.c  |   56 ++++++++++++++++++++++++--------
 drivers/net/wireless/ath/ath10k/pci.c  |   11 +------
 3 files changed, 81 insertions(+), 32 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 0e4a704..1d4c245 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -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);
 
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index afa8088..8989d06 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1735,13 +1735,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",
@@ -1752,9 +1791,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;
 }
@@ -1764,17 +1803,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);
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index c046df8..3790678 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -2361,22 +2361,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:
@@ -2415,7 +2407,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);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 9/9] ath10k: store firmware files in memory
  2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
                     ` (7 preceding siblings ...)
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 8/9] ath10k: defer hw setup to start/stop mac80211 hooks Michal Kazior
@ 2013-06-25  7:59   ` Michal Kazior
  2013-06-25  8:09     ` [ath9k-devel] [PATCH v3 10/10] ath10k: skip fw stats debugfs interface if device is down Michal Kazior
  8 siblings, 1 reply; 72+ messages in thread
From: Michal Kazior @ 2013-06-25  7:59 UTC (permalink / raw)
  To: ath9k-devel

Different FW versions may provide different
functions thus mean different hw capabilities
advertised to mac80211.

It is safe to swap firmware files on disk during
driver/device runtime without worries.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |  157 +++++++++++++++++++++-----------
 drivers/net/wireless/ath/ath10k/core.h |    4 +
 2 files changed, 109 insertions(+), 52 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 1d4c245..c37f79f 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
 
 static int ath10k_download_board_data(struct ath10k *ar)
 {
+	const struct firmware *fw = ar->board_data;
 	u32 board_data_size = QCA988X_BOARD_DATA_SZ;
 	u32 address;
-	const struct firmware *fw;
 	int ret;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.board);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch board data fw file (%ld)\n",
-			   PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
 	ret = ath10k_push_board_ext_data(ar, fw);
 	if (ret) {
 		ath10k_err("could not push board ext data (%d)\n", ret);
@@ -286,32 +278,20 @@ static int ath10k_download_board_data(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-	const struct firmware *fw;
-	u32 address;
+	const struct firmware *fw = ar->otp;
+	u32 address = ar->hw_params.patch_load_addr;
 	u32 exec_param;
 	int ret;
 
 	/* OTP is optional */
 
-	if (ar->hw_params.fw.otp == NULL) {
-		ath10k_info("otp file not defined\n");
-		return 0;
-	}
-
-	address = ar->hw_params.patch_load_addr;
-
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.otp);
-	if (IS_ERR(fw)) {
-		ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+	if (!ar->otp)
 		return 0;
-	}
 
 	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
 	if (ret) {
@@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_fw(struct ath10k *ar)
 {
-	const struct firmware *fw;
+	const struct firmware *fw = ar->firmware;
 	u32 address;
 	int ret;
 
-	if (ar->hw_params.fw.fw == NULL)
-		return -EINVAL;
-
 	address = ar->hw_params.patch_load_addr;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.fw);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
 	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
 	if (ret) {
 		ath10k_err("could not write fw (%d)\n", ret);
@@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
+	return ret;
+}
+
+static void ath10k_core_free_firmware_files(struct ath10k *ar)
+{
+	if (ar->board_data && !IS_ERR(ar->board_data))
+		release_firmware(ar->board_data);
+
+	if (ar->otp && !IS_ERR(ar->otp))
+		release_firmware(ar->otp);
+
+	if (ar->firmware && !IS_ERR(ar->firmware))
+		release_firmware(ar->firmware);
+
+	ar->board_data = NULL;
+	ar->otp = NULL;
+	ar->firmware = NULL;
+}
+
+static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+{
+	int ret = 0;
+
+	if (ar->hw_params.fw.fw == NULL) {
+		ath10k_err("firmware file not defined\n");
+		return -EINVAL;
+	}
+
+	if (ar->hw_params.fw.board == NULL) {
+		ath10k_err("board data file not defined");
+		return -EINVAL;
+	}
+
+	ar->board_data = ath10k_fetch_fw_file(ar,
+					      ar->hw_params.fw.dir,
+					      ar->hw_params.fw.board);
+	if (IS_ERR(ar->board_data)) {
+		ret = PTR_ERR(ar->board_data);
+		ath10k_err("could not fetch board data (%d)\n", ret);
+		goto err;
+	}
+
+	ar->firmware = ath10k_fetch_fw_file(ar,
+					    ar->hw_params.fw.dir,
+					    ar->hw_params.fw.fw);
+	if (IS_ERR(ar->firmware)) {
+		ret = PTR_ERR(ar->firmware);
+		ath10k_err("could not fetch firmware (%d)\n", ret);
+		goto err;
+	}
+
+	/* OTP may be undefined. If so, don't fetch it at all */
+	if (ar->hw_params.fw.otp == NULL)
+		return 0;
+
+	ar->otp = ath10k_fetch_fw_file(ar,
+				       ar->hw_params.fw.dir,
+				       ar->hw_params.fw.otp);
+	if (IS_ERR(ar->otp)) {
+		ret = PTR_ERR(ar->otp);
+		ath10k_err("could not fetch otp (%d)\n", ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	ath10k_core_free_firmware_files(ar);
 	return ret;
 }
 
@@ -502,23 +538,10 @@ EXPORT_SYMBOL(ath10k_core_destroy);
 
 int ath10k_core_start(struct ath10k *ar)
 {
-	struct bmi_target_info target_info;
 	int status;
 
 	ath10k_bmi_start(ar);
 
-	memset(&target_info, 0, sizeof(target_info));
-	status = ath10k_bmi_get_target_info(ar, &target_info);
-	if (status)
-		goto err;
-
-	ar->target_version = target_info.version;
-	ar->hw->wiphy->hw_version = target_info.version;
-
-	status = ath10k_init_hw_params(ar);
-	if (status)
-		goto err;
-
 	if (ath10k_init_configure_target(ar)) {
 		status = -EINVAL;
 		goto err;
@@ -617,7 +640,8 @@ EXPORT_SYMBOL(ath10k_core_stop);
  * hook will try to init it again) before registering */
 static int ath10k_core_probe_fw(struct ath10k *ar)
 {
-	int ret;
+	struct bmi_target_info target_info;
+	int ret = 0;
 
 	ret = ath10k_hif_power_up(ar);
 	if (ret) {
@@ -625,9 +649,35 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
 		return ret;
 	}
 
+	memset(&target_info, 0, sizeof(target_info));
+	ret = ath10k_bmi_get_target_info(ar, &target_info);
+	if (ret) {
+		ath10k_err("could not get target info (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
+	ar->target_version = target_info.version;
+	ar->hw->wiphy->hw_version = target_info.version;
+
+	ret = ath10k_init_hw_params(ar);
+	if (ret) {
+		ath10k_err("could not get hw params (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
+	ret = ath10k_core_fetch_firmware_files(ar);
+	if (ret) {
+		ath10k_err("could not fetch firmware files (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
 	ret = ath10k_core_start(ar);
 	if (ret) {
 		ath10k_err("could not init core (%d)\n", ret);
+		ath10k_core_free_firmware_files(ar);
 		ath10k_hif_power_down(ar);
 		return ret;
 	}
@@ -650,7 +700,7 @@ int ath10k_core_register(struct ath10k *ar)
 	status = ath10k_mac_register(ar);
 	if (status) {
 		ath10k_err("could not register to mac80211 (%d)\n", status);
-		return status;
+		goto err_release_fw;
 	}
 
 	status = ath10k_debug_create(ar);
@@ -663,6 +713,8 @@ int ath10k_core_register(struct ath10k *ar)
 
 err_unregister_mac:
 	ath10k_mac_unregister(ar);
+err_release_fw:
+	ath10k_core_free_firmware_files(ar);
 	return status;
 }
 EXPORT_SYMBOL(ath10k_core_register);
@@ -673,6 +725,7 @@ 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_free_firmware_files(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5c94ea0..413f1c5 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -300,6 +300,10 @@ struct ath10k {
 		} fw;
 	} hw_params;
 
+	const struct firmware *board_data;
+	const struct firmware *otp;
+	const struct firmware *firmware;
+
 	struct {
 		struct completion started;
 		struct completion completed;
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 10/10] ath10k: skip fw stats debugfs interface if device is down
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 9/9] ath10k: store firmware files in memory Michal Kazior
@ 2013-06-25  8:09     ` Michal Kazior
  2013-07-05  7:09       ` Kalle Valo
  0 siblings, 1 reply; 72+ messages in thread
From: Michal Kazior @ 2013-06-25  8:09 UTC (permalink / raw)
  To: ath9k-devel

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
I've missed this patch. It should go along with
this patchset. Sorry for the mess.

 drivers/net/wireless/ath/ath10k/debug.c |   30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 499034b..65279f5 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
 	struct wmi_pdev_stats *ps;
 	int i;
 
-	mutex_lock(&ar->conf_mutex);
+	spin_lock_bh(&ar->data_lock);
 
 	stats = &ar->debug.target_stats;
 
@@ -259,6 +259,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
 		}
 	}
 
+	spin_unlock_bh(&ar->data_lock);
 	mutex_unlock(&ar->conf_mutex);
 	complete(&ar->debug.event_stats_compl);
 }
@@ -268,35 +269,35 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
 {
 	struct ath10k *ar = file->private_data;
 	struct ath10k_target_stats *fw_stats;
-	char *buf;
+	char *buf = NULL;
 	unsigned int len = 0, buf_len = 2500;
-	ssize_t ret_cnt;
+	ssize_t ret_cnt = 0;
 	long left;
 	int i;
 	int ret;
 
 	fw_stats = &ar->debug.target_stats;
 
+	mutex_lock(&ar->conf_mutex);
+
+	if (ar->state != ATH10K_STATE_ON)
+		goto exit;
+
 	buf = kzalloc(buf_len, GFP_KERNEL);
 	if (!buf)
-		return -ENOMEM;
+		goto exit;
 
 	ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
 	if (ret) {
 		ath10k_warn("could not request stats (%d)\n", ret);
-		kfree(buf);
-		return -EIO;
+		goto exit;
 	}
 
 	left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+	if (left <= 0)
+		goto exit;
 
-	if (left <= 0) {
-		kfree(buf);
-		return -ETIMEDOUT;
-	}
-
-	mutex_lock(&ar->conf_mutex);
-
+	spin_lock_bh(&ar->data_lock);
 	len += scnprintf(buf + len, buf_len - len, "\n");
 	len += scnprintf(buf + len, buf_len - len, "%30s\n",
 			 "ath10k PDEV stats");
@@ -424,14 +425,15 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
 				 fw_stats->peer_stat[i].peer_tx_rate);
 		len += scnprintf(buf + len, buf_len - len, "\n");
 	}
+	spin_unlock_bh(&ar->data_lock);
 
 	if (len > buf_len)
 		len = buf_len;
 
 	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 
+exit:
 	mutex_unlock(&ar->conf_mutex);
-
 	kfree(buf);
 	return ret_cnt;
 }
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 1/9] ath10k: decouple pci start/stop logic
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 1/9] ath10k: decouple pci start/stop logic Michal Kazior
@ 2013-07-05  6:29     ` Kalle Valo
  2013-07-05  7:05       ` Michal Kazior
  0 siblings, 1 reply; 72+ messages in thread
From: Kalle Valo @ 2013-07-05  6:29 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior <michal.kazior@tieto.com> writes:

> Split logic that prepares the device for BMI
> phase/cleans up related resources.
>
> This is necessary for ath10k to be able to restart
> hw on the fly without reloading the module.
>
> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
> ---

[...]

> --- a/drivers/net/wireless/ath/ath10k/hif.h
> +++ b/drivers/net/wireless/ath/ath10k/hif.h
> @@ -46,8 +46,11 @@ struct ath10k_hif_ops {
>  				void *request, u32 request_len,
>  				void *response, u32 *response_len);
>  
> +	/* Post BMI phase, after FW is loaded. Starts regular operation */
>  	int (*start)(struct ath10k *ar);
>  
> +	/* Clean up what start() did. This does not revert to BMI phase. If
> +	 * desired so, call deinit() and init() */
>  	void (*stop)(struct ath10k *ar);

The last sentence in the comment can be removed, right? Or I
misunderstand the meaning.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 5/9] ath10k: decouple suspend code
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 5/9] ath10k: decouple suspend code Michal Kazior
@ 2013-07-05  6:51     ` Kalle Valo
  2013-07-05  7:12       ` Michal Kazior
  0 siblings, 1 reply; 72+ messages in thread
From: Kalle Valo @ 2013-07-05  6:51 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior <michal.kazior@tieto.com> writes:

> 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>

[...]

> +#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;
> +}

No need to change anything now, it's just moving code around anyway, but
from a quick look this function looks racy. Can we trust that there's no
other code path which could create problems during suspend? Or should we
properly serialise this, eg. by using conf_mutex and adding a new state?

I hope I didn't ask this already before, I just came back from vacation :)

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 6/9] ath10k: move free_vdev_map initialization
  2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 6/9] ath10k: move free_vdev_map initialization Michal Kazior
@ 2013-07-05  7:03     ` Kalle Valo
  0 siblings, 0 replies; 72+ messages in thread
From: Kalle Valo @ 2013-07-05  7:03 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior <michal.kazior@tieto.com> writes:

> This is necessary for hw reconfiguration to work.
> Since mac80211 is not calling remove_interface()
> is such case we must reset free_vdev_map.
>
> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>

[...]

> -	ar->free_vdev_map = 0xFF; /* 8 vdevs */

[...]

> +	ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;

As you do more than just move the initialisation you should also mention
that in the commit log.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 1/9] ath10k: decouple pci start/stop logic
  2013-07-05  6:29     ` Kalle Valo
@ 2013-07-05  7:05       ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-05  7:05 UTC (permalink / raw)
  To: ath9k-devel

On 5 July 2013 08:29, Kalle Valo <kvalo@qca.qualcomm.com> wrote:
> Michal Kazior <michal.kazior@tieto.com> writes:
>
>> Split logic that prepares the device for BMI
>> phase/cleans up related resources.
>>
>> This is necessary for ath10k to be able to restart
>> hw on the fly without reloading the module.
>>
>> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
>> ---
>
> [...]
>
>> --- a/drivers/net/wireless/ath/ath10k/hif.h
>> +++ b/drivers/net/wireless/ath/ath10k/hif.h
>> @@ -46,8 +46,11 @@ struct ath10k_hif_ops {
>>                               void *request, u32 request_len,
>>                               void *response, u32 *response_len);
>>
>> +     /* Post BMI phase, after FW is loaded. Starts regular operation */
>>       int (*start)(struct ath10k *ar);
>>
>> +     /* Clean up what start() did. This does not revert to BMI phase. If
>> +      * desired so, call deinit() and init() */
>>       void (*stop)(struct ath10k *ar);
>
> The last sentence in the comment can be removed, right? Or I
> misunderstand the meaning.

Oh. I didn't update the comment. It should state "call power_down()
and power_up()" since those function names have changed.

Otherwise we still expect either sequence:
 * power_up() -> power_down() [-> power_up() ..]
 * power_up() -> start() -> stop() -> power_down() [-> power_up() ..]



Pozdrawiam / Best regards,
Micha? Kazior.

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 10/10] ath10k: skip fw stats debugfs interface if device is down
  2013-06-25  8:09     ` [ath9k-devel] [PATCH v3 10/10] ath10k: skip fw stats debugfs interface if device is down Michal Kazior
@ 2013-07-05  7:09       ` Kalle Valo
  0 siblings, 0 replies; 72+ messages in thread
From: Kalle Valo @ 2013-07-05  7:09 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior <michal.kazior@tieto.com> writes:

> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
> ---
> I've missed this patch. It should go along with
> this patchset. Sorry for the mess.

Please write a commit log.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 5/9] ath10k: decouple suspend code
  2013-07-05  6:51     ` Kalle Valo
@ 2013-07-05  7:12       ` Michal Kazior
  2013-07-05  7:20         ` Kalle Valo
  0 siblings, 1 reply; 72+ messages in thread
From: Michal Kazior @ 2013-07-05  7:12 UTC (permalink / raw)
  To: ath9k-devel

On 5 July 2013 08:51, Kalle Valo <kvalo@qca.qualcomm.com> wrote:
> Michal Kazior <michal.kazior@tieto.com> writes:
>> +#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;
>> +}
>
> No need to change anything now, it's just moving code around anyway, but
> from a quick look this function looks racy. Can we trust that there's no
> other code path which could create problems during suspend? Or should we
> properly serialise this, eg. by using conf_mutex and adding a new state?
>
> I hope I didn't ask this already before, I just came back from vacation :)

Right. We could use a conf_mutex here. However this code is not used
at all. Nothing should trigger it (it's just left for reference) since
we don't advertise any WoWLAN capabilities to mac80211. I can update
the patch and include conf_mutex in suspend/resume functions if you
think that's necessary. Or perhaps we should just scrap the code. The
code will still remain in git history.


Pozdrawiam / Best regards,
Micha? Kazior.

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [ath9k-devel] [PATCH v3 5/9] ath10k: decouple suspend code
  2013-07-05  7:12       ` Michal Kazior
@ 2013-07-05  7:20         ` Kalle Valo
  0 siblings, 0 replies; 72+ messages in thread
From: Kalle Valo @ 2013-07-05  7:20 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior <michal.kazior@tieto.com> writes:

> On 5 July 2013 08:51, Kalle Valo <kvalo@qca.qualcomm.com> wrote:
>> Michal Kazior <michal.kazior@tieto.com> writes:
>>> +#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;
>>> +}
>>
>> No need to change anything now, it's just moving code around anyway, but
>> from a quick look this function looks racy. Can we trust that there's no
>> other code path which could create problems during suspend? Or should we
>> properly serialise this, eg. by using conf_mutex and adding a new state?
>>
>> I hope I didn't ask this already before, I just came back from vacation :)
>
> Right. We could use a conf_mutex here. However this code is not used
> at all. Nothing should trigger it (it's just left for reference) since
> we don't advertise any WoWLAN capabilities to mac80211. I can update
> the patch and include conf_mutex in suspend/resume functions if you
> think that's necessary. Or perhaps we should just scrap the code. The
> code will still remain in git history.

No need to change anything now, the patch is fine as it is. I was just
thinking for the future.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH v3 00/10] ath10k: device setup refactor
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
                   ` (10 preceding siblings ...)
  2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
@ 2013-07-16  7:19 ` Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 01/10] ath10k: decouple pci start/stop logic Michal Kazior
                     ` (10 more replies)
  2013-07-16  7:38   ` Michal Kazior
  12 siblings, 11 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:19 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is a respin of my patchset that addresses
hibernation issues.

This is also groundwork for proper hw recovery
that I'm going to post too.

v3:
 * update comments in pci logic decoupling
 * add missing commit message for one of the patches
 * update commit message for free_vdev_map patch

Michal Kazior (10):
  ath10k: decouple pci start/stop logic
  ath10k: decouple core start/stop logic
  ath10k: allow deferred regd update
  ath10k: reset BMI state upon init
  ath10k: decouple suspend code
  ath10k: move free_vdev_map initialization
  ath10k: make sure all resources are freed upon ath10k_stop()
  ath10k: defer hw setup to start/stop mac80211 hooks
  ath10k: store firmware files in memory
  ath10k: skip fw stats debugfs interface if device is down

 drivers/net/wireless/ath/ath10k/bmi.c   |    6 +
 drivers/net/wireless/ath/ath10k/bmi.h   |    1 +
 drivers/net/wireless/ath/ath10k/core.c  |  249 +++++++++++++++++----------
 drivers/net/wireless/ath/ath10k/core.h  |   16 +-
 drivers/net/wireless/ath/ath10k/debug.c |   30 ++--
 drivers/net/wireless/ath/ath10k/hif.h   |   39 +++++
 drivers/net/wireless/ath/ath10k/mac.c   |  151 ++++++++++++++++-
 drivers/net/wireless/ath/ath10k/pci.c   |  283 +++++++++++++------------------
 8 files changed, 497 insertions(+), 278 deletions(-)

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH v3 01/10] ath10k: decouple pci start/stop logic
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
@ 2013-07-16  7:19   ` Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 02/10] ath10k: decouple core " Michal Kazior
                     ` (9 subsequent siblings)
  10 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:19 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Split logic that prepares the device for BMI
phase/cleans up related resources.

This is necessary for ath10k to be able to restart
hw on the fly without reloading the module.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/hif.h |   20 ++++++
 drivers/net/wireless/ath/ath10k/pci.c |  110 +++++++++++++++++++++------------
 2 files changed, 91 insertions(+), 39 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 010e265..00d2940 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -46,8 +46,11 @@ struct ath10k_hif_ops {
 				void *request, u32 request_len,
 				void *response, u32 *response_len);
 
+	/* Post BMI phase, after FW is loaded. Starts regular operation */
 	int (*start)(struct ath10k *ar);
 
+	/* Clean up what start() did. This does not revert to BMI phase. If
+	 * desired so, call power_down() and power_up() */
 	void (*stop)(struct ath10k *ar);
 
 	int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
@@ -70,6 +73,13 @@ struct ath10k_hif_ops {
 			      struct ath10k_hif_cb *callbacks);
 
 	u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+
+	/* Power up the device and enter BMI transfer mode for FW download */
+	int (*power_up)(struct ath10k *ar);
+
+	/* 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);
 };
 
 
@@ -134,4 +144,14 @@ static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
 	return ar->hif.ops->get_free_queue_number(ar, pipe_id);
 }
 
+static inline int ath10k_hif_power_up(struct ath10k *ar)
+{
+	return ar->hif.ops->power_up(ar);
+}
+
+static inline void ath10k_hif_power_down(struct ath10k *ar)
+{
+	ar->hif.ops->power_down(ar);
+}
+
 #endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 7da0983..c15b0c5 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -54,6 +54,8 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
 					     int num);
 static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
 static void ath10k_pci_stop_ce(struct ath10k *ar);
+static void ath10k_pci_device_reset(struct ath10k *ar);
+static int ath10k_pci_reset_target(struct ath10k *ar);
 
 static const struct ce_attr host_ce_config_wlan[] = {
 	/* host->target HTC control and raw streams */
@@ -1734,6 +1736,66 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
 	ath10k_pci_sleep(ar);
 }
 
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
+{
+	int ret;
+
+	/*
+	 * Bring the target up cleanly.
+	 *
+	 * The target may be in an undefined state with an AUX-powered Target
+	 * and a Host in WoW mode. If the Host crashes, loses power, or is
+	 * restarted (without unloading the driver) then the Target is left
+	 * (aux) powered and running. On a subsequent driver load, the Target
+	 * is in an unexpected state. We try to catch that here in order to
+	 * reset the Target and retry the probe.
+	 */
+	ath10k_pci_device_reset(ar);
+
+	ret = ath10k_pci_reset_target(ar);
+	if (ret)
+		goto err;
+
+	if (ath10k_target_ps) {
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
+	} else {
+		/* Force AWAKE forever */
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
+		ath10k_do_pci_wake(ar);
+	}
+
+	ret = ath10k_pci_ce_init(ar);
+	if (ret)
+		goto err_ps;
+
+	ret = ath10k_pci_init_config(ar);
+	if (ret)
+		goto err_ce;
+
+	ret = ath10k_pci_wake_target_cpu(ar);
+	if (ret) {
+		ath10k_err("could not wake up target CPU (%d)\n", ret);
+		goto err_ce;
+	}
+
+	return 0;
+
+err_ce:
+	ath10k_pci_ce_deinit(ar);
+err_ps:
+	if (!ath10k_target_ps)
+		ath10k_do_pci_sleep(ar);
+err:
+	return ret;
+}
+
+static void ath10k_pci_hif_power_down(struct ath10k *ar)
+{
+	ath10k_pci_ce_deinit(ar);
+	if (!ath10k_target_ps)
+		ath10k_do_pci_sleep(ar);
+}
+
 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,
@@ -1744,6 +1806,8 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
 	.send_complete_check	= ath10k_pci_hif_send_complete_check,
 	.set_callbacks		= ath10k_pci_hif_set_callbacks,
 	.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,
 };
 
 static void ath10k_pci_ce_tasklet(unsigned long ptr)
@@ -2245,54 +2309,22 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 		goto err_iomap;
 	}
 
-	/*
-	 * Bring the target up cleanly.
-	 *
-	 * The target may be in an undefined state with an AUX-powered Target
-	 * and a Host in WoW mode. If the Host crashes, loses power, or is
-	 * restarted (without unloading the driver) then the Target is left
-	 * (aux) powered and running. On a subsequent driver load, the Target
-	 * is in an unexpected state. We try to catch that here in order to
-	 * reset the Target and retry the probe.
-	 */
-	ath10k_pci_device_reset(ar);
-
-	ret = ath10k_pci_reset_target(ar);
-	if (ret)
-		goto err_intr;
-
-	if (ath10k_target_ps) {
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
-	} else {
-		/* Force AWAKE forever */
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
-		ath10k_do_pci_wake(ar);
-	}
-
-	ret = ath10k_pci_ce_init(ar);
-	if (ret)
-		goto err_intr;
-
-	ret = ath10k_pci_init_config(ar);
-	if (ret)
-		goto err_ce;
-
-	ret = ath10k_pci_wake_target_cpu(ar);
+	ret = ath10k_pci_hif_power_up(ar);
 	if (ret) {
-		ath10k_err("could not wake up target CPU (%d)\n", ret);
-		goto err_ce;
+		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_ce;
+		goto err_hif;
 	}
 
 	return 0;
 
-err_ce:
-	ath10k_pci_ce_deinit(ar);
+err_hif:
+	ath10k_pci_hif_power_down(ar);
 err_intr:
 	ath10k_pci_stop_intr(ar);
 err_iomap:
@@ -2331,7 +2363,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
 	tasklet_kill(&ar_pci->msi_fw_err);
 
 	ath10k_core_unregister(ar);
-	ath10k_pci_ce_deinit(ar);
+	ath10k_pci_hif_power_down(ar);
 	ath10k_pci_stop_intr(ar);
 
 	pci_set_drvdata(pdev, NULL);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v3 02/10] ath10k: decouple core start/stop logic
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 01/10] ath10k: decouple pci start/stop logic Michal Kazior
@ 2013-07-16  7:19   ` Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 03/10] ath10k: allow deferred regd update Michal Kazior
                     ` (8 subsequent siblings)
  10 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:19 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Enables code reuse for proper hw reconfiguration
that is in turn required for proper
suspend/hibernation/wowlan support.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |   44 +++++++++++++++++++++++---------
 drivers/net/wireless/ath/ath10k/core.h |    2 ++
 2 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index f1312fa..dcddae4 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -502,8 +502,7 @@ void ath10k_core_destroy(struct ath10k *ar)
 }
 EXPORT_SYMBOL(ath10k_core_destroy);
 
-
-int ath10k_core_register(struct ath10k *ar)
+int ath10k_core_start(struct ath10k *ar)
 {
 	struct bmi_target_info target_info;
 	int status;
@@ -589,9 +588,36 @@ int ath10k_core_register(struct ath10k *ar)
 	if (status)
 		goto err_disconnect_htc;
 
+	return 0;
+
+err_disconnect_htc:
+	ath10k_htc_stop(&ar->htc);
+err_htt_detach:
+	ath10k_htt_detach(&ar->htt);
+err_wmi_detach:
+	ath10k_wmi_detach(ar);
+err:
+	return status;
+}
+
+void ath10k_core_stop(struct ath10k *ar)
+{
+	ath10k_htc_stop(&ar->htc);
+	ath10k_htt_detach(&ar->htt);
+	ath10k_wmi_detach(ar);
+}
+
+int ath10k_core_register(struct ath10k *ar)
+{
+	int status;
+
+	status = ath10k_core_start(ar);
+	if (status)
+		goto err;
+
 	status = ath10k_mac_register(ar);
 	if (status)
-		goto err_disconnect_htc;
+		goto err_core_stop;
 
 	status = ath10k_debug_create(ar);
 	if (status) {
@@ -603,12 +629,8 @@ int ath10k_core_register(struct ath10k *ar)
 
 err_unregister_mac:
 	ath10k_mac_unregister(ar);
-err_disconnect_htc:
-	ath10k_htc_stop(&ar->htc);
-err_htt_detach:
-	ath10k_htt_detach(&ar->htt);
-err_wmi_detach:
-	ath10k_wmi_detach(ar);
+err_core_stop:
+	ath10k_core_stop(ar);
 err:
 	return status;
 }
@@ -620,9 +642,7 @@ 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_htc_stop(&ar->htc);
-	ath10k_htt_detach(&ar->htt);
-	ath10k_wmi_detach(ar);
+	ath10k_core_stop(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 2f7065c..5a0b2ce 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -353,6 +353,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 				  const struct ath10k_hif_ops *hif_ops);
 void ath10k_core_destroy(struct ath10k *ar);
 
+int ath10k_core_start(struct ath10k *ar);
+void ath10k_core_stop(struct ath10k *ar);
 int ath10k_core_register(struct ath10k *ar);
 void ath10k_core_unregister(struct ath10k *ar);
 
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v3 03/10] ath10k: allow deferred regd update
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 01/10] ath10k: decouple pci start/stop logic Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 02/10] ath10k: decouple core " Michal Kazior
@ 2013-07-16  7:19   ` Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 04/10] ath10k: reset BMI state upon init Michal Kazior
                     ` (7 subsequent siblings)
  10 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:19 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Regulatory domain notification hook can be called
regardless of the hw state (i.e. before start
mac80211 callback).

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.h |    7 +++++++
 drivers/net/wireless/ath/ath10k/mac.c  |   26 +++++++++++++++++++-------
 2 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5a0b2ce..d5b2533 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -247,6 +247,11 @@ struct ath10k_debug {
 	struct completion event_stats_compl;
 };
 
+enum ath10k_state {
+	ATH10K_STATE_OFF = 0,
+	ATH10K_STATE_ON,
+};
+
 struct ath10k {
 	struct ath_common ath_common;
 	struct ieee80211_hw *hw;
@@ -344,6 +349,8 @@ struct ath10k {
 	struct completion offchan_tx_completed;
 	struct sk_buff *offchan_tx_skb;
 
+	enum ath10k_state state;
+
 #ifdef CONFIG_ATH10K_DEBUGFS
 	struct ath10k_debug debug;
 #endif
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 5082503..4627c16 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1310,23 +1310,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
 	return ret;
 }
 
-static void ath10k_reg_notifier(struct wiphy *wiphy,
-				struct regulatory_request *request)
+static void ath10k_regd_update(struct ath10k *ar)
 {
-	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
 	struct reg_dmn_pair_mapping *regpair;
-	struct ath10k *ar = hw->priv;
 	int ret;
 
-	mutex_lock(&ar->conf_mutex);
-
-	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+	lockdep_assert_held(&ar->conf_mutex);
 
 	ret = ath10k_update_channel_list(ar);
 	if (ret)
 		ath10k_warn("could not update channel list (%d)\n", ret);
 
 	regpair = ar->ath_common.regulatory.regpair;
+
 	/* Target allows setting up per-band regdomain but ath_common provides
 	 * a combined one only */
 	ret = ath10k_wmi_pdev_set_regdomain(ar,
@@ -1337,7 +1333,19 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
 					    regpair->reg_5ghz_ctl);
 	if (ret)
 		ath10k_warn("could not set pdev regdomain (%d)\n", ret);
+}
 
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+				struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct ath10k *ar = hw->priv;
+
+	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+	mutex_lock(&ar->conf_mutex);
+	if (ar->state == ATH10K_STATE_ON)
+		ath10k_regd_update(ar);
 	mutex_unlock(&ar->conf_mutex);
 }
 
@@ -1732,6 +1740,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);
+
 	mutex_unlock(&ar->conf_mutex);
 	return 0;
 }
@@ -1742,6 +1753,7 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 
 	mutex_lock(&ar->conf_mutex);
 	ath10k_offchan_tx_purge(ar);
+	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
 	cancel_work_sync(&ar->offchan_tx_work);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v3 04/10] ath10k: reset BMI state upon init
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
                     ` (2 preceding siblings ...)
  2013-07-16  7:19   ` [PATCH v3 03/10] ath10k: allow deferred regd update Michal Kazior
@ 2013-07-16  7:19   ` Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 05/10] ath10k: decouple suspend code Michal Kazior
                     ` (6 subsequent siblings)
  10 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:19 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is necessary if we want to be able to restart
hw on-the-fly.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/bmi.c  |    6 ++++++
 drivers/net/wireless/ath/ath10k/bmi.h  |    1 +
 drivers/net/wireless/ath/ath10k/core.c |    2 ++
 3 files changed, 9 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c
index aeae029..744da6d 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.c
+++ b/drivers/net/wireless/ath/ath10k/bmi.c
@@ -20,6 +20,12 @@
 #include "debug.h"
 #include "htc.h"
 
+void ath10k_bmi_start(struct ath10k *ar)
+{
+	ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
+	ar->bmi.done_sent = false;
+}
+
 int ath10k_bmi_done(struct ath10k *ar)
 {
 	struct bmi_cmd cmd;
diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h
index 32c56aa..8d81ce1 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.h
+++ b/drivers/net/wireless/ath/ath10k/bmi.h
@@ -184,6 +184,7 @@ struct bmi_target_info {
 #define BMI_CE_NUM_TO_TARG 0
 #define BMI_CE_NUM_TO_HOST 1
 
+void ath10k_bmi_start(struct ath10k *ar);
 int ath10k_bmi_done(struct ath10k *ar);
 int ath10k_bmi_get_target_info(struct ath10k *ar,
 			       struct bmi_target_info *target_info);
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index dcddae4..3d75c6a 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -507,6 +507,8 @@ int ath10k_core_start(struct ath10k *ar)
 	struct bmi_target_info target_info;
 	int status;
 
+	ath10k_bmi_start(ar);
+
 	memset(&target_info, 0, sizeof(target_info));
 	status = ath10k_bmi_get_target_info(ar, &target_info);
 	if (status)
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v3 05/10] ath10k: decouple suspend code
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
                     ` (3 preceding siblings ...)
  2013-07-16  7:19   ` [PATCH v3 04/10] ath10k: reset BMI state upon init Michal Kazior
@ 2013-07-16  7:19   ` Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 06/10] ath10k: move free_vdev_map initialization Michal Kazior
                     ` (5 subsequent siblings)
  10 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:19 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

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>
---
 drivers/net/wireless/ath/ath10k/core.c |   28 -----
 drivers/net/wireless/ath/ath10k/core.h |    3 -
 drivers/net/wireless/ath/ath10k/hif.h  |   19 ++++
 drivers/net/wireless/ath/ath10k/mac.c  |   66 ++++++++++++
 drivers/net/wireless/ath/ath10k/pci.c  |  176 ++++++++++----------------------
 5 files changed, 138 insertions(+), 154 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 3d75c6a..01c1d82 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -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");
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index d5b2533..5c94ea0 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -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_ */
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 00d2940..dcdea68 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.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_ */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 4627c16..febba7d 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -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) { \
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index c15b0c5..75f5427 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -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)
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v3 06/10] ath10k: move free_vdev_map initialization
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
                     ` (4 preceding siblings ...)
  2013-07-16  7:19   ` [PATCH v3 05/10] ath10k: decouple suspend code Michal Kazior
@ 2013-07-16  7:19   ` Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 07/10] ath10k: make sure all resources are freed upon ath10k_stop() Michal Kazior
                     ` (4 subsequent siblings)
  10 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:19 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is necessary for hw reconfiguration to work.
Since mac80211 is not calling remove_interface()
is such case we must reset free_vdev_map.

Also use a define instead of a hardcoded value for
vdev map initialization.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 01c1d82..0e4a704 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -458,8 +458,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 	ar->hif.priv = hif_priv;
 	ar->hif.ops = hif_ops;
 
-	ar->free_vdev_map = 0xFF; /* 8 vdevs */
-
 	init_completion(&ar->scan.started);
 	init_completion(&ar->scan.completed);
 	init_completion(&ar->scan.on_channel);
@@ -590,6 +588,8 @@ int ath10k_core_start(struct ath10k *ar)
 	if (status)
 		goto err_disconnect_htc;
 
+	ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+
 	return 0;
 
 err_disconnect_htc:
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v3 07/10] ath10k: make sure all resources are freed upon ath10k_stop()
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
                     ` (5 preceding siblings ...)
  2013-07-16  7:19   ` [PATCH v3 06/10] ath10k: move free_vdev_map initialization Michal Kazior
@ 2013-07-16  7:19   ` Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 08/10] ath10k: defer hw setup to start/stop mac80211 hooks Michal Kazior
                     ` (3 subsequent siblings)
  10 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:19 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is necessary for proper hw reconfiguration
and to avoid memory leaks.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/mac.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index febba7d..b9663e9 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -369,6 +369,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
 	spin_unlock_bh(&ar->data_lock);
 }
 
+static void ath10k_peer_cleanup_all(struct ath10k *ar)
+{
+	struct ath10k_peer *peer, *tmp;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	spin_lock_bh(&ar->data_lock);
+	list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+		list_del(&peer->list);
+		kfree(peer);
+	}
+	spin_unlock_bh(&ar->data_lock);
+}
+
 /************************/
 /* Interface management */
 /************************/
@@ -1753,7 +1767,18 @@ 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);
+
 	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v3 08/10] ath10k: defer hw setup to start/stop mac80211 hooks
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
                     ` (6 preceding siblings ...)
  2013-07-16  7:19   ` [PATCH v3 07/10] ath10k: make sure all resources are freed upon ath10k_stop() Michal Kazior
@ 2013-07-16  7:19   ` Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 09/10] ath10k: store firmware files in memory Michal Kazior
                     ` (2 subsequent siblings)
  10 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:19 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

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>
---
 drivers/net/wireless/ath/ath10k/core.c |   46 +++++++++++++++++++++-----
 drivers/net/wireless/ath/ath10k/mac.c  |   56 ++++++++++++++++++++++++--------
 drivers/net/wireless/ath/ath10k/pci.c  |   11 +------
 3 files changed, 81 insertions(+), 32 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 0e4a704..1d4c245 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -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);
 
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index b9663e9..2dfd446 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -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);
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 75f5427..bfe8561 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -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);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v3 09/10] ath10k: store firmware files in memory
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
                     ` (7 preceding siblings ...)
  2013-07-16  7:19   ` [PATCH v3 08/10] ath10k: defer hw setup to start/stop mac80211 hooks Michal Kazior
@ 2013-07-16  7:19   ` Michal Kazior
  2013-07-16  7:19   ` [PATCH v3 10/10] ath10k: skip fw stats debugfs interface if device is down Michal Kazior
  2013-07-16  7:27   ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
  10 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:19 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Different FW versions may provide different
functions thus mean different hw capabilities
advertised to mac80211.

It is safe to swap firmware files on disk during
driver/device runtime without worries.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |  157 +++++++++++++++++++++-----------
 drivers/net/wireless/ath/ath10k/core.h |    4 +
 2 files changed, 109 insertions(+), 52 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 1d4c245..c37f79f 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
 
 static int ath10k_download_board_data(struct ath10k *ar)
 {
+	const struct firmware *fw = ar->board_data;
 	u32 board_data_size = QCA988X_BOARD_DATA_SZ;
 	u32 address;
-	const struct firmware *fw;
 	int ret;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.board);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch board data fw file (%ld)\n",
-			   PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
 	ret = ath10k_push_board_ext_data(ar, fw);
 	if (ret) {
 		ath10k_err("could not push board ext data (%d)\n", ret);
@@ -286,32 +278,20 @@ static int ath10k_download_board_data(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-	const struct firmware *fw;
-	u32 address;
+	const struct firmware *fw = ar->otp;
+	u32 address = ar->hw_params.patch_load_addr;
 	u32 exec_param;
 	int ret;
 
 	/* OTP is optional */
 
-	if (ar->hw_params.fw.otp == NULL) {
-		ath10k_info("otp file not defined\n");
-		return 0;
-	}
-
-	address = ar->hw_params.patch_load_addr;
-
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.otp);
-	if (IS_ERR(fw)) {
-		ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+	if (!ar->otp)
 		return 0;
-	}
 
 	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
 	if (ret) {
@@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_fw(struct ath10k *ar)
 {
-	const struct firmware *fw;
+	const struct firmware *fw = ar->firmware;
 	u32 address;
 	int ret;
 
-	if (ar->hw_params.fw.fw == NULL)
-		return -EINVAL;
-
 	address = ar->hw_params.patch_load_addr;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.fw);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
 	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
 	if (ret) {
 		ath10k_err("could not write fw (%d)\n", ret);
@@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
+	return ret;
+}
+
+static void ath10k_core_free_firmware_files(struct ath10k *ar)
+{
+	if (ar->board_data && !IS_ERR(ar->board_data))
+		release_firmware(ar->board_data);
+
+	if (ar->otp && !IS_ERR(ar->otp))
+		release_firmware(ar->otp);
+
+	if (ar->firmware && !IS_ERR(ar->firmware))
+		release_firmware(ar->firmware);
+
+	ar->board_data = NULL;
+	ar->otp = NULL;
+	ar->firmware = NULL;
+}
+
+static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+{
+	int ret = 0;
+
+	if (ar->hw_params.fw.fw == NULL) {
+		ath10k_err("firmware file not defined\n");
+		return -EINVAL;
+	}
+
+	if (ar->hw_params.fw.board == NULL) {
+		ath10k_err("board data file not defined");
+		return -EINVAL;
+	}
+
+	ar->board_data = ath10k_fetch_fw_file(ar,
+					      ar->hw_params.fw.dir,
+					      ar->hw_params.fw.board);
+	if (IS_ERR(ar->board_data)) {
+		ret = PTR_ERR(ar->board_data);
+		ath10k_err("could not fetch board data (%d)\n", ret);
+		goto err;
+	}
+
+	ar->firmware = ath10k_fetch_fw_file(ar,
+					    ar->hw_params.fw.dir,
+					    ar->hw_params.fw.fw);
+	if (IS_ERR(ar->firmware)) {
+		ret = PTR_ERR(ar->firmware);
+		ath10k_err("could not fetch firmware (%d)\n", ret);
+		goto err;
+	}
+
+	/* OTP may be undefined. If so, don't fetch it at all */
+	if (ar->hw_params.fw.otp == NULL)
+		return 0;
+
+	ar->otp = ath10k_fetch_fw_file(ar,
+				       ar->hw_params.fw.dir,
+				       ar->hw_params.fw.otp);
+	if (IS_ERR(ar->otp)) {
+		ret = PTR_ERR(ar->otp);
+		ath10k_err("could not fetch otp (%d)\n", ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	ath10k_core_free_firmware_files(ar);
 	return ret;
 }
 
@@ -502,23 +538,10 @@ EXPORT_SYMBOL(ath10k_core_destroy);
 
 int ath10k_core_start(struct ath10k *ar)
 {
-	struct bmi_target_info target_info;
 	int status;
 
 	ath10k_bmi_start(ar);
 
-	memset(&target_info, 0, sizeof(target_info));
-	status = ath10k_bmi_get_target_info(ar, &target_info);
-	if (status)
-		goto err;
-
-	ar->target_version = target_info.version;
-	ar->hw->wiphy->hw_version = target_info.version;
-
-	status = ath10k_init_hw_params(ar);
-	if (status)
-		goto err;
-
 	if (ath10k_init_configure_target(ar)) {
 		status = -EINVAL;
 		goto err;
@@ -617,7 +640,8 @@ EXPORT_SYMBOL(ath10k_core_stop);
  * hook will try to init it again) before registering */
 static int ath10k_core_probe_fw(struct ath10k *ar)
 {
-	int ret;
+	struct bmi_target_info target_info;
+	int ret = 0;
 
 	ret = ath10k_hif_power_up(ar);
 	if (ret) {
@@ -625,9 +649,35 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
 		return ret;
 	}
 
+	memset(&target_info, 0, sizeof(target_info));
+	ret = ath10k_bmi_get_target_info(ar, &target_info);
+	if (ret) {
+		ath10k_err("could not get target info (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
+	ar->target_version = target_info.version;
+	ar->hw->wiphy->hw_version = target_info.version;
+
+	ret = ath10k_init_hw_params(ar);
+	if (ret) {
+		ath10k_err("could not get hw params (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
+	ret = ath10k_core_fetch_firmware_files(ar);
+	if (ret) {
+		ath10k_err("could not fetch firmware files (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
 	ret = ath10k_core_start(ar);
 	if (ret) {
 		ath10k_err("could not init core (%d)\n", ret);
+		ath10k_core_free_firmware_files(ar);
 		ath10k_hif_power_down(ar);
 		return ret;
 	}
@@ -650,7 +700,7 @@ int ath10k_core_register(struct ath10k *ar)
 	status = ath10k_mac_register(ar);
 	if (status) {
 		ath10k_err("could not register to mac80211 (%d)\n", status);
-		return status;
+		goto err_release_fw;
 	}
 
 	status = ath10k_debug_create(ar);
@@ -663,6 +713,8 @@ int ath10k_core_register(struct ath10k *ar)
 
 err_unregister_mac:
 	ath10k_mac_unregister(ar);
+err_release_fw:
+	ath10k_core_free_firmware_files(ar);
 	return status;
 }
 EXPORT_SYMBOL(ath10k_core_register);
@@ -673,6 +725,7 @@ 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_free_firmware_files(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5c94ea0..413f1c5 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -300,6 +300,10 @@ struct ath10k {
 		} fw;
 	} hw_params;
 
+	const struct firmware *board_data;
+	const struct firmware *otp;
+	const struct firmware *firmware;
+
 	struct {
 		struct completion started;
 		struct completion completed;
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v3 10/10] ath10k: skip fw stats debugfs interface if device is down
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
                     ` (8 preceding siblings ...)
  2013-07-16  7:19   ` [PATCH v3 09/10] ath10k: store firmware files in memory Michal Kazior
@ 2013-07-16  7:19   ` Michal Kazior
  2013-07-16  7:27   ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
  10 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:19 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

If the device is not running then there may be no
FW at all to send the query to. If the FW is
already there it might still trigger a crash if
the command is sent before the device is fully
initialized.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/debug.c |   30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 499034b..65279f5 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
 	struct wmi_pdev_stats *ps;
 	int i;
 
-	mutex_lock(&ar->conf_mutex);
+	spin_lock_bh(&ar->data_lock);
 
 	stats = &ar->debug.target_stats;
 
@@ -259,6 +259,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
 		}
 	}
 
+	spin_unlock_bh(&ar->data_lock);
 	mutex_unlock(&ar->conf_mutex);
 	complete(&ar->debug.event_stats_compl);
 }
@@ -268,35 +269,35 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
 {
 	struct ath10k *ar = file->private_data;
 	struct ath10k_target_stats *fw_stats;
-	char *buf;
+	char *buf = NULL;
 	unsigned int len = 0, buf_len = 2500;
-	ssize_t ret_cnt;
+	ssize_t ret_cnt = 0;
 	long left;
 	int i;
 	int ret;
 
 	fw_stats = &ar->debug.target_stats;
 
+	mutex_lock(&ar->conf_mutex);
+
+	if (ar->state != ATH10K_STATE_ON)
+		goto exit;
+
 	buf = kzalloc(buf_len, GFP_KERNEL);
 	if (!buf)
-		return -ENOMEM;
+		goto exit;
 
 	ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
 	if (ret) {
 		ath10k_warn("could not request stats (%d)\n", ret);
-		kfree(buf);
-		return -EIO;
+		goto exit;
 	}
 
 	left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+	if (left <= 0)
+		goto exit;
 
-	if (left <= 0) {
-		kfree(buf);
-		return -ETIMEDOUT;
-	}
-
-	mutex_lock(&ar->conf_mutex);
-
+	spin_lock_bh(&ar->data_lock);
 	len += scnprintf(buf + len, buf_len - len, "\n");
 	len += scnprintf(buf + len, buf_len - len, "%30s\n",
 			 "ath10k PDEV stats");
@@ -424,14 +425,15 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
 				 fw_stats->peer_stat[i].peer_tx_rate);
 		len += scnprintf(buf + len, buf_len - len, "\n");
 	}
+	spin_unlock_bh(&ar->data_lock);
 
 	if (len > buf_len)
 		len = buf_len;
 
 	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 
+exit:
 	mutex_unlock(&ar->conf_mutex);
-
 	kfree(buf);
 	return ret_cnt;
 }
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* Re: [PATCH v3 00/10] ath10k: device setup refactor
  2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
                     ` (9 preceding siblings ...)
  2013-07-16  7:19   ` [PATCH v3 10/10] ath10k: skip fw stats debugfs interface if device is down Michal Kazior
@ 2013-07-16  7:27   ` Michal Kazior
  10 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:27 UTC (permalink / raw)
  To: linux-wireless

On 16 July 2013 09:19, Michal Kazior <michal.kazior@tieto.com> wrote:
> This is a respin of my patchset that addresses
> hibernation issues.
>
> This is also groundwork for proper hw recovery
> that I'm going to post too.
>
> v3:
>  * update comments in pci logic decoupling
>  * add missing commit message for one of the patches
>  * update commit message for free_vdev_map patch
>
> Michal Kazior (10):
>   ath10k: decouple pci start/stop logic
>   ath10k: decouple core start/stop logic
>   ath10k: allow deferred regd update
>   ath10k: reset BMI state upon init
>   ath10k: decouple suspend code
>   ath10k: move free_vdev_map initialization
>   ath10k: make sure all resources are freed upon ath10k_stop()
>   ath10k: defer hw setup to start/stop mac80211 hooks
>   ath10k: store firmware files in memory
>   ath10k: skip fw stats debugfs interface if device is down
>
>  drivers/net/wireless/ath/ath10k/bmi.c   |    6 +
>  drivers/net/wireless/ath/ath10k/bmi.h   |    1 +
>  drivers/net/wireless/ath/ath10k/core.c  |  249 +++++++++++++++++----------
>  drivers/net/wireless/ath/ath10k/core.h  |   16 +-
>  drivers/net/wireless/ath/ath10k/debug.c |   30 ++--
>  drivers/net/wireless/ath/ath10k/hif.h   |   39 +++++
>  drivers/net/wireless/ath/ath10k/mac.c   |  151 ++++++++++++++++-
>  drivers/net/wireless/ath/ath10k/pci.c   |  283 +++++++++++++------------------
>  8 files changed, 497 insertions(+), 278 deletions(-)

Oh shoot. I got the patchset version wrong and had a typo in the new
ath10k mailing list address. Disregard this, please. I'll resend.

Sorry for the noise.


Pozdrawiam / Best regards,
Michał Kazior.

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH v4 00/10] ath10k: device setup refactor
  2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
@ 2013-07-16  7:38   ` Michal Kazior
  2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 02/10] ath10k: decouple core start/stop logic Michal Kazior
                     ` (11 subsequent siblings)
  12 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is a respin of my patchset that addresses
hibernation issues.

This is also groundwork for proper hw recovery
that I'm going to post too.

v4:
 * update comments in pci logic decoupling
 * add missing commit message for one of the patches
 * update commit message for free_vdev_map patch

Michal Kazior (10):
  ath10k: decouple pci start/stop logic
  ath10k: decouple core start/stop logic
  ath10k: allow deferred regd update
  ath10k: reset BMI state upon init
  ath10k: decouple suspend code
  ath10k: move free_vdev_map initialization
  ath10k: make sure all resources are freed upon ath10k_stop()
  ath10k: defer hw setup to start/stop mac80211 hooks
  ath10k: store firmware files in memory
  ath10k: skip fw stats debugfs interface if device is down

 drivers/net/wireless/ath/ath10k/bmi.c   |    6 +
 drivers/net/wireless/ath/ath10k/bmi.h   |    1 +
 drivers/net/wireless/ath/ath10k/core.c  |  249 +++++++++++++++++----------
 drivers/net/wireless/ath/ath10k/core.h  |   16 +-
 drivers/net/wireless/ath/ath10k/debug.c |   30 ++--
 drivers/net/wireless/ath/ath10k/hif.h   |   39 +++++
 drivers/net/wireless/ath/ath10k/mac.c   |  151 ++++++++++++++++-
 drivers/net/wireless/ath/ath10k/pci.c   |  283 +++++++++++++------------------
 8 files changed, 497 insertions(+), 278 deletions(-)

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH v4 00/10] ath10k: device setup refactor
@ 2013-07-16  7:38   ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is a respin of my patchset that addresses
hibernation issues.

This is also groundwork for proper hw recovery
that I'm going to post too.

v4:
 * update comments in pci logic decoupling
 * add missing commit message for one of the patches
 * update commit message for free_vdev_map patch

Michal Kazior (10):
  ath10k: decouple pci start/stop logic
  ath10k: decouple core start/stop logic
  ath10k: allow deferred regd update
  ath10k: reset BMI state upon init
  ath10k: decouple suspend code
  ath10k: move free_vdev_map initialization
  ath10k: make sure all resources are freed upon ath10k_stop()
  ath10k: defer hw setup to start/stop mac80211 hooks
  ath10k: store firmware files in memory
  ath10k: skip fw stats debugfs interface if device is down

 drivers/net/wireless/ath/ath10k/bmi.c   |    6 +
 drivers/net/wireless/ath/ath10k/bmi.h   |    1 +
 drivers/net/wireless/ath/ath10k/core.c  |  249 +++++++++++++++++----------
 drivers/net/wireless/ath/ath10k/core.h  |   16 +-
 drivers/net/wireless/ath/ath10k/debug.c |   30 ++--
 drivers/net/wireless/ath/ath10k/hif.h   |   39 +++++
 drivers/net/wireless/ath/ath10k/mac.c   |  151 ++++++++++++++++-
 drivers/net/wireless/ath/ath10k/pci.c   |  283 +++++++++++++------------------
 8 files changed, 497 insertions(+), 278 deletions(-)

-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH v4 01/10] ath10k: decouple pci start/stop logic
  2013-07-16  7:38   ` Michal Kazior
@ 2013-07-16  7:38     ` Michal Kazior
  -1 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Split logic that prepares the device for BMI
phase/cleans up related resources.

This is necessary for ath10k to be able to restart
hw on the fly without reloading the module.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/hif.h |   20 ++++++
 drivers/net/wireless/ath/ath10k/pci.c |  110 +++++++++++++++++++++------------
 2 files changed, 91 insertions(+), 39 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 010e265..00d2940 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -46,8 +46,11 @@ struct ath10k_hif_ops {
 				void *request, u32 request_len,
 				void *response, u32 *response_len);
 
+	/* Post BMI phase, after FW is loaded. Starts regular operation */
 	int (*start)(struct ath10k *ar);
 
+	/* Clean up what start() did. This does not revert to BMI phase. If
+	 * desired so, call power_down() and power_up() */
 	void (*stop)(struct ath10k *ar);
 
 	int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
@@ -70,6 +73,13 @@ struct ath10k_hif_ops {
 			      struct ath10k_hif_cb *callbacks);
 
 	u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+
+	/* Power up the device and enter BMI transfer mode for FW download */
+	int (*power_up)(struct ath10k *ar);
+
+	/* 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);
 };
 
 
@@ -134,4 +144,14 @@ static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
 	return ar->hif.ops->get_free_queue_number(ar, pipe_id);
 }
 
+static inline int ath10k_hif_power_up(struct ath10k *ar)
+{
+	return ar->hif.ops->power_up(ar);
+}
+
+static inline void ath10k_hif_power_down(struct ath10k *ar)
+{
+	ar->hif.ops->power_down(ar);
+}
+
 #endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 7da0983..c15b0c5 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -54,6 +54,8 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
 					     int num);
 static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
 static void ath10k_pci_stop_ce(struct ath10k *ar);
+static void ath10k_pci_device_reset(struct ath10k *ar);
+static int ath10k_pci_reset_target(struct ath10k *ar);
 
 static const struct ce_attr host_ce_config_wlan[] = {
 	/* host->target HTC control and raw streams */
@@ -1734,6 +1736,66 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
 	ath10k_pci_sleep(ar);
 }
 
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
+{
+	int ret;
+
+	/*
+	 * Bring the target up cleanly.
+	 *
+	 * The target may be in an undefined state with an AUX-powered Target
+	 * and a Host in WoW mode. If the Host crashes, loses power, or is
+	 * restarted (without unloading the driver) then the Target is left
+	 * (aux) powered and running. On a subsequent driver load, the Target
+	 * is in an unexpected state. We try to catch that here in order to
+	 * reset the Target and retry the probe.
+	 */
+	ath10k_pci_device_reset(ar);
+
+	ret = ath10k_pci_reset_target(ar);
+	if (ret)
+		goto err;
+
+	if (ath10k_target_ps) {
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
+	} else {
+		/* Force AWAKE forever */
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
+		ath10k_do_pci_wake(ar);
+	}
+
+	ret = ath10k_pci_ce_init(ar);
+	if (ret)
+		goto err_ps;
+
+	ret = ath10k_pci_init_config(ar);
+	if (ret)
+		goto err_ce;
+
+	ret = ath10k_pci_wake_target_cpu(ar);
+	if (ret) {
+		ath10k_err("could not wake up target CPU (%d)\n", ret);
+		goto err_ce;
+	}
+
+	return 0;
+
+err_ce:
+	ath10k_pci_ce_deinit(ar);
+err_ps:
+	if (!ath10k_target_ps)
+		ath10k_do_pci_sleep(ar);
+err:
+	return ret;
+}
+
+static void ath10k_pci_hif_power_down(struct ath10k *ar)
+{
+	ath10k_pci_ce_deinit(ar);
+	if (!ath10k_target_ps)
+		ath10k_do_pci_sleep(ar);
+}
+
 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,
@@ -1744,6 +1806,8 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
 	.send_complete_check	= ath10k_pci_hif_send_complete_check,
 	.set_callbacks		= ath10k_pci_hif_set_callbacks,
 	.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,
 };
 
 static void ath10k_pci_ce_tasklet(unsigned long ptr)
@@ -2245,54 +2309,22 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 		goto err_iomap;
 	}
 
-	/*
-	 * Bring the target up cleanly.
-	 *
-	 * The target may be in an undefined state with an AUX-powered Target
-	 * and a Host in WoW mode. If the Host crashes, loses power, or is
-	 * restarted (without unloading the driver) then the Target is left
-	 * (aux) powered and running. On a subsequent driver load, the Target
-	 * is in an unexpected state. We try to catch that here in order to
-	 * reset the Target and retry the probe.
-	 */
-	ath10k_pci_device_reset(ar);
-
-	ret = ath10k_pci_reset_target(ar);
-	if (ret)
-		goto err_intr;
-
-	if (ath10k_target_ps) {
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
-	} else {
-		/* Force AWAKE forever */
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
-		ath10k_do_pci_wake(ar);
-	}
-
-	ret = ath10k_pci_ce_init(ar);
-	if (ret)
-		goto err_intr;
-
-	ret = ath10k_pci_init_config(ar);
-	if (ret)
-		goto err_ce;
-
-	ret = ath10k_pci_wake_target_cpu(ar);
+	ret = ath10k_pci_hif_power_up(ar);
 	if (ret) {
-		ath10k_err("could not wake up target CPU (%d)\n", ret);
-		goto err_ce;
+		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_ce;
+		goto err_hif;
 	}
 
 	return 0;
 
-err_ce:
-	ath10k_pci_ce_deinit(ar);
+err_hif:
+	ath10k_pci_hif_power_down(ar);
 err_intr:
 	ath10k_pci_stop_intr(ar);
 err_iomap:
@@ -2331,7 +2363,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
 	tasklet_kill(&ar_pci->msi_fw_err);
 
 	ath10k_core_unregister(ar);
-	ath10k_pci_ce_deinit(ar);
+	ath10k_pci_hif_power_down(ar);
 	ath10k_pci_stop_intr(ar);
 
 	pci_set_drvdata(pdev, NULL);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 01/10] ath10k: decouple pci start/stop logic
@ 2013-07-16  7:38     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Split logic that prepares the device for BMI
phase/cleans up related resources.

This is necessary for ath10k to be able to restart
hw on the fly without reloading the module.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/hif.h |   20 ++++++
 drivers/net/wireless/ath/ath10k/pci.c |  110 +++++++++++++++++++++------------
 2 files changed, 91 insertions(+), 39 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 010e265..00d2940 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -46,8 +46,11 @@ struct ath10k_hif_ops {
 				void *request, u32 request_len,
 				void *response, u32 *response_len);
 
+	/* Post BMI phase, after FW is loaded. Starts regular operation */
 	int (*start)(struct ath10k *ar);
 
+	/* Clean up what start() did. This does not revert to BMI phase. If
+	 * desired so, call power_down() and power_up() */
 	void (*stop)(struct ath10k *ar);
 
 	int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
@@ -70,6 +73,13 @@ struct ath10k_hif_ops {
 			      struct ath10k_hif_cb *callbacks);
 
 	u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+
+	/* Power up the device and enter BMI transfer mode for FW download */
+	int (*power_up)(struct ath10k *ar);
+
+	/* 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);
 };
 
 
@@ -134,4 +144,14 @@ static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
 	return ar->hif.ops->get_free_queue_number(ar, pipe_id);
 }
 
+static inline int ath10k_hif_power_up(struct ath10k *ar)
+{
+	return ar->hif.ops->power_up(ar);
+}
+
+static inline void ath10k_hif_power_down(struct ath10k *ar)
+{
+	ar->hif.ops->power_down(ar);
+}
+
 #endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 7da0983..c15b0c5 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -54,6 +54,8 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
 					     int num);
 static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
 static void ath10k_pci_stop_ce(struct ath10k *ar);
+static void ath10k_pci_device_reset(struct ath10k *ar);
+static int ath10k_pci_reset_target(struct ath10k *ar);
 
 static const struct ce_attr host_ce_config_wlan[] = {
 	/* host->target HTC control and raw streams */
@@ -1734,6 +1736,66 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
 	ath10k_pci_sleep(ar);
 }
 
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
+{
+	int ret;
+
+	/*
+	 * Bring the target up cleanly.
+	 *
+	 * The target may be in an undefined state with an AUX-powered Target
+	 * and a Host in WoW mode. If the Host crashes, loses power, or is
+	 * restarted (without unloading the driver) then the Target is left
+	 * (aux) powered and running. On a subsequent driver load, the Target
+	 * is in an unexpected state. We try to catch that here in order to
+	 * reset the Target and retry the probe.
+	 */
+	ath10k_pci_device_reset(ar);
+
+	ret = ath10k_pci_reset_target(ar);
+	if (ret)
+		goto err;
+
+	if (ath10k_target_ps) {
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
+	} else {
+		/* Force AWAKE forever */
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
+		ath10k_do_pci_wake(ar);
+	}
+
+	ret = ath10k_pci_ce_init(ar);
+	if (ret)
+		goto err_ps;
+
+	ret = ath10k_pci_init_config(ar);
+	if (ret)
+		goto err_ce;
+
+	ret = ath10k_pci_wake_target_cpu(ar);
+	if (ret) {
+		ath10k_err("could not wake up target CPU (%d)\n", ret);
+		goto err_ce;
+	}
+
+	return 0;
+
+err_ce:
+	ath10k_pci_ce_deinit(ar);
+err_ps:
+	if (!ath10k_target_ps)
+		ath10k_do_pci_sleep(ar);
+err:
+	return ret;
+}
+
+static void ath10k_pci_hif_power_down(struct ath10k *ar)
+{
+	ath10k_pci_ce_deinit(ar);
+	if (!ath10k_target_ps)
+		ath10k_do_pci_sleep(ar);
+}
+
 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,
@@ -1744,6 +1806,8 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
 	.send_complete_check	= ath10k_pci_hif_send_complete_check,
 	.set_callbacks		= ath10k_pci_hif_set_callbacks,
 	.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,
 };
 
 static void ath10k_pci_ce_tasklet(unsigned long ptr)
@@ -2245,54 +2309,22 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 		goto err_iomap;
 	}
 
-	/*
-	 * Bring the target up cleanly.
-	 *
-	 * The target may be in an undefined state with an AUX-powered Target
-	 * and a Host in WoW mode. If the Host crashes, loses power, or is
-	 * restarted (without unloading the driver) then the Target is left
-	 * (aux) powered and running. On a subsequent driver load, the Target
-	 * is in an unexpected state. We try to catch that here in order to
-	 * reset the Target and retry the probe.
-	 */
-	ath10k_pci_device_reset(ar);
-
-	ret = ath10k_pci_reset_target(ar);
-	if (ret)
-		goto err_intr;
-
-	if (ath10k_target_ps) {
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
-	} else {
-		/* Force AWAKE forever */
-		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
-		ath10k_do_pci_wake(ar);
-	}
-
-	ret = ath10k_pci_ce_init(ar);
-	if (ret)
-		goto err_intr;
-
-	ret = ath10k_pci_init_config(ar);
-	if (ret)
-		goto err_ce;
-
-	ret = ath10k_pci_wake_target_cpu(ar);
+	ret = ath10k_pci_hif_power_up(ar);
 	if (ret) {
-		ath10k_err("could not wake up target CPU (%d)\n", ret);
-		goto err_ce;
+		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_ce;
+		goto err_hif;
 	}
 
 	return 0;
 
-err_ce:
-	ath10k_pci_ce_deinit(ar);
+err_hif:
+	ath10k_pci_hif_power_down(ar);
 err_intr:
 	ath10k_pci_stop_intr(ar);
 err_iomap:
@@ -2331,7 +2363,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
 	tasklet_kill(&ar_pci->msi_fw_err);
 
 	ath10k_core_unregister(ar);
-	ath10k_pci_ce_deinit(ar);
+	ath10k_pci_hif_power_down(ar);
 	ath10k_pci_stop_intr(ar);
 
 	pci_set_drvdata(pdev, NULL);
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 02/10] ath10k: decouple core start/stop logic
  2013-07-16  7:38   ` Michal Kazior
@ 2013-07-16  7:38     ` Michal Kazior
  -1 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Enables code reuse for proper hw reconfiguration
that is in turn required for proper
suspend/hibernation/wowlan support.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |   44 +++++++++++++++++++++++---------
 drivers/net/wireless/ath/ath10k/core.h |    2 ++
 2 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index f1312fa..dcddae4 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -502,8 +502,7 @@ void ath10k_core_destroy(struct ath10k *ar)
 }
 EXPORT_SYMBOL(ath10k_core_destroy);
 
-
-int ath10k_core_register(struct ath10k *ar)
+int ath10k_core_start(struct ath10k *ar)
 {
 	struct bmi_target_info target_info;
 	int status;
@@ -589,9 +588,36 @@ int ath10k_core_register(struct ath10k *ar)
 	if (status)
 		goto err_disconnect_htc;
 
+	return 0;
+
+err_disconnect_htc:
+	ath10k_htc_stop(&ar->htc);
+err_htt_detach:
+	ath10k_htt_detach(&ar->htt);
+err_wmi_detach:
+	ath10k_wmi_detach(ar);
+err:
+	return status;
+}
+
+void ath10k_core_stop(struct ath10k *ar)
+{
+	ath10k_htc_stop(&ar->htc);
+	ath10k_htt_detach(&ar->htt);
+	ath10k_wmi_detach(ar);
+}
+
+int ath10k_core_register(struct ath10k *ar)
+{
+	int status;
+
+	status = ath10k_core_start(ar);
+	if (status)
+		goto err;
+
 	status = ath10k_mac_register(ar);
 	if (status)
-		goto err_disconnect_htc;
+		goto err_core_stop;
 
 	status = ath10k_debug_create(ar);
 	if (status) {
@@ -603,12 +629,8 @@ int ath10k_core_register(struct ath10k *ar)
 
 err_unregister_mac:
 	ath10k_mac_unregister(ar);
-err_disconnect_htc:
-	ath10k_htc_stop(&ar->htc);
-err_htt_detach:
-	ath10k_htt_detach(&ar->htt);
-err_wmi_detach:
-	ath10k_wmi_detach(ar);
+err_core_stop:
+	ath10k_core_stop(ar);
 err:
 	return status;
 }
@@ -620,9 +642,7 @@ 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_htc_stop(&ar->htc);
-	ath10k_htt_detach(&ar->htt);
-	ath10k_wmi_detach(ar);
+	ath10k_core_stop(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 2f7065c..5a0b2ce 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -353,6 +353,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 				  const struct ath10k_hif_ops *hif_ops);
 void ath10k_core_destroy(struct ath10k *ar);
 
+int ath10k_core_start(struct ath10k *ar);
+void ath10k_core_stop(struct ath10k *ar);
 int ath10k_core_register(struct ath10k *ar);
 void ath10k_core_unregister(struct ath10k *ar);
 
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 02/10] ath10k: decouple core start/stop logic
@ 2013-07-16  7:38     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Enables code reuse for proper hw reconfiguration
that is in turn required for proper
suspend/hibernation/wowlan support.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |   44 +++++++++++++++++++++++---------
 drivers/net/wireless/ath/ath10k/core.h |    2 ++
 2 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index f1312fa..dcddae4 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -502,8 +502,7 @@ void ath10k_core_destroy(struct ath10k *ar)
 }
 EXPORT_SYMBOL(ath10k_core_destroy);
 
-
-int ath10k_core_register(struct ath10k *ar)
+int ath10k_core_start(struct ath10k *ar)
 {
 	struct bmi_target_info target_info;
 	int status;
@@ -589,9 +588,36 @@ int ath10k_core_register(struct ath10k *ar)
 	if (status)
 		goto err_disconnect_htc;
 
+	return 0;
+
+err_disconnect_htc:
+	ath10k_htc_stop(&ar->htc);
+err_htt_detach:
+	ath10k_htt_detach(&ar->htt);
+err_wmi_detach:
+	ath10k_wmi_detach(ar);
+err:
+	return status;
+}
+
+void ath10k_core_stop(struct ath10k *ar)
+{
+	ath10k_htc_stop(&ar->htc);
+	ath10k_htt_detach(&ar->htt);
+	ath10k_wmi_detach(ar);
+}
+
+int ath10k_core_register(struct ath10k *ar)
+{
+	int status;
+
+	status = ath10k_core_start(ar);
+	if (status)
+		goto err;
+
 	status = ath10k_mac_register(ar);
 	if (status)
-		goto err_disconnect_htc;
+		goto err_core_stop;
 
 	status = ath10k_debug_create(ar);
 	if (status) {
@@ -603,12 +629,8 @@ int ath10k_core_register(struct ath10k *ar)
 
 err_unregister_mac:
 	ath10k_mac_unregister(ar);
-err_disconnect_htc:
-	ath10k_htc_stop(&ar->htc);
-err_htt_detach:
-	ath10k_htt_detach(&ar->htt);
-err_wmi_detach:
-	ath10k_wmi_detach(ar);
+err_core_stop:
+	ath10k_core_stop(ar);
 err:
 	return status;
 }
@@ -620,9 +642,7 @@ 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_htc_stop(&ar->htc);
-	ath10k_htt_detach(&ar->htt);
-	ath10k_wmi_detach(ar);
+	ath10k_core_stop(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 2f7065c..5a0b2ce 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -353,6 +353,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 				  const struct ath10k_hif_ops *hif_ops);
 void ath10k_core_destroy(struct ath10k *ar);
 
+int ath10k_core_start(struct ath10k *ar);
+void ath10k_core_stop(struct ath10k *ar);
 int ath10k_core_register(struct ath10k *ar);
 void ath10k_core_unregister(struct ath10k *ar);
 
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 03/10] ath10k: allow deferred regd update
  2013-07-16  7:38   ` Michal Kazior
@ 2013-07-16  7:38     ` Michal Kazior
  -1 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Regulatory domain notification hook can be called
regardless of the hw state (i.e. before start
mac80211 callback).

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.h |    7 +++++++
 drivers/net/wireless/ath/ath10k/mac.c  |   26 +++++++++++++++++++-------
 2 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5a0b2ce..d5b2533 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -247,6 +247,11 @@ struct ath10k_debug {
 	struct completion event_stats_compl;
 };
 
+enum ath10k_state {
+	ATH10K_STATE_OFF = 0,
+	ATH10K_STATE_ON,
+};
+
 struct ath10k {
 	struct ath_common ath_common;
 	struct ieee80211_hw *hw;
@@ -344,6 +349,8 @@ struct ath10k {
 	struct completion offchan_tx_completed;
 	struct sk_buff *offchan_tx_skb;
 
+	enum ath10k_state state;
+
 #ifdef CONFIG_ATH10K_DEBUGFS
 	struct ath10k_debug debug;
 #endif
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 5082503..4627c16 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1310,23 +1310,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
 	return ret;
 }
 
-static void ath10k_reg_notifier(struct wiphy *wiphy,
-				struct regulatory_request *request)
+static void ath10k_regd_update(struct ath10k *ar)
 {
-	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
 	struct reg_dmn_pair_mapping *regpair;
-	struct ath10k *ar = hw->priv;
 	int ret;
 
-	mutex_lock(&ar->conf_mutex);
-
-	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+	lockdep_assert_held(&ar->conf_mutex);
 
 	ret = ath10k_update_channel_list(ar);
 	if (ret)
 		ath10k_warn("could not update channel list (%d)\n", ret);
 
 	regpair = ar->ath_common.regulatory.regpair;
+
 	/* Target allows setting up per-band regdomain but ath_common provides
 	 * a combined one only */
 	ret = ath10k_wmi_pdev_set_regdomain(ar,
@@ -1337,7 +1333,19 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
 					    regpair->reg_5ghz_ctl);
 	if (ret)
 		ath10k_warn("could not set pdev regdomain (%d)\n", ret);
+}
 
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+				struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct ath10k *ar = hw->priv;
+
+	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+	mutex_lock(&ar->conf_mutex);
+	if (ar->state == ATH10K_STATE_ON)
+		ath10k_regd_update(ar);
 	mutex_unlock(&ar->conf_mutex);
 }
 
@@ -1732,6 +1740,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);
+
 	mutex_unlock(&ar->conf_mutex);
 	return 0;
 }
@@ -1742,6 +1753,7 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 
 	mutex_lock(&ar->conf_mutex);
 	ath10k_offchan_tx_purge(ar);
+	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
 	cancel_work_sync(&ar->offchan_tx_work);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 03/10] ath10k: allow deferred regd update
@ 2013-07-16  7:38     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Regulatory domain notification hook can be called
regardless of the hw state (i.e. before start
mac80211 callback).

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.h |    7 +++++++
 drivers/net/wireless/ath/ath10k/mac.c  |   26 +++++++++++++++++++-------
 2 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5a0b2ce..d5b2533 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -247,6 +247,11 @@ struct ath10k_debug {
 	struct completion event_stats_compl;
 };
 
+enum ath10k_state {
+	ATH10K_STATE_OFF = 0,
+	ATH10K_STATE_ON,
+};
+
 struct ath10k {
 	struct ath_common ath_common;
 	struct ieee80211_hw *hw;
@@ -344,6 +349,8 @@ struct ath10k {
 	struct completion offchan_tx_completed;
 	struct sk_buff *offchan_tx_skb;
 
+	enum ath10k_state state;
+
 #ifdef CONFIG_ATH10K_DEBUGFS
 	struct ath10k_debug debug;
 #endif
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 5082503..4627c16 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1310,23 +1310,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
 	return ret;
 }
 
-static void ath10k_reg_notifier(struct wiphy *wiphy,
-				struct regulatory_request *request)
+static void ath10k_regd_update(struct ath10k *ar)
 {
-	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
 	struct reg_dmn_pair_mapping *regpair;
-	struct ath10k *ar = hw->priv;
 	int ret;
 
-	mutex_lock(&ar->conf_mutex);
-
-	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+	lockdep_assert_held(&ar->conf_mutex);
 
 	ret = ath10k_update_channel_list(ar);
 	if (ret)
 		ath10k_warn("could not update channel list (%d)\n", ret);
 
 	regpair = ar->ath_common.regulatory.regpair;
+
 	/* Target allows setting up per-band regdomain but ath_common provides
 	 * a combined one only */
 	ret = ath10k_wmi_pdev_set_regdomain(ar,
@@ -1337,7 +1333,19 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
 					    regpair->reg_5ghz_ctl);
 	if (ret)
 		ath10k_warn("could not set pdev regdomain (%d)\n", ret);
+}
 
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+				struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct ath10k *ar = hw->priv;
+
+	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+	mutex_lock(&ar->conf_mutex);
+	if (ar->state == ATH10K_STATE_ON)
+		ath10k_regd_update(ar);
 	mutex_unlock(&ar->conf_mutex);
 }
 
@@ -1732,6 +1740,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);
+
 	mutex_unlock(&ar->conf_mutex);
 	return 0;
 }
@@ -1742,6 +1753,7 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 
 	mutex_lock(&ar->conf_mutex);
 	ath10k_offchan_tx_purge(ar);
+	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
 	cancel_work_sync(&ar->offchan_tx_work);
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 04/10] ath10k: reset BMI state upon init
  2013-07-16  7:38   ` Michal Kazior
@ 2013-07-16  7:38     ` Michal Kazior
  -1 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is necessary if we want to be able to restart
hw on-the-fly.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/bmi.c  |    6 ++++++
 drivers/net/wireless/ath/ath10k/bmi.h  |    1 +
 drivers/net/wireless/ath/ath10k/core.c |    2 ++
 3 files changed, 9 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c
index aeae029..744da6d 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.c
+++ b/drivers/net/wireless/ath/ath10k/bmi.c
@@ -20,6 +20,12 @@
 #include "debug.h"
 #include "htc.h"
 
+void ath10k_bmi_start(struct ath10k *ar)
+{
+	ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
+	ar->bmi.done_sent = false;
+}
+
 int ath10k_bmi_done(struct ath10k *ar)
 {
 	struct bmi_cmd cmd;
diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h
index 32c56aa..8d81ce1 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.h
+++ b/drivers/net/wireless/ath/ath10k/bmi.h
@@ -184,6 +184,7 @@ struct bmi_target_info {
 #define BMI_CE_NUM_TO_TARG 0
 #define BMI_CE_NUM_TO_HOST 1
 
+void ath10k_bmi_start(struct ath10k *ar);
 int ath10k_bmi_done(struct ath10k *ar);
 int ath10k_bmi_get_target_info(struct ath10k *ar,
 			       struct bmi_target_info *target_info);
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index dcddae4..3d75c6a 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -507,6 +507,8 @@ int ath10k_core_start(struct ath10k *ar)
 	struct bmi_target_info target_info;
 	int status;
 
+	ath10k_bmi_start(ar);
+
 	memset(&target_info, 0, sizeof(target_info));
 	status = ath10k_bmi_get_target_info(ar, &target_info);
 	if (status)
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 04/10] ath10k: reset BMI state upon init
@ 2013-07-16  7:38     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is necessary if we want to be able to restart
hw on-the-fly.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/bmi.c  |    6 ++++++
 drivers/net/wireless/ath/ath10k/bmi.h  |    1 +
 drivers/net/wireless/ath/ath10k/core.c |    2 ++
 3 files changed, 9 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c
index aeae029..744da6d 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.c
+++ b/drivers/net/wireless/ath/ath10k/bmi.c
@@ -20,6 +20,12 @@
 #include "debug.h"
 #include "htc.h"
 
+void ath10k_bmi_start(struct ath10k *ar)
+{
+	ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
+	ar->bmi.done_sent = false;
+}
+
 int ath10k_bmi_done(struct ath10k *ar)
 {
 	struct bmi_cmd cmd;
diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h
index 32c56aa..8d81ce1 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.h
+++ b/drivers/net/wireless/ath/ath10k/bmi.h
@@ -184,6 +184,7 @@ struct bmi_target_info {
 #define BMI_CE_NUM_TO_TARG 0
 #define BMI_CE_NUM_TO_HOST 1
 
+void ath10k_bmi_start(struct ath10k *ar);
 int ath10k_bmi_done(struct ath10k *ar);
 int ath10k_bmi_get_target_info(struct ath10k *ar,
 			       struct bmi_target_info *target_info);
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index dcddae4..3d75c6a 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -507,6 +507,8 @@ int ath10k_core_start(struct ath10k *ar)
 	struct bmi_target_info target_info;
 	int status;
 
+	ath10k_bmi_start(ar);
+
 	memset(&target_info, 0, sizeof(target_info));
 	status = ath10k_bmi_get_target_info(ar, &target_info);
 	if (status)
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 05/10] ath10k: decouple suspend code
  2013-07-16  7:38   ` Michal Kazior
@ 2013-07-16  7:38     ` Michal Kazior
  -1 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

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>
---
 drivers/net/wireless/ath/ath10k/core.c |   28 -----
 drivers/net/wireless/ath/ath10k/core.h |    3 -
 drivers/net/wireless/ath/ath10k/hif.h  |   19 ++++
 drivers/net/wireless/ath/ath10k/mac.c  |   66 ++++++++++++
 drivers/net/wireless/ath/ath10k/pci.c  |  176 ++++++++++----------------------
 5 files changed, 138 insertions(+), 154 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 3d75c6a..01c1d82 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -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");
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index d5b2533..5c94ea0 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -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_ */
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 00d2940..dcdea68 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.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_ */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 4627c16..febba7d 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -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) { \
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index c15b0c5..75f5427 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -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)
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 05/10] ath10k: decouple suspend code
@ 2013-07-16  7:38     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

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>
---
 drivers/net/wireless/ath/ath10k/core.c |   28 -----
 drivers/net/wireless/ath/ath10k/core.h |    3 -
 drivers/net/wireless/ath/ath10k/hif.h  |   19 ++++
 drivers/net/wireless/ath/ath10k/mac.c  |   66 ++++++++++++
 drivers/net/wireless/ath/ath10k/pci.c  |  176 ++++++++++----------------------
 5 files changed, 138 insertions(+), 154 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 3d75c6a..01c1d82 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -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");
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index d5b2533..5c94ea0 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -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_ */
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 00d2940..dcdea68 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.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_ */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 4627c16..febba7d 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -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) { \
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index c15b0c5..75f5427 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -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)
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 06/10] ath10k: move free_vdev_map initialization
  2013-07-16  7:38   ` Michal Kazior
@ 2013-07-16  7:38     ` Michal Kazior
  -1 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is necessary for hw reconfiguration to work.
Since mac80211 is not calling remove_interface()
is such case we must reset free_vdev_map.

Also use a define instead of a hardcoded value for
vdev map initialization.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 01c1d82..0e4a704 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -458,8 +458,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 	ar->hif.priv = hif_priv;
 	ar->hif.ops = hif_ops;
 
-	ar->free_vdev_map = 0xFF; /* 8 vdevs */
-
 	init_completion(&ar->scan.started);
 	init_completion(&ar->scan.completed);
 	init_completion(&ar->scan.on_channel);
@@ -590,6 +588,8 @@ int ath10k_core_start(struct ath10k *ar)
 	if (status)
 		goto err_disconnect_htc;
 
+	ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+
 	return 0;
 
 err_disconnect_htc:
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 06/10] ath10k: move free_vdev_map initialization
@ 2013-07-16  7:38     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is necessary for hw reconfiguration to work.
Since mac80211 is not calling remove_interface()
is such case we must reset free_vdev_map.

Also use a define instead of a hardcoded value for
vdev map initialization.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 01c1d82..0e4a704 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -458,8 +458,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 	ar->hif.priv = hif_priv;
 	ar->hif.ops = hif_ops;
 
-	ar->free_vdev_map = 0xFF; /* 8 vdevs */
-
 	init_completion(&ar->scan.started);
 	init_completion(&ar->scan.completed);
 	init_completion(&ar->scan.on_channel);
@@ -590,6 +588,8 @@ int ath10k_core_start(struct ath10k *ar)
 	if (status)
 		goto err_disconnect_htc;
 
+	ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+
 	return 0;
 
 err_disconnect_htc:
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 07/10] ath10k: make sure all resources are freed upon ath10k_stop()
  2013-07-16  7:38   ` Michal Kazior
@ 2013-07-16  7:38     ` Michal Kazior
  -1 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is necessary for proper hw reconfiguration
and to avoid memory leaks.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/mac.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index febba7d..b9663e9 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -369,6 +369,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
 	spin_unlock_bh(&ar->data_lock);
 }
 
+static void ath10k_peer_cleanup_all(struct ath10k *ar)
+{
+	struct ath10k_peer *peer, *tmp;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	spin_lock_bh(&ar->data_lock);
+	list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+		list_del(&peer->list);
+		kfree(peer);
+	}
+	spin_unlock_bh(&ar->data_lock);
+}
+
 /************************/
 /* Interface management */
 /************************/
@@ -1753,7 +1767,18 @@ 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);
+
 	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 07/10] ath10k: make sure all resources are freed upon ath10k_stop()
@ 2013-07-16  7:38     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

This is necessary for proper hw reconfiguration
and to avoid memory leaks.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/mac.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index febba7d..b9663e9 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -369,6 +369,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
 	spin_unlock_bh(&ar->data_lock);
 }
 
+static void ath10k_peer_cleanup_all(struct ath10k *ar)
+{
+	struct ath10k_peer *peer, *tmp;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	spin_lock_bh(&ar->data_lock);
+	list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+		list_del(&peer->list);
+		kfree(peer);
+	}
+	spin_unlock_bh(&ar->data_lock);
+}
+
 /************************/
 /* Interface management */
 /************************/
@@ -1753,7 +1767,18 @@ 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);
+
 	ar->state = ATH10K_STATE_OFF;
 	mutex_unlock(&ar->conf_mutex);
 
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 08/10] ath10k: defer hw setup to start/stop mac80211 hooks
  2013-07-16  7:38   ` Michal Kazior
@ 2013-07-16  7:38     ` Michal Kazior
  -1 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

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>
---
 drivers/net/wireless/ath/ath10k/core.c |   46 +++++++++++++++++++++-----
 drivers/net/wireless/ath/ath10k/mac.c  |   56 ++++++++++++++++++++++++--------
 drivers/net/wireless/ath/ath10k/pci.c  |   11 +------
 3 files changed, 81 insertions(+), 32 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 0e4a704..1d4c245 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -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);
 
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index b9663e9..2dfd446 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -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);
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 75f5427..bfe8561 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -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);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 08/10] ath10k: defer hw setup to start/stop mac80211 hooks
@ 2013-07-16  7:38     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

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>
---
 drivers/net/wireless/ath/ath10k/core.c |   46 +++++++++++++++++++++-----
 drivers/net/wireless/ath/ath10k/mac.c  |   56 ++++++++++++++++++++++++--------
 drivers/net/wireless/ath/ath10k/pci.c  |   11 +------
 3 files changed, 81 insertions(+), 32 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 0e4a704..1d4c245 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -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);
 
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index b9663e9..2dfd446 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -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);
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 75f5427..bfe8561 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -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);
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 09/10] ath10k: store firmware files in memory
  2013-07-16  7:38   ` Michal Kazior
@ 2013-07-16  7:38     ` Michal Kazior
  -1 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Different FW versions may provide different
functions thus mean different hw capabilities
advertised to mac80211.

It is safe to swap firmware files on disk during
driver/device runtime without worries.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |  157 +++++++++++++++++++++-----------
 drivers/net/wireless/ath/ath10k/core.h |    4 +
 2 files changed, 109 insertions(+), 52 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 1d4c245..c37f79f 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
 
 static int ath10k_download_board_data(struct ath10k *ar)
 {
+	const struct firmware *fw = ar->board_data;
 	u32 board_data_size = QCA988X_BOARD_DATA_SZ;
 	u32 address;
-	const struct firmware *fw;
 	int ret;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.board);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch board data fw file (%ld)\n",
-			   PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
 	ret = ath10k_push_board_ext_data(ar, fw);
 	if (ret) {
 		ath10k_err("could not push board ext data (%d)\n", ret);
@@ -286,32 +278,20 @@ static int ath10k_download_board_data(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-	const struct firmware *fw;
-	u32 address;
+	const struct firmware *fw = ar->otp;
+	u32 address = ar->hw_params.patch_load_addr;
 	u32 exec_param;
 	int ret;
 
 	/* OTP is optional */
 
-	if (ar->hw_params.fw.otp == NULL) {
-		ath10k_info("otp file not defined\n");
-		return 0;
-	}
-
-	address = ar->hw_params.patch_load_addr;
-
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.otp);
-	if (IS_ERR(fw)) {
-		ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+	if (!ar->otp)
 		return 0;
-	}
 
 	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
 	if (ret) {
@@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_fw(struct ath10k *ar)
 {
-	const struct firmware *fw;
+	const struct firmware *fw = ar->firmware;
 	u32 address;
 	int ret;
 
-	if (ar->hw_params.fw.fw == NULL)
-		return -EINVAL;
-
 	address = ar->hw_params.patch_load_addr;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.fw);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
 	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
 	if (ret) {
 		ath10k_err("could not write fw (%d)\n", ret);
@@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
+	return ret;
+}
+
+static void ath10k_core_free_firmware_files(struct ath10k *ar)
+{
+	if (ar->board_data && !IS_ERR(ar->board_data))
+		release_firmware(ar->board_data);
+
+	if (ar->otp && !IS_ERR(ar->otp))
+		release_firmware(ar->otp);
+
+	if (ar->firmware && !IS_ERR(ar->firmware))
+		release_firmware(ar->firmware);
+
+	ar->board_data = NULL;
+	ar->otp = NULL;
+	ar->firmware = NULL;
+}
+
+static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+{
+	int ret = 0;
+
+	if (ar->hw_params.fw.fw == NULL) {
+		ath10k_err("firmware file not defined\n");
+		return -EINVAL;
+	}
+
+	if (ar->hw_params.fw.board == NULL) {
+		ath10k_err("board data file not defined");
+		return -EINVAL;
+	}
+
+	ar->board_data = ath10k_fetch_fw_file(ar,
+					      ar->hw_params.fw.dir,
+					      ar->hw_params.fw.board);
+	if (IS_ERR(ar->board_data)) {
+		ret = PTR_ERR(ar->board_data);
+		ath10k_err("could not fetch board data (%d)\n", ret);
+		goto err;
+	}
+
+	ar->firmware = ath10k_fetch_fw_file(ar,
+					    ar->hw_params.fw.dir,
+					    ar->hw_params.fw.fw);
+	if (IS_ERR(ar->firmware)) {
+		ret = PTR_ERR(ar->firmware);
+		ath10k_err("could not fetch firmware (%d)\n", ret);
+		goto err;
+	}
+
+	/* OTP may be undefined. If so, don't fetch it at all */
+	if (ar->hw_params.fw.otp == NULL)
+		return 0;
+
+	ar->otp = ath10k_fetch_fw_file(ar,
+				       ar->hw_params.fw.dir,
+				       ar->hw_params.fw.otp);
+	if (IS_ERR(ar->otp)) {
+		ret = PTR_ERR(ar->otp);
+		ath10k_err("could not fetch otp (%d)\n", ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	ath10k_core_free_firmware_files(ar);
 	return ret;
 }
 
@@ -502,23 +538,10 @@ EXPORT_SYMBOL(ath10k_core_destroy);
 
 int ath10k_core_start(struct ath10k *ar)
 {
-	struct bmi_target_info target_info;
 	int status;
 
 	ath10k_bmi_start(ar);
 
-	memset(&target_info, 0, sizeof(target_info));
-	status = ath10k_bmi_get_target_info(ar, &target_info);
-	if (status)
-		goto err;
-
-	ar->target_version = target_info.version;
-	ar->hw->wiphy->hw_version = target_info.version;
-
-	status = ath10k_init_hw_params(ar);
-	if (status)
-		goto err;
-
 	if (ath10k_init_configure_target(ar)) {
 		status = -EINVAL;
 		goto err;
@@ -617,7 +640,8 @@ EXPORT_SYMBOL(ath10k_core_stop);
  * hook will try to init it again) before registering */
 static int ath10k_core_probe_fw(struct ath10k *ar)
 {
-	int ret;
+	struct bmi_target_info target_info;
+	int ret = 0;
 
 	ret = ath10k_hif_power_up(ar);
 	if (ret) {
@@ -625,9 +649,35 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
 		return ret;
 	}
 
+	memset(&target_info, 0, sizeof(target_info));
+	ret = ath10k_bmi_get_target_info(ar, &target_info);
+	if (ret) {
+		ath10k_err("could not get target info (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
+	ar->target_version = target_info.version;
+	ar->hw->wiphy->hw_version = target_info.version;
+
+	ret = ath10k_init_hw_params(ar);
+	if (ret) {
+		ath10k_err("could not get hw params (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
+	ret = ath10k_core_fetch_firmware_files(ar);
+	if (ret) {
+		ath10k_err("could not fetch firmware files (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
 	ret = ath10k_core_start(ar);
 	if (ret) {
 		ath10k_err("could not init core (%d)\n", ret);
+		ath10k_core_free_firmware_files(ar);
 		ath10k_hif_power_down(ar);
 		return ret;
 	}
@@ -650,7 +700,7 @@ int ath10k_core_register(struct ath10k *ar)
 	status = ath10k_mac_register(ar);
 	if (status) {
 		ath10k_err("could not register to mac80211 (%d)\n", status);
-		return status;
+		goto err_release_fw;
 	}
 
 	status = ath10k_debug_create(ar);
@@ -663,6 +713,8 @@ int ath10k_core_register(struct ath10k *ar)
 
 err_unregister_mac:
 	ath10k_mac_unregister(ar);
+err_release_fw:
+	ath10k_core_free_firmware_files(ar);
 	return status;
 }
 EXPORT_SYMBOL(ath10k_core_register);
@@ -673,6 +725,7 @@ 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_free_firmware_files(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5c94ea0..413f1c5 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -300,6 +300,10 @@ struct ath10k {
 		} fw;
 	} hw_params;
 
+	const struct firmware *board_data;
+	const struct firmware *otp;
+	const struct firmware *firmware;
+
 	struct {
 		struct completion started;
 		struct completion completed;
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 09/10] ath10k: store firmware files in memory
@ 2013-07-16  7:38     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

Different FW versions may provide different
functions thus mean different hw capabilities
advertised to mac80211.

It is safe to swap firmware files on disk during
driver/device runtime without worries.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.c |  157 +++++++++++++++++++++-----------
 drivers/net/wireless/ath/ath10k/core.h |    4 +
 2 files changed, 109 insertions(+), 52 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 1d4c245..c37f79f 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
 
 static int ath10k_download_board_data(struct ath10k *ar)
 {
+	const struct firmware *fw = ar->board_data;
 	u32 board_data_size = QCA988X_BOARD_DATA_SZ;
 	u32 address;
-	const struct firmware *fw;
 	int ret;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.board);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch board data fw file (%ld)\n",
-			   PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
 	ret = ath10k_push_board_ext_data(ar, fw);
 	if (ret) {
 		ath10k_err("could not push board ext data (%d)\n", ret);
@@ -286,32 +278,20 @@ static int ath10k_download_board_data(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-	const struct firmware *fw;
-	u32 address;
+	const struct firmware *fw = ar->otp;
+	u32 address = ar->hw_params.patch_load_addr;
 	u32 exec_param;
 	int ret;
 
 	/* OTP is optional */
 
-	if (ar->hw_params.fw.otp == NULL) {
-		ath10k_info("otp file not defined\n");
-		return 0;
-	}
-
-	address = ar->hw_params.patch_load_addr;
-
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.otp);
-	if (IS_ERR(fw)) {
-		ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+	if (!ar->otp)
 		return 0;
-	}
 
 	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
 	if (ret) {
@@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
 	return ret;
 }
 
 static int ath10k_download_fw(struct ath10k *ar)
 {
-	const struct firmware *fw;
+	const struct firmware *fw = ar->firmware;
 	u32 address;
 	int ret;
 
-	if (ar->hw_params.fw.fw == NULL)
-		return -EINVAL;
-
 	address = ar->hw_params.patch_load_addr;
 
-	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-				  ar->hw_params.fw.fw);
-	if (IS_ERR(fw)) {
-		ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
-		return PTR_ERR(fw);
-	}
-
 	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
 	if (ret) {
 		ath10k_err("could not write fw (%d)\n", ret);
@@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar)
 	}
 
 exit:
-	release_firmware(fw);
+	return ret;
+}
+
+static void ath10k_core_free_firmware_files(struct ath10k *ar)
+{
+	if (ar->board_data && !IS_ERR(ar->board_data))
+		release_firmware(ar->board_data);
+
+	if (ar->otp && !IS_ERR(ar->otp))
+		release_firmware(ar->otp);
+
+	if (ar->firmware && !IS_ERR(ar->firmware))
+		release_firmware(ar->firmware);
+
+	ar->board_data = NULL;
+	ar->otp = NULL;
+	ar->firmware = NULL;
+}
+
+static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+{
+	int ret = 0;
+
+	if (ar->hw_params.fw.fw == NULL) {
+		ath10k_err("firmware file not defined\n");
+		return -EINVAL;
+	}
+
+	if (ar->hw_params.fw.board == NULL) {
+		ath10k_err("board data file not defined");
+		return -EINVAL;
+	}
+
+	ar->board_data = ath10k_fetch_fw_file(ar,
+					      ar->hw_params.fw.dir,
+					      ar->hw_params.fw.board);
+	if (IS_ERR(ar->board_data)) {
+		ret = PTR_ERR(ar->board_data);
+		ath10k_err("could not fetch board data (%d)\n", ret);
+		goto err;
+	}
+
+	ar->firmware = ath10k_fetch_fw_file(ar,
+					    ar->hw_params.fw.dir,
+					    ar->hw_params.fw.fw);
+	if (IS_ERR(ar->firmware)) {
+		ret = PTR_ERR(ar->firmware);
+		ath10k_err("could not fetch firmware (%d)\n", ret);
+		goto err;
+	}
+
+	/* OTP may be undefined. If so, don't fetch it at all */
+	if (ar->hw_params.fw.otp == NULL)
+		return 0;
+
+	ar->otp = ath10k_fetch_fw_file(ar,
+				       ar->hw_params.fw.dir,
+				       ar->hw_params.fw.otp);
+	if (IS_ERR(ar->otp)) {
+		ret = PTR_ERR(ar->otp);
+		ath10k_err("could not fetch otp (%d)\n", ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	ath10k_core_free_firmware_files(ar);
 	return ret;
 }
 
@@ -502,23 +538,10 @@ EXPORT_SYMBOL(ath10k_core_destroy);
 
 int ath10k_core_start(struct ath10k *ar)
 {
-	struct bmi_target_info target_info;
 	int status;
 
 	ath10k_bmi_start(ar);
 
-	memset(&target_info, 0, sizeof(target_info));
-	status = ath10k_bmi_get_target_info(ar, &target_info);
-	if (status)
-		goto err;
-
-	ar->target_version = target_info.version;
-	ar->hw->wiphy->hw_version = target_info.version;
-
-	status = ath10k_init_hw_params(ar);
-	if (status)
-		goto err;
-
 	if (ath10k_init_configure_target(ar)) {
 		status = -EINVAL;
 		goto err;
@@ -617,7 +640,8 @@ EXPORT_SYMBOL(ath10k_core_stop);
  * hook will try to init it again) before registering */
 static int ath10k_core_probe_fw(struct ath10k *ar)
 {
-	int ret;
+	struct bmi_target_info target_info;
+	int ret = 0;
 
 	ret = ath10k_hif_power_up(ar);
 	if (ret) {
@@ -625,9 +649,35 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
 		return ret;
 	}
 
+	memset(&target_info, 0, sizeof(target_info));
+	ret = ath10k_bmi_get_target_info(ar, &target_info);
+	if (ret) {
+		ath10k_err("could not get target info (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
+	ar->target_version = target_info.version;
+	ar->hw->wiphy->hw_version = target_info.version;
+
+	ret = ath10k_init_hw_params(ar);
+	if (ret) {
+		ath10k_err("could not get hw params (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
+	ret = ath10k_core_fetch_firmware_files(ar);
+	if (ret) {
+		ath10k_err("could not fetch firmware files (%d)\n", ret);
+		ath10k_hif_power_down(ar);
+		return ret;
+	}
+
 	ret = ath10k_core_start(ar);
 	if (ret) {
 		ath10k_err("could not init core (%d)\n", ret);
+		ath10k_core_free_firmware_files(ar);
 		ath10k_hif_power_down(ar);
 		return ret;
 	}
@@ -650,7 +700,7 @@ int ath10k_core_register(struct ath10k *ar)
 	status = ath10k_mac_register(ar);
 	if (status) {
 		ath10k_err("could not register to mac80211 (%d)\n", status);
-		return status;
+		goto err_release_fw;
 	}
 
 	status = ath10k_debug_create(ar);
@@ -663,6 +713,8 @@ int ath10k_core_register(struct ath10k *ar)
 
 err_unregister_mac:
 	ath10k_mac_unregister(ar);
+err_release_fw:
+	ath10k_core_free_firmware_files(ar);
 	return status;
 }
 EXPORT_SYMBOL(ath10k_core_register);
@@ -673,6 +725,7 @@ 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_free_firmware_files(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5c94ea0..413f1c5 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -300,6 +300,10 @@ struct ath10k {
 		} fw;
 	} hw_params;
 
+	const struct firmware *board_data;
+	const struct firmware *otp;
+	const struct firmware *firmware;
+
 	struct {
 		struct completion started;
 		struct completion completed;
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 10/10] ath10k: skip fw stats debugfs interface if device is down
  2013-07-16  7:38   ` Michal Kazior
@ 2013-07-16  7:38     ` Michal Kazior
  -1 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

If the device is not running then there may be no
FW at all to send the query to. If the FW is
already there it might still trigger a crash if
the command is sent before the device is fully
initialized.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/debug.c |   30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 499034b..65279f5 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
 	struct wmi_pdev_stats *ps;
 	int i;
 
-	mutex_lock(&ar->conf_mutex);
+	spin_lock_bh(&ar->data_lock);
 
 	stats = &ar->debug.target_stats;
 
@@ -259,6 +259,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
 		}
 	}
 
+	spin_unlock_bh(&ar->data_lock);
 	mutex_unlock(&ar->conf_mutex);
 	complete(&ar->debug.event_stats_compl);
 }
@@ -268,35 +269,35 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
 {
 	struct ath10k *ar = file->private_data;
 	struct ath10k_target_stats *fw_stats;
-	char *buf;
+	char *buf = NULL;
 	unsigned int len = 0, buf_len = 2500;
-	ssize_t ret_cnt;
+	ssize_t ret_cnt = 0;
 	long left;
 	int i;
 	int ret;
 
 	fw_stats = &ar->debug.target_stats;
 
+	mutex_lock(&ar->conf_mutex);
+
+	if (ar->state != ATH10K_STATE_ON)
+		goto exit;
+
 	buf = kzalloc(buf_len, GFP_KERNEL);
 	if (!buf)
-		return -ENOMEM;
+		goto exit;
 
 	ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
 	if (ret) {
 		ath10k_warn("could not request stats (%d)\n", ret);
-		kfree(buf);
-		return -EIO;
+		goto exit;
 	}
 
 	left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+	if (left <= 0)
+		goto exit;
 
-	if (left <= 0) {
-		kfree(buf);
-		return -ETIMEDOUT;
-	}
-
-	mutex_lock(&ar->conf_mutex);
-
+	spin_lock_bh(&ar->data_lock);
 	len += scnprintf(buf + len, buf_len - len, "\n");
 	len += scnprintf(buf + len, buf_len - len, "%30s\n",
 			 "ath10k PDEV stats");
@@ -424,14 +425,15 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
 				 fw_stats->peer_stat[i].peer_tx_rate);
 		len += scnprintf(buf + len, buf_len - len, "\n");
 	}
+	spin_unlock_bh(&ar->data_lock);
 
 	if (len > buf_len)
 		len = buf_len;
 
 	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 
+exit:
 	mutex_unlock(&ar->conf_mutex);
-
 	kfree(buf);
 	return ret_cnt;
 }
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 72+ messages in thread

* [PATCH v4 10/10] ath10k: skip fw stats debugfs interface if device is down
@ 2013-07-16  7:38     ` Michal Kazior
  0 siblings, 0 replies; 72+ messages in thread
From: Michal Kazior @ 2013-07-16  7:38 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Michal Kazior

If the device is not running then there may be no
FW at all to send the query to. If the FW is
already there it might still trigger a crash if
the command is sent before the device is fully
initialized.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/debug.c |   30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 499034b..65279f5 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
 	struct wmi_pdev_stats *ps;
 	int i;
 
-	mutex_lock(&ar->conf_mutex);
+	spin_lock_bh(&ar->data_lock);
 
 	stats = &ar->debug.target_stats;
 
@@ -259,6 +259,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
 		}
 	}
 
+	spin_unlock_bh(&ar->data_lock);
 	mutex_unlock(&ar->conf_mutex);
 	complete(&ar->debug.event_stats_compl);
 }
@@ -268,35 +269,35 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
 {
 	struct ath10k *ar = file->private_data;
 	struct ath10k_target_stats *fw_stats;
-	char *buf;
+	char *buf = NULL;
 	unsigned int len = 0, buf_len = 2500;
-	ssize_t ret_cnt;
+	ssize_t ret_cnt = 0;
 	long left;
 	int i;
 	int ret;
 
 	fw_stats = &ar->debug.target_stats;
 
+	mutex_lock(&ar->conf_mutex);
+
+	if (ar->state != ATH10K_STATE_ON)
+		goto exit;
+
 	buf = kzalloc(buf_len, GFP_KERNEL);
 	if (!buf)
-		return -ENOMEM;
+		goto exit;
 
 	ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
 	if (ret) {
 		ath10k_warn("could not request stats (%d)\n", ret);
-		kfree(buf);
-		return -EIO;
+		goto exit;
 	}
 
 	left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+	if (left <= 0)
+		goto exit;
 
-	if (left <= 0) {
-		kfree(buf);
-		return -ETIMEDOUT;
-	}
-
-	mutex_lock(&ar->conf_mutex);
-
+	spin_lock_bh(&ar->data_lock);
 	len += scnprintf(buf + len, buf_len - len, "\n");
 	len += scnprintf(buf + len, buf_len - len, "%30s\n",
 			 "ath10k PDEV stats");
@@ -424,14 +425,15 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
 				 fw_stats->peer_stat[i].peer_tx_rate);
 		len += scnprintf(buf + len, buf_len - len, "\n");
 	}
+	spin_unlock_bh(&ar->data_lock);
 
 	if (len > buf_len)
 		len = buf_len;
 
 	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 
+exit:
 	mutex_unlock(&ar->conf_mutex);
-
 	kfree(buf);
 	return ret_cnt;
 }
-- 
1.7.9.5


_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply related	[flat|nested] 72+ messages in thread

* Re: [PATCH v4 00/10] ath10k: device setup refactor
  2013-07-16  7:38   ` Michal Kazior
@ 2013-07-19 10:18     ` Kalle Valo
  -1 siblings, 0 replies; 72+ messages in thread
From: Kalle Valo @ 2013-07-19 10:18 UTC (permalink / raw)
  To: Michal Kazior; +Cc: ath10k, linux-wireless

Michal Kazior <michal.kazior@tieto.com> writes:

> This is a respin of my patchset that addresses
> hibernation issues.
>
> This is also groundwork for proper hw recovery
> that I'm going to post too.
>
> v4:
>  * update comments in pci logic decoupling
>  * add missing commit message for one of the patches
>  * update commit message for free_vdev_map patch
>
> Michal Kazior (10):
>   ath10k: decouple pci start/stop logic
>   ath10k: decouple core start/stop logic
>   ath10k: allow deferred regd update
>   ath10k: reset BMI state upon init
>   ath10k: decouple suspend code
>   ath10k: move free_vdev_map initialization
>   ath10k: make sure all resources are freed upon ath10k_stop()
>   ath10k: defer hw setup to start/stop mac80211 hooks
>   ath10k: store firmware files in memory
>   ath10k: skip fw stats debugfs interface if device is down

Thanks, all 10 applied.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH v4 00/10] ath10k: device setup refactor
@ 2013-07-19 10:18     ` Kalle Valo
  0 siblings, 0 replies; 72+ messages in thread
From: Kalle Valo @ 2013-07-19 10:18 UTC (permalink / raw)
  To: Michal Kazior; +Cc: linux-wireless, ath10k

Michal Kazior <michal.kazior@tieto.com> writes:

> This is a respin of my patchset that addresses
> hibernation issues.
>
> This is also groundwork for proper hw recovery
> that I'm going to post too.
>
> v4:
>  * update comments in pci logic decoupling
>  * add missing commit message for one of the patches
>  * update commit message for free_vdev_map patch
>
> Michal Kazior (10):
>   ath10k: decouple pci start/stop logic
>   ath10k: decouple core start/stop logic
>   ath10k: allow deferred regd update
>   ath10k: reset BMI state upon init
>   ath10k: decouple suspend code
>   ath10k: move free_vdev_map initialization
>   ath10k: make sure all resources are freed upon ath10k_stop()
>   ath10k: defer hw setup to start/stop mac80211 hooks
>   ath10k: store firmware files in memory
>   ath10k: skip fw stats debugfs interface if device is down

Thanks, all 10 applied.

-- 
Kalle Valo

_______________________________________________
ath10k mailing list
ath10k@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply	[flat|nested] 72+ messages in thread

end of thread, other threads:[~2013-07-19 10:19 UTC | newest]

Thread overview: 72+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-06-12 12:53 [ath9k-devel] [PATCH v2 00/10] ath10k: device setup refactor Michal Kazior
2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 01/10] ath10k: decouple pci init/deinit logic Michal Kazior
2013-06-13 18:09   ` Kalle Valo
2013-06-14 12:02     ` Michal Kazior
2013-06-14 12:14       ` Kalle Valo
2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 02/10] ath10k: decouple core start/stop logic Michal Kazior
2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 03/10] ath10k: allow deferred regd update Michal Kazior
2013-06-13 18:08   ` Kalle Valo
2013-06-14 12:03     ` Michal Kazior
2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 04/10] ath10k: reset BMI state upon init Michal Kazior
2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 05/10] ath10k: decouple suspend code Michal Kazior
2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 06/10] ath10k: move free_vdev_map initialization Michal Kazior
2013-06-12 12:53 ` [ath9k-devel] [PATCH v2 07/10] ath10k: make sure all resources are freed upon ath10k_stop() Michal Kazior
2013-06-12 12:54 ` [ath9k-devel] [PATCH v2 08/10] ath10k: defer hw setup to start/stop mac80211 hooks Michal Kazior
2013-06-14 12:23   ` Kalle Valo
2013-06-14 12:43     ` Michal Kazior
2013-06-12 12:54 ` [ath9k-devel] [PATCH v2 09/10] ath10k: skip updating some params during resume Michal Kazior
2013-06-12 12:54 ` [ath9k-devel] [PATCH v2 10/10] ath10k: store firmware files in memory Michal Kazior
2013-06-25  7:59 ` [ath9k-devel] [PATCH v3 0/9] ath10k: device setup refactor Michal Kazior
2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 1/9] ath10k: decouple pci start/stop logic Michal Kazior
2013-07-05  6:29     ` Kalle Valo
2013-07-05  7:05       ` Michal Kazior
2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 2/9] ath10k: decouple core " Michal Kazior
2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 3/9] ath10k: allow deferred regd update Michal Kazior
2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 4/9] ath10k: reset BMI state upon init Michal Kazior
2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 5/9] ath10k: decouple suspend code Michal Kazior
2013-07-05  6:51     ` Kalle Valo
2013-07-05  7:12       ` Michal Kazior
2013-07-05  7:20         ` Kalle Valo
2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 6/9] ath10k: move free_vdev_map initialization Michal Kazior
2013-07-05  7:03     ` Kalle Valo
2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 7/9] ath10k: make sure all resources are freed upon ath10k_stop() Michal Kazior
2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 8/9] ath10k: defer hw setup to start/stop mac80211 hooks Michal Kazior
2013-06-25  7:59   ` [ath9k-devel] [PATCH v3 9/9] ath10k: store firmware files in memory Michal Kazior
2013-06-25  8:09     ` [ath9k-devel] [PATCH v3 10/10] ath10k: skip fw stats debugfs interface if device is down Michal Kazior
2013-07-05  7:09       ` Kalle Valo
2013-07-16  7:19 ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
2013-07-16  7:19   ` [PATCH v3 01/10] ath10k: decouple pci start/stop logic Michal Kazior
2013-07-16  7:19   ` [PATCH v3 02/10] ath10k: decouple core " Michal Kazior
2013-07-16  7:19   ` [PATCH v3 03/10] ath10k: allow deferred regd update Michal Kazior
2013-07-16  7:19   ` [PATCH v3 04/10] ath10k: reset BMI state upon init Michal Kazior
2013-07-16  7:19   ` [PATCH v3 05/10] ath10k: decouple suspend code Michal Kazior
2013-07-16  7:19   ` [PATCH v3 06/10] ath10k: move free_vdev_map initialization Michal Kazior
2013-07-16  7:19   ` [PATCH v3 07/10] ath10k: make sure all resources are freed upon ath10k_stop() Michal Kazior
2013-07-16  7:19   ` [PATCH v3 08/10] ath10k: defer hw setup to start/stop mac80211 hooks Michal Kazior
2013-07-16  7:19   ` [PATCH v3 09/10] ath10k: store firmware files in memory Michal Kazior
2013-07-16  7:19   ` [PATCH v3 10/10] ath10k: skip fw stats debugfs interface if device is down Michal Kazior
2013-07-16  7:27   ` [PATCH v3 00/10] ath10k: device setup refactor Michal Kazior
2013-07-16  7:38 ` [PATCH v4 " Michal Kazior
2013-07-16  7:38   ` Michal Kazior
2013-07-16  7:38   ` [PATCH v4 01/10] ath10k: decouple pci start/stop logic Michal Kazior
2013-07-16  7:38     ` Michal Kazior
2013-07-16  7:38   ` [PATCH v4 02/10] ath10k: decouple core " Michal Kazior
2013-07-16  7:38     ` Michal Kazior
2013-07-16  7:38   ` [PATCH v4 03/10] ath10k: allow deferred regd update Michal Kazior
2013-07-16  7:38     ` Michal Kazior
2013-07-16  7:38   ` [PATCH v4 04/10] ath10k: reset BMI state upon init Michal Kazior
2013-07-16  7:38     ` Michal Kazior
2013-07-16  7:38   ` [PATCH v4 05/10] ath10k: decouple suspend code Michal Kazior
2013-07-16  7:38     ` Michal Kazior
2013-07-16  7:38   ` [PATCH v4 06/10] ath10k: move free_vdev_map initialization Michal Kazior
2013-07-16  7:38     ` Michal Kazior
2013-07-16  7:38   ` [PATCH v4 07/10] ath10k: make sure all resources are freed upon ath10k_stop() Michal Kazior
2013-07-16  7:38     ` Michal Kazior
2013-07-16  7:38   ` [PATCH v4 08/10] ath10k: defer hw setup to start/stop mac80211 hooks Michal Kazior
2013-07-16  7:38     ` Michal Kazior
2013-07-16  7:38   ` [PATCH v4 09/10] ath10k: store firmware files in memory Michal Kazior
2013-07-16  7:38     ` Michal Kazior
2013-07-16  7:38   ` [PATCH v4 10/10] ath10k: skip fw stats debugfs interface if device is down Michal Kazior
2013-07-16  7:38     ` Michal Kazior
2013-07-19 10:18   ` [PATCH v4 00/10] ath10k: device setup refactor Kalle Valo
2013-07-19 10:18     ` Kalle Valo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.