diff mbox

[TRUSTY] ahci: avoton port-disable reset-quirk

Message ID 1433254774-11118-1-git-send-email-rafael.tinoco@canonical.com
State New
Headers show

Commit Message

Rafael David Tinoco June 2, 2015, 2:19 p.m. UTC
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)
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(-)

Comments

Stefan Bader June 3, 2015, 7:29 a.m. UTC | #1
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)
>  {
>
Rafael David Tinoco June 3, 2015, 11:41 a.m. UTC | #2
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
Chris J Arges June 3, 2015, 1:13 p.m. UTC | #3
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 mbox

Patch

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)
 {