diff options
Diffstat (limited to 'drivers/ata/ahci.c')
| -rw-r--r-- | drivers/ata/ahci.c | 207 | 
1 files changed, 157 insertions, 50 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 9d715ae5ff6..4cd52a4541a 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -35,7 +35,6 @@  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/pci.h> -#include <linux/init.h>  #include <linux/blkdev.h>  #include <linux/delay.h>  #include <linux/interrupt.h> @@ -61,6 +60,7 @@ enum board_ids {  	/* board IDs by feature in alphabetical order */  	board_ahci,  	board_ahci_ign_iferr, +	board_ahci_noncq,  	board_ahci_nosntf,  	board_ahci_yes_fbs, @@ -83,6 +83,8 @@ enum board_ids {  static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);  static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,  				 unsigned long deadline); +static void ahci_mcp89_apple_enable(struct pci_dev *pdev); +static bool is_mcp89_apple(struct pci_dev *pdev);  static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,  				unsigned long deadline);  #ifdef CONFIG_PM @@ -119,6 +121,13 @@ static const struct ata_port_info ahci_port_info[] = {  		.udma_mask	= ATA_UDMA6,  		.port_ops	= &ahci_ops,  	}, +	[board_ahci_noncq] = { +		AHCI_HFLAGS	(AHCI_HFLAG_NO_NCQ), +		.flags		= AHCI_FLAG_COMMON, +		.pio_mask	= ATA_PIO4, +		.udma_mask	= ATA_UDMA6, +		.port_ops	= &ahci_ops, +	},  	[board_ahci_nosntf] = {  		AHCI_HFLAGS	(AHCI_HFLAG_NO_SNTF),  		.flags		= AHCI_FLAG_COMMON, @@ -292,6 +301,10 @@ static const struct pci_device_id ahci_pci_tbl[] = {  	{ PCI_VDEVICE(INTEL, 0x8d66), board_ahci }, /* Wellsburg RAID */  	{ PCI_VDEVICE(INTEL, 0x8d6e), board_ahci }, /* Wellsburg RAID */  	{ PCI_VDEVICE(INTEL, 0x23a3), board_ahci }, /* Coleto Creek AHCI */ +	{ PCI_VDEVICE(INTEL, 0x9c83), board_ahci }, /* Wildcat Point-LP AHCI */ +	{ PCI_VDEVICE(INTEL, 0x9c85), board_ahci }, /* Wildcat Point-LP RAID */ +	{ PCI_VDEVICE(INTEL, 0x9c87), board_ahci }, /* Wildcat Point-LP RAID */ +	{ PCI_VDEVICE(INTEL, 0x9c8f), board_ahci }, /* Wildcat Point-LP RAID */  	/* 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, @@ -423,17 +436,27 @@ static const struct pci_device_id ahci_pci_tbl[] = {  	  .driver_data = board_ahci_yes_fbs },			/* 88se9128 */  	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9125),  	  .driver_data = board_ahci_yes_fbs },			/* 88se9125 */ +	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_MARVELL_EXT, 0x9178, +			 PCI_VENDOR_ID_MARVELL_EXT, 0x9170), +	  .driver_data = board_ahci_yes_fbs },			/* 88se9170 */  	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x917a),  	  .driver_data = board_ahci_yes_fbs },			/* 88se9172 */  	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9172),  	  .driver_data = board_ahci_yes_fbs },			/* 88se9172 */  	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9192),  	  .driver_data = board_ahci_yes_fbs },			/* 88se9172 on some Gigabyte */ +	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0), +	  .driver_data = board_ahci_yes_fbs },  	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a3),  	  .driver_data = board_ahci_yes_fbs }, +	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9230), +	  .driver_data = board_ahci_yes_fbs }, +	{ PCI_DEVICE(PCI_VENDOR_ID_TTI, 0x0642), +	  .driver_data = board_ahci_yes_fbs },  	/* Promise */  	{ PCI_VDEVICE(PROMISE, 0x3f20), board_ahci },	/* PDC42819 */ +	{ PCI_VDEVICE(PROMISE, 0x3781), board_ahci },   /* FastTrak TX8660 ahci-mode */  	/* Asmedia */  	{ PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci },	/* ASM1060 */ @@ -441,6 +464,12 @@ static const struct pci_device_id ahci_pci_tbl[] = {  	{ PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci },	/* ASM1061 */  	{ PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci },	/* ASM1062 */ +	/* +	 * Samsung SSDs found on some macbooks.  NCQ times out. +	 * https://bugzilla.kernel.org/show_bug.cgi?id=60731 +	 */ +	{ PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_noncq }, +  	/* Enmotus */  	{ PCI_DEVICE(0x1c44, 0x8000), board_ahci }, @@ -553,6 +582,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,  				 unsigned long deadline)  {  	struct ata_port *ap = link->ap; +	struct ahci_host_priv *hpriv = ap->host->private_data;  	bool online;  	int rc; @@ -563,7 +593,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,  	rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),  				 deadline, &online, NULL); -	ahci_start_engine(ap); +	hpriv->start_engine(ap);  	DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); @@ -578,6 +608,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,  {  	struct ata_port *ap = link->ap;  	struct ahci_port_priv *pp = ap->private_data; +	struct ahci_host_priv *hpriv = ap->host->private_data;  	u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;  	struct ata_taskfile tf;  	bool online; @@ -593,7 +624,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,  	rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),  				 deadline, &online, NULL); -	ahci_start_engine(ap); +	hpriv->start_engine(ap);  	/* The pseudo configuration device on SIMG4726 attached to  	 * ASUS P5W-DH Deluxe doesn't send signature FIS after @@ -655,6 +686,10 @@ static int ahci_pci_device_resume(struct pci_dev *pdev)  	if (rc)  		return rc; +	/* Apple BIOS helpfully mangles the registers on resume */ +	if (is_mcp89_apple(pdev)) +		ahci_mcp89_apple_enable(pdev); +  	if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {  		rc = ahci_pci_reset_controller(host);  		if (rc) @@ -771,6 +806,48 @@ static void ahci_p5wdh_workaround(struct ata_host *host)  	}  } +/* + * Macbook7,1 firmware forcibly disables MCP89 AHCI and changes PCI ID when + * booting in BIOS compatibility mode.  We restore the registers but not ID. + */ +static void ahci_mcp89_apple_enable(struct pci_dev *pdev) +{ +	u32 val; + +	printk(KERN_INFO "ahci: enabling MCP89 AHCI mode\n"); + +	pci_read_config_dword(pdev, 0xf8, &val); +	val |= 1 << 0x1b; +	/* the following changes the device ID, but appears not to affect function */ +	/* val = (val & ~0xf0000000) | 0x80000000; */ +	pci_write_config_dword(pdev, 0xf8, val); + +	pci_read_config_dword(pdev, 0x54c, &val); +	val |= 1 << 0xc; +	pci_write_config_dword(pdev, 0x54c, val); + +	pci_read_config_dword(pdev, 0x4a4, &val); +	val &= 0xff; +	val |= 0x01060100; +	pci_write_config_dword(pdev, 0x4a4, val); + +	pci_read_config_dword(pdev, 0x54c, &val); +	val &= ~(1 << 0xc); +	pci_write_config_dword(pdev, 0x54c, val); + +	pci_read_config_dword(pdev, 0xf8, &val); +	val &= ~(1 << 0x1b); +	pci_write_config_dword(pdev, 0xf8, val); +} + +static bool is_mcp89_apple(struct pci_dev *pdev) +{ +	return pdev->vendor == PCI_VENDOR_ID_NVIDIA && +		pdev->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA && +		pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE && +		pdev->subsystem_device == 0xcb89; +} +  /* only some SB600 ahci controllers can do 64bit DMA */  static bool ahci_sb600_enable_64bit(struct pci_dev *pdev)  { @@ -1043,6 +1120,17 @@ static bool ahci_broken_online(struct pci_dev *pdev)  	return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff);  } +static bool ahci_broken_devslp(struct pci_dev *pdev) +{ +	/* device with broken DEVSLP but still showing SDS capability */ +	static const struct pci_device_id ids[] = { +		{ PCI_VDEVICE(INTEL, 0x0f23)}, /* Valleyview SoC */ +		{} +	}; + +	return pci_match_id(ids, pdev); +} +  #ifdef CONFIG_ATA_ACPI  static void ahci_gtf_filter_workaround(struct ata_host *host)  { @@ -1091,26 +1179,47 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)  {}  #endif -int ahci_init_interrupts(struct pci_dev *pdev, struct ahci_host_priv *hpriv) +static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, +				struct ahci_host_priv *hpriv)  { -	int rc; -	unsigned int maxvec; +	int rc, nvec; -	if (!(hpriv->flags & AHCI_HFLAG_NO_MSI)) { -		rc = pci_enable_msi_block_auto(pdev, &maxvec); -		if (rc > 0) { -			if ((rc == maxvec) || (rc == 1)) -				return rc; -			/* -			 * Assume that advantage of multipe MSIs is negated, -			 * so fallback to single MSI mode to save resources -			 */ -			pci_disable_msi(pdev); -			if (!pci_enable_msi(pdev)) -				return 1; -		} +	if (hpriv->flags & AHCI_HFLAG_NO_MSI) +		goto intx; + +	nvec = pci_msi_vec_count(pdev); +	if (nvec < 0) +		goto intx; + +	/* +	 * If number of MSIs is less than number of ports then Sharing Last +	 * Message mode could be enforced. In this case assume that advantage +	 * of multipe MSIs is negated and use single MSI mode instead. +	 */ +	if (nvec < n_ports) +		goto single_msi; + +	rc = pci_enable_msi_exact(pdev, nvec); +	if (rc == -ENOSPC) +		goto single_msi; +	else if (rc < 0) +		goto intx; + +	/* fallback to single MSI mode if the controller enforced MRSM mode */ +	if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) { +		pci_disable_msi(pdev); +		printk(KERN_INFO "ahci: MRSM is on, fallback to single MSI\n"); +		goto single_msi;  	} +	return nvec; + +single_msi: +	if (pci_enable_msi(pdev)) +		goto intx; +	return 1; + +intx:  	pci_intx(pdev, 1);  	return 0;  } @@ -1146,18 +1255,18 @@ int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis)  		return rc;  	for (i = 0; i < host->n_ports; i++) { -		const char* desc;  		struct ahci_port_priv *pp = host->ports[i]->private_data; -		/* pp is NULL for dummy ports */ -		if (pp) -			desc = pp->irq_desc; -		else -			desc = dev_driver_string(host->dev); +		/* Do not receive interrupts sent by dummy ports */ +		if (!pp) { +			disable_irq(irq + i); +			continue; +		} -		rc = devm_request_threaded_irq(host->dev, -			irq + i, ahci_hw_interrupt, ahci_thread_fn, IRQF_SHARED, -			desc, host->ports[i]); +		rc = devm_request_threaded_irq(host->dev, irq + i, +					       ahci_hw_interrupt, +					       ahci_thread_fn, IRQF_SHARED, +					       pp->irq_desc, host->ports[i]);  		if (rc)  			goto out_free_irqs;  	} @@ -1203,15 +1312,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)  	if (pdev->vendor == PCI_VENDOR_ID_MARVELL && !marvell_enable)  		return -ENODEV; -	/* -	 * For some reason, MCP89 on MacBook 7,1 doesn't work with -	 * ahci, use ata_generic instead. -	 */ -	if (pdev->vendor == PCI_VENDOR_ID_NVIDIA && -	    pdev->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA && -	    pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE && -	    pdev->subsystem_device == 0xcb89) -		return -ENODEV; +	/* Apple BIOS on MCP89 prevents us using AHCI */ +	if (is_mcp89_apple(pdev)) +		ahci_mcp89_apple_enable(pdev);  	/* Promise's PDC42819 is a SAS/SATA controller that has an AHCI mode.  	 * At the moment, we can only use the AHCI mode. Let the users know @@ -1232,15 +1335,6 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)  	if (rc)  		return rc; -	/* AHCI controllers often implement SFF compatible interface. -	 * Grab all PCI BARs just in case. -	 */ -	rc = pcim_iomap_regions_request_all(pdev, 1 << ahci_pci_bar, DRV_NAME); -	if (rc == -EBUSY) -		pcim_pin_device(pdev); -	if (rc) -		return rc; -  	if (pdev->vendor == PCI_VENDOR_ID_INTEL &&  	    (pdev->device == 0x2652 || pdev->device == 0x2653)) {  		u8 map; @@ -1257,6 +1351,15 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)  		}  	} +	/* AHCI controllers often implement SFF compatible interface. +	 * Grab all PCI BARs just in case. +	 */ +	rc = pcim_iomap_regions_request_all(pdev, 1 << ahci_pci_bar, DRV_NAME); +	if (rc == -EBUSY) +		pcim_pin_device(pdev); +	if (rc) +		return rc; +  	hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);  	if (!hpriv)  		return -ENOMEM; @@ -1277,9 +1380,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)  	hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; -	n_msis = ahci_init_interrupts(pdev, hpriv); -	if (n_msis > 1) -		hpriv->flags |= AHCI_HFLAG_MULTI_MSI; +	/* must set flag prior to save config in order to take effect */ +	if (ahci_broken_devslp(pdev)) +		hpriv->flags |= AHCI_HFLAG_NO_DEVSLP;  	/* save initial config */  	ahci_pci_save_initial_config(pdev, hpriv); @@ -1335,6 +1438,10 @@ 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)); +	n_msis = ahci_init_interrupts(pdev, n_ports, hpriv); +	if (n_msis > 1) +		hpriv->flags |= AHCI_HFLAG_MULTI_MSI; +  	host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);  	if (!host)  		return -ENOMEM; @@ -1343,7 +1450,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)  	if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)  		host->flags |= ATA_HOST_PARALLEL_SCAN;  	else -		printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n"); +		dev_info(&pdev->dev, "SSS flag set, parallel bus scan disabled\n");  	if (pi.flags & ATA_FLAG_EM)  		ahci_reset_em(host);  | 
