Skip to content

Commit

Permalink
sata_fsl: add workaround for data length mismatch on freescale V2 con…
Browse files Browse the repository at this point in the history
…troller

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>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
  • Loading branch information
Shaohui Xie authored and Jeff Garzik committed Sep 13, 2012
1 parent 65fe1f0 commit 100f586
Showing 1 changed file with 35 additions and 4 deletions.
39 changes: 35 additions & 4 deletions drivers/ata/sata_fsl.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ enum {
ONLINE = (1 << 31),
GOING_OFFLINE = (1 << 30),
BIST_ERR = (1 << 29),
CLEAR_ERROR = (1 << 27),

FATAL_ERR_HC_MASTER_ERR = (1 << 18),
FATAL_ERR_PARITY_ERR_TX = (1 << 17),
Expand All @@ -143,6 +144,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),

Expand Down Expand Up @@ -1181,25 +1183,54 @@ static void sata_fsl_host_intr(struct ata_port *ap)
u32 hstatus, done_mask = 0;
struct ata_queued_cmd *qc;
u32 SError;
u32 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;
/* Set HControl[27] to clear error registers */
hcontrol = ioread32(hcr_base + HCONTROL);
iowrite32(hcontrol | CLEAR_ERROR,
hcr_base + HCONTROL);

/* Clear HControl[27] */
iowrite32(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,
Expand Down

0 comments on commit 100f586

Please sign in to comment.