From: gwendal@google.com (Gwendal Grignou)
To: gwendal@gmail.com, tj@kernel.org
Cc: linux-ide@vger.kernel.org
Subject: [PATH 2/2] [sata_mv] enable FIS Based Switching when a Port Multiplier is connected
Date: Sun, 5 Oct 2008 18:28:26 -0700 (PDT) [thread overview]
Message-ID: <20081006012826.BCCFF2E29AC@localhost> (raw)
When sata_mv controller is connected to disks via port multiplier
but NCQ is disabled, FBS allows more than one oustanding command
per port, one to each drive.
Signed-off-by: Gwendal Grignou <gwendal@google.com>
---
drivers/ata/sata_mv.c | 139 ++++++++++++++++++++++++++-----------------------
1 files changed, 74 insertions(+), 65 deletions(-)
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index c815f8e..46c0a9a 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -219,6 +219,9 @@ enum {
SATA_FIS_IRQ_CAUSE_OFS = 0x364,
SATA_FIS_IRQ_AN = (1 << 9), /* async notification */
+ /* FIS registers */
+ SATA_FIS_DW0 = 0x370,
+
LTMODE_OFS = 0x30c,
LTMODE_BIT8 = (1 << 8), /* unknown, but necessary */
@@ -1145,6 +1148,14 @@ static int mv_qc_defer(struct ata_queued_cmd *qc)
(pp->pp_flags & MV_PP_FLAG_NCQ_EN) && ata_is_ncq(qc->tf.protocol))
return 0;
+ /*
+ * Allow DMA commands when EDMA is enabled only on different links
+ */
+ if ((pp->pp_flags & MV_PP_FLAG_EDMA_EN) &&
+ (pp->pp_flags & MV_PP_FLAG_FBS_EN) && ata_is_dma(qc->tf.protocol) &&
+ !ata_tag_valid(link->active_tag) && !link->sactive)
+ return 0;
+
return ATA_DEFER_PORT;
}
@@ -1165,9 +1176,8 @@ static void mv_config_fbs(void __iomem *port_mmio, int want_ncq, int want_fbs)
if (want_fbs) {
new_fiscfg = old_fiscfg | FISCFG_SINGLE_SYNC;
new_ltmode = old_ltmode | LTMODE_BIT8;
- if (want_ncq)
- new_haltcond &= ~EDMA_ERR_DEV;
- else
+ new_haltcond &= ~EDMA_ERR_DEV;
+ if (!want_ncq)
new_fiscfg |= FISCFG_WAIT_DEV_ERR;
}
@@ -1214,15 +1224,6 @@ static void mv_edma_cfg(struct ata_port *ap, int want_ncq)
} else if (IS_GEN_IIE(hpriv)) {
int want_fbs = sata_pmp_attached(ap);
- /*
- * Possible future enhancement:
- *
- * The chip can use FBS with non-NCQ, if we allow it,
- * But first we need to have the error handling in place
- * for this mode (datasheet section 7.3.15.4.2.3).
- * So disallow non-NCQ FBS for now.
- */
- want_fbs &= want_ncq;
mv_config_fbs(port_mmio, want_ncq, want_fbs);
@@ -1642,23 +1643,24 @@ static void mv_pmp_error_handler(struct ata_port *ap)
struct mv_port_priv *pp = ap->private_data;
if (pp->pp_flags & MV_PP_FLAG_DELAYED_EH) {
- /*
- * Perform NCQ error analysis on failed PMPs
- * before we freeze the port entirely.
- *
- * The failed PMPs are marked earlier by mv_pmp_eh_prep().
- */
- pmp_map = pp->delayed_eh_pmp_map;
pp->pp_flags &= ~MV_PP_FLAG_DELAYED_EH;
- for (pmp = 0; pmp_map != 0; pmp++) {
- unsigned int this_pmp = (1 << pmp);
- if (pmp_map & this_pmp) {
- struct ata_link *link = &ap->pmp_link[pmp];
- pmp_map &= ~this_pmp;
- ata_eh_analyze_ncq_error(link);
+ if (pp->pp_flags & MV_PP_FLAG_NCQ_EN) {
+ /*
+ * Perform NCQ error analysis on failed PMPs
+ * before we freeze the port entirely.
+ *
+ * The failed PMPs are marked earlier by mv_pmp_eh_prep().
+ */
+ pmp_map = pp->delayed_eh_pmp_map;
+ for (pmp = 0; pmp_map != 0; pmp++) {
+ unsigned int this_pmp = (1 << pmp);
+ if (pmp_map & this_pmp) {
+ struct ata_link *link = &ap->pmp_link[pmp];
+ pmp_map &= ~this_pmp;
+ ata_eh_analyze_ncq_error(link);
+ }
}
}
- ata_port_freeze(ap);
}
sata_pmp_error_handler(ap);
}
@@ -1742,7 +1744,6 @@ static int mv_handle_fbs_ncq_dev_err(struct ata_port *ap)
if (ap->nr_active_links <= failed_links && mv_req_q_empty(ap)) {
mv_process_crpb_entries(ap, pp);
- mv_stop_edma(ap);
mv_eh_freeze(ap);
ata_port_printk(ap, KERN_INFO, "%s: done\n", __func__);
return 1; /* handled */
@@ -1753,18 +1754,43 @@ static int mv_handle_fbs_ncq_dev_err(struct ata_port *ap)
static int mv_handle_fbs_non_ncq_dev_err(struct ata_port *ap)
{
+ u8 fis[6 * 4];
+ u8 pmp;
+ void __iomem *port_mmio = mv_ap_base(ap);
+ struct ata_queued_cmd *qc;
+ unsigned int tag;
+
/*
- * Possible future enhancement:
- *
- * FBS+non-NCQ operation is not yet implemented.
- * See related notes in mv_edma_cfg().
- *
* Device error during FBS+non-NCQ operation:
*
* We need to snapshot the shadow registers for each failed command.
* Follow recovery sequence from 6042/7042 datasheet (7.3.15.4.2.3).
*/
- return 0; /* not handled */
+ memcpy_fromio(fis, port_mmio + SATA_FIS_DW0, sizeof(fis));
+ pmp = fis[1] & 0xf;
+
+ /* If we are in FBS mode, we have a pmp attached */
+ WARN_ON(!sata_pmp_attached(ap));
+ if (pmp >= ap->nr_pmp_links) {
+ ata_port_printk(ap, KERN_ERR,
+ "%s: invalid FIS from device DW0=0x%08x\n",
+ __func__, *(u32*)(fis));
+ return 0;
+ }
+ tag = ap->pmp_link[pmp].active_tag;
+ qc = ata_qc_from_tag(ap, tag);
+ if (qc) {
+ qc->flags |= ATA_QCFLAG_RTF_VALID;
+ qc->result_tf.flags = qc->tf.flags;
+ ata_tf_from_fis(fis, &qc->result_tf);
+
+ return mv_handle_fbs_ncq_dev_err(ap);
+ }
+ else {
+ ata_port_printk(ap, KERN_ERR, "%s: no qc for tag=%d\n",
+ __func__, tag);
+ return 0;
+ }
}
static int mv_handle_dev_err(struct ata_port *ap, u32 edma_err_cause)
@@ -1779,34 +1805,12 @@ static int mv_handle_dev_err(struct ata_port *ap, u32 edma_err_cause)
if (!(edma_err_cause & EDMA_ERR_DEV))
return 0; /* non DEV error: not handled */
edma_err_cause &= ~EDMA_ERR_IRQ_TRANSIENT;
- if (edma_err_cause & ~(EDMA_ERR_DEV | EDMA_ERR_SELF_DIS))
+ if (edma_err_cause & ~EDMA_ERR_DEV)
return 0; /* other problems: not handled */
if (pp->pp_flags & MV_PP_FLAG_NCQ_EN) {
- /*
- * EDMA should NOT have self-disabled for this case.
- * If it did, then something is wrong elsewhere,
- * and we cannot handle it here.
- */
- if (edma_err_cause & EDMA_ERR_SELF_DIS) {
- ata_port_printk(ap, KERN_WARNING,
- "%s: err_cause=0x%x pp_flags=0x%x\n",
- __func__, edma_err_cause, pp->pp_flags);
- return 0; /* not handled */
- }
return mv_handle_fbs_ncq_dev_err(ap);
} else {
- /*
- * EDMA should have self-disabled for this case.
- * If it did not, then something is wrong elsewhere,
- * and we cannot handle it here.
- */
- if (!(edma_err_cause & EDMA_ERR_SELF_DIS)) {
- ata_port_printk(ap, KERN_WARNING,
- "%s: err_cause=0x%x pp_flags=0x%x\n",
- __func__, edma_err_cause, pp->pp_flags);
- return 0; /* not handled */
- }
return mv_handle_fbs_non_ncq_dev_err(ap);
}
return 0; /* not handled */
@@ -1855,7 +1859,7 @@ static void mv_err_intr(struct ata_port *ap)
unsigned int action = 0, err_mask = 0;
struct ata_eh_info *ehi = &ap->link.eh_info;
struct ata_queued_cmd *qc;
- int abort = 0;
+ int abort = 0, handled = 0;
/*
* Read and clear the SError and err_cause bits.
@@ -1866,20 +1870,25 @@ static void mv_err_intr(struct ata_port *ap)
sata_scr_write_flush(&ap->link, SCR_ERROR, serr);
edma_err_cause = readl(port_mmio + EDMA_ERR_IRQ_CAUSE_OFS);
- if (IS_GEN_IIE(hpriv) && (edma_err_cause & EDMA_ERR_TRANS_IRQ_7)) {
- fis_cause = readl(port_mmio + SATA_FIS_IRQ_CAUSE_OFS);
- writelfl(~fis_cause, port_mmio + SATA_FIS_IRQ_CAUSE_OFS);
- }
- writelfl(~edma_err_cause, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS);
-
if (edma_err_cause & EDMA_ERR_DEV) {
/*
* Device errors during FIS-based switching operation
* require special handling.
*/
- if (mv_handle_dev_err(ap, edma_err_cause))
- return;
+ handled = mv_handle_dev_err(ap, edma_err_cause);
}
+ if (IS_GEN_IIE(hpriv) &&
+ ((edma_err_cause & EDMA_ERR_TRANS_IRQ_7) ||
+ ((edma_err_cause & EDMA_ERR_DEV) &&
+ (pp->pp_flags & MV_PP_FLAG_FBS_EN) &&
+ !(pp->pp_flags & MV_PP_FLAG_NCQ_EN)))) {
+ fis_cause = readl(port_mmio + SATA_FIS_IRQ_CAUSE_OFS);
+ writelfl(~fis_cause, port_mmio + SATA_FIS_IRQ_CAUSE_OFS);
+ }
+ writelfl(~edma_err_cause, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS);
+
+ if (handled)
+ return;
qc = mv_get_active_qc(ap);
ata_ehi_clear_desc(ehi);
--
1.5.4.5
next reply other threads:[~2008-10-06 1:28 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-10-06 1:28 Gwendal Grignou [this message]
-- strict thread matches above, loose matches on Subject: below --
2008-10-06 1:17 [PATH 2/2] [sata_mv] enable FIS Based Switching when a Port Multiplier is connected Gwendal Grignou
2008-10-06 1:13 Gwendal Grignou
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20081006012826.BCCFF2E29AC@localhost \
--to=gwendal@google.com \
--cc=gwendal@gmail.com \
--cc=linux-ide@vger.kernel.org \
--cc=tj@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.