|
@@ -948,25 +948,49 @@ static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
|
|
pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16);
|
|
pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16);
|
|
}
|
|
}
|
|
|
|
|
|
-static int ahci_clo(struct ata_port *ap)
|
|
|
|
|
|
+static int ahci_kick_engine(struct ata_port *ap, int force_restart)
|
|
{
|
|
{
|
|
void __iomem *port_mmio = ap->ioaddr.cmd_addr;
|
|
void __iomem *port_mmio = ap->ioaddr.cmd_addr;
|
|
struct ahci_host_priv *hpriv = ap->host->private_data;
|
|
struct ahci_host_priv *hpriv = ap->host->private_data;
|
|
u32 tmp;
|
|
u32 tmp;
|
|
|
|
+ int busy, rc;
|
|
|
|
|
|
- if (!(hpriv->cap & HOST_CAP_CLO))
|
|
|
|
- return -EOPNOTSUPP;
|
|
|
|
|
|
+ /* do we need to kick the port? */
|
|
|
|
+ busy = ahci_check_status(ap) & (ATA_BUSY | ATA_DRQ);
|
|
|
|
+ if (!busy && !force_restart)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ /* stop engine */
|
|
|
|
+ rc = ahci_stop_engine(ap);
|
|
|
|
+ if (rc)
|
|
|
|
+ goto out_restart;
|
|
|
|
+
|
|
|
|
+ /* need to do CLO? */
|
|
|
|
+ if (!busy) {
|
|
|
|
+ rc = 0;
|
|
|
|
+ goto out_restart;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!(hpriv->cap & HOST_CAP_CLO)) {
|
|
|
|
+ rc = -EOPNOTSUPP;
|
|
|
|
+ goto out_restart;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ /* perform CLO */
|
|
tmp = readl(port_mmio + PORT_CMD);
|
|
tmp = readl(port_mmio + PORT_CMD);
|
|
tmp |= PORT_CMD_CLO;
|
|
tmp |= PORT_CMD_CLO;
|
|
writel(tmp, port_mmio + PORT_CMD);
|
|
writel(tmp, port_mmio + PORT_CMD);
|
|
|
|
|
|
|
|
+ rc = 0;
|
|
tmp = ata_wait_register(port_mmio + PORT_CMD,
|
|
tmp = ata_wait_register(port_mmio + PORT_CMD,
|
|
PORT_CMD_CLO, PORT_CMD_CLO, 1, 500);
|
|
PORT_CMD_CLO, PORT_CMD_CLO, 1, 500);
|
|
if (tmp & PORT_CMD_CLO)
|
|
if (tmp & PORT_CMD_CLO)
|
|
- return -EIO;
|
|
|
|
|
|
+ rc = -EIO;
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ /* restart engine */
|
|
|
|
+ out_restart:
|
|
|
|
+ ahci_start_engine(ap);
|
|
|
|
+ return rc;
|
|
}
|
|
}
|
|
|
|
|
|
static int ahci_softreset(struct ata_port *ap, unsigned int *class,
|
|
static int ahci_softreset(struct ata_port *ap, unsigned int *class,
|
|
@@ -991,27 +1015,10 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class,
|
|
}
|
|
}
|
|
|
|
|
|
/* prepare for SRST (AHCI-1.1 10.4.1) */
|
|
/* prepare for SRST (AHCI-1.1 10.4.1) */
|
|
- rc = ahci_stop_engine(ap);
|
|
|
|
- if (rc) {
|
|
|
|
- reason = "failed to stop engine";
|
|
|
|
- goto fail_restart;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* check BUSY/DRQ, perform Command List Override if necessary */
|
|
|
|
- if (ahci_check_status(ap) & (ATA_BUSY | ATA_DRQ)) {
|
|
|
|
- rc = ahci_clo(ap);
|
|
|
|
-
|
|
|
|
- if (rc == -EOPNOTSUPP) {
|
|
|
|
- reason = "port busy but CLO unavailable";
|
|
|
|
- goto fail_restart;
|
|
|
|
- } else if (rc) {
|
|
|
|
- reason = "port busy but CLO failed";
|
|
|
|
- goto fail_restart;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* restart engine */
|
|
|
|
- ahci_start_engine(ap);
|
|
|
|
|
|
+ rc = ahci_kick_engine(ap, 1);
|
|
|
|
+ if (rc)
|
|
|
|
+ ata_port_printk(ap, KERN_WARNING,
|
|
|
|
+ "failed to reset engine (errno=%d)", rc);
|
|
|
|
|
|
ata_tf_init(ap->device, &tf);
|
|
ata_tf_init(ap->device, &tf);
|
|
fis = pp->cmd_tbl;
|
|
fis = pp->cmd_tbl;
|
|
@@ -1070,8 +1077,6 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class,
|
|
DPRINTK("EXIT, class=%u\n", *class);
|
|
DPRINTK("EXIT, class=%u\n", *class);
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- fail_restart:
|
|
|
|
- ahci_start_engine(ap);
|
|
|
|
fail:
|
|
fail:
|
|
ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason);
|
|
ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason);
|
|
return rc;
|
|
return rc;
|
|
@@ -1516,11 +1521,9 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
|
|
{
|
|
{
|
|
struct ata_port *ap = qc->ap;
|
|
struct ata_port *ap = qc->ap;
|
|
|
|
|
|
- if (qc->flags & ATA_QCFLAG_FAILED) {
|
|
|
|
- /* make DMA engine forget about the failed command */
|
|
|
|
- ahci_stop_engine(ap);
|
|
|
|
- ahci_start_engine(ap);
|
|
|
|
- }
|
|
|
|
|
|
+ /* make DMA engine forget about the failed command */
|
|
|
|
+ if (qc->flags & ATA_QCFLAG_FAILED)
|
|
|
|
+ ahci_kick_engine(ap, 1);
|
|
}
|
|
}
|
|
|
|
|
|
static int ahci_port_resume(struct ata_port *ap)
|
|
static int ahci_port_resume(struct ata_port *ap)
|