diff options
author | Tejun Heo <htejun@gmail.com> | 2008-04-30 16:35:14 +0900 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-05-06 11:40:55 -0400 |
commit | b3f677e501a494aa1582d4ff35fb3ac6f0a59b08 (patch) | |
tree | 07742b58e6ad35add7cefc5027a87a25e72bb8d1 /drivers/ata | |
parent | 049e8e04986bde66df9648d88d0960ab4cbd6992 (diff) | |
download | talos-obmc-linux-b3f677e501a494aa1582d4ff35fb3ac6f0a59b08.tar.gz talos-obmc-linux-b3f677e501a494aa1582d4ff35fb3ac6f0a59b08.zip |
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: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/sata_inic162x.c | 81 |
1 files changed, 47 insertions, 34 deletions
diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c index 579154c27902..cdae435620f6 100644 --- a/drivers/ata/sata_inic162x.c +++ b/drivers/ata/sata_inic162x.c @@ -192,7 +192,8 @@ struct inic_prd { struct inic_pkt { 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; struct inic_host_priv { @@ -361,23 +362,18 @@ static void inic_host_intr(struct ata_port *ap) goto spurious; } - if (!ata_is_atapi(qc->tf.protocol)) { - if (likely(idma_stat & IDMA_STAT_DONE)) { - inic_stop_idma(ap); + if (likely(idma_stat & IDMA_STAT_DONE)) { + inic_stop_idma(ap); - /* Depending on circumstances, device error - * isn't reported by IDMA, check it explicitly. - */ - if (unlikely(readb(port_base + PORT_TF_COMMAND) & - (ATA_DF | ATA_ERR))) - qc->err_mask |= AC_ERR_DEV; + /* Depending on circumstances, device error + * isn't reported by IDMA, check it explicitly. + */ + if (unlikely(readb(port_base + PORT_TF_COMMAND) & + (ATA_DF | ATA_ERR))) + qc->err_mask |= AC_ERR_DEV; - ata_qc_complete(qc); - return; - } - } else { - if (likely(ata_sff_host_intr(ap, qc))) - return; + ata_qc_complete(qc); + return; } spurious: @@ -421,6 +417,19 @@ static irqreturn_t inic_interrupt(int irq, void *dev_instance) 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) { struct scatterlist *sg; @@ -452,20 +461,21 @@ static void inic_qc_prep(struct ata_queued_cmd *qc) struct inic_prd *prd = pkt->prd; bool is_atapi = ata_is_atapi(qc->tf.protocol); bool is_data = ata_is_data(qc->tf.protocol); + unsigned int cdb_len = 0; VPRINTK("ENTER\n"); if (is_atapi) - return; + cdb_len = qc->dev->cdb_len; /* prepare packet, based on initio driver */ memset(pkt, 0, sizeof(struct inic_pkt)); cpb->ctl_flags = CPB_CTL_VALID | CPB_CTL_IEN; - if (is_data) + if (is_atapi || is_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->device = qc->tf.device; @@ -486,6 +496,18 @@ static void inic_qc_prep(struct ata_queued_cmd *qc) cpb->command = qc->tf.command; /* 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 */ if (is_data) inic_fill_sg(prd, qc); @@ -498,16 +520,12 @@ static unsigned int inic_qc_issue(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; void __iomem *port_base = inic_port_base(ap); - if (!ata_is_atapi(qc->tf.protocol)) { - /* fire up the ADMA engine */ - writew(HCTL_FTHD0, port_base + HOST_CTL); - writew(IDMA_CTL_GO, port_base + PORT_IDMA_CTL); - writeb(0, port_base + PORT_CPB_PTQFIFO); - - return 0; - } + /* fire up the ADMA engine */ + writew(HCTL_FTHD0, port_base + HOST_CTL); + writew(IDMA_CTL_GO, port_base + PORT_IDMA_CTL); + writeb(0, port_base + PORT_CPB_PTQFIFO); - return ata_sff_qc_issue(qc); + return 0; } 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) static struct ata_port_operations inic_port_ops = { .inherits = &ata_sff_port_ops, + .check_atapi_dma = inic_check_atapi_dma, .qc_prep = inic_qc_prep, .qc_issue = inic_qc_issue, .qc_fill_rtf = inic_qc_fill_rtf, @@ -717,12 +736,6 @@ static struct ata_port_operations inic_port_ops = { }; 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, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ |