summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libata-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/libata-core.c')
-rw-r--r--drivers/scsi/libata-core.c511
1 files changed, 343 insertions, 168 deletions
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 665ae79e1fd..f55b9b3f7b3 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -562,16 +562,28 @@ static const u8 ata_rw_cmds[] = {
ATA_CMD_WRITE_MULTI,
ATA_CMD_READ_MULTI_EXT,
ATA_CMD_WRITE_MULTI_EXT,
+ 0,
+ 0,
+ 0,
+ ATA_CMD_WRITE_MULTI_FUA_EXT,
/* pio */
ATA_CMD_PIO_READ,
ATA_CMD_PIO_WRITE,
ATA_CMD_PIO_READ_EXT,
ATA_CMD_PIO_WRITE_EXT,
+ 0,
+ 0,
+ 0,
+ 0,
/* dma */
ATA_CMD_READ,
ATA_CMD_WRITE,
ATA_CMD_READ_EXT,
- ATA_CMD_WRITE_EXT
+ ATA_CMD_WRITE_EXT,
+ 0,
+ 0,
+ 0,
+ ATA_CMD_WRITE_FUA_EXT
};
/**
@@ -584,28 +596,35 @@ static const u8 ata_rw_cmds[] = {
* LOCKING:
* caller.
*/
-void ata_rwcmd_protocol(struct ata_queued_cmd *qc)
+int ata_rwcmd_protocol(struct ata_queued_cmd *qc)
{
struct ata_taskfile *tf = &qc->tf;
struct ata_device *dev = qc->dev;
+ u8 cmd;
- int index, lba48, write;
+ int index, fua, lba48, write;
+ fua = (tf->flags & ATA_TFLAG_FUA) ? 4 : 0;
lba48 = (tf->flags & ATA_TFLAG_LBA48) ? 2 : 0;
write = (tf->flags & ATA_TFLAG_WRITE) ? 1 : 0;
if (dev->flags & ATA_DFLAG_PIO) {
tf->protocol = ATA_PROT_PIO;
- index = dev->multi_count ? 0 : 4;
+ index = dev->multi_count ? 0 : 8;
} else {
tf->protocol = ATA_PROT_DMA;
- index = 8;
+ index = 16;
}
- tf->command = ata_rw_cmds[index + lba48 + write];
+ cmd = ata_rw_cmds[index + fua + lba48 + write];
+ if (cmd) {
+ tf->command = cmd;
+ return 0;
+ }
+ return -1;
}
-static const char * xfer_mode_str[] = {
+static const char * const xfer_mode_str[] = {
"UDMA/16",
"UDMA/25",
"UDMA/33",
@@ -1046,28 +1065,103 @@ static unsigned int ata_pio_modes(const struct ata_device *adev)
return modes;
}
-static int ata_qc_wait_err(struct ata_queued_cmd *qc,
- struct completion *wait)
+struct ata_exec_internal_arg {
+ unsigned int err_mask;
+ struct ata_taskfile *tf;
+ struct completion *waiting;
+};
+
+int ata_qc_complete_internal(struct ata_queued_cmd *qc)
{
- int rc = 0;
+ struct ata_exec_internal_arg *arg = qc->private_data;
+ struct completion *waiting = arg->waiting;
- if (wait_for_completion_timeout(wait, 30 * HZ) < 1) {
- /* timeout handling */
- unsigned int err_mask = ac_err_mask(ata_chk_status(qc->ap));
+ if (!(qc->err_mask & ~AC_ERR_DEV))
+ qc->ap->ops->tf_read(qc->ap, arg->tf);
+ arg->err_mask = qc->err_mask;
+ arg->waiting = NULL;
+ complete(waiting);
- if (!err_mask) {
- printk(KERN_WARNING "ata%u: slow completion (cmd %x)\n",
- qc->ap->id, qc->tf.command);
- } else {
- printk(KERN_WARNING "ata%u: qc timeout (cmd %x)\n",
- qc->ap->id, qc->tf.command);
- rc = -EIO;
+ return 0;
+}
+
+/**
+ * ata_exec_internal - execute libata internal command
+ * @ap: Port to which the command is sent
+ * @dev: Device to which the command is sent
+ * @tf: Taskfile registers for the command and the result
+ * @dma_dir: Data tranfer direction of the command
+ * @buf: Data buffer of the command
+ * @buflen: Length of data buffer
+ *
+ * Executes libata internal command with timeout. @tf contains
+ * command on entry and result on return. Timeout and error
+ * conditions are reported via return value. No recovery action
+ * is taken after a command times out. It's caller's duty to
+ * clean up after timeout.
+ *
+ * LOCKING:
+ * None. Should be called with kernel context, might sleep.
+ */
+
+static unsigned
+ata_exec_internal(struct ata_port *ap, struct ata_device *dev,
+ struct ata_taskfile *tf,
+ int dma_dir, void *buf, unsigned int buflen)
+{
+ u8 command = tf->command;
+ struct ata_queued_cmd *qc;
+ DECLARE_COMPLETION(wait);
+ unsigned long flags;
+ struct ata_exec_internal_arg arg;
+
+ spin_lock_irqsave(&ap->host_set->lock, flags);
+
+ qc = ata_qc_new_init(ap, dev);
+ BUG_ON(qc == NULL);
+
+ qc->tf = *tf;
+ qc->dma_dir = dma_dir;
+ if (dma_dir != DMA_NONE) {
+ ata_sg_init_one(qc, buf, buflen);
+ qc->nsect = buflen / ATA_SECT_SIZE;
+ }
+
+ arg.waiting = &wait;
+ arg.tf = tf;
+ qc->private_data = &arg;
+ qc->complete_fn = ata_qc_complete_internal;
+
+ if (ata_qc_issue(qc))
+ goto issue_fail;
+
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+ if (!wait_for_completion_timeout(&wait, ATA_TMOUT_INTERNAL)) {
+ spin_lock_irqsave(&ap->host_set->lock, flags);
+
+ /* We're racing with irq here. If we lose, the
+ * following test prevents us from completing the qc
+ * again. If completion irq occurs after here but
+ * before the caller cleans up, it will result in a
+ * spurious interrupt. We can live with that.
+ */
+ if (arg.waiting) {
+ qc->err_mask = AC_ERR_OTHER;
+ ata_qc_complete(qc);
+ printk(KERN_WARNING "ata%u: qc timeout (cmd 0x%x)\n",
+ ap->id, command);
}
- ata_qc_complete(qc, err_mask);
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
}
- return rc;
+ return arg.err_mask;
+
+ issue_fail:
+ ata_qc_free(qc);
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+ return AC_ERR_OTHER;
}
/**
@@ -1099,9 +1193,8 @@ static void ata_dev_identify(struct ata_port *ap, unsigned int device)
u16 tmp;
unsigned long xfer_modes;
unsigned int using_edd;
- DECLARE_COMPLETION(wait);
- struct ata_queued_cmd *qc;
- unsigned long flags;
+ struct ata_taskfile tf;
+ unsigned int err_mask;
int rc;
if (!ata_dev_present(dev)) {
@@ -1122,40 +1215,26 @@ static void ata_dev_identify(struct ata_port *ap, unsigned int device)
ata_dev_select(ap, device, 1, 1); /* select device 0/1 */
- qc = ata_qc_new_init(ap, dev);
- BUG_ON(qc == NULL);
-
- ata_sg_init_one(qc, dev->id, sizeof(dev->id));
- qc->dma_dir = DMA_FROM_DEVICE;
- qc->tf.protocol = ATA_PROT_PIO;
- qc->nsect = 1;
-
retry:
+ ata_tf_init(ap, &tf, device);
+
if (dev->class == ATA_DEV_ATA) {
- qc->tf.command = ATA_CMD_ID_ATA;
+ tf.command = ATA_CMD_ID_ATA;
DPRINTK("do ATA identify\n");
} else {
- qc->tf.command = ATA_CMD_ID_ATAPI;
+ tf.command = ATA_CMD_ID_ATAPI;
DPRINTK("do ATAPI identify\n");
}
- qc->waiting = &wait;
- qc->complete_fn = ata_qc_complete_noop;
-
- spin_lock_irqsave(&ap->host_set->lock, flags);
- rc = ata_qc_issue(qc);
- spin_unlock_irqrestore(&ap->host_set->lock, flags);
+ tf.protocol = ATA_PROT_PIO;
- if (rc)
- goto err_out;
- else
- ata_qc_wait_err(qc, &wait);
+ err_mask = ata_exec_internal(ap, dev, &tf, DMA_FROM_DEVICE,
+ dev->id, sizeof(dev->id));
- spin_lock_irqsave(&ap->host_set->lock, flags);
- ap->ops->tf_read(ap, &qc->tf);
- spin_unlock_irqrestore(&ap->host_set->lock, flags);
+ if (err_mask) {
+ if (err_mask & ~AC_ERR_DEV)
+ goto err_out;
- if (qc->tf.command & ATA_ERR) {
/*
* arg! EDD works for all test cases, but seems to return
* the ATA signature for some ATAPI devices. Until the
@@ -1168,13 +1247,9 @@ retry:
* to have this problem.
*/
if ((using_edd) && (dev->class == ATA_DEV_ATA)) {
- u8 err = qc->tf.feature;
+ u8 err = tf.feature;
if (err & ATA_ABORTED) {
dev->class = ATA_DEV_ATAPI;
- qc->cursg = 0;
- qc->cursg_ofs = 0;
- qc->cursect = 0;
- qc->nsect = 1;
goto retry;
}
}
@@ -1444,11 +1519,23 @@ void __sata_phy_reset(struct ata_port *ap)
} while (time_before(jiffies, timeout));
/* TODO: phy layer with polling, timeouts, etc. */
- if (sata_dev_present(ap))
+ sstatus = scr_read(ap, SCR_STATUS);
+ if (sata_dev_present(ap)) {
+ const char *speed;
+ u32 tmp;
+
+ tmp = (sstatus >> 4) & 0xf;
+ if (tmp & (1 << 0))
+ speed = "1.5";
+ else if (tmp & (1 << 1))
+ speed = "3.0";
+ else
+ speed = "<unknown>";
+ printk(KERN_INFO "ata%u: SATA link up %s Gbps (SStatus %X)\n",
+ ap->id, speed, sstatus);
ata_port_probe(ap);
- else {
- sstatus = scr_read(ap, SCR_STATUS);
- printk(KERN_INFO "ata%u: no device found (phy stat %08x)\n",
+ } else {
+ printk(KERN_INFO "ata%u: SATA link down (SStatus %X)\n",
ap->id, sstatus);
ata_port_disable(ap);
}
@@ -2071,7 +2158,7 @@ static void ata_pr_blacklisted(const struct ata_port *ap,
ap->id, dev->devno);
}
-static const char * ata_dma_blacklist [] = {
+static const char * const ata_dma_blacklist [] = {
"WDC AC11000H",
"WDC AC22100H",
"WDC AC32500H",
@@ -2266,34 +2353,23 @@ static int ata_choose_xfer_mode(const struct ata_port *ap,
static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev)
{
- DECLARE_COMPLETION(wait);
- struct ata_queued_cmd *qc;
- int rc;
- unsigned long flags;
+ struct ata_taskfile tf;
/* set up set-features taskfile */
DPRINTK("set features - xfer mode\n");
- qc = ata_qc_new_init(ap, dev);
- BUG_ON(qc == NULL);
-
- qc->tf.command = ATA_CMD_SET_FEATURES;
- qc->tf.feature = SETFEATURES_XFER;
- qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
- qc->tf.protocol = ATA_PROT_NODATA;
- qc->tf.nsect = dev->xfer_mode;
-
- qc->waiting = &wait;
- qc->complete_fn = ata_qc_complete_noop;
-
- spin_lock_irqsave(&ap->host_set->lock, flags);
- rc = ata_qc_issue(qc);
- spin_unlock_irqrestore(&ap->host_set->lock, flags);
+ ata_tf_init(ap, &tf, dev->devno);
+ tf.command = ATA_CMD_SET_FEATURES;
+ tf.feature = SETFEATURES_XFER;
+ tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ tf.protocol = ATA_PROT_NODATA;
+ tf.nsect = dev->xfer_mode;
- if (rc)
+ if (ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0)) {
+ printk(KERN_ERR "ata%u: failed to set xfermode, disabled\n",
+ ap->id);
ata_port_disable(ap);
- else
- ata_qc_wait_err(qc, &wait);
+ }
DPRINTK("EXIT\n");
}
@@ -2308,41 +2384,25 @@ static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev)
static void ata_dev_reread_id(struct ata_port *ap, struct ata_device *dev)
{
- DECLARE_COMPLETION(wait);
- struct ata_queued_cmd *qc;
- unsigned long flags;
- int rc;
-
- qc = ata_qc_new_init(ap, dev);
- BUG_ON(qc == NULL);
+ struct ata_taskfile tf;
- ata_sg_init_one(qc, dev->id, sizeof(dev->id));
- qc->dma_dir = DMA_FROM_DEVICE;
+ ata_tf_init(ap, &tf, dev->devno);
if (dev->class == ATA_DEV_ATA) {
- qc->tf.command = ATA_CMD_ID_ATA;
+ tf.command = ATA_CMD_ID_ATA;
DPRINTK("do ATA identify\n");
} else {
- qc->tf.command = ATA_CMD_ID_ATAPI;
+ tf.command = ATA_CMD_ID_ATAPI;
DPRINTK("do ATAPI identify\n");
}
- qc->tf.flags |= ATA_TFLAG_DEVICE;
- qc->tf.protocol = ATA_PROT_PIO;
- qc->nsect = 1;
-
- qc->waiting = &wait;
- qc->complete_fn = ata_qc_complete_noop;
+ tf.flags |= ATA_TFLAG_DEVICE;
+ tf.protocol = ATA_PROT_PIO;
- spin_lock_irqsave(&ap->host_set->lock, flags);
- rc = ata_qc_issue(qc);
- spin_unlock_irqrestore(&ap->host_set->lock, flags);
-
- if (rc)
+ if (ata_exec_internal(ap, dev, &tf, DMA_FROM_DEVICE,
+ dev->id, sizeof(dev->id)))
goto err_out;
- ata_qc_wait_err(qc, &wait);
-
swap_buf_le16(dev->id, ATA_ID_WORDS);
ata_dump_id(dev);
@@ -2351,6 +2411,7 @@ static void ata_dev_reread_id(struct ata_port *ap, struct ata_device *dev)
return;
err_out:
+ printk(KERN_ERR "ata%u: failed to reread ID, disabled\n", ap->id);
ata_port_disable(ap);
}
@@ -2364,10 +2425,7 @@ err_out:
static void ata_dev_init_params(struct ata_port *ap, struct ata_device *dev)
{
- DECLARE_COMPLETION(wait);
- struct ata_queued_cmd *qc;
- int rc;
- unsigned long flags;
+ struct ata_taskfile tf;
u16 sectors = dev->id[6];
u16 heads = dev->id[3];
@@ -2378,26 +2436,18 @@ static void ata_dev_init_params(struct ata_port *ap, struct ata_device *dev)
/* set up init dev params taskfile */
DPRINTK("init dev params \n");
- qc = ata_qc_new_init(ap, dev);
- BUG_ON(qc == NULL);
-
- qc->tf.command = ATA_CMD_INIT_DEV_PARAMS;
- qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
- qc->tf.protocol = ATA_PROT_NODATA;
- qc->tf.nsect = sectors;
- qc->tf.device |= (heads - 1) & 0x0f; /* max head = num. of heads - 1 */
-
- qc->waiting = &wait;
- qc->complete_fn = ata_qc_complete_noop;
-
- spin_lock_irqsave(&ap->host_set->lock, flags);
- rc = ata_qc_issue(qc);
- spin_unlock_irqrestore(&ap->host_set->lock, flags);
+ ata_tf_init(ap, &tf, dev->devno);
+ tf.command = ATA_CMD_INIT_DEV_PARAMS;
+ tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ tf.protocol = ATA_PROT_NODATA;
+ tf.nsect = sectors;
+ tf.device |= (heads - 1) & 0x0f; /* max head = num. of heads - 1 */
- if (rc)
+ if (ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0)) {
+ printk(KERN_ERR "ata%u: failed to init parameters, disabled\n",
+ ap->id);
ata_port_disable(ap);
- else
- ata_qc_wait_err(qc, &wait);
+ }
DPRINTK("EXIT\n");
}
@@ -2443,7 +2493,7 @@ static void ata_sg_clean(struct ata_queued_cmd *qc)
struct scatterlist *psg = &qc->pad_sgent;
void *addr = kmap_atomic(psg->page, KM_IRQ0);
memcpy(addr + psg->offset, pad_buf, qc->pad_len);
- kunmap_atomic(psg->page, KM_IRQ0);
+ kunmap_atomic(addr, KM_IRQ0);
}
} else {
if (sg_dma_len(&sg[0]) > 0)
@@ -2717,7 +2767,7 @@ static int ata_sg_setup(struct ata_queued_cmd *qc)
if (qc->tf.flags & ATA_TFLAG_WRITE) {
void *addr = kmap_atomic(psg->page, KM_IRQ0);
memcpy(pad_buf, addr + psg->offset, qc->pad_len);
- kunmap_atomic(psg->page, KM_IRQ0);
+ kunmap_atomic(addr, KM_IRQ0);
}
sg_dma_address(psg) = ap->pad_dma + (qc->tag * ATA_DMA_PAD_SZ);
@@ -2765,7 +2815,7 @@ skip_map:
* None. (grabs host lock)
*/
-void ata_poll_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask)
+void ata_poll_qc_complete(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
unsigned long flags;
@@ -2773,7 +2823,7 @@ void ata_poll_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask)
spin_lock_irqsave(&ap->host_set->lock, flags);
ap->flags &= ~ATA_FLAG_NOINTR;
ata_irq_on(ap);
- ata_qc_complete(qc, err_mask);
+ ata_qc_complete(qc);
spin_unlock_irqrestore(&ap->host_set->lock, flags);
}
@@ -2790,10 +2840,14 @@ void ata_poll_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask)
static unsigned long ata_pio_poll(struct ata_port *ap)
{
+ struct ata_queued_cmd *qc;
u8 status;
unsigned int poll_state = HSM_ST_UNKNOWN;
unsigned int reg_state = HSM_ST_UNKNOWN;
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ assert(qc != NULL);
+
switch (ap->hsm_task_state) {
case HSM_ST:
case HSM_ST_POLL:
@@ -2813,6 +2867,7 @@ static unsigned long ata_pio_poll(struct ata_port *ap)
status = ata_chk_status(ap);
if (status & ATA_BUSY) {
if (time_after(jiffies, ap->pio_task_timeout)) {
+ qc->err_mask |= AC_ERR_ATA_BUS;
ap->hsm_task_state = HSM_ST_TMOUT;
return 0;
}
@@ -2847,29 +2902,31 @@ static int ata_pio_complete (struct ata_port *ap)
* msecs, then chk-status again. If still busy, fall back to
* HSM_ST_POLL state.
*/
- drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 10);
- if (drv_stat & (ATA_BUSY | ATA_DRQ)) {
+ drv_stat = ata_busy_wait(ap, ATA_BUSY, 10);
+ if (drv_stat & ATA_BUSY) {
msleep(2);
- drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 10);
- if (drv_stat & (ATA_BUSY | ATA_DRQ)) {
+ drv_stat = ata_busy_wait(ap, ATA_BUSY, 10);
+ if (drv_stat & ATA_BUSY) {
ap->hsm_task_state = HSM_ST_LAST_POLL;
ap->pio_task_timeout = jiffies + ATA_TMOUT_PIO;
return 0;
}
}
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ assert(qc != NULL);
+
drv_stat = ata_wait_idle(ap);
if (!ata_ok(drv_stat)) {
+ qc->err_mask |= __ac_err_mask(drv_stat);
ap->hsm_task_state = HSM_ST_ERR;
return 0;
}
- qc = ata_qc_from_tag(ap, ap->active_tag);
- assert(qc != NULL);
-
ap->hsm_task_state = HSM_ST_IDLE;
- ata_poll_qc_complete(qc, 0);
+ assert(qc->err_mask == 0);
+ ata_poll_qc_complete(qc);
/* another command may start at this point */
@@ -3177,6 +3234,7 @@ static void atapi_pio_bytes(struct ata_queued_cmd *qc)
err_out:
printk(KERN_INFO "ata%u: dev %u: ATAPI check failed\n",
ap->id, dev->devno);
+ qc->err_mask |= AC_ERR_ATA_BUS;
ap->hsm_task_state = HSM_ST_ERR;
}
@@ -3215,8 +3273,16 @@ static void ata_pio_block(struct ata_port *ap)
qc = ata_qc_from_tag(ap, ap->active_tag);
assert(qc != NULL);
+ /* check error */
+ if (status & (ATA_ERR | ATA_DF)) {
+ qc->err_mask |= AC_ERR_DEV;
+ ap->hsm_task_state = HSM_ST_ERR;
+ return;
+ }
+
+ /* transfer data if any */
if (is_atapi_taskfile(&qc->tf)) {
- /* no more data to transfer or unsupported ATAPI command */
+ /* DRQ=0 means no more data to transfer */
if ((status & ATA_DRQ) == 0) {
ap->hsm_task_state = HSM_ST_LAST;
return;
@@ -3226,6 +3292,7 @@ static void ata_pio_block(struct ata_port *ap)
} else {
/* handle BSY=0, DRQ=0 as error */
if ((status & ATA_DRQ) == 0) {
+ qc->err_mask |= AC_ERR_ATA_BUS;
ap->hsm_task_state = HSM_ST_ERR;
return;
}
@@ -3243,9 +3310,14 @@ static void ata_pio_error(struct ata_port *ap)
qc = ata_qc_from_tag(ap, ap->active_tag);
assert(qc != NULL);
+ /* make sure qc->err_mask is available to
+ * know what's wrong and recover
+ */
+ assert(qc->err_mask);
+
ap->hsm_task_state = HSM_ST_IDLE;
- ata_poll_qc_complete(qc, AC_ERR_ATA_BUS);
+ ata_poll_qc_complete(qc);
}
static void ata_pio_task(void *_data)
@@ -3347,7 +3419,8 @@ static void ata_qc_timeout(struct ata_queued_cmd *qc)
ap->id, qc->tf.command, drv_stat, host_stat);
/* complete taskfile transaction */
- ata_qc_complete(qc, ac_err_mask(drv_stat));
+ qc->err_mask |= ac_err_mask(drv_stat);
+ ata_qc_complete(qc);
break;
}
@@ -3446,15 +3519,10 @@ struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
return qc;
}
-int ata_qc_complete_noop(struct ata_queued_cmd *qc, unsigned int err_mask)
-{
- return 0;
-}
-
static void __ata_qc_complete(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
- unsigned int tag, do_clear = 0;
+ unsigned int tag;
qc->flags = 0;
tag = qc->tag;
@@ -3462,17 +3530,8 @@ static void __ata_qc_complete(struct ata_queued_cmd *qc)
if (tag == ap->active_tag)
ap->active_tag = ATA_TAG_POISON;
qc->tag = ATA_TAG_POISON;
- do_clear = 1;
- }
-
- if (qc->waiting) {
- struct completion *waiting = qc->waiting;
- qc->waiting = NULL;
- complete(waiting);
- }
-
- if (likely(do_clear))
clear_bit(tag, &ap->qactive);
+ }
}
/**
@@ -3488,7 +3547,6 @@ static void __ata_qc_complete(struct ata_queued_cmd *qc)
void ata_qc_free(struct ata_queued_cmd *qc)
{
assert(qc != NULL); /* ata_qc_from_tag _might_ return NULL */
- assert(qc->waiting == NULL); /* nothing should be waiting */
__ata_qc_complete(qc);
}
@@ -3505,7 +3563,7 @@ void ata_qc_free(struct ata_queued_cmd *qc)
* spin_lock_irqsave(host_set lock)
*/
-void ata_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask)
+void ata_qc_complete(struct ata_queued_cmd *qc)
{
int rc;
@@ -3522,7 +3580,7 @@ void ata_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask)
qc->flags &= ~ATA_QCFLAG_ACTIVE;
/* call completion callback */
- rc = qc->complete_fn(qc, err_mask);
+ rc = qc->complete_fn(qc);
/* if callback indicates not to complete command (non-zero),
* return immediately
@@ -3960,7 +4018,8 @@ inline unsigned int ata_host_intr (struct ata_port *ap,
ap->ops->irq_clear(ap);
/* complete taskfile transaction */
- ata_qc_complete(qc, ac_err_mask(status));
+ qc->err_mask |= ac_err_mask(status);
+ ata_qc_complete(qc);
break;
default:
@@ -4054,13 +4113,17 @@ static void atapi_packet_task(void *_data)
/* sleep-wait for BSY to clear */
DPRINTK("busy wait\n");
- if (ata_busy_sleep(ap, ATA_TMOUT_CDB_QUICK, ATA_TMOUT_CDB))
- goto err_out_status;
+ if (ata_busy_sleep(ap, ATA_TMOUT_CDB_QUICK, ATA_TMOUT_CDB)) {
+ qc->err_mask |= AC_ERR_ATA_BUS;
+ goto err_out;
+ }
/* make sure DRQ is set */
status = ata_chk_status(ap);
- if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ)
+ if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ) {
+ qc->err_mask |= AC_ERR_ATA_BUS;
goto err_out;
+ }
/* send SCSI cdb */
DPRINTK("send cdb\n");
@@ -4092,10 +4155,8 @@ static void atapi_packet_task(void *_data)
return;
-err_out_status:
- status = ata_chk_status(ap);
err_out:
- ata_poll_qc_complete(qc, __ac_err_mask(status));
+ ata_poll_qc_complete(qc);
}
@@ -4112,6 +4173,96 @@ err_out:
* Inherited from caller.
*/
+/*
+ * Execute a 'simple' command, that only consists of the opcode 'cmd' itself,
+ * without filling any other registers
+ */
+static int ata_do_simple_cmd(struct ata_port *ap, struct ata_device *dev,
+ u8 cmd)
+{
+ struct ata_taskfile tf;
+ int err;
+
+ ata_tf_init(ap, &tf, dev->devno);
+
+ tf.command = cmd;
+ tf.flags |= ATA_TFLAG_DEVICE;
+ tf.protocol = ATA_PROT_NODATA;
+
+ err = ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0);
+ if (err)
+ printk(KERN_ERR "%s: ata command failed: %d\n",
+ __FUNCTION__, err);
+
+ return err;
+}
+
+static int ata_flush_cache(struct ata_port *ap, struct ata_device *dev)
+{
+ u8 cmd;
+
+ if (!ata_try_flush_cache(dev))
+ return 0;
+
+ if (ata_id_has_flush_ext(dev->id))
+ cmd = ATA_CMD_FLUSH_EXT;
+ else
+ cmd = ATA_CMD_FLUSH;
+
+ return ata_do_simple_cmd(ap, dev, cmd);
+}
+
+static int ata_standby_drive(struct ata_port *ap, struct ata_device *dev)
+{
+ return ata_do_simple_cmd(ap, dev, ATA_CMD_STANDBYNOW1);
+}
+
+static int ata_start_drive(struct ata_port *ap, struct ata_device *dev)
+{
+ return ata_do_simple_cmd(ap, dev, ATA_CMD_IDLEIMMEDIATE);
+}
+
+/**
+ * ata_device_resume - wakeup a previously suspended devices
+ *
+ * Kick the drive back into action, by sending it an idle immediate
+ * command and making sure its transfer mode matches between drive
+ * and host.
+ *
+ */
+int ata_device_resume(struct ata_port *ap, struct ata_device *dev)
+{
+ if (ap->flags & ATA_FLAG_SUSPENDED) {
+ ap->flags &= ~ATA_FLAG_SUSPENDED;
+ ata_set_mode(ap);
+ }
+ if (!ata_dev_present(dev))
+ return 0;
+ if (dev->class == ATA_DEV_ATA)
+ ata_start_drive(ap, dev);
+
+ return 0;
+}
+
+/**
+ * ata_device_suspend - prepare a device for suspend
+ *
+ * Flush the cache on the drive, if appropriate, then issue a
+ * standbynow command.
+ *
+ */
+int ata_device_suspend(struct ata_port *ap, struct ata_device *dev)
+{
+ if (!ata_dev_present(dev))
+ return 0;
+ if (dev->class == ATA_DEV_ATA)
+ ata_flush_cache(ap, dev);
+
+ ata_standby_drive(ap, dev);
+ ap->flags |= ATA_FLAG_SUSPENDED;
+ return 0;
+}
+
int ata_port_start (struct ata_port *ap)
{
struct device *dev = ap->host_set->dev;
@@ -4860,6 +5011,23 @@ int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits)
return (tmp == bits->val) ? 1 : 0;
}
+
+int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+ return 0;
+}
+
+int ata_pci_device_resume(struct pci_dev *pdev)
+{
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ pci_enable_device(pdev);
+ pci_set_master(pdev);
+ return 0;
+}
#endif /* CONFIG_PCI */
@@ -4963,4 +5131,11 @@ EXPORT_SYMBOL_GPL(ata_pci_host_stop);
EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
EXPORT_SYMBOL_GPL(ata_pci_init_one);
EXPORT_SYMBOL_GPL(ata_pci_remove_one);
+EXPORT_SYMBOL_GPL(ata_pci_device_suspend);
+EXPORT_SYMBOL_GPL(ata_pci_device_resume);
#endif /* CONFIG_PCI */
+
+EXPORT_SYMBOL_GPL(ata_device_suspend);
+EXPORT_SYMBOL_GPL(ata_device_resume);
+EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
+EXPORT_SYMBOL_GPL(ata_scsi_device_resume);