diff options
author | Tirumalesh Chalamarla <tchalamarla@caviumnetworks.com> | 2016-02-16 12:08:49 -0800 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2016-02-29 16:16:26 -0500 |
commit | d243bed32f5042582896237f88fa1798aee55ff9 (patch) | |
tree | b72b52732cc00899a85b7d9c25ef291370f851c2 /drivers/ata/ahci.c | |
parent | 4ee34ea3a12396f35b26d90a094c75db95080baa (diff) | |
download | linux-stable-d243bed32f5042582896237f88fa1798aee55ff9.tar.gz linux-stable-d243bed32f5042582896237f88fa1798aee55ff9.tar.bz2 linux-stable-d243bed32f5042582896237f88fa1798aee55ff9.zip |
ahci: Workaround for ThunderX Errata#22536
Due to Errata in ThunderX, HOST_IRQ_STAT should be
cleared before leaving the interrupt handler.
The patch attempts to satisfy the need.
Changes from V2:
- removed newfile
- code is now under CONFIG_ARM64
Changes from V1:
- Rebased on top of libata/for-4.6
- Moved ThunderX intr handler to new file
tj: Minor adjustments to comments.
Signed-off-by: Tirumalesh Chalamarla <tchalamarla@caviumnetworks.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/ata/ahci.c')
-rw-r--r-- | drivers/ata/ahci.c | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index b6263b3996ab..146dc0b8ec61 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1331,6 +1331,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. @@ -1566,6 +1604,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); |