aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaohui Xie <Shaohui.Xie@freescale.com>2012-09-11 10:48:53 +0800
committerJeff Garzik <jgarzik@redhat.com>2012-09-13 01:09:19 -0400
commit100f586bd0959fe0e52b8a0b8cb49a3df1c6b044 (patch)
tree8017ee731f9940ddb081883fb45e6fd9ee874aa2
parent65fe1f0f66a57380229a4ced844188103135f37b (diff)
sata_fsl: add workaround for data length mismatch on freescale V2 controller
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>
-rw-r--r--drivers/ata/sata_fsl.c39
1 files changed, 35 insertions, 4 deletions
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index d6577b93bee..124b2c1d9c0 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -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),
@@ -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),
@@ -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,