Patchwork [v2] sata_fsl: add workaround for data length mismatch on freescale V2 controller

login
register
mail settings
Submitter shaohui xie
Date Sept. 6, 2012, 4:28 a.m.
Message ID <1346905686-27912-1-git-send-email-Shaohui.Xie@freescale.com>
Download mbox | patch
Permalink /patch/182004/
State Not Applicable
Delegated to: David Miller
Headers show

Comments

shaohui xie - Sept. 6, 2012, 4:28 a.m.
The freescale V2 SATA controller checks if the received data length matches
the programmed length 'ttl', if not, it assumes that this is an error.
In ATAPI, the 'ttl' is based on max allocation length and not the actual
data transfer length, controller will raise 'DLM' (Data length Mismatch)
error bit in Hstatus register. Along with 'DLM', DE (Device error) and
FE (fatal Error) bits are also set in Hstatus register, 'E' (Internal Error)
bit is set in Serror register and CE (Command Error) and DE (Device error)
registers have the corresponding bit set. In this condition, we need to
clear errors in following way: in the service routine, based on 'DLM' flag,
HCONTROL[27] operation clears Hstatus, CE and DE registers, clear Serror
register.

Signed-off-by: Shaohui Xie <Shaohui.Xie@freescale.com>
Signed-off-by: Anju Bhartiya <Anju.Bhartiya@freescale.com>
---
changes for V2:
1. remove the using of quirk;
2. wrap errata codes in condition;

 drivers/ata/sata_fsl.c |   40 +++++++++++++++++++++++++++++++++++-----
 1 files changed, 35 insertions(+), 5 deletions(-)
Sergei Shtylyov - Sept. 6, 2012, 11:28 a.m.
Hello.

On 06-09-2012 8:28, Shaohui Xie wrote:

> The freescale V2 SATA controller checks if the received data length matches
> the programmed length 'ttl', if not, it assumes that this is an error.
> In ATAPI, the 'ttl' is based on max allocation length and not the actual
> data transfer length, controller will raise 'DLM' (Data length Mismatch)
> error bit in Hstatus register. Along with 'DLM', DE (Device error) and
> FE (fatal Error) bits are also set in Hstatus register, 'E' (Internal Error)
> bit is set in Serror register and CE (Command Error) and DE (Device error)
> registers have the corresponding bit set. In this condition, we need to
> clear errors in following way: in the service routine, based on 'DLM' flag,
> HCONTROL[27] operation clears Hstatus, CE and DE registers, clear Serror
> register.

> Signed-off-by: Shaohui Xie <Shaohui.Xie@freescale.com>
> Signed-off-by: Anju Bhartiya <Anju.Bhartiya@freescale.com>
> ---
> changes for V2:
> 1. remove the using of quirk;
> 2. wrap errata codes in condition;

>   drivers/ata/sata_fsl.c |   40 +++++++++++++++++++++++++++++++++++-----
>   1 files changed, 35 insertions(+), 5 deletions(-)

> diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
> index d6577b9..6b7b73e 100644
> --- a/drivers/ata/sata_fsl.c
> +++ b/drivers/ata/sata_fsl.c
[...]
> @@ -1180,26 +1181,55 @@ static void sata_fsl_host_intr(struct ata_port *ap)
>   	void __iomem *hcr_base = host_priv->hcr_base;
>   	u32 hstatus, done_mask = 0;
>   	struct ata_queued_cmd *qc;
> -	u32 SError;
> +	u32 SError, tag;
> +	u32 status_mask = INT_ON_ERROR;
>
>   	hstatus = ioread32(hcr_base + HSTATUS);
>
>   	sata_fsl_scr_read(&ap->link, SCR_ERROR, &SError);
>
> +	/* Read command completed register */
> +	done_mask = ioread32(hcr_base + CC);
> +
> +	/* Workaround for data length mismatch errata */
> +	if (unlikely(hstatus & INT_ON_DATA_LENGTH_MISMATCH)) {
> +		for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
> +			qc = ata_qc_from_tag(ap, tag);
> +			if (qc && ata_is_atapi(qc->tf.protocol)) {
> +				u32 Hcontrol;

    No uppercase in variable names please. Besides, you have 'hstatus' 
variable already and that would be inconsistent.

> +#define HCONTROL_CLEAR_ERROR	(1 << 27)
> +				/* Set HControl[27] to clear error registers */
> +				Hcontrol = ioread32(hcr_base + HCONTROL);
> +				iowrite32(Hcontrol | HCONTROL_CLEAR_ERROR,
> +						hcr_base + HCONTROL);
> +
> +				/* Clear HControl[27] */
> +				iowrite32(Hcontrol & (~HCONTROL_CLEAR_ERROR),

    Parens not needed around ~HCONTROL_CLEAR_ERROR.

> +						hcr_base + HCONTROL);

MBR, Sergei

--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
shaohui xie - Sept. 6, 2012, 11:57 a.m.
> >
> >   	hstatus = ioread32(hcr_base + HSTATUS);
> >
> >   	sata_fsl_scr_read(&ap->link, SCR_ERROR, &SError);
> >
> > +	/* Read command completed register */
> > +	done_mask = ioread32(hcr_base + CC);
> > +
> > +	/* Workaround for data length mismatch errata */
> > +	if (unlikely(hstatus & INT_ON_DATA_LENGTH_MISMATCH)) {
> > +		for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
> > +			qc = ata_qc_from_tag(ap, tag);
> > +			if (qc && ata_is_atapi(qc->tf.protocol)) {
> > +				u32 Hcontrol;
> 
>     No uppercase in variable names please. 
[S.H] OK.

Besides, you have 'hstatus'
> variable already and that would be inconsistent.
[S.H] Yes, but I ignored fatal error and device error, so this won't be an issue.

> 
> > +#define HCONTROL_CLEAR_ERROR	(1 << 27)
> > +				/* Set HControl[27] to clear error registers */
> > +				Hcontrol = ioread32(hcr_base + HCONTROL);
> > +				iowrite32(Hcontrol | HCONTROL_CLEAR_ERROR,
> > +						hcr_base + HCONTROL);
> > +
> > +				/* Clear HControl[27] */
> > +				iowrite32(Hcontrol & (~HCONTROL_CLEAR_ERROR),
> 
>     Parens not needed around ~HCONTROL_CLEAR_ERROR.
[S.H] OK.


Best Regards, 
Shaohui Xie

--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index d6577b9..6b7b73e 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -143,6 +143,7 @@  enum {
 	    FATAL_ERR_CRC_ERR_RX |
 	    FATAL_ERR_FIFO_OVRFL_TX | FATAL_ERR_FIFO_OVRFL_RX,
 
+	INT_ON_DATA_LENGTH_MISMATCH = (1 << 12),
 	INT_ON_FATAL_ERR = (1 << 5),
 	INT_ON_PHYRDY_CHG = (1 << 4),
 
@@ -1180,26 +1181,55 @@  static void sata_fsl_host_intr(struct ata_port *ap)
 	void __iomem *hcr_base = host_priv->hcr_base;
 	u32 hstatus, done_mask = 0;
 	struct ata_queued_cmd *qc;
-	u32 SError;
+	u32 SError, tag;
+	u32 status_mask = INT_ON_ERROR;
 
 	hstatus = ioread32(hcr_base + HSTATUS);
 
 	sata_fsl_scr_read(&ap->link, SCR_ERROR, &SError);
 
+	/* Read command completed register */
+	done_mask = ioread32(hcr_base + CC);
+
+	/* Workaround for data length mismatch errata */
+	if (unlikely(hstatus & INT_ON_DATA_LENGTH_MISMATCH)) {
+		for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
+			qc = ata_qc_from_tag(ap, tag);
+			if (qc && ata_is_atapi(qc->tf.protocol)) {
+				u32 Hcontrol;
+#define HCONTROL_CLEAR_ERROR	(1 << 27)
+				/* Set HControl[27] to clear error registers */
+				Hcontrol = ioread32(hcr_base + HCONTROL);
+				iowrite32(Hcontrol | HCONTROL_CLEAR_ERROR,
+						hcr_base + HCONTROL);
+
+				/* Clear HControl[27] */
+				iowrite32(Hcontrol & (~HCONTROL_CLEAR_ERROR),
+						hcr_base + HCONTROL);
+
+				/* Clear SError[E] bit */
+				sata_fsl_scr_write(&ap->link, SCR_ERROR,
+						SError);
+
+				/* Ignore fatal error and device error */
+				status_mask &= ~(INT_ON_SINGL_DEVICE_ERR
+						| INT_ON_FATAL_ERR);
+				break;
+			}
+		}
+	}
+
 	if (unlikely(SError & 0xFFFF0000)) {
 		DPRINTK("serror @host_intr : 0x%x\n", SError);
 		sata_fsl_error_intr(ap);
 	}
 
-	if (unlikely(hstatus & INT_ON_ERROR)) {
+	if (unlikely(hstatus & status_mask)) {
 		DPRINTK("error interrupt!!\n");
 		sata_fsl_error_intr(ap);
 		return;
 	}
 
-	/* Read command completed register */
-	done_mask = ioread32(hcr_base + CC);
-
 	VPRINTK("Status of all queues :\n");
 	VPRINTK("done_mask/CC = 0x%x, CA = 0x%x, CE=0x%x,CQ=0x%x,apqa=0x%x\n",
 		done_mask,