different interface types, and for supporting asynchronous
firmware loading.

Based on a patch from Pradeep Nemavat <pnemavat@marvell.com>
and Yogesh Powar <yogeshp@marvell.com>

Signed-off-by: Brian Cavagnolo <brian@cozybit.com>
---
 drivers/net/wireless/mwl8k.c |  345 ++++++++++++++++++++++++++----------------
 1 files changed, 214 insertions(+), 131 deletions(-)

diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index cfda87a..7bd8615 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -3942,73 +3942,10 @@ static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
 };
 MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table);
 
-static int __devinit mwl8k_probe(struct pci_dev *pdev,
-				 const struct pci_device_id *id)
+static int mwl8k_init_firmware(struct ieee80211_hw *hw)
 {
-	static int printed_version = 0;
-	struct ieee80211_hw *hw;
-	struct mwl8k_priv *priv;
+	struct mwl8k_priv *priv = hw->priv;
 	int rc;
-	int i;
-
-	if (!printed_version) {
-		printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION);
-		printed_version = 1;
-	}
-
-
-	rc = pci_enable_device(pdev);
-	if (rc) {
-		printk(KERN_ERR "%s: Cannot enable new PCI device\n",
-		       MWL8K_NAME);
-		return rc;
-	}
-
-	rc = pci_request_regions(pdev, MWL8K_NAME);
-	if (rc) {
-		printk(KERN_ERR "%s: Cannot obtain PCI resources\n",
-		       MWL8K_NAME);
-		goto err_disable_device;
-	}
-
-	pci_set_master(pdev);
-
-
-	hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops);
-	if (hw == NULL) {
-		printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME);
-		rc = -ENOMEM;
-		goto err_free_reg;
-	}
-
-	SET_IEEE80211_DEV(hw, &pdev->dev);
-	pci_set_drvdata(pdev, hw);
-
-	priv = hw->priv;
-	priv->hw = hw;
-	priv->pdev = pdev;
-	priv->device_info = &mwl8k_info_tbl[id->driver_data];
-
-
-	priv->sram = pci_iomap(pdev, 0, 0x10000);
-	if (priv->sram == NULL) {
-		wiphy_err(hw->wiphy, "Cannot map device SRAM\n");
-		goto err_iounmap;
-	}
-
-	/*
-	 * If BAR0 is a 32 bit BAR, the register BAR will be BAR1.
-	 * If BAR0 is a 64 bit BAR, the register BAR will be BAR2.
-	 */
-	priv->regs = pci_iomap(pdev, 1, 0x10000);
-	if (priv->regs == NULL) {
-		priv->regs = pci_iomap(pdev, 2, 0x10000);
-		if (priv->regs == NULL) {
-			wiphy_err(hw->wiphy, "Cannot map device registers\n");
-			goto err_iounmap;
-		}
-	}
-
 
 	/* Reset firmware and hardware */
 	mwl8k_hw_reset(priv);
@@ -4017,19 +3954,26 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
 	rc = mwl8k_request_firmware(priv);
 	if (rc) {
 		wiphy_err(hw->wiphy, "Firmware files not found\n");
-		goto err_stop_firmware;
+		return rc;
 	}
 
 	/* Load firmware into hardware */
 	rc = mwl8k_load_firmware(hw);
-	if (rc) {
+	if (rc)
 		wiphy_err(hw->wiphy, "Cannot start firmware\n");
-		goto err_stop_firmware;
-	}
 
 	/* Reclaim memory once firmware is successfully loaded */
 	mwl8k_release_firmware(priv);
 
+	return rc;
+}
+
+/* initialize hw after successfully loading a firmware image */
+static int mwl8k_probe_hw(struct ieee80211_hw *hw)
+{
+	struct mwl8k_priv *priv = hw->priv;
+	int rc = 0;
+	int i;
 
 	if (priv->ap_fw) {
 		priv->rxd_ops = priv->device_info->ap_rxd_ops;
@@ -4046,58 +3990,11 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
 	priv->wmm_enabled = false;
 	priv->pending_tx_pkts = 0;
 
-
-	/*
-	 * Extra headroom is the size of the required DMA header
-	 * minus the size of the smallest 802.11 frame (CTS frame).
-	 */
-	hw->extra_tx_headroom =
-		sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts);
-
-	hw->channel_change_time = 10;
-
-	hw->queues = MWL8K_TX_QUEUES;
-
-	/* Set rssi values to dBm */
-	hw->flags |= IEEE80211_HW_SIGNAL_DBM;
-	hw->vif_data_size = sizeof(struct mwl8k_vif);
-	hw->sta_data_size = sizeof(struct mwl8k_sta);
-
-	priv->macids_used = 0;
-	INIT_LIST_HEAD(&priv->vif_list);
-
-	/* Set default radio state and preamble */
-	priv->radio_on = 0;
-	priv->radio_short_preamble = 0;
-
-	/* Finalize join worker */
-	INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
-
-	/* TX reclaim and RX tasklets.  */
-	tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
-	tasklet_disable(&priv->poll_tx_task);
-	tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw);
-	tasklet_disable(&priv->poll_rx_task);
-
-	/* Power management cookie */
-	priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
-	if (priv->cookie == NULL)
-		goto err_stop_firmware;
-
 	rc = mwl8k_rxq_init(hw, 0);
 	if (rc)
-		goto err_free_cookie;
+		goto err_stop_firmware;
 	rxq_refill(hw, 0, INT_MAX);
 
-	mutex_init(&priv->fw_mutex);
-	priv->fw_mutex_owner = NULL;
-	priv->fw_mutex_depth = 0;
-	priv->hostcmd_wait = NULL;
-
-	spin_lock_init(&priv->tx_lock);
-
-	priv->tx_wait = NULL;
-
 	for (i = 0; i < MWL8K_TX_QUEUES; i++) {
 		rc = mwl8k_txq_init(hw, i);
 		if (rc)
@@ -4137,13 +4034,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
 		goto err_free_irq;
 	}
 
-	hw->wiphy->interface_modes = 0;
-	if (priv->ap_macids_supported)
-		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
-	if (priv->sta_macids_supported)
-		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
-
-
 	/* Turn radio off */
 	rc = mwl8k_cmd_radio_disable(hw);
 	if (rc) {
@@ -4162,12 +4052,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
 	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
 	free_irq(priv->pdev->irq, hw);
 
-	rc = ieee80211_register_hw(hw);
-	if (rc) {
-		wiphy_err(hw->wiphy, "Cannot register device\n");
-		goto err_free_queues;
-	}
-
 	wiphy_info(hw->wiphy, "%s v%d, %pm, %s firmware %u.%u.%u.%u\n",
 		   priv->device_info->part_name,
 		   priv->hw_rev, hw->wiphy->perm_addr,
@@ -4186,14 +4070,213 @@ err_free_queues:
 		mwl8k_txq_deinit(hw, i);
 	mwl8k_rxq_deinit(hw, 0);
 
+err_stop_firmware:
+	mwl8k_hw_reset(priv);
+
+	return rc;
+}
+
+/*
+ * invoke mwl8k_reload_firmware to change the firmware image after the device
+ * has already been registered
+ */
+static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
+{
+	int i, rc = 0;
+	struct mwl8k_priv *priv = hw->priv;
+
+	mwl8k_stop(hw);
+	mwl8k_rxq_deinit(hw, 0);
+
+	for (i = 0; i < MWL8K_TX_QUEUES; i++)
+		mwl8k_txq_deinit(hw, i);
+
+	rc = mwl8k_init_firmware(hw, fw_image);
+	if (rc)
+		goto fail;
+
+	rc = mwl8k_probe_hw(hw);
+	if (rc)
+		goto fail;
+
+	rc = mwl8k_start(hw);
+	if (rc)
+		goto fail;
+
+	rc = mwl8k_config(hw, ~0);
+	if (rc)
+		goto fail;
+
+	for (i = 0; i < MWL8K_TX_QUEUES; i++) {
+		rc = mwl8k_conf_tx(hw, i, &priv->wmm_params[i]);
+		if (rc)
+			goto fail;
+	}
+
+	return rc;
+
+fail:
+	printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n");
+	return rc;
+}
+
+static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
+{
+	struct ieee80211_hw *hw = priv->hw;
+	int i, rc;
+
+	/*
+	 * Extra headroom is the size of the required DMA header
+	 * minus the size of the smallest 802.11 frame (CTS frame).
+	 */
+	hw->extra_tx_headroom =
+		sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts);
+
+	hw->channel_change_time = 10;
+
+	hw->queues = MWL8K_TX_QUEUES;
+
+	/* Set rssi values to dBm */
+	hw->flags |= IEEE80211_HW_SIGNAL_DBM;
+	hw->vif_data_size = sizeof(struct mwl8k_vif);
+	hw->sta_data_size = sizeof(struct mwl8k_sta);
+
+	priv->macids_used = 0;
+	INIT_LIST_HEAD(&priv->vif_list);
+
+	/* Set default radio state and preamble */
+	priv->radio_on = 0;
+	priv->radio_short_preamble = 0;
+
+	/* Finalize join worker */
+	INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
+
+	/* TX reclaim and RX tasklets.  */
+	tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
+	tasklet_disable(&priv->poll_tx_task);
+	tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw);
+	tasklet_disable(&priv->poll_rx_task);
+
+	/* Power management cookie */
+	priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
+	if (priv->cookie == NULL)
+		return -ENOMEM;
+
+	mutex_init(&priv->fw_mutex);
+	priv->fw_mutex_owner = NULL;
+	priv->fw_mutex_depth = 0;
+	priv->hostcmd_wait = NULL;
+
+	spin_lock_init(&priv->tx_lock);
+
+	priv->tx_wait = NULL;
+
+	rc = mwl8k_probe_hw(hw);
+	if (rc)
+		goto err_free_cookie;
+
+	hw->wiphy->interface_modes = 0;
+	if (priv->ap_macids_supported || priv->device_info->fw_image_ap)
+		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
+	if (priv->sta_macids_supported || priv->device_info->fw_image_sta)
+		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
+
+	rc = ieee80211_register_hw(hw);
+	if (rc) {
+		wiphy_err(hw->wiphy, "Cannot register device\n");
+		goto err_unprobe_hw;
+	}
+
+	return 0;
+
+err_unprobe_hw:
+	for (i = 0; i < MWL8K_TX_QUEUES; i++)
+		mwl8k_txq_deinit(hw, i);
+	mwl8k_rxq_deinit(hw, 0);
+
 err_free_cookie:
 	if (priv->cookie != NULL)
 		pci_free_consistent(priv->pdev, 4,
 				priv->cookie, priv->cookie_dma);
 
+	return rc;
+}
+static int __devinit mwl8k_probe(struct pci_dev *pdev,
+				 const struct pci_device_id *id)
+{
+	static int printed_version;
+	struct ieee80211_hw *hw;
+	struct mwl8k_priv *priv;
+	int rc;
+
+	if (!printed_version) {
+		printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION);
+		printed_version = 1;
+	}
+
+
+	rc = pci_enable_device(pdev);
+	if (rc) {
+		printk(KERN_ERR "%s: Cannot enable new PCI device\n",
+		       MWL8K_NAME);
+		return rc;
+	}
+
+	rc = pci_request_regions(pdev, MWL8K_NAME);
+	if (rc) {
+		printk(KERN_ERR "%s: Cannot obtain PCI resources\n",
+		       MWL8K_NAME);
+		goto err_disable_device;
+	}
+
+	pci_set_master(pdev);
+
+
+	hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops);
+	if (hw == NULL) {
+		printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME);
+		rc = -ENOMEM;
+		goto err_free_reg;
+	}
+
+	SET_IEEE80211_DEV(hw, &pdev->dev);
+	pci_set_drvdata(pdev, hw);
+
+	priv = hw->priv;
+	priv->hw = hw;
+	priv->pdev = pdev;
+	priv->device_info = &mwl8k_info_tbl[id->driver_data];
+
+
+	priv->sram = pci_iomap(pdev, 0, 0x10000);
+	if (priv->sram == NULL) {
+		wiphy_err(hw->wiphy, "Cannot map device SRAM\n");
+		goto err_iounmap;
+	}
+
+	/*
+	 * If BAR0 is a 32 bit BAR, the register BAR will be BAR1.
+	 * If BAR0 is a 64 bit BAR, the register BAR will be BAR2.
+	 */
+	priv->regs = pci_iomap(pdev, 1, 0x10000);
+	if (priv->regs == NULL) {
+		priv->regs = pci_iomap(pdev, 2, 0x10000);
+		if (priv->regs == NULL) {
+			wiphy_err(hw->wiphy, "Cannot map device registers\n");
+			goto err_iounmap;
+		}
+	}
+
+	rc = mwl8k_init_firmware(hw);
+	if (rc)
+		goto err_stop_firmware;
+
+	rc = mwl8k_firmware_load_success(priv);
+	if (!rc)
+		return rc;
+
 err_stop_firmware:
 	mwl8k_hw_reset(priv);
-	mwl8k_release_firmware(priv);
 
 err_iounmap:
 	if (priv->regs != NULL)
