diff options
Diffstat (limited to 'drivers/ata/ahci.c')
| -rw-r--r-- | drivers/ata/ahci.c | 84 | 
1 files changed, 79 insertions, 5 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 15a23031833..fe3eba5d6b3 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -219,6 +219,8 @@ enum {  	AHCI_HFLAG_SECT255		= (1 << 8), /* max 255 sectors */  	AHCI_HFLAG_YES_NCQ		= (1 << 9), /* force NCQ cap on */  	AHCI_HFLAG_NO_SUSPEND		= (1 << 10), /* don't suspend */ +	AHCI_HFLAG_SRST_TOUT_IS_OFFLINE	= (1 << 11), /* treat SRST timeout as +							link offline */  	/* ap->flags bits */ @@ -513,11 +515,16 @@ static const struct pci_device_id ahci_pci_tbl[] = {  	{ PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */  	{ PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */  	{ PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */ +	{ PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */  	{ PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */ +	{ PCI_VDEVICE(INTEL, 0x3b22), board_ahci }, /* PCH AHCI */ +	{ PCI_VDEVICE(INTEL, 0x3b23), board_ahci }, /* PCH AHCI */  	{ PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */  	{ PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */ +	{ PCI_VDEVICE(INTEL, 0x3b29), board_ahci }, /* PCH AHCI */  	{ PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */  	{ PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */ +	{ PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */  	/* 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, @@ -1658,6 +1665,7 @@ static int ahci_do_softreset(struct ata_link *link, unsigned int *class,  			     int (*check_ready)(struct ata_link *link))  {  	struct ata_port *ap = link->ap; +	struct ahci_host_priv *hpriv = ap->host->private_data;  	const char *reason = NULL;  	unsigned long now, msecs;  	struct ata_taskfile tf; @@ -1696,12 +1704,21 @@ static int ahci_do_softreset(struct ata_link *link, unsigned int *class,  	/* wait for link to become ready */  	rc = ata_wait_after_reset(link, deadline, check_ready); -	/* link occupied, -ENODEV too is an error */ -	if (rc) { +	if (rc == -EBUSY && hpriv->flags & AHCI_HFLAG_SRST_TOUT_IS_OFFLINE) { +		/* +		 * Workaround for cases where link online status can't +		 * be trusted.  Treat device readiness timeout as link +		 * offline. +		 */ +		ata_link_printk(link, KERN_INFO, +				"device not ready, treating as offline\n"); +		*class = ATA_DEV_NONE; +	} else if (rc) { +		/* link occupied, -ENODEV too is an error */  		reason = "device not ready";  		goto fail; -	} -	*class = ahci_dev_classify(ap); +	} else +		*class = ahci_dev_classify(ap);  	DPRINTK("EXIT, class=%u\n", *class);  	return 0; @@ -1768,7 +1785,8 @@ static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,  		irq_sts = readl(port_mmio + PORT_IRQ_STAT);  		if (irq_sts & PORT_IRQ_BAD_PMP) {  			ata_link_printk(link, KERN_WARNING, -					"failed due to HW bug, retry pmp=0\n"); +					"applying SB600 PMP SRST workaround " +					"and retrying\n");  			rc = ahci_do_softreset(link, class, 0, deadline,  					       ahci_check_ready);  		} @@ -2721,6 +2739,56 @@ static bool ahci_broken_suspend(struct pci_dev *pdev)  	return !ver || strcmp(ver, dmi->driver_data) < 0;  } +static bool ahci_broken_online(struct pci_dev *pdev) +{ +#define ENCODE_BUSDEVFN(bus, slot, func)			\ +	(void *)(unsigned long)(((bus) << 8) | PCI_DEVFN((slot), (func))) +	static const struct dmi_system_id sysids[] = { +		/* +		 * There are several gigabyte boards which use +		 * SIMG5723s configured as hardware RAID.  Certain +		 * 5723 firmware revisions shipped there keep the link +		 * online but fail to answer properly to SRST or +		 * IDENTIFY when no device is attached downstream +		 * causing libata to retry quite a few times leading +		 * to excessive detection delay. +		 * +		 * As these firmwares respond to the second reset try +		 * with invalid device signature, considering unknown +		 * sig as offline works around the problem acceptably. +		 */ +		{ +			.ident = "EP45-DQ6", +			.matches = { +				DMI_MATCH(DMI_BOARD_VENDOR, +					  "Gigabyte Technology Co., Ltd."), +				DMI_MATCH(DMI_BOARD_NAME, "EP45-DQ6"), +			}, +			.driver_data = ENCODE_BUSDEVFN(0x0a, 0x00, 0), +		}, +		{ +			.ident = "EP45-DS5", +			.matches = { +				DMI_MATCH(DMI_BOARD_VENDOR, +					  "Gigabyte Technology Co., Ltd."), +				DMI_MATCH(DMI_BOARD_NAME, "EP45-DS5"), +			}, +			.driver_data = ENCODE_BUSDEVFN(0x03, 0x00, 0), +		}, +		{ }	/* terminate list */ +	}; +#undef ENCODE_BUSDEVFN +	const struct dmi_system_id *dmi = dmi_first_match(sysids); +	unsigned int val; + +	if (!dmi) +		return false; + +	val = (unsigned long)dmi->driver_data; + +	return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff); +} +  static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)  {  	static int printed_version; @@ -2836,6 +2904,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)  			   "BIOS update required for suspend/resume\n");  	} +	if (ahci_broken_online(pdev)) { +		hpriv->flags |= AHCI_HFLAG_SRST_TOUT_IS_OFFLINE; +		dev_info(&pdev->dev, +			 "online status unreliable, applying workaround\n"); +	} +  	/* CAP.NP sometimes indicate the index of the last enabled  	 * port, at other times, that of the last possible port, so  	 * determining the maximum port number requires looking at  |