diff options
Diffstat (limited to 'drivers/ata/sata_sil24.c')
-rw-r--r-- | drivers/ata/sata_sil24.c | 139 |
1 files changed, 96 insertions, 43 deletions
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index ac43a30..46fbbe7 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -326,8 +326,8 @@ struct sil24_port_priv { static void sil24_dev_config(struct ata_device *dev); static u8 sil24_check_status(struct ata_port *ap); -static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg); -static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val); +static int sil24_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val); +static int sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val); static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf); static void sil24_qc_prep(struct ata_queued_cmd *qc); static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc); @@ -464,15 +464,15 @@ static void sil24_dev_config(struct ata_device *dev) writel(PORT_CS_CDB16, port + PORT_CTRL_CLR); } -static inline void sil24_update_tf(struct ata_port *ap) +static void sil24_read_tf(struct ata_port *ap, int tag, struct ata_taskfile *tf) { - struct sil24_port_priv *pp = ap->private_data; void __iomem *port = ap->ioaddr.cmd_addr; - struct sil24_prb __iomem *prb = port; + struct sil24_prb __iomem *prb; u8 fis[6 * 4]; - memcpy_fromio(fis, prb->fis, 6 * 4); - ata_tf_from_fis(fis, &pp->tf); + prb = port + PORT_LRAM + sil24_tag(tag) * PORT_LRAM_SLOT_SZ; + memcpy_fromio(fis, prb->fis, sizeof(fis)); + ata_tf_from_fis(fis, tf); } static u8 sil24_check_status(struct ata_port *ap) @@ -488,25 +488,30 @@ static int sil24_scr_map[] = { [SCR_ACTIVE] = 3, }; -static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg) +static int sil24_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val) { void __iomem *scr_addr = ap->ioaddr.scr_addr; + if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { void __iomem *addr; addr = scr_addr + sil24_scr_map[sc_reg] * 4; - return readl(scr_addr + sil24_scr_map[sc_reg] * 4); + *val = readl(scr_addr + sil24_scr_map[sc_reg] * 4); + return 0; } - return 0xffffffffU; + return -EINVAL; } -static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val) +static int sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val) { void __iomem *scr_addr = ap->ioaddr.scr_addr; + if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { void __iomem *addr; addr = scr_addr + sil24_scr_map[sc_reg] * 4; writel(val, scr_addr + sil24_scr_map[sc_reg] * 4); + return 0; } + return -EINVAL; } static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf) @@ -531,15 +536,60 @@ static int sil24_init_port(struct ata_port *ap) return 0; } -static int sil24_softreset(struct ata_port *ap, unsigned int *class, - unsigned long deadline) +static int sil24_exec_polled_cmd(struct ata_port *ap, int pmp, + const struct ata_taskfile *tf, + int is_cmd, u32 ctrl, + unsigned long timeout_msec) { void __iomem *port = ap->ioaddr.cmd_addr; struct sil24_port_priv *pp = ap->private_data; struct sil24_prb *prb = &pp->cmd_block[0].ata.prb; dma_addr_t paddr = pp->cmd_block_dma; - u32 mask, irq_stat; + u32 irq_enabled, irq_mask, irq_stat; + int rc; + + prb->ctrl = cpu_to_le16(ctrl); + ata_tf_to_fis(tf, pmp, is_cmd, prb->fis); + + /* temporarily plug completion and error interrupts */ + irq_enabled = readl(port + PORT_IRQ_ENABLE_SET); + writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR, port + PORT_IRQ_ENABLE_CLR); + + writel((u32)paddr, port + PORT_CMD_ACTIVATE); + writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4); + + irq_mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT; + irq_stat = ata_wait_register(port + PORT_IRQ_STAT, irq_mask, 0x0, + 10, timeout_msec); + + writel(irq_mask, port + PORT_IRQ_STAT); /* clear IRQs */ + irq_stat >>= PORT_IRQ_RAW_SHIFT; + + if (irq_stat & PORT_IRQ_COMPLETE) + rc = 0; + else { + /* force port into known state */ + sil24_init_port(ap); + + if (irq_stat & PORT_IRQ_ERROR) + rc = -EIO; + else + rc = -EBUSY; + } + + /* restore IRQ enabled */ + writel(irq_enabled, port + PORT_IRQ_ENABLE_SET); + + return rc; +} + +static int sil24_do_softreset(struct ata_port *ap, unsigned int *class, + int pmp, unsigned long deadline) +{ + unsigned long timeout_msec = 0; + struct ata_taskfile tf; const char *reason; + int rc; DPRINTK("ENTER\n"); @@ -556,29 +606,22 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class, } /* do SRST */ - prb->ctrl = cpu_to_le16(PRB_CTRL_SRST); - prb->fis[1] = 0; /* no PMP yet */ - - writel((u32)paddr, port + PORT_CMD_ACTIVATE); - writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4); - - mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT; - irq_stat = ata_wait_register(port + PORT_IRQ_STAT, mask, 0x0, - 100, jiffies_to_msecs(deadline - jiffies)); - - writel(irq_stat, port + PORT_IRQ_STAT); /* clear IRQs */ - irq_stat >>= PORT_IRQ_RAW_SHIFT; - - if (!(irq_stat & PORT_IRQ_COMPLETE)) { - if (irq_stat & PORT_IRQ_ERROR) - reason = "SRST command error"; - else - reason = "timeout"; + if (time_after(deadline, jiffies)) + timeout_msec = jiffies_to_msecs(deadline - jiffies); + + ata_tf_init(ap->device, &tf); /* doesn't really matter */ + rc = sil24_exec_polled_cmd(ap, pmp, &tf, 0, PRB_CTRL_SRST, + timeout_msec); + if (rc == -EBUSY) { + reason = "timeout"; + goto err; + } else if (rc) { + reason = "SRST command error"; goto err; } - sil24_update_tf(ap); - *class = ata_dev_classify(&pp->tf); + sil24_read_tf(ap, 0, &tf); + *class = ata_dev_classify(&tf); if (*class == ATA_DEV_UNKNOWN) *class = ATA_DEV_NONE; @@ -592,6 +635,12 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class, return -EIO; } +static int sil24_softreset(struct ata_port *ap, unsigned int *class, + unsigned long deadline) +{ + return sil24_do_softreset(ap, class, 0, deadline); +} + static int sil24_hardreset(struct ata_port *ap, unsigned int *class, unsigned long deadline) { @@ -699,7 +748,7 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc) } prb->ctrl = cpu_to_le16(ctrl); - ata_tf_to_fis(&qc->tf, prb->fis, 0); + ata_tf_to_fis(&qc->tf, 0, 1, prb->fis); if (qc->flags & ATA_QCFLAG_DMAMAP) sil24_fill_sg(qc, sge); @@ -754,6 +803,7 @@ static void sil24_thaw(struct ata_port *ap) static void sil24_error_intr(struct ata_port *ap) { void __iomem *port = ap->ioaddr.cmd_addr; + struct sil24_port_priv *pp = ap->private_data; struct ata_eh_info *ehi = &ap->eh_info; int freeze = 0; u32 irq_stat; @@ -769,16 +819,16 @@ static void sil24_error_intr(struct ata_port *ap) if (irq_stat & (PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG)) { ata_ehi_hotplugged(ehi); - ata_ehi_push_desc(ehi, ", %s", - irq_stat & PORT_IRQ_PHYRDY_CHG ? - "PHY RDY changed" : "device exchanged"); + ata_ehi_push_desc(ehi, "%s", + irq_stat & PORT_IRQ_PHYRDY_CHG ? + "PHY RDY changed" : "device exchanged"); freeze = 1; } if (irq_stat & PORT_IRQ_UNK_FIS) { ehi->err_mask |= AC_ERR_HSM; ehi->action |= ATA_EH_SOFTRESET; - ata_ehi_push_desc(ehi , ", unknown FIS"); + ata_ehi_push_desc(ehi, "unknown FIS"); freeze = 1; } @@ -797,18 +847,18 @@ static void sil24_error_intr(struct ata_port *ap) if (ci && ci->desc) { err_mask |= ci->err_mask; action |= ci->action; - ata_ehi_push_desc(ehi, ", %s", ci->desc); + ata_ehi_push_desc(ehi, "%s", ci->desc); } else { err_mask |= AC_ERR_OTHER; action |= ATA_EH_SOFTRESET; - ata_ehi_push_desc(ehi, ", unknown command error %d", + ata_ehi_push_desc(ehi, "unknown command error %d", cerr); } /* record error info */ qc = ata_qc_from_tag(ap, ap->active_tag); if (qc) { - sil24_update_tf(ap); + sil24_read_tf(ap, qc->tag, &pp->tf); qc->err_mask |= err_mask; } else ehi->err_mask |= err_mask; @@ -825,8 +875,11 @@ static void sil24_error_intr(struct ata_port *ap) static void sil24_finish_qc(struct ata_queued_cmd *qc) { + struct ata_port *ap = qc->ap; + struct sil24_port_priv *pp = ap->private_data; + if (qc->flags & ATA_QCFLAG_RESULT_TF) - sil24_update_tf(qc->ap); + sil24_read_tf(ap, qc->tag, &pp->tf); } static inline void sil24_host_intr(struct ata_port *ap) |