From a1c823117894ed79943a87b1c718139cc1be1b6a Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Sun, 31 May 2015 13:55:17 +0200 Subject: ahci: Move interrupt enablement code to a separate function This patch refactors ahci_init_interrupts() and moves msi code to a separate function. Need the split since we add msix initialization in a later patch. The initialization for msix will be done after msi but before intx. Signed-off-by: Robert Richter Signed-off-by: Tejun Heo --- drivers/ata/ahci.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'drivers/ata/ahci.c') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index c7a92a743ed0..7ba5332476c6 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1201,17 +1201,17 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host) {} #endif -static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, - struct ahci_host_priv *hpriv) +static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports, + struct ahci_host_priv *hpriv) { int rc, nvec; if (hpriv->flags & AHCI_HFLAG_NO_MSI) - goto intx; + return -ENODEV; nvec = pci_msi_vec_count(pdev); if (nvec < 0) - goto intx; + return nvec; /* * If number of MSIs is less than number of ports then Sharing Last @@ -1224,8 +1224,8 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, rc = pci_enable_msi_exact(pdev, nvec); if (rc == -ENOSPC) goto single_msi; - else if (rc < 0) - goto intx; + if (rc < 0) + return rc; /* fallback to single MSI mode if the controller enforced MRSM mode */ if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) { @@ -1240,12 +1240,25 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, return nvec; single_msi: - if (pci_enable_msi(pdev)) - goto intx; + rc = pci_enable_msi(pdev); + if (rc < 0) + return rc; + return 1; +} -intx: +static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, + struct ahci_host_priv *hpriv) +{ + int nvec; + + nvec = ahci_init_msi(pdev, n_ports, hpriv); + if (nvec >= 0) + return nvec; + + /* lagacy intx interrupts */ pci_intx(pdev, 1); + return 0; } -- cgit v1.2.3 From 21bfd1aa9527811408d6073d45e5ac8283a28b72 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Sun, 31 May 2015 13:55:18 +0200 Subject: ahci: Store irq number in struct ahci_host_priv Currently, ahci supports only msi and intx. To also support msix the handling of the irq number need to be changed. The irq number for msix devices is taken from msi_list instead of pci_dev. Thus, the irq number of a device needs to be stored in struct ahci_host_priv now. This allows the host controller to be activated in a generic way. This change is only intended for ahci drivers. For that reason the irq number is stored in struct ahci_host_priv used only by ahci drivers. Thus, the ABI changes only for ahci_host_activate(), but existing ata drivers (about 50) are unaffected and keep unchanged. All users of ahci_host_activate() have been updated. While touching drivers/ata/libahci.c, doing a small code cleanup in ahci_port_start(). Signed-off-by: Robert Richter Signed-off-by: Tejun Heo --- drivers/ata/acard-ahci.c | 4 +++- drivers/ata/ahci.c | 15 ++++++++++----- drivers/ata/ahci.h | 4 ++-- drivers/ata/libahci.c | 16 +++++++--------- drivers/ata/libahci_platform.c | 4 +++- drivers/ata/sata_highbank.c | 3 ++- 6 files changed, 27 insertions(+), 19 deletions(-) (limited to 'drivers/ata/ahci.c') diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c index 12489ce863c4..ed6a30cd681a 100644 --- a/drivers/ata/acard-ahci.c +++ b/drivers/ata/acard-ahci.c @@ -433,6 +433,8 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; + + hpriv->irq = pdev->irq; hpriv->flags |= (unsigned long)pi.private_data; if (!(hpriv->flags & AHCI_HFLAG_NO_MSI)) @@ -498,7 +500,7 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id acard_ahci_pci_print_info(host); pci_set_master(pdev); - return ahci_host_activate(host, pdev->irq, &acard_ahci_sht); + return ahci_host_activate(host, &acard_ahci_sht); } module_pci_driver(acard_ahci_pci_driver); diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 7ba5332476c6..a3c66c3bb76e 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1237,14 +1237,18 @@ static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports, if (nvec > 1) hpriv->flags |= AHCI_HFLAG_MULTI_MSI; - return nvec; + goto out; single_msi: + nvec = 1; + rc = pci_enable_msi(pdev); if (rc < 0) return rc; +out: + hpriv->irq = pdev->irq; - return 1; + return nvec; } static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, @@ -1258,6 +1262,7 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, /* lagacy intx interrupts */ pci_intx(pdev, 1); + hpriv->irq = pdev->irq; return 0; } @@ -1423,13 +1428,13 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) */ n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); - ahci_init_interrupts(pdev, n_ports, hpriv); - host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); if (!host) return -ENOMEM; host->private_data = hpriv; + ahci_init_interrupts(pdev, n_ports, hpriv); + if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) host->flags |= ATA_HOST_PARALLEL_SCAN; else @@ -1475,7 +1480,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); - return ahci_host_activate(host, pdev->irq, &ahci_sht); + return ahci_host_activate(host, &ahci_sht); } module_pci_driver(ahci_pci_driver); diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index f4429600e2bf..5b8e8a0fab48 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -343,6 +343,7 @@ struct ahci_host_priv { struct phy **phys; unsigned nports; /* Number of ports */ void *plat_data; /* Other platform data */ + unsigned int irq; /* interrupt line */ /* * Optional ahci_start_engine override, if not set this gets set to the * default ahci_start_engine during ahci_save_initial_config, this can @@ -395,8 +396,7 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv, struct ata_port_info *pi); int ahci_reset_em(struct ata_host *host); void ahci_print_info(struct ata_host *host, const char *scc_s); -int ahci_host_activate(struct ata_host *host, int irq, - struct scsi_host_template *sht); +int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht); void ahci_error_handler(struct ata_port *ap); static inline void __iomem *__ahci_port_base(struct ata_host *host, diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 1add5baec584..1c99402a1017 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -2344,7 +2344,7 @@ static int ahci_port_start(struct ata_port *ap) /* * Switch to per-port locking in case each port has its own MSI vector. */ - if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) { + if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) { spin_lock_init(&pp->lock); ap->lock = &pp->lock; } @@ -2472,7 +2472,10 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq, rc = ata_host_start(host); if (rc) return rc; - + /* + * Requests IRQs according to AHCI-1.1 when multiple MSIs were + * allocated. That is one MSI per port, starting from @irq. + */ for (i = 0; i < host->n_ports; i++) { struct ahci_port_priv *pp = host->ports[i]->private_data; @@ -2511,23 +2514,18 @@ out_free_irqs: /** * ahci_host_activate - start AHCI host, request IRQs and register it * @host: target ATA host - * @irq: base IRQ number to request * @sht: scsi_host_template to use when registering the host * - * Similar to ata_host_activate, but requests IRQs according to AHCI-1.1 - * when multiple MSIs were allocated. That is one MSI per port, starting - * from @irq. - * * LOCKING: * Inherited from calling layer (may sleep). * * RETURNS: * 0 on success, -errno otherwise. */ -int ahci_host_activate(struct ata_host *host, int irq, - struct scsi_host_template *sht) +int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht) { struct ahci_host_priv *hpriv = host->private_data; + int irq = hpriv->irq; int rc; if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index d89305d289f6..aaa761b9081c 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -518,6 +518,8 @@ int ahci_platform_init_host(struct platform_device *pdev, return -EINVAL; } + hpriv->irq = irq; + /* prepare host */ pi.private_data = (void *)(unsigned long)hpriv->flags; @@ -588,7 +590,7 @@ int ahci_platform_init_host(struct platform_device *pdev, ahci_init_controller(host); ahci_print_info(host, "platform"); - return ahci_host_activate(host, irq, sht); + return ahci_host_activate(host, sht); } EXPORT_SYMBOL_GPL(ahci_platform_init_host); diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c index 24e311fe2c1c..8638d575b2b9 100644 --- a/drivers/ata/sata_highbank.c +++ b/drivers/ata/sata_highbank.c @@ -499,6 +499,7 @@ static int ahci_highbank_probe(struct platform_device *pdev) return -ENOMEM; } + hpriv->irq = irq; hpriv->flags |= (unsigned long)pi.private_data; hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); @@ -568,7 +569,7 @@ static int ahci_highbank_probe(struct platform_device *pdev) ahci_init_controller(host); ahci_print_info(host, "platform"); - rc = ahci_host_activate(host, irq, &ahci_highbank_platform_sht); + rc = ahci_host_activate(host, &ahci_highbank_platform_sht); if (rc) goto err0; -- cgit v1.2.3 From ee2aad42e4b6eaa9721196f07f7d5d8d049e6530 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 5 Jun 2015 19:49:25 +0200 Subject: ahci: Add generic MSI-X support for single interrupts to SATA PCI driver This patch adds generic MSI-X support for single interrupts to the SATA PCI driver. MSI-X support is needed for host controller that only have MSI-X support implemented, but no MSI or intx. This patch only adds support for single interrupts, multiple per-port MSI-X interrupts are not yet implemented. The new implementation still initializes MSIs first. Only if that fails, the code tries to enable MSI-X. If that fails too, setup is continued with intx interrupts. To not break other chips by this generic code change, there are the following precautions: * Interrupt ranges are not enabled at all. * Only single interrupt mode is enabled for msix cap devices. Thus, only one interrupt will be setup. * During the discussion with Tejun we agreed to change the init sequence from msix-msi-intx to msi-msix-intx. Thus, if a device offers msi and init does not fail, the msix init code will not be executed. This is equivalent to current code. With this, the code only setups single mode msix as a last resort if msi fails. No interrupt range is enabled at all. Only one interrupt will be enabled. tj: comment edits. Changes of the patch series: v5: * updated patch subject that the patch only implements single IRQ * moved Cavium specific code to a separate patch * detect Cavium ThunderX device with PCI_CLASS_STORAGE_SATA_AHCI instead of vendor/dev id * added more comments to the code * enable single msix support for all kind of devices (removing strict check) * rebased onto update libata/for-4.2 with patch 1, 2 applied v4: * removed implementation of ahci_init_intx() * improved patch descriptions * rebased onto libata/for-4.2 v3: * store irq number in struct ahci_host_priv * change initialization order from msix-msi-intx to msi-msix-intx * improve comments in ahci_init_msix() * improve error message in ahci_init_msix() * do not enable MSI-X if MSI is actively disabled for the device v2: * determine irq vector from pci_dev->msi_list Based on a patch from Sunil Goutham . Signed-off-by: Robert Richter Signed-off-by: Tejun Heo --- drivers/ata/ahci.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) (limited to 'drivers/ata/ahci.c') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index a3c66c3bb76e..77a34fc04138 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -1201,6 +1202,68 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host) {} #endif +static struct msi_desc *msix_get_desc(struct pci_dev *dev, u16 entry) +{ + struct msi_desc *desc; + + list_for_each_entry(desc, &dev->msi_list, list) { + if (desc->msi_attrib.entry_nr == entry) + return desc; + } + + return NULL; +} + +/* + * ahci_init_msix() only implements single MSI-X support, not multiple + * MSI-X per-port interrupts. This is needed for host controllers that only + * have MSI-X support implemented, but no MSI or intx. + */ +static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports, + struct ahci_host_priv *hpriv) +{ + struct msi_desc *desc; + int rc, nvec; + struct msix_entry entry = {}; + + /* Do not init MSI-X if MSI is disabled for the device */ + if (hpriv->flags & AHCI_HFLAG_NO_MSI) + return -ENODEV; + + nvec = pci_msix_vec_count(pdev); + if (nvec < 0) + return nvec; + + if (!nvec) { + rc = -ENODEV; + goto fail; + } + + /* + * There can be more than one vector (e.g. for error detection or + * hdd hotplug). Only the first vector (entry.entry = 0) is used. + */ + rc = pci_enable_msix_exact(pdev, &entry, 1); + if (rc < 0) + goto fail; + + desc = msix_get_desc(pdev, 0); /* first entry */ + if (!desc) { + rc = -EINVAL; + goto fail; + } + + hpriv->irq = desc->irq; + + return 1; +fail: + dev_err(&pdev->dev, + "failed to enable MSI-X with error %d, # of vectors: %d\n", + rc, nvec); + + return rc; +} + static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports, struct ahci_host_priv *hpriv) { @@ -1260,6 +1323,15 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, if (nvec >= 0) return nvec; + /* + * Currently, MSI-X support only implements single IRQ mode and + * exists for controllers which can't do other types of IRQ. Only + * set it up if MSI fails. + */ + nvec = ahci_init_msix(pdev, n_ports, hpriv); + if (nvec >= 0) + return nvec; + /* lagacy intx interrupts */ pci_intx(pdev, 1); hpriv->irq = pdev->irq; -- cgit v1.2.3 From b7ae128d728c42583dac9db48dce9a44bc0fb900 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 5 Jun 2015 19:49:26 +0200 Subject: ahci: Add support for Cavium's ThunderX host controller This patch adds support for Cavium's ThunderX host controller. The controller resides on the SoC and is a AHCI compatible SATA controller with one port, compliant with Serial ATA 3.1 and AHCI Revision 1.31. There can exists multiple SATA controllers on the SoC. The controller depends on MSI-X support since the PCI ECAM controller on the SoC does not implement MSI nor lagacy intx interrupt support. Thus, during device initialization, if MSI fails MSI-X will be used to enable the device's interrupts. The controller uses non-standard BAR0 for its register range. The already existing device lookup (vendor and device id) that is already implemented for other host controllers is used to change the PCI BAR. Signed-off-by: Robert Richter Signed-off-by: Tejun Heo --- drivers/ata/ahci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/ata/ahci.c') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 77a34fc04138..bdedaa4f9d7b 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -53,6 +53,7 @@ enum { AHCI_PCI_BAR_STA2X11 = 0, + AHCI_PCI_BAR_CAVIUM = 0, AHCI_PCI_BAR_ENMOTUS = 2, AHCI_PCI_BAR_STANDARD = 5, }; @@ -1374,11 +1375,13 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev_info(&pdev->dev, "PDC42819 can only drive SATA devices with this driver\n"); - /* Both Connext and Enmotus devices use non-standard BARs */ + /* Some devices use non-standard BARs */ if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == 0xCC06) ahci_pci_bar = AHCI_PCI_BAR_STA2X11; else if (pdev->vendor == 0x1c44 && pdev->device == 0x8000) ahci_pci_bar = AHCI_PCI_BAR_ENMOTUS; + else if (pdev->vendor == 0x177d && pdev->device == 0xa01c) + ahci_pci_bar = AHCI_PCI_BAR_CAVIUM; /* * The JMicron chip 361/363 contains one SATA controller and one -- cgit v1.2.3 From 34c56932ce5278b8bbae13936f9df05cd59e3193 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 17 Jun 2015 15:30:02 +0200 Subject: ahci, msix: Fix build error for !PCI_MSI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turned out the irq vector of the msix can be obtained from struct msix_entry. This makes the lookup function for msi_desc obsolete. This fixes a build error if PCI_MSI is unset: drivers/ata/ahci.c: In function ‘msix_get_desc’: drivers/ata/ahci.c:1210:2: error: ‘struct pci_dev’ has no member named ‘msi_list’ Catched by Fengguang's build bot. Reported-by: kbuild test robot Signed-off-by: Robert Richter Signed-off-by: Tejun Heo --- drivers/ata/ahci.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) (limited to 'drivers/ata/ahci.c') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index bdedaa4f9d7b..c478a40e32c6 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1203,18 +1203,6 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host) {} #endif -static struct msi_desc *msix_get_desc(struct pci_dev *dev, u16 entry) -{ - struct msi_desc *desc; - - list_for_each_entry(desc, &dev->msi_list, list) { - if (desc->msi_attrib.entry_nr == entry) - return desc; - } - - return NULL; -} - /* * ahci_init_msix() only implements single MSI-X support, not multiple * MSI-X per-port interrupts. This is needed for host controllers that only @@ -1223,7 +1211,6 @@ static struct msi_desc *msix_get_desc(struct pci_dev *dev, u16 entry) static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports, struct ahci_host_priv *hpriv) { - struct msi_desc *desc; int rc, nvec; struct msix_entry entry = {}; @@ -1248,13 +1235,7 @@ static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports, if (rc < 0) goto fail; - desc = msix_get_desc(pdev, 0); /* first entry */ - if (!desc) { - rc = -EINVAL; - goto fail; - } - - hpriv->irq = desc->irq; + hpriv->irq = entry.vector; return 1; fail: -- cgit v1.2.3