* 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>
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 */
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;
}
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);
#endif
- free_irq(bus(trans)->irq, trans);
+ 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);
}
}
}
-const struct iwl_trans_ops trans_ops_pcie;
-
-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 *iwl_trans = kzalloc(sizeof(struct iwl_trans) +
- sizeof(struct iwl_trans_pcie),
- GFP_KERNEL);
-
- if (WARN_ON(!iwl_trans))
- return NULL;
-
- 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)
{
.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;
+}
+