Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 29426
b: refs/heads/master
c: 12436c3
h: refs/heads/master
v: v3
  • Loading branch information
Tejun Heo committed May 15, 2006
1 parent a38e301 commit 8c0d48e
Show file tree
Hide file tree
Showing 22 changed files with 2,609 additions and 753 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 7894eaf291238a62a565e9e9777483beeb00eeae
refs/heads/master: 12436c30f4808e00fa008c6787c609bc6ae216ba
256 changes: 151 additions & 105 deletions trunk/drivers/scsi/ahci.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ enum {
AHCI_CMD_CLR_BUSY = (1 << 10),

RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */
RX_FIS_UNK = 0x60, /* offset of Unknown FIS data */

board_ahci = 0,
board_ahci_vt8251 = 1,
Expand Down Expand Up @@ -128,15 +129,16 @@ enum {
PORT_IRQ_PIOS_FIS = (1 << 1), /* PIO Setup FIS rx'd */
PORT_IRQ_D2H_REG_FIS = (1 << 0), /* D2H Register FIS rx'd */

PORT_IRQ_FATAL = PORT_IRQ_TF_ERR |
PORT_IRQ_HBUS_ERR |
PORT_IRQ_HBUS_DATA_ERR |
PORT_IRQ_IF_ERR,
DEF_PORT_IRQ = PORT_IRQ_FATAL | PORT_IRQ_PHYRDY |
PORT_IRQ_CONNECT | PORT_IRQ_SG_DONE |
PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_FIS |
PORT_IRQ_DMAS_FIS | PORT_IRQ_PIOS_FIS |
PORT_IRQ_D2H_REG_FIS,
PORT_IRQ_FREEZE = PORT_IRQ_HBUS_ERR |
PORT_IRQ_IF_ERR |
PORT_IRQ_CONNECT |
PORT_IRQ_UNK_FIS,
PORT_IRQ_ERROR = PORT_IRQ_FREEZE |
PORT_IRQ_TF_ERR |
PORT_IRQ_HBUS_DATA_ERR,
DEF_PORT_IRQ = PORT_IRQ_ERROR | PORT_IRQ_SG_DONE |
PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS |
PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS,

/* PORT_CMD bits */
PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */
Expand Down Expand Up @@ -197,13 +199,15 @@ 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 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);
static void ahci_port_stop(struct ata_port *ap);
static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
static void ahci_qc_prep(struct ata_queued_cmd *qc);
static u8 ahci_check_status(struct ata_port *ap);
static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
static void ahci_freeze(struct ata_port *ap);
static void ahci_thaw(struct ata_port *ap);
static void ahci_error_handler(struct ata_port *ap);
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
static void ahci_remove_one (struct pci_dev *pdev);

static struct scsi_host_template ahci_sht = {
Expand Down Expand Up @@ -237,14 +241,18 @@ static const struct ata_port_operations ahci_ops = {
.qc_prep = ahci_qc_prep,
.qc_issue = ahci_qc_issue,

.eng_timeout = ahci_eng_timeout,

.irq_handler = ahci_interrupt,
.irq_clear = ahci_irq_clear,

.scr_read = ahci_scr_read,
.scr_write = ahci_scr_write,

.freeze = ahci_freeze,
.thaw = ahci_thaw,

.error_handler = ahci_error_handler,
.post_internal_cmd = ahci_post_internal_cmd,

.port_start = ahci_port_start,
.port_stop = ahci_port_stop,
};
Expand Down Expand Up @@ -567,7 +575,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class)

DPRINTK("ENTER\n");

if (!sata_dev_present(ap)) {
if (ata_port_offline(ap)) {
DPRINTK("PHY reports no device\n");
*class = ATA_DEV_NONE;
return 0;
Expand Down Expand Up @@ -597,7 +605,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class)
/* restart engine */
ahci_start_engine(ap);

ata_tf_init(ap, &tf, 0);
ata_tf_init(ap->device, &tf);
fis = pp->cmd_tbl;

/* issue the first D2H Register FIS */
Expand Down Expand Up @@ -640,7 +648,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class)
msleep(150);

*class = ATA_DEV_NONE;
if (sata_dev_present(ap)) {
if (ata_port_online(ap)) {
if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) {
rc = -EIO;
reason = "device not ready";
Expand All @@ -655,8 +663,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class)
fail_restart:
ahci_start_engine(ap);
fail:
printk(KERN_ERR "ata%u: softreset failed (%s)\n",
ap->id, reason);
ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason);
return rc;
}

Expand All @@ -670,7 +677,7 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class)
rc = sata_std_hardreset(ap, class);
ahci_start_engine(ap);

if (rc == 0)
if (rc == 0 && ata_port_online(ap))
*class = ahci_dev_classify(ap);
if (*class == ATA_DEV_UNKNOWN)
*class = ATA_DEV_NONE;
Expand Down Expand Up @@ -790,109 +797,108 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)
ahci_fill_cmd_slot(pp, opts);
}

static void ahci_restart_port(struct ata_port *ap, u32 irq_stat)
static void ahci_error_intr(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;
struct ahci_port_priv *pp = ap->private_data;
struct ata_eh_info *ehi = &ap->eh_info;
unsigned int err_mask = 0, action = 0;
struct ata_queued_cmd *qc;
u32 serror;

if ((ap->device[0].class != ATA_DEV_ATAPI) ||
((irq_stat & PORT_IRQ_TF_ERR) == 0))
printk(KERN_WARNING "ata%u: port reset, "
"p_is %x is %x pis %x cmd %x tf %x ss %x se %x\n",
ap->id,
irq_stat,
readl(mmio + HOST_IRQ_STAT),
readl(port_mmio + PORT_IRQ_STAT),
readl(port_mmio + PORT_CMD),
readl(port_mmio + PORT_TFDATA),
readl(port_mmio + PORT_SCR_STAT),
readl(port_mmio + PORT_SCR_ERR));

/* stop DMA */
ahci_stop_engine(ap);
ata_ehi_clear_desc(ehi);

/* clear SATA phy error, if any */
tmp = readl(port_mmio + PORT_SCR_ERR);
writel(tmp, port_mmio + PORT_SCR_ERR);
/* AHCI needs SError cleared; otherwise, it might lock up */
serror = ahci_scr_read(ap, SCR_ERROR);
ahci_scr_write(ap, SCR_ERROR, serror);

/* if DRQ/BSY is set, device needs to be reset.
* if so, issue COMRESET
*/
tmp = readl(port_mmio + PORT_TFDATA);
if (tmp & (ATA_BUSY | ATA_DRQ)) {
writel(0x301, port_mmio + PORT_SCR_CTL);
readl(port_mmio + PORT_SCR_CTL); /* flush */
udelay(10);
writel(0x300, port_mmio + PORT_SCR_CTL);
readl(port_mmio + PORT_SCR_CTL); /* flush */
/* analyze @irq_stat */
ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);

if (irq_stat & PORT_IRQ_TF_ERR)
err_mask |= AC_ERR_DEV;

if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) {
err_mask |= AC_ERR_HOST_BUS;
action |= ATA_EH_SOFTRESET;
}

/* re-start DMA */
ahci_start_engine(ap);
}
if (irq_stat & PORT_IRQ_IF_ERR) {
err_mask |= AC_ERR_ATA_BUS;
action |= ATA_EH_SOFTRESET;
ata_ehi_push_desc(ehi, ", interface fatal error");
}

static void ahci_eng_timeout(struct ata_port *ap)
{
struct ata_host_set *host_set = ap->host_set;
void __iomem *mmio = host_set->mmio_base;
void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
struct ata_queued_cmd *qc;
unsigned long flags;
if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) {
err_mask |= AC_ERR_ATA_BUS;
action |= ATA_EH_SOFTRESET;
ata_ehi_push_desc(ehi, ", %s", irq_stat & PORT_IRQ_CONNECT ?
"connection status changed" : "PHY RDY changed");
}

if (irq_stat & PORT_IRQ_UNK_FIS) {
u32 *unk = (u32 *)(pp->rx_fis + RX_FIS_UNK);

printk(KERN_WARNING "ata%u: handling error/timeout\n", ap->id);
err_mask |= AC_ERR_HSM;
action |= ATA_EH_SOFTRESET;
ata_ehi_push_desc(ehi, ", unknown FIS %08x %08x %08x %08x",
unk[0], unk[1], unk[2], unk[3]);
}

spin_lock_irqsave(&host_set->lock, flags);
/* okay, let's hand over to EH */
ehi->serror |= serror;
ehi->action |= action;

ahci_restart_port(ap, readl(port_mmio + PORT_IRQ_STAT));
qc = ata_qc_from_tag(ap, ap->active_tag);
qc->err_mask |= AC_ERR_TIMEOUT;

spin_unlock_irqrestore(&host_set->lock, flags);
if (qc)
qc->err_mask |= err_mask;
else
ehi->err_mask |= err_mask;

ata_eh_qc_complete(qc);
if (irq_stat & PORT_IRQ_FREEZE)
ata_port_freeze(ap);
else
ata_port_abort(ap);
}

static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
static void ahci_host_intr(struct ata_port *ap)
{
void __iomem *mmio = ap->host_set->mmio_base;
void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
u32 status, serr, ci;

serr = readl(port_mmio + PORT_SCR_ERR);
writel(serr, port_mmio + PORT_SCR_ERR);
struct ata_queued_cmd *qc;
u32 status, ci;

status = readl(port_mmio + PORT_IRQ_STAT);
writel(status, port_mmio + PORT_IRQ_STAT);

ci = readl(port_mmio + PORT_CMD_ISSUE);
if (likely((ci & 0x1) == 0)) {
if (qc) {
WARN_ON(qc->err_mask);
if (unlikely(status & PORT_IRQ_ERROR)) {
ahci_error_intr(ap, status);
return;
}

if ((qc = ata_qc_from_tag(ap, ap->active_tag))) {
ci = readl(port_mmio + PORT_CMD_ISSUE);
if ((ci & 0x1) == 0) {
ata_qc_complete(qc);
qc = NULL;
return;
}
}

if (status & PORT_IRQ_FATAL) {
unsigned int err_mask;
if (status & PORT_IRQ_TF_ERR)
err_mask = AC_ERR_DEV;
else if (status & PORT_IRQ_IF_ERR)
err_mask = AC_ERR_ATA_BUS;
else
err_mask = AC_ERR_HOST_BUS;
/* hmmm... a spurious interupt */

/* command processing has stopped due to error; restart */
ahci_restart_port(ap, status);
/* ignore interim PIO setup fis interrupts */
if (ata_tag_valid(ap->active_tag)) {
struct ata_queued_cmd *qc =
ata_qc_from_tag(ap, ap->active_tag);

if (qc) {
qc->err_mask |= err_mask;
ata_qc_complete(qc);
}
if (qc && qc->tf.protocol == ATA_PROT_PIO &&
(status & PORT_IRQ_PIOS_FIS))
return;
}

return 1;
if (ata_ratelimit())
ata_port_printk(ap, KERN_INFO, "spurious interrupt "
"(irq_stat 0x%x active_tag %d)\n",
status, ap->active_tag);
}

static void ahci_irq_clear(struct ata_port *ap)
Expand Down Expand Up @@ -929,14 +935,7 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *

ap = host_set->ports[i];
if (ap) {
struct ata_queued_cmd *qc;
qc = ata_qc_from_tag(ap, ap->active_tag);
if (!ahci_host_intr(ap, qc))
if (ata_ratelimit())
dev_printk(KERN_WARNING, host_set->dev,
"unhandled interrupt on port %u\n",
i);

ahci_host_intr(ap);
VPRINTK("port %u\n", i);
} else {
VPRINTK("port %u (no irq)\n", i);
Expand All @@ -953,7 +952,7 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *
handled = 1;
}

spin_unlock(&host_set->lock);
spin_unlock(&host_set->lock);

VPRINTK("EXIT\n");

Expand All @@ -971,6 +970,56 @@ static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
return 0;
}

static void ahci_freeze(struct ata_port *ap)
{
void __iomem *mmio = ap->host_set->mmio_base;
void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);

/* turn IRQ off */
writel(0, port_mmio + PORT_IRQ_MASK);
}

static void ahci_thaw(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;

/* clear IRQ */
tmp = readl(port_mmio + PORT_IRQ_STAT);
writel(tmp, port_mmio + PORT_IRQ_STAT);
writel(1 << ap->id, mmio + HOST_IRQ_STAT);

/* turn IRQ back on */
writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
}

static void ahci_error_handler(struct ata_port *ap)
{
if (!(ap->flags & ATA_FLAG_FROZEN)) {
/* restart engine */
ahci_stop_engine(ap);
ahci_start_engine(ap);
}

/* perform recovery */
ata_do_eh(ap, ahci_softreset, ahci_hardreset, ahci_postreset);
}

static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;

if (qc->flags & ATA_QCFLAG_FAILED)
qc->err_mask |= AC_ERR_OTHER;

if (qc->err_mask) {
/* make DMA engine forget about the failed command */
ahci_stop_engine(ap);
ahci_start_engine(ap);
}
}

static void ahci_setup_port(struct ata_ioports *port, unsigned long base,
unsigned int port_idx)
{
Expand Down Expand Up @@ -1115,9 +1164,6 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
writel(tmp, port_mmio + PORT_IRQ_STAT);

writel(1 << i, mmio + HOST_IRQ_STAT);

/* set irq mask (enables interrupts) */
writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
}

tmp = readl(mmio + HOST_CTL);
Expand Down
Loading

0 comments on commit 8c0d48e

Please sign in to comment.