From 6c1153e00af8de755ec278d873a97c9ce2a72d10 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Fri, 13 Jul 2007 15:20:15 -0400 Subject: [libata] sata_mv: Micro-optimization and cleanups * Micro-optimization in the EDMA interrupt handling code * s/EDMA_ERR_CRBQ_PAR/EDMA_ERR_CRQB_PAR/ * Document EDMA Error Interrupt Cause register bits Signed-off-by: Jeff Garzik --- drivers/ata/sata_mv.c | 65 ++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index fb8a749423c..0ccd990c8da 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -227,26 +227,26 @@ enum { EDMA_ERR_IRQ_CAUSE_OFS = 0x8, EDMA_ERR_IRQ_MASK_OFS = 0xc, - EDMA_ERR_D_PAR = (1 << 0), - EDMA_ERR_PRD_PAR = (1 << 1), - EDMA_ERR_DEV = (1 << 2), - EDMA_ERR_DEV_DCON = (1 << 3), - EDMA_ERR_DEV_CON = (1 << 4), - EDMA_ERR_SERR = (1 << 5), + EDMA_ERR_D_PAR = (1 << 0), /* UDMA data parity err */ + EDMA_ERR_PRD_PAR = (1 << 1), /* UDMA PRD parity err */ + EDMA_ERR_DEV = (1 << 2), /* device error */ + EDMA_ERR_DEV_DCON = (1 << 3), /* device disconnect */ + EDMA_ERR_DEV_CON = (1 << 4), /* device connected */ + EDMA_ERR_SERR = (1 << 5), /* SError bits [WBDST] raised */ EDMA_ERR_SELF_DIS = (1 << 7), /* Gen II/IIE self-disable */ EDMA_ERR_SELF_DIS_5 = (1 << 8), /* Gen I self-disable */ - EDMA_ERR_BIST_ASYNC = (1 << 8), + EDMA_ERR_BIST_ASYNC = (1 << 8), /* BIST FIS or Async Notify */ EDMA_ERR_TRANS_IRQ_7 = (1 << 8), /* Gen IIE transprt layer irq */ - EDMA_ERR_CRBQ_PAR = (1 << 9), - EDMA_ERR_CRPB_PAR = (1 << 10), - EDMA_ERR_INTRL_PAR = (1 << 11), - EDMA_ERR_IORDY = (1 << 12), - EDMA_ERR_LNK_CTRL_RX = (0xf << 13), + EDMA_ERR_CRQB_PAR = (1 << 9), /* CRQB parity error */ + EDMA_ERR_CRPB_PAR = (1 << 10), /* CRPB parity error */ + EDMA_ERR_INTRL_PAR = (1 << 11), /* internal parity error */ + EDMA_ERR_IORDY = (1 << 12), /* IORdy timeout */ + EDMA_ERR_LNK_CTRL_RX = (0xf << 13), /* link ctrl rx error */ EDMA_ERR_LNK_CTRL_RX_2 = (1 << 15), - EDMA_ERR_LNK_DATA_RX = (0xf << 17), - EDMA_ERR_LNK_CTRL_TX = (0x1f << 21), - EDMA_ERR_LNK_DATA_TX = (0x1f << 26), - EDMA_ERR_TRANS_PROTO = (1 << 31), + EDMA_ERR_LNK_DATA_RX = (0xf << 17), /* link data rx error */ + EDMA_ERR_LNK_CTRL_TX = (0x1f << 21), /* link ctrl tx error */ + EDMA_ERR_LNK_DATA_TX = (0x1f << 26), /* link data tx error */ + EDMA_ERR_TRANS_PROTO = (1 << 31), /* transport protocol error */ EDMA_ERR_OVERRUN_5 = (1 << 5), EDMA_ERR_UNDERRUN_5 = (1 << 6), EDMA_EH_FREEZE = EDMA_ERR_D_PAR | @@ -255,7 +255,7 @@ enum { EDMA_ERR_DEV_CON | EDMA_ERR_SERR | EDMA_ERR_SELF_DIS | - EDMA_ERR_CRBQ_PAR | + EDMA_ERR_CRQB_PAR | EDMA_ERR_CRPB_PAR | EDMA_ERR_INTRL_PAR | EDMA_ERR_IORDY | @@ -270,7 +270,7 @@ enum { EDMA_ERR_OVERRUN_5 | EDMA_ERR_UNDERRUN_5 | EDMA_ERR_SELF_DIS_5 | - EDMA_ERR_CRBQ_PAR | + EDMA_ERR_CRQB_PAR | EDMA_ERR_CRPB_PAR | EDMA_ERR_INTRL_PAR | EDMA_ERR_IORDY, @@ -1393,7 +1393,7 @@ static void mv_err_intr(struct ata_port *ap, struct ata_queued_cmd *qc) if (edma_err_cause & EDMA_ERR_DEV) err_mask |= AC_ERR_DEV; if (edma_err_cause & (EDMA_ERR_D_PAR | EDMA_ERR_PRD_PAR | - EDMA_ERR_CRBQ_PAR | EDMA_ERR_CRPB_PAR | + EDMA_ERR_CRQB_PAR | EDMA_ERR_CRPB_PAR | EDMA_ERR_INTRL_PAR)) { err_mask |= AC_ERR_ATA_BUS; action |= ATA_EH_HARDRESET; @@ -1489,33 +1489,30 @@ static void mv_intr_edma(struct ata_port *ap) while (1) { u16 status; + unsigned int tag; /* get s/w response queue last-read pointer, and compare */ out_index = pp->resp_idx & MV_MAX_Q_DEPTH_MASK; if (in_index == out_index) break; - /* 50xx: get active ATA command */ if (IS_GEN_I(hpriv)) - qc = ata_qc_from_tag(ap, ap->active_tag); + tag = ap->active_tag; - /* 60xx: get active ATA command via tag, to enable support - * for queueing. this works transparently for queued and - * non-queued modes. + /* Gen II/IIE: get active ATA command via tag, to enable + * support for queueing. this works transparently for + * queued and non-queued modes. */ - else { - unsigned int tag; + else if (IS_GEN_II(hpriv)) + tag = (le16_to_cpu(pp->crpb[out_index].id) + >> CRPB_IOID_SHIFT_6) & 0x3f; - if (IS_GEN_II(hpriv)) - tag = (le16_to_cpu(pp->crpb[out_index].id) - >> CRPB_IOID_SHIFT_6) & 0x3f; - else - tag = (le16_to_cpu(pp->crpb[out_index].id) - >> CRPB_IOID_SHIFT_7) & 0x3f; + else /* IS_GEN_IIE */ + tag = (le16_to_cpu(pp->crpb[out_index].id) + >> CRPB_IOID_SHIFT_7) & 0x3f; - qc = ata_qc_from_tag(ap, tag); - } + qc = ata_qc_from_tag(ap, tag); /* lower 8 bits of status are EDMA_ERR_IRQ_CAUSE_OFS * bits (WARNING: might not necessarily be associated -- cgit v1.2.3 From 0ea9e179f436f153fc19fdaef7abbc1e0da20762 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Fri, 13 Jul 2007 17:06:45 -0400 Subject: [libata] sata_mv: minor cleanups * trim trailing whitespace * document some flags, registers, and register bits * fix locking around EDMA on/off and configuration * continue replacing "constant OP var" with "var OP constant" * use new pci_try_set_mwi() Signed-off-by: Jeff Garzik --- drivers/ata/sata_mv.c | 62 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 0ccd990c8da..80ade5b93b8 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -35,8 +35,6 @@ 6) Add port multiplier support (intermediate) - 7) Test and verify 3.0 Gbps support - 8) Develop a low-power-consumption strategy, and implement it. 9) [Experiment, low priority] See if ATAPI can be supported using @@ -286,10 +284,10 @@ enum { EDMA_RSP_Q_OUT_PTR_OFS = 0x24, /* also contains BASE_LO */ EDMA_RSP_Q_PTR_SHIFT = 3, - EDMA_CMD_OFS = 0x28, - EDMA_EN = (1 << 0), - EDMA_DS = (1 << 1), - ATA_RST = (1 << 2), + EDMA_CMD_OFS = 0x28, /* EDMA command register */ + EDMA_EN = (1 << 0), /* enable EDMA */ + EDMA_DS = (1 << 1), /* disable EDMA; self-negated */ + ATA_RST = (1 << 2), /* reset trans/link/phy */ EDMA_IORDY_TMOUT = 0x34, EDMA_ARB_CFG = 0x38, @@ -301,14 +299,13 @@ enum { MV_HP_ERRATA_60X1B2 = (1 << 3), MV_HP_ERRATA_60X1C0 = (1 << 4), MV_HP_ERRATA_XX42A0 = (1 << 5), - MV_HP_GEN_I = (1 << 6), - MV_HP_GEN_II = (1 << 7), - MV_HP_GEN_IIE = (1 << 8), + MV_HP_GEN_I = (1 << 6), /* Generation I: 50xx */ + MV_HP_GEN_II = (1 << 7), /* Generation II: 60xx */ + MV_HP_GEN_IIE = (1 << 8), /* Generation IIE: 6042/7042 */ /* Port private flags (pp_flags) */ - MV_PP_FLAG_EDMA_EN = (1 << 0), - MV_PP_FLAG_EDMA_DS_ACT = (1 << 1), - MV_PP_FLAG_HAD_A_RESET = (1 << 2), + MV_PP_FLAG_EDMA_EN = (1 << 0), /* is EDMA engine enabled? */ + MV_PP_FLAG_HAD_A_RESET = (1 << 2), /* 1st hard reset complete? */ }; #define IS_GEN_I(hpriv) ((hpriv)->hp_flags & MV_HP_GEN_I) @@ -318,8 +315,12 @@ enum { enum { MV_DMA_BOUNDARY = 0xffffffffU, + /* mask of register bits containing lower 32 bits + * of EDMA request queue DMA address + */ EDMA_REQ_Q_BASE_LO_MASK = 0xfffffc00U, + /* ditto, for response queue */ EDMA_RSP_Q_BASE_LO_MASK = 0xffffff00U, }; @@ -823,7 +824,7 @@ static void mv_start_dma(void __iomem *base, struct mv_host_priv *hpriv, } /** - * mv_stop_dma - Disable eDMA engine + * __mv_stop_dma - Disable eDMA engine * @ap: ATA channel to manipulate * * Verify the local cache of the eDMA state is accurate with a @@ -832,7 +833,7 @@ static void mv_start_dma(void __iomem *base, struct mv_host_priv *hpriv, * LOCKING: * Inherited from caller. */ -static int mv_stop_dma(struct ata_port *ap) +static int __mv_stop_dma(struct ata_port *ap) { void __iomem *port_mmio = mv_ap_base(ap); struct mv_port_priv *pp = ap->private_data; @@ -865,6 +866,18 @@ static int mv_stop_dma(struct ata_port *ap) return err; } +static int mv_stop_dma(struct ata_port *ap) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&ap->host->lock, flags); + rc = __mv_stop_dma(ap); + spin_unlock_irqrestore(&ap->host->lock, flags); + + return rc; +} + #ifdef ATA_DEBUG static void mv_dump_mem(void __iomem *start, unsigned bytes) { @@ -965,7 +978,7 @@ static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in) { unsigned int ofs = mv_scr_offset(sc_reg_in); - if (0xffffffffU != ofs) + if (ofs != 0xffffffffU) return readl(mv_ap_base(ap) + ofs); else return (u32) ofs; @@ -975,7 +988,7 @@ static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) { unsigned int ofs = mv_scr_offset(sc_reg_in); - if (0xffffffffU != ofs) + if (ofs != 0xffffffffU) writelfl(val, mv_ap_base(ap) + ofs); } @@ -1029,6 +1042,7 @@ static int mv_port_start(struct ata_port *ap) void __iomem *port_mmio = mv_ap_base(ap); void *mem; dma_addr_t mem_dma; + unsigned long flags; int rc; pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); @@ -1067,10 +1081,14 @@ static int mv_port_start(struct ata_port *ap) pp->sg_tbl = mem; pp->sg_tbl_dma = mem_dma; + spin_lock_irqsave(&ap->host->lock, flags); + mv_edma_cfg(ap, hpriv, port_mmio); mv_set_edma_ptrs(port_mmio, hpriv, pp); + spin_unlock_irqrestore(&ap->host->lock, flags); + /* Don't turn on EDMA here...do it before DMA commands only. Else * we'll be unable to send non-data, PIO, etc due to restricted access * to shadow regs. @@ -1090,11 +1108,7 @@ static int mv_port_start(struct ata_port *ap) */ static void mv_port_stop(struct ata_port *ap) { - unsigned long flags; - - spin_lock_irqsave(&ap->host->lock, flags); mv_stop_dma(ap); - spin_unlock_irqrestore(&ap->host->lock, flags); } /** @@ -1325,7 +1339,7 @@ static unsigned int mv_qc_issue(struct ata_queued_cmd *qc) * port. Turn off EDMA so there won't be problems accessing * shadow block, etc registers. */ - mv_stop_dma(ap); + __mv_stop_dma(ap); return ata_qc_issue_prot(qc); } @@ -1497,7 +1511,7 @@ static void mv_intr_edma(struct ata_port *ap) break; /* 50xx: get active ATA command */ - if (IS_GEN_I(hpriv)) + if (IS_GEN_I(hpriv)) tag = ap->active_tag; /* Gen II/IIE: get active ATA command via tag, to enable @@ -1532,7 +1546,7 @@ static void mv_intr_edma(struct ata_port *ap) ata_qc_complete(qc); } - /* advance software response queue pointer, to + /* advance software response queue pointer, to * indicate (after the loop completes) to hardware * that we have consumed a response queue entry. */ @@ -2206,7 +2220,7 @@ static int mv_prereset(struct ata_port *ap, unsigned long deadline) struct mv_port_priv *pp = ap->private_data; struct ata_eh_context *ehc = &ap->eh_context; int rc; - + rc = mv_stop_dma(ap); if (rc) ehc->i.action |= ATA_EH_HARDRESET; -- cgit v1.2.3 From 83b6839ec33becca78ebf6035489733a62e750bb Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 20 Jul 2007 12:50:58 +0900 Subject: pata_platform: Fix NULL pointer dereference pata_platform: Fix NULL pointer dereference pata_platform currently dereferences a NULL pointer in pata_platform_probe() if pdev->dev.platform_data is set to NULL. This breakage was most likely introduced by commit 5f45bc50976ee1f408f7171af155aec646655a37. Signed-off-by: Magnus Damm Signed-off-by: Jeff Garzik --- drivers/ata/pata_platform.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index 79f841bca59..a909f793ffc 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -213,8 +213,9 @@ static int __devinit pata_platform_probe(struct platform_device *pdev) pata_platform_setup_port(&ap->ioaddr, pp_info); /* activate */ - return ata_host_activate(host, platform_get_irq(pdev, 0), ata_interrupt, - pp_info->irq_flags, &pata_platform_sht); + return ata_host_activate(host, platform_get_irq(pdev, 0), + ata_interrupt, pp_info ? pp_info->irq_flags + : 0, &pata_platform_sht); } /** -- cgit v1.2.3 From 3fb6589ceaf06d9c65bdf2382249d818771e913b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 20 Jul 2007 12:49:38 +0900 Subject: libata: add ST9160821AS 3.CLF to NONCQ blacklist Yay, the first one from Seagate. 3.ALC firmware is okay. This was reported by Sam Freed on bugzilla bug 8759. Signed-off-by: Tejun Heo Signed-off-by: Sam Freed Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 88e2dd0983b..e2ecb7a4628 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3769,6 +3769,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "Hitachi HTS541616J9SA00", "SB4OC70P", ATA_HORKAGE_NONCQ, }, { "WDC WD740ADFD-00NLR1", NULL, ATA_HORKAGE_NONCQ, }, { "FUJITSU MHV2080BH", "00840028", ATA_HORKAGE_NONCQ, }, + { "ST9160821AS", "3.CLF", ATA_HORKAGE_NONCQ, }, /* Devices with NCQ limits */ -- cgit v1.2.3 From 028a2596336b19a7e3713cfa9fe04d0d32e95876 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 17 Jul 2007 23:48:48 +0400 Subject: ahci.c: fix CONFIG_PM=n compilation Commit df69c9c5438b4e396a64d42608b2a6c48a3e7475 moved only prototype of out of CONFIG_PM. Move function out as well. Box seems to boot fine. Signed-off-by: Alexey Dobriyan Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 11e4eb9f304..eaec5e506f5 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1519,6 +1519,14 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) } } +static int ahci_port_resume(struct ata_port *ap) +{ + ahci_power_up(ap); + ahci_start_port(ap); + + return 0; +} + #ifdef CONFIG_PM static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) { @@ -1536,14 +1544,6 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) return rc; } -static int ahci_port_resume(struct ata_port *ap) -{ - ahci_power_up(ap); - ahci_start_port(ap); - - return 0; -} - static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) { struct ata_host *host = dev_get_drvdata(&pdev->dev); -- cgit v1.2.3 From dcd0344775c1c0bf8ff3b9541863beea5088a7c9 Mon Sep 17 00:00:00 2001 From: Akira Iguchi Date: Tue, 17 Jul 2007 12:10:17 +0900 Subject: pata_scc.c: small fixes (Workaround for errata A308) This patch fixes some issues of the previous patch: - Use mode_filter() hook to limit ATAPI UDMA mode - "data loss" warning message - handling of udma_mask Signed-off-by: Kou Ishizaki Signed-off-by: Akira Iguchi Signed-off-by: Jeff Garzik --- drivers/ata/pata_scc.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c index c55667e0eb6..c0ffbed3e75 100644 --- a/drivers/ata/pata_scc.c +++ b/drivers/ata/pata_scc.c @@ -238,12 +238,6 @@ static void scc_set_dmamode (struct ata_port *ap, struct ata_device *adev) else offset = 0; /* 100MHz */ - /* errata A308 workaround: limit ATAPI UDMA mode to UDMA4 */ - if (adev->class == ATA_DEV_ATAPI && speed > XFER_UDMA_4) { - printk(KERN_INFO "%s: limit ATAPI UDMA to UDMA4\n", DRV_NAME); - speed = XFER_UDMA_4; - } - if (speed >= XFER_UDMA_0) idx = speed - XFER_UDMA_0; else @@ -264,6 +258,17 @@ static void scc_set_dmamode (struct ata_port *ap, struct ata_device *adev) JCTSStbl[offset][idx] << 16 | JCENVTtbl[offset][idx]); } +unsigned long scc_mode_filter(struct ata_device *adev, unsigned long mask) +{ + /* errata A308 workaround: limit ATAPI UDMA mode to UDMA4 */ + if (adev->class == ATA_DEV_ATAPI && + (mask & (0xE0 << ATA_SHIFT_UDMA))) { + printk(KERN_INFO "%s: limit ATAPI UDMA to UDMA4\n", DRV_NAME); + mask &= ~(0xE0 << ATA_SHIFT_UDMA); + } + return ata_pci_default_filter(adev, mask); +} + /** * scc_tf_load - send taskfile registers to host controller * @ap: Port to which output is sent @@ -741,7 +746,7 @@ static u8 scc_bmdma_status (struct ata_port *ap) return host_stat; /* errata A252,A308 workaround: Step4 */ - if (ata_altstatus(ap) & ATA_ERR && int_status & INTSTS_INTRQ) + if ((ata_altstatus(ap) & ATA_ERR) && (int_status & INTSTS_INTRQ)) return (host_stat | ATA_DMA_INTR); /* errata A308 workaround Step5 */ @@ -752,11 +757,11 @@ static u8 scc_bmdma_status (struct ata_port *ap) if ((qc->tf.protocol == ATA_PROT_DMA && qc->dev->xfer_mode > XFER_UDMA_4)) { if (!(int_status & INTSTS_ACTEINT)) { - printk(KERN_WARNING "ata%u: data lost occurred. (ACTEINT==0, retry:%d)\n", - ap->print_id, retry); + printk(KERN_WARNING "ata%u: operation failed (transfer data loss)\n", + ap->print_id); host_stat |= ATA_DMA_ERR; if (retry++) - ap->udma_mask >>= 1; + ap->udma_mask &= ~(1 << qc->dev->xfer_mode); } else retry = 0; } @@ -1016,7 +1021,7 @@ static const struct ata_port_operations scc_pata_ops = { .port_disable = ata_port_disable, .set_piomode = scc_set_piomode, .set_dmamode = scc_set_dmamode, - .mode_filter = ata_pci_default_filter, + .mode_filter = scc_mode_filter, .tf_load = scc_tf_load, .tf_read = scc_tf_read, -- cgit v1.2.3 From f3a03b0934fb83a030ccd3e6f1fbe845a9c44d49 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Mon, 16 Jul 2007 11:23:03 -0400 Subject: Correct comment in libata-sff.c The filename in the file header is incorrect. Signed-off-by: Dave Jones Signed-off-by: Jeff Garzik --- drivers/ata/libata-sff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index ca7d2245d68..1190c6703bc 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1,5 +1,5 @@ /* - * libata-bmdma.c - helper library for PCI IDE BMDMA + * libata-sff.c - helper library for PCI IDE BMDMA * * Maintained by: Jeff Garzik * Please ALWAYS copy linux-ide@vger.kernel.org -- cgit v1.2.3 From fe36cb53cfd82f3c0796a0826e1c9caf198c8f97 Mon Sep 17 00:00:00 2001 From: Petr Vandrovec Date: Fri, 20 Jul 2007 07:44:44 -0400 Subject: [libata] Fix reported task file values in sense data ata_tf_read was setting HOB bit when lba48 command was submitted, but was not clearing it before reading "normal" data. As it is only place which sets HOB bit in control register, and register reads should not be affected by other bits, let's just clear it when we are done with reading upper bytes so non-48bit commands do not have to touch ctl at all. pata_scc suffered from same problem... Signed-off-by: Petr Vandrovec Signed-off-by: Jeff Garzik --- drivers/ata/libata-sff.c | 2 ++ drivers/ata/pata_scc.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 1190c6703bc..6c289c7b132 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -211,6 +211,8 @@ void ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) tf->hob_lbal = ioread8(ioaddr->lbal_addr); tf->hob_lbam = ioread8(ioaddr->lbam_addr); tf->hob_lbah = ioread8(ioaddr->lbah_addr); + iowrite8(tf->ctl, ioaddr->ctl_addr); + ap->last_ctl = tf->ctl; } } diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c index c0ffbed3e75..36cdbd2b0bd 100644 --- a/drivers/ata/pata_scc.c +++ b/drivers/ata/pata_scc.c @@ -363,6 +363,8 @@ static void scc_tf_read (struct ata_port *ap, struct ata_taskfile *tf) tf->hob_lbal = in_be32(ioaddr->lbal_addr); tf->hob_lbam = in_be32(ioaddr->lbam_addr); tf->hob_lbah = in_be32(ioaddr->lbah_addr); + out_be32(ioaddr->ctl_addr, tf->ctl); + ap->last_ctl = tf->ctl; } } -- cgit v1.2.3 From 9977126c4b65c1396b665f7a0eeb8c7dede336f9 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:38 +0900 Subject: libata: add @is_cmd to ata_tf_to_fis() Add @is_cmd to ata_tf_to_fis(). This controls bit 7 of the second byte which tells the device whether this H2D FIS is for a command or not. This cleans up ahci a bit and will be used by PMP. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 10 ++++------ drivers/ata/libata-core.c | 14 ++++++++------ drivers/ata/sata_qstor.c | 2 +- drivers/ata/sata_sil24.c | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index eaec5e506f5..61c5b6e68de 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1020,8 +1020,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, cmd_fis_len | AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY); tf.ctl |= ATA_SRST; - ata_tf_to_fis(&tf, fis, 0); - fis[1] &= ~(1 << 7); /* turn off Command FIS bit */ + ata_tf_to_fis(&tf, 0, 0, fis); writel(1, port_mmio + PORT_CMD_ISSUE); @@ -1039,8 +1038,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, ahci_fill_cmd_slot(pp, 0, cmd_fis_len); tf.ctl &= ~ATA_SRST; - ata_tf_to_fis(&tf, fis, 0); - fis[1] &= ~(1 << 7); /* turn off Command FIS bit */ + ata_tf_to_fis(&tf, 0, 0, fis); writel(1, port_mmio + PORT_CMD_ISSUE); readl(port_mmio + PORT_CMD_ISSUE); /* flush */ @@ -1088,7 +1086,7 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class, /* clear D2H reception area to properly wait for D2H FIS */ ata_tf_init(ap->device, &tf); tf.command = 0x80; - ata_tf_to_fis(&tf, d2h_fis, 0); + ata_tf_to_fis(&tf, 0, 0, d2h_fis); rc = sata_std_hardreset(ap, class, deadline); @@ -1205,7 +1203,7 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc) */ cmd_tbl = pp->cmd_tbl + qc->tag * AHCI_CMD_TBL_SZ; - ata_tf_to_fis(&qc->tf, cmd_tbl, 0); + ata_tf_to_fis(&qc->tf, 0, 1, cmd_tbl); if (is_atapi) { memset(cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32); memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len); diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index e2ecb7a4628..39a8e986a4e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -111,8 +111,9 @@ MODULE_VERSION(DRV_VERSION); /** * ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure * @tf: Taskfile to convert - * @fis: Buffer into which data will output * @pmp: Port multiplier port + * @is_cmd: This FIS is for command + * @fis: Buffer into which data will output * * Converts a standard ATA taskfile to a Serial ATA * FIS structure (Register - Host to Device). @@ -120,12 +121,13 @@ MODULE_VERSION(DRV_VERSION); * LOCKING: * Inherited from caller. */ - -void ata_tf_to_fis(const struct ata_taskfile *tf, u8 *fis, u8 pmp) +void ata_tf_to_fis(const struct ata_taskfile *tf, u8 pmp, int is_cmd, u8 *fis) { - fis[0] = 0x27; /* Register - Host to Device FIS */ - fis[1] = (pmp & 0xf) | (1 << 7); /* Port multiplier number, - bit 7 indicates Command FIS */ + fis[0] = 0x27; /* Register - Host to Device FIS */ + fis[1] = pmp & 0xf; /* Port multiplier number*/ + if (is_cmd) + fis[1] |= (1 << 7); /* bit 7 indicates Command FIS */ + fis[2] = tf->command; fis[3] = tf->feature; diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 9ab554da89b..5aef4ac3701 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -337,7 +337,7 @@ static void qs_qc_prep(struct ata_queued_cmd *qc) buf[28] = dflags; /* frame information structure (FIS) */ - ata_tf_to_fis(&qc->tf, &buf[32], 0); + ata_tf_to_fis(&qc->tf, 0, 1, &buf[32]); } static inline void qs_packet_start(struct ata_queued_cmd *qc) diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index ac43a30ebe2..a11007b5071 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -699,7 +699,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); -- cgit v1.2.3 From 2cbb79ebbd4be07041368da5379a64f89f8ad518 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:38 +0900 Subject: ahci: use deadline instead of fixed timeout for 1st FIS for SRST Use deadline instead of fixed timeout for 1st FIS for SRST to improve robustness of SRST. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 61c5b6e68de..0451600bdcc 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -976,6 +976,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, void __iomem *port_mmio = ahci_port_base(ap); const u32 cmd_fis_len = 5; /* five dwords */ const char *reason = NULL; + unsigned long now, msecs; struct ata_taskfile tf; u32 tmp; u8 *fis; @@ -1016,6 +1017,11 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, fis = pp->cmd_tbl; /* issue the first D2H Register FIS */ + msecs = 0; + now = jiffies; + if (time_after(now, deadline)) + msecs = jiffies_to_msecs(deadline - now); + ahci_fill_cmd_slot(pp, 0, cmd_fis_len | AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY); @@ -1024,7 +1030,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, writel(1, port_mmio + PORT_CMD_ISSUE); - tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, 1, 500); + tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, 1, msecs); if (tmp & 0x1) { rc = -EIO; reason = "1st FIS failed"; -- cgit v1.2.3 From d2e75dfffbe9e86e1d646264792ac9bcd2cc4267 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:39 +0900 Subject: ahci: separate out ahci_kick_engine() Separate out stop_engine - CLO - start_engine sequence from ahci_softreset() and ahci_clo() into ahci_reset_engine() and use it in ahci_softreset() and ahci_post_internal_cmd(). The function will also be used to prepare for and clean up after PMP register access commands. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 69 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 0451600bdcc..1be238def0e 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -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); } -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; struct ahci_host_priv *hpriv = ap->host->private_data; 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 |= PORT_CMD_CLO; writel(tmp, port_mmio + PORT_CMD); + rc = 0; tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_CLO, PORT_CMD_CLO, 1, 500); 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, @@ -991,27 +1015,10 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, } /* 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); 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); return 0; - fail_restart: - ahci_start_engine(ap); fail: ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason); return rc; @@ -1516,11 +1521,9 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) { 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) -- cgit v1.2.3 From 91c4a2e09267b0ddc8e59d121e3748cd18675739 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:39 +0900 Subject: ahci: separate out ahci_exec_polled_cmd() Separate out ahci_exec_polled_cmd() from ahci_softreset(). This will be used to implement ahci_pmp_read/write(). ahci_exec_polled_cmd() performs reset_engine before returning if the command fails (times out). This is to improve robustness. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 54 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 1be238def0e..db65f4a1602 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -993,17 +993,42 @@ static int ahci_kick_engine(struct ata_port *ap, int force_restart) return rc; } -static int ahci_softreset(struct ata_port *ap, unsigned int *class, - unsigned long deadline) +static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp, + struct ata_taskfile *tf, int is_cmd, u16 flags, + unsigned long timeout_msec) { + const u32 cmd_fis_len = 5; /* five dwords */ struct ahci_port_priv *pp = ap->private_data; void __iomem *port_mmio = ahci_port_base(ap); - const u32 cmd_fis_len = 5; /* five dwords */ + u8 *fis = pp->cmd_tbl; + u32 tmp; + + /* prep the command */ + ata_tf_to_fis(tf, pmp, is_cmd, fis); + ahci_fill_cmd_slot(pp, 0, cmd_fis_len | flags | (pmp << 12)); + + /* issue & wait */ + writel(1, port_mmio + PORT_CMD_ISSUE); + + if (timeout_msec) { + tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, + 1, timeout_msec); + if (tmp & 0x1) { + ahci_kick_engine(ap, 1); + return -EBUSY; + } + } else + readl(port_mmio + PORT_CMD_ISSUE); /* flush */ + + return 0; +} + +static int ahci_softreset(struct ata_port *ap, unsigned int *class, + unsigned long deadline) +{ const char *reason = NULL; unsigned long now, msecs; struct ata_taskfile tf; - u32 tmp; - u8 *fis; int rc; DPRINTK("ENTER\n"); @@ -1021,7 +1046,6 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, "failed to reset engine (errno=%d)", rc); ata_tf_init(ap->device, &tf); - fis = pp->cmd_tbl; /* issue the first D2H Register FIS */ msecs = 0; @@ -1029,16 +1053,9 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, if (time_after(now, deadline)) msecs = jiffies_to_msecs(deadline - now); - ahci_fill_cmd_slot(pp, 0, - cmd_fis_len | AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY); - tf.ctl |= ATA_SRST; - ata_tf_to_fis(&tf, 0, 0, fis); - - writel(1, port_mmio + PORT_CMD_ISSUE); - - tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, 1, msecs); - if (tmp & 0x1) { + if (ahci_exec_polled_cmd(ap, 0, &tf, 0, + AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY, msecs)) { rc = -EIO; reason = "1st FIS failed"; goto fail; @@ -1048,13 +1065,8 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, msleep(1); /* issue the second D2H Register FIS */ - ahci_fill_cmd_slot(pp, 0, cmd_fis_len); - tf.ctl &= ~ATA_SRST; - ata_tf_to_fis(&tf, 0, 0, fis); - - writel(1, port_mmio + PORT_CMD_ISSUE); - readl(port_mmio + PORT_CMD_ISSUE); /* flush */ + ahci_exec_polled_cmd(ap, 0, &tf, 0, 0, 0); /* spec mandates ">= 2ms" before checking status. * We wait 150ms, because that was the magic delay used for -- cgit v1.2.3 From a9cf5e858100b2f82ad61028c26a1a3de11c4839 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:39 +0900 Subject: ahci: separate out ahci_do_softreset() Separate out ahci_do_softreset() which takes @pmp as its last argument. This will be used to implement ahci_pmp_softreset(). Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index db65f4a1602..c5034d450c6 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1023,8 +1023,8 @@ static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp, return 0; } -static int ahci_softreset(struct ata_port *ap, unsigned int *class, - unsigned long deadline) +static int ahci_do_softreset(struct ata_port *ap, unsigned int *class, + int pmp, unsigned long deadline) { const char *reason = NULL; unsigned long now, msecs; @@ -1054,7 +1054,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, msecs = jiffies_to_msecs(deadline - now); tf.ctl |= ATA_SRST; - if (ahci_exec_polled_cmd(ap, 0, &tf, 0, + if (ahci_exec_polled_cmd(ap, pmp, &tf, 0, AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY, msecs)) { rc = -EIO; reason = "1st FIS failed"; @@ -1066,7 +1066,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, /* issue the second D2H Register FIS */ tf.ctl &= ~ATA_SRST; - ahci_exec_polled_cmd(ap, 0, &tf, 0, 0, 0); + ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0); /* spec mandates ">= 2ms" before checking status. * We wait 150ms, because that was the magic delay used for @@ -1094,6 +1094,12 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class, return rc; } +static int ahci_softreset(struct ata_port *ap, unsigned int *class, + unsigned long deadline) +{ + return ahci_do_softreset(ap, class, 0, deadline); +} + static int ahci_hardreset(struct ata_port *ap, unsigned int *class, unsigned long deadline) { -- cgit v1.2.3 From e59f0dad33176a13803ff8a41cc639a06c43d381 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:39 +0900 Subject: sata_sil24: replace sil24_update_tf() with sil24_read_tf() Replace sil24_update_tf() to sil24_read_tf() which reads TF into passed int result TF argument and can read TFs of PMP links. This will be used by PMP support. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/sata_sil24.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index a11007b5071..5f8afa95004 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -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) @@ -538,6 +538,7 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class, 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; + struct ata_taskfile tf; u32 mask, irq_stat; const char *reason; @@ -577,8 +578,8 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class, 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; @@ -754,6 +755,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; @@ -808,7 +810,7 @@ static void sil24_error_intr(struct ata_port *ap) /* 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 +827,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) -- cgit v1.2.3 From 37b99cba8c2a3fd05a3a9f652cc2b3e48d1b9197 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:39 +0900 Subject: sata_sil24: separate out sil24_exec_polled_cmd() Separate out sil24_exec_polled_cmd() from sil24_softreset(). This will be used to implement sil24_pmp_read/write(). Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/sata_sil24.c | 76 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 5f8afa95004..e6fe4c424a6 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -531,16 +531,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 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_softreset(struct ata_port *ap, unsigned int *class, + unsigned long deadline) +{ + unsigned long timeout_msec = 0; struct ata_taskfile tf; - u32 mask, irq_stat; const char *reason; + int rc; DPRINTK("ENTER\n"); @@ -557,24 +601,16 @@ 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 */ + if (time_after(deadline, jiffies)) + timeout_msec = jiffies_to_msecs(deadline - jiffies); - 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"; + ata_tf_init(ap->device, &tf); /* doesn't really matter */ + rc = sil24_exec_polled_cmd(ap, 0, &tf, 0, PRB_CTRL_SRST, timeout_msec); + if (rc == -EBUSY) { + reason = "timeout"; + goto err; + } else if (rc) { + reason = "SRST command error"; goto err; } -- cgit v1.2.3 From 975530e8a33fdeb1ad80d82fde11d56bf9ed2760 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:39 +0900 Subject: sata_sil24: separate out sil24_do_softreset() Separate out sil24_do_softreset() which takes @pmp as its last argument. This will be used to implement sil24_pmp_softreset(). Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/sata_sil24.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index e6fe4c424a6..e538edc1b4e 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -578,8 +578,8 @@ static int sil24_exec_polled_cmd(struct ata_port *ap, int pmp, return rc; } -static int sil24_softreset(struct ata_port *ap, unsigned int *class, - unsigned long deadline) +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; @@ -605,7 +605,8 @@ static int sil24_softreset(struct ata_port *ap, unsigned int *class, timeout_msec = jiffies_to_msecs(deadline - jiffies); ata_tf_init(ap->device, &tf); /* doesn't really matter */ - rc = sil24_exec_polled_cmd(ap, 0, &tf, 0, PRB_CTRL_SRST, timeout_msec); + rc = sil24_exec_polled_cmd(ap, pmp, &tf, 0, PRB_CTRL_SRST, + timeout_msec); if (rc == -EBUSY) { reason = "timeout"; goto err; @@ -629,6 +630,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) { -- cgit v1.2.3 From b64bbc39f2122a2276578e40144af69ef01decd4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:39 +0900 Subject: libata: improve EH report formatting Requiring LLDs to format multiple error description messages properly doesn't work too well. Help LLDs a bit by making ata_ehi_push_desc() insert ", " on each invocation. __ata_ehi_push_desc() is the raw version without the automatic separator. While at it, make ehi_desc interface proper functions instead of macros. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 6 ++--- drivers/ata/libata-core.c | 3 +++ drivers/ata/libata-eh.c | 69 +++++++++++++++++++++++++++++++++++++++++++++-- drivers/ata/sata_mv.c | 8 +++--- drivers/ata/sata_nv.c | 22 ++++++++------- drivers/ata/sata_sil24.c | 12 ++++----- 6 files changed, 95 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index c5034d450c6..210292cd8ad 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1289,12 +1289,12 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) 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"); + ata_ehi_push_desc(ehi, "interface fatal error"); } if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) { ata_ehi_hotplugged(ehi); - ata_ehi_push_desc(ehi, ", %s", irq_stat & PORT_IRQ_CONNECT ? + ata_ehi_push_desc(ehi, "%s", irq_stat & PORT_IRQ_CONNECT ? "connection status changed" : "PHY RDY changed"); } @@ -1303,7 +1303,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) err_mask |= AC_ERR_HSM; action |= ATA_EH_SOFTRESET; - ata_ehi_push_desc(ehi, ", unknown FIS %08x %08x %08x %08x", + ata_ehi_push_desc(ehi, "unknown FIS %08x %08x %08x %08x", unk[0], unk[1], unk[2], unk[3]); } diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 39a8e986a4e..ecbc3278238 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6945,6 +6945,9 @@ EXPORT_SYMBOL_GPL(ata_pci_default_filter); EXPORT_SYMBOL_GPL(ata_pci_clear_simplex); #endif /* CONFIG_PCI */ +EXPORT_SYMBOL_GPL(__ata_ehi_push_desc); +EXPORT_SYMBOL_GPL(ata_ehi_push_desc); +EXPORT_SYMBOL_GPL(ata_ehi_clear_desc); EXPORT_SYMBOL_GPL(ata_eng_timeout); EXPORT_SYMBOL_GPL(ata_port_schedule_eh); EXPORT_SYMBOL_GPL(ata_port_abort); diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 9aa62a0754f..96b184ebf70 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -85,6 +85,71 @@ static void ata_eh_handle_port_resume(struct ata_port *ap) { } #endif /* CONFIG_PM */ +static void __ata_ehi_pushv_desc(struct ata_eh_info *ehi, const char *fmt, + va_list args) +{ + ehi->desc_len += vscnprintf(ehi->desc + ehi->desc_len, + ATA_EH_DESC_LEN - ehi->desc_len, + fmt, args); +} + +/** + * __ata_ehi_push_desc - push error description without adding separator + * @ehi: target EHI + * @fmt: printf format string + * + * Format string according to @fmt and append it to @ehi->desc. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void __ata_ehi_push_desc(struct ata_eh_info *ehi, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + __ata_ehi_pushv_desc(ehi, fmt, args); + va_end(args); +} + +/** + * ata_ehi_push_desc - push error description with separator + * @ehi: target EHI + * @fmt: printf format string + * + * Format string according to @fmt and append it to @ehi->desc. + * If @ehi->desc is not empty, ", " is added in-between. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void ata_ehi_push_desc(struct ata_eh_info *ehi, const char *fmt, ...) +{ + va_list args; + + if (ehi->desc_len) + __ata_ehi_push_desc(ehi, ", "); + + va_start(args, fmt); + __ata_ehi_pushv_desc(ehi, fmt, args); + va_end(args); +} + +/** + * ata_ehi_clear_desc - clean error description + * @ehi: target EHI + * + * Clear @ehi->desc. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void ata_ehi_clear_desc(struct ata_eh_info *ehi) +{ + ehi->desc[0] = '\0'; + ehi->desc_len = 0; +} + static void ata_ering_record(struct ata_ering *ering, int is_io, unsigned int err_mask) { @@ -1524,14 +1589,14 @@ static void ata_eh_report(struct ata_port *ap) ehc->i.err_mask, ap->sactive, ehc->i.serror, ehc->i.action, frozen); if (desc) - ata_dev_printk(ehc->i.dev, KERN_ERR, "(%s)\n", desc); + ata_dev_printk(ehc->i.dev, KERN_ERR, "%s\n", desc); } else { ata_port_printk(ap, KERN_ERR, "exception Emask 0x%x " "SAct 0x%x SErr 0x%x action 0x%x%s\n", ehc->i.err_mask, ap->sactive, ehc->i.serror, ehc->i.action, frozen); if (desc) - ata_port_printk(ap, KERN_ERR, "(%s)\n", desc); + ata_port_printk(ap, KERN_ERR, "%s\n", desc); } for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 80ade5b93b8..b4b737e081e 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -1411,12 +1411,12 @@ static void mv_err_intr(struct ata_port *ap, struct ata_queued_cmd *qc) EDMA_ERR_INTRL_PAR)) { err_mask |= AC_ERR_ATA_BUS; action |= ATA_EH_HARDRESET; - ata_ehi_push_desc(ehi, ", parity error"); + ata_ehi_push_desc(ehi, "parity error"); } if (edma_err_cause & (EDMA_ERR_DEV_DCON | EDMA_ERR_DEV_CON)) { ata_ehi_hotplugged(ehi); ata_ehi_push_desc(ehi, edma_err_cause & EDMA_ERR_DEV_DCON ? - ", dev disconnect" : ", dev connect"); + "dev disconnect" : "dev connect"); } if (IS_GEN_I(hpriv)) { @@ -1425,7 +1425,7 @@ static void mv_err_intr(struct ata_port *ap, struct ata_queued_cmd *qc) if (edma_err_cause & EDMA_ERR_SELF_DIS_5) { struct mv_port_priv *pp = ap->private_data; pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN; - ata_ehi_push_desc(ehi, ", EDMA self-disable"); + ata_ehi_push_desc(ehi, "EDMA self-disable"); } } else { eh_freeze_mask = EDMA_EH_FREEZE; @@ -1433,7 +1433,7 @@ static void mv_err_intr(struct ata_port *ap, struct ata_queued_cmd *qc) if (edma_err_cause & EDMA_ERR_SELF_DIS) { struct mv_port_priv *pp = ap->private_data; pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN; - ata_ehi_push_desc(ehi, ", EDMA self-disable"); + ata_ehi_push_desc(ehi, "EDMA self-disable"); } if (edma_err_cause & EDMA_ERR_SERR) { diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index db81e3efa5e..5d943da042f 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -715,19 +715,20 @@ static int nv_adma_check_cpb(struct ata_port *ap, int cpb_num, int force_err) int freeze = 0; ata_ehi_clear_desc(ehi); - ata_ehi_push_desc(ehi, "CPB resp_flags 0x%x", flags ); + __ata_ehi_push_desc(ehi, "CPB resp_flags 0x%x: ", flags ); if (flags & NV_CPB_RESP_ATA_ERR) { - ata_ehi_push_desc(ehi, ": ATA error"); + ata_ehi_push_desc(ehi, "ATA error"); ehi->err_mask |= AC_ERR_DEV; } else if (flags & NV_CPB_RESP_CMD_ERR) { - ata_ehi_push_desc(ehi, ": CMD error"); + ata_ehi_push_desc(ehi, "CMD error"); ehi->err_mask |= AC_ERR_DEV; } else if (flags & NV_CPB_RESP_CPB_ERR) { - ata_ehi_push_desc(ehi, ": CPB error"); + ata_ehi_push_desc(ehi, "CPB error"); ehi->err_mask |= AC_ERR_SYSTEM; freeze = 1; } else { /* notifier error, but no error in CPB flags? */ + ata_ehi_push_desc(ehi, "unknown"); ehi->err_mask |= AC_ERR_OTHER; freeze = 1; } @@ -854,20 +855,21 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance) struct ata_eh_info *ehi = &ap->eh_info; ata_ehi_clear_desc(ehi); - ata_ehi_push_desc(ehi, "ADMA status 0x%08x", status ); + __ata_ehi_push_desc(ehi, "ADMA status 0x%08x: ", status ); if (status & NV_ADMA_STAT_TIMEOUT) { ehi->err_mask |= AC_ERR_SYSTEM; - ata_ehi_push_desc(ehi, ": timeout"); + ata_ehi_push_desc(ehi, "timeout"); } else if (status & NV_ADMA_STAT_HOTPLUG) { ata_ehi_hotplugged(ehi); - ata_ehi_push_desc(ehi, ": hotplug"); + ata_ehi_push_desc(ehi, "hotplug"); } else if (status & NV_ADMA_STAT_HOTUNPLUG) { ata_ehi_hotplugged(ehi); - ata_ehi_push_desc(ehi, ": hot unplug"); + ata_ehi_push_desc(ehi, "hot unplug"); } else if (status & NV_ADMA_STAT_SERROR) { /* let libata analyze SError and figure out the cause */ - ata_ehi_push_desc(ehi, ": SError"); - } + ata_ehi_push_desc(ehi, "SError"); + } else + ata_ehi_push_desc(ehi, "unknown"); ata_port_freeze(ap); continue; } diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index e538edc1b4e..e201f1cab66 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -814,16 +814,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; } @@ -842,11 +842,11 @@ 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); } -- cgit v1.2.3 From 5335b729064e03319cd2d5219770451dbb1d7f67 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:40 +0900 Subject: libata: implement AC_ERR_NCQ When an NCQ command fails, all commands in flight are aborted and the offending one is reported using log page 10h. Depending on controller characteristics and LLD implementation, all commands may appear as having a device error due to shared TF status making it hard to determine what's actually going on. This patch adds AC_ERR_NCQ, marks the command reported by log page 10h with it and print extra "" after the error report for the command to help distinguishing the offending command. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-eh.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 96b184ebf70..19f9947bd96 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1195,7 +1195,7 @@ static void ata_eh_analyze_ncq_error(struct ata_port *ap) /* we've got the perpetrator, condemn it */ qc = __ata_qc_from_tag(ap, tag); memcpy(&qc->result_tf, &tf, sizeof(tf)); - qc->err_mask |= AC_ERR_DEV; + qc->err_mask |= AC_ERR_DEV | AC_ERR_NCQ; ehc->i.err_mask &= ~AC_ERR_DEV; } @@ -1616,7 +1616,7 @@ static void ata_eh_report(struct ata_port *ap) "cmd %02x/%02x:%02x:%02x:%02x:%02x/%02x:%02x:%02x:%02x:%02x/%02x " "tag %d cdb 0x%x data %u %s\n " "res %02x/%02x:%02x:%02x:%02x:%02x/%02x:%02x:%02x:%02x:%02x/%02x " - "Emask 0x%x (%s)\n", + "Emask 0x%x (%s)%s\n", cmd->command, cmd->feature, cmd->nsect, cmd->lbal, cmd->lbam, cmd->lbah, cmd->hob_feature, cmd->hob_nsect, @@ -1627,7 +1627,8 @@ static void ata_eh_report(struct ata_port *ap) res->lbal, res->lbam, res->lbah, res->hob_feature, res->hob_nsect, res->hob_lbal, res->hob_lbam, res->hob_lbah, - res->device, qc->err_mask, ata_err_string(qc->err_mask)); + res->device, qc->err_mask, ata_err_string(qc->err_mask), + qc->err_mask & AC_ERR_NCQ ? " " : ""); } } -- cgit v1.2.3 From da3dbb17a0e9a9ec7f5aed95f1fddadb790edc9d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:40 +0900 Subject: libata: make ->scr_read/write callbacks return error code Convert ->scr_read/write callbacks to return error code to better indicate failure. This will help handling of SCR_NOTIFICATION. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 23 ++++++++------- drivers/ata/libata-core.c | 21 +++++++------ drivers/ata/sata_inic162x.c | 16 +++++----- drivers/ata/sata_mv.c | 72 ++++++++++++++++++++++++++++++--------------- drivers/ata/sata_nv.c | 16 +++++----- drivers/ata/sata_promise.c | 25 +++++++++------- drivers/ata/sata_qstor.c | 16 +++++----- drivers/ata/sata_sil.c | 25 ++++++++++------ drivers/ata/sata_sil24.c | 17 +++++++---- drivers/ata/sata_sis.c | 22 +++++++------- drivers/ata/sata_svw.c | 13 ++++---- drivers/ata/sata_uli.c | 16 +++++----- drivers/ata/sata_via.c | 27 +++++++++-------- drivers/ata/sata_vsc.c | 13 ++++---- 14 files changed, 189 insertions(+), 133 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 210292cd8ad..e044d6477a0 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -216,8 +216,8 @@ struct ahci_port_priv { unsigned int ncq_saw_sdb:1; }; -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_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); +static int 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 unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); static void ahci_irq_clear(struct ata_port *ap); @@ -625,7 +625,7 @@ static void ahci_restore_initial_config(struct ata_host *host) (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ } -static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in) +static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) { unsigned int sc_reg; @@ -635,15 +635,15 @@ static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in) case SCR_ERROR: sc_reg = 2; break; case SCR_ACTIVE: sc_reg = 3; break; default: - return 0xffffffffU; + return -EINVAL; } - return readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + *val = readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + return 0; } -static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, - u32 val) +static int ahci_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) { unsigned int sc_reg; @@ -653,10 +653,11 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, case SCR_ERROR: sc_reg = 2; break; case SCR_ACTIVE: sc_reg = 3; break; default: - return; + return -EINVAL; } writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + return 0; } static void ahci_start_engine(struct ata_port *ap) @@ -1133,6 +1134,7 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class, static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class, unsigned long deadline) { + u32 serror; int rc; DPRINTK("ENTER\n"); @@ -1143,7 +1145,8 @@ static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class, deadline); /* vt8251 needs SError cleared for the port to operate */ - ahci_scr_write(ap, SCR_ERROR, ahci_scr_read(ap, SCR_ERROR)); + ahci_scr_read(ap, SCR_ERROR, &serror); + ahci_scr_write(ap, SCR_ERROR, serror); ahci_start_engine(ap); @@ -1265,7 +1268,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) ata_ehi_clear_desc(ehi); /* AHCI needs SError cleared; otherwise, it might lock up */ - serror = ahci_scr_read(ap, SCR_ERROR); + ahci_scr_read(ap, SCR_ERROR, &serror); ahci_scr_write(ap, SCR_ERROR, serror); /* analyze @irq_stat */ diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index ecbc3278238..5718c247e23 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5732,10 +5732,8 @@ int sata_scr_valid(struct ata_port *ap) */ int sata_scr_read(struct ata_port *ap, int reg, u32 *val) { - if (sata_scr_valid(ap)) { - *val = ap->ops->scr_read(ap, reg); - return 0; - } + if (sata_scr_valid(ap)) + return ap->ops->scr_read(ap, reg, val); return -EOPNOTSUPP; } @@ -5757,10 +5755,8 @@ int sata_scr_read(struct ata_port *ap, int reg, u32 *val) */ int sata_scr_write(struct ata_port *ap, int reg, u32 val) { - if (sata_scr_valid(ap)) { - ap->ops->scr_write(ap, reg, val); - return 0; - } + if (sata_scr_valid(ap)) + return ap->ops->scr_write(ap, reg, val); return -EOPNOTSUPP; } @@ -5781,10 +5777,13 @@ int sata_scr_write(struct ata_port *ap, int reg, u32 val) */ int sata_scr_write_flush(struct ata_port *ap, int reg, u32 val) { + int rc; + if (sata_scr_valid(ap)) { - ap->ops->scr_write(ap, reg, val); - ap->ops->scr_read(ap, reg); - return 0; + rc = ap->ops->scr_write(ap, reg, val); + if (rc == 0) + rc = ap->ops->scr_read(ap, reg, &val); + return rc; } return -EOPNOTSUPP; } diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c index 3de183461c3..a9c948d7604 100644 --- a/drivers/ata/sata_inic162x.c +++ b/drivers/ata/sata_inic162x.c @@ -190,34 +190,34 @@ static void inic_reset_port(void __iomem *port_base) writew(ctl, idma_ctl); } -static u32 inic_scr_read(struct ata_port *ap, unsigned sc_reg) +static int inic_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val) { void __iomem *scr_addr = ap->ioaddr.scr_addr; void __iomem *addr; - u32 val; if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) - return 0xffffffffU; + return -EINVAL; addr = scr_addr + scr_map[sc_reg] * 4; - val = readl(scr_addr + scr_map[sc_reg] * 4); + *val = readl(scr_addr + scr_map[sc_reg] * 4); /* this controller has stuck DIAG.N, ignore it */ if (sc_reg == SCR_ERROR) - val &= ~SERR_PHYRDY_CHG; - return val; + *val &= ~SERR_PHYRDY_CHG; + return 0; } -static void inic_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val) +static int inic_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val) { void __iomem *scr_addr = ap->ioaddr.scr_addr; void __iomem *addr; if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) - return; + return -EINVAL; addr = scr_addr + scr_map[sc_reg] * 4; writel(val, scr_addr + scr_map[sc_reg] * 4); + return 0; } /* diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index b4b737e081e..8ec520885b9 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -404,10 +404,10 @@ struct mv_host_priv { }; static void mv_irq_clear(struct ata_port *ap); -static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in); -static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val); -static u32 mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in); -static void mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val); +static int mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val); +static int mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val); +static int mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val); +static int mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val); static int mv_port_start(struct ata_port *ap); static void mv_port_stop(struct ata_port *ap); static void mv_qc_prep(struct ata_queued_cmd *qc); @@ -974,22 +974,26 @@ static unsigned int mv_scr_offset(unsigned int sc_reg_in) return ofs; } -static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in) +static int mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) { unsigned int ofs = mv_scr_offset(sc_reg_in); - if (ofs != 0xffffffffU) - return readl(mv_ap_base(ap) + ofs); - else - return (u32) ofs; + if (ofs != 0xffffffffU) { + *val = readl(mv_ap_base(ap) + ofs); + return 0; + } else + return -EINVAL; } -static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) +static int mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) { unsigned int ofs = mv_scr_offset(sc_reg_in); - if (ofs != 0xffffffffU) + if (ofs != 0xffffffffU) { writelfl(val, mv_ap_base(ap) + ofs); + return 0; + } else + return -EINVAL; } static void mv_edma_cfg(struct ata_port *ap, struct mv_host_priv *hpriv, @@ -1752,26 +1756,30 @@ static unsigned int mv5_scr_offset(unsigned int sc_reg_in) return ofs; } -static u32 mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in) +static int mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) { void __iomem *mmio = ap->host->iomap[MV_PRIMARY_BAR]; void __iomem *addr = mv5_phy_base(mmio, ap->port_no); unsigned int ofs = mv5_scr_offset(sc_reg_in); - if (ofs != 0xffffffffU) - return readl(addr + ofs); - else - return (u32) ofs; + if (ofs != 0xffffffffU) { + *val = readl(addr + ofs); + return 0; + } else + return -EINVAL; } -static void mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) +static int mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) { void __iomem *mmio = ap->host->iomap[MV_PRIMARY_BAR]; void __iomem *addr = mv5_phy_base(mmio, ap->port_no); unsigned int ofs = mv5_scr_offset(sc_reg_in); - if (ofs != 0xffffffffU) + if (ofs != 0xffffffffU) { writelfl(val, addr + ofs); + return 0; + } else + return -EINVAL; } static void mv5_reset_bus(struct pci_dev *pdev, void __iomem *mmio) @@ -2149,9 +2157,17 @@ static void mv_phy_reset(struct ata_port *ap, unsigned int *class, VPRINTK("ENTER, port %u, mmio 0x%p\n", ap->port_no, port_mmio); - DPRINTK("S-regs after ATA_RST: SStat 0x%08x SErr 0x%08x " - "SCtrl 0x%08x\n", mv_scr_read(ap, SCR_STATUS), - mv_scr_read(ap, SCR_ERROR), mv_scr_read(ap, SCR_CONTROL)); +#ifdef DEBUG + { + u32 sstatus, serror, scontrol; + + mv_scr_read(ap, SCR_STATUS, &sstatus); + mv_scr_read(ap, SCR_ERROR, &serror); + mv_scr_read(ap, SCR_CONTROL, &scontrol); + DPRINTK("S-regs after ATA_RST: SStat 0x%08x SErr 0x%08x " + "SCtrl 0x%08x\n", status, serror, scontrol); + } +#endif /* Issue COMRESET via SControl */ comreset_retry: @@ -2175,9 +2191,17 @@ comreset_retry: (retry-- > 0)) goto comreset_retry; - DPRINTK("S-regs after PHY wake: SStat 0x%08x SErr 0x%08x " - "SCtrl 0x%08x\n", mv_scr_read(ap, SCR_STATUS), - mv_scr_read(ap, SCR_ERROR), mv_scr_read(ap, SCR_CONTROL)); +#ifdef DEBUG + { + u32 sstatus, serror, scontrol; + + mv_scr_read(ap, SCR_STATUS, &sstatus); + mv_scr_read(ap, SCR_ERROR, &serror); + mv_scr_read(ap, SCR_CONTROL, &scontrol); + DPRINTK("S-regs after PHY wake: SStat 0x%08x SErr 0x%08x " + "SCtrl 0x%08x\n", sstatus, serror, scontrol); + } +#endif if (ata_port_offline(ap)) { *class = ATA_DEV_NONE; diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 5d943da042f..0b58c4df6fd 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -236,8 +236,8 @@ static void nv_ck804_host_stop(struct ata_host *host); static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance); static irqreturn_t nv_nf2_interrupt(int irq, void *dev_instance); static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance); -static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static int nv_scr_read (struct ata_port *ap, unsigned int sc_reg, u32 *val); +static int nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static void nv_nf2_freeze(struct ata_port *ap); static void nv_nf2_thaw(struct ata_port *ap); @@ -1393,20 +1393,22 @@ static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance) return ret; } -static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg) +static int nv_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) - return 0xffffffffU; + return -EINVAL; - return ioread32(ap->ioaddr.scr_addr + (sc_reg * 4)); + *val = ioread32(ap->ioaddr.scr_addr + (sc_reg * 4)); + return 0; } -static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +static int nv_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) - return; + return -EINVAL; iowrite32(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + return 0; } static void nv_nf2_freeze(struct ata_port *ap) diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index d2fcb9a6bec..d39ebc23c4a 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -128,8 +128,8 @@ struct pdc_port_priv { dma_addr_t pkt_dma; }; -static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static int pdc_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); +static int pdc_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); static int pdc_common_port_start(struct ata_port *ap); static int pdc_sata_port_start(struct ata_port *ap); @@ -427,19 +427,20 @@ static int pdc_sata_cable_detect(struct ata_port *ap) return ATA_CBL_SATA; } -static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) +static int pdc_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) - return 0xffffffffU; - return readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + return -EINVAL; + *val = readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + return 0; } -static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, - u32 val) +static int pdc_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) - return; + return -EINVAL; writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + return 0; } static void pdc_atapi_pkt(struct ata_queued_cmd *qc) @@ -642,8 +643,12 @@ static void pdc_error_intr(struct ata_port *ap, struct ata_queued_cmd *qc, | PDC_PCI_SYS_ERR | PDC1_PCI_PARITY_ERR)) ac_err_mask |= AC_ERR_HOST_BUS; - if (sata_scr_valid(ap)) - ehi->serror |= pdc_sata_scr_read(ap, SCR_ERROR); + if (sata_scr_valid(ap)) { + u32 serror; + + pdc_sata_scr_read(ap, SCR_ERROR, &serror); + ehi->serror |= serror; + } qc->err_mask |= ac_err_mask; diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 5aef4ac3701..c8f9242e7f4 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -111,8 +111,8 @@ struct qs_port_priv { qs_state_t state; }; -static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static int qs_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); +static int qs_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); static int qs_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); static int qs_port_start(struct ata_port *ap); static void qs_host_stop(struct ata_host *host); @@ -255,18 +255,20 @@ static void qs_eng_timeout(struct ata_port *ap) ata_eng_timeout(ap); } -static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg) +static int qs_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) - return ~0U; - return readl(ap->ioaddr.scr_addr + (sc_reg * 8)); + return -EINVAL; + *val = readl(ap->ioaddr.scr_addr + (sc_reg * 8)); + return 0; } -static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +static int qs_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) - return; + return -EINVAL; writel(val, ap->ioaddr.scr_addr + (sc_reg * 8)); + return 0; } static unsigned int qs_fill_sg(struct ata_queued_cmd *qc) diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index 2a86dc4598d..db676375895 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -115,8 +115,8 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); static int sil_pci_device_resume(struct pci_dev *pdev); #endif static void sil_dev_config(struct ata_device *dev); -static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static int sil_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); +static int sil_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); static int sil_set_mode (struct ata_port *ap, struct ata_device **r_failed); static void sil_freeze(struct ata_port *ap); static void sil_thaw(struct ata_port *ap); @@ -350,19 +350,26 @@ static inline void __iomem *sil_scr_addr(struct ata_port *ap, unsigned int sc_re return NULL; } -static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg) +static int sil_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) { void __iomem *mmio = sil_scr_addr(ap, sc_reg); - if (mmio) - return readl(mmio); - return 0xffffffffU; + + if (mmio) { + *val = readl(mmio); + return 0; + } + return -EINVAL; } -static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +static int sil_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) { void __iomem *mmio = sil_scr_addr(ap, sc_reg); - if (mmio) + + if (mmio) { writel(val, mmio); + return 0; + } + return -EINVAL; } static void sil_host_intr(struct ata_port *ap, u32 bmdma2) @@ -378,7 +385,7 @@ static void sil_host_intr(struct ata_port *ap, u32 bmdma2) * controllers continue to assert IRQ as long as * SError bits are pending. Clear SError immediately. */ - serror = sil_scr_read(ap, SCR_ERROR); + sil_scr_read(ap, SCR_ERROR, &serror); sil_scr_write(ap, SCR_ERROR, serror); /* Trigger hotplug and accumulate SError only if the diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index e201f1cab66..46fbbe7f121 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); @@ -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) diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c index 33716b00c6b..31a2f55aae6 100644 --- a/drivers/ata/sata_sis.c +++ b/drivers/ata/sata_sis.c @@ -64,8 +64,8 @@ enum { }; static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static int sis_scr_read (struct ata_port *ap, unsigned int sc_reg, u32 *val); +static int sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static const struct pci_device_id sis_pci_tbl[] = { { PCI_VDEVICE(SI, 0x0180), sis_180 }, /* SiS 964/180 */ @@ -207,36 +207,37 @@ static void sis_scr_cfg_write (struct ata_port *ap, unsigned int sc_reg, u32 val pci_write_config_dword(pdev, cfg_addr+0x10, val); } -static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg) +static int sis_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u32 val, val2 = 0; u8 pmr; if (sc_reg > SCR_CONTROL) - return 0xffffffffU; + return -EINVAL; if (ap->flags & SIS_FLAG_CFGSCR) return sis_scr_cfg_read(ap, sc_reg); pci_read_config_byte(pdev, SIS_PMR, &pmr); - val = ioread32(ap->ioaddr.scr_addr + (sc_reg * 4)); + *val = ioread32(ap->ioaddr.scr_addr + (sc_reg * 4)); if ((pdev->device == 0x0182) || (pdev->device == 0x0183) || (pdev->device == 0x1182) || (pmr & SIS_PMR_COMBINED)) - val2 = ioread32(ap->ioaddr.scr_addr + (sc_reg * 4) + 0x10); + *val |= ioread32(ap->ioaddr.scr_addr + (sc_reg * 4) + 0x10); + + *val &= 0xfffffffb; - return (val | val2) & 0xfffffffb; + return 0; } -static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +static int sis_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 pmr; if (sc_reg > SCR_CONTROL) - return; + return -EINVAL; pci_read_config_byte(pdev, SIS_PMR, &pmr); @@ -248,6 +249,7 @@ static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) (pdev->device == 0x1182) || (pmr & SIS_PMR_COMBINED)) iowrite32(val, ap->ioaddr.scr_addr + (sc_reg * 4)+0x10); } + return 0; } static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index 63fe99afd59..92e87707503 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -103,20 +103,21 @@ static int k2_sata_check_atapi_dma(struct ata_queued_cmd *qc) return 0; } -static u32 k2_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) +static int k2_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) - return 0xffffffffU; - return readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + return -EINVAL; + *val = readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + return 0; } -static void k2_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, - u32 val) +static int k2_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) - return; + return -EINVAL; writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + return 0; } diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index b52f83ab056..78c28512f01 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -57,8 +57,8 @@ struct uli_priv { }; static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static int uli_scr_read (struct ata_port *ap, unsigned int sc_reg, u32 *val); +static int uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static const struct pci_device_id uli_pci_tbl[] = { { PCI_VDEVICE(AL, 0x5289), uli_5289 }, @@ -164,20 +164,22 @@ static void uli_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val) pci_write_config_dword(pdev, cfg_addr, val); } -static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg) +static int uli_scr_read (struct ata_port *ap, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) - return 0xffffffffU; + return -EINVAL; - return uli_scr_cfg_read(ap, sc_reg); + *val = uli_scr_cfg_read(ap, sc_reg); + return 0; } -static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +static int uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0 - return; + return -EINVAL; uli_scr_cfg_write(ap, sc_reg, val); + return 0; } static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index c4124475f75..86b7bfc1732 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -72,8 +72,8 @@ enum { }; static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg); -static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static int svia_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); +static int svia_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); static void svia_noop_freeze(struct ata_port *ap); static void vt6420_error_handler(struct ata_port *ap); static int vt6421_pata_cable_detect(struct ata_port *ap); @@ -249,18 +249,20 @@ MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, svia_pci_tbl); MODULE_VERSION(DRV_VERSION); -static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg) +static int svia_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) - return 0xffffffffU; - return ioread32(ap->ioaddr.scr_addr + (4 * sc_reg)); + return -EINVAL; + *val = ioread32(ap->ioaddr.scr_addr + (4 * sc_reg)); + return 0; } -static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +static int svia_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) - return; + return -EINVAL; iowrite32(val, ap->ioaddr.scr_addr + (4 * sc_reg)); + return 0; } static void svia_noop_freeze(struct ata_port *ap) @@ -305,18 +307,19 @@ static int vt6420_prereset(struct ata_port *ap, unsigned long deadline) /* Resume phy. This is the old SATA resume sequence */ svia_scr_write(ap, SCR_CONTROL, 0x300); - svia_scr_read(ap, SCR_CONTROL); /* flush */ + svia_scr_read(ap, SCR_CONTROL, &scontrol); /* flush */ /* wait for phy to become ready, if necessary */ do { msleep(200); - if ((svia_scr_read(ap, SCR_STATUS) & 0xf) != 1) + svia_scr_read(ap, SCR_STATUS, &sstatus); + if ((sstatus & 0xf) != 1) break; } while (time_before(jiffies, timeout)); /* open code sata_print_link_status() */ - sstatus = svia_scr_read(ap, SCR_STATUS); - scontrol = svia_scr_read(ap, SCR_CONTROL); + svia_scr_read(ap, SCR_STATUS, &sstatus); + svia_scr_read(ap, SCR_CONTROL, &scontrol); online = (sstatus & 0xf) == 0x3; @@ -325,7 +328,7 @@ static int vt6420_prereset(struct ata_port *ap, unsigned long deadline) online ? "up" : "down", sstatus, scontrol); /* SStatus is read one more time */ - svia_scr_read(ap, SCR_STATUS); + svia_scr_read(ap, SCR_STATUS, &sstatus); if (!online) { /* tell EH to bail */ diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index 1b5d81faa10..24344d0d057 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -98,20 +98,21 @@ enum { VSC_SATA_INT_PHY_CHANGE), }; -static u32 vsc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) +static int vsc_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) - return 0xffffffffU; - return readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + return -EINVAL; + *val = readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + return 0; } -static void vsc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, - u32 val) +static int vsc_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) - return; + return -EINVAL; writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + return 0; } -- cgit v1.2.3 From 274c1fde5c775a53331427d454745b9ecc5c783b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:40 +0900 Subject: ahci: make NO_NCQ handling more consistent ahci_save_initial_config() is responsible for reading, screening the host CAP register and storing the modified result into hpriv->cap for the rest of the driver. Move ATA_FLAG_NO_NCQ handling into ahci_save_initial_config(). It's more consistent this way and the rest of the driver can always refer to hpriv->cap to determine configured capability. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index e044d6477a0..9475e5619af 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -545,13 +545,19 @@ static void ahci_save_initial_config(struct pci_dev *pdev, hpriv->saved_cap = cap = readl(mmio + HOST_CAP); hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL); - /* some chips lie about 64bit support */ + /* some chips have errata preventing 64bit use */ if ((cap & HOST_CAP_64) && (pi->flags & AHCI_FLAG_32BIT_ONLY)) { dev_printk(KERN_INFO, &pdev->dev, "controller can't do 64bit DMA, forcing 32bit\n"); cap &= ~HOST_CAP_64; } + if ((cap & HOST_CAP_NCQ) && (pi->flags & AHCI_FLAG_NO_NCQ)) { + dev_printk(KERN_INFO, &pdev->dev, + "controller can't do NCQ, turning off CAP_NCQ\n"); + cap &= ~HOST_CAP_NCQ; + } + /* fixup zero port_map */ if (!port_map) { port_map = (1 << ahci_nr_ports(cap)) - 1; @@ -1822,7 +1828,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ahci_save_initial_config(pdev, &pi, hpriv); /* prepare host */ - if (!(pi.flags & AHCI_FLAG_NO_NCQ) && (hpriv->cap & HOST_CAP_NCQ)) + if (hpriv->cap & HOST_CAP_NCQ) pi.flags |= ATA_FLAG_NCQ; host = ata_host_alloc_pinfo(&pdev->dev, ppi, fls(hpriv->port_map)); -- cgit v1.2.3 From 203ef6c456ad70e660cca67921d3d872b13aa516 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:40 +0900 Subject: ahci: implement SCR_NOTIFICATION r/w Make ahci_scr_read/write() handle SCR_NOTIFICATION if the controller supports it. Also, print "sntf" in the cap line if supported. While at it, convert eight space into a tab in ahci_print_info(). Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 68 +++++++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 9475e5619af..de8bffbf4bd 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -99,6 +99,7 @@ enum { HOST_CAP_SSC = (1 << 14), /* Slumber capable */ HOST_CAP_CLO = (1 << 24), /* Command List Override support */ HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */ + HOST_CAP_SNTF = (1 << 29), /* SNotification register */ HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ @@ -113,11 +114,11 @@ enum { PORT_TFDATA = 0x20, /* taskfile data */ PORT_SIG = 0x24, /* device TF signature */ PORT_CMD_ISSUE = 0x38, /* command issue */ - PORT_SCR = 0x28, /* SATA phy register block */ PORT_SCR_STAT = 0x28, /* SATA phy register: SStatus */ PORT_SCR_CTL = 0x2c, /* SATA phy register: SControl */ PORT_SCR_ERR = 0x30, /* SATA phy register: SError */ PORT_SCR_ACT = 0x34, /* SATA phy register: SActive */ + PORT_SCR_NTF = 0x3c, /* SATA phy register: SNotification */ /* PORT_IRQ_{STAT,MASK} bits */ PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */ @@ -631,39 +632,45 @@ static void ahci_restore_initial_config(struct ata_host *host) (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ } -static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) +static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg) { - unsigned int sc_reg; - - switch (sc_reg_in) { - case SCR_STATUS: sc_reg = 0; break; - case SCR_CONTROL: sc_reg = 1; break; - case SCR_ERROR: sc_reg = 2; break; - case SCR_ACTIVE: sc_reg = 3; break; - default: - return -EINVAL; - } + static const int offset[] = { + [SCR_STATUS] = PORT_SCR_STAT, + [SCR_CONTROL] = PORT_SCR_CTL, + [SCR_ERROR] = PORT_SCR_ERR, + [SCR_ACTIVE] = PORT_SCR_ACT, + [SCR_NOTIFICATION] = PORT_SCR_NTF, + }; + struct ahci_host_priv *hpriv = ap->host->private_data; - *val = readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + if (sc_reg < ARRAY_SIZE(offset) && + (sc_reg != SCR_NOTIFICATION || (hpriv->cap & HOST_CAP_SNTF))) + return offset[sc_reg]; return 0; } - -static int ahci_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) +static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) { - unsigned int sc_reg; - - switch (sc_reg_in) { - case SCR_STATUS: sc_reg = 0; break; - case SCR_CONTROL: sc_reg = 1; break; - case SCR_ERROR: sc_reg = 2; break; - case SCR_ACTIVE: sc_reg = 3; break; - default: - return -EINVAL; + void __iomem *port_mmio = ahci_port_base(ap); + int offset = ahci_scr_offset(ap, sc_reg); + + if (offset) { + *val = readl(port_mmio + offset); + return 0; } + return -EINVAL; +} - writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); - return 0; +static int ahci_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +{ + void __iomem *port_mmio = ahci_port_base(ap); + int offset = ahci_scr_offset(ap, sc_reg); + + if (offset) { + writel(val, port_mmio + offset); + return 0; + } + return -EINVAL; } static void ahci_start_engine(struct ata_port *ap) @@ -1768,12 +1775,13 @@ static void ahci_print_info(struct ata_host *host) dev_printk(KERN_INFO, &pdev->dev, "flags: " - "%s%s%s%s%s%s" - "%s%s%s%s%s%s%s\n" + "%s%s%s%s%s%s%s" + "%s%s%s%s%s%s%s\n" , cap & (1 << 31) ? "64bit " : "", cap & (1 << 30) ? "ncq " : "", + cap & (1 << 29) ? "sntf " : "", cap & (1 << 28) ? "ilck " : "", cap & (1 << 27) ? "stag " : "", cap & (1 << 26) ? "pm " : "", @@ -1842,10 +1850,8 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) void __iomem *port_mmio = ahci_port_base(ap); /* standard SATA port setup */ - if (hpriv->port_map & (1 << i)) { + if (hpriv->port_map & (1 << i)) ap->ioaddr.cmd_addr = port_mmio; - ap->ioaddr.scr_addr = port_mmio + PORT_SCR; - } /* disabled/not-implemented port */ else -- cgit v1.2.3 From badc2341579511a247f5993865aa68379e283c5c Mon Sep 17 00:00:00 2001 From: su henry Date: Fri, 20 Jul 2007 08:07:46 -0400 Subject: The SATA controller device ID is different according to the onchip SATA type set in the system BIOS: Device Device ID SATA in IDE mode 0x4390 SATA in AHCI mode 0x4391 SATA in non-raid5 driver 0x4392 SATA in raid5 driver 0x4393 Although the device ID is different, they use the same AHCI driver .The attached file is the patch for adding these device IDs for ATI SB700. Signed-off-by: henry.su.ati@gmail.com Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index de8bffbf4bd..06f212ff2b4 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -418,7 +418,10 @@ static const struct pci_device_id ahci_pci_tbl[] = { /* ATI */ { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */ - { PCI_VDEVICE(ATI, 0x4390), board_ahci_sb600 }, /* ATI SB700 */ + { PCI_VDEVICE(ATI, 0x4390), board_ahci_sb600 }, /* ATI SB700 IDE */ + { PCI_VDEVICE(ATI, 0x4391), board_ahci_sb600 }, /* ATI SB700 AHCI */ + { PCI_VDEVICE(ATI, 0x4392), board_ahci_sb600 }, /* ATI SB700 nraid5 */ + { PCI_VDEVICE(ATI, 0x4393), board_ahci_sb600 }, /* ATI SB700 raid5 */ /* VIA */ { PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */ -- cgit v1.2.3 From 008a78961ec72990d09d7625ef9499d7317d040d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:40 +0900 Subject: libata: improve SATA PHY speed down logic sata_down_spd_limit() first reads the current SPD from SStatus and limit the speed to the lower one of one below the current limit or one below the current SPD in SStatus. SPD may not be accessible or valid when SPD down is requested making sata_down_spd_limit() fail when it's most needed. This patch makes the current SPD cached after each successful reset and forces GEN I speed (1.5Gbps) if neither of SStatus or the cached value is valid, so sata_down_spd_limit() is now guaranteed to lower the speed limit if lower speed is available. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 29 ++++++++++++++++++++++------- drivers/ata/libata-eh.c | 6 ++++++ 2 files changed, 28 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 5718c247e23..c325b7a4246 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2389,21 +2389,35 @@ int sata_down_spd_limit(struct ata_port *ap) u32 sstatus, spd, mask; int rc, highbit; + if (!sata_scr_valid(ap)) + return -EOPNOTSUPP; + + /* If SCR can be read, use it to determine the current SPD. + * If not, use cached value in ap->sata_spd. + */ rc = sata_scr_read(ap, SCR_STATUS, &sstatus); - if (rc) - return rc; + if (rc == 0) + spd = (sstatus >> 4) & 0xf; + else + spd = ap->sata_spd; mask = ap->sata_spd_limit; if (mask <= 1) return -EINVAL; + + /* unconditionally mask off the highest bit */ highbit = fls(mask) - 1; mask &= ~(1 << highbit); - spd = (sstatus >> 4) & 0xf; - if (spd <= 1) - return -EINVAL; - spd--; - mask &= (1 << spd) - 1; + /* Mask off all speeds higher than or equal to the current + * one. Force 1.5Gbps if current SPD is not available. + */ + if (spd > 1) + mask &= (1 << (spd - 1)) - 1; + else + mask &= 1; + + /* were we already at the bottom? */ if (!mask) return -EINVAL; @@ -5995,6 +6009,7 @@ void ata_dev_init(struct ata_device *dev) /* SATA spd limit is bound to the first device */ ap->sata_spd_limit = ap->hw_sata_spd_limit; + ap->sata_spd = 0; /* High bits of dev->flags are used to record warm plug * requests which occur asynchronously. Synchronize using diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 19f9947bd96..183eaf466d4 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1799,12 +1799,18 @@ static int ata_eh_reset(struct ata_port *ap, int classify, } if (rc == 0) { + u32 sstatus; + /* After the reset, the device state is PIO 0 and the * controller state is undefined. Record the mode. */ for (i = 0; i < ATA_MAX_DEVICES; i++) ap->device[i].pio_mode = XFER_PIO_0; + /* record current link speed */ + if (sata_scr_read(ap, SCR_STATUS, &sstatus) == 0) + ap->sata_spd = (sstatus >> 4) & 0xf; + if (postreset) postreset(ap, classes); -- cgit v1.2.3 From f1545154a5c96590b1992aac8ee1e2c445e301ed Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:40 +0900 Subject: libata: quickly trigger SATA SPD down after debouncing failed Debouncing failure is a good indicator of basic link problem. Use -EPIPE to indicate debouncing failure and make ata_eh_reset() invoke sata_down_spd_limit() if the error occurs during reset. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 6 ++++-- drivers/ata/libata-eh.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c325b7a4246..05922e9f4fa 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3267,9 +3267,11 @@ int sata_phy_debounce(struct ata_port *ap, const unsigned long *params, last = cur; last_jiffies = jiffies; - /* check deadline */ + /* Check deadline. If debouncing failed, return + * -EPIPE to tell upper layer to lower link speed. + */ if (time_after(jiffies, deadline)) - return -EBUSY; + return -EPIPE; } } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 183eaf466d4..1a4397ae9e4 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1790,7 +1790,7 @@ static int ata_eh_reset(struct ata_port *ap, int classify, schedule_timeout_uninterruptible(delta); } - if (reset == hardreset && + if (rc == -EPIPE || try == ARRAY_SIZE(ata_eh_reset_timeouts) - 1) sata_down_spd_limit(ap); if (hardreset) -- cgit v1.2.3 From 1ae463171cc1b1ea6dad7bcb298e96c073e7373e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:40 +0900 Subject: libata: improve SCSI scan failure handling SCSI scan may fail due to memory allocation failure even if EH is not in progress. Due to use of GFP_ATOMIC in SCSI scan path, allocation failure isn't too rare especially while probing multiple devices at once which is the case when a bunch of devices are connected to PMP. This patch moves SCSI scan failure detetion logic from ata_scsi_hotplug() to ata_scsi_scan_host() and implement synchronous scan behavior. The synchronous path sleeps briefly and repeats SCSI scan if some devices aren't attached properly. It contains robust retry loop to minimize the chance of device misdetection during boot and falls back to async retry if everything fails. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 2 +- drivers/ata/libata-scsi.c | 63 +++++++++++++++++++++++++++++++++++------------ drivers/ata/libata.h | 2 +- 3 files changed, 49 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 05922e9f4fa..35b62129383 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6453,7 +6453,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; - ata_scsi_scan_host(ap); + ata_scsi_scan_host(ap, 1); } return 0; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index cfde22da07a..12ac0b511f7 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2947,17 +2947,22 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht) return rc; } -void ata_scsi_scan_host(struct ata_port *ap) +void ata_scsi_scan_host(struct ata_port *ap, int sync) { + int tries = 5; + struct ata_device *last_failed_dev = NULL; + struct ata_device *dev; unsigned int i; if (ap->flags & ATA_FLAG_DISABLED) return; + repeat: for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; struct scsi_device *sdev; + dev = &ap->device[i]; + if (!ata_dev_enabled(dev) || dev->sdev) continue; @@ -2967,6 +2972,45 @@ void ata_scsi_scan_host(struct ata_port *ap) scsi_device_put(sdev); } } + + /* If we scanned while EH was in progress or allocation + * failure occurred, scan would have failed silently. Check + * whether all devices are attached. + */ + for (i = 0; i < ATA_MAX_DEVICES; i++) { + dev = &ap->device[i]; + if (ata_dev_enabled(dev) && !dev->sdev) + break; + } + if (i == ATA_MAX_DEVICES) + return; + + /* we're missing some SCSI devices */ + if (sync) { + /* If caller requested synchrnous scan && we've made + * any progress, sleep briefly and repeat. + */ + if (dev != last_failed_dev) { + msleep(100); + last_failed_dev = dev; + goto repeat; + } + + /* We might be failing to detect boot device, give it + * a few more chances. + */ + if (--tries) { + msleep(100); + goto repeat; + } + + ata_port_printk(ap, KERN_ERR, "WARNING: synchronous SCSI scan " + "failed without making any progress,\n" + " switching to async\n"); + } + + queue_delayed_work(ata_aux_wq, &ap->hotplug_task, + round_jiffies_relative(HZ)); } /** @@ -3093,20 +3137,7 @@ void ata_scsi_hotplug(struct work_struct *work) } /* scan for new ones */ - ata_scsi_scan_host(ap); - - /* If we scanned while EH was in progress, scan would have - * failed silently. Requeue if there are enabled but - * unattached devices. - */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; - if (ata_dev_enabled(dev) && !dev->sdev) { - queue_delayed_work(ata_aux_wq, &ap->hotplug_task, - round_jiffies_relative(HZ)); - break; - } - } + ata_scsi_scan_host(ap, 0); DPRINTK("EXIT\n"); } diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index ba17fc5f2e9..48836b22ce2 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -112,7 +112,7 @@ static inline int ata_acpi_on_devcfg(struct ata_device *adev) { return 0; } /* libata-scsi.c */ extern int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht); -extern void ata_scsi_scan_host(struct ata_port *ap); +extern void ata_scsi_scan_host(struct ata_port *ap, int sync); extern int ata_scsi_offline_dev(struct ata_device *dev); extern void ata_scsi_hotplug(struct work_struct *work); extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, -- cgit v1.2.3 From fccb6ea5c240b9f29baa55448488fd6aee49f5a5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:41 +0900 Subject: libata: clear HOTPLUG flag after a reset ATA_EHI_HOTPLUGGED is a hint for reset functions indicating the the port might have gone through hotplug/unplug just before entering EH. Reset functions modify their behaviors a bit to handle the situation better - e.g. using longer debouncing delay. Currently, once HOTPLUG is set, it isn't cleared till the end of EH. This is unnecessary and makes EH take longer. Clear the HOTPLUGGED flag after a reset try (successful or not). Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-eh.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 1a4397ae9e4..a5a8f845306 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1714,7 +1714,7 @@ static int ata_eh_reset(struct ata_port *ap, int classify, } else ata_port_printk(ap, KERN_ERR, "prereset failed (errno=%d)\n", rc); - return rc; + goto out; } } @@ -1727,7 +1727,8 @@ static int ata_eh_reset(struct ata_port *ap, int classify, /* prereset told us not to reset, bang classes and return */ for (i = 0; i < ATA_MAX_DEVICES; i++) classes[i] = ATA_DEV_NONE; - return 0; + rc = 0; + goto out; } /* did prereset() screw up? if so, fix up to avoid oopsing */ @@ -1763,7 +1764,8 @@ static int ata_eh_reset(struct ata_port *ap, int classify, ata_port_printk(ap, KERN_ERR, "follow-up softreset required " "but no softreset avaliable\n"); - return -EINVAL; + rc = -EINVAL; + goto out; } ata_eh_about_to_do(ap, NULL, ATA_EH_RESET_MASK); @@ -1773,7 +1775,8 @@ static int ata_eh_reset(struct ata_port *ap, int classify, classes[0] == ATA_DEV_UNKNOWN) { ata_port_printk(ap, KERN_ERR, "classification failed\n"); - return -EINVAL; + rc = -EINVAL; + goto out; } } @@ -1818,7 +1821,9 @@ static int ata_eh_reset(struct ata_port *ap, int classify, ata_eh_done(ap, NULL, ehc->i.action & ATA_EH_RESET_MASK); ehc->i.action |= ATA_EH_REVALIDATE; } - + out: + /* clear hotplug flag */ + ehc->i.flags &= ~ATA_EHI_HOTPLUGGED; return rc; } -- cgit v1.2.3 From 4e57c517b3cbaceb7438eeec879ca129fc17442c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:41 +0900 Subject: libata: schedule probing after SError access failure during autopsy If SError isn't accessible, EH can't tell whether hotplug has happened or not. Report SError read failure with AC_ERR_OTHER and schedule probing with hardreset. This will be mainly useful for PMPs. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-eh.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index a5a8f845306..e7e2ba24ce6 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1478,8 +1478,12 @@ static void ata_eh_autopsy(struct ata_port *ap) if (rc == 0) { ehc->i.serror |= serror; ata_eh_analyze_serror(ap); - } else if (rc != -EOPNOTSUPP) + } else if (rc != -EOPNOTSUPP) { + /* SError read failed, force hardreset and probing */ + ata_ehi_schedule_probe(&ehc->i); ehc->i.action |= ATA_EH_HARDRESET; + ehc->i.err_mask |= AC_ERR_OTHER; + } /* analyze NCQ failure */ ata_eh_analyze_ncq_error(ap); -- cgit v1.2.3 From 5ddf24c5ea9d715dc4f5d5d5dd1c9337d90466dc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Jul 2007 14:29:41 +0900 Subject: libata: implement EH fast drain In most cases, when EH is scheduled, all in-flight commands are aborted causing EH to kick in immediately. However, in some cases (especially with PMP), it's unclear which commands are affected by the error condition and although aborting all in-flight commands work, it isn't optimal and may cause unnecessary disruption. On the other hand, waiting for in-flight commands to drain themselves can take up to 30seconds. This patch implements EH fast drain to handle such situations. It gives in-flight commands some time to finish up but doesn't wait for too long. After EH is scheduled, fast drain timer is started and if no other completion occurs in ATA_EH_FASTDRAIN_INTERVAL all in-flight commands are aborted. If any completion occurred in the interval, the port is given another interval to finish up itself. Currently ATA_EH_FASTDRAIN_INTERVAL is 3 secs which should be enough for finishing up most commands. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 3 ++ drivers/ata/libata-eh.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++- drivers/ata/libata.h | 1 + 3 files changed, 101 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 35b62129383..6001aae0b88 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6077,6 +6077,9 @@ struct ata_port *ata_port_alloc(struct ata_host *host) INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan); INIT_LIST_HEAD(&ap->eh_done_q); init_waitqueue_head(&ap->eh_wait_q); + init_timer_deferrable(&ap->fastdrain_timer); + ap->fastdrain_timer.function = ata_eh_fastdrain_timerfn; + ap->fastdrain_timer.data = (unsigned long)ap; ap->cbl = ATA_CBL_NONE; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index e7e2ba24ce6..ac6ceed4bb6 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -56,6 +56,7 @@ enum { */ enum { ATA_EH_PRERESET_TIMEOUT = 10 * HZ, + ATA_EH_FASTDRAIN_INTERVAL = 3 * HZ, }; /* The following table determines how we sequence resets. Each entry @@ -361,6 +362,9 @@ void ata_scsi_error(struct Scsi_Host *host) repeat: /* invoke error handler */ if (ap->ops->error_handler) { + /* kill fast drain timer */ + del_timer_sync(&ap->fastdrain_timer); + /* process port resume request */ ata_eh_handle_port_resume(ap); @@ -576,6 +580,94 @@ void ata_eng_timeout(struct ata_port *ap) DPRINTK("EXIT\n"); } +static int ata_eh_nr_in_flight(struct ata_port *ap) +{ + unsigned int tag; + int nr = 0; + + /* count only non-internal commands */ + for (tag = 0; tag < ATA_MAX_QUEUE - 1; tag++) + if (ata_qc_from_tag(ap, tag)) + nr++; + + return nr; +} + +void ata_eh_fastdrain_timerfn(unsigned long arg) +{ + struct ata_port *ap = (void *)arg; + unsigned long flags; + int cnt; + + spin_lock_irqsave(ap->lock, flags); + + cnt = ata_eh_nr_in_flight(ap); + + /* are we done? */ + if (!cnt) + goto out_unlock; + + if (cnt == ap->fastdrain_cnt) { + unsigned int tag; + + /* No progress during the last interval, tag all + * in-flight qcs as timed out and freeze the port. + */ + for (tag = 0; tag < ATA_MAX_QUEUE - 1; tag++) { + struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag); + if (qc) + qc->err_mask |= AC_ERR_TIMEOUT; + } + + ata_port_freeze(ap); + } else { + /* some qcs have finished, give it another chance */ + ap->fastdrain_cnt = cnt; + ap->fastdrain_timer.expires = + jiffies + ATA_EH_FASTDRAIN_INTERVAL; + add_timer(&ap->fastdrain_timer); + } + + out_unlock: + spin_unlock_irqrestore(ap->lock, flags); +} + +/** + * ata_eh_set_pending - set ATA_PFLAG_EH_PENDING and activate fast drain + * @ap: target ATA port + * @fastdrain: activate fast drain + * + * Set ATA_PFLAG_EH_PENDING and activate fast drain if @fastdrain + * is non-zero and EH wasn't pending before. Fast drain ensures + * that EH kicks in in timely manner. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +static void ata_eh_set_pending(struct ata_port *ap, int fastdrain) +{ + int cnt; + + /* already scheduled? */ + if (ap->pflags & ATA_PFLAG_EH_PENDING) + return; + + ap->pflags |= ATA_PFLAG_EH_PENDING; + + if (!fastdrain) + return; + + /* do we have in-flight qcs? */ + cnt = ata_eh_nr_in_flight(ap); + if (!cnt) + return; + + /* activate fast drain */ + ap->fastdrain_cnt = cnt; + ap->fastdrain_timer.expires = jiffies + ATA_EH_FASTDRAIN_INTERVAL; + add_timer(&ap->fastdrain_timer); +} + /** * ata_qc_schedule_eh - schedule qc for error handling * @qc: command to schedule error handling for @@ -593,7 +685,7 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc) WARN_ON(!ap->ops->error_handler); qc->flags |= ATA_QCFLAG_FAILED; - qc->ap->pflags |= ATA_PFLAG_EH_PENDING; + ata_eh_set_pending(ap, 1); /* The following will fail if timeout has already expired. * ata_scsi_error() takes care of such scmds on EH entry. @@ -620,7 +712,7 @@ void ata_port_schedule_eh(struct ata_port *ap) if (ap->pflags & ATA_PFLAG_INITIALIZING) return; - ap->pflags |= ATA_PFLAG_EH_PENDING; + ata_eh_set_pending(ap, 1); scsi_schedule_eh(ap->scsi_host); DPRINTK("port EH scheduled\n"); @@ -644,6 +736,9 @@ int ata_port_abort(struct ata_port *ap) WARN_ON(!ap->ops->error_handler); + /* we're gonna abort all commands, no need for fast drain */ + ata_eh_set_pending(ap, 0); + for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag); diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 48836b22ce2..564cd234c80 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -151,6 +151,7 @@ extern int ata_bus_probe(struct ata_port *ap); extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); extern void ata_scsi_error(struct Scsi_Host *host); extern void ata_port_wait_eh(struct ata_port *ap); +extern void ata_eh_fastdrain_timerfn(unsigned long arg); extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc); /* libata-sff.c */ -- cgit v1.2.3