diff options
author | Dan Williams <dan.j.williams@intel.com> | 2015-05-08 15:23:55 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2015-05-10 11:39:16 -0400 |
commit | dbfe8ef5599a5370abc441fcdbb382b656563eb4 (patch) | |
tree | b2cae946e43f9796870f4f7d5f07d023000d7bb1 | |
parent | 11fa7df1e12f19571bdce4580cbc63a8cb3e9e85 (diff) |
ahci: avoton port-disable reset-quirk
Avoton AHCI occasionally sees drive probe timeouts at driver load time.
When this happens SCR_STATUS indicates device detected, but no D2H FIS
reception. Reset the internal link state machines by bouncing
port-enable in the PCS register when this occurs.
Cc: <stable@vger.kernel.org>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
-rw-r--r-- | drivers/ata/ahci.c | 103 |
1 files changed, 95 insertions, 8 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index c7a92a743ed0..65ee94454bbd 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c | |||
@@ -66,6 +66,7 @@ enum board_ids { | |||
66 | board_ahci_yes_fbs, | 66 | board_ahci_yes_fbs, |
67 | 67 | ||
68 | /* board IDs for specific chipsets in alphabetical order */ | 68 | /* board IDs for specific chipsets in alphabetical order */ |
69 | board_ahci_avn, | ||
69 | board_ahci_mcp65, | 70 | board_ahci_mcp65, |
70 | board_ahci_mcp77, | 71 | board_ahci_mcp77, |
71 | board_ahci_mcp89, | 72 | board_ahci_mcp89, |
@@ -84,6 +85,8 @@ enum board_ids { | |||
84 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); | 85 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); |
85 | static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, | 86 | static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, |
86 | unsigned long deadline); | 87 | unsigned long deadline); |
88 | static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, | ||
89 | unsigned long deadline); | ||
87 | static void ahci_mcp89_apple_enable(struct pci_dev *pdev); | 90 | static void ahci_mcp89_apple_enable(struct pci_dev *pdev); |
88 | static bool is_mcp89_apple(struct pci_dev *pdev); | 91 | static bool is_mcp89_apple(struct pci_dev *pdev); |
89 | static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, | 92 | static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, |
@@ -107,6 +110,11 @@ static struct ata_port_operations ahci_p5wdh_ops = { | |||
107 | .hardreset = ahci_p5wdh_hardreset, | 110 | .hardreset = ahci_p5wdh_hardreset, |
108 | }; | 111 | }; |
109 | 112 | ||
113 | static struct ata_port_operations ahci_avn_ops = { | ||
114 | .inherits = &ahci_ops, | ||
115 | .hardreset = ahci_avn_hardreset, | ||
116 | }; | ||
117 | |||
110 | static const struct ata_port_info ahci_port_info[] = { | 118 | static const struct ata_port_info ahci_port_info[] = { |
111 | /* by features */ | 119 | /* by features */ |
112 | [board_ahci] = { | 120 | [board_ahci] = { |
@@ -151,6 +159,12 @@ static const struct ata_port_info ahci_port_info[] = { | |||
151 | .port_ops = &ahci_ops, | 159 | .port_ops = &ahci_ops, |
152 | }, | 160 | }, |
153 | /* by chipsets */ | 161 | /* by chipsets */ |
162 | [board_ahci_avn] = { | ||
163 | .flags = AHCI_FLAG_COMMON, | ||
164 | .pio_mask = ATA_PIO4, | ||
165 | .udma_mask = ATA_UDMA6, | ||
166 | .port_ops = &ahci_avn_ops, | ||
167 | }, | ||
154 | [board_ahci_mcp65] = { | 168 | [board_ahci_mcp65] = { |
155 | AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | | 169 | AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | |
156 | AHCI_HFLAG_YES_NCQ), | 170 | AHCI_HFLAG_YES_NCQ), |
@@ -290,14 +304,14 @@ static const struct pci_device_id ahci_pci_tbl[] = { | |||
290 | { PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */ | 304 | { PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */ |
291 | { PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */ | 305 | { PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */ |
292 | { PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */ | 306 | { PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */ |
293 | { PCI_VDEVICE(INTEL, 0x1f32), board_ahci }, /* Avoton AHCI */ | 307 | { PCI_VDEVICE(INTEL, 0x1f32), board_ahci_avn }, /* Avoton AHCI */ |
294 | { PCI_VDEVICE(INTEL, 0x1f33), board_ahci }, /* Avoton AHCI */ | 308 | { PCI_VDEVICE(INTEL, 0x1f33), board_ahci_avn }, /* Avoton AHCI */ |
295 | { PCI_VDEVICE(INTEL, 0x1f34), board_ahci }, /* Avoton RAID */ | 309 | { PCI_VDEVICE(INTEL, 0x1f34), board_ahci_avn }, /* Avoton RAID */ |
296 | { PCI_VDEVICE(INTEL, 0x1f35), board_ahci }, /* Avoton RAID */ | 310 | { PCI_VDEVICE(INTEL, 0x1f35), board_ahci_avn }, /* Avoton RAID */ |
297 | { PCI_VDEVICE(INTEL, 0x1f36), board_ahci }, /* Avoton RAID */ | 311 | { PCI_VDEVICE(INTEL, 0x1f36), board_ahci_avn }, /* Avoton RAID */ |
298 | { PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */ | 312 | { PCI_VDEVICE(INTEL, 0x1f37), board_ahci_avn }, /* Avoton RAID */ |
299 | { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */ | 313 | { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci_avn }, /* Avoton RAID */ |
300 | { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */ | 314 | { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci_avn }, /* Avoton RAID */ |
301 | { PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */ | 315 | { PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */ |
302 | { PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */ | 316 | { PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */ |
303 | { PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */ | 317 | { PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */ |
@@ -670,6 +684,79 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, | |||
670 | return rc; | 684 | return rc; |
671 | } | 685 | } |
672 | 686 | ||
687 | /* | ||
688 | * ahci_avn_hardreset - attempt more aggressive recovery of Avoton ports. | ||
689 | * | ||
690 | * It has been observed with some SSDs that the timing of events in the | ||
691 | * link synchronization phase can leave the port in a state that can not | ||
692 | * be recovered by a SATA-hard-reset alone. The failing signature is | ||
693 | * SStatus.DET stuck at 1 ("Device presence detected but Phy | ||
694 | * communication not established"). It was found that unloading and | ||
695 | * reloading the driver when this problem occurs allows the drive | ||
696 | * connection to be recovered (DET advanced to 0x3). The critical | ||
697 | * component of reloading the driver is that the port state machines are | ||
698 | * reset by bouncing "port enable" in the AHCI PCS configuration | ||
699 | * register. So, reproduce that effect by bouncing a port whenever we | ||
700 | * see DET==1 after a reset. | ||
701 | */ | ||
702 | static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, | ||
703 | unsigned long deadline) | ||
704 | { | ||
705 | const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); | ||
706 | struct ata_port *ap = link->ap; | ||
707 | struct ahci_port_priv *pp = ap->private_data; | ||
708 | struct ahci_host_priv *hpriv = ap->host->private_data; | ||
709 | u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; | ||
710 | unsigned long tmo = deadline - jiffies; | ||
711 | struct ata_taskfile tf; | ||
712 | bool online; | ||
713 | int rc, i; | ||
714 | |||
715 | DPRINTK("ENTER\n"); | ||
716 | |||
717 | ahci_stop_engine(ap); | ||
718 | |||
719 | for (i = 0; i < 2; i++) { | ||
720 | u16 val; | ||
721 | u32 sstatus; | ||
722 | int port = ap->port_no; | ||
723 | struct ata_host *host = ap->host; | ||
724 | struct pci_dev *pdev = to_pci_dev(host->dev); | ||
725 | |||
726 | /* clear D2H reception area to properly wait for D2H FIS */ | ||
727 | ata_tf_init(link->device, &tf); | ||
728 | tf.command = ATA_BUSY; | ||
729 | ata_tf_to_fis(&tf, 0, 0, d2h_fis); | ||
730 | |||
731 | rc = sata_link_hardreset(link, timing, deadline, &online, | ||
732 | ahci_check_ready); | ||
733 | |||
734 | if (sata_scr_read(link, SCR_STATUS, &sstatus) != 0 || | ||
735 | (sstatus & 0xf) != 1) | ||
736 | break; | ||
737 | |||
738 | ata_link_printk(link, KERN_INFO, "avn bounce port%d\n", | ||
739 | port); | ||
740 | |||
741 | pci_read_config_word(pdev, 0x92, &val); | ||
742 | val &= ~(1 << port); | ||
743 | pci_write_config_word(pdev, 0x92, val); | ||
744 | ata_msleep(ap, 1000); | ||
745 | val |= 1 << port; | ||
746 | pci_write_config_word(pdev, 0x92, val); | ||
747 | deadline += tmo; | ||
748 | } | ||
749 | |||
750 | hpriv->start_engine(ap); | ||
751 | |||
752 | if (online) | ||
753 | *class = ahci_dev_classify(ap); | ||
754 | |||
755 | DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); | ||
756 | return rc; | ||
757 | } | ||
758 | |||
759 | |||
673 | #ifdef CONFIG_PM | 760 | #ifdef CONFIG_PM |
674 | static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) | 761 | static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) |
675 | { | 762 | { |