iwlwifi: the read / write register ops move to transport
[deliverable/linux.git] / drivers / net / wireless / iwlwifi / iwl-trans-pcie.c
index 67d6e324e26f7b2e75910921aceadc034a5a10ed..80f531844f3eb5b6da24ff14ff56047159a89675 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -60,6 +60,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  *****************************************************************************/
+#include <linux/pci.h>
+#include <linux/pci-aspm.h>
 #include <linux/interrupt.h>
 #include <linux/debugfs.h>
 #include <linux/bitops.h>
@@ -647,8 +649,10 @@ static int iwl_nic_init(struct iwl_trans *trans)
 
        iwl_nic_config(priv(trans));
 
+#ifndef CONFIG_IWLWIFI_IDI
        /* Allocate the RX queue, or reset if it is already allocated */
        iwl_rx_init(trans);
+#endif
 
        /* Allocate or reset and init all Tx and Command queues */
        if (iwl_tx_init(trans))
@@ -835,7 +839,7 @@ static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask)
        iwl_write_prph(bus(trans), SCD_TXFACT, mask);
 }
 
-static void iwl_trans_pcie_tx_start(struct iwl_trans *trans)
+static void iwl_tx_start(struct iwl_trans *trans)
 {
        const struct queue_to_fifo_ac *queue_to_fifo;
        struct iwl_trans_pcie *trans_pcie =
@@ -948,6 +952,12 @@ static void iwl_trans_pcie_tx_start(struct iwl_trans *trans)
                          APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
 }
 
+static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans)
+{
+       iwl_reset_ict(trans);
+       iwl_tx_start(trans);
+}
+
 /**
  * iwlagn_txq_ctx_stop - Stop all Tx DMA channels
  */
@@ -1010,8 +1020,9 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
         */
        if (test_bit(STATUS_DEVICE_ENABLED, &trans->shrd->status)) {
                iwl_trans_tx_stop(trans);
+#ifndef CONFIG_IWLWIFI_IDI
                iwl_trans_rx_stop(trans);
-
+#endif
                /* Power-down device's busmaster DMA clocks */
                iwl_write_prph(bus(trans), APMG_CLK_DIS_REG,
                               APMG_CLK_VAL_DMA_CLK_RQT);
@@ -1033,7 +1044,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
        spin_unlock_irqrestore(&trans->shrd->lock, flags);
 
        /* wait to make sure we flush pending tasklet*/
-       synchronize_irq(bus(trans)->irq);
+       synchronize_irq(trans->irq);
        tasklet_kill(&trans_pcie->irq_tasklet);
 
        /* stop and reset the on-board processor */
@@ -1237,10 +1248,10 @@ static int iwl_trans_pcie_request_irq(struct iwl_trans *trans)
 
        iwl_alloc_isr_ict(trans);
 
-       err = request_irq(bus(trans)->irq, iwl_isr_ict, IRQF_SHARED,
+       err = request_irq(trans->irq, iwl_isr_ict, IRQF_SHARED,
                DRV_NAME, trans);
        if (err) {
-               IWL_ERR(trans, "Error allocating IRQ %d\n", bus(trans)->irq);
+               IWL_ERR(trans, "Error allocating IRQ %d\n", trans->irq);
                iwl_free_isr_ict(trans);
                return err;
        }
@@ -1288,13 +1299,40 @@ static int iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid,
        return 0;
 }
 
+static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
+{
+       iowrite8(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs);
+}
+
+static void iwl_trans_pcie_write32(struct iwl_trans *trans, u32 ofs, u32 val)
+{
+       iowrite32(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs);
+}
+
+static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs)
+{
+       u32 val = ioread32(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs);
+       return val;
+}
+
 static void iwl_trans_pcie_free(struct iwl_trans *trans)
 {
+       struct iwl_trans_pcie *trans_pcie =
+               IWL_TRANS_GET_PCIE_TRANS(trans);
+
        iwl_calib_free_results(trans);
        iwl_trans_pcie_tx_free(trans);
+#ifndef CONFIG_IWLWIFI_IDI
        iwl_trans_pcie_rx_free(trans);
-       free_irq(bus(trans)->irq, trans);
+#endif
+       free_irq(trans->irq, trans);
        iwl_free_isr_ict(trans);
+
+       pci_disable_msi(trans_pcie->pci_dev);
+       pci_iounmap(trans_pcie->pci_dev, trans_pcie->hw_base);
+       pci_release_regions(trans_pcie->pci_dev);
+       pci_disable_device(trans_pcie->pci_dev);
+
        trans->shrd->trans = NULL;
        kfree(trans);
 }
@@ -1363,25 +1401,6 @@ static void iwl_trans_pcie_wake_any_queue(struct iwl_trans *trans,
        }
 }
 
-const struct iwl_trans_ops trans_ops_pcie;
-
-static struct iwl_trans *iwl_trans_pcie_alloc(struct iwl_shared *shrd)
-{
-       struct iwl_trans *iwl_trans = kzalloc(sizeof(struct iwl_trans) +
-                                             sizeof(struct iwl_trans_pcie),
-                                             GFP_KERNEL);
-       if (iwl_trans) {
-               struct iwl_trans_pcie *trans_pcie =
-                       IWL_TRANS_GET_PCIE_TRANS(iwl_trans);
-               iwl_trans->ops = &trans_ops_pcie;
-               iwl_trans->shrd = shrd;
-               trans_pcie->trans = iwl_trans;
-               spin_lock_init(&iwl_trans->hcmd_lock);
-       }
-
-       return iwl_trans;
-}
-
 static void iwl_trans_pcie_stop_queue(struct iwl_trans *trans, int txq_id,
                                      const char *msg)
 {
@@ -1901,13 +1920,12 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
 #endif /*CONFIG_IWLWIFI_DEBUGFS */
 
 const struct iwl_trans_ops trans_ops_pcie = {
-       .alloc = iwl_trans_pcie_alloc,
        .request_irq = iwl_trans_pcie_request_irq,
+       .fw_alive = iwl_trans_pcie_fw_alive,
        .start_device = iwl_trans_pcie_start_device,
        .prepare_card_hw = iwl_trans_pcie_prepare_card_hw,
        .stop_device = iwl_trans_pcie_stop_device,
 
-       .tx_start = iwl_trans_pcie_tx_start,
        .wake_any_queue = iwl_trans_pcie_wake_any_queue,
 
        .send_cmd = iwl_trans_pcie_send_cmd,
@@ -1933,4 +1951,115 @@ const struct iwl_trans_ops trans_ops_pcie = {
        .suspend = iwl_trans_pcie_suspend,
        .resume = iwl_trans_pcie_resume,
 #endif
+       .write8 = iwl_trans_pcie_write8,
+       .write32 = iwl_trans_pcie_write32,
+       .read32 = iwl_trans_pcie_read32,
 };
+
+/* PCI registers */
+#define PCI_CFG_RETRY_TIMEOUT  0x041
+
+struct iwl_trans *iwl_trans_pcie_alloc(struct iwl_shared *shrd,
+                                      struct pci_dev *pdev,
+                                      const struct pci_device_id *ent)
+{
+       struct iwl_trans_pcie *trans_pcie;
+       struct iwl_trans *trans;
+       u16 pci_cmd;
+       int err;
+
+       trans = kzalloc(sizeof(struct iwl_trans) +
+                            sizeof(struct iwl_trans_pcie), GFP_KERNEL);
+
+       if (WARN_ON(!trans))
+               return NULL;
+
+       trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       trans->ops = &trans_ops_pcie;
+       trans->shrd = shrd;
+       trans_pcie->trans = trans;
+       spin_lock_init(&trans->hcmd_lock);
+
+       /* W/A - seems to solve weird behavior. We need to remove this if we
+        * don't want to stay in L1 all the time. This wastes a lot of power */
+       pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
+                               PCIE_LINK_STATE_CLKPM);
+
+       if (pci_enable_device(pdev)) {
+               err = -ENODEV;
+               goto out_no_pci;
+       }
+
+       pci_set_master(pdev);
+
+       err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
+       if (!err)
+               err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36));
+       if (err) {
+               err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+               if (!err)
+                       err = pci_set_consistent_dma_mask(pdev,
+                                                       DMA_BIT_MASK(32));
+               /* both attempts failed: */
+               if (err) {
+                       dev_printk(KERN_ERR, &pdev->dev,
+                                  "No suitable DMA available.\n");
+                       goto out_pci_disable_device;
+               }
+       }
+
+       err = pci_request_regions(pdev, DRV_NAME);
+       if (err) {
+               dev_printk(KERN_ERR, &pdev->dev, "pci_request_regions failed");
+               goto out_pci_disable_device;
+       }
+
+       trans_pcie->hw_base = pci_iomap(pdev, 0, 0);
+       if (!trans_pcie->hw_base) {
+               dev_printk(KERN_ERR, &pdev->dev, "pci_iomap failed");
+               err = -ENODEV;
+               goto out_pci_release_regions;
+       }
+
+       dev_printk(KERN_INFO, &pdev->dev,
+               "pci_resource_len = 0x%08llx\n",
+               (unsigned long long) pci_resource_len(pdev, 0));
+       dev_printk(KERN_INFO, &pdev->dev,
+               "pci_resource_base = %p\n", trans_pcie->hw_base);
+
+       dev_printk(KERN_INFO, &pdev->dev,
+               "HW Revision ID = 0x%X\n", pdev->revision);
+
+       /* We disable the RETRY_TIMEOUT register (0x41) to keep
+        * PCI Tx retries from interfering with C3 CPU state */
+       pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
+
+       err = pci_enable_msi(pdev);
+       if (err)
+               dev_printk(KERN_ERR, &pdev->dev,
+                       "pci_enable_msi failed(0X%x)", err);
+
+       trans->dev = &pdev->dev;
+       trans->irq = pdev->irq;
+       trans_pcie->pci_dev = pdev;
+
+       /* TODO: Move this away, not needed if not MSI */
+       /* enable rfkill interrupt: hw bug w/a */
+       pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
+       if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
+               pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
+               pci_write_config_word(pdev, PCI_COMMAND, pci_cmd);
+       }
+
+       return trans;
+
+out_pci_release_regions:
+       pci_release_regions(pdev);
+out_pci_disable_device:
+       pci_disable_device(pdev);
+out_no_pci:
+       kfree(trans);
+       return NULL;
+}
+
This page took 0.028176 seconds and 5 git commands to generate.