Commit b3f677e5 authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik

sata_inic162x: use IDMA for ATAPI commands

Use IDMA for ATAPI commands.  Write and some misc commands time out
when executed using ATAPI_PROT_DMA but ATAPI_PROT_PIO works fine.  As
PIO is driven by DMA too, it doesn't make any noticeable difference
for native SATA devices.  inic_check_atapi_dma() is implemented to
force PIO for those ATAPI commands.

After this change, sata_inic162x issues all commands using IDMA.
Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 049e8e04
...@@ -192,7 +192,8 @@ struct inic_prd { ...@@ -192,7 +192,8 @@ struct inic_prd {
struct inic_pkt { struct inic_pkt {
struct inic_cpb cpb; struct inic_cpb cpb;
struct inic_prd prd[LIBATA_MAX_PRD]; struct inic_prd prd[LIBATA_MAX_PRD + 1]; /* + 1 for cdb */
u8 cdb[ATAPI_CDB_LEN];
} __packed; } __packed;
struct inic_host_priv { struct inic_host_priv {
...@@ -361,7 +362,6 @@ static void inic_host_intr(struct ata_port *ap) ...@@ -361,7 +362,6 @@ static void inic_host_intr(struct ata_port *ap)
goto spurious; goto spurious;
} }
if (!ata_is_atapi(qc->tf.protocol)) {
if (likely(idma_stat & IDMA_STAT_DONE)) { if (likely(idma_stat & IDMA_STAT_DONE)) {
inic_stop_idma(ap); inic_stop_idma(ap);
...@@ -375,10 +375,6 @@ static void inic_host_intr(struct ata_port *ap) ...@@ -375,10 +375,6 @@ static void inic_host_intr(struct ata_port *ap)
ata_qc_complete(qc); ata_qc_complete(qc);
return; return;
} }
} else {
if (likely(ata_sff_host_intr(ap, qc)))
return;
}
spurious: spurious:
ap->ops->sff_check_status(ap); /* clear ATA interrupt */ ap->ops->sff_check_status(ap); /* clear ATA interrupt */
...@@ -421,6 +417,19 @@ static irqreturn_t inic_interrupt(int irq, void *dev_instance) ...@@ -421,6 +417,19 @@ static irqreturn_t inic_interrupt(int irq, void *dev_instance)
return IRQ_RETVAL(handled); return IRQ_RETVAL(handled);
} }
static int inic_check_atapi_dma(struct ata_queued_cmd *qc)
{
/* For some reason ATAPI_PROT_DMA doesn't work for some
* commands including writes and other misc ops. Use PIO
* protocol instead, which BTW is driven by the DMA engine
* anyway, so it shouldn't make much difference for native
* SATA devices.
*/
if (atapi_cmd_type(qc->cdb[0]) == READ)
return 0;
return 1;
}
static void inic_fill_sg(struct inic_prd *prd, struct ata_queued_cmd *qc) static void inic_fill_sg(struct inic_prd *prd, struct ata_queued_cmd *qc)
{ {
struct scatterlist *sg; struct scatterlist *sg;
...@@ -452,20 +461,21 @@ static void inic_qc_prep(struct ata_queued_cmd *qc) ...@@ -452,20 +461,21 @@ static void inic_qc_prep(struct ata_queued_cmd *qc)
struct inic_prd *prd = pkt->prd; struct inic_prd *prd = pkt->prd;
bool is_atapi = ata_is_atapi(qc->tf.protocol); bool is_atapi = ata_is_atapi(qc->tf.protocol);
bool is_data = ata_is_data(qc->tf.protocol); bool is_data = ata_is_data(qc->tf.protocol);
unsigned int cdb_len = 0;
VPRINTK("ENTER\n"); VPRINTK("ENTER\n");
if (is_atapi) if (is_atapi)
return; cdb_len = qc->dev->cdb_len;
/* prepare packet, based on initio driver */ /* prepare packet, based on initio driver */
memset(pkt, 0, sizeof(struct inic_pkt)); memset(pkt, 0, sizeof(struct inic_pkt));
cpb->ctl_flags = CPB_CTL_VALID | CPB_CTL_IEN; cpb->ctl_flags = CPB_CTL_VALID | CPB_CTL_IEN;
if (is_data) if (is_atapi || is_data)
cpb->ctl_flags |= CPB_CTL_DATA; cpb->ctl_flags |= CPB_CTL_DATA;
cpb->len = cpu_to_le32(qc->nbytes); cpb->len = cpu_to_le32(qc->nbytes + cdb_len);
cpb->prd = cpu_to_le32(pp->pkt_dma + offsetof(struct inic_pkt, prd)); cpb->prd = cpu_to_le32(pp->pkt_dma + offsetof(struct inic_pkt, prd));
cpb->device = qc->tf.device; cpb->device = qc->tf.device;
...@@ -486,6 +496,18 @@ static void inic_qc_prep(struct ata_queued_cmd *qc) ...@@ -486,6 +496,18 @@ static void inic_qc_prep(struct ata_queued_cmd *qc)
cpb->command = qc->tf.command; cpb->command = qc->tf.command;
/* don't load ctl - dunno why. it's like that in the initio driver */ /* don't load ctl - dunno why. it's like that in the initio driver */
/* setup PRD for CDB */
if (is_atapi) {
memcpy(pkt->cdb, qc->cdb, ATAPI_CDB_LEN);
prd->mad = cpu_to_le32(pp->pkt_dma +
offsetof(struct inic_pkt, cdb));
prd->len = cpu_to_le16(cdb_len);
prd->flags = PRD_CDB | PRD_WRITE;
if (!is_data)
prd->flags |= PRD_END;
prd++;
}
/* setup sg table */ /* setup sg table */
if (is_data) if (is_data)
inic_fill_sg(prd, qc); inic_fill_sg(prd, qc);
...@@ -498,16 +520,12 @@ static unsigned int inic_qc_issue(struct ata_queued_cmd *qc) ...@@ -498,16 +520,12 @@ static unsigned int inic_qc_issue(struct ata_queued_cmd *qc)
struct ata_port *ap = qc->ap; struct ata_port *ap = qc->ap;
void __iomem *port_base = inic_port_base(ap); void __iomem *port_base = inic_port_base(ap);
if (!ata_is_atapi(qc->tf.protocol)) {
/* fire up the ADMA engine */ /* fire up the ADMA engine */
writew(HCTL_FTHD0, port_base + HOST_CTL); writew(HCTL_FTHD0, port_base + HOST_CTL);
writew(IDMA_CTL_GO, port_base + PORT_IDMA_CTL); writew(IDMA_CTL_GO, port_base + PORT_IDMA_CTL);
writeb(0, port_base + PORT_CPB_PTQFIFO); writeb(0, port_base + PORT_CPB_PTQFIFO);
return 0; return 0;
}
return ata_sff_qc_issue(qc);
} }
static void inic_tf_read(struct ata_port *ap, struct ata_taskfile *tf) static void inic_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
...@@ -698,6 +716,7 @@ static int inic_port_start(struct ata_port *ap) ...@@ -698,6 +716,7 @@ static int inic_port_start(struct ata_port *ap)
static struct ata_port_operations inic_port_ops = { static struct ata_port_operations inic_port_ops = {
.inherits = &ata_sff_port_ops, .inherits = &ata_sff_port_ops,
.check_atapi_dma = inic_check_atapi_dma,
.qc_prep = inic_qc_prep, .qc_prep = inic_qc_prep,
.qc_issue = inic_qc_issue, .qc_issue = inic_qc_issue,
.qc_fill_rtf = inic_qc_fill_rtf, .qc_fill_rtf = inic_qc_fill_rtf,
...@@ -717,12 +736,6 @@ static struct ata_port_operations inic_port_ops = { ...@@ -717,12 +736,6 @@ static struct ata_port_operations inic_port_ops = {
}; };
static struct ata_port_info inic_port_info = { static struct ata_port_info inic_port_info = {
/* For some reason, ATAPI_PROT_PIO is broken on this
* controller, and no, PIO_POLLING does't fix it. It somehow
* manages to report the wrong ireason and ignoring ireason
* results in machine lock up. Tell libata to always prefer
* DMA.
*/
.flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA, .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA,
.pio_mask = 0x1f, /* pio0-4 */ .pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */ .mwdma_mask = 0x07, /* mwdma0-2 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment