aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata/ahci.c
diff options
context:
space:
mode:
authorRobert Richter <rrichter@cavium.com>2015-06-05 13:49:25 -0400
committerTejun Heo <tj@kernel.org>2015-06-16 16:12:13 -0400
commitee2aad42e4b6eaa9721196f07f7d5d8d049e6530 (patch)
treeda9231d2378e4b99f557f34523c049f20e9c299b /drivers/ata/ahci.c
parent0f5f264b38122b39cfa0beb65eef6b5ccac94917 (diff)
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 <sgoutham@cavium.com>. Signed-off-by: Robert Richter <rrichter@cavium.com> Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/ata/ahci.c')
-rw-r--r--drivers/ata/ahci.c72
1 files changed, 72 insertions, 0 deletions
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 @@
42#include <linux/device.h> 42#include <linux/device.h>
43#include <linux/dmi.h> 43#include <linux/dmi.h>
44#include <linux/gfp.h> 44#include <linux/gfp.h>
45#include <linux/msi.h>
45#include <scsi/scsi_host.h> 46#include <scsi/scsi_host.h>
46#include <scsi/scsi_cmnd.h> 47#include <scsi/scsi_cmnd.h>
47#include <linux/libata.h> 48#include <linux/libata.h>
@@ -1201,6 +1202,68 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
1201{} 1202{}
1202#endif 1203#endif
1203 1204
1205static struct msi_desc *msix_get_desc(struct pci_dev *dev, u16 entry)
1206{
1207 struct msi_desc *desc;
1208
1209 list_for_each_entry(desc, &dev->msi_list, list) {
1210 if (desc->msi_attrib.entry_nr == entry)
1211 return desc;
1212 }
1213
1214 return NULL;
1215}
1216
1217/*
1218 * ahci_init_msix() only implements single MSI-X support, not multiple
1219 * MSI-X per-port interrupts. This is needed for host controllers that only
1220 * have MSI-X support implemented, but no MSI or intx.
1221 */
1222static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
1223 struct ahci_host_priv *hpriv)
1224{
1225 struct msi_desc *desc;
1226 int rc, nvec;
1227 struct msix_entry entry = {};
1228
1229 /* Do not init MSI-X if MSI is disabled for the device */
1230 if (hpriv->flags & AHCI_HFLAG_NO_MSI)
1231 return -ENODEV;
1232
1233 nvec = pci_msix_vec_count(pdev);
1234 if (nvec < 0)
1235 return nvec;
1236
1237 if (!nvec) {
1238 rc = -ENODEV;
1239 goto fail;
1240 }
1241
1242 /*
1243 * There can be more than one vector (e.g. for error detection or
1244 * hdd hotplug). Only the first vector (entry.entry = 0) is used.
1245 */
1246 rc = pci_enable_msix_exact(pdev, &entry, 1);
1247 if (rc < 0)
1248 goto fail;
1249
1250 desc = msix_get_desc(pdev, 0); /* first entry */
1251 if (!desc) {
1252 rc = -EINVAL;
1253 goto fail;
1254 }
1255
1256 hpriv->irq = desc->irq;
1257
1258 return 1;
1259fail:
1260 dev_err(&pdev->dev,
1261 "failed to enable MSI-X with error %d, # of vectors: %d\n",
1262 rc, nvec);
1263
1264 return rc;
1265}
1266
1204static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports, 1267static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports,
1205 struct ahci_host_priv *hpriv) 1268 struct ahci_host_priv *hpriv)
1206{ 1269{
@@ -1260,6 +1323,15 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
1260 if (nvec >= 0) 1323 if (nvec >= 0)
1261 return nvec; 1324 return nvec;
1262 1325
1326 /*
1327 * Currently, MSI-X support only implements single IRQ mode and
1328 * exists for controllers which can't do other types of IRQ. Only
1329 * set it up if MSI fails.
1330 */
1331 nvec = ahci_init_msix(pdev, n_ports, hpriv);
1332 if (nvec >= 0)
1333 return nvec;
1334
1263 /* lagacy intx interrupts */ 1335 /* lagacy intx interrupts */
1264 pci_intx(pdev, 1); 1336 pci_intx(pdev, 1);
1265 hpriv->irq = pdev->irq; 1337 hpriv->irq = pdev->irq;