linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 01/06] sata_mv: cache frequently read registers
@ 2009-02-13  5:03 Mark Lord
  2009-02-13  5:05 ` [PATCH 02/06] sata_mv: enable ATAPI DMA for GEN_IIE chips Mark Lord
  2009-02-17  0:19 ` [PATCH 01/06] sata_mv: cache frequently read registers Jeff Garzik
  0 siblings, 2 replies; 9+ messages in thread
From: Mark Lord @ 2009-02-13  5:03 UTC (permalink / raw)
  To: Jeff Garzik, IDE/ATA development list

Maintain a local (mv_port_priv) cache of frequently accessed registers,
to avoid having to re-read them (very slow) on every transistion
between EDMA and non-EDMA modes.  This speeds up things like
flushing the drive write cache, and anything using basic DMA transfers.

Signed-off-by: Mark Lord <mlord@pobox.com>
--

Resending without any changes from original.

--- old/drivers/ata/sata_mv.c	2009-02-05 10:16:58.000000000 -0500
+++ linux/drivers/ata/sata_mv.c	2009-02-05 10:14:43.000000000 -0500
@@ -438,6 +438,17 @@
  	__le32			reserved;
  };

+/*
+ * We keep a local cache of a few frequently accessed port
+ * registers here, to avoid having to read them (very slow)
+ * when switching between EDMA and non-EDMA modes.
+ */
+struct mv_cached_regs {
+	u32			fiscfg;
+	u32			ltmode;
+	u32			haltcond;
+};
+
  struct mv_port_priv {
  	struct mv_crqb		*crqb;
  	dma_addr_t		crqb_dma;
@@ -450,6 +461,7 @@
  	unsigned int		resp_idx;

  	u32			pp_flags;
+	struct mv_cached_regs	cached;
  	unsigned int		delayed_eh_pmp_map;
  };

@@ -812,6 +824,43 @@
  	return ((port_flags & MV_FLAG_DUAL_HC) ? 2 : 1);
  }

+/**
+ *      mv_save_cached_regs - (re-)initialize cached port registers
+ *      @ap: the port whose registers we are caching
+ *
+ *	Initialize the local cache of port registers,
+ *	so that reading them over and over again can
+ *	be avoided on the hotter paths of this driver.
+ *	This saves a few microseconds each time we switch
+ *	to/from EDMA mode to perform (eg.) a drive cache flush.
+ */
+static void mv_save_cached_regs(struct ata_port *ap)
+{
+	void __iomem *port_mmio = mv_ap_base(ap);
+	struct mv_port_priv *pp = ap->private_data;
+
+	pp->cached.fiscfg = readl(port_mmio + FISCFG_OFS);
+	pp->cached.ltmode = readl(port_mmio + LTMODE_OFS);
+	pp->cached.haltcond = readl(port_mmio + EDMA_HALTCOND_OFS);
+}
+
+/**
+ *      mv_write_cached_reg - write to a cached port register
+ *      @addr: hardware address of the register
+ *      @old: pointer to cached value of the register
+ *      @new: new value for the register
+ *
+ *	Write a new value to a cached register,
+ *	but only if the value is different from before.
+ */
+static inline void mv_write_cached_reg(void __iomem *addr, u32 *old, u32 new)
+{
+	if (new != *old) {
+		*old = new;
+		writel(new, addr);
+	}
+}
+
  static void mv_set_edma_ptrs(void __iomem *port_mmio,
  			     struct mv_host_priv *hpriv,
  			     struct mv_port_priv *pp)
@@ -1159,35 +1208,33 @@
  	return ATA_DEFER_PORT;
  }

-static void mv_config_fbs(void __iomem *port_mmio, int want_ncq, int want_fbs)
+static void mv_config_fbs(struct ata_port *ap, int want_ncq, int want_fbs)
  {
-	u32 new_fiscfg, old_fiscfg;
-	u32 new_ltmode, old_ltmode;
-	u32 new_haltcond, old_haltcond;
-
-	old_fiscfg   = readl(port_mmio + FISCFG_OFS);
-	old_ltmode   = readl(port_mmio + LTMODE_OFS);
-	old_haltcond = readl(port_mmio + EDMA_HALTCOND_OFS);
-
-	new_fiscfg   = old_fiscfg & ~(FISCFG_SINGLE_SYNC | FISCFG_WAIT_DEV_ERR);
-	new_ltmode   = old_ltmode & ~LTMODE_BIT8;
-	new_haltcond = old_haltcond | EDMA_ERR_DEV;
+	struct mv_port_priv *pp = ap->private_data;
+	void __iomem *port_mmio;
+
+	u32 fiscfg,   *old_fiscfg   = &pp->cached.fiscfg;
+	u32 ltmode,   *old_ltmode   = &pp->cached.ltmode;
+	u32 haltcond, *old_haltcond = &pp->cached.haltcond;
+
+	ltmode   = *old_ltmode & ~LTMODE_BIT8;
+	haltcond = *old_haltcond | EDMA_ERR_DEV;

  	if (want_fbs) {
-		new_fiscfg = old_fiscfg | FISCFG_SINGLE_SYNC;
-		new_ltmode = old_ltmode | LTMODE_BIT8;
+		fiscfg = *old_fiscfg | FISCFG_SINGLE_SYNC;
+		ltmode = *old_ltmode | LTMODE_BIT8;
  		if (want_ncq)
-			new_haltcond &= ~EDMA_ERR_DEV;
+			haltcond &= ~EDMA_ERR_DEV;
  		else
-			new_fiscfg |=  FISCFG_WAIT_DEV_ERR;
+			fiscfg |=  FISCFG_WAIT_DEV_ERR;
+	} else {
+		fiscfg = *old_fiscfg & ~(FISCFG_SINGLE_SYNC | FISCFG_WAIT_DEV_ERR);
  	}

-	if (new_fiscfg != old_fiscfg)
-		writelfl(new_fiscfg, port_mmio + FISCFG_OFS);
-	if (new_ltmode != old_ltmode)
-		writelfl(new_ltmode, port_mmio + LTMODE_OFS);
-	if (new_haltcond != old_haltcond)
-		writelfl(new_haltcond, port_mmio + EDMA_HALTCOND_OFS);
+	port_mmio = mv_ap_base(ap);
+	mv_write_cached_reg(port_mmio + FISCFG_OFS, old_fiscfg, fiscfg);
+	mv_write_cached_reg(port_mmio + LTMODE_OFS, old_ltmode, ltmode);
+	mv_write_cached_reg(port_mmio + EDMA_HALTCOND_OFS, old_haltcond, haltcond);
  }

  static void mv_60x1_errata_sata25(struct ata_port *ap, int want_ncq)
@@ -1235,7 +1282,7 @@
  		 */
  		want_fbs &= want_ncq;

-		mv_config_fbs(port_mmio, want_ncq, want_fbs);
+		mv_config_fbs(ap, want_ncq, want_fbs);

  		if (want_fbs) {
  			pp->pp_flags |= MV_PP_FLAG_FBS_EN;
@@ -1339,6 +1386,7 @@
  			pp->sg_tbl_dma[tag] = pp->sg_tbl_dma[0];
  		}
  	}
+	mv_save_cached_regs(ap);
  	mv_edma_cfg(ap, 0, 0);
  	return 0;

@@ -2996,6 +3044,7 @@
  				extra = HZ; /* only extend it once, max */
  		}
  	} while (sstatus != 0x0 && sstatus != 0x113 && sstatus != 0x123);
+	mv_save_cached_regs(ap);
  	mv_edma_cfg(ap, 0, 0);

  	return rc;

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 02/06] sata_mv: enable ATAPI DMA for GEN_IIE chips
  2009-02-13  5:03 [PATCH 01/06] sata_mv: cache frequently read registers Mark Lord
@ 2009-02-13  5:05 ` Mark Lord
  2009-02-13  5:05   ` [PATCH 03/06] sata_mv: stricter irq masking Mark Lord
  2009-02-17  0:19 ` [PATCH 01/06] sata_mv: cache frequently read registers Jeff Garzik
  1 sibling, 1 reply; 9+ messages in thread
From: Mark Lord @ 2009-02-13  5:05 UTC (permalink / raw)
  To: Jeff Garzik, IDE/ATA development list

Enable use of (basic) DMA for ATAPI on GEN_IIE chips.
This also gets rid of any need for mv_mode_filter().

Using basic DMA on GEN_IIE requires setting an undocumented
bit in an undocumented register.  For safety, we clear that
bit again when switching back to EDMA mode.

To avoid a performance penalty when switching modes,
we cache the register in port_priv, as already done for other regs.

Signed-off-by: Mark Lord <mlord@pobox.com>

--- old/drivers/ata/sata_mv.c	2009-02-05 10:14:43.000000000 -0500
+++ linux/drivers/ata/sata_mv.c	2009-02-05 10:13:12.000000000 -0500
@@ -345,7 +345,7 @@
  	EDMA_ARB_CFG_OFS	= 0x38,

  	EDMA_HALTCOND_OFS	= 0x60,		/* GenIIe halt conditions */
-
+	EDMA_UNKNOWN_RSVD_OFS	= 0x6C,		/* GenIIe unknown/reserved */

  	BMDMA_CMD_OFS		= 0x224,	/* bmdma command register */
  	BMDMA_STATUS_OFS	= 0x228,	/* bmdma status register */
@@ -447,6 +447,7 @@
  	u32			fiscfg;
  	u32			ltmode;
  	u32			haltcond;
+	u32			unknown_rsvd;
  };

  struct mv_port_priv {
@@ -563,8 +564,6 @@
  static void mv_process_crpb_entries(struct ata_port *ap,
  					struct mv_port_priv *pp);

-static unsigned long mv_mode_filter(struct ata_device *dev,
-				    unsigned long xfer_mask);
  static void mv_sff_irq_clear(struct ata_port *ap);
  static int mv_check_atapi_dma(struct ata_queued_cmd *qc);
  static void mv_bmdma_setup(struct ata_queued_cmd *qc);
@@ -626,7 +625,6 @@
  	.bmdma_start		= mv_bmdma_start,
  	.bmdma_stop		= mv_bmdma_stop,
  	.bmdma_status		= mv_bmdma_status,
-	.mode_filter		= mv_mode_filter,
  };

  static struct ata_port_operations mv_iie_ops = {
@@ -842,6 +840,7 @@
  	pp->cached.fiscfg = readl(port_mmio + FISCFG_OFS);
  	pp->cached.ltmode = readl(port_mmio + LTMODE_OFS);
  	pp->cached.haltcond = readl(port_mmio + EDMA_HALTCOND_OFS);
+	pp->cached.unknown_rsvd = readl(port_mmio + EDMA_UNKNOWN_RSVD_OFS);
  }

  /**
@@ -1252,6 +1251,30 @@
  		writel(new, hpriv->base + MV_GPIO_PORT_CTL_OFS);
  }

+/**
+ * 	mv_bmdma_enable - set a magic bit on GEN_IIE to allow bmdma
+ * 	@ap: Port being initialized
+ *
+ *	There are two DMA modes on these chips:  basic DMA, and EDMA.
+ *
+ *	Bit-0 of the "EDMA RESERVED" register enables/disables use
+ *	of basic DMA on the GEN_IIE versions of the chips.
+ *
+ *	This bit survives EDMA resets, and must be set for basic DMA
+ *	to function, and should be cleared when EDMA is active.
+ */
+static void mv_bmdma_enable_iie(struct ata_port *ap, int enable_bmdma)
+{
+	struct mv_port_priv *pp = ap->private_data;
+	u32 new, *old = &pp->cached.unknown_rsvd;
+
+	if (enable_bmdma)
+		new = *old | 1;
+	else
+		new = *old & ~1;
+	mv_write_cached_reg(mv_ap_base(ap) + EDMA_UNKNOWN_RSVD_OFS, old, new);
+}
+
  static void mv_edma_cfg(struct ata_port *ap, int want_ncq, int want_edma)
  {
  	u32 cfg;
@@ -1297,6 +1320,7 @@
  		}
  		if (hpriv->hp_flags & MV_HP_CUT_THROUGH)
  			cfg |= (1 << 17); /* enab cut-thru (dis stor&forwrd) */
+		mv_bmdma_enable_iie(ap, !want_edma);
  	}

  	if (want_ncq) {
@@ -1465,26 +1489,6 @@
  }

  /**
- *	mv_mode_filter - Allow ATAPI DMA only on GenII chips.
- *	@dev: device whose xfer modes are being configured.
- *
- *	Only the GenII hardware can use DMA with ATAPI drives.
- */
-static unsigned long mv_mode_filter(struct ata_device *adev,
-				    unsigned long xfer_mask)
-{
-	if (adev->class == ATA_DEV_ATAPI) {
-		struct mv_host_priv *hpriv = adev->link->ap->host->private_data;
-		if (!IS_GEN_II(hpriv)) {
-			xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA);
-			ata_dev_printk(adev, KERN_INFO,
-				"ATAPI DMA not supported on this chipset\n");
-		}
-	}
-	return xfer_mask;
-}
-
-/**
   *	mv_sff_irq_clear - Clear hardware interrupt after DMA.
   *	@ap: Port associated with this ATA transaction.
   *

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 03/06] sata_mv: stricter irq masking
  2009-02-13  5:05 ` [PATCH 02/06] sata_mv: enable ATAPI DMA for GEN_IIE chips Mark Lord
@ 2009-02-13  5:05   ` Mark Lord
  2009-02-13  5:08     ` [PATCH 04/06] sata_mv: implement mv_sff_check_status Mark Lord
  0 siblings, 1 reply; 9+ messages in thread
From: Mark Lord @ 2009-02-13  5:05 UTC (permalink / raw)
  To: Jeff Garzik, IDE/ATA development list

Tighten up interrupt masking in mv_qc_issue() so that it doesn't
miss any protocols.  Handle future cases where a qc is specially
marked for polled issue or where a particular chip version prefers
interrupts over polling for PIO.

This mimics the polling decision logic from ata_sff_qc_issue().

Signed-off-by: Mark Lord <mlord@pobox.com>

--- old/drivers/ata/sata_mv.c	2009-02-12 20:02:42.000000000 -0500
+++ linux/drivers/ata/sata_mv.c	2009-02-12 20:12:50.000000000 -0500
@@ -1809,7 +1809,7 @@
  	void __iomem *port_mmio = mv_ap_base(ap);
  	struct mv_port_priv *pp = ap->private_data;
  	u32 in_index;
-	unsigned int port_irqs = DONE_IRQ | ERR_IRQ;
+	unsigned int port_irqs;

  	switch (qc->tf.protocol) {
  	case ATA_PROT_DMA:
@@ -1842,20 +1842,28 @@
  					"this may fail due to h/w errata\n");
  		}
  		/* drop through */
+	case ATA_PROT_NODATA:
  	case ATAPI_PROT_PIO:
-		port_irqs = ERR_IRQ;	/* leave DONE_IRQ masked for PIO */
-		/* drop through */
-	default:
-		/*
-		 * We're about to send a non-EDMA capable command to the
-		 * port.  Turn off EDMA so there won't be problems accessing
-		 * shadow block, etc registers.
-		 */
-		mv_stop_edma(ap);
-		mv_clear_and_enable_port_irqs(ap, mv_ap_base(ap), port_irqs);
-		mv_pmp_select(ap, qc->dev->link->pmp);
-		return ata_sff_qc_issue(qc);
+	case ATAPI_PROT_NODATA:
+		if (ap->flags & ATA_FLAG_PIO_POLLING)
+			qc->tf.flags |= ATA_TFLAG_POLLING;
+		break;
  	}
+
+	if (qc->tf.flags & ATA_TFLAG_POLLING)
+		port_irqs = ERR_IRQ;	/* mask device interrupt when polling */
+	else
+		port_irqs = ERR_IRQ | DONE_IRQ;	/* unmask all interrupts */
+
+	/*
+	 * We're about to send a non-EDMA capable command to the
+	 * port.  Turn off EDMA so there won't be problems accessing
+	 * shadow block, etc registers.
+	 */
+	mv_stop_edma(ap);
+	mv_clear_and_enable_port_irqs(ap, mv_ap_base(ap), port_irqs);
+	mv_pmp_select(ap, qc->dev->link->pmp);
+	return ata_sff_qc_issue(qc);
  }

  static struct ata_queued_cmd *mv_get_active_qc(struct ata_port *ap)

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 04/06] sata_mv: implement mv_sff_check_status
  2009-02-13  5:05   ` [PATCH 03/06] sata_mv: stricter irq masking Mark Lord
@ 2009-02-13  5:08     ` Mark Lord
  2009-02-13  5:08       ` [PATCH 05/06] sata_mv: export ata_pio_queue_task() Mark Lord
  0 siblings, 1 reply; 9+ messages in thread
From: Mark Lord @ 2009-02-13  5:08 UTC (permalink / raw)
  To: Jeff Garzik, IDE/ATA development list

Add a new mv_sff_check_status() function to sata_mv.
This is necessary for use with the upcoming "mv_qc_issue_fis()" patch,
but is being added separately here for easier code review.

When using command issue via the "mv_qc_issue_fis()" mechanism,
the initial ATA_BUSY bit does not show in the ATA status (shadow) register.
This can confuse libata!  So here we add a hook to fake ATA_BUSY
for that situation, until the first time a BUSY, DRQ, or ERR bit is seen.

Signed-off-by: Mark Lord <mlord@pobox.com>

--- old/drivers/ata/sata_mv.c	2009-02-12 22:25:34.000000000 -0500
+++ upstream/drivers/ata/sata_mv.c	2009-02-12 22:26:09.000000000 -0500
@@ -370,6 +370,7 @@
  	MV_PP_FLAG_NCQ_EN	= (1 << 1),	/* is EDMA set up for NCQ? */
  	MV_PP_FLAG_FBS_EN	= (1 << 2),	/* is EDMA set up for FBS? */
  	MV_PP_FLAG_DELAYED_EH	= (1 << 3),	/* delayed dev err handling */
+	MV_PP_FLAG_FAKE_ATA_BUSY = (1 << 4),	/* ignore initial ATA_DRDY */
  };

  #define IS_GEN_I(hpriv) ((hpriv)->hp_flags & MV_HP_GEN_I)
@@ -570,6 +571,7 @@
  static void mv_bmdma_start(struct ata_queued_cmd *qc);
  static void mv_bmdma_stop(struct ata_queued_cmd *qc);
  static u8   mv_bmdma_status(struct ata_port *ap);
+static u8 mv_sff_check_status(struct ata_port *ap);

  /* .sg_tablesize is (MV_MAX_SG_CT / 2) in the structures below
   * because we have to allow room for worst case splitting of
@@ -619,6 +621,7 @@
  	.softreset		= mv_softreset,
  	.error_handler		= mv_pmp_error_handler,

+ 	.sff_check_status	= mv_sff_check_status,
  	.sff_irq_clear		= mv_sff_irq_clear,
  	.check_atapi_dma	= mv_check_atapi_dma,
  	.bmdma_setup		= mv_bmdma_setup,
@@ -1284,7 +1287,8 @@

  	/* set up non-NCQ EDMA configuration */
  	cfg = EDMA_CFG_Q_DEPTH;		/* always 0x1f for *all* chips */
-	pp->pp_flags &= ~(MV_PP_FLAG_FBS_EN | MV_PP_FLAG_NCQ_EN);
+	pp->pp_flags &=
+	  ~(MV_PP_FLAG_FBS_EN | MV_PP_FLAG_NCQ_EN | MV_PP_FLAG_FAKE_ATA_BUSY);

  	if (IS_GEN_I(hpriv))
  		cfg |= (1 << 8);	/* enab config burst size mask */
@@ -1791,6 +1795,33 @@
  }

  /**
+ *	mv_sff_check_status - fetch device status, if valid
+ *	@ap: ATA port to fetch status from
+ *
+ *	When using command issue via mv_qc_issue_fis(),
+ *	the initial ATA_BUSY state does not show up in the
+ *	ATA status (shadow) register.  This can confuse libata!
+ *
+ *	So we have a hook here to fake ATA_BUSY for that situation,
+ *	until the first time a BUSY, DRQ, or ERR bit is seen.
+ *
+ *	The rest of the time, it simply returns the ATA status register.
+ */
+static u8 mv_sff_check_status(struct ata_port *ap)
+{
+	u8 stat = ioread8(ap->ioaddr.status_addr);
+	struct mv_port_priv *pp = ap->private_data;
+
+	if (pp->pp_flags & MV_PP_FLAG_FAKE_ATA_BUSY) {
+		if (stat & (ATA_BUSY | ATA_DRQ | ATA_ERR))
+			pp->pp_flags &= ~MV_PP_FLAG_FAKE_ATA_BUSY;
+		else
+			stat = ATA_BUSY;
+	}
+	return stat;
+}
+
+/**
   *      mv_qc_issue - Initiate a command to the host
   *      @qc: queued command to start
   *
@@ -1811,6 +1842,8 @@
  	u32 in_index;
  	unsigned int port_irqs;

+	pp->pp_flags &= ~MV_PP_FLAG_FAKE_ATA_BUSY; /* paranoia */
+
  	switch (qc->tf.protocol) {
  	case ATA_PROT_DMA:
  	case ATA_PROT_NCQ:
@@ -3037,6 +3070,8 @@

  	mv_reset_channel(hpriv, mmio, ap->port_no);
  	pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN;
+	pp->pp_flags &=
+	  ~(MV_PP_FLAG_FBS_EN | MV_PP_FLAG_NCQ_EN | MV_PP_FLAG_FAKE_ATA_BUSY);

  	/* Workaround for errata FEr SATA#10 (part 2) */
  	do {

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 05/06] sata_mv: export ata_pio_queue_task()
  2009-02-13  5:08     ` [PATCH 04/06] sata_mv: implement mv_sff_check_status Mark Lord
@ 2009-02-13  5:08       ` Mark Lord
  2009-02-13  5:09         ` [PATCH 06/06] sata_mv: implement mv_qc_issue_fis() for errata workaround Mark Lord
  0 siblings, 1 reply; 9+ messages in thread
From: Mark Lord @ 2009-02-13  5:08 UTC (permalink / raw)
  To: Jeff Garzik, IDE/ATA development list

Export ata_pio_queue_task() so that it can be used from sata_mv.

Signed-off-by: Mark Lord <mlord@pobox.com>

--- old/drivers/ata/libata-core.c	2009-02-12 22:41:42.000000000 -0500
+++ upstream/drivers/ata/libata-core.c	2009-02-12 22:41:46.000000000 -0500
@@ -6707,6 +6707,7 @@
  EXPORT_SYMBOL_GPL(ata_do_dev_read_id);
  EXPORT_SYMBOL_GPL(ata_scsi_simulate);

+EXPORT_SYMBOL_GPL(ata_pio_queue_task);
  EXPORT_SYMBOL_GPL(ata_pio_need_iordy);
  EXPORT_SYMBOL_GPL(ata_timing_find_mode);
  EXPORT_SYMBOL_GPL(ata_timing_compute);
--- old/drivers/ata/libata.h	2009-02-12 19:58:00.000000000 -0500
+++ upstream/drivers/ata/libata.h	2009-02-12 22:43:16.000000000 -0500
@@ -79,8 +79,6 @@
  			   u64 block, u32 n_block, unsigned int tf_flags,
  			   unsigned int tag);
  extern u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev);
-extern void ata_pio_queue_task(struct ata_port *ap, void *data,
-			       unsigned long delay);
  extern void ata_port_flush_task(struct ata_port *ap);
  extern unsigned ata_exec_internal(struct ata_device *dev,
  				  struct ata_taskfile *tf, const u8 *cdb,
--- old/include/linux/libata.h	2009-02-12 19:58:07.000000000 -0500
+++ upstream/include/linux/libata.h	2009-02-12 22:43:24.000000000 -0500
@@ -1006,6 +1006,9 @@
  extern int ata_cable_ignore(struct ata_port *ap);
  extern int ata_cable_unknown(struct ata_port *ap);

+extern void ata_pio_queue_task(struct ata_port *ap, void *data,
+			       unsigned long delay);
+
  /* Timing helpers */
  extern unsigned int ata_pio_need_iordy(const struct ata_device *);
  extern const struct ata_timing *ata_timing_find_mode(u8 xfer_mode);

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 06/06] sata_mv: implement mv_qc_issue_fis() for errata workaround
  2009-02-13  5:08       ` [PATCH 05/06] sata_mv: export ata_pio_queue_task() Mark Lord
@ 2009-02-13  5:09         ` Mark Lord
  0 siblings, 0 replies; 9+ messages in thread
From: Mark Lord @ 2009-02-13  5:09 UTC (permalink / raw)
  To: Jeff Garzik, IDE/ATA development list

Implement direct FIS transmission via mv_qc_issue_fis().
This is initially needed to work around NCQ errata,
whereby the READ_LOG_EXT command sometimes fails
when issued in the traditional (sff) fashion.

Portions of this code will likely be reused for
implementation of the target mode feature later on.

Signed-off-by: Mark Lord <mlord@pobox.com>

--- old/drivers/ata/sata_mv.c	2009-02-12 22:38:24.000000000 -0500
+++ upstream/drivers/ata/sata_mv.c	2009-02-12 22:47:59.000000000 -0500
@@ -1822,6 +1822,105 @@
  }

  /**
+ *	mv_send_fis - Send a FIS, using the "Vendor-Unique FIS" register
+ *	@fis: fis to be sent
+ *	@nwords: number of 32-bit words in the fis
+ */
+static unsigned int mv_send_fis(struct ata_port *ap, u32 *fis, int nwords)
+{
+	void __iomem *port_mmio = mv_ap_base(ap);
+	u32 ifctl, old_ifctl, ifstat;
+	int i, timeout = 200, final_word = nwords - 1;
+
+	/* Initiate FIS transmission mode */
+	old_ifctl = readl(port_mmio + SATA_IFCTL_OFS);
+	ifctl = 0x100 | (old_ifctl & 0xf);
+	writelfl(ifctl, port_mmio + SATA_IFCTL_OFS);
+
+	/* Send all words of the FIS except for the final word */
+	for (i = 0; i < final_word; ++i)
+		writel(fis[i], port_mmio + VENDOR_UNIQUE_FIS_OFS);
+
+	/* Flag end-of-transmission, and then send the final word */
+	writelfl(ifctl | 0x200, port_mmio + SATA_IFCTL_OFS);
+	writelfl(fis[final_word], port_mmio + VENDOR_UNIQUE_FIS_OFS);
+
+	/*
+	 * Wait for FIS transmission to complete.
+	 * This typically takes just a single iteration.
+	 */
+	do {
+		ifstat = readl(port_mmio + SATA_IFSTAT_OFS);
+	} while (!(ifstat & 0x1000) && --timeout);
+
+	/* Restore original port configuration */
+	writelfl(old_ifctl, port_mmio + SATA_IFCTL_OFS);
+
+	/* See if it worked */
+	if ((ifstat & 0x3000) != 0x1000) {
+		ata_port_printk(ap, KERN_WARNING,
+				"%s transmission error, ifstat=%08x\n",
+				__func__, ifstat);
+		return AC_ERR_OTHER;
+	}
+	return 0;
+}
+
+/**
+ *	mv_qc_issue_fis - Issue a command directly as a FIS
+ *	@qc: queued command to start
+ *
+ *	Note that the ATA shadow registers are not updated
+ *	after command issue, so the device will appear "READY"
+ *	if polled, even while it is BUSY processing the command.
+ *
+ *	So we use a status hook to fake ATA_BUSY until the drive changes state.
+ *
+ *	Note: we don't get updated shadow regs on *completion*
+ *	of non-data commands. So avoid sending them via this function,
+ *	as they will appear to have completed immediately.
+ *
+ *	GEN_IIE has special registers that we could get the result tf from,
+ *	but earlier chipsets do not.  For now, we ignore those registers.
+ */
+static unsigned int mv_qc_issue_fis(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct mv_port_priv *pp = ap->private_data;
+	struct ata_link *link = qc->dev->link;
+	u32 fis[5];
+	int err = 0;
+
+	ata_tf_to_fis(&qc->tf, link->pmp, 1, (void *)fis);
+	err = mv_send_fis(ap, fis, sizeof(fis) / sizeof(fis[0]));
+	if (err)
+		return err;
+
+	switch (qc->tf.protocol) {
+	case ATAPI_PROT_PIO:
+		pp->pp_flags |= MV_PP_FLAG_FAKE_ATA_BUSY;
+		/* fall through */
+	case ATAPI_PROT_NODATA:
+		ap->hsm_task_state = HSM_ST_FIRST;
+		break;
+	case ATA_PROT_PIO:
+		pp->pp_flags |= MV_PP_FLAG_FAKE_ATA_BUSY;
+		if (qc->tf.flags & ATA_TFLAG_WRITE)
+			ap->hsm_task_state = HSM_ST_FIRST;
+		else
+			ap->hsm_task_state = HSM_ST;
+		break;
+	default:
+		ap->hsm_task_state = HSM_ST_LAST;
+		break;
+	}
+
+	if (qc->tf.flags & ATA_TFLAG_POLLING)
+		ata_pio_queue_task(ap, qc, 0);
+	return 0;
+}
+
+/**
   *      mv_qc_issue - Initiate a command to the host
   *      @qc: queued command to start
   *
@@ -1896,6 +1995,23 @@
  	mv_stop_edma(ap);
  	mv_clear_and_enable_port_irqs(ap, mv_ap_base(ap), port_irqs);
  	mv_pmp_select(ap, qc->dev->link->pmp);
+
+	if (qc->tf.command == ATA_CMD_READ_LOG_EXT) {
+		struct mv_host_priv *hpriv = ap->host->private_data;
+		/*
+		 * Workaround for 88SX60x1 FEr SATA#25 (part 2).
+		 *
+		 * After any NCQ error, the READ_LOG_EXT command
+		 * from libata-eh *must* use mv_qc_issue_fis().
+		 * Otherwise it might fail, due to chip errata.
+		 *
+		 * Rather than special-case it, we'll just *always*
+		 * use this method here for READ_LOG_EXT, making for
+		 * easier testing.
+		 */
+		if (IS_GEN_II(hpriv))
+			return mv_qc_issue_fis(qc);
+	}
  	return ata_sff_qc_issue(qc);
  }


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 01/06] sata_mv: cache frequently read registers
  2009-02-13  5:03 [PATCH 01/06] sata_mv: cache frequently read registers Mark Lord
  2009-02-13  5:05 ` [PATCH 02/06] sata_mv: enable ATAPI DMA for GEN_IIE chips Mark Lord
@ 2009-02-17  0:19 ` Jeff Garzik
  2009-02-25 17:52   ` Mark Lord
       [not found]   ` <499A26B8.7070107@pobox.com>
  1 sibling, 2 replies; 9+ messages in thread
From: Jeff Garzik @ 2009-02-17  0:19 UTC (permalink / raw)
  To: Mark Lord; +Cc: IDE/ATA development list

Mark Lord wrote:
> Maintain a local (mv_port_priv) cache of frequently accessed registers,
> to avoid having to re-read them (very slow) on every transistion
> between EDMA and non-EDMA modes.  This speeds up things like
> flushing the drive write cache, and anything using basic DMA transfers.
> 
> Signed-off-by: Mark Lord <mlord@pobox.com>
> -- 
> 
> Resending without any changes from original.

ACK patches 1-6

however, they do not seem to apply to libata-dev.git#upstream, which 
already has several sata_mv changes in it.

Can you regenerate please?  No need to copy the list on resend, unless 
the patch content changes....

	Jeff





^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 01/06] sata_mv: cache frequently read registers
  2009-02-17  0:19 ` [PATCH 01/06] sata_mv: cache frequently read registers Jeff Garzik
@ 2009-02-25 17:52   ` Mark Lord
       [not found]   ` <499A26B8.7070107@pobox.com>
  1 sibling, 0 replies; 9+ messages in thread
From: Mark Lord @ 2009-02-25 17:52 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: IDE/ATA development list

Jeff Garzik wrote:
> Mark Lord wrote:
>> Maintain a local (mv_port_priv) cache of frequently accessed registers,
>> to avoid having to re-read them (very slow) on every transistion
>> between EDMA and non-EDMA modes.  This speeds up things like
>> flushing the drive write cache, and anything using basic DMA transfers.
>>
>> Signed-off-by: Mark Lord <mlord@pobox.com>
>> -- 
>>
>> Resending without any changes from original.
> 
> ACK patches 1-6
> 
> however, they do not seem to apply to libata-dev.git#upstream, which 
> already has several sata_mv changes in it.
> 
> Can you regenerate please?  No need to copy the list on resend, unless 
> the patch content changes....
..

I sent you the patches via email a week ago now,
and no word back .. and no updates visible in libata-dev.

Did you get them?

Thanks

Mark

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 01/06] sata_mv: cache frequently read registers
       [not found]   ` <499A26B8.7070107@pobox.com>
@ 2009-02-25 20:20     ` Jeff Garzik
  0 siblings, 0 replies; 9+ messages in thread
From: Jeff Garzik @ 2009-02-25 20:20 UTC (permalink / raw)
  To: Mark Lord; +Cc: Linux IDE mailing list

Mark Lord wrote:
> Jeff Garzik wrote:
> ..
>> ACK patches 1-6
>>
>> however, they do not seem to apply to libata-dev.git#upstream, which 
>> already has several sata_mv changes in it.
>>
>> Can you regenerate please?  No need to copy the list on resend, unless 
>> the patch content changes....
> ..
> 
> Mmm.. strange.  Here (attached) are the six patches again,
> along with what my git here gives for the upstream copy of sata_mv.c
> from *before* applying the patches.

Sorry for the delay... the attachments applied and worked just fine.

I eyeballed the first patch and it looked the same as your emailed first 
patch, so I'm guessing there was an email bugaboo somewhere, rather than 
a patch content problem.

Applied all 6 to #upstream.

	Jeff




^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2009-02-25 20:21 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-02-13  5:03 [PATCH 01/06] sata_mv: cache frequently read registers Mark Lord
2009-02-13  5:05 ` [PATCH 02/06] sata_mv: enable ATAPI DMA for GEN_IIE chips Mark Lord
2009-02-13  5:05   ` [PATCH 03/06] sata_mv: stricter irq masking Mark Lord
2009-02-13  5:08     ` [PATCH 04/06] sata_mv: implement mv_sff_check_status Mark Lord
2009-02-13  5:08       ` [PATCH 05/06] sata_mv: export ata_pio_queue_task() Mark Lord
2009-02-13  5:09         ` [PATCH 06/06] sata_mv: implement mv_qc_issue_fis() for errata workaround Mark Lord
2009-02-17  0:19 ` [PATCH 01/06] sata_mv: cache frequently read registers Jeff Garzik
2009-02-25 17:52   ` Mark Lord
     [not found]   ` <499A26B8.7070107@pobox.com>
2009-02-25 20:20     ` Jeff Garzik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).