* [PATCHSET 3/3] implement PMP support, take 2
@ 2006-07-08 5:58 Tejun Heo
2006-07-08 5:58 ` [PATCH 02/12] libata-pmp: update ata_eh_reset() for PMP Tejun Heo
` (12 more replies)
0 siblings, 13 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide, htejun
Hello,
This is part of patchset series described in [T].
This is the second 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.
* s/pm/pmp/g
* ATA_LFLAG_DISABLED handling (SError.N hotplug event detection for
disabled ports)
* Power Management support - libata suspends and resumes properly with
PMP attached.
* Batched EH - much shorter EH time on multiple device failure
* Updated to fit new #upstream
This patchset is against
upstream (309bade002e9226781c2d7a015340d0089e399b5)
+ [1] hp-poll patchset, take 2
+ [2] libata-link patchset, take 2
+ [3] libata-pmp-prep patchset, take 2
Thanks.
--
tejun
[T] http://article.gmane.org/gmane.linux.ide/11927
[L] http://article.gmane.org/gmane.linux.ide/10122
[1] http://article.gmane.org/gmane.linux.ide/11862
[2] http://article.gmane.org/gmane.linux.ide/11928
[3] http://article.gmane.org/gmane.linux.ide/11946
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 03/12] libata-pmp: implement ATA_LFLAG_DISABLED
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
` (2 preceding siblings ...)
2006-07-08 5:58 ` [PATCH 04/12] libata-pmp: implement Port Multiplier support Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-19 20:36 ` Jeff Garzik
2006-07-08 5:58 ` [PATCH 05/12] libata-pmp: hook PMP support and enable it Tejun Heo
` (8 subsequent siblings)
12 siblings, 1 reply; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: 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.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-eh.c | 26 +++++++++++++++++++++++++-
drivers/scsi/libata-scsi.c | 10 ++++++----
include/linux/libata.h | 13 +++++++++----
3 files changed, 40 insertions(+), 9 deletions(-)
5505907a9aab2d5f22760ce6b367b01f99980991
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index 0a6f17e..0125c18 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -1042,6 +1042,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;
@@ -1060,7 +1061,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;
@@ -1948,6 +1964,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 (ata_dev_absent(dev) || ata_dev_ready(dev))
@@ -2058,6 +2078,10 @@ 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.flags & ATA_EHI_ENABLE_LINK)
+ link->flags &= ~ATA_LFLAG_DISABLED;
+
ata_link_for_each_dev(dev, link) {
ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
index d446a86..f756acd 100644
--- a/drivers/scsi/libata-scsi.c
+++ b/drivers/scsi/libata-scsi.c
@@ -431,9 +431,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 */
@@ -494,9 +495,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 */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index dce5ab8..906225c 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -147,6 +147,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) */
@@ -276,9 +279,10 @@ enum {
/* ata_eh_info->flags */
ATA_EHI_HOTPLUGGED = (1 << 0), /* could have been hotplugged */
- ATA_EHI_RESUME_LINK = (1 << 1), /* need to resume link */
- ATA_EHI_NO_AUTOPSY = (1 << 2), /* no autopsy */
- ATA_EHI_QUIET = (1 << 3), /* be quiet */
+ ATA_EHI_ENABLE_LINK = (1 << 1), /* enable disabled link */
+ ATA_EHI_RESUME_LINK = (1 << 2), /* need to resume link */
+ ATA_EHI_NO_AUTOPSY = (1 << 3), /* no autopsy */
+ ATA_EHI_QUIET = (1 << 4), /* be quiet */
ATA_EHI_DID_RESET = (1 << 16), /* already reset this port */
@@ -932,7 +936,8 @@ static inline void __ata_ehi_hotplugged(
if (ehi->flags & ATA_EHI_HOTPLUGGED)
return;
- ehi->flags |= ATA_EHI_HOTPLUGGED | ATA_EHI_RESUME_LINK;
+ ehi->flags |=
+ ATA_EHI_HOTPLUGGED | ATA_EHI_ENABLE_LINK | ATA_EHI_RESUME_LINK;
ehi->hotplug_timestamp = jiffies;
ehi->action |= ATA_EH_SOFTRESET;
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 01/12] libata-pmp: add PMP related constants, fields, ops and update helpers
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
2006-07-08 5:58 ` [PATCH 02/12] libata-pmp: update ata_eh_reset() for PMP Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-19 20:34 ` Jeff Garzik
2006-07-08 5:58 ` [PATCH 04/12] libata-pmp: implement Port Multiplier support Tejun Heo
` (10 subsequent siblings)
12 siblings, 1 reply; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: Tejun Heo
Add PMP related constants, fields and ops. Also, update
ata_class_enabled/disabled() such that PMP classes are considered.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
include/linux/libata.h | 33 +++++++++++++++++++++++++++++----
1 files changed, 29 insertions(+), 4 deletions(-)
5ec84546db9b2cc5da979d3543f3939beeca1aca
diff --git a/include/linux/libata.h b/include/linux/libata.h
index dea1e5f..dce5ab8 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -128,6 +128,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 currently in PIO mode */
@@ -142,7 +143,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 */
@@ -163,6 +166,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
@@ -284,6 +289,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
@@ -293,6 +301,8 @@ enum {
* most devices.
*/
ATA_SPINUP_WAIT = 8000,
+
+ SATA_PMP_SCR_TIMEOUT = 500,
};
enum hsm_task_states {
@@ -450,7 +460,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;
@@ -509,6 +524,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;
@@ -617,6 +634,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.
*/
@@ -977,12 +1000,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)
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 02/12] libata-pmp: update ata_eh_reset() for PMP
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-08 5:58 ` [PATCH 01/12] libata-pmp: add PMP related constants, fields, ops and update helpers Tejun Heo
` (11 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: 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.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-eh.c | 7 +++++--
1 files changed, 5 insertions(+), 2 deletions(-)
2cf75f99537b7ef01bacb8c1f946b1bd2f7ba975
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index 122226b..0a6f17e 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -1552,13 +1552,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;
@@ -1633,7 +1636,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;
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 04/12] libata-pmp: implement Port Multiplier support
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
2006-07-08 5:58 ` [PATCH 02/12] libata-pmp: update ata_eh_reset() for PMP Tejun Heo
2006-07-08 5:58 ` [PATCH 01/12] libata-pmp: add PMP related constants, fields, ops and update helpers Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-08 5:58 ` [PATCH 03/12] libata-pmp: implement ATA_LFLAG_DISABLED Tejun Heo
` (9 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: 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.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/Makefile | 3
drivers/scsi/libata-core.c | 8
drivers/scsi/libata-pmp.c | 1080 ++++++++++++++++++++++++++++++++++++++++++++
drivers/scsi/libata.h | 6
include/linux/libata.h | 18 +
5 files changed, 1114 insertions(+), 1 deletions(-)
create mode 100644 drivers/scsi/libata-pmp.c
af47f89c3d8d5d808721e12c67017ef1bcd37656
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index ebd0cf0..b3c2fa7 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -166,7 +166,8 @@ ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \
CFLAGS_ncr53c8xx.o := $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m)
zalon7xx-objs := zalon.o ncr53c8xx.o
NCR_Q720_mod-objs := NCR_Q720.o ncr53c8xx.o
-libata-objs := libata-core.o libata-scsi.o libata-bmdma.o libata-eh.o
+libata-objs := libata-core.o libata-scsi.o libata-bmdma.o libata-eh.o \
+ libata-pmp.o
oktagon_esp_mod-objs := oktagon_esp.o oktagon_io.o
# Files generated that shall be removed upon make clean
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 346130c..c950435 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -6272,6 +6272,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);
diff --git a/drivers/scsi/libata-pmp.c b/drivers/scsi/libata-pmp.c
new file mode 100644
index 0000000..c0bf4c1
--- /dev/null
+++ b/drivers/scsi/libata-pmp.c
@@ -0,0 +1,1080 @@
+/*
+ * libata-pmp.c - libata port multiplier support
+ *
+ * Maintained by: Jeff Garzik <jgarzik@pobox.com>
+ * Please ALWAYS copy linux-ide@vger.kernel.org
+ * on emails.
+ *
+ * Copyright 2006 Tejun Heo <htejun@gmail.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ *
+ *
+ * libata documentation is available via 'make {ps|pdf}docs',
+ * as Documentation/DocBook/libata.*
+ *
+ * Hardware documentation available from http://www.t13.org/ and
+ * http://www.sata-io.org/
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+#include <linux/libata.h>
+
+#include "libata.h"
+
+/**
+ * sata_pmp_read_init_tf - initialize TF for PMP read
+ * @tf: taskfile to initialize
+ * @dev: PMP dev
+ * @pmp: port multiplier port number
+ * @reg: register to read
+ *
+ * Initialize @tf for PMP read command.
+ *
+ * LOCKING:
+ * None.
+ */
+void sata_pmp_read_init_tf(struct ata_taskfile *tf,
+ struct ata_device *dev, int pmp, int reg)
+{
+ ata_tf_init(dev, tf);
+ tf->command = ATA_CMD_PMP_READ;
+ tf->protocol = ATA_PROT_NODATA;
+ tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ tf->feature = reg;
+ tf->device = pmp;
+}
+
+/**
+ * sata_pmp_read_val - extract PMP read result from TF
+ * @tf: target TF
+ *
+ * Determine PMP read result from @tf.
+ *
+ * LOCKING:
+ * None.
+ */
+u32 sata_pmp_read_val(const struct ata_taskfile *tf)
+{
+ return tf->nsect | tf->lbal << 8 | tf->lbam << 16 | tf->lbah << 24;
+}
+
+/**
+ * sata_pmp_read_init_tf - initialize TF for PMP write
+ * @tf: taskfile to initialize
+ * @dev: PMP dev
+ * @pmp: port multiplier port number
+ * @reg: register to read
+ * @val: value to write
+ *
+ * Initialize @tf for PMP write command.
+ *
+ * LOCKING:
+ * None.
+ */
+void sata_pmp_write_init_tf(struct ata_taskfile *tf,
+ struct ata_device *dev, int pmp, int reg, u32 val)
+{
+ ata_tf_init(dev, tf);
+ tf->command = ATA_CMD_PMP_WRITE;
+ tf->protocol = ATA_PROT_NODATA;
+ tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ tf->feature = reg;
+ tf->device = pmp;
+ tf->nsect = val & 0xff;
+ tf->lbal = (val >> 8) & 0xff;
+ tf->lbam = (val >> 16) & 0xff;
+ tf->lbah = (val >> 24) & 0xff;
+}
+
+/**
+ * sata_pmp_scr_read - read PSCR
+ * @link: ATA link to read PSCR for
+ * @reg: PSCR to read
+ * @r_val: resulting value
+ *
+ * Read PSCR @reg into @r_val for @link, to be called from
+ * ata_scr_read().
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *r_val)
+{
+ struct ata_port *ap = link->ap;
+ struct ata_device *pmp_dev = ap->link.device;
+
+ might_sleep();
+
+ if (reg > SATA_PMP_PSCR_CONTROL)
+ return -EINVAL;
+
+ return ap->ops->pmp_read(pmp_dev, link->pmp, reg, r_val);
+}
+
+/**
+ * sata_pmp_scr_write - write PSCR
+ * @link: ATA link to write PSCR for
+ * @reg: PSCR to write
+ * @val: value to be written
+ *
+ * Write @val to PSCR @reg for @link, to be called from
+ * ata_scr_write() and ata_scr_write_flush().
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val)
+{
+ struct ata_port *ap = link->ap;
+ struct ata_device *pmp_dev = ap->link.device;
+
+ might_sleep();
+
+ if (reg > SATA_PMP_PSCR_CONTROL)
+ return -EINVAL;
+
+ return ap->ops->pmp_write(pmp_dev, link->pmp, reg, val);
+}
+
+/**
+ * sata_pmp_std_prereset - prepare PMP link for reset
+ * @link: link to be reset
+ *
+ * @link is about to be reset. Initialize it.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep)
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+int sata_pmp_std_prereset(struct ata_link *link)
+{
+ struct ata_port *ap = link->ap;
+ struct ata_device *pmp_dev = ap->link.device;
+ struct ata_eh_context *ehc = &link->eh_context;
+ const unsigned long *timing = sata_ehc_deb_timing(ehc);
+ int rc;
+
+ /* handle link resume & hotplug spinup */
+ if ((ehc->i.flags & ATA_EHI_RESUME_LINK) &&
+ (pmp_dev->flags & ATA_DFLAG_PMP_HRST_TO_RESUME))
+ ehc->i.action |= ATA_EH_HARDRESET;
+
+ if (ehc->i.flags & ATA_EHI_HOTPLUGGED)
+ ata_wait_spinup(link);
+
+ /* if we're about to do hardreset, nothing more to do */
+ if (link->eh_context.i.action & ATA_EH_HARDRESET)
+ return 0;
+
+ /* resume link */
+ rc = sata_link_resume(link, timing);
+ if (rc) {
+ /* phy resume failed */
+ ata_link_printk(link, KERN_WARNING, "failed to resume link "
+ "for reset (errno=%d)\n", rc);
+ return rc;
+ }
+
+ /* clear SError bits including .X which blocks the port when set */
+ rc = sata_scr_write(link, SCR_ERROR, 0xffffffff);
+ if (rc) {
+ ata_link_printk(link, KERN_ERR,
+ "failed to clear SError (errno=%d)\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * sata_pmp_std_hardreset - standard hardreset method for PMP link
+ * @link: link to be reset
+ *
+ * Hardreset PMP port @link. Note that this function doesn't
+ * wait for BSY clearance. There simply isn't a generic way to
+ * wait the event. Instead, this function return -EAGAIN thus
+ * telling libata-EH to followup with softreset.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep)
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+int sata_pmp_std_hardreset(struct ata_link *link, unsigned int *class)
+{
+ const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+ int rc;
+
+ DPRINTK("ENTER\n");
+
+ /* do hardreset */
+ rc = sata_link_hardreset(link, timing);
+ if (rc) {
+ ata_link_printk(link, KERN_ERR,
+ "COMRESET failed (errno=%d)\n", rc);
+ goto out;
+ }
+
+ /* clear SError bits including .X which blocks the port when set */
+ rc = sata_scr_write(link, SCR_ERROR, 0xffffffff);
+ if (rc) {
+ ata_link_printk(link, KERN_ERR, "failed to clear SError "
+ "during hardreset (errno=%d)\n", rc);
+ goto out;
+ }
+
+ /* follow up with softreset so that we can wait for !BSY */
+ rc = -EAGAIN;
+ out:
+ DPRINTK("EXIT, rc=%d\n", rc);
+ return rc;
+}
+
+/**
+ * ata_std_postreset - standard postreset method for PMP link
+ * @link: the target ata_link
+ * @classes: classes of attached devices
+ *
+ * This function is invoked after a successful reset. Note that
+ * the device might have been reset more than once using
+ * different reset methods before postreset is invoked.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep)
+ */
+void sata_pmp_std_postreset(struct ata_link *link, unsigned int *class)
+{
+ u32 serror;
+
+ DPRINTK("ENTER\n");
+
+ /* clear SError */
+ if (sata_scr_read(link, SCR_ERROR, &serror) == 0)
+ sata_scr_write(link, SCR_ERROR, serror);
+
+ /* print link status */
+ sata_print_link_status(link);
+
+ DPRINTK("EXIT\n");
+}
+
+/**
+ * sata_pmp_read_gscr - read GSCR block of SATA PMP
+ * @dev: PMP device
+ * @gscr: buffer to read GSCR block into
+ *
+ * Read selected PMP GSCRs from the PMP at @dev. This will serve
+ * as configuration and identification info for the PMP.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int sata_pmp_read_gscr(struct ata_device *dev, u32 *gscr)
+{
+ static const int gscr_to_read[] = { 0, 1, 2, 32, 33, 64, 96 };
+ struct ata_port *ap = dev->link->ap;
+ int i, rc;
+
+ for (i = 0; i < ARRAY_SIZE(gscr_to_read); i++) {
+ int reg = gscr_to_read[i];
+
+ rc = ap->ops->pmp_read(dev, SATA_PMP_CTRL_PORT, reg,
+ &gscr[reg]);
+ if (rc) {
+ ata_dev_printk(dev, KERN_ERR, "failed to read "
+ "PMP GSCR[%d] (errno=%d)\n", reg, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static const char *sata_pmp_spec_rev_str(const u32 *gscr)
+{
+ u32 rev = gscr[SATA_PMP_GSCR_REV];
+
+ if (rev & (1 << 2))
+ return "1.1";
+ if (rev & (1 << 1))
+ return "1.0";
+ return "<unknown>";
+}
+
+static void sata_pmp_quirks(struct ata_device *dev, int *nr_ports)
+{
+ u32 *gscr = dev->gscr;
+ u16 vendor = sata_pmp_gscr_vendor(gscr);
+ u16 devid = sata_pmp_gscr_devid(gscr);
+ u8 rev = sata_pmp_gscr_rev(gscr);
+
+ /* Early revisions of sil4726 report one extra port but later
+ * revisions report two extra ports - one for configuration
+ * and the other for enclosure processor.
+ */
+ if (vendor == 0x1095 && devid == 0x4726) {
+ if (rev <= 1)
+ *nr_ports -= 1;
+ else
+ *nr_ports -= 2;
+ dev->flags |= ATA_DFLAG_PMP_HRST_TO_RESUME;
+ }
+
+ /* sil3726 has fixed number(5) of fanout devices */
+ if (vendor == 0x1095 && devid == 0x3726) {
+ *nr_ports = 5;
+ dev->flags |= ATA_DFLAG_PMP_HRST_TO_RESUME;
+ }
+}
+
+static int sata_pmp_configure(struct ata_device *dev, int *r_nr_ports,
+ int print_info)
+{
+ struct ata_link *link = dev->link;
+ struct ata_port *ap = link->ap;
+ u32 *gscr = dev->gscr;
+ const char *reason;
+ int nr_ports, rc;
+
+ nr_ports = sata_pmp_gscr_ports(gscr);
+
+ sata_pmp_quirks(dev, &nr_ports);
+
+ if (nr_ports <= 0 || nr_ports > SATA_PMP_MAX_PORTS) {
+ rc = -EINVAL;
+ reason = "invalid nr_ports";
+ goto fail;
+ }
+
+ rc = ap->ops->pmp_write(dev, link->pmp, SATA_PMP_GSCR_ERROR_EN,
+ SERR_PHYRDY_CHG);
+ if (rc) {
+ reason = "failed to write GSCR_ERROR_EN";
+ goto fail;
+ }
+
+ if (ap->flags & ATA_FLAG_SDB_NOTIFY &&
+ gscr[SATA_PMP_GSCR_FEAT] & SATA_PMP_FEAT_NOTIFY) {
+ gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY;
+
+ rc = ap->ops->pmp_write(dev, link->pmp, SATA_PMP_GSCR_FEAT_EN,
+ gscr[SATA_PMP_GSCR_FEAT_EN]);
+ if (rc) {
+ reason = "failed to write GSCR_FEAT_EN";
+ goto fail;
+ }
+ }
+
+ if (print_info)
+ ata_dev_printk(dev, KERN_INFO, "Port Multiplier %s, "
+ "0x%04x:0x%04x r%d, %d ports, feat 0x%x/0x%x\n",
+ sata_pmp_spec_rev_str(gscr),
+ sata_pmp_gscr_vendor(gscr),
+ sata_pmp_gscr_devid(gscr),
+ sata_pmp_gscr_rev(gscr),
+ nr_ports, gscr[SATA_PMP_GSCR_FEAT_EN],
+ gscr[SATA_PMP_GSCR_FEAT]);
+
+ *r_nr_ports = nr_ports;
+ return 0;
+
+ fail:
+ ata_dev_printk(dev, KERN_ERR,
+ "failed to configure Port Multiplier (%s)\n", reason);
+ return rc;
+}
+
+static int sata_pmp_init_links(struct ata_port *ap, int nr_ports)
+{
+ struct ata_link *pmp_link = ap->pmp_link;
+ int i;
+
+ if (!pmp_link) {
+ pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS,
+ GFP_NOIO);
+ if (!pmp_link)
+ return -ENOMEM;
+
+ for (i = 0; i < SATA_PMP_MAX_PORTS; i++)
+ ata_link_init(ap, &pmp_link[i], i);
+
+ ap->pmp_link = pmp_link;
+ }
+
+ for (i = 0; i < nr_ports; i++) {
+ struct ata_link *link = &pmp_link[i];
+ struct ata_eh_context *ehc = &link->eh_context;
+
+ link->reset_tries = ATA_EH_PMP_RESET_TRIES;
+ ehc->i.probe_mask |= 1;
+ ehc->i.action |= ATA_EH_SOFTRESET;
+ ehc->i.flags |= ATA_EHI_RESUME_LINK;
+ }
+
+ return 0;
+}
+
+/**
+ * sata_pmp_attach - attach a SATA PMP device
+ * @dev: SATA PMP device to attach
+ *
+ * Configure and attach SATA PMP device @dev. This function is
+ * also responsible for allocating and initializing PMP links.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+int sata_pmp_attach(struct ata_device *dev)
+{
+ struct ata_link *link = dev->link;
+ struct ata_port *ap = link->ap;
+ unsigned long flags;
+ struct ata_link *tlink;
+ int nr_ports, rc;
+
+ /* is it haging off the right place? */
+ if (!(ap->flags & ATA_FLAG_PMP)) {
+ ata_dev_printk(dev, KERN_ERR,
+ "host does not support Port Multiplier\n");
+ return -EINVAL;
+ }
+
+ if (!ata_is_host_link(link)) {
+ ata_dev_printk(dev, KERN_ERR,
+ "Port Multipliers cannot be nested\n");
+ return -EINVAL;
+ }
+
+ if (dev->devno) {
+ ata_dev_printk(dev, KERN_ERR,
+ "Port Multiplier must be the first device\n");
+ return -EINVAL;
+ }
+
+ WARN_ON(link->pmp != 0);
+ link->pmp = SATA_PMP_CTRL_PORT;
+
+ /* read GSCR block */
+ rc = sata_pmp_read_gscr(dev, dev->gscr);
+ if (rc)
+ goto fail;
+
+ /* config PMP */
+ rc = sata_pmp_configure(dev, &nr_ports, 1);
+ if (rc)
+ goto fail;
+
+ rc = sata_pmp_init_links(ap, nr_ports);
+ if (rc) {
+ ata_dev_printk(dev, KERN_INFO,
+ "failed to initialize PMP links\n");
+ goto fail;
+ }
+
+ /* attach it */
+ spin_lock_irqsave(ap->lock, flags);
+ WARN_ON(ap->nr_pmp_links);
+ ap->nr_pmp_links = nr_ports;
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ if (ap->ops->pmp_attach)
+ ap->ops->pmp_attach(ap);
+
+ ata_port_for_each_link(tlink, ap)
+ sata_link_init_spd_limit(tlink);
+
+ return 0;
+
+ fail:
+ link->pmp = 0;
+ return rc;
+}
+
+/**
+ * sata_pmp_detach - detach a SATA PMP device
+ * @dev: SATA PMP device to detach
+ *
+ * Detach SATA PMP device @dev. This function is also
+ * responsible for deconfiguring PMP links.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void sata_pmp_detach(struct ata_device *dev)
+{
+ struct ata_link *link = dev->link;
+ struct ata_port *ap = link->ap;
+ struct ata_link *tlink;
+ unsigned long flags;
+
+ ata_dev_printk(dev, KERN_INFO, "Port Multiplier detaching\n");
+
+ WARN_ON(!ata_is_host_link(link) || dev->devno ||
+ link->pmp != SATA_PMP_CTRL_PORT);
+
+ if (ap->ops->pmp_detach)
+ ap->ops->pmp_detach(ap);
+
+ ata_port_for_each_link(tlink, ap)
+ ata_eh_detach_dev(tlink->device);
+
+ spin_lock_irqsave(ap->lock, flags);
+ ap->nr_pmp_links = 0;
+ link->pmp = 0;
+ spin_unlock_irqrestore(ap->lock, flags);
+}
+
+/**
+ * sata_pmp_same_pmp - does new GSCR matches the configured PMP?
+ * @dev: PMP device to compare against
+ * @new_gscr: GSCR block of the new device
+ *
+ * Compare @new_gscr against @dev and determine whether @dev is
+ * the PMP described by @new_gscr.
+ *
+ * LOCKING:
+ * None.
+ *
+ * RETURNS:
+ * 1 if @dev matches @new_gscr, 0 otherwise.
+ */
+static int sata_pmp_same_pmp(struct ata_device *dev, const u32 *new_gscr)
+{
+ const u32 *old_gscr = dev->gscr;
+ u16 old_vendor, new_vendor, old_devid, new_devid;
+ int old_nr_ports, new_nr_ports;
+
+ old_vendor = sata_pmp_gscr_vendor(old_gscr);
+ new_vendor = sata_pmp_gscr_vendor(new_gscr);
+ old_devid = sata_pmp_gscr_devid(old_gscr);
+ new_devid = sata_pmp_gscr_devid(new_gscr);
+ old_nr_ports = sata_pmp_gscr_ports(old_gscr);
+ new_nr_ports = sata_pmp_gscr_ports(new_gscr);
+
+ if (old_vendor != new_vendor) {
+ ata_dev_printk(dev, KERN_INFO, "Port Multiplier "
+ "vendor mismatch '0x%x' != '0x%x'\n",
+ old_vendor, new_vendor);
+ return 0;
+ }
+
+ if (old_devid != new_devid) {
+ ata_dev_printk(dev, KERN_INFO, "Port Multiplier "
+ "device ID mismatch '0x%x' != '0x%x'\n",
+ old_devid, new_devid);
+ return 0;
+ }
+
+ if (old_nr_ports != new_nr_ports) {
+ ata_dev_printk(dev, KERN_INFO, "Port Multiplier "
+ "nr_ports mismatch '0x%x' != '0x%x'\n",
+ old_nr_ports, new_nr_ports);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * sata_pmp_revalidate - revalidate SATA PMP
+ * @dev: PMP device to revalidate
+ * @new_class: new class code
+ *
+ * Re-read GSCR block and make sure @dev is still attached to the
+ * port and properly configured.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+static int sata_pmp_revalidate(struct ata_device *dev, unsigned int new_class)
+{
+ struct ata_link *link = dev->link;
+ struct ata_port *ap = link->ap;
+ u32 *gscr = (void *)ap->sector_buf;
+ int nr_ports, rc;
+
+ DPRINTK("ENTER\n");
+
+ ata_eh_about_to_do(link, NULL, ATA_EH_REVALIDATE);
+
+ if (!ata_dev_enabled(dev)) {
+ rc = -ENODEV;
+ goto fail;
+ }
+
+ /* sometimes wrong class is reported, let it retry */
+ if (ata_class_enabled(new_class) && new_class != ATA_DEV_PMP) {
+ rc = -EIO;
+ goto fail;
+ }
+
+ /* read GSCR */
+ rc = sata_pmp_read_gscr(dev, gscr);
+ if (rc)
+ goto fail;
+
+ /* is the pmp still there? */
+ if (!sata_pmp_same_pmp(dev, gscr)) {
+ rc = -ENODEV;
+ goto fail;
+ }
+
+ memcpy(dev->gscr, gscr, sizeof(gscr[0]) * SATA_PMP_GSCR_DWORDS);
+
+ rc = sata_pmp_configure(dev, &nr_ports, 0);
+ if (rc)
+ goto fail;
+
+ if (nr_ports != ap->nr_pmp_links) {
+ rc = -ENODEV;
+ goto fail;
+ }
+
+ ata_eh_done(link, NULL, ATA_EH_REVALIDATE);
+
+ DPRINTK("EXIT, rc=0\n");
+ return 0;
+
+ fail:
+ ata_dev_printk(dev, KERN_ERR,
+ "PMP revalidation failed (errno=%d)\n", rc);
+ DPRINTK("EXIT, rc=%d\n", rc);
+ return rc;
+}
+
+/**
+ * sata_pmp_revalidate_quick - revalidate SATA PMP quickly
+ * @dev: PMP device to revalidate
+ *
+ * Make sure the attached PMP is accessible.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+static int sata_pmp_revalidate_quick(struct ata_device *dev)
+{
+ struct ata_port *ap = dev->link->ap;
+ u32 prod_id;
+ int rc;
+
+ rc = ap->ops->pmp_read(dev, SATA_PMP_CTRL_PORT, SATA_PMP_GSCR_PROD_ID,
+ &prod_id);
+ if (rc) {
+ ata_dev_printk(dev, KERN_ERR, "failed to read PMP product ID\n");
+ return rc;
+ }
+
+ if (prod_id != dev->gscr[SATA_PMP_GSCR_PROD_ID]) {
+ ata_dev_printk(dev, KERN_ERR, "PMP product ID mismatch\n");
+ /* something weird is going on, request full PMP recovery */
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * sata_pmp_eh_recover_pmp - recover PMP
+ * @ap: ATA port PMP is attached to
+ * @prereset: prereset method (can be NULL)
+ * @softreset: softreset method
+ * @hardreset: hardreset method
+ * @postreset: postreset method (can be NULL)
+ *
+ * Recover PMP attached to @ap. Recovery procedure is somewhat
+ * similar to that of ata_eh_recover() except that reset should
+ * always be performed in hard->soft sequence and recovery
+ * failure results in PMP detachment.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
+ ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
+ ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
+{
+ struct ata_link *link = &ap->link;
+ struct ata_eh_context *ehc = &link->eh_context;
+ struct ata_device *dev = link->device;
+ int tries = link->reset_tries;
+ int detach = 0, probe = 0, rc = 0;
+
+ DPRINTK("ENTER\n");
+
+ if (dev->flags & ATA_DFLAG_DETACH) {
+ detach = 1;
+ goto fail;
+ }
+
+ retry:
+ ehc->classes[0] = ATA_DEV_UNKNOWN;
+
+ if (ehc->i.action & ATA_EH_RESET_MASK) {
+ struct ata_link *tlink;
+
+ ata_eh_freeze_port(ap);
+
+ /* reset */
+ ehc->i.action = ATA_EH_HARDRESET;
+ rc = ata_eh_reset(link, 0, prereset, softreset, hardreset,
+ postreset);
+ if (rc) {
+ ata_link_printk(link, KERN_ERR,
+ "failed to reset PMP, giving up\n");
+ goto fail;
+ }
+
+ ata_eh_thaw_port(ap);
+
+ ata_port_for_each_link(tlink, ap) {
+ struct ata_eh_context *tehc = &tlink->eh_context;
+
+ tehc->i.action |= ATA_EH_SOFTRESET;
+ tehc->i.flags |= ATA_EHI_RESUME_LINK;
+ }
+ }
+
+ /* If revalidation is requested, revalidate and reconfigure;
+ * otherwise, do quick revalidation.
+ */
+ if (ehc->i.action & ATA_EH_REVALIDATE)
+ rc = sata_pmp_revalidate(dev, ehc->classes[0]);
+ else
+ rc = sata_pmp_revalidate_quick(dev);
+
+ if (rc == -ENODEV) {
+ probe = 1;
+ goto fail;
+ } else if (rc) {
+ if (--tries) {
+ int sleep = ehc->i.flags & ATA_EHI_DID_RESET;
+
+ ata_dev_printk(dev, KERN_WARNING,
+ "retrying hardreset%s\n",
+ sleep ? " in 5 secs" : "");
+ if (sleep)
+ ssleep(5);
+ ehc->i.action |= ATA_EH_HARDRESET;
+ goto retry;
+ } else {
+ ata_dev_printk(dev, KERN_ERR, "failed to recover PMP "
+ "after %d resets, giving up\n",
+ link->reset_tries);
+ goto fail;
+ }
+ }
+
+ /* okay, PMP resurrected */
+ ehc->i.flags &= ~ATA_EHI_DID_RESET;
+
+ DPRINTK("EXIT, rc=0\n");
+ return 0;
+
+ fail:
+ sata_pmp_detach(dev);
+ if (probe || detach) {
+ ata_eh_detach_dev(dev);
+ if (probe)
+ ehc->i.probe_mask |= 1;
+ } else
+ ata_dev_disable(dev);
+
+ DPRINTK("EXIT, rc=%d\n", rc);
+ return rc;
+}
+
+static int sata_pmp_eh_handle_disabled_links(struct ata_port *ap)
+{
+ struct ata_device *pmp_device = ap->link.device;
+ struct ata_link *link;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(ap->lock, flags);
+
+ ata_port_for_each_link(link, ap) {
+ if (!(link->flags & ATA_LFLAG_DISABLED))
+ continue;
+
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ /* Some PMPs require hardreset sequence to get
+ * SError.N working.
+ */
+ if ((pmp_device->flags & ATA_DFLAG_PMP_HRST_TO_RESUME) &&
+ (link->eh_context.i.flags & ATA_EHI_RESUME_LINK))
+ sata_link_hardreset(link, sata_deb_timing_normal);
+
+ /* unconditionally clear SError.N */
+ rc = sata_scr_write(link, SCR_ERROR, SERR_PHYRDY_CHG);
+ if (rc) {
+ ata_link_printk(link, KERN_ERR, "failed to clear "
+ "SError.N (errno=%d)\n", rc);
+ return rc;
+ }
+
+ spin_lock_irqsave(ap->lock, flags);
+ }
+
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ return 0;
+}
+
+static int sata_pmp_handle_link_fail(struct ata_link *link, int *link_tries)
+{
+ struct ata_port *ap = link->ap;
+ unsigned long flags;
+
+ if (link_tries[link->pmp] && --link_tries[link->pmp])
+ return 1;
+
+ /* disable this link */
+ if (!(link->flags & ATA_LFLAG_DISABLED)) {
+ ata_link_printk(link, KERN_WARNING,
+ "failed to recover link after %d tries, disabling\n",
+ ATA_EH_PMP_LINK_TRIES);
+
+ spin_lock_irqsave(ap->lock, flags);
+ link->flags |= ATA_LFLAG_DISABLED;
+ spin_unlock_irqrestore(ap->lock, flags);
+ }
+
+ ata_dev_disable(link->device);
+ link->eh_context.i.action = 0;
+
+ return 0;
+}
+
+/**
+ * sata_pmp_eh_recover - recover PMP-enabled port
+ * @ap: ATA port to recover
+ * @prereset: prereset method (can be NULL)
+ * @softreset: softreset method
+ * @hardreset: hardreset method
+ * @postreset: postreset method (can be NULL)
+ * @pmp_prereset: PMP prereset method (can be NULL)
+ * @pmp_softreset: PMP softreset method (can be NULL)
+ * @pmp_hardreset: PMP hardreset method (can be NULL)
+ * @pmp_postreset: PMP postreset method (can be NULL)
+ *
+ * Drive EH recovery operation for PMP enabled port @ap. This
+ * function recovers host and PMP ports with proper retrials and
+ * fallbacks. Actual recovery operations are performed using
+ * ata_eh_recover() and sata_pmp_eh_recover_pmp().
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int sata_pmp_eh_recover(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)
+{
+ int pmp_tries, link_tries[SATA_PMP_MAX_PORTS];
+ struct ata_link *pmp_link = &ap->link;
+ struct ata_device *pmp_dev = pmp_link->device;
+ struct ata_eh_context *pmp_ehc = &pmp_link->eh_context;
+ struct ata_link *link;
+ struct ata_device *dev;
+ u32 gscr_error;
+ int cnt, rc;
+
+ pmp_tries = ATA_EH_PMP_TRIES;
+ ata_port_for_each_link(link, ap)
+ link_tries[link->pmp] = ATA_EH_PMP_LINK_TRIES;
+
+ retry:
+ /* PMP attached? */
+ if (!ap->nr_pmp_links) {
+ rc = ata_eh_recover(ap, prereset, softreset, hardreset,
+ postreset, NULL);
+ if (rc) {
+ /* recovery failed, activate hp-poll */
+ ata_hp_poll_activate(ap);
+
+ ata_link_for_each_dev(dev, &ap->link)
+ ata_dev_disable(dev);
+
+ return rc;
+ }
+
+ if (pmp_dev->class != ATA_DEV_PMP)
+ return 0;
+
+ /* new PMP online */
+ ata_port_for_each_link(link, ap)
+ link_tries[link->pmp] = ATA_EH_PMP_LINK_TRIES;
+
+ /* fall through */
+ }
+
+ /* recover pmp */
+ rc = sata_pmp_eh_recover_pmp(ap, prereset, softreset, hardreset,
+ postreset);
+ if (rc)
+ goto pmp_fail;
+
+ /* handle disabled links */
+ rc = sata_pmp_eh_handle_disabled_links(ap);
+ if (rc)
+ goto pmp_fail;
+
+ /* recover links */
+ rc = ata_eh_recover(ap, pmp_prereset, pmp_softreset, pmp_hardreset,
+ pmp_postreset, &link);
+ if (rc)
+ goto link_fail;
+
+ /* Connection status might have changed while resetting other
+ * links, check SATA_PMP_GSCR_ERROR before returning.
+ */
+
+ /* clear snotification */
+ sata_scr_write(pmp_link, SCR_NOTIFICATION, (1 << ap->nr_pmp_links) - 1);
+
+ /* check GSCR_ERROR */
+ rc = ap->ops->pmp_read(pmp_dev, pmp_link->pmp, SATA_PMP_GSCR_ERROR,
+ &gscr_error);
+ if (rc) {
+ ata_dev_printk(pmp_dev, KERN_ERR,
+ "failed to read PMP_GSCR_ERROR\n");
+ goto pmp_fail;
+ }
+
+ cnt = 0;
+ ata_port_for_each_link(link, ap) {
+ if (!(gscr_error & (1 << link->pmp)))
+ continue;
+
+ if (sata_pmp_handle_link_fail(link, link_tries)) {
+ ata_ehi_hotplugged(&link->eh_context.i);
+ cnt++;
+ } else {
+ ata_link_printk(link, KERN_WARNING,
+ "PHY status changed but maxed out on retries, "
+ "giving up\n");
+ ata_link_printk(link, KERN_WARNING,
+ "Manully issue scan to resume this link\n");
+ }
+ }
+
+ if (cnt) {
+ ata_port_printk(ap, KERN_INFO, "PMP SError.N set for some "
+ "ports, repeating recovery\n");
+ goto retry;
+ }
+
+ return 0;
+
+ link_fail:
+ if (sata_pmp_handle_link_fail(link, link_tries)) {
+ pmp_ehc->i.action |= ATA_EH_HARDRESET;
+ goto retry;
+ }
+
+ /* fall through */
+ pmp_fail:
+ /* Control always ends up here after detaching PMP. Shut up
+ * and return if we're unloading.
+ */
+ if (ap->pflags & ATA_PFLAG_UNLOADING)
+ return rc;
+
+ if (!ap->nr_pmp_links)
+ goto retry;
+
+ if (--pmp_tries) {
+ ata_port_printk(ap, KERN_WARNING,
+ "failed to recover PMP, retrying in 5 secs\n");
+ pmp_ehc->i.action |= ATA_EH_HARDRESET;
+ ssleep(5);
+ goto retry;
+ }
+
+ ata_port_printk(ap, KERN_ERR,
+ "failed to recover PMP after %d tries, giving up\n",
+ ATA_EH_PMP_TRIES);
+ sata_pmp_detach(pmp_dev);
+ ata_dev_disable(pmp_dev);
+
+ return rc;
+}
+
+/**
+ * sata_pmp_do_eh - do standard error handling for PMP-enabled host
+ * @ap: host port to handle error for
+ * @prereset: prereset method (can be NULL)
+ * @softreset: softreset method
+ * @hardreset: hardreset method
+ * @postreset: postreset method (can be NULL)
+ * @pmp_prereset: PMP prereset method (can be NULL)
+ * @pmp_softreset: PMP softreset method (can be NULL)
+ * @pmp_hardreset: PMP hardreset method (can be NULL)
+ * @pmp_postreset: PMP postreset method (can be NULL)
+ *
+ * Perform standard error handling sequence for PMP-enabled host
+ * @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+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)
+{
+ ata_eh_autopsy(ap);
+ ata_eh_report(ap);
+ sata_pmp_eh_recover(ap, prereset, softreset, hardreset, postreset,
+ pmp_prereset, pmp_softreset, pmp_hardreset,
+ pmp_postreset);
+ ata_eh_finish(ap);
+}
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index 7017b9d..0016b9e 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -115,6 +115,12 @@ extern void ata_scsi_rbuf_fill(struct at
extern void ata_schedule_scsi_eh(struct Scsi_Host *shost);
extern void ata_scsi_dev_rescan(void *data);
+/* 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);
+extern void sata_pmp_detach(struct ata_device *dev);
+
/* libata-eh.c */
extern struct list_head hp_poll_list;
extern struct work_struct hp_poll_work;
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 906225c..283f77f 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -881,6 +881,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);
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 05/12] libata-pmp: hook PMP support and enable it
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
` (3 preceding siblings ...)
2006-07-08 5:58 ` [PATCH 03/12] libata-pmp: implement ATA_LFLAG_DISABLED Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-08 5:58 ` [PATCH 07/12] sata_sil24: add PMP related constants Tejun Heo
` (7 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: Tejun Heo
Hook PMP support into libata and enable it. Connect SCR and probing
functions, and update ata_dev_classify() to detect PMP.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-core.c | 92 ++++++++++++++++++++++++++++++--------------
drivers/scsi/libata-eh.c | 15 +++++++
2 files changed, 78 insertions(+), 29 deletions(-)
7f13e180e59b2eeed428688536c0b476a08f4b16
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index c950435..7480b47 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -550,29 +550,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;
}
@@ -5043,24 +5060,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);
}
/**
@@ -5070,24 +5092,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);
}
/**
@@ -5100,21 +5127,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);
}
/**
@@ -5905,6 +5937,8 @@ int ata_scsi_release(struct Scsi_Host *h
ap->ops->port_disable(ap);
ata_host_remove(ap, 0);
+ kfree(ap->pmp_link);
+
DPRINTK("EXIT\n");
return 1;
}
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index 0125c18..c4946cc 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -1730,6 +1730,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;
@@ -1750,6 +1752,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);
@@ -2166,6 +2175,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);
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 08/12] sata_sil24: replace sil24_update_tf() with sil24_read_tf()
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
` (5 preceding siblings ...)
2006-07-08 5:58 ` [PATCH 07/12] sata_sil24: add PMP related constants Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-08 5:58 ` [PATCH 09/12] sata_sil24: separate out sil24_exec_polled_cmd() Tejun Heo
` (5 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: 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.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/sata_sil24.c | 23 ++++++++++++++---------
1 files changed, 14 insertions(+), 9 deletions(-)
8bcba5c383f003b3a97aef03041620a6c469e73c
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 370fc6d..069665f 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -474,15 +474,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 + 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)
@@ -548,6 +548,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;
@@ -587,8 +588,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;
@@ -767,6 +768,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;
@@ -821,7 +823,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;
@@ -838,8 +840,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)
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 07/12] sata_sil24: add PMP related constants
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
` (4 preceding siblings ...)
2006-07-08 5:58 ` [PATCH 05/12] libata-pmp: hook PMP support and enable it Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-08 5:58 ` [PATCH 08/12] sata_sil24: replace sil24_update_tf() with sil24_read_tf() Tejun Heo
` (6 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: Tejun Heo
Add PMP related constants.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/sata_sil24.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
e5690641aac1962a11a64b81d78b769a35f6d278
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 958038a..370fc6d 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -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,
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 09/12] sata_sil24: separate out sil24_exec_polled_cmd()
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
` (6 preceding siblings ...)
2006-07-08 5:58 ` [PATCH 08/12] sata_sil24: replace sil24_update_tf() with sil24_read_tf() Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-08 5:58 ` [PATCH 10/12] sata_sil24: separate out sil24_do_softreset() Tejun Heo
` (4 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: Tejun Heo
Separate out sil24_exec_polled_cmd() from sil24_softreset(). This
will be used to implement sil24_pmp_read/write().
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/sata_sil24.c | 75 ++++++++++++++++++++++++++++++++-------------
1 files changed, 54 insertions(+), 21 deletions(-)
ebb14f2dbe5aa5b8267f85d02f3ea431bb1a3e54
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 069665f..0ee8d45 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -541,16 +541,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");
@@ -567,24 +610,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;
}
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 06/12] sata_sil24: rename PMP related constants
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
` (8 preceding siblings ...)
2006-07-08 5:58 ` [PATCH 10/12] sata_sil24: separate out sil24_do_softreset() Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-08 5:58 ` [PATCH 12/12] sata_sil24: implement PORT_RST Tejun Heo
` (2 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: Tejun Heo
Rename PMP related constants for consistency.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/sata_sil24.c | 13 +++++++------
1 files changed, 7 insertions(+), 6 deletions(-)
08589377d5dfc16cda38f1d7cdeadf75057e0d5d
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 68a3fc1..958038a 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -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 */
@@ -562,7 +562,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);
@@ -1051,7 +1051,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 */
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 10/12] sata_sil24: separate out sil24_do_softreset()
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
` (7 preceding siblings ...)
2006-07-08 5:58 ` [PATCH 09/12] sata_sil24: separate out sil24_exec_polled_cmd() Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-08 5:58 ` [PATCH 06/12] sata_sil24: rename PMP related constants Tejun Heo
` (3 subsequent siblings)
12 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: Tejun Heo
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 <htejun@gmail.com>
---
drivers/scsi/sata_sil24.c | 10 ++++++++--
1 files changed, 8 insertions(+), 2 deletions(-)
fcd3110e005d4db89e0e0df4eb10230dafbf708d
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 0ee8d45..7c868f6 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -588,7 +588,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;
@@ -611,7 +612,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";
@@ -636,6 +637,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;
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 12/12] sata_sil24: implement PORT_RST
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
` (9 preceding siblings ...)
2006-07-08 5:58 ` [PATCH 06/12] sata_sil24: rename PMP related constants Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-08 5:58 ` [PATCH 11/12] sata_sil24: implement PMP support Tejun Heo
2006-07-19 20:37 ` [PATCHSET 3/3] implement PMP support, take 2 Jeff Garzik
12 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: 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.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/sata_sil24.c | 97 ++++++++++++++++++++++++++++++++-------------
1 files changed, 70 insertions(+), 27 deletions(-)
8972274451231ed8fda138f7a696e1f40d4deebb
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 5e93f1c..593c054 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -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_set->private_data */
@@ -539,6 +540,29 @@ static void sil24_tf_read(struct ata_por
*tf = pp->tf;
}
+static void sil24_config_port(void __iomem *port, unsigned long host_flags)
+{
+ /* configure IRQ WoC */
+ if (host_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;
@@ -567,6 +591,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;
writel(PORT_CS_INIT, port + PORT_CTRL_STAT);
@@ -578,8 +603,12 @@ static int sil24_init_port(struct ata_po
/* clear PMP error status */
sil24_clear_pmp(ap);
- 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;
}
@@ -688,10 +717,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);
@@ -728,6 +781,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;
}
@@ -996,6 +1054,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;
}
@@ -1143,18 +1202,18 @@ 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;
+ struct sil24_port_priv *pp = ap->private_data;
- if (sil24_init_port(ap)) {
+ if (sil24_init_port(ap))
ata_eh_freeze_port(ap);
- ehc->i.action |= ATA_EH_HARDRESET;
- }
/* perform recovery */
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);
+
+ pp->do_port_rst = 0;
}
static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)
@@ -1165,8 +1224,10 @@ 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) {
+ if (sil24_init_port(ap))
+ ata_eh_freeze_port(ap);
+ }
}
static inline void sil24_cblk_free(struct sil24_port_priv *pp, struct device *dev)
@@ -1268,26 +1329,8 @@ static void sil24_init_controller(struct
"failed to clear port RST\n");
}
- /* Configure IRQ WoC */
- if (host_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, host_flags);
}
/* Turn on interrupts */
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 11/12] sata_sil24: implement PMP support
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
` (10 preceding siblings ...)
2006-07-08 5:58 ` [PATCH 12/12] sata_sil24: implement PORT_RST Tejun Heo
@ 2006-07-08 5:58 ` Tejun Heo
2006-07-19 20:37 ` [PATCHSET 3/3] implement PMP support, take 2 Jeff Garzik
12 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-08 5:58 UTC (permalink / raw)
To: jgarzik, alan, lkml, forrest.zhao, linux-ide; +Cc: 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.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/sata_sil24.c | 212 +++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 202 insertions(+), 10 deletions(-)
35a05dc2f59de02f444eb3b993e0cfea24a7cdae
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 7c868f6..5e93f1c 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -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, struct pt_regs *regs);
+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);
@@ -345,6 +351,7 @@ static void sil24_port_stop(struct ata_p
static void sil24_host_stop(struct ata_host_set *host_set);
static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
static int sil24_pci_device_resume(struct pci_dev *pdev);
+static int sil24_port_resume(struct ata_port *ap);
static const struct pci_device_id sil24_pci_tbl[] = {
{ 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 },
@@ -396,7 +403,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,
@@ -406,6 +413,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,
@@ -417,6 +429,8 @@ static const struct ata_port_operations
.port_start = sil24_port_start,
.port_stop = sil24_port_stop,
.host_stop = sil24_host_stop,
+
+ .port_resume = sil24_port_resume,
};
/*
@@ -525,6 +539,31 @@ 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;
@@ -536,6 +575,9 @@ static int sil24_init_port(struct ata_po
tmp = ata_wait_register(port + PORT_CTRL_STAT,
PORT_CS_RDY, 0, 10, 100);
+ /* clear PMP error status */
+ sil24_clear_pmp(ap);
+
if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY)
return -EIO;
return 0;
@@ -639,7 +681,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)
@@ -709,6 +751,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_LINK;
+ } 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;
@@ -781,6 +855,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_clear_pmp(ap);
+}
+
+static void sil24_pmp_detach(struct ata_port *ap)
+{
+ sil24_clear_pmp(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;
@@ -808,7 +939,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;
@@ -817,6 +949,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);
@@ -836,12 +970,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);
@@ -860,7 +1041,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;
@@ -868,6 +1048,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 */
@@ -967,8 +1151,10 @@ static void sil24_error_handler(struct a
}
/* 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)
@@ -1260,6 +1446,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;
+}
+
static int __init sil24_init(void)
{
return pci_module_init(&sil24_pci_driver);
--
1.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 01/12] libata-pmp: add PMP related constants, fields, ops and update helpers
2006-07-08 5:58 ` [PATCH 01/12] libata-pmp: add PMP related constants, fields, ops and update helpers Tejun Heo
@ 2006-07-19 20:34 ` Jeff Garzik
0 siblings, 0 replies; 19+ messages in thread
From: Jeff Garzik @ 2006-07-19 20:34 UTC (permalink / raw)
To: Tejun Heo; +Cc: alan, lkml, forrest.zhao, linux-ide
Tejun Heo wrote:
> Add PMP related constants, fields and ops. Also, update
> ata_class_enabled/disabled() such that PMP classes are considered.
>
> Signed-off-by: Tejun Heo <htejun@gmail.com>
FWIW:
As I noted on IRC, for generic ATA and SATA constants (i.e. stuff
mentioned in a specification somewhere), feel free to add these to an
easy-to-merge "tj-easy" branch.
Newer SATA constants are just as welcome as new ATA constants.
Jeff
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 03/12] libata-pmp: implement ATA_LFLAG_DISABLED
2006-07-08 5:58 ` [PATCH 03/12] libata-pmp: implement ATA_LFLAG_DISABLED Tejun Heo
@ 2006-07-19 20:36 ` Jeff Garzik
2006-07-24 6:52 ` Tejun Heo
0 siblings, 1 reply; 19+ messages in thread
From: Jeff Garzik @ 2006-07-19 20:36 UTC (permalink / raw)
To: Tejun Heo; +Cc: alan, lkml, forrest.zhao, linux-ide
Tejun Heo wrote:
> 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.
why?
libata should notice new devices, when resuming.
Jeff
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCHSET 3/3] implement PMP support, take 2
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
` (11 preceding siblings ...)
2006-07-08 5:58 ` [PATCH 11/12] sata_sil24: implement PMP support Tejun Heo
@ 2006-07-19 20:37 ` Jeff Garzik
2006-07-24 6:56 ` Tejun Heo
12 siblings, 1 reply; 19+ messages in thread
From: Jeff Garzik @ 2006-07-19 20:37 UTC (permalink / raw)
To: Tejun Heo; +Cc: alan, lkml, forrest.zhao, linux-ide
Modulo the comments sent, this patchset looks pretty good...
Jeff
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 03/12] libata-pmp: implement ATA_LFLAG_DISABLED
2006-07-19 20:36 ` Jeff Garzik
@ 2006-07-24 6:52 ` Tejun Heo
0 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-24 6:52 UTC (permalink / raw)
To: Jeff Garzik; +Cc: alan, lkml, forrest.zhao, linux-ide
Jeff Garzik wrote:
> Tejun Heo wrote:
>> 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.
>
> why?
>
> libata should notice new devices, when resuming.
It will do so on links which are operating normally. ATA_LFLAG_DISABLED
is set only when libata EH fails to recover the link (link hardreset
failure) which takes quite some retries and is more severe than device
recovery failure. This is the red marking screaming "stay away from
this link!". For example, let's say a notebook has a broken SATA link
to drive bay and EH failed it. Without the above behavior, libata EH
will try to recover the link on every resume which is both inconvenient
and incorrect.
The thing is that resume from sleep is a link-resuming operation but not
a hotplug event, just as PMP slave link resets following host link reset
are link-resuming but not hotplugging. IOW, resume doesn't positively
indicate that the attached device could have been changed.
libata EH doesn't need hotplug events to do hotplugging. If it detects
such events during EH, it will perform the needed operations, but there
are things which are appropriate only when hotplug event is positively
indicated like detaching disabled (failed) devices or re-enabling failed
links.
--
tejun
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCHSET 3/3] implement PMP support, take 2
2006-07-19 20:37 ` [PATCHSET 3/3] implement PMP support, take 2 Jeff Garzik
@ 2006-07-24 6:56 ` Tejun Heo
0 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2006-07-24 6:56 UTC (permalink / raw)
To: Jeff Garzik; +Cc: alan, lkml, forrest.zhao, linux-ide
Jeff Garzik wrote:
> Modulo the comments sent, this patchset looks pretty good...
Thanks. Glad to hear that. I'll work on the commented stuff. As
powersave/hp-poll stuff seems to have more issues to resolve, I think
merging PMP first is better. As things have pretty much settled for
PMP, I won't separate easy patches out.
--
tejun
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 01/12] libata-pmp: add PMP related constants, fields, ops and update helpers
2007-07-01 10:26 [PATCHSET 3/4] libata: prep for PMP support, take 4 Tejun Heo
@ 2007-07-01 10:26 ` Tejun Heo
0 siblings, 0 replies; 19+ messages in thread
From: Tejun Heo @ 2007-07-01 10:26 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +Cc: Tejun Heo
Add PMP related constants, fields and ops. Also, update
ata_class_enabled/disabled() such that PMP classes are considered.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
include/linux/libata.h | 31 +++++++++++++++++++++++++++----
1 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/include/linux/libata.h b/include/linux/libata.h
index a353305..3db3aa5 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -153,7 +153,11 @@ 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_SEMB = 7, /* SEMB */
+ ATA_DEV_SEMB_UNSUP = 8, /* SEMB (unsupported) */
+ ATA_DEV_NONE = 9, /* no device */
/* struct ata_link flags */
ATA_LFLAG_HRST_TO_RESUME = (1 << 0), /* hardreset to resume link */
@@ -178,6 +182,8 @@ enum {
ATA_FLAG_IGN_SIMPLEX = (1 << 15), /* ignore SIMPLEX */
ATA_FLAG_NO_IORDY = (1 << 16), /* controller lacks iordy */
ATA_FLAG_ACPI_SATA = (1 << 17), /* need native SATA ACPI layout */
+ ATA_FLAG_PMP = (1 << 18),
+ ATA_FLAG_SDB_NOTIFY = (1 << 19),
/* The following flag belongs to ap->pflags but is kept in
* ap->flags because it's referenced in many LLDs and will be
@@ -295,6 +301,10 @@ enum {
/* how hard are we gonna try to probe/recover devices */
ATA_PROBE_MAX_TRIES = 3,
ATA_EH_DEV_TRIES = 3,
+ ATA_EH_PMP_TRIES = 5,
+ ATA_EH_PMP_LINK_TRIES = 3,
+
+ SATA_PMP_SCR_TIMEOUT = 250,
/* Horkage types. May be set by libata or controller on drives
(some horkage may be drive/controller pair dependant */
@@ -445,7 +455,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;
@@ -625,6 +640,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.
*/
@@ -1020,12 +1041,14 @@ static inline unsigned int ata_tag_internal(unsigned int tag)
*/
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 || class == ATA_DEV_SEMB;
}
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 || class == ATA_DEV_SEMB_UNSUP;
}
static inline unsigned int ata_class_absent(unsigned int class)
--
1.5.0.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
end of thread, other threads:[~2007-07-01 10:26 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-07-08 5:58 [PATCHSET 3/3] implement PMP support, take 2 Tejun Heo
2006-07-08 5:58 ` [PATCH 02/12] libata-pmp: update ata_eh_reset() for PMP Tejun Heo
2006-07-08 5:58 ` [PATCH 01/12] libata-pmp: add PMP related constants, fields, ops and update helpers Tejun Heo
2006-07-19 20:34 ` Jeff Garzik
2006-07-08 5:58 ` [PATCH 04/12] libata-pmp: implement Port Multiplier support Tejun Heo
2006-07-08 5:58 ` [PATCH 03/12] libata-pmp: implement ATA_LFLAG_DISABLED Tejun Heo
2006-07-19 20:36 ` Jeff Garzik
2006-07-24 6:52 ` Tejun Heo
2006-07-08 5:58 ` [PATCH 05/12] libata-pmp: hook PMP support and enable it Tejun Heo
2006-07-08 5:58 ` [PATCH 07/12] sata_sil24: add PMP related constants Tejun Heo
2006-07-08 5:58 ` [PATCH 08/12] sata_sil24: replace sil24_update_tf() with sil24_read_tf() Tejun Heo
2006-07-08 5:58 ` [PATCH 09/12] sata_sil24: separate out sil24_exec_polled_cmd() Tejun Heo
2006-07-08 5:58 ` [PATCH 10/12] sata_sil24: separate out sil24_do_softreset() Tejun Heo
2006-07-08 5:58 ` [PATCH 06/12] sata_sil24: rename PMP related constants Tejun Heo
2006-07-08 5:58 ` [PATCH 12/12] sata_sil24: implement PORT_RST Tejun Heo
2006-07-08 5:58 ` [PATCH 11/12] sata_sil24: implement PMP support Tejun Heo
2006-07-19 20:37 ` [PATCHSET 3/3] implement PMP support, take 2 Jeff Garzik
2006-07-24 6:56 ` Tejun Heo
-- strict thread matches above, loose matches on Subject: below --
2007-07-01 10:26 [PATCHSET 3/4] libata: prep for PMP support, take 4 Tejun Heo
2007-07-01 10:26 ` [PATCH 01/12] libata-pmp: add PMP related constants, fields, ops and update helpers Tejun Heo
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).