summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/libata-core.c17
-rw-r--r--drivers/scsi/libata-scsi.c5
-rw-r--r--drivers/scsi/libata.h1
-rw-r--r--include/linux/libata.h2
4 files changed, 25 insertions, 0 deletions
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index bc846b86f225..5fdb285cd87d 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -1919,7 +1919,24 @@ static void ata_fill_sg(struct ata_queued_cmd *qc)
if (idx)
ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
}
+/**
+ * ata_check_atapi_dma - Check whether ATAPI DMA can be supported
+ * @qc: Metadata associated with taskfile to check
+ *
+ * LOCKING:
+ * RETURNS: 0 when ATAPI DMA can be used
+ * nonzero otherwise
+ */
+int ata_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ int rc = 0; /* Assume ATAPI DMA is OK by default */
+
+ if (ap->ops->check_atapi_dma)
+ rc = ap->ops->check_atapi_dma(qc);
+ return rc;
+}
/**
* ata_qc_prep - Prepare taskfile for submission
* @qc: Metadata associated with taskfile to be prepared
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
index 36320e8a3e5b..1b8bc9bdd7de 100644
--- a/drivers/scsi/libata-scsi.c
+++ b/drivers/scsi/libata-scsi.c
@@ -1294,6 +1294,11 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
int using_pio = (dev->flags & ATA_DFLAG_PIO);
int nodata = (cmd->sc_data_direction == SCSI_DATA_NONE);
+ if (!using_pio)
+ /* Check whether ATAPI DMA is safe */
+ if (ata_check_atapi_dma(qc))
+ using_pio = 1;
+
memcpy(&qc->cdb, scsicmd, qc->ap->cdb_len);
qc->complete_fn = atapi_qc_complete;
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index f54c93152e09..bd67a21becf5 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -38,6 +38,7 @@ struct ata_scsi_args {
extern struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
struct ata_device *dev);
extern int ata_qc_issue(struct ata_queued_cmd *qc);
+extern int ata_check_atapi_dma(struct ata_queued_cmd *qc);
extern void ata_dev_select(struct ata_port *ap, unsigned int device,
unsigned int wait, unsigned int can_sleep);
extern void ata_tf_to_host_nolock(struct ata_port *ap, struct ata_taskfile *tf);
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 95a7b0ddb096..a436be7d78aa 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -339,6 +339,8 @@ struct ata_port_operations {
void (*phy_reset) (struct ata_port *ap);
void (*post_set_mode) (struct ata_port *ap);
+ int (*check_atapi_dma) (struct ata_queued_cmd *qc);
+
void (*bmdma_setup) (struct ata_queued_cmd *qc);
void (*bmdma_start) (struct ata_queued_cmd *qc);