diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 8e28f92..de6efcb 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1385,6 +1385,14 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); + /* We normally clear Bus Master on pci device shutdown. However, + * doing so for Intel Lynx Point-LP SATA Controller [AHCI mode] + * hangs the system. Therefore keep it. + * See bug report: https://bugzilla.kernel.org/show_bug.cgi?id=63861 + */ + if (pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->device == 0x9c03) + pdev->keep_busmaster_on_shutdown = 1; + if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) return ahci_host_activate(host, pdev->irq, n_msis); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 38f3c01..ff15b0c 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -392,10 +392,15 @@ static void pci_device_shutdown(struct device *dev) pci_msix_shutdown(pci_dev); /* - * Turn off Bus Master bit on the device to tell it to not - * continue to do DMA. Don't touch devices in D3cold or unknown states. + * If the hardware is okay with it, turn off Bus Master bit + * on the device to tell it not to continue doing DMA. + * Don't touch devices in D3cold or unknown states. + * On certain hardware clearing Bus Master bit on shutdown + * may hang the entire system. In these cases the driver of + * these devices should set keep_busmaster_on_shutdown to 1. */ - if (pci_dev->current_state <= PCI_D3hot) + if (!pci_dev->keep_busmaster_on_shutdown + && pci_dev->current_state <= PCI_D3hot) pci_clear_master(pci_dev); } diff --git a/include/linux/pci.h b/include/linux/pci.h index da172f9..63db735 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -322,6 +322,7 @@ struct pci_dev { /* keep track of device state */ unsigned int is_added:1; unsigned int is_busmaster:1; /* device is busmaster */ + unsigned int keep_busmaster_on_shutdown:1; /* do not clear busmaster on shutdown */ unsigned int no_msi:1; /* device may not use msi */ unsigned int block_cfg_access:1; /* config space access is blocked */ unsigned int broken_parity_status:1; /* Device generates false positive parity */ -- 1.8.4.2