summaryrefslogtreecommitdiff
path: root/drivers/ata
diff options
context:
space:
mode:
authorRobert Hancock <hancockr@shaw.ca>2007-03-27 09:43:36 +0400
committerJeff Garzik <jeff@garzik.org>2007-04-28 22:16:00 +0400
commitf2fb344beadf79f9d265c3fc60e8399bbf917a4d (patch)
tree2f9c3f269329484b60a3d89e2ffb6b60d67dd4a7 /drivers/ata
parent4dc5200d70a3e81510956c35fbb9fa3c15b440a5 (diff)
downloadlinux-f2fb344beadf79f9d265c3fc60e8399bbf917a4d.tar.xz
sata_nv: don't read shadow registers when in ADMA mode
Reading from the ATA shadow registers while we are in ADMA mode may cause undefined behavior. Don't read the ATA status register when completing commands for this reason, it shouldn't be needed as the controller will notify us if the command failed. Also, don't allow commands with result taskfile requested to execute in ADMA mode, since that requires accessing the shadow registers. We also still need to override tf_read since libata will read the result taskfile on a command failure, and we need to go into port register mode before allowing this. Signed-off-by: Robert Hancock <hancockr@shaw.ca> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/sata_nv.c33
1 files changed, 20 insertions, 13 deletions
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 9d9670a9b117..8a9473b16ec3 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -260,6 +260,7 @@ static int nv_adma_port_resume(struct ata_port *ap);
static void nv_adma_error_handler(struct ata_port *ap);
static void nv_adma_host_stop(struct ata_host *host);
static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc);
+static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
enum nv_host_type
{
@@ -435,7 +436,7 @@ static const struct ata_port_operations nv_ck804_ops = {
static const struct ata_port_operations nv_adma_ops = {
.port_disable = ata_port_disable,
.tf_load = ata_tf_load,
- .tf_read = ata_tf_read,
+ .tf_read = nv_adma_tf_read,
.check_atapi_dma = nv_adma_check_atapi_dma,
.exec_command = ata_exec_command,
.check_status = ata_check_status,
@@ -667,6 +668,18 @@ static int nv_adma_check_atapi_dma(struct ata_queued_cmd *qc)
return !(pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE);
}
+static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ /* Since commands where a result TF is requested are not
+ executed in ADMA mode, the only time this function will be called
+ in ADMA mode will be if a command fails. In this case we
+ don't care about going into register mode with ADMA commands
+ pending, as the commands will all shortly be aborted anyway. */
+ nv_adma_register_mode(ap);
+
+ ata_tf_read(ap, tf);
+}
+
static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, __le16 *cpb)
{
unsigned int idx = 0;
@@ -738,19 +751,11 @@ static int nv_adma_check_cpb(struct ata_port *ap, int cpb_num, int force_err)
return 1;
}
- if (flags & NV_CPB_RESP_DONE) {
+ if (likely(flags & NV_CPB_RESP_DONE)) {
struct ata_queued_cmd *qc = ata_qc_from_tag(ap, cpb_num);
VPRINTK("CPB flags done, flags=0x%x\n", flags);
if (likely(qc)) {
- /* Grab the ATA port status for non-NCQ commands.
- For NCQ commands the current status may have nothing to do with
- the command just completed. */
- if (qc->tf.protocol != ATA_PROT_NCQ) {
- u8 ata_status = readb(pp->ctl_block + (ATA_REG_STATUS * 4));
- qc->err_mask |= ac_err_mask(ata_status);
- }
- DPRINTK("Completing qc from tag %d with err_mask %u\n",cpb_num,
- qc->err_mask);
+ DPRINTK("Completing qc from tag %d\n",cpb_num);
ata_qc_complete(qc);
} else {
struct ata_eh_info *ehi = &ap->eh_info;
@@ -1167,9 +1172,11 @@ static int nv_adma_use_reg_mode(struct ata_queued_cmd *qc)
struct nv_adma_port_priv *pp = qc->ap->private_data;
/* ADMA engine can only be used for non-ATAPI DMA commands,
- or interrupt-driven no-data commands. */
+ or interrupt-driven no-data commands, where a result taskfile
+ is not required. */
if((pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) ||
- (qc->tf.flags & ATA_TFLAG_POLLING))
+ (qc->tf.flags & ATA_TFLAG_POLLING) ||
+ (qc->flags & ATA_QCFLAG_RESULT_TF))
return 1;
if((qc->flags & ATA_QCFLAG_DMAMAP) ||