Message ID | 1433254774-11118-1-git-send-email-rafael.tinoco@canonical.com |
---|---|
State | New |
Headers | show |
On 02.06.2015 16:19, Rafael David Tinoco wrote: > BugLink: https://bugs.launchpad.net/bugs/1458617 > > 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. > > OriginalAuthor: Dan Williams <dan.j.williams@intel.com> > Signed-off-by: Dan Williams <dan.j.williams@intel.com> > (backported from commit 682de5f4135f3414ec87bb76ef400de1148393c8 upstream) Appears to be dbfe8ef5599a5370abc441fcdbb382b656563eb4 upstream (v4.1-rc4) and marked for stable. Would Utopic and Vivid require a backport as well? The patch seems to be doing what it claims and has limited effect on specific hw only. Positive testing claimed in the bug report. -Stefan > Signed-off-by: Tejun Heo <tj@kernel.org> > Signed-off-by: Rafael David Tinoco <rafael.tinoco@canonical.com> > --- > drivers/ata/ahci.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++----- > 1 file changed, 95 insertions(+), 8 deletions(-) > > diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c > index ef8c437..f6d80c0 100644 > --- a/drivers/ata/ahci.c > +++ b/drivers/ata/ahci.c > @@ -67,6 +67,7 @@ enum board_ids { > board_ahci_yes_fbs, > > /* board IDs for specific chipsets in alphabetical order */ > + board_ahci_avn, > board_ahci_mcp65, > board_ahci_mcp77, > board_ahci_mcp89, > @@ -85,6 +86,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 int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, > + unsigned long deadline); > static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, > unsigned long deadline); > #ifdef CONFIG_PM > @@ -106,6 +109,11 @@ static struct ata_port_operations ahci_p5wdh_ops = { > .hardreset = ahci_p5wdh_hardreset, > }; > > +static struct ata_port_operations ahci_avn_ops = { > + .inherits = &ahci_ops, > + .hardreset = ahci_avn_hardreset, > +}; > + > static const struct ata_port_info ahci_port_info[] = { > /* by features */ > [board_ahci] = { > @@ -150,6 +158,12 @@ static const struct ata_port_info ahci_port_info[] = { > .port_ops = &ahci_ops, > }, > /* by chipsets */ > + [board_ahci_avn] = { > + .flags = AHCI_FLAG_COMMON, > + .pio_mask = ATA_PIO4, > + .udma_mask = ATA_UDMA6, > + .port_ops = &ahci_avn_ops, > + }, > [board_ahci_mcp65] = { > AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | > AHCI_HFLAG_YES_NCQ), > @@ -289,14 +303,14 @@ static const struct pci_device_id ahci_pci_tbl[] = { > { PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */ > { PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */ > { PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */ > - { PCI_VDEVICE(INTEL, 0x1f32), board_ahci }, /* Avoton AHCI */ > - { PCI_VDEVICE(INTEL, 0x1f33), board_ahci }, /* Avoton AHCI */ > - { PCI_VDEVICE(INTEL, 0x1f34), board_ahci }, /* Avoton RAID */ > - { PCI_VDEVICE(INTEL, 0x1f35), board_ahci }, /* Avoton RAID */ > - { PCI_VDEVICE(INTEL, 0x1f36), board_ahci }, /* Avoton RAID */ > - { PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */ > - { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */ > - { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */ > + { PCI_VDEVICE(INTEL, 0x1f32), board_ahci_avn }, /* Avoton AHCI */ > + { PCI_VDEVICE(INTEL, 0x1f33), board_ahci_avn }, /* Avoton AHCI */ > + { PCI_VDEVICE(INTEL, 0x1f34), board_ahci_avn }, /* Avoton RAID */ > + { PCI_VDEVICE(INTEL, 0x1f35), board_ahci_avn }, /* Avoton RAID */ > + { PCI_VDEVICE(INTEL, 0x1f36), board_ahci_avn }, /* Avoton RAID */ > + { PCI_VDEVICE(INTEL, 0x1f37), board_ahci_avn }, /* Avoton RAID */ > + { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci_avn }, /* Avoton RAID */ > + { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci_avn }, /* Avoton RAID */ > { PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */ > { PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */ > { PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */ > @@ -674,6 +688,79 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, > return rc; > } > > +/* > + * ahci_avn_hardreset - attempt more aggressive recovery of Avoton ports. > + * > + * It has been observed with some SSDs that the timing of events in the > + * link synchronization phase can leave the port in a state that can not > + * be recovered by a SATA-hard-reset alone. The failing signature is > + * SStatus.DET stuck at 1 ("Device presence detected but Phy > + * communication not established"). It was found that unloading and > + * reloading the driver when this problem occurs allows the drive > + * connection to be recovered (DET advanced to 0x3). The critical > + * component of reloading the driver is that the port state machines are > + * reset by bouncing "port enable" in the AHCI PCS configuration > + * register. So, reproduce that effect by bouncing a port whenever we > + * see DET==1 after a reset. > + */ > +static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, > + unsigned long deadline) > +{ > + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); > + 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; > + unsigned long tmo = deadline - jiffies; > + struct ata_taskfile tf; > + bool online; > + int rc, i; > + > + DPRINTK("ENTER\n"); > + > + ahci_stop_engine(ap); > + > + for (i = 0; i < 2; i++) { > + u16 val; > + u32 sstatus; > + int port = ap->port_no; > + struct ata_host *host = ap->host; > + struct pci_dev *pdev = to_pci_dev(host->dev); > + > + /* clear D2H reception area to properly wait for D2H FIS */ > + ata_tf_init(link->device, &tf); > + tf.command = ATA_BUSY; > + ata_tf_to_fis(&tf, 0, 0, d2h_fis); > + > + rc = sata_link_hardreset(link, timing, deadline, &online, > + ahci_check_ready); > + > + if (sata_scr_read(link, SCR_STATUS, &sstatus) != 0 || > + (sstatus & 0xf) != 1) > + break; > + > + ata_link_printk(link, KERN_INFO, "avn bounce port%d\n", > + port); > + > + pci_read_config_word(pdev, 0x92, &val); > + val &= ~(1 << port); > + pci_write_config_word(pdev, 0x92, val); > + ata_msleep(ap, 1000); > + val |= 1 << port; > + pci_write_config_word(pdev, 0x92, val); > + deadline += tmo; > + } > + > + hpriv->start_engine(ap); > + > + if (online) > + *class = ahci_dev_classify(ap); > + > + DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); > + return rc; > +} > + > + > #ifdef CONFIG_PM > static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) > { >
Yes Stefan, We do need to apply this on Utopic and Vivid, sorry for that. Want me to submit the patch for both ? Tks Rafael Tinoco > On Jun3, 2015, at 04:29 AM, Stefan Bader <stefan.bader@canonical.com> wrote: > > On 02.06.2015 16:19, Rafael David Tinoco wrote: >> BugLink: https://bugs.launchpad.net/bugs/1458617 >> >> 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. >> >> OriginalAuthor: Dan Williams <dan.j.williams@intel.com> >> Signed-off-by: Dan Williams <dan.j.williams@intel.com> >> (backported from commit 682de5f4135f3414ec87bb76ef400de1148393c8 upstream) > > Appears to be dbfe8ef5599a5370abc441fcdbb382b656563eb4 upstream (v4.1-rc4) and > marked for stable. Would Utopic and Vivid require a backport as well? > > The patch seems to be doing what it claims and has limited effect on specific hw > only. Positive testing claimed in the bug report. > > -Stefan > >> Signed-off-by: Tejun Heo <tj@kernel.org> >> Signed-off-by: Rafael David Tinoco <rafael.tinoco@canonical.com> >> --- >> drivers/ata/ahci.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++----- >> 1 file changed, 95 insertions(+), 8 deletions(-) >> >> diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c >> index ef8c437..f6d80c0 100644 >> --- a/drivers/ata/ahci.c >> +++ b/drivers/ata/ahci.c >> @@ -67,6 +67,7 @@ enum board_ids { >> board_ahci_yes_fbs, >> >> /* board IDs for specific chipsets in alphabetical order */ >> + board_ahci_avn, >> board_ahci_mcp65, >> board_ahci_mcp77, >> board_ahci_mcp89, >> @@ -85,6 +86,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 int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, >> + unsigned long deadline); >> static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, >> unsigned long deadline); >> #ifdef CONFIG_PM >> @@ -106,6 +109,11 @@ static struct ata_port_operations ahci_p5wdh_ops = { >> .hardreset = ahci_p5wdh_hardreset, >> }; >> >> +static struct ata_port_operations ahci_avn_ops = { >> + .inherits = &ahci_ops, >> + .hardreset = ahci_avn_hardreset, >> +}; >> + >> static const struct ata_port_info ahci_port_info[] = { >> /* by features */ >> [board_ahci] = { >> @@ -150,6 +158,12 @@ static const struct ata_port_info ahci_port_info[] = { >> .port_ops = &ahci_ops, >> }, >> /* by chipsets */ >> + [board_ahci_avn] = { >> + .flags = AHCI_FLAG_COMMON, >> + .pio_mask = ATA_PIO4, >> + .udma_mask = ATA_UDMA6, >> + .port_ops = &ahci_avn_ops, >> + }, >> [board_ahci_mcp65] = { >> AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | >> AHCI_HFLAG_YES_NCQ), >> @@ -289,14 +303,14 @@ static const struct pci_device_id ahci_pci_tbl[] = { >> { PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */ >> { PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */ >> { PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */ >> - { PCI_VDEVICE(INTEL, 0x1f32), board_ahci }, /* Avoton AHCI */ >> - { PCI_VDEVICE(INTEL, 0x1f33), board_ahci }, /* Avoton AHCI */ >> - { PCI_VDEVICE(INTEL, 0x1f34), board_ahci }, /* Avoton RAID */ >> - { PCI_VDEVICE(INTEL, 0x1f35), board_ahci }, /* Avoton RAID */ >> - { PCI_VDEVICE(INTEL, 0x1f36), board_ahci }, /* Avoton RAID */ >> - { PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */ >> - { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */ >> - { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */ >> + { PCI_VDEVICE(INTEL, 0x1f32), board_ahci_avn }, /* Avoton AHCI */ >> + { PCI_VDEVICE(INTEL, 0x1f33), board_ahci_avn }, /* Avoton AHCI */ >> + { PCI_VDEVICE(INTEL, 0x1f34), board_ahci_avn }, /* Avoton RAID */ >> + { PCI_VDEVICE(INTEL, 0x1f35), board_ahci_avn }, /* Avoton RAID */ >> + { PCI_VDEVICE(INTEL, 0x1f36), board_ahci_avn }, /* Avoton RAID */ >> + { PCI_VDEVICE(INTEL, 0x1f37), board_ahci_avn }, /* Avoton RAID */ >> + { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci_avn }, /* Avoton RAID */ >> + { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci_avn }, /* Avoton RAID */ >> { PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */ >> { PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */ >> { PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */ >> @@ -674,6 +688,79 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, >> return rc; >> } >> >> +/* >> + * ahci_avn_hardreset - attempt more aggressive recovery of Avoton ports. >> + * >> + * It has been observed with some SSDs that the timing of events in the >> + * link synchronization phase can leave the port in a state that can not >> + * be recovered by a SATA-hard-reset alone. The failing signature is >> + * SStatus.DET stuck at 1 ("Device presence detected but Phy >> + * communication not established"). It was found that unloading and >> + * reloading the driver when this problem occurs allows the drive >> + * connection to be recovered (DET advanced to 0x3). The critical >> + * component of reloading the driver is that the port state machines are >> + * reset by bouncing "port enable" in the AHCI PCS configuration >> + * register. So, reproduce that effect by bouncing a port whenever we >> + * see DET==1 after a reset. >> + */ >> +static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, >> + unsigned long deadline) >> +{ >> + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); >> + 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; >> + unsigned long tmo = deadline - jiffies; >> + struct ata_taskfile tf; >> + bool online; >> + int rc, i; >> + >> + DPRINTK("ENTER\n"); >> + >> + ahci_stop_engine(ap); >> + >> + for (i = 0; i < 2; i++) { >> + u16 val; >> + u32 sstatus; >> + int port = ap->port_no; >> + struct ata_host *host = ap->host; >> + struct pci_dev *pdev = to_pci_dev(host->dev); >> + >> + /* clear D2H reception area to properly wait for D2H FIS */ >> + ata_tf_init(link->device, &tf); >> + tf.command = ATA_BUSY; >> + ata_tf_to_fis(&tf, 0, 0, d2h_fis); >> + >> + rc = sata_link_hardreset(link, timing, deadline, &online, >> + ahci_check_ready); >> + >> + if (sata_scr_read(link, SCR_STATUS, &sstatus) != 0 || >> + (sstatus & 0xf) != 1) >> + break; >> + >> + ata_link_printk(link, KERN_INFO, "avn bounce port%d\n", >> + port); >> + >> + pci_read_config_word(pdev, 0x92, &val); >> + val &= ~(1 << port); >> + pci_write_config_word(pdev, 0x92, val); >> + ata_msleep(ap, 1000); >> + val |= 1 << port; >> + pci_write_config_word(pdev, 0x92, val); >> + deadline += tmo; >> + } >> + >> + hpriv->start_engine(ap); >> + >> + if (online) >> + *class = ahci_dev_classify(ap); >> + >> + DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); >> + return rc; >> +} >> + >> + >> #ifdef CONFIG_PM >> static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) >> { >> > > > -- > kernel-team mailing list > kernel-team@lists.ubuntu.com > https://lists.ubuntu.com/mailman/listinfo/kernel-team
On 06/03/2015 06:41 AM, Rafael David Tinoco wrote: > Yes Stefan, > > We do need to apply this on Utopic and Vivid, sorry for that. Want me to submit the patch for both ? > Yes please. Unless the patch submitted applies cleanly to multiple versions (which seeing this is a backport may not be the case). --chris > Tks > > Rafael Tinoco > >> On Jun3, 2015, at 04:29 AM, Stefan Bader <stefan.bader@canonical.com> wrote: >> >> On 02.06.2015 16:19, Rafael David Tinoco wrote: >>> BugLink: https://bugs.launchpad.net/bugs/1458617 >>> >>> 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. >>> >>> OriginalAuthor: Dan Williams <dan.j.williams@intel.com> >>> Signed-off-by: Dan Williams <dan.j.williams@intel.com> >>> (backported from commit 682de5f4135f3414ec87bb76ef400de1148393c8 upstream) >> >> Appears to be dbfe8ef5599a5370abc441fcdbb382b656563eb4 upstream (v4.1-rc4) and >> marked for stable. Would Utopic and Vivid require a backport as well? >> >> The patch seems to be doing what it claims and has limited effect on specific hw >> only. Positive testing claimed in the bug report. >> >> -Stefan >> >>> Signed-off-by: Tejun Heo <tj@kernel.org> >>> Signed-off-by: Rafael David Tinoco <rafael.tinoco@canonical.com> >>> --- >>> drivers/ata/ahci.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++----- >>> 1 file changed, 95 insertions(+), 8 deletions(-) >>> >>> diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c >>> index ef8c437..f6d80c0 100644 >>> --- a/drivers/ata/ahci.c >>> +++ b/drivers/ata/ahci.c >>> @@ -67,6 +67,7 @@ enum board_ids { >>> board_ahci_yes_fbs, >>> >>> /* board IDs for specific chipsets in alphabetical order */ >>> + board_ahci_avn, >>> board_ahci_mcp65, >>> board_ahci_mcp77, >>> board_ahci_mcp89, >>> @@ -85,6 +86,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 int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, >>> + unsigned long deadline); >>> static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, >>> unsigned long deadline); >>> #ifdef CONFIG_PM >>> @@ -106,6 +109,11 @@ static struct ata_port_operations ahci_p5wdh_ops = { >>> .hardreset = ahci_p5wdh_hardreset, >>> }; >>> >>> +static struct ata_port_operations ahci_avn_ops = { >>> + .inherits = &ahci_ops, >>> + .hardreset = ahci_avn_hardreset, >>> +}; >>> + >>> static const struct ata_port_info ahci_port_info[] = { >>> /* by features */ >>> [board_ahci] = { >>> @@ -150,6 +158,12 @@ static const struct ata_port_info ahci_port_info[] = { >>> .port_ops = &ahci_ops, >>> }, >>> /* by chipsets */ >>> + [board_ahci_avn] = { >>> + .flags = AHCI_FLAG_COMMON, >>> + .pio_mask = ATA_PIO4, >>> + .udma_mask = ATA_UDMA6, >>> + .port_ops = &ahci_avn_ops, >>> + }, >>> [board_ahci_mcp65] = { >>> AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | >>> AHCI_HFLAG_YES_NCQ), >>> @@ -289,14 +303,14 @@ static const struct pci_device_id ahci_pci_tbl[] = { >>> { PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */ >>> { PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */ >>> { PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */ >>> - { PCI_VDEVICE(INTEL, 0x1f32), board_ahci }, /* Avoton AHCI */ >>> - { PCI_VDEVICE(INTEL, 0x1f33), board_ahci }, /* Avoton AHCI */ >>> - { PCI_VDEVICE(INTEL, 0x1f34), board_ahci }, /* Avoton RAID */ >>> - { PCI_VDEVICE(INTEL, 0x1f35), board_ahci }, /* Avoton RAID */ >>> - { PCI_VDEVICE(INTEL, 0x1f36), board_ahci }, /* Avoton RAID */ >>> - { PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */ >>> - { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */ >>> - { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */ >>> + { PCI_VDEVICE(INTEL, 0x1f32), board_ahci_avn }, /* Avoton AHCI */ >>> + { PCI_VDEVICE(INTEL, 0x1f33), board_ahci_avn }, /* Avoton AHCI */ >>> + { PCI_VDEVICE(INTEL, 0x1f34), board_ahci_avn }, /* Avoton RAID */ >>> + { PCI_VDEVICE(INTEL, 0x1f35), board_ahci_avn }, /* Avoton RAID */ >>> + { PCI_VDEVICE(INTEL, 0x1f36), board_ahci_avn }, /* Avoton RAID */ >>> + { PCI_VDEVICE(INTEL, 0x1f37), board_ahci_avn }, /* Avoton RAID */ >>> + { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci_avn }, /* Avoton RAID */ >>> + { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci_avn }, /* Avoton RAID */ >>> { PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */ >>> { PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */ >>> { PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */ >>> @@ -674,6 +688,79 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, >>> return rc; >>> } >>> >>> +/* >>> + * ahci_avn_hardreset - attempt more aggressive recovery of Avoton ports. >>> + * >>> + * It has been observed with some SSDs that the timing of events in the >>> + * link synchronization phase can leave the port in a state that can not >>> + * be recovered by a SATA-hard-reset alone. The failing signature is >>> + * SStatus.DET stuck at 1 ("Device presence detected but Phy >>> + * communication not established"). It was found that unloading and >>> + * reloading the driver when this problem occurs allows the drive >>> + * connection to be recovered (DET advanced to 0x3). The critical >>> + * component of reloading the driver is that the port state machines are >>> + * reset by bouncing "port enable" in the AHCI PCS configuration >>> + * register. So, reproduce that effect by bouncing a port whenever we >>> + * see DET==1 after a reset. >>> + */ >>> +static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, >>> + unsigned long deadline) >>> +{ >>> + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); >>> + 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; >>> + unsigned long tmo = deadline - jiffies; >>> + struct ata_taskfile tf; >>> + bool online; >>> + int rc, i; >>> + >>> + DPRINTK("ENTER\n"); >>> + >>> + ahci_stop_engine(ap); >>> + >>> + for (i = 0; i < 2; i++) { >>> + u16 val; >>> + u32 sstatus; >>> + int port = ap->port_no; >>> + struct ata_host *host = ap->host; >>> + struct pci_dev *pdev = to_pci_dev(host->dev); >>> + >>> + /* clear D2H reception area to properly wait for D2H FIS */ >>> + ata_tf_init(link->device, &tf); >>> + tf.command = ATA_BUSY; >>> + ata_tf_to_fis(&tf, 0, 0, d2h_fis); >>> + >>> + rc = sata_link_hardreset(link, timing, deadline, &online, >>> + ahci_check_ready); >>> + >>> + if (sata_scr_read(link, SCR_STATUS, &sstatus) != 0 || >>> + (sstatus & 0xf) != 1) >>> + break; >>> + >>> + ata_link_printk(link, KERN_INFO, "avn bounce port%d\n", >>> + port); >>> + >>> + pci_read_config_word(pdev, 0x92, &val); >>> + val &= ~(1 << port); >>> + pci_write_config_word(pdev, 0x92, val); >>> + ata_msleep(ap, 1000); >>> + val |= 1 << port; >>> + pci_write_config_word(pdev, 0x92, val); >>> + deadline += tmo; >>> + } >>> + >>> + hpriv->start_engine(ap); >>> + >>> + if (online) >>> + *class = ahci_dev_classify(ap); >>> + >>> + DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); >>> + return rc; >>> +} >>> + >>> + >>> #ifdef CONFIG_PM >>> static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) >>> { >>> >> >> >> -- >> kernel-team mailing list >> kernel-team@lists.ubuntu.com >> https://lists.ubuntu.com/mailman/listinfo/kernel-team > >
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index ef8c437..f6d80c0 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -67,6 +67,7 @@ enum board_ids { board_ahci_yes_fbs, /* board IDs for specific chipsets in alphabetical order */ + board_ahci_avn, board_ahci_mcp65, board_ahci_mcp77, board_ahci_mcp89, @@ -85,6 +86,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 int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline); static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); #ifdef CONFIG_PM @@ -106,6 +109,11 @@ static struct ata_port_operations ahci_p5wdh_ops = { .hardreset = ahci_p5wdh_hardreset, }; +static struct ata_port_operations ahci_avn_ops = { + .inherits = &ahci_ops, + .hardreset = ahci_avn_hardreset, +}; + static const struct ata_port_info ahci_port_info[] = { /* by features */ [board_ahci] = { @@ -150,6 +158,12 @@ static const struct ata_port_info ahci_port_info[] = { .port_ops = &ahci_ops, }, /* by chipsets */ + [board_ahci_avn] = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_avn_ops, + }, [board_ahci_mcp65] = { AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), @@ -289,14 +303,14 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */ { PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */ { PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */ - { PCI_VDEVICE(INTEL, 0x1f32), board_ahci }, /* Avoton AHCI */ - { PCI_VDEVICE(INTEL, 0x1f33), board_ahci }, /* Avoton AHCI */ - { PCI_VDEVICE(INTEL, 0x1f34), board_ahci }, /* Avoton RAID */ - { PCI_VDEVICE(INTEL, 0x1f35), board_ahci }, /* Avoton RAID */ - { PCI_VDEVICE(INTEL, 0x1f36), board_ahci }, /* Avoton RAID */ - { PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */ - { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */ - { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f32), board_ahci_avn }, /* Avoton AHCI */ + { PCI_VDEVICE(INTEL, 0x1f33), board_ahci_avn }, /* Avoton AHCI */ + { PCI_VDEVICE(INTEL, 0x1f34), board_ahci_avn }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f35), board_ahci_avn }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f36), board_ahci_avn }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f37), board_ahci_avn }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci_avn }, /* Avoton RAID */ + { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci_avn }, /* Avoton RAID */ { PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */ { PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */ { PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */ @@ -674,6 +688,79 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, return rc; } +/* + * ahci_avn_hardreset - attempt more aggressive recovery of Avoton ports. + * + * It has been observed with some SSDs that the timing of events in the + * link synchronization phase can leave the port in a state that can not + * be recovered by a SATA-hard-reset alone. The failing signature is + * SStatus.DET stuck at 1 ("Device presence detected but Phy + * communication not established"). It was found that unloading and + * reloading the driver when this problem occurs allows the drive + * connection to be recovered (DET advanced to 0x3). The critical + * component of reloading the driver is that the port state machines are + * reset by bouncing "port enable" in the AHCI PCS configuration + * register. So, reproduce that effect by bouncing a port whenever we + * see DET==1 after a reset. + */ +static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + 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; + unsigned long tmo = deadline - jiffies; + struct ata_taskfile tf; + bool online; + int rc, i; + + DPRINTK("ENTER\n"); + + ahci_stop_engine(ap); + + for (i = 0; i < 2; i++) { + u16 val; + u32 sstatus; + int port = ap->port_no; + struct ata_host *host = ap->host; + struct pci_dev *pdev = to_pci_dev(host->dev); + + /* clear D2H reception area to properly wait for D2H FIS */ + ata_tf_init(link->device, &tf); + tf.command = ATA_BUSY; + ata_tf_to_fis(&tf, 0, 0, d2h_fis); + + rc = sata_link_hardreset(link, timing, deadline, &online, + ahci_check_ready); + + if (sata_scr_read(link, SCR_STATUS, &sstatus) != 0 || + (sstatus & 0xf) != 1) + break; + + ata_link_printk(link, KERN_INFO, "avn bounce port%d\n", + port); + + pci_read_config_word(pdev, 0x92, &val); + val &= ~(1 << port); + pci_write_config_word(pdev, 0x92, val); + ata_msleep(ap, 1000); + val |= 1 << port; + pci_write_config_word(pdev, 0x92, val); + deadline += tmo; + } + + hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); + + DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); + return rc; +} + + #ifdef CONFIG_PM static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) {