summaryrefslogtreecommitdiffstats
path: root/drivers/ata
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2008-04-30 16:35:14 +0900
committerJeff Garzik <jgarzik@redhat.com>2008-05-06 11:40:55 -0400
commitb3f677e501a494aa1582d4ff35fb3ac6f0a59b08 (patch)
tree07742b58e6ad35add7cefc5027a87a25e72bb8d1 /drivers/ata
parent049e8e04986bde66df9648d88d0960ab4cbd6992 (diff)
downloadtalos-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.c81
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 */
OpenPOWER on IntegriCloud