Skip to contentSkip to conversation

prep for PMP support, take 3

PATCH Applied Tejun Heo 2 participants 16 messages Oct 15, 2006 – Nov 1, 2006 libata

lore
Share & tools
lore

Tejun Heo

author
#0 lore raw

Hello,

This is the third take of libata-pmp patchset. This patchset contains 12 patches and implements PMP support.

#01-05: implement libata PMP support
#06-12: implement sata_sil24 PMP support

ATM, the only sata_sil24 supports PMP and only sil3726 and sil4726 PMPs are tested.

Changes from the last take[L] are.

* updated to fit new #upstream
* sil24 PMP configuration sequence updated

This patchset is against

upstream (da54f5fe54c7d75e2db7d17961fb36a8c28a8501)
+ [1] hp-poll patchset, take #5
+ [2] vt8251-ahci patchset, take #2
+ [3] libata-link patchset, take #3
+ [4] prep-pmp patchset, take #4

Git tree can be accessed at

http://htj.dyndns.org/git/?p=libata-tj.git;a=shortlog;h=pmp
git://htj.dyndns.org/libata-tj pmp

Jeff, I kept the behavior of ATA_LFLAG_DISABLED as the discussion didn't proceed from there. If you still don't agree with it, please let me know.

Thanks.

--
tejun

[L] http://article.gmane.org/gmane.linux.ide/11946
[1] http://article.gmane.org/gmane.linux.ide/13467
[2] http://article.gmane.org/gmane.linux.ide/13471
[3] http://article.gmane.org/gmane.linux.ide/13478
[4] http://article.gmane.org/gmane.linux.ide/13500

author
#1 lore raw

PATCH 1/12 libata-pmp: add PMP related constants, fields, ops and update helpers

Add PMP related constants, fields and ops. Also, update ata_class_enabled/disabled() such that PMP classes are considered.

---
 include/linux/libata.h |   35 ++++++++++++++++++++++++++++++-----
 1 files changed, 30 insertions(+), 5 deletions(-)
include/linux/libata.h +30 -5
index 0191ac8..fe64a31 100644
@@ -140,6 +140,7 @@ enum {
 	ATA_DFLAG_LBA48		= (1 << 1), /* device supports LBA48 */
 	ATA_DFLAG_CDB_INTR	= (1 << 2), /* device asserts INTRQ when ready for CDB */
 	ATA_DFLAG_NCQ		= (1 << 3), /* device supports NCQ */
+	ATA_DFLAG_PMP_HRST_TO_RESUME = (1 << 4), /* requires hardreset */
 	ATA_DFLAG_CFG_MASK	= (1 << 8) - 1,
 
 	ATA_DFLAG_PIO		= (1 << 8), /* device limited to PIO mode */
@@ -155,7 +156,9 @@ enum {
 	ATA_DEV_ATA_UNSUP	= 2,	/* ATA device (unsupported) */
 	ATA_DEV_ATAPI		= 3,	/* ATAPI device */
 	ATA_DEV_ATAPI_UNSUP	= 4,	/* ATAPI device (unsupported) */
-	ATA_DEV_NONE		= 5,	/* no device */
+	ATA_DEV_PMP		= 5,	/* SATA port multiplier */
+	ATA_DEV_PMP_UNSUP	= 6,	/* SATA port multiplier (unsupported) */
+	ATA_DEV_NONE		= 7,	/* no device */
 
 	/* struct ata_port flags */
 	ATA_FLAG_SLAVE_POSS	= (1 << 0), /* host supports slave dev */
@@ -176,6 +179,8 @@ enum {
 					      * Register FIS clearing BSY */
 	ATA_FLAG_DEBUGMSG	= (1 << 13),
 	ATA_FLAG_HP_POLLING	= (1 << 14), /* hotplug by polling */
+	ATA_FLAG_PMP		= (1 << 14),
+	ATA_FLAG_SDB_NOTIFY	= (1 << 15),
 
 	/* The following flag belongs to ap->pflags but is kept in
 	 * ap->flags because it's referenced in many LLDs and will be
@@ -300,6 +305,9 @@ enum {
 	ATA_PROBE_MAX_TRIES	= 3,
 	ATA_EH_RESET_TRIES	= 3,
 	ATA_EH_DEV_TRIES	= 3,
+	ATA_EH_PMP_TRIES	= 5,
+	ATA_EH_PMP_RESET_TRIES	= 2,
+	ATA_EH_PMP_LINK_TRIES	= 2,
 
 	/* Drive spinup time (time from power-on to the first D2H FIS)
 	 * in msecs - 8s currently.  Failing to get ready in this time
@@ -309,7 +317,9 @@ enum {
 	 * most devices.
 	 */
 	ATA_SPINUP_WAIT		= 8000,
-	
+
+	SATA_PMP_SCR_TIMEOUT	= 500,
+
 	/* Horkage types. May be set by libata or controller on drives
 	   (some horkage may be drive/controller pair dependant */
 
@@ -480,7 +490,12 @@ struct ata_device {
 	/* n_sector is used as CLEAR_OFFSET, read comment above CLEAR_OFFSET */
 	u64			n_sectors;	/* size of device, if ATA */
 	unsigned int		class;		/* ATA_DEV_xxx */
-	u16			id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */
+
+	union {
+		u16		id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */
+		u32		gscr[SATA_PMP_GSCR_DWORDS]; /* PMP GSCR block */
+	};
+
 	u8			pio_mode;
 	u8			dma_mode;
 	u8			xfer_mode;
@@ -540,6 +555,8 @@ struct ata_link {
 	unsigned int		active_tag;	/* active tag on this link */
 	u32			sactive;	/* active NCQ commands */
 
+	unsigned int		flags;		/* ATA_LFLAG_xxx */
+
 	unsigned int		hw_sata_spd_limit;
 	unsigned int		sata_spd_limit;
 
@@ -644,6 +661,12 @@ struct ata_port_operations {
 	void (*qc_prep) (struct ata_queued_cmd *qc);
 	unsigned int (*qc_issue) (struct ata_queued_cmd *qc);
 
+	/* port multiplier */
+	void (*pmp_attach) (struct ata_port *ap);
+	void (*pmp_detach) (struct ata_port *ap);
+	int (*pmp_read) (struct ata_device *dev, int pmp, int reg, u32 *r_val);
+	int (*pmp_write) (struct ata_device *dev, int pmp, int reg, u32 val);
+
 	/* Error handlers.  ->error_handler overrides ->eng_timeout and
 	 * indicates that new-style EH is in place.
 	 */
@@ -1023,12 +1046,14 @@ static inline unsigned int ata_tag_inter
  */
 static inline unsigned int ata_class_enabled(unsigned int class)
 {
-	return class == ATA_DEV_ATA || class == ATA_DEV_ATAPI;
+	return class == ATA_DEV_ATA || class == ATA_DEV_ATAPI ||
+		class == ATA_DEV_PMP;
 }
 
 static inline unsigned int ata_class_disabled(unsigned int class)
 {
-	return class == ATA_DEV_ATA_UNSUP || class == ATA_DEV_ATAPI_UNSUP;
+	return class == ATA_DEV_ATA_UNSUP || class == ATA_DEV_ATAPI_UNSUP ||
+		class == ATA_DEV_PMP_UNSUP;
 }
 
 static inline unsigned int ata_class_absent(unsigned int class)
Signature · 1 line
1.4.2.3
author
#2 lore raw

PATCH 2/12 libata-pmp: update ata_eh_reset() for PMP

In reply to Tejun Heo

PMP always requires SRST to be enabled. Also, hardreset reports classification code from the first device when PMP is attached. Update ata_eh_reset() such that followup softreset is performed if the controller is PMP capable.

---
 drivers/ata/libata-eh.c |    7 +++++--
 1 files changed, 5 insertions(+), 2 deletions(-)
drivers/ata/libata-eh.c +5 -2
index 103fc5e..2c302c9 100644
@@ -1565,13 +1565,16 @@ static int ata_do_reset(struct ata_link 
 	return 0;
 }
 
-static int ata_eh_followup_srst_needed(int rc, int classify,
+static int ata_eh_followup_srst_needed(struct ata_port *ap,
+				       int rc, int classify,
 				       const unsigned int *classes)
 {
 	if (rc == -EAGAIN)
 		return 1;
 	if (rc != 0)
 		return 0;
+	if (ap->flags & ATA_FLAG_PMP)
+		return 1;
 	if (classify && classes[0] == ATA_DEV_UNKNOWN)
 		return 1;
 	return 0;
@@ -1653,7 +1656,7 @@ int ata_eh_reset(struct ata_link *link, 
 
 	did_followup_srst = 0;
 	if (reset == hardreset &&
-	    ata_eh_followup_srst_needed(rc, classify, classes)) {
+	    ata_eh_followup_srst_needed(link->ap, rc, classify, classes)) {
 		/* okay, let's do follow-up softreset */
 		did_followup_srst = 1;
 		reset = softreset;
Signature · 1 line
1.4.2.3
author
#3 lore raw

PATCH 3/12 libata-pmp: implement ATA_LFLAG_DISABLED

In reply to Tejun Heo

Implement ATA_LFLAG_DISABLED. The flag indicates the link is disabled due to EH recovery failure. While a link is disabled, no EH action is taken on the link and suspend/resume become noop too. For host port, this doesn't introduce any behavior change other than not invoking hotplug sequence on failed ports on resume and this behavior change is intended.

This will be used by PMP links to manage failed links.

---
 drivers/ata/libata-eh.c   |   29 ++++++++++++++++++++++++++++-
 drivers/ata/libata-scsi.c |   10 ++++++----
 include/linux/libata.h    |    6 +++++-
 3 files changed, 39 insertions(+), 6 deletions(-)
drivers/ata/libata-eh.c +28 -1
index 2c302c9..228240a 100644
@@ -1058,6 +1058,7 @@ static void ata_eh_analyze_serror(struct
 	struct ata_eh_context *ehc = &link->eh_context;
 	u32 serror = ehc->i.serror;
 	unsigned int err_mask = 0, action = 0;
+	u32 hotplug_mask;
 
 	if (serror & SERR_PERSISTENT) {
 		err_mask |= AC_ERR_ATA_BUS;
@@ -1076,7 +1077,22 @@ static void ata_eh_analyze_serror(struct
 		err_mask |= AC_ERR_SYSTEM;
 		action |= ATA_EH_SOFTRESET;
 	}
-	if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG))
+
+	/* Determine whether a hotplug event has occurred.  Both
+	 * SError.N/X are considered hotplug events for enabled links.
+	 * For disabled host links, hp-poll is responsible for hotplug
+	 * event detection and thus both N/X bits are ignored.  For
+	 * disabled PMP links, only N bit is considered as X bit is
+	 * left at 1 for link plugging.
+	 */
+	hotplug_mask = 0;
+
+	if (!(link->flags & ATA_LFLAG_DISABLED))
+		hotplug_mask = SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+	else if (!ata_is_host_link(link))
+		hotplug_mask = SERR_PHYRDY_CHG;
+
+	if (serror & hotplug_mask)
 		ata_ehi_hotplugged(&ehc->i);
 
 	ehc->i.err_mask |= err_mask;
@@ -1968,6 +1984,10 @@ static int ata_eh_skip_recovery(struct a
 	struct ata_eh_context *ehc = &link->eh_context;
 	struct ata_device *dev;
 
+	/* skip disabled links */
+	if (link->flags & ATA_LFLAG_DISABLED)
+		return 1;
+
 	/* skip if all possible devices are suspended */
 	ata_link_for_each_dev(dev, link) {
 		if (!(dev->flags & ATA_DFLAG_SUSPENDED))
@@ -2079,6 +2099,13 @@ int ata_eh_recover(struct ata_port *ap, 
 	ata_port_for_each_link(link, ap) {
 		struct ata_eh_context *ehc = &link->eh_context;
 
+		/* re-enable link? */
+		if (ehc->i.action & ATA_EH_ENABLE_LINK) {
+			ata_eh_about_to_do(link, NULL, ATA_EH_ENABLE_LINK);
+			link->flags &= ~ATA_LFLAG_DISABLED;
+			ata_eh_done(link, NULL, ATA_EH_ENABLE_LINK);
+		}
+
 		ata_link_for_each_dev(dev, link) {
 			ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
 
drivers/ata/libata-scsi.c +6 -4
index c62992f..64777e1 100644
@@ -463,9 +463,10 @@ int ata_scsi_device_suspend(struct scsi_
 		spin_lock_irqsave(ap->lock, flags);
 	}
 
-	/* if @sdev is already detached, nothing to do */
+	/* if @sdev is already detached or link is disabled, nothing to do */
 	if (sdev->sdev_state == SDEV_OFFLINE ||
-	    sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL)
+	    sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL ||
+	    (dev->link->flags & ATA_LFLAG_DISABLED))
 		goto out_unlock;
 
 	/* request suspend */
@@ -526,9 +527,10 @@ int ata_scsi_device_resume(struct scsi_d
 
 	spin_lock_irqsave(ap->lock, flags);
 
-	/* if @sdev is already detached, nothing to do */
+	/* if @sdev is already detached or link is disabled, nothing to do */
 	if (sdev->sdev_state == SDEV_OFFLINE ||
-	    sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL)
+	    sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL ||
+	    (dev->link->flags & ATA_LFLAG_DISABLED))
 		goto out_unlock;
 
 	/* request resume */
include/linux/libata.h +5 -1
index fe64a31..a940b67 100644
@@ -160,6 +160,9 @@ enum {
 	ATA_DEV_PMP_UNSUP	= 6,	/* SATA port multiplier (unsupported) */
 	ATA_DEV_NONE		= 7,	/* no device */
 
+	/* struct ata_link flags */
+	ATA_LFLAG_DISABLED	= (1 << 0), /* this link is disabled (PMP) */
+
 	/* struct ata_port flags */
 	ATA_FLAG_SLAVE_POSS	= (1 << 0), /* host supports slave dev */
 					    /* (doesn't imply presence) */
@@ -283,6 +286,7 @@ enum {
 	ATA_EH_SUSPEND		= (1 << 3),
 	ATA_EH_RESUME		= (1 << 4),
 	ATA_EH_PM_FREEZE	= (1 << 5),
+	ATA_EH_ENABLE_LINK	= (1 << 6),
 
 	ATA_EH_RESET_MASK	= ATA_EH_SOFTRESET | ATA_EH_HARDRESET,
 	ATA_EH_PERDEV_MASK	= ATA_EH_REVALIDATE | ATA_EH_SUSPEND |
@@ -981,7 +985,7 @@ static inline void __ata_ehi_hotplugged(
 	ehi->flags |= ATA_EHI_HOTPLUGGED | ATA_EHI_RESUME_LINK;
 	ehi->hotplug_timestamp = jiffies;
 
-	ehi->action |= ATA_EH_SOFTRESET;
+	ehi->action |= ATA_EH_ENABLE_LINK | ATA_EH_SOFTRESET;
 	ehi->probe_mask |= (1 << ATA_MAX_DEVICES) - 1;
 }
 
Signature · 1 line
1.4.2.3
author
#4 lore raw

PATCH 4/12 libata-pmp: implement Port Multiplier support

In reply to Tejun Heo

Implement Port Multiplier support. To support PMP, a LLDD has to supply ops->pmp_read() and pmp_write(). If non-null, ->pmp_attach and ->pmp_detach are called on PMP attach and detach, respectively.

->pmp_read/write() can be called while the port is frozen, so they must be implemented by polling. This patch supplies several helpers to ease ->pmp_read/write() implementation.

Also, irq_handler and error_handler must be PMP aware. Bulk of PMP aware EH can be done by calling ata_pmp_do_eh() with appropriate methods. PMP EH uses separate set of reset methods and this patch implements standard prereset, hardreset and postreset methods.

This patch only implements PMP support. The next patch will integrate PMP into the reset of libata and thus enable PMP support.

---
 drivers/ata/Makefile      |    3 
 drivers/ata/libata-core.c |    8 
 drivers/ata/libata-pmp.c  | 1078 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/ata/libata.h      |    5 
 include/linux/libata.h    |   18 +
 5 files changed, 1111 insertions(+), 1 deletions(-)
drivers/ata/Makefile +2 -1
index ff6c6db..7bdb449 100644
@@ -59,5 +59,6 @@ obj-$(CONFIG_ATA_GENERIC)	+= ata_generic
 # Should be last libata driver
 obj-$(CONFIG_PATA_LEGACY)	+= pata_legacy.o
 
-libata-objs	:= libata-core.o libata-scsi.o libata-sff.o libata-eh.o
+libata-objs	:= libata-core.o libata-scsi.o libata-sff.o libata-eh.o \
+		   libata-pmp.o
 
drivers/ata/libata-core.c +8 -0
index 58836e3..27a4544 100644
@@ -6429,6 +6429,14 @@ #endif /* CONFIG_PCI */
 EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
 EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
 
+EXPORT_SYMBOL_GPL(sata_pmp_read_init_tf);
+EXPORT_SYMBOL_GPL(sata_pmp_read_val);
+EXPORT_SYMBOL_GPL(sata_pmp_write_init_tf);
+EXPORT_SYMBOL_GPL(sata_pmp_std_prereset);
+EXPORT_SYMBOL_GPL(sata_pmp_std_hardreset);
+EXPORT_SYMBOL_GPL(sata_pmp_std_postreset);
+EXPORT_SYMBOL_GPL(sata_pmp_do_eh);
+
 EXPORT_SYMBOL_GPL(ata_eng_timeout);
 EXPORT_SYMBOL_GPL(ata_port_schedule_eh);
 EXPORT_SYMBOL_GPL(ata_link_abort);
drivers/ata/libata-pmp.c new file +1078 -0

1,078 lines not on this page · raw message

drivers/ata/libata.h +5 -0
index 342888a..9629265 100644
@@ -118,6 +118,11 @@ extern void ata_schedule_scsi_eh(struct 
 extern void ata_scsi_dev_rescan(void *data);
 extern int ata_bus_probe(struct ata_port *ap);
 
+/* libata-pmp.c */
+extern int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *val);
+extern int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val);
+extern int sata_pmp_attach(struct ata_device *dev);
+
 /* libata-eh.c */
 extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
 extern void ata_scsi_error(struct Scsi_Host *host);
include/linux/libata.h +18 -0
index a940b67..2acc928 100644
@@ -927,6 +927,24 @@ extern unsigned long ata_pci_default_fil
 #endif /* CONFIG_PCI */
 
 /*
+ * PMP
+ */
+extern void sata_pmp_read_init_tf(struct ata_taskfile *tf,
+				  struct ata_device *dev, int pmp, int reg);
+extern u32 sata_pmp_read_val(const struct ata_taskfile *tf);
+extern void sata_pmp_write_init_tf(struct ata_taskfile *tf,
+				   struct ata_device *dev,
+				   int pmp, int reg, u32 val);
+extern int sata_pmp_std_prereset(struct ata_link *link);
+extern int sata_pmp_std_hardreset(struct ata_link *link, unsigned int *class);
+extern void sata_pmp_std_postreset(struct ata_link *link, unsigned int *class);
+extern void sata_pmp_do_eh(struct ata_port *ap,
+		ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
+		ata_reset_fn_t hardreset, ata_postreset_fn_t postreset,
+		ata_prereset_fn_t pmp_prereset, ata_reset_fn_t pmp_softreset,
+		ata_reset_fn_t pmp_hardreset, ata_postreset_fn_t pmp_postreset);
+
+/*
  * EH
  */
 extern void ata_eng_timeout(struct ata_port *ap);
Signature · 1 line
1.4.2.3
author
#5 lore raw

PATCH 5/12 libata-pmp: hook PMP support and enable it

In reply to Tejun Heo

Hook PMP support into libata and enable it. Connect SCR and probing functions, and update ata_dev_classify() to detect PMP.

---
 drivers/ata/libata-core.c |   92 +++++++++++++++++++++++++++++++--------------
 drivers/ata/libata-eh.c   |   15 +++++++
 2 files changed, 78 insertions(+), 29 deletions(-)
drivers/ata/libata-core.c +63 -29
index 27a4544..05f1667 100644
@@ -553,29 +553,46 @@ static unsigned int ata_devchk(struct at
  *	None.
  *
  *	RETURNS:
- *	Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, or %ATA_DEV_UNKNOWN
- *	the event of failure.
+ *	Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, %ATA_DEV_PMP or
+ *	%ATA_DEV_UNKNOWN the event of failure.
  */
-
 unsigned int ata_dev_classify(const struct ata_taskfile *tf)
 {
 	/* Apple's open source Darwin code hints that some devices only
 	 * put a proper signature into the LBA mid/high registers,
 	 * So, we only check those.  It's sufficient for uniqueness.
+	 *
+	 * ATA/ATAPI-7 (d1532v1r1: Feb. 19, 2003) specified separate
+	 * signatures for ATA and ATAPI devices attached on SerialATA,
+	 * 0x3c/0xc3 and 0x69/0x96 respectively.  However, SerialATA
+	 * spec has never mentioned about using different signatures
+	 * for ATA/ATAPI devices.  Then, Serial ATA II: Port
+	 * Multiplier specification began to use 0x69/0x96 to identify
+	 * port multpliers.  ATA/ATAPI-7 dropped descriptions about
+	 * 0x3c/0xc3 and 0x69/0x96 shortly and described them as
+	 * reserved for SerialATA.
+	 *
+	 * We follow the current spec and consider that 0x69/0x96
+	 * identifies a port multiplier.  If there is an ATAPI device
+	 * which returns 0x69/0x96 as its signature, we'll have to
+	 * implement 'try PMP, then try ATAPI' logic.
 	 */
-
 	if (((tf->lbam == 0) && (tf->lbah == 0)) ||
 	    ((tf->lbam == 0x3c) && (tf->lbah == 0xc3))) {
 		DPRINTK("found ATA device by sig\n");
 		return ATA_DEV_ATA;
 	}
 
-	if (((tf->lbam == 0x14) && (tf->lbah == 0xeb)) ||
-	    ((tf->lbam == 0x69) && (tf->lbah == 0x96))) {
+	if ((tf->lbam == 0x14) && (tf->lbah == 0xeb)) {
 		DPRINTK("found ATAPI device by sig\n");
 		return ATA_DEV_ATAPI;
 	}
 
+	if ((tf->lbam == 0x69) && (tf->lbah == 0x96)) {
+		DPRINTK("found PMP device by sig\n");
+		return ATA_DEV_PMP;
+	}
+
 	DPRINTK("unknown device\n");
 	return ATA_DEV_UNKNOWN;
 }
@@ -5102,24 +5119,29 @@ int sata_scr_valid(struct ata_link *link
  *	@val: Place to store read value
  *
  *	Read SCR register @reg of @link into *@val.  This function is
- *	guaranteed to succeed if the cable type of the port is SATA
- *	and the port implements ->scr_read.
+ *	guaranteed to succeed if @link is ap->link, the cable type of
+ *	the port is SATA and the port implements ->scr_read.
  *
  *	LOCKING:
- *	None.
+ *	None if @link is ap->link.  Kernel thread context otherwise.
  *
  *	RETURNS:
  *	0 on success, negative errno on failure.
  */
 int sata_scr_read(struct ata_link *link, int reg, u32 *val)
 {
-	struct ata_port *ap = link->ap;
+	if (ata_is_host_link(link)) {
+		struct ata_port *ap = link->ap;
 
-	if (sata_scr_valid(link)) {
-		*val = ap->ops->scr_read(ap, reg);
-		return 0;
+		if (sata_scr_valid(link)) {
+			*val = ap->ops->scr_read(ap, reg);
+			return 0;
+		}
+
+		return -EOPNOTSUPP;
 	}
-	return -EOPNOTSUPP;
+
+	return sata_pmp_scr_read(link, reg, val);
 }
 
 /**
@@ -5129,24 +5151,29 @@ int sata_scr_read(struct ata_link *link,
  *	@val: value to write
  *
  *	Write @val to SCR register @reg of @link.  This function is
- *	guaranteed to succeed if the cable type of the port is SATA
- *	and the port implements ->scr_read.
+ *	guaranteed to succeed if @link is ap->link, the cable type of
+ *	the port is SATA and the port implements ->scr_read.
  *
  *	LOCKING:
- *	None.
+ *	None if @link is ap->link.  Kernel thread context otherwise.
  *
  *	RETURNS:
  *	0 on success, negative errno on failure.
  */
 int sata_scr_write(struct ata_link *link, int reg, u32 val)
 {
-	struct ata_port *ap = link->ap;
+	if (ata_is_host_link(link)) {
+		struct ata_port *ap = link->ap;
 
-	if (sata_scr_valid(link)) {
-		ap->ops->scr_write(ap, reg, val);
-		return 0;
+		if (sata_scr_valid(link)) {
+			ap->ops->scr_write(ap, reg, val);
+			return 0;
+		}
+
+		return -EOPNOTSUPP;
 	}
-	return -EOPNOTSUPP;
+
+	return sata_pmp_scr_write(link, reg, val);
 }
 
 /**
@@ -5159,21 +5186,26 @@ int sata_scr_write(struct ata_link *link
  *	function performs flush after writing to the register.
  *
  *	LOCKING:
- *	None.
+ *	None if @link is ap->link.  Kernel thread context otherwise.
  *
  *	RETURNS:
  *	0 on success, negative errno on failure.
  */
 int sata_scr_write_flush(struct ata_link *link, int reg, u32 val)
 {
-	struct ata_port *ap = link->ap;
+	if (ata_is_host_link(link)) {
+		struct ata_port *ap = link->ap;
 
-	if (sata_scr_valid(link)) {
-		ap->ops->scr_write(ap, reg, val);
-		ap->ops->scr_read(ap, reg);
-		return 0;
+		if (sata_scr_valid(link)) {
+			ap->ops->scr_write(ap, reg, val);
+			ap->ops->scr_read(ap, reg);
+			return 0;
+		}
+
+		return -EOPNOTSUPP;
 	}
-	return -EOPNOTSUPP;
+
+	return sata_pmp_scr_write(link, reg, val);
 }
 
 /**
@@ -6020,6 +6052,8 @@ int ata_scsi_release(struct Scsi_Host *s
 	ap->ops->port_disable(ap);
 	ap->ops->port_stop(ap);
 
+	kfree(ap->pmp_link);
+
 	DPRINTK("EXIT\n");
 	return 1;
 }
drivers/ata/libata-eh.c +15 -0
index 228240a..7651983 100644
@@ -1750,6 +1750,8 @@ static int ata_eh_revalidate_and_attach(
 		unsigned int action = ata_eh_dev_action(dev);
 
 		if (action & ATA_EH_REVALIDATE && ata_dev_ready(dev)) {
+			WARN_ON(dev->class == ATA_DEV_PMP);
+
 			if (ata_link_offline(dev->link)) {
 				rc = -EIO;
 				break;
@@ -1770,6 +1772,13 @@ static int ata_eh_revalidate_and_attach(
 			   ata_class_enabled(ehc->classes[dev->devno])) {
 			dev->class = ehc->classes[dev->devno];
 
+			if (dev->class == ATA_DEV_PMP) {
+				rc = sata_pmp_attach(dev);
+				if (rc)
+					dev->class = ATA_DEV_UNKNOWN;
+				break;
+			}
+
 			rc = ata_dev_read_id(dev, &dev->class, 1, dev->id);
 			if (rc == 0)
 				rc = ata_dev_configure(dev, 1);
@@ -2190,6 +2199,12 @@ int ata_eh_recover(struct ata_port *ap, 
 		if (rc)
 			goto dev_fail;
 
+		/* if PMP got attached, return, we don't know what to do */
+		if (link->device->class == ATA_DEV_PMP) {
+			ehc->i.action = 0;
+			return 0;
+		}
+
 		/* configure transfer mode if the port has been reset */
 		if (ehc->i.flags & ATA_EHI_DID_RESET) {
 			rc = ata_set_mode(link, &dev);
Signature · 1 line
1.4.2.3
author
#6 lore raw

PATCH 6/12 sata_sil24: rename PMP related constants

In reply to Tejun Heo

Rename PMP related constants for consistency.

---
 drivers/ata/sata_sil24.c |   13 +++++++------
 1 files changed, 7 insertions(+), 6 deletions(-)
drivers/ata/sata_sil24.c +7 -6
index 9564e49..0a6306d 100644
@@ -100,10 +100,10 @@ enum {
 	 */
 	PORT_REGS_SIZE		= 0x2000,
 
-	PORT_LRAM		= 0x0000, /* 31 LRAM slots and PM regs */
+	PORT_LRAM		= 0x0000, /* 31 LRAM slots and PMP regs */
 	PORT_LRAM_SLOT_SZ	= 0x0080, /* 32 bytes PRB + 2 SGE, ACT... */
 
-	PORT_PM			= 0x0f80, /* 8 bytes PM * 16 (128 bytes) */
+	PORT_PMP		= 0x0f80, /* 8 bytes PMP * 16 (128 bytes) */
 		/* 32 bit regs */
 	PORT_CTRL_STAT		= 0x1000, /* write: ctrl-set, read: stat */
 	PORT_CTRL_CLR		= 0x1004, /* write: ctrl-clear */
@@ -139,9 +139,9 @@ enum {
 	PORT_CS_INIT		= (1 << 2), /* port initialize */
 	PORT_CS_IRQ_WOC		= (1 << 3), /* interrupt write one to clear */
 	PORT_CS_CDB16		= (1 << 5), /* 0=12b cdb, 1=16b cdb */
-	PORT_CS_RESUME		= (1 << 6), /* port resume */
+	PORT_CS_PMP_RESUME	= (1 << 6), /* PMP resume */
 	PORT_CS_32BIT_ACTV	= (1 << 10), /* 32-bit activation */
-	PORT_CS_PM_EN		= (1 << 13), /* port multiplier enable */
+	PORT_CS_PMP_EN		= (1 << 13), /* port multiplier enable */
 	PORT_CS_RDY		= (1 << 31), /* port ready to accept commands */
 
 	/* PORT_IRQ_STAT/ENABLE_SET/CLR */
@@ -567,7 +567,7 @@ static int sil24_softreset(struct ata_li
 
 	/* do SRST */
 	prb->ctrl = cpu_to_le16(PRB_CTRL_SRST);
-	prb->fis[1] = 0; /* no PM yet */
+	prb->fis[1] = 0; /* no PMP yet */
 
 	writel((u32)paddr, port + PORT_CMD_ACTIVATE);
 	writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4);
@@ -1056,7 +1056,8 @@ static void sil24_init_controller(struct
 		writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
 
 		/* Clear port multiplier enable and resume bits */
-		writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR);
+		writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME,
+		       port + PORT_CTRL_CLR);
 	}
 
 	/* Turn on interrupts */
Signature · 1 line
1.4.2.3
author
#8 lore raw

PATCH 7/12 sata_sil24: add PMP related constants

In reply to Tejun Heo

Add PMP related constants.

---
 drivers/ata/sata_sil24.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)
drivers/ata/sata_sil24.c +5 -0
index 0a6306d..866f84b 100644
@@ -104,6 +104,10 @@ enum {
 	PORT_LRAM_SLOT_SZ	= 0x0080, /* 32 bytes PRB + 2 SGE, ACT... */
 
 	PORT_PMP		= 0x0f80, /* 8 bytes PMP * 16 (128 bytes) */
+	PORT_PMP_STATUS		= 0x0000, /* port device status offset */
+	PORT_PMP_QACTIVE	= 0x0004, /* port device QActive offset */
+	PORT_PMP_SIZE		= 0x0008, /* 8 bytes per PMP */
+
 		/* 32 bit regs */
 	PORT_CTRL_STAT		= 0x1000, /* write: ctrl-set, read: stat */
 	PORT_CTRL_CLR		= 0x1004, /* write: ctrl-clear */
@@ -126,6 +130,7 @@ enum {
 	PORT_PHY_CFG		= 0x1050,
 	PORT_SLOT_STAT		= 0x1800,
 	PORT_CMD_ACTIVATE	= 0x1c00, /* 64 bit cmd activate * 31 (248 bytes) */
+	PORT_CONTEXT		= 0x1e04,
 	PORT_EXEC_DIAG		= 0x1e00, /* 32bit exec diag * 16 (64 bytes, 0-10 used on 3124) */
 	PORT_PSD_DIAG		= 0x1e40, /* 32bit psd diag * 16 (64 bytes, 0-8 used on 3124) */
 	PORT_SCONTROL		= 0x1f00,
Signature · 1 line
1.4.2.3
author
#10 lore raw

PATCH 8/12 sata_sil24: replace sil24_update_tf() with sil24_read_tf()

In reply to Tejun Heo

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.

---
 drivers/ata/sata_sil24.c |   23 ++++++++++++++---------
 1 files changed, 14 insertions(+), 9 deletions(-)
drivers/ata/sata_sil24.c +14 -9
index 866f84b..b7c0322 100644
@@ -479,15 +479,15 @@ static void sil24_dev_config(struct ata_
 		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 = (void __iomem *)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)
@@ -553,6 +553,7 @@ static int sil24_softreset(struct ata_li
 	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;
 
@@ -592,8 +593,8 @@ static int sil24_softreset(struct ata_li
 		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;
@@ -772,6 +773,7 @@ static void sil24_thaw(struct ata_port *
 static void sil24_error_intr(struct ata_port *ap)
 {
 	void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
+	struct sil24_port_priv *pp = ap->private_data;
 	struct ata_eh_info *ehi = &ap->link.eh_info;
 	int freeze = 0;
 	u32 irq_stat;
@@ -826,7 +828,7 @@ static void sil24_error_intr(struct ata_
 		/* record error info */
 		qc = ata_qc_from_tag(ap, ap->link.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;
@@ -843,8 +845,11 @@ static void sil24_error_intr(struct ata_
 
 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)
Signature · 1 line
1.4.2.3
author
#11 lore raw

PATCH 9/12 sata_sil24: separate out sil24_exec_polled_cmd()

In reply to Tejun Heo

Separate out sil24_exec_polled_cmd() from sil24_softreset(). This will be used to implement sil24_pmp_read/write().

---
 drivers/ata/sata_sil24.c |   75 +++++++++++++++++++++++++++++++++-------------
 1 files changed, 54 insertions(+), 21 deletions(-)
drivers/ata/sata_sil24.c +54 -21
index b7c0322..29e53d6 100644
@@ -546,16 +546,59 @@ static int sil24_init_port(struct ata_po
 	return 0;
 }
 
-static int sil24_softreset(struct ata_link *link, unsigned int *class)
+static int sil24_exec_polled_cmd(struct ata_port *ap, u32 ctrl,
+				 const struct ata_taskfile *tf,
+				 int pmp, int is_cmd,
+				 unsigned long timeout_msec)
 {
-	struct ata_port *ap = link->ap;
 	void __iomem *port = (void __iomem *)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_link *link, unsigned int *class)
+{
+	struct ata_port *ap = link->ap;
 	struct ata_taskfile tf;
-	u32 mask, irq_stat;
 	const char *reason;
+	int rc;
 
 	DPRINTK("ENTER\n");
 
@@ -572,24 +615,14 @@ static int sil24_softreset(struct ata_li
 	}
 
 	/* do SRST */
-	prb->ctrl = cpu_to_le16(PRB_CTRL_SRST);
-	prb->fis[1] = 0; /* no PMP yet */
-
-	writel((u32)paddr, port + PORT_CMD_ACTIVATE);
-	writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4);
-
-	mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT;
-	irq_stat = ata_wait_register(port + PORT_IRQ_STAT, mask, 0x0,
-				     100, ATA_TMOUT_BOOT / HZ * 1000);
-
-	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(link->device, &tf);	/* doesn't really matter */
+	rc = sil24_exec_polled_cmd(ap, PRB_CTRL_SRST, &tf, 0, 0,
+				   ATA_TMOUT_BOOT / HZ * 1000);
+	if (rc == -EBUSY) {
+		reason = "timeout";
+		goto err;
+	} else if (rc) {
+		reason = "SRST command error";
 		goto err;
 	}
 
Signature · 1 line
1.4.2.3
author
#12 lore raw

PATCH 10/12 sata_sil24: separate out sil24_do_softreset()

In reply to Tejun Heo

Separate out sil24_do_softreset() which takes @pmp as its last argument. This will be used to implement sil24_pmp_softreset().

---
 drivers/ata/sata_sil24.c |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)
drivers/ata/sata_sil24.c +8 -2
index 29e53d6..4530254 100644
@@ -593,7 +593,8 @@ static int sil24_exec_polled_cmd(struct 
 	return rc;
 }
 
-static int sil24_softreset(struct ata_link *link, unsigned int *class)
+static int sil24_do_softreset(struct ata_link *link, unsigned int *class,
+			      int pmp)
 {
 	struct ata_port *ap = link->ap;
 	struct ata_taskfile tf;
@@ -616,7 +617,7 @@ static int sil24_softreset(struct ata_li
 
 	/* do SRST */
 	ata_tf_init(link->device, &tf);	/* doesn't really matter */
-	rc = sil24_exec_polled_cmd(ap, PRB_CTRL_SRST, &tf, 0, 0,
+	rc = sil24_exec_polled_cmd(ap, PRB_CTRL_SRST, &tf, pmp, 0,
 				   ATA_TMOUT_BOOT / HZ * 1000);
 	if (rc == -EBUSY) {
 		reason = "timeout";
@@ -641,6 +642,11 @@ static int sil24_softreset(struct ata_li
 	return -EIO;
 }
 
+static int sil24_softreset(struct ata_link *link, unsigned int *class)
+{
+	return sil24_do_softreset(link, class, 0);
+}
+
 static int sil24_hardreset(struct ata_link *link, unsigned int *class)
 {
 	struct ata_port *ap = link->ap;
Signature · 1 line
1.4.2.3
author
#13 lore raw

PATCH 11/12 sata_sil24: implement PMP support

In reply to Tejun Heo

Implement PMP support. sil24 supports full FIS-switching. However, it has a PMP DMA CS errata which requires port-wide resetting if commands are outstanding to three or more devices when an error occurs on one of them.

ATAPI commands often result in CHECK SENSE and it's crucial to not reset them before fetching sense data. Unfortunately, ATAPI CHECK SENSE causes a lot of problem if command is outstanding to any other device usually resulting in port-wide reset. So, sata_sil24 implements sil24_qc_defer() which guarantees ATAPI command is run by itself.

---
 drivers/ata/sata_sil24.c |  227 ++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 209 insertions(+), 18 deletions(-)
drivers/ata/sata_sil24.c +209 -18
index 4530254..1832adf 100644
@@ -31,7 +31,7 @@ #include <linux/libata.h>
 #include <asm/io.h>
 
 #define DRV_NAME	"sata_sil24"
-#define DRV_VERSION	"0.3"
+#define DRV_VERSION	"0.4"
 
 /*
  * Port request block (PRB) 32 bytes
@@ -166,7 +166,7 @@ enum {
 
 	DEF_PORT_IRQ		= PORT_IRQ_COMPLETE | PORT_IRQ_ERROR |
 				  PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG |
-				  PORT_IRQ_UNK_FIS,
+				  PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_NOTIFY,
 
 	/* bits[27:16] are unmasked (raw) */
 	PORT_IRQ_RAW_SHIFT	= 16,
@@ -235,7 +235,8 @@ enum {
 	/* host flags */
 	SIL24_COMMON_FLAGS	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
 				  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-				  ATA_FLAG_NCQ | ATA_FLAG_SKIP_D2H_BSY,
+				  ATA_FLAG_NCQ | ATA_FLAG_SKIP_D2H_BSY |
+				  ATA_FLAG_PMP | ATA_FLAG_SDB_NOTIFY,
 	SIL24_FLAG_PCIX_IRQ_WOC	= (1 << 24), /* IRQ loss errata on PCI-X */
 
 	IRQ_STAT_4PORTS		= 0xf,
@@ -332,10 +333,15 @@ static u8 sil24_check_status(struct ata_
 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 void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
+static int sil24_qc_defer(struct ata_queued_cmd *qc);
 static void sil24_qc_prep(struct ata_queued_cmd *qc);
 static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc);
 static void sil24_irq_clear(struct ata_port *ap);
 static irqreturn_t sil24_interrupt(int irq, void *dev_instance);
+static void sil24_pmp_attach(struct ata_port *ap);
+static void sil24_pmp_detach(struct ata_port *ap);
+static int sil24_pmp_read(struct ata_device *dev, int pmp, int reg, u32 *r_val);
+static int sil24_pmp_write(struct ata_device *dev, int pmp, int reg, u32 val);
 static void sil24_freeze(struct ata_port *ap);
 static void sil24_thaw(struct ata_port *ap);
 static void sil24_error_handler(struct ata_port *ap);
@@ -346,6 +352,7 @@ static void sil24_host_stop(struct ata_h
 static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
 #ifdef CONFIG_PM
 static int sil24_pci_device_resume(struct pci_dev *pdev);
+static int sil24_port_resume(struct ata_port *ap);
 #endif
 
 static const struct pci_device_id sil24_pci_tbl[] = {
@@ -401,7 +408,7 @@ static const struct ata_port_operations 
 
 	.tf_read		= sil24_tf_read,
 
-	.qc_defer		= ata_std_qc_defer,
+	.qc_defer		= sil24_qc_defer,
 	.qc_prep		= sil24_qc_prep,
 	.qc_issue		= sil24_qc_issue,
 
@@ -411,6 +418,11 @@ static const struct ata_port_operations 
 	.scr_read		= sil24_scr_read,
 	.scr_write		= sil24_scr_write,
 
+	.pmp_attach		= sil24_pmp_attach,
+	.pmp_detach		= sil24_pmp_detach,
+	.pmp_read		= sil24_pmp_read,
+	.pmp_write		= sil24_pmp_write,
+
 	.freeze			= sil24_freeze,
 	.thaw			= sil24_thaw,
 	.error_handler		= sil24_error_handler,
@@ -422,6 +434,10 @@ static const struct ata_port_operations 
 	.port_start		= sil24_port_start,
 	.port_stop		= sil24_port_stop,
 	.host_stop		= sil24_host_stop,
+
+#ifdef CONFIG_PM
+	.port_resume		= sil24_port_resume,
+#endif
 };
 
 /*
@@ -530,11 +546,40 @@ static void sil24_tf_read(struct ata_por
 	*tf = pp->tf;
 }
 
+static void sil24_config_pmp(struct ata_port *ap, int attached)
+{
+	void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
+
+	if (attached)
+		writel(PORT_CS_PMP_EN, port + PORT_CTRL_STAT);
+	else
+		writel(PORT_CS_PMP_EN, port + PORT_CTRL_CLR);
+}
+
+static void sil24_clear_pmp(struct ata_port *ap)
+{
+	void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
+	int i;
+
+	writel(PORT_CS_PMP_RESUME, port + PORT_CTRL_CLR);
+
+	for (i = 0; i < SATA_PMP_MAX_PORTS; i++) {
+		void __iomem *pmp_base = port + PORT_PMP + i * PORT_PMP_SIZE;
+
+		writel(0, pmp_base + PORT_PMP_STATUS);
+		writel(0, pmp_base + PORT_PMP_QACTIVE);
+	}
+}
+
 static int sil24_init_port(struct ata_port *ap)
 {
 	void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
 	u32 tmp;
 
+	/* clear PMP error status */
+	if (ap->nr_pmp_links)
+		sil24_clear_pmp(ap);
+
 	writel(PORT_CS_INIT, port + PORT_CTRL_STAT);
 	ata_wait_register(port + PORT_CTRL_STAT,
 			  PORT_CS_INIT, PORT_CS_INIT, 10, 100);
@@ -644,7 +689,7 @@ static int sil24_do_softreset(struct ata
 
 static int sil24_softreset(struct ata_link *link, unsigned int *class)
 {
-	return sil24_do_softreset(link, class, 0);
+	return sil24_do_softreset(link, class, SATA_PMP_CTRL_PORT);
 }
 
 static int sil24_hardreset(struct ata_link *link, unsigned int *class)
@@ -714,6 +759,38 @@ static inline void sil24_fill_sg(struct 
 	}
 }
 
+static int sil24_qc_defer(struct ata_queued_cmd *qc)
+{
+	struct ata_link *link = qc->dev->link;
+	struct ata_port *ap = link->ap;
+	u8 prot = qc->tf.protocol;
+	int is_atapi = (prot == ATA_PROT_ATAPI ||
+			prot == ATA_PROT_ATAPI_NODATA ||
+			prot == ATA_PROT_ATAPI_DMA);
+
+	/* ATAPI commands completing with CHECK_SENSE cause various
+	 * weird problems if other commands are active.  PMP DMA CS
+	 * errata doesn't cover all and HSM violation occurs even with
+	 * only one other device active.  Always run an ATAPI command
+	 * by itself.
+	 */
+	if (unlikely(ap->excl_link)) {
+		if (link == ap->excl_link) {
+			if (ap->nr_active_links)
+				return ATA_DEFER_PORT;
+			qc->flags |= ATA_QCFLAG_CLEAR_EXCL;
+		} else
+			return ATA_DEFER_PORT;
+	} else if (unlikely(is_atapi)) {
+		ap->excl_link = link;
+		if (ap->nr_active_links)
+			return ATA_DEFER_PORT;
+		qc->flags |= ATA_QCFLAG_CLEAR_EXCL;
+	}
+
+	return ata_std_qc_defer(qc);
+}
+
 static void sil24_qc_prep(struct ata_queued_cmd *qc)
 {
 	struct ata_port *ap = qc->ap;
@@ -786,6 +863,63 @@ static void sil24_irq_clear(struct ata_p
 	/* unused */
 }
 
+static void sil24_pmp_attach(struct ata_port *ap)
+{
+	sil24_config_pmp(ap, 1);
+	sil24_init_port(ap);
+}
+
+static void sil24_pmp_detach(struct ata_port *ap)
+{
+	sil24_init_port(ap);
+	sil24_config_pmp(ap, 0);
+}
+
+static int sil24_pmp_read(struct ata_device *dev, int pmp, int reg, u32 *r_val)
+{
+	struct ata_port *ap = dev->link->ap;
+	struct ata_taskfile tf;
+	int rc;
+
+	sata_pmp_read_init_tf(&tf, dev, pmp, reg);
+	rc = sil24_exec_polled_cmd(ap, 0, &tf, SATA_PMP_CTRL_PORT, 1,
+				   SATA_PMP_SCR_TIMEOUT);
+	if (rc == 0) {
+		sil24_read_tf(ap, 0, &tf);
+		*r_val = sata_pmp_read_val(&tf);
+	}
+	return rc;
+}
+
+static int sil24_pmp_write(struct ata_device *dev, int pmp, int reg, u32 val)
+{
+	struct ata_port *ap = dev->link->ap;
+	struct ata_taskfile tf;
+
+	sata_pmp_write_init_tf(&tf, dev, pmp, reg, val);
+	return sil24_exec_polled_cmd(ap, 0, &tf, SATA_PMP_CTRL_PORT, 1,
+				     SATA_PMP_SCR_TIMEOUT);
+}
+
+static int sil24_pmp_softreset(struct ata_link *link, unsigned int *class)
+{
+	return sil24_do_softreset(link, class, link->pmp);
+}
+
+static int sil24_pmp_hardreset(struct ata_link *link, unsigned int *class)
+{
+	int rc;
+
+	rc = sil24_init_port(link->ap);
+	if (rc) {
+		ata_link_printk(link, KERN_ERR,
+				"hardreset failed (port not ready)\n");
+		return rc;
+	}
+
+	return sata_pmp_std_hardreset(link, class);
+}
+
 static void sil24_freeze(struct ata_port *ap)
 {
 	void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
@@ -813,7 +947,8 @@ static void sil24_error_intr(struct ata_
 {
 	void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
 	struct sil24_port_priv *pp = ap->private_data;
-	struct ata_eh_info *ehi = &ap->link.eh_info;
+	struct ata_link *link;
+	struct ata_eh_info *ehi;
 	int freeze = 0;
 	u32 irq_stat;
 
@@ -822,6 +957,8 @@ static void sil24_error_intr(struct ata_
 	writel(irq_stat, port + PORT_IRQ_STAT);
 
 	/* first, analyze and record host port events */
+	link = &ap->link;
+	ehi = &link->eh_info;
 	ata_ehi_clear_desc(ehi);
 
 	ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);
@@ -841,12 +978,59 @@ static void sil24_error_intr(struct ata_
 		freeze = 1;
 	}
 
+	if (irq_stat & PORT_IRQ_SDB_NOTIFY) {
+		/* No SNotification register.  We don't know which
+		 * ports should be aborted.  Just let them timeout.
+		 */
+		ehi->err_mask |= AC_ERR_OTHER;
+		ata_ehi_push_desc(ehi, ", SDB notify");
+		ata_port_schedule_eh(ap);
+	}
+
 	/* deal with command error */
 	if (irq_stat & PORT_IRQ_ERROR) {
 		struct sil24_cerr_info *ci = NULL;
+		struct ata_queued_cmd *qc = NULL;
 		unsigned int err_mask = 0, action = 0;
-		struct ata_queued_cmd *qc;
-		u32 cerr;
+		u32 context, cerr;
+		int pmp;
+
+		/* DMA Context Switch Failure in Port Multiplier Mode
+		 * errata.  If we have active commands to 3 or more
+		 * devices, any error condition on active devices can
+		 * corrupt DMA context switching.
+		 */
+		if (ap->nr_active_links >= 3) {
+			ehi->err_mask |= AC_ERR_OTHER;
+			ehi->action |= ATA_EH_HARDRESET;
+			ata_ehi_push_desc(ehi, ", PMP DMA CS errata");
+			freeze = 1;
+		}
+
+		/* find out the offending link and qc */
+		if (ap->nr_pmp_links) {
+			context = readl(port + PORT_CONTEXT);
+			pmp = (context >> 5) & 0xf;
+
+			if (pmp < ap->nr_pmp_links) {
+				link = &ap->pmp_link[pmp];
+				ehi = &link->eh_info;
+				qc = ata_qc_from_tag(ap, link->active_tag);
+
+				ata_ehi_clear_desc(ehi);
+				ata_ehi_push_desc(ehi, "irq_stat 0x%08x",
+						  irq_stat);
+
+				ata_link_abort(link);
+			} else {
+				err_mask |= AC_ERR_HSM;
+				action |= ATA_EH_HARDRESET;
+				freeze = 1;
+			}
+		} else {
+			qc = ata_qc_from_tag(ap, link->active_tag);
+			ata_link_abort(link);
+		}
 
 		/* analyze CMD_ERR */
 		cerr = readl(port + PORT_CMD_ERR);
@@ -865,7 +1049,6 @@ static void sil24_error_intr(struct ata_
 		}
 
 		/* record error info */
-		qc = ata_qc_from_tag(ap, ap->link.active_tag);
 		if (qc) {
 			sil24_read_tf(ap, qc->tag, &pp->tf);
 			qc->err_mask |= err_mask;
@@ -873,6 +1056,10 @@ static void sil24_error_intr(struct ata_
 			ehi->err_mask |= err_mask;
 
 		ehi->action |= action;
+
+		/* if PMP, resume */
+		if (ap->nr_pmp_links)
+			writel(PORT_CS_PMP_RESUME, port + PORT_CTRL_STAT);
 	}
 
 	/* freeze or abort */
@@ -964,16 +1151,14 @@ static irqreturn_t sil24_interrupt(int i
 
 static void sil24_error_handler(struct ata_port *ap)
 {
-	struct ata_eh_context *ehc = &ap->link.eh_context;
-
-	if (sil24_init_port(ap)) {
+	if (sil24_init_port(ap))
 		ata_eh_freeze_port(ap);
-		ehc->i.action |= ATA_EH_HARDRESET;
-	}
 
 	/* perform recovery */
-	ata_do_eh(ap, ata_std_prereset, sil24_softreset, sil24_hardreset,
-		  ata_std_postreset);
+	sata_pmp_do_eh(ap, ata_std_prereset, sil24_softreset, sil24_hardreset,
+		       ata_std_postreset, sata_pmp_std_prereset,
+		       sil24_pmp_softreset, sil24_pmp_hardreset,
+		       sata_pmp_std_postreset);
 }
 
 static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)
@@ -984,8 +1169,8 @@ static void sil24_post_internal_cmd(stru
 		qc->err_mask |= AC_ERR_OTHER;
 
 	/* make DMA engine forget about the failed command */
-	if (qc->err_mask)
-		sil24_init_port(ap);
+	if (qc->err_mask && sil24_init_port(ap))
+		ata_eh_freeze_port(ap);
 }
 
 static inline void sil24_cblk_free(struct sil24_port_priv *pp, struct device *dev)
@@ -1263,6 +1448,12 @@ static int sil24_pci_device_resume(struc
 
 	return 0;
 }
+
+static int sil24_port_resume(struct ata_port *ap)
+{
+	sil24_config_pmp(ap, ap->nr_pmp_links);
+	return 0;
+}
 #endif
 
 static int __init sil24_init(void)
Signature · 1 line
1.4.2.3
author
#14 lore raw

PATCH 12/12 sata_sil24: implement PORT_RST

In reply to Tejun Heo

As DEV_RST (hardreset) sometimes fail to recover the controller (especially after PMP DMA CS errata). In such cases, perform PORT_RST prior to DEV_RST.

---
 drivers/ata/sata_sil24.c |   87 +++++++++++++++++++++++++++++++++++-----------
 1 files changed, 66 insertions(+), 21 deletions(-)
drivers/ata/sata_sil24.c +66 -21
index 1832adf..75d2e9a 100644
@@ -320,6 +320,7 @@ struct sil24_port_priv {
 	union sil24_cmd_block *cmd_block;	/* 32 cmd blocks */
 	dma_addr_t cmd_block_dma;		/* DMA base addr for them */
 	struct ata_taskfile tf;			/* Cached taskfile registers */
+	int do_port_rst;
 };
 
 /* ap->host->private_data */
@@ -546,6 +547,29 @@ static void sil24_tf_read(struct ata_por
 	*tf = pp->tf;
 }
 
+static void sil24_config_port(void __iomem *port, unsigned long port_flags)
+{
+	/* configure IRQ WoC */
+	if (port_flags & SIL24_FLAG_PCIX_IRQ_WOC)
+		writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT);
+	else
+		writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
+
+	/* zero error counters. */
+	writel(0x8000, port + PORT_DECODE_ERR_THRESH);
+	writel(0x8000, port + PORT_CRC_ERR_THRESH);
+	writel(0x8000, port + PORT_HSHK_ERR_THRESH);
+	writel(0x0000, port + PORT_DECODE_ERR_CNT);
+	writel(0x0000, port + PORT_CRC_ERR_CNT);
+	writel(0x0000, port + PORT_HSHK_ERR_CNT);
+
+	/* always use 64bit activation */
+	writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
+
+	/* clear port multiplier enable and resume bits */
+	writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME, port + PORT_CTRL_CLR);
+}
+
 static void sil24_config_pmp(struct ata_port *ap, int attached)
 {
 	void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
@@ -574,6 +598,7 @@ static void sil24_clear_pmp(struct ata_p
 static int sil24_init_port(struct ata_port *ap)
 {
 	void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
+	struct sil24_port_priv *pp = ap->private_data;
 	u32 tmp;
 
 	/* clear PMP error status */
@@ -586,8 +611,12 @@ static int sil24_init_port(struct ata_po
 	tmp = ata_wait_register(port + PORT_CTRL_STAT,
 				PORT_CS_RDY, 0, 10, 100);
 
-	if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY)
+	if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY) {
+		pp->do_port_rst = 1;
+		ap->link.eh_context.i.action |= ATA_EH_HARDRESET;
 		return -EIO;
+	}
+
 	return 0;
 }
 
@@ -696,10 +725,34 @@ static int sil24_hardreset(struct ata_li
 {
 	struct ata_port *ap = link->ap;
 	void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
+	struct sil24_port_priv *pp = ap->private_data;
+	int did_port_rst = 0;
 	const char *reason;
 	int tout_msec, rc;
 	u32 tmp;
 
+ retry:
+	/* Sometimes, DEV_RST is not enough to recover the controller.
+	 * This happens a lot after PM DMA CS errata.
+	 */
+	if (pp->do_port_rst) {
+		ata_port_printk(ap, KERN_WARNING, "controller in dubious "
+				"state, performing PORT_RST\n");
+
+		writel(PORT_CS_PORT_RST, port + PORT_CTRL_STAT);
+		msleep(10);
+		writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
+		ata_wait_register(port + PORT_CTRL_STAT, PORT_CS_RDY, 0,
+				  10, 5000);
+
+		/* restore port configuration */
+		sil24_config_port(port, ap->flags);
+		sil24_config_pmp(ap, ap->nr_pmp_links);
+
+		pp->do_port_rst = 0;
+		did_port_rst = 1;
+	}
+
 	/* sil24 does the right thing(tm) without any protection */
 	sata_set_spd(link);
 
@@ -736,6 +789,11 @@ static int sil24_hardreset(struct ata_li
 	return -EAGAIN;
 
  err:
+	if (!did_port_rst) {
+		pp->do_port_rst = 1;
+		goto retry;
+	}
+
 	ata_link_printk(link, KERN_ERR, "hardreset failed (%s)\n", reason);
 	return -EIO;
 }
@@ -1004,6 +1062,7 @@ static void sil24_error_intr(struct ata_
 			ehi->err_mask |= AC_ERR_OTHER;
 			ehi->action |= ATA_EH_HARDRESET;
 			ata_ehi_push_desc(ehi, ", PMP DMA CS errata");
+			pp->do_port_rst = 1;
 			freeze = 1;
 		}
 
@@ -1151,6 +1210,8 @@ static irqreturn_t sil24_interrupt(int i
 
 static void sil24_error_handler(struct ata_port *ap)
 {
+	struct sil24_port_priv *pp = ap->private_data;
+
 	if (sil24_init_port(ap))
 		ata_eh_freeze_port(ap);
 
@@ -1159,6 +1220,8 @@ static void sil24_error_handler(struct a
 		       ata_std_postreset, sata_pmp_std_prereset,
 		       sil24_pmp_softreset, sil24_pmp_hardreset,
 		       sata_pmp_std_postreset);
+
+	pp->do_port_rst = 0;
 }
 
 static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)
@@ -1272,26 +1335,8 @@ static void sil24_init_controller(struct
 				           "failed to clear port RST\n");
 		}
 
-		/* Configure IRQ WoC */
-		if (port_flags & SIL24_FLAG_PCIX_IRQ_WOC)
-			writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT);
-		else
-			writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
-
-		/* Zero error counters. */
-		writel(0x8000, port + PORT_DECODE_ERR_THRESH);
-		writel(0x8000, port + PORT_CRC_ERR_THRESH);
-		writel(0x8000, port + PORT_HSHK_ERR_THRESH);
-		writel(0x0000, port + PORT_DECODE_ERR_CNT);
-		writel(0x0000, port + PORT_CRC_ERR_CNT);
-		writel(0x0000, port + PORT_HSHK_ERR_CNT);
-
-		/* Always use 64bit activation */
-		writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
-
-		/* Clear port multiplier enable and resume bits */
-		writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME,
-		       port + PORT_CTRL_CLR);
+		/* configure port */
+		sil24_config_port(port, port_flags);
 	}
 
 	/* Turn on interrupts */
Signature · 1 line
1.4.2.3