Skip to content

Commit

Permalink
sata_inic162x: use IDMA for ATAPI commands
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
htejun authored and Jeff Garzik committed May 6, 2008
1 parent 049e8e0 commit b3f677e
Showing 1 changed file with 47 additions and 34 deletions.
81 changes: 47 additions & 34 deletions drivers/ata/sata_inic162x.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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 */
Expand Down

0 comments on commit b3f677e

Please sign in to comment.