diff options
| author | Albert Lee <albertcc@tw.ibm.com> | 2004-12-27 05:29:14 -0500 |
|---|---|---|
| committer | Jeff Garzik <jgarzik@pobox.com> | 2004-12-27 05:29:14 -0500 |
| commit | a56bb4da9a52fb1e8da15f8152b665cbf6ca2d64 (patch) | |
| tree | a34e2b62fd62cd372bb6808f56e7637199a3e5cc | |
| parent | 623b57b30106cbc5755aa1e622f1806b3c8dd7a9 (diff) | |
[libata] verify ATAPI DMA for a given request is OK
After some testing, it seems that some PATA host adapter (ex. pdc20275) cannot
work reliably with specific request buffer sizes under ATAPI DMA mode.
Detailed test result:
4096, 2048, 1024, 512, 256: OK
384, 257, 255, 128, 96, 64, 32: failed (irq lost)
It seems multiple of 256 bytes are the safe ATAPI DMA buffer sizes to use.
Attached please find the patch to fix the pdc2027x ATAPI DMA problem.
Changes:
1. Add a callback function "check_atapi_dma()" to ata_port_operations such that libata core
can ask the driver: "Can this command be processed in ATAPI DMA mode safely? "
when the the command is received.
2. ATAPI DMA is off by default if the callback function is not provided by the driver
If the callback function is not provided by the driver, the ATAPI DMA should be as is.
The ATAPI DMA is already controlled by dev->flags.
BTW, the patch isolates the ATAPI DMA workaround to the pdc20275 driver itself,
not impacting libata core .
Signed-off-by: Albert Lee <albertcc@tw.ibm.com>
| -rw-r--r-- | drivers/scsi/libata-core.c | 17 | ||||
| -rw-r--r-- | drivers/scsi/libata-scsi.c | 5 | ||||
| -rw-r--r-- | drivers/scsi/libata.h | 1 | ||||
| -rw-r--r-- | include/linux/libata.h | 2 |
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); |
