diff options
Diffstat (limited to 'drivers/ata/ahci.c')
-rw-r--r-- | drivers/ata/ahci.c | 177 |
1 files changed, 144 insertions, 33 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 594fcabd22cd..a83bbcc58b4c 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -85,6 +85,7 @@ enum board_ids { }; static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); +static void ahci_remove_one(struct pci_dev *dev); static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, @@ -94,9 +95,13 @@ static bool is_mcp89_apple(struct pci_dev *pdev); static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); #ifdef CONFIG_PM -static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); -static int ahci_pci_device_resume(struct pci_dev *pdev); +static int ahci_pci_device_runtime_suspend(struct device *dev); +static int ahci_pci_device_runtime_resume(struct device *dev); +#ifdef CONFIG_PM_SLEEP +static int ahci_pci_device_suspend(struct device *dev); +static int ahci_pci_device_resume(struct device *dev); #endif +#endif /* CONFIG_PM */ static struct scsi_host_template ahci_sht = { AHCI_SHT("ahci"), @@ -264,6 +269,26 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */ { PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */ { PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */ + { PCI_VDEVICE(INTEL, 0x19b0), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b1), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b2), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b3), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b4), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b5), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b6), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19b7), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19bE), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19bF), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c0), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c1), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c2), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c3), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c4), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c5), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c6), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19c7), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19cE), board_ahci }, /* DNV AHCI */ + { PCI_VDEVICE(INTEL, 0x19cF), board_ahci }, /* DNV AHCI */ { PCI_VDEVICE(INTEL, 0x1c02), board_ahci }, /* CPT AHCI */ { PCI_VDEVICE(INTEL, 0x1c03), board_ahci }, /* CPT AHCI */ { PCI_VDEVICE(INTEL, 0x1c04), board_ahci }, /* CPT RAID */ @@ -347,15 +372,17 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0xa107), board_ahci }, /* Sunrise Point-H RAID */ { PCI_VDEVICE(INTEL, 0xa10f), board_ahci }, /* Sunrise Point-H RAID */ { PCI_VDEVICE(INTEL, 0x2822), board_ahci }, /* Lewisburg RAID*/ + { PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Lewisburg AHCI*/ { PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* Lewisburg RAID*/ + { PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Lewisburg RAID*/ { PCI_VDEVICE(INTEL, 0xa182), board_ahci }, /* Lewisburg AHCI*/ - { PCI_VDEVICE(INTEL, 0xa184), board_ahci }, /* Lewisburg RAID*/ { PCI_VDEVICE(INTEL, 0xa186), board_ahci }, /* Lewisburg RAID*/ - { PCI_VDEVICE(INTEL, 0xa18e), board_ahci }, /* Lewisburg RAID*/ + { PCI_VDEVICE(INTEL, 0xa1d2), board_ahci }, /* Lewisburg RAID*/ + { PCI_VDEVICE(INTEL, 0xa1d6), board_ahci }, /* Lewisburg RAID*/ { PCI_VDEVICE(INTEL, 0xa202), board_ahci }, /* Lewisburg AHCI*/ - { PCI_VDEVICE(INTEL, 0xa204), board_ahci }, /* Lewisburg RAID*/ { PCI_VDEVICE(INTEL, 0xa206), board_ahci }, /* Lewisburg RAID*/ - { PCI_VDEVICE(INTEL, 0xa20e), board_ahci }, /* Lewisburg RAID*/ + { PCI_VDEVICE(INTEL, 0xa252), board_ahci }, /* Lewisburg RAID*/ + { PCI_VDEVICE(INTEL, 0xa256), board_ahci }, /* Lewisburg RAID*/ /* JMicron 360/1/3/5/6, match class to avoid IDE function */ { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, @@ -537,16 +564,20 @@ static const struct pci_device_id ahci_pci_tbl[] = { { } /* terminate list */ }; +static const struct dev_pm_ops ahci_pci_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ahci_pci_device_suspend, ahci_pci_device_resume) + SET_RUNTIME_PM_OPS(ahci_pci_device_runtime_suspend, + ahci_pci_device_runtime_resume, NULL) +}; static struct pci_driver ahci_pci_driver = { .name = DRV_NAME, .id_table = ahci_pci_tbl, .probe = ahci_init_one, - .remove = ata_pci_remove_one, -#ifdef CONFIG_PM - .suspend = ahci_pci_device_suspend, - .resume = ahci_pci_device_resume, -#endif + .remove = ahci_remove_one, + .driver = { + .pm = &ahci_pci_pm_ops, + }, }; #if defined(CONFIG_PATA_MARVELL) || defined(CONFIG_PATA_MARVELL_MODULE) @@ -775,42 +806,66 @@ static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, #ifdef CONFIG_PM -static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) +static void ahci_pci_disable_interrupts(struct ata_host *host) { - struct ata_host *host = pci_get_drvdata(pdev); struct ahci_host_priv *hpriv = host->private_data; void __iomem *mmio = hpriv->mmio; u32 ctl; - if (mesg.event & PM_EVENT_SUSPEND && - hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { - dev_err(&pdev->dev, - "BIOS update required for suspend/resume\n"); - return -EIO; - } + /* AHCI spec rev1.1 section 8.3.3: + * Software must disable interrupts prior to requesting a + * transition of the HBA to D3 state. + */ + ctl = readl(mmio + HOST_CTL); + ctl &= ~HOST_IRQ_EN; + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ +} - if (mesg.event & PM_EVENT_SLEEP) { - /* AHCI spec rev1.1 section 8.3.3: - * Software must disable interrupts prior to requesting a - * transition of the HBA to D3 state. - */ - ctl = readl(mmio + HOST_CTL); - ctl &= ~HOST_IRQ_EN; - writel(ctl, mmio + HOST_CTL); - readl(mmio + HOST_CTL); /* flush */ - } +static int ahci_pci_device_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct ata_host *host = pci_get_drvdata(pdev); - return ata_pci_device_suspend(pdev, mesg); + ahci_pci_disable_interrupts(host); + return 0; } -static int ahci_pci_device_resume(struct pci_dev *pdev) +static int ahci_pci_device_runtime_resume(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct ata_host *host = pci_get_drvdata(pdev); int rc; - rc = ata_pci_device_do_resume(pdev); + rc = ahci_pci_reset_controller(host); if (rc) return rc; + ahci_pci_init_controller(host); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ahci_pci_device_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct ata_host *host = pci_get_drvdata(pdev); + struct ahci_host_priv *hpriv = host->private_data; + + if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { + dev_err(&pdev->dev, + "BIOS update required for suspend/resume\n"); + return -EIO; + } + + ahci_pci_disable_interrupts(host); + return ata_host_suspend(host, PMSG_SUSPEND); +} + +static int ahci_pci_device_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct ata_host *host = pci_get_drvdata(pdev); + int rc; /* Apple BIOS helpfully mangles the registers on resume */ if (is_mcp89_apple(pdev)) @@ -830,6 +885,8 @@ static int ahci_pci_device_resume(struct pci_dev *pdev) } #endif +#endif /* CONFIG_PM */ + static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac) { int rc; @@ -1305,6 +1362,44 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host) {} #endif +#ifdef CONFIG_ARM64 +/* + * Due to ERRATA#22536, ThunderX needs to handle HOST_IRQ_STAT differently. + * Workaround is to make sure all pending IRQs are served before leaving + * handler. + */ +static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance) +{ + struct ata_host *host = dev_instance; + struct ahci_host_priv *hpriv; + unsigned int rc = 0; + void __iomem *mmio; + u32 irq_stat, irq_masked; + unsigned int handled = 1; + + VPRINTK("ENTER\n"); + hpriv = host->private_data; + mmio = hpriv->mmio; + irq_stat = readl(mmio + HOST_IRQ_STAT); + if (!irq_stat) + return IRQ_NONE; + + do { + irq_masked = irq_stat & hpriv->port_map; + spin_lock(&host->lock); + rc = ahci_handle_port_intr(host, irq_masked); + if (!rc) + handled = 0; + writel(irq_stat, mmio + HOST_IRQ_STAT); + irq_stat = readl(mmio + HOST_IRQ_STAT); + spin_unlock(&host->lock); + } while (irq_stat); + VPRINTK("EXIT\n"); + + return IRQ_RETVAL(handled); +} +#endif + /* * ahci_init_msix() - optionally enable per-port MSI-X otherwise defer * to single msi. @@ -1540,6 +1635,11 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (ahci_broken_devslp(pdev)) hpriv->flags |= AHCI_HFLAG_NO_DEVSLP; +#ifdef CONFIG_ARM64 + if (pdev->vendor == 0x177d && pdev->device == 0xa01c) + hpriv->irq_handler = ahci_thunderx_irq_handler; +#endif + /* save initial config */ ahci_pci_save_initial_config(pdev, hpriv); @@ -1649,7 +1749,18 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); - return ahci_host_activate(host, &ahci_sht); + rc = ahci_host_activate(host, &ahci_sht); + if (rc) + return rc; + + pm_runtime_put_noidle(&pdev->dev); + return 0; +} + +static void ahci_remove_one(struct pci_dev *pdev) +{ + pm_runtime_get_noresume(&pdev->dev); + ata_pci_remove_one(pdev); } module_pci_driver(ahci_pci_driver); |