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>
This commit is contained in:
Robert Hancock 2007-03-26 21:43:36 -08:00 committed by Jeff Garzik
parent 4dc5200d70
commit f2fb344bea
1 changed files with 20 additions and 13 deletions

View File

@ -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_error_handler(struct ata_port *ap);
static void nv_adma_host_stop(struct ata_host *host); 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_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 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 = { static const struct ata_port_operations nv_adma_ops = {
.port_disable = ata_port_disable, .port_disable = ata_port_disable,
.tf_load = ata_tf_load, .tf_load = ata_tf_load,
.tf_read = ata_tf_read, .tf_read = nv_adma_tf_read,
.check_atapi_dma = nv_adma_check_atapi_dma, .check_atapi_dma = nv_adma_check_atapi_dma,
.exec_command = ata_exec_command, .exec_command = ata_exec_command,
.check_status = ata_check_status, .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); 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) static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, __le16 *cpb)
{ {
unsigned int idx = 0; 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; 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); struct ata_queued_cmd *qc = ata_qc_from_tag(ap, cpb_num);
VPRINTK("CPB flags done, flags=0x%x\n", flags); VPRINTK("CPB flags done, flags=0x%x\n", flags);
if (likely(qc)) { if (likely(qc)) {
/* Grab the ATA port status for non-NCQ commands. DPRINTK("Completing qc from tag %d\n",cpb_num);
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);
ata_qc_complete(qc); ata_qc_complete(qc);
} else { } else {
struct ata_eh_info *ehi = &ap->eh_info; 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; struct nv_adma_port_priv *pp = qc->ap->private_data;
/* ADMA engine can only be used for non-ATAPI DMA commands, /* 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) || 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; return 1;
if((qc->flags & ATA_QCFLAG_DMAMAP) || if((qc->flags & ATA_QCFLAG_DMAMAP) ||