summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ahci.c
diff options
context:
space:
mode:
authorJaroslav Kysela <perex@suse.cz>2006-03-22 11:02:08 +0100
committerJaroslav Kysela <perex@suse.cz>2006-03-22 11:02:08 +0100
commit5501972e0b5857bc8354770d900ceb9b40c7f6b7 (patch)
treeff239422827c4cd54d2998f8851304255de31b38 /drivers/scsi/ahci.c
parent9d2f928ddf64ca0361562e30faf584cd33055c60 (diff)
parente952f31bce6e9f64db01f607abc46529ba57ac9e (diff)
downloadop-kernel-dev-5501972e0b5857bc8354770d900ceb9b40c7f6b7.zip
op-kernel-dev-5501972e0b5857bc8354770d900ceb9b40c7f6b7.tar.gz
Merge with rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
Diffstat (limited to 'drivers/scsi/ahci.c')
-rw-r--r--drivers/scsi/ahci.c197
1 files changed, 117 insertions, 80 deletions
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 559ff7a..e97ab3e 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -66,6 +66,9 @@ enum {
AHCI_IRQ_ON_SG = (1 << 31),
AHCI_CMD_ATAPI = (1 << 5),
AHCI_CMD_WRITE = (1 << 6),
+ AHCI_CMD_PREFETCH = (1 << 7),
+ AHCI_CMD_RESET = (1 << 8),
+ AHCI_CMD_CLR_BUSY = (1 << 10),
RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */
@@ -85,6 +88,7 @@ enum {
/* HOST_CAP bits */
HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */
+ HOST_CAP_CLO = (1 << 24), /* Command List Override support */
/* registers for each SATA port */
PORT_LST_ADDR = 0x00, /* command list DMA addr */
@@ -138,6 +142,7 @@ enum {
PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */
PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */
PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */
+ PORT_CMD_CLO = (1 << 3), /* Command list override */
PORT_CMD_POWER_ON = (1 << 2), /* Power up device */
PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */
PORT_CMD_START = (1 << 0), /* Enable port DMA engine */
@@ -184,9 +189,9 @@ struct ahci_port_priv {
static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
-static int ahci_qc_issue(struct ata_queued_cmd *qc);
+static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc);
static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs);
-static void ahci_phy_reset(struct ata_port *ap);
+static int ahci_probe_reset(struct ata_port *ap, unsigned int *classes);
static void ahci_irq_clear(struct ata_port *ap);
static void ahci_eng_timeout(struct ata_port *ap);
static int ahci_port_start(struct ata_port *ap);
@@ -202,11 +207,11 @@ static struct scsi_host_template ahci_sht = {
.name = DRV_NAME,
.ioctl = ata_scsi_ioctl,
.queuecommand = ata_scsi_queuecmd,
+ .eh_timed_out = ata_scsi_timed_out,
.eh_strategy_handler = ata_scsi_error,
.can_queue = ATA_DEF_QUEUE,
.this_id = ATA_SHT_THIS_ID,
.sg_tablesize = AHCI_MAX_SG,
- .max_sectors = ATA_MAX_SECTORS,
.cmd_per_lun = ATA_SHT_CMD_PER_LUN,
.emulated = ATA_SHT_EMULATED,
.use_clustering = AHCI_USE_CLUSTERING,
@@ -225,7 +230,7 @@ static const struct ata_port_operations ahci_ops = {
.tf_read = ahci_tf_read,
- .phy_reset = ahci_phy_reset,
+ .probe_reset = ahci_probe_reset,
.qc_prep = ahci_qc_prep,
.qc_issue = ahci_qc_issue,
@@ -247,8 +252,7 @@ static const struct ata_port_info ahci_port_info[] = {
{
.sht = &ahci_sht,
.host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
- ATA_FLAG_PIO_DMA,
+ ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA,
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
.port_ops = &ahci_ops,
@@ -450,17 +454,48 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in,
writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4));
}
-static void ahci_phy_reset(struct ata_port *ap)
+static int ahci_stop_engine(struct ata_port *ap)
{
- void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
- struct ata_taskfile tf;
- struct ata_device *dev = &ap->device[0];
- u32 new_tmp, tmp;
+ void __iomem *mmio = ap->host_set->mmio_base;
+ void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+ int work;
+ u32 tmp;
- __sata_phy_reset(ap);
+ tmp = readl(port_mmio + PORT_CMD);
+ tmp &= ~PORT_CMD_START;
+ writel(tmp, port_mmio + PORT_CMD);
- if (ap->flags & ATA_FLAG_PORT_DISABLED)
- return;
+ /* wait for engine to stop. TODO: this could be
+ * as long as 500 msec
+ */
+ work = 1000;
+ while (work-- > 0) {
+ tmp = readl(port_mmio + PORT_CMD);
+ if ((tmp & PORT_CMD_LIST_ON) == 0)
+ return 0;
+ udelay(10);
+ }
+
+ return -EIO;
+}
+
+static void ahci_start_engine(struct ata_port *ap)
+{
+ void __iomem *mmio = ap->host_set->mmio_base;
+ void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+ u32 tmp;
+
+ tmp = readl(port_mmio + PORT_CMD);
+ tmp |= PORT_CMD_START;
+ writel(tmp, port_mmio + PORT_CMD);
+ readl(port_mmio + PORT_CMD); /* flush */
+}
+
+static unsigned int ahci_dev_classify(struct ata_port *ap)
+{
+ void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+ struct ata_taskfile tf;
+ u32 tmp;
tmp = readl(port_mmio + PORT_SIG);
tf.lbah = (tmp >> 24) & 0xff;
@@ -468,15 +503,46 @@ static void ahci_phy_reset(struct ata_port *ap)
tf.lbal = (tmp >> 8) & 0xff;
tf.nsect = (tmp) & 0xff;
- dev->class = ata_dev_classify(&tf);
- if (!ata_dev_present(dev)) {
- ata_port_disable(ap);
- return;
- }
+ return ata_dev_classify(&tf);
+}
+
+static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, u32 opts)
+{
+ pp->cmd_slot[0].opts = cpu_to_le32(opts);
+ pp->cmd_slot[0].status = 0;
+ pp->cmd_slot[0].tbl_addr = cpu_to_le32(pp->cmd_tbl_dma & 0xffffffff);
+ pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16);
+}
+
+static int ahci_hardreset(struct ata_port *ap, int verbose, unsigned int *class)
+{
+ int rc;
+
+ DPRINTK("ENTER\n");
+
+ ahci_stop_engine(ap);
+ rc = sata_std_hardreset(ap, verbose, class);
+ ahci_start_engine(ap);
+
+ if (rc == 0)
+ *class = ahci_dev_classify(ap);
+ if (*class == ATA_DEV_UNKNOWN)
+ *class = ATA_DEV_NONE;
+
+ DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
+ return rc;
+}
+
+static void ahci_postreset(struct ata_port *ap, unsigned int *class)
+{
+ void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+ u32 new_tmp, tmp;
+
+ ata_std_postreset(ap, class);
/* Make sure port's ATAPI bit is set appropriately */
new_tmp = tmp = readl(port_mmio + PORT_CMD);
- if (dev->class == ATA_DEV_ATAPI)
+ if (*class == ATA_DEV_ATAPI)
new_tmp |= PORT_CMD_ATAPI;
else
new_tmp &= ~PORT_CMD_ATAPI;
@@ -486,6 +552,12 @@ static void ahci_phy_reset(struct ata_port *ap)
}
}
+static int ahci_probe_reset(struct ata_port *ap, unsigned int *classes)
+{
+ return ata_drive_probe_reset(ap, NULL, NULL, ahci_hardreset,
+ ahci_postreset, classes);
+}
+
static u8 ahci_check_status(struct ata_port *ap)
{
void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr;
@@ -533,42 +605,36 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct ahci_port_priv *pp = ap->private_data;
+ int is_atapi = is_atapi_taskfile(&qc->tf);
u32 opts;
const u32 cmd_fis_len = 5; /* five dwords */
unsigned int n_elem;
/*
- * Fill in command slot information (currently only one slot,
- * slot 0, is currently since we don't do queueing)
- */
-
- opts = cmd_fis_len;
- if (qc->tf.flags & ATA_TFLAG_WRITE)
- opts |= AHCI_CMD_WRITE;
- if (is_atapi_taskfile(&qc->tf))
- opts |= AHCI_CMD_ATAPI;
-
- pp->cmd_slot[0].opts = cpu_to_le32(opts);
- pp->cmd_slot[0].status = 0;
- pp->cmd_slot[0].tbl_addr = cpu_to_le32(pp->cmd_tbl_dma & 0xffffffff);
- pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16);
-
- /*
* Fill in command table information. First, the header,
* a SATA Register - Host to Device command FIS.
*/
ata_tf_to_fis(&qc->tf, pp->cmd_tbl, 0);
- if (opts & AHCI_CMD_ATAPI) {
+ if (is_atapi) {
memset(pp->cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32);
- memcpy(pp->cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, ap->cdb_len);
+ memcpy(pp->cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb,
+ qc->dev->cdb_len);
}
- if (!(qc->flags & ATA_QCFLAG_DMAMAP))
- return;
+ n_elem = 0;
+ if (qc->flags & ATA_QCFLAG_DMAMAP)
+ n_elem = ahci_fill_sg(qc);
- n_elem = ahci_fill_sg(qc);
+ /*
+ * Fill in command slot information.
+ */
+ opts = cmd_fis_len | n_elem << 16;
+ if (qc->tf.flags & ATA_TFLAG_WRITE)
+ opts |= AHCI_CMD_WRITE;
+ if (is_atapi)
+ opts |= AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH;
- pp->cmd_slot[0].opts |= cpu_to_le32(n_elem << 16);
+ ahci_fill_cmd_slot(pp, opts);
}
static void ahci_restart_port(struct ata_port *ap, u32 irq_stat)
@@ -576,7 +642,6 @@ static void ahci_restart_port(struct ata_port *ap, u32 irq_stat)
void __iomem *mmio = ap->host_set->mmio_base;
void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
u32 tmp;
- int work;
if ((ap->device[0].class != ATA_DEV_ATAPI) ||
((irq_stat & PORT_IRQ_TF_ERR) == 0))
@@ -592,20 +657,7 @@ static void ahci_restart_port(struct ata_port *ap, u32 irq_stat)
readl(port_mmio + PORT_SCR_ERR));
/* stop DMA */
- tmp = readl(port_mmio + PORT_CMD);
- tmp &= ~PORT_CMD_START;
- writel(tmp, port_mmio + PORT_CMD);
-
- /* wait for engine to stop. TODO: this could be
- * as long as 500 msec
- */
- work = 1000;
- while (work-- > 0) {
- tmp = readl(port_mmio + PORT_CMD);
- if ((tmp & PORT_CMD_LIST_ON) == 0)
- break;
- udelay(10);
- }
+ ahci_stop_engine(ap);
/* clear SATA phy error, if any */
tmp = readl(port_mmio + PORT_SCR_ERR);
@@ -624,10 +676,7 @@ static void ahci_restart_port(struct ata_port *ap, u32 irq_stat)
}
/* re-start DMA */
- tmp = readl(port_mmio + PORT_CMD);
- tmp |= PORT_CMD_START;
- writel(tmp, port_mmio + PORT_CMD);
- readl(port_mmio + PORT_CMD); /* flush */
+ ahci_start_engine(ap);
}
static void ahci_eng_timeout(struct ata_port *ap)
@@ -642,25 +691,13 @@ static void ahci_eng_timeout(struct ata_port *ap)
spin_lock_irqsave(&host_set->lock, flags);
+ ahci_restart_port(ap, readl(port_mmio + PORT_IRQ_STAT));
qc = ata_qc_from_tag(ap, ap->active_tag);
- if (!qc) {
- printk(KERN_ERR "ata%u: BUG: timeout without command\n",
- ap->id);
- } else {
- ahci_restart_port(ap, readl(port_mmio + PORT_IRQ_STAT));
-
- /* hack alert! We cannot use the supplied completion
- * function from inside the ->eh_strategy_handler() thread.
- * libata is the only user of ->eh_strategy_handler() in
- * any kernel, so the default scsi_done() assumes it is
- * not being called from the SCSI EH.
- */
- qc->scsidone = scsi_finish_command;
- qc->err_mask |= AC_ERR_OTHER;
- ata_qc_complete(qc);
- }
+ qc->err_mask |= AC_ERR_TIMEOUT;
spin_unlock_irqrestore(&host_set->lock, flags);
+
+ ata_eh_qc_complete(qc);
}
static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
@@ -678,7 +715,7 @@ static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
ci = readl(port_mmio + PORT_CMD_ISSUE);
if (likely((ci & 0x1) == 0)) {
if (qc) {
- assert(qc->err_mask == 0);
+ WARN_ON(qc->err_mask);
ata_qc_complete(qc);
qc = NULL;
}
@@ -697,7 +734,7 @@ static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
ahci_restart_port(ap, status);
if (qc) {
- qc->err_mask |= AC_ERR_OTHER;
+ qc->err_mask |= err_mask;
ata_qc_complete(qc);
}
}
@@ -770,7 +807,7 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *
return IRQ_RETVAL(handled);
}
-static int ahci_qc_issue(struct ata_queued_cmd *qc)
+static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
OpenPOWER on IntegriCloud